Browse Source

Convert about dialog to C++

Signed-off-by: falkTX <falktx@falktx.com>
pull/2007/head
falkTX 3 months ago
parent
commit
263c6ddd37
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
16 changed files with 480 additions and 675 deletions
  1. +3
    -6
      source/frontend/Makefile
  2. +1
    -0
      source/frontend/carla_backend.py
  3. +130
    -17
      source/frontend/carla_frontend.cpp
  4. +14
    -3
      source/frontend/carla_frontend.h
  5. +12
    -2
      source/frontend/carla_frontend.py
  6. +4
    -1
      source/frontend/carla_host.py
  7. +0
    -133
      source/frontend/carla_widgets.py
  8. +0
    -19
      source/frontend/dialogs/__init__.py
  9. +165
    -0
      source/frontend/dialogs/aboutdialog.cpp
  10. +39
    -0
      source/frontend/dialogs/aboutdialog.hpp
  11. +4
    -4
      source/frontend/dialogs/aboutdialog.ui
  12. +64
    -145
      source/frontend/dialogs/jackappdialog.cpp
  13. +15
    -19
      source/frontend/dialogs/jackappdialog.hpp
  14. +0
    -221
      source/frontend/dialogs/jackappdialog.py
  15. +0
    -100
      source/frontend/pluginlist/pluginlistdialog.cpp
  16. +29
    -5
      source/frontend/pluginlist/pluginlistdialog.hpp

+ 3
- 6
source/frontend/Makefile View File

@@ -35,9 +35,9 @@ NON_STATIC_LINK_FLAGS = $(LINK_FLAGS)
endif

ifeq ($(WINDOWS),true)
QT_LINK_FLAGS += -L$(BINDIR) $(BINDIR)/libcarla_utils.dll
QT_LINK_FLAGS += -L$(BINDIR) $(BINDIR)/libcarla_standalone2.dll $(BINDIR)/libcarla_utils.dll
else
QT_LINK_FLAGS += -L$(BINDIR) -lcarla_utils
QT_LINK_FLAGS += -L$(BINDIR) -lcarla_standalone2 -lcarla_utils
endif

ifeq ($(MACOS),true)
@@ -58,6 +58,7 @@ QMs = $(patsubst %,translations/carla_%.qm,$(I18N_LANGUAGES))

CPP_FILES = \
carla_frontend.cpp \
dialogs/aboutdialog.cpp \
dialogs/jackappdialog.cpp \
pluginlist/pluginlistdialog.cpp

@@ -89,7 +90,6 @@ RES = \
$(BINDIR)/resources/notes-ui \
$(BINDIR)/resources/xycontroller-ui \
$(BINDIR)/resources/resources_rc.py \
$(BINDIR)/resources/ui_carla_about.py \
$(BINDIR)/resources/ui_carla_edit.py \
$(BINDIR)/resources/ui_carla_host.py \
$(BINDIR)/resources/ui_carla_osc_connect.py \
@@ -122,11 +122,8 @@ PLUGINLIST_UI_FILES = $(wildcard pluginlist/*.ui)
UIs = $(DIALOG_UI_FILES:dialogs/%.ui=dialogs/ui_%.h)
UIs += $(PLUGINLIST_UI_FILES:pluginlist/%.ui=pluginlist/ui_%.h)

UIs += $(DIALOG_UI_FILES:%.ui=%_ui.py)

# old stuff, not yet converted
UIs += \
ui_carla_about.py \
ui_carla_edit.py \
ui_carla_host.py \
ui_carla_osc_connect.py \


+ 1
- 0
source/frontend/carla_backend.py View File

@@ -1531,6 +1531,7 @@ class CarlaHostMeta():
self.isPlugin = False
self.isRemote = False
self.nsmOK = False
self.handle = None

# settings
self.processMode = ENGINE_PROCESS_MODE_PATCHBAY


+ 130
- 17
source/frontend/carla_frontend.cpp View File

@@ -1,23 +1,136 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#include "carla_frontend.h"

// -------------------------------------------------------------------------------------------------------------------
// common files

#include "utils/qsafesettings.cpp"

// -------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// aboutdialog

#include "dialogs/aboutdialog.hpp"

void carla_frontend_createAndExecAboutDialog(QWidget* const parent,
const CarlaHostHandle hostHandle,
const bool isControl,
const bool isPlugin)
{
AboutDialog(parent, hostHandle, isControl, isPlugin).exec();
}

// --------------------------------------------------------------------------------------------------------------------
// jackappdialog

#include "dialogs/jackappdialog.hpp"
#include "distrho/extra/String.hpp"

const JackAppDialogResults*
carla_frontend_createAndExecJackAppDialog(QWidget* const parent, const char* const projectFilename)
{
JackAppDialog gui(parent, projectFilename);

if (gui.exec())
{
static JackAppDialogResults ret = {};
static String retCommand;
static String retName;
static String retLabelSetup;

const JackAppDialog::CommandAndFlags cafs = gui.getCommandAndFlags();
retCommand = cafs.command.toUtf8().constData();
retName = cafs.name.toUtf8().constData();
retLabelSetup = cafs.labelSetup.toUtf8().constData();

ret.command = retCommand;
ret.name = retName;
ret.labelSetup = retLabelSetup;

return &ret;
}

return nullptr;
}

// --------------------------------------------------------------------------------------------------------------------
// pluginlistdialog

#include "pluginlist/pluginlistdialog.hpp"
#include "CarlaUtils.h"

PluginListDialog*
carla_frontend_createPluginListDialog(QWidget* const parent, const HostSettings* const hostSettings)
{
return new PluginListDialog(parent, hostSettings);
}

void
carla_frontend_destroyPluginListDialog(PluginListDialog* const dialog)
{
dialog->close();
delete dialog;
}

void
carla_frontend_setPluginListDialogPath(PluginListDialog* const dialog, const int ptype, const char* const path)
{
dialog->setPluginPath(static_cast<PluginType>(ptype), path);
}

const PluginListDialogResults*
carla_frontend_execPluginListDialog(PluginListDialog* const dialog)
{
if (dialog->exec())
{
static PluginListDialogResults ret;
static String category;
static String filename;
static String name;
static String label;
static String maker;

const PluginInfo& plugin(dialog->getSelectedPluginInfo());

category = plugin.category.toUtf8();
filename = plugin.filename.toUtf8();
name = plugin.name.toUtf8();
label = plugin.label.toUtf8();
maker = plugin.maker.toUtf8();

ret.build = plugin.build;
ret.type = plugin.type;
ret.hints = plugin.hints;
ret.category = category;
ret.filename = filename;
ret.name = name;
ret.label = label;
ret.maker = maker;
ret.uniqueId = plugin.uniqueId;
ret.audioIns = plugin.audioIns;
ret.audioOuts = plugin.audioOuts;
ret.cvIns = plugin.cvIns;
ret.cvOuts = plugin.cvOuts;
ret.midiIns = plugin.midiIns;
ret.midiOuts = plugin.midiOuts;
ret.parameterIns = plugin.parameterIns;
ret.parameterOuts = plugin.parameterOuts;

return &ret;
}

return nullptr;
}

// --------------------------------------------------------------------------------------------------------------------

// const PluginListDialogResults*
// carla_frontend_createAndExecPluginListDialog(void* const parent, const HostSettings* const hostSettings)
// {
// PluginListDialog gui(reinterpret_cast<QWidget*>(parent), hostSettings);
//
// return carla_frontend_execPluginListDialog(&gui);
// }

// --------------------------------------------------------------------------------------------------------------------

source/frontend/CarlaFrontend.h → source/frontend/carla_frontend.h View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once
@@ -10,6 +10,8 @@ using CARLA_BACKEND_NAMESPACE::PluginType;
extern "C" {
#endif

typedef struct _CarlaHostHandle* CarlaHostHandle;

// --------------------------------------------------------------------------------------------------------------------

typedef struct {
@@ -49,17 +51,26 @@ typedef struct {

#ifdef __cplusplus
class PluginListDialog;
class QWidget;
#else
struct PluginListDialog;
struct QWidget;
#endif

// --------------------------------------------------------------------------------------------------------------------

CARLA_PLUGIN_EXPORT void
carla_frontend_createAndExecAboutDialog(QWidget* parent, CarlaHostHandle hostHandle, bool isControl, bool isPlugin);

// --------------------------------------------------------------------------------------------------------------------

CARLA_PLUGIN_EXPORT const JackAppDialogResults*
carla_frontend_createAndExecJackAppDialog(void* parent, const char* projectFilename);
carla_frontend_createAndExecJackAppDialog(QWidget* parent, const char* projectFilename);

// --------------------------------------------------------------------------------------------------------------------

CARLA_PLUGIN_EXPORT PluginListDialog*
carla_frontend_createPluginListDialog(void* parent, const HostSettings* hostSettings);
carla_frontend_createPluginListDialog(QWidget* parent, const HostSettings* hostSettings);

CARLA_PLUGIN_EXPORT void
carla_frontend_destroyPluginListDialog(PluginListDialog* dialog);

+ 12
- 2
source/frontend/carla_frontend.py View File

@@ -81,6 +81,9 @@ class CarlaFrontendLib():
def __init__(self, filename):
self.lib = cdll.LoadLibrary(filename)

self.lib.carla_frontend_createAndExecAboutDialog.argtypes = (c_void_p, c_void_p, c_bool, c_bool)
self.lib.carla_frontend_createAndExecAboutDialog.restype = None

self.lib.carla_frontend_createAndExecJackAppDialog.argtypes = (c_void_p, c_char_p)
self.lib.carla_frontend_createAndExecJackAppDialog.restype = POINTER(JackApplicationDialogResults)

@@ -98,9 +101,16 @@ class CarlaFrontendLib():

# --------------------------------------------------------------------------------------------------------

def createAndExecAboutDialog(self, parent, hostHandle, isControl, isPlugin):
return structToDictOrNull(self.lib.carla_frontend_createAndExecAboutDialog(unwrapinstance(parent),
hostHandle,
isControl,
isPlugin))

def createAndExecJackAppDialog(self, parent, projectFilename):
return structToDictOrNull(self.lib.carla_frontend_createAndExecJackAppDialog(unwrapinstance(parent),
projectFilename.encode("utf-8")))
return structToDictOrNull(
self.lib.carla_frontend_createAndExecJackAppDialog(unwrapinstance(parent),
projectFilename.encode("utf-8")))

def createPluginListDialog(self, parent, hostSettings):
hostSettingsC = HostSettings()


+ 4
- 1
source/frontend/carla_host.py View File

@@ -2215,7 +2215,10 @@ class HostWindow(QMainWindow):

@pyqtSlot()
def slot_aboutCarla(self):
CarlaAboutW(self.fParentOrSelf, self.host).exec_()
gCarla.felib.createAndExecAboutDialog(self.fParentOrSelf,
self.host.handle,
self.host.isControl,
self.host.isPlugin)

@pyqtSlot()
def slot_aboutQt(self):


+ 0
- 133
source/frontend/carla_widgets.py View File

@@ -42,7 +42,6 @@ elif qt_config == 6:
# ------------------------------------------------------------------------------------------------------------
# Imports (Custom)

import ui_carla_about
import ui_carla_edit
import ui_carla_parameter

@@ -113,135 +112,6 @@ ICON_STATE_WAIT = 2 # nothing, sets as off
ICON_STATE_OFF = 1 # turns off, sets as null
ICON_STATE_NULL = 0 # nothing

# ------------------------------------------------------------------------------------------------------------
# Carla About dialog

class CarlaAboutW(QDialog):
def __init__(self, parent, host):
QDialog.__init__(self, parent)
self.ui = ui_carla_about.Ui_CarlaAboutW()
self.ui.setupUi(self)

if host.isControl:
extraInfo = " - <b>%s</b>" % self.tr("OSC Bridge Version")
elif host.isPlugin:
extraInfo = " - <b>%s</b>" % self.tr("Plugin Version")
else:
extraInfo = ""

self.ui.l_about.setText(self.tr(""
"<br>Version %s"
"<br>Carla is a fully-featured audio plugin host%s.<br>"
"<br>Copyright (C) 2011-2025 falkTX<br>"
"" % (CARLA_VERSION_STRING, extraInfo)))

if self.ui.about.palette().color(QPalette.Background).blackF() < 0.5:
self.ui.l_icons.setPixmap(QPixmap(":/bitmaps/carla_about_black.png"))
self.ui.ico_example_edit.setPixmap(QPixmap(":/bitmaps/button_file-black.png"))
self.ui.ico_example_file.setPixmap(QPixmap(":/scalable/button_edit-black.svg"))
self.ui.ico_example_gui.setPixmap(QPixmap(":/bitmaps/button_gui-black.png"))

if host.isControl:
self.ui.l_extended.hide()
self.ui.tabWidget.removeTab(3)
self.ui.tabWidget.removeTab(2)

self.ui.l_extended.setText(gCarla.utils.get_complete_license_text())

if host.is_engine_running() and not host.isControl:
self.ui.le_osc_url_tcp.setText(host.get_host_osc_url_tcp())
self.ui.le_osc_url_udp.setText(host.get_host_osc_url_udp())
else:
self.ui.le_osc_url_tcp.setText(self.tr("(Engine not running)"))
self.ui.le_osc_url_udp.setText(self.tr("(Engine not running)"))

# pylint: disable=line-too-long
self.ui.l_osc_cmds.setText("<table>"
"<tr><td>" "/set_active" "&nbsp;</td><td>&lt;i-value&gt;</td></tr>"
"<tr><td>" "/set_drywet" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_volume" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_balance_left" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_balance_right" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_panning" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_parameter_value" "&nbsp;</td><td>&lt;i-index&gt; &lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_parameter_midi_cc" "&nbsp;</td><td>&lt;i-index&gt; &lt;i-cc&gt;</td></tr>"
"<tr><td>" "/set_parameter_midi_channel" "&nbsp;</td><td>&lt;i-index&gt; &lt;i-channel&gt;</td></tr>"
"<tr><td>" "/set_program" "&nbsp;</td><td>&lt;i-index&gt;</td></tr>"
"<tr><td>" "/set_midi_program" "&nbsp;</td><td>&lt;i-index&gt;</td></tr>"
"<tr><td>" "/note_on" "&nbsp;</td><td>&lt;i-channel&gt; &lt;i-note&gt; &lt;i-velo&gt;</td></tr>"
"<tr><td>" "/note_off" "&nbsp;</td><td>&lt;i-channel&gt; &lt;i-note</td></tr>"
"</table>"
)

self.ui.l_example.setText("/Carla/2/set_parameter_value 5 1.0")
self.ui.l_example_help.setText("<i>(as in this example, \"2\" is the plugin number and \"5\" the parameter)</i>")
# pylint: enable=line-too-long

self.ui.l_ladspa.setText(self.tr("Everything! (Including LRDF)"))
self.ui.l_dssi.setText(self.tr("Everything! (Including CustomData/Chunks)"))
self.ui.l_lv2.setText(self.tr("About 110&#37; complete (using custom extensions)<br/>"
"Implemented Feature/Extensions:"
"<ul>"
"<li>http://lv2plug.in/ns/ext/atom</li>"
"<li>http://lv2plug.in/ns/ext/buf-size</li>"
"<li>http://lv2plug.in/ns/ext/data-access</li>"
#"<li>http://lv2plug.in/ns/ext/dynmanifest</li>"
"<li>http://lv2plug.in/ns/ext/event</li>"
"<li>http://lv2plug.in/ns/ext/instance-access</li>"
"<li>http://lv2plug.in/ns/ext/log</li>"
"<li>http://lv2plug.in/ns/ext/midi</li>"
#"<li>http://lv2plug.in/ns/ext/morph</li>"
"<li>http://lv2plug.in/ns/ext/options</li>"
"<li>http://lv2plug.in/ns/ext/parameters</li>"
#"<li>http://lv2plug.in/ns/ext/patch</li>"
"<li>http://lv2plug.in/ns/ext/port-props</li>"
"<li>http://lv2plug.in/ns/ext/presets</li>"
"<li>http://lv2plug.in/ns/ext/resize-port</li>"
"<li>http://lv2plug.in/ns/ext/state</li>"
"<li>http://lv2plug.in/ns/ext/time</li>"
"<li>http://lv2plug.in/ns/ext/uri-map</li>"
"<li>http://lv2plug.in/ns/ext/urid</li>"
"<li>http://lv2plug.in/ns/ext/worker</li>"
"<li>http://lv2plug.in/ns/extensions/ui</li>"
"<li>http://lv2plug.in/ns/extensions/units</li>"
"<li>http://home.gna.org/lv2dynparam/rtmempool/v1</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/external-ui</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/programs</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/props</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/rtmempool</li>"
"<li>http://ll-plugins.nongnu.org/lv2/ext/midimap</li>"
"<li>http://ll-plugins.nongnu.org/lv2/ext/miditype</li>"
"</ul>"))

self.ui.l_vst2.setText(self.tr("About 85&#37; complete (missing vst bank/presets and some minor stuff)"))
self.ui.l_vst3.setText(self.tr("About 66&#37; complete"))

if CARLA_OS_MAC:
self.ui.l_au.setText(self.tr("About 20&#37; complete"))
else:
self.ui.line_vst3.hide()
self.ui.l_au.hide()
self.ui.lid_au.hide()

# 3rd tab is usually longer than the 1st
# adjust appropriately
self.ui.tabWidget.setCurrentIndex(2)
self.adjustSize()
self.ui.tabWidget.setCurrentIndex(0)

self.setFixedSize(self.size())

flags = self.windowFlags()
flags &= ~Qt.WindowContextHelpButtonHint

if CARLA_OS_WIN:
flags |= Qt.MSWindowsFixedSizeDialogHint

self.setWindowFlags(flags)

if CARLA_OS_MAC:
self.setWindowModality(Qt.WindowModal)

# ------------------------------------------------------------------------------------------------------------
# Plugin Parameter

@@ -1852,9 +1722,6 @@ if __name__ == '__main__':
_host.add_plugin(BINARY_NATIVE, PLUGIN_DSSI, "/usr/lib/dssi/karplong.so", "karplong", "karplong", 0, None, 0x0)
_host.set_active(0, True)

gui1 = CarlaAboutW(None, _host)
gui1.show()

gui2 = PluginEdit(None, _host, 0)
gui2.testTimer()
gui2.show()


+ 0
- 19
source/frontend/dialogs/__init__.py View File

@@ -1,19 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Carla plugin host
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the doc/GPL.txt file.

from .jackappdialog import JackAppDialog

+ 165
- 0
source/frontend/dialogs/aboutdialog.cpp View File

@@ -0,0 +1,165 @@
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#include "aboutdialog.hpp"

#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
# pragma clang diagnostic ignored "-Wdeprecated-register"
#elif defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wclass-memaccess"
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif

// #include <QtCore/QFileInfo>
// #include <QtCore/QVector>
// #include <QtWidgets/QPushButton>

#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic pop
#endif

#include "CarlaHost.h"

// --------------------------------------------------------------------------------------------------------------------
// About Dialog

AboutDialog::AboutDialog(QWidget* const parent,
const CarlaHostHandle hostHandle,
const bool isControl,
const bool isPlugin)
: QDialog(parent)
{
ui.setupUi(this);

QString extraInfo;
if (isControl)
extraInfo = QString(" - <b>%1</b>").arg(tr("OSC Bridge Version"));
else if (isPlugin)
extraInfo = QString(" - <b>%1</b>").arg(tr("Plugin Version"));

ui.l_about->setText(tr(""
"<br>Version %1"
"<br>Carla is a fully-featured audio plugin host%2.<br>"
"<br>Copyright (C) 2011-2025 falkTX<br>"
"").arg(CARLA_VERSION_STRING).arg(extraInfo));

if (ui.about->palette().color(QPalette::Background).blackF() < 0.5)
{
ui.l_icons->setPixmap(QPixmap(":/bitmaps/carla_about_black.png"));
ui.ico_example_edit->setPixmap(QPixmap(":/bitmaps/button_file-black.png"));
ui.ico_example_file->setPixmap(QPixmap(":/scalable/button_edit-black.svg"));
ui.ico_example_gui->setPixmap(QPixmap(":/bitmaps/button_gui-black.png"));
}

if (isControl || isPlugin)
{
ui.l_extended->hide();
ui.tabWidget->removeTab(3);
ui.tabWidget->removeTab(2);
}
else if (carla_is_engine_running(hostHandle))
{
ui.le_osc_url_tcp->setText(carla_get_host_osc_url_tcp(hostHandle));
ui.le_osc_url_udp->setText(carla_get_host_osc_url_udp(hostHandle));
}
else
{
ui.le_osc_url_tcp->setText(tr("(Engine not running)"));
ui.le_osc_url_udp->setText(tr("(Engine not running)"));
}

ui.l_extended->setText(carla_get_complete_license_text());

ui.l_osc_cmds->setText("<table>"
"<tr><td>" "/set_active" "&nbsp;</td><td>&lt;i-value&gt;</td></tr>"
"<tr><td>" "/set_drywet" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_volume" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_balance_left" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_balance_right" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_panning" "&nbsp;</td><td>&lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_parameter_value" "&nbsp;</td><td>&lt;i-index&gt; &lt;f-value&gt;</td></tr>"
"<tr><td>" "/set_parameter_midi_cc" "&nbsp;</td><td>&lt;i-index&gt; &lt;i-cc&gt;</td></tr>"
"<tr><td>" "/set_parameter_midi_channel" "&nbsp;</td><td>&lt;i-index&gt; &lt;i-channel&gt;</td></tr>"
"<tr><td>" "/set_program" "&nbsp;</td><td>&lt;i-index&gt;</td></tr>"
"<tr><td>" "/set_midi_program" "&nbsp;</td><td>&lt;i-index&gt;</td></tr>"
"<tr><td>" "/note_on" "&nbsp;</td><td>&lt;i-channel&gt; &lt;i-note&gt; &lt;i-velo&gt;</td></tr>"
"<tr><td>" "/note_off" "&nbsp;</td><td>&lt;i-channel&gt; &lt;i-note</td></tr>"
"</table>");

ui.l_example->setText("/Carla/2/set_parameter_value 5 1.0");
ui.l_example_help->setText("<i>(as in this example, \"2\" is the plugin number and \"5\" the parameter)</i>");

ui.l_ladspa->setText(tr("Everything! (Including LRDF)"));
ui.l_dssi->setText(tr("Everything! (Including CustomData/Chunks)"));
ui.l_lv2->setText(tr("About 110&#37; complete (using custom extensions)<br/>"
"Implemented Feature/Extensions:"
"<ul>"
"<li>http://lv2plug.in/ns/ext/atom</li>"
"<li>http://lv2plug.in/ns/ext/buf-size</li>"
"<li>http://lv2plug.in/ns/ext/data-access</li>"
// "<li>http://lv2plug.in/ns/ext/dynmanifest</li>"
"<li>http://lv2plug.in/ns/ext/event</li>"
"<li>http://lv2plug.in/ns/ext/instance-access</li>"
"<li>http://lv2plug.in/ns/ext/log</li>"
"<li>http://lv2plug.in/ns/ext/midi</li>"
// "<li>http://lv2plug.in/ns/ext/morph</li>"
"<li>http://lv2plug.in/ns/ext/options</li>"
"<li>http://lv2plug.in/ns/ext/parameters</li>"
// "<li>http://lv2plug.in/ns/ext/patch</li>"
// "<li>http://lv2plug.in/ns/ext/port-groups</li>"
"<li>http://lv2plug.in/ns/ext/port-props</li>"
"<li>http://lv2plug.in/ns/ext/presets</li>"
"<li>http://lv2plug.in/ns/ext/resize-port</li>"
"<li>http://lv2plug.in/ns/ext/state</li>"
"<li>http://lv2plug.in/ns/ext/time</li>"
"<li>http://lv2plug.in/ns/ext/uri-map</li>"
"<li>http://lv2plug.in/ns/ext/urid</li>"
"<li>http://lv2plug.in/ns/ext/worker</li>"
"<li>http://lv2plug.in/ns/extensions/ui</li>"
"<li>http://lv2plug.in/ns/extensions/units</li>"
"<li>http://home.gna.org/lv2dynparam/rtmempool/v1</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/external-ui</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/programs</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/props</li>"
"<li>http://kxstudio.sf.net/ns/lv2ext/rtmempool</li>"
"<li>http://ll-plugins.nongnu.org/lv2/ext/midimap</li>"
"<li>http://ll-plugins.nongnu.org/lv2/ext/miditype</li>"
"</ul>"));

ui.l_vst2->setText(tr("About 85&#37; complete (missing vst bank/presets and some minor stuff)"));
ui.l_vst3->setText(tr("About 66&#37; complete"));

#ifdef CARLA_OS_MAC
ui.l_au->setText(tr("About 20&#37; complete"));
#else
ui.line_vst3->hide();
ui.l_au->hide();
ui.lid_au->hide();
#endif

// 3rd tab is usually longer than the 1st, adjust appropriately
ui.tabWidget->setCurrentIndex(2);
adjustSize();
ui.tabWidget->setCurrentIndex(0);

setFixedSize(size());

Qt::WindowFlags flags = windowFlags();
flags &= ~Qt::WindowContextHelpButtonHint;
#ifdef CARLA_OS_WIN
flags |= ~Qt::MSWindowsFixedSizeDialogHint;
#endif
setWindowFlags(flags);

#ifdef CARLA_OS_MAC
if (parent != nullptr)
setWindowModality(Qt::WindowModal);
#endif
}

// --------------------------------------------------------------------------------------------------------------------

+ 39
- 0
source/frontend/dialogs/aboutdialog.hpp View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "carla_frontend.h"

#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
# pragma clang diagnostic ignored "-Wdeprecated-register"
#elif defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wclass-memaccess"
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif

#include "ui_aboutdialog.h"

#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic pop
#endif

// --------------------------------------------------------------------------------------------------------------------
// About Dialog

class AboutDialog : public QDialog
{
Ui_AboutDialog ui;

// ----------------------------------------------------------------------------------------------------------------

public:
explicit AboutDialog(QWidget* parent, CarlaHostHandle hostHandle, bool isControl, bool isPlugin);
};

// --------------------------------------------------------------------------------------------------------------------

resources/ui/carla_about.ui → source/frontend/dialogs/aboutdialog.ui View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CarlaAboutW</class>
<widget class="QDialog" name="CarlaAboutW">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -1425,7 +1425,7 @@ POSSIBILITY OF SUCH DAMAGES.
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CarlaAboutW</receiver>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@@ -1441,7 +1441,7 @@ POSSIBILITY OF SUCH DAMAGES.
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CarlaAboutW</receiver>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

+ 64
- 145
source/frontend/dialogs/jackappdialog.cpp View File

@@ -1,19 +1,5 @@
/*
* Carla plugin host
* Copyright (C) 2011-2023 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#include "jackappdialog.hpp"

@@ -27,9 +13,7 @@
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif

#include "ui_jackappdialog.h"
#include <QtCore/QFileInfo>
#include <QtCore/QVector>
#include <QtWidgets/QPushButton>

#ifdef __clang__
@@ -40,43 +24,29 @@

#include "qsafesettings.hpp"

#include "CarlaFrontend.h"
#include "CarlaLibJackHints.h"
#include "CarlaString.hpp"

// --------------------------------------------------------------------------------------------------------------------
// Jack Application Dialog

enum {
UI_SESSION_NONE = 0,
UI_SESSION_LADISH = 1,
UI_SESSION_NSM = 2,
};

struct JackAppDialog::Self {
Ui_JackAppDialog ui;
struct JackAppDialog::PrivateData {
const QString fProjectFilename;

Self(const char* const projectFilename)
PrivateData(const char* const projectFilename)
: fProjectFilename(projectFilename) {}

static Self& create(const char* const projectFilename)
{
Self* const self = new Self(projectFilename);
return *self;
}
};

JackAppDialog::JackAppDialog(QWidget* const parent, const char* const projectFilename)
: QDialog(parent),
self(Self::create(projectFilename))
p(new PrivateData(projectFilename))
{
self.ui.setupUi(this);
ui.setupUi(this);

// -------------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------
// UI setup

self.ui.group_error->setVisible(false);
ui.group_error->setVisible(false);

adjustSize();
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
@@ -86,34 +56,34 @@ JackAppDialog::JackAppDialog(QWidget* const parent, const char* const projectFil
setWindowModality(Qt::WindowModal);
#endif

// -------------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------
// Load settings

loadSettings();

// -------------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------
// Set-up connections

connect(this, &QDialog::finished,
this, &JackAppDialog::slot_saveSettings);
connect(self.ui.cb_session_mgr, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
connect(ui.cb_session_mgr, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &JackAppDialog::slot_sessionManagerChanged);
connect(self.ui.le_command, &QLineEdit::textChanged,
connect(ui.le_command, &QLineEdit::textChanged,
this, &JackAppDialog::slot_commandChanged);
}

JackAppDialog::~JackAppDialog()
{
delete &self;
delete p;
}

// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// public methods

JackAppDialog::CommandAndFlags JackAppDialog::getCommandAndFlags() const
{
const QString command = self.ui.le_command->text();
QString name = self.ui.le_name->text();
const QString command = ui.le_command->text();
QString name = ui.le_name->text();

if (name.isEmpty())
{
@@ -122,7 +92,7 @@ JackAppDialog::CommandAndFlags JackAppDialog::getCommandAndFlags() const
}

SessionManager smgr;
switch (self.ui.cb_session_mgr->currentIndex())
switch (ui.cb_session_mgr->currentIndex())
{
case UI_SESSION_LADISH:
smgr = LIBJACK_SESSION_MANAGER_LADISH;
@@ -136,28 +106,28 @@ JackAppDialog::CommandAndFlags JackAppDialog::getCommandAndFlags() const
}

uint flags = 0x0;
if (self.ui.cb_manage_window->isChecked())
if (ui.cb_manage_window->isChecked())
flags |= LIBJACK_FLAG_CONTROL_WINDOW;
if (self.ui.cb_capture_first_window->isChecked())
if (ui.cb_capture_first_window->isChecked())
flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW;
if (self.ui.cb_buffers_addition_mode->isChecked())
if (ui.cb_buffers_addition_mode->isChecked())
flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION;
if (self.ui.cb_out_midi_mixdown->isChecked())
if (ui.cb_out_midi_mixdown->isChecked())
flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN;
if (self.ui.cb_external_start->isChecked())
if (ui.cb_external_start->isChecked())
flags |= LIBJACK_FLAG_EXTERNAL_START;

const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + self.ui.sb_audio_ins->value()))
.arg(QChar('0' + self.ui.sb_audio_outs->value()))
.arg(QChar('0' + self.ui.sb_midi_ins->value()))
.arg(QChar('0' + self.ui.sb_midi_outs->value()))
const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + ui.sb_audio_ins->value()))
.arg(QChar('0' + ui.sb_audio_outs->value()))
.arg(QChar('0' + ui.sb_midi_ins->value()))
.arg(QChar('0' + ui.sb_midi_outs->value()))
.arg(QChar('0' + smgr))
.arg(QChar('0' + flags)));

return {command, name, labelSetup};
}

// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// private methods

void JackAppDialog::checkIfButtonBoxShouldBeEnabled(const int index, const QCarlaString& command)
@@ -166,28 +136,28 @@ void JackAppDialog::checkIfButtonBoxShouldBeEnabled(const int index, const QCarl
QCarlaString showErr;

// NSM applications must not be abstract or absolute paths, and must not contain arguments
if (enabled and index == UI_SESSION_NSM)
if (enabled && index == UI_SESSION_NSM)
{
if (QVector<QChar>{'.', '/'}.contains(command[0]))
if (command[0] == '.' || command[0] == '/')
showErr = tr("NSM applications cannot use abstract or absolute paths");
else if (command.contains(' ') or command.contains(';') or command.contains('&'))
showErr = tr("NSM applications cannot use CLI arguments");
else if (self.fProjectFilename.isEmpty())
else if (p->fProjectFilename.isEmpty())
showErr = tr("You need to save the current Carla project before NSM can be used");
}

if (showErr.isNotEmpty())
{
enabled = false;
self.ui.l_error->setText(showErr);
self.ui.group_error->setVisible(true);
ui.l_error->setText(showErr);
ui.group_error->setVisible(true);
}
else
{
self.ui.group_error->setVisible(false);
ui.group_error->setVisible(false);
}

if (QPushButton* const button = self.ui.buttonBox->button(QDialogButtonBox::Ok))
if (QPushButton* const button = ui.buttonBox->button(QDialogButtonBox::Ok))
button->setEnabled(enabled);
}

@@ -198,104 +168,53 @@ void JackAppDialog::loadSettings()
const QString smName = settings.valueString("SessionManager", "");

if (smName == "LADISH (SIGUSR1)")
self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH);
ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH);
else if (smName == "NSM")
self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM);
ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM);
else
self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE);
self.ui.le_command->setText(settings.valueString("Command", ""));
self.ui.le_name->setText(settings.valueString("Name", ""));
self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
self.ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2));
self.ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0));
self.ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0));
self.ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true));
self.ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false));
self.ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false));
checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(),
self.ui.le_command->text());
ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE);
ui.le_command->setText(settings.valueString("Command", ""));
ui.le_name->setText(settings.valueString("Name", ""));
ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2));
ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0));
ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0));
ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true));
ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false));
ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false));
checkIfButtonBoxShouldBeEnabled(ui.cb_session_mgr->currentIndex(),
ui.le_command->text());
}

// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// private slots

void JackAppDialog::slot_commandChanged(const QString& command)
{
checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), command);
checkIfButtonBoxShouldBeEnabled(ui.cb_session_mgr->currentIndex(), command);
}

void JackAppDialog::slot_sessionManagerChanged(const int index)
{
checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command->text());
checkIfButtonBoxShouldBeEnabled(index, ui.le_command->text());
}

void JackAppDialog::slot_saveSettings()
{
QSafeSettings settings("falkTX", "CarlaAddJackApp");
settings.setValue("Command", self.ui.le_command->text());
settings.setValue("Name", self.ui.le_name->text());
settings.setValue("SessionManager", self.ui.cb_session_mgr->currentText());
settings.setValue("NumAudioIns", self.ui.sb_audio_ins->value());
settings.setValue("NumAudioOuts", self.ui.sb_audio_outs->value());
settings.setValue("NumMidiIns", self.ui.sb_midi_ins->value());
settings.setValue("NumMidiOuts", self.ui.sb_midi_outs->value());
settings.setValue("ManageWindow", self.ui.cb_manage_window->isChecked());
settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window->isChecked());
settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown->isChecked());
settings.setValue("Command", ui.le_command->text());
settings.setValue("Name", ui.le_name->text());
settings.setValue("SessionManager", ui.cb_session_mgr->currentText());
settings.setValue("NumAudioIns", ui.sb_audio_ins->value());
settings.setValue("NumAudioOuts", ui.sb_audio_outs->value());
settings.setValue("NumMidiIns", ui.sb_midi_ins->value());
settings.setValue("NumMidiOuts", ui.sb_midi_outs->value());
settings.setValue("ManageWindow", ui.cb_manage_window->isChecked());
settings.setValue("CaptureFirstWindow", ui.cb_capture_first_window->isChecked());
settings.setValue("MidiOutMixdown", ui.cb_out_midi_mixdown->isChecked());
}

// --------------------------------------------------------------------------------------------------------------------

const JackAppDialogResults*
carla_frontend_createAndExecJackAppDialog(void* const parent, const char* const projectFilename)
{
JackAppDialog gui(reinterpret_cast<QWidget*>(parent), projectFilename);

if (gui.exec())
{
static JackAppDialogResults ret = {};
static CarlaString retCommand;
static CarlaString retName;
static CarlaString retLabelSetup;

const JackAppDialog::CommandAndFlags cafs = gui.getCommandAndFlags();
retCommand = cafs.command.toUtf8().constData();
retName = cafs.name.toUtf8().constData();
retLabelSetup = cafs.labelSetup.toUtf8().constData();

ret.command = retCommand;
ret.name = retName;
ret.labelSetup = retLabelSetup;

return &ret;
}

return nullptr;
}

#if 0
// --------------------------------------------------------------------------------------------------------------------
// Testing

#include "../utils/qsafesettings.cpp"

int main(int argc, char* argv[])
{
QApplication app(argc, argv);

if (JackAppDialogResults* const res = carla_frontend_createAndExecJackAppDialog(nullptr, ""))
{
printf("Results:\n");
printf("\tCommand: %s\n", res->command);
printf("\tName: %s\n", res->name);
printf("\tLabelSetup: %s\n", res->labelSetup);
}

return 0;
}
#endif

// --------------------------------------------------------------------------------------------------------------------

+ 15
- 19
source/frontend/dialogs/jackappdialog.hpp View File

@@ -1,22 +1,10 @@
/*
* Carla plugin host
* Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "carla_frontend.h"

#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
@@ -27,7 +15,7 @@
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif

#include <QtWidgets/QDialog>
#include "ui_jackappdialog.h"

#ifdef __clang__
# pragma clang diagnostic pop
@@ -42,8 +30,16 @@

class JackAppDialog : public QDialog
{
struct Self;
Self& self;
enum {
UI_SESSION_NONE,
UI_SESSION_LADISH,
UI_SESSION_NSM,
};

struct PrivateData;
PrivateData* const p;

Ui_JackAppDialog ui;

// ----------------------------------------------------------------------------------------------------------------



+ 0
- 221
source/frontend/dialogs/jackappdialog.py View File

@@ -1,221 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Carla plugin host
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the doc/GPL.txt file.

# ---------------------------------------------------------------------------------------------------------------------
# Imports (Global)

import os

from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QWidget

# ---------------------------------------------------------------------------------------------------------------------
# Imports (Carla)

from utils import QSafeSettings

# ---------------------------------------------------------------------------------------------------------------------
# Imports (Local)

from jackappdialog_ui import Ui_JackAppDialog

# ---------------------------------------------------------------------------------------------------------------------
# Imports (API)

SESSION_MGR_NONE = 0
SESSION_MGR_AUTO = 1
SESSION_MGR_JACK = 2
SESSION_MGR_LADISH = 3
SESSION_MGR_NSM = 4

FLAG_CONTROL_WINDOW = 0x01
FLAG_CAPTURE_FIRST_WINDOW = 0x02
FLAG_BUFFERS_ADDITION_MODE = 0x10
FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN = 0x20
FLAG_EXTERNAL_START = 0x40

# ---------------------------------------------------------------------------------------------------------------------
# Jack Application Dialog

UI_SESSION_NONE = 0
UI_SESSION_LADISH = 1
UI_SESSION_NSM = 2

class JackAppDialog(QDialog):
def __init__(self, parent: QWidget, projectFilename: str):
QDialog.__init__(self, parent)
self.ui = Ui_JackAppDialog()
self.ui.setupUi(self)

self.fProjectFilename = projectFilename

# --------------------------------------------------------------------------------------------------------------
# UI setup

self.ui.group_error.setVisible(False)
self.adjustSize()
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)

# --------------------------------------------------------------------------------------------------------------
# Load settings

self._loadSettings()

# --------------------------------------------------------------------------------------------------------------
# Set-up connections

self.finished.connect(self._slot_saveSettings)
self.ui.cb_session_mgr.currentIndexChanged.connect(self._slot_sessionManagerChanged)
self.ui.le_command.textChanged.connect(self._slot_commandChanged)

# -----------------------------------------------------------------------------------------------------------------
# public methods

def getCommandAndFlags(self):
name = self.ui.le_name.text()
command = self.ui.le_command.text()
smgr = SESSION_MGR_NONE
flags = 0x0

if not name:
name = os.path.basename(command.split(" ",1)[0]).title()

uiSessionMgrIndex = self.ui.cb_session_mgr.currentIndex()
if uiSessionMgrIndex == UI_SESSION_LADISH:
smgr = SESSION_MGR_LADISH
elif uiSessionMgrIndex == UI_SESSION_NSM:
smgr = SESSION_MGR_NSM

if self.ui.cb_manage_window.isChecked():
flags |= FLAG_CONTROL_WINDOW
if self.ui.cb_capture_first_window.isChecked():
flags |= FLAG_CAPTURE_FIRST_WINDOW
if self.ui.cb_buffers_addition_mode.isChecked():
flags |= FLAG_BUFFERS_ADDITION_MODE
if self.ui.cb_out_midi_mixdown.isChecked():
flags |= FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN
if self.ui.cb_external_start.isChecked():
flags |= FLAG_EXTERNAL_START

bv = ord('0')
v1 = chr(bv + self.ui.sb_audio_ins.value())
v2 = chr(bv + self.ui.sb_audio_outs.value())
v3 = chr(bv + self.ui.sb_midi_ins.value())
v4 = chr(bv + self.ui.sb_midi_outs.value())
v5 = chr(bv + smgr)
v6 = chr(bv + flags)
labelSetup = f"{v1}{v2}{v3}{v4}{v5}{v6}"

return (command, name, labelSetup)

# -----------------------------------------------------------------------------------------------------------------
# private methods

def _checkIfButtonBoxShouldBeEnabled(self, index: int, command: str):
enabled = len(command) > 0
showErr = ""

# NSM applications must not be abstract or absolute paths, and must not contain arguments
if enabled and index == UI_SESSION_NSM:
if command[0] in (".", "/"):
showErr = self.tr("NSM applications cannot use abstract or absolute paths")
elif " " in command or ";" in command or "&" in command:
showErr = self.tr("NSM applications cannot use CLI arguments")
elif not self.fProjectFilename:
showErr = self.tr("You need to save the current Carla project before NSM can be used")

if showErr:
enabled = False
self.ui.l_error.setText(showErr)
self.ui.group_error.setVisible(True)
else:
self.ui.group_error.setVisible(False)

self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enabled)

def _loadSettings(self):
settings = QSafeSettings("falkTX", "CarlaAddJackApp")

smName = settings.value("SessionManager", "", str)

if smName == "LADISH (SIGUSR1)":
self.ui.cb_session_mgr.setCurrentIndex(UI_SESSION_LADISH)
elif smName == "NSM":
self.ui.cb_session_mgr.setCurrentIndex(UI_SESSION_NSM)
else:
self.ui.cb_session_mgr.setCurrentIndex(UI_SESSION_NONE)

self.ui.le_command.setText(settings.value("Command", "", str))
self.ui.le_name.setText(settings.value("Name", "", str))
self.ui.sb_audio_ins.setValue(settings.value("NumAudioIns", 2, int))
self.ui.sb_audio_ins.setValue(settings.value("NumAudioIns", 2, int))
self.ui.sb_audio_outs.setValue(settings.value("NumAudioOuts", 2, int))
self.ui.sb_midi_ins.setValue(settings.value("NumMidiIns", 0, int))
self.ui.sb_midi_outs.setValue(settings.value("NumMidiOuts", 0, int))
self.ui.cb_manage_window.setChecked(settings.value("ManageWindow", True, bool))
self.ui.cb_capture_first_window.setChecked(settings.value("CaptureFirstWindow", False, bool))
self.ui.cb_out_midi_mixdown.setChecked(settings.value("MidiOutMixdown", False, bool))

self._checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr.currentIndex(),
self.ui.le_command.text())

# -----------------------------------------------------------------------------------------------------------------
# private slots

@pyqtSlot(str)
def _slot_commandChanged(self, command: str):
self._checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr.currentIndex(), command)

@pyqtSlot(int)
def _slot_sessionManagerChanged(self, index: int):
self._checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command.text())

@pyqtSlot()
def _slot_saveSettings(self):
settings = QSafeSettings("falkTX", "CarlaAddJackApp")
settings.setValue("Command", self.ui.le_command.text())
settings.setValue("Name", self.ui.le_name.text())
settings.setValue("SessionManager", self.ui.cb_session_mgr.currentText())
settings.setValue("NumAudioIns", self.ui.sb_audio_ins.value())
settings.setValue("NumAudioOuts", self.ui.sb_audio_outs.value())
settings.setValue("NumMidiIns", self.ui.sb_midi_ins.value())
settings.setValue("NumMidiOuts", self.ui.sb_midi_outs.value())
settings.setValue("ManageWindow", self.ui.cb_manage_window.isChecked())
settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window.isChecked())
settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown.isChecked())

# ---------------------------------------------------------------------------------------------------------------------
# Testing

if __name__ == '__main__':
import sys
# pylint: disable=ungrouped-imports
from PyQt5.QtWidgets import QApplication
# pylint: enable=ungrouped-imports

_app = QApplication(sys.argv)
_gui = JackAppDialog(None, "")

if _gui.exec_():
_command, _name, _labelSetup = _gui.getCommandAndFlags()
print("Results:")
print(f"\tCommand: {_command}")
print(f"\tName: {_name}")
print(f"\tLabelSetup: {_labelSetup}")

# ---------------------------------------------------------------------------------------------------------------------

+ 0
- 100
source/frontend/pluginlist/pluginlistdialog.cpp View File

@@ -382,31 +382,6 @@ int fontMetricsHorizontalAdvance(const QFontMetrics& fontMetrics, const QString&
// --------------------------------------------------------------------------------------------------------------------
// Qt-compatible plugin info

// base details, nicely packed and POD-only so we can directly use as binary
struct PluginInfoHeader {
uint16_t build;
uint16_t type;
uint32_t hints;
uint64_t uniqueId;
uint16_t audioIns;
uint16_t audioOuts;
uint16_t cvIns;
uint16_t cvOuts;
uint16_t midiIns;
uint16_t midiOuts;
uint16_t parameterIns;
uint16_t parameterOuts;
};

// full details, now with non-POD types
struct PluginInfo : PluginInfoHeader {
QString category;
QString filename;
QString name;
QString label;
QString maker;
};

// convert PluginInfo to Qt types
static QVariant asByteArray(const PluginInfo& info)
{
@@ -2090,78 +2065,3 @@ void PluginListDialog::saveSettings()
}

// --------------------------------------------------------------------------------------------------------------------

PluginListDialog*
carla_frontend_createPluginListDialog(void* const parent, const HostSettings* const hostSettings)
{
return new PluginListDialog(reinterpret_cast<QWidget*>(parent), hostSettings);
}

void
carla_frontend_destroyPluginListDialog(PluginListDialog* const dialog)
{
dialog->close();
delete dialog;
}

void
carla_frontend_setPluginListDialogPath(PluginListDialog* const dialog, const int ptype, const char* const path)
{
dialog->setPluginPath(static_cast<PluginType>(ptype), path);
}

const PluginListDialogResults*
carla_frontend_execPluginListDialog(PluginListDialog* const dialog)
{
if (dialog->exec())
{
static PluginListDialogResults ret;
static CarlaString category;
static CarlaString filename;
static CarlaString name;
static CarlaString label;
static CarlaString maker;

const PluginInfo& plugin(dialog->getSelectedPluginInfo());

category = plugin.category.toUtf8();
filename = plugin.filename.toUtf8();
name = plugin.name.toUtf8();
label = plugin.label.toUtf8();
maker = plugin.maker.toUtf8();

ret.build = plugin.build;
ret.type = plugin.type;
ret.hints = plugin.hints;
ret.category = category;
ret.filename = filename;
ret.name = name;
ret.label = label;
ret.maker = maker;
ret.uniqueId = plugin.uniqueId;
ret.audioIns = plugin.audioIns;
ret.audioOuts = plugin.audioOuts;
ret.cvIns = plugin.cvIns;
ret.cvOuts = plugin.cvOuts;
ret.midiIns = plugin.midiIns;
ret.midiOuts = plugin.midiOuts;
ret.parameterIns = plugin.parameterIns;
ret.parameterOuts = plugin.parameterOuts;

return &ret;
}

return nullptr;
}

// --------------------------------------------------------------------------------------------------------------------

// const PluginListDialogResults*
// carla_frontend_createAndExecPluginListDialog(void* const parent, const HostSettings* const hostSettings)
// {
// PluginListDialog gui(reinterpret_cast<QWidget*>(parent), hostSettings);
//
// return carla_frontend_execPluginListDialog(&gui);
// }

// --------------------------------------------------------------------------------------------------------------------

+ 29
- 5
source/frontend/pluginlist/pluginlistdialog.hpp View File

@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "CarlaFrontend.h"
#include "carla_frontend.h"

#ifdef __clang__
# pragma clang diagnostic push
@@ -15,7 +15,6 @@
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif

#include <QtWidgets/QDialog>
#include "ui_pluginlistdialog.h"

#ifdef __clang__
@@ -24,10 +23,35 @@
# pragma GCC diagnostic pop
#endif

class QSafeSettings;
typedef struct _CarlaPluginDiscoveryInfo CarlaPluginDiscoveryInfo;
typedef struct _HostSettings HostSettings;
struct PluginInfo;

// --------------------------------------------------------------------------------------------------------------------

// base details, nicely packed and POD-only so we can directly use as binary
struct PluginInfoHeader {
uint16_t build;
uint16_t type;
uint32_t hints;
uint64_t uniqueId;
uint16_t audioIns;
uint16_t audioOuts;
uint16_t cvIns;
uint16_t cvOuts;
uint16_t midiIns;
uint16_t midiOuts;
uint16_t parameterIns;
uint16_t parameterOuts;
};

// full details, now with non-POD types
struct PluginInfo : PluginInfoHeader {
QString category;
QString filename;
QString name;
QString label;
QString maker;
};

// --------------------------------------------------------------------------------------------------------------------
// Plugin List Dialog


Loading…
Cancel
Save