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.

2931 lines
103KB

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