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.

2859 lines
100KB

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