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.

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