From 4269edacdddc21f94061b121678c78d9e4935f2f Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Wed, 18 May 2011 15:01:21 +0000 Subject: implemented crescendo function --- muse2/muse/functions.cpp | 61 ++++++++++ muse2/muse/functions.h | 4 + muse2/muse/midiedit/dcanvas.h | 2 +- muse2/muse/midiedit/drumedit.cpp | 4 + muse2/muse/midiedit/drumedit.h | 2 +- muse2/muse/midiedit/pianoroll.cpp | 5 + muse2/muse/midiedit/pianoroll.h | 1 + muse2/muse/midiedit/prcanvas.h | 4 +- muse2/muse/midiedit/scoreedit.cpp | 16 ++- muse2/muse/midiedit/scoreedit.h | 2 +- muse2/muse/widgets/CMakeLists.txt | 3 + muse2/muse/widgets/crescendo.cpp | 112 ++++++++++++++++++ muse2/muse/widgets/crescendo.h | 46 ++++++++ muse2/muse/widgets/crescendobase.ui | 223 ++++++++++++++++++++++++++++++++++++ 14 files changed, 477 insertions(+), 8 deletions(-) create mode 100644 muse2/muse/widgets/crescendo.cpp create mode 100644 muse2/muse/widgets/crescendo.h create mode 100644 muse2/muse/widgets/crescendobase.ui diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index 55daee2c..3c6bbc89 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -14,6 +14,8 @@ #include +#include + using namespace std; GateTime* gatetime_dialog=NULL; @@ -24,6 +26,7 @@ DelOverlaps* del_overlaps_dialog=NULL; Setlen* set_notelen_dialog=NULL; Move* move_notes_dialog=NULL; Transpose* transpose_dialog=NULL; +Crescendo* crescendo_dialog=NULL; void init_function_dialogs(QWidget* parent) { @@ -35,6 +38,7 @@ void init_function_dialogs(QWidget* parent) set_notelen_dialog = new Setlen(parent); move_notes_dialog = new Move(parent); transpose_dialog = new Transpose(parent); + crescendo_dialog = new Crescendo(parent); } set partlist_to_set(PartList* pl) @@ -159,6 +163,22 @@ bool transpose_notes(const set& parts) return true; } +bool crescendo(const set& parts) +{ + if (song->rpos() <= song->lpos()) + { + QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Please first select the range for crescendo with the loop markers.")); + return false; + } + + if (!crescendo_dialog->exec()) + return false; + + crescendo(parts,crescendo_dialog->range,crescendo_dialog->start_val,crescendo_dialog->end_val,crescendo_dialog->absolute); + + return true; +} + void modify_velocity(const set& parts, int range, int rate, int offset) @@ -391,6 +411,44 @@ void transpose_notes(const set& parts, int range, signed int halftonestep } } +void crescendo(const set& parts, int range, int start_val, int end_val, bool absolute) +{ + map events = get_events(parts, range); + + int from=song->lpos(); + int to=song->rpos(); + + if ( (!events.empty()) && (to>from) ) + { + song->startUndo(); + + for (map::iterator it=events.begin(); it!=events.end(); it++) + { + Event& event=*(it->first); + Part* part=it->second; + + unsigned tick = event.tick() + part->tick(); + float curr_val= (float)start_val + (float)(end_val-start_val) * (tick-from) / (to-from); + + Event newEvent = event.clone(); + int velo = event.velo(); + + if (absolute) + velo=curr_val; + else + velo=curr_val*velo/100; + + if (velo > 127) velo=127; + if (velo <= 0) velo=1; + newEvent.setVelo(velo); + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgChangeEvent(event, newEvent, part, false, false, false); + } + + song->endUndo(SC_EVENT_MODIFIED); + } +} + void move_notes(const set& parts, int range, signed int ticks) //TODO FINDMICH: safety checks { map events = get_events(parts, range); @@ -510,6 +568,8 @@ void read_function_dialog_config(Xml& xml) move_notes_dialog->read_configuration(xml); else if (tag == "transpose") transpose_dialog->read_configuration(xml); + else if (tag == "crescendo") + crescendo_dialog->read_configuration(xml); else xml.unknown("function_dialogs"); break; @@ -536,6 +596,7 @@ void write_function_dialog_config(int level, Xml& xml) set_notelen_dialog->write_configuration(level, xml); move_notes_dialog->write_configuration(level, xml); transpose_dialog->write_configuration(level, xml); + crescendo_dialog->write_configuration(level, xml); xml.tag(level, "/dialogs"); } diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index 9477767a..a0228831 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -10,6 +10,7 @@ #include "velocity.h" #include "quantize.h" +#include "crescendo.h" #include "gatetime.h" #include "remove.h" #include "transpose.h" @@ -29,6 +30,7 @@ extern DelOverlaps* del_overlaps_dialog; extern Setlen* set_notelen_dialog; extern Move* move_notes_dialog; extern Transpose* transpose_dialog; +extern Crescendo* crescendo_dialog; void init_function_dialogs(QWidget* parent); @@ -46,6 +48,7 @@ void delete_overlaps(const std::set& parts, int range); void set_notelen(const std::set& parts, int range, int len); void move_notes(const std::set& parts, int range, signed int ticks); void transpose_notes(const std::set& parts, int range, signed int halftonesteps); +void crescendo(const std::set& parts, int range, int start_val, int end_val, bool absolute); //the below functions automatically open the dialog @@ -56,6 +59,7 @@ bool quantize_notes(const std::set& parts); bool set_notelen(const std::set& parts); bool move_notes(const std::set& parts); bool transpose_notes(const std::set& parts); +bool crescendo(const std::set& parts); bool erase_notes(const std::set& parts); bool delete_overlaps(const std::set& parts); diff --git a/muse2/muse/midiedit/dcanvas.h b/muse2/muse/midiedit/dcanvas.h index 8bd70d89..cf653648 100644 --- a/muse2/muse/midiedit/dcanvas.h +++ b/muse2/muse/midiedit/dcanvas.h @@ -90,7 +90,7 @@ class DrumCanvas : public EventCanvas { CMD_CUT, CMD_COPY, CMD_PASTE, CMD_SAVE, CMD_LOAD, CMD_RESET, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PREV_PART, CMD_SELECT_NEXT_PART, - CMD_DEL, CMD_FIXED_LEN, CMD_RIGHT, CMD_LEFT, CMD_RIGHT_NOSNAP, CMD_LEFT_NOSNAP, CMD_MODIFY_VELOCITY, + CMD_DEL, CMD_FIXED_LEN, CMD_RIGHT, CMD_LEFT, CMD_RIGHT_NOSNAP, CMD_LEFT_NOSNAP, CMD_MODIFY_VELOCITY, CMD_CRESCENDO, CMD_QUANTIZE, CMD_ERASE_EVENT, CMD_NOTE_SHIFT, CMD_DELETE_OVERLAPS }; DrumCanvas(MidiEditor*, QWidget*, int, int, diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp index b8d97283..1b7001f2 100644 --- a/muse2/muse/midiedit/drumedit.cpp +++ b/muse2/muse/midiedit/drumedit.cpp @@ -235,6 +235,7 @@ DrumEdit::DrumEdit(PartList* pl, QWidget* parent, const char* name, unsigned ini fixedAction = menuFunctions->addAction(tr("Set Fixed Length")); veloAction = menuFunctions->addAction(tr("Modify Velocity")); + crescAction = menuFunctions->addAction(tr("Crescendo/Decrescendo")); quantizeAction = menuFunctions->addAction(tr("Quantize")); QAction* eraseEventAction = menuFunctions->addAction(tr("Erase Event")); QAction* noteShiftAction = menuFunctions->addAction(tr("Note Shift")); @@ -242,6 +243,7 @@ DrumEdit::DrumEdit(PartList* pl, QWidget* parent, const char* name, unsigned ini connect(fixedAction, SIGNAL(triggered()), signalMapper, SLOT(map())); connect(veloAction, SIGNAL(triggered()), signalMapper, SLOT(map())); + connect(crescAction, SIGNAL(triggered()), signalMapper, SLOT(map())); connect(quantizeAction, SIGNAL(triggered()), signalMapper, SLOT(map())); connect(eraseEventAction, SIGNAL(triggered()), signalMapper, SLOT(map())); connect(noteShiftAction, SIGNAL(triggered()), signalMapper, SLOT(map())); @@ -249,6 +251,7 @@ DrumEdit::DrumEdit(PartList* pl, QWidget* parent, const char* name, unsigned ini signalMapper->setMapping(fixedAction, DrumCanvas::CMD_FIXED_LEN); signalMapper->setMapping(veloAction, DrumCanvas::CMD_MODIFY_VELOCITY); + signalMapper->setMapping(crescAction, DrumCanvas::CMD_CRESCENDO); signalMapper->setMapping(quantizeAction, DrumCanvas::CMD_QUANTIZE); signalMapper->setMapping(eraseEventAction, DrumCanvas::CMD_ERASE_EVENT); signalMapper->setMapping(noteShiftAction, DrumCanvas::CMD_NOTE_SHIFT); @@ -900,6 +903,7 @@ void DrumEdit::cmd(int cmd) case DrumCanvas::CMD_SAVE: save(); break; case DrumCanvas::CMD_RESET: reset(); break; case DrumCanvas::CMD_MODIFY_VELOCITY: modify_velocity(partlist_to_set(parts())); break; + case DrumCanvas::CMD_CRESCENDO: crescendo(partlist_to_set(parts())); break; case DrumCanvas::CMD_ERASE_EVENT: erase_notes(partlist_to_set(parts())); break; case DrumCanvas::CMD_DEL: erase_notes(partlist_to_set(parts()),1); break; //delete selected events case DrumCanvas::CMD_DELETE_OVERLAPS: delete_overlaps(partlist_to_set(parts())); break; diff --git a/muse2/muse/midiedit/drumedit.h b/muse2/muse/midiedit/drumedit.h index 242ec963..30fe8487 100644 --- a/muse2/muse/midiedit/drumedit.h +++ b/muse2/muse/midiedit/drumedit.h @@ -75,7 +75,7 @@ class DrumEdit : public MidiEditor { QAction *loadAction, *saveAction, *resetAction; QAction *cutAction, *copyAction, *pasteAction, *deleteAction; - QAction *fixedAction, *veloAction, *quantizeAction; + QAction *fixedAction, *veloAction, *crescAction, *quantizeAction; QAction *sallAction, *snoneAction, *invAction, *inAction , *outAction; QAction *prevAction, *nextAction; diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 840bd1c7..9105a446 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -190,6 +190,10 @@ PianoRoll::PianoRoll(PartList* pl, QWidget* parent, const char* name, unsigned i mapper->setMapping(funcModVelAction, PianoCanvas::CMD_MODIFY_VELOCITY); connect(funcModVelAction, SIGNAL(triggered()), mapper, SLOT(map())); + funcCrescAction = menuFunctions->addAction(tr("Crescendo/Decrescendo")); + mapper->setMapping(funcCrescAction, PianoCanvas::CMD_CRESCENDO); + connect(funcCrescAction, SIGNAL(triggered()), mapper, SLOT(map())); + funcTransposeAction = menuFunctions->addAction(tr("Transpose")); mapper->setMapping(funcTransposeAction, PianoCanvas::CMD_TRANSPOSE); connect(funcTransposeAction, SIGNAL(triggered()), mapper, SLOT(map())); @@ -598,6 +602,7 @@ void PianoRoll::cmd(int cmd) { case PianoCanvas::CMD_MODIFY_GATE_TIME: modify_notelen(partlist_to_set(parts())); break; case PianoCanvas::CMD_MODIFY_VELOCITY: modify_velocity(partlist_to_set(parts())); break; + case PianoCanvas::CMD_CRESCENDO: crescendo(partlist_to_set(parts())); break; case PianoCanvas::CMD_QUANTIZE: quantize_notes(partlist_to_set(parts())); break; case PianoCanvas::CMD_TRANSPOSE: transpose_notes(partlist_to_set(parts())); break; case PianoCanvas::CMD_ERASE_EVENT: erase_notes(partlist_to_set(parts())); break; diff --git a/muse2/muse/midiedit/pianoroll.h b/muse2/muse/midiedit/pianoroll.h index e157db10..58c2487a 100644 --- a/muse2/muse/midiedit/pianoroll.h +++ b/muse2/muse/midiedit/pianoroll.h @@ -84,6 +84,7 @@ class PianoRoll : public MidiEditor { QAction* funcQuantizeAction; QAction* funcGateTimeAction; QAction* funcModVelAction; + QAction* funcCrescAction; QAction* funcTransposeAction; QAction* funcEraseEventAction; QAction* funcNoteShiftAction; diff --git a/muse2/muse/midiedit/prcanvas.h b/muse2/muse/midiedit/prcanvas.h index 39506a13..9922b471 100644 --- a/muse2/muse/midiedit/prcanvas.h +++ b/muse2/muse/midiedit/prcanvas.h @@ -89,8 +89,8 @@ class PianoCanvas : public EventCanvas { CMD_QUANTIZE, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PREV_PART, CMD_SELECT_NEXT_PART, - CMD_MODIFY_GATE_TIME, CMD_MODIFY_VELOCITY, - CMD_CRESCENDO, CMD_TRANSPOSE, CMD_THIN_OUT, CMD_ERASE_EVENT, + CMD_MODIFY_GATE_TIME, CMD_MODIFY_VELOCITY, CMD_CRESCENDO, + CMD_TRANSPOSE, CMD_THIN_OUT, CMD_ERASE_EVENT, CMD_NOTE_SHIFT, CMD_MOVE_CLOCK, CMD_COPY_MEASURE, CMD_ERASE_MEASURE, CMD_DELETE_MEASURE, CMD_CREATE_MEASURE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index 01b69e90..13586a72 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -358,9 +358,11 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) QAction* func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); QAction* func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); QAction* func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); + QAction* func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); menu_mapper->setMapping(func_quantize_action, CMD_QUANTIZE); menu_mapper->setMapping(func_notelen_action, CMD_NOTELEN); menu_mapper->setMapping(func_velocity_action, CMD_VELOCITY); + menu_mapper->setMapping(func_cresc_action, CMD_CRESCENDO); if (!default_toolbar_state.isEmpty()) restoreState(default_toolbar_state); @@ -562,6 +564,7 @@ void ScoreEdit::menu_command(int cmd) case CMD_QUANTIZE: quantize_notes(score_canvas->get_all_parts()); break; case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; + case CMD_CRESCENDO: crescendo(score_canvas->get_all_parts()); break; case CMD_NOTELEN: modify_notelen(score_canvas->get_all_parts()); break; default: @@ -4199,17 +4202,23 @@ void staff_t::apply_lasso(QRect rect, set& already_processed) * between, for example, when a cis is tied to a des * * CURRENT TODO - * x nothing atm + * o add functions like crescendo, set velo, mod/set velo-off + * o import midi: speed up + * o draw controllers in part "slivers" * * IMPORTANT TODO + * o display blue loop markers + * o transpose: support in-key-transpose + * o drum-loop-editor (like in sq korg ds xD) + * o in step-rec: insert chords + * * o add a select-clef-toolbox for tracks * o respect the track's clef (has to be implemented first in muse) * o do partial recalculating; recalculating can take pretty long * (0,5 sec) when displaying a whole song in scores - * o support edge-scrolling when opening a lasso + * o transpose etc. must also transpose key-pressure events * * less important stuff - * o add functions like crescendo, set velo, mod/set velo-off * o deal with expanding parts * o use bars instead of flags over groups of 8ths / 16ths etc * o support different keys in different tracks at the same time @@ -4217,6 +4226,7 @@ void staff_t::apply_lasso(QRect rect, set& already_processed) * calc_pos_add_list must be called before calc_item_pos then, * and calc_item_pos must respect the pos_add_list instead of * keeping its own pos_add variable (which is only an optimisation) + * o support edge-scrolling when opening a lasso * o save more configuration stuff (quant, color) * * really unimportant nice-to-haves diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index bdffd7b4..c26bdd84 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -57,7 +57,7 @@ enum {CMD_COLOR_BLACK, CMD_COLOR_VELO, CMD_COLOR_PART, CMD_NOTELEN_1, CMD_NOTELEN_2, CMD_NOTELEN_4, CMD_NOTELEN_8, CMD_NOTELEN_16, CMD_NOTELEN_32, CMD_NOTELEN_LAST, - CMD_QUANTIZE, CMD_VELOCITY, CMD_NOTELEN }; + CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN }; class ScoreCanvas; class EditToolBar; diff --git a/muse2/muse/widgets/CMakeLists.txt b/muse2/muse/widgets/CMakeLists.txt index 0ca33d5a..c8415d73 100644 --- a/muse2/muse/widgets/CMakeLists.txt +++ b/muse2/muse/widgets/CMakeLists.txt @@ -78,6 +78,7 @@ QT4_WRAP_CPP (widget_mocs unusedwavefiles.h velocity.h quantize.h + crescendo.h move.h remove.h deloverlaps.h @@ -120,6 +121,7 @@ file (GLOB widgets_ui_files unusedwavefiles.ui velocitybase.ui quantbase.ui + crescendobase.ui movebase.ui removebase.ui deloverlapsbase.ui @@ -193,6 +195,7 @@ file (GLOB widgets_source_files utils.cpp velocity.cpp quantize.cpp + crescendo.cpp move.cpp remove.cpp deloverlaps.cpp diff --git a/muse2/muse/widgets/crescendo.cpp b/muse2/muse/widgets/crescendo.cpp new file mode 100644 index 00000000..ef31c5ef --- /dev/null +++ b/muse2/muse/widgets/crescendo.cpp @@ -0,0 +1,112 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: crescendo.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ +// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +//========================================================= + +#include +#include "crescendo.h" +#include "xml.h" + +Crescendo::Crescendo(QWidget* parent) + : QDialog(parent) +{ + setupUi(this); + range_group = new QButtonGroup; + range_group->addButton(looped_events_button,2); + range_group->addButton(selected_looped_button,3); + + connect(absolute_button, SIGNAL(toggled(bool)), SLOT(absolute_changed(bool))); + + pull_values(); +} + +void Crescendo::pull_values() +{ + range = range_group->checkedId(); + start_val = start_spinbox->value(); + end_val = end_spinbox->value(); + absolute = absolute_button->isChecked(); +} + +void Crescendo::accept() +{ + pull_values(); + QDialog::accept(); +} + +int Crescendo::exec() +{ + if ((range < 2) || (range > 3)) range=3; + + range_group->button(range)->setChecked(true); + start_spinbox->setValue(start_val); + end_spinbox->setValue(end_val); + absolute_button->setChecked(absolute); + absolute_changed(absolute); + + return QDialog::exec(); +} + +void Crescendo::read_configuration(Xml& xml) +{ + for (;;) + { + Xml::Token token = xml.parse(); + if (token == Xml::Error || token == Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case Xml::TagStart: + if (tag == "range") + range=xml.parseInt(); + else if (tag == "start") + start_val=xml.parseInt(); + else if (tag == "end") + end_val=xml.parseInt(); + else if (tag == "absolute") + absolute=xml.parseInt(); + else + xml.unknown("Crescendo"); + break; + + case Xml::TagEnd: + if (tag == "crescendo") + return; + + default: + break; + } + } +} + +void Crescendo::write_configuration(int level, Xml& xml) +{ + xml.tag(level++, "crescendo"); + xml.intTag(level, "range", range); + xml.intTag(level, "start", start_val); + xml.intTag(level, "end", end_val); + xml.intTag(level, "absolute", absolute); + xml.tag(level, "/crescendo"); +} + +void Crescendo::absolute_changed(bool val) +{ + if (val) + { + start_spinbox->setMaximum(127); + start_spinbox->setSuffix(""); + end_spinbox->setMaximum(127); + end_spinbox->setSuffix(""); + } + else + { + start_spinbox->setMaximum(12700); + start_spinbox->setSuffix(" %"); + end_spinbox->setMaximum(12700); + end_spinbox->setSuffix(" %"); + } +} diff --git a/muse2/muse/widgets/crescendo.h b/muse2/muse/widgets/crescendo.h new file mode 100644 index 00000000..eb00e94f --- /dev/null +++ b/muse2/muse/widgets/crescendo.h @@ -0,0 +1,46 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: crescendo.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ +// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +//========================================================= + +#ifndef __CRESCENDO_H__ +#define __CRESCENDO_H__ + +#include "ui_crescendobase.h" + +class QButtonGroup; +class Xml; + +class Crescendo : public QDialog, public Ui::CrescendoBase +{ + private: + Q_OBJECT + QButtonGroup* range_group; + + protected slots: + void accept(); + void pull_values(); + + public: + Crescendo(QWidget* parent = 0); + + int range; + int start_val; + int end_val; + bool absolute; + + void read_configuration(Xml& xml); + void write_configuration(int level, Xml& xml); + + + public slots: + int exec(); + + private slots: + void absolute_changed(bool); +}; + +#endif + diff --git a/muse2/muse/widgets/crescendobase.ui b/muse2/muse/widgets/crescendobase.ui new file mode 100644 index 00000000..5f4ec1f4 --- /dev/null +++ b/muse2/muse/widgets/crescendobase.ui @@ -0,0 +1,223 @@ + + + CrescendoBase + + + + 0 + 0 + 275 + 293 + + + + MusE: Crescendo/Decrescendo + + + + 6 + + + 11 + + + + + Range + + + + 6 + + + 11 + + + + + Looped Events + + + + + + + Selected Looped + + + true + + + + + + + + + + Values + + + false + + + false + + + + 11 + + + 6 + + + + + Start velocity + + + false + + + + + + + % + + + 0 + + + 12700 + + + 80 + + + + + + + End velocity + + + + + + + Absolute + + + false + + + + + + + Relative + + + true + + + + + + + % + + + 12700 + + + 130 + + + + + + + + + + 6 + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + OK + + + false + + + true + + + + + + + Cancel + + + + + + + + + + + + okButton + clicked() + CrescendoBase + accept() + + + 20 + 20 + + + 20 + 20 + + + + + cancelButton + clicked() + CrescendoBase + reject() + + + 20 + 20 + + + 20 + 20 + + + + + -- cgit v1.2.3