"""\
Main wxGlade module: defines wxGladeFrame which contains the buttons to add
widgets and initializes all the stuff (tree, property_frame, etc.)

@copyright: 2002-2007 Alberto Griggio
@copyright: 2011-2014 Carsten Grohmann
@license: MIT (see license.txt) - THIS PROGRAM COMES WITH NO WARRANTY
"""

# import general python modules
import StringIO
import logging
import os
import os.path
import sys
import time
import wx
from xml.sax import SAXParseException

# import project modules
import application
import bugdialog
import clipboard
import common
import config
import preferencesdialog
import log
import misc
import template
from widget_properties import *
from tree import WidgetTree
from xml_parse import XmlWidgetBuilder, ProgressXmlWidgetBuilder, \
    XmlParsingError


class wxGladePropertyPanel(wx.Panel):
    """\
    Panel used to display the Properties of the various widgets
    """
    def SetTitle(self, title):
        try: self.GetParent().SetTitle(title)
        except AttributeError: pass

    def Layout(self):
        if self.is_visible():
            wx.Panel.Layout(self)
            self.GetParent().Layout()

    def is_visible(self):
        return self.GetParent().IsShown()

# end of class wxGladePropertyPanel


TOGGLE_BOX_EVENT = wx.NewEventType()

def EVT_TOGGLE_BOX(win, id, func):
    win.Connect(id, -1, TOGGLE_BOX_EVENT, func)

class ToggleBoxEvent(wx.PyCommandEvent):
    def __init__(self, id, value, strval):
        wx.PyCommandEvent.__init__(self)
        self.SetId(id)
        self.SetEventType(TOGGLE_BOX_EVENT)
        self.value = value
        self.strval = strval

    def GetValue(self):
        return self.value

    def GetStringValue(self):
        return self.strval

# end of class ToggleBoxEvent


class ToggleButtonBox(wx.Panel):
    def __init__(self, parent, id, choices=None, value=0):
        wx.Panel.__init__(self, parent, id)
        if choices is None:
            choices = []
        self.buttons = [wx.ToggleButton(self, -1, c) for c in choices]
        self.selected = None
        self.SetValue(value)
        for b in self.buttons:
            def handler(event, b=b):
                self.on_toggle(b, event)
            wx.EVT_TOGGLEBUTTON(self, b.GetId(), handler)
        sizer = wx.BoxSizer(wx.VERTICAL)
        for b in self.buttons:
            sizer.Add(b, 0, wx.ALL|wx.EXPAND, 1)
        self.SetAutoLayout(True)
        self.SetSizer(sizer)
        sizer.Fit(self)
        sizer.SetSizeHints(self)

    def on_toggle(self, button, event):
        if self.selected is button:
            self.selected.SetValue(True)
            return
        if self.selected is not None:
            self.selected.SetValue(False)
        self.selected = button
        wx.PostEvent(self, ToggleBoxEvent(self.GetId(), self.GetValue(),
                                         self.GetStringValue()))

    def GetValue(self):
        if self.selected is not None:
            return self.buttons.index(self.selected)
        return -1

    def GetStringValue(self):
        if self.selected is None: return None
        return self.selected.GetLabel()

    def SetValue(self, index):
        if self.selected is not None:
            self.selected.SetValue(False)
        if -1 < index < len(self.buttons):
            self.selected = self.buttons[index]
            self.selected.SetValue(True)

    def SetStringValue(self, strval):
        index = -1
        for i in range(len(self.buttons)):
            if self.buttons[i].GetLabel() == strval:
                index = i
                break
        self.SetValue(index)

# end of class ToggleButtonBox


class wxGladeArtProvider(wx.ArtProvider):
    def CreateBitmap(self, artid, client, size):
        if wx.Platform == '__WXGTK__' and artid == wx.ART_FOLDER:
            return wx.Bitmap(os.path.join(config.icons_path, 'closed_folder.xpm'),
                            wx.BITMAP_TYPE_XPM)
        return wx.NullBitmap

# end of class wxGladeArtProvider


class wxGladeFrame(wx.Frame):
    """\
    Main frame of wxGlade (palette)
    
    @ivar cur_dir: Last visited directory, used for wxFileDialog and not
                   for KDE dialogs
    @type cur_dir: str

    @ivar _logger: Instance specific logger
    """
    
    def __init__(self, parent=None):
        self._logger = logging.getLogger(self.__class__.__name__)
        style = wx.SYSTEM_MENU | wx.CAPTION | wx.MINIMIZE_BOX
        style |= wx.RESIZE_BORDER | wx.CLOSE_BOX
        wx.Frame.__init__(self, parent, -1, "wxGlade v%s" % config.version,
                          style=style)
        self.CreateStatusBar(1)

        if parent is None:
            parent = self
        common.palette = self  # to provide a reference accessible
                               # by the various widget classes
        icon = wx.EmptyIcon()
        bmp = wx.Bitmap(
            os.path.join(config.icons_path, "icon.xpm"),
            wx.BITMAP_TYPE_XPM
            )
        icon.CopyFromBitmap(bmp)
        self.SetIcon(icon)
        self.SetBackgroundColour(wx.SystemSettings_GetColour(
            wx.SYS_COLOUR_BTNFACE))
        menu_bar = wx.MenuBar()
        file_menu = wx.Menu(style=wx.MENU_TEAROFF)
        view_menu = wx.Menu(style=wx.MENU_TEAROFF)
        help_menu = wx.Menu(style=wx.MENU_TEAROFF)
        wx.ToolTip_SetDelay(1000)

        # load the available code generators
        core_buttons, local_buttons, sizer_buttons = common.init_codegen()
        
        append_item = misc.append_item
        self.TREE_ID = TREE_ID = wx.NewId()
        append_item(view_menu, TREE_ID, _("Show &Tree\tF2"))
        self.PROPS_ID = PROPS_ID = wx.NewId()
        self.RAISE_ID = RAISE_ID = wx.NewId()
        append_item(view_menu, PROPS_ID, _("Show &Properties\tF3"))
        append_item(view_menu, RAISE_ID, _("&Raise All\tF4"))
        NEW_ID = wx.NewId()
        append_item(file_menu, NEW_ID, _("&New\tCtrl+N"), wx.ART_NEW)
        NEW_FROM_TEMPLATE_ID = wx.NewId()
        append_item(file_menu, NEW_FROM_TEMPLATE_ID,
                    _("New from &Template...\tShift+Ctrl+N"))
        OPEN_ID = wx.NewId()
        append_item(file_menu, OPEN_ID, _("&Open...\tCtrl+O"), wx.ART_FILE_OPEN)
        SAVE_ID = wx.NewId()
        append_item(file_menu, SAVE_ID, _("&Save\tCtrl+S"), wx.ART_FILE_SAVE)
        SAVE_AS_ID = wx.NewId()
        append_item(file_menu, SAVE_AS_ID, _("Save As...\tShift+Ctrl+S"),
                    wx.ART_FILE_SAVE_AS)
        SAVE_TEMPLATE_ID = wx.NewId()
        append_item(file_menu, SAVE_TEMPLATE_ID, _("Save As Template..."))
        file_menu.AppendSeparator()
        RELOAD_ID = wx.ID_REFRESH
        append_item(file_menu, RELOAD_ID, _("&Refresh\tf5"))
        GENERATE_CODE_ID = wx.NewId()
        append_item(file_menu, GENERATE_CODE_ID, _("&Generate Code\tCtrl+G"),
                    wx.ART_EXECUTABLE_FILE)
        
        file_menu.AppendSeparator()
        IMPORT_ID = wx.NewId()
        append_item(file_menu, IMPORT_ID, _("&Import from XRC...\tCtrl+I"))
        
        EXIT_ID = wx.NewId()
        file_menu.AppendSeparator()
        append_item(file_menu, EXIT_ID, _('E&xit\tCtrl+Q'), wx.ART_QUIT)
        PREFS_ID = wx.ID_PREFERENCES
        view_menu.AppendSeparator()
        MANAGE_TEMPLATES_ID = wx.NewId()
        append_item(view_menu, MANAGE_TEMPLATES_ID, _('Templates Manager...'))
        view_menu.AppendSeparator()
        append_item(view_menu, PREFS_ID, _('Preferences...'))
        #wx.ART_HELP_SETTINGS)
        menu_bar.Append(file_menu, _("&File"))
        menu_bar.Append(view_menu, _("&View"))
        TUT_ID = wx.NewId()
        append_item(help_menu, TUT_ID, _('Contents\tF1'), wx.ART_HELP)
        ABOUT_ID = wx.ID_ABOUT
        append_item(help_menu, ABOUT_ID, _('About...'))
        menu_bar.Append(help_menu, _('&Help'))
        parent.SetMenuBar(menu_bar)
        # Mac tweaks...
        if wx.Platform == "__WXMAC__":
            wx.App_SetMacAboutMenuItemId(ABOUT_ID)
            wx.App_SetMacPreferencesMenuItemId(PREFS_ID)
            wx.App_SetMacExitMenuItemId(EXIT_ID)
            wx.App_SetMacHelpMenuTitleName(_('&Help'))

        # file history support
        self.file_history = wx.FileHistory(
            config.preferences.number_history)
        self.file_history.UseMenu(file_menu)
        files = common.load_history()
        files.reverse()
        for path in files:
            self.file_history.AddFileToHistory(path.strip())
            
        def open_from_history(event):
            if not self.ask_save():
                return
            pos = event.GetId() - wx.ID_FILE1
            filename = self.file_history.GetHistoryFile(pos)
            if not os.path.exists(filename):
                wx.MessageBox(
                    _("The file %s doesn't exist.") % filename,
                    _('Information'),
                    style=wx.CENTER | wx.ICON_INFORMATION | wx.OK)
                self.file_history.RemoveFileFromHistory(pos)
                common.remove_autosaved(filename)
                return
            if common.check_autosaved(filename):
                res = wx.MessageBox(
                    _('There seems to be auto saved data for this file: '
                      'do you want to restore it?'),
                    _('Auto save detected'),
                    style=wx.ICON_QUESTION | wx.YES_NO)
                if res == wx.YES:
                    common.restore_from_autosaved(filename)
                else:
                    common.remove_autosaved(filename)
            else:
                common.remove_autosaved(filename)
            self._open_app(filename)
            
        wx.EVT_MENU_RANGE(self, wx.ID_FILE1, wx.ID_FILE9, open_from_history)
        
        wx.EVT_MENU(self, TREE_ID, self.show_tree)
        wx.EVT_MENU(self, PROPS_ID, self.show_props_window)
        wx.EVT_MENU(self, RAISE_ID, self.raise_all)
        wx.EVT_MENU(self, NEW_ID, self.new_app)
        wx.EVT_MENU(self, NEW_FROM_TEMPLATE_ID, self.new_app_from_template)
        wx.EVT_MENU(self, OPEN_ID, self.open_app)
        wx.EVT_MENU(self, SAVE_ID, self.save_app)
        wx.EVT_MENU(self, SAVE_AS_ID, self.save_app_as)
        wx.EVT_MENU(self, SAVE_TEMPLATE_ID, self.save_app_as_template)
        def generate_code(event):
            common.app_tree.app.generate_code()
        wx.EVT_MENU(self, GENERATE_CODE_ID, generate_code)
        wx.EVT_MENU(self, EXIT_ID, lambda e: self.Close())
        wx.EVT_MENU(self, TUT_ID, self.show_tutorial)
        wx.EVT_MENU(self, ABOUT_ID, self.show_about_box)
        wx.EVT_MENU(self, PREFS_ID, self.edit_preferences)
        wx.EVT_MENU(self, MANAGE_TEMPLATES_ID, self.manage_templates)
        wx.EVT_MENU(self, IMPORT_ID, self.import_xrc)
        wx.EVT_MENU(self, RELOAD_ID, self.reload_app)

        PREVIEW_ID = wx.NewId()
        wx.EVT_MENU(self, PREVIEW_ID, self.preview)

        self.accel_table = wx.AcceleratorTable([
            (wx.ACCEL_CTRL, ord('N'), NEW_ID),
            (wx.ACCEL_CTRL, ord('O'), OPEN_ID),
            (wx.ACCEL_CTRL, ord('S'), SAVE_ID),
            (wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('S'), SAVE_AS_ID),
            (wx.ACCEL_CTRL, ord('G'), GENERATE_CODE_ID),
            (wx.ACCEL_CTRL, ord('I'), IMPORT_ID),
            (0, wx.WXK_F1, TUT_ID),
            (wx.ACCEL_CTRL, ord('Q'), EXIT_ID),
            (0, wx.WXK_F5, RELOAD_ID),
            (0, wx.WXK_F2, TREE_ID),
            (0, wx.WXK_F3, PROPS_ID),
            (0, wx.WXK_F4, RAISE_ID),
            (wx.ACCEL_CTRL, ord('P'), PREVIEW_ID),
            ])

        # layout
        # if there are custom components, add the toggle box...
        if local_buttons:
            main_sizer = wx.BoxSizer(wx.VERTICAL)
            show_core_custom = ToggleButtonBox(
                self, -1, [_("Core components"), _("Custom components")], 0)

            core_sizer = wx.FlexGridSizer(
                0,
                config.preferences.buttons_per_row
                )
            custom_sizer = wx.FlexGridSizer(
                0,
                config.preferences.buttons_per_row
                )
            self.SetAutoLayout(True)
            # core components
            for b in core_buttons:
                core_sizer.Add(b)
            for sb in sizer_buttons:
                core_sizer.Add(sb)
            # custom components
            for b in local_buttons:
                custom_sizer.Add(b)
                custom_sizer.Show(b, False)
            custom_sizer.Layout()
            main_sizer.Add(show_core_custom, 0, wx.EXPAND)
            main_sizer.Add(core_sizer, 0, wx.EXPAND)
            main_sizer.Add(custom_sizer, 0, wx.EXPAND)
            self.SetSizer(main_sizer)
            main_sizer.Fit(self)
            # events to display core/custom components
            def on_show_core_custom(event):
                show_core = True
                show_custom = False
                if event.GetValue() == 1:
                    show_core = False
                    show_custom = True
                for b in local_buttons:
                    custom_sizer.Show(b, show_custom)
                for b in core_buttons:
                    core_sizer.Show(b, show_core)
                for b in sizer_buttons:
                    core_sizer.Show(b, show_core)
                core_sizer.Layout()
                custom_sizer.Layout()
                main_sizer.Layout()
            EVT_TOGGLE_BOX(self, show_core_custom.GetId(), on_show_core_custom)
        # ... otherwise (the common case), just add the palette of core buttons
        else:
            sizer = wx.GridSizer(0, config.preferences.buttons_per_row)
            self.SetAutoLayout(True)
            # core components
            for b in core_buttons:
                sizer.Add(b)
            for sb in sizer_buttons:
                sizer.Add(sb)
            self.SetSizer(sizer)
            sizer.Fit(self)
        
        # Properties window
        frame_style = wx.DEFAULT_FRAME_STYLE
        frame_tool_win = config.preferences.frame_tool_win
        if frame_tool_win:
            frame_style |= wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT
            frame_style &= ~wx.MINIMIZE_BOX
            if wx.Platform != '__WXGTK__': frame_style |= wx.FRAME_TOOL_WINDOW
        
        self.frame2 = wx.Frame(self, -1, _('Properties - <%s>' % _('app')),
                               style=frame_style)
        self.frame2.SetBackgroundColour(wx.SystemSettings_GetColour(
            wx.SYS_COLOUR_BTNFACE))
        self.frame2.SetIcon(icon)
        
        sizer_tmp = wx.BoxSizer(wx.VERTICAL)
        property_panel = wxGladePropertyPanel(self.frame2, -1)

        #---- 2003-06-22 Fix for what seems to be a GTK2 bug (notebooks)
        misc.hidden_property_panel = wx.Panel(self.frame2, -1)
        sz = wx.BoxSizer(wx.VERTICAL)
        sz.Add(property_panel, 1, wx.EXPAND)
        sz.Add(misc.hidden_property_panel, 1, wx.EXPAND)
        self.frame2.SetSizer(sz)
        sz.Show(misc.hidden_property_panel, False)
        self.property_frame = self.frame2
        #--------------------------------------------------------
        
        property_panel.SetAutoLayout(True)
        self.hidden_frame = wx.Frame(self, -1, "")
        self.hidden_frame.Hide()
        sizer_tmp.Add(property_panel, 1, wx.EXPAND)
        self.frame2.SetAutoLayout(True)
        self.frame2.SetSizer(sizer_tmp)
        sizer_tmp = wx.BoxSizer(wx.VERTICAL)
        def hide_frame2(event):
            #menu_bar.Check(PROPS_ID, False)
            self.frame2.Hide()
        wx.EVT_CLOSE(self.frame2, hide_frame2)
        wx.EVT_CLOSE(self, self.cleanup)
        common.property_panel = property_panel
        # Tree of widgets
        self.tree_frame = wx.Frame(self, -1, _('wxGlade: Tree'),
                                  style=frame_style)
        self.tree_frame.SetIcon(icon)
        
        app = application.Application(common.property_panel)
        common.app_tree = WidgetTree(self.tree_frame, app)
        self.tree_frame.SetSize((300, 300))

        app.notebook.Show()
        sizer_tmp.Add(app.notebook, 1, wx.EXPAND)
        property_panel.SetSizer(sizer_tmp)
        sizer_tmp.Fit(property_panel)
        
        def on_tree_frame_close(event):
            #menu_bar.Check(TREE_ID, False)
            self.tree_frame.Hide()
        wx.EVT_CLOSE(self.tree_frame, on_tree_frame_close)
        # check to see if there are some remembered values
        prefs = config.preferences
        if prefs.remember_geometry:
            #self._logger.debug('initializing geometry')
            try:
                x, y, w, h = prefs.get_geometry('main')
                misc.set_geometry(self, (x, y))
            except Exception:
                pass
            misc.set_geometry(self.frame2, prefs.get_geometry('properties'))
            misc.set_geometry(self.tree_frame, prefs.get_geometry('tree'))
        else:
            if wx.Platform == '__WXMAC__':
                self.frame2.SetSize((345, 384)) # I've been told this is OK...
                self.SetPosition((0, 45)) # to avoid the OS X menubar
            else:
                self.frame2.SetSize((max(self.GetSize()[0], 250), 350))
                self.SetPosition((0, 0))
            x, y = self.GetPosition()
            h = self.GetSize()[1]
            w = self.frame2.GetSize()[0]
            if wx.Platform != '__WXMSW__':
                # under X, IceWM (and Sawfish, too), GetSize seems to ignore
                # window decorations
                h += 60
                w += 10
            self.frame2.SetPosition((x, y+h))
            self.tree_frame.SetPosition((x+w, y))
        self.Show()
        self.tree_frame.Show()
        self.frame2.Show()    

        #self._skip_activate = False
##         if frame_tool_win:
##             def on_iconize(event):
##                 if event.Iconized():
##                     self.hide_all()
##                 else:
##                     self.show_and_raise()
##                 event.Skip()
##             wx.EVT_ICONIZE(self, on_iconize)

        if wx.Platform == '__WXMSW__':
            import about
            # I'll pay a beer to anyone who can explain to me why this prevents
            # a segfault on Win32 when you exit without doing anything!!
            self.about_box = about.wxGladeAboutBox(self.GetParent())
        else:
            self.about_box = None

        # last visited directory, used on GTK for wxFileDialog
        self.cur_dir = config.preferences.open_save_path

        # set a drop target for us...
        self._droptarget = clipboard.FileDropTarget(self)
        self.SetDropTarget(self._droptarget)
        #self.tree_frame.SetDropTarget(self._droptarget)
        #self.frame2.SetDropTarget(self._droptarget)

        # ALB 2004-10-15, autosave support...
        self.autosave_timer = None
        if config.preferences.autosave:
            TIMER_ID = wx.NewId()
            self.autosave_timer = wx.Timer(self, TIMER_ID)
            wx.EVT_TIMER(self, TIMER_ID, self.on_autosave_timer)
            self.autosave_timer.Start(
                int(config.preferences.autosave_delay) * 1000)
        # ALB 2004-10-15
        CLEAR_SB_TIMER_ID = wx.NewId()
        self.clear_sb_timer = wx.Timer(self, CLEAR_SB_TIMER_ID)
        wx.EVT_TIMER(self, CLEAR_SB_TIMER_ID, self.on_clear_sb_timer)

        self.frame2.SetAcceleratorTable(self.accel_table)
        self.tree_frame.SetAcceleratorTable(self.accel_table)

        self.Raise()

        # ALB 2004-10-16
        if common.check_autosaved(None) and \
               wx.MessageBox(_("There seems to be auto saved data "
                            "from last wxGlade session: "
                            "do you want to restore it?"),
                            _("Auto save detected"),
                            style=wx.ICON_QUESTION|wx.YES_NO) == wx.YES:
            if self._open_app(common.get_name_for_autosave(),
                              add_to_history=False):
                common.app_tree.app.saved = False
                common.app_tree.app.filename = None
                self.user_message(_("Recovery from auto save complete"))
                common.remove_autosaved()
        else:
            common.remove_autosaved()

    def on_autosave_timer(self, event):
        if common.autosave_current():
            self.user_message(_("Auto saving... done"))
        
    def edit_preferences(self, event):
        dialog = preferencesdialog.wxGladePreferences(config.preferences)
        if dialog.ShowModal() == wx.ID_OK:
            wx.MessageBox(_('Changes will take effect after wxGlade is restarted'),
                          _('Preferences saved'),
                          wx.OK|wx.CENTRE|wx.ICON_INFORMATION)
            dialog.set_preferences()
        dialog.Destroy()
        
    def preview(self, event):
        """\
        Generate preview of the current loaded project.
        
        A preview can be triggered by keyboard shortcut or by pressing the
        preview button. The preview can be triggered for all selected widgets.
        This doesn't mean that the widget is opened for editing.
        
        A good indicator for editing is the availability of the preview
        button.
       
        @see: L{edit_windows.PreviewMixin.preview_button}
        """
        # Show preview only, if preview_button is available
        #
        if not getattr(common.app_tree.cur_widget, 'preview_button', None):
            return
        preview_widget = misc.get_toplevel_widget(common.app_tree.cur_widget)
        if preview_widget is not None:
            preview_widget.preview(None)

    def show_tree(self, event):
        self.tree_frame.Show()
        self.tree_frame.Raise()
        common.app_tree.SetFocus()

    def show_props_window(self, event):
        self.frame2.Show()
        self.frame2.Raise()
        try:
            c = self.frame2.GetSizer().GetChildren()
            if c: c[0].GetWindow().SetFocus()
        except (AttributeError, TypeError):
            self.frame2.SetFocus()

    def raise_all(self, event):
        children = self.GetChildren()
        for child in children:
            child = misc.get_toplevel_parent(child)
            if child.IsShown() and child.GetTitle(): child.Raise()
        self.Raise()

    def user_message(self, msg):
        sb = self.GetStatusBar()
        if sb:
            sb.SetStatusText(msg)
            self.clear_sb_timer.Start(5000, True)

    def on_clear_sb_timer(self, event):
        sb = self.GetStatusBar()
        if sb:
            sb.SetStatusText("")

    def ask_save(self):
        """\
        checks whether the current app has changed and needs to be saved:
        if so, prompts the user;
        returns False if the operation has been cancelled
        """
        if not common.app_tree.app.saved:
            ok = wx.MessageBox(_("Save changes to the current app?"),
                               _("Confirm"),
                               wx.YES_NO|wx.CANCEL|wx.CENTRE|wx.ICON_QUESTION)
            if ok == wx.YES:
                self.save_app(None)
            return ok != wx.CANCEL
        return True

    def new_app(self, event):
        """\
        creates a new wxGlade project
        """
        if self.ask_save():
            common.app_tree.clear()
            common.app_tree.app.filename = None
            common.app_tree.app.saved = True
            self.user_message("")
            common.remove_autosaved()
            if config.preferences.autosave and self.autosave_timer is not None:
                self.autosave_timer.Start()

    def new_app_from_template(self, event):
        """\
        creates a new wxGlade project from an existing template file
        """
        if not self.ask_save(): return
        infile = template.select_template()
        if infile:
            self._open_app(infile, add_to_history=False)
            common.app_tree.app.template_data = None

    def reload_app(self, event):
        self.ask_save()
        if not common.app_tree.app.filename:
            wx.MessageBox(_("Impossible to reload an unsaved application"),
                          _("Alert"), style=wx.OK|wx.ICON_INFORMATION)
            return
        path = common.app_tree.get_selected_path()
        self._open_app(common.app_tree.app.filename, add_to_history=False)
        common.app_tree.select_path(path)
        
    def open_app(self, event_unused):
        """\
        loads a wxGlade project from an xml file
        NOTE: this is very slow and needs optimisation efforts
        NOTE2: the note above should not be True anymore :)
        """
        if not self.ask_save():
            return
        infile = misc.FileSelector(_("Open file"),
                                   wildcard="wxGlade files (*.wxg)|*.wxg|"
                                   "wxGlade Template files (*.wgt)|*.wgt|"
                                   "XML files (*.xml)|*.xml|All files|*",
                                   flags=wx.OPEN|wx.FILE_MUST_EXIST,
                                   default_path=self.cur_dir)
        if infile:
            if common.check_autosaved(infile) and \
                   wx.MessageBox(_("There seems to be auto saved data for "
                                "this file: do you want to restore it?"),
                                _("Auto save detected"),
                                style=wx.ICON_QUESTION|wx.YES_NO) == wx.YES:
                common.restore_from_autosaved(infile)
            else:
                common.remove_autosaved(infile)
            self._open_app(infile)
            self.cur_dir = os.path.dirname(infile)

    def _open_app(self, infilename, use_progress_dialog=True,
                  is_filelike=False, add_to_history=True):
        start = time.clock()

        common.app_tree.clear()
        if not is_filelike:
            common.app_tree.app.filename = infilename
        else:
            common.app_tree.filename = getattr(infilename, 'name', None)
        common.property_panel.Reparent(self.hidden_frame)
        # prevent the auto-expansion of nodes
        common.app_tree.auto_expand = False

        old_dir = os.getcwd()
        infile = None
        error_msg = None

        try:
            try:
                if not is_filelike:
                    self._logger.info(
                        _('Read wxGlade project from file "%s"'), infilename
                    )
                    os.chdir(os.path.dirname(infilename))
                    infile = open(infilename)
                else:
                    infile = infilename
                    infilename = getattr(infile, 'name', None)
                    self._logger.info(
                        _('Read wxGlade project from file-like object')
                    )
                if use_progress_dialog and config.preferences.show_progress:
                    p = ProgressXmlWidgetBuilder(input_file=infile)
                else:
                    p = XmlWidgetBuilder()
                p.parse(infile)
            except (IOError, OSError, SAXParseException, XmlParsingError), msg:
                error_msg = _("Error loading file %s: %s") % \
                    (misc.wxstr(infilename), misc.wxstr(msg))
            except Exception, inst:
                if not is_filelike:
                    fn = os.path.basename(infilename).encode('ascii',
                                                             'replace')
                    msg = _('loading file "%s"') % fn
                else:
                    msg = _('loading from a file-like object')
                dialog = bugdialog.BugReport()
                dialog.SetContent(msg, inst)
                dialog.ShowModal()
        finally:
            if infile and not is_filelike:
                infile.close()

            if error_msg:
                common.app_tree.clear()
                common.property_panel.Reparent(self.frame2)
                common.app_tree.app.saved = True

                # reset the auto-expansion of nodes
                common.app_tree.auto_expand = True

                os.chdir(old_dir)

                # Show error message box
                wx.MessageBox(
                    error_msg,
                    _('Error'),
                    wx.OK | wx.CENTRE | wx.ICON_ERROR
                )

                return False

        common.app_tree.select_item(common.app_tree.root)
        common.app_tree.root.widget.show_properties()
        common.property_panel.Reparent(self.frame2)

        # reset the auto-expansion of nodes
        common.app_tree.auto_expand = True

        common.app_tree.expand()
        if common.app_tree.app.is_template:
            self._logger.info(_("Template loaded"))
            common.app_tree.app.template_data = template.Template(infilename)
            common.app_tree.app.filename = None

        end = time.clock()
        self._logger.info(_('Loading time: %.5f'), end - start)

        common.app_tree.app.saved = True

        if hasattr(self, 'file_history') and infilename is not None and \
                add_to_history and (not common.app_tree.app.is_template):
            self.file_history.AddFileToHistory(misc.wxstr(infilename))

        if config.preferences.autosave and self.autosave_timer is not None:
            self.autosave_timer.Start()

        self.user_message(
            _("Loaded %s (%.2f seconds)") %
            (misc.wxstr(common.app_tree.app.filename), end - start)
        )

        return True

    def save_app(self, event):
        """\
        saves a wxGlade project onto an xml file
        """
        if not common.app_tree.app.filename or \
           common.app_tree.app.is_template:
            self.save_app_as(event)
        else:
            # check whether we are saving a template
            ext = os.path.splitext(common.app_tree.app.filename)[1].lower()
            if ext == ".wgt":
                common.app_tree.app.is_template = True
            self._save_app(common.app_tree.app.filename)

    def _save_app(self, filename):
        try:
            obuffer = StringIO.StringIO()
            common.app_tree.write(obuffer)
            common.save_file(filename, obuffer.getvalue(), 'wxg')
        except (IOError, OSError), inst:
            common.app_tree.app.saved = False
            wx.MessageBox(_("Error saving app:\n%s") % inst, _("Error"),
                         wx.OK | wx.CENTRE | wx.ICON_ERROR)
        except Exception, inst:
            common.app_tree.app.saved = False
            fn = os.path.basename(filename).encode('ascii', 'replace')
            dialog = bugdialog.BugReport()
            dialog.SetContent(_('saving file "%s"') % fn, inst)
            dialog.ShowModal()
        else:
            common.app_tree.app.saved = True
            common.remove_autosaved()
            if config.preferences.autosave and \
                   self.autosave_timer is not None:
                self.autosave_timer.Start()
            self.user_message(_("Saved %s") % filename)

    def save_app_as(self, event):
        """\
        saves a wxGlade project onto an xml file chosen by the user
        """
        # both flags occurs several times
        fn = misc.FileSelector(_("Save project as..."),
                               wildcard="wxGlade files (*.wxg)|*.wxg|"
                               "wxGlade Template files (*.wgt) |*.wgt|"
                               "XML files (*.xml)|*.xml|All files|*",
                               flags=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
                               default_path=self.cur_dir)
        if fn:
            # check for file extension and add default extension if missing
            ext = os.path.splitext(fn)[1].lower()
            if not ext:
                fn = "%s.wxg" % fn
                
            common.app_tree.app.filename = fn
            #remove the template flag so we can save the file.
            common.app_tree.app.is_template = False

            self.save_app(event)
            self.cur_dir = os.path.dirname(fn)
            self.file_history.AddFileToHistory(fn)

    def save_app_as_template(self, event):
        """\
        save a wxGlade project as a template
        """
        data = getattr(common.app_tree.app, 'template_data', None)
        outfile, data = template.save_template(data)
        if outfile:
            common.app_tree.app.is_template = True
            common.app_tree.app.template_data = data
            self._save_app(outfile)

    def cleanup(self, event):
        if self.ask_save():
            # first, let's see if we have to save the geometry...
            prefs = config.preferences
            if prefs.remember_geometry:
                prefs.set_geometry('main', misc.get_geometry(self))
                prefs.set_geometry('tree',
                                   misc.get_geometry(self.tree_frame))
                prefs.set_geometry('properties',
                                   misc.get_geometry(self.frame2))
                prefs.changed = True
            common.app_tree.clear()
            if self.about_box:
                self.about_box.Destroy()
            try:
                common.save_preferences()
            except Exception, e:
                wx.MessageBox(_('Error saving preferences:\n%s') % e,
                              _('Error'),
                              wx.OK|wx.CENTRE|wx.ICON_ERROR)
            #self._skip_activate = True
            self.frame2.Destroy()
            self.tree_frame.Destroy()
            self.Destroy()
            common.remove_autosaved() # ALB 2004-10-15
            wx.CallAfter(wx.GetApp().ExitMainLoop)

    def show_about_box(self, event):
        """\
        show the about dialog
        
        @see: L{about.wxGladeAboutBox}
        """
        if self.about_box is None:
            import about
            self.about_box = about.wxGladeAboutBox(None)
        self.about_box.ShowModal()

    def show_tutorial(self, event):
        """\
        show the wxGlade user manual
        """
        if wx.Platform == "__WXMAC__":
            os.system(r'open -a Help\ Viewer.app %s' % config.tutorial_file)
        else:
            import webbrowser
            import threading
            # ALB 2004-08-15: why did this block the program?????
            # (at least on linux - GTK)
            def go():
                webbrowser.open_new(config.tutorial_file)
            t = threading.Thread(target=go)
            t.setDaemon(True)
            t.start()

    def show_and_raise(self):
        self.frame2.Show()  #self.GetMenuBar().IsChecked(self.PROPS_ID))
        self.tree_frame.Show()  #self.GetMenuBar().IsChecked(self.TREE_ID))
        self.frame2.Raise()
        self.tree_frame.Raise()
        self.Raise()

    def hide_all(self):
        self.tree_frame.Hide()
        self.frame2.Hide()

    def import_xrc(self, event):
        import xrc2wxg

        if not self.ask_save():
            return
        
        infilename = misc.FileSelector(
            _("Import file"),
            wildcard="XRC files (*.xrc)" "|*.xrc|All files|*",
            flags=wx.OPEN | wx.FILE_MUST_EXIST,
            default_path=self.cur_dir)
        if infilename:
            ibuffer = StringIO.StringIO()
            try:
                xrc2wxg.convert(infilename, ibuffer)
                ibuffer.seek(0)
                self._open_app(ibuffer, is_filelike=True)
                common.app_tree.app.saved = False
            except Exception, inst:
                fn = os.path.basename(infilename).encode('ascii', 'replace')
                dialog = bugdialog.BugReport()
                dialog.SetContent(_('importing file "%s"') % fn, inst)
                dialog.ShowModal()

    def manage_templates(self, event):
        to_edit = template.manage_templates()
        if to_edit is not None and self.ask_save():
            # edit the template
            # TODO, you still need to save it manually...
            self._open_app(to_edit, add_to_history=False)
            wx.MessageBox(_("To save the changes to the template, edit the "
                            "GUI as usual,\nand then click "
                            "File->Save As Template..."), _("Information"),
                          style=wx.OK|wx.ICON_INFORMATION)

# end of class wxGladeFrame


class wxGlade(wx.App):
    """\
    Class wxGlade
    
    @ivar _msg_dialog: Message dialog to show warning as well as error
                       messages.

    @type _msg_dialog: msgdialog.MessageDialog

    @ivar show_dialog: Show the error dialog automatically
    @type show_dialog: bool
    """

    _msg_dialog = None

    def OnInit(self):
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__
        self.show_dialog = True
        # needed for wx >= 2.3.4 to disable wxPyAssertionError exceptions
        self.SetAssertMode(0)
        wx.InitAllImageHandlers()
        common.init_preferences()
        if config.preferences.log_debug_info:
            log.setDebugLevel()

        if wx.Platform == '__WXGTK__' and config.preferences.use_kde_dialogs:
            import kdefiledialog
            if kdefiledialog.test_kde():
                misc.FileSelector = kdefiledialog.kde_file_selector
                misc.DirSelector = kdefiledialog.kde_dir_selector

        wx.ArtProvider.PushProvider(wxGladeArtProvider())

        frame = wxGladeFrame()
        self.SetTopWindow(frame)
        self.SetExitOnFrameDelete(True)

        wx.EVT_IDLE(self, self.on_idle)
        
        return True

    def on_idle(self, event):
        """\
        Show error messages if the GUI is idle.

        @see: L{show_error_dialog()}
        @see: L{show_dialog}
        """
        if self.show_dialog:
            self.show_error_dialog()
        event.Skip()

    def show_error_dialog(self):
        """\
        Show log messages if L{config.use_gui} is True

        @see: L{main.wxGlade.on_idle()}
        @see: L{log.getBufferAsList()}
        """
        log_msg = log.getBufferAsString(clean=True)
        if not (log_msg and config.use_gui):
            return

        # initialise message dialog
        if not self._msg_dialog:
            import msgdialog
            self._msg_dialog = msgdialog.MessageDialog(None, -1, "")
            self._msg_dialog.msg_list.InsertColumn(0, "")

        # clear dialog and show new messages
        self._msg_dialog.msg_list.Freeze()
        self._msg_dialog.msg_list.DeleteAllItems()
        for line in log_msg.split('\n'):
            self._msg_dialog.msg_list.Append([line, ])
        self._msg_dialog.msg_list.SetColumnWidth(0, -1)
        self._msg_dialog.msg_list.Thaw()
        self._msg_dialog.ShowModal()

# end of class wxGlade


def main(filename=None):
    """\
    if filename is not None, loads it
    """
    logging.info(_("Using wxPython %s"), config.wx_version)

    # now, silence a deprecation warning for py2.3
    import warnings
    warnings.filterwarnings(
        "ignore",
        "integer",
        DeprecationWarning,
        "wxPython.gdi",
        )

    app = wxGlade()
    if filename is not None:
        app.GetTopWindow()._open_app(filename, False)
    app.MainLoop()
