"""PyAlaCarte and PyAlaMode editors."""

__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id: editor.py 63478 2010-02-13 22:59:44Z RD $"
__revision__ = "$Revision: 63478 $"[11:-2]

import wx

from buffer import Buffer
import crust
import dispatcher
import editwindow
import frame
from shell import Shell
import version


class EditorFrame(frame.Frame):
    """Frame containing one editor."""

    def __init__(self, parent=None, id=-1, title='PyAlaCarte',
                 pos=wx.DefaultPosition, size=(800, 600), 
                 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE,
                 filename=None):
        """Create EditorFrame instance."""
        frame.Frame.__init__(self, parent, id, title, pos, size, style)
        self.buffers = {}
        self.buffer = None  # Current buffer.
        self.editor = None
        self._defaultText = title + ' - the tastiest Python editor.'
        self._statusText = self._defaultText
        self.SetStatusText(self._statusText)
        self.Bind(wx.EVT_IDLE, self.OnIdle)
        self._setup()
        if filename:
            self.bufferCreate(filename)

    def _setup(self):
        """Setup prior to first buffer creation.

        Useful for subclasses."""
        pass

    def setEditor(self, editor):
        self.editor = editor
        self.buffer = self.editor.buffer
        self.buffers[self.buffer.id] = self.buffer

    def OnAbout(self, event):
        """Display an About window."""
        title = 'About PyAlaCarte'
        text = 'Another fine, flaky program.'
        dialog = wx.MessageDialog(self, text, title,
                                  wx.OK | wx.ICON_INFORMATION)
        dialog.ShowModal()
        dialog.Destroy()

    def OnClose(self, event):
        """Event handler for closing."""
        for buffer in self.buffers.values():
            self.buffer = buffer
            if buffer.hasChanged():
                cancel = self.bufferSuggestSave()
                if cancel and event.CanVeto():
                    event.Veto()
                    return
        self.Destroy()

    def OnIdle(self, event):
        """Event handler for idle time."""
        self._updateStatus()
        if hasattr(self, 'notebook'):
            self._updateTabText()
        self._updateTitle()
        event.Skip()

    def _updateStatus(self):
        """Show current status information."""
        if self.editor and hasattr(self.editor, 'getStatus'):
            status = self.editor.getStatus()
            text = 'File: %s  |  Line: %d  |  Column: %d' % status
        else:
            text = self._defaultText
        if text != self._statusText:
            self.SetStatusText(text)
            self._statusText = text

    def _updateTabText(self):
        """Show current buffer information on notebook tab."""
##         suffix = ' **'
##         notebook = self.notebook
##         selection = notebook.GetSelection()
##         if selection == -1:
##             return
##         text = notebook.GetPageText(selection)
##         window = notebook.GetPage(selection)
##         if window.editor and window.editor.buffer.hasChanged():
##             if text.endswith(suffix):
##                 pass
##             else:
##                 notebook.SetPageText(selection, text + suffix)
##         else:
##             if text.endswith(suffix):
##                 notebook.SetPageText(selection, text[:len(suffix)])

    def _updateTitle(self):
        """Show current title information."""
        title = self.GetTitle()
        if self.bufferHasChanged():
            if title.startswith('* '):
                pass
            else:
                self.SetTitle('* ' + title)
        else:
            if title.startswith('* '):
                self.SetTitle(title[2:])
        
    def hasBuffer(self):
        """Return True if there is a current buffer."""
        if self.buffer:
            return True
        else:
            return False

    def bufferClose(self):
        """Close buffer."""
        if self.bufferHasChanged():
            cancel = self.bufferSuggestSave()
            if cancel:
                return cancel
        self.bufferDestroy()
        cancel = False
        return cancel

    def bufferCreate(self, filename=None):
        """Create new buffer."""
        self.bufferDestroy()
        buffer = Buffer()
        self.panel = panel = wx.Panel(parent=self, id=-1)
        panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x)        
        editor = Editor(parent=panel)
        panel.editor = editor
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(editor.window, 1, wx.EXPAND)
        panel.SetSizer(sizer)
        panel.SetAutoLayout(True)
        sizer.Layout()
        buffer.addEditor(editor)
        buffer.open(filename)
        self.setEditor(editor)
        self.editor.setFocus()
        self.SendSizeEvent()
        

    def bufferDestroy(self):
        """Destroy the current buffer."""
        if self.buffer:
            for editor in self.buffer.editors.values():
                editor.destroy()
            self.editor = None
            del self.buffers[self.buffer.id]
            self.buffer = None
            self.panel.Destroy()


    def bufferHasChanged(self):
        """Return True if buffer has changed since last save."""
        if self.buffer:
            return self.buffer.hasChanged()
        else:
            return False

    def bufferNew(self):
        """Create new buffer."""
        if self.bufferHasChanged():
            cancel = self.bufferSuggestSave()
            if cancel:
                return cancel
        self.bufferCreate()
        cancel = False
        return cancel

    def bufferOpen(self):
        """Open file in buffer."""
        if self.bufferHasChanged():
            cancel = self.bufferSuggestSave()
            if cancel:
                return cancel
        filedir = ''
        if self.buffer and self.buffer.doc.filedir:
            filedir = self.buffer.doc.filedir
        result = openSingle(directory=filedir)
        if result.path:
            self.bufferCreate(result.path)
        cancel = False
        return cancel

##     def bufferPrint(self):
##         """Print buffer."""
##         pass

##     def bufferRevert(self):
##         """Revert buffer to version of file on disk."""
##         pass

    def bufferSave(self):
        """Save buffer to its file."""
        if self.buffer.doc.filepath:
            self.buffer.save()
            cancel = False
        else:
            cancel = self.bufferSaveAs()
        return cancel

    def bufferSaveAs(self):
        """Save buffer to a new filename."""
        if self.bufferHasChanged() and self.buffer.doc.filepath:
            cancel = self.bufferSuggestSave()
            if cancel:
                return cancel
        filedir = ''
        if self.buffer and self.buffer.doc.filedir:
            filedir = self.buffer.doc.filedir
        result = saveSingle(directory=filedir)
        if result.path:
            self.buffer.saveAs(result.path)
            cancel = False
        else:
            cancel = True
        return cancel

    def bufferSuggestSave(self):
        """Suggest saving changes.  Return True if user selected Cancel."""
        result = messageDialog(parent=None,
                               message='%s has changed.\n'
                                       'Would you like to save it first'
                                       '?' % self.buffer.name,
                               title='Save current file?')
        if result.positive:
            cancel = self.bufferSave()
        else:
            cancel = result.text == 'Cancel'
        return cancel

    def updateNamespace(self):
        """Update the buffer namespace for autocompletion and calltips."""
        if self.buffer.updateNamespace():
            self.SetStatusText('Namespace updated')
        else:
            self.SetStatusText('Error executing, unable to update namespace')


class EditorNotebookFrame(EditorFrame):
    """Frame containing one or more editors in a notebook."""

    def __init__(self, parent=None, id=-1, title='PyAlaMode',
                 pos=wx.DefaultPosition, size=(800, 600), 
                 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE,
                 filename=None):
        """Create EditorNotebookFrame instance."""
        self.notebook = None
        EditorFrame.__init__(self, parent, id, title, pos,
                             size, style, filename)
        if self.notebook:
            dispatcher.connect(receiver=self._editorChange,
                               signal='EditorChange', sender=self.notebook)

    def _setup(self):
        """Setup prior to first buffer creation.

        Called automatically by base class during init."""
        self.notebook = EditorNotebook(parent=self)
        intro = 'Py %s' % version.VERSION
        import imp
        module = imp.new_module('__main__')
        import __builtin__
        module.__dict__['__builtins__'] = __builtin__
        namespace = module.__dict__.copy()
        self.crust = crust.Crust(parent=self.notebook, intro=intro, locals=namespace)
        self.shell = self.crust.shell
        # Override the filling so that status messages go to the status bar.
        self.crust.filling.tree.setStatusText = self.SetStatusText
        # Override the shell so that status messages go to the status bar.
        self.shell.setStatusText = self.SetStatusText
        # Fix a problem with the sash shrinking to nothing.
        self.crust.filling.SetSashPosition(200)
        self.notebook.AddPage(page=self.crust, text='*Shell*', select=True)
        self.setEditor(self.crust.editor)
        self.crust.editor.SetFocus()

    def _editorChange(self, editor):
        """Editor change signal receiver."""
        self.setEditor(editor)

    def OnAbout(self, event):
        """Display an About window."""
        title = 'About PyAlaMode'
        text = 'Another fine, flaky program.'
        dialog = wx.MessageDialog(self, text, title,
                                  wx.OK | wx.ICON_INFORMATION)
        dialog.ShowModal()
        dialog.Destroy()

    def _updateTitle(self):
        """Show current title information."""
        pass
##         title = self.GetTitle()
##         if self.bufferHasChanged():
##             if title.startswith('* '):
##                 pass
##             else:
##                 self.SetTitle('* ' + title)
##         else:
##             if title.startswith('* '):
##                 self.SetTitle(title[2:])
        
    def bufferCreate(self, filename=None):
        """Create new buffer."""
        buffer = Buffer()
        panel = wx.Panel(parent=self.notebook, id=-1)
        panel.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: x)        
        editor = Editor(parent=panel)
        panel.editor = editor
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(editor.window, 1, wx.EXPAND)
        panel.SetSizer(sizer)
        panel.SetAutoLayout(True)
        sizer.Layout()
        buffer.addEditor(editor)
        buffer.open(filename)
        self.setEditor(editor)
        self.notebook.AddPage(page=panel, text=self.buffer.name, select=True)
        self.editor.setFocus()

    def bufferDestroy(self):
        """Destroy the current buffer."""
        selection = self.notebook.GetSelection()
##         print "Destroy Selection:", selection
        if selection > 0:  # Don't destroy the PyCrust tab.
            if self.buffer:
                del self.buffers[self.buffer.id]
                self.buffer = None  # Do this before DeletePage().
            self.notebook.DeletePage(selection)

    def bufferNew(self):
        """Create new buffer."""
        self.bufferCreate()
        cancel = False
        return cancel

    def bufferOpen(self):
        """Open file in buffer."""
        filedir = ''
        if self.buffer and self.buffer.doc.filedir:
            filedir = self.buffer.doc.filedir
        result = openMultiple(directory=filedir)
        for path in result.paths:
            self.bufferCreate(path)
        cancel = False
        return cancel


class EditorNotebook(wx.Notebook):
    """A notebook containing a page for each editor."""

    def __init__(self, parent):
        """Create EditorNotebook instance."""
        wx.Notebook.__init__(self, parent, id=-1, style=wx.CLIP_CHILDREN)
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging, id=self.GetId())
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=self.GetId())
        self.Bind(wx.EVT_IDLE, self.OnIdle)

    def OnIdle(self, event):
        """Event handler for idle time."""
        self._updateTabText()
        event.Skip()

    def _updateTabText(self):
        """Show current buffer display name on all but first tab."""
        size = 3
        changed = ' **'
        unchanged = ' --'
        selection = self.GetSelection()
        if selection < 1:
            return
        text = self.GetPageText(selection)
        window = self.GetPage(selection)
        if not window.editor:
            return
        if text.endswith(changed) or text.endswith(unchanged):
            name = text[:-size]
        else:
            name = text
        if name != window.editor.buffer.name:
            text = window.editor.buffer.name
        if window.editor.buffer.hasChanged():
            if text.endswith(changed):
                text = None
            elif text.endswith(unchanged):
                text = text[:-size] + changed
            else:
                text += changed
        else:
            if text.endswith(changed):
                text = text[:-size] + unchanged
            elif text.endswith(unchanged):
                text = None
            else:
                text += unchanged
        if text is not None:
            self.SetPageText(selection, text)
            self.Refresh()  # Needed on Win98.

    def OnPageChanging(self, event):
        """Page changing event handler."""
        event.Skip()

    def OnPageChanged(self, event):
        """Page changed event handler."""
        new = event.GetSelection()
        window = self.GetPage(new)
        dispatcher.send(signal='EditorChange', sender=self,
                        editor=window.editor)
        window.SetFocus()
        event.Skip()


class EditorShellNotebookFrame(EditorNotebookFrame):
    """Frame containing a notebook containing EditorShellNotebooks."""

    def __init__(self, parent=None, id=-1, title='PyAlaModeTest',
                 pos=wx.DefaultPosition, size=(600, 400), 
                 style=wx.DEFAULT_FRAME_STYLE,
                 filename=None, singlefile=False):
        """Create EditorShellNotebookFrame instance."""
        self._singlefile = singlefile
        EditorNotebookFrame.__init__(self, parent, id, title, pos,
                                     size, style, filename)

    def _setup(self):
        """Setup prior to first buffer creation.

        Called automatically by base class during init."""
        if not self._singlefile:
            self.notebook = EditorNotebook(parent=self)

    def OnAbout(self, event):
        """Display an About window."""
        title = 'About PyAlaModePlus'
        text = 'Another fine, flaky program.'
        dialog = wx.MessageDialog(self, text, title,
                                  wx.OK | wx.ICON_INFORMATION)
        dialog.ShowModal()
        dialog.Destroy()

    def bufferCreate(self, filename=None):
        """Create new buffer."""
        if self._singlefile:
            self.bufferDestroy()
            notebook = EditorShellNotebook(parent=self,
                                           filename=filename)
            self.notebook = notebook
        else:
            notebook = EditorShellNotebook(parent=self.notebook,
                                           filename=filename)
        self.setEditor(notebook.editor)
        if not self._singlefile:
            self.notebook.AddPage(page=notebook, text=self.buffer.name,
                                  select=True)
        self.editor.setFocus()

    def bufferDestroy(self):
        """Destroy the current buffer."""
        if self.buffer:
            self.editor = None
            del self.buffers[self.buffer.id]
            self.buffer = None  # Do this before DeletePage().
        if self._singlefile:
            self.notebook.Destroy()
            self.notebook = None
        else:
            selection = self.notebook.GetSelection()
##             print "Destroy Selection:", selection
            self.notebook.DeletePage(selection)

    def bufferNew(self):
        """Create new buffer."""
        if self._singlefile and self.bufferHasChanged():
            cancel = self.bufferSuggestSave()
            if cancel:
                return cancel
        self.bufferCreate()
        cancel = False
        return cancel

    def bufferOpen(self):
        """Open file in buffer."""
        if self._singlefile and self.bufferHasChanged():
            cancel = self.bufferSuggestSave()
            if cancel:
                return cancel
        filedir = ''
        if self.buffer and self.buffer.doc.filedir:
            filedir = self.buffer.doc.filedir
        if self._singlefile:
            result = openSingle(directory=filedir)
            if result.path:
                self.bufferCreate(result.path)
        else:
            result = openMultiple(directory=filedir)
            for path in result.paths:
                self.bufferCreate(path)
        cancel = False
        return cancel


class EditorShellNotebook(wx.Notebook):
    """A notebook containing an editor page and a shell page."""

    def __init__(self, parent, filename=None):
        """Create EditorShellNotebook instance."""
        wx.Notebook.__init__(self, parent, id=-1)
        usePanels = True
        if usePanels:
            editorparent = editorpanel = wx.Panel(self, -1)
            shellparent = shellpanel = wx.Panel(self, -1)
        else:
            editorparent = self
            shellparent = self
        self.buffer = Buffer()
        self.editor = Editor(parent=editorparent)
        self.buffer.addEditor(self.editor)
        self.buffer.open(filename)
        self.shell = Shell(parent=shellparent, locals=self.buffer.interp.locals,
                           style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER)
        self.buffer.interp.locals.clear()
        if usePanels:
            self.AddPage(page=editorpanel, text='Editor', select=True)
            self.AddPage(page=shellpanel, text='Shell')
            # Setup sizers
            editorsizer = wx.BoxSizer(wx.VERTICAL)
            editorsizer.Add(self.editor.window, 1, wx.EXPAND)
            editorpanel.SetSizer(editorsizer)
            editorpanel.SetAutoLayout(True)
            shellsizer = wx.BoxSizer(wx.VERTICAL)
            shellsizer.Add(self.shell, 1, wx.EXPAND)
            shellpanel.SetSizer(shellsizer)
            shellpanel.SetAutoLayout(True)
        else:
            self.AddPage(page=self.editor.window, text='Editor', select=True)
            self.AddPage(page=self.shell, text='Shell')
        self.editor.setFocus()
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=self.GetId())

    def OnPageChanged(self, event):
        """Page changed event handler."""
        selection = event.GetSelection()
        if selection == 0:
            self.editor.setFocus()
        else:
            self.shell.SetFocus()
        event.Skip()

    def SetFocus(self):
        wx.Notebook.SetFocus(self)
        selection = self.GetSelection()
        if selection == 0:
            self.editor.setFocus()
        else:
            self.shell.SetFocus()


class Editor:
    """Editor having an EditWindow."""

    def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
                 size=wx.DefaultSize,
                 style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER):
        """Create Editor instance."""
        self.window = EditWindow(self, parent, id, pos, size, style)
        self.id = self.window.GetId()
        self.buffer = None
        # Assign handlers for keyboard events.
        self.window.Bind(wx.EVT_CHAR, self.OnChar)
        self.window.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

    def _setBuffer(self, buffer, text):
        """Set the editor to a buffer.  Private callback called by buffer."""
        self.buffer = buffer
        self.autoCompleteKeys = buffer.interp.getAutoCompleteKeys()
        self.clearAll()
        self.setText(text)
        self.emptyUndoBuffer()
        self.setSavePoint()

    def destroy(self):
        """Destroy all editor objects."""
        self.window.Destroy()

    def clearAll(self):
        self.window.ClearAll()

    def emptyUndoBuffer(self):
        self.window.EmptyUndoBuffer()

    def getStatus(self):
        """Return (filepath, line, column) status tuple."""
        if self.window:
            pos = self.window.GetCurrentPos()
            line = self.window.LineFromPosition(pos) + 1
            col = self.window.GetColumn(pos)
            if self.buffer:
                name = self.buffer.doc.filepath or self.buffer.name
            else:
                name = ''
            status = (name, line, col)
            return status
        else:
            return ('', 0, 0)

    def getText(self):
        """Return contents of editor."""
        return self.window.GetText()

    def hasChanged(self):
        """Return True if contents have changed."""
        return self.window.GetModify()

    def setFocus(self):
        """Set the input focus to the editor window."""
        self.window.SetFocus()

    def setSavePoint(self):
        self.window.SetSavePoint()

    def setText(self, text):
        """Set contents of editor."""
        self.window.SetText(text)

    def OnChar(self, event):
        """Keypress event handler.
        
        Only receives an event if OnKeyDown calls event.Skip() for the
        corresponding event."""

        key = event.GetKeyCode()
        if key in self.autoCompleteKeys:
            # Usually the dot (period) key activates auto completion.
            if self.window.AutoCompActive(): 
                self.window.AutoCompCancel()
            self.window.ReplaceSelection('')
            self.window.AddText(chr(key))
            text, pos = self.window.GetCurLine()
            text = text[:pos]
            if self.window.autoComplete: 
                self.autoCompleteShow(text)
        elif key == ord('('):
            # The left paren activates a call tip and cancels an
            # active auto completion.
            if self.window.AutoCompActive(): 
                self.window.AutoCompCancel()
            self.window.ReplaceSelection('')
            self.window.AddText('(')
            text, pos = self.window.GetCurLine()
            text = text[:pos]
            self.autoCallTipShow(text)
        else:
            # Allow the normal event handling to take place.
            event.Skip()

    def OnKeyDown(self, event):
        """Key down event handler."""

        key = event.GetKeyCode()
        # If the auto-complete window is up let it do its thing.
        if self.window.AutoCompActive():
            event.Skip()
            return
        controlDown = event.ControlDown()
        altDown = event.AltDown()
        shiftDown = event.ShiftDown()
        # Let Ctrl-Alt-* get handled normally.
        if controlDown and altDown:
            event.Skip()
        # Increase font size.
        elif controlDown and key in (ord(']'),):
            dispatcher.send(signal='FontIncrease')
        # Decrease font size.
        elif controlDown and key in (ord('['),):
            dispatcher.send(signal='FontDecrease')
        # Default font size.
        elif controlDown and key in (ord('='),):
            dispatcher.send(signal='FontDefault')
        else:
            event.Skip()

    def autoCompleteShow(self, command):
        """Display auto-completion popup list."""
        list = self.buffer.interp.getAutoCompleteList(command, 
                    includeMagic=self.window.autoCompleteIncludeMagic, 
                    includeSingle=self.window.autoCompleteIncludeSingle, 
                    includeDouble=self.window.autoCompleteIncludeDouble)
        if list:
            options = ' '.join(list)
            offset = 0
            self.window.AutoCompShow(offset, options)

    def autoCallTipShow(self, command):
        """Display argument spec and docstring in a popup window."""
        if self.window.CallTipActive():
            self.window.CallTipCancel()
        (name, argspec, tip) = self.buffer.interp.getCallTip(command)
        if tip:
            dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
        if not self.window.autoCallTip:
            return
        startpos = self.window.GetCurrentPos()
        if argspec:
            self.window.AddText(argspec + ')')
            endpos = self.window.GetCurrentPos()
            self.window.SetSelection(startpos, endpos)
        if tip:
            tippos = startpos - (len(name) + 1)
            fallback = startpos - self.GetColumn(startpos)
            # In case there isn't enough room, only go back to the
            # fallback.
            tippos = max(tippos, fallback)
            self.CallTipShow(tippos, tip)


class EditWindow(editwindow.EditWindow):
    """EditWindow based on StyledTextCtrl."""

    def __init__(self, editor, parent, id=-1, pos=wx.DefaultPosition,
                 size=wx.DefaultSize,
                 style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER):
        """Create EditWindow instance."""
        editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
        self.editor = editor


class DialogResults:
    """DialogResults class."""

    def __init__(self, returned):
        """Create wrapper for results returned by dialog."""
        self.returned = returned
        self.positive = returned in (wx.ID_OK, wx.ID_YES)
        self.text = self._asString()
        

    def __repr__(self):
        return str(self.__dict__)

    def _asString(self):
        returned = self.returned
        if returned == wx.ID_OK:
            return "Ok"
        elif returned == wx.ID_CANCEL:
            return "Cancel"
        elif returned == wx.ID_YES:
            return "Yes"
        elif returned == wx.ID_NO:
            return "No"


def fileDialog(parent=None, title='Open', directory='', filename='',
               wildcard='All Files (*.*)|*.*',
               style=wx.OPEN | wx.MULTIPLE):
    """File dialog wrapper function."""
    dialog = wx.FileDialog(parent, title, directory, filename,
                           wildcard, style)
    result = DialogResults(dialog.ShowModal())
    if result.positive:
        result.paths = dialog.GetPaths()
    else:
        result.paths = []
    dialog.Destroy()
    return result


def openSingle(parent=None, title='Open', directory='', filename='',
               wildcard='All Files (*.*)|*.*', style=wx.OPEN):
    """File dialog wrapper function."""
    dialog = wx.FileDialog(parent, title, directory, filename,
                           wildcard, style)
    result = DialogResults(dialog.ShowModal())
    if result.positive:
        result.path = dialog.GetPath()
    else:
        result.path = None
    dialog.Destroy()
    return result


def openMultiple(parent=None, title='Open', directory='', filename='',
                 wildcard='All Files (*.*)|*.*',
                 style=wx.OPEN | wx.MULTIPLE):
    """File dialog wrapper function."""
    return fileDialog(parent, title, directory, filename, wildcard, style)


def saveSingle(parent=None, title='Save', directory='', filename='',
               wildcard='All Files (*.*)|*.*',
               style=wx.SAVE | wx.OVERWRITE_PROMPT):
    """File dialog wrapper function."""
    dialog = wx.FileDialog(parent, title, directory, filename,
                           wildcard, style)
    result = DialogResults(dialog.ShowModal())
    if result.positive:
        result.path = dialog.GetPath()
    else:
        result.path = None
    dialog.Destroy()
    return result


def directory(parent=None, message='Choose a directory', path='', style=0,
              pos=wx.DefaultPosition, size=wx.DefaultSize):
    """Dir dialog wrapper function."""
    dialog = wx.DirDialog(parent, message, path, style, pos, size)
    result = DialogResults(dialog.ShowModal())
    if result.positive:
        result.path = dialog.GetPath()
    else:
        result.path = None
    dialog.Destroy()
    return result


def messageDialog(parent=None, message='', title='Message box',
                  style=wx.YES_NO | wx.CANCEL | wx.CENTRE | wx.ICON_QUESTION,
                  pos=wx.DefaultPosition):
    """Message dialog wrapper function."""
    dialog = wx.MessageDialog(parent, message, title, style, pos)
    result = DialogResults(dialog.ShowModal())
    dialog.Destroy()
    return result
