blob: 0f78693cee41ea7cb5806e16862bb5ba7b101b65 [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 *
8import Win
Jack Jansene160fe12001-07-16 21:57:50 +00009import Ctl
Jack Jansen17720092001-07-14 14:02:21 +000010import Qd
11import Res
12import Scrap
13import os
14import macfs
15import MacTextEditor
16import Mlte
17
18UNDOLABELS = [ # Indexed by MLTECanUndo() value
19 "Typing", "Cut", "Paste", "Clear", "Font Change", "Color Change", "Size Change",
20 "Style Change", "Align Left", "Align Center", "Align Right", "Drop", "Move"]
21
Jack Jansene160fe12001-07-16 21:57:50 +000022class MlteWindow(Window):
Jack Jansen17720092001-07-14 14:02:21 +000023 def open(self, path, name, data):
24 self.path = path
25 self.name = name
26 r = windowbounds(400, 400)
27 w = Win.NewWindow(r, name, 1, 0, -1, 1, 0x55555555)
28 self.wid = w
Jack Jansene160fe12001-07-16 21:57:50 +000029 flags = MacTextEditor.kTXNDrawGrowIconMask|MacTextEditor.kTXNWantHScrollBarMask| \
Jack Jansen17720092001-07-14 14:02:21 +000030 MacTextEditor.kTXNWantVScrollBarMask
31 self.ted, self.frameid = Mlte.TXNNewObject(None, w, None, flags, MacTextEditor.kTXNTextEditStyleFrameType,
32 MacTextEditor.kTXNTextFile, MacTextEditor.kTXNMacOSEncoding)
Jack Jansen17720092001-07-14 14:02:21 +000033 self.ted.TXNSetData(MacTextEditor.kTXNTextData, data, 0, 0x7fffffff)
Jack Jansen17720092001-07-14 14:02:21 +000034 self.changed = 0
35 self.do_postopen()
36 self.do_activate(1, None)
37
38 def do_idle(self, event):
Jack Jansen17720092001-07-14 14:02:21 +000039 self.ted.TXNIdle()
40 self.ted.TXNAdjustCursor(None)
41
Jack Jansen17720092001-07-14 14:02:21 +000042
43
44 def do_activate(self, onoff, evt):
Jack Jansen17720092001-07-14 14:02:21 +000045 if onoff:
Jack Jansene160fe12001-07-16 21:57:50 +000046## self.ted.TXNActivate(self.frameid, 0)
Jack Jansen17720092001-07-14 14:02:21 +000047 self.ted.TXNFocus(1)
48 self.parent.active = self
Jack Jansen17720092001-07-14 14:02:21 +000049 else:
50 self.ted.TXNFocus(0)
Jack Jansene160fe12001-07-16 21:57:50 +000051 self.parent.active = None
Jack Jansen17720092001-07-14 14:02:21 +000052 self.parent.updatemenubar()
53
Jack Jansene160fe12001-07-16 21:57:50 +000054 def do_update(self, wid, event):
55 self.ted.TXNDraw(None)
56
57 def do_postresize(self, width, height, window):
58 self.ted.TXNResizeFrame(width, height, self.frameid)
59
60 def do_contentclick(self, local, modifiers, evt):
61 self.ted.TXNClick(evt)
62 self.parent.updatemenubar()
63
Jack Jansen17720092001-07-14 14:02:21 +000064 def do_char(self, ch, event):
65 self.ted.TXNKeyDown(event)
Jack Jansene160fe12001-07-16 21:57:50 +000066 self.parent.updatemenubar()
Jack Jansen17720092001-07-14 14:02:21 +000067
68 def close(self):
69 if self.changed:
70 save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?'%self.name, 1)
71 if save > 0:
72 self.menu_save()
73 elif save < 0:
74 return
75 if self.parent.active == self:
76 self.parent.active = None
Jack Jansene160fe12001-07-16 21:57:50 +000077 self.ted.TXNDeleteObject()
Jack Jansen17720092001-07-14 14:02:21 +000078 del self.ted
79## del self.tedtexthandle
80 self.do_postclose()
81
82 def menu_save(self):
83 if not self.path:
84 self.menu_save_as()
85 return # Will call us recursively
Jack Jansen17720092001-07-14 14:02:21 +000086 dhandle = self.ted.TXNGetData(0, 0x7fffffff)
87 data = dhandle.data
88 fp = open(self.path, 'wb') # NOTE: wb, because data has CR for end-of-line
89 fp.write(data)
90 if data[-1] <> '\r': fp.write('\r')
91 fp.close()
92 self.changed = 0
93
94 def menu_save_as(self):
95 fss, ok = macfs.StandardPutFile('Save as:')
96 if not ok: return
97 self.path = fss.as_pathname()
98 self.name = os.path.split(self.path)[-1]
99 self.wid.SetWTitle(self.name)
100 self.menu_save()
101
102 def menu_cut(self):
103## self.ted.WESelView()
104 self.ted.TXNCut()
105### Mlte.ConvertToPublicScrap()
106## Scrap.ZeroScrap()
107## self.ted.WECut()
108## self.updatescrollbars()
109 self.parent.updatemenubar()
110 self.changed = 1
111
112 def menu_copy(self):
113## Scrap.ZeroScrap()
114 self.ted.TXNCopy()
115### Mlte.ConvertToPublicScrap()
116## self.updatescrollbars()
117 self.parent.updatemenubar()
118
119 def menu_paste(self):
120### Mlte.ConvertFromPublicScrap()
121 self.ted.TXNPaste()
122## self.updatescrollbars()
123 self.parent.updatemenubar()
124 self.changed = 1
125
126 def menu_clear(self):
127## self.ted.WESelView()
128 self.ted.TXNClear()
129## self.updatescrollbars()
130 self.parent.updatemenubar()
131 self.changed = 1
132
133 def menu_undo(self):
134 self.ted.TXNUndo()
135## self.updatescrollbars()
136 self.parent.updatemenubar()
137
138 def menu_redo(self):
139 self.ted.TXNRedo()
140## self.updatescrollbars()
141 self.parent.updatemenubar()
142
143 def have_selection(self):
144 start, stop = self.ted.TXNGetSelection()
145 return start < stop
146
147 def can_paste(self):
148 return Mlte.TXNIsScrapPastable()
149
150 def can_undo(self):
151 can, which = self.ted.TXNCanUndo()
152 if not can:
153 return None
154 if which >= len(UNDOLABELS):
155 # Unspecified undo
156 return "Undo"
157 which = UNDOLABELS[which]
158
159 return "Undo "+which
160
161 def can_redo(self):
162 can, which = self.ted.TXNCanRedo()
163 if not can:
164 return None
165 if which >= len(UNDOLABELS):
166 # Unspecified undo
167 return "Redo"
168 which = UNDOLABELS[which]
169
170 return "Redo "+which
171
172class Mlted(Application):
173 def __init__(self):
174 Application.__init__(self)
175 self.num = 0
176 self.active = None
177 self.updatemenubar()
178
179 def makeusermenus(self):
180 self.filemenu = m = Menu(self.menubar, "File")
181 self.newitem = MenuItem(m, "New window", "N", self.open)
182 self.openitem = MenuItem(m, "Open...", "O", self.openfile)
183 self.closeitem = MenuItem(m, "Close", "W", self.closewin)
184 m.addseparator()
185 self.saveitem = MenuItem(m, "Save", "S", self.save)
186 self.saveasitem = MenuItem(m, "Save as...", "", self.saveas)
187 m.addseparator()
188 self.quititem = MenuItem(m, "Quit", "Q", self.quit)
189
190 self.editmenu = m = Menu(self.menubar, "Edit")
191 self.undoitem = MenuItem(m, "Undo", "Z", self.undo)
Jack Jansene160fe12001-07-16 21:57:50 +0000192 self.redoitem = MenuItem(m, "Redo", None, self.redo)
193 m.addseparator()
Jack Jansen17720092001-07-14 14:02:21 +0000194 self.cutitem = MenuItem(m, "Cut", "X", self.cut)
195 self.copyitem = MenuItem(m, "Copy", "C", self.copy)
196 self.pasteitem = MenuItem(m, "Paste", "V", self.paste)
197 self.clearitem = MenuItem(m, "Clear", "", self.clear)
198
199 # Groups of items enabled together:
200 self.windowgroup = [self.closeitem, self.saveitem, self.saveasitem, self.editmenu]
201 self.focusgroup = [self.cutitem, self.copyitem, self.clearitem]
202 self.windowgroup_on = -1
203 self.focusgroup_on = -1
204 self.pastegroup_on = -1
205 self.undo_label = "never"
206 self.redo_label = "never"
207
208 def updatemenubar(self):
209 changed = 0
210 on = (self.active <> None)
211 if on <> self.windowgroup_on:
212 for m in self.windowgroup:
213 m.enable(on)
214 self.windowgroup_on = on
215 changed = 1
216 if on:
217 # only if we have an edit menu
218 on = self.active.have_selection()
219 if on <> self.focusgroup_on:
220 for m in self.focusgroup:
221 m.enable(on)
222 self.focusgroup_on = on
223 changed = 1
224 on = self.active.can_paste()
225 if on <> self.pastegroup_on:
226 self.pasteitem.enable(on)
227 self.pastegroup_on = on
228 changed = 1
229 on = self.active.can_undo()
230 if on <> self.undo_label:
231 if on:
232 self.undoitem.enable(1)
233 self.undoitem.settext(on)
234 self.undo_label = on
235 else:
236 self.undoitem.settext("Nothing to undo")
237 self.undoitem.enable(0)
238 changed = 1
239 on = self.active.can_redo()
240 if on <> self.redo_label:
241 if on:
242 self.redoitem.enable(1)
243 self.redoitem.settext(on)
244 self.redo_label = on
245 else:
246 self.redoitem.settext("Nothing to redo")
247 self.redoitem.enable(0)
248 changed = 1
249 if changed:
250 DrawMenuBar()
251
252 #
253 # Apple menu
254 #
255
256 def do_about(self, id, item, window, event):
257 EasyDialogs.Message("A simple single-font text editor based on MacTextEditor")
258
259 #
260 # File menu
261 #
262
263 def open(self, *args):
264 self._open(0)
265
266 def openfile(self, *args):
267 self._open(1)
268
269 def _open(self, askfile):
270 if askfile:
271 fss, ok = macfs.StandardGetFile('TEXT')
272 if not ok:
273 return
274 path = fss.as_pathname()
275 name = os.path.split(path)[-1]
276 try:
277 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
278 data = fp.read()
279 fp.close()
280 except IOError, arg:
281 EasyDialogs.Message("IOERROR: "+`arg`)
282 return
283 else:
284 path = None
285 name = "Untitled %d"%self.num
286 data = ''
Jack Jansene160fe12001-07-16 21:57:50 +0000287 w = MlteWindow(self)
Jack Jansen17720092001-07-14 14:02:21 +0000288 w.open(path, name, data)
289 self.num = self.num + 1
290
291 def closewin(self, *args):
292 if self.active:
293 self.active.close()
294 else:
295 EasyDialogs.Message("No active window?")
296
297 def save(self, *args):
298 if self.active:
299 self.active.menu_save()
300 else:
301 EasyDialogs.Message("No active window?")
302
303 def saveas(self, *args):
304 if self.active:
305 self.active.menu_save_as()
306 else:
307 EasyDialogs.Message("No active window?")
308
309
310 def quit(self, *args):
311 for w in self._windows.values():
312 w.close()
313 if self._windows:
314 return
315 self._quit()
316
317 #
318 # Edit menu
319 #
320
321 def undo(self, *args):
322 if self.active:
323 self.active.menu_undo()
324 else:
325 EasyDialogs.Message("No active window?")
326
327 def redo(self, *args):
328 if self.active:
329 self.active.menu_redo()
330 else:
331 EasyDialogs.Message("No active window?")
332
333 def cut(self, *args):
334 if self.active:
335 self.active.menu_cut()
336 else:
337 EasyDialogs.Message("No active window?")
338
339 def copy(self, *args):
340 if self.active:
341 self.active.menu_copy()
342 else:
343 EasyDialogs.Message("No active window?")
344
345 def paste(self, *args):
346 if self.active:
347 self.active.menu_paste()
348 else:
349 EasyDialogs.Message("No active window?")
350
351 def clear(self, *args):
352 if self.active:
353 self.active.menu_clear()
354 else:
355 EasyDialogs.Message("No active window?")
356
357 #
358 # Other stuff
359 #
360
361 def idle(self, event):
362 if self.active:
363 self.active.do_idle(event)
364
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