Laurent@814: #!/usr/bin/env python
Laurent@814: # -*- coding: utf-8 -*-
Laurent@814: 
andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for
andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
Laurent@814: #
andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
Laurent@814: #
andrej@1571: # See COPYING file for copyrights details.
Laurent@814: #
andrej@1571: # This program is free software; you can redistribute it and/or
andrej@1571: # modify it under the terms of the GNU General Public License
andrej@1571: # as published by the Free Software Foundation; either version 2
andrej@1571: # of the License, or (at your option) any later version.
Laurent@814: #
andrej@1571: # This program is distributed in the hope that it will be useful,
andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of
andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
andrej@1571: # GNU General Public License for more details.
Laurent@814: #
andrej@1571: # You should have received a copy of the GNU General Public License
andrej@1571: # along with this program; if not, write to the Free Software
andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Laurent@814: 
kinsamanka@3750: 
Laurent@814: import wx
Laurent@1169: 
Laurent@1169: from controls.CustomStyledTextCtrl import faces
Laurent@1169: 
andrej@1737: TOOLTIP_MAX_CHARACTERS = 30  # Maximum number of characters by line in ToolTip
andrej@1737: TOOLTIP_MAX_LINE = 5         # Maximum number of line in ToolTip
andrej@1737: TOOLTIP_WAIT_PERIOD = 0.5    # Wait period before displaying tooltip in second
Laurent@814: 
andrej@1782: # -------------------------------------------------------------------------------
Laurent@1169: #                               Custom ToolTip
andrej@1782: # -------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: 
Laurent@1169: class CustomToolTip(wx.PopupWindow):
andrej@1736:     """
andrej@1736:     Class that implements a custom tool tip
andrej@1736:     """
andrej@1730: 
Laurent@993:     def __init__(self, parent, tip, restricted=True):
Laurent@1169:         """
Laurent@1169:         Constructor
Laurent@1169:         @param parent: Parent window
Laurent@1169:         @param tip: Tip text (may be multiline)
andrej@1730:         @param restricted: Tool tip must follow size restriction in line and
Laurent@1169:             characters number defined (default True)
Laurent@1169:         """
Laurent@814:         wx.PopupWindow.__init__(self, parent)
andrej@1730: 
Laurent@993:         self.Restricted = restricted
andrej@1730: 
Laurent@814:         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
Laurent@814:         self.SetTip(tip)
andrej@1730: 
Laurent@1169:         # Initialize text font style
Laurent@1169:         self.Font = wx.Font(
andrej@1730:             faces["size"],
andrej@1730:             wx.SWISS,
andrej@1730:             wx.NORMAL,
andrej@1730:             wx.NORMAL,
andrej@1744:             faceName=faces["mono"])
andrej@1730: 
Laurent@814:         self.Bind(wx.EVT_PAINT, self.OnPaint)
andrej@1730: 
Laurent@993:     def SetFont(self, font):
Laurent@1169:         """
Laurent@1169:         Set tool tip text font style
Laurent@1169:         @param font: wx.Font object containing font style
Laurent@1169:         """
Laurent@993:         self.Font = font
Laurent@993:         self.RefreshTip()
andrej@1730: 
Laurent@814:     def SetTip(self, tip):
Laurent@1169:         """
Laurent@1169:         Set tool tip text
Laurent@1169:         @param tip: Tool tip text
Laurent@1169:         """
Laurent@1169:         if self.Restricted:
Laurent@1169:             # Compute tip text line return
Laurent@1169:             self.Tip = []
Laurent@1169:             for line in tip.splitlines():
Laurent@1169:                 if line != "":
Laurent@1169:                     words = line.split()
Laurent@1169:                     new_line = words[0]
Laurent@1169:                     for word in words[1:]:
Laurent@1169:                         # Add word to line
andrej@1769:                         if len(new_line + " " + word) <= TOOLTIP_MAX_CHARACTERS:
Laurent@1169:                             new_line += " " + word
Laurent@1169:                         # Create new line
Laurent@1169:                         else:
Laurent@1169:                             self.Tip.append(new_line)
Laurent@1169:                             new_line = word
Laurent@1169:                     self.Tip.append(new_line)
Laurent@1169:                 else:
Laurent@1169:                     self.Tip.append(line)
andrej@1730: 
Laurent@1169:             # Restrict number of lines
Laurent@1169:             if len(self.Tip) > TOOLTIP_MAX_LINE:
Laurent@1169:                 self.Tip = self.Tip[:TOOLTIP_MAX_LINE]
andrej@1730: 
Laurent@1169:                 # Add ... to the end of last line to indicate that tool tip
Laurent@1169:                 # text is too long
Laurent@1169:                 if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3:
Laurent@1169:                     self.Tip[-1] += "..."
Laurent@1169:                 else:
andrej@1771:                     self.Tip[-1] = self.Tip[-1][:TOOLTIP_MAX_CHARACTERS - 3] + \
andrej@1771:                                    "..."
Laurent@814:         else:
Laurent@1169:             self.Tip = tip.splitlines()
andrej@1730: 
Laurent@1169:         # Prevent to call wx method in non-wx threads
Laurent@814:         wx.CallAfter(self.RefreshTip)
andrej@1730: 
Laurent@1170:     def SetToolTipPosition(self, pos):
Laurent@1170:         """
Laurent@1170:         Set tool tip position
Laurent@1169:         @param pos: New tool tip position
Laurent@1169:         """
Laurent@1169:         # Get screen size to prevent tool tip to go out of the screen
Laurent@1169:         screen_width, screen_height = wx.GetDisplaySize()
andrej@1730: 
Laurent@1169:         # Calculate position of tool tip to stay in screen limits
Laurent@1169:         tip_width, tip_height = self.GetToolTipSize()
Laurent@1221:         self.SetPosition(wx.Point(
Laurent@1169:             max(0, min(pos.x, screen_width - tip_width)),
Laurent@1221:             max(0, min(pos.y, screen_height - tip_height))))
andrej@1730: 
Laurent@1169:     def GetToolTipSize(self):
Laurent@1169:         """
Laurent@1169:         Get tool tip size according to tip text and restriction
Laurent@1169:         @return: wx.Size(tool_tip_width, tool_tip_height)
Laurent@1169:         """
Laurent@1169:         max_width = max_height = 0
andrej@1730: 
Laurent@1169:         # Create a memory DC for calculating text extent
edouard@3303:         dc = wx.MemoryDC(wx.Bitmap(1, 1))
Laurent@1169:         dc.SetFont(self.Font)
andrej@1730: 
Laurent@1169:         # Compute max tip text size
Laurent@814:         for line in self.Tip:
Laurent@993:             w, h = dc.GetTextExtent(line)
Laurent@814:             max_width = max(max_width, w)
Laurent@814:             max_height += h
andrej@1730: 
Laurent@1169:         return wx.Size(max_width + 4, max_height + 4)
andrej@1730: 
Laurent@814:     def RefreshTip(self):
Laurent@1169:         """
Laurent@1169:         Refresh tip on screen
Laurent@1169:         """
Laurent@1169:         # Prevent to call this function if tool tip destroyed
Laurent@814:         if self:
Laurent@1169:             # Refresh tool tip size and position
Laurent@1221:             self.SetClientSize(self.GetToolTipSize())
andrej@1730: 
Laurent@1169:             # Redraw tool tip
Laurent@814:             self.Refresh()
andrej@1730: 
Laurent@814:     def OnPaint(self, event):
Laurent@1169:         """
Laurent@1169:         Callback for Paint Event
Laurent@1169:         @param event: Paint event
Laurent@1169:         """
Laurent@1169:         # Get buffered paint DC for tool tip
Laurent@814:         dc = wx.AutoBufferedPaintDC(self)
Laurent@814:         dc.Clear()
andrej@1730: 
Laurent@1169:         # Set DC drawing style
Laurent@1172:         dc.SetPen(wx.BLACK_PEN)
Laurent@814:         dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170)))
Laurent@993:         dc.SetFont(self.Font)
andrej@1730: 
Laurent@1169:         # Draw Tool tip
Laurent@1169:         tip_width, tip_height = self.GetToolTipSize()
andrej@1730: 
Laurent@1169:         # Draw background rectangle
Laurent@1169:         dc.DrawRectangle(0, 0, tip_width, tip_height)
andrej@1730: 
Laurent@1169:         # Draw tool tip text
Laurent@1169:         line_offset = 0
Laurent@814:         for line in self.Tip:
Laurent@1169:             dc.DrawText(line, 2, line_offset + 2)
andrej@1847:             _line_width, line_height = dc.GetTextExtent(line)
Laurent@1169:             line_offset += line_height
andrej@1730: 
andrej@1730: 
Laurent@814:         event.Skip()