/* * Copyright (C) 2012 Webdoc SA * * This file is part of Open-Sankoré. * * Open-Sankoré 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, version 3 of the License, * with a specific linking exception for the OpenSSL project's * "OpenSSL" library (or with modified versions of it that use the * same license as the "OpenSSL" library). * * Open-Sankoré 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 Open-Sankoré. If not, see . */ #include "UBPodcastController.h" #include "frameworks/UBFileSystemUtils.h" #include "frameworks/UBStringUtils.h" #include "frameworks/UBPlatformUtils.h" #include "core/UBApplication.h" #include "core/UBSettings.h" #include "core/UBSetting.h" #include "core/UBDisplayManager.h" #include "board/UBBoardController.h" #include "board/UBBoardView.h" #include "board/UBBoardPaletteManager.h" #include "gui/UBMainWindow.h" #include "web/UBWebController.h" #include "web/browser/WBWebView.h" #include "domain/UBGraphicsScene.h" #include "UBAbstractVideoEncoder.h" #include "podcast/youtube/UBYouTubePublisher.h" #include "podcast/intranet/UBIntranetPodcastPublisher.h" #include "UBPodcastRecordingPalette.h" #ifdef Q_WS_WIN #include "windowsmedia/UBWindowsMediaVideoEncoder.h" #include "windowsmedia/UBWaveRecorder.h" #elif defined(Q_WS_MAC) #include "quicktime/UBQuickTimeVideoEncoder.h" #include "quicktime/UBAudioQueueRecorder.h" #endif #include "core/memcheck.h" UBPodcastController* UBPodcastController::sInstance = 0; unsigned int UBPodcastController::sBackgroundColor = 0x00000000; // BBGGRRAA UBPodcastController::UBPodcastController(QObject* pParent) : QObject(pParent) , mVideoEncoder(0) , mIsGrabbing(false) , mInitialized(false) , mVideoFramesPerSecondAtStart(10) , mVideoFrameSizeAtStart(1024, 768) , mVideoBitsPerSecondAtStart(1700000) , mSourceWidget(0) , mSourceScene(0) , mScreenGrabingTimerEventID(0) , mRecordingProgressTimerEventID(0) , mRecordingPalette(0) , mRecordingState(Stopped) , mApplicationIsClosing(false) , mRecordingTimestampOffset(0) , mDefaultAudioInputDeviceAction(0) , mNoAudioInputDeviceAction(0) , mSmallVideoSizeAction(0) , mMediumVideoSizeAction(0) , mFullVideoSizeAction(0) , mYoutubePublicationAction(0) , mIntranetPublicationAction(0) { connect(UBApplication::applicationController, SIGNAL(mainModeChanged(UBApplicationController::MainMode)), this, SLOT(applicationMainModeChanged(UBApplicationController::MainMode))); connect(UBApplication::applicationController, SIGNAL(desktopMode(bool)), this, SLOT(applicationDesktopMode(bool))); connect(UBApplication::webController, SIGNAL(activeWebPageChanged(WBWebView*)), this, SLOT(webActiveWebPageChanged(WBWebView*))); connect(UBApplication::app(), SIGNAL(lastWindowClosed()), this, SLOT(applicationAboutToQuit())); } UBPodcastController::~UBPodcastController() { // NOOP } void UBPodcastController::applicationAboutToQuit() { mApplicationIsClosing = true; if(mRecordingState == Recording || mRecordingState == Paused) { stop(); } } void UBPodcastController::groupActionTriggered(QAction* action) { Q_UNUSED(action); updateActionState(); } void UBPodcastController::actionToggled(bool checked) { Q_UNUSED(checked); updateActionState(); } void UBPodcastController::updateActionState() { if (mSmallVideoSizeAction && mSmallVideoSizeAction->isChecked()) UBSettings::settings()->podcastVideoSize->set("Small"); else if (mFullVideoSizeAction && mFullVideoSizeAction->isChecked()) UBSettings::settings()->podcastVideoSize->set("Full"); else UBSettings::settings()->podcastVideoSize->reset(); UBSettings::settings()->podcastAudioRecordingDevice->reset(); if (mDefaultAudioInputDeviceAction && mDefaultAudioInputDeviceAction->isChecked()) UBSettings::settings()->podcastAudioRecordingDevice->set("Default"); else if (mNoAudioInputDeviceAction && mNoAudioInputDeviceAction->isChecked()) UBSettings::settings()->podcastAudioRecordingDevice->set("None"); else { foreach(QAction* action, mAudioInputDevicesActions) { if (action->isChecked()) { UBSettings::settings()->podcastAudioRecordingDevice->set(action->text()); break; } } } UBSettings::settings()->podcastPublishToYoutube->set(mYoutubePublicationAction && mYoutubePublicationAction->isChecked()); UBSettings::settings()->podcastPublishToIntranet->set(mIntranetPublicationAction && mIntranetPublicationAction->isChecked()); } void UBPodcastController::setSourceWidget(QWidget* pWidget) { if (mSourceWidget != pWidget) { if (mSourceWidget == qApp->desktop()) { killTimer(mScreenGrabingTimerEventID); mScreenGrabingTimerEventID = 0; } else if (mSourceWidget) { mSourceWidget->removeEventFilter(this); } mSourceWidget = pWidget; mInitialized = false; mViewToVideoTransform.reset(); mLatestCapture.fill(sBackgroundColor); if (mSourceWidget) { QSizeF sourceWidgetSize(mSourceWidget->size()); if (mSourceWidget == qApp->desktop()) sourceWidgetSize = qApp->desktop()->availableGeometry(UBApplication::applicationController->displayManager()->controleScreenIndex()).size(); QSizeF videoFrameSize(mVideoFrameSizeAtStart); qreal scaleHorizontal = videoFrameSize.width() / sourceWidgetSize.width(); qreal scaleVertical = videoFrameSize.height() / sourceWidgetSize.height(); qreal scale = qMin(scaleHorizontal, scaleVertical); mViewToVideoTransform.scale(scale, scale); UBBoardView *bv = qobject_cast(mSourceWidget); if (bv) { connect(UBApplication::boardController, SIGNAL(activeSceneChanged()), this, SLOT(activeSceneChanged())); connect(UBApplication::boardController, SIGNAL(backgroundChanged()), this, SLOT(sceneBackgroundChanged())); connect(UBApplication::boardController, SIGNAL(controlViewportChanged()), this, SLOT(activeSceneChanged())); activeSceneChanged(); } else { QSizeF scaledWidgetSize = sourceWidgetSize * scale; int offsetX = (videoFrameSize.width() - scaledWidgetSize.width()) / 2; int offsetY = (videoFrameSize.height() - scaledWidgetSize.height()) / 2; mViewToVideoTransform.translate(offsetX / scale, offsetY / scale); disconnect(UBApplication::boardController, SIGNAL(activeSceneChanged()), this, SLOT(activeSceneChanged())); disconnect(UBApplication::boardController, SIGNAL(backgroundChanged()), this, SLOT(sceneBackgroundChanged())); disconnect(UBApplication::boardController, SIGNAL(controlViewportChanged()), this, SLOT(activeSceneChanged())); mSourceScene = 0; startNextChapter(); if(mSourceWidget == qApp->desktop()) { mScreenGrabingTimerEventID = startTimer(1000 / mVideoFramesPerSecondAtStart); } else { mSourceWidget->installEventFilter(this); } } } } } UBPodcastController* UBPodcastController::instance() { if(!sInstance) sInstance = new UBPodcastController(UBApplication::staticMemoryCleaner); return sInstance; } void UBPodcastController::start() { if (mRecordingState == Stopped) { mInitialized = false; QSize recommendedSize(1024, 768); int fullBitRate = UBSettings::settings()->podcastWindowsMediaBitsPerSecond->get().toInt(); if (mSmallVideoSizeAction && mSmallVideoSizeAction->isChecked()) { recommendedSize = QSize(640, 480); mVideoBitsPerSecondAtStart = fullBitRate / 4; } else if (mMediumVideoSizeAction && mMediumVideoSizeAction->isChecked()) { recommendedSize = QSize(1024, 768); mVideoBitsPerSecondAtStart = fullBitRate / 2; } else if (mFullVideoSizeAction && mFullVideoSizeAction->isChecked()) { recommendedSize = UBApplication::boardController->controlView()->size(); mVideoBitsPerSecondAtStart = fullBitRate; } QSize scaledboardSize = UBApplication::boardController->controlView()->size(); scaledboardSize.scale(recommendedSize, Qt::KeepAspectRatio); // Video width/height should be a multiple of 4 int width = scaledboardSize.width(); int height = scaledboardSize.height(); if (width % 4 != 0) width = ((width / 4) * 4); if (height % 4 != 0) height = ((height / 4) * 4); mVideoFrameSizeAtStart = QSize(width, height); applicationMainModeChanged(UBApplication::applicationController->displayMode()); #ifdef Q_WS_WIN mVideoEncoder = new UBWindowsMediaVideoEncoder(this); //deleted on stop #elif defined(Q_WS_MAC) mVideoEncoder = new UBQuickTimeVideoEncoder(this); //deleted on stop #endif if (mVideoEncoder) { connect(mVideoEncoder, SIGNAL(encodingStatus(const QString&)), this, SLOT(encodingStatus(const QString&))); connect(mVideoEncoder, SIGNAL(encodingFinished(bool)), this, SLOT(encodingFinished(bool))); if(mRecordingPalette) { connect(mVideoEncoder, SIGNAL(audioLevelChanged(quint8)) , mRecordingPalette, SLOT(audioLevelChanged(quint8))); } mVideoEncoder->setRecordAudio(!mNoAudioInputDeviceAction->isChecked()); QString recordingDevice = ""; if (!mNoAudioInputDeviceAction->isChecked() && !mDefaultAudioInputDeviceAction->isChecked()) { foreach(QAction* audioDevice, mAudioInputDevicesActions) { if (audioDevice->isChecked()) { recordingDevice = audioDevice->text(); break; } } } mVideoEncoder->setAudioRecordingDevice(recordingDevice); mVideoEncoder->setFramesPerSecond(mVideoFramesPerSecondAtStart); mVideoEncoder->setVideoSize(mVideoFrameSizeAtStart); mVideoEncoder->setVideoBitsPerSecond(mVideoBitsPerSecondAtStart); mPartNumber = 0; mPodcastRecordingPath = UBSettings::settings()->userPodcastRecordingDirectory(); qDebug() << "mPodcastRecordingPath: " << mPodcastRecordingPath; QString videoFileName; if (mIntranetPublicationAction && mIntranetPublicationAction->isChecked()) { videoFileName = mPodcastRecordingPath + "/" + "Podcast-" + QDateTime::currentDateTime().toString("yyyyMMddhhmmss") + "-" + UBPlatformUtils::computerName() + "." + mVideoEncoder->videoFileExtension(); } else { videoFileName = mPodcastRecordingPath + "/" + tr("Sankore Cast") + "." + mVideoEncoder->videoFileExtension(); } videoFileName = UBFileSystemUtils::nextAvailableFileName(videoFileName, " "); mVideoEncoder->setVideoFileName(videoFileName); mLatestCapture = QImage(mVideoFrameSizeAtStart, QImage::Format_RGB32); //0xffRRGGBB mRecordStartTime = QTime::currentTime(); mRecordingProgressTimerEventID = startTimer(100); if(mVideoEncoder->start()) { applicationMainModeChanged(UBApplication::applicationController->displayMode()); setRecordingState(Recording); if (mSourceScene) { processScenePaintEvent(); } else if (mSourceWidget) { processWidgetPaintEvent(); } } else { UBApplication::showMessage(tr("Failed to start encoder ..."), false); } } else { UBApplication::showMessage(tr("No Podcast encoder available ..."), false); } } } void UBPodcastController::pause() { if (mVideoEncoder && mRecordingState == Recording && mVideoEncoder->canPause()) { sendLatestPixmapToEncoder(); mTimeAtPaused = QTime::currentTime(); if (mVideoEncoder->pause()) { setRecordingState(Paused); } } } void UBPodcastController::unpause() { if (mVideoEncoder && mRecordingState == Paused && mVideoEncoder->canPause()) { if (mVideoEncoder->unpause()) { mRecordingTimestampOffset += mTimeAtPaused.msecsTo(QTime::currentTime()); sendLatestPixmapToEncoder(); setRecordingState(Recording); } } } void UBPodcastController::stop() { if ((mRecordingState == Recording || mRecordingState == Paused) && mVideoEncoder) { if (mScreenGrabingTimerEventID != 0) { killTimer(mScreenGrabingTimerEventID); mScreenGrabingTimerEventID = 0; } if (mRecordingProgressTimerEventID != 0) killTimer(mRecordingProgressTimerEventID); sendLatestPixmapToEncoder(); setRecordingState(Stopping); mVideoEncoder->stop(); } } bool UBPodcastController::eventFilter(QObject *obj, QEvent *event) { if (mRecordingState == Recording && !mIsGrabbing && event->type() == QEvent::Paint) { QPaintEvent *paintEvent = static_cast(event); mWidgetRepaintRectQueue.enqueue(paintEvent->rect()); if (mWidgetRepaintRectQueue.length() == 1) QTimer::singleShot(1000.0 / mVideoFramesPerSecondAtStart, this, SLOT(processWidgetPaintEvent())); } return QObject::eventFilter(obj, event); } void UBPodcastController::processWidgetPaintEvent() { if(mRecordingState != Recording) return; QRect repaintRect; if (!mInitialized) { mWidgetRepaintRectQueue.clear(); repaintRect = mSourceWidget->geometry(); mLatestCapture.fill(sBackgroundColor); mInitialized = true; } else { while(mWidgetRepaintRectQueue.size() > 0) { repaintRect = repaintRect.unite(mWidgetRepaintRectQueue.dequeue()); } } if (!repaintRect.isNull()) { mIsGrabbing = true; QPainter p(&mLatestCapture); p.setTransform(mViewToVideoTransform); p.setRenderHints(QPainter::Antialiasing); p.setRenderHints(QPainter::SmoothPixmapTransform); mSourceWidget->render(&p, repaintRect.topLeft(), QRegion(repaintRect), QWidget::DrawChildren); mIsGrabbing = false; sendLatestPixmapToEncoder(); } } void UBPodcastController::activeSceneChanged() { if (mSourceScene) { disconnect(mSourceScene, SIGNAL(changed(const QList&)), this, SLOT(sceneChanged(const QList &))); } mSourceScene = UBApplication::boardController->activeScene(); connect(mSourceScene, SIGNAL(changed(const QList&)), this, SLOT(sceneChanged(const QList &))); mInitialized = false; startNextChapter(); UBBoardView *bv = qobject_cast(mSourceWidget); if (bv) { QRectF viewportRect = bv->mapToScene(bv->geometry()).boundingRect(); mSceneRepaintRectQueue.enqueue(viewportRect); } processScenePaintEvent(); } void UBPodcastController::sceneBackgroundChanged() { UBBoardView *bv = qobject_cast(mSourceWidget); if (bv) { mInitialized = false; } processScenePaintEvent(); } long UBPodcastController::elapsedRecordingMs() { QTime now = QTime::currentTime(); long msFromStart = mRecordStartTime.msecsTo(now); return msFromStart - mRecordingTimestampOffset; } void UBPodcastController::startNextChapter() { if (mVideoEncoder) { //punch chapter in ++mPartNumber; mVideoEncoder->newChapter(tr("Part %1").arg(mPartNumber), elapsedRecordingMs()); } } void UBPodcastController::sceneChanged(const QList & region) { if(mRecordingState != Recording) return; bool shouldRepaint = (mSceneRepaintRectQueue.length() == 0); UBBoardView *bv = qobject_cast(mSourceWidget); if (bv) { QRectF viewportRect = bv->mapToScene(QRect(0, 0, bv->width(), bv->height())).boundingRect(); foreach(const QRectF rect, region) { QRectF maxRect = rect.intersect(viewportRect); mSceneRepaintRectQueue.enqueue(maxRect); } if (shouldRepaint) QTimer::singleShot(1000.0 / mVideoFramesPerSecondAtStart, this, SLOT(processScenePaintEvent())); } } void UBPodcastController::processScenePaintEvent() { if(mRecordingState != Recording) return; UBBoardView *bv = qobject_cast(mSourceWidget); if(!bv) return; QRectF repaintRect; if (!mInitialized) { mSceneRepaintRectQueue.clear(); repaintRect = bv->mapToScene(QRect(0, 0, bv->width(), bv->height())).boundingRect(); if (bv->scene()->isDarkBackground()) mLatestCapture.fill(Qt::black); else mLatestCapture.fill(Qt::white); mLatestCapture.fill(Qt::white); mInitialized = true; } else { while(mSceneRepaintRectQueue.size() > 0) { repaintRect = repaintRect.unite(mSceneRepaintRectQueue.dequeue()); } } if (!repaintRect.isNull()) { UBGraphicsScene *scene = bv->scene(); QPainter p(&mLatestCapture); p.setTransform(mViewToVideoTransform); p.setTransform(bv->viewportTransform(), true); p.setRenderHints(QPainter::Antialiasing); p.setRenderHints(QPainter::SmoothPixmapTransform); repaintRect.adjust(-1, -1, 1, 1); p.setClipRect(repaintRect); if (scene->isDarkBackground()) p.fillRect(repaintRect, Qt::black); else p.fillRect(repaintRect, Qt::white); scene->setRenderingContext(UBGraphicsScene::Podcast); scene->render(&p, repaintRect, repaintRect); scene->setRenderingContext(UBGraphicsScene::Screen); sendLatestPixmapToEncoder(); } } void UBPodcastController::applicationMainModeChanged(UBApplicationController::MainMode pMode) { if (pMode == UBApplicationController::Internet) { setSourceWidget(UBApplication::webController->controlView()); } else { setSourceWidget(UBApplication::boardController->controlView()); } } void UBPodcastController::applicationDesktopMode(bool displayed) { Q_UNUSED(displayed); if (displayed) { setSourceWidget(qApp->desktop()); } else { applicationMainModeChanged(UBApplication::applicationController->displayMode()); } } void UBPodcastController::webActiveWebPageChanged(WBWebView* pWebView) { if(UBApplication::applicationController->displayMode() == UBApplicationController::Internet) { setSourceWidget(pWebView); } } void UBPodcastController::encodingStatus(const QString& pStatus) { UBApplication::showMessage(pStatus, true); } void UBPodcastController::encodingFinished(bool ok) { if (mVideoEncoder) { if (ok) { if (!mApplicationIsClosing) { QString location; if (mPodcastRecordingPath == QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)) location = tr("on your desktop ..."); else { QDir dir(mPodcastRecordingPath); location = tr("in folder %1").arg(mPodcastRecordingPath); } UBApplication::showMessage(tr("Podcast created %1").arg(location), false); if (mIntranetPublicationAction && mIntranetPublicationAction->isChecked()) { UBIntranetPodcastPublisher* intranet = new UBIntranetPodcastPublisher(this); // Self destroyed intranet->publishVideo(mVideoEncoder->videoFileName(), elapsedRecordingMs()); } if (mYoutubePublicationAction && mYoutubePublicationAction->isChecked()) { UBYouTubePublisher* youTube = new UBYouTubePublisher(this); // Self destroyed youTube->uploadVideo(mVideoEncoder->videoFileName()); } } } else { qWarning() << mVideoEncoder->lastErrorMessage(); UBApplication::showMessage(tr("Podcast recording error (%1)").arg(mVideoEncoder->lastErrorMessage()), false); } mVideoEncoder->deleteLater(); setRecordingState(Stopped); } } void UBPodcastController::sendLatestPixmapToEncoder() { if (mVideoEncoder) mVideoEncoder->newPixmap(mLatestCapture, elapsedRecordingMs()); } void UBPodcastController::timerEvent(QTimerEvent *event) { if (mRecordingState == Recording && event->timerId() == mScreenGrabingTimerEventID && mSourceWidget == qApp->desktop()) { QPixmap desktop = QPixmap::grabWindow(qApp->desktop()->screen(UBApplication::applicationController->displayManager()->controleScreenIndex())->winId()); { QPainter p(&mLatestCapture); if (!mInitialized) { mLatestCapture.fill(sBackgroundColor); mInitialized = true; } QRectF targetRect = mViewToVideoTransform.mapRect(QRectF(0, 0, desktop.width(), desktop.height())); p.setRenderHints(QPainter::Antialiasing); p.setRenderHints(QPainter::SmoothPixmapTransform); p.drawPixmap(targetRect.left(), targetRect.top(), desktop.scaled(targetRect.width(), targetRect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } sendLatestPixmapToEncoder(); } if (mRecordingProgressTimerEventID == event->timerId() && mRecordingState == Recording) { emit recordingProgressChanged(elapsedRecordingMs()); } } QStringList UBPodcastController::audioRecordingDevices() { QStringList devices; #ifdef Q_WS_WIN devices = UBWaveRecorder::waveInDevices(); #elif defined(Q_WS_MAC) devices = UBAudioQueueRecorder::waveInDevices(); #endif return devices; } void UBPodcastController::recordToggled(bool record) { if ((mRecordingState == Stopped) && record) start(); else stop(); } void UBPodcastController::pauseToggled(bool paused) { if ((mRecordingState == Recording) && paused) pause(); else unpause(); } void UBPodcastController::toggleRecordingPalette(bool visible) { if(!mRecordingPalette) { mRecordingPalette = new UBPodcastRecordingPalette(UBApplication::mainWindow); mRecordingPalette->adjustSizeAndPosition(); mRecordingPalette->setCustomPosition(true); int left = UBApplication::boardController->controlView()->width() * 0.75 - mRecordingPalette->width() / 2; int top = UBApplication::boardController->controlView()->height() - mRecordingPalette->height() - UBSettings::boardMargin; QPoint controlViewPoint(left, top); QPoint mainWindowsPoint = UBApplication::boardController->controlView()->mapTo(UBApplication::mainWindow, controlViewPoint); mRecordingPalette->move(mainWindowsPoint); connect(UBApplication::mainWindow->actionPodcastRecord, SIGNAL(triggered(bool)) , this, SLOT(recordToggled(bool))); connect(UBApplication::mainWindow->actionPodcastPause, SIGNAL(toggled(bool)) , this, SLOT(pauseToggled(bool))); connect(this, SIGNAL(recordingStateChanged(UBPodcastController::RecordingState)) , mRecordingPalette, SLOT(recordingStateChanged(UBPodcastController::RecordingState))); connect(this, SIGNAL(recordingProgressChanged(qint64)) , mRecordingPalette, SLOT(recordingProgressChanged(qint64))); } mRecordingPalette->setVisible(visible); } void UBPodcastController::setRecordingState(RecordingState pRecordingState) { if(mRecordingState != pRecordingState) { mRecordingState = pRecordingState; emit recordingStateChanged(mRecordingState); } } QList UBPodcastController::audioRecordingDevicesActions() { if (mAudioInputDevicesActions.length() == 0) { QString settingsDevice = UBSettings::settings()->podcastAudioRecordingDevice->get().toString(); mDefaultAudioInputDeviceAction = new QAction(tr("Default Audio Input"), this); QAction *checkedAction = mDefaultAudioInputDeviceAction; mNoAudioInputDeviceAction = new QAction(tr("No Audio Recording"), this); if (settingsDevice == "None") checkedAction = mNoAudioInputDeviceAction; mAudioInputDevicesActions << mNoAudioInputDeviceAction; mAudioInputDevicesActions << mDefaultAudioInputDeviceAction; foreach(QString audioDevice, audioRecordingDevices()) { QAction* act = new QAction(audioDevice, this); act->setCheckable(true); mAudioInputDevicesActions << act; if (settingsDevice == audioDevice) checkedAction = act; } QActionGroup* audioInputActionGroup = new QActionGroup(this); audioInputActionGroup->setExclusive(true); foreach(QAction* action, mAudioInputDevicesActions) { audioInputActionGroup->addAction(action); action->setCheckable(true); } checkedAction->setChecked(true); connect(audioInputActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(groupActionTriggered(QAction*))); } return mAudioInputDevicesActions; } QList UBPodcastController::videoSizeActions() { if (mVideoSizesActions.length() == 0) { QList videoSizeActions; mSmallVideoSizeAction = new QAction(tr("Small"), this); mMediumVideoSizeAction = new QAction(tr("Medium"), this); mFullVideoSizeAction = new QAction(tr("Full"), this); mVideoSizesActions << mSmallVideoSizeAction; mVideoSizesActions << mMediumVideoSizeAction; mVideoSizesActions << mFullVideoSizeAction; QActionGroup* videoSizeActionGroup = new QActionGroup(this); videoSizeActionGroup->setExclusive(true); foreach(QAction* videoSizeAction, mVideoSizesActions) { videoSizeAction->setCheckable(true); videoSizeActionGroup->addAction(videoSizeAction); } QString videoSize = UBSettings::settings()->podcastVideoSize->get().toString(); if (videoSize == "Small") mSmallVideoSizeAction->setChecked(true); else if (videoSize == "Full") mFullVideoSizeAction->setChecked(true); else mMediumVideoSizeAction->setChecked(true); connect(videoSizeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(groupActionTriggered(QAction*))); } return mVideoSizesActions; } QList UBPodcastController::podcastPublicationActions() { if (mPodcastPublicationActions.length() == 0) { mIntranetPublicationAction = new QAction(tr("Publish to Intranet"), this); mIntranetPublicationAction->setCheckable(true); mIntranetPublicationAction->setChecked(UBSettings::settings()->podcastPublishToIntranet->get().toBool()); mPodcastPublicationActions << mIntranetPublicationAction; mYoutubePublicationAction = new QAction(tr("Publish to Youtube"), this); mYoutubePublicationAction->setCheckable(true); mYoutubePublicationAction->setChecked(UBSettings::settings()->podcastPublishToYoutube->get().toBool()); mPodcastPublicationActions << mYoutubePublicationAction; foreach(QAction* publicationAction, mPodcastPublicationActions) { connect(publicationAction, SIGNAL(toggled(bool)), this, SLOT(actionToggled(bool))); } } return mPodcastPublicationActions; }