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.

2851 lines
100KB

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