MainWindow.cpp 34.7 KB
Newer Older
Ludovic Fauvet's avatar
Ludovic Fauvet committed
1
/*****************************************************************************
2
 * MainWindow.cpp: VLMC MainWindow
Ludovic Fauvet's avatar
Ludovic Fauvet committed
3
 *****************************************************************************
4
 * Copyright (C) 2008-2016 VideoLAN
Ludovic Fauvet's avatar
Ludovic Fauvet committed
5 6
 *
 * Authors: Ludovic Fauvet <etix@l0cal.com>
7
 *          Hugo Beauzée-Luyssen <hugo@beauzee.fr>
Ludovic Fauvet's avatar
Ludovic Fauvet committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

24 25 26
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
27

28
#include <QSizePolicy>
29
#include <QDockWidget>
30
#include <QFileDialog>
31
#include <QSlider>
32
#include <QMessageBox>
33
#include <QDesktopServices>
34
#include <QUndoView>
35
#include <QUrl>
36
#include <QNetworkProxy>
37
#include <QSysInfo>
luyikei's avatar
luyikei committed
38
#include <QScrollArea>
39
#include <QProgressBar>
40
#include "Main/Core.h"
41
#include "Project/Project.h"
42 43 44
#include "Library/Library.h"
#include "Tools/VlmcDebug.h"
#include "Tools/VlmcLogger.h"
luyikei's avatar
luyikei committed
45
#include "Backend/IBackend.h"
46 47
#include "Workflow/MainWorkflow.h"
#include "Renderer/ClipRenderer.h"
48
#include "Commands/AbstractUndoStack.h"
49 50 51 52 53 54

/* Gui */
#include "MainWindow.h"
#include "About.h"
#include "export/RendererSettings.h"
#include "export/ShareOnInternet.h"
55
#include "settings/SettingsDialog.h"
56 57

/* Widgets */
58
#include "effectsengine/EffectsListView.h"
59
#include "library/MediaLibraryView.h"
luyikei's avatar
luyikei committed
60
#include "library/ClipLibraryView.h"
61
#include "preview/PreviewWidget.h"
62 63 64
#include "timeline/Timeline.h"

/* Settings / Preferences */
65
#include "Project/RecentProjects.h"
66
#include "wizard/ProjectWizard.h"
67
#include "Settings/Settings.h"
68
#include "LanguageHelper.h"
69
#include "Commands/KeyboardShortcutHelper.h"
Ludovic Fauvet's avatar
Ludovic Fauvet committed
70

luyikei's avatar
luyikei committed
71
MainWindow::MainWindow( Backend::IBackend* backend, QWidget *parent )
72 73
    : QMainWindow( parent )
    , m_backend( backend )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
74 75
    , m_projectPreferences( nullptr )
    , m_wizard( nullptr )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
76 77
{
    m_ui.setupUi( this );
78

luyikei's avatar
luyikei committed
79
    Core::instance()->logger()->setup();
80 81 82
    //Preferences
    initVlmcPreferences();

83
    // GUI
84
    createGlobalPreferences();
85
    initToolbar();
86
    createStatusBar();
87
    initializeDockWidgets();
88 89
    checkFolders();
    loadGlobalProxySettings();
90
    createProjectPreferences();
91
    updateRecentProjects();
92

Ludovic Fauvet's avatar
Ludovic Fauvet committed
93
#ifdef WITH_CRASHBUTTON
94 95
    setupCrashTester();
#endif
96

97 98 99 100 101 102 103 104
    connect( Core::instance()->project(), &Project::projectNameChanged,
             this, &MainWindow::projectNameChanged );
    connect( Core::instance()->project(), &Project::outdatedBackupFileFound,
             this, &MainWindow::onOudatedBackupFile );
    connect( Core::instance()->project(), &Project::backupProjectLoaded,
             this, &MainWindow::onBackupFileLoaded );
    connect( Core::instance()->project(), &Project::projectSaved,
             this, &MainWindow::onProjectSaved );
105 106
    connect( Core::instance()->project(), &Project::cleanStateChanged,
             this, &MainWindow::cleanStateChanged );
107 108
    connect( Core::instance()->recentProjects(), &RecentProjects::updated,
             this, &MainWindow::updateRecentProjects );
109 110

    // Connect the medialibrary before it starts discovering/reloading
111
    connect( Core::instance()->library(), &Library::progressUpdated, this,
112 113
             [this]( int percent ) {
        if ( percent < 100 )
114
        {
115
            m_progressBar->setValue( percent );
116 117 118 119
            m_ui.statusbar->show();
            m_ui.actionStatusbar->setEnabled( false );
            m_ui.actionStatusbar->setChecked( true );
        }
120 121 122 123
        else
        {
            m_progressBar->hide();
            m_ui.statusbar->clearMessage();
124 125 126 127 128 129
            m_ui.actionStatusbar->setEnabled( true );
            if ( VLMC_GET_BOOL( "private/ShowStatusbar" ) == false )
            {
                m_ui.actionStatusbar->setChecked( false );
                m_ui.statusbar->hide();
            }
130
        }
131
    });
132

133
    connect( Core::instance()->library(), &Library::discoveryProgress, this,
134 135 136
        [this](const QString& folder) {
            m_ui.statusbar->showMessage( "Discovering " + folder + "...", 2500 );
    }, Qt::QueuedConnection );
137
    connect( Core::instance()->library(), &Library::discoveryCompleted, this,
138 139 140 141
        [this]( const QString& ) {
            m_ui.statusbar->showMessage( "Analyzing media" );
    }, Qt::QueuedConnection );

142
#ifdef WITH_CRASHHANDLER
143
    if ( restoreSession() == true )
144 145
        return ;
#endif
146 147 148 149

    //All preferences have been created: restore them:
    Core::instance()->settings()->load();

150
    // Restore the geometry
151
    restoreGeometry( VLMC_GET_BYTEARRAY( "private/MainWindowGeometry" ) );
152
    // Restore the layout
153
    restoreState( VLMC_GET_BYTEARRAY( "private/MainWindowState" ) );
154
    retranslateUi();
155
    projectNameChanged( Project::unNamedProject );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
156 157
}

158 159
MainWindow::~MainWindow()
{
160 161
    m_projectPreview->stop();
    m_clipPreview->stop();
luyikei's avatar
luyikei committed
162
    delete m_timeline;
163 164
}

165 166 167
void
MainWindow::showWizard()
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
168
    if ( m_wizard == nullptr )
169
    {
170
        m_wizard = new ProjectWizard( this );
171 172 173
        m_wizard->setModal( true );
    }
    m_wizard->show();
174 175
}

176 177 178 179 180
void
MainWindow::retranslateUi()
{
    m_dockedUndoView->setWindowTitle( tr( "History" ) );
    m_dockedEffectsList->setWindowTitle( tr( "Effects List" ) );
luyikei's avatar
luyikei committed
181 182
    m_dockedMediaLibrary->setWindowTitle( tr( "Media Library" ) );
    m_dockedClipLibrary->setWindowTitle( tr( "Clip Library" ) );
183 184
    m_dockedClipPreview->setWindowTitle( tr( "Clip Preview" ) );
    m_dockedProjectPreview->setWindowTitle( tr( "Project Preview" ) );
185 186
}

187
void
188
MainWindow::changeEvent( QEvent *e )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
189 190 191 192
{
    switch ( e->type() )
    {
    case QEvent::LanguageChange:
193 194 195
        // Translate m_ui as a separate step to avoid calling it twice upon first
        // creation. We call retranslateUi() from MainWindow's constructor, but we also initialize
        // m_ui, which calls m_ui.retranslateUi()
Ludovic Fauvet's avatar
Ludovic Fauvet committed
196
        m_ui.retranslateUi( this );
197
        retranslateUi();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
198
        break;
199 200 201 202 203 204
    case QEvent::WindowStateChange:
        if ( isFullScreen() )
            m_ui.actionFullscreen->setChecked( true );
        else
            m_ui.actionFullscreen->setChecked( false );
        break;
Ludovic Fauvet's avatar
Ludovic Fauvet committed
205 206 207 208 209
    default:
        break;
    }
}

210
//use this helper when the shortcut is binded to a menu action
211 212 213
#define CREATE_MENU_SHORTCUT( key, defaultValue, name, desc, actionInstance  )      \
        VLMC_CREATE_PREFERENCE_KEYBOARD( key, defaultValue, name, desc );           \
        new KeyboardShortcutHelper( key, m_ui.actionInstance, this );
214

215 216 217
void
MainWindow::initVlmcPreferences()
{
218
    //Setup VLMC Keyboard Preference...
219 220 221
    VLMC_CREATE_PREFERENCE_KEYBOARD( "keyboard/mediapreview", "Ctrl+Return",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Media preview" ),
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Preview the selected media, or pause the current preview" ) );
222

223 224 225
    VLMC_CREATE_PREFERENCE_KEYBOARD( "keyboard/renderpreview", "Space",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Render preview" ),
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Preview the project, or pause the current preview" ) );
226

227
    //A bit nasty, but we better use what Qt's providing as default shortcut
228 229 230 231
    CREATE_MENU_SHORTCUT( "keyboard/undo",
                          QKeySequence( QKeySequence::Undo ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Undo" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Undo the last action" ), actionUndo );
232

233 234 235 236
    CREATE_MENU_SHORTCUT( "keyboard/redo",
                          QKeySequence( QKeySequence::Redo ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Redo" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Redo the last action" ), actionRedo );
237

238 239 240 241
    CREATE_MENU_SHORTCUT( "keyboard/help",
                          QKeySequence( QKeySequence::HelpContents ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Help" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Toggle the help page" ), actionHelp );
242

243 244 245
    CREATE_MENU_SHORTCUT( "keyboard/quit", "Ctrl+Q",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Quit" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Quit VLMC" ), actionQuit );
246

247 248 249
    CREATE_MENU_SHORTCUT( "keyboard/preferences", "Alt+P",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Preferences" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Open VLMC preferences" ), actionPreferences );
250

251 252 253 254
    CREATE_MENU_SHORTCUT( "keyboard/projectpreferences", "Ctrl+P",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Project preferences" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Open the project preferences"), actionProject_Preferences );

255 256 257
    CREATE_MENU_SHORTCUT( "keyboard/fullscreen", "F",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Fullscreen" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Switch to fullscreen mode" ), actionFullscreen );
258

259 260 261
    CREATE_MENU_SHORTCUT( "keyboard/newproject",
                          QKeySequence( QKeySequence::New ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "New project" ),
262
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Open the new project wizard" ), actionNew_Project );
263

264 265 266 267
    CREATE_MENU_SHORTCUT( "keyboard/openproject",
                          QKeySequence( QKeySequence::Open ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Open a project" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Open an existing project" ), actionLoad_Project );
268

269 270 271 272
    CREATE_MENU_SHORTCUT( "keyboard/save",
                          QKeySequence( QKeySequence::Save ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Save" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Save the current project" ), actionSave );
273

274 275 276
    CREATE_MENU_SHORTCUT( "keyboard/saveas", "Ctrl+Shift+S",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Save as" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Save the current project to a new file" ), actionSave_As );
277

278 279 280 281
    CREATE_MENU_SHORTCUT( "keyboard/closeproject",
                          QKeySequence( QKeySequence::Close ).toString().toLocal8Bit(),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Close the project" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Close the current project" ), actionClose_Project );
282

283 284 285
    CREATE_MENU_SHORTCUT( "keyboard/importmedia", "Ctrl+I",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Import media" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Open the import window" ), actionImport );
286

287 288 289 290
    CREATE_MENU_SHORTCUT( "keyboard/renderproject", "Ctrl+R",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Render the project" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Render the project to a file" ), actionRender );

291 292 293 294 295 296 297 298
    CREATE_MENU_SHORTCUT( "keyboard/defaultmode", "n",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Selection mode" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Select the selection tool in the timeline" ), actionSelection_mode );

    CREATE_MENU_SHORTCUT( "keyboard/cutmode", "x",
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Cut mode" ),
                          QT_TRANSLATE_NOOP( "PreferenceWidget", "Select the cut/razor tool in the timeline" ), actionCut_mode );

299
    //Setup VLMC Lang. Preferences...
300
    SettingValue* lang = VLMC_CREATE_PREFERENCE_LANGUAGE( "vlmc/VLMCLang", "default",
301
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Language" ),
302
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "The VLMC's UI language" ) );
303

304
    connect( lang, SIGNAL( changed( QVariant ) ),
luyikei's avatar
luyikei committed
305
             LanguageHelper::instance(), SLOT( languageChanged( const QVariant& ) ) );
306

307
    //Setup VLMC General Preferences...
308
    VLMC_CREATE_PREFERENCE_BOOL( "vlmc/ConfirmDeletion", true,
309
                                 QT_TRANSLATE_NOOP( "PreferenceWidget", "Confirm clip deletion"),
310
                                 QT_TRANSLATE_NOOP( "PreferenceWidget", "Ask for confirmation before deleting a clip from the timeline" ) );
311

312
    VLMC_CREATE_PREFERENCE_PATH( "vlmc/TempFolderLocation", QDir::tempPath() + "/VLMC/",
Rohit Yadav's avatar
Rohit Yadav committed
313
                                    QT_TRANSLATE_NOOP( "PreferenceWidget", "Temporary folder" ),
314 315
                                    QT_TRANSLATE_NOOP( "PreferenceWidget", "The temporary folder used by VLMC to process videos." ) );

316
    //Setup VLMC Youtube Preference...
317
    VLMC_CREATE_PREFERENCE_STRING( "youtube/DeveloperKey", "",
318
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Youtube Developer Key" ),
319
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "VLMC's Youtube Developer Key" ) );
320 321 322

    VLMC_CREATE_PREFERENCE_STRING( "youtube/Username", "username",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Youtube Username" ),
323 324 325 326
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Valid YouTube username" ) );

    VLMC_CREATE_PREFERENCE_PASSWORD( "youtube/Password", "",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Youtube Password" ),
327
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Leave this field empty, password will be stored in unencrypted form." ) );
328 329

    //Setup VLMC Proxy Settings
330 331 332 333 334 335 336
    VLMC_CREATE_PREFERENCE_BOOL( "network/UseProxy", false,
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Enable Proxy for VLMC"),
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Enables Global Network Proxy for VLMC." ) );

    VLMC_CREATE_PREFERENCE_STRING( "network/ProxyHost", "",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Proxy Hostname" ),
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Set Proxy Hostname." ) );
337 338 339

    VLMC_CREATE_PREFERENCE_STRING( "network/ProxyPort", "",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Proxy Port" ),
340
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Set Proxy Port." ) );
341 342 343

    VLMC_CREATE_PREFERENCE_STRING( "network/ProxyUsername", "",
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Proxy Username" ),
344
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Set Proxy Username, if any." ) );
345

346
    VLMC_CREATE_PREFERENCE_PASSWORD( "network/ProxyPassword", "",
347
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Proxy Password" ),
348
                                     QT_TRANSLATE_NOOP( "PreferenceWidget", "Set Proxy Password, if any." ) );
349

350 351 352 353 354 355
    // Setup private variables
    VLMC_CREATE_PRIVATE_PREFERENCE_BOOL( "private/CleanQuit", true );
    VLMC_CREATE_PRIVATE_PREFERENCE_STRING( "private/EmergencyBackup", "" );
    VLMC_CREATE_PRIVATE_PREFERENCE_STRING( "private/ImportPreviouslySelectPath", QDir::homePath() );
    VLMC_CREATE_PRIVATE_PREFERENCE_BYTEARRAY( "private/MainWindowGeometry", "" );
    VLMC_CREATE_PRIVATE_PREFERENCE_BYTEARRAY( "private/MainWindowState", "" );
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374

    // Toolbar
    auto toolbarSetting = VLMC_CREATE_PRIVATE_PREFERENCE_BOOL( "private/ShowToolbar", false );
    connect( toolbarSetting, &SettingValue::changed, this, [this]( const QVariant& var )
    {
        bool v = var.toBool();
        m_ui.toolBar->setVisible( v );
        m_ui.actionToolbar->setChecked( v );
    } );
    connect( m_ui.actionToolbar, &QAction::toggled, toolbarSetting, &SettingValue::set );

    // Statusbar
    auto statusbarSetting = VLMC_CREATE_PRIVATE_PREFERENCE_BOOL( "private/ShowStatusbar", false );
    connect( statusbarSetting, &SettingValue::changed, this, [this]( const QVariant& var )
    {
        bool v = var.toBool();
        m_ui.statusbar->setVisible( v );
        m_ui.actionStatusbar->setChecked( v );
    } );
375
    connect( m_ui.actionStatusbar, &QAction::triggered, statusbarSetting, &SettingValue::set );
376 377 378 379 380 381 382 383 384 385 386 387 388 389

    // Layout Lock
    auto lockSetting = VLMC_CREATE_PRIVATE_PREFERENCE_BOOL( "private/LayoutLock", true );
    connect( lockSetting, &SettingValue::changed, this, [this]( const QVariant& var )
    {
        bool v = var.toBool();
        for ( auto w : findChildren<QDockWidget *>() )
            if ( v == true )
                w->setTitleBarWidget( new QWidget );
            else
                w->setTitleBarWidget( 0 );
         m_ui.actionLock->setChecked( v );
    } );
    connect( m_ui.actionLock, &QAction::toggled, lockSetting, &SettingValue::set );
390 391
}

392 393
#undef CREATE_MENU_SHORTCUT

394 395
void
MainWindow::on_actionSave_triggered()
396
{
luyikei's avatar
luyikei committed
397
    if ( Core::instance()->project()->hasProjectFile() == false )
398
        on_actionSave_As_triggered();
barun511's avatar
barun511 committed
399
    else
luyikei's avatar
luyikei committed
400
        Core::instance()->project()->save();
401 402
}

403
void
404
MainWindow::on_actionSave_As_triggered()
405
{
406 407 408 409 410
    auto path = Core::instance()->recentProjects()->mostRecentProjectFile();

    if ( path.isEmpty() == true )
        path = VLMC_GET_STRING( "vlmc/WorkspaceLocation" );

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
411
    QString dest = QFileDialog::getSaveFileName( nullptr, QObject::tr( "Enter the output file name" ),
412
                                  path, QObject::tr( "VLMC project file(*.vlmc)" ) );
413 414
    if ( dest.isEmpty() == true )
        return;
415
    if ( !dest.endsWith( ".vlmc" ) )
416
        dest += ".vlmc";
luyikei's avatar
luyikei committed
417
    Core::instance()->project()->saveAs( dest );
418 419
}

420
void
421
MainWindow::on_actionLoad_Project_triggered()
422
{
423
    QString folder = VLMC_GET_STRING( "vlmc/WorkspaceLocation" );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
424
    QString fileName = QFileDialog::getOpenFileName( nullptr, tr( "Please choose a project file" ),
425
                                    folder, tr( "VLMC project file(*.vlmc)" ) );
426 427
    if ( fileName.isEmpty() == true )
        return ;
luyikei's avatar
luyikei committed
428
    Core::instance()->loadProject( fileName );
429 430
}

431
void
432
MainWindow::createStatusBar()
433
{
434 435 436
    m_progressBar = new QProgressBar( this );
    m_ui.statusbar->addPermanentWidget( m_progressBar );
    m_progressBar->show();
437

438 439 440 441 442
    // Spacer
    QWidget* spacer = new QWidget( this );
    spacer->setFixedWidth( 20 );
    m_ui.statusbar->addPermanentWidget( spacer );

443 444 445
    // Zoom Out
    QToolButton* zoomOutButton = new QToolButton( this );
    zoomOutButton->setIcon( QIcon( ":/images/zoomout" ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
446
    zoomOutButton->setStatusTip( tr( "Zoom out" ) );
447 448 449
    m_ui.statusbar->addPermanentWidget( zoomOutButton );
    connect( zoomOutButton, SIGNAL( clicked() ),
             this, SLOT( zoomOut() ) );
450 451

    // Zoom slider
452 453 454 455 456 457
    m_zoomSlider = new QSlider( this );
    m_zoomSlider->setOrientation( Qt::Horizontal );
    m_zoomSlider->setTickInterval( 1 );
    m_zoomSlider->setSingleStep( 1 );
    m_zoomSlider->setPageStep( 1 );
    m_zoomSlider->setMinimum( 0 );
458
    m_zoomSlider->setMaximum( 9 );
459
    m_zoomSlider->setValue( 4 );
460
    m_zoomSlider->setFixedWidth( 80 );
461
    m_zoomSlider->setInvertedAppearance( true );
462
    m_ui.statusbar->addPermanentWidget( m_zoomSlider );
463

464 465 466
    // Zoom IN
    QToolButton* zoomInButton = new QToolButton( this );
    zoomInButton->setIcon( QIcon( ":/images/zoomin" ) );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
467
    zoomInButton->setStatusTip( tr( "Zoom in" ) );
468 469 470
    m_ui.statusbar->addPermanentWidget( zoomInButton );
    connect( zoomInButton, SIGNAL( clicked() ),
             this, SLOT( zoomIn() ) );
471 472
    connect( m_zoomSlider, &QSlider::valueChanged, this, [this]( int val )
    {
473
        emit scaleChanged( val );
474
    } );
475 476
}

477
void
478
MainWindow::initializeDockWidgets()
479
{
480
    m_timeline = new Timeline( Core::instance()->project()->settings(), this );
luyikei's avatar
luyikei committed
481
    setCentralWidget( m_timeline->container() );
482

483
    setupLibrary();
484
    setupEffectsList();
485 486
    setupClipPreview();
    setupProjectPreview();
487 488
    setupUndoRedoWidget();
}
489

490 491 492
void
MainWindow::setupUndoRedoWidget()
{
493
    m_undoView = new QUndoView;
luyikei's avatar
luyikei committed
494
    m_undoView->setObjectName( QStringLiteral( "History" ) );
495
    m_dockedUndoView = dockWidget( m_undoView, Qt::TopDockWidgetArea );
496
    auto stack = Core::instance()->workflow()->undoStack();
497 498 499 500 501
    connect( stack, SIGNAL( canUndoChanged( bool ) ), this, SLOT( canUndoChanged( bool ) ) );
    connect( stack, SIGNAL( canRedoChanged( bool ) ), this, SLOT( canRedoChanged( bool ) ) );
    canUndoChanged( stack->canUndo() );
    canRedoChanged( stack->canRedo() );
    m_undoView->setStack( stack );
502
}
503

504 505 506
void
MainWindow::setupEffectsList()
{
507
    m_effectsList = new EffectsListView;
508
    m_dockedEffectsList = dockWidget( m_effectsList->container(), Qt::TopDockWidgetArea );
509 510 511 512 513
}

void
MainWindow::setupLibrary()
{
514
    m_mediaLibrary = new MediaLibraryView( this );
luyikei's avatar
luyikei committed
515 516 517
    m_clipLibrary = new ClipLibraryView( this );
    m_dockedMediaLibrary = dockWidget( m_mediaLibrary->container(), Qt::TopDockWidgetArea );
    m_dockedClipLibrary = dockWidget( m_clipLibrary->container(), Qt::TopDockWidgetArea );
518 519
}

520 521 522 523 524 525 526
void
MainWindow::setupClipPreview()
{
    m_clipPreview = new PreviewWidget;
    auto renderer = new ClipRenderer;
    renderer->setParent( m_clipPreview );
    m_clipPreview->setRenderer( renderer );
luyikei's avatar
luyikei committed
527 528
    connect( Core::instance()->library(), &Library::clipRemoved,
             renderer, &ClipRenderer::clipUnloaded );
luyikei's avatar
luyikei committed
529 530
    connect( m_clipLibrary, &ClipLibraryView::clipSelected, renderer, [renderer]( const QString& uuid )
    {
531
        renderer->setClip( Core::instance()->library()->clip( uuid ) );
luyikei's avatar
luyikei committed
532
    } );
533 534
    connect( m_mediaLibrary, &MediaLibraryView::baseClipSelected, renderer, [renderer]( const QString& uuid )
    {
535
        renderer->setClip( Core::instance()->library()->clip( uuid ) );
536 537
    } );

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554

    KeyboardShortcutHelper* clipShortcut = new KeyboardShortcutHelper( "keyboard/mediapreview", this );
    connect( clipShortcut, SIGNAL( activated() ), m_clipPreview, SLOT( on_pushButtonPlay_clicked() ) );
    m_dockedClipPreview = dockWidget( m_clipPreview, Qt::TopDockWidgetArea );
}

void
MainWindow::setupProjectPreview()
{
    m_projectPreview = new PreviewWidget;
    m_projectPreview->setClipEdition( false );
    m_projectPreview->setRenderer( Core::instance()->workflow()->renderer() );
    KeyboardShortcutHelper* renderShortcut = new KeyboardShortcutHelper( "keyboard/renderpreview", this );
    connect( renderShortcut, SIGNAL( activated() ), m_projectPreview, SLOT( on_pushButtonPlay_clicked() ) );
    m_dockedProjectPreview = dockWidget( m_projectPreview, Qt::TopDockWidgetArea );
}

555 556 557 558 559 560 561 562
void
MainWindow::initToolbar()
{
    QActionGroup    *mouseActions = new QActionGroup( m_ui.toolBar );
    mouseActions->addAction( m_ui.actionSelection_mode );
    mouseActions->addAction( m_ui.actionCut_mode );
    m_ui.actionSelection_mode->setChecked( true );
    m_ui.toolBar->addActions( mouseActions->actions() );
563
    connect( mouseActions, SIGNAL( triggered( QAction* ) ),
564 565
             this, SLOT( toolButtonClicked( QAction* ) ) );
    m_ui.menuTools->addActions( mouseActions->actions() );
566 567 568 569 570 571 572
#if defined ( Q_OS_MAC )
    // Use native fullscreen on OS X >= LION
    if ( QSysInfo::macVersion() >= QSysInfo::MV_LION ) {
        m_ui.actionFullscreen->setEnabled( false );
        m_ui.actionFullscreen->setVisible( false );
    }
#endif
573 574
}

575 576
void
MainWindow::createGlobalPreferences()
577
{
luyikei's avatar
luyikei committed
578
    m_globalPreferences = new SettingsDialog( Core::instance()->settings(), tr( "VLMC Preferences" ), this );
579 580 581 582
    m_globalPreferences->addCategory( "vlmc", QT_TRANSLATE_NOOP( "Settings", "General" ), QIcon( ":/images/vlmc" ) );
    m_globalPreferences->addCategory( "keyboard", QT_TRANSLATE_NOOP( "Settings", "Keyboard" ), QIcon( ":/images/keyboard" ) );
    m_globalPreferences->addCategory( "youtube", QT_TRANSLATE_NOOP( "Settings", "YouTube" ), QIcon( ":/images/youtube" ) );
    m_globalPreferences->addCategory( "network", QT_TRANSLATE_NOOP( "Settings", "Network" ), QIcon( ":/images/network" ) );
583 584
}

585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
void
MainWindow::loadGlobalProxySettings()
{
    if( VLMC_GET_BOOL( "network/UseProxy" ) )
    {
        /* Updates Global Proxy for VLMC */
        QNetworkProxy proxy;
        proxy.setType( QNetworkProxy::HttpProxy );
        proxy.setHostName( VLMC_GET_STRING( "network/ProxyHost" ) );
        proxy.setPort( VLMC_GET_STRING( "network/ProxyPort" ).toInt() );
        proxy.setUser( VLMC_GET_STRING( "network/ProxyUsername" ) );
        proxy.setPassword( VLMC_GET_STRING( "network/ProxyPassword" ) );
        QNetworkProxy::setApplicationProxy( proxy );
        return;
    }

    QNetworkProxy::setApplicationProxy( QNetworkProxy::NoProxy );
}

604 605
void
MainWindow::createProjectPreferences()
606
{
luyikei's avatar
luyikei committed
607
    m_projectPreferences = new SettingsDialog( Core::instance()->project()->settings(), tr( "Project preferences" ), this );
608 609 610
    m_projectPreferences->addCategory( "general", QT_TRANSLATE_NOOP( "Settings", "General" ), QIcon( ":/images/vlmc" ) );
    m_projectPreferences->addCategory( "video", QT_TRANSLATE_NOOP( "Settings", "Video" ), QIcon( ":/images/video" ) );
    m_projectPreferences->addCategory( "audio", QT_TRANSLATE_NOOP( "Settings", "Audio" ), QIcon( ":/images/audio" ) );
611 612
}

613 614 615 616
void
MainWindow::checkFolders()
{
    QDir dirUtil;
617 618
    if ( !dirUtil.exists( VLMC_GET_STRING( "vlmc/TempFolderLocation" ) ) )
        dirUtil.mkdir( VLMC_GET_STRING( "vlmc/TempFolderLocation" ) );
619 620
}

621 622 623 624
void
MainWindow::clearTemporaryFiles()
{
    QDir dirUtil;
625
    if( dirUtil.cd( VLMC_GET_STRING( "vlmc/TempFolderLocation" ) ) )
luyikei's avatar
luyikei committed
626
        for ( const QString& file: dirUtil.entryList( QDir::Files ) )
627 628
            dirUtil.remove( file );
}
629

630 631
//Private slots definition

632
void
633
MainWindow::on_actionQuit_triggered()
Ludovic Fauvet's avatar
Ludovic Fauvet committed
634
{
635
    saveSettings();
636
    close();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
637
}
638

639
void
640
MainWindow::on_actionPreferences_triggered()
641
{
642
   m_globalPreferences->show();
643
}
Ludovic Fauvet's avatar
Ludovic Fauvet committed
644

645
void
646
MainWindow::on_actionAbout_triggered()
Ludovic Fauvet's avatar
Ludovic Fauvet committed
647
{
648 649
    About about( this );
    about.exec();
Ludovic Fauvet's avatar
Ludovic Fauvet committed
650
}
651

652 653
bool
MainWindow::checkVideoLength()
654
{
luyikei's avatar
luyikei committed
655
    if ( Core::instance()->workflow()->canRender() == false )
656
    {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
657
        QMessageBox::warning( nullptr, tr ( "VLMC Renderer" ), tr( "There is nothing to render." ) );
658
        return false;
luyikei's avatar
luyikei committed
659
    }
660 661 662 663
    return true;
}

bool
664
MainWindow::renderVideoSettings( bool shareOnInternet )
665
{
666
    RendererSettings settings( shareOnInternet );
667

668
    if ( settings.exec() == QDialog::Rejected )
669 670
        return false;

671 672 673 674 675 676 677
    QString     outputFileName = settings.outputFileName();
    quint32     width          = settings.width();
    quint32     height         = settings.height();
    double      fps            = settings.fps();
    quint32     vbitrate       = settings.videoBitrate();
    quint32     abitrate       = settings.audioBitrate();
    auto        ar             = settings.aspectRatio();
678 679
    auto        nbChannels     = settings.nbChannels();
    auto        sampleRate     = settings.sampleRate();
680

luyikei's avatar
luyikei committed
681 682 683 684

    return  Core::instance()->workflow()->startRenderToFile( outputFileName, width, height,
                                                             fps, ar, vbitrate, abitrate,
                                                             nbChannels, sampleRate );
685 686
}

687 688 689 690 691 692 693 694
QDockWidget*
MainWindow::dockWidget( QWidget* widget, Qt::DockWidgetArea startArea )
{
    QDockWidget*    dock = new QDockWidget( this );

    dock->setAllowedAreas( Qt::AllDockWidgetAreas );
    widget->setParent( dock );
    dock->setWidget( widget );
695
    dock->setObjectName( widget->objectName() );
696 697 698 699
    if ( VLMC_GET_BOOL( "private/LayoutLock" ) == true )
        dock->setTitleBarWidget( new QWidget );
    else
        dock->setTitleBarWidget( 0 );
700 701 702 703 704
    addDockWidget( startArea, dock );
    registerWidgetInWindowMenu( dock );
    return dock;
}

705
void
706 707 708
MainWindow::on_actionRender_triggered()
{
    if ( checkVideoLength() )
709
    {
710
        //Setup dialog box for querying render parameters.
711
        renderVideoSettings( false );
712
    }
713 714
}

715
void
716
MainWindow::on_actionShare_On_Internet_triggered()
717 718 719
{
    if ( checkVideoLength() )
    {
720 721
        if( !renderVideoSettings( true ) )
            return;
722

723
        checkFolders();
724
        QString fileName = VLMC_GET_STRING( "vlmc/TempFolderLocation" ) + "/" +
luyikei's avatar
luyikei committed
725
                           Core::instance()->project()->name() +
726
                           "-vlmc.mp4";
727

728
        loadGlobalProxySettings();
729

730 731
        ShareOnInternet *shareVideo = new ShareOnInternet();
        shareVideo->setVideoFile( fileName );
732

733 734 735 736 737
        if ( shareVideo->exec() == QDialog::Rejected )
        {
            delete shareVideo;
            return;
        }
738

739
        delete shareVideo;
740 741 742
    }
}

743
void
744
MainWindow::on_actionNew_Project_triggered()
745
{
746 747
    m_wizard->restart();
    m_wizard->show();
748 749
}

750
void
751
MainWindow::on_actionHelp_triggered()
752
{
753
    QDesktopServices::openUrl( QUrl( "http://videolan.org/vlmc" ) );
754 755
}

756
void
757
MainWindow::zoomIn()
Ludovic Fauvet's avatar
Ludovic Fauvet committed
758 759 760 761
{
    m_zoomSlider->setValue( m_zoomSlider->value() - 1 );
}

762
void
763
MainWindow::zoomOut()
Ludovic Fauvet's avatar
Ludovic Fauvet committed
764 765 766
{
    m_zoomSlider->setValue( m_zoomSlider->value() + 1 );
}
Ludovic Fauvet's avatar
Ludovic Fauvet committed
767

luyikei's avatar
luyikei committed
768 769 770 771 772 773
void
MainWindow::setScale( quint32 scale )
{
    m_zoomSlider->setValue( scale );
}

774
void
775
MainWindow::on_actionFullscreen_triggered( bool checked )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
776
{
777 778 779
    if ( checked && !isFullScreen() )
    {
        setUnifiedTitleAndToolBarOnMac( false );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
780
        showFullScreen();
781
    }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
782
    else
783 784
    {
        setUnifiedTitleAndToolBarOnMac( true );
Ludovic Fauvet's avatar
Ludovic Fauvet committed
785
        showNormal();
786
    }
Ludovic Fauvet's avatar
Ludovic Fauvet committed
787
}
788

789
void
790
MainWindow::registerWidgetInWindowMenu( QDockWidget* widget )
791
{
792
    m_ui.menuView->insertAction( m_ui.widgetsSeparator, widget->toggleViewAction() );
793
}
794

795
void
796
MainWindow::toolButtonClicked( QAction *action )
797
{
798
    if ( action == m_ui.actionSelection_mode )
799
        emit selectionToolSelected();
800
    else if ( action == m_ui.actionCut_mode )
801
        emit cutToolSelected();
802
    else
803
        vlmcCritical() << "Unknown tool. This should not happen !";
804
}
805

806
void
807
MainWindow::on_actionProject_Preferences_triggered()
808
{
809
  m_projectPreferences->show();
810
}
811

812 813
bool
MainWindow::saveSettings()
814
{
815
    // ??????
816
    clearTemporaryFiles();
luyikei's avatar
luyikei committed
817
    Settings* settings = Core::instance()->settings();
818 819 820 821 822 823
    // Save the current geometry
    settings->setValue( "private/MainWindowGeometry", saveGeometry() );
    // Save the current layout
    settings->setValue( "private/MainWindowState", saveState() );
    settings->setValue( "private/CleanQuit", true );
    return true;
824 825 826 827 828
}

void
MainWindow::closeEvent( QCloseEvent* e )
{
luyikei's avatar
luyikei committed
829
    if ( Core::instance()->project()->isClean() == false )
830 831 832 833 834 835 836 837 838 839
    {
        QMessageBox msgBox;
        msgBox.setText( QObject::tr( "The project has been modified." ) );
        msgBox.setInformativeText( QObject::tr( "Do you want to save it?" ) );
        msgBox.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
        msgBox.setDefaultButton( QMessageBox::Save );
        int     ret = msgBox.exec();
        switch ( ret )
        {
        case QMessageBox::Save:
840
            on_actionSave_triggered();
841 842 843 844 845 846 847 848 849 850
            break;
        case QMessageBox::Discard:
            break;
        case QMessageBox::Cancel:
            e->ignore();
            return;
        }
    }
    saveSettings();
    e->accept();
851
}
852

853 854 855 856 857 858 859
void
MainWindow::updateRecentProjects()
{
    auto menu = new QMenu;
    for ( const auto& var : Core::instance()->recentProjects()->toVariant().toList() )
    {
        const auto& m = var.toMap();
860 861
        const auto& name = m["name"].toString();
        const auto& file = m["file"].toString();
862 863 864 865 866 867 868 869 870 871 872 873
        auto action = menu->addAction( QString( "%1 - %2" )
                                       .arg( name )
                                       .arg( file )
                                       );
        connect( action, &QAction::triggered, this, [this, file]()
        {
            Core::instance()->loadProject( file );
        } );
    }
    m_ui.actionRecent_Projects->setMenu( menu );
}

874
void
875
MainWindow::projectNameChanged( const QString& projectName )
876
{
877
    QString title = tr( "%1 VideoLAN Movie Creator [*]" ).arg( projectName );
878 879 880 881 882 883
    setWindowTitle( title );
}

void
MainWindow::cleanStateChanged( bool isClean )
{
884 885 886 887 888 889 890
    setWindowModified( isClean == false );
}

void
MainWindow::onProjectSaved()
{
    setWindowModified( false );
891
}
892

893
void
894
MainWindow::on_actionUndo_triggered()
895
{
896
    Core::instance()->workflow()->undoStack()->undo();
897 898
}

899
void
900
MainWindow::on_actionRedo_triggered()
901
{
902
    Core::instance()->workflow()->undoStack()->redo();
903 904
}

905
void
906
MainWindow::on_actionCrash_triggered()
907
{
908 909 910 911 912
    //WARNING: read this part at your own risk !!
    //I'm not responsible if you puke while reading this :D
    QString str;
    int test = 1 / str.length();
    Q_UNUSED( test );
913
}
914

915
bool
916
MainWindow::restoreSession()
917
{
918 919
    bool    cleanQuit = VLMC_GET_BOOL( "private/CleanQuit" );
    bool    ret = false;
920

921
    if ( cleanQuit == false )
922
    {
923
        QMessageBox::StandardButton res = QMessageBox::question( this, tr( "Crash recovery" ), tr( "VLMC didn't close nicely. Do you want to recover your project?" ),
924 925
                               QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes );
        if ( res == QMessageBox::Yes )
926
        {
927 928 929 930 931 932 933 934 935 936 937
            auto path = Core::instance()->recentProjects()->mostRecentProjectFile();
            if ( path.isEmpty() == true )
            {
                QMessageBox::warning( this, tr( "Can't restore project" ), tr( "VLMC didn't manage to restore your project. We apology for the inconvenience" ) );
                ret = false;
            }
            else
            {
                Core::instance()->project()->load( path );
                ret = true;
            }
938 939
        }
    }
940
    Core::instance()->settings()->setValue( "private/CleanQuit", ret );
941 942
    return ret;
}
943

944
void
945
MainWindow::canUndoChanged( bool canUndo )
946 947 948 949
{
    m_ui.actionUndo->setEnabled( canUndo );
}

950
void
951
MainWindow::canRedoChanged( bool canRedo )
952 953 954 955
{
    m_ui.actionRedo->setEnabled( canRedo );
}

956 957 958
void
MainWindow::onOudatedBackupFile()
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
959
    if ( QMessageBox::question( nullptr, QObject::tr( "Backup file" ),
960 961 962 963
                                      QObject::tr( "An outdated backup file was found. "
                                     "Do you want to erase it?" ),
                                    QMessageBox::Ok | QMessageBox::No ) == QMessageBox::Ok )
    {
luyikei's avatar
luyikei committed
964
        Core::instance()->project()->removeBackupFile();
965 966 967 968 969 970 971 972 973
    }
}

void
MainWindow::onBackupFileLoaded()
{
    //FIXME: Adjust the behavior depending on how we react to a crash
}

Ludovic Fauvet's avatar
Ludovic Fauvet committed
974
#ifdef WITH_CRASHBUTTON
975 976
void
MainWindow::setupCrashTester()
977 978 979 980 981 982 983 984
{
    QAction* actionCrash = new QAction( this );
    actionCrash->setObjectName( QString::fromUtf8( "actionCrash" ) );
    m_ui.menuTools->addAction( actionCrash );
    actionCrash->setText( QApplication::translate( "MainWindow", "Crash", 0, QApplication::UnicodeUTF8 ) );
    connect( actionCrash, SIGNAL( triggered( bool ) ), this, SLOT( on_actionCrash_triggered() ) );
}
#endif
985