import FrameWork
from Carbon import Win
from Carbon import Qd
from Carbon import Evt
import MacOS
from Carbon import Events
import traceback
from types import *
from Carbon import Menu; MenuToolbox = Menu; del Menu
import macresource
from Carbon import File

if hasattr(Win, "FrontNonFloatingWindow"):
    MyFrontWindow = Win.FrontNonFloatingWindow
else:
    MyFrontWindow = Win.FrontWindow


KILLUNKNOWNWINDOWS = 0  # Set to 0 for debugging.

class Application(FrameWork.Application):

    def __init__(self, signature='Pyth'):
        # Open our resource file, if it is not open yet
        macresource.need('CURS', 468, "Widgets.rsrc")
        import W
        W.setapplication(self, signature)
        FrameWork.Application.__init__(self)
        self._suspended = 0
        self.quitting = 0
        self.debugger_quitting = 1
        self.DebuggerQuit = 'DebuggerQuitDummyException'
        self._idlefuncs = []
        # map certain F key codes to equivalent command-letter combos (JJS)
        self.fkeymaps = {122:"z", 120:"x", 99:"c", 118:"v"}

    def mainloop(self, mask=FrameWork.everyEvent, wait=None):
        import W
        self.quitting = 0
        if hasattr(MacOS, 'EnableAppswitch'):
            saveyield = MacOS.EnableAppswitch(-1)
        try:
            while not self.quitting:
                try:
                    self.do1event(mask, wait)
                except W.AlertError, detail:
                    if hasattr(MacOS, 'EnableAppswitch'):
                        MacOS.EnableAppswitch(-1)
                    W.Message(detail)
                except self.DebuggerQuit:
                    if hasattr(MacOS, 'EnableAppswitch'):
                        MacOS.EnableAppswitch(-1)
                except:
                    if hasattr(MacOS, 'EnableAppswitch'):
                        MacOS.EnableAppswitch(-1)
                    import PyEdit
                    PyEdit.tracebackwindow.traceback()
        finally:
            if hasattr(MacOS, 'EnableAppswitch'):
                MacOS.EnableAppswitch(1)

    def debugger_mainloop(self, mask=FrameWork.everyEvent, wait=None):
        import W
        self.debugger_quitting = 0
        if hasattr(MacOS, 'EnableAppswitch'):
            saveyield = MacOS.EnableAppswitch(-1)
        try:
            while not self.quitting and not self.debugger_quitting:
                try:
                    self.do1event(mask, wait)
                except W.AlertError, detail:
                    W.Message(detail)
                except:
                    import PyEdit
                    PyEdit.tracebackwindow.traceback()
        finally:
            if hasattr(MacOS, 'EnableAppswitch'):
                MacOS.EnableAppswitch(saveyield)

    def breathe(self, wait=1):
        import W
        ok, event = Evt.WaitNextEvent(FrameWork.updateMask |
                        FrameWork.mDownMask | FrameWork.osMask |
                        FrameWork.activMask,
                        wait)
        if ok:
            (what, message, when, where, modifiers) = event
            #print FrameWork.eventname[what]
            if FrameWork.eventname[what] == 'mouseDown':
                partcode, wid = Win.FindWindow(where)
                if FrameWork.partname[partcode] <> 'inDesk':
                    return
            else:
                W.SetCursor('watch')
            self.dispatch(event)

    def refreshwindows(self, wait=1):
        import W
        while 1:
            ok, event = Evt.WaitNextEvent(FrameWork.updateMask, wait)
            if not ok:
                break
            self.dispatch(event)

    def addidlefunc(self, func):
        self._idlefuncs.append(func)

    def removeidlefunc(self, func):
        self._idlefuncs.remove(func)

    def idle(self, event):
        if not self._suspended:
            if not self.do_frontWindowMethod("idle", event):
                Qd.InitCursor()
        if self._idlefuncs:
            for func in self._idlefuncs:
                try:
                    func()
                except:
                    import sys
                    sys.stderr.write("exception in idle function %r; killed:\n" % (func,))
                    traceback.print_exc()
                    self._idlefuncs.remove(func)
                    break

    def do_frontWindowMethod(self, attr, *args):
        wid = MyFrontWindow()
        if wid and self._windows.has_key(wid):
            window = self._windows[wid]
            if hasattr(window, attr):
                handler = getattr(window, attr)
                apply(handler, args)
                return 1

    def getfrontwindow(self):
        wid = MyFrontWindow()
        if wid and self._windows.has_key(wid):
            return self._windows[wid]
        return None

    def appendwindow(self, wid, window):
        self._windows[wid] = window
        self.makeopenwindowsmenu()

    def removewindow(self, wid):
        del self._windows[wid]
        self.makeopenwindowsmenu()

    def makeopenwindowsmenu(self):
        # dummy; could be the full version from PythonIDEMain.py
        self._openwindows = {}
        self._openwindowscheckmark = 0
        if not hasattr(self, "_menustocheck"):
            self._menustocheck = []

    def do_key(self, event):
        (what, message, when, where, modifiers) = event
        ch = chr(message & FrameWork.charCodeMask)
        rest = message & ~FrameWork.charCodeMask
        keycode = (message & FrameWork.keyCodeMask) >> 8
        if keycode in self.fkeymaps.keys():             # JJS
            ch = self.fkeymaps[keycode]
            modifiers = modifiers | FrameWork.cmdKey
        wid = MyFrontWindow()
        if modifiers & FrameWork.cmdKey and not modifiers & FrameWork.shiftKey:
            if wid and self._windows.has_key(wid):
                self.checkmenus(self._windows[wid])
            else:
                self.checkmenus(None)
            event = (what, ord(ch) | rest, when, where, modifiers)
            result = MenuToolbox.MenuKey(ord(ch))
            id = (result>>16) & 0xffff      # Hi word
            item = result & 0xffff          # Lo word
            if id:
                self.do_rawmenu(id, item, None, event)
                return  # here! we had a menukey!
            #else:
            #       print "XXX Command-%r" % ch
        # See whether the front window wants it
        if wid and self._windows.has_key(wid):
            window = self._windows[wid]
            try:
                do_char = window.do_char
            except AttributeError:
                do_char = self.do_char
            do_char(ch, event)
        # else it wasn't for us, sigh...

    def do_inMenuBar(self, partcode, window, event):
        Qd.InitCursor()
        (what, message, when, where, modifiers) = event
        self.checkopenwindowsmenu()
        wid = MyFrontWindow()
        if wid and self._windows.has_key(wid):
            self.checkmenus(self._windows[wid])
        else:
            self.checkmenus(None)
        result = MenuToolbox.MenuSelect(where)
        id = (result>>16) & 0xffff      # Hi word
        if id >= 0x8000:
            id = -0x10000 + id
        item = result & 0xffff          # Lo word
        self.do_rawmenu(id, item, window, event)

    def do_updateEvt(self, event):
        (what, message, when, where, modifiers) = event
        wid = Win.WhichWindow(message)
        if wid and self._windows.has_key(wid):
            window = self._windows[wid]
            window.do_rawupdate(wid, event)
        else:
            if KILLUNKNOWNWINDOWS and wid:
                wid.HideWindow()
                import sys
                sys.stderr.write("XXX killed unknown (crashed?) Python window.\n")
            else:
                if hasattr(MacOS, 'HandleEvent'):
                    MacOS.HandleEvent(event)
                else:
                    print 'Unexpected updateEvent:', event

    def suspendresume(self, onoff):
        pass

    def do_suspendresume(self, event):
        self._suspended = not event[1] & 1
        FrameWork.Application.do_suspendresume(self, event)

    def checkopenwindowsmenu(self):
        if self._openwindowscheckmark:
            self.openwindowsmenu.menu.CheckMenuItem(self._openwindowscheckmark, 0)
        window = MyFrontWindow()
        if window:
            for item, wid in self._openwindows.items():
                if wid == window:
                    #self.pythonwindowsmenuitem.check(1)
                    self.openwindowsmenu.menu.CheckMenuItem(item, 1)
                    self._openwindowscheckmark = item
                    break
        else:
            self._openwindowscheckmark = 0
        #if self._openwindows:
        #       self.pythonwindowsmenuitem.enable(1)
        #else:
        #       self.pythonwindowsmenuitem.enable(0)

    def checkmenus(self, window):
        for item in self._menustocheck:
            callback = item.menu.items[item.item-1][2]
            if type(callback) <> StringType:
                item.enable(1)
            elif hasattr(window, "domenu_" + callback):
                if hasattr(window, "can_" + callback):
                    canhandler = getattr(window, "can_" + callback)
                    if canhandler(item):
                        item.enable(1)
                    else:
                        item.enable(0)
                else:
                    item.enable(1)
            else:
                item.enable(0)

    def enablemenubar(self, onoff):
        for m in self.menubar.menus.values():
            if onoff:
                m.menu.EnableMenuItem(0)
            elif m.menu.GetMenuItemText(3) <> 'Cut': # ew...
                m.menu.DisableMenuItem(0)
        MenuToolbox.DrawMenuBar()

    def makemenubar(self):
        self.menubar = MenuBar(self)
        FrameWork.AppleMenu(self.menubar, self.getabouttext(), self.do_about)
        self.makeusermenus()

    def scriptswalk(self, top, menu, done=None):
        if menu.id > 200:
            import W
            W.Message("Scripts folder not completely traversed: running out of menus")
            return False
        if done is None:
            done = {}
        if done.has_key(top):
            return True
        done[top] = 1
        import os, string
        try:
            names = os.listdir(top)
        except os.error:
            FrameWork.MenuItem(menu, '(Scripts Folder not found)', None, None)
            return True
        savedir = os.getcwd()
        os.chdir(top)
        for name in names:
            if name == "CVS":
                continue
            try:
                fsr, isdir, isalias = File.FSResolveAliasFile(name, 1)
            except:
                # maybe a broken alias
                continue
            path = fsr.as_pathname()
            if done.has_key(path):
                continue
            name = string.strip(name)
            if os.name == "posix":
                name = unicode(name, "utf-8")
            if name[-3:] == '---':
                menu.addseparator()
            elif isdir:
                submenu = FrameWork.SubMenu(menu, name)
                if not self.scriptswalk(path, submenu, done):
                    return False
            else:
                creator, type = MacOS.GetCreatorAndType(path)
                if type == 'TEXT':
                    if name[-3:] == '.py':
                        name = name[:-3]
                    item = FrameWork.MenuItem(menu, name, None, self.domenu_script)
                    self._scripts[(menu.id, item.item)] = path
            done[path] = 1
        os.chdir(savedir)
        return True

    def domenu_script(self, id, item, window, event):
        (what, message, when, where, modifiers) = event
        path = self._scripts[(id, item)]
        import os
        if not os.path.exists(path):
            self.makescriptsmenu()
            import W
            raise W.AlertError, "File not found."
        if ord(Evt.GetKeys()[7]) & 4:
            self.openscript(path)
        else:
            import W, MacOS, sys
            W.SetCursor("watch")
            sys.argv = [path]
            #cwd = os.getcwd()
            #os.chdir(os.path.dirname(path) + ':')
            try:
                # xxx if there is a script window for this file,
                # exec in that window's namespace.
                # xxx what to do when it's not saved???
                # promt to save?
                if hasattr(MacOS, 'EnableAppswitch'):
                    MacOS.EnableAppswitch(0)
                execfile(path, {'__name__': '__main__', '__file__': path})
            except W.AlertError, detail:
                if hasattr(MacOS, 'EnableAppswitch'):
                    MacOS.EnableAppswitch(-1)
                raise W.AlertError, detail
            except KeyboardInterrupt:
                if hasattr(MacOS, 'EnableAppswitch'):
                    MacOS.EnableAppswitch(-1)
            except:
                if hasattr(MacOS, 'EnableAppswitch'):
                    MacOS.EnableAppswitch(-1)
                import PyEdit
                PyEdit.tracebackwindow.traceback(1)
            else:
                if hasattr(MacOS, 'EnableAppswitch'):
                    MacOS.EnableAppswitch(-1)
            #os.chdir(cwd)

    def openscript(self, filename, lineno=None, charoffset=0, modname=""):
        import os, PyEdit, W
        editor = self.getscript(filename)
        if editor:
            editor.select()
        elif os.path.exists(filename):
            editor = PyEdit.Editor(filename)
        elif filename[-3:] == '.py' or filename[-4:] == '.pyc':
            import imp
            if not modname:
                if filename[-1] == 'c':
                    modname = os.path.basename(filename)[:-4]
                else:
                    modname = os.path.basename(filename)[:-3]
            try:
                # XXX This does not work correctly with packages!
                # XXX The docs say we should do it manually, pack, then sub, then sub2 etc.
                # XXX It says we should use imp.load_module(), but that *reloads* a package,
                # XXX and that's the last thing we want here.
                f, filename, (suff, mode, dummy) = imp.find_module(modname)
            except ImportError:
                raise W.AlertError, "Can't find file for \"%s\"" % modname
            else:
                if not f:
                    raise W.AlertError, "Can't find file for \"%s\"" % modname
                f.close()
            if suff == '.py':
                self.openscript(filename, lineno, charoffset)
                return
            else:
                raise W.AlertError, "Can't find file for \"%s\"" % modname
        else:
            raise W.AlertError, "Can't find file \"%s\"" % filename
        if lineno is not None:
            editor.selectline(lineno, charoffset)
        return editor

    def getscript(self, filename):
        if filename[:1] == '<' and filename[-1:] == '>':
            filename = filename[1:-1]
        import string
        lowpath = string.lower(filename)
        for wid, window in self._windows.items():
            if hasattr(window, "path") and type(window.path) == StringType and \
                            lowpath == string.lower(window.path):
                return window
            elif hasattr(window, "path") and filename == wid.GetWTitle():
                return window

    def getprefs(self):
        import MacPrefs
        return MacPrefs.GetPrefs(self.preffilepath)

    def do_editorprefs(self, *args):
        import PyEdit
        PyEdit.EditorDefaultSettings()

    def do_setwindowfont(self, *args):
        import FontSettings, W
        prefs = self.getprefs()
        settings = FontSettings.FontDialog(prefs.defaultfont)
        if settings:
            prefs.defaultfont, tabsettings = settings
            raise W.AlertError, "Note that changes will only affect new windows!"



class MenuBar(FrameWork.MenuBar):

    possibleIDs = range(10, 256)

    def getnextid(self):
        id = self.possibleIDs[0]
        del self.possibleIDs[0]
        return id

    def __init__(self, parent = None):
        self.bar = MenuToolbox.GetMenuBar()
        MenuToolbox.ClearMenuBar()
        self.menus = {}
        self.parent = parent

    def dispatch(self, id, item, window, event):
        if self.menus.has_key(id):
            self.menus[id].dispatch(id, item, window, event)

    def delmenu(self, id):
        MenuToolbox.DeleteMenu(id)
        if id in self.possibleIDs:
            print "XXX duplicate menu ID!", id
        self.possibleIDs.append(id)


class Menu(FrameWork.Menu):

    def dispatch(self, id, item, window, event):
        title, shortcut, callback, kind = self.items[item-1]
        if type(callback) == StringType:
            callback = self._getmenuhandler(callback)
        if callback:
            import W
            W.CallbackCall(callback, 0, id, item, window, event)

    def _getmenuhandler(self, callback):
        menuhandler = None
        wid = MyFrontWindow()
        if wid and self.bar.parent._windows.has_key(wid):
            window = self.bar.parent._windows[wid]
            if hasattr(window, "domenu_" + callback):
                menuhandler = getattr(window, "domenu_" + callback)
            elif hasattr(self.bar.parent, "domenu_" + callback):
                menuhandler = getattr(self.bar.parent, "domenu_" + callback)
        elif hasattr(self.bar.parent, "domenu_" + callback):
            menuhandler = getattr(self.bar.parent, "domenu_" + callback)
        return menuhandler
