diff --git a/source/frontend/carla_host.py b/source/frontend/carla_host.py index b07cfca02..4fdedb1a9 100644 --- a/source/frontend/carla_host.py +++ b/source/frontend/carla_host.py @@ -1607,8 +1607,8 @@ class HostWindow(QMainWindow): else: portType = patchcanvas.PORT_TYPE_NULL isAlternate = False - - patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate) + print('addPort', portId, portGroupId) + patchcanvas.addPort(clientId, portId, portName, portMode, portType, portGroupId, isAlternate) self.updateMiniCanvasLater() @pyqtSlot(int, int) diff --git a/source/frontend/patchcanvas/__init__.py b/source/frontend/patchcanvas/__init__.py index 264fd7b2d..85433bb0c 100644 --- a/source/frontend/patchcanvas/__init__.py +++ b/source/frontend/patchcanvas/__init__.py @@ -132,6 +132,7 @@ class Canvas(object): self.group_list = [] self.port_list = [] + self.port_group_list = [] self.connection_list = [] self.animation_list = [] self.group_plugin_map = {} @@ -143,6 +144,8 @@ class Canvas(object): self.last_connection_id = 0 self.initial_pos = QPointF(0, 0) self.size_rect = QRectF() + + self.is_line_mov = False def callback(self, action, value1, value2, value_str): print("Canvas::callback({}, {}, {}, {})".format(action, value1, value2, value_str)) @@ -169,10 +172,21 @@ class port_dict_t(object): 'port_name', 'port_mode', 'port_type', + 'port_group_id', 'is_alternate', 'widget' ] +class port_group_dict_t(object): + __slots__ = [ + 'port_group_id', + 'group_id', + 'port_mode', + 'port_type', + 'port_id_list', + 'widget' + ] + class connection_dict_t(object): __slots__ = [ 'connection_id', diff --git a/source/frontend/patchcanvas/canvasbezierline.py b/source/frontend/patchcanvas/canvasbezierline.py index 9271562d2..be52918fe 100644 --- a/source/frontend/patchcanvas/canvasbezierline.py +++ b/source/frontend/patchcanvas/canvasbezierline.py @@ -39,6 +39,7 @@ from . import ( PORT_TYPE_PARAMETER, ) +from .utils import CanvasGetPortGroupPosition from .canvasportglow import CanvasPortGlow # ------------------------------------------------------------------------------------------------------------ @@ -98,18 +99,20 @@ class CanvasBezierLine(QGraphicsPathItem): if self.item1.getPortMode() == PORT_MODE_OUTPUT: item1_x = self.item1.scenePos().x() + self.item1.getPortWidth() + 12 - port_posinport_group1, port_group_lenght1 = CanvasGetPortPositionAndPortGroupLenght(self.item1.getPortId(), self.item1.getPortGroupId()) + port_pos_1, port_group_len_1 = CanvasGetPortGroupPosition( + self.item1.getGroupId(), self.item1.getPortId(), + self.item1.getPortGroupId()) - if port_group_lenght1 > 2: + if port_group_len_1 > 2: phi = 0.75 else: phi = 0.62 - if port_group_lenght1 > 1: + if port_group_len_1 > 1: first_old_y = canvas.theme.port_height * phi - last_old_y = canvas.theme.port_height * (port_group_lenght1 - phi) - delta = (last_old_y - first_old_y) / (port_group_lenght1 -1) - old_y1 = first_old_y + (port_posinport_group1 * delta) - (canvas.theme.port_height * port_posinport_group1) + last_old_y = canvas.theme.port_height * (port_group_len_1 - phi) + delta = (last_old_y - first_old_y) / (port_group_len_1 -1) + old_y1 = first_old_y + (port_pos_1 * delta) - (canvas.theme.port_height * port_pos_1) else: old_y1 = canvas.theme.port_height / 2 @@ -117,18 +120,20 @@ class CanvasBezierLine(QGraphicsPathItem): item2_x = self.item2.scenePos().x() - port_posinport_group2, port_group_lenght2 = CanvasGetPortPositionAndPortGroupLenght(self.item2.getPortId(), self.item2.getPortGroupId()) + port_pos_2, port_group_len_2 = CanvasGetPortGroupPosition( + self.item2.getGroupId(), self.item2.getPortId(), + self.item2.getPortGroupId()) - if port_group_lenght2 > 2: + if port_group_len_2 > 2: phi = 0.75 else: phi = 0.62 - if port_group_lenght2 > 1: + if port_group_len_2 > 1: first_old_y = canvas.theme.port_height * phi - last_old_y = canvas.theme.port_height * (port_group_lenght2 - phi) - delta = (last_old_y - first_old_y) / (port_group_lenght2 -1) - old_y2 = first_old_y + (port_posinport_group2 * delta) - (canvas.theme.port_height * port_posinport_group2) + last_old_y = canvas.theme.port_height * (port_group_len_2 - phi) + delta = (last_old_y - first_old_y) / (port_group_len_2 -1) + old_y2 = first_old_y + (port_pos_2 * delta) - (canvas.theme.port_height * port_pos_2) else: old_y2 = canvas.theme.port_height / 2 diff --git a/source/frontend/patchcanvas/canvasport.py b/source/frontend/patchcanvas/canvasport.py index 143b891ea..1ac0f7655 100644 --- a/source/frontend/patchcanvas/canvasport.py +++ b/source/frontend/patchcanvas/canvasport.py @@ -51,12 +51,17 @@ from . import ( from .canvasbezierlinemov import CanvasBezierLineMov from .canvaslinemov import CanvasLineMov from .theme import Theme -from .utils import CanvasGetFullPortName, CanvasGetPortConnectionList +from .utils import ( + CanvasGetFullPortName, + CanvasGetPortConnectionList, + CanvasGetPortGroupPosition, + CanvasGetPortPrintName) # ------------------------------------------------------------------------------------------------------------ class CanvasPort(QGraphicsItem): - def __init__(self, group_id, port_id, port_name, port_mode, port_type, is_alternate, parent): + def __init__(self, group_id, port_id, port_name, port_mode, + port_type, port_group_id, is_alternate, parent): QGraphicsItem.__init__(self) self.setParentItem(parent) @@ -66,6 +71,7 @@ class CanvasPort(QGraphicsItem): self.m_port_mode = port_mode self.m_port_type = port_type self.m_port_name = port_name + self.m_port_group_id = port_group_id self.m_is_alternate = is_alternate # Base Variables @@ -76,8 +82,10 @@ class CanvasPort(QGraphicsItem): 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_line_mov_list = [] + self.m_dotcon_list = [] self.m_hover_item = None + self.m_last_selected_state = False self.m_mouse_down = False self.m_cursor_moving = False @@ -102,6 +110,9 @@ class CanvasPort(QGraphicsItem): def getPortName(self): return self.m_port_name + def getPortGroupId(self): + return self.m_port_group_id + def getFullPortName(self): return self.parentItem().getGroupName() + ":" + self.m_port_name @@ -119,6 +130,10 @@ class CanvasPort(QGraphicsItem): self.m_port_type = port_type self.update() + def setPortGroupId(self, port_group_id): + self.m_port_group_id = port_group_id + 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) @@ -132,7 +147,95 @@ class CanvasPort(QGraphicsItem): self.m_port_width = port_width self.update() - + + def deleteLine2(self): + for line_mov in self.m_line_mov_list: + if self.m_line_mov_list.index(line_mov) > 0: + canvas.scene.removeItem(line_mov) + else: + line_mov.setPortPosInPortGroupTo(PORT_IN_PORT_GROUP_POSITION_MONO) + self.m_line_mov_list = self.m_line_mov_list[:1] + + def resetDotLines(self): + for connection in self.m_dotcon_list: + if connection.widget.isReadyToDisc(): + connection.widget.setReadyToDisc(False) + connection.widget.updateLineGradient() + + for line_mov in self.m_line_mov_list: + line_mov.setReadyToDisc(False) + self.m_dotcon_list.clear() + + def SetAsStereo(self, port_id): + port_id_list = [] + for port in canvas.port_list: + if port.port_id in (self.m_port_id, port_id): + port_id_list.append(port.port_id) + + CanvasAddPortGroup(self.m_group_id, self.m_port_mode, self.m_port_type, port_id_list ) + group_name = CanvasGetGroupName(self.m_group_id) + port_name_list = [] + + # get new port_group_num + canvas.settings.beginGroup("CanvasPortGroups") + all_port_group_nums = canvas.settings.childKeys() + canvas.settings.endGroup() + + n = 1 + new_port_group_num_name = 'PortGroup_%i' % n + while new_port_group_num_name in all_port_group_nums: + n +=1 + new_port_group_num_name = 'PortGroup_%i' % n + + port_name_list = [] + for port in canvas.port_list: + if port.port_id in port_id_list: + port_name_list.append(port.port_name) + canvas.settings.remove("ForcedMonoPorts/%s:%s" % (group_name, port.port_name)) + + port_group_tab = [ group_name, self.m_port_mode ] + port_name_list + canvas.settings.setValue("CanvasPortGroups/%s" % new_port_group_num_name, port_group_tab) + + #canvas.callback(ACTION_SAVE_PORT_GROUP, port_id_list.copy(), (self.m_port_mode, group_name, port_name_list), '') + + self.parentItem().updatePositions() + + def connectToHover(self): + if self.m_hover_item: + if self.m_hover_item.type() == CanvasPortType: + hover_port_id_list = [ self.m_hover_item.getPortId() ] + elif self.m_hover_item.type() == CanvasPortGroupType: + hover_port_id_list = self.m_hover_item.getPortsList() + + hover_group_id = self.m_hover_item.getGroupId() + con_list = [] + ports_connected_list = [] + + for porthover_id in hover_port_id_list: + for connection in canvas.connection_list: + 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 == porthover_id) + or (connection.group_out_id == hover_group_id + and connection.port_out_id == porthover_id + and connection.group_in_id == self.m_group_id + and connection.port_in_id == self.m_port_id)): + con_list.append(connection) + ports_connected_list.append(porthover_id) + + if len(con_list) == len(hover_port_id_list): + for connection in con_list: + canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") + else: + for porthover_id in hover_port_id_list: + if not porthover_id in ports_connected_list: + if self.m_port_mode == PORT_MODE_OUTPUT: + conn = "%i:%i:%i:%i" % (self.m_group_id, self.m_port_id, hover_group_id, porthover_id) + else: + conn = "%i:%i:%i:%i" % (hover_group_id, porthover_id, self.m_group_id, self.m_port_id) + canvas.callback(ACTION_PORTS_CONNECT, '', '', conn) + def type(self): return CanvasPortType @@ -173,14 +276,23 @@ class CanvasPort(QGraphicsItem): ): connection.widget.setLocked(True) - if not self.m_line_mov: + if not self.m_line_mov_list: + port_pos, port_group_len = CanvasGetPortGroupPosition( + self.m_group_id, self.m_port_id, + self.m_port_group_id) + if options.use_bezier_lines: - self.m_line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, self) + line_mov = CanvasBezierLineMov(self.m_port_mode, + self.m_port_type, port_pos, + port_group_len, 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) + line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, + port_pos, port_group_len, self) + + line_mov.setPortPosInPortGroupTo(0) + self.m_line_mov_list.append(line_mov) + canvas.is_line_mov = True + line_mov.setZValue(canvas.last_z_value) canvas.last_z_value += 1 self.parentItem().setZValue(canvas.last_z_value) @@ -188,7 +300,7 @@ class CanvasPort(QGraphicsItem): items = canvas.scene.items(event.scenePos(), Qt.ContainsItemShape, Qt.AscendingOrder) #for i in range(len(items)): for _, itemx in enumerate(items): - if itemx.type() != CanvasPortType: + if not itemx.type() in (CanvasPortType, CanvasPortGroupType): continue if itemx == self: continue @@ -201,21 +313,99 @@ class CanvasPort(QGraphicsItem): if item is not None: if item.getPortMode() != self.m_port_mode and item.getPortType() == self.m_port_type: item.setSelected(True) - self.m_hover_item = item + if item.type() == CanvasPortGroupType: + if self.m_hover_item != item: + self.m_hover_item = item + self.resetDotLines() + + if len(self.m_line_mov_list) <= 1: + #make original line going to first port of the hover port_group + for line_mov in self.m_line_mov_list: + line_mov.setPortPosInPortGroupTo(0) + line_mov.setPortGroupLenghtTo(len(self.m_hover_item.m_port_id_list)) + + port_pos, port_group_len = CanvasGetPortGroupPosition( + self.m_group_id, self.m_port_id, self.m_port_group_id) + + #create one line for each port of the hover port_group + for x in range(1, len(self.m_hover_item.m_port_id_list)): + if options.use_bezier_lines: + line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, port_pos, port_group_len, self) + else: + line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, port_pos, port_group_len, self) + line_mov.setPortPosInPortGroupTo(x) + line_mov.setPortGroupLenghtTo(len(self.m_hover_item.m_port_id_list)) + self.m_line_mov_list.append(line_mov) + + hover_group_id = self.m_hover_item.getGroupId() + + for port_id in self.m_hover_item.getPortsList(): + for connection in canvas.connection_list: + 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 == port_id) + or (connection.group_out_id == hover_group_id + and connection.port_out_id == port_id + and connection.group_in_id == self.m_group_id + and connection.port_in_id == self.m_port_id)): + self.m_dotcon_list.append(connection) + + if len(self.m_dotcon_list) == len(self.m_hover_item.getPortsList()): + for connection in self.m_dotcon_list: + connection.widget.setReadyToDisc(True) + connection.widget.updateLineGradient() + + for line_mov in self.m_line_mov_list: + line_mov.setReadyToDisc(True) + + elif item.type() == CanvasPortType: + if self.m_hover_item != item: + self.m_hover_item = item + self.deleteLine2() + self.resetDotLines() + + for connection in canvas.connection_list: + 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 == port_id) + or (connection.group_out_id == hover_group_id + and connection.port_out_id == port_id + and connection.group_in_id == self.m_group_id + and connection.port_in_id == self.m_port_id)): + for line_mov in self.m_line_mov_list: + line_mov.setReadyToDisc(True) + connection.widget.setReadyToDisc(True) + connection.widget.updateLineGradient() + self.m_dotcon_list.append(connection) + elif connection.widget.isReadyToDisc(): + connection.widget.setReadyToDisc(False) + connection.widget.updateLineGradient() else: self.m_hover_item = None + self.deleteLine2() + self.resetDotLines() else: self.m_hover_item = None + self.deleteLine2() + self.resetDotLines() + + for line_mov in self.m_line_mov_list: + line_mov.updateLinePos(event.scenePos()) + + return event.accept() - self.m_line_mov.updateLinePos(event.scenePos()) + 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 + if self.m_line_mov_list: + for line_mov in self.m_line_mov_list: + item = line_mov + canvas.scene.removeItem(item) + del item + self.m_line_mov_list.clear() for connection in canvas.connection_list: if ( @@ -228,34 +418,7 @@ class CanvasPort(QGraphicsItem): 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() - - # FIXME clean ths big if stuff - 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) + self.connectToHover() canvas.scene.clearSelection() @@ -269,7 +432,6 @@ class CanvasPort(QGraphicsItem): def contextMenuEvent(self, event): event.accept() - canvas.scene.clearSelection() self.setSelected(True) @@ -281,7 +443,7 @@ class CanvasPort(QGraphicsItem): 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.setData([conn_id]) act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect) else: act_x_disc = discMenu.addAction("No connections") @@ -290,6 +452,38 @@ class CanvasPort(QGraphicsItem): menu.addMenu(discMenu) act_x_disc_all = menu.addAction("Disconnect &All") act_x_sep_1 = menu.addSeparator() + + if self.m_port_type == PORT_TYPE_AUDIO_JACK and self.m_port_group_id: + StereoMenu = QMenu('Set as Stereo with', menu) + menu.addMenu(StereoMenu) + + #get list of available mono ports settables as stereo with port + port_cousin_list = [] + for port in canvas.port_list: + if (port.port_type == PORT_TYPE_AUDIO_JACK + and port.group_id == self.m_group_id + and port.port_mode == self.m_port_mode): + port_cousin_list.append(port.port_id) + + selfport_index = port_cousin_list.index(self.m_port_id) + stereo_able_ids_list = [] + if selfport_index > 0: + stereo_able_ids_list.append(port_cousin_list[selfport_index -1]) + if selfport_index < len(port_cousin_list) -1: + stereo_able_ids_list.append(port_cousin_list[selfport_index +1]) + + at_least_one = False + for port in canvas.port_list: + if port.port_id in stereo_able_ids_list and not port.port_group_id: + act_x_setasstereo = StereoMenu.addAction(port.port_name) + act_x_setasstereo.setData([self, port.port_id]) + act_x_setasstereo.triggered.connect(canvas.qobject.SetasStereoWith) + at_least_one = True + + if not at_least_one: + act_x_setasstereo = StereoMenu.addAction('no available mono port') + act_x_setasstereo.setEnabled(False) + act_x_info = menu.addAction("Get &Info") act_x_rename = menu.addAction("&Rename") @@ -305,23 +499,24 @@ class CanvasPort(QGraphicsItem): act_selected = menu.exec_(event.screenPos()) if act_selected == act_x_disc_all: - self.triggerDisconnect(conn_list) + 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, "") - + def setPortSelected(self, yesno): for connection in canvas.connection_list: if ( (connection.group_out_id == self.m_group_id and - connection.port_out_id == self.m_port_id) + 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.port_in_id == self.m_port_id) + ): connection.widget.updateLineSelected() def itemChange(self, change, value): @@ -336,12 +531,19 @@ class CanvasPort(QGraphicsItem): canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "") def boundingRect(self): - return QRectF(0, 0, self.m_port_width + 12, self.m_port_height) + if self.m_port_group_id: + if self.m_port_mode == PORT_MODE_INPUT: + return QRectF(0, 0, canvas.theme.port_in_port_group_width, self.m_port_height) + else: + return QRectF(self.m_port_width +12 - canvas.theme.port_in_port_group_width, + 0, canvas.theme.port_in_port_group_width, self.m_port_height) + else: + 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)) - + selected = self.isSelected() theme = canvas.theme if self.m_port_type == PORT_TYPE_AUDIO_JACK: @@ -380,7 +582,7 @@ class CanvasPort(QGraphicsItem): lineHinting = poly_pen.widthF() / 2 - poly_locx = [0, 0, 0, 0, 0] + poly_locx = [0, 0, 0, 0, 0, 0] poly_corner_xhinting = (float(canvas.theme.port_height)/2) % floor(float(canvas.theme.port_height)/2) if poly_corner_xhinting == 0: poly_corner_xhinting = 0.5 * (1 - 7 / (float(canvas.theme.port_height)/2)) @@ -394,12 +596,14 @@ class CanvasPort(QGraphicsItem): poly_locx[2] = self.m_port_width + 12 - poly_corner_xhinting poly_locx[3] = self.m_port_width + 5 - lineHinting poly_locx[4] = lineHinting + poly_locx[5] = canvas.theme.port_in_port_group_width elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: poly_locx[0] = lineHinting poly_locx[1] = self.m_port_width + 5 - lineHinting poly_locx[2] = self.m_port_width + 5 - lineHinting poly_locx[3] = self.m_port_width + 5 - lineHinting poly_locx[4] = lineHinting + poly_locx[5] = canvas.theme.port_in_port_group_width else: qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return @@ -413,12 +617,14 @@ class CanvasPort(QGraphicsItem): poly_locx[2] = 0 + poly_corner_xhinting poly_locx[3] = 7 + lineHinting poly_locx[4] = self.m_port_width + 12 - lineHinting + poly_locx[5] = self.m_port_width + 12 - canvas.theme.port_in_port_group_width - lineHinting elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: poly_locx[0] = self.m_port_width + 12 - lineHinting poly_locx[1] = 5 + lineHinting poly_locx[2] = 5 + lineHinting poly_locx[3] = 5 + lineHinting poly_locx[4] = self.m_port_width + 12 - lineHinting + poly_locx[5] = self.m_port_width + 12 -canvas.theme.port_in_port_group_width - lineHinting else: qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return @@ -427,13 +633,46 @@ class CanvasPort(QGraphicsItem): qCritical("PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode)) return - polygon = QPolygonF() - polygon += QPointF(poly_locx[0], lineHinting) - polygon += QPointF(poly_locx[1], lineHinting) - polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2) - polygon += QPointF(poly_locx[3], canvas.theme.port_height - lineHinting) - polygon += QPointF(poly_locx[4], canvas.theme.port_height - lineHinting) - polygon += QPointF(poly_locx[0], lineHinting) + 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() + + if self.m_port_group_id: + first_of_port_group = False + last_of_port_group = False + + for port_group in canvas.port_group_list: + if port_group.port_group_id == self.m_port_group_id: + if self.m_port_id == port_group.port_id_list[0]: + first_of_port_group = True + if self.m_port_id == port_group.port_id_list[-1]: + last_of_port_group = True + break + + if first_of_port_group: + polygon += QPointF(poly_locx[0] , lineHinting) + polygon += QPointF(poly_locx[5] , lineHinting) + else: + polygon += QPointF(poly_locx[0] , 0) + polygon += QPointF(poly_locx[5] , 0) + + if last_of_port_group: + polygon += QPointF(poly_locx[5], canvas.theme.port_height - lineHinting) + polygon += QPointF(poly_locx[0], canvas.theme.port_height - lineHinting) + else: + polygon += QPointF(poly_locx[5], canvas.theme.port_height) + polygon += QPointF(poly_locx[0], canvas.theme.port_height) + else: + polygon += QPointF(poly_locx[0], lineHinting) + polygon += QPointF(poly_locx[1], lineHinting) + polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2) + polygon += QPointF(poly_locx[3], canvas.theme.port_height - lineHinting) + polygon += QPointF(poly_locx[4], canvas.theme.port_height - lineHinting) + polygon += QPointF(poly_locx[0], lineHinting) if canvas.theme.port_bg_pixmap: portRect = polygon.boundingRect().adjusted(-lineHinting+1, -lineHinting+1, lineHinting-1, lineHinting-1) @@ -447,21 +686,44 @@ class CanvasPort(QGraphicsItem): painter.setPen(text_pen) painter.setFont(self.m_port_font) - painter.drawText(text_pos, self.m_port_name) + + if self.m_port_group_id: + print_name = CanvasGetPortPrintName( + self.m_group_id, self.m_port_id, + self.m_port_group_id) + print_name_size = QFontMetrics(self.m_port_font).width(print_name) + if self.m_port_mode == PORT_MODE_OUTPUT: + + text_pos = QPointF(self.m_port_width + 9 - print_name_size, canvas.theme.port_text_ypos) + + if print_name_size > (canvas.theme.port_in_port_group_width - 6): + painter.setPen(QPen(poly_color, 3)) + painter.drawLine(poly_locx[5], 3, poly_locx[5], canvas.theme.port_height - 3) + painter.setPen(text_pen) + painter.setFont(self.m_port_font) + painter.drawText(text_pos, print_name) + + else: + 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: - conn_pen.setCosmetic(True) - conn_pen.setWidthF(0.4) - painter.setPen(conn_pen) + painter.setPen(Qt.NoPen) + painter.setBrush(conn_pen.brush()) if self.m_port_mode == PORT_MODE_INPUT: - connLineX = portRect.left()+1 + connRect = QRectF(portRect.topLeft(), QSizeF(2, portRect.height())) else: - connLineX = portRect.right()-1 - conn_path = QPainterPath() - conn_path.addRect(QRectF(connLineX-1, portRect.top(), 2, portRect.height())) - painter.fillPath(conn_path, conn_pen.brush()) - painter.drawLine(QLineF(connLineX, portRect.top(), connLineX, portRect.bottom())) + connRect = QRectF(QPointF(portRect.right()-2, portRect.top()), QSizeF(2, portRect.height())) + + painter.drawRect(connRect) + + self.m_last_selected_state = self.isSelected() painter.restore() diff --git a/source/frontend/patchcanvas/canvasportgroup.py b/source/frontend/patchcanvas/canvasportgroup.py new file mode 100644 index 000000000..f1e14f61d --- /dev/null +++ b/source/frontend/patchcanvas/canvasportgroup.py @@ -0,0 +1,694 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# PatchBay Canvas engine using QGraphicsView/Scene +# Copyright (C) 2010-2019 Filipe Coelho +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the doc/GPL.txt file. + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) + +from math import floor + +from PyQt5.QtCore import qCritical, Qt, QLineF, QPointF, QRectF, QTimer +from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QPainter, QPainterPath, QPen, QPolygonF +from PyQt5.QtWidgets import QGraphicsItem, QMenu + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Custom) + +from . import ( + canvas, + features, + options, + port_mode2str, + port_type2str, + CanvasPortType, + ANTIALIASING_FULL, + ACTION_PORT_INFO, + ACTION_PORT_RENAME, + ACTION_PORTS_CONNECT, + ACTION_PORTS_DISCONNECT, + PORT_MODE_INPUT, + PORT_MODE_OUTPUT, + PORT_TYPE_AUDIO_JACK, + PORT_TYPE_MIDI_ALSA, + PORT_TYPE_MIDI_JACK, + PORT_TYPE_PARAMETER, +) + +from .canvasbezierlinemov import CanvasBezierLineMov +from .canvaslinemov import CanvasLineMov +from .theme import Theme +from .utils import CanvasGetFullPortName, CanvasGetPortConnectionList, CanvasGetPortGroupPosition + +# ------------------------------------------------------------------------------------------------------------ + + + +class CanvasPortGroup(QGraphicsItem): + def __init__(self, port_group_id, port_mode, port_type, port_id_list, parent): + QGraphicsItem.__init__(self) + self.setParentItem(parent) + + # Save Variables, useful for later + self.m_port_group_id = port_group_id + self.m_port_mode = port_mode + self.m_port_type = port_type + self.m_port_id_list = port_id_list + self.m_group_id = parent.getGroupId() + + # Base Variables + self.m_port_group_width = 15 + self.m_port_group_height = canvas.theme.port_height + self.m_port_group_font = QFont() + self.m_port_group_font.setFamily(canvas.theme.port_font_name) + self.m_port_group_font.setPixelSize(canvas.theme.port_font_size) + self.m_port_group_font.setWeight(canvas.theme.port_font_state) + + self.m_line_mov_list = [] + self.m_r_click_conn = None + self.m_r_click_time = 0 + self.m_dotcon_list = [] + 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 getPortGroupId(self): + return self.m_port_group_id + + def getPortMode(self): + return self.m_port_mode + + def getPortType(self): + return self.m_port_type + + def getGroupId(self): + return self.m_group_id + + def getPortWidth(self): + return self.m_port_group_width + + def getPortGroupWidth(self): + return self.m_port_group_width + + def getPortGroupHeight(self): + return self.m_port_height + + def getPortsList(self): + return self.m_port_id_list + + def setPortMode(self, port_mode): + self.m_port_mode = port_mode + self.update() + + def type(self): + return CanvasPortGroupType + + def setPortGroupWidth(self, port_group_width): + if port_group_width < self.m_port_group_width: + QTimer.singleShot(0, canvas.scene.update) + + self.m_port_group_width = port_group_width + self.update() + + def SplitToMonos(self): + groupItem = self.parentItem() + CanvasSplitPortGroup(self.m_port_group_id) + groupItem.updatePositions() + + def ConnectToHover(self): + if self.m_hover_item: + if self.m_hover_item.type() == CanvasPortType: + hover_port_id_list = [ self.m_hover_item.getPortId() ] + elif self.m_hover_item.type() == CanvasPortGroupType: + hover_port_id_list = self.m_hover_item.getPortsList() + + con_list = [] + ports_connected_list = [] + + for port in canvas.port_list: + if port.port_id == hover_port_id_list[0]: + hover_port_group_id = port.group_id + break + else: + return + + for port in canvas.port_list: + if port.port_id in self.m_port_id_list: + selfgroup_id = port.group_id + break + else: + return + + for portself_id in self.m_port_id_list: + for porthover_id in hover_port_id_list: + for connection in canvas.connection_list: + if ( (connection.port_out_id == portself_id and connection.port_in_id == porthover_id) or + (connection.port_out_id == porthover_id and connection.port_in_id == portself_id) ): + + if ( (self.m_port_id_list.index(portself_id) % len(hover_port_id_list)) == + (hover_port_id_list.index(porthover_id) % len(self.m_port_id_list)) ): + con_list.append(connection) + ports_connected_list.append([portself_id, porthover_id]) + else: + canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") + + maxport_group = len(hover_port_id_list) if len(hover_port_id_list) > len(self.m_port_id_list) else len(self.m_port_id_list) + + if len(con_list) == maxport_group: + for connection in con_list: + canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") + else: + for portself_id in self.m_port_id_list: + for porthover_id in hover_port_id_list: + if ((self.m_port_id_list.index(portself_id) % len(hover_port_id_list)) == + (hover_port_id_list.index(porthover_id) % len(self.m_port_id_list))): + if not [portself_id, porthover_id] in ports_connected_list: + if self.m_port_mode == PORT_MODE_OUTPUT: + conn = "%i:%i:%i:%i" % (selfgroup_id, portself_id, hover_port_group_id, porthover_id) + else: + conn = "%i:%i:%i:%i" % (hover_port_group_id, porthover_id, selfgroup_id, portself_id) + canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn) + + def resetDotLines(self): + for connection in self.m_dotcon_list: + if connection.widget.isReadyToDisc(): + connection.widget.setReadyToDisc(False) + connection.widget.updateLineGradient() + + for line_mov in self.m_line_mov_list: + line_mov.setReadyToDisc(False) + + def resetLineMovPositions(self): + for line_mov in self.m_line_mov_list: + if self.m_line_mov_list.index(line_mov) < len(self.m_port_id_list): + line_mov.setPortPosInPortGroupTo(line_mov.m_port_posinport_group) + line_mov.setPortGroupLenghtTo(line_mov.m_port_group_lenght) + else: + item = line_mov + canvas.scene.removeItem(item) + del item + + self.m_line_mov_list = self.m_line_mov_list[:len(self.m_port_id_list)] + + + def mousePressEvent(self, event): + if event.button() == Qt.LeftButton: + self.m_hover_item = None + self.m_mouse_down = True + self.m_cursor_moving = False + + #elif event.button() == Qt.RightButton: + #if canvas.is_line_mov: + #if self.m_hover_item: + #self.ConnectToHover() + ##self.m_r_click_conn = self.m_hover_item + ##self.m_r_click_time = time.time() + + #for line_mov in self.m_line_mov_list: + #line_mov.toggleReadyToDisc() + #line_mov.updateLinePos(event.scenePos()) + + 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 in self.m_port_id_list or connection.port_in_id in self.m_port_id_list: + connection.widget.setLocked(True) + + if not self.m_line_mov_list: + self.m_r_click_conn = None + canvas.last_z_value += 1 + self.setZValue(canvas.last_z_value) + canvas.last_z_value += 1 + for port in canvas.port_list: + if port.port_id in self.m_port_id_list: + port.widget.setZValue(canvas.last_z_value) + + #for port in canvas.port_list: + #if (not port.port_id in self.m_port_id_list and + #(port.port_type != self.m_port_type or + #port.port_mode == self.m_port_mode)): + #port.widget.setOpacity(0.35) + + #for port_group in canvas.port_group_list: + #if (port_group.port_group_id != self.m_port_group_id and + #(port_group.port_type != self.m_port_type or + #port_group.port_mode == self.m_port_mode)): + #port_group.widget.setOpacity(0.35) + + for port_id in self.m_port_id_list: + port_posinport_group, port_group_lenght = CanvasGetPortPositionAndPortGroupLenght(port_id, self.m_port_group_id) + + if options.use_bezier_lines: + line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, port_posinport_group, port_group_lenght, self) + else: + line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, port_posinport_group, port_group_lenght, self) + #line_mov.setZValue(canvas.last_z_value) + self.m_line_mov_list.append(line_mov) + + canvas.is_line_mov = True + 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 or items[i].type() == CanvasPortGroupType: + 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) + if item.type() == CanvasPortType: + if self.m_hover_item != item: + self.m_hover_item = item + self.resetDotLines() + self.resetLineMovPositions() + for line_mov in self.m_line_mov_list: + line_mov.setPortPosInPortGroupTo(PORT_IN_PORT_GROUP_POSITION_MONO) + + self.m_dotcon_list = [] + for port_id in self.m_port_id_list: + for connection in canvas.connection_list: + if ( (connection.port_out_id == self.m_hover_item.getPortId() and connection.port_in_id == port_id) or + (connection.port_out_id == port_id and connection.port_in_id == self.m_hover_item.getPortId()) ): + self.m_dotcon_list.append(connection) + + if len(self.m_dotcon_list) == len(self.m_port_id_list): + for connection in self.m_dotcon_list: + connection.widget.setReadyToDisc(True) + connection.widget.updateLineGradient() + for line_mov in self.m_line_mov_list: + line_mov.setReadyToDisc(True) + + elif item.type() == CanvasPortGroupType: + if self.m_hover_item != item: + self.m_hover_item = item + self.resetDotLines() + self.resetLineMovPositions() + + if len(self.m_hover_item.m_port_id_list) <= len(self.m_line_mov_list): + for line_mov in self.m_line_mov_list: + line_mov.setPortPosInPortGroupTo(line_mov.m_port_posinport_group % len(self.m_hover_item.m_port_id_list)) + line_mov.setPortGroupLenghtTo(len(self.m_hover_item.m_port_id_list)) + else: + + for line_mov in self.m_line_mov_list: + line_mov.setPortGroupLenghtTo(len(self.m_hover_item.m_port_id_list)) + + #create new lines when line is hover a biggest port_group + for x in range(len(self.m_port_id_list), len(self.m_hover_item.m_port_id_list)): + port_posinport_group = x % len(self.m_port_id_list) + port_group_lenght = len(self.m_port_id_list) + if options.use_bezier_lines: + line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, port_posinport_group, port_group_lenght, self) + else: + line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, port_posinport_group, port_group_lenght, self) + + line_mov.setPortPosInPortGroupTo(x) + line_mov.setPortGroupLenghtTo(len(self.m_hover_item.m_port_id_list)) + self.m_line_mov_list.append(line_mov) + + self.m_dotcon_list = [] + symetric_con_list = [] + for portself_id in self.m_port_id_list: + for porthover_id in self.m_hover_item.getPortsList(): + for connection in canvas.connection_list: + if ( (connection.port_out_id == portself_id and connection.port_in_id == porthover_id) or + (connection.port_out_id == porthover_id and connection.port_in_id == portself_id) ): + if (self.m_port_id_list.index(portself_id) % len(self.m_hover_item.getPortsList())) == (self.m_hover_item.m_port_id_list.index(porthover_id) % len(self.m_port_id_list)): + self.m_dotcon_list.append(connection) + symetric_con_list.append(connection) + else: + self.m_dotcon_list.append(connection) + connection.widget.setReadyToDisc(True) + connection.widget.updateLineGradient() + + if len(self.m_port_id_list) >= len(self.m_hover_item.getPortsList()): + biggest_list = self.m_port_id_list + else: + biggest_list = self.m_hover_item.getPortsList() + + if len(symetric_con_list) == len(biggest_list): + for connection in self.m_dotcon_list: + connection.widget.setReadyToDisc(True) + connection.widget.updateLineGradient() + for line_mov in self.m_line_mov_list: + line_mov.setReadyToDisc(True) + + else: + self.m_hover_item = None + self.m_r_click_conn = None + self.resetDotLines() + self.resetLineMovPositions() + for port_id in self.m_port_id_list: + port_posinport_group = self.m_port_id_list.index(port_id) + self.m_line_mov_list[port_posinport_group].setPortPosInPortGroupTo(port_posinport_group) + + else: + if self.m_hover_item: + self.m_hover_item = None + self.m_r_click_conn = None + self.resetDotLines() + self.resetLineMovPositions() + for port_id in self.m_port_id_list: + port_posinport_group = self.m_port_id_list.index(port_id) + self.m_line_mov_list[port_posinport_group].setPortPosInPortGroupTo(port_posinport_group) + + for line_mov in self.m_line_mov_list: + line_mov.updateLinePos(event.scenePos()) + return event.accept() + + QGraphicsItem.mouseMoveEvent(self, event) + + def mouseReleaseEvent(self, event): + if event.button() == Qt.LeftButton: + if self.m_mouse_down: + + for line_mov in self.m_line_mov_list: + item = line_mov + canvas.scene.removeItem(item) + del item + self.m_line_mov_list.clear() + + for port in canvas.port_list: + port.widget.setOpacity(1) + + for port_group in canvas.port_group_list: + port_group.widget.setOpacity(1) + + for connection in canvas.connection_list: + if connection.port_out_id in self.m_port_id_list or connection.port_in_id in self.m_port_id_list: + connection.widget.setLocked(False) + if self.m_hover_item: + #if self.m_r_click_conn != self.m_hover_item: + #if time.time() > self.m_r_click_time + 0.3: + self.ConnectToHover() + + canvas.scene.clearSelection() + else: + if self.m_r_click_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 + canvas.is_line_mov = False + QGraphicsItem.mouseReleaseEvent(self, event) + + + def contextMenuEvent(self, event): + if canvas.is_line_mov: + return event.ignore() + + canvas.scene.clearSelection() + self.setSelected(True) + + menu = QMenu() + discMenu = QMenu("Disconnect", menu) + + #get all connected ports and all connected groups + all_connected_ports_ids = [] + group_ids_list = [] + for selfport_id in self.m_port_id_list: + con_list = CanvasGetPortConnectionList(self.m_group_id, selfport_id) + for con_id, group_id, port_id in con_list: + for port in canvas.port_list: + if port.port_id == port_id: + if not port.group_id in group_ids_list: + group_ids_list.append(port.group_id) + + if not port_id in all_connected_ports_ids: + all_connected_ports_ids.append(port_id) + + #Display 'no connection' action in disconnect menu + if len(all_connected_ports_ids) == 0: + action_noconnection = discMenu.addAction('no connection') + action_noconnection.setEnabled(False) + + #check each group to get no trouble with ports in the same port_group which could have non-consecutive port ids + for group_id in group_ids_list: + #get a list of connected port from this group + gr_port_ids_list = [] + for port in canvas.port_list: + if port.group_id == group_id: + if port.port_id in all_connected_ports_ids: + if not port.port_id in gr_port_ids_list: + gr_port_ids_list.append(port.port_id) + + port_group_list_ids = [] + port_group_dirty_connect_list = [] #is used to display individual ports of port_group or not + last_is_dirty_port_group = False #is used to display menu separator or not + + #create action for each port of this group connected to the box + for port in canvas.port_list: + if port.port_id in gr_port_ids_list: + + #get port connection list + port_con_list = [] + for connection in canvas.connection_list: + if ( (connection.port_out_id in self.m_port_id_list and connection.port_in_id == port.port_id) or + (connection.port_out_id == port.port_id and connection.port_in_id in self.m_port_id_list) ): + port_con_list.append(connection.connection_id) + + if port.port_group_id: + port_alone_connected = False + + if not port.port_group_id in port_group_list_ids: + port_group_list_ids.append(port.port_group_id) + + for port_group in canvas.port_group_list: + if port_group.port_group_id == port.port_group_id: + + #count ports from port_group connected to the box + c = 0 + for p in port_group.port_id_list: + if p in all_connected_ports_ids: + c += 1 + + if c == 1: + port_alone_connected = True + + #Set disconnect port action if port is the only one of the port_group connected to the box + if port_alone_connected: + if last_is_dirty_port_group: + discMenu.addSeparator() + last_is_dirty_port_group = False + + full_port_name = CanvasGetFullPortName(port.port_id) + act_x_disc_port = discMenu.addAction(full_port_name) + act_x_disc_port.setData(port_con_list) + act_x_disc_port.triggered.connect(canvas.qobject.PortContextMenuDisconnect) + + + else: + #get port_group connection list + port_group_con_list = [] + for connection in canvas.connection_list: + if (connection.port_out_id in self.m_port_id_list and connection.port_in_id in port_group.port_id_list or + connection.port_out_id in port_group.port_id_list and connection.port_in_id in self.m_port_id_list): + port_group_con_list.append(connection.connection_id) + if connection.port_out_id in self.m_port_id_list: + if (self.m_port_id_list.index(connection.port_out_id) % len(port_group.port_id_list) != + port_group.port_id_list.index(connection.port_in_id) % len(self.m_port_id_list) ): + port_group_dirty_connect_list.append(port_group.port_group_id) + else: + if (self.m_port_id_list.index(connection.port_in_id) % len(port_group.port_id_list) != + port_group.port_id_list.index(connection.port_out_id) % len(self.m_port_id_list) ): + port_group_dirty_connect_list.append(port_group.port_group_id) + + if port_group.port_group_id in port_group_dirty_connect_list: + discMenu.addSeparator() + last_is_dirty_port_group = True + elif last_is_dirty_port_group: + discMenu.addSeparator() + last_is_dirty_port_group = False + + + #set port_group action in submenu + port_group_print_name = CanvasGetPortGroupFullName(port_group.port_group_id) + act_x_disc_port_group = discMenu.addAction(port_group_print_name) + act_x_disc_port_group.setData(port_group_con_list) + act_x_disc_port_group.triggered.connect(canvas.qobject.PortContextMenuDisconnect) + + + #set port action in submenu (if port is in a port_group and isn't the only one connected to the box) + if not port_alone_connected and port.port_group_id in port_group_dirty_connect_list: + port_print_name = CanvasGetPortPrintName(port.port_id, port.port_group_id) + act_x_disc_port = discMenu.addAction(' → ' + port_print_name) + act_x_disc_port.setData(port_con_list) + act_x_disc_port.triggered.connect(canvas.qobject.PortContextMenuDisconnect) + + else: + if last_is_dirty_port_group: + discMenu.addSeparator() + last_is_dirty_port_group = False + + #set port action in submenu (if port is not in a port_group) + full_port_name = CanvasGetFullPortName(port.group_id, port.port_id) + act_x_disc_port = discMenu.addAction(full_port_name) + act_x_disc_port.setData(port_con_list) + act_x_disc_port.triggered.connect(canvas.qobject.PortContextMenuDisconnect) + + + menu.addMenu(discMenu) + act_x_disc_alltheport_group = menu.addAction("Disconnect &All") + act_x_sep_1 = menu.addSeparator() + act_x_setasmono = menu.addAction('Split to Monos') + + act_selected = menu.exec_(event.screenPos()) + + if act_selected == act_x_disc_alltheport_group: + for connection in canvas.connection_list: + if connection.port_out_id in self.m_port_id_list or connection.port_in_id in self.m_port_id_list: + canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") + + elif act_selected == act_x_setasmono: + self.SplitToMonos() + + event.accept() + + def boundingRect(self): + self.m_port_group_width = self.getPortGroupWidth() + if self.m_port_mode == PORT_MODE_INPUT: + return QRectF(canvas.theme.port_in_port_group_width, 0, self.m_port_group_width + 12 - canvas.theme.port_in_port_group_width, canvas.theme.port_height * len(self.m_port_id_list)) + else: + return QRectF(0, 0, self.m_port_group_width + 12 - canvas.theme.port_in_port_group_width, canvas.theme.port_height * len(self.m_port_id_list)) + + def paint(self, painter, option, widget): + painter.save() + painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) + + lineHinting = canvas.theme.port_audio_jack_pen.widthF() / 2 + + poly_locx = [0, 0, 0, 0, 0] + poly_corner_xhinting = (float(canvas.theme.port_height)/2) % floor(float(canvas.theme.port_height)/2) + if poly_corner_xhinting == 0: + poly_corner_xhinting = 0.5 * (1 - 7 / (float(canvas.theme.port_height)/2)) + + if self.m_port_mode == PORT_MODE_INPUT: + port_width = canvas.theme.port_in_port_group_width + + for port in canvas.port_list: + if port.port_id in self.m_port_id_list: + port_print_name = CanvasGetPortPrintName(port.port_id, self.m_port_group_id) + port_in_p_width = QFontMetrics(self.m_port_group_font).width(port_print_name) + 3 + port_width = max(port_width, port_in_p_width) + + text_pos = QPointF(port_width + 3, canvas.theme.port_text_ypos + (canvas.theme.port_height * (len(self.m_port_id_list) -1)/2)) + + if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: + poly_locx[0] = canvas.theme.port_in_port_group_width - lineHinting + poly_locx[1] = self.m_port_group_width + 5 + lineHinting + poly_locx[2] = self.m_port_group_width + 12 + lineHinting + poly_locx[3] = self.m_port_group_width + 5 + lineHinting + poly_locx[4] = canvas.theme.port_in_port_group_width - lineHinting + elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: + poly_locx[0] = canvas.theme.port_in_port_group_width - lineHinting + poly_locx[1] = self.m_port_group_width + 5 + lineHinting + poly_locx[2] = self.m_port_group_width + 5 + lineHinting + poly_locx[3] = self.m_port_group_width + 5 + lineHinting + poly_locx[4] = canvas.theme.port_in_port_group_width - lineHinting + else: + qCritical("PatchCanvas::CanvasPortGroup.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 + (canvas.theme.port_height * (len(self.m_port_id_list) -1)/2)) + + if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: + poly_locx[0] = self.m_port_group_width + 12 - canvas.theme.port_in_port_group_width - lineHinting + poly_locx[1] = 7 + lineHinting + poly_locx[2] = 0 + lineHinting + poly_locx[3] = 7 + lineHinting + poly_locx[4] = self.m_port_group_width + 12 - canvas.theme.port_in_port_group_width - lineHinting + elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: + poly_locx[0] = self.m_port_group_width + 12 - canvas.theme.port_in_port_group_width - lineHinting + poly_locx[1] = 5 + lineHinting + poly_locx[2] = 5 + lineHinting + poly_locx[3] = 5 + lineHinting + poly_locx[4] = self.m_port_group_width + 12 - canvas.theme.port_in_port_group_width - lineHinting + else: + qCritical("PatchCanvas::CanvasPortGroup.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) + return + + else: + qCritical("PatchCanvas::CanvasPortGroup.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode)) + return + + poly_pen = canvas.theme.port_group_audio_jack_pen_sel if self.isSelected() else canvas.theme.port_group_audio_jack_pen + text_pen = canvas.theme.port_audio_jack_text_sel if self.isSelected() else canvas.theme.port_audio_jack_text + + if self.m_port_mode == PORT_MODE_OUTPUT: + real_port_group_width = self.m_port_group_width - canvas.theme.port_in_port_group_width + port_group_gradient = QLinearGradient(real_port_group_width -40, 0, real_port_group_width, 0) + port_group_gradient.setColorAt(0, canvas.theme.port_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_audio_jack_bg) + port_group_gradient.setColorAt(1, canvas.theme.port_group_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_group_audio_jack_bg) + else: + port_group_gradient = QLinearGradient(canvas.theme.port_in_port_group_width, 0, canvas.theme.port_in_port_group_width + 40, 0) + port_group_gradient.setColorAt(0, canvas.theme.port_group_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_group_audio_jack_bg) + port_group_gradient.setColorAt(1, canvas.theme.port_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_audio_jack_bg) + + + polygon = QPolygonF() + polygon += QPointF(poly_locx[0], lineHinting) + polygon += QPointF(poly_locx[1], lineHinting) + polygon += QPointF(poly_locx[2], float(canvas.theme.port_height / 2) ) + polygon += QPointF(poly_locx[2], float(canvas.theme.port_height * (len(self.m_port_id_list) - 1/2)) ) + polygon += QPointF(poly_locx[3], canvas.theme.port_height * len(self.m_port_id_list) - lineHinting) + polygon += QPointF(poly_locx[4], canvas.theme.port_height * len(self.m_port_id_list) - lineHinting) + + if canvas.theme.port_bg_pixmap: + portRect = polygon.boundingRect() + portPos = portRect.topLeft() + painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos) + else: + painter.setBrush(port_group_gradient) + + painter.setPen(poly_pen) + painter.drawPolygon(polygon) + + painter.setPen(text_pen) + painter.setFont(self.m_port_group_font) + port_group_name = CanvasGetPortGroupName(self.m_port_id_list) + painter.drawText(text_pos, port_group_name) + + if self.isSelected() != self.m_last_selected_state: + CanvasUpdateSelectedLines() + + self.m_last_selected_state = self.isSelected() + + painter.restore() + diff --git a/source/frontend/patchcanvas/patchcanvas.py b/source/frontend/patchcanvas/patchcanvas.py index b3bfd946f..5f273e64a 100644 --- a/source/frontend/patchcanvas/patchcanvas.py +++ b/source/frontend/patchcanvas/patchcanvas.py @@ -31,6 +31,7 @@ from . import ( options, group_dict_t, port_dict_t, + port_group_dict_t, connection_dict_t, bool2str, icon2str, @@ -57,6 +58,7 @@ from .canvasbezierline import CanvasBezierLine from .canvasline import CanvasLine from .theme import Theme, getDefaultTheme, getThemeName from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRemoveItemFX +from .utils import CanvasGetPortGroupPosition # FIXME from . import * @@ -226,6 +228,7 @@ def clear(): canvas.group_list = [] canvas.port_list = [] + canvas.port_group_list = [] canvas.connection_list = [] canvas.group_plugin_map = {} @@ -448,6 +451,7 @@ def splitGroup(group_id): port_dict.port_name = port.port_name port_dict.port_mode = port.port_mode port_dict.port_type = port.port_type + port_dict.port_group_id = port.port_group_id port_dict.is_alternate = port.is_alternate port_dict.widget = None ports_data.append(port_dict) @@ -536,6 +540,7 @@ def joinGroup(group_id): port_dict.port_name = port.port_name port_dict.port_mode = port.port_mode port_dict.port_type = port.port_type + port_dict.port_group_id = port.port_group_id port_dict.is_alternate = port.is_alternate port_dict.widget = None ports_data.append(port_dict) @@ -696,7 +701,7 @@ def setGroupAsPlugin(group_id, plugin_id, hasUI, hasInlineDisplay): # ------------------------------------------------------------------------------------------------------------ -def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False): +def addPort(group_id, port_id, port_name, port_mode, port_type, port_group_id, is_alternate=False): if canvas.debug: print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % ( group_id, port_id, port_name.encode(), @@ -718,7 +723,9 @@ def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=Fal else: n = 0 box_widget = group.widgets[n] - port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate) + port_widget = box_widget.addPortFromGroup( + port_id, port_mode, port_type, + port_name, port_group_id, is_alternate) break if not (box_widget and port_widget): @@ -732,6 +739,7 @@ def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=Fal port_dict.port_name = port_name port_dict.port_mode = port_mode port_dict.port_type = port_type + port_dict.port_group_id = port_group_id port_dict.is_alternate = is_alternate port_dict.widget = port_widget canvas.port_list.append(port_dict) @@ -807,7 +815,7 @@ def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_ 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: diff --git a/source/frontend/patchcanvas/utils.py b/source/frontend/patchcanvas/utils.py index 33e638c89..f434301c6 100644 --- a/source/frontend/patchcanvas/utils.py +++ b/source/frontend/patchcanvas/utils.py @@ -83,6 +83,71 @@ def CanvasGetPortConnectionList(group_id, port_id): return conn_list +def CanvasGetPortGroupPosition(group_id, port_id, port_group_id): + if port_group_id < 0: + return (0, 1) + + for port_group in canvas.port_group_list: + if (port_group.group_id == group_id + and port_group.port_group_id == port_group_id): + for i in range(len(port_group.port_id_list)): + if port_id == port_group.port_id_list[i]: + return (i, len(port_group.port_id_list)) + return (0, 1) + +def CanvasGetPortGroupName(group_id, ports_ids_list): + ports_names = [] + + for port in canvas.port_list: + if port.group_id == group_id and port.port_id in ports_ids_list: + ports_names.append(port.port_name) + + if len(ports_names) < 2: + return '' + + port_group_name_ends = ( ' ', '_', '.', '-', '#', ':', 'out', 'in', 'Out', + 'In', 'Output', 'Input', 'output', 'input' ) + + # set port_group name + port_group_name = '' + checkCharacter = True + + for c in ports_names[0]: + for eachname in ports_names: + if not eachname.startswith(port_group_name + c): + checkCharacter = False + break + if not checkCharacter: + break + port_group_name += c + + # reduce port_group name until it ends with one of the characters + # in port_group_name_ends + check = False + while not check: + for x in port_group_name_ends: + if port_group_name.endswith(x): + check = True + break + + if len(port_group_name) == 0 or port_group_name in ports_names: + check = True + + if not check: + port_group_name = port_group_name[:-1] + + return port_group_name + +def CanvasGetPortPrintName(group_id, port_id, port_group_id): + for port_group in canvas.port_group_list: + if (port_group.group_id == group_id + and port_group.port_group_id == port_group_id): + port_group_name = CanvasGetPortGroupName(group_id, + port_group.port_id_list) + for port in canvas.port_list: + if port.group_id == group_id and port.port_id == port_id: + return port.port_name.replace(port_group_name, '', 1) + def CanvasCallback(action, value1, value2, value_str): if canvas.debug: print("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode()))