blob: 6a0f8823046a11aaee5222015f312d280e6e8b8f [file] [log] [blame]
Jack Jansen111fdcf1996-04-16 14:34:26 +00001# A minimal text editor.
Jack Jansene7b900d1996-04-11 15:41:39 +00002#
Jack Jansen111fdcf1996-04-16 14:34:26 +00003# To be done:
4# - Update viewrect after resize
5# - Handle horizontal scrollbar correctly
6# - Functionality: find, etc.
Jack Jansene7b900d1996-04-11 15:41:39 +00007
Jack Jansen5a6fdcd2001-08-25 12:15:04 +00008from Carbon.Menu import DrawMenuBar
Jack Jansene7b900d1996-04-11 15:41:39 +00009from FrameWork import *
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000010from Carbon import Win
11from Carbon import Qd
12from Carbon import TE
13from Carbon import Scrap
Jack Jansene7b900d1996-04-11 15:41:39 +000014import os
15import macfs
16
Jack Jansen111fdcf1996-04-16 14:34:26 +000017class TEWindow(ScrolledWindow):
Jack Jansene7b900d1996-04-11 15:41:39 +000018 def open(self, path, name, data):
19 self.path = path
20 self.name = name
Jack Jansen84445071996-04-19 15:54:47 +000021 r = windowbounds(400, 400)
Jack Jansencbed91b2001-08-03 13:31:36 +000022 w = Win.NewWindow(r, name, 1, 0, -1, 1, 0)
Jack Jansene7b900d1996-04-11 15:41:39 +000023 self.wid = w
Jack Jansenee8662f1996-08-19 10:58:37 +000024 x0, y0, x1, y1 = self.wid.GetWindowPort().portRect
25 x0 = x0 + 4
26 y0 = y0 + 4
27 x1 = x1 - 20
28 y1 = y1 - 20
29 vr = dr = x0, y0, x1, y1
30 ##vr = 4, 0, r[2]-r[0]-15, r[3]-r[1]-15
31 ##dr = (0, 0, vr[2], 0)
Jack Jansene7b900d1996-04-11 15:41:39 +000032 Qd.SetPort(w)
33 Qd.TextFont(4)
34 Qd.TextSize(9)
Jack Jansen84445071996-04-19 15:54:47 +000035 self.ted = TE.TENew(dr, vr)
Jack Jansene7b900d1996-04-11 15:41:39 +000036 self.ted.TEAutoView(1)
37 self.ted.TESetText(data)
38 w.DrawGrowIcon()
Jack Jansen111fdcf1996-04-16 14:34:26 +000039 self.scrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +000040 self.changed = 0
41 self.do_postopen()
Jack Jansen111fdcf1996-04-16 14:34:26 +000042 self.do_activate(1, None)
Jack Jansene7b900d1996-04-11 15:41:39 +000043
44 def do_idle(self):
45 self.ted.TEIdle()
46
Jack Jansen111fdcf1996-04-16 14:34:26 +000047 def getscrollbarvalues(self):
48 dr = self.ted.destRect
49 vr = self.ted.viewRect
50 height = self.ted.nLines * self.ted.lineHeight
51 vx = self.scalebarvalue(dr[0], dr[2]-dr[0], vr[0], vr[2])
52 vy = self.scalebarvalue(dr[1], dr[1]+height, vr[1], vr[3])
53 print dr, vr, height, vx, vy
Jack Jansen84445071996-04-19 15:54:47 +000054 return None, vy
Jack Jansen111fdcf1996-04-16 14:34:26 +000055
56 def scrollbar_callback(self, which, what, value):
57 if which == 'y':
58 if what == 'set':
59 height = self.ted.nLines * self.ted.lineHeight
60 cur = self.getscrollbarvalues()[1]
61 delta = (cur-value)*height/32767
62 if what == '-':
63 delta = self.ted.lineHeight
64 elif what == '--':
65 delta = (self.ted.viewRect[3]-self.ted.lineHeight)
66 if delta <= 0:
67 delta = self.ted.lineHeight
68 elif what == '+':
69 delta = -self.ted.lineHeight
70 elif what == '++':
71 delta = -(self.ted.viewRect[3]-self.ted.lineHeight)
72 if delta >= 0:
73 delta = -self.ted.lineHeight
74 self.ted.TEPinScroll(0, delta)
75 print 'SCROLL Y', delta
76 else:
Jack Jansen84445071996-04-19 15:54:47 +000077 pass # No horizontal scrolling
Jack Jansen111fdcf1996-04-16 14:34:26 +000078
Jack Jansene7b900d1996-04-11 15:41:39 +000079 def do_activate(self, onoff, evt):
Jack Jansen111fdcf1996-04-16 14:34:26 +000080 print "ACTIVATE", onoff
81 ScrolledWindow.do_activate(self, onoff, evt)
Jack Jansene7b900d1996-04-11 15:41:39 +000082 if onoff:
83 self.ted.TEActivate()
84 self.parent.active = self
85 self.parent.updatemenubar()
86 else:
87 self.ted.TEDeactivate()
88
89 def do_update(self, wid, event):
90 Qd.EraseRect(wid.GetWindowPort().portRect)
91 self.ted.TEUpdate(wid.GetWindowPort().portRect)
Jack Jansen111fdcf1996-04-16 14:34:26 +000092 self.updatescrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +000093
94 def do_contentclick(self, local, modifiers, evt):
95 shifted = (modifiers & 0x200)
96 self.ted.TEClick(local, shifted)
Jack Jansen111fdcf1996-04-16 14:34:26 +000097 self.updatescrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +000098 self.parent.updatemenubar()
99
100 def do_char(self, ch, event):
Jack Jansen111fdcf1996-04-16 14:34:26 +0000101 self.ted.TESelView()
Jack Jansene7b900d1996-04-11 15:41:39 +0000102 self.ted.TEKey(ord(ch))
103 self.changed = 1
Jack Jansen111fdcf1996-04-16 14:34:26 +0000104 self.updatescrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +0000105 self.parent.updatemenubar()
106
107 def close(self):
108 if self.changed:
109 save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?'%self.name, 1)
110 if save > 0:
111 self.menu_save()
112 elif save < 0:
113 return
114 if self.parent.active == self:
115 self.parent.active = None
116 self.parent.updatemenubar()
117 del self.ted
118 self.do_postclose()
119
120 def menu_save(self):
121 if not self.path:
122 self.menu_save_as()
123 return # Will call us recursively
124 print 'Saving to ', self.path
125 dhandle = self.ted.TEGetText()
126 data = dhandle.data
127 fp = open(self.path, 'wb') # NOTE: wb, because data has CR for end-of-line
128 fp.write(data)
129 if data[-1] <> '\r': fp.write('\r')
130 fp.close()
131 self.changed = 0
132
133 def menu_save_as(self):
134 fss, ok = macfs.StandardPutFile('Save as:')
135 if not ok: return
136 self.path = fss.as_pathname()
137 self.name = os.path.split(self.path)[-1]
138 self.wid.SetWTitle(self.name)
139 self.menu_save()
140
141 def menu_cut(self):
Jack Jansen111fdcf1996-04-16 14:34:26 +0000142 self.ted.TESelView()
Jack Jansene7b900d1996-04-11 15:41:39 +0000143 self.ted.TECut()
Jack Jansen9cf00dd2001-12-31 15:02:56 +0000144 if hasattr(Scrap, 'ZeroScrap'):
145 Scrap.ZeroScrap()
146 else:
147 Scrap.ClearCurrentScrap()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000148 TE.TEToScrap()
149 self.updatescrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +0000150 self.parent.updatemenubar()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000151 self.changed = 1
Jack Jansene7b900d1996-04-11 15:41:39 +0000152
153 def menu_copy(self):
154 self.ted.TECopy()
Jack Jansen9cf00dd2001-12-31 15:02:56 +0000155 if hasattr(Scrap, 'ZeroScrap'):
156 Scrap.ZeroScrap()
157 else:
158 Scrap.ClearCurrentScrap()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000159 TE.TEToScrap()
160 self.updatescrollbars()
161 self.parent.updatemenubar()
Jack Jansene7b900d1996-04-11 15:41:39 +0000162
163 def menu_paste(self):
Jack Jansen111fdcf1996-04-16 14:34:26 +0000164 TE.TEFromScrap()
165 self.ted.TESelView()
Jack Jansene7b900d1996-04-11 15:41:39 +0000166 self.ted.TEPaste()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000167 self.updatescrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +0000168 self.parent.updatemenubar()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000169 self.changed = 1
Jack Jansene7b900d1996-04-11 15:41:39 +0000170
171 def menu_clear(self):
Jack Jansen111fdcf1996-04-16 14:34:26 +0000172 self.ted.TESelView()
Jack Jansene7b900d1996-04-11 15:41:39 +0000173 self.ted.TEDelete()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000174 self.updatescrollbars()
Jack Jansene7b900d1996-04-11 15:41:39 +0000175 self.parent.updatemenubar()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000176 self.changed = 1
Jack Jansene7b900d1996-04-11 15:41:39 +0000177
178 def have_selection(self):
Jack Jansen111fdcf1996-04-16 14:34:26 +0000179 return (self.ted.selStart < self.ted.selEnd)
Jack Jansene7b900d1996-04-11 15:41:39 +0000180
181class Ped(Application):
182 def __init__(self):
183 Application.__init__(self)
184 self.num = 0
185 self.active = None
186 self.updatemenubar()
187
188 def makeusermenus(self):
189 self.filemenu = m = Menu(self.menubar, "File")
190 self.newitem = MenuItem(m, "New window", "N", self.open)
191 self.openitem = MenuItem(m, "Open...", "O", self.openfile)
192 self.closeitem = MenuItem(m, "Close", "W", self.closewin)
193 m.addseparator()
194 self.saveitem = MenuItem(m, "Save", "S", self.save)
195 self.saveasitem = MenuItem(m, "Save as...", "", self.saveas)
196 m.addseparator()
197 self.quititem = MenuItem(m, "Quit", "Q", self.quit)
198
199 self.editmenu = m = Menu(self.menubar, "Edit")
200 self.undoitem = MenuItem(m, "Undo", "Z", self.undo)
201 self.cutitem = MenuItem(m, "Cut", "X", self.cut)
202 self.copyitem = MenuItem(m, "Copy", "C", self.copy)
203 self.pasteitem = MenuItem(m, "Paste", "V", self.paste)
204 self.clearitem = MenuItem(m, "Clear", "", self.clear)
205
206 # Not yet implemented:
207 self.undoitem.enable(0)
208
209 # Groups of items enabled together:
210 self.windowgroup = [self.closeitem, self.saveitem, self.saveasitem, self.editmenu]
211 self.focusgroup = [self.cutitem, self.copyitem, self.clearitem]
212 self.windowgroup_on = -1
213 self.focusgroup_on = -1
Jack Jansen111fdcf1996-04-16 14:34:26 +0000214 self.pastegroup_on = -1
Jack Jansene7b900d1996-04-11 15:41:39 +0000215
216 def updatemenubar(self):
217 changed = 0
218 on = (self.active <> None)
219 if on <> self.windowgroup_on:
220 for m in self.windowgroup:
221 m.enable(on)
222 self.windowgroup_on = on
223 changed = 1
224 if on:
Jack Jansen111fdcf1996-04-16 14:34:26 +0000225 # only if we have an edit menu
Jack Jansene7b900d1996-04-11 15:41:39 +0000226 on = self.active.have_selection()
Jack Jansen111fdcf1996-04-16 14:34:26 +0000227 if on <> self.focusgroup_on:
228 for m in self.focusgroup:
229 m.enable(on)
230 self.focusgroup_on = on
231 changed = 1
Jack Jansenee677912001-04-25 22:05:36 +0000232 if hasattr(Scrap, 'InfoScrap'):
233 on = (Scrap.InfoScrap()[0] <> 0)
234 else:
Jack Jansen9cf00dd2001-12-31 15:02:56 +0000235 flavors = Scrap.GetCurrentScrap().GetScrapFlavorInfoList()
236 for tp, info in flavors:
237 if tp == 'TEXT':
238 on = 1
239 break
240 else:
241 on = 0
Jack Jansen111fdcf1996-04-16 14:34:26 +0000242 if on <> self.pastegroup_on:
243 self.pasteitem.enable(on)
244 self.pastegroup_on = on
245 changed = 1
Jack Jansene7b900d1996-04-11 15:41:39 +0000246 if changed:
247 DrawMenuBar()
248
249 #
250 # Apple menu
251 #
252
253 def do_about(self, id, item, window, event):
254 EasyDialogs.Message("A simple single-font text editor")
255
256 #
257 # File menu
258 #
259
260 def open(self, *args):
261 self._open(0)
262
263 def openfile(self, *args):
264 self._open(1)
265
266 def _open(self, askfile):
267 if askfile:
268 fss, ok = macfs.StandardGetFile('TEXT')
269 if not ok:
270 return
271 path = fss.as_pathname()
272 name = os.path.split(path)[-1]
273 try:
274 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
275 data = fp.read()
276 fp.close()
277 except IOError, arg:
278 EasyDialogs.Message("IOERROR: "+`arg`)
279 return
280 else:
281 path = None
282 name = "Untitled %d"%self.num
283 data = ''
284 w = TEWindow(self)
285 w.open(path, name, data)
286 self.num = self.num + 1
287
288 def closewin(self, *args):
289 if self.active:
290 self.active.close()
291 else:
292 EasyDialogs.Message("No active window?")
293
294 def save(self, *args):
295 if self.active:
296 self.active.menu_save()
297 else:
298 EasyDialogs.Message("No active window?")
299
300 def saveas(self, *args):
301 if self.active:
302 self.active.menu_save_as()
303 else:
304 EasyDialogs.Message("No active window?")
305
306
307 def quit(self, *args):
308 for w in self._windows.values():
309 w.close()
310 if self._windows:
311 return
Jack Jansenef5cd051996-09-17 12:39:12 +0000312 self._quit()
Jack Jansene7b900d1996-04-11 15:41:39 +0000313
314 #
315 # Edit menu
316 #
317
318 def undo(self, *args):
319 pass
320
321 def cut(self, *args):
322 if self.active:
323 self.active.menu_cut()
324 else:
325 EasyDialogs.Message("No active window?")
326
327 def copy(self, *args):
328 if self.active:
329 self.active.menu_copy()
330 else:
331 EasyDialogs.Message("No active window?")
332
333 def paste(self, *args):
334 if self.active:
335 self.active.menu_paste()
336 else:
337 EasyDialogs.Message("No active window?")
338
339 def clear(self, *args):
340 if self.active:
341 self.active.menu_clear()
342 else:
343 EasyDialogs.Message("No active window?")
344
345 #
346 # Other stuff
347 #
348
349 def idle(self, *args):
Jack Jansen84445071996-04-19 15:54:47 +0000350 if self.active:
351 self.active.do_idle()
Jack Jansencbed91b2001-08-03 13:31:36 +0000352 else:
353 Qd.SetCursor(Qd.qd.arrow)
Jack Jansene7b900d1996-04-11 15:41:39 +0000354
355def main():
356 App = Ped()
357 App.mainloop()
358
359if __name__ == '__main__':
360 main()
361