From d827762474e90e9db682e5c0694255777fc473c2 Mon Sep 17 00:00:00 2001
From: Bart Crouch <bartius.crouch@gmail.com>
Date: Thu, 5 Jan 2012 15:24:18 +0000
Subject: [PATCH] pep8 compliancy general code cleaning reset multiplier after
 text has disappeared support for mousewheel in icon-display improved fading,
 using a timer

---
 space_view3d_screencast_keys.py | 324 +++++++++++++++++---------------
 1 file changed, 169 insertions(+), 155 deletions(-)

diff --git a/space_view3d_screencast_keys.py b/space_view3d_screencast_keys.py
index be431f41b..8494a9ff1 100644
--- a/space_view3d_screencast_keys.py
+++ b/space_view3d_screencast_keys.py
@@ -16,12 +16,14 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
+# <pep8 compliant>
+
 bl_info = {
     'name': 'Screencast Keys',
-    'author': 'Paulo Gomes, Bart Crouch, John E. Herrenyo',
-    'version': (1, 4),
-    'blender': (2, 5, 9),
-    'api': 39933,
+    'author': 'Paulo Gomes, Bart Crouch, John E. Herrenyo, Gaia Clary',
+    'version': (1, 5),
+    'blender': (2, 6, 1),
+    'api': 43073,
     'location': 'View3D > Properties panel > Screencast Keys',
     'warning': '',
     'description': 'Display keys pressed in the 3d-view, '\
@@ -33,53 +35,47 @@ bl_info = {
     'category': '3D View'}
 
 
-# #####
-#
-# Modification history:
-# - Version 1,4
-# - 07-sep-2011 (Gaia Clary):
-# - settings now stored in blend file  
-# - grouping mouse&text 
-# - mouse_size and font_size separated 
-# - boundingBox for improved readability.
-# - missing mouse "release" clicks added 
-#
-# ####
-
-
 import bgl
 import blf
 import bpy
 import time
 
+
 MOUSE_RATIO = 0.535
 
+
 def getDisplayLocation(context):
-    sc   = context.scene
-    mouse_size = sc.screencast_keys_mouse_size
-    pos_x = int( (context.region.width  - mouse_size*MOUSE_RATIO) * sc.screencast_keys_pos_x / 100)
-    pos_y = int( (context.region.height - mouse_size) * sc.screencast_keys_pos_y / 100)
-    return pos_x, pos_y
+    scene   = context.scene
+    mouse_size = scene.screencast_keys_mouse_size
+
+    pos_x = int( (context.region.width  - mouse_size * MOUSE_RATIO) * \
+        scene.screencast_keys_pos_x / 100)
+    pos_y = int( (context.region.height - mouse_size) * 
+        scene.screencast_keys_pos_y / 100)
+
+    return(pos_x, pos_y)
+
 
 def getBoundingBox(current_width, current_height, new_text):
     w,h = blf.dimensions(0,new_text)
     if w > current_width:
         current_width = w
     current_height += h
-    return current_width, current_height
+
+    return(current_width, current_height)
+
 
 def draw_callback_px(self, context):
     wm = context.window_manager
     sc = context.scene
     if not wm.screencast_keys_keys:
         return
-   
+
     font_size  = sc.screencast_keys_font_size
     mouse_size = sc.screencast_keys_mouse_size
     link       = sc.screencast_keys_link
     pos_x, pos_y = getDisplayLocation(context)
 
-
     # draw text in the 3d-view
     # ========================
     blf.size(0, sc.screencast_keys_font_size, 72)
@@ -107,17 +103,17 @@ def draw_callback_px(self, context):
             alpha = min(1.0, max(0.0, 2 * (2 - label_time)))
             bgl.glColor4f(r, g, b, alpha)
             blf.draw(0, self.key[i])
-            text_width, text_height = getBoundingBox(text_width, text_height, self.key[i])
+            text_width, text_height = getBoundingBox(text_width, text_height,
+                self.key[i])
             row_count += 1
-            final = i
+            final = i + 1
         else:
             break
 
     # get rid of status texts that aren't displayed anymore
-    self.key = self.key[:final+1]
-    self.time = self.time[:final+1]
-  
- 
+    self.key = self.key[:final]
+    self.time = self.time[:final]
+
     # draw graphical representation of the mouse
     # ==========================================
     if sc.screencast_keys_mouse == 'icon':
@@ -132,49 +128,32 @@ def draw_callback_px(self, context):
                 if shape:
                     alpha = min(1.0, max(0.0, 2 * (2 - click_time)))
                     draw_mouse(context, shape, "filled", alpha)
-                final = i
+                final = i + 1
             else:
                 break
 
-    
     # get rid of mouse clicks that aren't displayed anymore
-    self.mouse = self.mouse[:final+1]
-    self.mouse_time = self.mouse_time[:final+1]
-
+    self.mouse = self.mouse[:final]
+    self.mouse_time = self.mouse_time[:final]
 
     # Draw border (if enabled)
     # ========================
-    if link == True and row_count > 0:
-        bgl.glEnable(bgl.GL_BLEND)
-        bgl.glBegin(bgl.GL_QUADS)
-        bgl.glLineWidth(2)
-        bgl.glColor4f(r, g, b, 0.2)
-        drawRectangle(pos_x , pos_y , text_width+mouse_size*MOUSE_RATIO*1.3 , max(mouse_size, font_size*row_count), 4 )
-        bgl.glEnd()
-        bgl.glBegin(bgl.GL_LINES)
-        bgl.glColor4f(r, g, b, alpha )
-        drawRectangle(pos_x , pos_y , text_width+mouse_size*MOUSE_RATIO*1.3 , max(mouse_size, font_size*row_count), 4 )
-        bgl.glEnd()
-
-
-# Draw a line. currently not used.
-def drawLinef(from_x, from_y, to_x, to_y):
-    bgl.glVertex2f(from_x, from_y)
-    bgl.glVertex2f(to_x, to_y)
-
-
-# Draw a rectangle. Currently not used
-def drawRectangle (ox, oy, ow, oh, padding=0):
-
-    x = ox - 2*padding
-    y = oy - padding
-    w = ow + 4 * padding
-    h = oh + 2 * padding
-
-    drawLinef(x,   y,   x+w, y)
-    drawLinef(x+w, y,   x+w, y+h)
-    drawLinef(x+w, y+h, x  , y+h)
-    drawLinef(x  , y+h, x  ,   y)
+    if link and row_count > 0:
+        padding = 8
+        x0 = max(0, pos_x - padding)
+        y0 = max(0, pos_y - padding)
+        x1 = pos_x + text_width + mouse_size * MOUSE_RATIO * 1.3 + padding
+        y1 = pos_y + max(mouse_size, font_size * row_count) + padding
+        positions = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]]
+        settings = [[bgl.GL_QUADS, min(0.2, alpha)], [bgl.GL_LINE_LOOP, alpha]]
+
+        for mode, box_alpha in settings:
+            bgl.glEnable(bgl.GL_BLEND)
+            bgl.glBegin(mode)
+            bgl.glColor4f(r, g, b, box_alpha)
+            for v1, v2 in positions: 
+                bgl.glVertex2f(v1, v2)
+            bgl.glEnd()
 
 
 def draw_mouse(context, shape, style, alpha):
@@ -185,7 +164,7 @@ def draw_mouse(context, shape, style, alpha):
     link = sc.screencast_keys_link
 
     pos_x, pos_y = getDisplayLocation(context)
-    if link==True:
+    if link:
         offset_x = pos_x
     else:
         offset_x = context.region.width - pos_x - (mouse_size * MOUSE_RATIO)
@@ -193,7 +172,7 @@ def draw_mouse(context, shape, style, alpha):
     offset_y = pos_y
     if font_size > mouse_size:
         offset_y += (font_size - mouse_size) / 2
- 
+
     shape_data = get_shape_data(shape)
 
     bgl.glTranslatef(offset_x, offset_y, 0)
@@ -201,15 +180,14 @@ def draw_mouse(context, shape, style, alpha):
     # color
     r, g, b = sc.screencast_keys_color
     bgl.glEnable(bgl.GL_BLEND)
-    #bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
-    #bgl.glColor4f(r, g, b, alpha)
-    
+    bgl.glColor4f(r, g, b, alpha)
+
     # inner shape for filled style
     if style == "filled":
         inner_shape = []
         for i in shape_data:
             inner_shape.append(i[0])
-    
+
     # outer shape
     for i in shape_data:
         shape_segment = i
@@ -217,16 +195,14 @@ def draw_mouse(context, shape, style, alpha):
         shape_segment[1] = [mouse_size * k for k in shape_segment[1]]
         shape_segment[2] = [mouse_size * k for k in shape_segment[2]]
         shape_segment[3] = [mouse_size * k for k in shape_segment[3]]
-        
+
         # create the buffer
         shape_buffer = bgl.Buffer(bgl.GL_FLOAT, [4, 3], shape_segment)
-        
+
         # create the map and draw the triangle fan
         bgl.glMap1f(bgl.GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, shape_buffer)
         bgl.glEnable(bgl.GL_MAP1_VERTEX_3)
-        bgl.glColor4f(r, g, b, alpha)
 
-        
         if style == "outline":
             bgl.glBegin(bgl.GL_LINE_STRIP)
         else: # style == "filled"
@@ -234,22 +210,21 @@ def draw_mouse(context, shape, style, alpha):
         for j in range(10):
             bgl.glEvalCoord1f(j / 10.0)
         x, y, z = shape_segment[3]
-        
+
         # make sure the last vertex is indeed the last one, to avoid gaps
         bgl.glVertex3f(x, y, z)
         bgl.glEnd()
         bgl.glDisable(bgl.GL_MAP1_VERTEX_3)
-    
+
     # draw interior
     if style == "filled":
-        bgl.glColor4f(r, g, b, alpha)
         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
         for i in inner_shape:
             j = [mouse_size * k for k in i]
             x, y, z = j
             bgl.glVertex3f(x, y, z)
         bgl.glEnd()
-    
+
     bgl.glTranslatef(-offset_x, -offset_y, 0)
 
 
@@ -339,6 +314,44 @@ def get_shape_data(shape):
             [0.303, 0.833, 0.0], 
             [0.302, 0.817, 0.0], 
             [0.301, 0.8, 0.0]]]
+    elif shape == "middle_down_button":
+        data = [[[0.301, 0.8, 0.0], 
+            [0.298, 0.768, 0.0], 
+            [0.231, 0.768, 0.0], 
+            [0.228, 0.8, 0.0]], 
+            [[0.228, 0.8, 0.0], 
+            [0.226, 0.817, 0.0], 
+            [0.225, 0.833, 0.0], 
+            [0.224, 0.85, 0.0]], 
+            [[0.224, 0.85, 0.0], 
+            [0.264, 0.873, 0.0], 
+            [0.284, 0.873, 0.0], 
+            [0.305, 0.85, 0.0]], 
+            [[0.305, 0.85, 0.0], 
+            [0.303, 0.833, 0.0], 
+            [0.302, 0.817, 0.0], 
+            [0.301, 0.8, 0.0]]]
+    elif shape == "middle_up_button":
+        data = [[[0.270, 0.873, 0.0], 
+            [0.264, 0.873, 0.0], 
+            [0.222, 0.877, 0.0], 
+            [0.224, 0.9, 0.0]], 
+            [[0.224, 0.9, 0.0], 
+            [0.225, 0.917, 0.0], 
+            [0.226, 0.933, 0.0], 
+            [0.228, 0.95, 0.0]], 
+            [[0.228, 0.95, 0.0], 
+            [0.231, 0.982, 0.0], 
+            [0.298, 0.982, 0.0], 
+            [0.301, 0.95, 0.0]], 
+            [[0.301, 0.95, 0.0], 
+            [0.302, 0.933, 0.0], 
+            [0.303, 0.917, 0.0], 
+            [0.305, 0.9, 0.0]], 
+            [[0.305, 0.9, 0.0], 
+            [0.307, 0.877, 0.0], 
+            [0.284, 0.873, 0.0], 
+            [0.270, 0.873, 0.0]]]
     elif shape == "right_button":
         data = [[[0.375, 0.763, 0.0], 
             [0.402, 0.755, 0.0], 
@@ -364,20 +377,25 @@ def get_shape_data(shape):
             [0.334, 0.77, 0.0], 
             [0.348, 0.771, 0.0], 
             [0.375, 0.763, 0.0]]]
-    
+
     return(data)
 
 
 # return the shape that belongs to the given event
 def map_mouse_event(event):
     shape = False
-    
+
     if event == 'LEFTMOUSE':
         shape = "left_button"
     elif event == 'MIDDLEMOUSE':
         shape = "middle_button"
     elif event == 'RIGHTMOUSE':
         shape = "right_button"
+    elif event == 'WHEELDOWNMOUSE':
+        shape = "middle_down_button"
+    elif event == 'WHEELUPMOUSE':
+        shape = "middle_up_button"
+
     return(shape)
 
 
@@ -387,34 +405,38 @@ class ScreencastKeysStatus(bpy.types.Operator):
     bl_description = "Display keys pressed in the 3D-view"
     last_activity = 'NONE'
 
+    _handle = None
+    _timer = None
+
     def modal(self, context, event):
         if context.area:
             context.area.tag_redraw()
-        sc = context.scene
+        
+        if event.type == 'TIMER':
+            # no input, so no need to change the display
+            return {'PASS_THROUGH'}
+        
+        scene = context.scene
         # keys that shouldn't show up in the 3d-view
         mouse_keys = ['MOUSEMOVE','MIDDLEMOUSE','LEFTMOUSE',
          'RIGHTMOUSE', 'WHEELDOWNMOUSE','WHEELUPMOUSE']
         ignore_keys = ['LEFT_SHIFT', 'RIGHT_SHIFT', 'LEFT_ALT',
          'RIGHT_ALT', 'LEFT_CTRL', 'RIGHT_CTRL', 'TIMER']
-        if sc.screencast_keys_mouse != 'text':
+        if scene.screencast_keys_mouse != 'text':
             ignore_keys.extend(mouse_keys)
 
-           
-        #if (event.value != "NOTHING" and event.value != "PRESS"):
-        #    print (event.value, event.type, "Previous activity was: ", self.last_activity)
-
-        if event.value == 'PRESS' or (event.value == 'RELEASE' and self.last_activity == 'KEYBOARD' and  event.type in mouse_keys ) :
+        if event.value == 'PRESS' or (event.value == 'RELEASE' and \
+        self.last_activity == 'KEYBOARD' and event.type in mouse_keys):
             # add key-press to display-list
             sc_keys = []
 
-            
             if event.ctrl:
                 sc_keys.append("Ctrl ")
             if event.alt:
                 sc_keys.append("Alt ")
             if event.shift:
                 sc_keys.append("Shift ")
-            
+
             sc_amount = ""
 
             if self.key:
@@ -430,14 +452,15 @@ class ScreencastKeysStatus(bpy.types.Operator):
                             sc_amount = " x2"
                         del self.key[0]
                         del self.time[0]
-         
+
             if event.type not in ignore_keys:
                 #print("Recorded as key")
                 sc_keys.append(event.type)
                 self.key.insert(0, "+ ".join(sc_keys) + sc_amount)
                 self.time.insert(0, time.time())
-             
-            elif event.type in mouse_keys and sc.screencast_keys_mouse == 'icon':
+
+            elif event.type in mouse_keys and \
+            scene.screencast_keys_mouse == 'icon':
                 #print("Recorded as mouse press")
                 self.mouse.insert(0, event.type)
                 self.mouse_time.insert(0, time.time()) 
@@ -447,22 +470,22 @@ class ScreencastKeysStatus(bpy.types.Operator):
             else:
                 self.last_activity = 'KEYBOARD'
             #print("Last activity set to:", self.last_activity)
-            
+
         if not context.window_manager.screencast_keys_keys:
             # stop script
+            context.window_manager.event_timer_remove(self._timer)
             context.region.callback_remove(self._handle)
             return {'CANCELLED'}
 
         return {'PASS_THROUGH'}
 
-
     def cancel(self, context):
         if context.window_manager.screencast_keys_keys:
+            context.window_manager.event_timer_remove(self._timer)
             context.region.callback_remove(self._handle)
             context.window_manager.screencast_keys_keys = False
         return {'CANCELLED'}
 
-
     def invoke(self, context, event):
         if context.area.type == 'VIEW_3D':
             if context.window_manager.screencast_keys_keys == False:
@@ -475,6 +498,8 @@ class ScreencastKeysStatus(bpy.types.Operator):
                 self.mouse_time = []
                 self._handle = context.region.callback_add(draw_callback_px,
                     (self, context), 'POST_PIXEL')
+                self._timer = context.window_manager.event_timer_add(0.05,
+                    context.window)
                 return {'RUNNING_MODAL'}
             else:
                 # operator is called again, stop displaying
@@ -491,62 +516,52 @@ class ScreencastKeysStatus(bpy.types.Operator):
 
 # properties used by the script
 def init_properties():
-
-    sc = bpy.types.Scene
+    scene = bpy.types.Scene
     wm = bpy.types.WindowManager
 
-    sc.screencast_keys_pos_x = bpy.props.IntProperty(
-            name="Pos X",
-            description="Margin on the x axis",
-            default=5,
-            min=0,
-            max=100)
-
-    sc.screencast_keys_pos_y = bpy.props.IntProperty(
-            name="Pos Y",
-            description="Margin on the y axis",
-            default=10,
-            min=0,
-            max=100)
-
-    sc.screencast_keys_font_size = bpy.props.IntProperty(
-            name="Font",
-            description="Fontsize",
-            default=20, min=10, max=150)
-
-    sc.screencast_keys_mouse_size = bpy.props.IntProperty(
-            name="Mouse",
-            description="Mousesize",
-            default=60, min=10, max=150)
-
-
-    sc.screencast_keys_color = bpy.props.FloatVectorProperty(
-            name="Color",
-            description="Font color",
-            default=(1.0, 1.0, 1.0),
-            min=0,
-            max=1,
-            subtype='COLOR')
-
-    sc.screencast_keys_mouse = bpy.props.EnumProperty(
-            items=(("none", "None", "Don't display mouse events"), 
-                  ("icon", "Icon", "Display graphical represenation of "\
-                   "the mouse"),
-                  ("text", "Text", "Display mouse events as text lines")),
-            name="Mouse display",
-            description="Display mouse events",
-            default='text')
-
-    sc.screencast_keys_link = bpy.props.BoolProperty(
-            name="Group Mouse & Text",
-            description = "Link mouse to text",
-            default = False)
-
-    print ("Screencast Keys: initialized from AddOn default settings.")
+    scene.screencast_keys_pos_x = bpy.props.IntProperty(
+        name="Pos X",
+        description="Margin on the x axis",
+        default=5,
+        min=0,
+        max=100)
+    scene.screencast_keys_pos_y = bpy.props.IntProperty(
+        name="Pos Y",
+        description="Margin on the y axis",
+        default=10,
+        min=0,
+        max=100)
+    scene.screencast_keys_font_size = bpy.props.IntProperty(
+        name="Font",
+        description="Fontsize",
+        default=20, min=10, max=150)
+    scene.screencast_keys_mouse_size = bpy.props.IntProperty(
+        name="Mouse",
+        description="Mousesize",
+        default=60, min=10, max=150)
+    scene.screencast_keys_color = bpy.props.FloatVectorProperty(
+        name="Color",
+        description="Font color",
+        default=(1.0, 1.0, 1.0),
+        min=0,
+        max=1,
+        subtype='COLOR')
+    scene.screencast_keys_mouse = bpy.props.EnumProperty(
+        items=(("none", "None", "Don't display mouse events"), 
+              ("icon", "Icon", "Display graphical represenation of "\
+               "the mouse"),
+              ("text", "Text", "Display mouse events as text lines")),
+        name="Mouse display",
+        description="Display mouse events",
+        default='text')
+    scene.screencast_keys_link = bpy.props.BoolProperty(
+        name="Group Mouse & Text",
+        description = "Link mouse to text",
+        default = False)
 
     # Runstate initially always set to False
     # note: it is not stored in the Scene, but in window manager:
-    wm.screencast_keys_keys      = bpy.props.BoolProperty(default=False)
+    wm.screencast_keys_keys = bpy.props.BoolProperty(default=False)
 
 
 # removal of properties when script is disabled
@@ -569,19 +584,19 @@ class OBJECT_PT_keys_status(bpy.types.Panel):
     bl_label = "Screencast Keys"
     bl_space_type = "VIEW_3D"
     bl_region_type = "UI"
-    
+
     def draw(self, context):
         sc = context.scene
         wm = context.window_manager
         layout = self.layout
-        
+
         if not wm.screencast_keys_keys:
             layout.operator("view3d.screencast_keys", text="Start display",
                 icon='PLAY')
         else:
             layout.operator("view3d.screencast_keys", text="Stop display",
                 icon='PAUSE')
-        
+
         col = layout.column(align=True)
         row = col.row(align=True)
         row.prop(sc, "screencast_keys_pos_x")
@@ -593,7 +608,7 @@ class OBJECT_PT_keys_status(bpy.types.Panel):
         row.prop(sc, "screencast_keys_mouse", text="Mouse")
         row = col.row(align=True)
         row.prop(sc, "screencast_keys_link")
-        
+
         layout.prop(sc, "screencast_keys_color")
 
 
@@ -615,4 +630,3 @@ def unregister():
 
 if __name__ == "__main__":
     register()
-
-- 
GitLab