2652 lines
92 KiB
Python
2652 lines
92 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# PatchBay Canvas engine using QGraphicsView/Scene
|
|
# Copyright (C) 2010-2018 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 GPL.txt file
|
|
|
|
# Imports (Global)
|
|
if True:
|
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, qDebug, 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.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem
|
|
from PyQt5.QtWidgets import QGraphicsColorizeEffect, QGraphicsDropShadowEffect
|
|
from PyQt5.QtWidgets import QInputDialog, QLineEdit, QMenu
|
|
from PyQt5.QtSvg import QGraphicsSvgItem, QSvgRenderer
|
|
else:
|
|
from PyQt4.QtCore import pyqtSignal, pyqtSlot, qDebug, 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
|
|
from PyQt4.QtGui import QInputDialog, QLineEdit, QMenu
|
|
from PyQt4.QtSvg import QGraphicsSvgItem, QSvgRenderer
|
|
|
|
# Imports (Theme)
|
|
from patchcanvas_theme import *
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# patchcanvas-api.h
|
|
|
|
# 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_A2J = 3
|
|
PORT_TYPE_MIDI_ALSA = 4
|
|
|
|
# Callback Action
|
|
ACTION_GROUP_INFO = 0 # group_id, N, N
|
|
ACTION_GROUP_RENAME = 1 # group_id, N, new_name
|
|
ACTION_GROUP_SPLIT = 2 # group_id, N, N
|
|
ACTION_GROUP_JOIN = 3 # group_id, N, N
|
|
ACTION_PORT_INFO = 4 # port_id, N, N
|
|
ACTION_PORT_RENAME = 5 # port_id, N, new_name
|
|
ACTION_PORTS_CONNECT = 6 # out_id, in_id, N
|
|
ACTION_PORTS_DISCONNECT = 7 # conn_id, 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',
|
|
'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',
|
|
'widgets'
|
|
]
|
|
|
|
class port_dict_t(object):
|
|
__slots__ = [
|
|
'group_id',
|
|
'port_id',
|
|
'port_name',
|
|
'port_mode',
|
|
'port_type',
|
|
'widget'
|
|
]
|
|
|
|
class connection_dict_t(object):
|
|
__slots__ = [
|
|
'connection_id',
|
|
'port_in_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 AnimationIdle(self):
|
|
animation = self.sender()
|
|
if animation:
|
|
CanvasRemoveAnimation(animation)
|
|
|
|
@pyqtSlot()
|
|
def AnimationHide(self):
|
|
animation = self.sender()
|
|
if animation:
|
|
item = animation.item()
|
|
if item:
|
|
item.hide()
|
|
CanvasRemoveAnimation(animation)
|
|
|
|
@pyqtSlot()
|
|
def AnimationDestroy(self):
|
|
animation = self.sender()
|
|
if animation:
|
|
item = animation.item()
|
|
if item:
|
|
CanvasRemoveItemFX(item)
|
|
CanvasRemoveAnimation(animation)
|
|
|
|
@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.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_A2J:
|
|
return "PORT_TYPE_MIDI_A2J"
|
|
elif port_type == PORT_TYPE_MIDI_ALSA:
|
|
return "PORT_TYPE_MIDI_ALSA"
|
|
else:
|
|
return "PORT_TYPE_???"
|
|
|
|
def icon2str(icon):
|
|
if icon == ICON_HARDWARE:
|
|
return "ICON_HARDWARE"
|
|
elif ICON_APPLICATION:
|
|
return "ICON_APPLICATION"
|
|
elif 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_???"
|
|
|
|
# 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.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:
|
|
qDebug("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:
|
|
qDebug("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.port_id)
|
|
|
|
for connection in canvas.connection_list:
|
|
connection_list_ids.append(connection.connection_id)
|
|
|
|
for idx in connection_list_ids:
|
|
disconnectPorts(idx)
|
|
|
|
for idx in port_list_ids:
|
|
removePort(idx)
|
|
|
|
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.clear()
|
|
|
|
canvas.initiated = False
|
|
|
|
def setInitialPos(x, y):
|
|
if canvas.debug:
|
|
qDebug("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:
|
|
qDebug("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:
|
|
qDebug("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 and features.handle_group_pos:
|
|
split = canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, split, type=int)
|
|
|
|
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.widgets = [group_box, None]
|
|
|
|
if split == SPLIT_YES:
|
|
group_box.setSplit(True, PORT_MODE_OUTPUT)
|
|
|
|
if features.handle_group_pos:
|
|
group_box.setPos(canvas.settings.value("CanvasPositions/%s_OUTPUT" % group_name, CanvasGetNewGroupPos(), type=QPointF))
|
|
else:
|
|
group_box.setPos(CanvasGetNewGroupPos())
|
|
|
|
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(canvas.settings.value("CanvasPositions/%s_INPUT" % group_name, CanvasGetNewGroupPos(True), type=QPointF))
|
|
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)
|
|
|
|
group_sbox.checkItemPos()
|
|
|
|
else:
|
|
group_box.setSplit(False)
|
|
|
|
if features.handle_group_pos:
|
|
group_box.setPos(canvas.settings.value("CanvasPositions/%s" % group_name, CanvasGetNewGroupPos(), type=QPointF))
|
|
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:
|
|
CanvasItemFX(group_box, True)
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
|
|
def removeGroup(group_id):
|
|
if canvas.debug:
|
|
qDebug("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:
|
|
qDebug("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:
|
|
qDebug("PatchCanvas::splitGroup(%i)" % group_id)
|
|
|
|
item = None
|
|
group_name = ""
|
|
group_icon = ICON_APPLICATION
|
|
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
|
|
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.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.port_in_id = connection.port_in_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(port_id)
|
|
|
|
removeGroup(group_id)
|
|
|
|
# Step 3 - Re-create Item, now splitted
|
|
addGroup(group_id, group_name, SPLIT_YES, group_icon)
|
|
|
|
for port in ports_data:
|
|
addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type)
|
|
|
|
for conn in conns_data:
|
|
connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id)
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
|
|
def joinGroup(group_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::joinGroup(%i)" % group_id)
|
|
|
|
item = None
|
|
s_item = None
|
|
group_name = ""
|
|
group_icon = ICON_APPLICATION
|
|
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
|
|
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.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.port_in_id = connection.port_in_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(port_id)
|
|
|
|
removeGroup(group_id)
|
|
|
|
# Step 3 - Re-create Item, now together
|
|
addGroup(group_id, group_name, SPLIT_NO, group_icon)
|
|
|
|
for port in ports_data:
|
|
addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type)
|
|
|
|
for conn in conns_data:
|
|
connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id)
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
|
|
def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
|
|
if canvas.debug:
|
|
qDebug("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 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:
|
|
qDebug("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:
|
|
qDebug("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 addPort(group_id, port_id, port_name, port_mode, port_type):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::addPort(%i, %i, %s, %s, %s)" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
|
|
|
|
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)
|
|
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
|
|
|
|
if options.eyecandy == EYECANDY_FULL:
|
|
CanvasItemFX(port_widget, True)
|
|
|
|
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.widget = port_widget
|
|
canvas.port_list.append(port_dict)
|
|
|
|
box_widget.updatePositions()
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
|
|
def removePort(port_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::removePort(%i)" % port_id)
|
|
|
|
for port in canvas.port_list:
|
|
if port.port_id == port_id:
|
|
item = port.widget
|
|
item.parentItem().removePortFromGroup(port_id)
|
|
canvas.scene.removeItem(item)
|
|
del item
|
|
|
|
canvas.port_list.remove(port)
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
return
|
|
|
|
qCritical("PatchCanvas::removePort(%i) - Unable to find port to remove" % port_id)
|
|
|
|
def renamePort(port_id, new_port_name):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::renamePort(%i, %s)" % (port_id, new_port_name.encode()))
|
|
|
|
for port in canvas.port_list:
|
|
if 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, %s) - Unable to find port to rename" % (port_id, new_port_name.encode()))
|
|
|
|
def connectPorts(connection_id, port_out_id, port_in_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::connectPorts(%i, %i, %i)" % (connection_id, port_out_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.port_id == port_out_id:
|
|
port_out = port.widget
|
|
port_out_parent = port_out.parentItem()
|
|
elif 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) - unable to find ports to connect" % (connection_id, port_out_id, port_in_id))
|
|
return
|
|
|
|
connection_dict = connection_dict_t()
|
|
connection_dict.connection_id = connection_id
|
|
connection_dict.port_out_id = port_out_id
|
|
connection_dict.port_in_id = port_in_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
|
|
CanvasItemFX(item, True)
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
|
|
def disconnectPorts(connection_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::disconnectPorts(%i)" % connection_id)
|
|
|
|
port_1_id = port_2_id = 0
|
|
line = None
|
|
item1 = None
|
|
item2 = None
|
|
|
|
for connection in canvas.connection_list:
|
|
if connection.connection_id == connection_id:
|
|
port_1_id = connection.port_out_id
|
|
port_2_id = 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.port_id == port_1_id:
|
|
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.port_id == port_2_id:
|
|
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:
|
|
CanvasItemFX(line, False, True)
|
|
else:
|
|
line.deleteFromScene()
|
|
|
|
QTimer.singleShot(0, canvas.scene.update)
|
|
|
|
def arrange():
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::arrange()")
|
|
|
|
def updateZValues():
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::updateZValues()")
|
|
|
|
for group in canvas.group_list:
|
|
group.widgets[0].resetLinesZValue()
|
|
|
|
if group.split and group.widgets[1]:
|
|
group.widgets[1].resetLinesZValue()
|
|
|
|
# Extra Internal functions
|
|
|
|
def CanvasGetGroupName(group_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasGetGroupName(%i)" % group_id)
|
|
|
|
for group in canvas.group_list:
|
|
if group.group_id == group_id:
|
|
return group.group_name
|
|
|
|
qCritical("PatchCanvas::CanvasGetGroupName(%i) - unable to find group" % group_id)
|
|
return ""
|
|
|
|
def CanvasGetGroupPortCount(group_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasGetGroupPortCount(%i)" % group_id)
|
|
|
|
port_count = 0
|
|
for port in canvas.port_list:
|
|
if port.group_id == group_id:
|
|
port_count += 1
|
|
|
|
return port_count
|
|
|
|
def CanvasGetNewGroupPos(horizontal=False):
|
|
if canvas.debug:
|
|
qDebug("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(port_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasGetFullPortName(%i)" % port_id)
|
|
|
|
for port in canvas.port_list:
|
|
if 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) - unable to find port" % port_id)
|
|
return ""
|
|
|
|
def CanvasGetPortConnectionList(port_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasGetPortConnectionList(%i)" % port_id)
|
|
|
|
port_con_list = []
|
|
|
|
for connection in canvas.connection_list:
|
|
if connection.port_out_id == port_id or connection.port_in_id == port_id:
|
|
port_con_list.append(connection.connection_id)
|
|
|
|
return port_con_list
|
|
|
|
def CanvasGetConnectedPort(connection_id, port_id):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasGetConnectedPort(%i, %i)" % (connection_id, port_id))
|
|
|
|
for connection in canvas.connection_list:
|
|
if connection.connection_id == connection_id:
|
|
if connection.port_out_id == port_id:
|
|
return connection.port_in_id
|
|
else:
|
|
return connection.port_out_id
|
|
|
|
qCritical("PatchCanvas::CanvasGetConnectedPort(%i, %i) - unable to find connection" % (connection_id, port_id))
|
|
return 0
|
|
|
|
def CanvasRemoveAnimation(f_animation):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasRemoveAnimation(%s)" % f_animation)
|
|
|
|
for animation in canvas.animation_list:
|
|
if animation.animation == f_animation:
|
|
canvas.animation_list.remove(animation)
|
|
del animation.animation
|
|
break
|
|
|
|
def CanvasCallback(action, value1, value2, value_str):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode()))
|
|
|
|
canvas.callback(action, value1, value2, value_str)
|
|
|
|
def CanvasItemFX(item, show, destroy=False):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasItemFX(%s, %s, %s)" % (item, bool2str(show), bool2str(destroy)))
|
|
|
|
# Check if item already has an animationItemFX
|
|
for animation in canvas.animation_list:
|
|
if animation.item == item:
|
|
animation.animation.stop()
|
|
canvas.animation_list.remove(animation)
|
|
del animation.animation
|
|
break
|
|
|
|
animation = CanvasFadeAnimation(item, show)
|
|
animation.setDuration(750 if show else 500)
|
|
|
|
animation_dict = animation_dict_t()
|
|
animation_dict.animation = animation
|
|
animation_dict.item = item
|
|
canvas.animation_list.append(animation_dict)
|
|
|
|
if show:
|
|
animation.finished.connect(canvas.qobject.AnimationIdle)
|
|
else:
|
|
if destroy:
|
|
animation.finished.connect(canvas.qobject.AnimationDestroy)
|
|
else:
|
|
animation.finished.connect(canvas.qobject.AnimationHide)
|
|
|
|
animation.start()
|
|
|
|
def CanvasRemoveItemFX(item):
|
|
if canvas.debug:
|
|
qDebug("PatchCanvas::CanvasRemoveItemFX(%s)" % item)
|
|
|
|
if item.type() == CanvasBoxType:
|
|
item.removeIconFromScene()
|
|
canvas.scene.removeItem(item)
|
|
|
|
elif item.type() == CanvasPortType:
|
|
canvas.scene.removeItem(item)
|
|
|
|
elif item.type() in (CanvasLineType, CanvasBezierLineType):
|
|
pass #item.deleteFromScene()
|
|
|
|
# Force deletion of item if needed
|
|
if item.type() in (CanvasBoxType, CanvasPortType):
|
|
del item
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# patchscene.cpp
|
|
|
|
class PatchScene(QGraphicsScene):
|
|
scaleChanged = pyqtSignal(float)
|
|
sceneGroupMoved = pyqtSignal(int, int, QPointF)
|
|
|
|
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")
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# canvasfadeanimation.cpp
|
|
|
|
class CanvasFadeAnimation(QAbstractAnimation):
|
|
def __init__(self, item, show, parent=None):
|
|
QAbstractAnimation.__init__(self, parent)
|
|
|
|
self.m_show = show
|
|
self.m_duration = 0
|
|
self.m_item = item
|
|
|
|
def item(self):
|
|
return self.m_item
|
|
|
|
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)
|
|
|
|
self.m_item.setOpacity(value)
|
|
|
|
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 deleteFromScene(self):
|
|
canvas.scene.removeItem(self)
|
|
del self
|
|
|
|
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_A2J:
|
|
port_gradient.setColorAt(pos1, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
|
|
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)
|
|
|
|
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_A2J:
|
|
port_gradient.setColorAt(pos2, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
|
|
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)
|
|
|
|
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 deleteFromScene(self):
|
|
canvas.scene.removeItem(self)
|
|
del self
|
|
|
|
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_A2J:
|
|
port_gradient.setColorAt(pos1, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
|
|
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)
|
|
|
|
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_A2J:
|
|
port_gradient.setColorAt(pos2, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
|
|
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)
|
|
|
|
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_A2J:
|
|
pen = QPen(canvas.theme.line_midi_a2j, 2)
|
|
elif port_type == PORT_TYPE_MIDI_ALSA:
|
|
pen = QPen(canvas.theme.line_midi_alsa, 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 deleteFromScene(self):
|
|
canvas.scene.removeItem(self)
|
|
del self
|
|
|
|
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_A2J:
|
|
pen = QPen(canvas.theme.line_midi_a2j, 2)
|
|
elif port_type == PORT_TYPE_MIDI_ALSA:
|
|
pen = QPen(canvas.theme.line_midi_alsa, 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 deleteFromScene(self):
|
|
canvas.scene.removeItem(self)
|
|
del self
|
|
|
|
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, port_id, port_name, port_mode, port_type, parent):
|
|
QGraphicsItem.__init__(self, parent)
|
|
|
|
# Save Variables, useful for later
|
|
self.m_port_id = port_id
|
|
self.m_port_mode = port_mode
|
|
self.m_port_type = port_type
|
|
self.m_port_name = port_name
|
|
|
|
# Base Variables
|
|
self.m_port_width = 15
|
|
self.m_port_height = canvas.theme.port_height
|
|
self.m_port_font = QFont(canvas.theme.port_font_name, canvas.theme.port_font_size, 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)
|
|
|
|
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 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.port_out_id == self.m_port_id or 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:
|
|
a2j_connection = bool(item.getPortType() == PORT_TYPE_MIDI_JACK and self.m_port_type == PORT_TYPE_MIDI_A2J) or (item.getPortType() == PORT_TYPE_MIDI_A2J and self.m_port_type == PORT_TYPE_MIDI_JACK)
|
|
|
|
if item.getPortMode() != self.m_port_mode and (item.getPortType() == self.m_port_type or a2j_connection):
|
|
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:
|
|
self.m_line_mov.deleteFromScene()
|
|
self.m_line_mov = None
|
|
|
|
for connection in canvas.connection_list:
|
|
if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id:
|
|
connection.widget.setLocked(False)
|
|
|
|
if self.m_hover_item:
|
|
check = False
|
|
for connection in canvas.connection_list:
|
|
if ( (connection.port_out_id == self.m_port_id and connection.port_in_id == self.m_hover_item.getPortId()) or
|
|
(connection.port_out_id == self.m_hover_item.getPortId() and connection.port_in_id == self.m_port_id) ):
|
|
canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "")
|
|
check = True
|
|
break
|
|
|
|
if not check:
|
|
if self.m_port_mode == PORT_MODE_OUTPUT:
|
|
canvas.callback(ACTION_PORTS_CONNECT, self.m_port_id, self.m_hover_item.getPortId(), "")
|
|
else:
|
|
canvas.callback(ACTION_PORTS_CONNECT, self.m_hover_item.getPortId(), self.m_port_id, "")
|
|
|
|
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)
|
|
|
|
port_con_list = CanvasGetPortConnectionList(self.m_port_id)
|
|
|
|
if len(port_con_list) > 0:
|
|
for port_id in port_con_list:
|
|
port_con_id = CanvasGetConnectedPort(port_id, self.m_port_id)
|
|
act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id))
|
|
act_x_disc.setData(port_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 port_id in port_con_list:
|
|
canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "")
|
|
|
|
elif act_selected == act_x_info:
|
|
canvas.callback(ACTION_PORT_INFO, self.m_port_id, 0, "")
|
|
|
|
elif act_selected == act_x_rename:
|
|
new_name_try = QInputDialog.getText(None, "Rename Port", "New name:", QLineEdit.Normal, self.m_port_name)
|
|
if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
|
|
canvas.callback(ACTION_PORT_RENAME, self.m_port_id, 0, new_name_try[0])
|
|
|
|
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_A2J:
|
|
poly_color = canvas.theme.port_midi_a2j_bg_sel if self.isSelected() else canvas.theme.port_midi_a2j_bg
|
|
poly_pen = canvas.theme.port_midi_a2j_pen_sel if self.isSelected() else canvas.theme.port_midi_a2j_pen
|
|
text_pen = canvas.theme.port_midi_a2j_text_sel if self.isSelected() else canvas.theme.port_midi_a2j_text
|
|
conn_pen = canvas.theme.port_midi_a2j_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
|
|
else:
|
|
qCritical("PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type))
|
|
return
|
|
|
|
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)
|
|
|
|
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.port_out_id == self.m_port_id or 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
|
|
|
|
# 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(canvas.theme.box_font_name, canvas.theme.box_font_size, canvas.theme.box_font_state)
|
|
self.m_font_port = QFont(canvas.theme.port_font_name, canvas.theme.port_font_size, 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.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 setIcon(self, icon):
|
|
if self.icon_svg:
|
|
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):
|
|
if len(self.m_port_list_ids) == 0:
|
|
if options.auto_hide_groups:
|
|
if options.eyecandy == EYECANDY_FULL:
|
|
CanvasItemFX(self, True)
|
|
self.setVisible(True)
|
|
|
|
new_widget = CanvasPort(port_id, port_name, port_mode, port_type, 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.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)
|
|
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:
|
|
canvas.scene.removeItem(self.icon_svg)
|
|
|
|
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_a2j_in = have_midi_alsa_in = False
|
|
have_audio_jack_out = have_midi_jack_out = have_midi_a2j_out = have_midi_alsa_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.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_A2J and not have_midi_a2j_in:
|
|
have_midi_a2j_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_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_A2J and not have_midi_a2j_out:
|
|
have_midi_a2j_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
|
|
|
|
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:
|
|
if port.port_mode == PORT_MODE_INPUT:
|
|
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:
|
|
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:
|
|
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_A2J
|
|
for port in port_list:
|
|
if port.port_type == PORT_TYPE_MIDI_A2J:
|
|
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:
|
|
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)
|
|
|
|
port_con_list = []
|
|
port_con_list_ids = []
|
|
|
|
for port_id in self.m_port_list_ids:
|
|
tmp_port_con_list = CanvasGetPortConnectionList(port_id)
|
|
for port_con_id in tmp_port_con_list:
|
|
if port_con_id not in port_con_list:
|
|
port_con_list.append(port_con_id)
|
|
port_con_list_ids.append(port_id)
|
|
|
|
if len(port_con_list) > 0:
|
|
for i in range(len(port_con_list)):
|
|
port_con_id = CanvasGetConnectedPort(port_con_list[i], port_con_list_ids[i])
|
|
act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id))
|
|
act_x_disc.setData(port_con_list[i])
|
|
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)
|
|
|
|
haveIns = haveOuts = False
|
|
for port in canvas.port_list:
|
|
if 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 == act_x_disc_all:
|
|
for port_id in port_con_list:
|
|
canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "")
|
|
|
|
elif act_selected == act_x_info:
|
|
canvas.callback(ACTION_GROUP_INFO, self.m_group_id, 0, "")
|
|
|
|
elif act_selected == act_x_rename:
|
|
new_name_try = QInputDialog.getText(None, "Rename Group", "New name:", QLineEdit.Normal, self.m_group_name)
|
|
if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
|
|
canvas.callback(ACTION_GROUP_RENAME, self.m_group_id, 0, new_name_try[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, "")
|
|
|
|
event.accept()
|
|
|
|
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(QRectF(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(QRectF(1, 1, self.p_width-2, canvas.theme.box_header_height))
|
|
|
|
headerPos = QPointF(1, 1)
|
|
headerRect = QRectF(2, 2, int(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_A2J:
|
|
self.setColor(canvas.theme.line_midi_a2j_glow)
|
|
elif port_type == PORT_TYPE_MIDI_ALSA:
|
|
self.setColor(canvas.theme.line_midi_alsa_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)
|