"""\
wxPanel objects

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

import logging
import wx
import common
import config
import misc
from tree import Tree
from widget_properties import *
from edit_windows import ManagedBase, TopLevelBase, EditStylesMixin


class PanelBase(EditStylesMixin):
    """\
    Class PanelBase

    @ivar _logger: Class specific logging instance
    """

    _custom_base_classes = True

    def __init__(self, style='wxTAB_TRAVERSAL'):
        """\
        Class to handle wxPanel objects
        """
        # initialise instance logger
        self._logger = logging.getLogger(self.__class__.__name__)

        # initialise instance
        EditStylesMixin.__init__(self, 'wxPanel')

        self.top_sizer = None  # sizer to handle the layout of children
        self.no_custom_class = False
        self.access_functions['no_custom_class'] = (self.get_no_custom_class,
                                                    self.set_no_custom_class)
        self.properties['no_custom_class'] = CheckBoxProperty(
            self, 'no_custom_class',
            label=_("Don't generate code for this custom class"))

        self.set_style(style)
        self.access_functions['style'] = (self.get_style, self.set_style)
        self.properties['style'] = CheckListProperty(self, 'style',
                                                     self.widget_writer)
        self.access_functions['scrollable'] = (self.get_scrollable,
                                               self.set_scrollable)
        self.scrollable = False
        self.properties['scrollable'] = CheckBoxProperty(
            self, 'scrollable', label=_("scrollable"))
        self.scroll_rate = (10, 10)
        self.access_functions['scroll_rate'] = (self.get_scroll_rate,
                                                self.set_scroll_rate)
        self.properties['scroll_rate'] = TextProperty(
            self, 'scroll_rate', can_disable=True, label=_("Scroll rate"))

    def finish_widget_creation(self):
        super(PanelBase, self).finish_widget_creation(
            sel_marker_parent=self.widget)
        if not self.scrollable:
            self.widget.SetScrollRate(0, 0)
        else:
            self.widget.SetScrollRate(*self.scroll_rate)
        # this must be done here since ManagedBase.finish_widget_creation
        # normally sets EVT_LEFT_DOWN to update_view
        if not self.widget.Disconnect(-1, -1, wx.wxEVT_LEFT_DOWN):
            self._logger.warning(
                _("EditPanel: Unable to disconnect the event handler")
                )
        wx.EVT_LEFT_DOWN(self.widget, self.drop_sizer)

    def _update_markers(self, event):
        def get_pos():
            x, y = self.widget.GetPosition()
            xx, yy = self.widget.GetViewStart()
            return x+xx, y+yy
        old = self.widget.GetPosition
        self.widget.GetPosition = get_pos
        #self._logger.debug("%s, %s", self.widget, self.sel_marker.owner)
        self.sel_marker.update()
        self.widget.GetPosition = old
        event.Skip()

    def create_properties(self):
        super(PanelBase, self).create_properties()
        panel = wx.ScrolledWindow(self.notebook, -1, style=wx.TAB_TRAVERSAL)
        panel.SetScrollRate(5, 5)
        szr = wx.BoxSizer(wx.VERTICAL)
        self.properties['no_custom_class'].display(panel)
        szr.Add(self.properties['no_custom_class'].panel, 0, wx.EXPAND)
        label = self.properties['no_custom_class'].cb
        label.SetToolTip(
            wx.ToolTip(_('If this is a custom class, setting this property '
                       'prevents wxGlade\nfrom generating the class '
                       'definition code')))
        self.properties['style'].display(panel)
        szr.Add(self.properties['style'].panel, 0, wx.EXPAND)
        self.properties['scrollable'].display(panel)
        szr.Add(self.properties['scrollable'].panel, 0, wx.EXPAND)
        self.properties['scroll_rate'].display(panel)
        szr.Add(self.properties['scroll_rate'].panel, 0, wx.EXPAND)
        panel.SetAutoLayout(True)
        panel.SetSizer(szr)
        szr.Fit(panel)
        self.notebook.AddPage(panel, 'Widget')
        
    def on_enter(self, event):
        if not self.top_sizer and common.adding_sizer:
            self.widget.SetCursor(wx.CROSS_CURSOR)
        else:
            self.widget.SetCursor(wx.STANDARD_CURSOR)

    def set_sizer(self, sizer):
        self.top_sizer = sizer
        if self.top_sizer and self.top_sizer.widget and self.widget:
            self.widget.SetAutoLayout(True)
            self.widget.SetSizer(self.top_sizer.widget)
            self.widget.Layout()
        elif self.top_sizer is None and self.widget:
            self.widget.SetSizer(None)

    def drop_sizer(self, event):
        if self.top_sizer or not common.adding_sizer:
            self.on_set_focus(event) # default behaviour: call show_properties
            return
        self.widget.SetCursor(wx.NullCursor)
        common.widgets[common.widget_to_add](self, None, None)
        common.adding_widget = common.adding_sizer = False
        common.widget_to_add = None
        common.app_tree.app.saved = False

    def get_widget_best_size(self):
        if self.top_sizer and self.widget.GetSizer():
            self.top_sizer.fit_parent()
            return self.widget.GetSize()
        return wx.ScrolledWindow.GetBestSize(self.widget)

    def get_scrollable(self):
        return self.scrollable

    def set_scrollable(self, value):
        self.scrollable = bool(int(value))
        if self.scrollable:
            if self.klass == 'wxPanel':
                self.klass = 'wxScrolledWindow'
                self.klass_prop.set_value(self.klass)
        else:
            if self.klass == 'wxScrolledWindow':
                self.klass = 'wxPanel'
                self.klass_prop.set_value(self.klass)
        if not self.widget:
            return
        if self.scrollable:
            self.properties['scroll_rate'].toggle_active(True)
            self.widget.SetScrollRate(*self.scroll_rate)
        else:
            self.properties['scroll_rate'].toggle_active(False)
            self.widget.SetScrollRate(0, 0)

    def get_scroll_rate(self):
        return '%d, %d' % self.scroll_rate

    def set_scroll_rate(self, value):
        invalid = False
        try:
            srx, sry = [int(t) for t in value.split(',', 1)]
            if srx < 0 or sry < 0:
                invalid = True
        except:
            invalid = True
        if invalid:
            self.properties['scroll_rate'].set_value(self.get_scroll_rate())
            return
        self.scroll_rate = srx, sry
        if self.widget:
            self.widget.SetScrollRate(srx, sry)

    def get_no_custom_class(self):
        return self.no_custom_class

    def set_no_custom_class(self, value):
        self.no_custom_class = bool(int(value))

    def __getstate__(self):
        state = self.__dict__.copy()
        del state['_logger']
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)

        # re-initialise logger instance deleted from __getstate__
        self._logger = logging.getLogger(self.__class__.__name__)

# end of class PanelBase
    

class EditPanel(PanelBase, ManagedBase):
    def __init__(self, name, parent, id, sizer, pos, property_window,
                 show=True, style='wxTAB_TRAVERSAL'):
        """\
        Class to handle wxPanel objects
        """
        ManagedBase.__init__(self, name, 'wxPanel', parent, id, sizer,
                             pos, property_window, show=show)
        PanelBase.__init__(self, style)

    def create_widget(self):
        self.widget = wx.ScrolledWindow(self.parent.widget, self.id, style=0)
        wx.EVT_ENTER_WINDOW(self.widget, self.on_enter)
        self.widget.GetBestSize = self.get_widget_best_size
        if self.sizer.is_virtual():
            def GetBestSize():
                if self.widget and self.widget.GetSizer():
                    return self.widget.GetSizer().GetMinSize()
                #return wx.Panel.GetBestSize(self.widget)
                return wx.ScrolledWindow.GetBestSize(self.widget)
            self.widget.GetBestSize = GetBestSize

    def set_sizer(self, sizer):
        super(EditPanel, self).set_sizer(sizer)
        if self.top_sizer and self.top_sizer.widget and self.widget:
            self.sizer.set_item(self.pos, size=self.widget.GetBestSize())

    def set_scrollable(self, value):
        super(EditPanel, self).set_scrollable(value)
        if self.scrollable:
            # 2003-06-26 ALB: change the "class name", to allow code generation
            # for a wxScrolledWindow (see Tree.Node.write and
            # common.class_names usage in xml_parse.py)
            self._classname = 'EditScrolledWindow'
        else:
            self._classname = self.__class__.__name__

    def popup_menu(self, event):
        if self.widget:
            if not self._rmenu:
                COPY_ID, REMOVE_ID, CUT_ID = [wx.NewId() for i in range(3)]
                self._rmenu = misc.wxGladePopupMenu(self.name)
                misc.append_item(self._rmenu, REMOVE_ID, _('Remove\tDel'),
                                 wx.ART_DELETE)
                misc.append_item(self._rmenu, COPY_ID, _('Copy\tCtrl+C'),
                                 wx.ART_COPY)
                misc.append_item(self._rmenu, CUT_ID, _('Cut\tCtrl+X'),
                                 wx.ART_CUT)
                def bind(method):
                    return lambda e: wx.CallAfter(method)
                wx.EVT_MENU(self.widget, REMOVE_ID, bind(self.remove))
                wx.EVT_MENU(self.widget, COPY_ID, bind(self.clipboard_copy))
                wx.EVT_MENU(self.widget, CUT_ID, bind(self.clipboard_cut))
                # paste
                PASTE_ID = wx.NewId()
                misc.append_item(self._rmenu, PASTE_ID, _('Paste\tCtrl+V'),
                                 wx.ART_PASTE)
                wx.EVT_MENU(self.widget, PASTE_ID, bind(self.clipboard_paste))
                PREVIEW_ID = wx.NewId()
                self._rmenu.AppendSeparator()
                misc.append_item(self._rmenu, PREVIEW_ID, _('Preview'))
                wx.EVT_MENU(self.widget, PREVIEW_ID, bind(self.preview_parent))

            self.setup_preview_menu()
            self.widget.PopupMenu(self._rmenu, event.GetPosition())

    def clipboard_paste(self, *args):
        import clipboard
        import xml_parse
        size = self.widget.GetSize()
        try:
            if clipboard.paste(self, None, 0):
                common.app_tree.app.saved = False
                self.widget.SetSize(size)
        except xml_parse.XmlParsingError:
            self._logger.warning(_('Only sizers can be pasted here'))
            
# end of class EditPanel


class EditTopLevelPanel(PanelBase, TopLevelBase):
    _is_toplevel = False  # used to avoid to appear in the "Top Window"
                          # property of the app
    
    def __init__(self, name, parent, id, property_window, klass='wxPanel',
                 show=True, style='wxTAB_TRAVERSAL'):
        TopLevelBase.__init__(self, name, klass, parent, id,
                              property_window, show=show, has_title=False)
        PanelBase.__init__(self, style)
        self.base = 'wxPanel'
        self.skip_on_size = False

    def create_widget(self):
        win = wx.Frame(common.palette, -1, misc.design_title(self.name),
                       size=(400, 300)) 
        import os
        icon = wx.EmptyIcon()
        xpm = os.path.join(config.icons_path, 'panel.xpm')
        icon.CopyFromBitmap(misc.get_xpm_bitmap(xpm))
        win.SetIcon(icon)
        #self.widget = wx.Panel(win, self.id, style=0)
        self.widget = wx.ScrolledWindow(win, self.id, style=0)
        wx.EVT_ENTER_WINDOW(self.widget, self.on_enter)
        self.widget.GetBestSize = self.get_widget_best_size
        #self.widget.SetSize = win.SetSize
        wx.EVT_CLOSE(win, self.hide_widget)
        if wx.Platform == '__WXMSW__':
            win.CentreOnScreen()

    def show_widget(self, yes):
        oldval = self.get_size()
        super(EditTopLevelPanel, self).show_widget(yes)
        if self.widget:
            if yes and not self.properties['size'].is_active() \
                   and self.top_sizer:
                self.top_sizer.fit_parent()
            self.widget.GetParent().Show(yes)
        self.set_size(oldval)

    def hide_widget(self, *args):
        super(EditTopLevelPanel, self).hide_widget(*args)
        self.widget.GetParent().Hide()

    def set_name(self, name):
        super(EditTopLevelPanel, self).set_name(name)
        if self.widget:
            self.widget.GetParent().SetTitle(misc.design_title(self.name))

    def delete(self):
        win = None
        if self.widget:
            win = self.widget.GetParent()
        super(EditTopLevelPanel, self).delete()
        if win is not None:
            win.Destroy()

    def on_size(self, event):
        w, h = event.GetSize()
        if self.skip_on_size:
            self.skip_on_size = False
            return
        super(EditTopLevelPanel, self).on_size(event)
        self.skip_on_size = True
        if self.widget.GetParent().GetClientSize() != (w, h):
            self.widget.GetParent().SetClientSize((w+2, h+2))

    def set_scrollable(self, value):
        super(EditTopLevelPanel, self).set_scrollable(value)
        if self.scrollable:
            # 2003-06-26 ALB: change the "class name", to allow code generation
            # for a wxScrolledWindow (see Tree.Node.write and
            # common.class_names usage in xml_parse.py)
            self._classname = 'EditTopLevelScrolledWindow'
        else:
            self._classname = self.__class__.__name__
            
# end of class EditTopLevelPanel
        

def builder(parent, sizer, pos, number=[1]):
    """\
    factory function for EditPanel objects.
    """
    name = 'panel_%d' % number[0]
    while common.app_tree.has_name(name):
        number[0] += 1
        name = 'panel_%d' % number[0]
    panel = EditPanel(name, parent, wx.NewId(), sizer, pos,
                      common.property_panel, style='')
    node = Tree.Node(panel)
    panel.node = node

    panel.set_option(1)
    panel.set_style("wxEXPAND")

    panel.show_widget(True)

    common.app_tree.insert(node, sizer.node, pos - 1)
    sizer.set_item(panel.pos, 1, wx.EXPAND)


def xml_builder(attrs, parent, sizer, sizeritem, pos=None):
    """\
    factory to build EditPanel objects from a XML file
    """
    from xml_parse import XmlParsingError
    try:
        name = attrs['name']
    except KeyError:
        raise XmlParsingError(_("'name' attribute missing"))
    if not sizer or not sizeritem:
        raise XmlParsingError(_("sizer or sizeritem object cannot be None"))
    panel = EditPanel(name, parent, wx.NewId(), sizer, pos,
                      common.property_panel, True, style='')
    sizer.set_item(panel.pos, option=sizeritem.option, flag=sizeritem.flag,
                   border=sizeritem.border)
    node = Tree.Node(panel)
    panel.node = node
    if pos is None:
        common.app_tree.add(node, sizer.node)
    else:
        common.app_tree.insert(node, sizer.node, pos - 1)
    return panel


def xml_toplevel_builder(attrs, parent, sizer, sizeritem, pos=None):
    from xml_parse import XmlParsingError
    try:
        label = attrs['name']
    except KeyError:
        raise XmlParsingError(_("'name' attribute missing"))
    panel = EditTopLevelPanel(label, parent, wx.NewId(),
                              common.property_panel, show=False, style='')
    node = Tree.Node(panel)
    panel.node = node
    common.app_tree.add(node)
    return panel


def initialize():
    """\
    initialization function for the module: returns a wxBitmapButton to be
    added to the main palette.
    """
    common.widgets['EditPanel'] = builder
    common.widgets_from_xml['EditPanel'] = xml_builder

    #common.widgets['EditScrolledWindow'] = builder
    common.widgets_from_xml['EditScrolledWindow'] = xml_builder
    
    common.widgets_from_xml['EditTopLevelPanel'] = xml_toplevel_builder
    common.widgets_from_xml['EditTopLevelScrolledWindow'] = \
                                                          xml_toplevel_builder
    from tree import WidgetTree
    import os.path
    icon = os.path.join(config.icons_path, 'panel.xpm')
    WidgetTree.images['EditTopLevelPanel'] = icon
    WidgetTree.images['EditScrolledWindow'] = icon
    WidgetTree.images['EditTopLevelScrolledWindow'] = icon

    # these are for backwards compatibility (may be removed someday...)
    common.widgets_from_xml['SplitterPane'] = xml_builder
    WidgetTree.images['SplitterPane'] = os.path.join(
        config.icons_path,
        'panel.xpm'
        )
    common.widgets_from_xml['NotebookPane'] = xml_builder
    WidgetTree.images['NotebookPane'] = os.path.join(
        config.icons_path,
        'panel.xpm'
        )    
    return common.make_object_button('EditPanel', 'icons/panel.xpm',
                                     tip='Add a Panel/ScrolledWindow')
    
