hydrogen 1.2.3
MainForm.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <core/EventQueue.h>
24#include <core/Version.h>
25#include <core/Hydrogen.h>
28#include <core/Smf/SMF.h>
29#include <core/Timeline.h>
30#include <core/Helpers/Files.h>
31#include <core/Basics/Pattern.h>
38#include <core/NsmClient.h>
40
41#include "AboutDialog.h"
42#include "AudioEngineInfoForm.h"
43#include "CommonStrings.h"
44#include "ExportSongDialog.h"
45#include "ExportMidiDialog.h"
46#include "HydrogenApp.h"
47#include "Skin.h"
48#include "InstrumentRack.h"
49#include "MainForm.h"
50#include "PlayerControl.h"
51#include "LadspaFXProperties.h"
53#include "UndoActions.h"
54#include "Widgets/InfoBar.h"
55#include "Widgets/FileDialog.h"
56
57#include "Director.h"
58#include "Mixer/Mixer.h"
69
70#include <QtGui>
71#include <QtWidgets>
72
73#ifndef WIN32
74#include <sys/time.h>
75#include <sys/socket.h>
76#endif
77
78#ifdef H2CORE_HAVE_LASH
79#include <lash-1.0/lash/lash.h>
81#endif
82
83#include <memory>
84#include <cassert>
85
86using namespace H2Core;
87
89
90MainForm::MainForm( QApplication * pQApplication, QString sSongFilename )
91 : QMainWindow( nullptr )
92 , m_sPreviousAutoSaveFilename( "" )
93{
95 auto pHydrogen = H2Core::Hydrogen::get_instance();
96
97 setObjectName( "MainForm" );
98 setMinimumSize( QSize( 1000, 500 ) );
99
100#ifndef WIN32
101 if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigusr1Fd)) {
102 qFatal("Couldn't create HUP socketpair");
103 }
104 snUsr1 = new QSocketNotifier(sigusr1Fd[1], QSocketNotifier::Read, this);
105 connect(snUsr1, SIGNAL(activated(int)), this, SLOT( handleSigUsr1() ));
106#endif
107
108 m_pQApp = pQApplication;
109
110 m_pQApp->processEvents();
111
112 // When using the Non Session Management system, the new Song
113 // will be loaded by the NSM client singleton itself and not
114 // by the MainForm. The latter will just access the already
115 // loaded Song.
116 if ( ! pHydrogen->isUnderSessionManagement() ){
117 std::shared_ptr<H2Core::Song>pSong = nullptr;
118
119 if ( sSongFilename.isEmpty() ) {
120 if ( pPref->isRestoreLastSongEnabled() ) {
121 sSongFilename = pPref->getLastSongFilename();
122 }
123 }
124
125 bool bRet = false;
126 if ( !sSongFilename.isEmpty() ) {
127 if ( sSongFilename == H2Core::Filesystem::empty_song_path() ) {
129 } else {
130 bRet = HydrogenApp::openSong( sSongFilename );
131 }
132 }
133
134 if ( sSongFilename.isEmpty() || ! bRet ) {
136 HydrogenApp::openSong( pSong );
137 }
138 }
139
140 QFont font( pPref->getApplicationFontFamily(), getPointSize( pPref->getFontSize() ) );
141 setFont( font );
142 m_pQApp->setFont( font );
143
144 h2app = new HydrogenApp( this );
146 h2app->addEventListener( this );
151
152 h2app->showStatusBarMessage( tr("Hydrogen Ready.") );
153
155
156 // we need to do all this to support the keyboard playing
157 // for all the window modes
158 h2app->getMixer()->installEventFilter (this);
159 h2app->getPatternEditorPanel()->installEventFilter (this);
160 h2app->getSongEditorPanel()->installEventFilter (this);
161 h2app->getPlayerControl()->installEventFilter(this);
162 InstrumentEditorPanel::get_instance()->installEventFilter(this);
163 h2app->getAudioEngineInfoForm()->installEventFilter(this);
164 h2app->getDirector()->installEventFilter(this);
165 // h2app->getPlayListDialog()->installEventFilter(this);
166 installEventFilter( this );
167
168 connect( &m_AutosaveTimer, SIGNAL(timeout()), this, SLOT(onAutoSaveTimer()));
170
171#ifdef H2CORE_HAVE_LASH
172
173 if ( pPref->useLash() ){
174 LashClient* lashClient = LashClient::get_instance();
175 if (lashClient->isConnected())
176 {
177 // send alsa client id now since it can only be sent
178 // after the audio engine has been started.
179 if ( pPref->m_sMidiDriver == "ALSA" ) {
180 // infoLog("[LASH] Sending alsa seq id to LASH server");
181 lashClient->sendAlsaClientId();
182 }
183 // start timer for polling lash events
184 lashPollTimer = new QTimer(this);
185 connect( lashPollTimer, SIGNAL( timeout() ), this, SLOT( onLashPollTimer() ) );
186 lashPollTimer->start(500);
187 }
188 }
189#endif
190
191 //playlist display timer
192 QTimer *playlistDisplayTimer = new QTimer(this);
193 connect( playlistDisplayTimer, SIGNAL( timeout() ), this, SLOT( onPlaylistDisplayTimer() ) );
194 playlistDisplayTimer->start(30000); // update player control at
195 // ~ playlist display timer
196
197 //beatcouter
198 pHydrogen->setBcOffsetAdjust();
199
200 m_pUndoView = new QUndoView(h2app->m_pUndoStack);
201 m_pUndoView->setWindowTitle(tr("Undo history"));
202
203 //restore last playlist
204 if ( pPref->isRestoreLastPlaylistEnabled() &&
205 ! pPref->getLastPlaylistFilename().isEmpty() ) {
206 bool bLoadSuccessful = h2app->getPlayListDialog()->loadListByFileName(
207 pPref->getLastPlaylistFilename() );
208 if ( bLoadSuccessful ) {
209 if ( pPref->getPlaylistDialogProperties().visible ){
210 // If there was a playlist used during the last
211 // session and it was still visible/shown when closing
212 // Hydrogen, bring it up again.
214 }
215 }
216 else {
217 _ERRORLOG( QString( "Unable to load last playlist [%1]" )
218 .arg( pPref->getLastPlaylistFilename() ) );
219 }
220 }
221
222 // Must be done _after_ the creation of the HydrogenApp instance.
223 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
224
225 // Check whether the audio driver could be loaded based on the
226 // content of the config file
227 if ( pHydrogen->getAudioOutput() == nullptr ||
228 dynamic_cast<NullDriver*>(pHydrogen->getAudioOutput()) != nullptr ) {
229 QMessageBox::warning( this, "Hydrogen",
230 QString( "%1 [%2]\n%3" )
231 .arg( pCommonStrings->getAudioDriverStartError() )
232 .arg( pPref->m_sAudioDriver )
233 .arg( pCommonStrings->getAudioDriverErrorHint() ) );
234 }
235}
236
237
239{
240 auto pHydrogen = Hydrogen::get_instance();
241
242 // Remove the autosave file in case all modifications already have
243 // been written to disk.
244 if ( ! pHydrogen->getSong()->getIsModified() ) {
245 QFile file( getAutoSaveFilename() );
246 file.remove();
247 }
248
249 if ( (Hydrogen::get_instance()->getAudioEngine()->getState() == H2Core::AudioEngine::State::Playing) ) {
251 }
252
253 hide();
254
255 delete m_pUndoView;
256
257 if (h2app != nullptr) {
258 delete Playlist::get_instance();
259 delete h2app;
260 h2app = nullptr;
261 }
262
263}
264
269{
270 // menubar
271 QMenuBar *pMenubar = new QMenuBar( this );
272 pMenubar->setObjectName( "MainMenu" );
273 setMenuBar( pMenubar );
274
275 // FILE menu
276 m_pFileMenu = pMenubar->addMenu( tr( "Pro&ject" ) );
277
278 // Then under session management a couple of options will be named
279 // differently and some must be even omitted.
280 const bool bUnderSessionManagement = H2Core::Hydrogen::get_instance()->isUnderSessionManagement();
281
282 QString sLabelNew, sLabelOpen, sLabelOpenRecent, sLabelSaveAs, sLabelOpenDemo;
283
284 if ( bUnderSessionManagement ) {
285 /*: When Hydrogen is under session management the path the
286 song is stored to can not be changed by the user. This option
287 allows to replace the current song with an empty one.*/
288 sLabelNew = tr( "Replace With &New Song" );
289 /*: When Hydrogen is under session management the path the
290 song is stored to can not be changed by the user. This option
291 allows to replace the current song with one chosen by the
292 user via a file browser widget.*/
293 sLabelOpen = tr( "Imp&ort Into Session" );
294 /*: When Hydrogen is under session management the path the
295 song is stored to can not be changed by the user. This option
296 allows to replace the current song with one chosen recently
297 used by the user.*/
298 sLabelOpenRecent = tr( "Import &Recent Into Session" );
299 /*: When Hydrogen is under session management the path the
300 song is stored to can not be changed by the user. This option
301 allows the user store the current song in a .h2song anywhere
302 on her system. The filepath of the current song won't be
303 altered.*/
304 sLabelSaveAs = tr( "Export From Session &As..." );
305 } else {
306 sLabelNew = tr( "&New" );
307 sLabelOpen = tr( "&Open" );
308 sLabelOpenRecent = tr( "Open &Recent" );
309 sLabelSaveAs = tr( "Save &As..." );
310 sLabelOpenDemo = tr( "Open &Demo" );
311 }
312
313 m_pFileMenu->addAction( sLabelNew, this, SLOT( action_file_new() ), QKeySequence( "Ctrl+N" ) );
314
315 m_pFileMenu->addSeparator(); // -----
316
317 m_pFileMenu->addAction( tr( "Song Properties" ), this, SLOT( action_file_songProperties() ), QKeySequence( "" ) );
318
319 m_pFileMenu->addSeparator(); // -----
320
321 m_pFileMenu->addAction( sLabelOpen, this, SLOT( action_file_open() ), QKeySequence( "Ctrl+O" ) );
322 if ( ! bUnderSessionManagement ) {
323 m_pFileMenu->addAction( sLabelOpenDemo, this, SLOT( action_file_openDemo() ), QKeySequence( "Ctrl+D" ) );
324 }
325 m_pRecentFilesMenu = m_pFileMenu->addMenu( sLabelOpenRecent );
326
327 m_pFileMenu->addSeparator(); // -----
328
329 m_pFileMenu->addAction( tr( "&Save" ), this, SLOT( action_file_save() ), QKeySequence( "Ctrl+S" ) );
330 m_pFileMenu->addAction( sLabelSaveAs, this, SLOT( action_file_save_as() ), QKeySequence( "Ctrl+Shift+S" ) );
331
332 m_pFileMenu->addSeparator(); // -----
333
334 m_pFileMenu->addAction ( tr ( "Open &Pattern" ), this, SLOT ( action_file_openPattern() ), QKeySequence ( "Ctrl+Shift+P" ) );
335 m_pFileMenu->addAction( tr( "E&xport Pattern As..." ), this, SLOT( action_file_export_pattern_as() ), QKeySequence( "Ctrl+P" ) );
336
337 m_pFileMenu->addSeparator(); // -----
338
339 m_pFileMenu->addAction( tr( "Export &MIDI File" ), this, SLOT( action_file_export_midi() ), QKeySequence( "Ctrl+M" ) );
340 m_pFileMenu->addAction( tr( "&Export Song" ), this, SLOT( action_file_export() ), QKeySequence( "Ctrl+E" ) );
341 m_pFileMenu->addAction( tr( "Export &LilyPond File" ), this, SLOT( action_file_export_lilypond() ), QKeySequence( "Ctrl+L" ) );
342
343
344#ifndef Q_OS_MACX
345 m_pFileMenu->addSeparator(); // -----
346
347 m_pFileMenu->addAction( tr("&Quit"), this, SLOT( action_file_exit() ), QKeySequence( "Ctrl+Q" ) );
348#endif
349
351 connect( m_pRecentFilesMenu, SIGNAL( triggered(QAction*) ), this, SLOT( action_file_open_recent(QAction*) ) );
352 // ~ FILE menu
353
354 // Undo menu
355 m_pUndoMenu = pMenubar->addMenu( tr( "&Undo" ) );
356 m_pUndoMenu->addAction( tr( "&Undo" ), this, SLOT( action_undo() ), QKeySequence( "Ctrl+Z" ) );
357 m_pUndoMenu->addAction( tr( "&Redo" ), this, SLOT( action_redo() ), QKeySequence( "Shift+Ctrl+Z" ) );
358 m_pUndoMenu->addAction( tr( "Undo &History" ), this, SLOT( openUndoStack() ), QKeySequence( "" ) );
359
360 // DRUMKITS MENU
361 m_pDrumkitsMenu = pMenubar->addMenu( tr( "Drum&kits" ) );
362 m_pDrumkitsMenu->addAction( tr( "&New" ), this, SLOT( action_instruments_clearAll() ), QKeySequence( "" ) );
363 m_pDrumkitsMenu->addAction( tr( "&Open" ), this, SLOT( action_banks_open() ), QKeySequence( "" ) );
364 m_pDrumkitsMenu->addAction( tr( "&Properties" ), this, SLOT( action_banks_properties() ), QKeySequence( "" ) );
365
366 m_pDrumkitsMenu->addSeparator(); // -----
367
368 m_pDrumkitsMenu->addAction( tr( "&Save" ), this, SLOT( action_instruments_saveLibrary() ), QKeySequence( "" ) );
369 m_pDrumkitsMenu->addAction( tr( "Save &As" ), this, SLOT( action_instruments_saveAsLibrary() ), QKeySequence( "" ) );
370
371 m_pDrumkitsMenu->addSeparator(); // -----
372
373 m_pDrumkitsMenu->addAction( tr( "&Export" ), this, SLOT( action_instruments_exportLibrary() ), QKeySequence( "" ) );
374 m_pDrumkitsMenu->addAction( tr( "&Import" ), this, SLOT( action_instruments_importLibrary() ), QKeySequence( "" ) );
375 m_pDrumkitsMenu->addAction( tr( "On&line Import" ), this, SLOT( action_instruments_onlineImportLibrary() ), QKeySequence( "" ) );
376
377 // INSTRUMENTS MENU
378 m_pInstrumentsMenu = pMenubar->addMenu( tr( "In&struments" ) );
379 m_pInstrumentsMenu->addAction( tr( "Add &Instrument" ), this, SLOT( action_instruments_addInstrument() ), QKeySequence( "" ) );
380 m_pInstrumentsMenu->addAction( tr( "Clea&r All" ), this, SLOT( action_instruments_clearAll() ), QKeySequence( "" ) );
381
382 m_pInstrumentsMenu->addSeparator(); // -----
383
384 m_pInstrumentsMenu->addAction( tr( "Add &Component" ), this, SLOT( action_instruments_addComponent() ), QKeySequence( "" ) );
385
386 // VIEW MENU
387 m_pViewMenu = pMenubar->addMenu( tr( "&View" ) );
388
389 m_pViewPlaylistEditorAction = m_pViewMenu->addAction( tr("Play&list Editor"), this, SLOT( action_window_showPlaylistDialog() ), QKeySequence( "" ) );
390 m_pViewPlaylistEditorAction->setCheckable( true );
391 m_pViewDirectorAction = m_pViewMenu->addAction( tr("&Director"), this, SLOT( action_window_show_DirectorWidget() ), QKeySequence( "Alt+D" ) );
392 m_pViewDirectorAction->setCheckable( true );
393
394 m_pFileMenu->addSeparator();
395 m_pViewMixerAction = m_pViewMenu->addAction( tr("&Mixer"), this, SLOT( action_window_showMixer() ), QKeySequence( "Alt+M" ) );
396 m_pViewMixerAction->setCheckable( true );
397 update_mixer_checkbox(); // if checkbox need to be checked.
398
399 m_pViewMixerInstrumentRackAction = m_pViewMenu->addAction( tr("&Instrument Rack"), this, SLOT( action_window_showInstrumentRack() ), QKeySequence( "Alt+I" ) );
400 m_pViewMixerInstrumentRackAction->setCheckable( true );
401 update_instrument_checkbox( Preferences::get_instance()->getInstrumentRackProperties().visible );
402
403 m_pViewAutomationPathAction = m_pViewMenu->addAction( tr("&Automation Path"), this, SLOT( action_window_showAutomationArea() ), QKeySequence( "Alt+A" ) );
404 m_pViewAutomationPathAction->setCheckable( true );
406
407 m_pViewMenu->addSeparator(); // -----
408
409 m_pViewTimelineAction = m_pViewMenu->addAction( tr("&Timeline"), this, SLOT( action_window_showTimeline() ), QKeySequence( "" ) );
410 m_pViewTimelineAction->setCheckable( true );
411
412 m_pViewPlaybackTrackAction = m_pViewMenu->addAction( tr("&Playback Track"), this, SLOT( action_window_showPlaybackTrack() ), QKeySequence( "" ) );
413 m_pViewPlaybackTrackAction->setCheckable( true );
414
415 m_pViewPlaybackTrackActionGroup = new QActionGroup( this );
419
420 m_pViewMenu->addSeparator(); // -----
421
422 m_pViewMenu->addAction( tr("&Full screen"), this, SLOT( action_window_toggleFullscreen() ), QKeySequence( "Alt+F" ) );
423
424
425 // Options menu
426 m_pOptionsMenu = pMenubar->addMenu( tr( "&Options" ));
427
428 m_pInputModeMenu = m_pOptionsMenu->addMenu( tr( "Input &Mode" ) );
429 m_pInstrumentAction = m_pInputModeMenu->addAction( tr( "&Instrument" ), this, SLOT( action_inputMode_instrument() ), QKeySequence( "Ctrl+Alt+I" ) );
430 m_pInstrumentAction->setCheckable( true );
431
432 m_pDrumkitAction = m_pInputModeMenu->addAction( tr( "&Drumkit" ), this, SLOT( action_inputMode_drumkit() ), QKeySequence( "Ctrl+Alt+D" ) );
433 m_pDrumkitAction->setCheckable( true );
434
435 if( Preferences::get_instance()->__playselectedinstrument )
436 {
437 m_pInstrumentAction->setChecked( true );
438 m_pDrumkitAction->setChecked (false );
439 } else {
440 m_pInstrumentAction->setChecked( false );
441 m_pDrumkitAction->setChecked (true );
442 }
443
444 m_pOptionsMenu->addAction( tr("&Preferences"), this, SLOT( showPreferencesDialog() ), QKeySequence( "Alt+P" ) );
445
446 // ~ Tools menu
447
448
449 Logger *pLogger = Logger::get_instance();
450 if ( pLogger->bit_mask() >= 1 ) {
451 // DEBUG menu
452 m_pDebugMenu = pMenubar->addMenu( tr("De&bug") );
453 m_pDebugMenu->addAction( tr( "Show &Audio Engine Info" ), this, SLOT( action_debug_showAudioEngineInfo() ) );
454 m_pDebugMenu->addAction( tr( "Show &Filesystem Info" ), this, SLOT( action_debug_showFilesystemInfo() ) );
455
456 m_pLogLevelMenu = m_pDebugMenu->addMenu( tr( "&Log Level" ) );
457 m_pLogLevelMenu->addAction( tr( "&None" ), this, SLOT( action_debug_logLevel_none() ), QKeySequence( "" ) );
458 m_pLogLevelMenu->addAction( tr( "&Error" ), this, SLOT( action_debug_logLevel_info() ), QKeySequence( "" ) );
459 m_pLogLevelMenu->addAction( tr( "&Warning" ), this, SLOT( action_debug_logLevel_warn() ), QKeySequence( "" ) );
460 m_pLogLevelMenu->addAction( tr( "&Info" ), this, SLOT( action_debug_logLevel_info() ), QKeySequence( "" ) );
461 m_pLogLevelMenu->addAction( tr( "&Debug" ), this, SLOT( action_debug_logLevel_debug() ), QKeySequence( "" ) );
462
463 m_pDebugMenu->addAction( tr( "&Open Log File" ), this, SLOT( action_debug_openLogfile()) );
464
465 if(pLogger->bit_mask() == 8) { // hydrogen -V8 list object map in console
466 m_pDebugMenu->addAction( tr( "&Print Objects" ), this, SLOT( action_debug_printObjects() ) );
467 }
468 // ~ DEBUG menu
469 }
470
471 // INFO menu
472 m_pInfoMenu = pMenubar->addMenu( tr( "I&nfo" ) );
473 m_pInfoMenu->addAction( tr("User &Manual"), this, SLOT( showUserManual() ), QKeySequence( "Ctrl+?" ) );
474 m_pInfoMenu->addSeparator();
475 m_pInfoMenu->addAction( tr("&About"), this, SLOT( action_help_about() ), QKeySequence( tr("", "Info|About") ) );
476 m_pInfoMenu->addAction( tr("&Report Bug"), this, SLOT( action_report_bug() ));
477 m_pInfoMenu->addAction( tr("&Donate"), this, SLOT( action_donate() ));
478 // ~ INFO menu
479}
480
482 int nAutosavesPerHour = Preferences::get_instance()->m_nAutosavesPerHour;
483
484 if ( nAutosavesPerHour > 0 ) {
485 if ( nAutosavesPerHour > 360 ) {
486 ERRORLOG( QString( "Too many autosaves per hour set [%1]. Using 360 - once a second - instead." )
487 .arg( nAutosavesPerHour ) );
488 nAutosavesPerHour = 360;
489 }
490 m_AutosaveTimer.start( std::round( 60 * 60 * 1000 /
491 static_cast<float>(nAutosavesPerHour) ) );
492 } else {
493 DEBUGLOG( "Autosave disabled" );
494 }
495}
496
498{
499#ifdef H2CORE_HAVE_LASH
500 if ( Preferences::get_instance()->useLash() ){
502
503 if (!client->isConnected())
504 {
505 WARNINGLOG("[LASH] Not connected to server!");
506 return;
507 }
508
509 bool keep_running = true;
510
511 lash_event_t* event;
512
513 std::string songFilename;
514 QString filenameSong;
515 std::shared_ptr<Song> song = Hydrogen::get_instance()->getSong();
516 // Extra parentheses for -Wparentheses
517 while ( (event = client->getNextEvent()) ) {
518
519 switch (lash_event_get_type(event)) {
520
521 case LASH_Save_File:
522
523 INFOLOG("[LASH] Save file");
524
525 songFilename.append(lash_event_get_string(event));
526 songFilename.append("/hydrogen.h2song");
527
528 filenameSong = QString::fromLocal8Bit( songFilename.c_str() );
529 song->setFilename( filenameSong );
531
532 client->sendEvent(LASH_Save_File);
533
534 break;
535
536 case LASH_Restore_File:
537
538 songFilename.append(lash_event_get_string(event));
539 songFilename.append("/hydrogen.h2song");
540
541 INFOLOG( QString("[LASH] Restore file: %1")
542 .arg( songFilename.c_str() ) );
543
544 filenameSong = QString::fromLocal8Bit( songFilename.c_str() );
545
546 HydrogenApp::get_instance()->openSong( filenameSong );
547
548 client->sendEvent(LASH_Restore_File);
549
550 break;
551
552 case LASH_Quit:
553
554 // infoLog("[LASH] Quit!");
555 keep_running = false;
556
557 break;
558
559 default:
560 ;
561 // infoLog("[LASH] Got unknown event!");
562
563 }
564
565 lash_event_destroy(event);
566
567 }
568
569 if (!keep_running)
570 {
571 lashPollTimer->stop();
573 }
574 }
575#endif
576}
577
579{
580 QMessageBox donationDialog;
581 donationDialog.setText( tr( "Hydrogen is an open source project which is developed by multiple people in their spare time. By making a donation you can say 'thank you' to the involved persons." ) );
582 donationDialog.setStandardButtons( QMessageBox::Cancel );
583 donationDialog.addButton( tr( "&Donate!" ), QMessageBox::AcceptRole );
584
585 int nRet = donationDialog.exec();
586
587 if ( nRet == QMessageBox::AcceptRole ) {
588 QDesktopServices::openUrl(QUrl::fromEncoded("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=sebastian%2emoors%40gmail%2ecom&lc=DE&item_name=Hydrogen%20donation&no_note=0&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHostedGuest"));
589 }
590}
591
594{
595 bool proceed = handleUnsavedChanges();
596 if(!proceed) {
597 return false;
598 }
599 closeAll();
600 return true;
601}
602
603
604
606{
607 const bool bUnderSessionManagement = H2Core::Hydrogen::get_instance()->isUnderSessionManagement();
608
609 Hydrogen * pHydrogen = Hydrogen::get_instance();
611 pHydrogen->sequencer_stop();
612 }
613
614 bool proceed = handleUnsavedChanges();
615 if(!proceed) {
616 return;
617 }
618
619 std::shared_ptr<Song> pSong = Song::getEmptySong();
620
621 if ( bUnderSessionManagement ) {
622 // Just a single click will allow the user to discard the
623 // current song and replace it with an empty one with no way
624 // of undoing the action. Therefore, a warning popup will
625 // check whether the action was intentional.
626 QMessageBox confirmationBox;
627 confirmationBox.setText( tr( "Replace current song with empty one?" ) );
628 confirmationBox.setInformativeText( tr( "You won't be able to undo this action after saving the new song! Please export the current song from the session first in order to keep it." ) );
629 confirmationBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
630 confirmationBox.setDefaultButton( QMessageBox::No );
631
632 int confirmationChoice = confirmationBox.exec();
633
634 if ( confirmationChoice == QMessageBox::No ) {
635 return;
636 }
637 }
638
639 // Since the user explicitly chooses to open an empty song, we do
640 // not attempt to recover the autosave file generated while last
641 // working on an empty song but, instead, remove the corresponding
642 // autosave file in order to start fresh.
643 QFileInfo fileInfo( Filesystem::empty_song_path() );
644 QString sBaseName( fileInfo.completeBaseName() );
645 if ( sBaseName.startsWith( "." ) ) {
646 sBaseName.remove( 0, 1 );
647 }
648 QFileInfo autoSaveFile( QString( "%1/.%2.autosave.h2song" )
649 .arg( fileInfo.absoluteDir().absolutePath() )
650 .arg( sBaseName ) );
651 if ( autoSaveFile.exists() ) {
652 Filesystem::rm( autoSaveFile.absoluteFilePath() );
653 }
654
655 h2app->openSong( pSong );
656
657 // The drumkit of the new song will linked into the session
658 // folder during the next song save.
659 pHydrogen->setSessionDrumkitNeedsRelinking( true );
660}
661
662
663
665{
666 auto pHydrogen = Hydrogen::get_instance();
667 auto pSong = pHydrogen->getSong();
668
669 if ( pSong == nullptr ) {
670 return;
671 }
672
673 const bool bUnderSessionManagement = pHydrogen->isUnderSessionManagement();
674 if ( bUnderSessionManagement &&
675 pHydrogen->getSessionDrumkitNeedsRelinking() ) {
676 // When used under session management "save as" will be used
677 // for exporting and Hydrogen is allowed to
678 // be in a transient state which is not ready for export. This
679 // way the user is able to undo e.g. loading a drumkit by
680 // closing the session without storing and the overall state
681 // is not getting bricked during unexpected shut downs.
682 //
683 // We will prompt for saving the changes applied to the
684 // drumkit usage and require the user to exit this transient
685 // state first.
686 if ( QMessageBox::information( this, "Hydrogen",
687 tr( "\nThere have been recent changes to the drumkit settings.\n"
688 "The session needs to be saved before exporting will can be continued.\n" ),
689 QMessageBox::Save | QMessageBox::Cancel,
690 QMessageBox::Save )
691 == QMessageBox::Cancel ) {
692 INFOLOG( "Exporting cancelled at relinking" );
693 return;
694 }
695
697 }
698
700 if ( ! Filesystem::dir_writable( sPath, false ) ){
701 sPath = Filesystem::songs_dir();
702 }
703
704 //std::auto_ptr<QFileDialog> fd( new QFileDialog );
705 FileDialog fd(this);
706 fd.setFileMode( QFileDialog::AnyFile );
707 fd.setNameFilter( Filesystem::songs_filter_name );
708 fd.setAcceptMode( QFileDialog::AcceptSave );
709 fd.setDirectory( sPath );
710
711 if ( bUnderSessionManagement ) {
712 fd.setWindowTitle( tr( "Export song from Session" ) );
713 } else {
714 fd.setWindowTitle( tr( "Save song" ) );
715 }
716
717 fd.setSidebarUrls( fd.sidebarUrls() << QUrl::fromLocalFile( Filesystem::songs_dir() ) );
718
719 QString sDefaultFilename;
720
721 // Cachce a couple of things we have to restore when under session
722 // management.
723 QString sLastFilename = pSong->getFilename();
724 QString sLastLoadedDrumkitPath = pSong->getLastLoadedDrumkitPath();
725
726 if ( sLastFilename == Filesystem::empty_song_path() ) {
727 sDefaultFilename = Filesystem::default_song_name();
728 } else if ( sLastFilename.isEmpty() ) {
729 sDefaultFilename = pHydrogen->getSong()->getName();
730 } else {
731 QFileInfo fileInfo( sLastFilename );
732 sDefaultFilename = fileInfo.completeBaseName();
733 }
734 sDefaultFilename += Filesystem::songs_ext;
735
736 fd.selectFile( sDefaultFilename );
737
738 if (fd.exec() == QDialog::Accepted) {
739 QString sNewFilename = fd.selectedFiles().first();
740
741 if ( ! sNewFilename.isEmpty() ) {
742 Preferences::get_instance()->setLastSaveSongAsDirectory( fd.directory().absolutePath( ) );
743
744 if ( ! sNewFilename.endsWith( Filesystem::songs_ext ) ) {
745 sNewFilename += Filesystem::songs_ext;
746 }
747
748#ifdef H2CORE_HAVE_OSC
749 // In a session all main samples (last loaded drumkit) are
750 // taken from the session folder itself (either via a
751 // symlink or a copy of the whole drumkit). When exporting
752 // a song, these "local" references have to be replaced by
753 // global ones (drumkits in the system's or user's data
754 // folder).
755 if ( bUnderSessionManagement ) {
756 pHydrogen->setSessionIsExported( true );
757 int nRet = NsmClient::dereferenceDrumkit( pSong );
758 if ( nRet == -2 ) {
759 QMessageBox::warning( this, "Hydrogen",
760 tr( "Drumkit [%1] used in session could not found on your system. Please install it in to make the exported song work properly." )
761 .arg( pSong->getLastLoadedDrumkitName() ) );
762 }
763 }
764#endif
765
766 // We do not use the CoreActionController::saveSongAs
767 // function directly since action_file_save as does some
768 // additional checks and prompts the user a warning dialog
769 // if required.
770 action_file_save( sNewFilename );
771 }
772
773#ifdef H2CORE_HAVE_OSC
774 // When Hydrogen is under session management, we only copy a
775 // backup of the song to a different place but keep working on
776 // the original.
777 if ( bUnderSessionManagement ) {
778 pSong->setFilename( sLastFilename );
779 NsmClient::replaceDrumkitPath( pSong, sLastLoadedDrumkitPath );
780 h2app->showStatusBarMessage( tr("Song exported as: ") + sDefaultFilename );
781 pHydrogen->setSessionIsExported( false );
782 }
783 else {
784 h2app->showStatusBarMessage( tr("Song saved as: ") + sDefaultFilename );
785 }
786#else
787 h2app->showStatusBarMessage( tr("Song saved as: ") + sDefaultFilename );
788#endif
789
791 }
792}
793
794
795
797{
798 return action_file_save( "" );
799}
800void MainForm::action_file_save( const QString& sNewFilename )
801{
802 auto pHydrogen = H2Core::Hydrogen::get_instance();
803 auto pSong = pHydrogen->getSong();
804
805 if ( pSong == nullptr ) {
806 return;
807 }
808
809 auto pCoreActionController = pHydrogen->getCoreActionController();
810 QString sFilename = pSong->getFilename();
811
812 if ( sNewFilename.isEmpty() &&
813 ( sFilename.isEmpty() ||
814 sFilename == Filesystem::empty_song_path() ) ) {
815 // The empty song is treated differently in order to allow
816 // recovering changes and unsaved sessions. Therefore the
817 // users are ask to store a new song using a different file
818 // name.
819 return action_file_save_as();
820 }
821
822 if ( pSong->hasMissingSamples() ) {
823 if ( QMessageBox::information( this, "Hydrogen",
824 tr( "Some samples used by this song failed to load. If you save the song now "
825 "these missing samples will be removed from the song entirely.\n"
826 "Are you sure you want to save?" ),
827 QMessageBox::Save | QMessageBox::Cancel,
828 QMessageBox::Save )
829 == QMessageBox::Cancel ) {
830 return;
831 }
832 pSong->clearMissingSamples();
833 }
834
835 // Clear the pattern editor selection to resolve any duplicates
837
838 bool bSaved;
839 if ( sNewFilename.isEmpty() ) {
840 bSaved = pCoreActionController->saveSong();
841 } else {
842 bSaved = pCoreActionController->saveSongAs( sNewFilename );
843 }
844
845 if( ! bSaved ) {
846 QMessageBox::warning( this, "Hydrogen", tr("Could not save song.") );
847 } else {
848 h2app->showStatusBarMessage( tr("Song saved into") + QString(": ") +
849 sFilename );
850 }
851}
852
853
855{
856 if( !Preferences::get_instance()->__playselectedinstrument )
857 {
859 m_pDrumkitAction->setChecked (false );
860 }
861 m_pInstrumentAction->setChecked( true );
862}
863
865{
866 if( Preferences::get_instance()->__playselectedinstrument )
867 {
869 m_pInstrumentAction->setChecked( false );
870 }
871 m_pDrumkitAction->setChecked (true );
872}
873
875 AboutDialog *dialog = new AboutDialog( nullptr );
876 dialog->exec();
877}
878
880{
881 QDesktopServices::openUrl(QString("https://github.com/hydrogen-music/hydrogen/issues"));
882}
883
884// Find and open (a translation of) the manual appropriate for the user's preferences and locale
886{
887 QString sDocPath = H2Core::Filesystem::doc_dir();
888 QString sPreferredLanguage = Preferences::get_instance()->getPreferredLanguage();
889 QStringList languages;
890
891 if ( !sPreferredLanguage.isNull() ) {
892 languages << sPreferredLanguage;
893 }
894 languages << QLocale::system().uiLanguages()
895 << "en"; // English as fallback
896
897 // Find manual in filesystem
898 for ( QString sLang : languages ) {
899 QStringList sCandidates ( sLang );
900 QStringList s = sLang.split('-');
901 if ( s.size() != 1 ) {
902 sCandidates << s[0];
903 }
904 for ( QString sCandidate : sCandidates ) {
905 QString sManualPath = QString( "%1/manual_%2.html" ) .arg( sDocPath ).arg( sCandidate );
906 if ( Filesystem::file_exists( sManualPath ) ) {
907 QDesktopServices::openUrl( QUrl::fromLocalFile( sManualPath ) );
908 return;
909 }
910 }
911 }
912
913 // No manual found, not even the default English one. This must be a broken installation, so let's open
914 // the online manual as a sensible fallback option.
915
916 QDesktopServices::openUrl( QString( "http://hydrogen-music.org/documentation/manual/manual_en.html" ) );
917
918}
919
921{
922 Hydrogen *pHydrogen = Hydrogen::get_instance();
923
924 if ( ( Hydrogen::get_instance()->getAudioEngine()->getState() == H2Core::AudioEngine::State::Playing ) ) {
926 }
927
928 if ( nPatternRow == -1 ) {
929 nPatternRow = pHydrogen->getSelectedPatternNumber();
930 }
931
932 if ( nPatternRow == -1 ) {
933 QMessageBox::warning( this, "Hydrogen", tr("No pattern selected.") );
934 return;
935 }
936
937 std::shared_ptr<Song> pSong = pHydrogen->getSong();
938
939 Pattern *pPattern = pSong->getPatternList()->get( nPatternRow );
940
942 if ( ! Filesystem::dir_writable( sPath, false ) ){
943 sPath = Filesystem::patterns_dir();
944 }
945
946 QString title = tr( "Save Pattern as ..." );
947 FileDialog fd(this);
948 fd.setWindowTitle( title );
949 fd.setDirectory( sPath );
950 fd.selectFile( pPattern->get_name() );
951 fd.setFileMode( QFileDialog::AnyFile );
952 fd.setNameFilter( Filesystem::patterns_filter_name );
953 fd.setAcceptMode( QFileDialog::AcceptSave );
954 fd.setSidebarUrls( fd.sidebarUrls() << QUrl::fromLocalFile( Filesystem::patterns_dir() ) );
955 fd.setDefaultSuffix( Filesystem::patterns_ext );
956
957 if ( fd.exec() != QDialog::Accepted ) {
958 return;
959 }
960
961 QFileInfo fileInfo = fd.selectedFiles().first();
963 QString filePath = fileInfo.absoluteFilePath();
964
965 QString originalName = pPattern->get_name();
966 pPattern->set_name( fileInfo.baseName() );
967 QString path = Files::savePatternPath( filePath, pPattern, pSong, pHydrogen->getLastLoadedDrumkitName() );
968 pPattern->set_name( originalName );
969
970 if ( path.isEmpty() ) {
971 QMessageBox::warning( this, "Hydrogen", tr("Could not export pattern.") );
972 return;
973 }
974
975 h2app->showStatusBarMessage( tr( "Pattern saved." ) );
976
977 if ( filePath.indexOf( Filesystem::patterns_dir() ) == 0 ) {
979
980 }
981}
982
985 if ( ! Filesystem::dir_readable( sPath, false ) ){
986 sPath = Filesystem::songs_dir();
987 }
988
989 QString sWindowTitle;
990 if ( H2Core::Hydrogen::get_instance()->isUnderSessionManagement() ) {
991 sWindowTitle = tr( "Import song into Session" );
992 } else {
993 sWindowTitle = tr( "Open song" );
994 }
995
996 openSongWithDialog( sWindowTitle, sPath, false );
997}
998
999
1001{
1002 Hydrogen *pHydrogen = Hydrogen::get_instance();
1003 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1004
1006 if ( ! Filesystem::dir_readable( sPath, false ) ){
1007 sPath = Filesystem::patterns_dir();
1008 }
1009
1010 FileDialog fd(this);
1011 fd.setAcceptMode( QFileDialog::AcceptOpen );
1012 fd.setFileMode ( QFileDialog::ExistingFiles );
1013 fd.setDirectory ( sPath );
1014 fd.setNameFilter( Filesystem::patterns_filter_name );
1015
1016 fd.setWindowTitle ( tr ( "Open Pattern" ) );
1017
1018 if ( fd.exec() == QDialog::Accepted ) {
1019 Preferences::get_instance()->setLastOpenPatternDirectory( fd.directory().absolutePath() );
1020
1021 for ( auto& ssFilename : fd.selectedFiles() ) {
1022
1023 auto pNewPattern = Pattern::load_file( ssFilename, pSong->getInstrumentList() );
1024 if ( pNewPattern == nullptr ) {
1025 QMessageBox::critical( this, "Hydrogen", HydrogenApp::get_instance()->getCommonStrings()->getPatternLoadError() );
1026 } else {
1027 int nRow;
1028 if ( pHydrogen->getSelectedPatternNumber() == -1 ) {
1029 nRow = pSong->getPatternList()->size();
1030 } else {
1031 nRow = pHydrogen->getSelectedPatternNumber() + 1;
1032 }
1033
1034 SE_insertPatternAction* pAction =
1035 new SE_insertPatternAction( nRow, pNewPattern );
1036 HydrogenApp::get_instance()->m_pUndoStack->push( pAction );
1037 }
1038 }
1039 }
1040}
1041
1043 QString sWindowTitle;
1044 if ( ! H2Core::Hydrogen::get_instance()->isUnderSessionManagement() ) {
1045 sWindowTitle = tr( "Open Demo Song" );
1046 } else {
1047 sWindowTitle = tr( "Import Demo Song into Session" );
1048 }
1049
1050 openSongWithDialog( sWindowTitle, Filesystem::demos_dir(), true );
1051}
1052
1054
1055 auto pHydrogen = Hydrogen::get_instance();
1056 if ( pHydrogen->getAudioEngine()->getState() ==
1058 pHydrogen->sequencer_stop();
1059 }
1060
1061 return handleUnsavedChanges();
1062}
1063
1064void MainForm::openSongWithDialog( const QString& sWindowTitle, const QString& sPath, bool bIsDemo ) {
1065 // Check for unsaved changes.
1066 if ( ! prepareSongOpening() ) {
1067 return;
1068 }
1069
1070 auto pHydrogen = Hydrogen::get_instance();
1071
1072 FileDialog fd(this);
1073 fd.setAcceptMode( QFileDialog::AcceptOpen );
1074 fd.setFileMode( QFileDialog::ExistingFile );
1075 fd.setDirectory( sPath );
1076 fd.setNameFilter( Filesystem::songs_filter_name );
1077 fd.setWindowTitle( sWindowTitle );
1078
1079 QString sFilename;
1080 if ( fd.exec() == QDialog::Accepted ) {
1081 if ( ! bIsDemo ) {
1082 Preferences::get_instance()->setLastOpenSongDirectory( fd.directory().absolutePath() );
1083 }
1084 sFilename = fd.selectedFiles().first();
1085 }
1086
1087 if ( !sFilename.isEmpty() ) {
1088 HydrogenApp::get_instance()->openSong( sFilename );
1089 if ( bIsDemo &&
1090 ! pHydrogen->isUnderSessionManagement() ) {
1091 pHydrogen->getSong()->setFilename( "" );
1092 }
1093 }
1094}
1095
1100
1105
1106// function to update director status in menu bar
1108{
1109 bool isVisible = HydrogenApp::get_instance()->getPlayListDialog()->isVisible();
1110 m_pViewPlaylistEditorAction->setChecked( isVisible );
1111}
1112
1117
1118// function to update director status in menu bar
1120{
1121 bool isVisible = HydrogenApp::get_instance()->getDirector()->isVisible();
1122 m_pViewDirectorAction->setChecked( isVisible );
1123}
1124
1126{
1127 if( this->isFullScreen() ){
1128 this->showNormal();
1129 } else {
1130 this->showFullScreen();
1131 }
1132}
1133
1135{
1136 bool isVisible = HydrogenApp::get_instance()->getMixer()->isVisible();
1137 h2app->showMixer( !isVisible );
1138}
1139
1140// function to update mixer status in menu bar
1142{
1143 bool isVisible = HydrogenApp::get_instance()->getMixer()->isVisible();
1144 m_pViewMixerAction->setChecked( isVisible );
1145}
1146
1151
1156
1158{
1159 Logger* pLogger = Logger::get_instance();
1160 pLogger->set_bit_mask( Logger::None );
1161}
1162
1168
1174
1180
1186
1188{
1189 QDesktopServices::openUrl( Filesystem::log_file_path() );
1190}
1191
1192
1197{
1198 bool isVisible = h2app->getSongEditorPanel()->isVisible();
1199 h2app->getSongEditorPanel()->setHidden( isVisible );
1200}
1201
1206
1207
1212
1217
1218
1219
1225
1226
1228{
1229 bool bIsOkPressed;
1230 QString sNewName = QInputDialog::getText( this, "Hydrogen", tr( "Component name" ), QLineEdit::Normal, "New Component", &bIsOkPressed );
1231 if ( bIsOkPressed ) {
1232 Hydrogen *pHydrogen = Hydrogen::get_instance();
1233
1234 auto pDrumkitComponent = std::make_shared<DrumkitComponent>( InstrumentEditor::findFreeDrumkitComponentId(), sNewName );
1235 pHydrogen->getSong()->getComponents()->push_back( pDrumkitComponent );
1236
1238
1239 // this will force an update...
1241
1242#ifdef H2CORE_HAVE_JACK
1243 pHydrogen->renameJackPorts(pHydrogen->getSong());
1244#endif
1245 }
1246 else {
1247 // user entered nothing or pressed Cancel
1248 }
1249}
1250
1251
1253{
1254 SoundLibraryOpenDialog dialog( this );
1255 dialog.exec();
1256}
1257
1258
1260{
1261 switch(
1262 QMessageBox::information( this, //NOLINT
1263 "Hydrogen",
1264 tr("Clear all instruments?"),
1265 QMessageBox::Cancel | QMessageBox::Ok,
1266 QMessageBox::Cancel)) {
1267 case QMessageBox::Ok:
1268 // ok btn pressed
1269 break;
1270 case QMessageBox::Cancel:
1271 // cancel btn pressed
1272 return;
1273 default:
1274 // Not reached
1275 return;
1276 }
1277
1278 // Remove all instruments
1279 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
1280 auto pList = pSong->getInstrumentList();
1281 for (uint i = pList->size(); i > 0; i--) {
1283 }
1284
1286}
1287
1289{
1290 Hydrogen* pHydrogen = Hydrogen::get_instance();
1291 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1292 auto pSelectedInstrument = pSong->getInstrumentList()->get( nInstrument );
1293 if ( pSelectedInstrument == nullptr ) {
1294 ERRORLOG( "No instrument selected" );
1295 return;
1296 }
1297
1298 std::list< Note* > noteList;
1299 PatternList *pPatternList = pSong->getPatternList();
1300
1301 QString sInstrumentName = pSelectedInstrument->get_name();
1302 QString sDrumkitPath = pSelectedInstrument->get_drumkit_path();
1303
1304 for ( int i = 0; i < pPatternList->size(); i++ ) {
1305 const H2Core::Pattern *pPattern = pPatternList->get(i);
1306 const Pattern::notes_t* notes = pPattern->get_notes();
1308 Note *pNote = it->second;
1309 assert( pNote );
1310 if ( pNote->get_instrument() == pSelectedInstrument ) {
1311 pNote->set_pattern_idx( i );
1312 noteList.push_back( pNote );
1313 }
1314 }
1315 }
1316
1317 SE_deleteInstrumentAction *pAction =
1318 new SE_deleteInstrumentAction( noteList, sDrumkitPath,
1319 sInstrumentName, nInstrument );
1320 HydrogenApp::get_instance()->m_pUndoStack->push( pAction );
1321}
1322
1323
1325
1326 auto pHydrogen = H2Core::Hydrogen::get_instance();
1327 auto pSong = pHydrogen->getSong();
1328
1329 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()
1330 ->getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
1331
1332 if ( pDrumkit != nullptr ){
1333
1334 auto pNewDrumkit = std::make_shared<Drumkit>( pDrumkit );
1335 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
1336 pNewDrumkit->set_components( pSong->getComponents() );
1337 SoundLibraryExportDialog exportDialog( this, pNewDrumkit );
1338 exportDialog.exec();
1339 }
1340 else {
1341 QMessageBox::warning( this, "Hydrogen", QString( "%1 [%2]")
1342 .arg( HydrogenApp::get_instance()->getCommonStrings()->getSoundLibraryFailedPreDrumkitLoad() )
1343 .arg( pHydrogen->getLastLoadedDrumkitPath() ) );
1344 }
1345}
1346
1347
1348
1349
1351{
1352 SoundLibraryImportDialog dialog( this, false );
1353 dialog.exec();
1354}
1355
1356
1358{
1359 SoundLibraryImportDialog dialog( this, true );
1360 dialog.exec();
1361}
1362
1363
1365{
1366 auto pHydrogen = Hydrogen::get_instance();
1367 auto pSong = pHydrogen->getSong();
1368
1369 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()->
1370 getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
1371 auto drumkitType = Filesystem::determineDrumkitType(
1372 pHydrogen->getLastLoadedDrumkitPath() );
1373
1374 // In case the user does not have write access to the folder of
1375 // pDrumkit, the save as dialog will be opened.
1376 if ( pDrumkit != nullptr &&
1377 ( drumkitType == Filesystem::DrumkitType::User ||
1378 drumkitType == Filesystem::DrumkitType::SessionReadWrite ) ) {
1379 auto pNewDrumkit = std::make_shared<Drumkit>(pDrumkit);
1380 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
1381 pNewDrumkit->set_components( pSong->getComponents() );
1382
1383 if ( ! HydrogenApp::checkDrumkitLicense( pNewDrumkit ) ) {
1384 ERRORLOG( "User cancelled dialog due to licensing issues." );
1385 return;
1386 }
1387
1388 if ( ! pNewDrumkit->save() ) {
1389 QMessageBox::information( this, "Hydrogen", tr( "Saving of this library failed."));
1390 return;
1391 }
1392
1393 pHydrogen->getSoundLibraryDatabase()->updateDrumkits();
1394 }
1395 else {
1397 }
1398}
1399
1400
1405
1406
1407
1408
1409
1410
1411
1415void MainForm::closeEvent( QCloseEvent* ev )
1416{
1417 if ( action_file_exit() == false ) {
1418 // don't close!!!
1419 ev->ignore();
1420 return;
1421 }
1422
1423 ev->accept();
1424}
1425
1426
1427
1429{
1430 if ( Hydrogen::get_instance()->getAudioEngine()->getState() == H2Core::AudioEngine::State::Playing ) {
1432 }
1433
1434 ExportSongDialog *dialog = new ExportSongDialog(this);
1435 dialog->exec();
1436 delete dialog;
1437}
1438
1439
1440
1442{
1444 pPanel->setHidden( pPanel->isVisible() );
1445 update_instrument_checkbox( pPanel->isVisible() );
1446}
1447
1449{
1450 m_pViewMixerInstrumentRackAction->setChecked( show );
1451}
1452
1454{
1456
1457 if(pref->getShowAutomationArea()){
1458 m_pViewAutomationPathAction->setChecked(true);
1459 } else {
1460 m_pViewAutomationPathAction->setChecked(false);
1461 }
1462}
1463
1465{
1467
1468 // Note that the ActionGroup unchecks the other menu item automatically
1469 if ( pPref->getShowPlaybackTrack() ) {
1470 m_pViewPlaybackTrackAction->setChecked( true );
1471 } else {
1472 m_pViewTimelineAction->setChecked( true );
1473 }
1474}
1475
1477 // save window properties in the preferences files
1478 Preferences *pPreferences = Preferences::get_instance();
1479
1480 // mainform
1481 pPreferences->setMainFormProperties( h2app->getWindowProperties( this ) );
1482 // Save mixer properties
1484 // save pattern editor properties
1486 // save song editor properties
1489 // save audio engine info properties
1491
1492 pPreferences->setPlaylistDialogProperties(
1494 pPreferences->setDirectorProperties(
1496
1497#ifdef H2CORE_HAVE_LADSPA
1498 // save LADSPA FX window properties
1499 for (uint nFX = 0; nFX < MAX_FX; nFX++) {
1500 pPreferences->setLadspaProperties( nFX, h2app->getWindowProperties( h2app->getLadspaFXProperties( nFX ) ) );
1501 }
1502#endif
1503}
1504
1506 // Store the last playlist in the Preferences in order to allow to
1507 // reopen it at startup.
1509 Playlist::get_instance()->getFilename() );
1510
1512 m_pQApp->quit();
1513}
1514
1515
1517 auto pPref = H2Core::Preferences::get_instance();
1518
1519 if ( changes & H2Core::Preferences::Changes::Font ) {
1520
1521 QFont font( pPref->getApplicationFontFamily(), getPointSize( pPref->getFontSize() ) );
1522 m_pQApp->setFont( font );
1523 menuBar()->setFont( font );
1524
1525 m_pFileMenu->setFont( font );
1526 m_pUndoMenu->setFont( font );
1527 m_pDrumkitsMenu->setFont( font );
1528 m_pInstrumentsMenu->setFont( font );
1529 m_pViewMenu->setFont( font );
1530 m_pOptionsMenu->setFont( font );
1531 if ( m_pDebugMenu != nullptr ) {
1532 m_pDebugMenu->setFont( font );
1533 }
1534 m_pInfoMenu->setFont( font );
1535
1537 }
1538
1539 if ( changes & H2Core::Preferences::Changes::Colors ) {
1541 }
1542
1545 }
1546}
1547
1548
1549// keybindings..
1550
1552{
1553 switch ( Hydrogen::get_instance()->getAudioEngine()->getState() ) {
1556 break;
1557
1560 break;
1561
1562 default:
1563 ERRORLOG( "[MainForm::onPlayStopAccelEvent()] Unhandled case." );
1564 }
1565}
1566
1567
1568
1570{
1571 Hydrogen* pHydrogen = Hydrogen::get_instance();
1572 pHydrogen->getCoreActionController()->locateToColumn( 0 );
1573}
1574
1575
1576
1578 auto pHydrogen = Hydrogen::get_instance();
1579 auto pAudioEngine = pHydrogen->getAudioEngine();
1580
1581 pHydrogen->getSong()->setBpm( pAudioEngine->getTransportPosition()->getBpm() + 0.1 );
1582
1583 pAudioEngine->lock( RIGHT_HERE );
1584 pAudioEngine->setNextBpm( pAudioEngine->getTransportPosition()->getBpm() + 0.1 );
1585 pAudioEngine->unlock();
1586
1588}
1589
1590
1591
1593 auto pHydrogen = Hydrogen::get_instance();
1594 auto pAudioEngine = pHydrogen->getAudioEngine();
1595
1596 pHydrogen->getSong()->setBpm( pAudioEngine->getTransportPosition()->getBpm() - 0.1 );
1597
1598 pAudioEngine->lock( RIGHT_HERE );
1599 pAudioEngine->setNextBpm( pAudioEngine->getTransportPosition()->getBpm() - 0.1 );
1600 pAudioEngine->unlock();
1601
1603}
1604
1606{
1607 m_pRecentFilesMenu->clear();
1608
1610 std::vector<QString> recentUsedSongs = pPref->getRecentFiles();
1611
1612 QString sFilename;
1613
1614 for ( uint i = 0; i < recentUsedSongs.size(); ++i ) {
1615 sFilename = recentUsedSongs[ i ];
1616
1617 if ( !sFilename.isEmpty() ) {
1618 QAction *pAction = new QAction( this );
1619 pAction->setText( sFilename );
1620 m_pRecentFilesMenu->addAction( pAction );
1621 }
1622 }
1623}
1624
1625
1626
1628{
1629 // Check for unsaved changes.
1630 if ( ! prepareSongOpening() ) {
1631 return;
1632 }
1633
1634 HydrogenApp::get_instance()->openSong( pAction->text() );
1635}
1636
1638{
1639 if ( Hydrogen::get_instance()->getSong()->hasMissingSamples() ) {
1641 m_pMissingSamplesInfoBar->setTitle( tr( "Song drumkit samples" ) );
1642 m_pMissingSamplesInfoBar->setText( tr( "Some samples used in this song could not be loaded. This may be because it uses an older default drumkit. This might be fixed by opening a new drumkit." ) );
1643
1644 QPushButton *fix = m_pMissingSamplesInfoBar->addButton( tr( "Open drumkit" ) );
1645 QObject::connect( fix, SIGNAL( clicked() ),
1646 this, SLOT( onFixMissingSamples() ) );
1648 }
1649}
1650
1651
1653{
1654 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
1655 if ( pSong->getInstrumentList()->has_all_midi_notes_same() ) {
1656 WARNINGLOG( "Incorrect MIDI setup" );
1657
1660 m_pMidiSetupInfoBar->setTitle( tr("MIDI setup advice") );
1661 m_pMidiSetupInfoBar->setText( tr("MIDI out notes are not configured for this drumkit, so exporting this song to MIDI file may fail. Would you like Hydrogen to automatically fix this by assigning default values?") );
1662 QPushButton *fix = m_pMidiSetupInfoBar->addButton( tr("Set default values") );
1663 QObject::connect( fix, SIGNAL(clicked()), this, SLOT(onFixMidiSetup()) );
1664 m_pMidiSetupInfoBar->show();
1665 } else {
1666 m_pMidiSetupInfoBar = nullptr;
1667 }
1668}
1669
1671{
1672 //Make sure that all directories which are needed by Hydrogen are existing and usable.
1673 QString sTempDir = Filesystem::tmp_dir();
1674
1675 if( !Filesystem::dir_writable(sTempDir))
1676 {
1677 QMessageBox::warning( this, "Hydrogen", tr("Could not write to temporary directory %1.").arg(sTempDir) );
1678 }
1679}
1680
1682{
1683 INFOLOG( "Fixing MIDI setup" );
1684 auto pHydrogen = Hydrogen::get_instance();
1685 auto pSong = pHydrogen->getSong();
1686 if ( pSong != nullptr ) {
1687 pSong->getInstrumentList()->set_default_midi_out_notes();
1688 pHydrogen->setIsModified( true );
1689
1690 m_pMidiSetupInfoBar->hide();
1691 }
1692}
1693
1694
1696{
1697 INFOLOG( "Fixing MIDI setup" );
1698 SoundLibraryOpenDialog dialog( this );
1699 dialog.exec();
1700
1702}
1703
1704
1706{
1707
1708 QString loc = QLocale::system().name();
1709 int instr = 0;
1710
1712 //locale for keyboardlayout QWERTZ
1713 // de_DE, de_AT, de_LU, de_CH, de
1714
1715 //locale for keyboardlayout AZERTY
1716 // fr_BE, fr_CA, fr_FR, fr_LU, fr_CH
1717
1718 //locale for keyboardlayout QWERTY
1719 // en_GB, en_US, en_ZA, usw.
1720
1721 if ( loc.contains( "de" ) || loc.contains( "DE" )){
1722 keycodeInstrumentMap[Qt::Key_Y] = instr++;
1723 keycodeInstrumentMap[Qt::Key_S] = instr++;
1724 keycodeInstrumentMap[Qt::Key_X] = instr++;
1725 keycodeInstrumentMap[Qt::Key_D] = instr++;
1726 keycodeInstrumentMap[Qt::Key_C] = instr++;
1727 keycodeInstrumentMap[Qt::Key_V] = instr++;
1728 keycodeInstrumentMap[Qt::Key_G] = instr++;
1729 keycodeInstrumentMap[Qt::Key_B] = instr++;
1730 keycodeInstrumentMap[Qt::Key_H] = instr++;
1731 keycodeInstrumentMap[Qt::Key_N] = instr++;
1732 keycodeInstrumentMap[Qt::Key_J] = instr++;
1733 keycodeInstrumentMap[Qt::Key_M] = instr++;
1734
1735 keycodeInstrumentMap[Qt::Key_Q] = instr++;
1736 keycodeInstrumentMap[Qt::Key_2] = instr++;
1737 keycodeInstrumentMap[Qt::Key_W] = instr++;
1738 keycodeInstrumentMap[Qt::Key_3] = instr++;
1739 keycodeInstrumentMap[Qt::Key_E] = instr++;
1740 keycodeInstrumentMap[Qt::Key_R] = instr++;
1741 keycodeInstrumentMap[Qt::Key_5] = instr++;
1742 keycodeInstrumentMap[Qt::Key_T] = instr++;
1743 keycodeInstrumentMap[Qt::Key_6] = instr++;
1744 keycodeInstrumentMap[Qt::Key_Z] = instr++;
1745 keycodeInstrumentMap[Qt::Key_7] = instr++;
1746 keycodeInstrumentMap[Qt::Key_U] = instr++;
1747 }
1748 else if ( loc.contains( "fr" ) || loc.contains( "FR" )){
1749 keycodeInstrumentMap[Qt::Key_W] = instr++;
1750 keycodeInstrumentMap[Qt::Key_S] = instr++;
1751 keycodeInstrumentMap[Qt::Key_X] = instr++;
1752 keycodeInstrumentMap[Qt::Key_D] = instr++;
1753 keycodeInstrumentMap[Qt::Key_C] = instr++;
1754 keycodeInstrumentMap[Qt::Key_V] = instr++;
1755 keycodeInstrumentMap[Qt::Key_G] = instr++;
1756 keycodeInstrumentMap[Qt::Key_B] = instr++;
1757 keycodeInstrumentMap[Qt::Key_H] = instr++;
1758 keycodeInstrumentMap[Qt::Key_N] = instr++;
1759 keycodeInstrumentMap[Qt::Key_J] = instr++;
1760 keycodeInstrumentMap[Qt::Key_Question] = instr++;
1761
1762 keycodeInstrumentMap[Qt::Key_A] = instr++;
1763 keycodeInstrumentMap[Qt::Key_2] = instr++;
1764 keycodeInstrumentMap[Qt::Key_Z] = instr++;
1765 keycodeInstrumentMap[Qt::Key_3] = instr++;
1766 keycodeInstrumentMap[Qt::Key_E] = instr++;
1767 keycodeInstrumentMap[Qt::Key_R] = instr++;
1768 keycodeInstrumentMap[Qt::Key_5] = instr++;
1769 keycodeInstrumentMap[Qt::Key_T] = instr++;
1770 keycodeInstrumentMap[Qt::Key_6] = instr++;
1771 keycodeInstrumentMap[Qt::Key_Y] = instr++;
1772 keycodeInstrumentMap[Qt::Key_7] = instr++;
1773 keycodeInstrumentMap[Qt::Key_U] = instr++;
1774 }else
1775 {
1776 keycodeInstrumentMap[Qt::Key_Z] = instr++;
1777 keycodeInstrumentMap[Qt::Key_S] = instr++;
1778 keycodeInstrumentMap[Qt::Key_X] = instr++;
1779 keycodeInstrumentMap[Qt::Key_D] = instr++;
1780 keycodeInstrumentMap[Qt::Key_C] = instr++;
1781 keycodeInstrumentMap[Qt::Key_V] = instr++;
1782 keycodeInstrumentMap[Qt::Key_G] = instr++;
1783 keycodeInstrumentMap[Qt::Key_B] = instr++;
1784 keycodeInstrumentMap[Qt::Key_H] = instr++;
1785 keycodeInstrumentMap[Qt::Key_N] = instr++;
1786 keycodeInstrumentMap[Qt::Key_J] = instr++;
1787 keycodeInstrumentMap[Qt::Key_M] = instr++;
1788
1789 keycodeInstrumentMap[Qt::Key_Q] = instr++;
1790 keycodeInstrumentMap[Qt::Key_2] = instr++;
1791 keycodeInstrumentMap[Qt::Key_W] = instr++;
1792 keycodeInstrumentMap[Qt::Key_3] = instr++;
1793 keycodeInstrumentMap[Qt::Key_E] = instr++;
1794 keycodeInstrumentMap[Qt::Key_R] = instr++;
1795 keycodeInstrumentMap[Qt::Key_5] = instr++;
1796 keycodeInstrumentMap[Qt::Key_T] = instr++;
1797 keycodeInstrumentMap[Qt::Key_6] = instr++;
1798 keycodeInstrumentMap[Qt::Key_Y] = instr++;
1799 keycodeInstrumentMap[Qt::Key_7] = instr++;
1800 keycodeInstrumentMap[Qt::Key_U] = instr++;
1801 }
1802}
1803
1804
1805bool MainForm::eventFilter( QObject *o, QEvent *e )
1806{
1807 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
1808 auto pHydrogen = Hydrogen::get_instance();
1809 auto pHydrogenApp = HydrogenApp::get_instance();
1810
1811 if ( e->type() == QEvent::FileOpen ) {
1812 // Mac OS always opens files (including via double click in Finder) via a FileOpenEvent.
1813 QFileOpenEvent *fe = dynamic_cast<QFileOpenEvent*>(e);
1814 assert( fe != nullptr );
1815 QString sFileName = fe->file();
1816
1817 if ( sFileName.endsWith( H2Core::Filesystem::songs_ext ) ) {
1818 if ( handleUnsavedChanges() ) {
1819 pHydrogenApp->openSong( sFileName );
1820 }
1821
1822 } else if ( sFileName.endsWith( H2Core::Filesystem::drumkit_ext ) ) {
1823 H2Core::Drumkit::install( sFileName );
1824
1825 } else if ( sFileName.endsWith( H2Core::Filesystem::playlist_ext ) ) {
1826 bool loadlist = pHydrogenApp->getPlayListDialog()->loadListByFileName( sFileName );
1827 if ( loadlist ) {
1829 }
1830 }
1831 return true;
1832
1833 } else if ( e->type() == QEvent::KeyPress ) {
1834 // special processing for key press
1835 QKeyEvent *k = (QKeyEvent *)e;
1836
1837 if ( k->matches( QKeySequence::StandardKey::Undo ) ) {
1838 k->accept();
1839 action_undo();
1840 return true;
1841 } else if ( k->matches( QKeySequence::StandardKey::Redo ) ) {
1842 k->accept();
1843 action_redo();
1844 return true;
1845 }
1846
1847
1848 // qDebug( "Got key press for instrument '%c'", k->ascii() );
1849 switch (k->key()) {
1850 case Qt::Key_Space:
1851
1852 // Hint that something is wrong in case there is no proper audio
1853 // driver set.
1854 if ( pHydrogen->getAudioOutput() == nullptr ||
1855 dynamic_cast<NullDriver*>(pHydrogen->getAudioOutput()) != nullptr ) {
1856 QMessageBox::warning( this, "Hydrogen",
1857 QString( "%1\n%2" )
1858 .arg( pCommonStrings->getAudioDriverNotPresent() )
1859 .arg( pCommonStrings->getAudioDriverErrorHint() ) );
1860 return true;
1861 }
1862
1863 switch ( k->modifiers() ) {
1864 case Qt::NoModifier:
1866 break;
1867
1868#ifndef Q_OS_MACX
1869 case Qt::ControlModifier:
1871 break;
1872 }
1873#else
1874 case Qt::AltModifier:
1876 break;
1877 }
1878#endif
1879
1880 return true; // eat event
1881 break;
1882
1883 case Qt::Key_Comma:
1884 pHydrogen->handleBeatCounter();
1885 return true; // eat even
1886 break;
1887
1888 case Qt::Key_Backspace:
1890 return true; // eat event
1891 break;
1892
1893 case Qt::Key_Plus:
1895 return true; // eat event
1896 break;
1897
1898 case Qt::Key_Minus:
1900 return true; // eat event
1901 break;
1902
1903 case Qt::Key_Backslash:
1904 pHydrogen->onTapTempoAccelEvent();
1905 return true; // eat event
1906 break;
1907
1908 case Qt::Key_S:
1909 if ( k->modifiers() ==
1910 ( Qt::ControlModifier | Qt::ShiftModifier ) ) {
1912 return true;
1913 } else if ( k->modifiers() == Qt::ControlModifier ) {
1915 return true;
1916 }
1917 break;
1918
1919 case Qt::Key_F5 :
1920 if( Playlist::get_instance()->size() == 0) {
1921 break;
1922 }
1924 break;
1925
1926 case Qt::Key_F6 :
1927 if( Playlist::get_instance()->size() == 0) {
1928 break;
1929 }
1931 break;
1932
1933 case Qt::Key_F12 : //panic button stop all playing notes
1934 pHydrogen->__panic();
1935 //QMessageBox::information( this, "Hydrogen", tr( "Panic" ) );
1936 return true;
1937 break;
1938
1939 case Qt::Key_F9 : // Qt::Key_Left do not work. Some ideas ?
1940 pHydrogen->getCoreActionController()->locateToColumn( pHydrogen->getAudioEngine()->getTransportPosition()->getColumn() - 1 );
1941 return true;
1942 break;
1943
1944 case Qt::Key_F10 : // Qt::Key_Right do not work. Some ideas ?
1945 pHydrogen->getCoreActionController()->locateToColumn( pHydrogen->getAudioEngine()->getTransportPosition()->getColumn() + 1 );
1946 return true;
1947 break;
1948 }
1949
1950 // virtual keyboard handling
1951 if ( k->modifiers() == Qt::NoModifier ) {
1952 std::map<int,int>::iterator found = keycodeInstrumentMap.find ( k->key() );
1953 if (found != keycodeInstrumentMap.end()) {
1954 // INFOLOG( "[eventFilter] virtual keyboard event" );
1955 // insert note at the current column in time
1956 // if event recording enabled
1957 int row = (*found).second;
1958
1959 float velocity = 0.8;
1960
1961 pHydrogen->addRealtimeNote( row, velocity, 0.f, false, row + 36 );
1962
1963 return true; // eat event
1964 }
1965 }
1966 return false; // let it go
1967 }
1968 else {
1969 return false; // standard event processing
1970 }
1971}
1972
1973
1974
1975
1976
1979{
1980 INFOLOG( "[action_debug_printObjects]" );
1982}
1983
1984
1985
1986
1987
1988
1990{
1991 if ( Hydrogen::get_instance()->getAudioEngine()->getState() == H2Core::AudioEngine::State::Playing ) {
1993 }
1994
1995 ExportMidiDialog *dialog = new ExportMidiDialog(this);
1996 dialog->exec();
1997 delete dialog;
1998}
1999
2000
2001
2002
2004{
2005 if ( Hydrogen::get_instance()->getAudioEngine()->getState() == H2Core::AudioEngine::State::Playing ) {
2007 }
2008
2009 QMessageBox::information(
2010 this,
2011 "Hydrogen",
2012 tr( "\nThe LilyPond export is an experimental feature.\n"
2013 "It should work like a charm provided that you use the "
2014 "GMRockKit, and that you do not use triplet.\n" ),
2015 QMessageBox::Ok );
2016
2018 if ( ! Filesystem::dir_writable( sPath, false ) ){
2019 sPath = Filesystem::usr_data_path();
2020 }
2021
2022 FileDialog fd( this );
2023 fd.setFileMode( QFileDialog::AnyFile );
2024 fd.setNameFilter( tr( "LilyPond file (*.ly)" ) );
2025 fd.setDirectory( sPath );
2026 fd.setWindowTitle( tr( "Export LilyPond file" ) );
2027 fd.setAcceptMode( QFileDialog::AcceptSave );
2028
2029 QString sFilename;
2030 if ( fd.exec() == QDialog::Accepted ) {
2031 Preferences::get_instance()->setLastExportLilypondDirectory( fd.directory().absolutePath() );
2032 sFilename = fd.selectedFiles().first();
2033 }
2034
2035 if ( !sFilename.isEmpty() ) {
2036 if ( sFilename.endsWith( ".ly" ) == false ) {
2037 sFilename += ".ly";
2038 }
2039
2040 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
2041
2042 LilyPond ly;
2043 ly.extractData( *pSong );
2044 ly.write( sFilename );
2045 }
2046}
2047
2048void MainForm::errorEvent( int nErrorCode )
2049{
2050 //ERRORLOG( "[errorEvent]" );
2051
2052 QString msg;
2053 switch (nErrorCode) {
2055 msg = tr( "Unknown audio driver" );
2056 break;
2057
2059 msg = tr( "Error starting audio driver" );
2060 break;
2061
2063 msg = tr( "Jack driver: server shutdown" );
2064 break;
2065
2067 msg = tr( "Jack driver: cannot activate client" );
2068 break;
2069
2071 msg = tr( "Jack driver: cannot connect output port" );
2072 break;
2073
2075 msg = tr( "Jack driver: cannot disconnect client" );
2076 break;
2077
2079 msg = tr( "Jack driver: error in port register" );
2080 break;
2081
2083 msg = QString( tr( "OSC Server: Cannot connect to given port, using port %1 instead" ) ).arg( Preferences::get_instance()->m_nOscTemporaryPort );
2084 break;
2085
2087 msg = tr( "Playback track couldn't be read" );
2088 break;
2089
2090 default:
2091 msg = QString( tr( "Unknown error %1" ) ).arg( nErrorCode );
2092 }
2093 QMessageBox::information( this, "Hydrogen", msg );
2094}
2095
2097{
2098 Playlist* pPlaylist = Playlist::get_instance();
2099
2100 QString songFilename;
2101 if( !pPlaylist->getSongFilenameByNumber( nIndex, songFilename ) ) {
2102 return;
2103 }
2104
2105 HydrogenApp::get_instance()->openSong( songFilename );
2106
2107 pPlaylist->activateSong( nIndex );
2108
2109 HydrogenApp::get_instance()->showStatusBarMessage( tr( "Playlist: Set song No. %1" )
2110 .arg( nIndex +1 ) );
2111}
2112
2114{
2115 switch (nEvent){
2116 case 0:
2118 break;
2119 case 1:
2121 break;
2122 }
2123
2124}
2125
2127{
2128 if ( H2Core::Hydrogen::get_instance()->getSong() == nullptr ) {
2129 return;
2130 }
2131
2132 SongPropertiesDialog *pDialog = new SongPropertiesDialog( this );
2133 if ( pDialog->exec() ) {
2134 // Ensure the update name is taken into account in the window
2135 // title.
2137 }
2138 delete pDialog;
2139}
2140
2141
2143{
2144 bool isVisible = HydrogenApp::get_instance()->getPatternEditorPanel()->isVisible();
2145 HydrogenApp::get_instance()->getPatternEditorPanel()->setHidden( isVisible );
2146}
2147
2148
2150{
2151 Preferences *pPreferences = Preferences::get_instance();
2152 bool isDevelWarningEnabled = pPreferences->getShowDevelWarning();
2153
2154 //set this to 'false' for the case that you want to make a release..
2155 if ( H2CORE_IS_DEVEL_BUILD ) {
2156 if(isDevelWarningEnabled) {
2157 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
2158
2159 QString msg = tr( "You're using a development version of Hydrogen, please help us reporting bugs or suggestions in the hydrogen-devel mailing list.<br><br>Thank you!" );
2160 QMessageBox develMessageBox( this );
2161 develMessageBox.setText( msg );
2162 develMessageBox.addButton( pCommonStrings->getButtonOk(),
2163 QMessageBox::YesRole );
2164 develMessageBox.addButton( pCommonStrings->getMutableDialog(),
2165 QMessageBox::AcceptRole );
2166
2167 if( develMessageBox.exec() == 1 ){
2168 //don't show warning again
2169 pPreferences->setShowDevelWarning( false );
2170 }
2171 }
2172 } else {
2173 // Release builds
2174 if ( !isDevelWarningEnabled ) {
2175 // Running a release build, we should re-enable the development-build warning if it's been
2176 // disabled, since the user might have tried a release build at some time in the past, then
2177 // continued working happily with a release build. They will still benefit from knowing that a
2178 // *new* release build they're trying is in fact a release build.
2179 pPreferences->setShowDevelWarning( true );
2180 }
2181 }
2182}
2183
2184
2185
2187{
2188 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
2189 assert( pSong );
2190 QString sOldFilename = pSong->getFilename();
2191 QString sNewName;
2192
2193 if ( !sOldFilename.isEmpty() ) {
2194
2195 QFileInfo fileInfo( sOldFilename );
2196
2197 // In case the user did open a hidden file, the baseName()
2198 // will be an empty string.
2199 QString sBaseName( fileInfo.completeBaseName() );
2200 if ( sBaseName.startsWith( "." ) ) {
2201 sBaseName.remove( 0, 1 );
2202 }
2203
2204 QString sAbsoluteDir( fileInfo.absoluteDir().absolutePath() );
2205 if ( ! Filesystem::file_writable( sOldFilename, true ) ) {
2206
2207 sNewName = QString( "%1%2.autosave.h2song" )
2208 .arg( Filesystem::songs_dir() ).arg( sBaseName );
2209
2210 WARNINGLOG( QString( "Path of current song [%1] is not writable. Autosave will store the song as [%2] instead." )
2211 .arg( sOldFilename ).arg( sNewName ) );
2212 } else {
2213 sNewName = QString( "%1/.%2.autosave.h2song" )
2214 .arg( sAbsoluteDir ).arg( sBaseName );
2215 }
2216 } else {
2217 // Store the default autosave file in the user's song data
2218 // folder to not clutter their working directory.
2219 sNewName = QString( "%1autosave.h2song" )
2220 .arg( Filesystem::songs_dir() );
2221 }
2222
2223 return sNewName;
2224}
2225
2226
2227
2229{
2230 auto pHydrogen = Hydrogen::get_instance();
2231 std::shared_ptr<Song> pSong = pHydrogen->getSong();
2232
2233 assert( pSong );
2234 if ( pSong->getIsModified() ) {
2235 QString sOldFilename = pSong->getFilename();
2236
2237 QString sAutoSaveFilename = getAutoSaveFilename();
2238 if ( sAutoSaveFilename != m_sPreviousAutoSaveFilename ) {
2239 if ( ! m_sPreviousAutoSaveFilename.isEmpty() ) {
2240 QFile file( m_sPreviousAutoSaveFilename );
2241 file.remove();
2242 }
2243 m_sPreviousAutoSaveFilename = sAutoSaveFilename;
2244 }
2245
2246 pSong->save( sAutoSaveFilename );
2247
2248 pSong->setFilename( sOldFilename );
2249 pHydrogen->setIsModified( true );
2250 }
2251}
2252
2253
2255{
2256 if( Playlist::get_instance()->size() == 0) {
2257 return;
2258 }
2259
2260 int songnumber = Playlist::get_instance()->getActiveSongNumber();
2261 QString songname;
2262 if ( songnumber == -1 ) {
2263 return;
2264 }
2265
2266 if ( Hydrogen::get_instance()->getSong()->getName() == "Untitled Song" ){
2267 songname = Hydrogen::get_instance()->getSong()->getFilename();
2268 } else {
2269 songname = Hydrogen::get_instance()->getSong()->getName();
2270 }
2271 QString message = (tr("Playlist: Song No. %1").arg( songnumber + 1)) + QString(" --- Songname: ") + songname + QString(" --- Author: ") + Hydrogen::get_instance()->getSong()->getAuthor();
2273}
2274
2275// Returns true if unsaved changes are successfully handled (saved, discarded, etc.)
2276// Returns false if not (i.e. Cancel)
2278{
2279 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
2280 bool done = false;
2281 bool rv = true;
2282 while ( !done && Hydrogen::get_instance()->getSong()->getIsModified() ) {
2283 switch(
2284 QMessageBox::information( this, "Hydrogen",
2285 tr("\nThe document contains unsaved changes.\n"
2286 "Do you want to save the changes?\n"),
2287 pCommonStrings->getButtonSave(),
2288 pCommonStrings->getButtonDiscard(),
2289 pCommonStrings->getButtonCancel(),
2290 0, // Enter == button 0
2291 2 ) ) { // Escape == button 2
2292 case 0: // Save clicked or Alt+S pressed or Enter pressed.
2293 // If the save fails, the __is_modified flag will still be true
2294 if ( ! Hydrogen::get_instance()->getSong()->getFilename().isEmpty() ) {
2296 } else {
2297 // never been saved
2299 }
2300 // save
2301 break;
2302 case 1: // Discard clicked or Alt+D pressed
2303 // don't save but exit
2304 done = true;
2305 break;
2306 case 2: // Cancel clicked or Alt+C pressed or Escape pressed
2307 // don't exit
2308 done = true;
2309 rv = false;
2310 break;
2311 }
2312 }
2313
2314 if( rv != false ) {
2315 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
2316 while ( !done && Playlist::get_instance()->getIsModified() ) {
2317 switch(
2318 QMessageBox::information(
2319 this,
2320 "Hydrogen",
2321 tr("\nThe current playlist contains unsaved changes.\n"
2322 "Do you want to discard the changes?\n"),
2323 pCommonStrings->getButtonDiscard(),
2324 pCommonStrings->getButtonCancel(),
2325 nullptr, // Enter == button 0
2326 2 ) ) { // Escape == button 1
2327 case 0: // Discard clicked or Alt+D pressed
2328 // don't save but exit
2329 done = true;
2330 break;
2331 case 1: // Cancel clicked or Alt+C pressed or Escape pressed
2332 // don't exit
2333 done = true;
2334 rv = false;
2335 break;
2336 }
2337 }
2338 }
2339
2340
2341 return rv;
2342}
2343
2344
2346{
2347 char a = 1;
2348 ::write(sigusr1Fd[0], &a, sizeof(a));
2349}
2350
2352{
2353 snUsr1->setEnabled(false);
2354 char tmp;
2355 ::read(sigusr1Fd[1], &tmp, sizeof(tmp));
2356
2358 snUsr1->setEnabled(true);
2359}
2360
2362{
2363 m_pUndoView->show();
2364 m_pUndoView->setAttribute(Qt::WA_QuitOnClose, false);
2365}
2366
2368 h2app->m_pUndoStack->undo();
2369}
2370
2372 h2app->m_pUndoStack->redo();
2373}
2374
2376
2377 if ( nValue == 0 ) {
2378 // Write the state of the GUI to the Preferences.
2381
2382 } else if ( nValue == 1 ) {
2383
2384 // Reflect the changes in the preferences in the objects
2385 // stored in MainForm.
2386 if( Preferences::get_instance()->__playselectedinstrument ) {
2387 m_pInstrumentAction->setChecked( true );
2388 m_pDrumkitAction->setChecked (false );
2389 } else {
2390 m_pInstrumentAction->setChecked( false );
2391 m_pDrumkitAction->setChecked (true );
2392 }
2393
2394 } else {
2395 ERRORLOG( QString( "Unknown event parameter [%1] MainForm::updatePreferencesEvent" )
2396 .arg( nValue ) );
2397 }
2398
2399}
2400
2402 if( nEvent == 0 ) {
2403 h2app->m_pUndoStack->undo();
2404 } else if(nEvent == 1) {
2405 h2app->m_pUndoStack->redo();
2406 }
2407}
2408
2410{
2411 int nPlaylistSize = Playlist::get_instance()->size();
2412 int nSongnumber = Playlist::get_instance()->getActiveSongNumber();
2413
2414 if( nSongnumber+step >= 0 && nSongnumber+step <= nPlaylistSize-1 ){
2415 Playlist::get_instance()->setNextSongByNumber( nSongnumber + step );
2416 } else {
2417 return false;
2418 }
2419
2420 return true;
2421}
2422
2426
2427void MainForm::editDrumkitProperties( bool bDrumkitNameLocked )
2428{
2429 auto pHydrogen = Hydrogen::get_instance();
2430 auto pSong = pHydrogen->getSong();
2431
2432 auto pDrumkit = pHydrogen->getSoundLibraryDatabase()
2433 ->getDrumkit( pHydrogen->getLastLoadedDrumkitPath() );
2434
2435 if ( pDrumkit == nullptr ) {
2436 ERRORLOG( QString( "Unable to find drumkit at path [%1]. Trying drumkit name [%2] instead." )
2437 .arg( pHydrogen->getLastLoadedDrumkitPath() )
2438 .arg( pHydrogen->getLastLoadedDrumkitName() ) );
2439 // No luck when searching for the kit using the absolute path found in
2440 // the .h2song. Let's try the last loaded drumkit name.
2441 const QString sDrumkitPath =
2442 Filesystem::drumkit_path_search( pHydrogen->getLastLoadedDrumkitName(),
2443 Filesystem::Lookup::stacked, true );
2444 pDrumkit = pHydrogen->getSoundLibraryDatabase()
2445 ->getDrumkit( sDrumkitPath );
2446 }
2447
2448 if ( pDrumkit == nullptr && ! bDrumkitNameLocked ) {
2449 ERRORLOG( QString( "Unable to find drumkit of name [%1] either. Falling back to empty one." )
2450 .arg( pHydrogen->getLastLoadedDrumkitName() ) );
2451 // If that didn't worked either and the user wants to "Save As", we fall
2452 // back to the default kit.
2453 pDrumkit = std::make_shared<Drumkit>();
2454 }
2455
2456 if ( pDrumkit != nullptr ){
2457
2458 auto pNewDrumkit = std::make_shared<Drumkit>( pDrumkit );
2459 pNewDrumkit->set_instruments( pSong->getInstrumentList() );
2460 pNewDrumkit->set_components( pSong->getComponents() );
2461
2462 SoundLibraryPropertiesDialog dialog( this, pNewDrumkit, bDrumkitNameLocked );
2463 if ( dialog.exec() == QDialog::Accepted ) {
2464 // Saving was successful.
2465
2466 if ( pNewDrumkit->get_path() != pDrumkit->get_path() ) {
2467 // A new drumkit was created based on the original
2468 // one. We call the drumkit setter to ensure
2469 // everything in the Song and GUI is still in sync.
2470 pHydrogen->getCoreActionController()->setDrumkit( pNewDrumkit, false );
2471 }
2472 }
2473 }
2474 else {
2475 QMessageBox::warning( this, "Hydrogen", QString( "%1 [%2]")
2476 .arg( HydrogenApp::get_instance()->getCommonStrings()->getSoundLibraryFailedPreDrumkitLoad() )
2477 .arg( pHydrogen->getLastLoadedDrumkitPath() ) );
2478 }
2479}
2480
2481void MainForm::updateSongEvent( int nValue ) {
2482 if ( nValue == 0 || nValue == 1 ) {
2483 // A new song was set.
2485 }
2486}
2487
2489 closeAll();
2490}
2491
2492void MainForm::startPlaybackAtCursor( QObject* pObject ) {
2493
2494 Hydrogen* pHydrogen = Hydrogen::get_instance();
2495 auto pSong = pHydrogen->getSong();
2497 auto pCoreActionController = pHydrogen->getCoreActionController();
2498 auto pAudioEngine = pHydrogen->getAudioEngine();
2499
2500 if ( pSong == nullptr ) {
2501 return;
2502 }
2503
2504 if ( pObject->inherits( "SongEditorPanel" ) ) {
2505
2506 if ( pHydrogen->getMode() != Song::Mode::Song ) {
2507 pCoreActionController->activateSongMode( true );
2508 }
2509
2510 const int nCursorColumn =
2512
2513 // Within the core locating to a position beyond the length of
2514 // the song with loop mode enabled is a valid
2515 // operation. The resulting location will the wrapped as if
2516 // transport was looped. This is important when allowing
2517 // external applications to relocate but it is not what we
2518 // want in here.
2519 if ( nCursorColumn >= pSong->getPatternGroupVector()->size() ) {
2520 ERRORLOG( QString( "Cursor column [%1] is outside of the current song [0,%2]" )
2521 .arg( nCursorColumn )
2522 .arg( pSong->getPatternGroupVector()->size() - 1 ) );
2523 return;
2524 }
2525
2526 if ( ! pCoreActionController->locateToColumn( nCursorColumn ) ) {
2527 // Cursor is at a position it is not allowed to locate to.
2528 return;
2529 }
2530
2531 } else if ( pObject->inherits( "PatternEditorPanel" ) ) {
2532 // Covers both the PatternEditor and the
2533 // NotePropertiesRuler.
2534
2535 if ( pHydrogen->getMode() != Song::Mode::Pattern ) {
2536 pCoreActionController->activateSongMode( false );
2537 }
2538
2539 // To provide a similar behaviour as when pressing
2540 // [backspace], transport is relocated to the beginning of
2541 // the song.
2542 const int nCursorColumn = pApp->getPatternEditorPanel()->getCursorPosition();
2543
2544 if ( ! pCoreActionController->locateToTick( nCursorColumn ) ) {
2545 // Cursor is at a position it is not allowed to locate to.
2546 return;
2547 }
2548 } else {
2549 ERRORLOG( QString( "Unknown object class" ) );
2550 }
2551
2552 if ( pAudioEngine->getState() == H2Core::AudioEngine::State::Ready ) {
2553 pHydrogen->sequencer_play();
2554 pApp->showStatusBarMessage( tr("Playing.") );
2555 }
2556}
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:59
#define INFOLOG(x)
Definition Object.h:237
#define WARNINGLOG(x)
Definition Object.h:238
#define ERRORLOG(x)
Definition Object.h:239
#define _ERRORLOG(x)
Definition Object.h:245
#define DEBUGLOG(x)
Definition Object.h:236
#define FOREACH_NOTE_CST_IT_BEGIN_END(_notes, _it)
Iterate over all provided notes in an immutable way.
Definition Pattern.h:268
virtual void selectedInstrumentChangedEvent()
Dialog for exporting song to midi.
Dialog for exporting song.
Custom file dialog checking whether the user has write access to the selected folder before allowing ...
Definition FileDialog.h:33
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
State getState() const
static void write_objects_map_to_cerr()
output objects map to stderr
Definition Object.h:95
bool locateToColumn(int nPatternGroup)
Relocates transport to the beginning of a particular column/Pattern group.
static bool install(const QString &sSourcePath, const QString &sTargetPath="", bool bSilent=false)
Extract a .h2drumkit file.
Definition Drumkit.cpp:565
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
Definition EventQueue.h:224
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
static QString savePatternPath(const QString &filePath, Pattern *pattern, std::shared_ptr< Song > song, const QString &drumkitName)
save the given pattern to filePath will overwrite an existing file
Definition Files.h:88
static const QString patterns_filter_name
Definition Filesystem.h:96
static QString demos_dir()
returns system demos path
static bool dir_readable(const QString &path, bool silent=false)
returns true if the given path is a readable regular directory
static const QString patterns_ext
Definition Filesystem.h:89
static QString songs_dir()
returns user songs path
static DrumkitType determineDrumkitType(const QString &sPath)
static bool file_writable(const QString &path, bool silent=false)
returns true if the given path is a possibly writable file (may exist or not)
static bool rm(const QString &path, bool recursive=false, bool bSilent=false)
remove a path
static QString doc_dir()
returns documentation path
static bool dir_writable(const QString &path, bool silent=false)
returns true if the given path is a writable regular directory
static bool file_exists(const QString &path, bool silent=false)
returns true if the given path is an existing regular file
static QString empty_song_path()
Provides the full path to the current empty song.
static QString drumkit_path_search(const QString &dk_name, Lookup lookup=Lookup::stacked, bool bSilent=false)
Returns the path to a H2Core::Drumkit folder.
static const QString playlist_ext
Definition Filesystem.h:90
static const QString songs_filter_name
Definition Filesystem.h:93
static QString tmp_dir()
returns temp path
static QString usr_data_path()
returns user data path
static QString log_file_path()
returns the full path (including filename) of the logfile
static const QString drumkit_ext
Definition Filesystem.h:91
static const QString songs_ext
Definition Filesystem.h:87
static QString default_song_name()
Default option to offer the user when saving an empty song to disk.
static QString patterns_dir()
returns user patterns path
Hydrogen Audio Engine.
Definition Hydrogen.h:54
void sequencer_stop()
Stop the internal sequencer.
Definition Hydrogen.cpp:226
void renameJackPorts(std::shared_ptr< Song > pSong)
Calls audioEngine_renameJackPorts() if Preferences::m_bJackTrackOuts is set to true.
Definition Hydrogen.cpp:931
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
bool isUnderSessionManagement() const
Song::Mode getMode() const
int getSelectedPatternNumber() const
Definition Hydrogen.h:660
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:649
@ UNKNOWN_DRIVER
The provided input string in createDriver() does not match any of the choices for Preferences::m_sAud...
Definition Hydrogen.h:245
@ ERROR_STARTING_DRIVER
Unable to connect the audio driver stored in H2Core::AudioEngine::m_pAudioDriver in audioEngine_start...
Definition Hydrogen.h:252
@ JACK_CANNOT_ACTIVATE_CLIENT
Definition Hydrogen.h:254
@ JACK_ERROR_IN_PORT_REGISTER
Unable to register output ports for the JACK client using jack_port_register() (jack/jack....
Definition Hydrogen.h:278
@ OSC_CANNOT_CONNECT_TO_PORT
Unable to start the OSC server with the given port number.
Definition Hydrogen.h:283
@ JACK_CANNOT_CLOSE_CLIENT
The client of Hydrogen can not be disconnected from the JACK server using jack_client_close() (jack/j...
Definition Hydrogen.h:271
@ JACK_CANNOT_CONNECT_OUTPUT_PORT
Unable to connect either the JackAudioDriver::output_port_1 and the JackAudioDriver::output_port_name...
Definition Hydrogen.h:265
void setSessionDrumkitNeedsRelinking(bool bNeedsRelinking)
Definition Hydrogen.h:669
SoundLibraryDatabase * getSoundLibraryDatabase() const
Definition Hydrogen.h:94
QString getLastLoadedDrumkitName() const
CoreActionController * getCoreActionController() const
Definition Hydrogen.h:639
void sequencer_play()
Start the internal sequencer.
Definition Hydrogen.cpp:218
A class to convert a Hydrogen song to LilyPond format.
Definition Lilypond.h:40
void extractData(const Song &song)
Definition Lilypond.cpp:65
void write(const QString &sFilename) const
Definition Lilypond.cpp:86
Class for writing logs to the console.
Definition Logger.h:42
static unsigned bit_mask()
return the current log level bit mask
Definition Logger.h:90
static Logger * get_instance()
Returns a pointer to the current H2Core::Logger singleton stored in __instance.
Definition Logger.h:74
static void set_bit_mask(unsigned msk)
set the bitmask
Definition Logger.h:88
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
void set_pattern_idx(int value)
__pattern_idx setter
Definition Note.h:590
PatternList is a collection of patterns.
Definition PatternList.h:43
int size() const
returns the numbers of patterns
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
Definition Pattern.h:46
void set_name(const QString &name)
get the name of the pattern
Definition Pattern.h:305
const QString & get_name() const
set the category of the pattern
Definition Pattern.h:310
const notes_t * get_notes() const
get the virtual pattern set
Definition Pattern.h:355
std::multimap< int, Note * > notes_t
< multimap note type
Definition Pattern.h:50
static Pattern * load_file(const QString &pattern_path, std::shared_ptr< InstrumentList > instruments)
load a pattern from a file
Definition Pattern.cpp:106
Drumkit info.
Definition Playlist.h:37
void setNextSongByNumber(int SongNumber)
Definition Playlist.cpp:197
int getActiveSongNumber()
Definition Playlist.h:149
void activateSong(int SongNumber)
Definition Playlist.cpp:173
static Playlist * get_instance()
Returns a pointer to the current Playlist singleton stored in __instance.
Definition Playlist.h:60
int size() const
Definition Playlist.h:123
bool getSongFilenameByNumber(int songNumber, QString &fileName)
Definition Playlist.cpp:181
Manager for User Preferences File (singleton)
Definition Preferences.h:78
QString getLastSaveSongAsDirectory() const
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString getLastExportPatternAsDirectory() const
void setLadspaProperties(unsigned nFX, const WindowProperties &prop)
void setMixerProperties(const WindowProperties &prop)
void setPatternEditorProperties(const WindowProperties &prop)
void setLastExportLilypondDirectory(QString sPath)
void setSongEditorProperties(const WindowProperties &prop)
QString getLastExportLilypondDirectory() const
void setShowDevelWarning(bool value)
QString getLastOpenSongDirectory() const
bool savePreferences()
Save the preferences file.
void setInstrumentRackProperties(const WindowProperties &prop)
QString getLastOpenPatternDirectory() const
void setDirectorProperties(const WindowProperties &prop)
void setPlaylistDialogProperties(const WindowProperties &prop)
void setLastOpenSongDirectory(QString sPath)
void setLastOpenPatternDirectory(QString sPath)
void setMainFormProperties(const WindowProperties &prop)
void setAudioEngineInfoProperties(const WindowProperties &prop)
std::vector< QString > getRecentFiles() const
const QString & getPreferredLanguage()
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ GeneralTab
Any option in the General tab appeared.
@ Font
Either the font size or font family have changed.
@ Colors
At least one of the colors has changed.
bool getShowPlaybackTrack() const
void setLastSaveSongAsDirectory(QString sPath)
void setLastPlaylistFilename(const QString &filename)
void setLastExportPatternAsDirectory(QString sPath)
static std::shared_ptr< Song > getEmptySong()
Definition Song.cpp:953
void updatePatterns(bool bTriggerEvent=true)
PlayerControl * getPlayerControl()
void updateWindowTitle()
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
std::shared_ptr< CommonStrings > getCommonStrings()
InfoBar * addInfoBar()
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
void showAudioEngineInfoForm()
void showFilesystemInfoForm()
InstrumentRack * getInstrumentRack()
static bool openSong(QString sFilename)
PlaylistDialog * getPlayListDialog()
static bool checkDrumkitLicense(std::shared_ptr< H2Core::Drumkit > pDrumkit)
void showDirector()
void showStatusBarMessage(const QString &sMessage, const QString &sCaller="")
void showMixer(bool bShow)
void showPlaylistDialog()
static bool recoverEmptySong()
Specialized version of openSong( QString sFilename ) trying to open the autosave file corresponding t...
H2Core::WindowProperties getWindowProperties(QWidget *pWindow)
AudioEngineInfoForm * getAudioEngineInfoForm()
void showPreferencesDialog()
SongEditorPanel * getSongEditorPanel()
Director * getDirector()
Mixer * getMixer()
void setText(const QString &text)
Definition InfoBar.cpp:109
QPushButton * addButton(const QString &label)
Definition InfoBar.cpp:123
void reset()
Definition InfoBar.cpp:133
void setTitle(const QString &text)
Definition InfoBar.cpp:102
static InstrumentEditorPanel * get_instance()
static int findFreeDrumkitComponentId(int startingPoint=0)
void sendAlsaClientId()
lash_event_t * getNextEvent()
void sendEvent(LASH_Event_Type eventType, const char *value)
bool isConnected()
static LashClient * get_instance()
Definition LashClient.h:39
void startPlaybackAtCursor(QObject *pObject)
Relocates to current position of the cursor and starts playback if the transport isn't rolling yet.
QMenu * m_pDrumkitsMenu
Definition MainForm.h:313
void onBPMMinusAccelEvent()
void action_file_save_as()
Project > Save As / Export from Session handling function.
Definition MainForm.cpp:664
void update_instrument_checkbox(bool show)
virtual void errorEvent(int nErrorCode) override
bool handleSelectNextPrevSongOnPlaylist(int step)
void handleSigUsr1()
void action_window_showTimeline()
void action_debug_logLevel_warn()
void action_file_export_lilypond()
void showUserManual()
Definition MainForm.cpp:885
void action_debug_logLevel_debug()
void showDevelWarning()
void action_window_showPatternEditor()
QAction * m_pViewTimelineAction
Definition MainForm.h:259
QMenu * m_pInputModeMenu
Definition MainForm.h:253
void action_file_export_pattern_as(int nPatternRow=-1)
Definition MainForm.cpp:920
void closeAll()
Wrapper around savePreferences() and quit() method of m_pQApp.
void action_debug_logLevel_error()
bool eventFilter(QObject *o, QEvent *e) override
void savePreferences()
Stores the current state of the GUI (position, width, height, and visibility of the widgets) in the H...
void onFixMissingSamples()
QMenu * m_pRecentFilesMenu
Definition MainForm.h:265
void action_window_showAutomationArea()
void action_debug_showFilesystemInfo()
void updateRecentUsedSongList()
void action_file_open()
Project > Open / Import into Session handling function.
Definition MainForm.cpp:983
void action_window_showSongEditor()
Shows the song editor.
QAction * m_pViewPlaybackTrackAction
Definition MainForm.h:260
std::map< int, int > keycodeInstrumentMap
Definition MainForm.h:284
QMenu * m_pUndoMenu
Definition MainForm.h:312
void action_instruments_importLibrary()
void action_file_open_recent(QAction *pAction)
void action_banks_properties()
void onRestartAccelEvent()
void action_report_bug()
Definition MainForm.cpp:879
void checkMissingSamples()
void action_debug_printObjects()
print the object map
void action_window_showPlaylistDialog()
void action_file_save()
Definition MainForm.cpp:796
HydrogenApp * h2app
Definition MainForm.h:247
QString getAutoSaveFilename()
void action_inputMode_instrument()
Definition MainForm.cpp:854
void update_automation_checkbox()
QAction * m_pViewPlaylistEditorAction
Definition MainForm.h:254
void action_file_export()
void action_instruments_addInstrument()
void createMenuBar()
Create the menubar.
Definition MainForm.cpp:268
QMenu * m_pInfoMenu
Definition MainForm.h:318
void onFixMidiSetup()
virtual void updatePreferencesEvent(int nValue) override
Handles the loading and saving of the H2Core::Preferences from the core part of H2Core::Hydrogen.
MainForm(QApplication *pQApplication, QString sSongFilename)
Definition MainForm.cpp:90
void update_mixer_checkbox()
virtual void quitEvent(int) override
void onPlayStopAccelEvent()
bool action_file_exit()
return true if the app needs to be closed.
Definition MainForm.cpp:593
void action_file_new()
Project > New handling function.
Definition MainForm.cpp:605
void onPreferencesChanged(H2Core::Preferences::Changes changes)
void closeEvent(QCloseEvent *ev) override
Window close event.
void action_help_about()
Definition MainForm.cpp:874
virtual void updateSongEvent(int nValue) override
void action_debug_logLevel_none()
void onLashPollTimer()
Definition MainForm.cpp:497
QAction * m_pDrumkitAction
Definition MainForm.h:263
virtual void undoRedoActionEvent(int nEvent) override
void action_donate()
Definition MainForm.cpp:578
void action_instruments_exportLibrary()
bool prepareSongOpening()
QSocketNotifier * snUsr1
Definition MainForm.h:250
QApplication * m_pQApp
Definition MainForm.h:52
static int sigusr1Fd[2]
Definition MainForm.h:249
virtual void jacksessionEvent(int nValue) override
QAction * m_pInstrumentAction
Definition MainForm.h:262
void action_instruments_onlineImportLibrary()
void startAutosaveTimer()
debug only
Definition MainForm.cpp:481
void update_playback_track_group()
void openSongWithDialog(const QString &sWindowTitle, const QString &sPath, bool bIsDemo)
void action_undo()
QMenu * m_pInstrumentsMenu
Definition MainForm.h:314
void action_instruments_saveAsLibrary()
void update_director_checkbox()
void checkMidiSetup()
QAction * m_pViewMixerAction
Definition MainForm.h:256
void functionDeleteInstrument(int nInstrument)
QMenu * m_pDebugMenu
Definition MainForm.h:317
void action_instruments_clearAll()
void openUndoStack()
void action_window_showMixer()
QMenu * m_pViewMenu
Definition MainForm.h:315
QMenu * m_pOptionsMenu
Definition MainForm.h:316
QUndoView * m_pUndoView
Definition MainForm.h:272
void action_instruments_saveLibrary()
void onAutoSaveTimer()
InfoBar * m_pMidiSetupInfoBar
Definition MainForm.h:292
QString m_sPreviousAutoSaveFilename
Since the filename of the current song does change whenever the users uses "Save As" multiple autosav...
Definition MainForm.h:326
void update_playlist_checkbox()
void onPlaylistDisplayTimer()
QMenu * m_pLogLevelMenu
Definition MainForm.h:252
void action_banks_open()
void action_window_showInstrumentRack()
void action_instruments_addComponent()
void action_inputMode_drumkit()
Definition MainForm.cpp:864
QAction * m_pViewDirectorAction
Definition MainForm.h:255
void checkNecessaryDirectories()
void action_file_openDemo()
QAction * m_pViewAutomationPathAction
Definition MainForm.h:258
void showPreferencesDialog()
void editDrumkitProperties(bool bDrumkitNameLocked)
InfoBar * m_pMissingSamplesInfoBar
Definition MainForm.h:293
bool handleUnsavedChanges()
void action_window_toggleFullscreen()
void action_debug_openLogfile()
void action_file_export_midi()
void action_file_openPattern()
void action_redo()
QAction * m_pViewMixerInstrumentRackAction
Definition MainForm.h:257
void action_debug_showAudioEngineInfo()
void action_window_showPlaybackTrack()
void action_window_show_DirectorWidget()
virtual void playlistLoadSongEvent(int nIndex) override
void onBPMPlusAccelEvent()
static void usr1SignalHandler(int unused)
void action_file_songProperties()
QActionGroup * m_pViewPlaybackTrackActionGroup
Definition MainForm.h:261
void initKeyInstMap()
QMenu * m_pFileMenu
Definition MainForm.h:311
QTimer m_AutosaveTimer
Definition MainForm.h:275
void action_debug_logLevel_info()
static int dereferenceDrumkit(std::shared_ptr< H2Core::Song > pSong)
Replaces a path in Song::m_sLastLoadedDrumkitPath pointing to the session folder with one pointing to...
static void replaceDrumkitPath(std::shared_ptr< H2Core::Song > pSong, const QString &sDrumkitPath)
Replaces @H2Core::Song::m_sLastLoadedDrumkitPath as well as all @H2Core::Instrument::__drumkit_path b...
DrumPatternEditor * getDrumPatternEditor()
void clearSelection()
Clear the pattern editor selection.
bool loadListByFileName(QString filename)
static void setPalette(QApplication *pQApp)
Function used to update the global palette of the QApplication.
Definition Skin.cpp:106
void toggleAutomationAreaVisibility()
SongEditor * getSongEditor() const
int getCursorColumn() const
Definition SongEditor.h:249
Song Properties Dialog.
This dialog is used to import a SoundLibrary file from a local file or via HTTP.
constexpr int getPointSize(H2Core::FontTheme::FontSize fontSize) const
#define MAX_FX
Maximum number of effects.
Definition config.dox:83
@ EVENT_TEMPO_CHANGED
Definition EventQueue.h:103
@ EVENT_SELECTED_INSTRUMENT_CHANGED
Definition EventQueue.h:77