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.

2922 lines
102KB

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