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