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.

2987 lines
104KB

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