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.

373 lines
12KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Rack List Widget, a custom Qt4 widget
  4. # Copyright (C) 2011-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 Qt, QSize
  24. from PyQt5.QtGui import QPainter, QPixmap
  25. from PyQt5.QtWidgets import QAbstractItemView, QFrame, QListWidget, QListWidgetItem
  26. else:
  27. from PyQt4.QtCore import Qt, QSize
  28. from PyQt4.QtGui import QAbstractItemView, QFrame, QListWidget, QListWidgetItem, QPainter, QPixmap
  29. # ------------------------------------------------------------------------------------------------------------
  30. # Imports (Custom Stuff)
  31. from carla_skin import *
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Rack Widget item
  34. class RackListItem(QListWidgetItem):
  35. kRackItemType = QListWidgetItem.UserType + 1
  36. kMinimumWidth = 620
  37. def __init__(self, parent, pluginId, useSkins):
  38. QListWidgetItem.__init__(self, parent, self.kRackItemType)
  39. self.host = parent.host
  40. if False:
  41. # kdevelop likes this :)
  42. parent = RackListWidget()
  43. host = CarlaHostNull()
  44. self.host = host
  45. self.fWidget = AbstractPluginSlot()
  46. # ----------------------------------------------------------------------------------------------------
  47. # Internal stuff
  48. self.fParent = parent
  49. self.fPluginId = pluginId
  50. self.fWidget = None
  51. self.fOptions = {
  52. 'compact': bool(self.host.get_custom_data_value(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkinIsCompacted") == "true"),
  53. 'useSkins': useSkins
  54. }
  55. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  56. #self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled)
  57. # ----------------------------------------------------------------------------------------------------
  58. # Set-up GUI
  59. self.recreateWidget(firstInit = True)
  60. # --------------------------------------------------------------------------------------------------------
  61. def close(self):
  62. if self.fWidget is None:
  63. return
  64. widget = self.fWidget
  65. self.fWidget = None
  66. self.fParent.customClearSelection()
  67. self.fParent.setItemWidget(self, None)
  68. widget.fEditDialog.close()
  69. widget.fEditDialog.setParent(None)
  70. widget.fEditDialog.deleteLater()
  71. del widget.fEditDialog
  72. widget.close()
  73. widget.setParent(None)
  74. widget.deleteLater()
  75. del widget
  76. def getEditDialog(self):
  77. if self.fWidget is None:
  78. return None
  79. return self.fWidget.fEditDialog
  80. def getPluginId(self):
  81. return self.fPluginId
  82. def getWidget(self):
  83. return self.fWidget
  84. def isCompacted(self):
  85. return self.fOptions['compact']
  86. def isUsingSkins(self):
  87. return self.fOptions['useSkins']
  88. def isGuiShown(self):
  89. if self.fWidget is None or self.fWidget.b_gui is not None:
  90. return None
  91. return self.fWidget.b_gui.isChecked()
  92. # --------------------------------------------------------------------------------------------------------
  93. def setPluginId(self, pluginId):
  94. self.fPluginId = pluginId
  95. if self.fWidget is not None:
  96. self.fWidget.setPluginId(pluginId)
  97. def setSelected(self, select):
  98. if self.fWidget is not None:
  99. self.fWidget.setSelected(select)
  100. QListWidgetItem.setSelected(self, select)
  101. # --------------------------------------------------------------------------------------------------------
  102. def setCompacted(self, compact):
  103. self.fOptions['compact'] = compact
  104. def setUsingSkins(self, useSkins):
  105. self.fOptions['useSkins'] = useSkins
  106. # --------------------------------------------------------------------------------------------------------
  107. def compact(self):
  108. if self.fOptions['compact']:
  109. return
  110. self.recreateWidget(True)
  111. def expand(self):
  112. if not self.fOptions['compact']:
  113. return
  114. self.recreateWidget(True)
  115. def recreateWidget(self, invertCompactOption = False, firstInit = False):
  116. if invertCompactOption:
  117. self.fOptions['compact'] = not self.fOptions['compact']
  118. wasGuiShown = None
  119. if self.fWidget is not None and self.fWidget.b_gui is not None:
  120. wasGuiShown = self.fWidget.b_gui.isChecked()
  121. self.close()
  122. self.fWidget = createPluginSlot(self.fParent, self.host, self.fPluginId, self.fOptions)
  123. self.fWidget.setFixedHeight(self.fWidget.getFixedHeight())
  124. if wasGuiShown and self.fWidget.b_gui is not None:
  125. self.fWidget.b_gui.setChecked(True)
  126. self.setSizeHint(QSize(self.kMinimumWidth, self.fWidget.getFixedHeight()))
  127. self.fParent.setItemWidget(self, self.fWidget)
  128. if not firstInit:
  129. self.host.set_custom_data(self.fPluginId, CUSTOM_DATA_TYPE_PROPERTY,
  130. "CarlaSkinIsCompacted", "true" if self.fOptions['compact'] else "false")
  131. def recreateWidget2(self, wasCompacted, wasGuiShown):
  132. self.fOptions['compact'] = wasCompacted
  133. self.close()
  134. self.fWidget = createPluginSlot(self.fParent, self.host, self.fPluginId, self.fOptions)
  135. self.fWidget.setFixedHeight(self.fWidget.getFixedHeight())
  136. if wasGuiShown and self.fWidget.b_gui is not None:
  137. self.fWidget.b_gui.setChecked(True)
  138. self.setSizeHint(QSize(self.kMinimumWidth, self.fWidget.getFixedHeight()))
  139. self.fParent.setItemWidget(self, self.fWidget)
  140. self.host.set_custom_data(self.fPluginId, CUSTOM_DATA_TYPE_PROPERTY,
  141. "CarlaSkinIsCompacted", "true" if wasCompacted else "false")
  142. # ------------------------------------------------------------------------------------------------------------
  143. # Rack Widget
  144. class RackListWidget(QListWidget):
  145. def __init__(self, parent):
  146. QListWidget.__init__(self, parent)
  147. self.host = None
  148. self.fParent = None
  149. if False:
  150. # kdevelop likes this :)
  151. from carla_backend import CarlaHostMeta
  152. self.host = host = CarlaHostNull()
  153. exts = gCarla.utils.get_supported_file_extensions()
  154. self.fSupportedExtensions = tuple(("." + i) for i in exts)
  155. self.fLastSelectedItem = None
  156. self.fWasLastDragValid = False
  157. self.fPixmapL = QPixmap(":/bitmaps/rack_interior_left.png")
  158. self.fPixmapR = QPixmap(":/bitmaps/rack_interior_right.png")
  159. self.fPixmapWidth = self.fPixmapL.width()
  160. self.setMinimumWidth(RackListItem.kMinimumWidth)
  161. self.setSelectionMode(QAbstractItemView.SingleSelection)
  162. self.setSortingEnabled(False)
  163. self.setDragEnabled(True)
  164. self.setDragDropMode(QAbstractItemView.DropOnly)
  165. self.setDropIndicatorShown(True)
  166. self.viewport().setAcceptDrops(True)
  167. self.setFrameShape(QFrame.NoFrame)
  168. self.setFrameShadow(QFrame.Plain)
  169. # --------------------------------------------------------------------------------------------------------
  170. def createItem(self, pluginId, useSkins):
  171. return RackListItem(self, pluginId, useSkins)
  172. def getPluginCount(self):
  173. return self.fParent.getPluginCount()
  174. def setHostAndParent(self, host, parent):
  175. self.host = host
  176. self.fParent = parent
  177. # --------------------------------------------------------------------------------------------------------
  178. def customClearSelection(self):
  179. self.setCurrentRow(-1)
  180. self.clearSelection()
  181. self.clearFocus()
  182. def isDragUrlValid(self, filename):
  183. if os.path.isdir(filename):
  184. #if os.path.exists(os.path.join(filename, "manifest.ttl")):
  185. #return True
  186. if MACOS and filename.lower().endswith(".vst"):
  187. return True
  188. elif os.path.isfile(filename):
  189. if filename.lower().endswith(self.fSupportedExtensions):
  190. return True
  191. return False
  192. # --------------------------------------------------------------------------------------------------------
  193. def dragEnterEvent(self, event):
  194. urls = event.mimeData().urls()
  195. for url in urls:
  196. if self.isDragUrlValid(url.toLocalFile()):
  197. self.fWasLastDragValid = True
  198. event.acceptProposedAction()
  199. return
  200. self.fWasLastDragValid = False
  201. QListWidget.dragEnterEvent(self, event)
  202. def dragMoveEvent(self, event):
  203. if not self.fWasLastDragValid:
  204. QListWidget.dragMoveEvent(self, event)
  205. return
  206. event.acceptProposedAction()
  207. tryItem = self.itemAt(event.pos())
  208. if tryItem is not None:
  209. self.setCurrentRow(tryItem.getPluginId())
  210. else:
  211. self.setCurrentRow(-1)
  212. def dragLeaveEvent(self, event):
  213. self.fWasLastDragValid = False
  214. QListWidget.dragLeaveEvent(self, event)
  215. # --------------------------------------------------------------------------------------------------------
  216. # FIXME: this needs some attention
  217. # if dropping project file over 1 plugin, load it in rack or patchbay
  218. # if dropping regular files over 1 plugin, keep replacing plugins
  219. def dropEvent(self, event):
  220. event.acceptProposedAction()
  221. urls = event.mimeData().urls()
  222. if len(urls) == 0:
  223. return
  224. tryItem = self.itemAt(event.pos())
  225. if tryItem is not None:
  226. pluginId = tryItem.getPluginId()
  227. else:
  228. pluginId = -1
  229. for url in urls:
  230. if pluginId >= 0:
  231. self.host.replace_plugin(pluginId)
  232. pluginId += 1
  233. if pluginId > self.host.get_current_plugin_count():
  234. pluginId = -1
  235. filename = url.toLocalFile()
  236. if not self.host.load_file(filename):
  237. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  238. self.tr("Failed to load file"),
  239. self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  240. if tryItem is not None:
  241. self.host.replace_plugin(self.host.get_max_plugin_number())
  242. #tryItem.widget.setActive(True, True, True)
  243. # --------------------------------------------------------------------------------------------------------
  244. def mousePressEvent(self, event):
  245. if self.itemAt(event.pos()) is None and self.currentRow() != -1:
  246. event.accept()
  247. self.customClearSelection()
  248. return
  249. QListWidget.mousePressEvent(self, event)
  250. def paintEvent(self, event):
  251. painter = QPainter(self.viewport())
  252. painter.drawTiledPixmap(0, 0, self.fPixmapWidth, self.height(), self.fPixmapL)
  253. painter.drawTiledPixmap(self.width()-self.fPixmapWidth-2, 0, self.fPixmapWidth, self.height(), self.fPixmapR)
  254. QListWidget.paintEvent(self, event)
  255. # --------------------------------------------------------------------------------------------------------
  256. def selectionChanged(self, selected, deselected):
  257. for index in deselected.indexes():
  258. item = self.itemFromIndex(index)
  259. if item is not None:
  260. item.setSelected(False)
  261. for index in selected.indexes():
  262. item = self.itemFromIndex(index)
  263. if item is not None:
  264. item.setSelected(True)
  265. QListWidget.selectionChanged(self, selected, deselected)
  266. # ------------------------------------------------------------------------------------------------------------