Move CustomToolTip from GraphicCommons to controls
authorLaurent Bessard
Thu, 23 May 2013 18:47:44 +0200
changeset 1169 53e4a2b775a7
parent 1168 7838595559ba
child 1170 074e46cdedbc
Move CustomToolTip from GraphicCommons to controls
controls/CustomToolTip.py
controls/LogViewer.py
graphics/GraphicCommons.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/controls/CustomToolTip.py	Thu May 23 18:47:44 2013 +0200
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
+#based on the plcopen standard. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import wx
+
+from controls.CustomStyledTextCtrl import faces
+
+TOOLTIP_MAX_CHARACTERS = 30 # Maximum number of characters by line in ToolTip
+TOOLTIP_MAX_LINE = 5        # Maximum number of line in ToolTip
+TOOLTIP_WAIT_PERIOD = 0.5   # Wait period before displaying tooltip in second
+
+#-------------------------------------------------------------------------------
+#                               Custom ToolTip
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements a custom tool tip
+"""
+
+class CustomToolTip(wx.PopupWindow):
+    
+    def __init__(self, parent, tip, restricted=True):
+        """
+        Constructor
+        @param parent: Parent window
+        @param tip: Tip text (may be multiline)
+        @param restricted: Tool tip must follow size restriction in line and 
+            characters number defined (default True)
+        """
+        wx.PopupWindow.__init__(self, parent)
+        
+        self.CurrentPosition = wx.Point(0, 0)
+        self.Restricted = restricted
+        
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        self.SetTip(tip)
+        
+        # Initialize text font style
+        self.Font = wx.Font(
+            faces["size"], 
+            wx.SWISS, 
+            wx.NORMAL, 
+            wx.NORMAL, 
+            faceName = faces["mono"])
+        
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+    
+    def SetFont(self, font):
+        """
+        Set tool tip text font style
+        @param font: wx.Font object containing font style
+        """
+        self.Font = font
+        self.RefreshTip()
+    
+    def SetTip(self, tip):
+        """
+        Set tool tip text
+        @param tip: Tool tip text
+        """
+        if self.Restricted:
+            # Compute tip text line return
+            self.Tip = []
+            for line in tip.splitlines():
+                if line != "":
+                    words = line.split()
+                    new_line = words[0]
+                    for word in words[1:]:
+                        # Add word to line
+                        if len(new_line + " " + word) <= \
+                            TOOLTIP_MAX_CHARACTERS:
+                            new_line += " " + word
+                        # Create new line
+                        else:
+                            self.Tip.append(new_line)
+                            new_line = word
+                    self.Tip.append(new_line)
+                else:
+                    self.Tip.append(line)
+            
+            # Restrict number of lines
+            if len(self.Tip) > TOOLTIP_MAX_LINE:
+                self.Tip = self.Tip[:TOOLTIP_MAX_LINE]
+                
+                # Add ... to the end of last line to indicate that tool tip
+                # text is too long
+                if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3:
+                    self.Tip[-1] += "..."
+                else:
+                    self.Tip[-1] = self.Tip[-1]\
+                        [:TOOLTIP_MAX_CHARACTERS - 3] + "..."
+        else:
+            self.Tip = tip.splitlines()
+        
+        # Prevent to call wx method in non-wx threads
+        wx.CallAfter(self.RefreshTip)
+    
+    def MoveToolTip(self, pos):
+        """
+        Move tool tip
+        @param pos: New tool tip position
+        """
+        # Get screen size to prevent tool tip to go out of the screen
+        screen_width, screen_height = wx.GetDisplaySize()
+        
+        # Calculate position of tool tip to stay in screen limits
+        tip_width, tip_height = self.GetToolTipSize()
+        self.CurrentPosition = wx.Point(
+            max(0, min(pos.x, screen_width - tip_width)),
+            max(0, min(pos.y, screen_height - tip_height))) 
+        
+        self.SetPosition(pos)
+    
+    def GetToolTipSize(self):
+        """
+        Get tool tip size according to tip text and restriction
+        @return: wx.Size(tool_tip_width, tool_tip_height)
+        """
+        max_width = max_height = 0
+        
+        # Create a memory DC for calculating text extent
+        dc = wx.MemoryDC()
+        dc.SetFont(self.Font)
+        
+        # Compute max tip text size
+        for line in self.Tip:
+            w, h = dc.GetTextExtent(line)
+            max_width = max(max_width, w)
+            max_height += h
+        
+        return wx.Size(max_width + 4, max_height + 4)
+    
+    def RefreshTip(self):
+        """
+        Refresh tip on screen
+        """
+        # Prevent to call this function if tool tip destroyed
+        if self:
+            # Refresh tool tip size and position
+            self.SetSize(self.GetToolTipSize())
+            self.SetPosition(self.CurrentPosition)
+            
+            # Redraw tool tip
+            self.Refresh()
+    
+    def OnPaint(self, event):
+        """
+        Callback for Paint Event
+        @param event: Paint event
+        """
+        # Get buffered paint DC for tool tip
+        dc = wx.AutoBufferedPaintDC(self)
+        dc.Clear()
+        
+        # Set DC drawing style
+        pen = wx.Pen(wx.BLACK)
+        pen.SetJoin(wx.JOIN_MITER)
+        pen.SetCap(wx.CAP_PROJECTING)
+        dc.SetPen(pen)
+        dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170)))
+        dc.SetFont(self.Font)
+        
+        # Draw Tool tip
+        dc.BeginDrawing()
+        tip_width, tip_height = self.GetToolTipSize()
+        
+        # Draw background rectangle
+        dc.DrawRectangle(0, 0, tip_width, tip_height)
+        
+        # Draw tool tip text
+        line_offset = 0
+        for line in self.Tip:
+            dc.DrawText(line, 2, line_offset + 2)
+            line_width, line_height = dc.GetTextExtent(line)
+            line_offset += line_height
+        
+        dc.EndDrawing()
+        
+        event.Skip()
--- a/controls/LogViewer.py	Fri May 17 20:58:34 2013 +0200
+++ b/controls/LogViewer.py	Thu May 23 18:47:44 2013 +0200
@@ -28,7 +28,8 @@
 
 import wx
 
-from graphics import DebugViewer, REFRESH_PERIOD, ToolTip, TOOLTIP_WAIT_PERIOD
+from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD
+from graphics import DebugViewer, REFRESH_PERIOD
 from targets.typemapping import LogLevelsCount, LogLevels
 from util.BitmapLibrary import GetBitmap
 
@@ -699,7 +700,7 @@
                 tooltip_pos = self.MessagePanel.ClientToScreen(self.LastMousePos)
                 tooltip_pos.x += 10
                 tooltip_pos.y += 10
-                self.MessageToolTip = ToolTip(self.MessagePanel, message.GetFullText(), False)
+                self.MessageToolTip = CustomToolTip(self.MessagePanel, message.GetFullText(), False)
                 self.MessageToolTip.SetFont(self.Font)
                 self.MessageToolTip.MoveToolTip(tooltip_pos)
                 self.MessageToolTip.Show()
--- a/graphics/GraphicCommons.py	Fri May 17 20:58:34 2013 +0200
+++ b/graphics/GraphicCommons.py	Thu May 23 18:47:44 2013 +0200
@@ -29,6 +29,8 @@
 import datetime
 from threading import Lock,Timer
 
+from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD
+
 #-------------------------------------------------------------------------------
 #                               Common constants
 #-------------------------------------------------------------------------------
@@ -102,9 +104,6 @@
 # Define highlight refresh inhibition period in second
 REFRESH_HIGHLIGHT_PERIOD = 0.1
 
-# Define tooltip wait for displaying period in second
-TOOLTIP_WAIT_PERIOD = 0.5
-
 HANDLE_CURSORS = {
     (1, 1) : 2,
     (3, 3) : 2,
@@ -555,119 +554,6 @@
         dc.SetUserScale(scalex, scaley)
 
 #-------------------------------------------------------------------------------
-#                               Viewer ToolTip
-#-------------------------------------------------------------------------------
-
-"""
-Class that implements a custom tool tip
-"""
-
-if wx.Platform == '__WXMSW__':
-    faces = { 'times': 'Times New Roman',
-              'mono' : 'Courier New',
-              'helv' : 'Arial',
-              'other': 'Comic Sans MS',
-              'size' : 10,
-             }
-else:
-    faces = { 'times': 'Times',
-              'mono' : 'Courier',
-              'helv' : 'Helvetica',
-              'other': 'new century schoolbook',
-              'size' : 12,
-             }
-
-TOOLTIP_MAX_CHARACTERS = 30
-TOOLTIP_MAX_LINE = 5
-
-class ToolTip(wx.PopupWindow):
-    
-    def __init__(self, parent, tip, restricted=True):
-        wx.PopupWindow.__init__(self, parent)
-        
-        self.CurrentPosition = wx.Point(0, 0)
-        self.Restricted = restricted
-        
-        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
-        self.SetTip(tip)
-        
-        self.Font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["mono"])
-        
-        self.Bind(wx.EVT_PAINT, self.OnPaint)
-    
-    def SetFont(self, font):
-        self.Font = font
-        self.RefreshTip()
-    
-    def SetTip(self, tip):
-        lines = []
-        for line in tip.splitlines():
-            if self.Restricted and line != "":
-                words = line.split()
-                new_line = words[0]
-                for word in words[1:]:
-                    if len(new_line + " " + word) <= TOOLTIP_MAX_CHARACTERS:
-                        new_line += " " + word
-                    else:
-                        lines.append(new_line)
-                        new_line = word
-                lines.append(new_line)
-            else:
-                lines.append(line)
-        if self.Restricted and len(lines) > TOOLTIP_MAX_LINE:
-            self.Tip = lines[:TOOLTIP_MAX_LINE]
-            if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3:
-                self.Tip[-1] += "..."
-            else:
-                self.Tip[-1] = self.Tip[-1][:TOOLTIP_MAX_CHARACTERS - 3] + "..."
-        else:
-            self.Tip = lines
-        wx.CallAfter(self.RefreshTip)
-    
-    def MoveToolTip(self, pos):
-        screen_size = wx.GetDisplaySize()
-        w, h = self.GetTipExtent()
-        self.CurrentPosition = wx.Point(
-            max(0, min(pos.x, screen_size[0] - w - 4)),
-            max(0, min(pos.y, screen_size[1] - h - 4))) 
-        self.SetPosition(pos)
-    
-    def GetTipExtent(self):
-        max_width = 0
-        max_height = 0
-        for line in self.Tip:
-            dc = wx.MemoryDC()
-            dc.SetFont(self.Font)
-            w, h = dc.GetTextExtent(line)
-            max_width = max(max_width, w)
-            max_height += h
-        return max_width, max_height
-    
-    def RefreshTip(self):
-        if self:
-            w, h = self.GetTipExtent()
-            self.SetSize(wx.Size(w + 4, h + 4))
-            self.SetPosition(self.CurrentPosition)
-            self.Refresh()
-        
-    def OnPaint(self, event):
-        dc = wx.AutoBufferedPaintDC(self)
-        dc.Clear()
-        dc.SetPen(MiterPen(wx.BLACK))
-        dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170)))
-        dc.SetFont(self.Font)
-        dc.BeginDrawing()
-        w, h = self.GetTipExtent()
-        dc.DrawRectangle(0, 0, w + 4, h + 4)
-        offset = 0
-        for line in self.Tip:
-            dc.DrawText(line, 2, offset + 2)
-            w, h = dc.GetTextExtent(line)
-            offset += h
-        dc.EndDrawing()
-        event.Skip()
-
-#-------------------------------------------------------------------------------
 #                    Helpers for highlighting text
 #-------------------------------------------------------------------------------
 
@@ -1107,7 +993,7 @@
     def OnToolTipTimer(self, event):
         value = self.GetToolTipValue()
         if value is not None and self.ToolTipPos is not None:
-            self.ToolTip = ToolTip(self.Parent, value)
+            self.ToolTip = CustomToolTip(self.Parent, value)
             self.ToolTip.MoveToolTip(self.ToolTipPos)
             self.ToolTip.Show()