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.

2865 lines
101KB

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