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