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.

2842 lines
99KB

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