CONFIG += release
TEMPLATE = app
-VERSION = 1.6
+VERSION = 1.7
DEFINES += APP_VERSION="$$VERSION"
INCLUDEPATH += /usr/include/phonon
src/videowidget.h \
src/videodefinition.h \
src/fontutils.h \
- src/thlibrary/thblackbar.h \
src/globalshortcuts.h \
src/globalshortcutbackend.h \
src/downloadmanager.h \
src/youtubesuggest.h \
src/suggester.h \
src/channelsuggest.h \
- src/temporary.h
+ src/temporary.h \
+ src/segmentedcontrol.h \
+ src/playlistview.h
SOURCES += src/main.cpp \
src/MainWindow.cpp \
src/SearchView.cpp \
src/videodefinition.cpp \
src/constants.cpp \
src/fontutils.cpp \
- src/thlibrary/thblackbar.cpp \
src/globalshortcuts.cpp \
src/globalshortcutbackend.cpp \
src/downloadmanager.cpp \
src/downloadsettings.cpp \
src/youtubesuggest.cpp \
src/channelsuggest.cpp \
- src/temporary.cpp
+ src/temporary.cpp \
+ src/segmentedcontrol.cpp \
+ src/playlistview.cpp
RESOURCES += resources.qrc
DESTDIR = build/target/
OBJECTS_DIR = build/obj/
}
mac|win32:include(local/local.pri)
+
+
+
+
#include <QtGui>
#include "View.h"
#include "constants.h"
+#ifdef APP_MAC
+#include "macutils.h"
+#endif
class AboutView : public QWidget, public View {
public:
AboutView(QWidget *parent);
- void appear() {}
+ void appear() {
+#ifdef APP_MAC
+ mac::uncloseWindow(window()->winId());
+#endif
+ }
void disappear() {}
QMap<QString, QVariant> metadata() {
QMap<QString, QVariant> metadata;
m_activeVideo = 0;
m_activeRow = -1;
skip = 1;
+
+ hoveredRow = -1;
+ authorHovered = false;
+ authorPressed = false;
}
ListModel::~ListModel() {
return video == m_activeVideo;
case Qt::DisplayRole:
return video->title();
+ case HoveredItemRole:
+ return hoveredRow == index.row();
+ case AuthorHoveredRole:
+ return authorHovered;
+ case AuthorPressedRole:
+ return authorPressed;
}
return QVariant();
return -1;
}
+int ListModel::previousRow() const {
+ int prevRow = m_activeRow - 1;
+ if (rowExists(prevRow))
+ return prevRow;
+ return -1;
+}
+
Video* ListModel::videoAt( int row ) const {
if ( rowExists( row ) )
return videos.at( row );
emit needSelectionFor(movedVideos);
}
+
+/* row hovering */
+
+void ListModel::setHoveredRow(int row) {
+ int oldRow = hoveredRow;
+ hoveredRow = row;
+ emit dataChanged( createIndex( oldRow, 0 ), createIndex( oldRow, columnCount() - 1 ) );
+ emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+}
+
+void ListModel::clearHover() {
+ emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+ hoveredRow = -1;
+}
+
+/* clickable author */
+
+void ListModel::enterAuthorHover() {
+ if (authorHovered) return;
+ authorHovered = true;
+ updateAuthor();
+}
+
+void ListModel::exitAuthorHover() {
+ if (!authorHovered) return;
+ authorHovered = false;
+ updateAuthor();
+ setHoveredRow(hoveredRow);
+}
+
+void ListModel::enterAuthorPressed() {
+ if (authorPressed) return;
+ authorPressed = true;
+ updateAuthor();
+}
+
+void ListModel::exitAuthorPressed() {
+ if (!authorPressed) return;
+ authorPressed = false;
+ updateAuthor();
+}
+
+void ListModel::updateAuthor() {
+ emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+}
DownloadItemRole,
HoveredItemRole,
DownloadButtonHoveredRole,
- DownloadButtonPressedRole
+ DownloadButtonPressedRole,
+ AuthorHoveredRole,
+ AuthorPressedRole
};
enum ItemTypes {
bool rowExists( int row ) const { return (( row >= 0 ) && ( row < videos.size() ) ); }
int activeRow() const { return m_activeRow; } // returns -1 if there is no active row
int nextRow() const;
+ int previousRow() const;
void removeIndexes(QModelIndexList &indexes);
int rowForVideo(Video* video);
QModelIndex indexForVideo(Video* video);
void searchError(QString message);
void updateThumbnail();
+ void setHoveredRow(int row);
+ void clearHover();
+ void enterAuthorHover();
+ void exitAuthorHover();
+ void enterAuthorPressed();
+ void exitAuthorPressed();
+ void updateAuthor();
+
signals:
void activeRowChanged(int);
void needSelectionFor(QList<Video*>);
Video *m_activeVideo;
QString errorMessage;
+
+ int hoveredRow;
+ bool authorHovered;
+ bool authorPressed;
};
#endif
#include "mac_startup.h"
#include "macfullscreen.h"
#include "macsupport.h"
+#include "macutils.h"
#endif
#ifndef Q_WS_X11
#include "extra.h"
#include "demostartupview.h"
#endif
#include "temporary.h"
+#ifdef APP_MAC
+#include "searchlineedit_mac.h"
+#else
+#include "searchlineedit.h"
+#endif
+
+static MainWindow *singleton = 0;
+
+MainWindow* MainWindow::instance() {
+ if (!singleton) singleton = new MainWindow();
+ return singleton;
+}
MainWindow::MainWindow() :
aboutView(0),
m_fullscreen(false),
updateChecker(0) {
+ singleton = this;
+
// views mechanism
history = new QStack<QWidget*>();
views = new QStackedWidget(this);
mediaView = new MediaView(this);
views->addWidget(mediaView);
- toolbarSearch = new SearchLineEdit(this);
- toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
- toolbarSearch->setSuggester(new YouTubeSuggest(this));
- connect(toolbarSearch, SIGNAL(search(const QString&)), this, SLOT(startToolbarSearch(const QString&)));
-
// build ui
createActions();
createMenus();
connect(&shortcuts, SIGNAL(PlayPause()), pauseAct, SLOT(trigger()));
connect(&shortcuts, SIGNAL(Stop()), this, SLOT(stop()));
connect(&shortcuts, SIGNAL(Next()), skipAct, SLOT(trigger()));
+ connect(&shortcuts, SIGNAL(Previous()), skipBackwardAct, SLOT(trigger()));
connect(DownloadManager::instance(), SIGNAL(statusMessageChanged(QString)),
SLOT(updateDownloadMessage(QString)));
delete history;
}
+void MainWindow::changeEvent(QEvent* event) {
+#ifdef APP_MAC
+ if (event->type() == QEvent::WindowStateChange) {
+ The::globalActions()->value("minimize")->setEnabled(!isMinimized());
+ }
+#endif
+ QMainWindow::changeEvent(event);
+}
+
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
#ifdef Q_WS_X11
if (event->type() == QEvent::MouseMove && this->m_fullscreen) {
actions->insert("stop", stopAct);
connect(stopAct, SIGNAL(triggered()), this, SLOT(stop()));
+ skipBackwardAct = new QAction(
+ QtIconLoader::icon("media-skip-backward"),
+ tr("P&revious"), this);
+ skipBackwardAct->setStatusTip(tr("Go back to the previous track"));
+ skipBackwardAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Left));
+#if QT_VERSION >= 0x040600
+ skipBackwardAct->setPriority(QAction::LowPriority);
+#endif
+ skipBackwardAct->setEnabled(false);
+ actions->insert("previous", skipBackwardAct);
+ connect(skipBackwardAct, SIGNAL(triggered()), mediaView, SLOT(skipBackward()));
+
skipAct = new QAction(QtIconLoader::icon("media-skip-forward"), tr("S&kip"), this);
skipAct->setStatusTip(tr("Skip to the next video"));
skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext));
#ifdef APP_MAC
fsShortcuts << QKeySequence(Qt::CTRL + Qt::META + Qt::Key_F);
#else
- fsShortcuts << QKeySequence(Qt::Key_F11);
+ fsShortcuts << QKeySequence(Qt::Key_F11) << QKeySequence(Qt::ALT + Qt::Key_Return);
#endif
fullscreenAct->setShortcuts(fsShortcuts);
fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
quitAct = new QAction(tr("&Quit"), this);
quitAct->setMenuRole(QAction::QuitRole);
- quitAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Q")) << QKeySequence(Qt::CTRL + Qt::Key_W));
+ quitAct->setShortcut(QKeySequence::Quit);
quitAct->setStatusTip(tr("Bye"));
actions->insert("quit", quitAct);
- connect(quitAct, SIGNAL(triggered()), this, SLOT(close()));
+ connect(quitAct, SIGNAL(triggered()), SLOT(quit()));
siteAct = new QAction(tr("&Website"), this);
siteAct->setShortcut(QKeySequence::HelpContents);
addAction(searchFocusAct);
volumeUpAct = new QAction(this);
- volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus) << QKeySequence(Qt::Key_VolumeUp));
+ volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus));
actions->insert("volume-up", volumeUpAct);
connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp()));
addAction(volumeUpAct);
volumeDownAct = new QAction(this);
- volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus) << QKeySequence(Qt::Key_VolumeDown));
+ volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus));
actions->insert("volume-down", volumeDownAct);
connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown()));
addAction(volumeDownAct);
volumeMuteAct = new QAction(this);
volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-high"));
volumeMuteAct->setStatusTip(tr("Mute volume"));
- volumeMuteAct->setShortcuts(QList<QKeySequence>()
- << QKeySequence(tr("Ctrl+M"))
- << QKeySequence(Qt::Key_VolumeMute));
+ volumeMuteAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_E));
actions->insert("volume-mute", volumeMuteAct);
connect(volumeMuteAct, SIGNAL(triggered()), SLOT(volumeMute()));
addAction(volumeMuteAct);
actions->insert("email", action);
connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaEmail()));
+ action = new QAction(tr("&Close"), this);
+ action->setShortcut(QKeySequence(QKeySequence::Close));
+ actions->insert("close", action);
+ connect(action, SIGNAL(triggered()), SLOT(close()));
+
+ action = new QAction(QtIconLoader::icon("go-top"), tr("&Float on Top"), this);
+ action->setCheckable(true);
+ actions->insert("ontop", action);
+ connect(action, SIGNAL(toggled(bool)), SLOT(floatOnTop(bool)));
+
+ action = new QAction(QtIconLoader::icon("media-playback-stop"), tr("&Stop after this video"), this);
+ action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Escape));
+ action->setCheckable(true);
+ action->setEnabled(false);
+ actions->insert("stopafterthis", action);
+ connect(action, SIGNAL(toggled(bool)), SLOT(showStopAfterThisInStatusBar(bool)));
+
// common action properties
foreach (QAction *action, actions->values()) {
action->setAutoRepeat(false);
// set to something more meaningful then the toolbar text
- // HELP! how to remove tooltips altogether?!
if (!action->statusTip().isEmpty())
action->setToolTip(action->statusTip());
QMap<QString, QMenu*> *menus = The::globalMenus();
fileMenu = menuBar()->addMenu(tr("&Application"));
- // menus->insert("file", fileMenu);
+#ifdef APP_DEMO
+ QAction* action = new QAction(tr("Buy %1...").arg(Constants::NAME), this);
+ action->setMenuRole(QAction::ApplicationSpecificRole);
+ connect(action, SIGNAL(triggered()), SLOT(buy()));
+ fileMenu->addAction(action);
+#endif
fileMenu->addAction(clearAct);
#ifndef APP_MAC
fileMenu->addSeparator();
QMenu* playbackMenu = menuBar()->addMenu(tr("&Playback"));
menus->insert("playback", playbackMenu);
- playbackMenu->addAction(stopAct);
playbackMenu->addAction(pauseAct);
+ playbackMenu->addAction(stopAct);
+ playbackMenu->addAction(The::globalActions()->value("stopafterthis"));
+ playbackMenu->addSeparator();
playbackMenu->addAction(skipAct);
+ playbackMenu->addAction(skipBackwardAct);
#ifdef APP_MAC
MacSupport::dockMenu(playbackMenu);
#endif
menus->insert("view", viewMenu);
viewMenu->addAction(fullscreenAct);
viewMenu->addAction(compactViewAct);
+ viewMenu->addSeparator();
+ viewMenu->addAction(The::globalActions()->value("ontop"));
QMenu* shareMenu = menuBar()->addMenu(tr("&Share"));
menus->insert("share", shareMenu);
shareMenu->addAction(The::globalActions()->value("facebook"));
shareMenu->addAction(The::globalActions()->value("email"));
+#ifdef APP_MAC
+ MacSupport::windowMenu(this);
+#endif
+
helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(siteAct);
#if !defined(APP_MAC) && !defined(APP_WIN)
mainToolBar->addWidget(new Spacer());
+#ifdef APP_MAC
+ SearchWrapper* searchWrapper = new SearchWrapper(this);
+ toolbarSearch = searchWrapper->getSearchLineEdit();
+#else
+ toolbarSearch = new SearchLineEdit(this);
+#endif
+ toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
+ toolbarSearch->setSuggester(new YouTubeSuggest(this));
+ connect(toolbarSearch, SIGNAL(search(const QString&)), this, SLOT(startToolbarSearch(const QString&)));
+ connect(toolbarSearch, SIGNAL(suggestionAccepted(const QString&)), SLOT(startToolbarSearch(const QString&)));
toolbarSearch->setStatusTip(searchFocusAct->statusTip());
+#ifdef APP_MAC
+ mainToolBar->addWidget(searchWrapper);
+#else
mainToolBar->addWidget(toolbarSearch);
-
- mainToolBar->addWidget(new Spacer());
+ Spacer* spacer = new Spacer();
+ spacer->setWidth(4);
+ mainToolBar->addWidget(spacer);
+#endif
addToolBar(mainToolBar);
}
void MainWindow::createStatusBar() {
- QToolBar *toolBar = new QToolBar(this);
+ QToolBar* toolBar = new QToolBar(this);
+ statusToolBar = toolBar;
toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolBar->setIconSize(QSize(16, 16));
toolBar->addAction(The::globalActions()->value("downloads"));
statusBar()->show();
}
+void MainWindow::showStopAfterThisInStatusBar(bool show) {
+ QAction* action = The::globalActions()->value("stopafterthis");
+ if (show) {
+ statusToolBar->insertAction(statusToolBar->actions().first(), action);
+ } else {
+ statusToolBar->removeAction(action);
+ }
+}
+
+void MainWindow::showFloatOnTopInStatusBar(bool show) {
+ QAction* action = The::globalActions()->value("ontop");
+ if (show) {
+ statusToolBar->insertAction(statusToolBar->actions().first(), action);
+ } else {
+ statusToolBar->removeAction(action);
+ }
+}
+
void MainWindow::readSettings() {
QSettings settings;
restoreGeometry(settings.value("geometry").toByteArray());
findVideoPartsAct->setEnabled(widget == mediaView);
toolbarSearch->setEnabled(widget == searchView || widget == mediaView || widget == downloadView);
+ if (widget == searchView) {
+ skipAct->setEnabled(false);
+ The::globalActions()->value("previous")->setEnabled(false);
+ The::globalActions()->value("download")->setEnabled(false);
+ The::globalActions()->value("stopafterthis")->setEnabled(false);
+ }
+
The::globalActions()->value("twitter")->setEnabled(widget == mediaView);
The::globalActions()->value("facebook")->setEnabled(widget == mediaView);
The::globalActions()->value("email")->setEnabled(widget == mediaView);
aboutAct->setEnabled(widget != aboutView);
- The::globalActions()->value("download")->setEnabled(widget == mediaView);
The::globalActions()->value("downloads")->setChecked(widget == downloadView);
// toolbar only for the mediaView
}
void MainWindow::quit() {
+#ifdef APP_MAC
+ if (!confirmQuit()) {
+ return;
+ }
+#endif
writeSettings();
Temporary::deleteAll();
qApp->quit();
}
void MainWindow::closeEvent(QCloseEvent *event) {
+#ifdef APP_MAC
+ mac::closeWindow(winId());
+ event->ignore();
+#else
+ if (!confirmQuit()) {
+ event->ignore();
+ return;
+ }
+ quit();
+ QWidget::closeEvent(event);
+#endif
+}
+
+bool MainWindow::confirmQuit() {
if (DownloadManager::instance()->activeItems() > 0) {
- QMessageBox msgBox;
+ QMessageBox msgBox(this);
msgBox.setIconPixmap(QPixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
msgBox.setText(tr("Do you want to exit %1 with a download in progress?").arg(Constants::NAME));
msgBox.setInformativeText(tr("If you close %1 now, this download will be cancelled.").arg(Constants::NAME));
msgBox.setModal(true);
+ // make it a "sheet" on the Mac
+ msgBox.setWindowModality(Qt::WindowModal);
msgBox.addButton(tr("Close and cancel download"), QMessageBox::RejectRole);
QPushButton *waitButton = msgBox.addButton(tr("Wait for download to finish"), QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == waitButton) {
- event->ignore();
- return;
+ return false;
}
-
}
- quit();
- QWidget::closeEvent(event);
+ return true;
}
void MainWindow::showSearch() {
}
void MainWindow::showMedia(SearchParams *searchParams) {
- if (toolbarSearch->text().isEmpty() && !searchParams->keywords().isEmpty()) {
- toolbarSearch->lineEdit()->setText(searchParams->keywords());
- }
mediaView->search(searchParams);
showWidget(mediaView);
}
pauseAct->setIcon(QtIconLoader::icon("media-playback-pause"));
pauseAct->setText(tr("&Pause"));
pauseAct->setStatusTip(tr("Pause playback") + " (" + pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
- skipAct->setEnabled(true);
// stopAct->setEnabled(true);
break;
case Phonon::StoppedState:
pauseAct->setEnabled(false);
- skipAct->setEnabled(false);
// stopAct->setEnabled(false);
break;
case Phonon::PausedState:
- skipAct->setEnabled(true);
pauseAct->setEnabled(true);
pauseAct->setIcon(QtIconLoader::icon("media-playback-start"));
pauseAct->setText(tr("&Play"));
case Phonon::BufferingState:
case Phonon::LoadingState:
- skipAct->setEnabled(true);
pauseAct->setEnabled(false);
currentTime->clear();
totalTime->clear();
}
}
+
+void MainWindow::floatOnTop(bool onTop) {
+ showFloatOnTopInStatusBar(onTop);
+#ifdef APP_MAC
+ mac::floatOnTop(winId(), onTop);
+ return;
+#endif
+ if (onTop) {
+ setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
+ } else {
+ setWindowFlags(windowFlags() ^ Qt::WindowStaysOnTopHint);
+ }
+}
+
+void MainWindow::messageReceived(const QString &message) {
+ if (!message.isEmpty()) {
+ SearchParams *searchParams = new SearchParams();
+ searchParams->setKeywords(message);
+ showMedia(searchParams);
+ }
+}
#define MAINWINDOW_H
#include <QtGui>
-#include "searchlineedit.h"
#include <phonon/audiooutput.h>
#include <phonon/volumeslider.h>
#include <phonon/mediaobject.h>
#include "AboutView.h"
#include "downloadview.h"
+class SearchLineEdit;
class UpdateChecker;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
+ static MainWindow* instance();
MainWindow();
~MainWindow();
Phonon::SeekSlider* getSeekSlider() { return seekSlider; }
+ void readSettings();
+ void writeSettings();
public slots:
void showMedia(SearchParams *params);
+ void messageReceived(const QString &message);
protected:
+ void changeEvent(QEvent *);
void closeEvent(QCloseEvent *);
bool eventFilter(QObject *obj, QEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void toggleDownloads(bool show);
void startToolbarSearch(QString query);
+ void floatOnTop(bool);
+ void showFloatOnTopInStatusBar(bool show);
+ void showStopAfterThisInStatusBar(bool show);
private:
void initPhonon();
void createMenus();
void createToolBars();
void createStatusBar();
- void readSettings();
- void writeSettings();
void showWidget(QWidget*);
static QString formatTime(qint64 time);
+ bool confirmQuit();
UpdateChecker *updateChecker;
QAction *searchFocusAct;
// media actions
+ QAction *skipBackwardAct;
QAction *skipAct;
QAction *pauseAct;
QAction *stopAct;
// toolbar
QToolBar *mainToolBar;
SearchLineEdit *toolbarSearch;
+ QToolBar *statusToolBar;
// phonon
Phonon::SeekSlider *seekSlider;
#include "MediaView.h"
+#include "playlistview.h"
#include "playlist/PrettyItemDelegate.h"
#include "networkaccess.h"
#include "videowidget.h"
splitter = new MiniSplitter(this);
splitter->setChildrenCollapsible(false);
- sortBar = new THBlackBar(this);
+ sortBar = new SegmentedControl(this);
mostRelevantAction = new QAction(tr("Most relevant"), this);
QKeySequence keySequence(Qt::CTRL + Qt::Key_1);
mostRelevantAction->setShortcut(keySequence);
connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
sortBar->addAction(mostViewedAction);
- listView = new QListView(this);
+ listView = new PlaylistView(this);
listView->setItemDelegate(new PrettyItemDelegate(this));
listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
+ connect(listView, SIGNAL(authorPushed(QModelIndex)), SLOT(authorPushed(QModelIndex)));
+
playlistWidget = new PlaylistWidget(this, sortBar, listView);
splitter->addWidget(playlistWidget);
}
+void MediaView::appear() {
+ listView->setFocus();
+}
+
void MediaView::disappear() {
timerPlayFlag = true;
}
qDebug("loading");
break;
- default:
- ;
}
}
QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
- The::globalActions()->value("download")->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
// ensure active item is visible
// int row = listModel->activeRow();
listView->scrollTo(index, QAbstractItemView::EnsureVisible);
}
+ // enable/disable actions
+ The::globalActions()->value("download")->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
+ The::globalActions()->value("skip")->setEnabled(true);
+ The::globalActions()->value("previous")->setEnabled(row > 0);
+ The::globalActions()->value("stopafterthis")->setEnabled(true);
+
// see you in gotStreamUrl...
}
listModel->setActiveRow(nextRow);
}
+void MediaView::skipBackward() {
+ int prevRow = listModel->previousRow();
+ if (prevRow == -1) return;
+ listModel->setActiveRow(prevRow);
+}
+
void MediaView::playbackFinished() {
// qDebug() << "finished" << mediaObject->currentTime() << mediaObject->totalTime();
// add 10 secs for imprecise Phonon backends (VLC, Xine)
if (mediaObject->currentTime() + 10000 < mediaObject->totalTime()) {
// mediaObject->seek(mediaObject->currentTime());
QTimer::singleShot(3000, this, SLOT(playbackResume()));
- } else skip();
+ } else {
+ QAction* stopAfterThisAction = The::globalActions()->value("stopafterthis");
+ if (stopAfterThisAction->isChecked()) {
+ stopAfterThisAction->setChecked(false);
+ } else skip();
+ }
}
void MediaView::playbackResume() {
url.addQueryItem("body", body);
QDesktopServices::openUrl(url);
}
+
+void MediaView::authorPushed(QModelIndex index) {
+ Video* video = listModel->videoAt(index.row());
+ if (!video) return;
+
+ QString channel = video->author();
+ if (channel.isEmpty()) return;
+
+ SearchParams *searchParams = new SearchParams();
+ searchParams->setAuthor(channel);
+ searchParams->setSortBy(SearchParams::SortByNewest);
+
+ // go!
+ search(searchParams);
+}
#include <phonon/videowidget.h>
#include "View.h"
#include "ListModel.h"
-#include "thlibrary/thblackbar.h"
+#include "segmentedcontrol.h"
#include "searchparams.h"
#include "playlistwidget.h"
#include "loadingwidget.h"
#include "videoareawidget.h"
class DownloadItem;
+class PlaylistView;
namespace The {
QMap<QString, QAction*>* globalActions();
void initialize();
// View
- void appear() {
- listView->setFocus();
- }
+ void appear();
void disappear();
QMap<QString, QVariant> metadata() {
QMap<QString, QVariant> metadata;
void pause();
void stop();
void skip();
+ void skipBackward();
void skipVideo();
void openWebPage();
void copyWebPage();
void downloadStatusChanged();
void playbackFinished();
void playbackResume();
+ void authorPushed(QModelIndex);
/*
void downloadProgress(int percent);
QSplitter *splitter;
PlaylistWidget *playlistWidget;
- QListView *listView;
+ PlaylistView *listView;
ListModel *listModel;
// sortBar
- THBlackBar *sortBar;
+ SegmentedControl *sortBar;
QAction *mostRelevantAction;
QAction *mostRecentAction;
QAction *mostViewedAction;
#endif
DownloadItem *downloadItem;
- // QSlider *slider;
};
#include "searchparams.h"
#include "youtubesuggest.h"
#include "channelsuggest.h"
+#ifdef APP_MAC
+#include "searchlineedit_mac.h"
+#else
+#include "searchlineedit.h"
+#endif
namespace The {
QMap<QString, QAction*>* globalActions();
queryEdit = new SearchLineEdit(this);
queryEdit->setFont(biggerFont);
queryEdit->setMinimumWidth(queryEdit->fontInfo().pixelSize()*15);
- queryEdit->sizeHint();
- queryEdit->setFocus(Qt::OtherFocusReason);
- connect(queryEdit, SIGNAL(search(const QString&)), this, SLOT(watch(const QString&)));
- connect(queryEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
+ connect(queryEdit, SIGNAL(search(const QString&)), SLOT(watch(const QString&)));
+ connect(queryEdit, SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
+ connect(queryEdit, SIGNAL(suggestionAccepted(const QString&)), SLOT(watch(const QString&)));
youtubeSuggest = new YouTubeSuggest(this);
channelSuggest = new ChannelSuggest(this);
recentKeywordsLayout->setSpacing(5);
recentKeywordsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
recentKeywordsLabel = new QLabel(tr("Recent keywords").toUpper(), this);
-#if defined(APP_MAC) | defined(APP_WIN)
- QPalette palette = recentKeywordsLabel->palette();
- palette.setColor(QPalette::WindowText, QColor(0x65, 0x71, 0x80));
- recentKeywordsLabel->setPalette(palette);
-#else
+ recentKeywordsLabel->setProperty("recentHeader", true);
recentKeywordsLabel->setForegroundRole(QPalette::Dark);
-#endif
recentKeywordsLabel->hide();
recentKeywordsLabel->setFont(smallerFont);
recentKeywordsLayout->addWidget(recentKeywordsLabel);
recentChannelsLayout->setSpacing(5);
recentChannelsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
recentChannelsLabel = new QLabel(tr("Recent channels").toUpper(), this);
-#if defined(APP_MAC) | defined(APP_WIN)
- palette = recentChannelsLabel->palette();
- palette.setColor(QPalette::WindowText, QColor(0x65, 0x71, 0x80));
- recentChannelsLabel->setPalette(palette);
-#else
+ recentChannelsLabel->setProperty("recentHeader", true);
recentChannelsLabel->setForegroundRole(QPalette::Dark);
-#endif
recentChannelsLabel->hide();
recentChannelsLabel->setFont(smallerFont);
recentChannelsLayout->addWidget(recentChannelsLabel);
}
+void SearchView::appear() {
+ updateRecentKeywords();
+ updateRecentChannels();
+ queryEdit->selectAll();
+ queryEdit->enableSuggest();
+ QTimer::singleShot(0, queryEdit, SLOT(setFocus()));
+}
+
void SearchView::updateRecentKeywords() {
// cleanup
+ "\" style=\"color:palette(text); text-decoration:none\">"
+ display + "</a>", this);
itemLabel->setAttribute(Qt::WA_DeleteOnClose);
+ itemLabel->setProperty("recentItem", true);
itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
// itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// Make links navigable with the keyboard too
+ "\" style=\"color:palette(text); text-decoration:none\">"
+ display + "</a>", this);
itemLabel->setAttribute(Qt::WA_DeleteOnClose);
+ itemLabel->setProperty("recentItem", true);
itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
// itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// Make links navigable with the keyboard too
}
// remove spaces from channel name
- channel = channel.replace(" ", "");
+ channel = channel.remove(" ");
SearchParams *searchParams = new SearchParams();
searchParams->setAuthor(channel);
#include <QtGui>
#include "View.h"
-#include "searchlineedit.h"
+class SearchLineEdit;
class SearchParams;
class YouTubeSuggest;
class ChannelSuggest;
SearchView(QWidget *parent);
void updateRecentKeywords();
void updateRecentChannels();
-
- void appear() {
- updateRecentKeywords();
- updateRecentChannels();
- queryEdit->clear();
- queryEdit->enableSuggest();
- QTimer::singleShot(0, queryEdit, SLOT(setFocus()));
- }
-
+ void appear();
void disappear() {}
QMap<QString, QVariant> metadata() {
#include "suggester.h"
AutoComplete::AutoComplete(QWidget *parent, QLineEdit *editor):
- QObject(parent), buddy(parent), editor(editor), suggester(0) {
+ QObject(parent), buddy(parent), editor(editor), suggester(0) {
enabled = true;
popup = new QListWidget;
popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- popup->installEventFilter(this);
popup->setMouseTracking(true);
popup->setWindowOpacity(.9);
+ popup->installEventFilter(this);
+ popup->setWindowFlags(Qt::Popup);
+ popup->setFocusPolicy(Qt::NoFocus);
+ popup->setFocusProxy(parent);
- connect(popup, SIGNAL(itemClicked(QListWidgetItem*)),
- SLOT(doneCompletion()));
+ connect(popup, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(doneCompletion()));
// connect(popup, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
// SLOT(currentItemChanged(QListWidgetItem *)));
// connect(popup, SIGNAL(itemEntered(QListWidgetItem*)),
// SLOT(currentItemChanged(QListWidgetItem *)));
- popup->setWindowFlags(Qt::Popup);
- popup->setFocusPolicy(Qt::NoFocus);
- popup->setFocusProxy(parent);
-
timer = new QTimer(this);
timer->setSingleShot(true);
timer->setInterval(300);
connect(timer, SIGNAL(timeout()), SLOT(autoSuggest()));
- connect(editor, SIGNAL(textEdited(QString)), timer, SLOT(start()));
+ connect(parent, SIGNAL(textChanged(QString)), timer, SLOT(start()));
}
popup->adjustSize();
popup->setUpdatesEnabled(true);
- int h = popup->sizeHintForRow(0) * choices.count() + 4;
+ int h = popup->sizeHintForRow(0) * choices.count();
popup->resize(buddy->width(), h);
popup->move(buddy->mapToGlobal(QPoint(0, buddy->height())));
QListWidgetItem *item = popup->currentItem();
if (item) {
editor->setText(item->text());
- QKeyEvent *e;
- e = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
- QApplication::postEvent(editor, e);
- e = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier);
- QApplication::postEvent(editor, e);
+ emit suggestionAccepted(item->text());
}
}
QString query = editor->text();
originalText = query;
// qDebug() << "originalText" << originalText;
- if (query.isEmpty()) return;
+ if (query.isEmpty()) {
+ popup->hide();
+ return;
+ }
if (suggester)
suggester->suggest(query);
bool eventFilter(QObject *obj, QEvent *ev);
void showCompletion(const QStringList &choices);
void setSuggester(Suggester* suggester);
+ QListWidget* getPopup() { return popup; }
public slots:
void doneCompletion();
void currentItemChanged(QListWidgetItem *current);
void suggestionsReady(QStringList suggestions);
+signals:
+ void suggestionAccepted(QString suggestion);
+
private:
QWidget *buddy;
QLineEdit *editor;
#include <QDesktopServices>
#include <QDebug>
+#ifdef APP_MAC
+#include "macutils.h"
+#endif
+
namespace The {
NetworkAccess* http();
}
void DownloadItem::openFolder() {
QFileInfo info(m_file);
+#ifdef APP_MAC
+ mac::showInFinder(info.absoluteFilePath());
+#else
QUrl url = QUrl::fromLocalFile(info.absolutePath());
QDesktopServices::openUrl(url);
+#endif
}
void DownloadItem::tryAgain() {
#include "downloadlistview.h"
#include "downloadmodel.h"
#include "playlist/PrettyItemDelegate.h"
-#include <QtGui>
DownloadListView::DownloadListView(QWidget *parent) : QListView(parent) {
- // playIconHovered = false;
- // setMouseTracking(true);
-
}
void DownloadListView::leaveEvent(QEvent * /* event */) {
QListView::mouseMoveEvent(event);
if (isHoveringPlayIcon(event)) {
- QMetaObject::invokeMethod(model(), "enterPlayIconHover", Qt::DirectConnection);
+ QMetaObject::invokeMethod(model(), "enterPlayIconHover");
} else {
- QMetaObject::invokeMethod(model(), "exitPlayIconHover", Qt::DirectConnection);
+ QMetaObject::invokeMethod(model(), "exitPlayIconHover");
}
}
void DownloadListView::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton
&& isHoveringPlayIcon(event)) {
- QMetaObject::invokeMethod(model(), "enterPlayIconPressed", Qt::DirectConnection);
+ QMetaObject::invokeMethod(model(), "enterPlayIconPressed");
} else {
QListView::mousePressEvent(event);
}
void DownloadListView::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
- QMetaObject::invokeMethod(model(), "exitPlayIconPressed", Qt::DirectConnection);
+ QMetaObject::invokeMethod(model(), "exitPlayIconPressed");
if (isHoveringPlayIcon(event))
emit downloadButtonPushed(indexAt(event->pos()));
} else {
#ifndef DOWNLOADLISTVIEW_H
#define DOWNLOADLISTVIEW_H
-#include <QListView>
-#include <QModelIndex>
+#include <QtGui>
class DownloadListView : public QListView {
#include "downloadsettings.h"
#include "ListModel.h"
#include "playlist/PrettyItemDelegate.h"
-#include "thlibrary/thblackbar.h"
+#include "segmentedcontrol.h"
DownloadView::DownloadView(QWidget *parent) : QWidget(parent) {
layout->setMargin(0);
layout->setSpacing(0);
- bar = new THBlackBar(this);
+ bar = new SegmentedControl(this);
QAction *action = new QAction(tr("Downloads"), this);
bar->addAction(action);
layout->addWidget(bar);
#include <QtGui>
#include "View.h"
-class THBlackBar;
+class SegmentedControl;
class DownloadModel;
class DownloadListView;
class DownloadSettings;
void buttonPushed(QModelIndex index);
private:
- THBlackBar *bar;
+ SegmentedControl *bar;
DownloadListView *listView;
DownloadModel *listModel;
QTimer *updateTimer;
#endif
QtSingleApplication app(argc, argv);
- if (app.sendMessage("Wake up!"))
+ QString message = app.arguments().size() > 1 ? app.arguments().at(1) : "";
+ if (app.sendMessage(message))
return 0;
app.setApplicationName(Constants::NAME);
mainWin.setWindowTitle(Constants::NAME);
#ifdef Q_WS_MAC
+ app.setQuitOnLastWindowClosed(false);
mac::SetupFullScreenWindow(mainWin.winId());
#endif
mainWin.show();
+ mainWin.connect(&app, SIGNAL(messageReceived(const QString &)), &mainWin, SLOT(messageReceived(const QString &)));
app.setActivationWindow(&mainWin, true);
// all string literals are UTF-8
#include <QFontMetricsF>
#include <QPainter>
+#include <QHash>
const qreal PrettyItemDelegate::THUMB_HEIGHT = 90.0;
const qreal PrettyItemDelegate::THUMB_WIDTH = 120.0;
const qreal PrettyItemDelegate::PADDING = 10.0;
+QRect lastAuthorRect;
+QHash<int, QRect> authorRects;
+
PrettyItemDelegate::PrettyItemDelegate(QObject* parent, bool downloadInfo)
: QStyledItemDelegate(parent),
downloadInfo(downloadInfo) {
painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
// author
+ bool authorHovered = false;
+ bool authorPressed = false;
+ const bool isHovered = index.data(HoveredItemRole).toBool();
+ if (isHovered) {
+ authorHovered = index.data(AuthorHoveredRole).toBool();
+ authorPressed = index.data(AuthorPressedRole).toBool();
+ }
+
painter->save();
painter->setFont(smallerBoldFont);
- if (!isSelected && !isActive)
- painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
+ if (!isSelected) {
+ if (authorHovered)
+ painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
+ else
+ painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
+ }
QString authorString = video->author();
QSizeF authorStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
textLoc.setX(textLoc.x() + publishedStringSize.width() + PADDING);
QRectF authorTextBox( textLoc , authorStringSize);
+ authorRects.insert(index.row(), authorTextBox.toRect());
painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
painter->restore();
16,
16);
}
+
+QRect PrettyItemDelegate::authorRect(const QModelIndex& index) const {
+ return authorRects.value(index.row());
+}
QSize sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const;
void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const;
QRect downloadButtonRect(QRect line) const;
+ QRect authorRect(const QModelIndex& index) const;
private:
void createPlayIcon();
bool downloadInfo;
QProgressBar *progressBar;
+
};
#endif
--- /dev/null
+#include "playlistview.h"
+#include "ListModel.h"
+#include "playlist/PrettyItemDelegate.h"
+
+PlaylistView::PlaylistView(QWidget *parent) : QListView(parent) {
+ connect(this, SIGNAL(entered(const QModelIndex &)), SLOT(itemEntered(const QModelIndex &)));
+ setMouseTracking(true);
+}
+
+void PlaylistView::itemEntered(const QModelIndex &index) {
+ ListModel *listModel = dynamic_cast<ListModel *>(model());
+ if (listModel) listModel->setHoveredRow(index.row());
+}
+
+void PlaylistView::leaveEvent(QEvent * /* event */) {
+ ListModel *listModel = dynamic_cast<ListModel *>(model());
+ if (listModel) listModel->clearHover();
+}
+
+void PlaylistView::mouseMoveEvent(QMouseEvent *event) {
+ // qDebug() << "PlaylistView::mouseMoveEvent" << event->pos();
+
+ QListView::mouseMoveEvent(event);
+
+ if (isHoveringAuthor(event)) {
+
+ // check for special "message" item
+ ListModel *listModel = dynamic_cast<ListModel *>(model());
+ if (listModel && listModel->rowCount() == indexAt(event->pos()).row())
+ return;
+
+ QMetaObject::invokeMethod(model(), "enterAuthorHover");
+ setCursor(Qt::PointingHandCursor);
+ } else {
+ QMetaObject::invokeMethod(model(), "exitAuthorHover");
+ unsetCursor();
+ }
+
+}
+
+void PlaylistView::mousePressEvent(QMouseEvent *event) {
+ if (event->button() == Qt::LeftButton
+ && isHoveringAuthor(event)) {
+ QMetaObject::invokeMethod(model(), "enterAuthorPressed");
+ event->ignore();
+ } else {
+ QListView::mousePressEvent(event);
+ }
+}
+
+void PlaylistView::mouseReleaseEvent(QMouseEvent *event) {
+ if (event->button() == Qt::LeftButton) {
+ QMetaObject::invokeMethod(model(), "exitAuthorPressed");
+ if (isHoveringAuthor(event))
+ emit authorPushed(indexAt(event->pos()));
+ } else {
+ QListView::mousePressEvent(event);
+ }
+}
+
+bool PlaylistView::isHoveringAuthor(QMouseEvent *event) {
+ const QModelIndex itemIndex = indexAt(event->pos());
+ const QRect itemRect = visualRect(itemIndex);
+ // qDebug() << " itemRect.x()" << itemRect.x();
+
+ PrettyItemDelegate *delegate = dynamic_cast<PrettyItemDelegate *>(itemDelegate());
+ if (!delegate) return false;
+
+ QRect rect = delegate->authorRect(itemIndex);
+
+ const int x = event->x() - itemRect.x() - rect.x();
+ const int y = event->y() - itemRect.y() - rect.y();
+ bool ret = x > 0 && x < rect.width() && y > 0 && y < rect.height();
+
+ return ret;
+}
--- /dev/null
+#ifndef PLAYLISTVIEW_H
+#define PLAYLISTVIEW_H
+
+#include <QtGui>
+
+class PlaylistView : public QListView {
+
+ Q_OBJECT
+
+public:
+ PlaylistView(QWidget *parent = 0);
+
+protected:
+ void leaveEvent(QEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ bool isHoveringAuthor(QMouseEvent *event);
+
+signals:
+ void authorPushed(QModelIndex index);
+
+private slots:
+ void itemEntered(const QModelIndex &index);
+
+};
+
+#endif // PLAYLISTVIEW_H
#include "playlistwidget.h"
-PlaylistWidget::PlaylistWidget (QWidget *parent, THBlackBar *tabBar, QListView *listView)
+PlaylistWidget::PlaylistWidget (QWidget *parent, SegmentedControl *tabBar, QListView *listView)
: QWidget(parent) {
QBoxLayout *layout = new QVBoxLayout();
layout->setMargin(0);
#define PLAYLISTWIDGET_H
#include <QtGui>
-#include "thlibrary/thblackbar.h"
+#include "segmentedcontrol.h"
class PlaylistWidget : public QWidget
{
public:
- PlaylistWidget(QWidget *parent, THBlackBar *tabBar, QListView *listView);
+ PlaylistWidget(QWidget *parent, SegmentedControl *tabBar, QListView *listView);
};
#endif // PLAYLISTWIDGET_H
void updateGeometries();
void enableSuggest();
void preventSuggest();
- void selectAll() { lineEdit()->selectAll(); };
+ void selectAll() { lineEdit()->selectAll(); }
void setSuggester(Suggester *suggester) { completion->setSuggester(suggester); }
protected:
--- /dev/null
+#include "segmentedcontrol.h"
+#include "fontutils.h"
+
+static const QColor borderColor = QColor(0x26, 0x26, 0x26);
+
+class SegmentedControl::Private {
+public:
+ QList<QAction *> actionList;
+ QAction *checkedAction;
+ QAction *hoveredAction;
+ QAction *pressedAction;
+};
+
+SegmentedControl::SegmentedControl (QWidget *parent)
+ : QWidget(parent), d(new SegmentedControl::Private) {
+
+ setMouseTracking(true);
+
+ d->hoveredAction = 0;
+ d->checkedAction = 0;
+ d->pressedAction = 0;
+}
+
+SegmentedControl::~SegmentedControl() {
+ delete d;
+}
+
+QAction *SegmentedControl::addAction(QAction *action) {
+ QWidget::addAction(action);
+ action->setCheckable(true);
+ d->actionList.append(action);
+ return action;
+}
+
+bool SegmentedControl::setCheckedAction(int index) {
+ if (index < 0) {
+ d->checkedAction = 0;
+ return true;
+ }
+ QAction* newCheckedAction = d->actionList.at(index);
+ return setCheckedAction(newCheckedAction);
+}
+
+bool SegmentedControl::setCheckedAction(QAction *action) {
+ if (d->checkedAction == action) {
+ return false;
+ }
+ d->checkedAction = action;
+ update();
+ return true;
+}
+
+QSize SegmentedControl::minimumSizeHint (void) const {
+ int itemsWidth = calculateButtonWidth() * d->actionList.size() * 1.2;
+ return(QSize(itemsWidth, QFontMetrics(font()).height() * 1.9));
+}
+
+void SegmentedControl::paintEvent (QPaintEvent *event) {
+ int height = event->rect().height();
+ int width = event->rect().width();
+
+ QPainter p(this);
+
+ QLinearGradient linearGrad(rect().topLeft(), rect().bottomLeft());
+ linearGrad.setColorAt(0, borderColor);
+ linearGrad.setColorAt(1, QColor(0x3c, 0x3c, 0x3c));
+ p.fillRect(rect(), QBrush(linearGrad));
+
+ // Calculate Buttons Size & Location
+ const int buttonWidth = width / d->actionList.size();
+
+ // Draw Buttons
+ QRect rect(0, 0, buttonWidth, height);
+ const int actionCount = d->actionList.size();
+ for (int i = 0; i < actionCount; i++) {
+ QAction *action = d->actionList.at(i);
+
+ if (i + 1 == actionCount) {
+ rect.setWidth(width - buttonWidth * (actionCount-1));
+ drawButton(&p, rect, action);
+ } else {
+ drawButton(&p, rect, action);
+ rect.moveLeft(rect.x() + rect.width());
+ }
+
+ }
+
+}
+
+void SegmentedControl::mouseMoveEvent (QMouseEvent *event) {
+ QWidget::mouseMoveEvent(event);
+
+ QAction *action = hoveredAction(event->pos());
+
+ if (!action && d->hoveredAction) {
+ d->hoveredAction = 0;
+ update();
+ } else if (action && action != d->hoveredAction) {
+ d->hoveredAction = action;
+ action->hover();
+ update();
+
+ // status tip
+ QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
+ if (mainWindow) mainWindow->statusBar()->showMessage(action->statusTip());
+ }
+}
+
+void SegmentedControl::mousePressEvent(QMouseEvent *event) {
+ QWidget::mousePressEvent(event);
+ if (d->hoveredAction) {
+ d->pressedAction = d->hoveredAction;
+ update();
+ }
+}
+
+void SegmentedControl::mouseReleaseEvent(QMouseEvent *event) {
+ QWidget::mouseReleaseEvent(event);
+ d->pressedAction = 0;
+ if (d->hoveredAction) {
+ bool changed = setCheckedAction(d->hoveredAction);
+ if (changed) d->hoveredAction->trigger();
+ }
+}
+
+void SegmentedControl::leaveEvent(QEvent *event) {
+ QWidget::leaveEvent(event);
+ // status tip
+ QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
+ if (mainWindow) mainWindow->statusBar()->clearMessage();
+ d->hoveredAction = 0;
+ d->pressedAction = 0;
+ update();
+}
+
+QAction *SegmentedControl::hoveredAction(const QPoint& pos) const {
+ if (pos.y() <= 0 || pos.y() >= height())
+ return 0;
+
+ int buttonWidth = width() / d->actionList.size();
+ int buttonsWidth = width();
+ int buttonsX = 0;
+
+ if (pos.x() <= buttonsX || pos.x() >= (buttonsX + buttonsWidth))
+ return 0;
+
+ int buttonIndex = (pos.x() - buttonsX) / buttonWidth;
+
+ if (buttonIndex >= d->actionList.size())
+ return 0;
+ return(d->actionList[buttonIndex]);
+}
+
+int SegmentedControl::calculateButtonWidth (void) const {
+ QFont smallerBoldFont = FontUtils::smallBold();
+ QFontMetrics fontMetrics(smallerBoldFont);
+ int tmpItemWidth, itemWidth = 0;
+ foreach (QAction *action, d->actionList) {
+ tmpItemWidth = fontMetrics.width(action->text());
+ if (itemWidth < tmpItemWidth) itemWidth = tmpItemWidth;
+ }
+ return itemWidth;
+}
+
+void SegmentedControl::drawButton (QPainter *painter,
+ const QRect& rect,
+ const QAction *action) {
+ if (action == d->checkedAction)
+ drawSelectedButton(painter, rect, action);
+ else
+ drawUnselectedButton(painter, rect, action);
+}
+
+void SegmentedControl::drawUnselectedButton (QPainter *painter,
+ const QRect& rect,
+ const QAction *action) {
+ paintButton(painter, rect, action);
+}
+
+void SegmentedControl::drawSelectedButton (QPainter *painter,
+ const QRect& rect,
+ const QAction *action) {
+ painter->save();
+ painter->translate(rect.topLeft());
+
+ const int width = rect.width();
+ const int height = rect.height();
+ const int hCenter = width * .5;
+ QRadialGradient gradient(hCenter, 0,
+ width,
+ hCenter, 0);
+ gradient.setColorAt(1, Qt::black);
+ gradient.setColorAt(0, QColor(0x33, 0x33, 0x33));
+ painter->fillRect(0, 0, width, height, QBrush(gradient));
+
+ painter->restore();
+ paintButton(painter, rect, action);
+}
+
+void SegmentedControl::paintButton(QPainter *painter, const QRect& rect, const QAction *action) {
+ painter->save();
+ painter->translate(rect.topLeft());
+
+ const int height = rect.height();
+ const int width = rect.width();
+
+ if (action == d->pressedAction && action != d->checkedAction) {
+ const int hCenter = width * .5;
+ QRadialGradient gradient(hCenter, 0,
+ width,
+ hCenter, 0);
+ gradient.setColorAt(1, QColor(0x00, 0x00, 0x00, 0));
+ gradient.setColorAt(0, QColor(0x00, 0x00, 0x00, 16));
+ painter->fillRect(0, 0, width, height, QBrush(gradient));
+ } else if (action == d->hoveredAction && action != d->checkedAction) {
+ const int hCenter = width * .5;
+ QRadialGradient gradient(hCenter, 0,
+ width,
+ hCenter, 0);
+ gradient.setColorAt(1, QColor(0xff, 0xff, 0xff, 0));
+ gradient.setColorAt(0, QColor(0xff, 0xff, 0xff, 16));
+ painter->fillRect(0, 0, width, height, QBrush(gradient));
+ }
+
+ painter->setPen(borderColor);
+#if defined(APP_MAC) | defined(APP_WIN)
+ painter->drawRect(-1, -1, width, height);
+#else
+ painter->drawRect(0, 0, width, height - 1);
+#endif
+
+ painter->setFont(FontUtils::smallBold());
+
+ // text shadow
+ painter->setPen(QColor(0, 0, 0, 128));
+ painter->drawText(0, -1, width, height, Qt::AlignCenter, action->text());
+
+ painter->setPen(QPen(Qt::white, 1));
+ painter->drawText(0, 0, width, height, Qt::AlignCenter, action->text());
+
+ painter->restore();
+}
+
--- /dev/null
+#ifndef SEGMENTEDCONTROL_H
+#define SEGMENTEDCONTROL_H
+
+#include <QtGui>
+
+class SegmentedControl : public QWidget {
+
+ Q_OBJECT
+
+public:
+ SegmentedControl(QWidget *parent = 0);
+ ~SegmentedControl();
+ QAction *addAction(QAction *action);
+ bool setCheckedAction(int index);
+ bool setCheckedAction(QAction *action);
+ QSize minimumSizeHint(void) const;
+
+signals:
+ void checkedActionChanged(QAction & action);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void leaveEvent(QEvent *event);
+
+private:
+ void drawButton(QPainter *painter,
+ const QRect& rect,
+ const QAction *action);
+ void drawUnselectedButton(QPainter *painter,
+ const QRect& rect,
+ const QAction *action);
+ void drawSelectedButton(QPainter *painter,
+ const QRect& rect,
+ const QAction *action);
+ void paintButton(QPainter *painter,
+ const QRect& rect,
+ const QAction *action);
+ QAction *hoveredAction(const QPoint& pos) const;
+ int calculateButtonWidth(void) const;
+
+ class Private;
+ Private *d;
+
+};
+
+#endif /* !SEGMENTEDCONTROL_H */