| 
							- #!/usr/bin/env python3
 - # -*- coding: utf-8 -*-
 - 
 - # PatchBay Canvas engine using QGraphicsView/Scene
 - # Copyright (C) 2010-2014 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 (Config)
 - 
 - from carla_config import *
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Global)
 - 
 - if config_UseQt5:
 -     from PyQt5.QtCore import pyqtSignal, pyqtSlot, qCritical, qFatal, qWarning, Qt, QObject
 -     from PyQt5.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer
 -     from PyQt5.QtGui import QColor, QLinearGradient, QPen, QPolygonF, QPainter, QPainterPath
 -     from PyQt5.QtGui import QCursor, QFont, QFontMetrics
 -     from PyQt5.QtSvg import QGraphicsSvgItem, QSvgRenderer
 -     from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem
 -     from PyQt5.QtWidgets import QGraphicsColorizeEffect, QGraphicsDropShadowEffect, QMenu
 - else:
 -     from PyQt4.QtCore import pyqtSignal, pyqtSlot, qCritical, qFatal, qWarning, Qt, QObject
 -     from PyQt4.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer
 -     from PyQt4.QtGui import QColor, QLinearGradient, QPen, QPolygonF, QPainter, QPainterPath
 -     from PyQt4.QtGui import QCursor, QFont, QFontMetrics
 -     from PyQt4.QtGui import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem
 -     from PyQt4.QtGui import QGraphicsColorizeEffect, QGraphicsDropShadowEffect, QMenu
 -     from PyQt4.QtSvg import QGraphicsSvgItem, QSvgRenderer
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Theme)
 - 
 - from patchcanvas_theme import *
 - 
 - # ------------------------------------------------------------------------------
 - # patchcanvas-api.h
 - 
 - # Maximum Id for a plugin, treated as invalid/zero if above this value
 - MAX_PLUGIN_ID_ALLOWED = 0x7FF
 - 
 - # Port Mode
 - PORT_MODE_NULL   = 0
 - PORT_MODE_INPUT  = 1
 - PORT_MODE_OUTPUT = 2
 - 
 - # Port Type
 - PORT_TYPE_NULL       = 0
 - PORT_TYPE_AUDIO_JACK = 1
 - PORT_TYPE_MIDI_JACK  = 2
 - PORT_TYPE_MIDI_ALSA  = 3
 - PORT_TYPE_PARAMETER  = 4
 - 
 - # Callback Action
 - ACTION_GROUP_INFO       =  0 # group_id, N, N
 - ACTION_GROUP_RENAME     =  1 # group_id, N, N
 - ACTION_GROUP_SPLIT      =  2 # group_id, N, N
 - ACTION_GROUP_JOIN       =  3 # group_id, N, N
 - ACTION_PORT_INFO        =  4 # group_id, port_id, N
 - ACTION_PORT_RENAME      =  5 # group_id, port_id, N
 - ACTION_PORTS_CONNECT    =  6 # N, N, "outG:outP:inG:inP"
 - ACTION_PORTS_DISCONNECT =  7 # conn_id, N, N
 - ACTION_PLUGIN_CLONE     =  8 # plugin_id, N, N
 - ACTION_PLUGIN_EDIT      =  9 # plugin_id, N, N
 - ACTION_PLUGIN_RENAME    = 10 # plugin_id, N, N
 - ACTION_PLUGIN_REPLACE   = 11 # plugin_id, N, N
 - ACTION_PLUGIN_REMOVE    = 12 # plugin_id, N, N
 - ACTION_PLUGIN_SHOW_UI   = 13 # plugin_id, N, N
 - ACTION_BG_RIGHT_CLICK   = 14 # N, N, N
 - 
 - # Icon
 - ICON_APPLICATION = 0
 - ICON_HARDWARE    = 1
 - ICON_DISTRHO     = 2
 - ICON_FILE        = 3
 - ICON_PLUGIN      = 4
 - ICON_LADISH_ROOM = 5
 - 
 - # Split Option
 - SPLIT_UNDEF = 0
 - SPLIT_NO    = 1
 - SPLIT_YES   = 2
 - 
 - # Antialiasing Option
 - ANTIALIASING_NONE  = 0
 - ANTIALIASING_SMALL = 1
 - ANTIALIASING_FULL  = 2
 - 
 - # Eye-Candy Option
 - EYECANDY_NONE  = 0
 - EYECANDY_SMALL = 1
 - EYECANDY_FULL  = 2
 - 
 - # Canvas options
 - class options_t(object):
 -     __slots__ = [
 -         'theme_name',
 -         'auto_hide_groups',
 -         'auto_select_items',
 -         'use_bezier_lines',
 -         'antialiasing',
 -         'eyecandy'
 -     ]
 - 
 - # Canvas features
 - class features_t(object):
 -     __slots__ = [
 -         'group_info',
 -         'group_rename',
 -         'port_info',
 -         'port_rename',
 -         'handle_group_pos'
 -     ]
 - 
 - # ------------------------------------------------------------------------------
 - # patchcanvas.h
 - 
 - # object types
 - CanvasBoxType           = QGraphicsItem.UserType + 1
 - CanvasIconType          = QGraphicsItem.UserType + 2
 - CanvasPortType          = QGraphicsItem.UserType + 3
 - CanvasLineType          = QGraphicsItem.UserType + 4
 - CanvasBezierLineType    = QGraphicsItem.UserType + 5
 - CanvasLineMovType       = QGraphicsItem.UserType + 6
 - CanvasBezierLineMovType = QGraphicsItem.UserType + 7
 - 
 - # object lists
 - class group_dict_t(object):
 -     __slots__ = [
 -         'group_id',
 -         'group_name',
 -         'split',
 -         'icon',
 -         'plugin_id',
 -         'plugin_ui',
 -         'widgets'
 -     ]
 - 
 - class port_dict_t(object):
 -     __slots__ = [
 -         'group_id',
 -         'port_id',
 -         'port_name',
 -         'port_mode',
 -         'port_type',
 -         'is_alternate',
 -         'widget'
 -     ]
 - 
 - class connection_dict_t(object):
 -     __slots__ = [
 -         'connection_id',
 -         'group_in_id',
 -         'port_in_id',
 -         'group_out_id',
 -         'port_out_id',
 -         'widget'
 -     ]
 - 
 - class animation_dict_t(object):
 -     __slots__ = [
 -         'animation',
 -         'item'
 -     ]
 - 
 - # Main Canvas object
 - class Canvas(object):
 -     __slots__ = [
 -         'scene',
 -         'callback',
 -         'debug',
 -         'last_z_value',
 -         'last_connection_id',
 -         'initial_pos',
 -         'size_rect',
 -         'group_list',
 -         'port_list',
 -         'connection_list',
 -         'animation_list',
 -         'qobject',
 -         'settings',
 -         'theme',
 -         'initiated'
 -     ]
 - 
 - # ------------------------------------------------------------------------------
 - # patchcanvas.cpp
 - 
 - class CanvasObject(QObject):
 -     def __init__(self, parent=None):
 -         QObject.__init__(self, parent)
 - 
 -     @pyqtSlot()
 -     def AnimationFinishedShow(self):
 -         animation = self.sender()
 -         if animation:
 -             animation.forceStop()
 -             canvas.animation_list.remove(animation)
 - 
 -     @pyqtSlot()
 -     def AnimationFinishedHide(self):
 -         animation = self.sender()
 -         if animation:
 -             animation.forceStop()
 -             canvas.animation_list.remove(animation)
 -             item = animation.item()
 -             if item: item.hide()
 - 
 -     @pyqtSlot()
 -     def AnimationFinishedDestroy(self):
 -         animation = self.sender()
 -         if animation:
 -             animation.forceStop()
 -             canvas.animation_list.remove(animation)
 -             item = animation.item()
 -             if item: CanvasRemoveItemFX(item)
 - 
 -     @pyqtSlot()
 -     def PortContextMenuDisconnect(self):
 -         try:
 -             connectionId = int(self.sender().data())
 -         except:
 -             return
 - 
 -         CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "")
 - 
 - # Global objects
 - canvas = Canvas()
 - canvas.qobject    = None
 - canvas.settings   = None
 - canvas.theme      = None
 - canvas.initiated  = False
 - canvas.group_list = []
 - canvas.port_list  = []
 - canvas.connection_list = []
 - canvas.animation_list  = []
 - 
 - options = options_t()
 - options.theme_name = getDefaultThemeName()
 - options.auto_hide_groups  = False
 - options.auto_select_items = False
 - options.use_bezier_lines  = True
 - options.antialiasing      = ANTIALIASING_SMALL
 - options.eyecandy          = EYECANDY_SMALL
 - 
 - features = features_t()
 - features.group_info   = False
 - features.group_rename = False
 - features.port_info    = False
 - features.port_rename  = False
 - features.handle_group_pos = False
 - 
 - # Internal functions
 - def bool2str(check):
 -     return "True" if check else "False"
 - 
 - def port_mode2str(port_mode):
 -     if port_mode == PORT_MODE_NULL:
 -         return "PORT_MODE_NULL"
 -     elif port_mode == PORT_MODE_INPUT:
 -         return "PORT_MODE_INPUT"
 -     elif port_mode == PORT_MODE_OUTPUT:
 -         return "PORT_MODE_OUTPUT"
 -     else:
 -         return "PORT_MODE_???"
 - 
 - def port_type2str(port_type):
 -     if port_type == PORT_TYPE_NULL:
 -         return "PORT_TYPE_NULL"
 -     elif port_type == PORT_TYPE_AUDIO_JACK:
 -         return "PORT_TYPE_AUDIO_JACK"
 -     elif port_type == PORT_TYPE_MIDI_JACK:
 -         return "PORT_TYPE_MIDI_JACK"
 -     elif port_type == PORT_TYPE_MIDI_ALSA:
 -         return "PORT_TYPE_MIDI_ALSA"
 -     elif port_type == PORT_TYPE_PARAMETER:
 -         return "PORT_TYPE_MIDI_PARAMETER"
 -     else:
 -         return "PORT_TYPE_???"
 - 
 - def icon2str(icon):
 -     if icon == ICON_APPLICATION:
 -         return "ICON_APPLICATION"
 -     elif icon == ICON_HARDWARE:
 -         return "ICON_HARDWARE"
 -     elif icon == ICON_DISTRHO:
 -         return "ICON_DISTRHO"
 -     elif icon == ICON_FILE:
 -         return "ICON_FILE"
 -     elif icon == ICON_PLUGIN:
 -         return "ICON_PLUGIN"
 -     elif icon == ICON_LADISH_ROOM:
 -         return "ICON_LADISH_ROOM"
 -     else:
 -         return "ICON_???"
 - 
 - def split2str(split):
 -     if split == SPLIT_UNDEF:
 -         return "SPLIT_UNDEF"
 -     elif split == SPLIT_NO:
 -         return "SPLIT_NO"
 -     elif split == SPLIT_YES:
 -         return "SPLIT_YES"
 -     else:
 -         return "SPLIT_???"
 - 
 - def getStoredCanvasPosition(key, fallback_pos):
 -     try:
 -         return canvas.settings.value("CanvasPositions/" + key, fallback_pos, type=QPointF)
 -     except:
 -         return fallback_pos
 - 
 - def getStoredCanvasSplit(group_name, fallback_split_mode):
 -     try:
 -         return canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, fallback_split_mode, type=int)
 -     except:
 -         return fallback_split_mode
 - 
 - # PatchCanvas API
 - def setOptions(new_options):
 -     if canvas.initiated: return
 -     options.theme_name        = new_options.theme_name
 -     options.auto_hide_groups  = new_options.auto_hide_groups
 -     options.auto_select_items = new_options.auto_select_items
 -     options.use_bezier_lines  = new_options.use_bezier_lines
 -     options.antialiasing      = new_options.antialiasing
 -     options.eyecandy          = new_options.eyecandy
 - 
 - def setFeatures(new_features):
 -     if canvas.initiated: return
 -     features.group_info   = new_features.group_info
 -     features.group_rename = new_features.group_rename
 -     features.port_info    = new_features.port_info
 -     features.port_rename  = new_features.port_rename
 -     features.handle_group_pos = new_features.handle_group_pos
 - 
 - def init(appName, scene, callback, debug=False):
 -     if debug:
 -         print("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug)))
 - 
 -     if canvas.initiated:
 -         qCritical("PatchCanvas::init() - already initiated")
 -         return
 - 
 -     if not callback:
 -         qFatal("PatchCanvas::init() - fatal error: callback not set")
 -         return
 - 
 -     canvas.scene    = scene
 -     canvas.callback = callback
 -     canvas.debug    = debug
 - 
 -     canvas.last_z_value = 0
 -     canvas.last_connection_id = 0
 -     canvas.initial_pos = QPointF(0, 0)
 -     canvas.size_rect = QRectF()
 - 
 -     if not canvas.qobject:  canvas.qobject = CanvasObject()
 -     if not canvas.settings: canvas.settings = QSettings("falkTX", appName)
 - 
 -     if canvas.theme:
 -         del canvas.theme
 -         canvas.theme = None
 - 
 -     for i in range(Theme.THEME_MAX):
 -         this_theme_name = getThemeName(i)
 -         if this_theme_name == options.theme_name:
 -             canvas.theme = Theme(i)
 -             break
 - 
 -     if not canvas.theme:
 -         canvas.theme = Theme(getDefaultTheme())
 - 
 -     canvas.scene.updateTheme()
 - 
 -     canvas.initiated = True
 - 
 - def clear():
 -     if canvas.debug:
 -         print("PatchCanvas::clear()")
 - 
 -     group_list_ids = []
 -     port_list_ids  = []
 -     connection_list_ids = []
 - 
 -     for group in canvas.group_list:
 -         group_list_ids.append(group.group_id)
 - 
 -     for port in canvas.port_list:
 -         port_list_ids.append((port.group_id, port.port_id))
 - 
 -     for connection in canvas.connection_list:
 -         connection_list_ids.append(connection.connection_id)
 - 
 -     for idx in connection_list_ids:
 -         disconnectPorts(idx)
 - 
 -     for group_id, port_id in port_list_ids:
 -         removePort(group_id, port_id)
 - 
 -     for idx in group_list_ids:
 -         removeGroup(idx)
 - 
 -     canvas.last_z_value = 0
 -     canvas.last_connection_id = 0
 - 
 -     canvas.group_list = []
 -     canvas.port_list = []
 -     canvas.connection_list = []
 - 
 -     canvas.scene.clearSelection()
 - 
 -     animatedItems = []
 -     for animation in canvas.animation_list:
 -         animatedItems.append(animation.item())
 - 
 -     for item in canvas.scene.items():
 -         if item.type() != CanvasIconType and item not in animatedItems:
 -             canvas.scene.removeItem(item)
 -             del item
 - 
 -     canvas.initiated = False
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def setInitialPos(x, y):
 -     if canvas.debug:
 -         print("PatchCanvas::setInitialPos(%i, %i)" % (x, y))
 - 
 -     canvas.initial_pos.setX(x)
 -     canvas.initial_pos.setY(y)
 - 
 - def setCanvasSize(x, y, width, height):
 -     if canvas.debug:
 -         print("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height))
 - 
 -     canvas.size_rect.setX(x)
 -     canvas.size_rect.setY(y)
 -     canvas.size_rect.setWidth(width)
 -     canvas.size_rect.setHeight(height)
 - 
 - def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION):
 -     if canvas.debug:
 -         print("PatchCanvas::addGroup(%i, %s, %s, %s)" % (group_id, group_name.encode(), split2str(split), icon2str(icon)))
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % (group_id, group_name.encode(), split2str(split), icon2str(icon)))
 -             return
 - 
 -     if split == SPLIT_UNDEF:
 -         isHardware = bool(icon == ICON_HARDWARE)
 - 
 -         if features.handle_group_pos:
 -             split = getStoredCanvasSplit(group_name, SPLIT_YES if isHardware else split)
 -         elif isHardware:
 -             split = SPLIT_YES
 - 
 -     group_box = CanvasBox(group_id, group_name, icon)
 - 
 -     group_dict = group_dict_t()
 -     group_dict.group_id = group_id
 -     group_dict.group_name = group_name
 -     group_dict.split = bool(split == SPLIT_YES)
 -     group_dict.icon = icon
 -     group_dict.plugin_id = -1
 -     group_dict.plugin_ui = False
 -     group_dict.widgets = [group_box, None]
 - 
 -     if split == SPLIT_YES:
 -         group_box.setSplit(True, PORT_MODE_OUTPUT)
 - 
 -         if features.handle_group_pos:
 -             group_box.setPos(getStoredCanvasPosition(group_name + "_OUTPUT", CanvasGetNewGroupPos(False)))
 -         else:
 -             group_box.setPos(CanvasGetNewGroupPos(False))
 - 
 -         group_sbox = CanvasBox(group_id, group_name, icon)
 -         group_sbox.setSplit(True, PORT_MODE_INPUT)
 - 
 -         group_dict.widgets[1] = group_sbox
 - 
 -         if features.handle_group_pos:
 -             group_sbox.setPos(getStoredCanvasPosition(group_name + "_INPUT", CanvasGetNewGroupPos(True)))
 -         else:
 -             group_sbox.setPos(CanvasGetNewGroupPos(True))
 - 
 -         canvas.last_z_value += 1
 -         group_sbox.setZValue(canvas.last_z_value)
 - 
 -         if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
 -             CanvasItemFX(group_sbox, True, False)
 - 
 -         group_sbox.checkItemPos()
 - 
 -     else:
 -         group_box.setSplit(False)
 - 
 -         if features.handle_group_pos:
 -             group_box.setPos(getStoredCanvasPosition(group_name, CanvasGetNewGroupPos(False)))
 -         else:
 -             # Special ladish fake-split groups
 -             horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM)
 -             group_box.setPos(CanvasGetNewGroupPos(horizontal))
 - 
 -     group_box.checkItemPos()
 - 
 -     canvas.last_z_value += 1
 -     group_box.setZValue(canvas.last_z_value)
 - 
 -     canvas.group_list.append(group_dict)
 - 
 -     if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
 -         return CanvasItemFX(group_box, True, False)
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def removeGroup(group_id):
 -     if canvas.debug:
 -         print("PatchCanvas::removeGroup(%i)" % group_id)
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             item = group.widgets[0]
 -             group_name = group.group_name
 - 
 -             if group.split:
 -                 s_item = group.widgets[1]
 - 
 -                 if features.handle_group_pos:
 -                     canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos())
 -                     canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos())
 -                     canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES)
 - 
 -                 if options.eyecandy == EYECANDY_FULL:
 -                     CanvasItemFX(s_item, False, True)
 -                 else:
 -                     s_item.removeIconFromScene()
 -                     canvas.scene.removeItem(s_item)
 -                     del s_item
 - 
 -             else:
 -                 if features.handle_group_pos:
 -                     canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos())
 -                     canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO)
 - 
 -             if options.eyecandy == EYECANDY_FULL:
 -                 CanvasItemFX(item, False, True)
 -             else:
 -                 item.removeIconFromScene()
 -                 canvas.scene.removeItem(item)
 -                 del item
 - 
 -             canvas.group_list.remove(group)
 - 
 -             QTimer.singleShot(0, canvas.scene.update)
 -             return
 - 
 -     qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id)
 - 
 - def renameGroup(group_id, new_group_name):
 -     if canvas.debug:
 -         print("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode()))
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             group.group_name = new_group_name
 -             group.widgets[0].setGroupName(new_group_name)
 - 
 -             if group.split and group.widgets[1]:
 -                 group.widgets[1].setGroupName(new_group_name)
 - 
 -             QTimer.singleShot(0, canvas.scene.update)
 -             return
 - 
 -     qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode()))
 - 
 - def splitGroup(group_id):
 -     if canvas.debug:
 -         print("PatchCanvas::splitGroup(%i)" % group_id)
 - 
 -     item = None
 -     group_name = ""
 -     group_icon = ICON_APPLICATION
 -     plugin_id  = -1
 -     plugin_ui  = False
 -     ports_data = []
 -     conns_data = []
 - 
 -     # Step 1 - Store all Item data
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             if group.split:
 -                 qCritical("PatchCanvas::splitGroup(%i) - group is already splitted" % group_id)
 -                 return
 - 
 -             item = group.widgets[0]
 -             group_name = group.group_name
 -             group_icon = group.icon
 -             plugin_id  = group.plugin_id
 -             plugin_ui  = group.plugin_ui
 -             break
 - 
 -     if not item:
 -         qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id)
 -         return
 - 
 -     port_list_ids = list(item.getPortList())
 - 
 -     for port in canvas.port_list:
 -         if port.port_id in port_list_ids:
 -             port_dict = port_dict_t()
 -             port_dict.group_id = port.group_id
 -             port_dict.port_id = port.port_id
 -             port_dict.port_name = port.port_name
 -             port_dict.port_mode = port.port_mode
 -             port_dict.port_type = port.port_type
 -             port_dict.is_alternate = port.is_alternate
 -             port_dict.widget = None
 -             ports_data.append(port_dict)
 - 
 -     for connection in canvas.connection_list:
 -         if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
 -             connection_dict = connection_dict_t()
 -             connection_dict.connection_id = connection.connection_id
 -             connection_dict.group_in_id = connection.group_in_id
 -             connection_dict.port_in_id = connection.port_in_id
 -             connection_dict.group_out_id = connection.group_out_id
 -             connection_dict.port_out_id = connection.port_out_id
 -             connection_dict.widget = None
 -             conns_data.append(connection_dict)
 - 
 -     # Step 2 - Remove Item and Children
 -     for conn in conns_data:
 -         disconnectPorts(conn.connection_id)
 - 
 -     for port_id in port_list_ids:
 -         removePort(group_id, port_id)
 - 
 -     removeGroup(group_id)
 - 
 -     # Step 3 - Re-create Item, now splitted
 -     addGroup(group_id, group_name, SPLIT_YES, group_icon)
 - 
 -     if plugin_id >= 0:
 -         setGroupAsPlugin(group_id, plugin_id, plugin_ui)
 - 
 -     for port in ports_data:
 -         addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
 - 
 -     for conn in conns_data:
 -         connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id)
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def joinGroup(group_id):
 -     if canvas.debug:
 -         print("PatchCanvas::joinGroup(%i)" % group_id)
 - 
 -     item   = None
 -     s_item = None
 -     group_name = ""
 -     group_icon = ICON_APPLICATION
 -     plugin_id  = -1
 -     plugin_ui  = False
 -     ports_data = []
 -     conns_data = []
 - 
 -     # Step 1 - Store all Item data
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             if not group.split:
 -                 qCritical("PatchCanvas::joinGroup(%i) - group is not splitted" % group_id)
 -                 return
 - 
 -             item = group.widgets[0]
 -             s_item = group.widgets[1]
 -             group_name = group.group_name
 -             group_icon = group.icon
 -             plugin_id  = group.plugin_id
 -             plugin_ui  = group.plugin_ui
 -             break
 - 
 -     # FIXME
 -     if not (item and s_item):
 -         qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id)
 -         return
 - 
 -     port_list_ids = list(item.getPortList())
 -     port_list_idss = s_item.getPortList()
 - 
 -     for port_id in port_list_idss:
 -         if port_id not in port_list_ids:
 -             port_list_ids.append(port_id)
 - 
 -     for port in canvas.port_list:
 -         if port.port_id in port_list_ids:
 -             port_dict = port_dict_t()
 -             port_dict.group_id = port.group_id
 -             port_dict.port_id = port.port_id
 -             port_dict.port_name = port.port_name
 -             port_dict.port_mode = port.port_mode
 -             port_dict.port_type = port.port_type
 -             port_dict.is_alternate = port.is_alternate
 -             port_dict.widget = None
 -             ports_data.append(port_dict)
 - 
 -     for connection in canvas.connection_list:
 -         if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
 -             connection_dict = connection_dict_t()
 -             connection_dict.connection_id = connection.connection_id
 -             connection_dict.group_in_id = connection.group_in_id
 -             connection_dict.port_in_id = connection.port_in_id
 -             connection_dict.group_out_id = connection.group_out_id
 -             connection_dict.port_out_id = connection.port_out_id
 -             connection_dict.widget = None
 -             conns_data.append(connection_dict)
 - 
 -     # Step 2 - Remove Item and Children
 -     for conn in conns_data:
 -         disconnectPorts(conn.connection_id)
 - 
 -     for port_id in port_list_ids:
 -         removePort(group_id, port_id)
 - 
 -     removeGroup(group_id)
 - 
 -     # Step 3 - Re-create Item, now together
 -     addGroup(group_id, group_name, SPLIT_NO, group_icon)
 - 
 -     if plugin_id >= 0:
 -         setGroupAsPlugin(group_id, plugin_id, plugin_ui)
 - 
 -     for port in ports_data:
 -         addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
 - 
 -     for conn in conns_data:
 -         connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id)
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
 -     if canvas.debug:
 -         print("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode)))
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             if group.split:
 -                 if port_mode == PORT_MODE_OUTPUT:
 -                     return group.widgets[0].pos()
 -                 elif port_mode == PORT_MODE_INPUT:
 -                     return group.widgets[1].pos()
 -                 else:
 -                     return QPointF(0, 0)
 -             else:
 -                 return group.widgets[0].pos()
 - 
 -     qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
 -     return QPointF(0, 0)
 - 
 - def saveGroupPositions():
 -     if canvas.debug:
 -         print("PatchCanvas::getGroupPositions()")
 - 
 -     ret = []
 - 
 -     for group in canvas.group_list:
 -         if group.split:
 -             pos1 = group.widgets[0].pos()
 -             pos2 = group.widgets[1].pos()
 -         else:
 -             pos1 = group.widgets[0].pos()
 -             pos2 = QPointF(0, 0)
 - 
 -         ret.append({
 -             "name" : group.group_name,
 -             "pos1x": pos1.x(),
 -             "pos1y": pos1.y(),
 -             "pos2x": pos2.x(),
 -             "pos2y": pos2.y(),
 -             "split": group.split,
 -         })
 - 
 -     return ret
 - 
 - def restoreGroupPositions(dataList):
 -     if canvas.debug:
 -         print("PatchCanvas::restoreGroupPositions(...)")
 - 
 -     map = {}
 - 
 -     for group in canvas.group_list:
 -         map[group.group_name] = group
 - 
 -     for data in dataList:
 -         name  = data['name']
 -         group = map.get(name, None)
 - 
 -         if group is None:
 -             continue
 - 
 -         group.widgets[0].setPos(data['pos1x'], data['pos1y'])
 - 
 -         if group.split and group.widgets[1]:
 -             group.widgets[1].setPos(data['pos2x'], data['pos2y'])
 - 
 - def setGroupPos(group_id, group_pos_x, group_pos_y):
 -     setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y)
 - 
 - def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i):
 -     if canvas.debug:
 -         print("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             group.widgets[0].setPos(group_pos_x_o, group_pos_y_o)
 - 
 -             if group.split and group.widgets[1]:
 -                 group.widgets[1].setPos(group_pos_x_i, group_pos_y_i)
 - 
 -             QTimer.singleShot(0, canvas.scene.update)
 -             return
 - 
 -     qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
 - 
 - def setGroupIcon(group_id, icon):
 -     if canvas.debug:
 -         print("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon)))
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             group.icon = icon
 -             group.widgets[0].setIcon(icon)
 - 
 -             if group.split and group.widgets[1]:
 -                 group.widgets[1].setIcon(icon)
 - 
 -             QTimer.singleShot(0, canvas.scene.update)
 -             return
 - 
 -     qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon)))
 - 
 - def setGroupAsPlugin(group_id, plugin_id, hasUi):
 -     if canvas.debug:
 -         print("PatchCanvas::setGroupAsPlugin(%i, %i, %s)" % (group_id, plugin_id, bool2str(hasUi)))
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             group.plugin_id = plugin_id
 -             group.plugin_ui = hasUi
 -             group.widgets[0].setAsPlugin(plugin_id, hasUi)
 - 
 -             if group.split and group.widgets[1]:
 -                 group.widgets[1].setAsPlugin(plugin_id, hasUi)
 -             return
 - 
 -     qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s) - unable to find group to set as plugin" % (group_id, plugin_id, bool2str(hasUi)))
 - 
 - def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False):
 -     if canvas.debug:
 -         print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate)))
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group_id and port.port_id == port_id:
 -             qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
 -             return
 - 
 -     box_widget  = None
 -     port_widget = None
 - 
 -     for group in canvas.group_list:
 -         if group.group_id == group_id:
 -             if group.split and group.widgets[0].getSplittedMode() != port_mode and group.widgets[1]:
 -                 n = 1
 -             else:
 -                 n = 0
 -             box_widget  = group.widgets[n]
 -             port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate)
 -             break
 - 
 -     if not (box_widget and port_widget):
 -         qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
 -         return
 - 
 -     port_dict = port_dict_t()
 -     port_dict.group_id  = 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 = port_widget
 -     canvas.port_list.append(port_dict)
 - 
 -     box_widget.updatePositions()
 - 
 -     if options.eyecandy == EYECANDY_FULL:
 -         return CanvasItemFX(port_widget, True, False)
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def removePort(group_id, port_id):
 -     if canvas.debug:
 -         print("PatchCanvas::removePort(%i, %i)" % (group_id, port_id))
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group_id and port.port_id == port_id:
 -             item = port.widget
 -             item.parentItem().removePortFromGroup(port_id)
 -             canvas.scene.removeItem(item)
 -             canvas.port_list.remove(port)
 -             del item
 - 
 -             QTimer.singleShot(0, canvas.scene.update)
 -             return
 - 
 -     qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id))
 - 
 - def renamePort(group_id, port_id, new_port_name):
 -     if canvas.debug:
 -         print("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode()))
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group_id and port.port_id == port_id:
 -             port.port_name = new_port_name
 -             port.widget.setPortName(new_port_name)
 -             port.widget.parentItem().updatePositions()
 - 
 -             QTimer.singleShot(0, canvas.scene.update)
 -             return
 - 
 -     qCritical("PatchCanvas::renamePort(%i, %i, %s) - Unable to find port to rename" % (group_id, port_id, new_port_name.encode()))
 - 
 - def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id):
 -     if canvas.debug:
 -         print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % (connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
 - 
 -     port_out = None
 -     port_in  = None
 -     port_out_parent = None
 -     port_in_parent  = None
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group_out_id and port.port_id == port_out_id:
 -             port_out = port.widget
 -             port_out_parent = port_out.parentItem()
 -         elif port.group_id == group_in_id and port.port_id == port_in_id:
 -             port_in = port.widget
 -             port_in_parent = port_in.parentItem()
 - 
 -     # FIXME
 -     if not (port_out and port_in):
 -         qCritical("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - unable to find ports to connect" % (connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
 -         return
 - 
 -     connection_dict = connection_dict_t()
 -     connection_dict.connection_id = connection_id
 -     connection_dict.group_in_id = group_in_id
 -     connection_dict.port_in_id = port_in_id
 -     connection_dict.group_out_id = group_out_id
 -     connection_dict.port_out_id = port_out_id
 - 
 -     if options.use_bezier_lines:
 -         connection_dict.widget = CanvasBezierLine(port_out, port_in, None)
 -     else:
 -         connection_dict.widget = CanvasLine(port_out, port_in, None)
 - 
 -     canvas.scene.addItem(connection_dict.widget)
 - 
 -     port_out_parent.addLineFromGroup(connection_dict.widget, connection_id)
 -     port_in_parent.addLineFromGroup(connection_dict.widget, connection_id)
 - 
 -     canvas.last_z_value += 1
 -     port_out_parent.setZValue(canvas.last_z_value)
 -     port_in_parent.setZValue(canvas.last_z_value)
 - 
 -     canvas.last_z_value += 1
 -     connection_dict.widget.setZValue(canvas.last_z_value)
 - 
 -     canvas.connection_list.append(connection_dict)
 - 
 -     if options.eyecandy == EYECANDY_FULL:
 -         item = connection_dict.widget
 -         return CanvasItemFX(item, True, False)
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def disconnectPorts(connection_id):
 -     if canvas.debug:
 -         print("PatchCanvas::disconnectPorts(%i)" % connection_id)
 - 
 -     line  = None
 -     item1 = None
 -     item2 = None
 -     group1id = port1id = 0
 -     group2id = port2id = 0
 - 
 -     for connection in canvas.connection_list:
 -         if connection.connection_id == connection_id:
 -             group1id = connection.group_out_id
 -             group2id = connection.group_in_id
 -             port1id = connection.port_out_id
 -             port2id = connection.port_in_id
 -             line = connection.widget
 -             canvas.connection_list.remove(connection)
 -             break
 - 
 -     if not line:
 -         qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id)
 -         return
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group1id and port.port_id == port1id:
 -             item1 = port.widget
 -             break
 - 
 -     if not item1:
 -         qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id)
 -         return
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group2id and port.port_id == port2id:
 -             item2 = port.widget
 -             break
 - 
 -     if not item2:
 -         qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id)
 -         return
 - 
 -     item1.parentItem().removeLineFromGroup(connection_id)
 -     item2.parentItem().removeLineFromGroup(connection_id)
 - 
 -     if options.eyecandy == EYECANDY_FULL:
 -         return CanvasItemFX(line, False, True)
 - 
 -     canvas.scene.removeItem(line)
 -     del line
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - def arrange():
 -     if canvas.debug:
 -         print("PatchCanvas::arrange()")
 - 
 - def updateZValues():
 -     if canvas.debug:
 -         print("PatchCanvas::updateZValues()")
 - 
 -     for group in canvas.group_list:
 -         group.widgets[0].resetLinesZValue()
 - 
 -         if group.split and group.widgets[1]:
 -             group.widgets[1].resetLinesZValue()
 - 
 - def handlePluginRemoved(plugin_id):
 -     if canvas.debug:
 -         print("PatchCanvas::handlePluginRemoved(%i)" % plugin_id)
 - 
 -     for group in canvas.group_list:
 -         if group.plugin_id < plugin_id or group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
 -             continue
 - 
 -         group.plugin_id -= 1
 -         group.widgets[0].m_plugin_id -= 1
 - 
 -         if group.split and group.widgets[1]:
 -             group.widgets[1].m_plugin_id -= 1
 - 
 - def handleAllPluginsRemoved():
 -     if canvas.debug:
 -         print("PatchCanvas::handleAllPluginsRemoved()")
 - 
 -     for group in canvas.group_list:
 -         if group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
 -             continue
 - 
 -         group.plugin_id = -1
 -         group.plugin_ui = False
 -         group.widgets[0].m_plugin_id = -1
 -         group.widgets[0].m_plugin_ui = False
 - 
 -         if group.split and group.widgets[1]:
 -             group.widgets[1].m_plugin_id = -1
 -             group.widgets[1].m_plugin_ui = False
 - 
 - # Extra Internal functions
 - 
 - def CanvasGetNewGroupPos(horizontal):
 -     if canvas.debug:
 -         print("PatchCanvas::CanvasGetNewGroupPos(%s)" % bool2str(horizontal))
 - 
 -     new_pos = QPointF(canvas.initial_pos.x(), canvas.initial_pos.y())
 -     items = canvas.scene.items()
 - 
 -     break_loop = False
 -     while not break_loop:
 -         break_for = False
 -         for i in range(len(items)):
 -             item = items[i]
 -             if item and item.type() == CanvasBoxType:
 -                 if item.sceneBoundingRect().contains(new_pos):
 -                     if horizontal:
 -                         new_pos += QPointF(item.boundingRect().width() + 15, 0)
 -                     else:
 -                         new_pos += QPointF(0, item.boundingRect().height() + 15)
 -                     break_for = True
 -                     break
 - 
 -             if i >= len(items) - 1 and not break_for:
 -                 break_loop = True
 - 
 -     return new_pos
 - 
 - def CanvasGetFullPortName(group_id, port_id):
 -     if canvas.debug:
 -         print("PatchCanvas::CanvasGetFullPortName(%i, %i)" % (group_id, port_id))
 - 
 -     for port in canvas.port_list:
 -         if port.group_id == group_id and port.port_id == port_id:
 -             group_id = port.group_id
 -             for group in canvas.group_list:
 -                 if group.group_id == group_id:
 -                     return group.group_name + ":" + port.port_name
 -             break
 - 
 -     qCritical("PatchCanvas::CanvasGetFullPortName(%i, %i) - unable to find port" % (group_id, port_id))
 -     return ""
 - 
 - def CanvasGetPortConnectionList(group_id, port_id):
 -     if canvas.debug:
 -         print("PatchCanvas::CanvasGetPortConnectionList(%i, %i)" % (group_id, port_id))
 - 
 -     conn_list = []
 - 
 -     for connection in canvas.connection_list:
 -         if connection.group_out_id == group_id and connection.port_out_id == port_id:
 -             conn_list.append((connection.connection_id, connection.group_in_id, connection.port_in_id))
 -         elif connection.group_in_id == group_id and connection.port_in_id == port_id:
 -             conn_list.append((connection.connection_id, connection.group_out_id, connection.port_out_id))
 - 
 -     return conn_list
 - 
 - def CanvasCallback(action, value1, value2, value_str):
 -     if canvas.debug:
 -         print("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode()))
 - 
 -     canvas.callback(action, value1, value2, value_str)
 - 
 - def CanvasItemFX(item, show, destroy):
 -     if canvas.debug:
 -         print("PatchCanvas::CanvasItemFX(%s, %s, %s)" % (item, bool2str(show), bool2str(destroy)))
 - 
 -     # Check if the item already has an animation
 -     for animation in canvas.animation_list:
 -         if animation.item() == item:
 -             animation.forceStop()
 -             canvas.animation_list.remove(animation)
 -             del animation
 -             break
 - 
 -     animation = CanvasFadeAnimation(item, show)
 -     animation.setDuration(750 if show else 500)
 - 
 -     if show:
 -         animation.finished.connect(canvas.qobject.AnimationFinishedShow)
 -     else:
 -         if destroy:
 -             animation.finished.connect(canvas.qobject.AnimationFinishedDestroy)
 -         else:
 -             animation.finished.connect(canvas.qobject.AnimationFinishedHide)
 - 
 -     canvas.animation_list.append(animation)
 - 
 -     animation.start()
 - 
 - def CanvasRemoveItemFX(item):
 -     if canvas.debug:
 -         print("PatchCanvas::CanvasRemoveItemFX(%s)" % item)
 - 
 -     if item.type() == CanvasBoxType:
 -         item.removeIconFromScene()
 - 
 -     canvas.scene.removeItem(item)
 -     del item
 - 
 -     QTimer.singleShot(0, canvas.scene.update)
 - 
 - # ------------------------------------------------------------------------------
 - # patchscene.cpp
 - 
 - class PatchScene(QGraphicsScene):
 -     scaleChanged    = pyqtSignal(float)
 -     sceneGroupMoved = pyqtSignal(int, int, QPointF)
 -     pluginSelected  = pyqtSignal(list)
 - 
 -     def __init__(self, parent, view):
 -         QGraphicsScene.__init__(self, parent)
 - 
 -         self.m_ctrl_down = False
 -         self.m_mouse_down_init = False
 -         self.m_mouse_rubberband = False
 - 
 -         self.addRubberBand()
 - 
 -         self.m_view = view
 -         if not self.m_view:
 -             qFatal("PatchCanvas::PatchScene() - invalid view")
 - 
 -         self.selectionChanged.connect(self.slot_selectionChanged)
 - 
 -     def addRubberBand(self):
 -         self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0))
 -         self.m_rubberband.setZValue(-1)
 -         self.m_rubberband.hide()
 -         self.m_rubberband_selection = False
 -         self.m_rubberband_orig_point = QPointF(0, 0)
 - 
 -     def clear(self):
 -         QGraphicsScene.clear(self)
 - 
 -         # Re-add rubberband, that just got deleted
 -         self.addRubberBand()
 - 
 -     def fixScaleFactor(self):
 -         scale = self.m_view.transform().m11()
 -         if scale > 3.0:
 -             self.m_view.resetTransform()
 -             self.m_view.scale(3.0, 3.0)
 -         elif scale < 0.2:
 -             self.m_view.resetTransform()
 -             self.m_view.scale(0.2, 0.2)
 -         self.scaleChanged.emit(self.m_view.transform().m11())
 - 
 -     def updateTheme(self):
 -         self.setBackgroundBrush(canvas.theme.canvas_bg)
 -         self.m_rubberband.setPen(canvas.theme.rubberband_pen)
 -         self.m_rubberband.setBrush(canvas.theme.rubberband_brush)
 - 
 -     def zoom_fit(self):
 -         min_x = min_y = max_x = max_y = None
 -         first_value = True
 - 
 -         items_list = self.items()
 - 
 -         if len(items_list) > 0:
 -             for item in items_list:
 -                 if item and item.isVisible() and item.type() == CanvasBoxType:
 -                     pos = item.scenePos()
 -                     rect = item.boundingRect()
 - 
 -                     if first_value:
 -                         min_x = pos.x()
 -                     elif pos.x() < min_x:
 -                         min_x = pos.x()
 - 
 -                     if first_value:
 -                         min_y = pos.y()
 -                     elif pos.y() < min_y:
 -                         min_y = pos.y()
 - 
 -                     if first_value:
 -                         max_x = pos.x() + rect.width()
 -                     elif pos.x() + rect.width() > max_x:
 -                         max_x = pos.x() + rect.width()
 - 
 -                     if first_value:
 -                         max_y = pos.y() + rect.height()
 -                     elif pos.y() + rect.height() > max_y:
 -                         max_y = pos.y() + rect.height()
 - 
 -                     first_value = False
 - 
 -             if not first_value:
 -                 self.m_view.fitInView(min_x, min_y, abs(max_x - min_x), abs(max_y - min_y), Qt.KeepAspectRatio)
 -                 self.fixScaleFactor()
 - 
 -     def zoom_in(self):
 -         if self.m_view.transform().m11() < 3.0:
 -             self.m_view.scale(1.2, 1.2)
 -         self.scaleChanged.emit(self.m_view.transform().m11())
 - 
 -     def zoom_out(self):
 -         if self.m_view.transform().m11() > 0.2:
 -             self.m_view.scale(0.8, 0.8)
 -         self.scaleChanged.emit(self.m_view.transform().m11())
 - 
 -     def zoom_reset(self):
 -         self.m_view.resetTransform()
 -         self.scaleChanged.emit(1.0)
 - 
 -     @pyqtSlot()
 -     def slot_selectionChanged(self):
 -         items_list = self.selectedItems()
 - 
 -         if len(items_list) == 0:
 -             self.pluginSelected.emit([])
 -             return
 - 
 -         plugin_list = []
 - 
 -         for item in items_list:
 -             if item and item.isVisible():
 -                 group_item = None
 - 
 -                 if item.type() == CanvasBoxType:
 -                     group_item = item
 -                 elif item.type() == CanvasPortType:
 -                     group_item = item.parentItem()
 -                 #elif item.type() in (CanvasLineType, CanvasBezierLineType, CanvasLineMovType, CanvasBezierLineMovType):
 -                     #plugin_list = []
 -                     #break
 - 
 -                 if group_item is not None and group_item.m_plugin_id >= 0:
 -                     plugin_id = group_item.m_plugin_id
 -                     if plugin_id > MAX_PLUGIN_ID_ALLOWED:
 -                         plugin_id = 0
 -                     plugin_list.append(plugin_id)
 - 
 -         self.pluginSelected.emit(plugin_list)
 - 
 -     def keyPressEvent(self, event):
 -         if not self.m_view:
 -             return event.ignore()
 - 
 -         if event.key() == Qt.Key_Control:
 -             self.m_ctrl_down = True
 - 
 -         elif event.key() == Qt.Key_Home:
 -             self.zoom_fit()
 -             return event.accept()
 - 
 -         elif self.m_ctrl_down:
 -             if event.key() == Qt.Key_Plus:
 -                 self.zoom_in()
 -                 return event.accept()
 -             elif event.key() == Qt.Key_Minus:
 -                 self.zoom_out()
 -                 return event.accept()
 -             elif event.key() == Qt.Key_1:
 -                 self.zoom_reset()
 -                 return event.accept()
 - 
 -         QGraphicsScene.keyPressEvent(self, event)
 - 
 -     def keyReleaseEvent(self, event):
 -         if event.key() == Qt.Key_Control:
 -             self.m_ctrl_down = False
 -         QGraphicsScene.keyReleaseEvent(self, event)
 - 
 -     def mousePressEvent(self, event):
 -         self.m_mouse_down_init  = bool(event.button() == Qt.LeftButton)
 -         self.m_mouse_rubberband = False
 -         QGraphicsScene.mousePressEvent(self, event)
 - 
 -     def mouseMoveEvent(self, event):
 -         if self.m_mouse_down_init:
 -             self.m_mouse_down_init  = False
 -             self.m_mouse_rubberband = bool(len(self.selectedItems()) == 0)
 - 
 -         if self.m_mouse_rubberband:
 -             if not self.m_rubberband_selection:
 -                 self.m_rubberband.show()
 -                 self.m_rubberband_selection = True
 -                 self.m_rubberband_orig_point = event.scenePos()
 - 
 -             pos = event.scenePos()
 - 
 -             if pos.x() > self.m_rubberband_orig_point.x():
 -                 x = self.m_rubberband_orig_point.x()
 -             else:
 -                 x = pos.x()
 - 
 -             if pos.y() > self.m_rubberband_orig_point.y():
 -                 y = self.m_rubberband_orig_point.y()
 -             else:
 -                 y = pos.y()
 - 
 -             self.m_rubberband.setRect(x, y, abs(pos.x() - self.m_rubberband_orig_point.x()), abs(pos.y() - self.m_rubberband_orig_point.y()))
 -             return event.accept()
 - 
 -         QGraphicsScene.mouseMoveEvent(self, event)
 - 
 -     def mouseReleaseEvent(self, event):
 -         if self.m_rubberband_selection:
 -             items_list = self.items()
 -             if len(items_list) > 0:
 -                 for item in items_list:
 -                     if item and item.isVisible() and item.type() == CanvasBoxType:
 -                         item_rect = item.sceneBoundingRect()
 -                         item_top_left = QPointF(item_rect.x(), item_rect.y())
 -                         item_bottom_right = QPointF(item_rect.x() + item_rect.width(), item_rect.y() + item_rect.height())
 - 
 -                         if self.m_rubberband.contains(item_top_left) and self.m_rubberband.contains(item_bottom_right):
 -                             item.setSelected(True)
 - 
 -                 self.m_rubberband.hide()
 -                 self.m_rubberband.setRect(0, 0, 0, 0)
 -                 self.m_rubberband_selection = False
 - 
 -         else:
 -             items_list = self.selectedItems()
 -             for item in items_list:
 -                 if item and item.isVisible() and item.type() == CanvasBoxType:
 -                     item.checkItemPos()
 -                     self.sceneGroupMoved.emit(item.getGroupId(), item.getSplittedMode(), item.scenePos())
 - 
 -             if len(items_list) > 1:
 -                 canvas.scene.update()
 - 
 -         self.m_mouse_down_init  = False
 -         self.m_mouse_rubberband = False
 -         QGraphicsScene.mouseReleaseEvent(self, event)
 - 
 -     def wheelEvent(self, event):
 -         if not self.m_view:
 -             return event.ignore()
 - 
 -         if self.m_ctrl_down:
 -             factor = 1.41 ** (event.delta() / 240.0)
 -             self.m_view.scale(factor, factor)
 - 
 -             self.fixScaleFactor()
 -             return event.accept()
 - 
 -         QGraphicsScene.wheelEvent(self, event)
 - 
 -     def contextMenuEvent(self, event):
 -         if len(self.selectedItems()) == 0:
 -             event.accept()
 -             canvas.callback(ACTION_BG_RIGHT_CLICK, 0, 0, "")
 -         else:
 -             QGraphicsScene.contextMenuEvent(self, event)
 - 
 - # ------------------------------------------------------------------------------
 - # canvasfadeanimation.cpp
 - 
 - class CanvasFadeAnimation(QAbstractAnimation):
 -     def __init__(self, item, show):
 -         QAbstractAnimation.__init__(self)
 - 
 -         self.m_show = show
 -         self.m_duration = 0
 -         self.m_item = item
 - 
 -     def item(self):
 -         return self.m_item
 - 
 -     def forceStop(self):
 -         self.blockSignals(True)
 -         self.stop()
 - 
 -     def setDuration(self, time):
 -         if self.m_item.opacity() == 0 and not self.m_show:
 -             self._duration = 0
 -         else:
 -             self.m_item.show()
 -             self.m_duration = time
 - 
 -     def duration(self):
 -         return self.m_duration
 - 
 -     def updateCurrentTime(self, time):
 -         if self.m_duration == 0:
 -             return
 - 
 -         if self.m_show:
 -             value = float(time) / self.m_duration
 -         else:
 -             value = 1.0 - (float(time) / self.m_duration)
 - 
 -         try:
 -             self.m_item.setOpacity(value)
 -         except:
 -             print("Error: failed to animate canvas item, already destroyed?")
 -             self.forceStop()
 -             canvas.animation_list.remove(self)
 -             return
 - 
 -         if self.m_item.type() == CanvasBoxType:
 -             self.m_item.setShadowOpacity(value)
 - 
 -     def updateDirection(self, direction):
 -         pass
 - 
 -     def updateState(self, oldState, newState):
 -         pass
 - 
 - # ------------------------------------------------------------------------------
 - # canvasline.cpp
 - 
 - class CanvasLine(QGraphicsLineItem):
 -     def __init__(self, item1, item2, parent):
 -         QGraphicsLineItem.__init__(self, parent)
 - 
 -         self.item1 = item1
 -         self.item2 = item2
 - 
 -         self.m_locked = False
 -         self.m_lineSelected = False
 - 
 -         self.setGraphicsEffect(None)
 -         self.updateLinePos()
 - 
 -     def isLocked(self):
 -         return self.m_locked
 - 
 -     def setLocked(self, yesno):
 -         self.m_locked = yesno
 - 
 -     def isLineSelected(self):
 -         return self.m_lineSelected
 - 
 -     def setLineSelected(self, yesno):
 -         if self.m_locked:
 -             return
 - 
 -         if options.eyecandy == EYECANDY_FULL:
 -             if yesno:
 -                 self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject()))
 -             else:
 -                 self.setGraphicsEffect(None)
 - 
 -         self.m_lineSelected = yesno
 -         self.updateLineGradient()
 - 
 -     def updateLinePos(self):
 -         if self.item1.getPortMode() == PORT_MODE_OUTPUT:
 -             line = QLineF(self.item1.scenePos().x() + self.item1.getPortWidth() + 12,
 -                           self.item1.scenePos().y() + float(canvas.theme.port_height)/2,
 -                           self.item2.scenePos().x(),
 -                           self.item2.scenePos().y() + float(canvas.theme.port_height)/2)
 -             self.setLine(line)
 - 
 -             self.m_lineSelected = False
 -             self.updateLineGradient()
 - 
 -     def type(self):
 -         return CanvasLineType
 - 
 -     def updateLineGradient(self):
 -         pos_top = self.boundingRect().top()
 -         pos_bot = self.boundingRect().bottom()
 -         if self.item2.scenePos().y() >= self.item1.scenePos().y():
 -             pos1 = 0
 -             pos2 = 1
 -         else:
 -             pos1 = 1
 -             pos2 = 0
 - 
 -         port_type1 = self.item1.getPortType()
 -         port_type2 = self.item2.getPortType()
 -         port_gradient = QLinearGradient(0, pos_top, 0, pos_bot)
 - 
 -         if port_type1 == PORT_TYPE_AUDIO_JACK:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
 -         elif port_type1 == PORT_TYPE_MIDI_JACK:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
 -         elif port_type1 == PORT_TYPE_MIDI_ALSA:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
 -         elif port_type1 == PORT_TYPE_PARAMETER:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
 - 
 -         if port_type2 == PORT_TYPE_AUDIO_JACK:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
 -         elif port_type2 == PORT_TYPE_MIDI_JACK:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
 -         elif port_type2 == PORT_TYPE_MIDI_ALSA:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
 -         elif port_type2 == PORT_TYPE_PARAMETER:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
 - 
 -         self.setPen(QPen(port_gradient, 2))
 - 
 -     def paint(self, painter, option, widget):
 -         painter.save()
 -         painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
 -         QGraphicsLineItem.paint(self, painter, option, widget)
 -         painter.restore()
 - 
 - # ------------------------------------------------------------------------------
 - # canvasbezierline.cpp
 - 
 - class CanvasBezierLine(QGraphicsPathItem):
 -     def __init__(self, item1, item2, parent):
 -         QGraphicsPathItem.__init__(self, parent)
 - 
 -         self.item1 = item1
 -         self.item2 = item2
 - 
 -         self.m_locked = False
 -         self.m_lineSelected = False
 - 
 -         self.setBrush(QColor(0, 0, 0, 0))
 -         self.setGraphicsEffect(None)
 -         self.updateLinePos()
 - 
 -     def isLocked(self):
 -         return self.m_locked
 - 
 -     def setLocked(self, yesno):
 -         self.m_locked = yesno
 - 
 -     def isLineSelected(self):
 -         return self.m_lineSelected
 - 
 -     def setLineSelected(self, yesno):
 -         if self.m_locked:
 -             return
 - 
 -         if options.eyecandy == EYECANDY_FULL:
 -             if yesno:
 -                 self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject()))
 -             else:
 -                 self.setGraphicsEffect(None)
 - 
 -         self.m_lineSelected = yesno
 -         self.updateLineGradient()
 - 
 -     def updateLinePos(self):
 -         if self.item1.getPortMode() == PORT_MODE_OUTPUT:
 -             item1_x = self.item1.scenePos().x() + self.item1.getPortWidth() + 12
 -             item1_y = self.item1.scenePos().y() + float(canvas.theme.port_height)/2
 - 
 -             item2_x = self.item2.scenePos().x()
 -             item2_y = self.item2.scenePos().y() + float(canvas.theme.port_height)/2
 - 
 -             item1_mid_x = abs(item1_x - item2_x) / 2
 -             item1_new_x = item1_x + item1_mid_x
 - 
 -             item2_mid_x = abs(item1_x - item2_x) / 2
 -             item2_new_x = item2_x - item2_mid_x
 - 
 -             path = QPainterPath(QPointF(item1_x, item1_y))
 -             path.cubicTo(item1_new_x, item1_y, item2_new_x, item2_y, item2_x, item2_y)
 -             self.setPath(path)
 - 
 -             self.m_lineSelected = False
 -             self.updateLineGradient()
 - 
 -     def type(self):
 -         return CanvasBezierLineType
 - 
 -     def updateLineGradient(self):
 -         pos_top = self.boundingRect().top()
 -         pos_bot = self.boundingRect().bottom()
 -         if self.item2.scenePos().y() >= self.item1.scenePos().y():
 -             pos1 = 0
 -             pos2 = 1
 -         else:
 -             pos1 = 1
 -             pos2 = 0
 - 
 -         port_type1 = self.item1.getPortType()
 -         port_type2 = self.item2.getPortType()
 -         port_gradient = QLinearGradient(0, pos_top, 0, pos_bot)
 - 
 -         if port_type1 == PORT_TYPE_AUDIO_JACK:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
 -         elif port_type1 == PORT_TYPE_MIDI_JACK:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
 -         elif port_type1 == PORT_TYPE_MIDI_ALSA:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
 -         elif port_type1 == PORT_TYPE_PARAMETER:
 -             port_gradient.setColorAt(pos1, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
 - 
 -         if port_type2 == PORT_TYPE_AUDIO_JACK:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
 -         elif port_type2 == PORT_TYPE_MIDI_JACK:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
 -         elif port_type2 == PORT_TYPE_MIDI_ALSA:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
 -         elif port_type2 == PORT_TYPE_PARAMETER:
 -             port_gradient.setColorAt(pos2, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
 - 
 -         self.setPen(QPen(port_gradient, 2))
 - 
 -     def paint(self, painter, option, widget):
 -         painter.save()
 -         painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
 -         QGraphicsPathItem.paint(self, painter, option, widget)
 -         painter.restore()
 - 
 - # ------------------------------------------------------------------------------
 - # canvaslivemov.cpp
 - 
 - class CanvasLineMov(QGraphicsLineItem):
 -     def __init__(self, port_mode, port_type, parent):
 -         QGraphicsLineItem.__init__(self, parent)
 - 
 -         self.m_port_mode = port_mode
 -         self.m_port_type = port_type
 - 
 -         # Port position doesn't change while moving around line
 -         self.p_lineX = self.scenePos().x()
 -         self.p_lineY = self.scenePos().y()
 -         self.p_width = self.parentItem().getPortWidth()
 - 
 -         if port_type == PORT_TYPE_AUDIO_JACK:
 -             pen = QPen(canvas.theme.line_audio_jack, 2)
 -         elif port_type == PORT_TYPE_MIDI_JACK:
 -             pen = QPen(canvas.theme.line_midi_jack, 2)
 -         elif port_type == PORT_TYPE_MIDI_ALSA:
 -             pen = QPen(canvas.theme.line_midi_alsa, 2)
 -         elif port_type == PORT_TYPE_PARAMETER:
 -             pen = QPen(canvas.theme.line_parameter, 2)
 -         else:
 -             qWarning("PatchCanvas::CanvasLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent))
 -             pen = QPen(Qt.black)
 - 
 -         self.setPen(pen)
 - 
 -     def updateLinePos(self, scenePos):
 -         item_pos = [0, 0]
 - 
 -         if self.m_port_mode == PORT_MODE_INPUT:
 -             item_pos[0] = 0
 -             item_pos[1] = float(canvas.theme.port_height)/2
 -         elif self.m_port_mode == PORT_MODE_OUTPUT:
 -             item_pos[0] = self.p_width + 12
 -             item_pos[1] = float(canvas.theme.port_height)/2
 -         else:
 -             return
 - 
 -         line = QLineF(item_pos[0], item_pos[1], scenePos.x() - self.p_lineX, scenePos.y() - self.p_lineY)
 -         self.setLine(line)
 - 
 -     def type(self):
 -         return CanvasLineMovType
 - 
 -     def paint(self, painter, option, widget):
 -         painter.save()
 -         painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
 -         QGraphicsLineItem.paint(self, painter, option, widget)
 -         painter.restore()
 - 
 - # ------------------------------------------------------------------------------
 - # canvasbezierlinemov.cpp
 - 
 - class CanvasBezierLineMov(QGraphicsPathItem):
 -     def __init__(self, port_mode, port_type, parent):
 -         QGraphicsPathItem.__init__(self, parent)
 - 
 -         self.m_port_mode = port_mode
 -         self.m_port_type = port_type
 - 
 -         # Port position doesn't change while moving around line
 -         self.p_itemX = self.scenePos().x()
 -         self.p_itemY = self.scenePos().y()
 -         self.p_width = self.parentItem().getPortWidth()
 - 
 -         if port_type == PORT_TYPE_AUDIO_JACK:
 -             pen = QPen(canvas.theme.line_audio_jack, 2)
 -         elif port_type == PORT_TYPE_MIDI_JACK:
 -             pen = QPen(canvas.theme.line_midi_jack, 2)
 -         elif port_type == PORT_TYPE_MIDI_ALSA:
 -             pen = QPen(canvas.theme.line_midi_alsa, 2)
 -         elif port_type == PORT_TYPE_PARAMETER:
 -             pen = QPen(canvas.theme.line_parameter, 2)
 -         else:
 -             qWarning("PatchCanvas::CanvasBezierLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent))
 -             pen = QPen(Qt.black)
 - 
 -         self.setBrush(QColor(0, 0, 0, 0))
 -         self.setPen(pen)
 - 
 -     def updateLinePos(self, scenePos):
 -         if self.m_port_mode == PORT_MODE_INPUT:
 -             old_x = 0
 -             old_y = float(canvas.theme.port_height)/2
 -             mid_x = abs(scenePos.x() - self.p_itemX) / 2
 -             new_x = old_x - mid_x
 -         elif self.m_port_mode == PORT_MODE_OUTPUT:
 -             old_x = self.p_width + 12
 -             old_y = float(canvas.theme.port_height)/2
 -             mid_x = abs(scenePos.x() - (self.p_itemX + old_x)) / 2
 -             new_x = old_x + mid_x
 -         else:
 -             return
 - 
 -         final_x = scenePos.x() - self.p_itemX
 -         final_y = scenePos.y() - self.p_itemY
 - 
 -         path = QPainterPath(QPointF(old_x, old_y))
 -         path.cubicTo(new_x, old_y, new_x, final_y, final_x, final_y)
 -         self.setPath(path)
 - 
 -     def type(self):
 -         return CanvasBezierLineMovType
 - 
 -     def paint(self, painter, option, widget):
 -         painter.save()
 -         painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
 -         QGraphicsPathItem.paint(self, painter, option, widget)
 -         painter.restore()
 - 
 - # ------------------------------------------------------------------------------
 - # canvasport.cpp
 - 
 - class CanvasPort(QGraphicsItem):
 -     def __init__(self, group_id, port_id, port_name, port_mode, port_type, is_alternate, parent):
 -         QGraphicsItem.__init__(self, parent)
 - 
 -         # Save Variables, useful for later
 -         self.m_group_id  = group_id
 -         self.m_port_id   = port_id
 -         self.m_port_mode = port_mode
 -         self.m_port_type = port_type
 -         self.m_port_name = port_name
 -         self.m_is_alternate = is_alternate
 - 
 -         # Base Variables
 -         self.m_port_width  = 15
 -         self.m_port_height = canvas.theme.port_height
 -         self.m_port_font = QFont()
 -         self.m_port_font.setFamily(canvas.theme.port_font_name)
 -         self.m_port_font.setPixelSize(canvas.theme.port_font_size)
 -         self.m_port_font.setWeight(canvas.theme.port_font_state)
 - 
 -         self.m_line_mov = None
 -         self.m_hover_item = None
 -         self.m_last_selected_state = False
 - 
 -         self.m_mouse_down = False
 -         self.m_cursor_moving = False
 - 
 -         self.setFlags(QGraphicsItem.ItemIsSelectable)
 - 
 -         if options.auto_select_items:
 -             self.setAcceptHoverEvents(True)
 - 
 -     def getGroupId(self):
 -         return self.m_group_id
 - 
 -     def getPortId(self):
 -         return self.m_port_id
 - 
 -     def getPortMode(self):
 -         return self.m_port_mode
 - 
 -     def getPortType(self):
 -         return self.m_port_type
 - 
 -     def getPortName(self):
 -         return self.m_port_name
 - 
 -     def getFullPortName(self):
 -         return self.parentItem().getGroupName() + ":" + self.m_port_name
 - 
 -     def getPortWidth(self):
 -         return self.m_port_width
 - 
 -     def getPortHeight(self):
 -         return self.m_port_height
 - 
 -     def setPortMode(self, port_mode):
 -         self.m_port_mode = port_mode
 -         self.update()
 - 
 -     def setPortType(self, port_type):
 -         self.m_port_type = port_type
 -         self.update()
 - 
 -     def setPortName(self, port_name):
 -         if QFontMetrics(self.m_port_font).width(port_name) < QFontMetrics(self.m_port_font).width(self.m_port_name):
 -             QTimer.singleShot(0, canvas.scene.update)
 - 
 -         self.m_port_name = port_name
 -         self.update()
 - 
 -     def setPortWidth(self, port_width):
 -         if port_width < self.m_port_width:
 -             QTimer.singleShot(0, canvas.scene.update)
 - 
 -         self.m_port_width = port_width
 -         self.update()
 - 
 -     def type(self):
 -         return CanvasPortType
 - 
 -     def hoverEnterEvent(self, event):
 -         if options.auto_select_items:
 -             self.setSelected(True)
 -         QGraphicsItem.hoverEnterEvent(self, event)
 - 
 -     def hoverLeaveEvent(self, event):
 -         if options.auto_select_items:
 -             self.setSelected(False)
 -         QGraphicsItem.hoverLeaveEvent(self, event)
 - 
 -     def mousePressEvent(self, event):
 -         self.m_hover_item = None
 -         self.m_mouse_down = bool(event.button() == Qt.LeftButton)
 -         self.m_cursor_moving = False
 -         QGraphicsItem.mousePressEvent(self, event)
 - 
 -     def mouseMoveEvent(self, event):
 -         if self.m_mouse_down:
 -             if not self.m_cursor_moving:
 -                 self.setCursor(QCursor(Qt.CrossCursor))
 -                 self.m_cursor_moving = True
 - 
 -                 for connection in canvas.connection_list:
 -                     if ((connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id) or
 -                         (connection.group_in_id  == self.m_group_id and connection.port_in_id  == self.m_port_id)):
 -                         connection.widget.setLocked(True)
 - 
 -             if not self.m_line_mov:
 -                 if options.use_bezier_lines:
 -                     self.m_line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, self)
 -                 else:
 -                     self.m_line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, self)
 - 
 -                 canvas.last_z_value += 1
 -                 self.m_line_mov.setZValue(canvas.last_z_value)
 -                 canvas.last_z_value += 1
 -                 self.parentItem().setZValue(canvas.last_z_value)
 - 
 -             item = None
 -             items = canvas.scene.items(event.scenePos(), Qt.ContainsItemShape, Qt.AscendingOrder)
 -             for i in range(len(items)):
 -                 if items[i].type() == CanvasPortType:
 -                     if items[i] != self:
 -                         if not item:
 -                             item = items[i]
 -                         elif items[i].parentItem().zValue() > item.parentItem().zValue():
 -                             item = items[i]
 - 
 -             if self.m_hover_item and self.m_hover_item != item:
 -                 self.m_hover_item.setSelected(False)
 - 
 -             if item:
 -                 if item.getPortMode() != self.m_port_mode and item.getPortType() == self.m_port_type:
 -                     item.setSelected(True)
 -                     self.m_hover_item = item
 -                 else:
 -                     self.m_hover_item = None
 -             else:
 -                 self.m_hover_item = None
 - 
 -             self.m_line_mov.updateLinePos(event.scenePos())
 -             return event.accept()
 - 
 -         QGraphicsItem.mouseMoveEvent(self, event)
 - 
 -     def mouseReleaseEvent(self, event):
 -         if self.m_mouse_down:
 -             if self.m_line_mov is not None:
 -                 item = self.m_line_mov
 -                 self.m_line_mov = None
 -                 canvas.scene.removeItem(item)
 -                 del item
 - 
 -             for connection in canvas.connection_list:
 -                 if ((connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id) or
 -                     (connection.group_in_id  == self.m_group_id and connection.port_in_id  == self.m_port_id)):
 -                     connection.widget.setLocked(False)
 - 
 -             if self.m_hover_item:
 -                 # TODO: a better way to check already existing connection
 -                 for connection in canvas.connection_list:
 -                     hover_group_id = self.m_hover_item.getGroupId()
 -                     hover_port_id  = self.m_hover_item.getPortId()
 - 
 -                     if (
 -                         (connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id and
 -                          connection.group_in_id  == hover_group_id  and connection.port_in_id  == hover_port_id) or
 -                         (connection.group_out_id == hover_group_id  and connection.port_out_id == hover_port_id and
 -                          connection.group_in_id  == self.m_group_id and connection.port_in_id  == self.m_port_id)
 -                        ):
 -                         canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "")
 -                         break
 -                 else:
 -                     if self.m_port_mode == PORT_MODE_OUTPUT:
 -                         conn = "%i:%i:%i:%i" % (self.m_group_id, self.m_port_id, self.m_hover_item.getGroupId(), self.m_hover_item.getPortId())
 -                         canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn)
 -                     else:
 -                         conn = "%i:%i:%i:%i" % (self.m_hover_item.getGroupId(), self.m_hover_item.getPortId(), self.m_group_id, self.m_port_id)
 -                         canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn)
 - 
 -                 canvas.scene.clearSelection()
 - 
 -         if self.m_cursor_moving:
 -             self.setCursor(QCursor(Qt.ArrowCursor))
 - 
 -         self.m_hover_item = None
 -         self.m_mouse_down = False
 -         self.m_cursor_moving = False
 -         QGraphicsItem.mouseReleaseEvent(self, event)
 - 
 -     def contextMenuEvent(self, event):
 -         canvas.scene.clearSelection()
 -         self.setSelected(True)
 - 
 -         menu = QMenu()
 -         discMenu = QMenu("Disconnect", menu)
 - 
 -         conn_list = CanvasGetPortConnectionList(self.m_group_id, self.m_port_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(discMenu)
 -         act_x_disc_all = menu.addAction("Disconnect &All")
 -         act_x_sep_1 = menu.addSeparator()
 -         act_x_info = menu.addAction("Get &Info")
 -         act_x_rename = menu.addAction("&Rename")
 - 
 -         if not features.port_info:
 -             act_x_info.setVisible(False)
 - 
 -         if not features.port_rename:
 -             act_x_rename.setVisible(False)
 - 
 -         if not (features.port_info and features.port_rename):
 -             act_x_sep_1.setVisible(False)
 - 
 -         act_selected = menu.exec_(event.screenPos())
 - 
 -         if act_selected == act_x_disc_all:
 -             for conn_id, group_id, port_id in conn_list:
 -                 canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "")
 - 
 -         elif act_selected == act_x_info:
 -             canvas.callback(ACTION_PORT_INFO, self.m_group_id, self.m_port_id, "")
 - 
 -         elif act_selected == act_x_rename:
 -             canvas.callback(ACTION_PORT_RENAME, self.m_group_id, self.m_port_id, "")
 - 
 -         event.accept()
 - 
 -     def boundingRect(self):
 -         return QRectF(0, 0, self.m_port_width + 12, self.m_port_height)
 - 
 -     def paint(self, painter, option, widget):
 -         painter.save()
 -         painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))
 - 
 -         poly_locx = [0, 0, 0, 0, 0]
 - 
 -         if self.m_port_mode == PORT_MODE_INPUT:
 -             text_pos = QPointF(3, canvas.theme.port_text_ypos)
 - 
 -             if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
 -                 poly_locx[0] = 0
 -                 poly_locx[1] = self.m_port_width + 5
 -                 poly_locx[2] = self.m_port_width + 12
 -                 poly_locx[3] = self.m_port_width + 5
 -                 poly_locx[4] = 0
 -             elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
 -                 poly_locx[0] = 0
 -                 poly_locx[1] = self.m_port_width + 5
 -                 poly_locx[2] = self.m_port_width + 5
 -                 poly_locx[3] = self.m_port_width + 5
 -                 poly_locx[4] = 0
 -             else:
 -                 qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode)
 -                 return
 - 
 -         elif self.m_port_mode == PORT_MODE_OUTPUT:
 -             text_pos = QPointF(9, canvas.theme.port_text_ypos)
 - 
 -             if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
 -                 poly_locx[0] = self.m_port_width + 12
 -                 poly_locx[1] = 7
 -                 poly_locx[2] = 0
 -                 poly_locx[3] = 7
 -                 poly_locx[4] = self.m_port_width + 12
 -             elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
 -                 poly_locx[0] = self.m_port_width + 12
 -                 poly_locx[1] = 5
 -                 poly_locx[2] = 5
 -                 poly_locx[3] = 5
 -                 poly_locx[4] = self.m_port_width + 12
 -             else:
 -                 qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode)
 -                 return
 - 
 -         else:
 -             qCritical("PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode))
 -             return
 - 
 -         if self.m_port_type == PORT_TYPE_AUDIO_JACK:
 -             poly_color = canvas.theme.port_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_audio_jack_bg
 -             poly_pen = canvas.theme.port_audio_jack_pen_sel  if self.isSelected() else canvas.theme.port_audio_jack_pen
 -             text_pen = canvas.theme.port_audio_jack_text_sel if self.isSelected() else canvas.theme.port_audio_jack_text
 -             conn_pen = canvas.theme.port_audio_jack_pen_sel
 -         elif self.m_port_type == PORT_TYPE_MIDI_JACK:
 -             poly_color = canvas.theme.port_midi_jack_bg_sel if self.isSelected() else canvas.theme.port_midi_jack_bg
 -             poly_pen = canvas.theme.port_midi_jack_pen_sel  if self.isSelected() else canvas.theme.port_midi_jack_pen
 -             text_pen = canvas.theme.port_midi_jack_text_sel if self.isSelected() else canvas.theme.port_midi_jack_text
 -             conn_pen = canvas.theme.port_midi_jack_pen_sel
 -         elif self.m_port_type == PORT_TYPE_MIDI_ALSA:
 -             poly_color = canvas.theme.port_midi_alsa_bg_sel if self.isSelected() else canvas.theme.port_midi_alsa_bg
 -             poly_pen = canvas.theme.port_midi_alsa_pen_sel  if self.isSelected() else canvas.theme.port_midi_alsa_pen
 -             text_pen = canvas.theme.port_midi_alsa_text_sel if self.isSelected() else canvas.theme.port_midi_alsa_text
 -             conn_pen = canvas.theme.port_midi_alsa_pen_sel
 -         elif self.m_port_type == PORT_TYPE_PARAMETER:
 -             poly_color = canvas.theme.port_parameter_bg_sel if self.isSelected() else canvas.theme.port_parameter_bg
 -             poly_pen = canvas.theme.port_parameter_pen_sel  if self.isSelected() else canvas.theme.port_parameter_pen
 -             text_pen = canvas.theme.port_parameter_text_sel if self.isSelected() else canvas.theme.port_parameter_text
 -             conn_pen = canvas.theme.port_parameter_pen_sel
 -         else:
 -             qCritical("PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type))
 -             return
 - 
 -         if self.m_is_alternate:
 -             poly_color = poly_color.darker(180)
 -             #poly_pen.setColor(poly_pen.color().darker(110))
 -             #text_pen.setColor(text_pen.color()) #.darker(150))
 -             #conn_pen.setColor(conn_pen.color()) #.darker(150))
 - 
 -         polygon  = QPolygonF()
 -         polygon += QPointF(poly_locx[0], 0)
 -         polygon += QPointF(poly_locx[1], 0)
 -         polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2)
 -         polygon += QPointF(poly_locx[3], canvas.theme.port_height)
 -         polygon += QPointF(poly_locx[4], canvas.theme.port_height)
 - 
 -         if canvas.theme.port_bg_pixmap:
 -             portRect = polygon.boundingRect()
 -             portPos  = portRect.topLeft()
 -             painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos)
 -         else:
 -             painter.setBrush(poly_color) #.lighter(200))
 - 
 -         painter.setPen(poly_pen)
 -         painter.drawPolygon(polygon)
 - 
 -         painter.setPen(text_pen)
 -         painter.setFont(self.m_port_font)
 -         painter.drawText(text_pos, self.m_port_name)
 - 
 -         if self.isSelected() != self.m_last_selected_state:
 -             for connection in canvas.connection_list:
 -                 if ((connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id) or
 -                     (connection.group_in_id  == self.m_group_id and connection.port_in_id  == self.m_port_id)):
 -                     connection.widget.setLineSelected(self.isSelected())
 - 
 -         if canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap:
 -             painter.setPen(Qt.NoPen)
 -             painter.setBrush(conn_pen.brush())
 - 
 -             if self.m_port_mode == PORT_MODE_INPUT:
 -                 connRect = QRectF(portRect.topLeft(), QSizeF(2, portRect.height()))
 -             else:
 -                 connRect = QRectF(QPointF(portRect.right()-2, portRect.top()), QSizeF(2, portRect.height()))
 - 
 -             painter.drawRect(connRect)
 - 
 -         self.m_last_selected_state = self.isSelected()
 - 
 -         painter.restore()
 - 
 - # ------------------------------------------------------------------------------
 - # canvasbox.cpp
 - 
 - class cb_line_t(object):
 -     __slots__ = [
 -         'line',
 -         'connection_id'
 -     ]
 - 
 - class CanvasBox(QGraphicsItem):
 -     def __init__(self, group_id, group_name, icon, parent=None):
 -         QGraphicsItem.__init__(self, 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
 - 
 -         # Base Variables
 -         self.p_width  = 50
 -         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_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
 -         if options.eyecandy:
 -             self.shadow = CanvasBoxShadow(self.toGraphicsObject())
 -             self.shadow.setFakeParent(self)
 -             self.setGraphicsEffect(self.shadow)
 -         else:
 -             self.shadow = None
 - 
 -         # Final touches
 -         self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
 - 
 -         # Wait for at least 1 port
 -         if options.auto_hide_groups:
 -             self.setVisible(False)
 - 
 -         self.setFlag(QGraphicsItem.ItemIsFocusable, True)
 - 
 -         if options.auto_select_items:
 -             self.setAcceptHoverEvents(True)
 - 
 -         self.updatePositions()
 - 
 -         canvas.scene.addItem(self)
 - 
 -     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 setAsPlugin(self, plugin_id, hasUi):
 -         self.m_plugin_id = plugin_id
 -         self.m_plugin_ui = hasUi
 - 
 -     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()
 -         new_cbline.line = line
 -         new_cbline.connection_id = 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()
 - 
 -         max_in_width   = 0
 -         max_in_height  = canvas.theme.box_header_height + canvas.theme.box_header_spacing
 -         max_out_width  = 0
 -         max_out_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing
 -         port_spacing   = canvas.theme.port_height + canvas.theme.port_spacing
 - 
 -         have_audio_jack_in  = have_midi_jack_in  = have_midi_alsa_in  = have_parameter_in  = False
 -         have_audio_jack_out = have_midi_jack_out = have_midi_alsa_out = have_parameter_out = False
 - 
 -         # reset box size
 -         self.p_width  = 50
 -         self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1
 - 
 -         # Check Text Name size
 -         app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30
 -         if app_name_size > self.p_width:
 -             self.p_width = 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)
 - 
 -         # Get Max Box Width/Height
 -         for port in port_list:
 -             if port.port_mode == PORT_MODE_INPUT:
 -                 max_in_height += port_spacing
 - 
 -                 size = QFontMetrics(self.m_font_port).width(port.port_name)
 -                 if size > max_in_width:
 -                     max_in_width = size
 - 
 -                 if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_in:
 -                     have_audio_jack_in = True
 -                     max_in_height += canvas.theme.port_spacingT
 -                 elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_in:
 -                     have_midi_jack_in = True
 -                     max_in_height += canvas.theme.port_spacingT
 -                 elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_in:
 -                     have_midi_alsa_in = True
 -                     max_in_height += canvas.theme.port_spacingT
 -                 elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_in:
 -                     have_parameter_in = True
 -                     max_in_height += canvas.theme.port_spacingT
 - 
 -             elif port.port_mode == PORT_MODE_OUTPUT:
 -                 max_out_height += port_spacing
 - 
 -                 size = QFontMetrics(self.m_font_port).width(port.port_name)
 -                 if size > max_out_width:
 -                     max_out_width = size
 - 
 -                 if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_out:
 -                     have_audio_jack_out = True
 -                     max_out_height += canvas.theme.port_spacingT
 -                 elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_out:
 -                     have_midi_jack_out = True
 -                     max_out_height += canvas.theme.port_spacingT
 -                 elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_out:
 -                     have_midi_alsa_out = True
 -                     max_out_height += canvas.theme.port_spacingT
 -                 elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_out:
 -                     have_parameter_out = True
 -                     max_out_height += canvas.theme.port_spacingT
 - 
 -         if canvas.theme.port_spacingT == 0:
 -             max_in_height  += 2
 -             max_out_height += 2
 - 
 -         final_width = 30 + max_in_width + max_out_width
 -         if final_width > self.p_width:
 -             self.p_width = final_width
 - 
 -         if max_in_height > self.p_height:
 -             self.p_height = max_in_height
 - 
 -         if max_out_height > self.p_height:
 -             self.p_height = max_out_height
 - 
 -         # Remove bottom space
 -         self.p_height -= canvas.theme.port_spacingT
 - 
 -         if canvas.theme.box_header_spacing > 0:
 -             if len(port_list) == 0:
 -                 self.p_height -= canvas.theme.box_header_spacing
 -             else:
 -                 self.p_height -= canvas.theme.box_header_spacing/2
 - 
 -         last_in_pos  = canvas.theme.box_header_height + canvas.theme.box_header_spacing
 -         last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
 -         last_in_type  = PORT_TYPE_NULL
 -         last_out_type = PORT_TYPE_NULL
 - 
 -         # Re-position ports, AUDIO_JACK
 -         for port in port_list:
 -             if port.port_type != PORT_TYPE_AUDIO_JACK:
 -                 continue
 - 
 -             if port.port_mode == PORT_MODE_INPUT:
 -                 if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
 -                     last_in_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
 -                 port.widget.setPortWidth(max_in_width)
 - 
 -                 last_in_pos += port_spacing
 -                 last_in_type = port.port_type
 - 
 -             elif port.port_mode == PORT_MODE_OUTPUT:
 -                 if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
 -                     last_out_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
 -                 port.widget.setPortWidth(max_out_width)
 - 
 -                 last_out_pos += port_spacing
 -                 last_out_type = port.port_type
 - 
 -         # Re-position ports, MIDI_JACK
 -         for port in port_list:
 -             if port.port_type != PORT_TYPE_MIDI_JACK:
 -                 continue
 - 
 -             if port.port_mode == PORT_MODE_INPUT:
 -                 if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
 -                     last_in_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
 -                 port.widget.setPortWidth(max_in_width)
 - 
 -                 last_in_pos += port_spacing
 -                 last_in_type = port.port_type
 - 
 -             elif port.port_mode == PORT_MODE_OUTPUT:
 -                 if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
 -                     last_out_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
 -                 port.widget.setPortWidth(max_out_width)
 - 
 -                 last_out_pos += port_spacing
 -                 last_out_type = port.port_type
 - 
 -         # Re-position ports, MIDI_ALSA
 -         for port in port_list:
 -             if port.port_type != PORT_TYPE_MIDI_ALSA:
 -                 continue
 - 
 -             if port.port_mode == PORT_MODE_INPUT:
 -                 if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
 -                     last_in_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
 -                 port.widget.setPortWidth(max_in_width)
 - 
 -                 last_in_pos += port_spacing
 -                 last_in_type = port.port_type
 - 
 -             elif port.port_mode == PORT_MODE_OUTPUT:
 -                 if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
 -                     last_out_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
 -                 port.widget.setPortWidth(max_out_width)
 - 
 -                 last_out_pos += port_spacing
 -                 last_out_type = port.port_type
 - 
 -         # Re-position ports, PARAMETER
 -         for port in port_list:
 -             if port.port_type != PORT_TYPE_PARAMETER:
 -                 continue
 - 
 -             if port.port_mode == PORT_MODE_INPUT:
 -                 if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
 -                     last_in_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
 -                 port.widget.setPortWidth(max_in_width)
 - 
 -                 last_in_pos += port_spacing
 -                 last_in_type = port.port_type
 - 
 -             elif port.port_mode == PORT_MODE_OUTPUT:
 -                 if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
 -                     last_out_pos += canvas.theme.port_spacingT
 - 
 -                 port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
 -                 port.widget.setPortWidth(max_out_width)
 - 
 -                 last_out_pos += port_spacing
 -                 last_out_type = port.port_type
 - 
 -         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):
 -         menu = QMenu()
 -         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 conn_id, group_id, port_id in tmp_conn_list:
 -                 if conn_id not in conn_list_ids:
 -                     conn_list.append((conn_id, group_id, port_id))
 -                     conn_list_ids.append(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(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, "")
 - 
 -         event.accept()
 - 
 -     def keyPressEvent(self, event):
 -         if self.m_plugin_id >= 0 and event.key() == Qt.Key_Delete:
 -             canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "")
 -             return event.accept()
 -         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:
 -             canvas.callback(ACTION_PLUGIN_SHOW_UI if self.m_plugin_ui else ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "")
 -             return event.accept()
 -         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:
 -             canvas.scene.clearSelection()
 -             self.setSelected(True)
 -             self.m_mouse_down = False
 -             return event.accept()
 - 
 -         elif event.button() == Qt.LeftButton:
 -             if self.sceneBoundingRect().contains(event.scenePos()):
 -                 self.m_mouse_down = True
 -             else:
 -                 # Fix a weird Qt behaviour with right-click mouseMove
 -                 self.m_mouse_down = False
 -                 return event.ignore()
 - 
 -         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.setCursor(QCursor(Qt.ArrowCursor))
 -         self.m_mouse_down = False
 -         self.m_cursor_moving = False
 -         QGraphicsItem.mouseReleaseEvent(self, event)
 - 
 -     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, False)
 - 
 -         # Draw rectangle
 -         if self.isSelected():
 -             painter.setPen(canvas.theme.box_pen_sel)
 -         else:
 -             painter.setPen(canvas.theme.box_pen)
 - 
 -         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)
 - 
 -         painter.drawRect(0, 0, self.p_width, self.p_height)
 - 
 -         # Draw pixmap header
 -         if canvas.theme.box_header_pixmap:
 -             painter.setPen(Qt.NoPen)
 -             painter.setBrush(canvas.theme.box_bg_2)
 -             painter.drawRect(1, 1, self.p_width-2, canvas.theme.box_header_height)
 - 
 -             headerPos  = QPointF(1, 1)
 -             headerRect = QRectF(2, 2, self.p_width-4, canvas.theme.box_header_height-3)
 -             painter.drawTiledPixmap(headerRect, canvas.theme.box_header_pixmap, headerPos)
 - 
 -         # 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()
 - 
 - # ------------------------------------------------------------------------------
 - # canvasicon.cpp
 - 
 - class CanvasIcon(QGraphicsSvgItem):
 -     def __init__(self, icon, name, parent):
 -         QGraphicsSvgItem.__init__(self, parent)
 - 
 -         self.m_renderer = None
 -         self.p_size = QRectF(0, 0, 0, 0)
 - 
 -         self.m_colorFX = QGraphicsColorizeEffect(self)
 -         self.m_colorFX.setColor(canvas.theme.box_text.color())
 - 
 -         self.setGraphicsEffect(self.m_colorFX)
 -         self.setIcon(icon, name)
 - 
 -     def setIcon(self, icon, name):
 -         name = name.lower()
 -         icon_path = ""
 - 
 -         if icon == ICON_APPLICATION:
 -             self.p_size = QRectF(3, 2, 19, 18)
 - 
 -             if "audacious" in name:
 -                 icon_path   = ":/scalable/pb_audacious.svg"
 -                 self.p_size = QRectF(5, 4, 16, 16)
 -             elif "clementine" in name:
 -                 icon_path = ":/scalable/pb_clementine.svg"
 -                 self.p_size = QRectF(5, 4, 16, 16)
 -             elif "distrho" in name:
 -                 icon_path = ":/scalable/pb_distrho.svg"
 -                 self.p_size = QRectF(5, 4, 14, 14)
 -             elif "jamin" in name:
 -                 icon_path = ":/scalable/pb_jamin.svg"
 -                 self.p_size = QRectF(5, 3, 16, 16)
 -             elif "mplayer" in name:
 -                 icon_path = ":/scalable/pb_mplayer.svg"
 -                 self.p_size = QRectF(5, 4, 16, 16)
 -             elif "vlc" in name:
 -                 icon_path = ":/scalable/pb_vlc.svg"
 -                 self.p_size = QRectF(5, 3, 16, 16)
 - 
 -             else:
 -                 icon_path = ":/scalable/pb_generic.svg"
 -                 self.p_size = QRectF(4, 3, 16, 16)
 - 
 -         elif icon == ICON_HARDWARE:
 -             icon_path = ":/scalable/pb_hardware.svg"
 -             self.p_size = QRectF(5, 2, 16, 16)
 - 
 -         elif icon == ICON_DISTRHO:
 -             icon_path = ":/scalable/pb_distrho.svg"
 -             self.p_size = QRectF(5, 4, 14, 14)
 - 
 -         elif icon == ICON_FILE:
 -             icon_path = ":/scalable/pb_file.svg"
 -             self.p_size = QRectF(5, 4, 12, 14)
 - 
 -         elif icon == ICON_PLUGIN:
 -             icon_path = ":/scalable/pb_plugin.svg"
 -             self.p_size = QRectF(5, 4, 14, 14)
 - 
 -         elif icon == ICON_LADISH_ROOM:
 -             # TODO - make a unique ladish-room icon
 -             icon_path = ":/scalable/pb_hardware.svg"
 -             self.p_size = QRectF(5, 2, 16, 16)
 - 
 -         else:
 -             self.p_size = QRectF(0, 0, 0, 0)
 -             qCritical("PatchCanvas::CanvasIcon.setIcon(%s, %s) - unsupported icon requested" % (icon2str(icon), name.encode()))
 -             return
 - 
 -         self.m_renderer = QSvgRenderer(icon_path, canvas.scene)
 -         self.setSharedRenderer(self.m_renderer)
 -         self.update()
 - 
 -     def type(self):
 -         return CanvasIconType
 - 
 -     def boundingRect(self):
 -         return self.p_size
 - 
 -     def paint(self, painter, option, widget):
 -         if self.m_renderer:
 -             painter.save()
 -             painter.setRenderHint(QPainter.Antialiasing, False)
 -             painter.setRenderHint(QPainter.TextAntialiasing, False)
 -             self.m_renderer.render(painter, self.p_size)
 -             painter.restore()
 -         else:
 -             QGraphicsSvgItem.paint(self, painter, option, widget)
 - 
 - # ------------------------------------------------------------------------------
 - # canvasportglow.cpp
 - 
 - class CanvasPortGlow(QGraphicsDropShadowEffect):
 -     def __init__(self, port_type, parent):
 -         QGraphicsDropShadowEffect.__init__(self, parent)
 - 
 -         self.setBlurRadius(12)
 -         self.setOffset(0, 0)
 - 
 -         if port_type == PORT_TYPE_AUDIO_JACK:
 -             self.setColor(canvas.theme.line_audio_jack_glow)
 -         elif port_type == PORT_TYPE_MIDI_JACK:
 -             self.setColor(canvas.theme.line_midi_jack_glow)
 -         elif port_type == PORT_TYPE_MIDI_ALSA:
 -             self.setColor(canvas.theme.line_midi_alsa_glow)
 -         elif port_type == PORT_TYPE_PARAMETER:
 -             self.setColor(canvas.theme.line_parameter_glow)
 - 
 - # ------------------------------------------------------------------------------
 - # canvasboxshadow.cpp
 - 
 - class CanvasBoxShadow(QGraphicsDropShadowEffect):
 -     def __init__(self, parent):
 -         QGraphicsDropShadowEffect.__init__(self, parent)
 - 
 -         self.m_fakeParent = None
 - 
 -         self.setBlurRadius(20)
 -         self.setColor(canvas.theme.box_shadow)
 -         self.setOffset(0, 0)
 - 
 -     def setFakeParent(self, fakeParent):
 -         self.m_fakeParent = fakeParent
 - 
 -     def setOpacity(self, opacity):
 -         color = QColor(canvas.theme.box_shadow)
 -         color.setAlphaF(opacity)
 -         self.setColor(color)
 - 
 -     def draw(self, painter):
 -         if self.m_fakeParent:
 -             self.m_fakeParent.repaintLines()
 -         QGraphicsDropShadowEffect.draw(self, painter)
 
 
  |