mainwindow.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. /*
  2. This file is part of QTau
  3. Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
  4. Copyright (C) 2013 digited <https://github.com/digited>
  5. Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
  6. QTau is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. SPDX-License-Identifier: GPL-3.0+
  17. */
  18. #define HAVE_DYNDRAWER
  19. //this will be fixed for next release
  20. #include "mainwindow.h"
  21. #include "qformlayout.h"
  22. #include "ui_mainwindow.h"
  23. #include <QIcon>
  24. #include <QtCore>
  25. #include <QtGui>
  26. #include "Controller.h"
  27. #include "Session.h"
  28. #include <QComboBox>
  29. #include <QDial>
  30. #include <QFileDialog>
  31. #include <QGridLayout>
  32. #include <QGroupBox>
  33. #include <QLineEdit>
  34. #include <QMessageBox>
  35. #include <QPushButton>
  36. #include <QScrollArea>
  37. #include <QScrollBar>
  38. #include <QSplitter>
  39. #include <QTabWidget>
  40. #include <QTextEdit>
  41. #include <QToolBar>
  42. #include <QHeaderView>
  43. #include "ui/Config.h"
  44. #include "ui/dynDrawer.h"
  45. #include "ui/meter.h"
  46. #include "ui/noteEditor.h"
  47. #include "ui/piano.h"
  48. #include "tempomap.h"
  49. #include "ui/tempodialog.h"
  50. #include <QTableView>
  51. #include "dyntablemodel.h"
  52. #define __devloglevel__ 8
  53. const int cdef_bars = 128; // 128 bars "is enough for everyone" // TODO: make dynamic
  54. const int c_piano_min_width = 110;
  55. const int c_piano_min_height = 40;
  56. const int c_meter_min_height = 40;
  57. const int c_waveform_min_height = 50;
  58. const int c_drawzone_min_height = 100;
  59. const int c_dynbuttons_num = 12;
  60. const QString c_dynlbl_css_off =
  61. QString("QLabel { color : %1; }").arg(cdef_color_dynbtn_off);
  62. const QString c_dynlbl_css_bg =
  63. QString("QLabel { color : %1; }").arg(cdef_color_dynbtn_bg);
  64. const QString c_dynlbl_css_fg =
  65. QString("QLabel { color : %1; background-color : %2; }")
  66. .arg(cdef_color_dynbtn_on)
  67. .arg(cdef_color_dynbtn_on_bg);
  68. QSettings settings("QTau", c_qtau_name);
  69. const QString c_key_dir_score = QStringLiteral("last_score_dir");
  70. const QString c_key_dir_audio = QStringLiteral("last_audio_dir");
  71. const QString c_key_win_size = QStringLiteral("window_size");
  72. const QString c_key_win_max = QStringLiteral("window_fullscreen");
  73. const QString c_key_show_lognum = QStringLiteral("show_new_log_number");
  74. const QString c_key_dynpanel_on = QStringLiteral("dynamics_panel_visible");
  75. const QString c_key_sound = QStringLiteral("sould_level");
  76. const QString c_key_audio_codec = QStringLiteral("save_audio_codec");
  77. const QString c_doc_txt = QStringLiteral(":/tr/documentation_en.txt");
  78. const QString c_icon_app = QStringLiteral(":/images/appicon_ouka_alice.png");
  79. const QString c_icon_sound = QStringLiteral(":/images/speaker.png");
  80. const QString c_icon_mute = QStringLiteral(":/images/speaker_mute.png");
  81. const QString c_icon_editor = QStringLiteral(":/images/b_notes.png");
  82. const QString c_icon_voices = QStringLiteral(":/images/b_mic.png");
  83. const QString c_icon_plugins = QStringLiteral(":/images/b_plug.png");
  84. const QString c_icon_settings = QStringLiteral(":/images/b_gear.png");
  85. const QString c_icon_doc = QStringLiteral(":/images/b_manual.png");
  86. const QString c_icon_log = QStringLiteral(":/images/b_envelope.png");
  87. const QString c_icon_play = QStringLiteral(":/images/b_play.png");
  88. const QString c_icon_jack = QStringLiteral(":/images/jack-transport.png");
  89. const QString c_icon_pause = QStringLiteral(":/images/b_pause.png");
  90. MainWindow::MainWindow(QWidget *parent)
  91. : QMainWindow(parent),
  92. ui(new Ui::MainWindow),
  93. _logNewMessages(0),
  94. _logHasErrors(false),
  95. _showNewLogNumber(true) {
  96. _ns.tmap = new TempoMap();
  97. _ns.tmap->addTempo(0, 120);
  98. _ns.tmap->addTimeSignature(0, 4, 4); // default values
  99. _ns.barScreenOffsets = new int[1024];
  100. _drawZone = nullptr;
  101. ui->setupUi(this);
  102. setWindowIcon(QIcon(c_icon_app));
  103. setWindowTitle(c_qtau_name);
  104. setAcceptDrops(true);
  105. setContextMenuPolicy(Qt::NoContextMenu);
  106. //-----------------------------------------
  107. _mbe = new MeterBarLineEdit();
  108. _mbe->setText("");
  109. _mbl = new QLabel("");
  110. // QLabel* labelHW = new QLabel("HelloWorld");
  111. _mbe->setFixedSize(110, 20);
  112. _meter = new qtauMeterBar(this);
  113. _meter->setMinimumHeight(c_meter_min_height);
  114. _meter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  115. _meter->setContentsMargins(0, 0, 0, 0);
  116. _piano = new qtauPiano(ui->centralWidget);
  117. _piano->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
  118. _piano->setMinimumSize(c_piano_min_width, c_piano_min_height);
  119. _piano->setContentsMargins(0, 0, 0, 0);
  120. _zoom = new QSlider(Qt::Horizontal, ui->centralWidget);
  121. _zoom->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
  122. _zoom->setRange(0, c_zoom_num - 1);
  123. _zoom->setSingleStep(1);
  124. _zoom->setPageStep(1);
  125. _zoom->setValue(cdef_zoom_index);
  126. _zoom->setMinimumWidth(c_piano_min_width);
  127. _zoom->setGeometry(0, 0, c_piano_min_width, 10);
  128. _zoom->setContentsMargins(0, 0, 0, 0);
  129. _noteEditor = new qtauNoteEditor(ui->centralWidget);
  130. _noteEditor->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  131. _noteEditor->setContentsMargins(0, 0, 0, 0);
  132. _hscr = new QScrollBar(Qt::Horizontal, ui->centralWidget);
  133. _vscr = new QScrollBar(Qt::Vertical, ui->centralWidget);
  134. _hscr->setContentsMargins(0, 0, 0, 0);
  135. _vscr->setContentsMargins(0, 0, 0, 0);
  136. _hscr->setRange(0, _ns.note.width() * 4 * cdef_bars); // FIXXME
  137. _vscr->setRange(0, _ns.note.height() * 12 * _ns.numOctaves);
  138. _hscr->setSingleStep(_ns.note.width());
  139. _vscr->setSingleStep(_ns.note.height());
  140. _hscr->setContextMenuPolicy(Qt::NoContextMenu);
  141. _vscr->setContextMenuPolicy(Qt::NoContextMenu);
  142. updateSetup();
  143. //---- vocal and music waveform panels, hidden until synthesized (vocal wave)
  144. //and/or loaded (music wave)
  145. QScrollBar *dummySB = new QScrollBar(this);
  146. dummySB->setOrientation(Qt::Vertical);
  147. dummySB->setRange(0, 0);
  148. dummySB->setEnabled(false);
  149. QFrame *waveControls = new QFrame(this);
  150. waveControls->setContentsMargins(0, 0, 0, 0);
  151. waveControls->setMinimumSize(c_piano_min_width, c_waveform_min_height);
  152. waveControls->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
  153. waveControls->setFrameStyle(QFrame::Panel | QFrame::Raised);
  154. QGridLayout *waveformL = new QGridLayout();
  155. waveformL->setContentsMargins(0, 0, 0, 0);
  156. waveformL->setSpacing(0);
  157. waveformL->addWidget(waveControls, 0, 0, 2, 1);
  158. waveformL->addWidget(dummySB, 0, 2, 2, 1);
  159. _wavePanel = new QWidget(this);
  160. _wavePanel->setContentsMargins(0, 0, 0, 0);
  161. _wavePanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  162. _wavePanel->setLayout(waveformL);
  163. _wavePanel->setVisible(false);
  164. //---- notes' dynamics setup area --------------
  165. #if 0
  166. QGridLayout *dynBtnL = new QGridLayout();
  167. QString btnNames[c_dynbuttons_num] = {"VEL", "DYN", "BRE", "BRI",
  168. "CLE", "OPE", "GEN", "POR",
  169. "PIT", "PBS", "XSY", "GWL"
  170. };
  171. //VEL: scalar
  172. //DYN: envelope
  173. //BRE: scalar ?
  174. //BRI: scalar ?
  175. //CLE: scalar ?
  176. //OPE: scalar ?
  177. //GEN: scalar *
  178. //POR: scalar
  179. //PIT: envelope
  180. //PBS: ?
  181. //XSY: ?
  182. //GWL ?
  183. for (int i = 0; i < c_dynbuttons_num; ++i) {
  184. qtauDynLabel *l = new qtauDynLabel(btnNames[i], this);
  185. l->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
  186. dynBtnL->addWidget(l, i / 2, i % 2, 1, 1);
  187. l->setStyleSheet(c_dynlbl_css_off);
  188. l->setFrameStyle(QFrame::Box);
  189. l->setLineWidth(1);
  190. connect(l, SIGNAL(leftClicked()), SLOT(dynBtnLClicked()));
  191. connect(l, SIGNAL(rightClicked()), SLOT(dynBtnRClicked()));
  192. }
  193. dynBtnL->setRowStretch(c_dynbuttons_num / 2, 100);
  194. #endif
  195. QFrame *dynBtnPanel = new QFrame(this);
  196. dynBtnPanel->setContentsMargins(0, 0, 0, 0);
  197. dynBtnPanel->setMinimumSize(c_piano_min_width, c_drawzone_min_height);
  198. dynBtnPanel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
  199. dynBtnPanel->setFrameStyle(QFrame::Panel | QFrame::Raised);
  200. //dynBtnPanel->setLayout(dynBtnL);
  201. _drawZone = new qtauDynDrawer(_noteEditor, ui->centralWidget);
  202. _drawZone->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  203. _drawZone->setMinimumHeight(c_drawzone_min_height);
  204. _drawZone->setContentsMargins(0, 0, 0, 0);
  205. _drawZone->configure(_ns);
  206. QScrollBar *dummySB3 = new QScrollBar(this);
  207. dummySB3->setOrientation(Qt::Vertical);
  208. dummySB3->setRange(0, 0);
  209. dummySB3->setEnabled(false);
  210. QHBoxLayout *singParamsL = new QHBoxLayout();
  211. singParamsL->setContentsMargins(0, 0, 0, 0);
  212. singParamsL->setSpacing(0);
  213. singParamsL->addWidget(dynBtnPanel);
  214. //only one singParamsL->addWidget(_drawZone);
  215. _dynTable= new QTableView(this);
  216. _dynTableModel = new DynTableModel();
  217. _dynTable->setModel(_dynTableModel);
  218. _dynTable->setEnabled(false);
  219. _dynTable->horizontalHeader()->setStretchLastSection(true);
  220. singParamsL->addWidget(_dynTable);
  221. singParamsL->addWidget(dummySB3);
  222. _drawZonePanel = new QWidget(this);
  223. _drawZonePanel->setContentsMargins(0, 0, 0, 0);
  224. _drawZonePanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
  225. _drawZonePanel->setLayout(singParamsL);
  226. //---- Combining editor panels into hi-level layout ------
  227. QGridLayout *gl = new QGridLayout();
  228. gl->setContentsMargins(0, 0, 0, 0);
  229. gl->setSpacing(0);
  230. QFrame *meterFrame = new QFrame(this);
  231. QVBoxLayout *meterLayout = new QVBoxLayout();
  232. meterLayout->setSpacing(0);
  233. meterLayout->setMargin(0);
  234. meterFrame->setMaximumSize(110, 40);
  235. meterFrame->setLayout(meterLayout);
  236. meterLayout->addWidget(_mbl);
  237. meterLayout->addWidget(_mbe);
  238. gl->addWidget(meterFrame, 0, 0, 1, 1);
  239. gl->addWidget(_meter, 0, 1, 1, 1);
  240. gl->addWidget(_piano, 1, 0, 1, 1);
  241. gl->addWidget(_zoom, 2, 0, 1, 1);
  242. gl->addWidget(_noteEditor, 1, 1, 1, 1);
  243. gl->addWidget(_hscr, 2, 1, 1, 1);
  244. gl->addWidget(_vscr, 1, 2, 1, 1);
  245. QWidget *editorUpperPanel = new QWidget(this);
  246. editorUpperPanel->setContentsMargins(0, 0, 0, 0);
  247. editorUpperPanel->setSizePolicy(QSizePolicy::Expanding,
  248. QSizePolicy::Expanding);
  249. editorUpperPanel->setMaximumSize(9000, 9000);
  250. editorUpperPanel->setLayout(gl);
  251. _editorSplitter = new QSplitter(Qt::Vertical, this);
  252. _editorSplitter->setContentsMargins(0, 0, 0, 0);
  253. _editorSplitter->addWidget(editorUpperPanel);
  254. _wavePanel->setVisible(false); // keep wavepanel for now, but set hidden
  255. _editorSplitter->addWidget(_wavePanel);
  256. #ifdef HAVE_DYNDRAWER
  257. _drawZonePanel->setVisible(true);
  258. #else
  259. _drawZonePanel->setVisible(false);
  260. #endif
  261. _editorSplitter->addWidget(_drawZonePanel);
  262. _editorSplitter->setStretchFactor(0, 1);
  263. _editorSplitter->setStretchFactor(1, 0);
  264. _editorSplitter->setStretchFactor(2, 0);
  265. _editorSplitter->setStretchFactor(3, 0);
  266. QList<int> sizes = _editorSplitter->sizes();
  267. sizes[1] = 0;
  268. _editorSplitter->setSizes(sizes);
  269. QVBoxLayout *edVBL = new QVBoxLayout();
  270. edVBL->setContentsMargins(0, 0, 0, 0);
  271. edVBL->addWidget(_editorSplitter);
  272. QWidget *editorPanel = new QWidget(this);
  273. editorPanel->setContentsMargins(0, 0, 0, 0);
  274. editorPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  275. editorPanel->setMaximumSize(9000, 9000);
  276. editorPanel->setLayout(edVBL);
  277. //---- Log tab ---------------------------------
  278. QWidget *logPanel = new QWidget(this);
  279. logPanel->setContentsMargins(0, 0, 0, 0);
  280. logPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  281. logPanel->setMaximumSize(9000, 9000);
  282. _logpad = new QTextEdit(this);
  283. _logpad->setReadOnly(true);
  284. _logpad->setUndoRedoEnabled(false);
  285. _logpad->setContextMenuPolicy(Qt::NoContextMenu);
  286. _logpad->setStyleSheet("p, pre { white-space: 1.2; }");
  287. QGridLayout *logL = new QGridLayout();
  288. logL->setContentsMargins(0, 0, 0, 0);
  289. logL->addWidget(_logpad, 0, 0, 1, 1);
  290. logPanel->setLayout(logL);
  291. //---- Combining tabs togeter ------------------
  292. _tabs = new QTabWidget(this);
  293. _tabs->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  294. _tabs->setContentsMargins(0, 0, 0, 0);
  295. _tabs->setMaximumSize(9000, 9000);
  296. _tabs->setTabPosition(QTabWidget::South);
  297. _tabs->setMovable(false); // just to be sure
  298. _tabs->addTab(editorPanel, QIcon(c_icon_editor), tr("Editor"));
  299. _tabs->addTab(logPanel, QIcon(c_icon_log), tr("Log"));
  300. _tabs->widget(0)->setContentsMargins(0, 0, 0, 0);
  301. _tabs->widget(1)->setContentsMargins(0, 0, 0, 0);
  302. _logTabTextColor = _tabs->tabBar()->tabTextColor(1);
  303. connect(_tabs, SIGNAL(currentChanged(int)), SLOT(onTabSelected(int)));
  304. QVBoxLayout *vbl = new QVBoxLayout();
  305. vbl->setContentsMargins(0, 0, 0, 0);
  306. vbl->addWidget(_tabs);
  307. ui->centralWidget->setContentsMargins(0, 0, 0, 0);
  308. ui->centralWidget->setLayout(vbl);
  309. //---- Toolbars --------------------------------
  310. QToolBar *fileTB = new QToolBar("Fileops", this);
  311. QToolBar *playerTB = new QToolBar("Playback", this);
  312. QToolBar *toolsTB = new QToolBar("Toolset", this);
  313. fileTB->setFloatable(false);
  314. playerTB->setFloatable(false);
  315. toolsTB->setFloatable(false);
  316. fileTB->addAction(ui->actionSave);
  317. fileTB->addAction(ui->actionSave_audio_as);
  318. fileTB->addAction(ui->actionUndo);
  319. fileTB->addAction(ui->actionRedo);
  320. playerTB->addAction(ui->actionPlay);
  321. playerTB->addAction(ui->actionStop);
  322. playerTB->addAction(ui->actionBack);
  323. playerTB->addAction(ui->actionJack);
  324. QComboBox *quantizeCombo = new QComboBox(this);
  325. QComboBox *lengthCombo = new QComboBox(this);
  326. quantizeCombo->addItems(QStringList() << "Q/4"
  327. << "Q/8"
  328. << "Q/16"
  329. << "Q/32"
  330. << "Q/64");
  331. lengthCombo->addItems(QStringList() << "♪/4"
  332. << "♪/8"
  333. << "♪/16"
  334. << "♪/32"
  335. << "♪/64");
  336. quantizeCombo->setCurrentIndex(3);
  337. lengthCombo->setCurrentIndex(3);
  338. toolsTB->addAction(ui->actionEdit_Mode);
  339. toolsTB->addAction(ui->actionGrid_Snap);
  340. toolsTB->addSeparator();
  341. toolsTB->addWidget(quantizeCombo);
  342. toolsTB->addWidget(lengthCombo);
  343. toolsTB->addSeparator();
  344. toolsTB->addWidget(new QLabel("Singer: "));
  345. _singerSelect = new QComboBox();
  346. _singerSelect->setMinimumWidth(100);
  347. connect(_singerSelect, SIGNAL(currentIndexChanged(int)),
  348. SLOT(onSingerSelected(int)));
  349. toolsTB->addWidget(_singerSelect);
  350. for (int i = 0; i < MAXRECENTFILES; ++i) {
  351. _recentFileActs[i] = new QAction(this);
  352. _recentFileActs[i]->setVisible(false);
  353. this->ui->menuRecent_files->addAction(_recentFileActs[i]);
  354. connect(_recentFileActs[i], SIGNAL(triggered()), this,
  355. SLOT(openRecentFile()));
  356. }
  357. updateRecentFileActions();
  358. addToolBar(fileTB);
  359. addToolBar(playerTB);
  360. addToolBar(toolsTB);
  361. _toolbars.append(fileTB);
  362. _toolbars.append(playerTB);
  363. _toolbars.append(toolsTB);
  364. //----------------------------------------------
  365. connect(quantizeCombo, SIGNAL(currentIndexChanged(int)),
  366. SLOT(onQuantizeSelected(int)));
  367. connect(lengthCombo, SIGNAL(currentIndexChanged(int)),
  368. SLOT(onNotelengthSelected(int)));
  369. connect(_piano, &qtauPiano ::heightChanged, this,
  370. &MainWindow::onPianoHeightChanged);
  371. connect(_noteEditor, &qtauNoteEditor ::widthChanged, this,
  372. &MainWindow::onNoteEditorWidthChanged);
  373. connect(_meter, &qtauMeterBar ::scrolled, this, &MainWindow::notesHScrolled);
  374. connect(_piano, &qtauPiano ::scrolled, this, &MainWindow::notesVScrolled);
  375. connect(_drawZone, &qtauDynDrawer ::scrolled, this,
  376. &MainWindow::notesHScrolled);
  377. connect(_noteEditor, &qtauNoteEditor ::vscrolled, this,
  378. &MainWindow::notesVScrolled);
  379. connect(_noteEditor, &qtauNoteEditor ::hscrolled, this,
  380. &MainWindow::notesHScrolled);
  381. connect(_vscr, &QScrollBar ::valueChanged, this, &MainWindow::vertScrolled);
  382. connect(_hscr, &QScrollBar ::valueChanged, this, &MainWindow::horzScrolled);
  383. connect(_noteEditor, &qtauNoteEditor ::rmbScrolled, this,
  384. &MainWindow::onEditorRMBScrolled);
  385. connect(_noteEditor, &qtauNoteEditor ::requestsOffset, this,
  386. &MainWindow::onEditorRequestOffset);
  387. connect(_zoom, &QSlider ::valueChanged, this, &MainWindow::onZoomed);
  388. connect(_meter, &qtauMeterBar ::zoomed, this, &MainWindow::onEditorZoomed);
  389. connect(_noteEditor, &qtauNoteEditor ::zoomed, this,
  390. &MainWindow::onEditorZoomed);
  391. connect(_drawZone, &qtauDynDrawer ::zoomed, this,
  392. &MainWindow::onEditorZoomed);
  393. connect(ui->actionQuit, &QAction::triggered, [=]() {
  394. this->close();
  395. });
  396. connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onOpenUST);
  397. connect(ui->actionSave, &QAction::triggered, this, &MainWindow::onSaveUST);
  398. connect(ui->actionSave_as, &QAction::triggered, this,
  399. &MainWindow::onSaveUSTAs);
  400. connect(ui->actionMIDI, &QAction::triggered, this, &MainWindow::onMIDIImport);
  401. connect(ui->actionMIDI_2, &QAction::triggered, this,
  402. &MainWindow::onMIDIExport);
  403. connect(ui->actionUndo, &QAction::triggered, this, &MainWindow::onUndo);
  404. connect(ui->actionRedo, &QAction::triggered, this, &MainWindow::onRedo);
  405. connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
  406. connect(ui->actionEdit_Mode, &QAction::triggered, this,
  407. &MainWindow::onEditMode);
  408. connect(ui->actionGrid_Snap, &QAction::triggered, this,
  409. &MainWindow::onGridSnap);
  410. connect(_noteEditor,&qtauNoteEditor::selectionChanged,this,&MainWindow::onSelectionChanged);
  411. //----------------------------------------------
  412. _lastScoreDir = settings.value(c_key_dir_score, "").toString();
  413. _lastAudioDir = settings.value(c_key_dir_audio, "").toString();
  414. _audioExt = settings.value(c_key_audio_codec, "").toString();
  415. _showNewLogNumber = settings.value(c_key_show_lognum, true).toBool();
  416. bool jack_auto_connect = settings.value("jack_auto_connect",true).toBool();
  417. ui->actionConnect2Jack->setCheckable(true);
  418. ui->actionConnect2Jack->setChecked(jack_auto_connect);
  419. if (!settings.value(c_key_dynpanel_on, true).toBool()) {
  420. QList<int> panelSizes = _editorSplitter->sizes();
  421. panelSizes.last() = 0;
  422. _editorSplitter->setSizes(panelSizes);
  423. }
  424. if (settings.value(c_key_win_max, false).toBool())
  425. showMaximized();
  426. else {
  427. QRect winGeom = geometry();
  428. QRect setGeom =
  429. settings.value(c_key_win_size, QRect(winGeom.topLeft(), minimumSize()))
  430. .value<QRect>();
  431. if (setGeom.width() >= winGeom.width() &&
  432. setGeom.height() >= setGeom.height())
  433. setGeometry(setGeom);
  434. }
  435. //----------------------------------------------
  436. // -- hacks for debugging UI -- remove or refactor
  437. ui->actionPlay->setText(tr("Play"));
  438. ui->actionPlay->setIcon(QIcon(c_icon_play));
  439. ui->actionStop->setEnabled(true);
  440. ui->actionBack->setEnabled(true);
  441. ui->actionRepeat->setEnabled(false);
  442. ui->actionSave_audio_as->setEnabled(false);
  443. ui->actionPlay->setChecked(false);
  444. ui->actionRepeat->setChecked(false);
  445. ui->actionPlay->setEnabled(true);
  446. _menuPlugins = new QMenu("Plugins", this);
  447. this->ui->menuTools->addMenu(_menuPlugins);
  448. }
  449. void MainWindow::addPluginAction(QAction *action) {
  450. _menuPlugins->addAction(action);
  451. }
  452. void MainWindow::closeEvent(QCloseEvent *event) {
  453. // store settings
  454. settings.setValue(c_key_dir_score, _lastScoreDir);
  455. settings.setValue(c_key_dir_audio, _lastAudioDir);
  456. settings.setValue(c_key_win_size, geometry());
  457. settings.setValue(c_key_win_max, isMaximized());
  458. settings.setValue(c_key_show_lognum, _showNewLogNumber);
  459. settings.setValue(c_key_audio_codec, _audioExt);
  460. settings.setValue(c_key_dynpanel_on, _editorSplitter->sizes().last() > 0);
  461. event->accept();
  462. }
  463. MainWindow::~MainWindow() {
  464. delete ui;
  465. }
  466. //========================================================================================
  467. bool MainWindow::setController(qtauController &c, qtauSession &s) {
  468. // NOTE: bind what uses qtauSession only here (menu/toolbar button states etc)
  469. _doc = &s;
  470. _ctrl = &c;
  471. connect(_noteEditor, &qtauNoteEditor::editorEvent, _doc,
  472. &qtauSession::onUIEvent);
  473. connect(_dynTableModel, &DynTableModel::noteEffect, _doc,
  474. &qtauSession::onUIEvent);
  475. connect(&c, &qtauController::transportPositionChanged, this,
  476. &MainWindow::onTransportPositionChanged);
  477. connect(ui->actionNew, &QAction::triggered, &s, &qtauSession::onNewSession);
  478. connect(_doc, &qtauSession::dataReloaded, this, &MainWindow::onDocReloaded);
  479. connect(_doc, &qtauSession::modifiedStatus, this, &MainWindow::onDocStatus);
  480. connect(_doc, &qtauSession::undoStatus, this, &MainWindow::onUndoStatus);
  481. connect(_doc, &qtauSession::redoStatus, this, &MainWindow::onRedoStatus);
  482. connect(_doc, &qtauSession::onEvent, this, &MainWindow::onDocEvent);
  483. connect(ui->actionPlay, &QAction::triggered, &c,
  484. &qtauController::onRequestStartPlayback);
  485. connect(ui->actionStop, &QAction::triggered, &c,
  486. &qtauController::onRequestStopPlayback);
  487. connect(ui->actionBack, &QAction::triggered, &c,
  488. &qtauController::onRequestResetPlayback);
  489. connect(this, &MainWindow::loadUST, &c, &qtauController::onLoadUST);
  490. connect(this, &MainWindow::saveUST, &c, &qtauController::onSaveUST);
  491. connect(_piano, &qtauPiano::keyPressed, &c, &qtauController::pianoKeyPressed);
  492. connect(_piano, &qtauPiano::keyReleased, &c,
  493. &qtauController::pianoKeyReleased);
  494. connect(ui->actionJack, &QAction::triggered, this,
  495. &MainWindow::onActionJackTriggered);
  496. connect(_meter, &qtauMeterBar::barClicked, this,
  497. &MainWindow::meter_barClicked);
  498. connect(_mbe, &MeterBarLineEdit::keyPressHooked, this,
  499. &MainWindow::mbe_keyPressHooked);
  500. connect(ui->actionOpen_Voice_Directory,&QAction::triggered, &c,
  501. &qtauController::openVoiceDirectory);
  502. connect(ui->actionRescan_Voice_Directory,&QAction::triggered, &c,
  503. &qtauController::rescanVoiceDirectory);
  504. connect(&c,&qtauController::voiceListChanged,this,&MainWindow::voiceListChanged);
  505. //-----------------------------------------------------------------------
  506. // widget configuration - maybe read app settings here?
  507. _noteEditor->setRMBScrollEnabled(!ui->actionEdit_Mode->isChecked());
  508. _noteEditor->setEditingEnabled(ui->actionEdit_Mode->isChecked());
  509. _noteEditor->setFocus();
  510. if(c.needDownload()) onLog("no voice is installed, qtau will install defa",ELog::info);
  511. voiceListChanged();
  512. return true;
  513. }
  514. void MainWindow::voiceListChanged()
  515. {
  516. _singerSelect->clear();
  517. foreach (QString voice, _ctrl->voices()) {
  518. _singerSelect->addItem(voice);
  519. }
  520. }
  521. void MainWindow::updateUndoRedoTexts() {
  522. if (_doc->canUndo())
  523. ui->actionUndo->setText("Undo " + _doc->undoAction());
  524. else
  525. ui->actionUndo->setText("Undo");
  526. if (_doc->canRedo())
  527. ui->actionRedo->setText("Redo " + _doc->redoAction());
  528. else
  529. ui->actionRedo->setText("Redo");
  530. }
  531. void MainWindow::onOpenUST() {
  532. QString fileName = QFileDialog::getOpenFileName(
  533. this, tr("Open USTJ"), _lastScoreDir,
  534. tr("UTAU JSON Sequence Text Files (*.ustj)"));
  535. if (!fileName.isEmpty()) {
  536. _lastScoreDir = QFileInfo(fileName).absolutePath();
  537. emit loadUST(fileName);
  538. }
  539. }
  540. void MainWindow::onSaveUST() {
  541. if (_doc->documentFile().isEmpty())
  542. onSaveUSTAs();
  543. else
  544. emit saveUST(_doc->documentFile(), true);
  545. }
  546. void MainWindow::onSaveUSTAs() {
  547. QString fileName = QFileDialog::getSaveFileName(
  548. this, tr("Save USTJ"), _lastScoreDir,
  549. tr("UTAU JSON Sequence Text Files (*.ustj)"));
  550. if (!fileName.isEmpty()) {
  551. if (!fileName.endsWith(".ustj")) fileName += ".ustj";
  552. _lastScoreDir = QFileInfo(fileName).absolutePath();
  553. emit saveUST(fileName, true);
  554. }
  555. }
  556. void MainWindow::onTransportPositionChanged(float pos) {
  557. _noteEditor->setPlaybackPosition(pos * 480); // FIXME: do not hardcode utau
  558. }
  559. void MainWindow::onMIDIImport() {
  560. QString dir = settings.value("last_midi_dir","/tmp").toString();
  561. QString fileName =
  562. QFileDialog::getOpenFileName(this, tr("Import MIDI"),
  563. dir, // FIXME do not hardcode
  564. tr("MIDI Files (*.mid *.mid *.smf)"));
  565. if(fileName.length()) {
  566. QFileInfo f(fileName);
  567. dir = f.dir().path();
  568. settings.setValue("last_midi_dir",dir);
  569. _doc->setDocName("imported "+f.fileName());
  570. _doc->importMIDI(fileName);
  571. }
  572. }
  573. void MainWindow::onMIDIExport() {
  574. QString dir = settings.value("last_midi_dir","/tmp").toString();
  575. QString fileName =
  576. QFileDialog::getSaveFileName(this, tr("Export as MIDI"), dir,
  577. tr("MIDI Files (*.mid *.mid *.smf)"));
  578. if (!fileName.endsWith(".mid")) fileName += ".mid";
  579. if(fileName.length()) {
  580. QFileInfo f(fileName);
  581. dir = f.dir().path();
  582. settings.setValue("last_midi_dir",dir);
  583. _doc->exportMIDI(fileName);
  584. }
  585. }
  586. void MainWindow::notesVScrolled(int delta) {
  587. if (delta > 0 && _vscr->value() > 0) // scroll up
  588. delta = -_ns.note.height();
  589. else if (delta < 0 && _vscr->value() < _vscr->maximum()) // scroll down
  590. delta = _ns.note.height();
  591. else
  592. delta = 0;
  593. if (delta != 0) _vscr->setValue(_vscr->value() + delta);
  594. }
  595. void MainWindow::notesHScrolled(int delta) {
  596. if (delta > 0 && _hscr->value() > 0) // scroll left
  597. delta = -_ns.note.width();
  598. else if (delta < 0 && _hscr->value() < _hscr->maximum()) // scroll right
  599. delta = _ns.note.width();
  600. else
  601. delta = 0;
  602. if (delta != 0) _hscr->setValue(_hscr->value() + delta);
  603. }
  604. void MainWindow::vertScrolled(int delta) {
  605. _piano->setOffset(delta);
  606. _noteEditor->setVOffset(delta);
  607. }
  608. void MainWindow::horzScrolled(int delta) {
  609. _noteEditor->setHOffset(delta);
  610. _meter->setOffset(delta);
  611. _drawZone->setOffset(delta);
  612. }
  613. void MainWindow::onEditorRMBScrolled(QPoint mouseDelta, QPoint origOffset) {
  614. // moving editor space in reverse of mouse delta
  615. int hOff = qMax(qMin(origOffset.x() - mouseDelta.x(), _hscr->maximum()), 0);
  616. int vOff = qMax(qMin(origOffset.y() - mouseDelta.y(), _vscr->maximum()), 0);
  617. _hscr->setValue(hOff);
  618. _vscr->setValue(vOff);
  619. }
  620. void MainWindow::onEditorRequestOffset(QPoint off) {
  621. off.setX(qMax(qMin(off.x(), _hscr->maximum()), 0));
  622. off.setY(qMax(qMin(off.y(), _vscr->maximum()), 0));
  623. _hscr->setValue(off.x());
  624. _vscr->setValue(off.y());
  625. }
  626. void MainWindow::onPianoHeightChanged(int newHeight) {
  627. _vscr->setMaximum(_ns.note.height() * 12 * _ns.numOctaves - newHeight + 1);
  628. _vscr->setPageStep(_piano->geometry().height());
  629. }
  630. void MainWindow::onNoteEditorWidthChanged(int newWidth) {
  631. //_hscr->setMaximum(_ns.note.width() * _ns.notesInBar * cdef_bars - newWidth +
  632. //1);
  633. _hscr->setMaximum(_ns.note.width() * 4 * cdef_bars - newWidth + 1);
  634. _hscr->setPageStep(_noteEditor->geometry().width());
  635. }
  636. void MainWindow::onUndo() {
  637. if (_doc->canUndo())
  638. _doc->undo();
  639. else
  640. ui->actionUndo->setEnabled(false);
  641. updateUndoRedoTexts();
  642. }
  643. void MainWindow::onRedo() {
  644. if (_doc->canRedo())
  645. _doc->redo();
  646. else
  647. ui->actionRedo->setEnabled(false);
  648. updateUndoRedoTexts();
  649. }
  650. void MainWindow::onDelete() {
  651. _noteEditor->deleteSelected();
  652. }
  653. void MainWindow::onEditMode(bool toggled) {
  654. _noteEditor->setEditingEnabled(toggled);
  655. _noteEditor->setRMBScrollEnabled(!toggled);
  656. }
  657. void MainWindow::onGridSnap(bool toggled) {
  658. _noteEditor->setGridSnapEnabled(toggled);
  659. }
  660. void MainWindow::onQuantizeSelected(int index) {
  661. int newQuant = 4 * (int)(pow(2, index) + 0.001);
  662. if (newQuant != _ns.quantize) {
  663. _ns.quantize = newQuant;
  664. _noteEditor->configure(_ns);
  665. }
  666. }
  667. void MainWindow::onNotelengthSelected(int index) {
  668. int newNoteLength = 4 * (int)(pow(2, index) + 0.001);
  669. if (newNoteLength != _ns.length) {
  670. _ns.length = newNoteLength;
  671. _noteEditor->configure(_ns);
  672. }
  673. }
  674. void MainWindow::dynBtnLClicked() {
  675. qtauDynLabel *l = qobject_cast<qtauDynLabel *>(sender());
  676. if (l && (_fgDynLbl == 0 || l != _fgDynLbl)) {
  677. if (_fgDynLbl) {
  678. _fgDynLbl->setState(qtauDynLabel::off);
  679. _fgDynLbl->setStyleSheet(c_dynlbl_css_off);
  680. }
  681. if (l == _bgDynLbl) {
  682. _bgDynLbl->setState(qtauDynLabel::off);
  683. _bgDynLbl->setStyleSheet(c_dynlbl_css_off);
  684. _bgDynLbl = 0;
  685. }
  686. l->setStyleSheet(c_dynlbl_css_fg);
  687. _fgDynLbl = l;
  688. }
  689. }
  690. void MainWindow::dynBtnRClicked() {
  691. qtauDynLabel *l = qobject_cast<qtauDynLabel *>(sender());
  692. if (l) {
  693. if (_bgDynLbl != 0 && l == _bgDynLbl) {
  694. // clicking on same dynkey - switch it off
  695. _bgDynLbl->setState(qtauDynLabel::off);
  696. _bgDynLbl->setStyleSheet(c_dynlbl_css_off);
  697. _bgDynLbl = 0;
  698. } else { // clicking on other dynkey
  699. if (_bgDynLbl) { // switch off previous one, if any
  700. _bgDynLbl->setState(qtauDynLabel::off);
  701. _bgDynLbl->setStyleSheet(c_dynlbl_css_off);
  702. _bgDynLbl = 0;
  703. }
  704. if (l != _fgDynLbl) { // clicking on not-foreground dynkey
  705. l->setStyleSheet(c_dynlbl_css_bg);
  706. _bgDynLbl = l;
  707. }
  708. }
  709. }
  710. }
  711. void MainWindow::onLog(const QString &msg, ELog type) {
  712. QString color = "black";
  713. bool viewingLog = _tabs->currentIndex() == _tabs->count() - 1;
  714. switch (type) {
  715. case ELog::error:
  716. color = "red";
  717. break;
  718. case ELog::success:
  719. color = "green";
  720. break;
  721. default:
  722. break;
  723. }
  724. if (!viewingLog) {
  725. QTabBar *tb = const_cast<QTabBar *>(
  726. _tabs->tabBar()); // dirty hack I know, but no other way atm
  727. if (_showNewLogNumber) {
  728. tb->setTabText(tb->count() - 1,
  729. tr("Log") + QString(" (%1)").arg(_logNewMessages));
  730. _logNewMessages++;
  731. }
  732. if (type == ELog::error) {
  733. tb->setTabTextColor(tb->count() - 1, QColor(cdef_color_logtab_err));
  734. _logHasErrors = true;
  735. }
  736. }
  737. _logpad->moveCursor(QTextCursor::End);
  738. _logpad->insertHtml(
  739. QString("<pre style=\"color: %1;\">%2</pre><p></p>").arg(color).arg(msg));
  740. }
  741. void MainWindow::enableToolbars(bool enable) {
  742. foreach (QToolBar *t, _toolbars)
  743. t->setVisible(enable); // t->setEnabled(enable);
  744. }
  745. void MainWindow::onTabSelected(int index) {
  746. enableToolbars(index == 0);
  747. if (index == _tabs->count() - 1) {
  748. QTabBar *tb = const_cast<QTabBar *>(_tabs->tabBar());
  749. if (_logNewMessages > 0) {
  750. tb->setTabText(tb->count() - 1, tr("Log"));
  751. _logNewMessages = 0;
  752. }
  753. if (_logHasErrors) {
  754. tb->setTabTextColor(tb->count() - 1,
  755. _logTabTextColor); // set default color back
  756. _logHasErrors = false;
  757. }
  758. }
  759. }
  760. void MainWindow::onZoomed(int z) {
  761. // modify note data and send it to widgets
  762. _ns.note.setWidth(c_zoom_note_widths[z]);
  763. _meter->configure2(_ns);
  764. _piano->configure(_ns);
  765. _noteEditor->configure(_ns);
  766. _drawZone->configure(_ns);
  767. // modify scrollbar sizes and position
  768. double hscr_val = (double)_hscr->value() / _hscr->maximum();
  769. _hscr->setMaximum(_ns.note.width() * 4 * cdef_bars - _noteEditor->width() +
  770. 1);
  771. _hscr->setValue(_hscr->maximum() * hscr_val);
  772. _hscr->setSingleStep(_ns.note.width());
  773. horzScrolled(_hscr->value());
  774. }
  775. void MainWindow::onEditorZoomed(int delta) {
  776. if (delta != 0)
  777. if ((delta > 0 && _zoom->value() >= 0) ||
  778. (delta < 0 && _zoom->value() < c_zoom_num))
  779. _zoom->setValue(_zoom->value() + ((delta > 0) ? 1 : -1));
  780. }
  781. void MainWindow::onDocReloaded() {
  782. setWindowTitle(_doc->documentName() + " - QTau");
  783. _noteEditor->reset();
  784. }
  785. void MainWindow::onDocStatus(bool isModified) {
  786. QString newDocName = _doc->documentName();
  787. if (_docName != newDocName) _docName = newDocName;
  788. setWindowTitle((isModified ? "*" : "") + _docName + " - QTau");
  789. ui->actionSave->setEnabled(isModified);
  790. }
  791. void MainWindow::onUndoStatus(bool canUndo) {
  792. ui->actionUndo->setEnabled(canUndo);
  793. updateUndoRedoTexts();
  794. }
  795. void MainWindow::onRedoStatus(bool canRedo) {
  796. ui->actionRedo->setEnabled(canRedo);
  797. updateUndoRedoTexts();
  798. }
  799. void MainWindow::onDocEvent(qtauEvent *event) {
  800. // FIXME: why here
  801. QString singerName = _doc->getSingerName();
  802. if (singerName.length()) {
  803. int count = _singerSelect->count();
  804. for (int i = 0; i < count; i++) {
  805. if (singerName == _singerSelect->itemText(i)) {
  806. _singerSelect->setCurrentIndex(i);
  807. _ctrl->selectSinger(singerName);
  808. break;
  809. }
  810. }
  811. }
  812. QJsonArray arr = _doc->getTempoMap();
  813. if (arr.size()) {
  814. _ns.tmap->fromJson(arr);
  815. DEVLOG_DEBUG("update setup from json");
  816. updateSetup();
  817. }
  818. if (event->type() >= ENoteEvents::add && event->type() <= ENoteEvents::effect)
  819. _noteEditor->onEvent(event);
  820. else
  821. DEVLOG_ERROR("bad event");
  822. }
  823. void MainWindow::dragEnterEvent(QDragEnterEvent *event) {
  824. // accepting filepaths
  825. if (event->mimeData()->hasFormat("text/uri-list"))
  826. event->acceptProposedAction();
  827. }
  828. void MainWindow::dragMoveEvent(QDragMoveEvent *event) {
  829. // accepting filepaths
  830. if (event->mimeData()->hasFormat("text/uri-list"))
  831. event->acceptProposedAction();
  832. }
  833. void MainWindow::dropEvent(QDropEvent *event) {
  834. QList<QUrl> uris;
  835. foreach (const QByteArray &uriData,
  836. event->mimeData()->data("text/uri-list").split('\n'))
  837. if (!uriData.isEmpty())
  838. uris << QUrl::fromEncoded(uriData).toLocalFile().remove('\r');
  839. if (!uris.isEmpty()) {
  840. QFileInfo fi(uris.first().toString());
  841. if (uris.size() > 1)
  842. DEVLOG_DEBUG("Multiple URIs dropped, currently using only first one");
  843. if (fi.exists() && !fi.isDir() &&
  844. !fi.suffix().isEmpty()) // if it's an existing file with some extension
  845. {
  846. // maybe it's a note/lyrics file? (ust/vsq/vsqx/midi)
  847. if (fi.suffix() == "ustj") {
  848. emit loadUST(fi.absoluteFilePath());
  849. } else
  850. DEVLOG_ERROR("File extension not supported: " + fi.suffix());
  851. }
  852. }
  853. }
  854. // autoconneccted slot
  855. void MainWindow::on_actionPhoneme_Transformation_triggered() {
  856. _noteEditor->doPhonemeTransformation();
  857. }
  858. void MainWindow::onSingerSelected(int index) {
  859. _ctrl->selectSinger(_singerSelect->itemText(index));
  860. _doc->setSingerName(_singerSelect->itemText(index));
  861. }
  862. void MainWindow::onActionJackTriggered() {
  863. QAction *a = (QAction *)QObject::sender();
  864. _ctrl->setJackTranportEnabled(a->isChecked());
  865. }
  866. void MainWindow::markOverlappingNotes(quint64 id1, quint64 id2) {
  867. _noteEditor->setNoteColor(id1, EColor::red);
  868. _noteEditor->setNoteColor(id2, EColor::red);
  869. onLog("found overlapping notes", ELog::error);
  870. _noteEditor->update();
  871. }
  872. void MainWindow::updateNoteColors() {
  873. foreach (quint64 id, _noteEditor->getNoteIDs()) {
  874. _noteEditor->setNoteColor(id, EColor::none);
  875. }
  876. }
  877. void MainWindow::updateSetup() {
  878. // DEVLOG_DEBUG("MainWindow::updateSetup()");
  879. _piano->configure(_ns);
  880. _meter->configure2(_ns);
  881. _noteEditor->configure(_ns);
  882. if (_drawZone) _drawZone->configure(_ns);
  883. }
  884. void MainWindow::updateRecentFileActions() {
  885. QSettings settings;
  886. QStringList files = settings.value("recentFileList").toStringList();
  887. int numRecentFiles = qMin(files.size(), (int)MAXRECENTFILES);
  888. for (int i = 0; i < numRecentFiles; ++i) {
  889. QString fileName = QFileInfo(files[i]).fileName();
  890. QString text = tr("&%1 %2").arg(i + 1).arg(fileName);
  891. _recentFileActs[i]->setText(text);
  892. _recentFileActs[i]->setData(files[i]);
  893. _recentFileActs[i]->setVisible(true);
  894. }
  895. for (int j = numRecentFiles; j < MAXRECENTFILES; ++j)
  896. _recentFileActs[j]->setVisible(false);
  897. }
  898. void MainWindow::openRecentFile() {
  899. QAction *action = qobject_cast<QAction *>(sender());
  900. if (action) {
  901. QString ustFileName = action->data().toString();
  902. if (QFile(ustFileName).exists())
  903. loadUST(ustFileName);
  904. else {
  905. QSettings settings;
  906. QStringList files = settings.value("recentFileList").toStringList();
  907. files.removeOne(ustFileName);
  908. settings.setValue("recentFileList", files);
  909. updateRecentFileActions();
  910. QMessageBox msgBox;
  911. msgBox.setText("The file \"" + ustFileName + "\"does not exist.");
  912. msgBox.exec();
  913. }
  914. }
  915. }
  916. void MainWindow::on_actionSave_Last_Play_triggered() {
  917. QString lastPlay = _ctrl->lastPlay();
  918. DEVLOG_DEBUG("lastPlay: " + lastPlay);
  919. if (lastPlay.length()) {
  920. QString fileName = QFileInfo(lastPlay).fileName();
  921. QString fileNameCopy = QFileDialog::getSaveFileName(
  922. this, tr("Save Last Play"), _lastScoreDir, tr("WAV Files (*.wav)"));
  923. if (!fileNameCopy.isEmpty()) {
  924. if (!fileNameCopy.endsWith(".wav")) fileNameCopy += ".wav";
  925. DEVLOG_DEBUG("CP lastPlay: " + fileNameCopy);
  926. if (QFileInfo(fileNameCopy).exists()) QFile::remove(fileNameCopy);
  927. QFile::copy(lastPlay, fileNameCopy);
  928. }
  929. }
  930. }
  931. struct selectionRange MainWindow::getSelectionRange() {
  932. return _noteEditor->getSelectionRange();
  933. }
  934. void MainWindow::on_actionMusicXML_triggered() {
  935. QString dir = settings.value("last_musicxml_dir","/tmp").toString();
  936. QString fileName = QFileDialog::getOpenFileName(
  937. this, tr("Import MusicXML"),
  938. dir, // FIXME do not hardcode
  939. tr("MusicXML Files (*.xml)"));
  940. if(fileName.length()) {
  941. QFileInfo f(fileName);
  942. dir = f.dir().path();
  943. settings.setValue("last_musicxml_dir",dir);
  944. _doc->setDocName("imported "+f.fileName());
  945. _doc->importMusicXML(fileName);
  946. }
  947. }
  948. void MainWindow::on_actionUST_triggered() {
  949. QString dir = settings.value("last_ust_dir","/tmp").toString();
  950. QString fileName = QFileDialog::getOpenFileName(
  951. this, tr("Import MusicXML"),
  952. dir,
  953. tr("UTAU Sequence Text Files (*.ust)"));
  954. {
  955. QFileInfo f(fileName);
  956. dir = f.dir().path();
  957. settings.setValue("last_ust_dir",dir);
  958. _doc->setDocName("imported "+f.fileName());
  959. _doc->importUST(fileName);
  960. }
  961. }
  962. void MainWindow::updateTMap() {
  963. QJsonArray array;
  964. _ns.tmap->toJson(array);
  965. _doc->setTempoMap(array);
  966. updateSetup();
  967. }
  968. void MainWindow::on_actionEdit_Tempo_Time_Signature_triggered() {
  969. _ns.tmap->beginEditing();
  970. TempoDialog dialog(_ns.tmap, this);
  971. if (dialog.exec() == QDialog::Accepted) {
  972. updateTMap();
  973. } else {
  974. // UNDO changes
  975. _ns.tmap->undo();
  976. }
  977. }
  978. void MainWindow::meter_barClicked(int bar, tmlabel mode) {
  979. _current_bar = bar;
  980. _current_mode = mode;
  981. if (mode == TM_BPM) {
  982. _mbl->setText("[" + STR(bar) + "] BPM=");
  983. _mbe->setText("120"); // FIXME
  984. _mbe->setFocus();
  985. } else {
  986. _mbl->setText("[" + STR(bar) + "] SIG=");
  987. _mbe->setText("4/4");
  988. _mbe->setFocus();
  989. }
  990. }
  991. void MainWindow::mbe_keyPressHooked(int) {
  992. QString text = _mbe->text();
  993. if (_current_mode == TM_BPM || _current_mode == TM_SIG) {
  994. if (_ns.tmap->setLabel(_current_mode, _current_bar - 1, text)) {
  995. updateTMap();
  996. _mbl->setText("");
  997. _mbe->setText("");
  998. _current_mode = TM_ALL;
  999. }
  1000. } else {
  1001. QStringList cmd = text.split(" ");
  1002. if (cmd.length() == 2) {
  1003. if (cmd[0] == "BPM") meter_barClicked(QVariant(cmd[1]).toInt(), TM_BPM);
  1004. if (cmd[0] == "SIG") meter_barClicked(QVariant(cmd[1]).toInt(), TM_SIG);
  1005. }
  1006. }
  1007. }
  1008. void MainWindow::on_actionConnect2Jack_triggered()
  1009. {
  1010. bool jack_auto_connect = ui->actionConnect2Jack->isChecked();
  1011. settings.setValue("jack_auto_connect",jack_auto_connect);
  1012. QMessageBox msgbox;
  1013. msgbox.setText("jack_auto_connect changed. Please restart QTAU.");
  1014. msgbox.exec();
  1015. }
  1016. void MainWindow::onSelectionChanged()
  1017. {
  1018. auto sel = _noteEditor->getSelection();
  1019. bool en = _doc->updateModel(_dynTableModel,sel);
  1020. _dynTable->setEnabled(en);
  1021. }