/* * Patchbay Canvas engine using QGraphicsView/Scene * Copyright (C) 2010-2012 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 COPYING file */ #include "canvasbox.h" #include #include #include #include #include #include #include #include "canvasline.h" #include "canvasbezierline.h" #include "canvasport.h" #include "canvasboxshadow.h" #include "canvasicon.h" START_NAMESPACE_PATCHCANVAS CanvasBox::CanvasBox(int group_id, QString group_name, Icon icon, QGraphicsItem* parent) : QGraphicsItem(parent, canvas.scene) { // Save Variables, useful for later m_group_id = group_id; m_group_name = group_name; // Base Variables p_width = 50; p_height = 25; m_last_pos = QPointF(); m_splitted = false; m_splitted_mode = PORT_MODE_NULL; m_cursor_moving = false; m_forced_split = false; m_mouse_down = false; m_port_list_ids.clear(); m_connection_lines.clear(); // Set Font m_font_name = QFont(canvas.theme->box_font_name, canvas.theme->box_font_size, canvas.theme->box_font_state); m_font_port = QFont(canvas.theme->port_font_name, canvas.theme->port_font_size, canvas.theme->port_font_state); // Icon icon_svg = new CanvasIcon(icon, group_name, this); // Shadow if (options.eyecandy) { shadow = new CanvasBoxShadow(toGraphicsObject()); shadow->setFakeParent(this); setGraphicsEffect(shadow); } else shadow = 0; // Final touches setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable); // Wait for at least 1 port if (options.auto_hide_groups) setVisible(false); updatePositions(); } CanvasBox::~CanvasBox() { if (shadow) delete shadow; delete icon_svg; } int CanvasBox::getGroupId() { return m_group_id; } QString CanvasBox::getGroupName() { return m_group_name; } bool CanvasBox::isSplitted() { return m_splitted; } PortMode CanvasBox::getSplittedMode() { return m_splitted_mode; } int CanvasBox::getPortCount() { return m_port_list_ids.count(); } QList CanvasBox::getPortList() { return m_port_list_ids; } void CanvasBox::setIcon(Icon icon) { icon_svg->setIcon(icon, m_group_name); } void CanvasBox::setSplit(bool split, PortMode mode) { m_splitted = split; m_splitted_mode = mode; } void CanvasBox::setGroupName(QString group_name) { m_group_name = group_name; updatePositions(); } void CanvasBox::setShadowOpacity(float opacity) { if (shadow) shadow->setOpacity(opacity); } CanvasPort* CanvasBox::addPortFromGroup(int port_id, QString port_name, PortMode port_mode, PortType port_type) { if (m_port_list_ids.count() == 0) { if (options.auto_hide_groups) { if (options.eyecandy == EYECANDY_FULL) CanvasItemFX(this, true); setVisible(true); } } CanvasPort* new_widget = new CanvasPort(port_id, port_name, port_mode, port_type, this); port_dict_t port_dict; port_dict.group_id = 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; m_port_list_ids.append(port_id); return new_widget; } void CanvasBox::removePortFromGroup(int port_id) { if (m_port_list_ids.contains(port_id)) { m_port_list_ids.removeOne(port_id); } else { qCritical("PatchCanvas::CanvasBox->removePort(%i) - unable to find port to remove", port_id); return; } if (m_port_list_ids.count() > 0) { updatePositions(); } else if (isVisible()) { if (options.auto_hide_groups) { if (options.eyecandy == EYECANDY_FULL) CanvasItemFX(this, false); else setVisible(false); } } } void CanvasBox::addLineFromGroup(AbstractCanvasLine* line, int connection_id) { cb_line_t new_cbline; new_cbline.line = line; new_cbline.connection_id = connection_id; m_connection_lines.append(new_cbline); } void CanvasBox::removeLineFromGroup(int connection_id) { foreach2 (const cb_line_t& connection, m_connection_lines) if (connection.connection_id == connection_id) { m_connection_lines.takeAt(i); return; } } qCritical("PatchCanvas::CanvasBox->removeLineFromGroup(%i) - unable to find line to remove", connection_id); } void CanvasBox::checkItemPos() { if (canvas.size_rect.isNull() == false) { QPointF pos = scenePos(); if (canvas.size_rect.contains(pos) == false || canvas.size_rect.contains(pos+QPointF(p_width, p_height)) == false) { if (pos.x() < canvas.size_rect.x()) setPos(canvas.size_rect.x(), pos.y()); else if (pos.x()+p_width > canvas.size_rect.width()) setPos(canvas.size_rect.width()-p_width, pos.y()); pos = scenePos(); if (pos.y() < canvas.size_rect.y()) setPos(pos.x(), canvas.size_rect.y()); else if (pos.y()+p_height > canvas.size_rect.height()) setPos(pos.x(), canvas.size_rect.height()-p_height); } } } void CanvasBox::removeIconFromScene() { canvas.scene->removeItem(icon_svg); } void CanvasBox::updatePositions() { prepareGeometryChange(); int max_in_width = 0; int max_in_height = 24; int max_out_width = 0; int max_out_height = 24; bool have_audio_jack_in, have_audio_jack_out, have_midi_jack_in, have_midi_jack_out; bool have_midi_a2j_in, have_midi_a2j_out, have_midi_alsa_in, have_midi_alsa_out; 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 p_width = 50; p_height = 25; // Check Text Name size int app_name_size = QFontMetrics(m_font_name).width(m_group_name)+30; if (app_name_size > p_width) p_width = app_name_size; // Get Port List QList port_list; foreach (const port_dict_t& port, canvas.port_list) { if (m_port_list_ids.contains(port.port_id)) port_list.append(port); } // Get Max Box Width/Height foreach (const port_dict_t& port, port_list) { if (port.port_mode == PORT_MODE_INPUT) { max_in_height += 18; int size = QFontMetrics(m_font_port).width(port.port_name); if (size > max_in_width) max_in_width = size; if (port.port_type == PORT_TYPE_AUDIO_JACK && have_audio_jack_in == false) { have_audio_jack_in = true; max_in_height += 2; } else if (port.port_type == PORT_TYPE_MIDI_JACK && have_midi_jack_in == false) { have_midi_jack_in = true; max_in_height += 2; } else if (port.port_type == PORT_TYPE_MIDI_A2J && have_midi_a2j_in == false) { have_midi_a2j_in = true; max_in_height += 2; } else if (port.port_type == PORT_TYPE_MIDI_ALSA && have_midi_alsa_in == false) { have_midi_alsa_in = true; max_in_height += 2; } } else if (port.port_mode == PORT_MODE_OUTPUT) { max_out_height += 18; int size = QFontMetrics(m_font_port).width(port.port_name); if (size > max_out_width) max_out_width = size; if (port.port_type == PORT_TYPE_AUDIO_JACK && have_audio_jack_out == false) { have_audio_jack_out = true; max_out_height += 2; } else if (port.port_type == PORT_TYPE_MIDI_JACK && have_midi_jack_out == false) { have_midi_jack_out = true; max_out_height += 2; } else if (port.port_type == PORT_TYPE_MIDI_A2J && have_midi_a2j_out == false) { have_midi_a2j_out = true; max_out_height += 2; } else if (port.port_type == PORT_TYPE_MIDI_ALSA && have_midi_alsa_out == false) { have_midi_alsa_out = true; max_out_height += 2; } } } int final_width = 30 + max_in_width + max_out_width; if (final_width > p_width) p_width = final_width; if (max_in_height > p_height) p_height = max_in_height; if (max_out_height > p_height) p_height = max_out_height; // Remove bottom space p_height -= 2; int last_in_pos = 24; int last_out_pos = 24; PortType last_in_type = PORT_TYPE_NULL; PortType last_out_type = PORT_TYPE_NULL; // Re-position ports, AUDIO_JACK foreach (const port_dict_t& port, port_list) { if (port.port_type == PORT_TYPE_AUDIO_JACK) { if (port.port_mode == PORT_MODE_INPUT) { port.widget->setPos(QPointF(1, last_in_pos)); port.widget->setPortWidth(max_in_width); last_in_pos += 18; last_in_type = port.port_type; } else if (port.port_mode == PORT_MODE_OUTPUT) { port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos)); port.widget->setPortWidth(max_out_width); last_out_pos += 18; last_out_type = port.port_type; } } } // Re-position ports, MIDI_JACK foreach (const port_dict_t& port, port_list) { if (port.port_type == PORT_TYPE_MIDI_JACK) { if (port.port_mode == PORT_MODE_INPUT) { if (last_in_type != PORT_TYPE_NULL && port.port_type != last_in_type) last_in_pos += 2; port.widget->setPos(QPointF(1, last_in_pos)); port.widget->setPortWidth(max_in_width); last_in_pos += 18; last_in_type = port.port_type; } else if (port.port_mode == PORT_MODE_OUTPUT) { if (last_out_type != PORT_TYPE_NULL && port.port_type != last_out_type) last_out_pos += 2; port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos)); port.widget->setPortWidth(max_out_width); last_out_pos += 18; last_out_type = port.port_type; } } } // Re-position ports, MIDI_A2J foreach (const port_dict_t& port, port_list) { if (port.port_type == PORT_TYPE_MIDI_A2J) { if (port.port_mode == PORT_MODE_INPUT) { if (last_in_type != PORT_TYPE_NULL && port.port_type != last_in_type) last_in_pos += 2; port.widget->setPos(QPointF(1, last_in_pos)); port.widget->setPortWidth(max_in_width); last_in_pos += 18; last_in_type = port.port_type; } else if (port.port_mode == PORT_MODE_OUTPUT) { if (last_out_type != PORT_TYPE_NULL && port.port_type != last_out_type) last_out_pos += 2; port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos)); port.widget->setPortWidth(max_out_width); last_out_pos += 18; last_out_type = port.port_type; } } } // Re-position ports, MIDI_ALSA foreach (const port_dict_t& port, port_list) { if (port.port_type == PORT_TYPE_MIDI_ALSA) { if (port.port_mode == PORT_MODE_INPUT) { if (last_in_type != PORT_TYPE_NULL && port.port_type != last_in_type) last_in_pos += 2; port.widget->setPos(QPointF(1, last_in_pos)); port.widget->setPortWidth(max_in_width); last_in_pos += 18; last_in_type = port.port_type; } else if (port.port_mode == PORT_MODE_OUTPUT) { if (last_out_type != PORT_TYPE_NULL && port.port_type != last_out_type) last_out_pos += 2; port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos)); port.widget->setPortWidth(max_out_width); last_out_pos += 18; last_out_type = port.port_type; } } } repaintLines(true); update(); } void CanvasBox::repaintLines(bool forced) { if (pos() != m_last_pos || forced) { foreach (const cb_line_t& connection, m_connection_lines) connection.line->updateLinePos(); } m_last_pos = pos(); } void CanvasBox::resetLinesZValue() { foreach (const connection_dict_t& connection, canvas.connection_list) { int z_value; if (m_port_list_ids.contains(connection.port_out_id) && m_port_list_ids.contains(connection.port_in_id)) z_value = canvas.last_z_value; else z_value = canvas.last_z_value-1; connection.widget->setZValue(z_value); } } int CanvasBox::type() const { return CanvasBoxType; } void CanvasBox::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { QMenu menu; QMenu discMenu("Disconnect", &menu); QList port_con_list; QList port_con_list_ids; foreach (const int& port_id, m_port_list_ids) { QList tmp_port_con_list = CanvasGetPortConnectionList(port_id); foreach (const int& port_con_id, tmp_port_con_list) { if (port_con_list.contains(port_con_id) == false) { port_con_list.append(port_con_id); port_con_list_ids.append(port_id); } } } if (port_con_list.count() > 0) { for (int i=0; i < port_con_list.count(); i++) { int port_con_id = CanvasGetConnectedPort(port_con_list[i], port_con_list_ids[i]); QAction* act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id)); act_x_disc->setData(port_con_list[i]); QObject::connect(act_x_disc, SIGNAL(triggered()), canvas.qobject, SLOT(PortContextMenuDisconnect())); } } else { QAction* act_x_disc = discMenu.addAction("No connections"); act_x_disc->setEnabled(false); } menu.addMenu(&discMenu); QAction* act_x_disc_all = menu.addAction("Disconnect &All"); QAction* act_x_sep1 = menu.addSeparator(); QAction* act_x_info = menu.addAction("&Info"); QAction* act_x_rename = menu.addAction("&Rename"); QAction* act_x_sep2 = menu.addSeparator(); QAction* act_x_split_join = menu.addAction(m_splitted ? "Join" : "Split"); if (features.group_info == false) act_x_info->setVisible(false); if (features.group_rename == false) act_x_rename->setVisible(false); if (features.group_info == false && features.group_rename == false) act_x_sep1->setVisible(false); bool haveIns, haveOuts; haveIns = haveOuts = false; foreach (const port_dict_t& port, canvas.port_list) { if (m_port_list_ids.contains(port.port_id)) { if (port.port_mode == PORT_MODE_INPUT) haveIns = true; else if (port.port_mode == PORT_MODE_OUTPUT) haveOuts = true; } } if (m_splitted == false && (haveIns && haveOuts) == false) { act_x_sep2->setVisible(false); act_x_split_join->setVisible(false); } QAction* act_selected = menu.exec(event->screenPos()); if (act_selected == act_x_disc_all) { foreach (const int& port_id, port_con_list) canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, ""); } else if (act_selected == act_x_info) { canvas.callback(ACTION_GROUP_INFO, m_group_id, 0, ""); } else if (act_selected == act_x_rename) { bool ok_check; QString new_name = QInputDialog::getText(0, "Rename Group", "New name:", QLineEdit::Normal, m_group_name, &ok_check); if (ok_check and !new_name.isEmpty()) { canvas.callback(ACTION_GROUP_RENAME, m_group_id, 0, new_name); } } else if (act_selected == act_x_split_join) { if (m_splitted) canvas.callback(ACTION_GROUP_JOIN, m_group_id, 0, ""); else canvas.callback(ACTION_GROUP_SPLIT, m_group_id, 0, ""); } event->accept(); } void CanvasBox::mousePressEvent(QGraphicsSceneMouseEvent* event) { canvas.last_z_value += 1; setZValue(canvas.last_z_value); resetLinesZValue(); m_cursor_moving = false; if (event->button() == Qt::RightButton) { canvas.scene->clearSelection(); setSelected(true); m_mouse_down = false; return event->accept(); } else if (event->button() == Qt::LeftButton) { if (sceneBoundingRect().contains(event->scenePos())) m_mouse_down = true; else { // Fixes a weird Qt behaviour with right-click mouseMove m_mouse_down = false; return event->ignore(); } } else m_mouse_down = false; QGraphicsItem::mousePressEvent(event); } void CanvasBox::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { if (m_mouse_down) { if (m_cursor_moving == false) { setCursor(QCursor(Qt::SizeAllCursor)); m_cursor_moving = true; } repaintLines(); } QGraphicsItem::mouseMoveEvent(event); } void CanvasBox::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { if (m_cursor_moving) setCursor(QCursor(Qt::ArrowCursor)); m_mouse_down = false; m_cursor_moving = false; QGraphicsItem::mouseReleaseEvent(event); } QRectF CanvasBox::boundingRect() const { return QRectF(0, 0, p_width, p_height); } void CanvasBox::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { painter->setRenderHint(QPainter::Antialiasing, false); if (isSelected()) painter->setPen(canvas.theme->box_pen_sel); else painter->setPen(canvas.theme->box_pen); QLinearGradient box_gradient(0, 0, 0, p_height); box_gradient.setColorAt(0, canvas.theme->box_bg_1); box_gradient.setColorAt(1, canvas.theme->box_bg_2); painter->setBrush(box_gradient); painter->drawRect(0, 0, p_width, p_height); QPointF text_pos(25, 16); painter->setFont(m_font_name); painter->setPen(canvas.theme->box_text); painter->drawText(text_pos, m_group_name); repaintLines(); } END_NAMESPACE_PATCHCANVAS