Update for release.
[python/dscho.git] / Mac / Demo / textedit / ped.py
blob80cf7e5ce8b35508e947924a13407138b8efe7df
1 # A minimal text editor.
3 # To be done:
4 # - Update viewrect after resize
5 # - Handle horizontal scrollbar correctly
6 # - Functionality: find, etc.
8 from Carbon.Menu import DrawMenuBar
9 from FrameWork import *
10 from Carbon import Win
11 from Carbon import Qd
12 from Carbon import TE
13 from Carbon import Scrap
14 import os
15 import macfs
17 class TEWindow(ScrolledWindow):
18 def open(self, path, name, data):
19 self.path = path
20 self.name = name
21 r = windowbounds(400, 400)
22 w = Win.NewWindow(r, name, 1, 0, -1, 1, 0)
23 self.wid = w
24 x0, y0, x1, y1 = self.wid.GetWindowPort().GetPortBounds()
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)
32 Qd.SetPort(w)
33 Qd.TextFont(4)
34 Qd.TextSize(9)
35 self.ted = TE.TENew(dr, vr)
36 self.ted.TEAutoView(1)
37 self.ted.TESetText(data)
38 w.DrawGrowIcon()
39 self.scrollbars()
40 self.changed = 0
41 self.do_postopen()
42 self.do_activate(1, None)
44 def do_idle(self):
45 self.ted.TEIdle()
47 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
54 return None, vy
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:
77 pass # No horizontal scrolling
79 def do_activate(self, onoff, evt):
80 print "ACTIVATE", onoff
81 ScrolledWindow.do_activate(self, onoff, evt)
82 if onoff:
83 self.ted.TEActivate()
84 self.parent.active = self
85 self.parent.updatemenubar()
86 else:
87 self.ted.TEDeactivate()
89 def do_update(self, wid, event):
90 Qd.EraseRect(wid.GetWindowPort().GetPortBounds())
91 self.ted.TEUpdate(wid.GetWindowPort().GetPortBounds())
92 self.updatescrollbars()
94 def do_contentclick(self, local, modifiers, evt):
95 shifted = (modifiers & 0x200)
96 self.ted.TEClick(local, shifted)
97 self.updatescrollbars()
98 self.parent.updatemenubar()
100 def do_char(self, ch, event):
101 self.ted.TESelView()
102 self.ted.TEKey(ord(ch))
103 self.changed = 1
104 self.updatescrollbars()
105 self.parent.updatemenubar()
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()
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
133 def menu_save_as(self):
134 path = EasyDialogs.AskFileForSave(message='Save as:')
135 if not path: return
136 self.path = path
137 self.name = os.path.split(self.path)[-1]
138 self.wid.SetWTitle(self.name)
139 self.menu_save()
141 def menu_cut(self):
142 self.ted.TESelView()
143 self.ted.TECut()
144 if hasattr(Scrap, 'ZeroScrap'):
145 Scrap.ZeroScrap()
146 else:
147 Scrap.ClearCurrentScrap()
148 TE.TEToScrap()
149 self.updatescrollbars()
150 self.parent.updatemenubar()
151 self.changed = 1
153 def menu_copy(self):
154 self.ted.TECopy()
155 if hasattr(Scrap, 'ZeroScrap'):
156 Scrap.ZeroScrap()
157 else:
158 Scrap.ClearCurrentScrap()
159 TE.TEToScrap()
160 self.updatescrollbars()
161 self.parent.updatemenubar()
163 def menu_paste(self):
164 TE.TEFromScrap()
165 self.ted.TESelView()
166 self.ted.TEPaste()
167 self.updatescrollbars()
168 self.parent.updatemenubar()
169 self.changed = 1
171 def menu_clear(self):
172 self.ted.TESelView()
173 self.ted.TEDelete()
174 self.updatescrollbars()
175 self.parent.updatemenubar()
176 self.changed = 1
178 def have_selection(self):
179 return (self.ted.selStart < self.ted.selEnd)
181 class Ped(Application):
182 def __init__(self):
183 Application.__init__(self)
184 self.num = 0
185 self.active = None
186 self.updatemenubar()
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)
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)
206 # Not yet implemented:
207 self.undoitem.enable(0)
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
214 self.pastegroup_on = -1
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:
225 # only if we have an edit menu
226 on = self.active.have_selection()
227 if on <> self.focusgroup_on:
228 for m in self.focusgroup:
229 m.enable(on)
230 self.focusgroup_on = on
231 changed = 1
232 if hasattr(Scrap, 'InfoScrap'):
233 on = (Scrap.InfoScrap()[0] <> 0)
234 else:
235 flavors = Scrap.GetCurrentScrap().GetScrapFlavorInfoList()
236 for tp, info in flavors:
237 if tp == 'TEXT':
238 on = 1
239 break
240 else:
241 on = 0
242 if on <> self.pastegroup_on:
243 self.pasteitem.enable(on)
244 self.pastegroup_on = on
245 changed = 1
246 if changed:
247 DrawMenuBar()
250 # Apple menu
253 def do_about(self, id, item, window, event):
254 EasyDialogs.Message("A simple single-font text editor")
257 # File menu
260 def open(self, *args):
261 self._open(0)
263 def openfile(self, *args):
264 self._open(1)
266 def _open(self, askfile):
267 if askfile:
268 path = EasyDialogs.AskFileForOpen(typeList=('TEXT',))
269 if not path:
270 return
271 name = os.path.split(path)[-1]
272 try:
273 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
274 data = fp.read()
275 fp.close()
276 except IOError, arg:
277 EasyDialogs.Message("IOERROR: "+`arg`)
278 return
279 else:
280 path = None
281 name = "Untitled %d"%self.num
282 data = ''
283 w = TEWindow(self)
284 w.open(path, name, data)
285 self.num = self.num + 1
287 def closewin(self, *args):
288 if self.active:
289 self.active.close()
290 else:
291 EasyDialogs.Message("No active window?")
293 def save(self, *args):
294 if self.active:
295 self.active.menu_save()
296 else:
297 EasyDialogs.Message("No active window?")
299 def saveas(self, *args):
300 if self.active:
301 self.active.menu_save_as()
302 else:
303 EasyDialogs.Message("No active window?")
306 def quit(self, *args):
307 for w in self._windows.values():
308 w.close()
309 if self._windows:
310 return
311 self._quit()
314 # Edit menu
317 def undo(self, *args):
318 pass
320 def cut(self, *args):
321 if self.active:
322 self.active.menu_cut()
323 else:
324 EasyDialogs.Message("No active window?")
326 def copy(self, *args):
327 if self.active:
328 self.active.menu_copy()
329 else:
330 EasyDialogs.Message("No active window?")
332 def paste(self, *args):
333 if self.active:
334 self.active.menu_paste()
335 else:
336 EasyDialogs.Message("No active window?")
338 def clear(self, *args):
339 if self.active:
340 self.active.menu_clear()
341 else:
342 EasyDialogs.Message("No active window?")
345 # Other stuff
348 def idle(self, *args):
349 if self.active:
350 self.active.do_idle()
351 else:
352 Qd.SetCursor(Qd.GetQDGlobalsArrow())
354 def main():
355 App = Ped()
356 App.mainloop()
358 if __name__ == '__main__':
359 main()