# --------------------------------------------------------------------------- #
# HYPERLINKSCTRL wxPython IMPLEMENTATION
# Ported From Angelo Mandato C++ Code By:
#
# Andrea Gavana, @ 27 Mar 2005
# Latest Revision: 16 Jul 2012, 15.00 GMT
#
#
# Original Web Site (For The C++ Code):
#
# http://www.spaceblue.com/codedetail.php?CodeID=7
#
#
# Thanks to E. A. Tacao for his nice suggestions and improvements of the code.
#
# For all kind of problems, requests of enhancements and bug reports, please
# write to me at:
#
# andrea.gavana@gmail.com
# andrea.gavana@maerskoil.com
#
# Or, obviously, to the wxPython mailing list!!!
#
# Tags:        phoenix-port, unittest, documented, py3-port
#
# End Of Comments
# --------------------------------------------------------------------------- #

"""
:class:`~wx.lib.agw.hyperlink.HyperLinkCtrl` is a control for wxPython that acts like a hyper link
in a typical browser.


Description
===========

:class:`HyperLinkCtrl` is a control for wxPython that acts like a hyper link
in a typical browser. Latest features include the ability to capture
your own left, middle, and right click events to perform your own
custom event handling and ability to open link in a new or current
browser window.

Special thanks to Robin Dunn for the event binder for the 3 mouse buttons.


Usage
=====

Usage example::

    import wx
    import wx.lib.agw.hyperlink as hl

    class MyFrame(wx.Frame):

        def __init__(self, parent):

            wx.Frame.__init__(self, parent, -1, "HyperLink Demo")

            panel = wx.Panel(self, -1)

            # Default Web links:
            hyper1 = hl.HyperLinkCtrl(panel, -1, "wxPython Main Page", pos=(100, 100),
                                      URL="http://www.wxpython.org/")


            # Web link with underline rollovers, opens in same window
            hyper2 = hl.HyperLinkCtrl(panel, -1, "My Home Page", pos=(100, 150),
                                      URL="http://xoomer.virgilio.it/infinity77/")

            hyper2.AutoBrowse(False)
            hyper2.SetColours("BLUE", "BLUE", "BLUE")
            hyper2.EnableRollover(True)
            hyper2.SetUnderlines(False, False, True)
            hyper2.SetBold(True)
            hyper2.OpenInSameWindow(True)
            hyper2.SetToolTip(wx.ToolTip("Hello World!"))
            hyper2.UpdateLink()


    # our normal wxApp-derived class, as usual

    app = wx.App(0)

    frame = MyFrame(None)
    app.SetTopWindow(frame)
    frame.Show()

    app.MainLoop()



Window Styles
=============

`No particular window styles are available for this class.`


Events Processing
=================

This class processes the following events:

======================== ==================================================
Event Name               Description
======================== ==================================================
``EVT_HYPERLINK_LEFT``   Responds to a left mouse button event. Sent when the left mouse button is clicked, but only if `AutoBrowse` is set to ``False``.
``EVT_HYPERLINK_MIDDLE`` Responds to a middle mouse button event. Sent when the middle mouse button is clicked.
``EVT_HYPERLINK_RIGHT``  Handles a right mouse button event. Sent when the right mouse button is clicked, but only if `DoPopup` is set to ``False``.
======================== ==================================================


License And Version
===================

:class:`HyperLinkCtrl` is distributed under the wxPython license.

Latest Revision: Andrea Gavana @ 16 Jul 2012, 15.00 GMT

Version 0.7

"""

import wx
from wx.lib.stattext import GenStaticText as StaticText

# Import the useful webbrowser module for platform-independent results
import webbrowser

# Set no delay time to open the web page
webbrowser.PROCESS_CREATION_DELAY = 0

# To show a popup that copies the hyperlinks on the clipboard
wxHYPERLINKS_POPUP_COPY = 1000
""" Flag used to show a popup that copies the hyperlinks on the clipboard. """


#-----------------------------------#
#        HyperLinksEvents
#-----------------------------------#

# wxEVT_HYPERLINK_LEFT: Respond To A Left Mouse Button Event
# wxEVT_HYPERLINK_MIDDLE: Respond To A Middle Mouse Button Event
# wxEVT_HYPERLINK_RIGHT: Respond To A Right Mouse Button Event

wxEVT_HYPERLINK_LEFT = wx.NewEventType()
wxEVT_HYPERLINK_MIDDLE = wx.NewEventType()
wxEVT_HYPERLINK_RIGHT = wx.NewEventType()

EVT_HYPERLINK_LEFT = wx.PyEventBinder(wxEVT_HYPERLINK_LEFT, 1)
""" Responds to a left mouse button event. Sent when the left mouse button is""" \
""" clicked, but only if `AutoBrowse` is set to ``False``. """
EVT_HYPERLINK_MIDDLE = wx.PyEventBinder(wxEVT_HYPERLINK_MIDDLE, 1)
""" Responds to a middle mouse button event. Sent when the middle mouse button is clicked. """
EVT_HYPERLINK_RIGHT = wx.PyEventBinder(wxEVT_HYPERLINK_RIGHT, 1)
""" Handles a right mouse button event. Sent when the right mouse button is""" \
""" clicked, but only if `DoPopup` is set to ``False``. """


# ------------------------------------------------------------
# This class implements the event listener for the hyperlinks
# ------------------------------------------------------------

class HyperLinkEvent(wx.CommandEvent):
    """
    Event object sent in response to clicking on a :class:`HyperLinkCtrl`.
    """

    def __init__(self, eventType, eventId):
        """
        Default class constructor.

        :param `eventType`: the event type;
        :param `eventId`: the event identifier.
        """

        wx.CommandEvent.__init__(self, eventType, eventId)
        self._eventType = eventType


    def SetPosition(self, pos):
        """
        Sets the event position.

        :param `pos`: an instance of :class:`wx.Point`.
        """

        self._pos = pos


    def GetPosition(self):
        """ Returns the event position. """

        return self._pos


# -------------------------------------------------
# This is the main HyperLinkCtrl implementation
# it user the StatiText from wx.lib.stattext
# because of its "quasi-dynamic" behavior
# -------------------------------------------------

class HyperLinkCtrl(StaticText):
    """
    :class:`HyperLinkCtrl` is a control for wxPython that acts like a hyper
    link in a typical browser. Latest features include the ability to
    capture your own left, middle, and right click events to perform
    your own custom event handling and ability to open link in a new
    or current browser window.
    """

    def __init__(self, parent, id=-1, label="", pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0, name="staticText", URL=""):
        """
        Default class constructor.

        :param `parent`: the window parent. Must not be ``None``;
        :param `id`: window identifier. A value of -1 indicates a default value;
        :param `label`: the control label;
        :param `pos`: the control position. A value of (-1, -1) indicates a default position,
         chosen by either the windowing system or wxPython, depending on platform;
        :param `size`: the control size. A value of (-1, -1) indicates a default size,
         chosen by either the windowing system or wxPython, depending on platform;
        :param `style`: the window style;
        :param `name`: the window name;
        :param `URL`: a string specifying the url link to navigate to.

        :note: Pass URL="" to use the label as the url link to navigate to.
        """

        StaticText.__init__(self, parent, id, label, pos, size,
                            style, name)

        if URL.strip() == "":
            self._URL = label
        else:
            self._URL = URL

        # Set Tooltip
        self.SetToolTip(wx.ToolTip(self._URL))

        # Set default properties
        # default: True
        self.ReportErrors()

        # default: True, True, True
        self.SetUnderlines()

        # default: blue, violet, blue
        self.SetColours()

        # default: False
        self.SetVisited()

        # default: False
        self.EnableRollover()

        # default: False
        self.SetBold()

        # default: wx.CURSOR_HAND
        self.SetLinkCursor()

        # default True
        self.AutoBrowse()

        # default True
        self.DoPopup()

        # default False
        self.OpenInSameWindow()

        # Set control properties and refresh
        self.UpdateLink(True)

        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
        self.Bind(wx.EVT_MOTION, self.OnMouseEvent)


    def GotoURL(self, URL, ReportErrors=True, NotSameWinIfPossible=False):
        """
        Goto the specified URL.

        :param `URL`: the url link we wish to navigate;
        :param `ReportErrors`: Use ``True`` to display error dialog if an error
         occurrs navigating to the URL;
        :param `NotSameWinIfPossible`: Use ``True`` to attempt to open the URL
         in new browser window.
        """

        logOff = wx.LogNull()

        try:
            webbrowser.open(URL, new=NotSameWinIfPossible)
            self.SetVisited(True)
            self.UpdateLink(True)

            return True

        except:
            self.DisplayError("Unable To Launch Browser.", ReportErrors)
            return False


    def OnMouseEvent(self, event):
        """
        Handles the ``wx.EVT_MOUSE_EVENTS`` events for :class:`HyperLinkCtrl`.

        :param `event`: a :class:`MouseEvent` event to be processed.
        """

        if event.Moving():
            # Mouse Is Moving On The StaticText
            # Set The Hand Cursor On The Link
            self.SetCursor(self._CursorHand)

            if self._EnableRollover:
                fontTemp = self.GetFont()
                fontTemp.SetUnderlined(self._RolloverUnderline)
                if self._Bold:
                    fontTemp.SetWeight(wx.FONTWEIGHT_BOLD)

                needRefresh = False

                if self.GetFont() != fontTemp:
                    self.SetFont(fontTemp)
                    needRefresh = True

                if self.GetForegroundColour() != self._LinkRolloverColour:
                    self.SetForegroundColour(self._LinkRolloverColour)
                    needRefresh = True

                if needRefresh:
                    self.Refresh()

        else:
            # Restore The Original Cursor
            self.SetCursor(wx.NullCursor)
            if self._EnableRollover:
                self.UpdateLink(True)

            if event.LeftUp():
                # Left Button Was Pressed
                if self._AutoBrowse:
                    self.GotoURL(self._URL, self._ReportErrors,
                                 self._NotSameWinIfPossible)

                else:
                    eventOut = HyperLinkEvent(wxEVT_HYPERLINK_LEFT, self.GetId())
                    eventOut.SetEventObject(self)
                    eventOut.SetPosition(event.GetPosition())
                    self.GetEventHandler().ProcessEvent(eventOut)

                self.SetVisited(True)

            elif event.RightUp():
                # Right Button Was Pressed
                if self._DoPopup:
                    # Popups A Menu With The "Copy HyperLynks" Feature
                    menuPopUp = wx.Menu("", wx.MENU_TEAROFF)
                    menuPopUp.Append(wxHYPERLINKS_POPUP_COPY, "Copy HyperLink")
                    self.Bind(wx.EVT_MENU, self.OnPopUpCopy, id=wxHYPERLINKS_POPUP_COPY)
                    self.PopupMenu(menuPopUp)
                    menuPopUp.Destroy()
                    self.Unbind(wx.EVT_MENU, id=wxHYPERLINKS_POPUP_COPY)

                else:
                    eventOut = HyperLinkEvent(wxEVT_HYPERLINK_RIGHT, self.GetId())
                    eventOut.SetEventObject(self)
                    eventOut.SetPosition(event.GetPosition())
                    self.GetEventHandler().ProcessEvent(eventOut)

            elif event.MiddleUp():
                # Middle Button Was Pressed
                eventOut = HyperLinkEvent(wxEVT_HYPERLINK_MIDDLE, self.GetId())
                eventOut.SetEventObject(self)
                eventOut.SetPosition(event.GetPosition())
                self.GetEventHandler().ProcessEvent(eventOut)

        event.Skip()


    def OnPopUpCopy(self, event):
        """
        Handles the ``wx.EVT_MENU`` event for :class:`HyperLinkCtrl`.

        :param `event`: a :class:`wx.MenuEvent` event to be processed.

        :note: This method copies the data from the :class:`HyperLinkCtrl` to the clipboard.
        """

        wx.TheClipboard.UsePrimarySelection(False)
        if not wx.TheClipboard.Open():
            return
        data = wx.TextDataObject(self._URL)
        wx.TheClipboard.SetData(data)
        wx.TheClipboard.Close()


    def UpdateLink(self, OnRefresh=True):
        """
        Updates the link, changing text properties if:

        - User specific setting;
        - Link visited;
        - New link;

        :param `OnRefresh`: ``True`` to refresh the control, ``False`` otherwise.

        """

        fontTemp = self.GetFont()

        if self._Visited:
            self.SetForegroundColour(self._VisitedColour)
            fontTemp.SetUnderlined(self._VisitedUnderline)

        else:

            self.SetForegroundColour(self._LinkColour)
            fontTemp.SetUnderlined(self._LinkUnderline)

        if self._Bold:
            fontTemp.SetWeight(wx.FONTWEIGHT_BOLD)

        if self.GetFont() != fontTemp:
            self.SetFont(fontTemp)

        self.Refresh(OnRefresh)


    def DisplayError(self, ErrorMessage, ReportErrors=True):
        """
        Displays an error message (according to the `ReportErrors` parameter) in a
        :class:`MessageBox`.

        :param `ErrorMessage`: a string representing the error to display;
        :param `ReportErrors`: ``True`` to display error dialog if an error occurrs
         navigating to the URL.
        """

        if ReportErrors:
            wx.MessageBox(ErrorMessage, "HyperLinks Error", wx.OK | wx.CENTRE | wx.ICON_ERROR)


    def SetColours(self, link=wx.Colour(0, 0, 255), visited=wx.Colour(79, 47, 79),
                   rollover=wx.Colour(0, 0, 255)):
        """
        Sets the colours for the link, the visited link and the mouse rollover.

        - Visited link: VIOLET
        - Rollover: BLUE

        :param `link`: a valid :class:`wx.Colour` to use as text foreground for new links
         (default=RED);
        :param `visited`: a valid :class:`wx.Colour` to use as text foreground for visited
         links (default=VIOLET);
        :param `rollover`: a valid :class:`wx.Colour` to use as text foreground for links
         rollovers (default=BLUE).
        """

        self._LinkColour = link
        self._VisitedColour = visited
        self._LinkRolloverColour = rollover


    def GetColours(self):
        """
        Gets the colours for the link, the visited link and the mouse
        rollover.
        """

        return self._LinkColour, self._VisitedColour, self._LinkRolloverColour


    def SetUnderlines(self, link=True, visited=True, rollover=True):
        """
        Sets whether the text should be underlined or not for new links, visited
        links and rollovers.

        :param `link`: ``True`` to set the text of new links as underlined, ``False``
         otherwise;
        :param `visited`: ``True`` to set the text of visited links as underlined,
         ``False`` otherwise;
        :param `rollover`: ``True`` to set the text of rollovers as underlined,
         ``False`` otherwise.
        """

        self._LinkUnderline = link
        self._RolloverUnderline = rollover
        self._VisitedUnderline = visited


    def GetUnderlines(self):
        """
        Returns if link is underlined, if the mouse rollover is
        underlined and if the visited link is underlined.
        """

        return self._LinkUnderline, self._RolloverUnderline, self._VisitedUnderline


    def SetLinkCursor(self, cur=wx.CURSOR_HAND):
        """
        Sets link cursor properties.

        :param `cur`: an integer representing a :ref:`Cursor` constant.
        """

        self._CursorHand = wx.Cursor(cur)


    def GetLinkCursor(self):
        """ Gets the link cursor. """

        return self._CursorHand


    def SetVisited(self, Visited=False):
        """
        Sets a link as visited.

        :param `Visited`: ``True`` to set a link as visited, ``False`` otherwise.
        """

        self._Visited = Visited


    def GetVisited(self):
        """ Returns whether a link has been visited or not. """

        return self._Visited


    def SetBold(self, Bold=False):
        """
        Sets the :class:`HyperLinkCtrl` label in bold text.

        :param `Bold`: ``True`` to set the :class:`HyperLinkCtrl` label as bold, ``False``
         otherwise.
        """

        self._Bold = Bold


    def GetBold(self):
        """ Returns whether the :class:`HyperLinkCtrl` has text in bold or not. """

        return self._Bold


    def SetURL(self, URL):
        """
        Sets the :class:`HyperLinkCtrl` text to the specified URL.

        :param `URL`: the new URL associated with :class:`HyperLinkCtrl`.
        """

        self._URL = URL


    def GetURL(self):
        """ Retrieve the URL associated to the :class:`HyperLinkCtrl`. """

        return self._URL


    def OpenInSameWindow(self, NotSameWinIfPossible=False):
        """
        Open multiple URL in the same window (if possible).

        :param `NotSameWinIfPossible`: ``True`` to open an hyperlink in a new browser
         window, ``False`` to use an existing browser window.
        """

        self._NotSameWinIfPossible = NotSameWinIfPossible


    def EnableRollover(self, EnableRollover=False):
        """
        Enable/disable rollover.

        :param `EnableRollover`: ``True`` to enable text effects during rollover,
         ``False`` to disable them.
        """

        self._EnableRollover = EnableRollover


    def ReportErrors(self, ReportErrors=True):
        """
        Set whether to report browser errors or not.

        :param `ReportErrors`: Use ``True`` to display error dialog if an error
         occurrs navigating to the URL;
        """

        self._ReportErrors = ReportErrors


    def AutoBrowse(self, AutoBrowse=True):
        """
        Automatically browse to URL when clicked.

        :param `AutoBrowse`: ``True`` to automatically browse to an URL when clicked,
         ``False`` otherwise.

        :note: Set `AutoBrowse` to ``False`` to receive ``EVT_HYPERLINK_LEFT`` events.
        """

        self._AutoBrowse = AutoBrowse


    def DoPopup(self, DoPopup=True):
        """
        Sets whether to show popup menu on right click or not.

        :param `DoPopup`: ``True`` to show a popup menu on right click, ``False`` otherwise.
        """

        self._DoPopup = DoPopup



if __name__ == '__main__':

    import wx

    class MyFrame(wx.Frame):

        def __init__(self, parent):

            wx.Frame.__init__(self, parent, -1, "HyperLink Demo")

            panel = wx.Panel(self, -1)

            # Default Web links:
            hyper1 = HyperLinkCtrl(panel, -1, "wxPython Main Page", pos=(100, 100),
                                   URL="http://www.wxpython.org/")


            # Web link with underline rollovers, opens in same window
            hyper2 = HyperLinkCtrl(panel, -1, "My Home Page", pos=(100, 150),
                                   URL="http://xoomer.virgilio.it/infinity77/")

            hyper2.AutoBrowse(False)
            hyper2.SetColours("BLUE", "BLUE", "BLUE")
            hyper2.EnableRollover(True)
            hyper2.SetUnderlines(False, False, True)
            hyper2.SetBold(True)
            hyper2.OpenInSameWindow(True)
            hyper2.SetToolTip(wx.ToolTip("Hello World!"))
            hyper2.UpdateLink()


    # our normal wxApp-derived class, as usual

    app = wx.App(0)

    frame = MyFrame(None)
    app.SetTopWindow(frame)
    frame.Show()

    app.MainLoop()
