1# A minimal text editor. 2# 3# To be done: 4# - Update viewrect after resize 5# - Handle horizontal scrollbar correctly 6# - Functionality: find, etc. 7 8from Carbon.Menu import DrawMenuBar 9from FrameWork import * 10from Carbon import Win 11from Carbon import Qd 12from Carbon import TE 13from Carbon import Scrap 14import os 15import macfs 16 17class 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) 43 44 def do_idle(self): 45 self.ted.TEIdle() 46 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 55 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 78 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() 88 89 def do_update(self, wid, event): 90 Qd.EraseRect(wid.GetWindowPort().GetPortBounds()) 91 self.ted.TEUpdate(wid.GetWindowPort().GetPortBounds()) 92 self.updatescrollbars() 93 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() 99 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() 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 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() 140 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 152 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() 162 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 170 171 def menu_clear(self): 172 self.ted.TESelView() 173 self.ted.TEDelete() 174 self.updatescrollbars() 175 self.parent.updatemenubar() 176 self.changed = 1 177 178 def have_selection(self): 179 return (self.ted.selStart < self.ted.selEnd) 180 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 214 self.pastegroup_on = -1 215 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() 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 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: %r" % (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 286 287 def closewin(self, *args): 288 if self.active: 289 self.active.close() 290 else: 291 EasyDialogs.Message("No active window?") 292 293 def save(self, *args): 294 if self.active: 295 self.active.menu_save() 296 else: 297 EasyDialogs.Message("No active window?") 298 299 def saveas(self, *args): 300 if self.active: 301 self.active.menu_save_as() 302 else: 303 EasyDialogs.Message("No active window?") 304 305 306 def quit(self, *args): 307 for w in self._windows.values(): 308 w.close() 309 if self._windows: 310 return 311 self._quit() 312 313 # 314 # Edit menu 315 # 316 317 def undo(self, *args): 318 pass 319 320 def cut(self, *args): 321 if self.active: 322 self.active.menu_cut() 323 else: 324 EasyDialogs.Message("No active window?") 325 326 def copy(self, *args): 327 if self.active: 328 self.active.menu_copy() 329 else: 330 EasyDialogs.Message("No active window?") 331 332 def paste(self, *args): 333 if self.active: 334 self.active.menu_paste() 335 else: 336 EasyDialogs.Message("No active window?") 337 338 def clear(self, *args): 339 if self.active: 340 self.active.menu_clear() 341 else: 342 EasyDialogs.Message("No active window?") 343 344 # 345 # Other stuff 346 # 347 348 def idle(self, *args): 349 if self.active: 350 self.active.do_idle() 351 else: 352 Qd.SetCursor(Qd.GetQDGlobalsArrow()) 353 354def main(): 355 App = Ped() 356 App.mainloop() 357 358if __name__ == '__main__': 359 main() 360