| 
							- #!/usr/bin/env python3
 - # -*- coding: utf-8 -*-
 - 
 - # PatchBay Canvas engine using QGraphicsView/Scene
 - # Copyright (C) 2010-2019 Filipe Coelho <falktx@falktx.com>
 - #
 - # This program 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 of
 - # the License, or any later version.
 - #
 - # This program 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.
 - #
 - # For a full copy of the GNU General Public License see the doc/GPL.txt file.
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Global)
 - 
 - from sip import voidptr
 - from struct import pack
 - 
 - from PyQt5.QtCore import qCritical, Qt, QPointF, QRectF, QTimer
 - from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QImage, QLinearGradient, QPainter, QPen
 - from PyQt5.QtWidgets import QGraphicsItem, QMenu
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Custom)
 - 
 - from . import (
 -     canvas,
 -     features,
 -     options,
 -     port_dict_t,
 -     CanvasBoxType,
 -     ANTIALIASING_FULL,
 -     ACTION_PLUGIN_EDIT,
 -     ACTION_PLUGIN_SHOW_UI,
 -     ACTION_PLUGIN_CLONE,
 -     ACTION_PLUGIN_REMOVE,
 -     ACTION_PLUGIN_RENAME,
 -     ACTION_PLUGIN_REPLACE,
 -     ACTION_GROUP_INFO,
 -     ACTION_GROUP_JOIN,
 -     ACTION_GROUP_SPLIT,
 -     ACTION_GROUP_RENAME,
 -     ACTION_PORTS_DISCONNECT,
 -     ACTION_INLINE_DISPLAY,
 -     EYECANDY_FULL,
 -     PORT_MODE_NULL,
 -     PORT_MODE_INPUT,
 -     PORT_MODE_OUTPUT,
 -     PORT_TYPE_NULL,
 -     PORT_TYPE_AUDIO_JACK,
 -     PORT_TYPE_MIDI_ALSA,
 -     PORT_TYPE_MIDI_JACK,
 -     PORT_TYPE_PARAMETER,
 -     MAX_PLUGIN_ID_ALLOWED,
 - )
 - 
 - from .canvasboxshadow import CanvasBoxShadow
 - from .canvasicon import CanvasIcon
 - from .canvasport import CanvasPort
 - from .theme import Theme
 - from .utils import CanvasItemFX, CanvasGetFullPortName, CanvasGetPortConnectionList
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - 
 - class cb_line_t(object):
 -     def __init__(self, line, connection_id):
 -         self.line = line
 -         self.connection_id = connection_id
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - 
 - class CanvasBox(QGraphicsItem):
 -     INLINE_DISPLAY_DISABLED = 0
 -     INLINE_DISPLAY_ENABLED  = 1
 -     INLINE_DISPLAY_CACHED   = 2
 - 
 -     def __init__(self, group_id, group_name, icon, parent=None):
 -         QGraphicsItem.__init__(self)
 -         self.setParentItem(parent)
 - 
 -         # Save Variables, useful for later
 -         self.m_group_id = group_id
 -         self.m_group_name = group_name
 - 
 -         # plugin Id, < 0 if invalid
 -         self.m_plugin_id = -1
 -         self.m_plugin_ui = False
 -         self.m_plugin_inline = self.INLINE_DISPLAY_DISABLED
 - 
 -         # Base Variables
 -         self.p_width = 50
 -         self.p_width_in = 0
 -         self.p_width_out = 0
 -         self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1
 - 
 -         self.m_last_pos = QPointF()
 -         self.m_splitted = False
 -         self.m_splitted_mode = PORT_MODE_NULL
 - 
 -         self.m_cursor_moving = False
 -         self.m_forced_split = False
 -         self.m_mouse_down = False
 -         self.m_inline_data = None
 -         self.m_inline_image = None
 -         self.m_inline_scaling = 1.0
 - 
 -         self.m_port_list_ids = []
 -         self.m_connection_lines = []
 - 
 -         # Set Font
 -         self.m_font_name = QFont()
 -         self.m_font_name.setFamily(canvas.theme.box_font_name)
 -         self.m_font_name.setPixelSize(canvas.theme.box_font_size)
 -         self.m_font_name.setWeight(canvas.theme.box_font_state)
 - 
 -         self.m_font_port = QFont()
 -         self.m_font_port.setFamily(canvas.theme.port_font_name)
 -         self.m_font_port.setPixelSize(canvas.theme.port_font_size)
 -         self.m_font_port.setWeight(canvas.theme.port_font_state)
 - 
 -         # Icon
 -         if canvas.theme.box_use_icon:
 -             self.icon_svg = CanvasIcon(icon, self.m_group_name, self)
 -         else:
 -             self.icon_svg = None
 - 
 -         # Shadow
 -         # FIXME FX on top of graphic items make them lose high-dpi
 -         # See https://bugreports.qt.io/browse/QTBUG-65035
 -         if options.eyecandy and canvas.scene.getDevicePixelRatioF() == 1.0:
 -             self.shadow = CanvasBoxShadow(self.toGraphicsObject())
 -             self.shadow.setFakeParent(self)
 -             self.setGraphicsEffect(self.shadow)
 -         else:
 -             self.shadow = None
 - 
 -         # Final touches
 -         self.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
 - 
 -         # Wait for at least 1 port
 -         if options.auto_hide_groups:
 -             self.setVisible(False)
 - 
 -         if options.auto_select_items:
 -             self.setAcceptHoverEvents(True)
 - 
 -         self.updatePositions()
 - 
 -         canvas.scene.addItem(self)
 -         QTimer.singleShot(0, self.fixPos)
 - 
 -     def getGroupId(self):
 -         return self.m_group_id
 - 
 -     def getGroupName(self):
 -         return self.m_group_name
 - 
 -     def isSplitted(self):
 -         return self.m_splitted
 - 
 -     def getSplittedMode(self):
 -         return self.m_splitted_mode
 - 
 -     def getPortCount(self):
 -         return len(self.m_port_list_ids)
 - 
 -     def getPortList(self):
 -         return self.m_port_list_ids
 - 
 -     def redrawInlineDisplay(self):
 -         if self.m_plugin_inline == self.INLINE_DISPLAY_CACHED:
 -             self.m_plugin_inline = self.INLINE_DISPLAY_ENABLED
 -             self.update()
 - 
 -     def removeAsPlugin(self):
 -         #del self.m_inline_image
 -         #self.m_inline_data = None
 -         #self.m_inline_image = None
 -         #self.m_inline_scaling = 1.0
 - 
 -         self.m_plugin_id = -1
 -         self.m_plugin_ui = False
 -         #self.m_plugin_inline = self.INLINE_DISPLAY_DISABLED
 - 
 -     def setAsPlugin(self, plugin_id, hasUI, hasInlineDisplay):
 -         if hasInlineDisplay and not options.inline_displays:
 -             hasInlineDisplay = False
 - 
 -         if not hasInlineDisplay:
 -             del self.m_inline_image
 -             self.m_inline_data = None
 -             self.m_inline_image = None
 -             self.m_inline_scaling = 1.0
 - 
 -         self.m_plugin_id = plugin_id
 -         self.m_plugin_ui = hasUI
 -         self.m_plugin_inline = self.INLINE_DISPLAY_ENABLED if hasInlineDisplay else self.INLINE_DISPLAY_DISABLED
 -         self.update()
 - 
 -     def setIcon(self, icon):
 -         if self.icon_svg is not None:
 -             self.icon_svg.setIcon(icon, self.m_group_name)
 - 
 -     def setSplit(self, split, mode=PORT_MODE_NULL):
 -         self.m_splitted = split
 -         self.m_splitted_mode = mode
 - 
 -     def setGroupName(self, group_name):
 -         self.m_group_name = group_name
 -         self.updatePositions()
 - 
 -     def setShadowOpacity(self, opacity):
 -         if self.shadow:
 -             self.shadow.setOpacity(opacity)
 - 
 -     def addPortFromGroup(self, port_id, port_mode, port_type, port_name, is_alternate):
 -         if len(self.m_port_list_ids) == 0:
 -             if options.auto_hide_groups:
 -                 if options.eyecandy == EYECANDY_FULL:
 -                     CanvasItemFX(self, True, False)
 -                 self.setVisible(True)
 - 
 -         new_widget = CanvasPort(self.m_group_id, port_id, port_name, port_mode, port_type, is_alternate, self)
 - 
 -         port_dict = port_dict_t()
 -         port_dict.group_id = self.m_group_id
 -         port_dict.port_id = port_id
 -         port_dict.port_name = port_name
 -         port_dict.port_mode = port_mode
 -         port_dict.port_type = port_type
 -         port_dict.is_alternate = is_alternate
 -         port_dict.widget = new_widget
 - 
 -         self.m_port_list_ids.append(port_id)
 - 
 -         return new_widget
 - 
 -     def removePortFromGroup(self, port_id):
 -         if port_id in self.m_port_list_ids:
 -             self.m_port_list_ids.remove(port_id)
 -         else:
 -             qCritical("PatchCanvas::CanvasBox.removePort(%i) - unable to find port to remove" % port_id)
 -             return
 - 
 -         if len(self.m_port_list_ids) > 0:
 -             self.updatePositions()
 - 
 -         elif self.isVisible():
 -             if options.auto_hide_groups:
 -                 if options.eyecandy == EYECANDY_FULL:
 -                     CanvasItemFX(self, False, False)
 -                 else:
 -                     self.setVisible(False)
 - 
 -     def addLineFromGroup(self, line, connection_id):
 -         new_cbline = cb_line_t(line, connection_id)
 -         self.m_connection_lines.append(new_cbline)
 - 
 -     def removeLineFromGroup(self, connection_id):
 -         for connection in self.m_connection_lines:
 -             if connection.connection_id == connection_id:
 -                 self.m_connection_lines.remove(connection)
 -                 return
 -         qCritical("PatchCanvas::CanvasBox.removeLineFromGroup(%i) - unable to find line to remove" % connection_id)
 - 
 -     def checkItemPos(self):
 -         if not canvas.size_rect.isNull():
 -             pos = self.scenePos()
 -             if not (canvas.size_rect.contains(pos) and
 -                     canvas.size_rect.contains(pos + QPointF(self.p_width, self.p_height))):
 -                 if pos.x() < canvas.size_rect.x():
 -                     self.setPos(canvas.size_rect.x(), pos.y())
 -                 elif pos.x() + self.p_width > canvas.size_rect.width():
 -                     self.setPos(canvas.size_rect.width() - self.p_width, pos.y())
 - 
 -                 pos = self.scenePos()
 -                 if pos.y() < canvas.size_rect.y():
 -                     self.setPos(pos.x(), canvas.size_rect.y())
 -                 elif pos.y() + self.p_height > canvas.size_rect.height():
 -                     self.setPos(pos.x(), canvas.size_rect.height() - self.p_height)
 - 
 -     def removeIconFromScene(self):
 -         if self.icon_svg is None:
 -             return
 - 
 -         item = self.icon_svg
 -         self.icon_svg = None
 -         canvas.scene.removeItem(item)
 -         del item
 - 
 -     def updatePositions(self):
 -         self.prepareGeometryChange()
 - 
 -         # Check Text Name size
 -         app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30
 -         self.p_width = max(200 if self.m_plugin_inline != self.INLINE_DISPLAY_DISABLED else 50, app_name_size)
 - 
 -         # Get Port List
 -         port_list = []
 -         for port in canvas.port_list:
 -             if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids:
 -                 port_list.append(port)
 - 
 -         if len(port_list) == 0:
 -             self.p_height = canvas.theme.box_header_height
 -             self.p_width_in = 0
 -             self.p_width_out = 0
 -         else:
 -             max_in_width = max_out_width = 0
 -             port_spacing = canvas.theme.port_height + canvas.theme.port_spacing
 - 
 -             # Get Max Box Width, vertical ports re-positioning
 -             port_types = [PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER]
 -             last_in_type = last_out_type = PORT_TYPE_NULL
 -             last_in_pos = last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
 - 
 -             for port_type in port_types:
 -                 for port in port_list:
 -                     if port.port_type != port_type:
 -                         continue
 - 
 -                     size = QFontMetrics(self.m_font_port).width(port.port_name)
 - 
 -                     if port.port_mode == PORT_MODE_INPUT:
 -                         max_in_width = max(max_in_width, size)
 -                         if port.port_type != last_in_type:
 -                             if last_in_type != PORT_TYPE_NULL:
 -                                 last_in_pos += canvas.theme.port_spacingT
 -                             last_in_type = port.port_type
 -                         port.widget.setY(last_in_pos)
 -                         last_in_pos += port_spacing
 - 
 -                     elif port.port_mode == PORT_MODE_OUTPUT:
 -                         max_out_width = max(max_out_width, size)
 -                         if port.port_type != last_out_type:
 -                             if last_out_type != PORT_TYPE_NULL:
 -                                 last_out_pos += canvas.theme.port_spacingT
 -                             last_out_type = port.port_type
 -                         port.widget.setY(last_out_pos)
 -                         last_out_pos += port_spacing
 - 
 -             self.p_width = max(self.p_width, (100 if self.m_plugin_inline != self.INLINE_DISPLAY_DISABLED else 30) + max_in_width + max_out_width)
 -             self.p_width_in = max_in_width
 -             self.p_width_out = max_out_width
 - 
 -             #if self.m_plugin_inline:
 -                 #self.p_width += 10
 - 
 -             # Horizontal ports re-positioning
 -             inX = canvas.theme.port_offset
 -             outX = self.p_width - max_out_width - canvas.theme.port_offset - 12
 -             for port_type in port_types:
 -                 for port in port_list:
 -                     if port.port_mode == PORT_MODE_INPUT:
 -                         port.widget.setX(inX)
 -                         port.widget.setPortWidth(max_in_width)
 - 
 -                     elif port.port_mode == PORT_MODE_OUTPUT:
 -                         port.widget.setX(outX)
 -                         port.widget.setPortWidth(max_out_width)
 - 
 -             self.p_height = max(last_in_pos, last_out_pos)
 -             self.p_height += max(canvas.theme.port_spacing, canvas.theme.port_spacingT) - canvas.theme.port_spacing
 -             self.p_height += canvas.theme.box_pen.widthF()
 - 
 -         self.repaintLines(True)
 -         self.update()
 - 
 -     def repaintLines(self, forced=False):
 -         if self.pos() != self.m_last_pos or forced:
 -             for connection in self.m_connection_lines:
 -                 connection.line.updateLinePos()
 - 
 -         self.m_last_pos = self.pos()
 - 
 -     def resetLinesZValue(self):
 -         for connection in canvas.connection_list:
 -             if connection.port_out_id in self.m_port_list_ids and connection.port_in_id in self.m_port_list_ids:
 -                 z_value = canvas.last_z_value
 -             else:
 -                 z_value = canvas.last_z_value - 1
 - 
 -             connection.widget.setZValue(z_value)
 - 
 -     def type(self):
 -         return CanvasBoxType
 - 
 -     def contextMenuEvent(self, event):
 -         event.accept()
 -         menu = QMenu()
 - 
 -         # Conenct menu stuff
 -         connMenu = QMenu("Connect", menu)
 - 
 -         our_port_types = []
 -         our_port_outs = {
 -             PORT_TYPE_AUDIO_JACK: [],
 -             PORT_TYPE_MIDI_JACK: [],
 -             PORT_TYPE_MIDI_ALSA: [],
 -         }
 -         for port in canvas.port_list:
 -             if port.group_id != self.m_group_id:
 -                 continue
 -             if port.port_mode != PORT_MODE_OUTPUT:
 -                 continue
 -             if port.port_id not in self.m_port_list_ids:
 -                 continue
 -             if port.port_type not in our_port_types:
 -                 our_port_types.append(port.port_type)
 -             our_port_outs[port.port_type].append((port.group_id, port.port_id))
 - 
 -         if len(our_port_types) != 0:
 -             act_x_conn = None
 -             for group in canvas.group_list:
 -                 if self.m_group_id == group.group_id:
 -                     continue
 - 
 -                 has_ports = False
 -                 target_ports = {
 -                     PORT_TYPE_AUDIO_JACK: [],
 -                     PORT_TYPE_MIDI_JACK: [],
 -                     PORT_TYPE_MIDI_ALSA: [],
 -                 }
 - 
 -                 for port in canvas.port_list:
 -                     if port.group_id != group.group_id:
 -                         continue
 -                     if port.port_mode != PORT_MODE_INPUT:
 -                         continue
 -                     if port.port_type not in our_port_types:
 -                         continue
 -                     has_ports = True
 -                     target_ports[port.port_type].append((port.group_id, port.port_id))
 - 
 -                 if not has_ports:
 -                     continue
 - 
 -                 act_x_conn = connMenu.addAction(group.group_name)
 -                 act_x_conn.setData((our_port_outs, target_ports))
 -                 act_x_conn.triggered.connect(canvas.qobject.PortContextMenuConnect)
 - 
 -             if act_x_conn is None:
 -                 act_x_disc = connMenu.addAction("Nothing to connect to")
 -                 act_x_disc.setEnabled(False)
 - 
 -         else:
 -             act_x_disc = connMenu.addAction("No output ports")
 -             act_x_disc.setEnabled(False)
 - 
 -         # Disconnect menu stuff
 -         discMenu = QMenu("Disconnect", menu)
 - 
 -         conn_list = []
 -         conn_list_ids = []
 - 
 -         for port_id in self.m_port_list_ids:
 -             tmp_conn_list = CanvasGetPortConnectionList(self.m_group_id, port_id)
 -             for tmp_conn_id, tmp_group_id, tmp_port_id in tmp_conn_list:
 -                 if tmp_conn_id not in conn_list_ids:
 -                     conn_list.append((tmp_conn_id, tmp_group_id, tmp_port_id))
 -                     conn_list_ids.append(tmp_conn_id)
 - 
 -         if len(conn_list) > 0:
 -             for conn_id, group_id, port_id in conn_list:
 -                 act_x_disc = discMenu.addAction(CanvasGetFullPortName(group_id, port_id))
 -                 act_x_disc.setData(conn_id)
 -                 act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect)
 -         else:
 -             act_x_disc = discMenu.addAction("No connections")
 -             act_x_disc.setEnabled(False)
 - 
 -         menu.addMenu(connMenu)
 -         menu.addMenu(discMenu)
 -         act_x_disc_all = menu.addAction("Disconnect &All")
 -         act_x_sep1 = menu.addSeparator()
 -         act_x_info = menu.addAction("Info")
 -         act_x_rename = menu.addAction("Rename")
 -         act_x_sep2 = menu.addSeparator()
 -         act_x_split_join = menu.addAction("Join" if self.m_splitted else "Split")
 - 
 -         if not features.group_info:
 -             act_x_info.setVisible(False)
 - 
 -         if not features.group_rename:
 -             act_x_rename.setVisible(False)
 - 
 -         if not (features.group_info and features.group_rename):
 -             act_x_sep1.setVisible(False)
 - 
 -         if self.m_plugin_id >= 0 and self.m_plugin_id <= MAX_PLUGIN_ID_ALLOWED:
 -             menu.addSeparator()
 -             act_p_edit = menu.addAction("Edit")
 -             act_p_ui = menu.addAction("Show Custom UI")
 -             menu.addSeparator()
 -             act_p_clone = menu.addAction("Clone")
 -             act_p_rename = menu.addAction("Rename...")
 -             act_p_replace = menu.addAction("Replace...")
 -             act_p_remove = menu.addAction("Remove")
 - 
 -             if not self.m_plugin_ui:
 -                 act_p_ui.setVisible(False)
 - 
 -         else:
 -             act_p_edit = act_p_ui = None
 -             act_p_clone = act_p_rename = None
 -             act_p_replace = act_p_remove = None
 - 
 -         haveIns = haveOuts = False
 -         for port in canvas.port_list:
 -             if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids:
 -                 if port.port_mode == PORT_MODE_INPUT:
 -                     haveIns = True
 -                 elif port.port_mode == PORT_MODE_OUTPUT:
 -                     haveOuts = True
 - 
 -         if not (self.m_splitted or bool(haveIns and haveOuts)):
 -             act_x_sep2.setVisible(False)
 -             act_x_split_join.setVisible(False)
 - 
 -         act_selected = menu.exec_(event.screenPos())
 - 
 -         if act_selected is None:
 -             pass
 - 
 -         elif act_selected == act_x_disc_all:
 -             for conn_id in conn_list_ids:
 -                 canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "")
 - 
 -         elif act_selected == act_x_info:
 -             canvas.callback(ACTION_GROUP_INFO, self.m_group_id, 0, "")
 - 
 -         elif act_selected == act_x_rename:
 -             canvas.callback(ACTION_GROUP_RENAME, self.m_group_id, 0, "")
 - 
 -         elif act_selected == act_x_split_join:
 -             if self.m_splitted:
 -                 canvas.callback(ACTION_GROUP_JOIN, self.m_group_id, 0, "")
 -             else:
 -                 canvas.callback(ACTION_GROUP_SPLIT, self.m_group_id, 0, "")
 - 
 -         elif act_selected == act_p_edit:
 -             canvas.callback(ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "")
 - 
 -         elif act_selected == act_p_ui:
 -             canvas.callback(ACTION_PLUGIN_SHOW_UI, self.m_plugin_id, 0, "")
 - 
 -         elif act_selected == act_p_clone:
 -             canvas.callback(ACTION_PLUGIN_CLONE, self.m_plugin_id, 0, "")
 - 
 -         elif act_selected == act_p_rename:
 -             canvas.callback(ACTION_PLUGIN_RENAME, self.m_plugin_id, 0, "")
 - 
 -         elif act_selected == act_p_replace:
 -             canvas.callback(ACTION_PLUGIN_REPLACE, self.m_plugin_id, 0, "")
 - 
 -         elif act_selected == act_p_remove:
 -             canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "")
 - 
 -     def keyPressEvent(self, event):
 -         if self.m_plugin_id >= 0 and event.key() == Qt.Key_Delete:
 -             event.accept()
 -             canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "")
 -             return
 -         QGraphicsItem.keyPressEvent(self, event)
 - 
 -     def hoverEnterEvent(self, event):
 -         if options.auto_select_items:
 -             if len(canvas.scene.selectedItems()) > 0:
 -                 canvas.scene.clearSelection()
 -             self.setSelected(True)
 -         QGraphicsItem.hoverEnterEvent(self, event)
 - 
 -     def mouseDoubleClickEvent(self, event):
 -         if self.m_plugin_id >= 0:
 -             event.accept()
 -             canvas.callback(ACTION_PLUGIN_SHOW_UI if self.m_plugin_ui else ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "")
 -             return
 - 
 -         QGraphicsItem.mouseDoubleClickEvent(self, event)
 - 
 -     def mousePressEvent(self, event):
 -         canvas.last_z_value += 1
 -         self.setZValue(canvas.last_z_value)
 -         self.resetLinesZValue()
 -         self.m_cursor_moving = False
 - 
 -         if event.button() == Qt.RightButton:
 -             event.accept()
 -             canvas.scene.clearSelection()
 -             self.setSelected(True)
 -             self.m_mouse_down = False
 -             return
 - 
 -         elif event.button() == Qt.LeftButton:
 -             if self.sceneBoundingRect().contains(event.scenePos()):
 -                 self.m_mouse_down = True
 -             else:
 -                 # FIXME: Check if still valid: Fix a weird Qt behaviour with right-click mouseMove
 -                 self.m_mouse_down = False
 -                 event.ignore()
 -                 return
 - 
 -         else:
 -             self.m_mouse_down = False
 - 
 -         QGraphicsItem.mousePressEvent(self, event)
 - 
 -     def mouseMoveEvent(self, event):
 -         if self.m_mouse_down:
 -             if not self.m_cursor_moving:
 -                 self.setCursor(QCursor(Qt.SizeAllCursor))
 -                 self.m_cursor_moving = True
 -             self.repaintLines()
 -         QGraphicsItem.mouseMoveEvent(self, event)
 - 
 -     def mouseReleaseEvent(self, event):
 -         if self.m_cursor_moving:
 -             self.unsetCursor()
 -             QTimer.singleShot(0, self.fixPos)
 -         self.m_mouse_down = False
 -         self.m_cursor_moving = False
 -         QGraphicsItem.mouseReleaseEvent(self, event)
 - 
 -     def fixPos(self):
 -         self.setX(round(self.x()))
 -         self.setY(round(self.y()))
 - 
 -     def boundingRect(self):
 -         return QRectF(0, 0, self.p_width, self.p_height)
 - 
 -     def paint(self, painter, option, widget):
 -         painter.save()
 -         painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))
 -         rect = QRectF(0, 0, self.p_width, self.p_height)
 - 
 -         # Draw rectangle
 -         pen = QPen(canvas.theme.box_pen_sel if self.isSelected() else canvas.theme.box_pen)
 -         pen.setWidthF(pen.widthF() + 0.00001)
 -         painter.setPen(pen)
 -         lineHinting = pen.widthF() / 2
 - 
 -         if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT:
 -             box_gradient = QLinearGradient(0, 0, 0, self.p_height)
 -             box_gradient.setColorAt(0, canvas.theme.box_bg_1)
 -             box_gradient.setColorAt(1, canvas.theme.box_bg_2)
 -             painter.setBrush(box_gradient)
 -         else:
 -             painter.setBrush(canvas.theme.box_bg_1)
 - 
 -         rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting)
 -         painter.drawRect(rect)
 - 
 -         # Draw plugin inline display if supported
 -         self.paintInlineDisplay(painter)
 - 
 -         # Draw pixmap header
 -         rect.setHeight(canvas.theme.box_header_height)
 -         if canvas.theme.box_header_pixmap:
 -             painter.setPen(Qt.NoPen)
 -             painter.setBrush(canvas.theme.box_bg_2)
 - 
 -             # outline
 -             rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting)
 -             painter.drawRect(rect)
 - 
 -             rect.adjust(1, 1, -1, 0)
 -             painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, rect.topLeft())
 - 
 -         # Draw text
 -         painter.setFont(self.m_font_name)
 - 
 -         if self.isSelected():
 -             painter.setPen(canvas.theme.box_text_sel)
 -         else:
 -             painter.setPen(canvas.theme.box_text)
 - 
 -         if canvas.theme.box_use_icon:
 -             textPos = QPointF(25, canvas.theme.box_text_ypos)
 -         else:
 -             appNameSize = QFontMetrics(self.m_font_name).width(self.m_group_name)
 -             rem = self.p_width - appNameSize
 -             textPos = QPointF(rem/2, canvas.theme.box_text_ypos)
 - 
 -         painter.drawText(textPos, self.m_group_name)
 - 
 -         self.repaintLines()
 - 
 -         painter.restore()
 - 
 -     def paintInlineDisplay(self, painter):
 -         if self.m_plugin_inline == self.INLINE_DISPLAY_DISABLED:
 -             return
 -         if not options.inline_displays:
 -             return
 - 
 -         inwidth  = self.p_width - self.p_width_in - self.p_width_out - 16
 -         inheight = self.p_height - canvas.theme.box_header_height - canvas.theme.box_header_spacing - canvas.theme.port_spacing - 3
 -         scaling  = canvas.scene.getScaleFactor() * canvas.scene.getDevicePixelRatioF()
 - 
 -         if self.m_plugin_id >= 0 and self.m_plugin_id <= MAX_PLUGIN_ID_ALLOWED and (
 -            self.m_plugin_inline == self.INLINE_DISPLAY_ENABLED or self.m_inline_scaling != scaling):
 -             size = "%i:%i" % (int(inwidth*scaling), int(inheight*scaling))
 -             data = canvas.callback(ACTION_INLINE_DISPLAY, self.m_plugin_id, 0, size)
 -             if data is None:
 -                 return
 - 
 -             # invalidate old image first
 -             del self.m_inline_image
 - 
 -             self.m_inline_data = pack("%iB" % (data['height'] * data['stride']), *data['data'])
 -             self.m_inline_image = QImage(voidptr(self.m_inline_data), data['width'], data['height'], data['stride'], QImage.Format_ARGB32)
 -             self.m_inline_scaling = scaling
 -             self.m_plugin_inline = self.INLINE_DISPLAY_CACHED
 - 
 -         if self.m_inline_image is None:
 -             print("ERROR: inline display image is None for", self.m_plugin_id, self.m_group_name)
 -             return
 - 
 -         swidth = self.m_inline_image.width() / scaling
 -         sheight = self.m_inline_image.height() / scaling
 - 
 -         srcx = int(self.p_width_in + (self.p_width - self.p_width_in - self.p_width_out) / 2 - swidth / 2)
 -         srcy = int(canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1 + (inheight - sheight) / 2)
 - 
 -         painter.drawImage(QRectF(srcx, srcy, swidth, sheight), self.m_inline_image)
 - 
 - # ------------------------------------------------------------------------------------------------------------
 
 
  |