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.

carla_rack.py 22KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla rack widget code
  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, QTimer
  24. from PyQt5.QtGui import QPixmap
  25. from PyQt5.QtWidgets import QAbstractItemView, QApplication, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QScrollBar
  26. else:
  27. from PyQt4.QtCore import Qt, QSize, QTimer
  28. from PyQt4.QtGui import QAbstractItemView, QApplication, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPixmap, QScrollBar
  29. # ------------------------------------------------------------------------------------------------------------
  30. # Imports (Custom Stuff)
  31. from carla_skin import *
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Rack widget item
  34. class CarlaRackItem(QListWidgetItem):
  35. kRackItemType = QListWidgetItem.UserType + 1
  36. def __init__(self, parent, pluginId):
  37. QListWidgetItem.__init__(self, parent, self.kRackItemType)
  38. self.fParent = parent
  39. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  40. #self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled|Qt.ItemIsDropEnabled)
  41. self.createWidget(pluginId)
  42. # -----------------------------------------------------------------
  43. def createWidget(self, pluginId):
  44. self.widget = createPluginSlot(self.fParent, pluginId)
  45. self.widget.setFixedHeight(self.widget.getFixedHeight())
  46. self.setSizeHint(QSize(640, self.widget.getFixedHeight()))
  47. self.fParent.setItemWidget(self, self.widget)
  48. # -----------------------------------------------------------------
  49. def close(self):
  50. self.widget.fEditDialog.close()
  51. def reloadAll(self, pluginId):
  52. self.widget.fEditDialog.close()
  53. del self.widget
  54. self.createWidget(pluginId)
  55. # ------------------------------------------------------------------------------------------------------------
  56. # Rack widget list
  57. class CarlaRackList(QListWidget):
  58. def __init__(self, parent):
  59. QListWidget.__init__(self, parent)
  60. exts = gCarla.host.get_supported_file_extensions().split(";") if gCarla.host is not None else ["wav",]
  61. # plugin files
  62. exts.append("dll")
  63. if MACOS:
  64. exts.append("dylib")
  65. if not WINDOWS:
  66. exts.append("so")
  67. self.fSupportedExtensions = tuple(i.replace("*.","") for i in exts)
  68. self.fWasLastDragValid = False
  69. self.setMinimumWidth(640+20) # required by zita, 591 was old value
  70. self.setSelectionMode(QAbstractItemView.SingleSelection)
  71. self.setSortingEnabled(False)
  72. #self.setSortingEnabled(True)
  73. self.setDragEnabled(True)
  74. self.setDragDropMode(QAbstractItemView.DropOnly)
  75. self.setDropIndicatorShown(True)
  76. self.viewport().setAcceptDrops(True)
  77. self.fPixmapL = QPixmap(":/bitmaps/rack_interior_left.png")
  78. self.fPixmapR = QPixmap(":/bitmaps/rack_interior_right.png")
  79. self.fPixmapWidth = self.fPixmapL.width()
  80. def isDragEventValid(self, urls):
  81. for url in urls:
  82. filename = url.toLocalFile()
  83. if os.path.isdir(filename):
  84. if os.path.exists(os.path.join(filename, "manifest.ttl")):
  85. return True
  86. elif os.path.isfile(filename):
  87. if filename.lower().endswith(self.fSupportedExtensions):
  88. return True
  89. return False
  90. def dragEnterEvent(self, event):
  91. if self.isDragEventValid(event.mimeData().urls()):
  92. self.fWasLastDragValid = True
  93. event.acceptProposedAction()
  94. return
  95. self.fWasLastDragValid = False
  96. QListWidget.dragEnterEvent(self, event)
  97. def dragMoveEvent(self, event):
  98. if self.fWasLastDragValid:
  99. event.acceptProposedAction()
  100. tryItem = self.itemAt(event.pos())
  101. if tryItem is not None:
  102. self.setCurrentRow(tryItem.widget.getPluginId())
  103. else:
  104. self.setCurrentRow(-1)
  105. return
  106. QListWidget.dragMoveEvent(self, event)
  107. #def dragLeaveEvent(self, event):
  108. #self.fWasLastDragValid = False
  109. #QListWidget.dragLeaveEvent(self, event)
  110. def dropEvent(self, event):
  111. event.acceptProposedAction()
  112. urls = event.mimeData().urls()
  113. if len(urls) == 0:
  114. return
  115. tryItem = self.itemAt(event.pos())
  116. if tryItem is not None:
  117. pluginId = tryItem.widget.getPluginId()
  118. gCarla.host.replace_plugin(pluginId)
  119. for url in urls:
  120. filename = url.toLocalFile()
  121. if not gCarla.host.load_file(filename):
  122. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  123. self.tr("Failed to load file"),
  124. gCarla.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  125. if tryItem is not None:
  126. gCarla.host.replace_plugin(self.parent().fPluginCount)
  127. tryItem.widget.setActive(True, True, True)
  128. def mousePressEvent(self, event):
  129. if self.itemAt(event.pos()) is None:
  130. event.accept()
  131. self.setCurrentRow(-1)
  132. return
  133. QListWidget.mousePressEvent(self, event)
  134. def paintEvent(self, event):
  135. painter = QPainter(self.viewport())
  136. painter.drawTiledPixmap(0, 0, self.fPixmapWidth, self.height(), self.fPixmapL)
  137. painter.drawTiledPixmap(self.width()-self.fPixmapWidth-2, 0, self.fPixmapWidth, self.height(), self.fPixmapR)
  138. QListWidget.paintEvent(self, event)
  139. # ------------------------------------------------------------------------------------------------------------
  140. # Rack widget
  141. class CarlaRackW(QFrame):
  142. def __init__(self, parent, doSetup = True):
  143. QFrame.__init__(self, parent)
  144. self.fLayout = QHBoxLayout(self)
  145. self.fLayout.setContentsMargins(0, 0, 0, 0)
  146. self.fLayout.setSpacing(0)
  147. self.setLayout(self.fLayout)
  148. self.fPadLeft = QLabel(self)
  149. self.fPadLeft.setFixedWidth(25)
  150. self.fPadLeft.setObjectName("PadLeft")
  151. self.fPadLeft.setText("")
  152. self.fPadRight = QLabel(self)
  153. self.fPadRight.setFixedWidth(25)
  154. self.fPadRight.setObjectName("PadRight")
  155. self.fPadRight.setText("")
  156. self.fRack = CarlaRackList(self)
  157. self.fRack.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
  158. self.fRack.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
  159. self.fRack.currentRowChanged.connect(self.slot_currentRowChanged)
  160. sb = self.fRack.verticalScrollBar()
  161. self.fScrollBar = QScrollBar(Qt.Vertical, self)
  162. self.fScrollBar.setMinimum(sb.minimum())
  163. self.fScrollBar.setMaximum(sb.maximum())
  164. self.fScrollBar.setValue(sb.value())
  165. #sb.actionTriggered.connect(self.fScrollBar.triggerAction)
  166. #sb.sliderMoved.connect(self.fScrollBar.)
  167. #sb.sliderPressed.connect(self.fScrollBar.)
  168. #sb.sliderReleased.connect(self.fScrollBar.)
  169. sb.rangeChanged.connect(self.fScrollBar.setRange)
  170. sb.valueChanged.connect(self.fScrollBar.setValue)
  171. self.fScrollBar.rangeChanged.connect(sb.setRange)
  172. self.fScrollBar.valueChanged.connect(sb.setValue)
  173. self.fLayout.addWidget(self.fPadLeft)
  174. self.fLayout.addWidget(self.fRack)
  175. self.fLayout.addWidget(self.fPadRight)
  176. self.fLayout.addWidget(self.fScrollBar)
  177. # -------------------------------------------------------------
  178. # Internal stuff
  179. self.fParent = parent
  180. self.fPluginCount = 0
  181. self.fPluginList = []
  182. self.fCurrentRow = -1
  183. self.fLastSelectedItem = None
  184. # -------------------------------------------------------------
  185. # Set-up GUI stuff
  186. #app = QApplication.instance()
  187. #pal1 = app.palette().base().color()
  188. #pal2 = app.palette().button().color()
  189. #col1 = "stop:0 rgb(%i, %i, %i)" % (pal1.red(), pal1.green(), pal1.blue())
  190. #col2 = "stop:1 rgb(%i, %i, %i)" % (pal2.red(), pal2.green(), pal2.blue())
  191. self.setStyleSheet("""
  192. QLabel#PadLeft {
  193. background-image: url(:/bitmaps/rack_padding_left.png);
  194. background-repeat: repeat-y;
  195. }
  196. QLabel#PadRight {
  197. background-image: url(:/bitmaps/rack_padding_right.png);
  198. background-repeat: repeat-y;
  199. }
  200. QListWidget {
  201. background-color: black;
  202. }
  203. """)
  204. # -------------------------------------------------------------
  205. # Connect actions to functions
  206. if not doSetup: return
  207. parent.ui.menu_Canvas.hide()
  208. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  209. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  210. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  211. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  212. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  213. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  214. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  215. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  216. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  217. parent.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
  218. parent.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
  219. parent.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
  220. parent.ParameterMidiCcChangedCallback.connect(self.slot_handleParameterMidiCcChangedCallback)
  221. parent.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
  222. parent.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
  223. parent.UiStateChangedCallback.connect(self.slot_handleUiStateChangedCallback)
  224. parent.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  225. parent.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  226. parent.UpdateCallback.connect(self.slot_handleUpdateCallback)
  227. parent.ReloadInfoCallback.connect(self.slot_handleReloadInfoCallback)
  228. parent.ReloadParametersCallback.connect(self.slot_handleReloadParametersCallback)
  229. parent.ReloadProgramsCallback.connect(self.slot_handleReloadProgramsCallback)
  230. parent.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  231. # -----------------------------------------------------------------
  232. def getPluginCount(self):
  233. return self.fPluginCount
  234. # -----------------------------------------------------------------
  235. def addPlugin(self, pluginId, isProjectLoading):
  236. pitem = CarlaRackItem(self.fRack, pluginId)
  237. self.fPluginList.append(pitem)
  238. self.fPluginCount += 1
  239. if not isProjectLoading:
  240. pitem.widget.setActive(True, True, True)
  241. def removePlugin(self, pluginId):
  242. if pluginId >= self.fPluginCount:
  243. return
  244. pitem = self.fPluginList[pluginId]
  245. if pitem is None:
  246. return
  247. self.fPluginCount -= 1
  248. self.fPluginList.pop(pluginId)
  249. self.fRack.takeItem(pluginId)
  250. pitem.close()
  251. del pitem
  252. # push all plugins 1 slot back
  253. for i in range(pluginId, self.fPluginCount):
  254. pitem = self.fPluginList[i]
  255. pitem.widget.setId(i)
  256. def renamePlugin(self, pluginId, newName):
  257. if pluginId >= self.fPluginCount:
  258. return
  259. pitem = self.fPluginList[pluginId]
  260. if pitem is None:
  261. return
  262. pitem.widget.setName(newName)
  263. def disablePlugin(self, pluginId, errorMsg):
  264. if pluginId >= self.fPluginCount:
  265. return
  266. pitem = self.fPluginList[pluginId]
  267. if pitem is None:
  268. return
  269. def removeAllPlugins(self):
  270. while self.fRack.takeItem(0):
  271. pass
  272. for i in range(self.fPluginCount):
  273. pitem = self.fPluginList[i]
  274. if pitem is None:
  275. break
  276. pitem.close()
  277. del pitem
  278. self.fPluginCount = 0
  279. self.fPluginList = []
  280. # -----------------------------------------------------------------
  281. def engineStarted(self):
  282. pass
  283. def engineStopped(self):
  284. pass
  285. def engineChanged(self):
  286. pass
  287. # -----------------------------------------------------------------
  288. def idleFast(self):
  289. for i in range(self.fPluginCount):
  290. pitem = self.fPluginList[i]
  291. if pitem is None:
  292. break
  293. pitem.widget.idleFast()
  294. def idleSlow(self):
  295. for i in range(self.fPluginCount):
  296. pitem = self.fPluginList[i]
  297. if pitem is None:
  298. break
  299. pitem.widget.idleSlow()
  300. # -----------------------------------------------------------------
  301. def projectLoadingStarted(self):
  302. self.fRack.setEnabled(False)
  303. def projectLoadingFinished(self):
  304. self.fRack.setEnabled(True)
  305. # -----------------------------------------------------------------
  306. def saveSettings(self, settings):
  307. pass
  308. def showEditDialog(self, pluginId):
  309. if pluginId >= self.fPluginCount:
  310. return
  311. pitem = self.fPluginList[pluginId]
  312. if pitem is None:
  313. return
  314. pitem.widget.slot_showEditDialog(True)
  315. # -----------------------------------------------------------------
  316. @pyqtSlot()
  317. def slot_pluginsEnable(self):
  318. if not gCarla.host.is_engine_running():
  319. return
  320. for i in range(self.fPluginCount):
  321. pitem = self.fPluginList[i]
  322. if pitem is None:
  323. break
  324. pitem.widget.setActive(True, True, True)
  325. @pyqtSlot()
  326. def slot_pluginsDisable(self):
  327. if not gCarla.host.is_engine_running():
  328. return
  329. for i in range(self.fPluginCount):
  330. pitem = self.fPluginList[i]
  331. if pitem is None:
  332. break
  333. pitem.widget.setActive(False, True, True)
  334. @pyqtSlot()
  335. def slot_pluginsVolume100(self):
  336. if not gCarla.host.is_engine_running():
  337. return
  338. for i in range(self.fPluginCount):
  339. pitem = self.fPluginList[i]
  340. if pitem is None:
  341. break
  342. pitem.widget.setInternalParameter(PLUGIN_CAN_VOLUME, 1.0)
  343. @pyqtSlot()
  344. def slot_pluginsMute(self):
  345. if not gCarla.host.is_engine_running():
  346. return
  347. for i in range(self.fPluginCount):
  348. pitem = self.fPluginList[i]
  349. if pitem is None:
  350. break
  351. pitem.widget.setInternalParameter(PLUGIN_CAN_VOLUME, 0.0)
  352. @pyqtSlot()
  353. def slot_pluginsWet100(self):
  354. if not gCarla.host.is_engine_running():
  355. return
  356. for i in range(self.fPluginCount):
  357. pitem = self.fPluginList[i]
  358. if pitem is None:
  359. break
  360. pitem.widget.setInternalParameter(PLUGIN_CAN_DRYWET, 1.0)
  361. @pyqtSlot()
  362. def slot_pluginsBypass(self):
  363. if not gCarla.host.is_engine_running():
  364. return
  365. for i in range(self.fPluginCount):
  366. pitem = self.fPluginList[i]
  367. if pitem is None:
  368. break
  369. pitem.widget.setInternalParameter(PLUGIN_CAN_DRYWET, 0.0)
  370. @pyqtSlot()
  371. def slot_pluginsCenter(self):
  372. if not gCarla.host.is_engine_running():
  373. return
  374. for i in range(self.fPluginCount):
  375. pitem = self.fPluginList[i]
  376. if pitem is None:
  377. break
  378. pitem.widget.setInternalParameter(PARAMETER_BALANCE_LEFT, -1.0)
  379. pitem.widget.setInternalParameter(PARAMETER_BALANCE_RIGHT, 1.0)
  380. pitem.widget.setInternalParameter(PARAMETER_PANNING, 0.0)
  381. # -----------------------------------------------------------------
  382. @pyqtSlot()
  383. def slot_configureCarla(self):
  384. if self.fParent is None or not self.fParent.openSettingsWindow(False, False):
  385. return
  386. self.fParent.loadSettings(False)
  387. # -----------------------------------------------------------------
  388. @pyqtSlot(int, int, float)
  389. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  390. if pluginId >= self.fPluginCount:
  391. return
  392. pitem = self.fPluginList[pluginId]
  393. if pitem is None:
  394. return
  395. pitem.widget.setParameterValue(index, value, True)
  396. @pyqtSlot(int, int, float)
  397. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  398. if pluginId >= self.fPluginCount:
  399. return
  400. pitem = self.fPluginList[pluginId]
  401. if pitem is None:
  402. return
  403. pitem.widget.setParameterDefault(index, value)
  404. @pyqtSlot(int, int, int)
  405. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  406. if pluginId >= self.fPluginCount:
  407. return
  408. pitem = self.fPluginList[pluginId]
  409. if pitem is None:
  410. return
  411. pitem.widget.setParameterMidiControl(index, cc)
  412. @pyqtSlot(int, int, int)
  413. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  414. if pluginId >= self.fPluginCount:
  415. return
  416. pitem = self.fPluginList[pluginId]
  417. if pitem is None:
  418. return
  419. pitem.widget.setParameterMidiChannel(index, channel)
  420. # -----------------------------------------------------------------
  421. @pyqtSlot(int, int)
  422. def slot_handleProgramChangedCallback(self, pluginId, index):
  423. if pluginId >= self.fPluginCount:
  424. return
  425. pitem = self.fPluginList[pluginId]
  426. if pitem is None:
  427. return
  428. pitem.widget.setProgram(index, True)
  429. @pyqtSlot(int, int)
  430. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  431. if pluginId >= self.fPluginCount:
  432. return
  433. pitem = self.fPluginList[pluginId]
  434. if pitem is None:
  435. return
  436. pitem.widget.setMidiProgram(index, True)
  437. # -----------------------------------------------------------------
  438. @pyqtSlot(int, int)
  439. def slot_handleUiStateChangedCallback(self, pluginId, state):
  440. if pluginId >= self.fPluginCount:
  441. return
  442. pitem = self.fPluginList[pluginId]
  443. if pitem is None:
  444. return
  445. pitem.widget.customUiStateChanged(state)
  446. # -----------------------------------------------------------------
  447. @pyqtSlot(int, int, int, int)
  448. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  449. if pluginId >= self.fPluginCount:
  450. return
  451. pitem = self.fPluginList[pluginId]
  452. if pitem is None:
  453. return
  454. pitem.widget.sendNoteOn(channel, note)
  455. @pyqtSlot(int, int, int)
  456. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  457. if pluginId >= self.fPluginCount:
  458. return
  459. pitem = self.fPluginList[pluginId]
  460. if pitem is None:
  461. return
  462. pitem.widget.sendNoteOff(channel, note)
  463. # -----------------------------------------------------------------
  464. @pyqtSlot(int)
  465. def slot_handleUpdateCallback(self, pluginId):
  466. if pluginId >= self.fPluginCount:
  467. return
  468. pitem = self.fPluginList[pluginId]
  469. if pitem is None:
  470. return
  471. pitem.widget.fEditDialog.updateInfo()
  472. @pyqtSlot(int)
  473. def slot_handleReloadInfoCallback(self, pluginId):
  474. if pluginId >= self.fPluginCount:
  475. return
  476. pitem = self.fPluginList[pluginId]
  477. if pitem is None:
  478. return
  479. pitem.widget.fEditDialog.reloadInfo()
  480. @pyqtSlot(int)
  481. def slot_handleReloadParametersCallback(self, pluginId):
  482. if pluginId >= self.fPluginCount:
  483. return
  484. pitem = self.fPluginList[pluginId]
  485. if pitem is None:
  486. return
  487. pitem.widget.fEditDialog.reloadParameters()
  488. @pyqtSlot(int)
  489. def slot_handleReloadProgramsCallback(self, pluginId):
  490. if pluginId >= self.fPluginCount:
  491. return
  492. pitem = self.fPluginList[pluginId]
  493. if pitem is None:
  494. return
  495. pitem.widget.fEditDialog.reloadPrograms()
  496. @pyqtSlot(int)
  497. def slot_handleReloadAllCallback(self, pluginId):
  498. if pluginId >= self.fPluginCount:
  499. return
  500. pitem = self.fPluginList[pluginId]
  501. if pitem is None:
  502. return
  503. self.fRack.setCurrentRow(-1)
  504. self.fCurrentRow = -1
  505. self.fLastSelectedItem = None
  506. pitem.reloadAll(pluginId)
  507. # -----------------------------------------------------------------
  508. def slot_currentRowChanged(self, row):
  509. self.fCurrentRow = row
  510. if self.fLastSelectedItem is not None:
  511. self.fLastSelectedItem.setSelected(False)
  512. if row < 0 or row >= self.fPluginCount or self.fPluginList[row] is None:
  513. self.fLastSelectedItem = None
  514. return
  515. pitem = self.fPluginList[row]
  516. pitem.widget.setSelected(True)
  517. self.fLastSelectedItem = pitem.widget
  518. # -----------------------------------------------------------------