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.

racklistwidget.py 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. def __init__(self, parent, pluginId, useSkins):
  37. QListWidgetItem.__init__(self, parent, self.kRackItemType)
  38. self.host = parent.host
  39. if False:
  40. # kdevelop likes this :)
  41. parent = RackListWidget()
  42. host = CarlaHostMeta()
  43. self.host = host
  44. self.fWidget = AbstractPluginSlot()
  45. # ----------------------------------------------------------------------------------------------------
  46. # Internal stuff
  47. self.fParent = parent
  48. self.fPluginId = pluginId
  49. self.fUseSkins = useSkins
  50. self.fWidget = None
  51. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  52. #self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled|Qt.ItemIsDropEnabled)
  53. # ----------------------------------------------------------------------------------------------------
  54. # Set-up GUI
  55. self.recreateWidget()
  56. # --------------------------------------------------------------------------------------------------------
  57. def close(self):
  58. if self.fWidget is None:
  59. return
  60. self.fWidget.fEditDialog.close()
  61. self.fWidget.fEditDialog.setParent(None)
  62. self.fWidget.fEditDialog.deleteLater()
  63. del self.fWidget.fEditDialog
  64. self.fWidget.close()
  65. self.fWidget.setParent(None)
  66. self.fWidget.deleteLater()
  67. del self.fWidget
  68. self.fWidget = None
  69. def getEditDialog(self):
  70. if self.fWidget is None:
  71. return None
  72. return self.fWidget.fEditDialog
  73. def getPluginId(self):
  74. return self.fPluginId
  75. def getWidget(self):
  76. return self.fWidget
  77. # --------------------------------------------------------------------------------------------------------
  78. def setPluginId(self, pluginId):
  79. self.fPluginId = pluginId
  80. if self.fWidget is not None:
  81. self.fWidget.setPluginId(pluginId)
  82. # --------------------------------------------------------------------------------------------------------
  83. def recreateWidget(self):
  84. self.close()
  85. self.fWidget = createPluginSlot(self.fParent, self.host, self.fPluginId, self.fUseSkins)
  86. self.fWidget.setFixedHeight(self.fWidget.getFixedHeight())
  87. self.setSizeHint(QSize(620, self.fWidget.getFixedHeight()))
  88. self.fParent.setItemWidget(self, self.fWidget)
  89. # ------------------------------------------------------------------------------------------------------------
  90. # Rack Widget
  91. class RackListWidget(QListWidget):
  92. def __init__(self, parent):
  93. QListWidget.__init__(self, parent)
  94. self.host = None
  95. if False:
  96. # kdevelop likes this :)
  97. from carla_backend import CarlaHostMeta
  98. host = CarlaHostMeta()
  99. self.host = host
  100. exts = gCarla.utils.get_supported_file_extensions().split(";")
  101. exts.append(".dll")
  102. if MACOS:
  103. exts.append(".dylib")
  104. if not WINDOWS:
  105. exts.append(".so")
  106. self.fSupportedExtensions = tuple(i.replace("*","") for i in exts)
  107. self.fWasLastDragValid = False
  108. self.setMinimumWidth(640)
  109. self.setSelectionMode(QAbstractItemView.SingleSelection)
  110. self.setSortingEnabled(False)
  111. self.setDragEnabled(True)
  112. self.setDragDropMode(QAbstractItemView.DropOnly)
  113. self.setDropIndicatorShown(True)
  114. self.viewport().setAcceptDrops(True)
  115. self.setFrameShape(QFrame.NoFrame)
  116. self.setFrameShadow(QFrame.Plain)
  117. self.fPixmapL = QPixmap(":/bitmaps/rack_interior_left.png")
  118. self.fPixmapR = QPixmap(":/bitmaps/rack_interior_right.png")
  119. self.fPixmapWidth = self.fPixmapL.width()
  120. # --------------------------------------------------------------------------------------------------------
  121. def createItem(self, pluginId, useSkins):
  122. return RackListItem(self, pluginId, useSkins)
  123. def setHost(self, host):
  124. self.host = host
  125. # --------------------------------------------------------------------------------------------------------
  126. def isDragUrlValid(self, url):
  127. filename = url.toLocalFile()
  128. if os.path.isdir(filename):
  129. if os.path.exists(os.path.join(filename, "manifest.ttl")):
  130. return True
  131. if filename.lower().endswith((".vst", ".vst3")):
  132. return True
  133. elif os.path.isfile(filename):
  134. if filename.lower().endswith(self.fSupportedExtensions):
  135. return True
  136. return False
  137. # --------------------------------------------------------------------------------------------------------
  138. def dragEnterEvent(self, event):
  139. urls = event.mimeData().urls()
  140. for url in urls:
  141. if self.isDragUrlValid(url):
  142. self.fWasLastDragValid = True
  143. event.acceptProposedAction()
  144. return
  145. self.fWasLastDragValid = False
  146. QListWidget.dragEnterEvent(self, event)
  147. def dragMoveEvent(self, event):
  148. if not self.fWasLastDragValid:
  149. QListWidget.dragMoveEvent(self, event)
  150. return
  151. event.acceptProposedAction()
  152. tryItem = self.itemAt(event.pos())
  153. if tryItem is not None:
  154. self.setCurrentRow(tryItem.getPluginId())
  155. else:
  156. self.setCurrentRow(-1)
  157. def dragLeaveEvent(self, event):
  158. self.fWasLastDragValid = False
  159. QListWidget.dragLeaveEvent(self, event)
  160. # --------------------------------------------------------------------------------------------------------
  161. # FIXME: this needs some attention
  162. # if dropping project file over 1 plugin, load it in rack or patchbay
  163. # if dropping regular files over 1 plugin, keep replacing plugins
  164. def dropEvent(self, event):
  165. event.acceptProposedAction()
  166. urls = event.mimeData().urls()
  167. if len(urls) == 0:
  168. return
  169. tryItem = self.itemAt(event.pos())
  170. if tryItem is not None:
  171. pluginId = tryItem.getPluginId()
  172. else:
  173. pluginId = -1
  174. for url in urls:
  175. if pluginId >= 0:
  176. self.host.replace_plugin(pluginId)
  177. pluginId += 1
  178. if pluginId > self.host.get_current_plugin_count():
  179. pluginId = -1
  180. filename = url.toLocalFile()
  181. if not self.host.load_file(filename):
  182. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  183. self.tr("Failed to load file"),
  184. self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  185. if tryItem is not None:
  186. self.host.replace_plugin(self.host.get_max_plugin_number())
  187. #tryItem.widget.setActive(True, True, True)
  188. # --------------------------------------------------------------------------------------------------------
  189. def mousePressEvent(self, event):
  190. if self.itemAt(event.pos()) is None:
  191. event.accept()
  192. self.setCurrentRow(-1)
  193. return
  194. QListWidget.mousePressEvent(self, event)
  195. def paintEvent(self, event):
  196. painter = QPainter(self.viewport())
  197. painter.drawTiledPixmap(0, 0, self.fPixmapWidth, self.height(), self.fPixmapL)
  198. painter.drawTiledPixmap(self.width()-self.fPixmapWidth-2, 0, self.fPixmapWidth, self.height(), self.fPixmapR)
  199. QListWidget.paintEvent(self, event)
  200. # ------------------------------------------------------------------------------------------------------------