Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

690 lines
19KB

  1. /*
  2. * Patchbay Canvas engine using QGraphicsView/Scene
  3. * Copyright (C) 2010-2012 Filipe Coelho <falktx@gmail.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. #include "canvasbox.h"
  18. #include <QtCore/QTimer>
  19. #include <QtGui/QCursor>
  20. #include <QtGui/QInputDialog>
  21. #include <QtGui/QMenu>
  22. #include <QtGui/QGraphicsSceneContextMenuEvent>
  23. #include <QtGui/QGraphicsSceneMouseEvent>
  24. #include <QtGui/QPainter>
  25. #include "canvasline.h"
  26. #include "canvasbezierline.h"
  27. #include "canvasport.h"
  28. #include "canvasboxshadow.h"
  29. #include "canvasicon.h"
  30. START_NAMESPACE_PATCHCANVAS
  31. CanvasBox::CanvasBox(int group_id, QString group_name, Icon icon, QGraphicsItem* parent) :
  32. QGraphicsItem(parent, canvas.scene)
  33. {
  34. // Save Variables, useful for later
  35. m_group_id = group_id;
  36. m_group_name = group_name;
  37. // Base Variables
  38. p_width = 50;
  39. p_height = 25;
  40. m_last_pos = QPointF();
  41. m_splitted = false;
  42. m_splitted_mode = PORT_MODE_NULL;
  43. m_cursor_moving = false;
  44. m_forced_split = false;
  45. m_mouse_down = false;
  46. m_port_list_ids.clear();
  47. m_connection_lines.clear();
  48. // Set Font
  49. m_font_name = QFont(canvas.theme->box_font_name, canvas.theme->box_font_size, canvas.theme->box_font_state);
  50. m_font_port = QFont(canvas.theme->port_font_name, canvas.theme->port_font_size, canvas.theme->port_font_state);
  51. // Icon
  52. icon_svg = new CanvasIcon(icon, group_name, this);
  53. // Shadow
  54. if (options.eyecandy)
  55. {
  56. shadow = new CanvasBoxShadow(toGraphicsObject());
  57. shadow->setFakeParent(this);
  58. setGraphicsEffect(shadow);
  59. }
  60. else
  61. shadow = 0;
  62. // Final touches
  63. setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);
  64. // Wait for at least 1 port
  65. if (options.auto_hide_groups)
  66. setVisible(false);
  67. updatePositions();
  68. }
  69. CanvasBox::~CanvasBox()
  70. {
  71. if (shadow)
  72. delete shadow;
  73. delete icon_svg;
  74. }
  75. int CanvasBox::getGroupId()
  76. {
  77. return m_group_id;
  78. }
  79. QString CanvasBox::getGroupName()
  80. {
  81. return m_group_name;
  82. }
  83. bool CanvasBox::isSplitted()
  84. {
  85. return m_splitted;
  86. }
  87. PortMode CanvasBox::getSplittedMode()
  88. {
  89. return m_splitted_mode;
  90. }
  91. int CanvasBox::getPortCount()
  92. {
  93. return m_port_list_ids.count();
  94. }
  95. QList<int> CanvasBox::getPortList()
  96. {
  97. return m_port_list_ids;
  98. }
  99. void CanvasBox::setIcon(Icon icon)
  100. {
  101. icon_svg->setIcon(icon, m_group_name);
  102. }
  103. void CanvasBox::setSplit(bool split, PortMode mode)
  104. {
  105. m_splitted = split;
  106. m_splitted_mode = mode;
  107. }
  108. void CanvasBox::setGroupName(QString group_name)
  109. {
  110. m_group_name = group_name;
  111. updatePositions();
  112. }
  113. CanvasPort* CanvasBox::addPortFromGroup(int port_id, QString port_name, PortMode port_mode, PortType port_type)
  114. {
  115. if (m_port_list_ids.count() == 0)
  116. {
  117. if (options.auto_hide_groups)
  118. {
  119. if (options.eyecandy == EYECANDY_FULL)
  120. CanvasItemFX(this, true);
  121. setVisible(true);
  122. }
  123. }
  124. CanvasPort* new_widget = new CanvasPort(port_id, port_name, port_mode, port_type, this);
  125. port_dict_t port_dict;
  126. port_dict.group_id = m_group_id;
  127. port_dict.port_id = port_id;
  128. port_dict.port_name = port_name;
  129. port_dict.port_mode = port_mode;
  130. port_dict.port_type = port_type;
  131. port_dict.widget = new_widget;
  132. m_port_list_ids.append(port_id);
  133. return new_widget;
  134. }
  135. void CanvasBox::removePortFromGroup(int port_id)
  136. {
  137. if (m_port_list_ids.contains(port_id))
  138. {
  139. m_port_list_ids.removeOne(port_id);
  140. }
  141. else
  142. {
  143. qCritical("PatchCanvas::CanvasBox->removePort(%i) - unable to find port to remove", port_id);
  144. return;
  145. }
  146. if (m_port_list_ids.count() > 0)
  147. {
  148. updatePositions();
  149. }
  150. else if (isVisible())
  151. {
  152. if (options.auto_hide_groups)
  153. {
  154. if (options.eyecandy == EYECANDY_FULL)
  155. CanvasItemFX(this, false);
  156. else
  157. setVisible(false);
  158. }
  159. }
  160. }
  161. void CanvasBox::addLineFromGroup(AbstractCanvasLine* line, int connection_id)
  162. {
  163. cb_line_t new_cbline;
  164. new_cbline.line = line;
  165. new_cbline.connection_id = connection_id;
  166. m_connection_lines.append(new_cbline);
  167. }
  168. void CanvasBox::removeLineFromGroup(int connection_id)
  169. {
  170. foreach2 (const cb_line_t& connection, m_connection_lines)
  171. if (connection.connection_id == connection_id)
  172. {
  173. m_connection_lines.takeAt(i);
  174. return;
  175. }
  176. }
  177. qCritical("PatchCanvas::CanvasBox->removeLineFromGroup(%i) - unable to find line to remove", connection_id);
  178. }
  179. void CanvasBox::checkItemPos()
  180. {
  181. if (canvas.size_rect.isNull() == false)
  182. {
  183. QPointF pos = scenePos();
  184. if (canvas.size_rect.contains(pos) == false || canvas.size_rect.contains(pos+QPointF(p_width, p_height)) == false)
  185. {
  186. if (pos.x() < canvas.size_rect.x())
  187. setPos(canvas.size_rect.x(), pos.y());
  188. else if (pos.x()+p_width > canvas.size_rect.width())
  189. setPos(canvas.size_rect.width()-p_width, pos.y());
  190. pos = scenePos();
  191. if (pos.y() < canvas.size_rect.y())
  192. setPos(pos.x(), canvas.size_rect.y());
  193. else if (pos.y()+p_height > canvas.size_rect.height())
  194. setPos(pos.x(), canvas.size_rect.height()-p_height);
  195. }
  196. }
  197. }
  198. void CanvasBox::removeIconFromScene()
  199. {
  200. canvas.scene->removeItem(icon_svg);
  201. }
  202. void CanvasBox::updatePositions()
  203. {
  204. prepareGeometryChange();
  205. int max_in_width = 0;
  206. int max_in_height = 24;
  207. int max_out_width = 0;
  208. int max_out_height = 24;
  209. bool have_audio_jack_in, have_audio_jack_out, have_midi_jack_in, have_midi_jack_out;
  210. bool have_midi_a2j_in, have_midi_a2j_out, have_midi_alsa_in, have_midi_alsa_out;
  211. have_audio_jack_in = have_midi_jack_in = have_midi_a2j_in = have_midi_alsa_in = false;
  212. have_audio_jack_out = have_midi_jack_out = have_midi_a2j_out = have_midi_alsa_out = false;
  213. // reset box size
  214. p_width = 50;
  215. p_height = 25;
  216. // Check Text Name size
  217. int app_name_size = QFontMetrics(m_font_name).width(m_group_name)+30;
  218. if (app_name_size > p_width)
  219. p_width = app_name_size;
  220. // Get Port List
  221. QList<port_dict_t> port_list;
  222. foreach (const port_dict_t& port, canvas.port_list)
  223. {
  224. if (m_port_list_ids.contains(port.port_id))
  225. port_list.append(port);
  226. }
  227. // Get Max Box Width/Height
  228. foreach (const port_dict_t& port, port_list)
  229. {
  230. if (port.port_mode == PORT_MODE_INPUT)
  231. {
  232. max_in_height += 18;
  233. int size = QFontMetrics(m_font_port).width(port.port_name);
  234. if (size > max_in_width)
  235. max_in_width = size;
  236. if (port.port_type == PORT_TYPE_AUDIO_JACK && have_audio_jack_in == false)
  237. {
  238. have_audio_jack_in = true;
  239. max_in_height += 2;
  240. }
  241. else if (port.port_type == PORT_TYPE_MIDI_JACK && have_midi_jack_in == false)
  242. {
  243. have_midi_jack_in = true;
  244. max_in_height += 2;
  245. }
  246. else if (port.port_type == PORT_TYPE_MIDI_A2J && have_midi_a2j_in == false)
  247. {
  248. have_midi_a2j_in = true;
  249. max_in_height += 2;
  250. }
  251. else if (port.port_type == PORT_TYPE_MIDI_ALSA && have_midi_alsa_in == false)
  252. {
  253. have_midi_alsa_in = true;
  254. max_in_height += 2;
  255. }
  256. }
  257. else if (port.port_mode == PORT_MODE_OUTPUT)
  258. {
  259. max_out_height += 18;
  260. int size = QFontMetrics(m_font_port).width(port.port_name);
  261. if (size > max_out_width)
  262. max_out_width = size;
  263. if (port.port_type == PORT_TYPE_AUDIO_JACK && have_audio_jack_out == false)
  264. {
  265. have_audio_jack_out = true;
  266. max_out_height += 2;
  267. }
  268. else if (port.port_type == PORT_TYPE_MIDI_JACK && have_midi_jack_out == false)
  269. {
  270. have_midi_jack_out = true;
  271. max_out_height += 2;
  272. }
  273. else if (port.port_type == PORT_TYPE_MIDI_A2J && have_midi_a2j_out == false)
  274. {
  275. have_midi_a2j_out = true;
  276. max_out_height += 2;
  277. }
  278. else if (port.port_type == PORT_TYPE_MIDI_ALSA && have_midi_alsa_out == false)
  279. {
  280. have_midi_alsa_out = true;
  281. max_out_height += 2;
  282. }
  283. }
  284. }
  285. int final_width = 30 + max_in_width + max_out_width;
  286. if (final_width > p_width)
  287. p_width = final_width;
  288. if (max_in_height > p_height)
  289. p_height = max_in_height;
  290. if (max_out_height > p_height)
  291. p_height = max_out_height;
  292. // Remove bottom space
  293. p_height -= 2;
  294. int last_in_pos = 24;
  295. int last_out_pos = 24;
  296. PortType last_in_type = PORT_TYPE_NULL;
  297. PortType last_out_type = PORT_TYPE_NULL;
  298. // Re-position ports, AUDIO_JACK
  299. foreach (const port_dict_t& port, port_list)
  300. {
  301. if (port.port_type == PORT_TYPE_AUDIO_JACK)
  302. {
  303. if (port.port_mode == PORT_MODE_INPUT)
  304. {
  305. port.widget->setPos(QPointF(1, last_in_pos));
  306. port.widget->setPortWidth(max_in_width);
  307. last_in_pos += 18;
  308. last_in_type = port.port_type;
  309. }
  310. else if (port.port_mode == PORT_MODE_OUTPUT)
  311. {
  312. port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos));
  313. port.widget->setPortWidth(max_out_width);
  314. last_out_pos += 18;
  315. last_out_type = port.port_type;
  316. }
  317. }
  318. }
  319. // Re-position ports, MIDI_JACK
  320. foreach (const port_dict_t& port, port_list)
  321. {
  322. if (port.port_type == PORT_TYPE_MIDI_JACK)
  323. {
  324. if (port.port_mode == PORT_MODE_INPUT)
  325. {
  326. if (last_in_type != PORT_TYPE_NULL && port.port_type != last_in_type)
  327. last_in_pos += 2;
  328. port.widget->setPos(QPointF(1, last_in_pos));
  329. port.widget->setPortWidth(max_in_width);
  330. last_in_pos += 18;
  331. last_in_type = port.port_type;
  332. }
  333. else if (port.port_mode == PORT_MODE_OUTPUT)
  334. {
  335. if (last_out_type != PORT_TYPE_NULL && port.port_type != last_out_type)
  336. last_out_pos += 2;
  337. port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos));
  338. port.widget->setPortWidth(max_out_width);
  339. last_out_pos += 18;
  340. last_out_type = port.port_type;
  341. }
  342. }
  343. }
  344. // Re-position ports, MIDI_A2J
  345. foreach (const port_dict_t& port, port_list)
  346. {
  347. if (port.port_type == PORT_TYPE_MIDI_A2J)
  348. {
  349. if (port.port_mode == PORT_MODE_INPUT)
  350. {
  351. if (last_in_type != PORT_TYPE_NULL && port.port_type != last_in_type)
  352. last_in_pos += 2;
  353. port.widget->setPos(QPointF(1, last_in_pos));
  354. port.widget->setPortWidth(max_in_width);
  355. last_in_pos += 18;
  356. last_in_type = port.port_type;
  357. }
  358. else if (port.port_mode == PORT_MODE_OUTPUT)
  359. {
  360. if (last_out_type != PORT_TYPE_NULL && port.port_type != last_out_type)
  361. last_out_pos += 2;
  362. port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos));
  363. port.widget->setPortWidth(max_out_width);
  364. last_out_pos += 18;
  365. last_out_type = port.port_type;
  366. }
  367. }
  368. }
  369. // Re-position ports, MIDI_ALSA
  370. foreach (const port_dict_t& port, port_list)
  371. {
  372. if (port.port_type == PORT_TYPE_MIDI_ALSA)
  373. {
  374. if (port.port_mode == PORT_MODE_INPUT)
  375. {
  376. if (last_in_type != PORT_TYPE_NULL && port.port_type != last_in_type)
  377. last_in_pos += 2;
  378. port.widget->setPos(QPointF(1, last_in_pos));
  379. port.widget->setPortWidth(max_in_width);
  380. last_in_pos += 18;
  381. last_in_type = port.port_type;
  382. }
  383. else if (port.port_mode == PORT_MODE_OUTPUT)
  384. {
  385. if (last_out_type != PORT_TYPE_NULL && port.port_type != last_out_type)
  386. last_out_pos += 2;
  387. port.widget->setPos(QPointF(p_width-max_out_width-13, last_out_pos));
  388. port.widget->setPortWidth(max_out_width);
  389. last_out_pos += 18;
  390. last_out_type = port.port_type;
  391. }
  392. }
  393. }
  394. repaintLines(true);
  395. update();
  396. }
  397. void CanvasBox::repaintLines(bool forced)
  398. {
  399. if (pos() != m_last_pos || forced)
  400. {
  401. foreach (const cb_line_t& connection, m_connection_lines)
  402. connection.line->updateLinePos();
  403. }
  404. m_last_pos = pos();
  405. }
  406. void CanvasBox::resetLinesZValue()
  407. {
  408. foreach (const connection_dict_t& connection, canvas.connection_list)
  409. {
  410. int z_value;
  411. if (m_port_list_ids.contains(connection.port_out_id) && m_port_list_ids.contains(connection.port_in_id))
  412. z_value = canvas.last_z_value;
  413. else
  414. z_value = canvas.last_z_value-1;
  415. connection.widget->setZValue(z_value);
  416. }
  417. }
  418. int CanvasBox::type() const
  419. {
  420. return CanvasBoxType;
  421. }
  422. void CanvasBox::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
  423. {
  424. QMenu menu;
  425. QMenu discMenu("Disconnect", &menu);
  426. QList<int> port_con_list;
  427. QList<int> port_con_list_ids;
  428. foreach (const int& port_id, m_port_list_ids)
  429. {
  430. QList<int> tmp_port_con_list = CanvasGetPortConnectionList(port_id);
  431. foreach (const int& port_con_id, tmp_port_con_list)
  432. {
  433. if (port_con_list.contains(port_con_id) == false)
  434. {
  435. port_con_list.append(port_con_id);
  436. port_con_list_ids.append(port_id);
  437. }
  438. }
  439. }
  440. if (port_con_list.count() > 0)
  441. {
  442. for (int i=0; i < port_con_list.count(); i++)
  443. {
  444. int port_con_id = CanvasGetConnectedPort(port_con_list[i], port_con_list_ids[i]);
  445. QAction* act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id));
  446. act_x_disc->setData(port_con_list[i]);
  447. QObject::connect(act_x_disc, SIGNAL(triggered()), canvas.qobject, SLOT(PortContextMenuDisconnect()));
  448. }
  449. }
  450. else
  451. {
  452. QAction* act_x_disc = discMenu.addAction("No connections");
  453. act_x_disc->setEnabled(false);
  454. }
  455. menu.addMenu(&discMenu);
  456. QAction* act_x_disc_all = menu.addAction("Disconnect &All");
  457. QAction* act_x_sep1 = menu.addSeparator();
  458. QAction* act_x_info = menu.addAction("&Info");
  459. QAction* act_x_rename = menu.addAction("&Rename");
  460. QAction* act_x_sep2 = menu.addSeparator();
  461. QAction* act_x_split_join = menu.addAction(m_splitted ? "Join" : "Split");
  462. if (features.group_info == false)
  463. act_x_info->setVisible(false);
  464. if (features.group_rename == false)
  465. act_x_rename->setVisible(false);
  466. if (features.group_info == false && features.group_rename == false)
  467. act_x_sep1->setVisible(false);
  468. bool haveIns, haveOuts;
  469. haveIns = haveOuts = false;
  470. foreach (const port_dict_t& port, canvas.port_list)
  471. {
  472. if (m_port_list_ids.contains(port.port_id))
  473. {
  474. if (port.port_mode == PORT_MODE_INPUT)
  475. haveIns = true;
  476. else if (port.port_mode == PORT_MODE_OUTPUT)
  477. haveOuts = true;
  478. }
  479. }
  480. if (m_splitted == false && (haveIns && haveOuts) == false)
  481. {
  482. act_x_sep2->setVisible(false);
  483. act_x_split_join->setVisible(false);
  484. }
  485. QAction* act_selected = menu.exec(event->screenPos());
  486. if (act_selected == act_x_disc_all)
  487. {
  488. foreach (const int& port_id, port_con_list)
  489. canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "");
  490. }
  491. else if (act_selected == act_x_info)
  492. {
  493. canvas.callback(ACTION_GROUP_INFO, m_group_id, 0, "");
  494. }
  495. else if (act_selected == act_x_rename)
  496. {
  497. bool ok_check;
  498. QString new_name = QInputDialog::getText(0, "Rename Group", "New name:", QLineEdit::Normal, m_group_name, &ok_check);
  499. if (ok_check and !new_name.isEmpty())
  500. {
  501. canvas.callback(ACTION_GROUP_RENAME, m_group_id, 0, new_name);
  502. }
  503. }
  504. else if (act_selected == act_x_split_join)
  505. {
  506. if (m_splitted)
  507. canvas.callback(ACTION_GROUP_JOIN, m_group_id, 0, "");
  508. else
  509. canvas.callback(ACTION_GROUP_SPLIT, m_group_id, 0, "");
  510. }
  511. event->accept();
  512. }
  513. void CanvasBox::mousePressEvent(QGraphicsSceneMouseEvent* event)
  514. {
  515. canvas.last_z_value += 1;
  516. setZValue(canvas.last_z_value);
  517. resetLinesZValue();
  518. m_cursor_moving = false;
  519. if (event->button() == Qt::RightButton)
  520. {
  521. canvas.scene->clearSelection();
  522. setSelected(true);
  523. m_mouse_down = false;
  524. return event->accept();
  525. }
  526. else if (event->button() == Qt::LeftButton)
  527. {
  528. if (sceneBoundingRect().contains(event->scenePos()))
  529. m_mouse_down = true;
  530. else
  531. {
  532. // Fixes a weird Qt behaviour with right-click mouseMove
  533. m_mouse_down = false;
  534. return event->ignore();
  535. }
  536. }
  537. else
  538. m_mouse_down = false;
  539. QGraphicsItem::mousePressEvent(event);
  540. }
  541. void CanvasBox::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
  542. {
  543. if (m_mouse_down)
  544. {
  545. if (m_cursor_moving == false)
  546. {
  547. setCursor(QCursor(Qt::SizeAllCursor));
  548. m_cursor_moving = true;
  549. }
  550. repaintLines();
  551. }
  552. QGraphicsItem::mouseMoveEvent(event);
  553. }
  554. void CanvasBox::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
  555. {
  556. if (m_cursor_moving)
  557. setCursor(QCursor(Qt::ArrowCursor));
  558. m_mouse_down = false;
  559. m_cursor_moving = false;
  560. QGraphicsItem::mouseReleaseEvent(event);
  561. }
  562. QRectF CanvasBox::boundingRect() const
  563. {
  564. return QRectF(0, 0, p_width, p_height);
  565. }
  566. void CanvasBox::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
  567. {
  568. painter->setRenderHint(QPainter::Antialiasing, false);
  569. if (isSelected())
  570. painter->setPen(canvas.theme->box_pen_sel);
  571. else
  572. painter->setPen(canvas.theme->box_pen);
  573. QLinearGradient box_gradient(0, 0, 0, p_height);
  574. box_gradient.setColorAt(0, canvas.theme->box_bg_1);
  575. box_gradient.setColorAt(1, canvas.theme->box_bg_2);
  576. painter->setBrush(box_gradient);
  577. painter->drawRect(0, 0, p_width, p_height);
  578. QPointF text_pos(25, 16);
  579. painter->setFont(m_font_name);
  580. painter->setPen(canvas.theme->box_text);
  581. painter->drawText(text_pos, m_group_name);
  582. repaintLines();
  583. }
  584. END_NAMESPACE_PATCHCANVAS