Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2867 lines
101KB

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