/Mac/Demo/mlte/mlted.py

http://unladen-swallow.googlecode.com/ · Python · 374 lines · 285 code · 53 blank · 36 comment · 56 complexity · d19dc82ff4f2f88b968a5442990047b6 MD5 · raw file

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