#------------------------------------------------------------------------------
# Copyright (c) 2005, Enthought, Inc.
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in enthought/LICENSE.txt and may be redistributed only
# under the conditions described in the aforementioned license.  The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
# Thanks for using Enthought open source!
#
# Author: Enthought, Inc.
# Description: <Enthought util package component>
#------------------------------------------------------------------------------
""" An 'Ok/Cancel (and Help!)' dialog. """


# Standard library imports.
import copy, pickle

# Major package imports.
import wx

# Enthought library imports.
from enthought.traits.api import HasTraits, TraitProperty
from enthought.util.traits import HasTraitsPlus


class OkCancelModel(HasTraitsPlus):
    """ The default model for the Ok/Cancel pattern. """

    def __init__(self, obj, ignore_list=None):
        """ Create a new editor model. """

        # The object being edited.
        self.obj = obj

        # We actually edit a copy of the object.
        self.clone = self.create_clone(obj)

        # An optional list of traits to ignore in the commit.
        self._ignore_list = ignore_list

        return

    ###########################################################################
    # 'OkCancelModel' interface.
    ###########################################################################

    def create_clone(self, obj):
        """ Attempts to create a clone of an object. """

        return self.deepcopy(obj)

    def commit(self):
        """ Commits the changes from the clone back to the original. """

        if self._ignore_list is not None:
            trait_names = self.obj.trait_names()
            for ignore in self._ignore_list:
                trait_names.remove(ignore)

            self.obj.clone_traits(self.clone, traits=trait_names)

        else:
            self.obj.clone_traits(self.clone)

        return

    def deepcopy(self, obj):
        """ Creates a deep copy of the specified object. """

        clone_method = getattr(obj, 'clone', None)
        if clone_method is not None and callable(clone_method):
            clone = obj.clone()

        else:
            try:
                clone = copy.deepcopy(obj)

            except:
                clone = pickle.loads(pickle.dumps(obj))

        return clone

    def copy(self, obj):
        """ Creates a shallow copy of the specified object. """

        return copy.copy(obj)


class OkCancelDialog(wx.Dialog, HasTraits):
    """ An 'Ok/Cancel (and Help!)' dialog. """


    # Default dialog style.
    STYLE = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.CLIP_CHILDREN


    def __init__(self, parent, wxid, title, obj, traits=None, handler=None,
                 help_id=None, user_data=None, style=STYLE, **kw):
        """ Creates a new dialog. """

        # Was a specific size specified?
        self._sized = kw.has_key('size')
        self._rel_size = kw.get('rel_size', (0,0))

        if kw.has_key('rel_size'):
            del kw['rel_size']

        # Base-class constructor.
        wx.Dialog.__init__(self, parent, wxid, title, style=style, **kw)

        # The help Id (the 'Help' button will be displayed iff this is NOT
        # None).
        self._help_id = help_id

        # The object being edited.
        self._obj = obj

        # Because this is an Ok/Cancel dialog, we do not modify the object
        # itself, but a clone of it.
        self._model = obj.ok_cancel_model()

        # fixme: A quick hack to allow the layer editor dialog to be
        # brought up at a particular page in the tab!
        self._model.user_data = user_data

        # Create the widget!
        self._create_widget(self._model.clone, traits, handler)

        return

    ###########################################################################
    # wx event handlers.
    ###########################################################################

    def _on_ok(self, event):
        """ Called when the 'OK' button is pressed. """

        # Apply any changes made in the dialog.
        self._model.commit()

        # Let the default handler close the dialog appropriately.
        event.Skip()

        return

    def _on_cancel(self, event):
        """ Called when the 'Cancel' button is pressed. """

        # Let the default handler close the dialog appropriately.
        event.Skip()

        return

    def _on_help(self, event):
        """ Called when the 'Help' button is pressed. """

        # fixme: ProAVA specific stuff in here?!?!?!?!

        # Show the appropriate help topic.
        try:
            from enthought.envisage.application import get_application
            get_application().proava2.helpviewer.view_id(self._help_id)
        except:
            pass

        return

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _create_widget(self, clone, traits, handler):
        """ Creates the widget. """

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        self.SetAutoLayout(True)

        # The object's trait sheet.
        self.trait_sheet = trait_sheet = clone.get_trait_sheet(
            self, traits=traits, handler=handler
        )
        sizer.Add(trait_sheet, 1, wx.EXPAND | wx.ALL, 5)

        # The buttons.
        buttons = self._create_buttons(self)
        sizer.Add(buttons, 0, wx.ALIGN_RIGHT | wx.ALL, 5)

        # Resize the dialog to match the sizer's minimal size.
        if not self._sized:
            sizer.Fit(self)
            w, h = self.GetSizeTuple()
            dx, dy = self._rel_size
            self.SetSize(wx.Size(w + dx, h + dy))

        return

    def _create_buttons(self, parent):
        """ Creates the buttons. """

        sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 'OK' button.
        ok = wx.Button(parent, wx.ID_OK, "OK")
        ok.SetDefault()
        wx.EVT_BUTTON(parent, wx.ID_OK, self._on_ok)
        sizer.Add(ok)

        # 'Cancel' button.
        cancel = wx.Button(parent, wx.ID_CANCEL, "Cancel")
        wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._on_cancel)
        sizer.Add(cancel, 0, wx.LEFT, 10)

        # 'Help' button.
        if self._help_id is not None:
            help = wx.Button(parent, wx.ID_HELP, "Help")
            wx.EVT_BUTTON(self, wx.ID_HELP, self._on_help)
            sizer.Add(help, 0, wx.LEFT, 10)

        return sizer

#### EOF ######################################################################
