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.

1118 lines
38KB

  1. #!/usr/bin/env python3
  2. # SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
  3. # SPDX-License-Identifier: GPL-2.0-or-later
  4. # ------------------------------------------------------------------------------------------------------------
  5. # Imports (Global)
  6. from qt_compat import qt_config
  7. if qt_config == 5:
  8. from PyQt5.QtCore import pyqtSlot, qCritical, qFatal, qWarning, QObject
  9. from PyQt5.QtCore import QPointF, QRectF, QTimer
  10. from PyQt5.QtWidgets import QGraphicsObject
  11. elif qt_config == 6:
  12. from PyQt6.QtCore import pyqtSlot, qCritical, qFatal, qWarning, QObject
  13. from PyQt6.QtCore import QPointF, QRectF, QTimer
  14. from PyQt6.QtWidgets import QGraphicsObject
  15. # ------------------------------------------------------------------------------------------------------------
  16. # Imports (Custom)
  17. from . import (
  18. canvas,
  19. features,
  20. options,
  21. group_dict_t,
  22. port_dict_t,
  23. connection_dict_t,
  24. bool2str,
  25. icon2str,
  26. split2str,
  27. port_mode2str,
  28. port_type2str,
  29. CanvasIconType,
  30. CanvasRubberbandType,
  31. ACTION_PORTS_DISCONNECT,
  32. EYECANDY_FULL,
  33. ICON_APPLICATION,
  34. ICON_HARDWARE,
  35. ICON_LADISH_ROOM,
  36. PORT_MODE_INPUT,
  37. PORT_MODE_OUTPUT,
  38. SPLIT_YES,
  39. SPLIT_NO,
  40. SPLIT_UNDEF,
  41. MAX_PLUGIN_ID_ALLOWED,
  42. )
  43. from .canvasbox import CanvasBox
  44. from .canvasbezierline import CanvasBezierLine
  45. from .canvasline import CanvasLine
  46. from .theme import Theme, getDefaultTheme, getThemeName
  47. from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRemoveItemFX
  48. # FIXME
  49. from . import *
  50. from .scene import PatchScene
  51. from utils import QSafeSettings
  52. # ------------------------------------------------------------------------------------------------------------
  53. class CanvasObject(QObject):
  54. def __init__(self, parent=None):
  55. QObject.__init__(self, parent)
  56. @pyqtSlot()
  57. def AnimationFinishedShow(self):
  58. animation = self.sender()
  59. if animation:
  60. animation.forceStop()
  61. canvas.animation_list.remove(animation)
  62. @pyqtSlot()
  63. def AnimationFinishedHide(self):
  64. animation = self.sender()
  65. if animation:
  66. animation.forceStop()
  67. canvas.animation_list.remove(animation)
  68. item = animation.item()
  69. if item:
  70. if isinstance(item, QGraphicsObject):
  71. item.blockSignals(True)
  72. item.hide()
  73. item.blockSignals(False)
  74. else:
  75. item.hide()
  76. @pyqtSlot()
  77. def AnimationFinishedDestroy(self):
  78. animation = self.sender()
  79. if animation:
  80. animation.forceStop()
  81. canvas.animation_list.remove(animation)
  82. item = animation.item()
  83. if item:
  84. CanvasRemoveItemFX(item)
  85. @pyqtSlot()
  86. def PortContextMenuConnect(self):
  87. try:
  88. sources, targets = self.sender().data()
  89. except:
  90. return
  91. for port_type in (PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER):
  92. source_ports = sources[port_type]
  93. target_ports = targets[port_type]
  94. source_ports_len = len(source_ports)
  95. target_ports_len = len(target_ports)
  96. if source_ports_len == 0 or target_ports_len == 0:
  97. continue
  98. for i in range(min(source_ports_len, target_ports_len)):
  99. data = "%i:%i:%i:%i" % (source_ports[i][0],
  100. source_ports[i][1],
  101. target_ports[i][0],
  102. target_ports[i][1])
  103. CanvasCallback(ACTION_PORTS_CONNECT, 0, 0, data)
  104. if source_ports_len == 1 and target_ports_len > 1:
  105. for i in range(1, target_ports_len):
  106. data = "%i:%i:%i:%i" % (source_ports[0][0],
  107. source_ports[0][1],
  108. target_ports[i][0],
  109. target_ports[i][1])
  110. CanvasCallback(ACTION_PORTS_CONNECT, 0, 0, data)
  111. @pyqtSlot()
  112. def PortContextMenuDisconnect(self):
  113. try:
  114. connection_ids = self.sender().data()
  115. if isinstance(connection_ids, (list, tuple)):
  116. connection_ids = [int(conn_id) for conn_id in connection_ids]
  117. else:
  118. connection_ids = [int(connection_ids)]
  119. except:
  120. return
  121. for connection_id in connection_ids:
  122. CanvasCallback(ACTION_PORTS_DISCONNECT, connection_id, 0, "")
  123. @pyqtSlot(int, bool, int, int)
  124. def boxPositionChanged(self, groupId, split, x, y):
  125. x2 = y2 = 0
  126. if split:
  127. for group in canvas.group_list:
  128. if group.group_id == groupId:
  129. if group.split:
  130. pos = group.widgets[1].pos()
  131. x2 = pos.x()
  132. y2 = pos.y()
  133. break
  134. valueStr = "%i:%i:%i:%i" % (x, y, x2, y2)
  135. CanvasCallback(ACTION_GROUP_POSITION, groupId, 0, valueStr)
  136. @pyqtSlot(int, bool, int, int)
  137. def sboxPositionChanged(self, groupId, split, x2, y2):
  138. x = y = 0
  139. for group in canvas.group_list:
  140. if group.group_id == groupId:
  141. pos = group.widgets[0].pos()
  142. x = pos.x()
  143. y = pos.y()
  144. break
  145. valueStr = "%i:%i:%i:%i" % (x, y, x2, y2)
  146. CanvasCallback(ACTION_GROUP_POSITION, groupId, 0, valueStr)
  147. # ------------------------------------------------------------------------------------------------------------
  148. def getStoredCanvasPosition(key, fallback_pos):
  149. try:
  150. return canvas.settings.value("CanvasPositions/" + key, fallback_pos, type=QPointF)
  151. except:
  152. return fallback_pos
  153. def getStoredCanvasSplit(group_name, fallback_split_mode):
  154. try:
  155. return canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, fallback_split_mode, type=int)
  156. except:
  157. return fallback_split_mode
  158. # ------------------------------------------------------------------------------------------------------------
  159. def init(appName, scene, callback, debug=False):
  160. if debug:
  161. print("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug)))
  162. if canvas.initiated:
  163. qCritical("PatchCanvas::init() - already initiated")
  164. return
  165. if not callback:
  166. qFatal("PatchCanvas::init() - fatal error: callback not set")
  167. return
  168. canvas.callback = callback
  169. canvas.debug = debug
  170. canvas.scene = scene
  171. canvas.last_z_value = 0
  172. canvas.last_connection_id = 0
  173. canvas.initial_pos = QPointF(0, 0)
  174. canvas.size_rect = QRectF()
  175. if not canvas.qobject:
  176. canvas.qobject = CanvasObject()
  177. if not canvas.settings:
  178. canvas.settings = QSafeSettings("falkTX", appName)
  179. if canvas.theme:
  180. del canvas.theme
  181. canvas.theme = None
  182. for i in range(Theme.THEME_MAX):
  183. this_theme_name = getThemeName(i)
  184. if this_theme_name == options.theme_name:
  185. canvas.theme = Theme(i)
  186. break
  187. if not canvas.theme:
  188. canvas.theme = Theme(getDefaultTheme())
  189. canvas.scene.updateTheme()
  190. canvas.initiated = True
  191. def clear():
  192. if canvas.debug:
  193. print("PatchCanvas::clear()")
  194. group_pos = {}
  195. group_list_ids = []
  196. port_list_ids = []
  197. connection_list_ids = []
  198. for group in canvas.group_list:
  199. group_pos[group.group_name] = (
  200. group.split,
  201. group.widgets[0].pos(),
  202. group.widgets[1].pos() if group.split else None,
  203. )
  204. group_list_ids.append(group.group_id)
  205. for port in canvas.port_list:
  206. port_list_ids.append((port.group_id, port.port_id))
  207. for connection in canvas.connection_list:
  208. connection_list_ids.append(connection.connection_id)
  209. for idx in connection_list_ids:
  210. disconnectPorts(idx)
  211. for group_id, port_id in port_list_ids:
  212. removePort(group_id, port_id)
  213. for idx in group_list_ids:
  214. removeGroup(idx)
  215. canvas.last_z_value = 0
  216. canvas.last_connection_id = 0
  217. canvas.group_list = []
  218. canvas.port_list = []
  219. canvas.connection_list = []
  220. canvas.group_plugin_map = {}
  221. canvas.old_group_pos = group_pos
  222. canvas.scene.clearSelection()
  223. animatedItems = []
  224. for animation in canvas.animation_list:
  225. animatedItems.append(animation.item())
  226. for item in canvas.scene.items():
  227. if item.type() in (CanvasIconType, CanvasRubberbandType) or item in animatedItems:
  228. continue
  229. canvas.scene.removeItem(item)
  230. del item
  231. canvas.initiated = False
  232. QTimer.singleShot(0, canvas.scene.update)
  233. # ------------------------------------------------------------------------------------------------------------
  234. def setInitialPos(x, y):
  235. if canvas.debug:
  236. print("PatchCanvas::setInitialPos(%i, %i)" % (x, y))
  237. canvas.initial_pos.setX(x)
  238. canvas.initial_pos.setY(y)
  239. def setCanvasSize(x, y, width, height):
  240. if canvas.debug:
  241. print("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height))
  242. canvas.size_rect.setX(x)
  243. canvas.size_rect.setY(y)
  244. canvas.size_rect.setWidth(width)
  245. canvas.size_rect.setHeight(height)
  246. canvas.scene.updateLimits()
  247. canvas.scene.fixScaleFactor()
  248. def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION):
  249. if canvas.debug:
  250. print("PatchCanvas::addGroup(%i, %s, %s, %s)" % (
  251. group_id, group_name.encode(), split2str(split), icon2str(icon)))
  252. for group in canvas.group_list:
  253. if group.group_id == group_id:
  254. qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % (
  255. group_id, group_name.encode(), split2str(split), icon2str(icon)))
  256. return None
  257. old_matching_group = canvas.old_group_pos.pop(group_name, None)
  258. if split == SPLIT_UNDEF:
  259. isHardware = bool(icon == ICON_HARDWARE)
  260. if features.handle_group_pos:
  261. split = getStoredCanvasSplit(group_name, SPLIT_YES if isHardware else split)
  262. elif isHardware:
  263. split = SPLIT_YES
  264. elif old_matching_group is not None and old_matching_group[0]:
  265. split = SPLIT_YES
  266. group_box = CanvasBox(group_id, group_name, icon)
  267. group_box.positionChanged.connect(canvas.qobject.boxPositionChanged)
  268. group_box.blockSignals(True)
  269. group_dict = group_dict_t()
  270. group_dict.group_id = group_id
  271. group_dict.group_name = group_name
  272. group_dict.split = bool(split == SPLIT_YES)
  273. group_dict.icon = icon
  274. group_dict.plugin_id = -1
  275. group_dict.plugin_ui = False
  276. group_dict.plugin_inline = False
  277. group_dict.widgets = [group_box, None]
  278. if split == SPLIT_YES:
  279. group_box.setSplit(True, PORT_MODE_OUTPUT)
  280. if features.handle_group_pos:
  281. group_box.setPos(getStoredCanvasPosition(group_name + "_OUTPUT", CanvasGetNewGroupPos(False)))
  282. elif old_matching_group is not None:
  283. group_box.setPos(old_matching_group[1])
  284. else:
  285. group_box.setPos(CanvasGetNewGroupPos(False))
  286. group_sbox = CanvasBox(group_id, group_name, icon)
  287. group_sbox.positionChanged.connect(canvas.qobject.sboxPositionChanged)
  288. group_sbox.blockSignals(True)
  289. group_sbox.setSplit(True, PORT_MODE_INPUT)
  290. group_dict.widgets[1] = group_sbox
  291. if features.handle_group_pos:
  292. group_sbox.setPos(getStoredCanvasPosition(group_name + "_INPUT", CanvasGetNewGroupPos(True)))
  293. elif old_matching_group is not None and old_matching_group[0]:
  294. group_sbox.setPos(old_matching_group[2])
  295. else:
  296. group_sbox.setPos(group_box.x() + group_box.boundingRect().width() + 300, group_box.y())
  297. canvas.last_z_value += 1
  298. group_sbox.setZValue(canvas.last_z_value)
  299. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  300. CanvasItemFX(group_sbox, True, False)
  301. group_sbox.checkItemPos()
  302. group_sbox.blockSignals(False)
  303. else:
  304. group_box.setSplit(False)
  305. if features.handle_group_pos:
  306. group_box.setPos(getStoredCanvasPosition(group_name, CanvasGetNewGroupPos(False)))
  307. elif old_matching_group is not None:
  308. group_box.setPos(old_matching_group[1])
  309. else:
  310. # Special ladish fake-split groups
  311. horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM)
  312. group_box.setPos(CanvasGetNewGroupPos(horizontal))
  313. canvas.last_z_value += 1
  314. group_box.setZValue(canvas.last_z_value)
  315. group_box.checkItemPos()
  316. group_box.blockSignals(False)
  317. canvas.group_list.append(group_dict)
  318. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  319. CanvasItemFX(group_box, True, False)
  320. else:
  321. QTimer.singleShot(0, canvas.scene.update)
  322. return group_dict
  323. def removeGroup(group_id):
  324. if canvas.debug:
  325. print("PatchCanvas::removeGroup(%i)" % group_id)
  326. for group in canvas.group_list:
  327. if group.group_id == group_id:
  328. item = group.widgets[0]
  329. group_name = group.group_name
  330. if group.split:
  331. s_item = group.widgets[1]
  332. if features.handle_group_pos:
  333. canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos())
  334. canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos())
  335. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES)
  336. if options.eyecandy == EYECANDY_FULL:
  337. CanvasItemFX(s_item, False, True)
  338. else:
  339. s_item.removeIconFromScene()
  340. canvas.scene.removeItem(s_item)
  341. del s_item
  342. else:
  343. if features.handle_group_pos:
  344. canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos())
  345. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO)
  346. if options.eyecandy == EYECANDY_FULL:
  347. CanvasItemFX(item, False, True)
  348. else:
  349. item.removeIconFromScene()
  350. canvas.scene.removeItem(item)
  351. del item
  352. canvas.group_list.remove(group)
  353. canvas.group_plugin_map.pop(group.plugin_id, None)
  354. QTimer.singleShot(0, canvas.scene.update)
  355. return
  356. qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id)
  357. def renameGroup(group_id, new_group_name):
  358. if canvas.debug:
  359. print("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode()))
  360. for group in canvas.group_list:
  361. if group.group_id == group_id:
  362. group.group_name = new_group_name
  363. group.widgets[0].setGroupName(new_group_name)
  364. if group.split and group.widgets[1]:
  365. group.widgets[1].setGroupName(new_group_name)
  366. QTimer.singleShot(0, canvas.scene.update)
  367. return
  368. qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode()))
  369. def splitGroup(group_id):
  370. if canvas.debug:
  371. print("PatchCanvas::splitGroup(%i)" % group_id)
  372. item = None
  373. group_name = ""
  374. group_icon = ICON_APPLICATION
  375. group_pos = None
  376. plugin_id = -1
  377. plugin_ui = False
  378. plugin_inline = False
  379. ports_data = []
  380. conns_data = []
  381. # Step 1 - Store all Item data
  382. for group in canvas.group_list:
  383. if group.group_id == group_id:
  384. if group.split:
  385. if canvas.debug:
  386. print("PatchCanvas::splitGroup(%i) - group is already split" % group_id)
  387. return
  388. item = group.widgets[0]
  389. group_name = group.group_name
  390. group_icon = group.icon
  391. group_pos = item.pos()
  392. plugin_id = group.plugin_id
  393. plugin_ui = group.plugin_ui
  394. plugin_inline = group.plugin_inline
  395. break
  396. if not item:
  397. qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id)
  398. return
  399. port_list_ids = list(item.getPortList())
  400. for port in canvas.port_list:
  401. if port.group_id == group_id and port.port_id in port_list_ids:
  402. port_dict = port_dict_t()
  403. port_dict.group_id = port.group_id
  404. port_dict.port_id = port.port_id
  405. port_dict.port_name = port.port_name
  406. port_dict.port_mode = port.port_mode
  407. port_dict.port_type = port.port_type
  408. port_dict.is_alternate = port.is_alternate
  409. port_dict.widget = None
  410. ports_data.append(port_dict)
  411. for connection in canvas.connection_list:
  412. if (connection.group_in_id == group_id and connection.port_in_id in port_list_ids) or \
  413. (connection.group_out_id == group_id and connection.port_out_id in port_list_ids):
  414. connection_dict = connection_dict_t()
  415. connection_dict.connection_id = connection.connection_id
  416. connection_dict.group_in_id = connection.group_in_id
  417. connection_dict.port_in_id = connection.port_in_id
  418. connection_dict.group_out_id = connection.group_out_id
  419. connection_dict.port_out_id = connection.port_out_id
  420. connection_dict.widget = None
  421. conns_data.append(connection_dict)
  422. # Step 2 - Remove Item and Children
  423. for conn in conns_data:
  424. disconnectPorts(conn.connection_id)
  425. for port_id in port_list_ids:
  426. removePort(group_id, port_id)
  427. removeGroup(group_id)
  428. # Step 3 - Re-create Item, now split
  429. group = addGroup(group_id, group_name, SPLIT_YES, group_icon)
  430. if plugin_id >= 0:
  431. setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline)
  432. for port in ports_data:
  433. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  434. for conn in conns_data:
  435. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id, True)
  436. if group is not None:
  437. pos1 = group.widgets[0].pos()
  438. pos2 = group.widgets[1].pos()
  439. group2_pos = QPointF(group_pos.x() + group.widgets[1].boundingRect().width() * 3/2, group_pos.y())
  440. group.widgets[0].blockSignals(True)
  441. group.widgets[0].setPos(group_pos)
  442. group.widgets[0].blockSignals(False)
  443. group.widgets[1].blockSignals(True)
  444. group.widgets[1].setPos(group2_pos)
  445. group.widgets[1].checkItemPos()
  446. group.widgets[1].blockSignals(False)
  447. valueStr = "%i:%i:%i:%i" % (group_pos.x(), group_pos.y(), group2_pos.x(), group2_pos.y())
  448. CanvasCallback(ACTION_GROUP_POSITION, group_id, 0, valueStr)
  449. QTimer.singleShot(0, canvas.scene.update)
  450. def joinGroup(group_id):
  451. if canvas.debug:
  452. print("PatchCanvas::joinGroup(%i)" % group_id)
  453. item = None
  454. s_item = None
  455. group_name = ""
  456. group_icon = ICON_APPLICATION
  457. group_pos = None
  458. plugin_id = -1
  459. plugin_ui = False
  460. plugin_inline = False
  461. ports_data = []
  462. conns_data = []
  463. # Step 1 - Store all Item data
  464. for group in canvas.group_list:
  465. if group.group_id == group_id:
  466. if not group.split:
  467. if canvas.debug:
  468. print("PatchCanvas::joinGroup(%i) - group is not split" % group_id)
  469. return
  470. item = group.widgets[0]
  471. s_item = group.widgets[1]
  472. group_name = group.group_name
  473. group_icon = group.icon
  474. group_pos = item.pos()
  475. plugin_id = group.plugin_id
  476. plugin_ui = group.plugin_ui
  477. plugin_inline = group.plugin_inline
  478. break
  479. # FIXME
  480. if not (item and s_item):
  481. qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id)
  482. return
  483. port_list_ids = list(item.getPortList())
  484. port_list_idss = s_item.getPortList()
  485. for port_id in port_list_idss:
  486. if port_id not in port_list_ids:
  487. port_list_ids.append(port_id)
  488. for port in canvas.port_list:
  489. if port.group_id == group_id and port.port_id in port_list_ids:
  490. port_dict = port_dict_t()
  491. port_dict.group_id = port.group_id
  492. port_dict.port_id = port.port_id
  493. port_dict.port_name = port.port_name
  494. port_dict.port_mode = port.port_mode
  495. port_dict.port_type = port.port_type
  496. port_dict.is_alternate = port.is_alternate
  497. port_dict.widget = None
  498. ports_data.append(port_dict)
  499. for connection in canvas.connection_list:
  500. if (connection.group_in_id == group_id and connection.port_in_id in port_list_ids) or \
  501. (connection.group_out_id == group_id and connection.port_out_id in port_list_ids):
  502. connection_dict = connection_dict_t()
  503. connection_dict.connection_id = connection.connection_id
  504. connection_dict.group_in_id = connection.group_in_id
  505. connection_dict.port_in_id = connection.port_in_id
  506. connection_dict.group_out_id = connection.group_out_id
  507. connection_dict.port_out_id = connection.port_out_id
  508. connection_dict.widget = None
  509. conns_data.append(connection_dict)
  510. # Step 2 - Remove Item and Children
  511. for conn in conns_data:
  512. disconnectPorts(conn.connection_id)
  513. for port_id in port_list_ids:
  514. removePort(group_id, port_id)
  515. removeGroup(group_id)
  516. # Step 3 - Re-create Item, now together
  517. group = addGroup(group_id, group_name, SPLIT_NO, group_icon)
  518. if plugin_id >= 0:
  519. setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline)
  520. for port in ports_data:
  521. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  522. for conn in conns_data:
  523. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id, True)
  524. if group is not None:
  525. group.widgets[0].blockSignals(True)
  526. group.widgets[0].setPos(group_pos)
  527. group.widgets[0].checkItemPos()
  528. group.widgets[0].blockSignals(False)
  529. valueStr = "%i:%i:%i:%i" % (group_pos.x(), group_pos.y(), 0, 0)
  530. CanvasCallback(ACTION_GROUP_POSITION, group_id, 0, valueStr)
  531. QTimer.singleShot(0, canvas.scene.update)
  532. # ------------------------------------------------------------------------------------------------------------
  533. def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
  534. if canvas.debug:
  535. print("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode)))
  536. for group in canvas.group_list:
  537. if group.group_id == group_id:
  538. return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos()
  539. qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
  540. return QPointF(0, 0)
  541. def saveGroupPositions():
  542. if canvas.debug:
  543. print("PatchCanvas::getGroupPositions()")
  544. ret = []
  545. for group in canvas.group_list:
  546. if group.split:
  547. pos1 = group.widgets[0].pos()
  548. pos2 = group.widgets[1].pos()
  549. else:
  550. pos1 = group.widgets[0].pos()
  551. pos2 = QPointF(0, 0)
  552. ret.append({
  553. "name" : group.group_name,
  554. "pos1x": pos1.x(),
  555. "pos1y": pos1.y(),
  556. "pos2x": pos2.x(),
  557. "pos2y": pos2.y(),
  558. "split": group.split,
  559. })
  560. return ret
  561. def restoreGroupPositions(dataList):
  562. if canvas.debug:
  563. print("PatchCanvas::restoreGroupPositions(...)")
  564. mapping = {}
  565. for group in canvas.group_list:
  566. mapping[group.group_name] = group
  567. for data in dataList:
  568. name = data['name']
  569. group = mapping.get(name, None)
  570. if group is None:
  571. continue
  572. group.widgets[0].blockSignals(True)
  573. group.widgets[0].setPos(data['pos1x'], data['pos1y'])
  574. group.widgets[0].blockSignals(False)
  575. if group.split and group.widgets[1]:
  576. group.widgets[1].blockSignals(True)
  577. group.widgets[1].setPos(data['pos2x'], data['pos2y'])
  578. group.widgets[1].blockSignals(False)
  579. def setGroupPos(group_id, group_pos_x, group_pos_y):
  580. setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y)
  581. def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i):
  582. if canvas.debug:
  583. print("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (
  584. group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  585. for group in canvas.group_list:
  586. if group.group_id == group_id:
  587. group.widgets[0].blockSignals(True)
  588. group.widgets[0].setPos(group_pos_x_o, group_pos_y_o)
  589. group.widgets[0].checkItemPos()
  590. group.widgets[0].blockSignals(False)
  591. if group.split and group.widgets[1]:
  592. group.widgets[1].blockSignals(True)
  593. group.widgets[1].setPos(group_pos_x_i, group_pos_y_i)
  594. group.widgets[1].checkItemPos()
  595. group.widgets[1].blockSignals(False)
  596. QTimer.singleShot(0, canvas.scene.update)
  597. return
  598. qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % (
  599. group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  600. # ------------------------------------------------------------------------------------------------------------
  601. def setGroupIcon(group_id, icon):
  602. if canvas.debug:
  603. print("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon)))
  604. for group in canvas.group_list:
  605. if group.group_id == group_id:
  606. group.icon = icon
  607. group.widgets[0].setIcon(icon)
  608. if group.split and group.widgets[1]:
  609. group.widgets[1].setIcon(icon)
  610. QTimer.singleShot(0, canvas.scene.update)
  611. return
  612. qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon)))
  613. def setGroupAsPlugin(group_id, plugin_id, hasUI, hasInlineDisplay):
  614. if canvas.debug:
  615. print("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s)" % (
  616. group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))
  617. for group in canvas.group_list:
  618. if group.group_id == group_id:
  619. group.plugin_id = plugin_id
  620. group.plugin_ui = hasUI
  621. group.plugin_inline = hasInlineDisplay
  622. group.widgets[0].setAsPlugin(plugin_id, hasUI, hasInlineDisplay)
  623. if group.split and group.widgets[1]:
  624. group.widgets[1].setAsPlugin(plugin_id, hasUI, hasInlineDisplay)
  625. canvas.group_plugin_map[plugin_id] = group
  626. return
  627. qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s) - unable to find group to set as plugin" % (
  628. group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))
  629. # ------------------------------------------------------------------------------------------------------------
  630. def focusGroupUsingPluginId(plugin_id):
  631. if canvas.debug:
  632. print("PatchCanvas::focusGroupUsingPluginId(%i)" % (plugin_id,))
  633. if plugin_id < 0 or plugin_id >= MAX_PLUGIN_ID_ALLOWED:
  634. return False
  635. for group in canvas.group_list:
  636. if group.plugin_id == plugin_id:
  637. item = group.widgets[0]
  638. canvas.scene.clearSelection()
  639. canvas.scene.getView().centerOn(item)
  640. item.setSelected(True)
  641. return True
  642. def focusGroupUsingGroupName(group_name):
  643. if canvas.debug:
  644. print("PatchCanvas::focusGroupUsingGroupName(%s)" % (group_name,))
  645. for group in canvas.group_list:
  646. if group.group_name == group_name:
  647. item = group.widgets[0]
  648. canvas.scene.clearSelection()
  649. canvas.scene.getView().centerOn(item)
  650. item.setSelected(True)
  651. return True
  652. # ------------------------------------------------------------------------------------------------------------
  653. def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False):
  654. if canvas.debug:
  655. print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % (
  656. group_id, port_id, port_name.encode(),
  657. port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate)))
  658. for port in canvas.port_list:
  659. if port.group_id == group_id and port.port_id == port_id:
  660. qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % (
  661. group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  662. return
  663. box_widget = None
  664. port_widget = None
  665. for group in canvas.group_list:
  666. if group.group_id == group_id:
  667. if group.split and group.widgets[0].getSplitMode() != port_mode and group.widgets[1]:
  668. n = 1
  669. else:
  670. n = 0
  671. box_widget = group.widgets[n]
  672. port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate)
  673. break
  674. if not (box_widget and port_widget):
  675. qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % (
  676. group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  677. return
  678. port_dict = port_dict_t()
  679. port_dict.group_id = group_id
  680. port_dict.port_id = port_id
  681. port_dict.port_name = port_name
  682. port_dict.port_mode = port_mode
  683. port_dict.port_type = port_type
  684. port_dict.is_alternate = is_alternate
  685. port_dict.widget = port_widget
  686. canvas.port_list.append(port_dict)
  687. box_widget.updatePositions()
  688. if options.eyecandy == EYECANDY_FULL:
  689. CanvasItemFX(port_widget, True, False)
  690. return
  691. QTimer.singleShot(0, canvas.scene.update)
  692. def removePort(group_id, port_id):
  693. if canvas.debug:
  694. print("PatchCanvas::removePort(%i, %i)" % (group_id, port_id))
  695. for port in canvas.port_list:
  696. if port.group_id == group_id and port.port_id == port_id:
  697. item = port.widget
  698. try:
  699. pitem = item.parentItem()
  700. canvas.scene.removeItem(item)
  701. except RuntimeError:
  702. pass
  703. else:
  704. pitem.removePortFromGroup(port_id)
  705. canvas.port_list.remove(port)
  706. del item
  707. QTimer.singleShot(0, canvas.scene.update)
  708. return
  709. qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id))
  710. def renamePort(group_id, port_id, new_port_name):
  711. if canvas.debug:
  712. print("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode()))
  713. for port in canvas.port_list:
  714. if port.group_id == group_id and port.port_id == port_id:
  715. port.port_name = new_port_name
  716. port.widget.setPortName(new_port_name)
  717. port.widget.parentItem().updatePositions()
  718. QTimer.singleShot(0, canvas.scene.update)
  719. return
  720. qCritical("PatchCanvas::renamePort(%i, %i, %s) - Unable to find port to rename" % (
  721. group_id, port_id, new_port_name.encode()))
  722. def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id, fromSplitOrJoin = False):
  723. if canvas.last_connection_id >= connection_id and not fromSplitOrJoin:
  724. print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - invalid connection id received (last: %i)" % (
  725. connection_id, group_out_id, port_out_id, group_in_id, port_in_id, canvas.last_connection_id))
  726. return
  727. canvas.last_connection_id = connection_id
  728. if canvas.debug:
  729. print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % (
  730. connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  731. port_out = None
  732. port_in = None
  733. port_out_parent = None
  734. port_in_parent = None
  735. for port in canvas.port_list:
  736. if port.group_id == group_out_id and port.port_id == port_out_id:
  737. port_out = port.widget
  738. port_out_parent = port_out.parentItem()
  739. elif port.group_id == group_in_id and port.port_id == port_in_id:
  740. port_in = port.widget
  741. port_in_parent = port_in.parentItem()
  742. # FIXME
  743. if not (port_out and port_in):
  744. qCritical("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - unable to find ports to connect" % (
  745. connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  746. return
  747. connection_dict = connection_dict_t()
  748. connection_dict.connection_id = connection_id
  749. connection_dict.group_in_id = group_in_id
  750. connection_dict.port_in_id = port_in_id
  751. connection_dict.group_out_id = group_out_id
  752. connection_dict.port_out_id = port_out_id
  753. if options.use_bezier_lines:
  754. connection_dict.widget = CanvasBezierLine(port_out, port_in, None)
  755. else:
  756. connection_dict.widget = CanvasLine(port_out, port_in, None)
  757. canvas.scene.addItem(connection_dict.widget)
  758. port_out_parent.addLineFromGroup(connection_dict.widget, connection_id)
  759. port_in_parent.addLineFromGroup(connection_dict.widget, connection_id)
  760. canvas.last_z_value += 1
  761. port_out_parent.setZValue(canvas.last_z_value)
  762. port_in_parent.setZValue(canvas.last_z_value)
  763. canvas.last_z_value += 1
  764. connection_dict.widget.setZValue(canvas.last_z_value)
  765. canvas.connection_list.append(connection_dict)
  766. if options.eyecandy == EYECANDY_FULL:
  767. item = connection_dict.widget
  768. CanvasItemFX(item, True, False)
  769. return
  770. QTimer.singleShot(0, canvas.scene.update)
  771. def disconnectPorts(connection_id):
  772. if canvas.debug:
  773. print("PatchCanvas::disconnectPorts(%i)" % connection_id)
  774. line = None
  775. item1 = None
  776. item2 = None
  777. group1id = port1id = 0
  778. group2id = port2id = 0
  779. for connection in canvas.connection_list:
  780. if connection.connection_id == connection_id:
  781. group1id = connection.group_out_id
  782. group2id = connection.group_in_id
  783. port1id = connection.port_out_id
  784. port2id = connection.port_in_id
  785. line = connection.widget
  786. canvas.connection_list.remove(connection)
  787. break
  788. if not line:
  789. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id)
  790. return
  791. for port in canvas.port_list:
  792. if port.group_id == group1id and port.port_id == port1id:
  793. item1 = port.widget
  794. break
  795. if not item1:
  796. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id)
  797. return
  798. for port in canvas.port_list:
  799. if port.group_id == group2id and port.port_id == port2id:
  800. item2 = port.widget
  801. break
  802. if not item2:
  803. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id)
  804. return
  805. item1p = item1.parentItem()
  806. item2p = item2.parentItem()
  807. if item1p:
  808. item1p.removeLineFromGroup(connection_id)
  809. if item2p:
  810. item2p.removeLineFromGroup(connection_id)
  811. if options.eyecandy == EYECANDY_FULL:
  812. CanvasItemFX(line, False, True)
  813. return
  814. canvas.scene.removeItem(line)
  815. del line
  816. QTimer.singleShot(0, canvas.scene.update)
  817. # ------------------------------------------------------------------------------------------------------------
  818. def arrange():
  819. if canvas.debug:
  820. print("PatchCanvas::arrange()")
  821. # ------------------------------------------------------------------------------------------------------------
  822. def updateZValues():
  823. if canvas.debug:
  824. print("PatchCanvas::updateZValues()")
  825. for group in canvas.group_list:
  826. group.widgets[0].resetLinesZValue()
  827. if group.split and group.widgets[1]:
  828. group.widgets[1].resetLinesZValue()
  829. # ------------------------------------------------------------------------------------------------------------
  830. def redrawPluginGroup(plugin_id):
  831. group = canvas.group_plugin_map.get(plugin_id, None)
  832. if group is None:
  833. #qCritical("PatchCanvas::redrawPluginGroup(%i) - unable to find group" % plugin_id)
  834. return
  835. group.widgets[0].redrawInlineDisplay()
  836. if group.split and group.widgets[1]:
  837. group.widgets[1].redrawInlineDisplay()
  838. def handlePluginRemoved(plugin_id):
  839. if canvas.debug:
  840. print("PatchCanvas::handlePluginRemoved(%i)" % plugin_id)
  841. canvas.scene.clearSelection()
  842. group = canvas.group_plugin_map.pop(plugin_id, None)
  843. if group is not None:
  844. group.plugin_id = -1
  845. group.plugin_ui = False
  846. group.plugin_inline = False
  847. group.widgets[0].removeAsPlugin()
  848. if group.split and group.widgets[1]:
  849. group.widgets[1].removeAsPlugin()
  850. for group in canvas.group_list:
  851. if group.plugin_id < plugin_id or group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
  852. continue
  853. group.plugin_id -= 1
  854. group.widgets[0].m_plugin_id -= 1
  855. if group.split and group.widgets[1]:
  856. group.widgets[1].m_plugin_id -= 1
  857. canvas.group_plugin_map[plugin_id] = group
  858. def handleAllPluginsRemoved():
  859. if canvas.debug:
  860. print("PatchCanvas::handleAllPluginsRemoved()")
  861. canvas.group_plugin_map = {}
  862. for group in canvas.group_list:
  863. if group.plugin_id < 0:
  864. continue
  865. if group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
  866. continue
  867. group.plugin_id = -1
  868. group.plugin_ui = False
  869. group.plugin_inline = False
  870. group.widgets[0].removeAsPlugin()
  871. if group.split and group.widgets[1]:
  872. group.widgets[1].removeAsPlugin()
  873. # ------------------------------------------------------------------------------------------------------------