Laurent@814: #!/usr/bin/env python
Laurent@814: # -*- coding: utf-8 -*-
Laurent@814: 
Laurent@814: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
Laurent@814: #based on the plcopen standard. 
Laurent@814: #
Laurent@814: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
Laurent@814: #
Laurent@814: #See COPYING file for copyrights details.
Laurent@814: #
Laurent@814: #This library is free software; you can redistribute it and/or
Laurent@814: #modify it under the terms of the GNU General Public
Laurent@814: #License as published by the Free Software Foundation; either
Laurent@814: #version 2.1 of the License, or (at your option) any later version.
Laurent@814: #
Laurent@814: #This library is distributed in the hope that it will be useful,
Laurent@814: #but WITHOUT ANY WARRANTY; without even the implied warranty of
Laurent@814: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Laurent@814: #General Public License for more details.
Laurent@814: #
Laurent@814: #You should have received a copy of the GNU General Public
Laurent@814: #License along with this library; if not, write to the Free Software
Laurent@814: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Laurent@814: 
Laurent@814: import wx
Laurent@1169: 
Laurent@1169: from controls.CustomStyledTextCtrl import faces
Laurent@1169: 
Laurent@1169: TOOLTIP_MAX_CHARACTERS = 30 # Maximum number of characters by line in ToolTip
Laurent@1169: TOOLTIP_MAX_LINE = 5        # Maximum number of line in ToolTip
Laurent@1169: TOOLTIP_WAIT_PERIOD = 0.5   # Wait period before displaying tooltip in second
Laurent@814: 
Laurent@814: #-------------------------------------------------------------------------------
Laurent@1169: #                               Custom ToolTip
Laurent@814: #-------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: """
Laurent@814: Class that implements a custom tool tip
Laurent@814: """
Laurent@814: 
Laurent@1169: class CustomToolTip(wx.PopupWindow):
Laurent@814:     
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)
Laurent@1169:         @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)
Laurent@814:         
Laurent@814:         self.CurrentPosition = wx.Point(0, 0)
Laurent@993:         self.Restricted = restricted
Laurent@814:         
Laurent@814:         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
Laurent@814:         self.SetTip(tip)
Laurent@814:         
Laurent@1169:         # Initialize text font style
Laurent@1169:         self.Font = wx.Font(
Laurent@1169:             faces["size"], 
Laurent@1169:             wx.SWISS, 
Laurent@1169:             wx.NORMAL, 
Laurent@1169:             wx.NORMAL, 
Laurent@1169:             faceName = faces["mono"])
Laurent@993:         
Laurent@814:         self.Bind(wx.EVT_PAINT, self.OnPaint)
Laurent@993:     
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()
Laurent@993:     
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
Laurent@1169:                         if len(new_line + " " + word) <= \
Laurent@1169:                             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)
Laurent@1169:             
Laurent@1169:             # Restrict number of lines
Laurent@1169:             if len(self.Tip) > TOOLTIP_MAX_LINE:
Laurent@1169:                 self.Tip = self.Tip[:TOOLTIP_MAX_LINE]
Laurent@1169:                 
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:
Laurent@1169:                     self.Tip[-1] = self.Tip[-1]\
Laurent@1169:                         [:TOOLTIP_MAX_CHARACTERS - 3] + "..."
Laurent@814:         else:
Laurent@1169:             self.Tip = tip.splitlines()
Laurent@1169:         
Laurent@1169:         # Prevent to call wx method in non-wx threads
Laurent@814:         wx.CallAfter(self.RefreshTip)
Laurent@814:     
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()
Laurent@1169:         
Laurent@1169:         # Calculate position of tool tip to stay in screen limits
Laurent@1169:         tip_width, tip_height = self.GetToolTipSize()
Laurent@993:         self.CurrentPosition = wx.Point(
Laurent@1169:             max(0, min(pos.x, screen_width - tip_width)),
Laurent@1169:             max(0, min(pos.y, screen_height - tip_height))) 
Laurent@1169:         
Laurent@814:         self.SetPosition(pos)
Laurent@814:     
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
Laurent@1169:         
Laurent@1169:         # Create a memory DC for calculating text extent
Laurent@1169:         dc = wx.MemoryDC()
Laurent@1169:         dc.SetFont(self.Font)
Laurent@1169:         
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
Laurent@1169:         
Laurent@1169:         return wx.Size(max_width + 4, max_height + 4)
Laurent@814:     
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@1169:             self.SetSize(self.GetToolTipSize())
Laurent@814:             self.SetPosition(self.CurrentPosition)
Laurent@1169:             
Laurent@1169:             # Redraw tool tip
Laurent@814:             self.Refresh()
Laurent@1169:     
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()
Laurent@1169:         
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)
Laurent@1169:         
Laurent@1169:         # Draw Tool tip
Laurent@814:         dc.BeginDrawing()
Laurent@1169:         tip_width, tip_height = self.GetToolTipSize()
Laurent@1169:         
Laurent@1169:         # Draw background rectangle
Laurent@1169:         dc.DrawRectangle(0, 0, tip_width, tip_height)
Laurent@1169:         
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)
Laurent@1169:             line_width, line_height = dc.GetTextExtent(line)
Laurent@1169:             line_offset += line_height
Laurent@1169:         
Laurent@814:         dc.EndDrawing()
Laurent@1169:         
Laurent@814:         event.Skip()