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.

1113 lines
37KB

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