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()