Audio plugin host https://kx.studio/carla
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.

2867 lines
101KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # PatchBay Canvas engine using QGraphicsView/Scene
  4. # Copyright (C) 2010-2014 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the doc/GPL.txt file.
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Config)
  19. from carla_config import *
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Global)
  22. if config_UseQt5:
  23. from PyQt5.QtCore import pyqtSignal, pyqtSlot, qDebug, qCritical, qFatal, qWarning, Qt, QObject
  24. from PyQt5.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer
  25. from PyQt5.QtGui import QColor, QLinearGradient, QPen, QPolygonF, QPainter, QPainterPath
  26. from PyQt5.QtGui import QCursor, QFont, QFontMetrics
  27. from PyQt5.QtSvg import QGraphicsSvgItem, QSvgRenderer
  28. from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem
  29. from PyQt5.QtWidgets import QGraphicsColorizeEffect, QGraphicsDropShadowEffect
  30. from PyQt5.QtWidgets import QInputDialog, QLineEdit, QMenu
  31. else:
  32. from PyQt4.QtCore import pyqtSignal, pyqtSlot, qDebug, qCritical, qFatal, qWarning, Qt, QObject
  33. from PyQt4.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer
  34. from PyQt4.QtGui import QColor, QLinearGradient, QPen, QPolygonF, QPainter, QPainterPath
  35. from PyQt4.QtGui import QCursor, QFont, QFontMetrics
  36. from PyQt4.QtGui import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem
  37. from PyQt4.QtGui import QGraphicsColorizeEffect, QGraphicsDropShadowEffect
  38. from PyQt4.QtGui import QInputDialog, QLineEdit, QMenu
  39. from PyQt4.QtSvg import QGraphicsSvgItem, QSvgRenderer
  40. # ------------------------------------------------------------------------------------------------------------
  41. # Imports (Theme)
  42. from patchcanvas_theme import *
  43. # ------------------------------------------------------------------------------
  44. # patchcanvas-api.h
  45. # Port Mode
  46. PORT_MODE_NULL = 0
  47. PORT_MODE_INPUT = 1
  48. PORT_MODE_OUTPUT = 2
  49. # Port Type
  50. PORT_TYPE_NULL = 0
  51. PORT_TYPE_AUDIO_JACK = 1
  52. PORT_TYPE_MIDI_JACK = 2
  53. PORT_TYPE_MIDI_ALSA = 3
  54. PORT_TYPE_PARAMETER = 4
  55. # Callback Action
  56. ACTION_GROUP_INFO = 0 # group_id, N, N
  57. ACTION_GROUP_RENAME = 1 # group_id, N, new_name
  58. ACTION_GROUP_SPLIT = 2 # group_id, N, N
  59. ACTION_GROUP_JOIN = 3 # group_id, N, N
  60. ACTION_PORT_INFO = 4 # group_id, port_id, N
  61. ACTION_PORT_RENAME = 5 # group_id, port_id, new_name
  62. ACTION_PORTS_CONNECT = 6 # N, N, "outG:outP:inG:inP"
  63. ACTION_PORTS_DISCONNECT = 7 # conn_id, N, N
  64. ACTION_PLUGIN_CLONE = 8 # plugin_id, N, N
  65. ACTION_PLUGIN_EDIT = 9 # plugin_id, N, N
  66. ACTION_PLUGIN_RENAME = 10 # plugin_id, N, N
  67. ACTION_PLUGIN_REMOVE = 11 # plugin_id, N, N
  68. ACTION_PLUGIN_SHOW_UI = 12 # plugin_id, N, new_name
  69. # Icon
  70. ICON_APPLICATION = 0
  71. ICON_HARDWARE = 1
  72. ICON_DISTRHO = 2
  73. ICON_FILE = 3
  74. ICON_PLUGIN = 4
  75. ICON_LADISH_ROOM = 5
  76. # Split Option
  77. SPLIT_UNDEF = 0
  78. SPLIT_NO = 1
  79. SPLIT_YES = 2
  80. # Antialiasing Option
  81. ANTIALIASING_NONE = 0
  82. ANTIALIASING_SMALL = 1
  83. ANTIALIASING_FULL = 2
  84. # Eye-Candy Option
  85. EYECANDY_NONE = 0
  86. EYECANDY_SMALL = 1
  87. EYECANDY_FULL = 2
  88. # Canvas options
  89. class options_t(object):
  90. __slots__ = [
  91. 'theme_name',
  92. 'auto_hide_groups',
  93. 'auto_select_items',
  94. 'use_bezier_lines',
  95. 'antialiasing',
  96. 'eyecandy'
  97. ]
  98. # Canvas features
  99. class features_t(object):
  100. __slots__ = [
  101. 'group_info',
  102. 'group_rename',
  103. 'port_info',
  104. 'port_rename',
  105. 'handle_group_pos'
  106. ]
  107. # ------------------------------------------------------------------------------
  108. # patchcanvas.h
  109. # object types
  110. CanvasBoxType = QGraphicsItem.UserType + 1
  111. CanvasIconType = QGraphicsItem.UserType + 2
  112. CanvasPortType = QGraphicsItem.UserType + 3
  113. CanvasLineType = QGraphicsItem.UserType + 4
  114. CanvasBezierLineType = QGraphicsItem.UserType + 5
  115. CanvasLineMovType = QGraphicsItem.UserType + 6
  116. CanvasBezierLineMovType = QGraphicsItem.UserType + 7
  117. # object lists
  118. class group_dict_t(object):
  119. __slots__ = [
  120. 'group_id',
  121. 'group_name',
  122. 'split',
  123. 'icon',
  124. 'plugin_id',
  125. 'widgets'
  126. ]
  127. class port_dict_t(object):
  128. __slots__ = [
  129. 'group_id',
  130. 'port_id',
  131. 'port_name',
  132. 'port_mode',
  133. 'port_type',
  134. 'is_alternate',
  135. 'widget'
  136. ]
  137. class connection_dict_t(object):
  138. __slots__ = [
  139. 'connection_id',
  140. 'group_in_id',
  141. 'port_in_id',
  142. 'group_out_id',
  143. 'port_out_id',
  144. 'widget'
  145. ]
  146. class animation_dict_t(object):
  147. __slots__ = [
  148. 'animation',
  149. 'item'
  150. ]
  151. # Main Canvas object
  152. class Canvas(object):
  153. __slots__ = [
  154. 'scene',
  155. 'callback',
  156. 'debug',
  157. 'last_z_value',
  158. 'last_connection_id',
  159. 'initial_pos',
  160. 'size_rect',
  161. 'group_list',
  162. 'port_list',
  163. 'connection_list',
  164. 'animation_list',
  165. 'qobject',
  166. 'settings',
  167. 'theme',
  168. 'initiated'
  169. ]
  170. # ------------------------------------------------------------------------------
  171. # patchcanvas.cpp
  172. class CanvasObject(QObject):
  173. def __init__(self, parent=None):
  174. QObject.__init__(self, parent)
  175. @pyqtSlot()
  176. def AnimationIdle(self):
  177. animation = self.sender()
  178. if animation:
  179. CanvasRemoveAnimation(animation)
  180. @pyqtSlot()
  181. def AnimationHide(self):
  182. animation = self.sender()
  183. if animation:
  184. item = animation.item()
  185. if item:
  186. item.hide()
  187. CanvasRemoveAnimation(animation)
  188. @pyqtSlot()
  189. def AnimationDestroy(self):
  190. animation = self.sender()
  191. if animation:
  192. item = animation.item()
  193. if item:
  194. CanvasRemoveItemFX(item)
  195. CanvasRemoveAnimation(animation)
  196. @pyqtSlot()
  197. def PortContextMenuDisconnect(self):
  198. try:
  199. connectionId = int(self.sender().data())
  200. except:
  201. return
  202. CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "")
  203. # Global objects
  204. canvas = Canvas()
  205. canvas.qobject = None
  206. canvas.settings = None
  207. canvas.theme = None
  208. canvas.initiated = False
  209. canvas.group_list = []
  210. canvas.port_list = []
  211. canvas.connection_list = []
  212. canvas.animation_list = []
  213. options = options_t()
  214. options.theme_name = getDefaultThemeName()
  215. options.auto_hide_groups = False
  216. options.auto_select_items = False
  217. options.use_bezier_lines = True
  218. options.antialiasing = ANTIALIASING_SMALL
  219. options.eyecandy = EYECANDY_SMALL
  220. features = features_t()
  221. features.group_info = False
  222. features.group_rename = False
  223. features.port_info = False
  224. features.port_rename = False
  225. features.handle_group_pos = False
  226. # Internal functions
  227. def bool2str(check):
  228. return "True" if check else "False"
  229. def port_mode2str(port_mode):
  230. if port_mode == PORT_MODE_NULL:
  231. return "PORT_MODE_NULL"
  232. elif port_mode == PORT_MODE_INPUT:
  233. return "PORT_MODE_INPUT"
  234. elif port_mode == PORT_MODE_OUTPUT:
  235. return "PORT_MODE_OUTPUT"
  236. else:
  237. return "PORT_MODE_???"
  238. def port_type2str(port_type):
  239. if port_type == PORT_TYPE_NULL:
  240. return "PORT_TYPE_NULL"
  241. elif port_type == PORT_TYPE_AUDIO_JACK:
  242. return "PORT_TYPE_AUDIO_JACK"
  243. elif port_type == PORT_TYPE_MIDI_JACK:
  244. return "PORT_TYPE_MIDI_JACK"
  245. elif port_type == PORT_TYPE_MIDI_ALSA:
  246. return "PORT_TYPE_MIDI_ALSA"
  247. elif port_type == PORT_TYPE_PARAMETER:
  248. return "PORT_TYPE_MIDI_PARAMETER"
  249. else:
  250. return "PORT_TYPE_???"
  251. def icon2str(icon):
  252. if icon == ICON_APPLICATION:
  253. return "ICON_APPLICATION"
  254. elif icon == ICON_HARDWARE:
  255. return "ICON_HARDWARE"
  256. elif icon == ICON_DISTRHO:
  257. return "ICON_DISTRHO"
  258. elif icon == ICON_FILE:
  259. return "ICON_FILE"
  260. elif icon == ICON_PLUGIN:
  261. return "ICON_PLUGIN"
  262. elif icon == ICON_LADISH_ROOM:
  263. return "ICON_LADISH_ROOM"
  264. else:
  265. return "ICON_???"
  266. def split2str(split):
  267. if split == SPLIT_UNDEF:
  268. return "SPLIT_UNDEF"
  269. elif split == SPLIT_NO:
  270. return "SPLIT_NO"
  271. elif split == SPLIT_YES:
  272. return "SPLIT_YES"
  273. else:
  274. return "SPLIT_???"
  275. # PatchCanvas API
  276. def setOptions(new_options):
  277. if canvas.initiated: return
  278. options.theme_name = new_options.theme_name
  279. options.auto_hide_groups = new_options.auto_hide_groups
  280. options.auto_select_items = new_options.auto_select_items
  281. options.use_bezier_lines = new_options.use_bezier_lines
  282. options.antialiasing = new_options.antialiasing
  283. options.eyecandy = new_options.eyecandy
  284. def setFeatures(new_features):
  285. if canvas.initiated: return
  286. features.group_info = new_features.group_info
  287. features.group_rename = new_features.group_rename
  288. features.port_info = new_features.port_info
  289. features.port_rename = new_features.port_rename
  290. features.handle_group_pos = new_features.handle_group_pos
  291. def init(appName, scene, callback, debug=False):
  292. if debug:
  293. qDebug("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug)))
  294. if canvas.initiated:
  295. qCritical("PatchCanvas::init() - already initiated")
  296. return
  297. if not callback:
  298. qFatal("PatchCanvas::init() - fatal error: callback not set")
  299. return
  300. canvas.scene = scene
  301. canvas.callback = callback
  302. canvas.debug = debug
  303. canvas.last_z_value = 0
  304. canvas.last_connection_id = 0
  305. canvas.initial_pos = QPointF(0, 0)
  306. canvas.size_rect = QRectF()
  307. if not canvas.qobject: canvas.qobject = CanvasObject()
  308. if not canvas.settings: canvas.settings = QSettings("falkTX", appName)
  309. if canvas.theme:
  310. del canvas.theme
  311. canvas.theme = None
  312. for i in range(Theme.THEME_MAX):
  313. this_theme_name = getThemeName(i)
  314. if this_theme_name == options.theme_name:
  315. canvas.theme = Theme(i)
  316. break
  317. if not canvas.theme:
  318. canvas.theme = Theme(getDefaultTheme())
  319. canvas.scene.updateTheme()
  320. canvas.initiated = True
  321. def clear():
  322. if canvas.debug:
  323. qDebug("PatchCanvas::clear()")
  324. group_list_ids = []
  325. port_list_ids = []
  326. connection_list_ids = []
  327. for group in canvas.group_list:
  328. group_list_ids.append(group.group_id)
  329. for port in canvas.port_list:
  330. port_list_ids.append((port.group_id, port.port_id))
  331. for connection in canvas.connection_list:
  332. connection_list_ids.append(connection.connection_id)
  333. for idx in connection_list_ids:
  334. disconnectPorts(idx)
  335. for group_id, port_id in port_list_ids:
  336. removePort(group_id, port_id)
  337. for idx in group_list_ids:
  338. removeGroup(idx)
  339. canvas.last_z_value = 0
  340. canvas.last_connection_id = 0
  341. canvas.group_list = []
  342. canvas.port_list = []
  343. canvas.connection_list = []
  344. canvas.scene.clear()
  345. canvas.initiated = False
  346. def setInitialPos(x, y):
  347. if canvas.debug:
  348. qDebug("PatchCanvas::setInitialPos(%i, %i)" % (x, y))
  349. canvas.initial_pos.setX(x)
  350. canvas.initial_pos.setY(y)
  351. def setCanvasSize(x, y, width, height):
  352. if canvas.debug:
  353. qDebug("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height))
  354. canvas.size_rect.setX(x)
  355. canvas.size_rect.setY(y)
  356. canvas.size_rect.setWidth(width)
  357. canvas.size_rect.setHeight(height)
  358. def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION):
  359. if canvas.debug:
  360. qDebug("PatchCanvas::addGroup(%i, %s, %s, %s)" % (group_id, group_name.encode(), split2str(split), icon2str(icon)))
  361. for group in canvas.group_list:
  362. if group.group_id == group_id:
  363. qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % (group_id, group_name.encode(), split2str(split), icon2str(icon)))
  364. return
  365. if split == SPLIT_UNDEF:
  366. isHardware = bool(icon == ICON_HARDWARE)
  367. if features.handle_group_pos:
  368. split = canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES if isHardware else split, type=int)
  369. elif isHardware:
  370. split = SPLIT_YES
  371. group_box = CanvasBox(group_id, group_name, icon)
  372. group_dict = group_dict_t()
  373. group_dict.group_id = group_id
  374. group_dict.group_name = group_name
  375. group_dict.split = bool(split == SPLIT_YES)
  376. group_dict.icon = icon
  377. group_dict.plugin_id = -1
  378. group_dict.widgets = [group_box, None]
  379. if split == SPLIT_YES:
  380. group_box.setSplit(True, PORT_MODE_OUTPUT)
  381. if features.handle_group_pos:
  382. group_box.setPos(canvas.settings.value("CanvasPositions/%s_OUTPUT" % group_name, CanvasGetNewGroupPos(False), type=QPointF))
  383. else:
  384. group_box.setPos(CanvasGetNewGroupPos(False))
  385. group_sbox = CanvasBox(group_id, group_name, icon)
  386. group_sbox.setSplit(True, PORT_MODE_INPUT)
  387. group_dict.widgets[1] = group_sbox
  388. if features.handle_group_pos:
  389. group_sbox.setPos(canvas.settings.value("CanvasPositions/%s_INPUT" % group_name, CanvasGetNewGroupPos(True), type=QPointF))
  390. else:
  391. group_sbox.setPos(CanvasGetNewGroupPos(True))
  392. canvas.last_z_value += 1
  393. group_sbox.setZValue(canvas.last_z_value)
  394. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  395. CanvasItemFX(group_sbox, True)
  396. group_sbox.checkItemPos()
  397. else:
  398. group_box.setSplit(False)
  399. if features.handle_group_pos:
  400. group_box.setPos(canvas.settings.value("CanvasPositions/%s" % group_name, CanvasGetNewGroupPos(False), type=QPointF))
  401. else:
  402. # Special ladish fake-split groups
  403. horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM)
  404. group_box.setPos(CanvasGetNewGroupPos(horizontal))
  405. group_box.checkItemPos()
  406. canvas.last_z_value += 1
  407. group_box.setZValue(canvas.last_z_value)
  408. canvas.group_list.append(group_dict)
  409. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  410. CanvasItemFX(group_box, True)
  411. QTimer.singleShot(0, canvas.scene.update)
  412. def removeGroup(group_id):
  413. if canvas.debug:
  414. qDebug("PatchCanvas::removeGroup(%i)" % group_id)
  415. for group in canvas.group_list:
  416. if group.group_id == group_id:
  417. item = group.widgets[0]
  418. group_name = group.group_name
  419. if group.split:
  420. s_item = group.widgets[1]
  421. if features.handle_group_pos:
  422. canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos())
  423. canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos())
  424. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES)
  425. if options.eyecandy == EYECANDY_FULL:
  426. CanvasItemFX(s_item, False, True)
  427. else:
  428. s_item.removeIconFromScene()
  429. canvas.scene.removeItem(s_item)
  430. del s_item
  431. else:
  432. if features.handle_group_pos:
  433. canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos())
  434. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO)
  435. if options.eyecandy == EYECANDY_FULL:
  436. CanvasItemFX(item, False, True)
  437. else:
  438. item.removeIconFromScene()
  439. canvas.scene.removeItem(item)
  440. del item
  441. canvas.group_list.remove(group)
  442. QTimer.singleShot(0, canvas.scene.update)
  443. return
  444. qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id)
  445. def renameGroup(group_id, new_group_name):
  446. if canvas.debug:
  447. qDebug("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode()))
  448. for group in canvas.group_list:
  449. if group.group_id == group_id:
  450. group.group_name = new_group_name
  451. group.widgets[0].setGroupName(new_group_name)
  452. if group.split and group.widgets[1]:
  453. group.widgets[1].setGroupName(new_group_name)
  454. QTimer.singleShot(0, canvas.scene.update)
  455. return
  456. qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode()))
  457. def splitGroup(group_id):
  458. if canvas.debug:
  459. qDebug("PatchCanvas::splitGroup(%i)" % group_id)
  460. item = None
  461. group_name = ""
  462. group_icon = ICON_APPLICATION
  463. ports_data = []
  464. conns_data = []
  465. # Step 1 - Store all Item data
  466. for group in canvas.group_list:
  467. if group.group_id == group_id:
  468. if group.split:
  469. qCritical("PatchCanvas::splitGroup(%i) - group is already splitted" % group_id)
  470. return
  471. item = group.widgets[0]
  472. group_name = group.group_name
  473. group_icon = group.icon
  474. break
  475. if not item:
  476. qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id)
  477. return
  478. port_list_ids = list(item.getPortList())
  479. for port in canvas.port_list:
  480. if port.port_id in port_list_ids:
  481. port_dict = port_dict_t()
  482. port_dict.group_id = port.group_id
  483. port_dict.port_id = port.port_id
  484. port_dict.port_name = port.port_name
  485. port_dict.port_mode = port.port_mode
  486. port_dict.port_type = port.port_type
  487. port_dict.is_alternate = port.is_alternate
  488. port_dict.widget = None
  489. ports_data.append(port_dict)
  490. for connection in canvas.connection_list:
  491. if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
  492. connection_dict = connection_dict_t()
  493. connection_dict.connection_id = connection.connection_id
  494. connection_dict.group_in_id = connection.group_in_id
  495. connection_dict.port_in_id = connection.port_in_id
  496. connection_dict.group_out_id = connection.group_out_id
  497. connection_dict.port_out_id = connection.port_out_id
  498. connection_dict.widget = None
  499. conns_data.append(connection_dict)
  500. # Step 2 - Remove Item and Children
  501. for conn in conns_data:
  502. disconnectPorts(conn.connection_id)
  503. for port_id in port_list_ids:
  504. removePort(group_id, port_id)
  505. removeGroup(group_id)
  506. # Step 3 - Re-create Item, now splitted
  507. addGroup(group_id, group_name, SPLIT_YES, group_icon)
  508. for port in ports_data:
  509. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  510. for conn in conns_data:
  511. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id)
  512. QTimer.singleShot(0, canvas.scene.update)
  513. def joinGroup(group_id):
  514. if canvas.debug:
  515. qDebug("PatchCanvas::joinGroup(%i)" % group_id)
  516. item = None
  517. s_item = None
  518. group_name = ""
  519. group_icon = ICON_APPLICATION
  520. ports_data = []
  521. conns_data = []
  522. # Step 1 - Store all Item data
  523. for group in canvas.group_list:
  524. if group.group_id == group_id:
  525. if not group.split:
  526. qCritical("PatchCanvas::joinGroup(%i) - group is not splitted" % group_id)
  527. return
  528. item = group.widgets[0]
  529. s_item = group.widgets[1]
  530. group_name = group.group_name
  531. group_icon = group.icon
  532. break
  533. # FIXME
  534. if not (item and s_item):
  535. qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id)
  536. return
  537. port_list_ids = list(item.getPortList())
  538. port_list_idss = s_item.getPortList()
  539. for port_id in port_list_idss:
  540. if port_id not in port_list_ids:
  541. port_list_ids.append(port_id)
  542. for port in canvas.port_list:
  543. if port.port_id in port_list_ids:
  544. port_dict = port_dict_t()
  545. port_dict.group_id = port.group_id
  546. port_dict.port_id = port.port_id
  547. port_dict.port_name = port.port_name
  548. port_dict.port_mode = port.port_mode
  549. port_dict.port_type = port.port_type
  550. port_dict.is_alternate = port.is_alternate
  551. port_dict.widget = None
  552. ports_data.append(port_dict)
  553. for connection in canvas.connection_list:
  554. if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
  555. connection_dict = connection_dict_t()
  556. connection_dict.connection_id = connection.connection_id
  557. connection_dict.group_in_id = connection.group_in_id
  558. connection_dict.port_in_id = connection.port_in_id
  559. connection_dict.group_out_id = connection.group_out_id
  560. connection_dict.port_out_id = connection.port_out_id
  561. connection_dict.widget = None
  562. conns_data.append(connection_dict)
  563. # Step 2 - Remove Item and Children
  564. for conn in conns_data:
  565. disconnectPorts(conn.connection_id)
  566. for port_id in port_list_ids:
  567. removePort(group_id, port_id)
  568. removeGroup(group_id)
  569. # Step 3 - Re-create Item, now together
  570. addGroup(group_id, group_name, SPLIT_NO, group_icon)
  571. for port in ports_data:
  572. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  573. for conn in conns_data:
  574. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id)
  575. QTimer.singleShot(0, canvas.scene.update)
  576. def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
  577. if canvas.debug:
  578. qDebug("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode)))
  579. for group in canvas.group_list:
  580. if group.group_id == group_id:
  581. if group.split:
  582. if port_mode == PORT_MODE_OUTPUT:
  583. return group.widgets[0].pos()
  584. elif port_mode == PORT_MODE_INPUT:
  585. return group.widgets[1].pos()
  586. else:
  587. return QPointF(0, 0)
  588. else:
  589. return group.widgets[0].pos()
  590. qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
  591. return QPointF(0, 0)
  592. def setGroupPos(group_id, group_pos_x, group_pos_y):
  593. setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y)
  594. def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i):
  595. if canvas.debug:
  596. qDebug("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  597. for group in canvas.group_list:
  598. if group.group_id == group_id:
  599. group.widgets[0].setPos(group_pos_x_o, group_pos_y_o)
  600. if group.split and group.widgets[1]:
  601. group.widgets[1].setPos(group_pos_x_i, group_pos_y_i)
  602. QTimer.singleShot(0, canvas.scene.update)
  603. return
  604. qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  605. def setGroupIcon(group_id, icon):
  606. if canvas.debug:
  607. qDebug("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon)))
  608. for group in canvas.group_list:
  609. if group.group_id == group_id:
  610. group.icon = icon
  611. group.widgets[0].setIcon(icon)
  612. if group.split and group.widgets[1]:
  613. group.widgets[1].setIcon(icon)
  614. QTimer.singleShot(0, canvas.scene.update)
  615. return
  616. qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon)))
  617. def getPluginAsGroup(plugin_id):
  618. if canvas.debug:
  619. qDebug("PatchCanvas::getPluginAsGroup(%i)" % plugin_id)
  620. for group in canvas.group_list:
  621. if group.plugin_id == plugin_id:
  622. return (True, group.group_id)
  623. qCritical("PatchCanvas::getPluginAsGroup(%i) - no such plugin" % plugin_id)
  624. return (False, -1)
  625. def setGroupAsPlugin(group_id, plugin_id, hasUi):
  626. if canvas.debug:
  627. qDebug("PatchCanvas::setGroupAsPlugin(%i, %i, %s)" % (group_id, plugin_id, bool2str(hasUi)))
  628. for group in canvas.group_list:
  629. if group.group_id == group_id:
  630. group.plugin_id = plugin_id
  631. group.widgets[0].setAsPlugin(plugin_id, hasUi)
  632. if group.split and group.widgets[1]:
  633. group.widgets[1].setAsPlugin(plugin_id, hasUi)
  634. return
  635. qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s) - unable to find group to set as plugin" % (group_id, plugin_id, bool2str(hasUi)))
  636. def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False):
  637. if canvas.debug:
  638. qDebug("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate)))
  639. for port in canvas.port_list:
  640. if port.group_id == group_id and port.port_id == port_id:
  641. qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  642. return
  643. box_widget = None
  644. port_widget = None
  645. for group in canvas.group_list:
  646. if group.group_id == group_id:
  647. if group.split and group.widgets[0].getSplittedMode() != port_mode and group.widgets[1]:
  648. n = 1
  649. else:
  650. n = 0
  651. box_widget = group.widgets[n]
  652. port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate)
  653. break
  654. if not (box_widget and port_widget):
  655. qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  656. return
  657. if options.eyecandy == EYECANDY_FULL:
  658. CanvasItemFX(port_widget, True)
  659. port_dict = port_dict_t()
  660. port_dict.group_id = group_id
  661. port_dict.port_id = port_id
  662. port_dict.port_name = port_name
  663. port_dict.port_mode = port_mode
  664. port_dict.port_type = port_type
  665. port_dict.is_alternate = is_alternate
  666. port_dict.widget = port_widget
  667. canvas.port_list.append(port_dict)
  668. box_widget.updatePositions()
  669. QTimer.singleShot(0, canvas.scene.update)
  670. def removePort(group_id, port_id):
  671. if canvas.debug:
  672. qDebug("PatchCanvas::removePort(%i, %i)" % (group_id, port_id))
  673. for port in canvas.port_list:
  674. if port.group_id == group_id and port.port_id == port_id:
  675. item = port.widget
  676. item.parentItem().removePortFromGroup(port_id)
  677. canvas.scene.removeItem(item)
  678. del item
  679. canvas.port_list.remove(port)
  680. QTimer.singleShot(0, canvas.scene.update)
  681. return
  682. qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id))
  683. def renamePort(group_id, port_id, new_port_name):
  684. if canvas.debug:
  685. qDebug("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode()))
  686. for port in canvas.port_list:
  687. if port.group_id == group_id and port.port_id == port_id:
  688. port.port_name = new_port_name
  689. port.widget.setPortName(new_port_name)
  690. port.widget.parentItem().updatePositions()
  691. QTimer.singleShot(0, canvas.scene.update)
  692. return
  693. qCritical("PatchCanvas::renamePort(%i, %i, %s) - Unable to find port to rename" % (group_id, port_id, new_port_name.encode()))
  694. def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id):
  695. if canvas.debug:
  696. qDebug("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % (connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  697. port_out = None
  698. port_in = None
  699. port_out_parent = None
  700. port_in_parent = None
  701. for port in canvas.port_list:
  702. if port.group_id == group_out_id and port.port_id == port_out_id:
  703. port_out = port.widget
  704. port_out_parent = port_out.parentItem()
  705. elif port.group_id == group_in_id and port.port_id == port_in_id:
  706. port_in = port.widget
  707. port_in_parent = port_in.parentItem()
  708. # FIXME
  709. if not (port_out and port_in):
  710. qCritical("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - unable to find ports to connect" % (connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  711. return
  712. connection_dict = connection_dict_t()
  713. connection_dict.connection_id = connection_id
  714. connection_dict.group_in_id = group_in_id
  715. connection_dict.port_in_id = port_in_id
  716. connection_dict.group_out_id = group_out_id
  717. connection_dict.port_out_id = port_out_id
  718. if options.use_bezier_lines:
  719. connection_dict.widget = CanvasBezierLine(port_out, port_in, None)
  720. else:
  721. connection_dict.widget = CanvasLine(port_out, port_in, None)
  722. canvas.scene.addItem(connection_dict.widget)
  723. port_out_parent.addLineFromGroup(connection_dict.widget, connection_id)
  724. port_in_parent.addLineFromGroup(connection_dict.widget, connection_id)
  725. canvas.last_z_value += 1
  726. port_out_parent.setZValue(canvas.last_z_value)
  727. port_in_parent.setZValue(canvas.last_z_value)
  728. canvas.last_z_value += 1
  729. connection_dict.widget.setZValue(canvas.last_z_value)
  730. canvas.connection_list.append(connection_dict)
  731. if options.eyecandy == EYECANDY_FULL:
  732. item = connection_dict.widget
  733. CanvasItemFX(item, True)
  734. QTimer.singleShot(0, canvas.scene.update)
  735. def disconnectPorts(connection_id):
  736. if canvas.debug:
  737. qDebug("PatchCanvas::disconnectPorts(%i)" % connection_id)
  738. line = None
  739. item1 = None
  740. item2 = None
  741. group1id = port1id = 0
  742. group2id = port2id = 0
  743. for connection in canvas.connection_list:
  744. if connection.connection_id == connection_id:
  745. group1id = connection.group_out_id
  746. group2id = connection.group_in_id
  747. port1id = connection.port_out_id
  748. port2id = connection.port_in_id
  749. line = connection.widget
  750. canvas.connection_list.remove(connection)
  751. break
  752. if not line:
  753. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id)
  754. return
  755. for port in canvas.port_list:
  756. if port.group_id == group1id and port.port_id == port1id:
  757. item1 = port.widget
  758. break
  759. if not item1:
  760. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id)
  761. return
  762. for port in canvas.port_list:
  763. if port.group_id == group2id and port.port_id == port2id:
  764. item2 = port.widget
  765. break
  766. if not item2:
  767. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id)
  768. return
  769. item1.parentItem().removeLineFromGroup(connection_id)
  770. item2.parentItem().removeLineFromGroup(connection_id)
  771. if options.eyecandy == EYECANDY_FULL:
  772. CanvasItemFX(line, False, True)
  773. else:
  774. line.deleteFromScene()
  775. QTimer.singleShot(0, canvas.scene.update)
  776. def arrange():
  777. if canvas.debug:
  778. qDebug("PatchCanvas::arrange()")
  779. def updateZValues():
  780. if canvas.debug:
  781. qDebug("PatchCanvas::updateZValues()")
  782. for group in canvas.group_list:
  783. group.widgets[0].resetLinesZValue()
  784. if group.split and group.widgets[1]:
  785. group.widgets[1].resetLinesZValue()
  786. def handlePluginRemoved(plugin_id):
  787. if canvas.debug:
  788. qDebug("PatchCanvas::handlePluginRemoved(%i)" % plugin_id)
  789. for group in canvas.group_list:
  790. if group.plugin_id < plugin_id:
  791. continue
  792. group.plugin_id -= 1
  793. group.widgets[0].m_plugin_id -= 1
  794. if group.split and group.widgets[1]:
  795. group.widgets[1].m_plugin_id -= 1
  796. def handleAllPluginsRemoved():
  797. if canvas.debug:
  798. qDebug("PatchCanvas::handleAllPluginsRemoved()")
  799. for group in canvas.group_list:
  800. group.plugin_id -= 1
  801. group.widgets[0].m_plugin_id -= 1
  802. if group.split and group.widgets[1]:
  803. group.widgets[1].m_plugin_id -= 1
  804. # Extra Internal functions
  805. def CanvasGetNewGroupPos(horizontal):
  806. if canvas.debug:
  807. qDebug("PatchCanvas::CanvasGetNewGroupPos(%s)" % bool2str(horizontal))
  808. new_pos = QPointF(canvas.initial_pos.x(), canvas.initial_pos.y())
  809. items = canvas.scene.items()
  810. break_loop = False
  811. while not break_loop:
  812. break_for = False
  813. for i in range(len(items)):
  814. item = items[i]
  815. if item and item.type() == CanvasBoxType:
  816. if item.sceneBoundingRect().contains(new_pos):
  817. if horizontal:
  818. new_pos += QPointF(item.boundingRect().width() + 15, 0)
  819. else:
  820. new_pos += QPointF(0, item.boundingRect().height() + 15)
  821. break_for = True
  822. break
  823. if i >= len(items) - 1 and not break_for:
  824. break_loop = True
  825. return new_pos
  826. def CanvasGetFullPortName(group_id, port_id):
  827. if canvas.debug:
  828. qDebug("PatchCanvas::CanvasGetFullPortName(%i, %i)" % (group_id, port_id))
  829. for port in canvas.port_list:
  830. if port.group_id == group_id and port.port_id == port_id:
  831. group_id = port.group_id
  832. for group in canvas.group_list:
  833. if group.group_id == group_id:
  834. return group.group_name + ":" + port.port_name
  835. break
  836. qCritical("PatchCanvas::CanvasGetFullPortName(%i, %i) - unable to find port" % (group_id, port_id))
  837. return ""
  838. def CanvasGetPortConnectionList(group_id, port_id):
  839. if canvas.debug:
  840. qDebug("PatchCanvas::CanvasGetPortConnectionList(%i, %i)" % (group_id, port_id))
  841. conn_list = []
  842. for connection in canvas.connection_list:
  843. if connection.group_out_id == group_id and connection.port_out_id == port_id:
  844. conn_list.append((connection.connection_id, connection.group_in_id, connection.port_in_id))
  845. elif connection.group_in_id == group_id and connection.port_in_id == port_id:
  846. conn_list.append((connection.connection_id, connection.group_out_id, connection.port_out_id))
  847. return conn_list
  848. def CanvasRemoveAnimation(f_animation):
  849. if canvas.debug:
  850. qDebug("PatchCanvas::CanvasRemoveAnimation(%s)" % f_animation)
  851. for animation in canvas.animation_list:
  852. if animation.animation == f_animation:
  853. canvas.animation_list.remove(animation)
  854. del animation.animation
  855. break
  856. def CanvasCallback(action, value1, value2, value_str):
  857. if canvas.debug:
  858. qDebug("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode()))
  859. canvas.callback(action, value1, value2, value_str)
  860. def CanvasItemFX(item, show, destroy=False):
  861. if canvas.debug:
  862. qDebug("PatchCanvas::CanvasItemFX(%s, %s, %s)" % (item, bool2str(show), bool2str(destroy)))
  863. # Check if item already has an animationItemFX
  864. for animation in canvas.animation_list:
  865. if animation.item == item:
  866. animation.animation.stop()
  867. canvas.animation_list.remove(animation)
  868. del animation.animation
  869. break
  870. animation = CanvasFadeAnimation(item, show)
  871. animation.setDuration(750 if show else 500)
  872. animation_dict = animation_dict_t()
  873. animation_dict.animation = animation
  874. animation_dict.item = item
  875. canvas.animation_list.append(animation_dict)
  876. if show:
  877. animation.finished.connect(canvas.qobject.AnimationIdle)
  878. else:
  879. if destroy:
  880. animation.finished.connect(canvas.qobject.AnimationDestroy)
  881. else:
  882. animation.finished.connect(canvas.qobject.AnimationHide)
  883. animation.start()
  884. def CanvasRemoveItemFX(item):
  885. if canvas.debug:
  886. qDebug("PatchCanvas::CanvasRemoveItemFX(%s)" % item)
  887. if item.type() == CanvasBoxType:
  888. item.removeIconFromScene()
  889. canvas.scene.removeItem(item)
  890. elif item.type() == CanvasPortType:
  891. canvas.scene.removeItem(item)
  892. elif item.type() in (CanvasLineType, CanvasBezierLineType):
  893. pass #item.deleteFromScene()
  894. # Force deletion of item if needed
  895. if item.type() in (CanvasBoxType, CanvasPortType):
  896. del item
  897. # ------------------------------------------------------------------------------
  898. # patchscene.cpp
  899. class PatchScene(QGraphicsScene):
  900. scaleChanged = pyqtSignal(float)
  901. sceneGroupMoved = pyqtSignal(int, int, QPointF)
  902. pluginSelected = pyqtSignal(list)
  903. def __init__(self, parent, view):
  904. QGraphicsScene.__init__(self, parent)
  905. self.m_ctrl_down = False
  906. self.m_mouse_down_init = False
  907. self.m_mouse_rubberband = False
  908. self.addRubberBand()
  909. self.m_view = view
  910. if not self.m_view:
  911. qFatal("PatchCanvas::PatchScene() - invalid view")
  912. self.selectionChanged.connect(self.slot_selectionChanged)
  913. def addRubberBand(self):
  914. self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0))
  915. self.m_rubberband.setZValue(-1)
  916. self.m_rubberband.hide()
  917. self.m_rubberband_selection = False
  918. self.m_rubberband_orig_point = QPointF(0, 0)
  919. def clear(self):
  920. QGraphicsScene.clear(self)
  921. # Re-add rubberband, that just got deleted
  922. self.addRubberBand()
  923. def fixScaleFactor(self):
  924. scale = self.m_view.transform().m11()
  925. if scale > 3.0:
  926. self.m_view.resetTransform()
  927. self.m_view.scale(3.0, 3.0)
  928. elif scale < 0.2:
  929. self.m_view.resetTransform()
  930. self.m_view.scale(0.2, 0.2)
  931. self.scaleChanged.emit(self.m_view.transform().m11())
  932. def updateTheme(self):
  933. self.setBackgroundBrush(canvas.theme.canvas_bg)
  934. self.m_rubberband.setPen(canvas.theme.rubberband_pen)
  935. self.m_rubberband.setBrush(canvas.theme.rubberband_brush)
  936. def zoom_fit(self):
  937. min_x = min_y = max_x = max_y = None
  938. first_value = True
  939. items_list = self.items()
  940. if len(items_list) > 0:
  941. for item in items_list:
  942. if item and item.isVisible() and item.type() == CanvasBoxType:
  943. pos = item.scenePos()
  944. rect = item.boundingRect()
  945. if first_value:
  946. min_x = pos.x()
  947. elif pos.x() < min_x:
  948. min_x = pos.x()
  949. if first_value:
  950. min_y = pos.y()
  951. elif pos.y() < min_y:
  952. min_y = pos.y()
  953. if first_value:
  954. max_x = pos.x() + rect.width()
  955. elif pos.x() + rect.width() > max_x:
  956. max_x = pos.x() + rect.width()
  957. if first_value:
  958. max_y = pos.y() + rect.height()
  959. elif pos.y() + rect.height() > max_y:
  960. max_y = pos.y() + rect.height()
  961. first_value = False
  962. if not first_value:
  963. self.m_view.fitInView(min_x, min_y, abs(max_x - min_x), abs(max_y - min_y), Qt.KeepAspectRatio)
  964. self.fixScaleFactor()
  965. def zoom_in(self):
  966. if self.m_view.transform().m11() < 3.0:
  967. self.m_view.scale(1.2, 1.2)
  968. self.scaleChanged.emit(self.m_view.transform().m11())
  969. def zoom_out(self):
  970. if self.m_view.transform().m11() > 0.2:
  971. self.m_view.scale(0.8, 0.8)
  972. self.scaleChanged.emit(self.m_view.transform().m11())
  973. def zoom_reset(self):
  974. self.m_view.resetTransform()
  975. self.scaleChanged.emit(1.0)
  976. @pyqtSlot()
  977. def slot_selectionChanged(self):
  978. items_list = self.selectedItems()
  979. if len(items_list) == 0:
  980. self.pluginSelected.emit([])
  981. return
  982. plugin_list = []
  983. for item in items_list:
  984. if item and item.isVisible():
  985. group_item = None
  986. if item.type() == CanvasBoxType:
  987. group_item = item
  988. elif item.type() == CanvasPortType:
  989. group_item = item.parentItem()
  990. #elif item.type() in (CanvasLineType, CanvasBezierLineType, CanvasLineMovType, CanvasBezierLineMovType):
  991. #plugin_list = []
  992. #break
  993. if group_item is not None and group_item.m_plugin_id >= 0:
  994. plugin_list.append(group_item.m_plugin_id)
  995. self.pluginSelected.emit(plugin_list)
  996. def keyPressEvent(self, event):
  997. if not self.m_view:
  998. return event.ignore()
  999. if event.key() == Qt.Key_Control:
  1000. self.m_ctrl_down = True
  1001. elif event.key() == Qt.Key_Home:
  1002. self.zoom_fit()
  1003. return event.accept()
  1004. elif self.m_ctrl_down:
  1005. if event.key() == Qt.Key_Plus:
  1006. self.zoom_in()
  1007. return event.accept()
  1008. elif event.key() == Qt.Key_Minus:
  1009. self.zoom_out()
  1010. return event.accept()
  1011. elif event.key() == Qt.Key_1:
  1012. self.zoom_reset()
  1013. return event.accept()
  1014. QGraphicsScene.keyPressEvent(self, event)
  1015. def keyReleaseEvent(self, event):
  1016. if event.key() == Qt.Key_Control:
  1017. self.m_ctrl_down = False
  1018. QGraphicsScene.keyReleaseEvent(self, event)
  1019. def mousePressEvent(self, event):
  1020. self.m_mouse_down_init = bool(event.button() == Qt.LeftButton)
  1021. self.m_mouse_rubberband = False
  1022. QGraphicsScene.mousePressEvent(self, event)
  1023. def mouseMoveEvent(self, event):
  1024. if self.m_mouse_down_init:
  1025. self.m_mouse_down_init = False
  1026. self.m_mouse_rubberband = bool(len(self.selectedItems()) == 0)
  1027. if self.m_mouse_rubberband:
  1028. if not self.m_rubberband_selection:
  1029. self.m_rubberband.show()
  1030. self.m_rubberband_selection = True
  1031. self.m_rubberband_orig_point = event.scenePos()
  1032. pos = event.scenePos()
  1033. if pos.x() > self.m_rubberband_orig_point.x():
  1034. x = self.m_rubberband_orig_point.x()
  1035. else:
  1036. x = pos.x()
  1037. if pos.y() > self.m_rubberband_orig_point.y():
  1038. y = self.m_rubberband_orig_point.y()
  1039. else:
  1040. y = pos.y()
  1041. self.m_rubberband.setRect(x, y, abs(pos.x() - self.m_rubberband_orig_point.x()), abs(pos.y() - self.m_rubberband_orig_point.y()))
  1042. return event.accept()
  1043. QGraphicsScene.mouseMoveEvent(self, event)
  1044. def mouseReleaseEvent(self, event):
  1045. if self.m_rubberband_selection:
  1046. items_list = self.items()
  1047. if len(items_list) > 0:
  1048. for item in items_list:
  1049. if item and item.isVisible() and item.type() == CanvasBoxType:
  1050. item_rect = item.sceneBoundingRect()
  1051. item_top_left = QPointF(item_rect.x(), item_rect.y())
  1052. item_bottom_right = QPointF(item_rect.x() + item_rect.width(), item_rect.y() + item_rect.height())
  1053. if self.m_rubberband.contains(item_top_left) and self.m_rubberband.contains(item_bottom_right):
  1054. item.setSelected(True)
  1055. self.m_rubberband.hide()
  1056. self.m_rubberband.setRect(0, 0, 0, 0)
  1057. self.m_rubberband_selection = False
  1058. else:
  1059. items_list = self.selectedItems()
  1060. for item in items_list:
  1061. if item and item.isVisible() and item.type() == CanvasBoxType:
  1062. item.checkItemPos()
  1063. self.sceneGroupMoved.emit(item.getGroupId(), item.getSplittedMode(), item.scenePos())
  1064. if len(items_list) > 1:
  1065. canvas.scene.update()
  1066. self.m_mouse_down_init = False
  1067. self.m_mouse_rubberband = False
  1068. QGraphicsScene.mouseReleaseEvent(self, event)
  1069. def wheelEvent(self, event):
  1070. if not self.m_view:
  1071. return event.ignore()
  1072. if self.m_ctrl_down:
  1073. factor = 1.41 ** (event.delta() / 240.0)
  1074. self.m_view.scale(factor, factor)
  1075. self.fixScaleFactor()
  1076. return event.accept()
  1077. QGraphicsScene.wheelEvent(self, event)
  1078. # ------------------------------------------------------------------------------
  1079. # canvasfadeanimation.cpp
  1080. class CanvasFadeAnimation(QAbstractAnimation):
  1081. def __init__(self, item, show, parent=None):
  1082. QAbstractAnimation.__init__(self, parent)
  1083. self.m_show = show
  1084. self.m_duration = 0
  1085. self.m_item = item
  1086. def item(self):
  1087. return self.m_item
  1088. def setDuration(self, time):
  1089. if self.m_item.opacity() == 0 and not self.m_show:
  1090. self._duration = 0
  1091. else:
  1092. self.m_item.show()
  1093. self.m_duration = time
  1094. def duration(self):
  1095. return self.m_duration
  1096. def updateCurrentTime(self, time):
  1097. if self.m_duration == 0:
  1098. return
  1099. if self.m_show:
  1100. value = float(time) / self.m_duration
  1101. else:
  1102. value = 1.0 - (float(time) / self.m_duration)
  1103. self.m_item.setOpacity(value)
  1104. if self.m_item.type() == CanvasBoxType:
  1105. self.m_item.setShadowOpacity(value)
  1106. def updateDirection(self, direction):
  1107. pass
  1108. def updateState(self, oldState, newState):
  1109. pass
  1110. # ------------------------------------------------------------------------------
  1111. # canvasline.cpp
  1112. class CanvasLine(QGraphicsLineItem):
  1113. def __init__(self, item1, item2, parent):
  1114. QGraphicsLineItem.__init__(self, parent)
  1115. self.item1 = item1
  1116. self.item2 = item2
  1117. self.m_locked = False
  1118. self.m_lineSelected = False
  1119. self.setGraphicsEffect(None)
  1120. self.updateLinePos()
  1121. def deleteFromScene(self):
  1122. canvas.scene.removeItem(self)
  1123. del self
  1124. def isLocked(self):
  1125. return self.m_locked
  1126. def setLocked(self, yesno):
  1127. self.m_locked = yesno
  1128. def isLineSelected(self):
  1129. return self.m_lineSelected
  1130. def setLineSelected(self, yesno):
  1131. if self.m_locked:
  1132. return
  1133. if options.eyecandy == EYECANDY_FULL:
  1134. if yesno:
  1135. self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject()))
  1136. else:
  1137. self.setGraphicsEffect(None)
  1138. self.m_lineSelected = yesno
  1139. self.updateLineGradient()
  1140. def updateLinePos(self):
  1141. if self.item1.getPortMode() == PORT_MODE_OUTPUT:
  1142. line = QLineF(self.item1.scenePos().x() + self.item1.getPortWidth() + 12,
  1143. self.item1.scenePos().y() + float(canvas.theme.port_height)/2,
  1144. self.item2.scenePos().x(),
  1145. self.item2.scenePos().y() + float(canvas.theme.port_height)/2)
  1146. self.setLine(line)
  1147. self.m_lineSelected = False
  1148. self.updateLineGradient()
  1149. def type(self):
  1150. return CanvasLineType
  1151. def updateLineGradient(self):
  1152. pos_top = self.boundingRect().top()
  1153. pos_bot = self.boundingRect().bottom()
  1154. if self.item2.scenePos().y() >= self.item1.scenePos().y():
  1155. pos1 = 0
  1156. pos2 = 1
  1157. else:
  1158. pos1 = 1
  1159. pos2 = 0
  1160. port_type1 = self.item1.getPortType()
  1161. port_type2 = self.item2.getPortType()
  1162. port_gradient = QLinearGradient(0, pos_top, 0, pos_bot)
  1163. if port_type1 == PORT_TYPE_AUDIO_JACK:
  1164. port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1165. elif port_type1 == PORT_TYPE_MIDI_JACK:
  1166. port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1167. elif port_type1 == PORT_TYPE_MIDI_ALSA:
  1168. port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1169. elif port_type1 == PORT_TYPE_PARAMETER:
  1170. port_gradient.setColorAt(pos1, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
  1171. if port_type2 == PORT_TYPE_AUDIO_JACK:
  1172. port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1173. elif port_type2 == PORT_TYPE_MIDI_JACK:
  1174. port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1175. elif port_type2 == PORT_TYPE_MIDI_ALSA:
  1176. port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1177. elif port_type2 == PORT_TYPE_PARAMETER:
  1178. port_gradient.setColorAt(pos2, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
  1179. self.setPen(QPen(port_gradient, 2))
  1180. def paint(self, painter, option, widget):
  1181. painter.save()
  1182. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1183. QGraphicsLineItem.paint(self, painter, option, widget)
  1184. painter.restore()
  1185. # ------------------------------------------------------------------------------
  1186. # canvasbezierline.cpp
  1187. class CanvasBezierLine(QGraphicsPathItem):
  1188. def __init__(self, item1, item2, parent):
  1189. QGraphicsPathItem.__init__(self, parent)
  1190. self.item1 = item1
  1191. self.item2 = item2
  1192. self.m_locked = False
  1193. self.m_lineSelected = False
  1194. self.setBrush(QColor(0, 0, 0, 0))
  1195. self.setGraphicsEffect(None)
  1196. self.updateLinePos()
  1197. def deleteFromScene(self):
  1198. canvas.scene.removeItem(self)
  1199. del self
  1200. def isLocked(self):
  1201. return self.m_locked
  1202. def setLocked(self, yesno):
  1203. self.m_locked = yesno
  1204. def isLineSelected(self):
  1205. return self.m_lineSelected
  1206. def setLineSelected(self, yesno):
  1207. if self.m_locked:
  1208. return
  1209. if options.eyecandy == EYECANDY_FULL:
  1210. if yesno:
  1211. self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject()))
  1212. else:
  1213. self.setGraphicsEffect(None)
  1214. self.m_lineSelected = yesno
  1215. self.updateLineGradient()
  1216. def updateLinePos(self):
  1217. if self.item1.getPortMode() == PORT_MODE_OUTPUT:
  1218. item1_x = self.item1.scenePos().x() + self.item1.getPortWidth() + 12
  1219. item1_y = self.item1.scenePos().y() + float(canvas.theme.port_height)/2
  1220. item2_x = self.item2.scenePos().x()
  1221. item2_y = self.item2.scenePos().y() + float(canvas.theme.port_height)/2
  1222. item1_mid_x = abs(item1_x - item2_x) / 2
  1223. item1_new_x = item1_x + item1_mid_x
  1224. item2_mid_x = abs(item1_x - item2_x) / 2
  1225. item2_new_x = item2_x - item2_mid_x
  1226. path = QPainterPath(QPointF(item1_x, item1_y))
  1227. path.cubicTo(item1_new_x, item1_y, item2_new_x, item2_y, item2_x, item2_y)
  1228. self.setPath(path)
  1229. self.m_lineSelected = False
  1230. self.updateLineGradient()
  1231. def type(self):
  1232. return CanvasBezierLineType
  1233. def updateLineGradient(self):
  1234. pos_top = self.boundingRect().top()
  1235. pos_bot = self.boundingRect().bottom()
  1236. if self.item2.scenePos().y() >= self.item1.scenePos().y():
  1237. pos1 = 0
  1238. pos2 = 1
  1239. else:
  1240. pos1 = 1
  1241. pos2 = 0
  1242. port_type1 = self.item1.getPortType()
  1243. port_type2 = self.item2.getPortType()
  1244. port_gradient = QLinearGradient(0, pos_top, 0, pos_bot)
  1245. if port_type1 == PORT_TYPE_AUDIO_JACK:
  1246. port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1247. elif port_type1 == PORT_TYPE_MIDI_JACK:
  1248. port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1249. elif port_type1 == PORT_TYPE_MIDI_ALSA:
  1250. port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1251. elif port_type1 == PORT_TYPE_PARAMETER:
  1252. port_gradient.setColorAt(pos1, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
  1253. if port_type2 == PORT_TYPE_AUDIO_JACK:
  1254. port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1255. elif port_type2 == PORT_TYPE_MIDI_JACK:
  1256. port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1257. elif port_type2 == PORT_TYPE_MIDI_ALSA:
  1258. port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1259. elif port_type2 == PORT_TYPE_PARAMETER:
  1260. port_gradient.setColorAt(pos2, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter)
  1261. self.setPen(QPen(port_gradient, 2))
  1262. def paint(self, painter, option, widget):
  1263. painter.save()
  1264. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1265. QGraphicsPathItem.paint(self, painter, option, widget)
  1266. painter.restore()
  1267. # ------------------------------------------------------------------------------
  1268. # canvaslivemov.cpp
  1269. class CanvasLineMov(QGraphicsLineItem):
  1270. def __init__(self, port_mode, port_type, parent):
  1271. QGraphicsLineItem.__init__(self, parent)
  1272. self.m_port_mode = port_mode
  1273. self.m_port_type = port_type
  1274. # Port position doesn't change while moving around line
  1275. self.p_lineX = self.scenePos().x()
  1276. self.p_lineY = self.scenePos().y()
  1277. self.p_width = self.parentItem().getPortWidth()
  1278. if port_type == PORT_TYPE_AUDIO_JACK:
  1279. pen = QPen(canvas.theme.line_audio_jack, 2)
  1280. elif port_type == PORT_TYPE_MIDI_JACK:
  1281. pen = QPen(canvas.theme.line_midi_jack, 2)
  1282. elif port_type == PORT_TYPE_MIDI_ALSA:
  1283. pen = QPen(canvas.theme.line_midi_alsa, 2)
  1284. elif port_type == PORT_TYPE_PARAMETER:
  1285. pen = QPen(canvas.theme.line_parameter, 2)
  1286. else:
  1287. qWarning("PatchCanvas::CanvasLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent))
  1288. pen = QPen(Qt.black)
  1289. self.setPen(pen)
  1290. def deleteFromScene(self):
  1291. canvas.scene.removeItem(self)
  1292. del self
  1293. def updateLinePos(self, scenePos):
  1294. item_pos = [0, 0]
  1295. if self.m_port_mode == PORT_MODE_INPUT:
  1296. item_pos[0] = 0
  1297. item_pos[1] = float(canvas.theme.port_height)/2
  1298. elif self.m_port_mode == PORT_MODE_OUTPUT:
  1299. item_pos[0] = self.p_width + 12
  1300. item_pos[1] = float(canvas.theme.port_height)/2
  1301. else:
  1302. return
  1303. line = QLineF(item_pos[0], item_pos[1], scenePos.x() - self.p_lineX, scenePos.y() - self.p_lineY)
  1304. self.setLine(line)
  1305. def type(self):
  1306. return CanvasLineMovType
  1307. def paint(self, painter, option, widget):
  1308. painter.save()
  1309. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1310. QGraphicsLineItem.paint(self, painter, option, widget)
  1311. painter.restore()
  1312. # ------------------------------------------------------------------------------
  1313. # canvasbezierlinemov.cpp
  1314. class CanvasBezierLineMov(QGraphicsPathItem):
  1315. def __init__(self, port_mode, port_type, parent):
  1316. QGraphicsPathItem.__init__(self, parent)
  1317. self.m_port_mode = port_mode
  1318. self.m_port_type = port_type
  1319. # Port position doesn't change while moving around line
  1320. self.p_itemX = self.scenePos().x()
  1321. self.p_itemY = self.scenePos().y()
  1322. self.p_width = self.parentItem().getPortWidth()
  1323. if port_type == PORT_TYPE_AUDIO_JACK:
  1324. pen = QPen(canvas.theme.line_audio_jack, 2)
  1325. elif port_type == PORT_TYPE_MIDI_JACK:
  1326. pen = QPen(canvas.theme.line_midi_jack, 2)
  1327. elif port_type == PORT_TYPE_MIDI_ALSA:
  1328. pen = QPen(canvas.theme.line_midi_alsa, 2)
  1329. elif port_type == PORT_TYPE_PARAMETER:
  1330. pen = QPen(canvas.theme.line_parameter, 2)
  1331. else:
  1332. qWarning("PatchCanvas::CanvasBezierLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent))
  1333. pen = QPen(Qt.black)
  1334. self.setBrush(QColor(0, 0, 0, 0))
  1335. self.setPen(pen)
  1336. def deleteFromScene(self):
  1337. canvas.scene.removeItem(self)
  1338. del self
  1339. def updateLinePos(self, scenePos):
  1340. if self.m_port_mode == PORT_MODE_INPUT:
  1341. old_x = 0
  1342. old_y = float(canvas.theme.port_height)/2
  1343. mid_x = abs(scenePos.x() - self.p_itemX) / 2
  1344. new_x = old_x - mid_x
  1345. elif self.m_port_mode == PORT_MODE_OUTPUT:
  1346. old_x = self.p_width + 12
  1347. old_y = float(canvas.theme.port_height)/2
  1348. mid_x = abs(scenePos.x() - (self.p_itemX + old_x)) / 2
  1349. new_x = old_x + mid_x
  1350. else:
  1351. return
  1352. final_x = scenePos.x() - self.p_itemX
  1353. final_y = scenePos.y() - self.p_itemY
  1354. path = QPainterPath(QPointF(old_x, old_y))
  1355. path.cubicTo(new_x, old_y, new_x, final_y, final_x, final_y)
  1356. self.setPath(path)
  1357. def type(self):
  1358. return CanvasBezierLineMovType
  1359. def paint(self, painter, option, widget):
  1360. painter.save()
  1361. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1362. QGraphicsPathItem.paint(self, painter, option, widget)
  1363. painter.restore()
  1364. # ------------------------------------------------------------------------------
  1365. # canvasport.cpp
  1366. class CanvasPort(QGraphicsItem):
  1367. def __init__(self, group_id, port_id, port_name, port_mode, port_type, is_alternate, parent):
  1368. QGraphicsItem.__init__(self, parent)
  1369. # Save Variables, useful for later
  1370. self.m_group_id = group_id
  1371. self.m_port_id = port_id
  1372. self.m_port_mode = port_mode
  1373. self.m_port_type = port_type
  1374. self.m_port_name = port_name
  1375. self.m_is_alternate = is_alternate
  1376. # Base Variables
  1377. self.m_port_width = 15
  1378. self.m_port_height = canvas.theme.port_height
  1379. self.m_port_font = QFont()
  1380. self.m_port_font.setFamily(canvas.theme.port_font_name)
  1381. self.m_port_font.setPixelSize(canvas.theme.port_font_size)
  1382. self.m_port_font.setWeight(canvas.theme.port_font_state)
  1383. self.m_line_mov = None
  1384. self.m_hover_item = None
  1385. self.m_last_selected_state = False
  1386. self.m_mouse_down = False
  1387. self.m_cursor_moving = False
  1388. self.setFlags(QGraphicsItem.ItemIsSelectable)
  1389. if options.auto_select_items:
  1390. self.setAcceptHoverEvents(True)
  1391. def getGroupId(self):
  1392. return self.m_group_id
  1393. def getPortId(self):
  1394. return self.m_port_id
  1395. def getPortMode(self):
  1396. return self.m_port_mode
  1397. def getPortType(self):
  1398. return self.m_port_type
  1399. def getPortName(self):
  1400. return self.m_port_name
  1401. def getFullPortName(self):
  1402. return self.parentItem().getGroupName() + ":" + self.m_port_name
  1403. def getPortWidth(self):
  1404. return self.m_port_width
  1405. def getPortHeight(self):
  1406. return self.m_port_height
  1407. def setPortMode(self, port_mode):
  1408. self.m_port_mode = port_mode
  1409. self.update()
  1410. def setPortType(self, port_type):
  1411. self.m_port_type = port_type
  1412. self.update()
  1413. def setPortName(self, port_name):
  1414. if QFontMetrics(self.m_port_font).width(port_name) < QFontMetrics(self.m_port_font).width(self.m_port_name):
  1415. QTimer.singleShot(0, canvas.scene.update)
  1416. self.m_port_name = port_name
  1417. self.update()
  1418. def setPortWidth(self, port_width):
  1419. if port_width < self.m_port_width:
  1420. QTimer.singleShot(0, canvas.scene.update)
  1421. self.m_port_width = port_width
  1422. self.update()
  1423. def type(self):
  1424. return CanvasPortType
  1425. def hoverEnterEvent(self, event):
  1426. if options.auto_select_items:
  1427. self.setSelected(True)
  1428. QGraphicsItem.hoverEnterEvent(self, event)
  1429. def hoverLeaveEvent(self, event):
  1430. if options.auto_select_items:
  1431. self.setSelected(False)
  1432. QGraphicsItem.hoverLeaveEvent(self, event)
  1433. def mousePressEvent(self, event):
  1434. self.m_hover_item = None
  1435. self.m_mouse_down = bool(event.button() == Qt.LeftButton)
  1436. self.m_cursor_moving = False
  1437. QGraphicsItem.mousePressEvent(self, event)
  1438. def mouseMoveEvent(self, event):
  1439. if self.m_mouse_down:
  1440. if not self.m_cursor_moving:
  1441. self.setCursor(QCursor(Qt.CrossCursor))
  1442. self.m_cursor_moving = True
  1443. for connection in canvas.connection_list:
  1444. if ((connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id) or
  1445. (connection.group_in_id == self.m_group_id and connection.port_in_id == self.m_port_id)):
  1446. connection.widget.setLocked(True)
  1447. if not self.m_line_mov:
  1448. if options.use_bezier_lines:
  1449. self.m_line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, self)
  1450. else:
  1451. self.m_line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, self)
  1452. canvas.last_z_value += 1
  1453. self.m_line_mov.setZValue(canvas.last_z_value)
  1454. canvas.last_z_value += 1
  1455. self.parentItem().setZValue(canvas.last_z_value)
  1456. item = None
  1457. items = canvas.scene.items(event.scenePos(), Qt.ContainsItemShape, Qt.AscendingOrder)
  1458. for i in range(len(items)):
  1459. if items[i].type() == CanvasPortType:
  1460. if items[i] != self:
  1461. if not item:
  1462. item = items[i]
  1463. elif items[i].parentItem().zValue() > item.parentItem().zValue():
  1464. item = items[i]
  1465. if self.m_hover_item and self.m_hover_item != item:
  1466. self.m_hover_item.setSelected(False)
  1467. if item:
  1468. if item.getPortMode() != self.m_port_mode and item.getPortType() == self.m_port_type:
  1469. item.setSelected(True)
  1470. self.m_hover_item = item
  1471. else:
  1472. self.m_hover_item = None
  1473. else:
  1474. self.m_hover_item = None
  1475. self.m_line_mov.updateLinePos(event.scenePos())
  1476. return event.accept()
  1477. QGraphicsItem.mouseMoveEvent(self, event)
  1478. def mouseReleaseEvent(self, event):
  1479. if self.m_mouse_down:
  1480. if self.m_line_mov:
  1481. self.m_line_mov.deleteFromScene()
  1482. self.m_line_mov = None
  1483. for connection in canvas.connection_list:
  1484. if ((connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id) or
  1485. (connection.group_in_id == self.m_group_id and connection.port_in_id == self.m_port_id)):
  1486. connection.widget.setLocked(False)
  1487. if self.m_hover_item:
  1488. # TODO: a better way to check already existing connection
  1489. for connection in canvas.connection_list:
  1490. hover_group_id = self.m_hover_item.getGroupId()
  1491. hover_port_id = self.m_hover_item.getPortId()
  1492. if (
  1493. (connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id and
  1494. connection.group_in_id == hover_group_id and connection.port_in_id == hover_port_id) or
  1495. (connection.group_out_id == hover_group_id and connection.port_out_id == hover_port_id and
  1496. connection.group_in_id == self.m_group_id and connection.port_in_id == self.m_port_id)
  1497. ):
  1498. canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "")
  1499. break
  1500. else:
  1501. if self.m_port_mode == PORT_MODE_OUTPUT:
  1502. conn = "%i:%i:%i:%i" % (self.m_group_id, self.m_port_id, self.m_hover_item.getGroupId(), self.m_hover_item.getPortId())
  1503. canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn)
  1504. else:
  1505. conn = "%i:%i:%i:%i" % (self.m_hover_item.getGroupId(), self.m_hover_item.getPortId(), self.m_group_id, self.m_port_id)
  1506. canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn)
  1507. canvas.scene.clearSelection()
  1508. if self.m_cursor_moving:
  1509. self.setCursor(QCursor(Qt.ArrowCursor))
  1510. self.m_hover_item = None
  1511. self.m_mouse_down = False
  1512. self.m_cursor_moving = False
  1513. QGraphicsItem.mouseReleaseEvent(self, event)
  1514. def contextMenuEvent(self, event):
  1515. canvas.scene.clearSelection()
  1516. self.setSelected(True)
  1517. menu = QMenu()
  1518. discMenu = QMenu("Disconnect", menu)
  1519. conn_list = CanvasGetPortConnectionList(self.m_group_id, self.m_port_id)
  1520. if len(conn_list) > 0:
  1521. for conn_id, group_id, port_id in conn_list:
  1522. act_x_disc = discMenu.addAction(CanvasGetFullPortName(group_id, port_id))
  1523. act_x_disc.setData(conn_id)
  1524. act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect)
  1525. else:
  1526. act_x_disc = discMenu.addAction("No connections")
  1527. act_x_disc.setEnabled(False)
  1528. menu.addMenu(discMenu)
  1529. act_x_disc_all = menu.addAction("Disconnect &All")
  1530. act_x_sep_1 = menu.addSeparator()
  1531. act_x_info = menu.addAction("Get &Info")
  1532. act_x_rename = menu.addAction("&Rename")
  1533. if not features.port_info:
  1534. act_x_info.setVisible(False)
  1535. if not features.port_rename:
  1536. act_x_rename.setVisible(False)
  1537. if not (features.port_info and features.port_rename):
  1538. act_x_sep_1.setVisible(False)
  1539. act_selected = menu.exec_(event.screenPos())
  1540. if act_selected == act_x_disc_all:
  1541. for conn_id, group_id, port_id in conn_list:
  1542. canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "")
  1543. elif act_selected == act_x_info:
  1544. canvas.callback(ACTION_PORT_INFO, self.m_group_id, self.m_port_id, "")
  1545. elif act_selected == act_x_rename:
  1546. new_name_try = QInputDialog.getText(None, "Rename Port", "New name:", QLineEdit.Normal, self.m_port_name)
  1547. if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
  1548. canvas.callback(ACTION_PORT_RENAME, self.m_group_id, self.m_port_id, new_name_try[0])
  1549. event.accept()
  1550. def boundingRect(self):
  1551. return QRectF(0, 0, self.m_port_width + 12, self.m_port_height)
  1552. def paint(self, painter, option, widget):
  1553. painter.save()
  1554. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))
  1555. poly_locx = [0, 0, 0, 0, 0]
  1556. if self.m_port_mode == PORT_MODE_INPUT:
  1557. text_pos = QPointF(3, canvas.theme.port_text_ypos)
  1558. if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
  1559. poly_locx[0] = 0
  1560. poly_locx[1] = self.m_port_width + 5
  1561. poly_locx[2] = self.m_port_width + 12
  1562. poly_locx[3] = self.m_port_width + 5
  1563. poly_locx[4] = 0
  1564. elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
  1565. poly_locx[0] = 0
  1566. poly_locx[1] = self.m_port_width + 5
  1567. poly_locx[2] = self.m_port_width + 5
  1568. poly_locx[3] = self.m_port_width + 5
  1569. poly_locx[4] = 0
  1570. else:
  1571. qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode)
  1572. return
  1573. elif self.m_port_mode == PORT_MODE_OUTPUT:
  1574. text_pos = QPointF(9, canvas.theme.port_text_ypos)
  1575. if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
  1576. poly_locx[0] = self.m_port_width + 12
  1577. poly_locx[1] = 7
  1578. poly_locx[2] = 0
  1579. poly_locx[3] = 7
  1580. poly_locx[4] = self.m_port_width + 12
  1581. elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
  1582. poly_locx[0] = self.m_port_width + 12
  1583. poly_locx[1] = 5
  1584. poly_locx[2] = 5
  1585. poly_locx[3] = 5
  1586. poly_locx[4] = self.m_port_width + 12
  1587. else:
  1588. qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode)
  1589. return
  1590. else:
  1591. qCritical("PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode))
  1592. return
  1593. if self.m_port_type == PORT_TYPE_AUDIO_JACK:
  1594. poly_color = canvas.theme.port_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_audio_jack_bg
  1595. poly_pen = canvas.theme.port_audio_jack_pen_sel if self.isSelected() else canvas.theme.port_audio_jack_pen
  1596. text_pen = canvas.theme.port_audio_jack_text_sel if self.isSelected() else canvas.theme.port_audio_jack_text
  1597. conn_pen = canvas.theme.port_audio_jack_pen_sel
  1598. elif self.m_port_type == PORT_TYPE_MIDI_JACK:
  1599. poly_color = canvas.theme.port_midi_jack_bg_sel if self.isSelected() else canvas.theme.port_midi_jack_bg
  1600. poly_pen = canvas.theme.port_midi_jack_pen_sel if self.isSelected() else canvas.theme.port_midi_jack_pen
  1601. text_pen = canvas.theme.port_midi_jack_text_sel if self.isSelected() else canvas.theme.port_midi_jack_text
  1602. conn_pen = canvas.theme.port_midi_jack_pen_sel
  1603. elif self.m_port_type == PORT_TYPE_MIDI_ALSA:
  1604. poly_color = canvas.theme.port_midi_alsa_bg_sel if self.isSelected() else canvas.theme.port_midi_alsa_bg
  1605. poly_pen = canvas.theme.port_midi_alsa_pen_sel if self.isSelected() else canvas.theme.port_midi_alsa_pen
  1606. text_pen = canvas.theme.port_midi_alsa_text_sel if self.isSelected() else canvas.theme.port_midi_alsa_text
  1607. conn_pen = canvas.theme.port_midi_alsa_pen_sel
  1608. elif self.m_port_type == PORT_TYPE_PARAMETER:
  1609. poly_color = canvas.theme.port_parameter_bg_sel if self.isSelected() else canvas.theme.port_parameter_bg
  1610. poly_pen = canvas.theme.port_parameter_pen_sel if self.isSelected() else canvas.theme.port_parameter_pen
  1611. text_pen = canvas.theme.port_parameter_text_sel if self.isSelected() else canvas.theme.port_parameter_text
  1612. conn_pen = canvas.theme.port_parameter_pen_sel
  1613. else:
  1614. qCritical("PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type))
  1615. return
  1616. if self.m_is_alternate:
  1617. poly_color = poly_color.darker(180)
  1618. #poly_pen.setColor(poly_pen.color().darker(110))
  1619. #text_pen.setColor(text_pen.color()) #.darker(150))
  1620. #conn_pen.setColor(conn_pen.color()) #.darker(150))
  1621. polygon = QPolygonF()
  1622. polygon += QPointF(poly_locx[0], 0)
  1623. polygon += QPointF(poly_locx[1], 0)
  1624. polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2)
  1625. polygon += QPointF(poly_locx[3], canvas.theme.port_height)
  1626. polygon += QPointF(poly_locx[4], canvas.theme.port_height)
  1627. if canvas.theme.port_bg_pixmap:
  1628. portRect = polygon.boundingRect()
  1629. portPos = portRect.topLeft()
  1630. painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos)
  1631. else:
  1632. painter.setBrush(poly_color) #.lighter(200))
  1633. painter.setPen(poly_pen)
  1634. painter.drawPolygon(polygon)
  1635. painter.setPen(text_pen)
  1636. painter.setFont(self.m_port_font)
  1637. painter.drawText(text_pos, self.m_port_name)
  1638. if self.isSelected() != self.m_last_selected_state:
  1639. for connection in canvas.connection_list:
  1640. if ((connection.group_out_id == self.m_group_id and connection.port_out_id == self.m_port_id) or
  1641. (connection.group_in_id == self.m_group_id and connection.port_in_id == self.m_port_id)):
  1642. connection.widget.setLineSelected(self.isSelected())
  1643. if canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap:
  1644. painter.setPen(Qt.NoPen)
  1645. painter.setBrush(conn_pen.brush())
  1646. if self.m_port_mode == PORT_MODE_INPUT:
  1647. connRect = QRectF(portRect.topLeft(), QSizeF(2, portRect.height()))
  1648. else:
  1649. connRect = QRectF(QPointF(portRect.right()-2, portRect.top()), QSizeF(2, portRect.height()))
  1650. painter.drawRect(connRect)
  1651. self.m_last_selected_state = self.isSelected()
  1652. painter.restore()
  1653. # ------------------------------------------------------------------------------
  1654. # canvasbox.cpp
  1655. class cb_line_t(object):
  1656. __slots__ = [
  1657. 'line',
  1658. 'connection_id'
  1659. ]
  1660. class CanvasBox(QGraphicsItem):
  1661. def __init__(self, group_id, group_name, icon, parent=None):
  1662. QGraphicsItem.__init__(self, parent)
  1663. # Save Variables, useful for later
  1664. self.m_group_id = group_id
  1665. self.m_group_name = group_name
  1666. # plugin Id, < 0 if invalid
  1667. self.m_plugin_id = -1
  1668. self.m_plugin_ui = False
  1669. # Base Variables
  1670. self.p_width = 50
  1671. self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1
  1672. self.m_last_pos = QPointF()
  1673. self.m_splitted = False
  1674. self.m_splitted_mode = PORT_MODE_NULL
  1675. self.m_cursor_moving = False
  1676. self.m_forced_split = False
  1677. self.m_mouse_down = False
  1678. self.m_port_list_ids = []
  1679. self.m_connection_lines = []
  1680. # Set Font
  1681. self.m_font_name = QFont()
  1682. self.m_font_name.setFamily(canvas.theme.box_font_name)
  1683. self.m_font_name.setPixelSize(canvas.theme.box_font_size)
  1684. self.m_font_name.setWeight(canvas.theme.box_font_state)
  1685. self.m_font_port = QFont()
  1686. self.m_font_port.setFamily(canvas.theme.port_font_name)
  1687. self.m_font_port.setPixelSize(canvas.theme.port_font_size)
  1688. self.m_font_port.setWeight(canvas.theme.port_font_state)
  1689. # Icon
  1690. if canvas.theme.box_use_icon:
  1691. self.icon_svg = CanvasIcon(icon, self.m_group_name, self)
  1692. else:
  1693. self.icon_svg = None
  1694. # Shadow
  1695. if options.eyecandy:
  1696. self.shadow = CanvasBoxShadow(self.toGraphicsObject())
  1697. self.shadow.setFakeParent(self)
  1698. self.setGraphicsEffect(self.shadow)
  1699. else:
  1700. self.shadow = None
  1701. # Final touches
  1702. self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
  1703. # Wait for at least 1 port
  1704. if options.auto_hide_groups:
  1705. self.setVisible(False)
  1706. self.setFlag(QGraphicsItem.ItemIsFocusable, True)
  1707. if options.auto_select_items:
  1708. self.setAcceptHoverEvents(True)
  1709. self.updatePositions()
  1710. canvas.scene.addItem(self)
  1711. def getGroupId(self):
  1712. return self.m_group_id
  1713. def getGroupName(self):
  1714. return self.m_group_name
  1715. def isSplitted(self):
  1716. return self.m_splitted
  1717. def getSplittedMode(self):
  1718. return self.m_splitted_mode
  1719. def getPortCount(self):
  1720. return len(self.m_port_list_ids)
  1721. def getPortList(self):
  1722. return self.m_port_list_ids
  1723. def setAsPlugin(self, plugin_id, hasUi):
  1724. self.m_plugin_id = plugin_id
  1725. self.m_plugin_ui = hasUi
  1726. def setIcon(self, icon):
  1727. if self.icon_svg:
  1728. self.icon_svg.setIcon(icon, self.m_group_name)
  1729. def setSplit(self, split, mode=PORT_MODE_NULL):
  1730. self.m_splitted = split
  1731. self.m_splitted_mode = mode
  1732. def setGroupName(self, group_name):
  1733. self.m_group_name = group_name
  1734. self.updatePositions()
  1735. def setShadowOpacity(self, opacity):
  1736. if self.shadow:
  1737. self.shadow.setOpacity(opacity)
  1738. def addPortFromGroup(self, port_id, port_mode, port_type, port_name, is_alternate):
  1739. if len(self.m_port_list_ids) == 0:
  1740. if options.auto_hide_groups:
  1741. if options.eyecandy == EYECANDY_FULL:
  1742. CanvasItemFX(self, True)
  1743. self.setVisible(True)
  1744. new_widget = CanvasPort(self.m_group_id, port_id, port_name, port_mode, port_type, is_alternate, self)
  1745. port_dict = port_dict_t()
  1746. port_dict.group_id = self.m_group_id
  1747. port_dict.port_id = port_id
  1748. port_dict.port_name = port_name
  1749. port_dict.port_mode = port_mode
  1750. port_dict.port_type = port_type
  1751. port_dict.is_alternate = is_alternate
  1752. port_dict.widget = new_widget
  1753. self.m_port_list_ids.append(port_id)
  1754. return new_widget
  1755. def removePortFromGroup(self, port_id):
  1756. if port_id in self.m_port_list_ids:
  1757. self.m_port_list_ids.remove(port_id)
  1758. else:
  1759. qCritical("PatchCanvas::CanvasBox.removePort(%i) - unable to find port to remove" % port_id)
  1760. return
  1761. if len(self.m_port_list_ids) > 0:
  1762. self.updatePositions()
  1763. elif self.isVisible():
  1764. if options.auto_hide_groups:
  1765. if options.eyecandy == EYECANDY_FULL:
  1766. CanvasItemFX(self, False)
  1767. else:
  1768. self.setVisible(False)
  1769. def addLineFromGroup(self, line, connection_id):
  1770. new_cbline = cb_line_t()
  1771. new_cbline.line = line
  1772. new_cbline.connection_id = connection_id
  1773. self.m_connection_lines.append(new_cbline)
  1774. def removeLineFromGroup(self, connection_id):
  1775. for connection in self.m_connection_lines:
  1776. if connection.connection_id == connection_id:
  1777. self.m_connection_lines.remove(connection)
  1778. return
  1779. qCritical("PatchCanvas::CanvasBox.removeLineFromGroup(%i) - unable to find line to remove" % connection_id)
  1780. def checkItemPos(self):
  1781. if not canvas.size_rect.isNull():
  1782. pos = self.scenePos()
  1783. if not (canvas.size_rect.contains(pos) and canvas.size_rect.contains(pos + QPointF(self.p_width, self.p_height))):
  1784. if pos.x() < canvas.size_rect.x():
  1785. self.setPos(canvas.size_rect.x(), pos.y())
  1786. elif pos.x() + self.p_width > canvas.size_rect.width():
  1787. self.setPos(canvas.size_rect.width() - self.p_width, pos.y())
  1788. pos = self.scenePos()
  1789. if pos.y() < canvas.size_rect.y():
  1790. self.setPos(pos.x(), canvas.size_rect.y())
  1791. elif pos.y() + self.p_height > canvas.size_rect.height():
  1792. self.setPos(pos.x(), canvas.size_rect.height() - self.p_height)
  1793. def removeIconFromScene(self):
  1794. if self.icon_svg:
  1795. canvas.scene.removeItem(self.icon_svg)
  1796. def updatePositions(self):
  1797. self.prepareGeometryChange()
  1798. max_in_width = 0
  1799. max_in_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing
  1800. max_out_width = 0
  1801. max_out_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing
  1802. port_spacing = canvas.theme.port_height + canvas.theme.port_spacing
  1803. have_audio_jack_in = have_midi_jack_in = have_midi_alsa_in = have_parameter_in = False
  1804. have_audio_jack_out = have_midi_jack_out = have_midi_alsa_out = have_parameter_out = False
  1805. # reset box size
  1806. self.p_width = 50
  1807. self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1
  1808. # Check Text Name size
  1809. app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30
  1810. if app_name_size > self.p_width:
  1811. self.p_width = app_name_size
  1812. # Get Port List
  1813. port_list = []
  1814. for port in canvas.port_list:
  1815. if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids:
  1816. port_list.append(port)
  1817. # Get Max Box Width/Height
  1818. for port in port_list:
  1819. if port.port_mode == PORT_MODE_INPUT:
  1820. max_in_height += port_spacing
  1821. size = QFontMetrics(self.m_font_port).width(port.port_name)
  1822. if size > max_in_width:
  1823. max_in_width = size
  1824. if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_in:
  1825. have_audio_jack_in = True
  1826. max_in_height += canvas.theme.port_spacingT
  1827. elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_in:
  1828. have_midi_jack_in = True
  1829. max_in_height += canvas.theme.port_spacingT
  1830. elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_in:
  1831. have_midi_alsa_in = True
  1832. max_in_height += canvas.theme.port_spacingT
  1833. elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_in:
  1834. have_parameter_in = True
  1835. max_in_height += canvas.theme.port_spacingT
  1836. elif port.port_mode == PORT_MODE_OUTPUT:
  1837. max_out_height += port_spacing
  1838. size = QFontMetrics(self.m_font_port).width(port.port_name)
  1839. if size > max_out_width:
  1840. max_out_width = size
  1841. if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_out:
  1842. have_audio_jack_out = True
  1843. max_out_height += canvas.theme.port_spacingT
  1844. elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_out:
  1845. have_midi_jack_out = True
  1846. max_out_height += canvas.theme.port_spacingT
  1847. elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_out:
  1848. have_midi_alsa_out = True
  1849. max_out_height += canvas.theme.port_spacingT
  1850. elif port.port_type == PORT_TYPE_PARAMETER and not have_parameter_out:
  1851. have_parameter_out = True
  1852. max_out_height += canvas.theme.port_spacingT
  1853. if canvas.theme.port_spacingT == 0:
  1854. max_in_height += 2
  1855. max_out_height += 2
  1856. final_width = 30 + max_in_width + max_out_width
  1857. if final_width > self.p_width:
  1858. self.p_width = final_width
  1859. if max_in_height > self.p_height:
  1860. self.p_height = max_in_height
  1861. if max_out_height > self.p_height:
  1862. self.p_height = max_out_height
  1863. # Remove bottom space
  1864. self.p_height -= canvas.theme.port_spacingT
  1865. if canvas.theme.box_header_spacing > 0:
  1866. if len(port_list) == 0:
  1867. self.p_height -= canvas.theme.box_header_spacing
  1868. else:
  1869. self.p_height -= canvas.theme.box_header_spacing/2
  1870. last_in_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
  1871. last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
  1872. last_in_type = PORT_TYPE_NULL
  1873. last_out_type = PORT_TYPE_NULL
  1874. # Re-position ports, AUDIO_JACK
  1875. for port in port_list:
  1876. if port.port_type != PORT_TYPE_AUDIO_JACK:
  1877. continue
  1878. if port.port_mode == PORT_MODE_INPUT:
  1879. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1880. last_in_pos += canvas.theme.port_spacingT
  1881. port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
  1882. port.widget.setPortWidth(max_in_width)
  1883. last_in_pos += port_spacing
  1884. last_in_type = port.port_type
  1885. elif port.port_mode == PORT_MODE_OUTPUT:
  1886. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1887. last_out_pos += canvas.theme.port_spacingT
  1888. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
  1889. port.widget.setPortWidth(max_out_width)
  1890. last_out_pos += port_spacing
  1891. last_out_type = port.port_type
  1892. # Re-position ports, MIDI_JACK
  1893. for port in port_list:
  1894. if port.port_type != PORT_TYPE_MIDI_JACK:
  1895. continue
  1896. if port.port_mode == PORT_MODE_INPUT:
  1897. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1898. last_in_pos += canvas.theme.port_spacingT
  1899. port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
  1900. port.widget.setPortWidth(max_in_width)
  1901. last_in_pos += port_spacing
  1902. last_in_type = port.port_type
  1903. elif port.port_mode == PORT_MODE_OUTPUT:
  1904. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1905. last_out_pos += canvas.theme.port_spacingT
  1906. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
  1907. port.widget.setPortWidth(max_out_width)
  1908. last_out_pos += port_spacing
  1909. last_out_type = port.port_type
  1910. # Re-position ports, MIDI_ALSA
  1911. for port in port_list:
  1912. if port.port_type != PORT_TYPE_MIDI_ALSA:
  1913. continue
  1914. if port.port_mode == PORT_MODE_INPUT:
  1915. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1916. last_in_pos += canvas.theme.port_spacingT
  1917. port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
  1918. port.widget.setPortWidth(max_in_width)
  1919. last_in_pos += port_spacing
  1920. last_in_type = port.port_type
  1921. elif port.port_mode == PORT_MODE_OUTPUT:
  1922. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1923. last_out_pos += canvas.theme.port_spacingT
  1924. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
  1925. port.widget.setPortWidth(max_out_width)
  1926. last_out_pos += port_spacing
  1927. last_out_type = port.port_type
  1928. # Re-position ports, PARAMETER
  1929. for port in port_list:
  1930. if port.port_type != PORT_TYPE_PARAMETER:
  1931. continue
  1932. if port.port_mode == PORT_MODE_INPUT:
  1933. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1934. last_in_pos += canvas.theme.port_spacingT
  1935. port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos))
  1936. port.widget.setPortWidth(max_in_width)
  1937. last_in_pos += port_spacing
  1938. last_in_type = port.port_type
  1939. elif port.port_mode == PORT_MODE_OUTPUT:
  1940. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1941. last_out_pos += canvas.theme.port_spacingT
  1942. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos))
  1943. port.widget.setPortWidth(max_out_width)
  1944. last_out_pos += port_spacing
  1945. last_out_type = port.port_type
  1946. self.repaintLines(True)
  1947. self.update()
  1948. def repaintLines(self, forced=False):
  1949. if self.pos() != self.m_last_pos or forced:
  1950. for connection in self.m_connection_lines:
  1951. connection.line.updateLinePos()
  1952. self.m_last_pos = self.pos()
  1953. def resetLinesZValue(self):
  1954. for connection in canvas.connection_list:
  1955. if connection.port_out_id in self.m_port_list_ids and connection.port_in_id in self.m_port_list_ids:
  1956. z_value = canvas.last_z_value
  1957. else:
  1958. z_value = canvas.last_z_value - 1
  1959. connection.widget.setZValue(z_value)
  1960. def type(self):
  1961. return CanvasBoxType
  1962. def contextMenuEvent(self, event):
  1963. menu = QMenu()
  1964. discMenu = QMenu("Disconnect", menu)
  1965. conn_list = []
  1966. conn_list_ids = []
  1967. for port_id in self.m_port_list_ids:
  1968. tmp_conn_list = CanvasGetPortConnectionList(self.m_group_id, port_id)
  1969. for conn_id, group_id, port_id in tmp_conn_list:
  1970. if conn_id not in conn_list_ids:
  1971. conn_list.append((conn_id, group_id, port_id))
  1972. conn_list_ids.append(conn_id)
  1973. if len(conn_list) > 0:
  1974. for conn_id, group_id, port_id in conn_list:
  1975. act_x_disc = discMenu.addAction(CanvasGetFullPortName(group_id, port_id))
  1976. act_x_disc.setData(conn_id)
  1977. act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect)
  1978. else:
  1979. act_x_disc = discMenu.addAction("No connections")
  1980. act_x_disc.setEnabled(False)
  1981. menu.addMenu(discMenu)
  1982. act_x_disc_all = menu.addAction("Disconnect &All")
  1983. act_x_sep1 = menu.addSeparator()
  1984. act_x_info = menu.addAction("&Info")
  1985. act_x_rename = menu.addAction("&Rename")
  1986. act_x_sep2 = menu.addSeparator()
  1987. act_x_split_join = menu.addAction("Join" if self.m_splitted else "Split")
  1988. if not features.group_info:
  1989. act_x_info.setVisible(False)
  1990. if not features.group_rename:
  1991. act_x_rename.setVisible(False)
  1992. if not (features.group_info and features.group_rename):
  1993. act_x_sep1.setVisible(False)
  1994. if self.m_plugin_id >= 0:
  1995. menu.addSeparator()
  1996. act_p_edit = menu.addAction("&Edit")
  1997. act_p_ui = menu.addAction("&Show Custom UI")
  1998. menu.addSeparator()
  1999. act_p_clone = menu.addAction("&Clone")
  2000. act_p_rename = menu.addAction("&Rename...")
  2001. act_p_remove = menu.addAction("Re&move")
  2002. if not self.m_plugin_ui:
  2003. act_p_ui.setVisible(False)
  2004. else:
  2005. act_p_edit = act_p_ui = None
  2006. act_p_clone = act_p_rename = act_p_remove = None
  2007. haveIns = haveOuts = False
  2008. for port in canvas.port_list:
  2009. if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids:
  2010. if port.port_mode == PORT_MODE_INPUT:
  2011. haveIns = True
  2012. elif port.port_mode == PORT_MODE_OUTPUT:
  2013. haveOuts = True
  2014. if not (self.m_splitted or bool(haveIns and haveOuts)):
  2015. act_x_sep2.setVisible(False)
  2016. act_x_split_join.setVisible(False)
  2017. act_selected = menu.exec_(event.screenPos())
  2018. if act_selected is None:
  2019. pass
  2020. elif act_selected == act_x_disc_all:
  2021. for conn_id in conn_list_ids:
  2022. canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "")
  2023. elif act_selected == act_x_info:
  2024. canvas.callback(ACTION_GROUP_INFO, self.m_group_id, 0, "")
  2025. elif act_selected == act_x_rename:
  2026. new_name_try = QInputDialog.getText(None, "Rename Group", "New name:", QLineEdit.Normal, self.m_group_name)
  2027. if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
  2028. canvas.callback(ACTION_GROUP_RENAME, self.m_group_id, 0, new_name_try[0])
  2029. elif act_selected == act_x_split_join:
  2030. if self.m_splitted:
  2031. canvas.callback(ACTION_GROUP_JOIN, self.m_group_id, 0, "")
  2032. else:
  2033. canvas.callback(ACTION_GROUP_SPLIT, self.m_group_id, 0, "")
  2034. elif act_selected == act_p_edit:
  2035. canvas.callback(ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "")
  2036. elif act_selected == act_p_ui:
  2037. canvas.callback(ACTION_PLUGIN_SHOW_UI, self.m_plugin_id, 0, "")
  2038. elif act_selected == act_p_clone:
  2039. canvas.callback(ACTION_PLUGIN_CLONE, self.m_plugin_id, 0, "")
  2040. elif act_selected == act_p_rename:
  2041. new_name_try = QInputDialog.getText(None, "Rename Plugin", "New name:", QLineEdit.Normal, self.m_group_name)
  2042. if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
  2043. canvas.callback(ACTION_PLUGIN_RENAME, self.m_plugin_id, 0, new_name_try[0])
  2044. elif act_selected == act_p_remove:
  2045. canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "")
  2046. event.accept()
  2047. def keyPressEvent(self, event):
  2048. if self.m_plugin_id >= 0 and event.key() == Qt.Key_Delete:
  2049. canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "")
  2050. return event.accept()
  2051. QGraphicsItem.keyPressEvent(self, event)
  2052. def hoverEnterEvent(self, event):
  2053. if options.auto_select_items:
  2054. if len(canvas.scene.selectedItems()) > 0:
  2055. canvas.scene.clearSelection()
  2056. self.setSelected(True)
  2057. QGraphicsItem.hoverEnterEvent(self, event)
  2058. def mouseDoubleClickEvent(self, event):
  2059. if self.m_plugin_id >= 0:
  2060. canvas.callback(ACTION_PLUGIN_SHOW_UI if self.m_plugin_ui else ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "")
  2061. return event.accept()
  2062. QGraphicsItem.mouseDoubleClickEvent(self, event)
  2063. def mousePressEvent(self, event):
  2064. canvas.last_z_value += 1
  2065. self.setZValue(canvas.last_z_value)
  2066. self.resetLinesZValue()
  2067. self.m_cursor_moving = False
  2068. if event.button() == Qt.RightButton:
  2069. canvas.scene.clearSelection()
  2070. self.setSelected(True)
  2071. self.m_mouse_down = False
  2072. return event.accept()
  2073. elif event.button() == Qt.LeftButton:
  2074. if self.sceneBoundingRect().contains(event.scenePos()):
  2075. self.m_mouse_down = True
  2076. else:
  2077. # Fix a weird Qt behaviour with right-click mouseMove
  2078. self.m_mouse_down = False
  2079. return event.ignore()
  2080. else:
  2081. self.m_mouse_down = False
  2082. QGraphicsItem.mousePressEvent(self, event)
  2083. def mouseMoveEvent(self, event):
  2084. if self.m_mouse_down:
  2085. if not self.m_cursor_moving:
  2086. self.setCursor(QCursor(Qt.SizeAllCursor))
  2087. self.m_cursor_moving = True
  2088. self.repaintLines()
  2089. QGraphicsItem.mouseMoveEvent(self, event)
  2090. def mouseReleaseEvent(self, event):
  2091. if self.m_cursor_moving:
  2092. self.setCursor(QCursor(Qt.ArrowCursor))
  2093. self.m_mouse_down = False
  2094. self.m_cursor_moving = False
  2095. QGraphicsItem.mouseReleaseEvent(self, event)
  2096. def boundingRect(self):
  2097. return QRectF(0, 0, self.p_width, self.p_height)
  2098. def paint(self, painter, option, widget):
  2099. painter.save()
  2100. painter.setRenderHint(QPainter.Antialiasing, False)
  2101. # Draw rectangle
  2102. if self.isSelected():
  2103. painter.setPen(canvas.theme.box_pen_sel)
  2104. else:
  2105. painter.setPen(canvas.theme.box_pen)
  2106. if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT:
  2107. box_gradient = QLinearGradient(0, 0, 0, self.p_height)
  2108. box_gradient.setColorAt(0, canvas.theme.box_bg_1)
  2109. box_gradient.setColorAt(1, canvas.theme.box_bg_2)
  2110. painter.setBrush(box_gradient)
  2111. else:
  2112. painter.setBrush(canvas.theme.box_bg_1)
  2113. painter.drawRect(0, 0, self.p_width, self.p_height)
  2114. # Draw pixmap header
  2115. if canvas.theme.box_header_pixmap:
  2116. painter.setPen(Qt.NoPen)
  2117. painter.setBrush(canvas.theme.box_bg_2)
  2118. painter.drawRect(1, 1, self.p_width-2, canvas.theme.box_header_height)
  2119. headerPos = QPointF(1, 1)
  2120. headerRect = QRectF(2, 2, self.p_width-4, canvas.theme.box_header_height-3)
  2121. painter.drawTiledPixmap(headerRect, canvas.theme.box_header_pixmap, headerPos)
  2122. # Draw text
  2123. painter.setFont(self.m_font_name)
  2124. if self.isSelected():
  2125. painter.setPen(canvas.theme.box_text_sel)
  2126. else:
  2127. painter.setPen(canvas.theme.box_text)
  2128. if canvas.theme.box_use_icon:
  2129. textPos = QPointF(25, canvas.theme.box_text_ypos)
  2130. else:
  2131. appNameSize = QFontMetrics(self.m_font_name).width(self.m_group_name)
  2132. rem = self.p_width - appNameSize
  2133. textPos = QPointF(rem/2, canvas.theme.box_text_ypos)
  2134. painter.drawText(textPos, self.m_group_name)
  2135. self.repaintLines()
  2136. painter.restore()
  2137. # ------------------------------------------------------------------------------
  2138. # canvasicon.cpp
  2139. class CanvasIcon(QGraphicsSvgItem):
  2140. def __init__(self, icon, name, parent):
  2141. QGraphicsSvgItem.__init__(self, parent)
  2142. self.m_renderer = None
  2143. self.p_size = QRectF(0, 0, 0, 0)
  2144. self.m_colorFX = QGraphicsColorizeEffect(self)
  2145. self.m_colorFX.setColor(canvas.theme.box_text.color())
  2146. self.setGraphicsEffect(self.m_colorFX)
  2147. self.setIcon(icon, name)
  2148. def setIcon(self, icon, name):
  2149. name = name.lower()
  2150. icon_path = ""
  2151. if icon == ICON_APPLICATION:
  2152. self.p_size = QRectF(3, 2, 19, 18)
  2153. if "audacious" in name:
  2154. icon_path = ":/scalable/pb_audacious.svg"
  2155. self.p_size = QRectF(5, 4, 16, 16)
  2156. elif "clementine" in name:
  2157. icon_path = ":/scalable/pb_clementine.svg"
  2158. self.p_size = QRectF(5, 4, 16, 16)
  2159. elif "distrho" in name:
  2160. icon_path = ":/scalable/pb_distrho.svg"
  2161. self.p_size = QRectF(5, 4, 14, 14)
  2162. elif "jamin" in name:
  2163. icon_path = ":/scalable/pb_jamin.svg"
  2164. self.p_size = QRectF(5, 3, 16, 16)
  2165. elif "mplayer" in name:
  2166. icon_path = ":/scalable/pb_mplayer.svg"
  2167. self.p_size = QRectF(5, 4, 16, 16)
  2168. elif "vlc" in name:
  2169. icon_path = ":/scalable/pb_vlc.svg"
  2170. self.p_size = QRectF(5, 3, 16, 16)
  2171. else:
  2172. icon_path = ":/scalable/pb_generic.svg"
  2173. self.p_size = QRectF(4, 3, 16, 16)
  2174. elif icon == ICON_HARDWARE:
  2175. icon_path = ":/scalable/pb_hardware.svg"
  2176. self.p_size = QRectF(5, 2, 16, 16)
  2177. elif icon == ICON_DISTRHO:
  2178. icon_path = ":/scalable/pb_distrho.svg"
  2179. self.p_size = QRectF(5, 4, 14, 14)
  2180. elif icon == ICON_FILE:
  2181. icon_path = ":/scalable/pb_file.svg"
  2182. self.p_size = QRectF(5, 4, 12, 14)
  2183. elif icon == ICON_PLUGIN:
  2184. icon_path = ":/scalable/pb_plugin.svg"
  2185. self.p_size = QRectF(5, 4, 14, 14)
  2186. elif icon == ICON_LADISH_ROOM:
  2187. # TODO - make a unique ladish-room icon
  2188. icon_path = ":/scalable/pb_hardware.svg"
  2189. self.p_size = QRectF(5, 2, 16, 16)
  2190. else:
  2191. self.p_size = QRectF(0, 0, 0, 0)
  2192. qCritical("PatchCanvas::CanvasIcon.setIcon(%s, %s) - unsupported icon requested" % (icon2str(icon), name.encode()))
  2193. return
  2194. self.m_renderer = QSvgRenderer(icon_path, canvas.scene)
  2195. self.setSharedRenderer(self.m_renderer)
  2196. self.update()
  2197. def type(self):
  2198. return CanvasIconType
  2199. def boundingRect(self):
  2200. return self.p_size
  2201. def paint(self, painter, option, widget):
  2202. if self.m_renderer:
  2203. painter.save()
  2204. painter.setRenderHint(QPainter.Antialiasing, False)
  2205. painter.setRenderHint(QPainter.TextAntialiasing, False)
  2206. self.m_renderer.render(painter, self.p_size)
  2207. painter.restore()
  2208. else:
  2209. QGraphicsSvgItem.paint(self, painter, option, widget)
  2210. # ------------------------------------------------------------------------------
  2211. # canvasportglow.cpp
  2212. class CanvasPortGlow(QGraphicsDropShadowEffect):
  2213. def __init__(self, port_type, parent):
  2214. QGraphicsDropShadowEffect.__init__(self, parent)
  2215. self.setBlurRadius(12)
  2216. self.setOffset(0, 0)
  2217. if port_type == PORT_TYPE_AUDIO_JACK:
  2218. self.setColor(canvas.theme.line_audio_jack_glow)
  2219. elif port_type == PORT_TYPE_MIDI_JACK:
  2220. self.setColor(canvas.theme.line_midi_jack_glow)
  2221. elif port_type == PORT_TYPE_MIDI_ALSA:
  2222. self.setColor(canvas.theme.line_midi_alsa_glow)
  2223. elif port_type == PORT_TYPE_PARAMETER:
  2224. self.setColor(canvas.theme.line_parameter_glow)
  2225. # ------------------------------------------------------------------------------
  2226. # canvasboxshadow.cpp
  2227. class CanvasBoxShadow(QGraphicsDropShadowEffect):
  2228. def __init__(self, parent):
  2229. QGraphicsDropShadowEffect.__init__(self, parent)
  2230. self.m_fakeParent = None
  2231. self.setBlurRadius(20)
  2232. self.setColor(canvas.theme.box_shadow)
  2233. self.setOffset(0, 0)
  2234. def setFakeParent(self, fakeParent):
  2235. self.m_fakeParent = fakeParent
  2236. def setOpacity(self, opacity):
  2237. color = QColor(canvas.theme.box_shadow)
  2238. color.setAlphaF(opacity)
  2239. self.setColor(color)
  2240. def draw(self, painter):
  2241. if self.m_fakeParent:
  2242. self.m_fakeParent.repaintLines()
  2243. QGraphicsDropShadowEffect.draw(self, painter)