1 /*
2 Copyright 2007-2016 David Robillard <d@drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <ctype.h> // workaround for error: use of undeclared identifier 'isascii'
18
19 #include "jalv_internal.h"
20
21 #include "lilv/lilv.h"
22 #include "suil/suil.h"
23 #include "zix/sem.h"
24
25 #include <QtGlobal>
26
27 #if QT_VERSION >= 0x050000
28 # include <QAction>
29 # include <QApplication>
30 # include <QDial>
31 # include <QFontMetrics>
32 # include <QGroupBox>
33 # include <QGuiApplication>
34 # include <QHBoxLayout>
35 # include <QKeySequence>
36 # include <QLabel>
37 # include <QLayout>
38 # include <QLayoutItem>
39 # include <QList>
40 # include <QMainWindow>
41 # include <QMenu>
42 # include <QMenuBar>
43 # include <QObject>
44 # include <QPoint>
45 # include <QRect>
46 # include <QScreen>
47 # include <QScrollArea>
48 # include <QSize>
49 # include <QSizePolicy>
50 # include <QString>
51 # include <QStyle>
52 # include <QTimer>
53 # include <QVBoxLayout>
54 # include <QWidget>
55 # include <QtCore>
56 #else
57 # include <QtGui>
58 #endif
59
60 #include <algorithm>
61 #include <cmath>
62 #include <cstdint>
63 #include <cstring>
64 #include <map>
65 #include <vector>
66
67 #define CONTROL_WIDTH 150
68 #define DIAL_STEPS 10000
69
70 static QApplication* app = nullptr;
71
72 class FlowLayout : public QLayout
73 {
74 public:
75 explicit FlowLayout(QWidget* parent,
76 int margin,
77 int hSpacing,
78 int vSpacing);
79
80 explicit FlowLayout(int margin, int hSpacing, int vSpacing);
81
82 FlowLayout(const FlowLayout&) = delete;
83 FlowLayout& operator=(const FlowLayout&) = delete;
84
85 FlowLayout(FlowLayout&&) = delete;
86 FlowLayout&& operator=(FlowLayout&&) = delete;
87
88 ~FlowLayout() override;
89
90 void addItem(QLayoutItem* item) override;
91 int horizontalSpacing() const;
92 int verticalSpacing() const;
93 Qt::Orientations expandingDirections() const override;
94 bool hasHeightForWidth() const override;
95 int heightForWidth(int) const override;
96 int count() const override;
97 QLayoutItem* itemAt(int index) const override;
98 QSize minimumSize() const override;
99 void setGeometry(const QRect &rect) override;
100 QSize sizeHint() const override;
101 QLayoutItem* takeAt(int index) override;
102
103 private:
104 int doLayout(const QRect &rect, bool testOnly) const;
105 int smartSpacing(QStyle::PixelMetric pm) const;
106
107 QList<QLayoutItem*> itemList;
108 int m_hSpace;
109 int m_vSpace;
110 };
111
FlowLayout(QWidget * parent,int margin,int hSpacing,int vSpacing)112 FlowLayout::FlowLayout(QWidget* parent, int margin, int hSpacing, int vSpacing)
113 : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
114 {
115 setContentsMargins(margin, margin, margin, margin);
116 }
117
FlowLayout(int margin,int hSpacing,int vSpacing)118 FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
119 : m_hSpace(hSpacing), m_vSpace(vSpacing)
120 {
121 setContentsMargins(margin, margin, margin, margin);
122 }
123
~FlowLayout()124 FlowLayout::~FlowLayout()
125 {
126 QLayoutItem* item = nullptr;
127 while ((item = takeAt(0))) {
128 delete item;
129 }
130 }
131
132 void
addItem(QLayoutItem * item)133 FlowLayout::addItem(QLayoutItem* item)
134 {
135 itemList.append(item);
136 }
137
138 int
horizontalSpacing() const139 FlowLayout::horizontalSpacing() const
140 {
141 if (m_hSpace >= 0) {
142 return m_hSpace;
143 } else {
144 return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
145 }
146 }
147
148 int
verticalSpacing() const149 FlowLayout::verticalSpacing() const
150 {
151 if (m_vSpace >= 0) {
152 return m_vSpace;
153 } else {
154 return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
155 }
156 }
157
158 int
count() const159 FlowLayout::count() const
160 {
161 return itemList.size();
162 }
163
164 QLayoutItem*
itemAt(int index) const165 FlowLayout::itemAt(int index) const
166 {
167 return itemList.value(index);
168 }
169
170 QLayoutItem*
takeAt(int index)171 FlowLayout::takeAt(int index)
172 {
173 if (index >= 0 && index < itemList.size()) {
174 return itemList.takeAt(index);
175 } else {
176 return nullptr;
177 }
178 }
179
180 Qt::Orientations
expandingDirections() const181 FlowLayout::expandingDirections() const
182 {
183 return Qt::Orientations();
184 }
185
186 bool
hasHeightForWidth() const187 FlowLayout::hasHeightForWidth() const
188 {
189 return true;
190 }
191
192 int
heightForWidth(int width) const193 FlowLayout::heightForWidth(int width) const
194 {
195 return doLayout(QRect(0, 0, width, 0), true);
196 }
197
198 void
setGeometry(const QRect & rect)199 FlowLayout::setGeometry(const QRect &rect)
200 {
201 QLayout::setGeometry(rect);
202 doLayout(rect, false);
203 }
204
205 QSize
sizeHint() const206 FlowLayout::sizeHint() const
207 {
208 return minimumSize();
209 }
210
211 QSize
minimumSize() const212 FlowLayout::minimumSize() const
213 {
214 QSize size = {};
215 QLayoutItem* item = nullptr;
216 foreach (item, itemList) {
217 size = size.expandedTo(item->minimumSize());
218 }
219
220 return size + QSize(2 * margin(), 2 * margin());
221 }
222
223 int
doLayout(const QRect & rect,bool testOnly) const224 FlowLayout::doLayout(const QRect &rect, bool testOnly) const
225 {
226 int left = 0;
227 int top = 0;
228 int right = 0;
229 int bottom = 0;
230 getContentsMargins(&left, &top, &right, &bottom);
231
232 QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
233 int x = effectiveRect.x();
234 int y = effectiveRect.y();
235 int lineHeight = 0;
236
237 QLayoutItem* item = nullptr;
238 foreach (item, itemList) {
239 QWidget* wid = item->widget();
240
241 int spaceX = horizontalSpacing();
242 if (spaceX == -1) {
243 spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton,
244 QSizePolicy::PushButton,
245 Qt::Horizontal,
246 nullptr,
247 nullptr);
248 }
249 int spaceY = verticalSpacing();
250 if (spaceY == -1) {
251 spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton,
252 QSizePolicy::PushButton,
253 Qt::Vertical,
254 nullptr,
255 nullptr);
256 }
257
258 int nextX = x + item->sizeHint().width() + spaceX;
259 if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
260 x = effectiveRect.x();
261 y = y + lineHeight + spaceY;
262 nextX = x + item->sizeHint().width() + spaceX;
263 lineHeight = 0;
264 }
265
266 if (!testOnly) {
267 item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
268 }
269
270 x = nextX;
271 lineHeight = qMax(lineHeight, item->sizeHint().height());
272 }
273 return y + lineHeight - rect.y() + bottom;
274 }
275
276 int
smartSpacing(QStyle::PixelMetric pm) const277 FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
278 {
279 QObject* parent = this->parent();
280 if (!parent) {
281 return -1;
282 } else if (parent->isWidgetType()) {
283 QWidget* pw = static_cast<QWidget*>(parent);
284 return pw->style()->pixelMetric(pm, nullptr, pw);
285 } else {
286 return static_cast<QLayout*>(parent)->spacing();
287 }
288 }
289
290 class PresetAction : public QAction
291 {
292 Q_OBJECT // NOLINT
293
294 public:
PresetAction(QObject * parent,Jalv * jalv,LilvNode * preset)295 PresetAction(QObject* parent, Jalv* jalv, LilvNode* preset)
296 : QAction(parent)
297 , _jalv(jalv)
298 , _preset(preset)
299 {
300 connect(this, SIGNAL(triggered()),
301 this, SLOT(presetChosen()));
302 }
303
presetChosen()304 Q_SLOT void presetChosen() {
305 jalv_apply_preset(_jalv, _preset);
306 }
307
308 private:
309 Jalv* _jalv;
310 LilvNode* _preset;
311 };
312
313 typedef struct {
314 Jalv* jalv;
315 struct Port* port;
316 } PortContainer;
317
318 class Control : public QGroupBox
319 {
320 Q_OBJECT // NOLINT
321
322 public:
323 explicit Control(PortContainer portContainer, QWidget* parent);
324
325 Q_SLOT void dialChanged(int value);
326
327 void setValue(float value);
328
329 private:
330 void setRange(float min, float max);
331 QString getValueLabel(float value);
332 float getValue();
333 int stringWidth(const QString& str);
334
335 QDial* dial;
336 const LilvPlugin* plugin;
337 struct Port* port;
338
339 QLabel* label;
340 QString name;
341 int steps;
342 float max;
343 float min;
344 bool isInteger;
345 bool isEnum;
346 bool isLogarithmic;
347
348 std::vector<float> scalePoints;
349 std::map<float, const char*> scaleMap;
350 };
351
352 #if QT_VERSION >= 0x050000
353 # include "jalv_qt5_meta.hpp" // IWYU pragma: keep
354 #else
355 # include "jalv_qt4_meta.hpp" // IWYU pragma: keep
356 #endif
357
358 extern "C" {
359
360 int
jalv_init(int * argc,char *** argv,JalvOptions *)361 jalv_init(int* argc, char*** argv, JalvOptions*)
362 {
363 app = new QApplication(*argc, *argv, true);
364 app->setStyleSheet("QGroupBox::title { subcontrol-position: top center }");
365
366 return 0;
367 }
368
369 const char*
jalv_native_ui_type(void)370 jalv_native_ui_type(void)
371 {
372 #if QT_VERSION >= 0x050000
373 return "http://lv2plug.in/ns/extensions/ui#Qt5UI";
374 #else
375 return "http://lv2plug.in/ns/extensions/ui#Qt4UI";
376 #endif
377 }
378
379 void
jalv_ui_port_event(Jalv * jalv,uint32_t port_index,uint32_t buffer_size,uint32_t protocol,const void * buffer)380 jalv_ui_port_event(Jalv* jalv,
381 uint32_t port_index,
382 uint32_t buffer_size,
383 uint32_t protocol,
384 const void* buffer)
385 {
386 if (jalv->ui_instance) {
387 suil_instance_port_event(jalv->ui_instance, port_index,
388 buffer_size, protocol, buffer);
389 } else {
390 Control* control =
391 static_cast<Control*>(jalv->ports[port_index].widget);
392 if (control) {
393 control->setValue(*static_cast<const float*>(buffer));
394 }
395 }
396 }
397
398 class Timer : public QTimer
399 {
400 public:
Timer(Jalv * jalv)401 explicit Timer(Jalv* jalv) : _jalv(jalv) {}
402
timerEvent(QTimerEvent *)403 void timerEvent(QTimerEvent*) override {
404 jalv_update(_jalv);
405 }
406
407 private:
408 Jalv* _jalv;
409 };
410
411 static int
add_preset_to_menu(Jalv * jalv,const LilvNode * node,const LilvNode * title,void * data)412 add_preset_to_menu(Jalv* jalv,
413 const LilvNode* node,
414 const LilvNode* title,
415 void* data)
416 {
417 QMenu* menu = static_cast<QMenu*>(data);
418 const char* label = lilv_node_as_string(title);
419
420 QAction* action = new PresetAction(menu, jalv, lilv_node_duplicate(node));
421 action->setText(label);
422 menu->addAction(action);
423 return 0;
424 }
425
Control(PortContainer portContainer,QWidget * parent)426 Control::Control(PortContainer portContainer, QWidget* parent)
427 : QGroupBox(parent)
428 , dial(new QDial())
429 , plugin(portContainer.jalv->plugin)
430 , port(portContainer.port)
431 , label(new QLabel())
432 , max(1.0f)
433 , min(0.0f)
434 , isInteger(false)
435 , isEnum(false)
436 , isLogarithmic(false)
437 {
438 JalvNodes* nodes = &portContainer.jalv->nodes;
439 const LilvPort* lilvPort = port->lilv_port;
440
441 LilvNode* nmin = nullptr;
442 LilvNode* nmax = nullptr;
443 LilvNode* ndef = nullptr;
444 lilv_port_get_range(plugin, lilvPort, &ndef, &nmin, &nmax);
445
446 LilvNode* stepsNode = lilv_port_get(plugin, lilvPort, nodes->pprops_rangeSteps);
447 if (lilv_node_is_int(stepsNode)) {
448 steps = std::max(lilv_node_as_int(stepsNode), 2);
449 } else {
450 steps = DIAL_STEPS;
451 }
452 lilv_node_free(stepsNode);
453
454 // Fill scalePoints Map
455 LilvScalePoints* sp = lilv_port_get_scale_points(plugin, lilvPort);
456 if (sp) {
457 LILV_FOREACH(scale_points, s, sp) {
458 const LilvScalePoint* p = lilv_scale_points_get(sp, s);
459 const LilvNode* val = lilv_scale_point_get_value(p);
460 if (!lilv_node_is_float(val) && !lilv_node_is_int(val)) {
461 continue;
462 }
463
464 const float f = lilv_node_as_float(val);
465 scalePoints.push_back(f);
466 scaleMap[f] = lilv_node_as_string(lilv_scale_point_get_label(p));
467 }
468
469 lilv_scale_points_free(sp);
470 }
471
472 // Check port properties
473 isLogarithmic = lilv_port_has_property(plugin, lilvPort, nodes->pprops_logarithmic);
474 isInteger = lilv_port_has_property(plugin, lilvPort, nodes->lv2_integer);
475 isEnum = lilv_port_has_property(plugin, lilvPort, nodes->lv2_enumeration);
476
477 if (lilv_port_has_property(plugin, lilvPort, nodes->lv2_toggled)) {
478 isInteger = true;
479
480 if (!scaleMap[0]) {
481 scaleMap[0] = "Off";
482 }
483 if (!scaleMap[1]) {
484 scaleMap[1] = "On" ;
485 }
486 }
487
488 // Find and set min, max and default values for port
489 float defaultValue = ndef ? lilv_node_as_float(ndef) : port->control;
490 setRange(lilv_node_as_float(nmin), lilv_node_as_float(nmax));
491 setValue(defaultValue);
492
493 // Fill layout
494 QVBoxLayout* layout = new QVBoxLayout();
495 layout->addWidget(label, 0, Qt::AlignHCenter);
496 layout->addWidget(dial, 0, Qt::AlignHCenter);
497 setLayout(layout);
498
499 setMinimumWidth(CONTROL_WIDTH);
500 setMaximumWidth(CONTROL_WIDTH);
501
502 LilvNode* nname = lilv_port_get_name(plugin, lilvPort);
503 name = QString("%1").arg(lilv_node_as_string(nname));
504
505 // Handle long names
506 if (stringWidth(name) > CONTROL_WIDTH) {
507 setTitle(fontMetrics().elidedText(name, Qt::ElideRight, CONTROL_WIDTH));
508 } else {
509 setTitle(name);
510 }
511
512 // Set tooltip if comment is available
513 LilvNode* comment = lilv_port_get(plugin, lilvPort, nodes->rdfs_comment);
514 if (comment) {
515 QString* tooltip = new QString();
516 tooltip->append(lilv_node_as_string(comment));
517 setToolTip(*tooltip);
518 }
519
520 setFlat(true);
521
522 connect(dial, SIGNAL(valueChanged(int)), this, SLOT(dialChanged(int)));
523
524 lilv_node_free(nmin);
525 lilv_node_free(nmax);
526 lilv_node_free(ndef);
527 lilv_node_free(nname);
528 lilv_node_free(comment);
529 }
530
531 void
setValue(float value)532 Control::setValue(float value)
533 {
534 float step = 0.0f;
535
536 if (isInteger) {
537 step = value;
538 } else if (isEnum) {
539 step = (std::find(scalePoints.begin(), scalePoints.end(), value)
540 - scalePoints.begin());
541 } else if (isLogarithmic) {
542 step = steps * logf(value / min) / logf(max / min);
543 } else {
544 step = value * steps;
545 }
546
547 dial->setValue(step);
548 label->setText(getValueLabel(value));
549 }
550
551 QString
getValueLabel(float value)552 Control::getValueLabel(float value)
553 {
554 if (scaleMap[value]) {
555 if (stringWidth(scaleMap[value]) > CONTROL_WIDTH) {
556 label->setToolTip(scaleMap[value]);
557 return fontMetrics().elidedText(QString(scaleMap[value]),
558 Qt::ElideRight,
559 CONTROL_WIDTH);
560 }
561 return scaleMap[value];
562 }
563
564 return QString("%1").arg(value);
565 }
566
567 void
setRange(float minRange,float maxRange)568 Control::setRange(float minRange, float maxRange)
569 {
570 min = minRange;
571 max = maxRange;
572
573 if (isLogarithmic) {
574 minRange = 1;
575 maxRange = steps;
576 } else if (isEnum) {
577 minRange = 0;
578 maxRange = scalePoints.size() - 1;
579 } else if (!isInteger) {
580 minRange *= steps;
581 maxRange *= steps;
582 }
583
584 dial->setRange(minRange, maxRange);
585 }
586
587 float
getValue()588 Control::getValue()
589 {
590 if (isEnum) {
591 return scalePoints[dial->value()];
592 } else if (isInteger) {
593 return dial->value();
594 } else if (isLogarithmic) {
595 return min * powf(max / min, (float)dial->value() / (steps - 1));
596 } else {
597 return (float)dial->value() / steps;
598 }
599 }
600
601 int
stringWidth(const QString & str)602 Control::stringWidth(const QString& str)
603 {
604 #if QT_VERSION >= 0x050B00
605 return fontMetrics().horizontalAdvance(str);
606 #else
607 return fontMetrics().width(str);
608 #endif
609 }
610
611 void
dialChanged(int)612 Control::dialChanged(int)
613 {
614 float value = getValue();
615
616 label->setText(getValueLabel(value));
617 port->control = value;
618 }
619
620 static bool
portGroupLessThan(const PortContainer & p1,const PortContainer & p2)621 portGroupLessThan(const PortContainer &p1, const PortContainer &p2)
622 {
623 Jalv* jalv = p1.jalv;
624 const LilvPort* port1 = p1.port->lilv_port;
625 const LilvPort* port2 = p2.port->lilv_port;
626
627 LilvNode* group1 = lilv_port_get(
628 jalv->plugin, port1, jalv->nodes.pg_group);
629 LilvNode* group2 = lilv_port_get(
630 jalv->plugin, port2, jalv->nodes.pg_group);
631
632 const int cmp = (group1 && group2)
633 ? strcmp(lilv_node_as_string(group1), lilv_node_as_string(group2))
634 : (intptr_t(group1) - intptr_t(group2));
635
636 lilv_node_free(group2);
637 lilv_node_free(group1);
638
639 return cmp < 0;
640 }
641
642 static QWidget*
build_control_widget(Jalv * jalv)643 build_control_widget(Jalv* jalv)
644 {
645 const LilvPlugin* plugin = jalv->plugin;
646 LilvWorld* world = jalv->world;
647
648 QList<PortContainer> portContainers;
649 for (unsigned i = 0; i < jalv->num_ports; ++i) {
650 if (!jalv->opts.show_hidden &&
651 lilv_port_has_property(plugin, jalv->ports[i].lilv_port,
652 jalv->nodes.pprops_notOnGUI)) {
653 continue;
654 }
655
656 if (jalv->ports[i].type == TYPE_CONTROL) {
657 PortContainer portContainer;
658 portContainer.jalv = jalv;
659 portContainer.port = &jalv->ports[i];
660 portContainers.append(portContainer);
661 }
662 }
663
664 std::sort(portContainers.begin(), portContainers.end(), portGroupLessThan);
665
666 QWidget* grid = new QWidget();
667 FlowLayout* flowLayout = new FlowLayout(-1, -1, -1);
668 QLayout* layout = flowLayout;
669
670 LilvNode* lastGroup = nullptr;
671 QHBoxLayout* groupLayout = nullptr;
672 for (int i = 0; i < portContainers.count(); ++i) {
673 PortContainer portContainer = portContainers[i];
674 Port* port = portContainer.port;
675
676 Control* control = new Control(portContainer, nullptr);
677 LilvNode* group = lilv_port_get(
678 plugin, port->lilv_port, jalv->nodes.pg_group);
679 if (group) {
680 if (!lilv_node_equals(group, lastGroup)) {
681 /* Group has changed */
682 LilvNode* groupName = lilv_world_get(
683 world, group, jalv->nodes.lv2_name, nullptr);
684 if (!groupName) {
685 groupName = lilv_world_get(
686 world, group, jalv->nodes.rdfs_label, nullptr);
687 }
688
689 QGroupBox* groupBox = new QGroupBox(lilv_node_as_string(groupName));
690
691 groupLayout = new QHBoxLayout();
692 groupBox->setLayout(groupLayout);
693 layout->addWidget(groupBox);
694 }
695
696 groupLayout->addWidget(control);
697 } else {
698 layout->addWidget(control);
699 }
700 lilv_node_free(lastGroup);
701 lastGroup = group;
702
703 uint32_t index = lilv_port_get_index(plugin, port->lilv_port);
704 jalv->ports[index].widget = control;
705 }
706 lilv_node_free(lastGroup);
707
708 grid->setLayout(layout);
709
710 return grid;
711 }
712
713 bool
jalv_discover_ui(Jalv *)714 jalv_discover_ui(Jalv*)
715 {
716 return true;
717 }
718
719 float
jalv_ui_refresh_rate(Jalv *)720 jalv_ui_refresh_rate(Jalv*)
721 {
722 #if QT_VERSION >= 0x050000
723 return (float)QGuiApplication::primaryScreen()->refreshRate();
724 #else
725 return 30.0f;
726 #endif
727 }
728
729 int
jalv_open_ui(Jalv * jalv)730 jalv_open_ui(Jalv* jalv)
731 {
732 QMainWindow* win = new QMainWindow();
733 QMenu* file_menu = win->menuBar()->addMenu("&File");
734 QMenu* presets_menu = win->menuBar()->addMenu("&Presets");
735 QAction* quit_action = new QAction("&Quit", win);
736
737 QObject::connect(quit_action, SIGNAL(triggered()), win, SLOT(close()));
738 quit_action->setShortcuts(QKeySequence::Quit);
739 quit_action->setStatusTip("Quit Jalv");
740 file_menu->addAction(quit_action);
741
742 jalv_load_presets(jalv, add_preset_to_menu, presets_menu);
743
744 if (jalv->ui && !jalv->opts.generic_ui) {
745 jalv_ui_instantiate(jalv, jalv_native_ui_type(), win);
746 }
747
748 QWidget* widget = nullptr;
749 if (jalv->ui_instance) {
750 widget =
751 static_cast<QWidget*>(suil_instance_get_widget(jalv->ui_instance));
752 } else {
753 QWidget* controlWidget = build_control_widget(jalv);
754
755 widget = new QScrollArea();
756 static_cast<QScrollArea*>(widget)->setWidget(controlWidget);
757 static_cast<QScrollArea*>(widget)->setWidgetResizable(true);
758 widget->setMinimumWidth(800);
759 widget->setMinimumHeight(600);
760 }
761
762 LilvNode* name = lilv_plugin_get_name(jalv->plugin);
763 win->setWindowTitle(lilv_node_as_string(name));
764 lilv_node_free(name);
765
766 win->setCentralWidget(widget);
767 app->connect(app, SIGNAL(lastWindowClosed()), app, SLOT(quit()));
768
769 jalv_init_ui(jalv);
770
771 win->show();
772 if (jalv->ui_instance && !jalv_ui_is_resizable(jalv)) {
773 widget->setMinimumSize(widget->width(), widget->height());
774 widget->setMaximumSize(widget->width(), widget->height());
775 win->adjustSize();
776 win->setFixedSize(win->width(), win->height());
777 } else {
778 win->resize(widget->width(),
779 widget->height() + win->menuBar()->height());
780 }
781
782 Timer* timer = new Timer(jalv);
783 timer->start(1000 / jalv->ui_update_hz);
784
785 int ret = app->exec();
786 zix_sem_post(&jalv->done);
787 return ret;
788 }
789
790 int
jalv_close_ui(Jalv *)791 jalv_close_ui(Jalv*)
792 {
793 app->quit();
794 return 0;
795 }
796
797 } // extern "C"
798