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.

830 lines
26KB

  1. /*
  2. * DISTRHO Ildaeil Plugin
  3. * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. #include "CarlaNativePlugin.h"
  18. #include "DistrhoUI.hpp"
  19. #include "DistrhoPlugin.hpp"
  20. #include "PluginHostWindow.hpp"
  21. #include "extra/Thread.hpp"
  22. // IDE helper
  23. #include "DearImGui.hpp"
  24. #include <vector>
  25. START_NAMESPACE_DISTRHO
  26. // --------------------------------------------------------------------------------------------------------------------
  27. class IldaeilPlugin : public Plugin
  28. {
  29. public:
  30. const NativePluginDescriptor* fCarlaPluginDescriptor;
  31. NativePluginHandle fCarlaPluginHandle;
  32. NativeHostDescriptor fCarlaHostDescriptor;
  33. CarlaHostHandle fCarlaHostHandle;
  34. void* fUI;
  35. // ...
  36. };
  37. // --------------------------------------------------------------------------------------------------------------------
  38. void ildaeilParameterChangeForUI(void* ui, uint32_t index, float value);
  39. const char* ildaeilOpenFileForUI(void* ui, bool isDir, const char* title, const char* filter);
  40. // --------------------------------------------------------------------------------------------------------------------
  41. using namespace CarlaBackend;
  42. // shared resource pointer
  43. // carla_juce_init();
  44. class IldaeilUI : public UI,
  45. public Thread,
  46. public PluginHostWindow::Callbacks
  47. {
  48. static constexpr const uint kInitialWidth = 1220;
  49. static constexpr const uint kInitialHeight = 640;
  50. static constexpr const uint kGenericWidth = 300;
  51. static constexpr const uint kGenericHeight = 400;
  52. static constexpr const uint kExtraHeight = 35;
  53. enum {
  54. kDrawingInit,
  55. kDrawingError,
  56. kDrawingLoading,
  57. kDrawingPluginList,
  58. kDrawingPluginEmbedUI,
  59. kDrawingPluginGenericUI,
  60. kDrawingPluginPendingFromInit
  61. } fDrawingState;
  62. struct PluginInfoCache {
  63. char* name;
  64. char* label;
  65. PluginInfoCache()
  66. : name(nullptr),
  67. label(nullptr) {}
  68. ~PluginInfoCache()
  69. {
  70. std::free(name);
  71. std::free(label);
  72. }
  73. };
  74. struct PluginGenericUI {
  75. char* title;
  76. uint parameterCount;
  77. struct Parameter {
  78. char* name;
  79. char* format;
  80. uint32_t rindex;
  81. bool boolean, bvalue;
  82. float min, max;
  83. Parameter()
  84. : name(nullptr),
  85. format(nullptr),
  86. rindex(0),
  87. boolean(false),
  88. bvalue(false),
  89. min(0.0f),
  90. max(1.0f) {}
  91. ~Parameter()
  92. {
  93. std::free(name);
  94. std::free(format);
  95. }
  96. }* parameters;
  97. float* values;
  98. PluginGenericUI()
  99. : title(nullptr),
  100. parameterCount(0),
  101. parameters(nullptr),
  102. values(nullptr) {}
  103. ~PluginGenericUI()
  104. {
  105. std::free(title);
  106. delete[] parameters;
  107. delete[] values;
  108. }
  109. };
  110. IldaeilPlugin* const fPlugin;
  111. PluginHostWindow fPluginHostWindow;
  112. PluginType fPluginType;
  113. uint fPluginCount;
  114. uint fPluginSelected;
  115. bool fPluginScanningFinished;
  116. bool fPluginHasCustomUI;
  117. bool fPluginHasEmbedUI;
  118. PluginInfoCache* fPlugins;
  119. ScopedPointer<PluginGenericUI> fPluginGenericUI;
  120. bool fPluginSearchActive;
  121. char fPluginSearchString[0xff];
  122. public:
  123. IldaeilUI()
  124. : UI(kInitialWidth, kInitialHeight),
  125. Thread("IldaeilScanner"),
  126. fDrawingState(kDrawingInit),
  127. fPlugin((IldaeilPlugin*)getPluginInstancePointer()),
  128. fPluginHostWindow(getWindow(), this),
  129. fPluginType(PLUGIN_LV2),
  130. fPluginCount(0),
  131. fPluginSelected(0),
  132. fPluginScanningFinished(false),
  133. fPluginHasCustomUI(false),
  134. fPluginHasEmbedUI(false),
  135. fPlugins(nullptr),
  136. fPluginSearchActive(false)
  137. {
  138. if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr)
  139. {
  140. fDrawingState = kDrawingError;
  141. return;
  142. }
  143. std::strcpy(fPluginSearchString, "Search...");
  144. const double scaleFactor = getScaleFactor();
  145. if (d_isNotEqual(scaleFactor, 1.0))
  146. {
  147. setSize(kInitialWidth * scaleFactor, kInitialHeight * scaleFactor);
  148. fPluginHostWindow.setPositionAndSize(0, kExtraHeight * scaleFactor,
  149. kInitialWidth * scaleFactor, (kInitialHeight - kExtraHeight) * scaleFactor);
  150. }
  151. else
  152. {
  153. fPluginHostWindow.setPositionAndSize(0, kExtraHeight, kInitialWidth, kInitialHeight-kExtraHeight);
  154. }
  155. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  156. char winIdStr[24];
  157. std::snprintf(winIdStr, sizeof(winIdStr), "%lx", (ulong)getWindow().getNativeWindowHandle());
  158. carla_set_engine_option(handle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr);
  159. carla_set_engine_option(handle, ENGINE_OPTION_FRONTEND_UI_SCALE, getScaleFactor()*1000, nullptr);
  160. if (carla_get_current_plugin_count(handle) != 0)
  161. {
  162. const uint hints = carla_get_plugin_info(handle, 0)->hints;
  163. fDrawingState = kDrawingPluginPendingFromInit;
  164. fPluginHasCustomUI = hints & PLUGIN_HAS_CUSTOM_UI;
  165. fPluginHasEmbedUI = hints & PLUGIN_HAS_CUSTOM_EMBED_UI;
  166. }
  167. fPlugin->fUI = this;
  168. }
  169. ~IldaeilUI() override
  170. {
  171. if (fPlugin != nullptr && fPlugin->fCarlaHostHandle != nullptr)
  172. {
  173. fPlugin->fUI = nullptr;
  174. carla_set_engine_option(fPlugin->fCarlaHostHandle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0");
  175. }
  176. if (isThreadRunning())
  177. stopThread(-1);
  178. hidePluginUI();
  179. fPluginGenericUI = nullptr;
  180. delete[] fPlugins;
  181. }
  182. void changeParameterFromDSP(const uint32_t index, const float value)
  183. {
  184. if (PluginGenericUI* const ui = fPluginGenericUI)
  185. {
  186. for (uint32_t i=0; i < ui->parameterCount; ++i)
  187. {
  188. if (ui->parameters[i].rindex != index)
  189. continue;
  190. ui->values[i] = value;
  191. if (ui->parameters[i].boolean)
  192. ui->parameters[i].bvalue = value > ui->parameters[i].min;
  193. break;
  194. }
  195. }
  196. }
  197. const char* openFileFromDSP(const bool /*isDir*/, const char* const title, const char* const /*filter*/)
  198. {
  199. Window::FileBrowserOptions opts;
  200. opts.title = title;
  201. getWindow().openFileBrowser(opts);
  202. return nullptr;
  203. }
  204. void showPluginUI(const CarlaHostHandle handle)
  205. {
  206. const CarlaPluginInfo* const info = carla_get_plugin_info(handle, 0);
  207. if (info->hints & PLUGIN_HAS_CUSTOM_EMBED_UI)
  208. {
  209. fDrawingState = kDrawingPluginEmbedUI;
  210. fPluginHasCustomUI = true;
  211. fPluginHasEmbedUI = true;
  212. carla_embed_custom_ui(handle, 0, fPluginHostWindow.attachAndGetWindowHandle());
  213. }
  214. else
  215. {
  216. fDrawingState = kDrawingPluginGenericUI;
  217. fPluginHasCustomUI = info->hints & PLUGIN_HAS_CUSTOM_UI;
  218. fPluginHasEmbedUI = false;
  219. if (fPluginGenericUI == nullptr)
  220. createPluginGenericUI(handle, info);
  221. else
  222. updatePluginGenericUI(handle);
  223. const double scaleFactor = getScaleFactor();
  224. setSize(kGenericWidth * scaleFactor, (kGenericHeight + kExtraHeight) * scaleFactor);
  225. }
  226. repaint();
  227. }
  228. void hidePluginUI()
  229. {
  230. if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr)
  231. return;
  232. if (fDrawingState == kDrawingPluginGenericUI || fDrawingState == kDrawingPluginEmbedUI)
  233. carla_show_custom_ui(fPlugin->fCarlaHostHandle, 0, false);
  234. fPluginHostWindow.hide();
  235. }
  236. void createPluginGenericUI(const CarlaHostHandle handle, const CarlaPluginInfo* const info)
  237. {
  238. PluginGenericUI* const ui = new PluginGenericUI;
  239. String title(info->name);
  240. title += " by ";
  241. title += info->maker;
  242. ui->title = title.getAndReleaseBuffer();
  243. const uint32_t pcount = ui->parameterCount = carla_get_parameter_count(handle, 0);
  244. // make count of valid parameters
  245. for (uint32_t i=0; i < pcount; ++i)
  246. {
  247. const ParameterData* const pdata = carla_get_parameter_data(handle, 0, i);
  248. if (pdata->type != PARAMETER_INPUT ||
  249. (pdata->hints & PARAMETER_IS_ENABLED) == 0x0 ||
  250. (pdata->hints & PARAMETER_IS_READ_ONLY) != 0x0)
  251. {
  252. --ui->parameterCount;
  253. continue;
  254. }
  255. }
  256. ui->parameters = new PluginGenericUI::Parameter[ui->parameterCount];
  257. ui->values = new float[ui->parameterCount];
  258. // now safely fill in details
  259. for (uint32_t i=0, j=0; i < pcount; ++i)
  260. {
  261. const ParameterData* const pdata = carla_get_parameter_data(handle, 0, i);
  262. if (pdata->type != PARAMETER_INPUT ||
  263. (pdata->hints & PARAMETER_IS_ENABLED) == 0x0 ||
  264. (pdata->hints & PARAMETER_IS_READ_ONLY) != 0x0)
  265. continue;
  266. const CarlaParameterInfo* const pinfo = carla_get_parameter_info(handle, 0, i);
  267. const ::ParameterRanges* const pranges = carla_get_parameter_ranges(handle, 0, i);
  268. String format;
  269. if (pdata->hints & PARAMETER_IS_INTEGER)
  270. format = "%.0f ";
  271. else
  272. format = "%.3f ";
  273. format += pinfo->unit;
  274. PluginGenericUI::Parameter& param(ui->parameters[j]);
  275. param.name = strdup(pinfo->name);
  276. param.format = format.getAndReleaseBuffer();
  277. param.rindex = i;
  278. param.boolean = pdata->hints & PARAMETER_IS_BOOLEAN;
  279. param.min = pranges->min;
  280. param.max = pranges->max;
  281. ui->values[j] = carla_get_current_parameter_value(handle, 0, i);
  282. if (param.boolean)
  283. param.bvalue = ui->values[j] > param.min;
  284. else
  285. param.bvalue = false;
  286. ++j;
  287. }
  288. fPluginGenericUI = ui;
  289. }
  290. void updatePluginGenericUI(const CarlaHostHandle handle)
  291. {
  292. PluginGenericUI* const ui = fPluginGenericUI;
  293. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  294. for (uint32_t i=0; i < ui->parameterCount; ++i)
  295. {
  296. ui->values[i] = carla_get_current_parameter_value(handle, 0, ui->parameters[i].rindex);
  297. if (ui->parameters[i].boolean)
  298. ui->parameters[i].bvalue = ui->values[i] > ui->parameters[i].min;
  299. }
  300. }
  301. bool loadPlugin(const CarlaHostHandle handle, const char* const label)
  302. {
  303. if (carla_get_current_plugin_count(handle) != 0)
  304. {
  305. hidePluginUI();
  306. carla_replace_plugin(handle, 0);
  307. }
  308. if (carla_add_plugin(handle, BINARY_NATIVE, fPluginType, nullptr, nullptr,
  309. label, 0, 0x0, PLUGIN_OPTIONS_NULL))
  310. {
  311. fPluginGenericUI = nullptr;
  312. showPluginUI(handle);
  313. return true;
  314. }
  315. return false;
  316. }
  317. protected:
  318. void pluginWindowResized(uint width, uint height) override
  319. {
  320. setSize(width, height + kExtraHeight * getScaleFactor());
  321. }
  322. void uiIdle() override
  323. {
  324. switch (fDrawingState)
  325. {
  326. case kDrawingInit:
  327. fDrawingState = kDrawingLoading;
  328. startThread();
  329. repaint();
  330. break;
  331. case kDrawingPluginPendingFromInit:
  332. showPluginUI(fPlugin->fCarlaHostHandle);
  333. startThread();
  334. break;
  335. case kDrawingPluginEmbedUI:
  336. fPlugin->fCarlaPluginDescriptor->ui_idle(fPlugin->fCarlaPluginHandle);
  337. fPluginHostWindow.idle();
  338. break;
  339. case kDrawingPluginGenericUI:
  340. fPlugin->fCarlaPluginDescriptor->ui_idle(fPlugin->fCarlaPluginHandle);
  341. break;
  342. default:
  343. break;
  344. }
  345. }
  346. void uiFileBrowserSelected(const char* const filename) override
  347. {
  348. if (fPlugin != nullptr && fPlugin->fCarlaHostHandle != nullptr && filename != nullptr)
  349. carla_set_custom_data(fPlugin->fCarlaHostHandle, 0, CUSTOM_DATA_TYPE_STRING, "file", filename);
  350. }
  351. void run() override
  352. {
  353. if (const uint count = carla_get_cached_plugin_count(fPluginType, nullptr))
  354. {
  355. fPluginCount = 0;
  356. fPlugins = new PluginInfoCache[count];
  357. if (fDrawingState == kDrawingLoading)
  358. fDrawingState = kDrawingPluginList;
  359. for (uint i=0, j; i < count && ! shouldThreadExit(); ++i)
  360. {
  361. const CarlaCachedPluginInfo* const info = carla_get_cached_plugin_info(fPluginType, i);
  362. DISTRHO_SAFE_ASSERT_CONTINUE(info != nullptr);
  363. if (! info->valid)
  364. continue;
  365. #if DISTRHO_PLUGIN_IS_SYNTH
  366. if (info->midiIns != 1 || info->audioOuts != 2)
  367. continue;
  368. if ((info->hints & PLUGIN_IS_SYNTH) == 0x0)
  369. continue;
  370. #elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  371. if (info->midiIns != 1 || info->midiOuts != 1)
  372. continue;
  373. if (info->audioIns != 0 || info->audioOuts != 0)
  374. continue;
  375. #else
  376. if (info->audioIns != 2 || info->audioOuts != 2)
  377. continue;
  378. #endif
  379. j = fPluginCount;
  380. fPlugins[j].name = strdup(info->name);
  381. fPlugins[j].label = strdup(info->label);
  382. ++fPluginCount;
  383. }
  384. }
  385. if (! shouldThreadExit())
  386. fPluginScanningFinished = true;
  387. }
  388. void onImGuiDisplay() override
  389. {
  390. switch (fDrawingState)
  391. {
  392. case kDrawingInit:
  393. case kDrawingLoading:
  394. case kDrawingPluginPendingFromInit:
  395. drawLoading();
  396. break;
  397. case kDrawingPluginList:
  398. drawPluginList();
  399. break;
  400. case kDrawingError:
  401. // TODO display error message
  402. break;
  403. case kDrawingPluginGenericUI:
  404. drawGenericUI();
  405. // fall-through
  406. case kDrawingPluginEmbedUI:
  407. drawTopBar();
  408. break;
  409. }
  410. }
  411. void drawTopBar()
  412. {
  413. ImGui::SetNextWindowPos(ImVec2(0, 0));
  414. ImGui::SetNextWindowSize(ImVec2(getWidth(), kExtraHeight * getScaleFactor()));
  415. if (ImGui::Begin("Current Plugin", nullptr, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize))
  416. {
  417. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  418. if (ImGui::Button("Pick Another..."))
  419. {
  420. hidePluginUI();
  421. fDrawingState = kDrawingPluginList;
  422. const double scaleFactor = getScaleFactor();
  423. setSize(kInitialWidth * scaleFactor, kInitialHeight * scaleFactor);
  424. }
  425. ImGui::SameLine();
  426. if (ImGui::Button("Reset"))
  427. {
  428. loadPlugin(handle, carla_get_plugin_info(handle, 0)->label);
  429. }
  430. if (fDrawingState == kDrawingPluginGenericUI && fPluginHasCustomUI)
  431. {
  432. ImGui::SameLine();
  433. if (ImGui::Button("Show Custom GUI"))
  434. {
  435. if (fPluginHasEmbedUI)
  436. {
  437. fDrawingState = kDrawingPluginEmbedUI;
  438. carla_embed_custom_ui(handle, 0, fPluginHostWindow.attachAndGetWindowHandle());
  439. }
  440. else
  441. {
  442. carla_show_custom_ui(handle, 0, true);
  443. }
  444. }
  445. }
  446. if (fDrawingState == kDrawingPluginEmbedUI)
  447. {
  448. ImGui::SameLine();
  449. if (ImGui::Button("Show Generic GUI"))
  450. {
  451. hidePluginUI();
  452. fDrawingState = kDrawingPluginGenericUI;
  453. if (fPluginGenericUI == nullptr)
  454. createPluginGenericUI(handle, carla_get_plugin_info(handle, 0));
  455. else
  456. updatePluginGenericUI(handle);
  457. const double scaleFactor = getScaleFactor();
  458. setSize(std::max(getWidth(), static_cast<uint>(kGenericWidth * scaleFactor + 0.5)),
  459. (kGenericHeight + kExtraHeight) * scaleFactor);
  460. }
  461. }
  462. }
  463. ImGui::End();
  464. }
  465. void setupMainWindowPos()
  466. {
  467. float y = 0;
  468. float height = getHeight();
  469. if (fDrawingState == kDrawingPluginGenericUI)
  470. {
  471. y = (kExtraHeight - 1) * getScaleFactor();
  472. height -= y;
  473. }
  474. ImGui::SetNextWindowPos(ImVec2(0, y));
  475. ImGui::SetNextWindowSize(ImVec2(getWidth(), height));
  476. }
  477. void drawGenericUI()
  478. {
  479. setupMainWindowPos();
  480. PluginGenericUI* const ui = fPluginGenericUI;
  481. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  482. if (ImGui::Begin(ui->title, nullptr, ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoCollapse))
  483. {
  484. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  485. for (uint32_t i=0; i < ui->parameterCount; ++i)
  486. {
  487. PluginGenericUI::Parameter& param(ui->parameters[i]);
  488. if (param.boolean)
  489. {
  490. if (ImGui::Checkbox(param.name, &ui->parameters[i].bvalue))
  491. {
  492. if (ImGui::IsItemActivated())
  493. {
  494. carla_set_parameter_touch(handle, 0, param.rindex, true);
  495. // editParameter(0, true);
  496. }
  497. ui->values[i] = ui->parameters[i].bvalue ? ui->parameters[i].max : ui->parameters[i].min;
  498. carla_set_parameter_value(handle, 0, param.rindex, ui->values[i]);
  499. // setParameterValue(0, ui->values[i]);
  500. }
  501. }
  502. else
  503. {
  504. if (ImGui::SliderFloat(param.name, &ui->values[i], param.min, param.max, param.format))
  505. {
  506. if (ImGui::IsItemActivated())
  507. {
  508. carla_set_parameter_touch(handle, 0, param.rindex, true);
  509. // editParameter(0, true);
  510. }
  511. carla_set_parameter_value(handle, 0, param.rindex, ui->values[i]);
  512. // setParameterValue(0, ui->values[i]);
  513. }
  514. }
  515. if (ImGui::IsItemDeactivated())
  516. {
  517. carla_set_parameter_touch(handle, 0, param.rindex, false);
  518. // editParameter(0, false);
  519. }
  520. }
  521. }
  522. ImGui::End();
  523. }
  524. void drawLoading()
  525. {
  526. setupMainWindowPos();
  527. if (ImGui::Begin("Plugin List", nullptr, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize))
  528. {
  529. ImGui::TextUnformatted("Loading...", nullptr);
  530. }
  531. ImGui::End();
  532. }
  533. void drawPluginList()
  534. {
  535. setupMainWindowPos();
  536. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  537. if (ImGui::Begin("Plugin List", nullptr, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize))
  538. {
  539. if (ImGui::InputText("", fPluginSearchString, sizeof(fPluginSearchString)-1, ImGuiInputTextFlags_CharsNoBlank|ImGuiInputTextFlags_AutoSelectAll))
  540. fPluginSearchActive = true;
  541. ImGui::BeginDisabled(!fPluginScanningFinished);
  542. if (ImGui::Button("Load Plugin"))
  543. {
  544. do {
  545. const PluginInfoCache& info(fPlugins[fPluginSelected]);
  546. const char* label = nullptr;
  547. switch (fPluginType)
  548. {
  549. case PLUGIN_INTERNAL:
  550. label = info.label;
  551. break;
  552. case PLUGIN_LV2: {
  553. const char* const slash = std::strchr(info.label, DISTRHO_OS_SEP);
  554. DISTRHO_SAFE_ASSERT_BREAK(slash != nullptr);
  555. label = slash+1;
  556. break;
  557. }
  558. default:
  559. break;
  560. }
  561. DISTRHO_SAFE_ASSERT_BREAK(label != nullptr);
  562. d_stdout("Loading %s...", info.name);
  563. if (loadPlugin(handle, label))
  564. {
  565. ImGui::EndDisabled();
  566. ImGui::End();
  567. return;
  568. }
  569. } while (false);
  570. }
  571. if (carla_get_current_plugin_count(handle) != 0)
  572. {
  573. ImGui::SameLine();
  574. if (ImGui::Button("Cancel"))
  575. {
  576. showPluginUI(handle);
  577. }
  578. }
  579. ImGui::EndDisabled();
  580. if (ImGui::BeginChild("pluginlistwindow"))
  581. {
  582. if (ImGui::BeginTable("pluginlist",
  583. fPluginType == PLUGIN_LV2 ? 3 : 2,
  584. ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_NoClip))
  585. {
  586. const char* const search = fPluginSearchActive && fPluginSearchString[0] != '\0' ? fPluginSearchString : nullptr;
  587. switch (fPluginType)
  588. {
  589. case PLUGIN_INTERNAL:
  590. ImGui::TableSetupColumn("Name");
  591. ImGui::TableSetupColumn("Label");
  592. ImGui::TableHeadersRow();
  593. break;
  594. case PLUGIN_LV2:
  595. ImGui::TableSetupColumn("Name");
  596. ImGui::TableSetupColumn("Bundle");
  597. ImGui::TableSetupColumn("URI");
  598. ImGui::TableHeadersRow();
  599. break;
  600. default:
  601. break;
  602. }
  603. for (uint i=0; i<fPluginCount; ++i)
  604. {
  605. const PluginInfoCache& info(fPlugins[i]);
  606. if (search != nullptr && strcasestr(info.name, search) == nullptr)
  607. continue;
  608. bool selected = fPluginSelected == i;
  609. switch (fPluginType)
  610. {
  611. case PLUGIN_INTERNAL:
  612. ImGui::TableNextRow();
  613. ImGui::TableSetColumnIndex(0);
  614. ImGui::Selectable(info.name, &selected);
  615. ImGui::TableSetColumnIndex(1);
  616. ImGui::Selectable(info.label, &selected);
  617. break;
  618. case PLUGIN_LV2: {
  619. const char* const slash = std::strchr(info.label, DISTRHO_OS_SEP);
  620. DISTRHO_SAFE_ASSERT_CONTINUE(slash != nullptr);
  621. ImGui::TableNextRow();
  622. ImGui::TableSetColumnIndex(0);
  623. ImGui::Selectable(info.name, &selected);
  624. ImGui::TableSetColumnIndex(1);
  625. ImGui::Selectable(slash+1, &selected);
  626. ImGui::TableSetColumnIndex(2);
  627. ImGui::TextUnformatted(info.label, slash);
  628. break;
  629. }
  630. default:
  631. break;
  632. }
  633. if (selected)
  634. fPluginSelected = i;
  635. }
  636. ImGui::EndTable();
  637. }
  638. ImGui::EndChild();
  639. }
  640. }
  641. ImGui::End();
  642. }
  643. protected:
  644. /* --------------------------------------------------------------------------------------------------------
  645. * DSP/Plugin Callbacks */
  646. void parameterChanged(uint32_t, float) override
  647. {
  648. }
  649. void stateChanged(const char* const key, const char* const) override
  650. {
  651. if (std::strcmp(key, "project") == 0)
  652. hidePluginUI();
  653. }
  654. // -------------------------------------------------------------------------------------------------------
  655. private:
  656. /**
  657. Set our UI class as non-copyable and add a leak detector just in case.
  658. */
  659. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(IldaeilUI)
  660. };
  661. // --------------------------------------------------------------------------------------------------------------------
  662. void ildaeilParameterChangeForUI(void* const ui, const uint32_t index, const float value)
  663. {
  664. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  665. static_cast<IldaeilUI*>(ui)->changeParameterFromDSP(index, value);
  666. }
  667. const char* ildaeilOpenFileForUI(void* const ui, const bool isDir, const char* const title, const char* const filter)
  668. {
  669. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, nullptr);
  670. return static_cast<IldaeilUI*>(ui)->openFileFromDSP(isDir, title, filter);
  671. }
  672. /* --------------------------------------------------------------------------------------------------------------------
  673. * UI entry point, called by DPF to create a new UI instance. */
  674. UI* createUI()
  675. {
  676. return new IldaeilUI();
  677. }
  678. // --------------------------------------------------------------------------------------------------------------------
  679. END_NAMESPACE_DISTRHO