1 /*
2  * Patchbay Canvas engine using QGraphicsView/Scene
3  * Copyright (C) 2010-2012 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the COPYING file
16  */
17 
18 #include "canvasport.h"
19 
20 #include <QtCore/QTimer>
21 #include <QtGui/QCursor>
22 #include <QtGui/QGraphicsSceneContextMenuEvent>
23 #include <QtGui/QGraphicsSceneMouseEvent>
24 #include <QtGui/QInputDialog>
25 #include <QtGui/QMenu>
26 #include <QtGui/QPainter>
27 
28 #include "canvaslinemov.h"
29 #include "canvasbezierlinemov.h"
30 #include "canvasbox.h"
31 
32 START_NAMESPACE_PATCHCANVAS
33 
CanvasPort(int port_id,QString port_name,PortMode port_mode,PortType port_type,QGraphicsItem * parent)34 CanvasPort::CanvasPort(int port_id, QString port_name, PortMode port_mode, PortType port_type, QGraphicsItem* parent) :
35         QGraphicsItem(parent, canvas.scene)
36 {
37     // Save Variables, useful for later
38     m_port_id   = port_id;
39     m_port_mode = port_mode;
40     m_port_type = port_type;
41     m_port_name = port_name;
42 
43     // Base Variables
44     m_port_width  = 15;
45     m_port_height = 15;
46     m_port_font   = QFont(canvas.theme->port_font_name, canvas.theme->port_font_size, canvas.theme->port_font_state);
47 
48     m_line_mov   = 0;
49     m_hover_item = 0;
50     m_last_selected_state = false;
51 
52     m_mouse_down    = false;
53     m_cursor_moving = false;
54 
55     setFlags(QGraphicsItem::ItemIsSelectable);
56 }
57 
getPortId()58 int CanvasPort::getPortId()
59 {
60     return m_port_id;
61 }
62 
getPortMode()63 PortMode CanvasPort::getPortMode()
64 {
65     return m_port_mode;
66 }
67 
getPortType()68 PortType CanvasPort::getPortType()
69 {
70     return m_port_type;
71 }
72 
getPortName()73 QString CanvasPort::getPortName()
74 {
75     return m_port_name;
76 }
77 
getFullPortName()78 QString CanvasPort::getFullPortName()
79 {
80     return ((CanvasBox*)parentItem())->getGroupName()+":"+m_port_name;
81 }
82 
getPortWidth()83 int CanvasPort::getPortWidth()
84 {
85     return m_port_width;
86 }
87 
getPortHeight()88 int CanvasPort::getPortHeight()
89 {
90     return m_port_height;
91 }
92 
setPortMode(PortMode port_mode)93 void CanvasPort::setPortMode(PortMode port_mode)
94 {
95     m_port_mode = port_mode;
96     update();
97 }
98 
setPortType(PortType port_type)99 void CanvasPort::setPortType(PortType port_type)
100 {
101     m_port_type = port_type;
102     update();
103 }
104 
setPortName(QString port_name)105 void CanvasPort::setPortName(QString port_name)
106 {
107     if (QFontMetrics(m_port_font).width(port_name) < QFontMetrics(m_port_font).width(m_port_name))
108         QTimer::singleShot(0, canvas.scene, SLOT(update()));
109 
110     m_port_name = port_name;
111     update();
112 }
113 
setPortWidth(int port_width)114 void CanvasPort::setPortWidth(int port_width)
115 {
116     if (port_width < m_port_width)
117         QTimer::singleShot(0, canvas.scene, SLOT(update()));
118 
119     m_port_width = port_width;
120     update();
121 }
122 
type() const123 int CanvasPort::type() const
124 {
125     return CanvasPortType;
126 }
127 
mousePressEvent(QGraphicsSceneMouseEvent * event)128 void CanvasPort::mousePressEvent(QGraphicsSceneMouseEvent* event)
129 {
130     m_hover_item = 0;
131     m_mouse_down = (event->button() == Qt::LeftButton);
132     m_cursor_moving = false;
133     QGraphicsItem::mousePressEvent(event);
134 }
135 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)136 void CanvasPort::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
137 {
138     if (m_mouse_down)
139     {
140         if (m_cursor_moving == false)
141         {
142             setCursor(QCursor(Qt::CrossCursor));
143             m_cursor_moving = true;
144 
145             foreach (const connection_dict_t& connection, canvas.connection_list)
146             {
147                 if (connection.port_out_id == m_port_id || connection.port_in_id == m_port_id)
148                     connection.widget->setLocked(true);
149             }
150         }
151 
152         if (! m_line_mov)
153         {
154             if (options.use_bezier_lines)
155                 m_line_mov = new CanvasBezierLineMov(m_port_mode, m_port_type, this);
156             else
157                 m_line_mov = new CanvasLineMov(m_port_mode, m_port_type, this);
158 
159             canvas.last_z_value += 1;
160             m_line_mov->setZValue(canvas.last_z_value);
161             canvas.last_z_value += 1;
162             parentItem()->setZValue(canvas.last_z_value);
163         }
164 
165         CanvasPort* item = 0;
166         QList<QGraphicsItem*> items = canvas.scene->items(event->scenePos(), Qt::ContainsItemShape, Qt::AscendingOrder);
167         for (int i=0; i < items.count(); i++)
168         {
169             if (items[i]->type() == CanvasPortType)
170             {
171                 if (items[i] != this)
172                 {
173                     if (! item)
174                         item = (CanvasPort*)items[i];
175                     else if (items[i]->parentItem()->zValue() > item->parentItem()->zValue())
176                         item = (CanvasPort*)items[i];
177                 }
178             }
179         }
180 
181         if (m_hover_item and m_hover_item != item)
182             m_hover_item->setSelected(false);
183 
184         if (item)
185         {
186             bool a2j_connection = (item->getPortType() == PORT_TYPE_MIDI_JACK && m_port_type == PORT_TYPE_MIDI_A2J) || (item->getPortType() == PORT_TYPE_MIDI_A2J && m_port_type == PORT_TYPE_MIDI_JACK);
187             if (item->getPortMode() != m_port_mode && (item->getPortType() == m_port_type || a2j_connection))
188             {
189                 item->setSelected(true);
190                 m_hover_item = item;
191             }
192             else
193                 m_hover_item = 0;
194         }
195         else
196             m_hover_item = 0;
197 
198         m_line_mov->updateLinePos(event->scenePos());
199         return event->accept();
200     }
201 
202     QGraphicsItem::mouseMoveEvent(event);
203 }
204 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)205 void CanvasPort::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
206 {
207     if (m_mouse_down)
208     {
209         if (m_line_mov)
210         {
211             m_line_mov->deleteFromScene();
212             m_line_mov = 0;
213         }
214 
215         foreach (const connection_dict_t& connection, canvas.connection_list)
216         {
217             if (connection.port_out_id == m_port_id || connection.port_in_id == m_port_id)
218                 connection.widget->setLocked(false);
219         }
220 
221         if (m_hover_item)
222         {
223             bool check = false;
224             foreach (const connection_dict_t& connection, canvas.connection_list)
225             {
226                 if ( (connection.port_out_id == m_port_id && connection.port_in_id == m_hover_item->getPortId()) ||
227                      (connection.port_out_id == m_hover_item->getPortId() && connection.port_in_id == m_port_id) )
228                 {
229                     canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "");
230                     check = true;
231                     break;
232                 }
233             }
234 
235             if (check == false)
236             {
237                 if (m_port_mode == PORT_MODE_OUTPUT)
238                     canvas.callback(ACTION_PORTS_CONNECT, m_port_id, m_hover_item->getPortId(), "");
239                 else
240                     canvas.callback(ACTION_PORTS_CONNECT, m_hover_item->getPortId(), m_port_id, "");
241             }
242 
243             canvas.scene->clearSelection();
244         }
245     }
246 
247     if (m_cursor_moving)
248         setCursor(QCursor(Qt::ArrowCursor));
249 
250     m_hover_item = 0;
251     m_mouse_down = false;
252     m_cursor_moving = false;
253     QGraphicsItem::mouseReleaseEvent(event);
254 }
255 
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)256 void CanvasPort::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
257 {
258     canvas.scene->clearSelection();
259     setSelected(true);
260 
261     QMenu menu;
262     QMenu discMenu("Disconnect", &menu);
263 
264     QList<int> port_con_list = CanvasGetPortConnectionList(m_port_id);
265 
266     if (port_con_list.count() > 0)
267     {
268         foreach (int port_id, port_con_list)
269         {
270             int port_con_id = CanvasGetConnectedPort(port_id, m_port_id);
271             QAction* act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id));
272             act_x_disc->setData(port_id);
273             QObject::connect(act_x_disc, SIGNAL(triggered()), canvas.qobject, SLOT(PortContextMenuDisconnect()));
274         }
275     }
276     else
277     {
278         QAction* act_x_disc = discMenu.addAction("No connections");
279         act_x_disc->setEnabled(false);
280     }
281 
282     menu.addMenu(&discMenu);
283     QAction* act_x_disc_all = menu.addAction("Disconnect &All");
284     QAction* act_x_sep_1    = menu.addSeparator();
285     QAction* act_x_info     = menu.addAction("Get &Info");
286     QAction* act_x_rename   = menu.addAction("&Rename");
287 
288     if (features.port_info == false)
289         act_x_info->setVisible(false);
290 
291     if (features.port_rename == false)
292         act_x_rename->setVisible(false);
293 
294     if (features.port_info == false && features.port_rename == false)
295         act_x_sep_1->setVisible(false);
296 
297     QAction* act_selected = menu.exec(event->screenPos());
298 
299     if (act_selected == act_x_disc_all)
300     {
301         foreach (int port_id, port_con_list)
302             canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "");
303     }
304     else if (act_selected == act_x_info)
305     {
306         canvas.callback(ACTION_PORT_INFO, m_port_id, 0, "");
307     }
308     else if (act_selected == act_x_rename)
309     {
310         bool ok_check;
311         QString new_name = QInputDialog::getText(0, "Rename Port", "New name:", QLineEdit::Normal, m_port_name, &ok_check);
312         if (ok_check and new_name.isEmpty() == false)
313         {
314             canvas.callback(ACTION_PORT_RENAME, m_port_id, 0, new_name);
315         }
316     }
317 
318     event->accept();
319 }
320 
boundingRect() const321 QRectF CanvasPort::boundingRect() const
322 {
323     return QRectF(0, 0, m_port_width+12, m_port_height);
324 }
325 
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)326 void CanvasPort::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
327 {
328     painter->setRenderHint(QPainter::Antialiasing, (options.antialiasing == ANTIALIASING_FULL));
329 
330     QPointF text_pos;
331     int poly_locx[5] = { 0 };
332 
333     if (m_port_mode == PORT_MODE_INPUT)
334     {
335         text_pos = QPointF(3, 12);
336 
337         if (canvas.theme->port_mode == Theme::THEME_PORT_POLYGON)
338         {
339             poly_locx[0] = 0;
340             poly_locx[1] = m_port_width+5;
341             poly_locx[2] = m_port_width+12;
342             poly_locx[3] = m_port_width+5;
343             poly_locx[4] = 0;
344         }
345         else if (canvas.theme->port_mode == Theme::THEME_PORT_SQUARE)
346         {
347             poly_locx[0] = 0;
348             poly_locx[1] = m_port_width+5;
349             poly_locx[2] = m_port_width+5;
350             poly_locx[3] = m_port_width+5;
351             poly_locx[4] = 0;
352         }
353         else
354         {
355             qCritical("PatchCanvas::CanvasPort->paint() - invalid theme port mode '%i'", canvas.theme->port_mode);
356             return;
357         }
358     }
359     else if (m_port_mode == PORT_MODE_OUTPUT)
360     {
361         text_pos = QPointF(9, 12);
362 
363         if (canvas.theme->port_mode == Theme::THEME_PORT_POLYGON)
364         {
365             poly_locx[0] = m_port_width+12;
366             poly_locx[1] = 7;
367             poly_locx[2] = 0;
368             poly_locx[3] = 7;
369             poly_locx[4] = m_port_width+12;
370         }
371         else if (canvas.theme->port_mode == Theme::THEME_PORT_SQUARE)
372         {
373             poly_locx[0] = m_port_width+12;
374             poly_locx[1] = 5;
375             poly_locx[2] = 5;
376             poly_locx[3] = 5;
377             poly_locx[4] = m_port_width+12;
378         }
379         else
380         {
381             qCritical("PatchCanvas::CanvasPort->paint() - invalid theme port mode '%i'", canvas.theme->port_mode);
382             return;
383         }
384     }
385     else
386     {
387         qCritical("PatchCanvas::CanvasPort->paint() - invalid port mode '%s'", port_mode2str(m_port_mode));
388         return;
389     }
390 
391     QColor poly_color;
392     QPen poly_pen;
393 
394     if (m_port_type == PORT_TYPE_AUDIO_JACK)
395     {
396         poly_color = isSelected() ? canvas.theme->port_audio_jack_bg_sel : canvas.theme->port_audio_jack_bg;
397         poly_pen = isSelected() ? canvas.theme->port_audio_jack_pen_sel : canvas.theme->port_audio_jack_pen;
398     }
399     else if (m_port_type == PORT_TYPE_MIDI_JACK)
400     {
401         poly_color = isSelected() ? canvas.theme->port_midi_jack_bg_sel : canvas.theme->port_midi_jack_bg;
402         poly_pen = isSelected() ? canvas.theme->port_midi_jack_pen_sel : canvas.theme->port_midi_jack_pen;
403     }
404     else if (m_port_type == PORT_TYPE_MIDI_A2J)
405     {
406         poly_color = isSelected() ? canvas.theme->port_midi_a2j_bg_sel : canvas.theme->port_midi_a2j_bg;
407         poly_pen = isSelected() ? canvas.theme->port_midi_a2j_pen_sel : canvas.theme->port_midi_a2j_pen;
408     }
409     else if (m_port_type == PORT_TYPE_MIDI_ALSA)
410     {
411         poly_color = isSelected() ? canvas.theme->port_midi_alsa_bg_sel : canvas.theme->port_midi_alsa_bg;
412         poly_pen = isSelected() ? canvas.theme->port_midi_alsa_pen_sel : canvas.theme->port_midi_alsa_pen;
413     }
414     else
415     {
416         qCritical("PatchCanvas::CanvasPort->paint() - invalid port type '%s'", port_type2str(m_port_type));
417         return;
418     }
419 
420     QPolygonF polygon;
421     polygon += QPointF(poly_locx[0], 0);
422     polygon += QPointF(poly_locx[1], 0);
423     polygon += QPointF(poly_locx[2], 7.5);
424     polygon += QPointF(poly_locx[3], 15);
425     polygon += QPointF(poly_locx[4], 15);
426 
427     painter->setBrush(poly_color);
428     painter->setPen(poly_pen);
429     painter->drawPolygon(polygon);
430 
431     painter->setPen(canvas.theme->port_text);
432     painter->setFont(m_port_font);
433     painter->drawText(text_pos, m_port_name);
434 
435     if (isSelected() != m_last_selected_state)
436     {
437         foreach (const connection_dict_t& connection, canvas.connection_list)
438         {
439             if (connection.port_out_id == m_port_id || connection.port_in_id == m_port_id)
440                 connection.widget->setLineSelected(isSelected());
441         }
442     }
443 
444     m_last_selected_state = isSelected();
445 }
446 
447 END_NAMESPACE_PATCHCANVAS
448