1 /*
2 Copyright (C) 2008, 2009 Andres Cabrera
3 mantaraya36@gmail.com
4
5 This file is part of CsoundQt.
6
7 CsoundQt is free software; you can redistribute it
8 and/or modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 CsoundQt is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with Csound; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA
21 */
22
23 #include "qutewidget.h"
24 #include "widgetlayout.h"
25
QuteWidget(QWidget * parent)26 QuteWidget::QuteWidget(QWidget *parent):
27 QWidget(parent), dialog(NULL)
28 {
29 propertiesAct = new QAction(tr("&Properties"), this);
30 propertiesAct->setStatusTip(tr("Open widget properties"));
31 connect(propertiesAct, SIGNAL(triggered()), this, SLOT(openProperties()));
32
33 addChn_kAct = new QAction(tr("Add chn_k to csd"),this);
34 addChn_kAct->setStatusTip(tr("Add chn_k definitionto ;;channels section in editor"));
35 connect(addChn_kAct, SIGNAL(triggered()), this, SLOT(addChn_k()));
36
37 m_value = 0.0;
38 m_value2 = 0.0;
39 m_stringValue = "";
40 m_valueChanged = false;
41 m_value2Changed = false;
42 m_locked = false;
43 m_description = "";
44 // used by all widgets which need access to the api (TableDisplay)
45 // TODO: adapt Scope and Graph to use this instead of implementing their own
46 m_csoundUserData = nullptr;
47
48 this->setMinimumSize(2,2);
49 this->setMouseTracking(true); // Necessary to pass mouse tracking to widget panel for _MouseX channels
50
51 setProperty("QCS_x", 0);
52 setProperty("QCS_y", 0);
53 //setProperty("width", 20);
54 //setProperty("height", 20);
55 setProperty("QCS_width", 20);
56 setProperty("QCS_height", 20);
57 setProperty("QCS_uuid", QUuid::createUuid().toString());
58 setProperty("QCS_visible", true);
59 setProperty("QCS_midichan", 0);
60 setProperty("QCS_midicc", -3);
61 setProperty("QCS_description", "");
62 }
63
~QuteWidget()64 QuteWidget::~QuteWidget()
65 {
66 }
67
setWidgetGeometry(int x,int y,int w,int h)68 void QuteWidget::setWidgetGeometry(int x, int y, int w, int h)
69 {
70 // qDebug() << "QuteWidget::setWidgetGeometry" <<x<<y<<w<<h;
71 Q_ASSERT(w > 0 && h > 0);
72 // Q_ASSERT(x > 0 && y > 0 and w > 0 && h > 0);
73 this->setGeometry(QRect(x,y,w,h));
74 m_widget->blockSignals(true);
75 m_widget->setGeometry(QRect(0,0,w,h));
76 m_widget->blockSignals(false);
77 // this->markChanged(); // It's better not to have geometry changes trigger markChanged as geometry changes can occur for various reasons (e.g. when calling applyInternalProperties)
78 }
79
setValue(double value)80 void QuteWidget::setValue(double value)
81 {
82 #ifdef USE_WIDGET_MUTEX
83 widgetLock.lockForWrite();
84 #endif
85 m_value = value;
86 m_valueChanged = true;
87 #ifdef USE_WIDGET_MUTEX
88 widgetLock.unlock();
89 #endif
90 }
91
setValue2(double value)92 void QuteWidget::setValue2(double value)
93 {
94 #ifdef USE_WIDGET_MUTEX
95 widgetLock.lockForWrite();
96 #endif
97 m_value2 = value;
98 m_value2Changed = true;
99 #ifdef USE_WIDGET_MUTEX
100 widgetLock.unlock();
101 #endif
102 }
103
setValue(QString value)104 void QuteWidget::setValue(QString value)
105 {
106 #ifdef USE_WIDGET_MUTEX
107 widgetLock.lockForWrite();
108 #endif
109 m_stringValue = value;
110 m_valueChanged = true;
111 #ifdef USE_WIDGET_MUTEX
112 widgetLock.unlock();
113 #endif
114 }
115
setMidiValue(int)116 void QuteWidget::setMidiValue(int /* value */)
117 {
118 qDebug() << "Not available for this widget." << this;
119 }
120
setMidiValue2(int)121 void QuteWidget::setMidiValue2(int /* value */)
122 {
123 qDebug() << "Not available for this widget." << this;
124 }
125
widgetMessage(QString path,QString text)126 void QuteWidget::widgetMessage(QString path, QString text)
127 {
128 qDebug() << text;
129 if (property(path.toLocal8Bit()).isValid()) {
130 setProperty(path.toLocal8Bit(), text);
131 // applyInternalProperties();
132 }
133 }
134
widgetMessage(QString path,double value)135 void QuteWidget::widgetMessage(QString path, double value)
136 {
137 qDebug() << value;
138 if (property(path.toLocal8Bit()).isValid()) {
139 setProperty(path.toLocal8Bit(), value);
140 // applyInternalProperties();
141 }
142 }
143
getChannelName()144 QString QuteWidget::getChannelName()
145 {
146 // widgetLock.lockForRead();
147 QString name = m_channel;
148 // widgetLock.unlock();
149 return name;
150 }
151
getChannel2Name()152 QString QuteWidget::getChannel2Name()
153 {
154 // widgetLock.lockForRead();
155 QString name = m_channel2;
156 // widgetLock.unlock();
157 return name;
158 }
159
getCabbageLine()160 QString QuteWidget::getCabbageLine()
161 {
162 //Widgets return empty strings when not supported
163 return QString("");
164 }
165
createXmlWriter(QXmlStreamWriter & s)166 void QuteWidget::createXmlWriter(QXmlStreamWriter &s)
167 {
168 s.setAutoFormatting(true);
169 s.writeStartElement("bsbObject");
170 s.writeAttribute("type", getWidgetType());
171
172 s.writeAttribute("version", QCS_CURRENT_XML_VERSION); // Only for compatibility with blue (absolute values)
173
174 s.writeTextElement("objectName", m_channel);
175 s.writeTextElement("x", QString::number(x()));
176 s.writeTextElement("y", QString::number(y()));
177 s.writeTextElement("width", QString::number(width()));
178 s.writeTextElement("height", QString::number(height()));
179 s.writeTextElement("uuid", property("QCS_uuid").toString());
180 s.writeTextElement("visible", property("QCS_visible").toBool() ? "true":"false");
181 s.writeTextElement("midichan", QString::number(property("QCS_midichan").toInt()));
182 s.writeTextElement("midicc", QString::number(property("QCS_midicc").toInt()));
183 s.writeTextElement("description", m_description);
184 }
185
getValue()186 double QuteWidget::getValue()
187 {
188 // When reimplementing this, remember to use the widget mutex to protect data,
189 // as this can be called from many different places
190 #ifdef USE_WIDGET_MUTEX
191 widgetLock.lockForRead();
192 #endif
193 double value = m_value;
194 #ifdef USE_WIDGET_MUTEX
195 widgetLock.unlock();
196 #endif
197 return value;
198 }
199
getValue2()200 double QuteWidget::getValue2()
201 {
202 // When reimplementing this, remember to use the widget mutex to protect data, as this can be called from many different places
203 #ifdef USE_WIDGET_MUTEX
204 widgetLock.lockForRead();
205 #endif
206 double value = m_value2;
207 #ifdef USE_WIDGET_MUTEX
208 widgetLock.unlock();
209 #endif
210 return value;
211 }
212
getStringValue()213 QString QuteWidget::getStringValue()
214 {
215 // When reimplementing this, remember to use the widget mutex to protect data, as this can be called from many different places
216 #ifdef USE_WIDGET_MUTEX
217 widgetLock.lockForRead();
218 #endif
219 QString value = m_stringValue;
220 #ifdef USE_WIDGET_MUTEX
221 widgetLock.unlock();
222 #endif
223 return value;
224 }
225
getDescription()226 QString QuteWidget::getDescription()
227 {
228 return m_description;
229 }
230
getCsladspaLine()231 QString QuteWidget::getCsladspaLine()
232 {
233 //Widgets return empty strings when not supported
234 return QString("");
235 }
236
getQml()237 QString QuteWidget::getQml()
238 {
239 //Widgets return empty strings when not supported
240 return QString();
241 }
242
getUuid()243 QString QuteWidget::getUuid()
244 {
245 if (property("QCS_uuid").isValid())
246 return property("QCS_uuid").toString();
247 else
248 return QString();
249 }
250
applyInternalProperties()251 void QuteWidget::applyInternalProperties()
252 {
253 // qDebug() << "QuteWidget::applyInternalProperties()";
254 #ifdef USE_WIDGET_MUTEX
255 widgetLock.lockForRead();
256 #endif
257 int x,y,width, height;
258 x = property("QCS_x").toInt();
259 y = property("QCS_y").toInt();
260 width = property("QCS_width").toInt();
261 height = property("QCS_height").toInt();
262 setWidgetGeometry(x,y,width, height);
263 m_channel = property("QCS_objectName").toString();
264 m_channel2 = property("QCS_objectName2").toString();
265 m_midicc = property("QCS_midicc").toInt();
266 m_midichan = property("QCS_midichan").toInt();
267 setVisible(property("QCS_visible").toBool());
268 m_valueChanged = true;
269 m_description = property("QCS_description").toString();
270 #ifdef USE_WIDGET_MUTEX
271 widgetLock.unlock();
272 #endif
273 }
274
markChanged()275 void QuteWidget::markChanged()
276 {
277 emit widgetChanged(this);
278 }
279
canFocus(bool can)280 void QuteWidget::canFocus(bool can)
281 {
282 if (can) {
283 this->setFocusPolicy(Qt::StrongFocus);
284 m_widget->setFocusPolicy(Qt::StrongFocus);
285 }
286 else {
287 this->setFocusPolicy(Qt::NoFocus);
288 m_widget->setFocusPolicy(Qt::NoFocus);
289 }
290 }
291
updateDialogWindow(int cc,int channel)292 void QuteWidget::updateDialogWindow(int cc, int channel) // to update values from midi Learn window to widget properties' dialog
293 {
294
295 if (!dialog) {
296 qDebug() << "Dialog window not careated";
297 return;
298 }
299
300 if (dialog->isVisible() && acceptsMidi()) {
301 midiccSpinBox->setValue(cc);
302 midichanSpinBox->setValue(channel);
303 }
304 }
305
contextMenuEvent(QContextMenuEvent * event)306 void QuteWidget::contextMenuEvent(QContextMenuEvent *event)
307 {
308 popUpMenu(event->globalPos());
309 }
310
popUpMenu(QPoint pos)311 void QuteWidget::popUpMenu(QPoint pos)
312 {
313 if (m_locked) {
314 return;
315 }
316 QMenu menu(this);
317 menu.addAction(propertiesAct);
318 menu.addSeparator();
319
320 if (!m_channel.isEmpty() || !m_channel2.isEmpty()) {
321 menu.addAction(addChn_kAct);
322 menu.addSeparator();
323 }
324
325 if (acceptsMidi()) {
326 menu.addAction(tr("MIDI learn"), this, SLOT(openMidiDialog()) );
327 menu.addSeparator();
328 }
329
330 QList<QAction *> actionList = getParentActionList();
331
332 for (int i = 0; i < actionList.size(); i++) {
333 auto action = actionList[i];
334 if(action == nullptr)
335 menu.addSeparator();
336 else
337 menu.addAction(action);
338 }
339
340 menu.addSeparator();
341
342 WidgetLayout *layout = static_cast<WidgetLayout *>(this->parentWidget());
343 layout->setCurrentPosition(layout->mapFromGlobal(pos));
344
345 menu.addAction(layout->storePresetAct);
346 menu.addAction(layout->newPresetAct);
347 menu.addAction(layout->recallPresetAct);
348
349 menu.addSeparator();
350
351 QMenu presetMenu(tr("Presets"), &menu);
352
353 QList<int> list = layout->getPresetNums();
354 for (int i = 0; i < list.size(); i++) {
355 QAction *act = new QAction(layout->getPresetName(list[i]), &menu);
356 act->setData(i);
357 connect(act, SIGNAL(triggered()), layout, SLOT(loadPresetFromAction()));
358 presetMenu.addAction(act);
359 }
360
361 /*
362 menu.addSeparator();
363
364 QMenu createMenu(tr("Create New", "Menu name in widget right-click menu"),&menu);
365 createMenu.addAction(layout->createSliderAct);
366 createMenu.addAction(layout->createLabelAct);
367 createMenu.addAction(layout->createDisplayAct);
368 createMenu.addAction(layout->createScrollNumberAct);
369 createMenu.addAction(layout->createLineEditAct);
370 createMenu.addAction(layout->createSpinBoxAct);
371 createMenu.addAction(layout->createButtonAct);
372 createMenu.addAction(layout->createKnobAct);
373 createMenu.addAction(layout->createCheckBoxAct);
374 createMenu.addAction(layout->createMenuAct);
375 createMenu.addAction(layout->createMeterAct);
376 createMenu.addAction(layout->createConsoleAct);
377 createMenu.addAction(layout->createGraphAct);
378 createMenu.addAction(layout->createScopeAct);
379
380 menu.addMenu(&createMenu);
381 */
382 menu.exec(pos);
383 }
384
openProperties()385 void QuteWidget::openProperties()
386 {
387 createPropertiesDialog();
388
389 connect(acceptButton, SIGNAL(released()), dialog, SLOT(accept()));
390 connect(dialog, SIGNAL(accepted()), this, SLOT(apply()));
391 connect(applyButton, SIGNAL(released()), this, SLOT(apply()));
392 connect(cancelButton, SIGNAL(released()), dialog, SLOT(close()));
393 if (acceptsMidi()) {
394 connect(midiLearnButton, SIGNAL(released()),this, SLOT(openMidiDialog()));
395 }
396 dialog->exec();
397 if (dialog->result() != QDialog::Accepted) {
398 qDebug() << "QuteWidget::openProperties() dialog not accepted";
399 }
400 // dialog->deleteLater();
401 parentWidget()->setFocus(Qt::OtherFocusReason); // For some reason focus is grabbed away from the layout, but this doesn't solve the problem...
402 }
403
404
deleteWidget()405 void QuteWidget::deleteWidget()
406 {
407 // qDebug("QuteWidget::deleteWidget()");
408 emit(deleteThisWidget(this));
409 }
410
openMidiDialog()411 void QuteWidget::openMidiDialog()
412 {
413 //createPropertiesDialog(); <- tryout for midi learn from context menu
414 emit showMidiLearn(this);
415 }
416
addChn_k()417 void QuteWidget::addChn_k()
418 {
419 //qDebug()<<Q_FUNC_INFO << m_channel << m_channel2;
420 if (!m_channel.isEmpty()) {
421 emit addChn_kSignal(m_channel);
422 }
423 if (!m_channel2.isEmpty()) {
424 emit addChn_kSignal(m_channel2);
425 }
426 }
427
createPropertiesDialog()428 void QuteWidget::createPropertiesDialog()
429 {
430 qDebug() << "QuteWidget::createPropertiesDialog()---Dynamic Properties:\n"
431 << dynamicPropertyNames ();
432 int footerRow = 20;
433 dialog = new QDialog(this);
434 dialog->resize(360, 300);
435 // dialog->setModal(true);
436 layout = new QGridLayout(dialog);
437 QLabel *label;
438
439 label = new QLabel("X =", dialog);
440 layout->addWidget(label, 0, 0, Qt::AlignRight|Qt::AlignVCenter);
441
442 xSpinBox = new QSpinBox(dialog);
443 xSpinBox->setMaximum(9999);
444 layout->addWidget(xSpinBox, 0, 1, Qt::AlignLeft|Qt::AlignVCenter);
445
446 label = new QLabel("Y =", dialog);
447 layout->addWidget(label, 0, 2, Qt::AlignRight|Qt::AlignVCenter);
448
449 ySpinBox = new QSpinBox(dialog);
450 ySpinBox->setMaximum(9999);
451 layout->addWidget(ySpinBox, 0, 3, Qt::AlignLeft|Qt::AlignVCenter);
452
453 label = new QLabel(tr("Width ="), dialog);
454 layout->addWidget(label, 1, 0, Qt::AlignRight|Qt::AlignVCenter);
455
456 wSpinBox = new QSpinBox(dialog);
457 wSpinBox->setMaximum(9999);
458 layout->addWidget(wSpinBox, 1, 1, Qt::AlignLeft|Qt::AlignVCenter);
459
460 label = new QLabel(tr("Height ="), dialog);
461 layout->addWidget(label, 1, 2, Qt::AlignRight|Qt::AlignVCenter);
462
463 hSpinBox = new QSpinBox(dialog);
464 hSpinBox->setMaximum(9999);
465 layout->addWidget(hSpinBox, 1, 3, Qt::AlignLeft|Qt::AlignVCenter);
466
467 channelLabel = new QLabel(tr("Channel ="), dialog);
468 layout->addWidget(channelLabel, 3, 0, Qt::AlignRight|Qt::AlignVCenter);
469
470 nameLineEdit = new QLineEdit(dialog);
471 nameLineEdit->setFocus(Qt::OtherFocusReason);
472 nameLineEdit->selectAll();
473 layout->addWidget(nameLineEdit, 3, 1, 1, 3, Qt::AlignLeft|Qt::AlignVCenter);
474
475 label = new QLabel(tr("Description ="), dialog);
476 layout->addWidget(label, footerRow-2, 0, Qt::AlignRight|Qt::AlignVCenter);
477
478 descriptionLineEdit = new QLineEdit(dialog);
479 descriptionLineEdit->setMinimumWidth(300);
480 layout->addWidget(descriptionLineEdit, footerRow-2, 1, 1, 4, Qt::AlignLeft|Qt::AlignVCenter);
481
482
483 if (acceptsMidi()) { // only when MIDI-enabled widgets
484 int midiRow = footerRow - 1;
485 label = new QLabel("MIDI CC =", dialog);
486 layout->addWidget(label, midiRow, 0, Qt::AlignRight|Qt::AlignVCenter);
487
488 midiccSpinBox = new QSpinBox(dialog);
489 midiccSpinBox->setRange(0,119);
490 layout->addWidget(midiccSpinBox, midiRow, 1, Qt::AlignLeft|Qt::AlignVCenter);
491
492 label = new QLabel("MIDI Channel =", dialog);
493 layout->addWidget(label, midiRow, 2, Qt::AlignRight|Qt::AlignVCenter);
494
495 midichanSpinBox = new QSpinBox(dialog);
496 midichanSpinBox->setRange(0,127);
497 layout->addWidget(midichanSpinBox, midiRow,3, Qt::AlignLeft|Qt::AlignVCenter);
498
499 midiLearnButton = new QPushButton(tr("MIDI learn"));
500 layout->addWidget(midiLearnButton, midiRow, 4, Qt::AlignLeft|Qt::AlignVCenter);
501 }
502 acceptButton = new QPushButton(tr("Ok"));
503 acceptButton->setDefault(true);
504 layout->addWidget(acceptButton, footerRow, 3, Qt::AlignCenter|Qt::AlignVCenter);
505
506 applyButton = new QPushButton(tr("Apply"));
507 layout->addWidget(applyButton, footerRow, 1, Qt::AlignCenter|Qt::AlignVCenter);
508 cancelButton = new QPushButton(tr("Cancel"));
509 layout->addWidget(cancelButton, footerRow, 2, Qt::AlignCenter|Qt::AlignVCenter);
510
511 #ifdef USE_WIDGET_MUTEX
512 widgetLock.lockForRead();
513 #endif
514 xSpinBox->setValue(this->x());
515 ySpinBox->setValue(this->y());
516 wSpinBox->setValue(this->width());
517 hSpinBox->setValue(this->height());
518 nameLineEdit->setText(getChannelName());
519 descriptionLineEdit->setText(getDescription());
520 if (acceptsMidi()) {
521 midiccSpinBox->setValue(this->m_midicc);
522 midichanSpinBox->setValue(this->m_midichan);
523 }
524 #ifdef USE_WIDGET_MUTEX
525 widgetLock.unlock();
526 #endif
527 }
528
applyProperties()529 void QuteWidget::applyProperties()
530 {
531 // qDebug();
532 #ifdef USE_WIDGET_MUTEX
533 widgetLock.lockForRead();
534 #endif
535 setProperty("QCS_objectName", nameLineEdit->text());
536 setProperty("QCS_x", xSpinBox->value());
537 setProperty("QCS_y",ySpinBox->value());
538 setProperty("QCS_width", wSpinBox->value());
539 setProperty("QCS_height", hSpinBox->value());
540 if (acceptsMidi()) {
541 setProperty("QCS_midicc", midiccSpinBox->value());
542 setProperty("QCS_midichan", midichanSpinBox->value());
543 }
544 setProperty("QCS_description", descriptionLineEdit->text());
545 #ifdef USE_WIDGET_MUTEX
546 widgetLock.unlock();
547 #endif
548 applyInternalProperties();
549 // setChannelName(nameLineEdit->text());
550 // setWidgetGeometry(xSpinBox->value(), ySpinBox->value(), wSpinBox->value(), hSpinBox->value());
551
552 // this->setMouseTracking(true); // Necessary to pass mouse tracking to widget panel for _MouseX channels
553 emit(widgetChanged(this));
554 emit propertiesAccepted();
555 parentWidget()->setFocus(Qt::PopupFocusReason); // For some reason focus is grabbed away from the layout
556 m_valueChanged = true;
557 }
558
getParentActionList()559 QList<QAction *> QuteWidget::getParentActionList()
560 {
561 QList<QAction *> actionList;
562 // A bit of a kludge... Must get the Widget Panel, which is the parent to the widget which
563 // holds the actual QuteWidgets
564 WidgetLayout *layout = static_cast<WidgetLayout *>(this->parentWidget());
565 actionList.append(layout->alignLeftAct);
566 actionList.append(layout->alignRightAct);
567 actionList.append(layout->alignTopAct);
568 actionList.append(layout->alignBottomAct);
569 actionList.append(nullptr);
570 actionList.append(layout->alignCenterHorizontalAct);
571 actionList.append(layout->alignCenterVerticalAct);
572 actionList.append(nullptr);
573 actionList.append(layout->sendToBackAct);
574 actionList.append(layout->sendToFrontAct);
575 actionList.append(nullptr);
576 actionList.append(layout->distributeHorizontalAct);
577 actionList.append(layout->distributeVerticalAct);
578 actionList.append(nullptr);
579 actionList.append(layout->copyAct);
580 actionList.append(layout->pasteAct);
581 actionList.append(layout->cutAct);
582 actionList.append(layout->deleteAct);
583 actionList.append(layout->duplicateAct);
584
585 // FIXME put edit action in menu
586 // actionList.append(layout->editAct);
587 return actionList;
588 }
589
apply()590 void QuteWidget::apply()
591 {
592 applyProperties();
593 }
594