diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d9c2338d0..13bb0ecf4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,7 +91,9 @@ ENDIF() SET( tomahawkUI ${tomahawkUI} TomahawkWindow.ui DiagnosticsDialog.ui - StackedSettingsDialog.ui + Settings_Accounts.ui + Settings_Advanced.ui + Settings_Collection.ui ProxyDialog.ui AudioControls.ui diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index 06217c8aa..e573842c4 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -53,75 +53,65 @@ #include "utils/Logger.h" #include "accounts/AccountFactoryWrapper.h" #include "accounts/spotify/SpotifyAccount.h" +#include "thirdparty/Qocoa/qtoolbartabdialog.h" +#include "thirdparty/Qocoa/qbutton.h" #include "ui_ProxyDialog.h" -#include "ui_StackedSettingsDialog.h" +#include "ui_Settings_Accounts.h" +#include "ui_Settings_Collection.h" +#include "ui_Settings_Advanced.h" using namespace Tomahawk; using namespace Accounts; -SettingsDialog::SettingsDialog( QWidget *parent ) - : QDialog( parent ) - , ui( new Ui_StackedSettingsDialog ) - , m_proxySettings( this ) +SettingsDialog::SettingsDialog(QObject *parent ) + : QObject( parent ) + , m_accountsWidgetUi( new Ui_Settings_Accounts ) + , m_accountsWidget( new QWidget ) + , m_collectionWidgetUi( new Ui_Settings_Collection ) + , m_collectionWidget( new QWidget ) + , m_advancedWidgetUi( new Ui_Settings_Advanced ) + , m_advancedWidget( new QWidget ) + , m_proxySettings( 0 ) , m_rejected( false ) , m_restartRequired( false ) , m_accountModel( 0 ) , m_sipSpinner( 0 ) { - ui->setupUi( this ); + m_accountsWidgetUi->setupUi( m_accountsWidget ); + m_collectionWidgetUi->setupUi( m_collectionWidget ); + m_advancedWidgetUi->setupUi( m_advancedWidget ); + + m_accountsWidgetUi->accountsFilterCombo->setFocusPolicy( Qt::NoFocus ); + + m_dialog = new QToolbarTabDialog; + TomahawkSettings* s = TomahawkSettings::instance(); - TomahawkUtils::unmarginLayout( layout() ); - TomahawkUtils::unmarginLayout( ui->horizontalLayout ); - -#ifdef Q_WS_X11 - ui->stackedWidget->setContentsMargins( 4, 4, 4, 4 ); -#else - ui->stackedWidget->setContentsMargins( 4, 4, 4, 0 ); -#endif - ui->checkBoxReporter->setChecked( s->crashReporterEnabled() ); - ui->checkBoxHttp->setChecked( s->httpEnabled() ); - - QFrame *sepLine = new QFrame( this ); - sepLine->setFrameShape( QFrame::HLine ); - sepLine->setFrameShadow( QFrame::Sunken ); - ui->horizontalLayout->insertWidget( 0, sepLine ); - - m_toolBar = new QToolBar( tr( "Tomahawk Settings" ), this ); - ui->horizontalLayout->insertWidget( 0, m_toolBar ); - m_toolBar->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + m_advancedWidgetUi->checkBoxReporter->setChecked( s->crashReporterEnabled() ); + m_advancedWidgetUi->checkBoxHttp->setChecked( s->httpEnabled() ); //Network settings TomahawkSettings::ExternalAddressMode mode = TomahawkSettings::instance()->externalAddressMode(); if ( mode == TomahawkSettings::Lan ) - ui->lanOnlyRadioButton->setChecked( true ); + m_advancedWidgetUi->lanOnlyRadioButton->setChecked( true ); else if ( mode == TomahawkSettings::Static ) - ui->staticIpRadioButton->setChecked( true ); + m_advancedWidgetUi->staticIpRadioButton->setChecked( true ); else - ui->upnpRadioButton->setChecked( true ); + m_advancedWidgetUi->upnpRadioButton->setChecked( true ); - ui->staticHostNamePortLabel->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticHostName->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticPort->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticHostNameLabel->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticPortLabel->setEnabled( ui->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticHostNamePortLabel->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticHostName->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticPort->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticHostNameLabel->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticPortLabel->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); bool useProxy = TomahawkSettings::instance()->proxyType() == QNetworkProxy::Socks5Proxy; - ui->enableProxyCheckBox->setChecked( useProxy ); - ui->proxyButton->setEnabled( useProxy ); + m_advancedWidgetUi->enableProxyCheckBox->setChecked( useProxy ); + m_advancedWidgetUi->proxyButton->setEnabled( useProxy ); - ui->aclEntryClearButton->setEnabled( TomahawkSettings::instance()->aclEntries().size() > 0 ); - connect( ui->aclEntryClearButton, SIGNAL( clicked( bool ) ), this, SLOT( aclEntryClearButtonClicked() ) ); - - createIcons(); -#ifdef Q_WS_X11 - setContentsMargins( 4, 4, 4, 4 ); -#elif defined( Q_OS_MAC ) - setContentsMargins( 0, 0, 0, 4 ); -#else - setContentsMargins( 0, 4, 4, 4 ); -#endif + m_advancedWidgetUi->aclEntryClearButton->setEnabled( TomahawkSettings::instance()->aclEntries().size() > 0 ); + connect( m_advancedWidgetUi->aclEntryClearButton, SIGNAL( clicked( bool ) ), this, SLOT( aclEntryClearButtonClicked() ) ); #ifdef Q_WS_MAC // Avoid resize handles on sheets on osx @@ -130,16 +120,18 @@ SettingsDialog::SettingsDialog( QWidget *parent ) p->setFixedSize( 0, 0 ); #endif + m_accountsWidgetUi->installFromFileBtn->setText( tr( "Install from file" ) ); + // Accounts AccountDelegate* accountDelegate = new AccountDelegate( this ); - ui->accountsView->setItemDelegate( accountDelegate ); - ui->accountsView->setContextMenuPolicy( Qt::CustomContextMenu ); - ui->accountsView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); - ui->accountsView->setMouseTracking( true ); + m_accountsWidgetUi->accountsView->setItemDelegate( accountDelegate ); + m_accountsWidgetUi->accountsView->setContextMenuPolicy( Qt::CustomContextMenu ); + m_accountsWidgetUi->accountsView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + m_accountsWidgetUi->accountsView->setMouseTracking( true ); connect( accountDelegate, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) ); connect( accountDelegate, SIGNAL( openConfig( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( openAccountFactoryConfig( Tomahawk::Accounts::AccountFactory* ) ) ); - connect( accountDelegate, SIGNAL( update( QModelIndex ) ), ui->accountsView, SLOT( update( QModelIndex ) ) ); + connect( accountDelegate, SIGNAL( update( QModelIndex ) ), m_accountsWidgetUi->accountsView, SLOT( update( QModelIndex ) ) ); m_accountModel = new AccountModel( this ); m_accountProxy = new AccountModelFilterProxy( m_accountModel ); @@ -150,59 +142,75 @@ SettingsDialog::SettingsDialog( QWidget *parent ) connect( m_accountProxy, SIGNAL( errorInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( errorInstalling(QPersistentModelIndex) ) ); connect( m_accountProxy, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( scrollTo( QModelIndex ) ) ); - ui->accountsView->setModel( m_accountProxy ); + m_accountsWidgetUi->accountsView->setModel( m_accountProxy ); - connect( ui->installFromFileBtn, SIGNAL( clicked( bool ) ), this, SLOT( installFromFile() ) ); + connect( m_accountsWidgetUi->installFromFileBtn, SIGNAL( clicked( bool ) ), this, SLOT( installFromFile() ) ); connect( m_accountModel, SIGNAL( createAccount( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( createAccountFromFactory( Tomahawk::Accounts::AccountFactory* ) ) ); - ui->accountsFilterCombo->addItem( tr( "All" ), Accounts::NoType ); - ui->accountsFilterCombo->addItem( accountTypeToString( SipType ), SipType ); - ui->accountsFilterCombo->addItem( accountTypeToString( ResolverType ), ResolverType ); - ui->accountsFilterCombo->addItem( accountTypeToString( StatusPushType ), StatusPushType ); + m_accountsWidgetUi->accountsFilterCombo->addItem( tr( "All" ), Accounts::NoType ); + m_accountsWidgetUi->accountsFilterCombo->addItem( accountTypeToString( SipType ), SipType ); + m_accountsWidgetUi->accountsFilterCombo->addItem( accountTypeToString( ResolverType ), ResolverType ); + m_accountsWidgetUi->accountsFilterCombo->addItem( accountTypeToString( StatusPushType ), StatusPushType ); - connect( ui->accountsFilterCombo, SIGNAL( activated( int ) ), this, SLOT( accountsFilterChanged( int ) ) ); + connect( m_accountsWidgetUi->accountsFilterCombo, SIGNAL( activated( int ) ), this, SLOT( accountsFilterChanged( int ) ) ); if ( !Servent::instance()->isReady() ) { - m_sipSpinner = new AnimatedSpinner( ui->accountsView ); + m_sipSpinner = new AnimatedSpinner( m_accountsWidgetUi->accountsView ); m_sipSpinner->fadeIn(); connect( Servent::instance(), SIGNAL( ready() ), this, SLOT( serventReady() ) ); } // ADVANCED - ui->staticHostName->setText( s->externalHostname() ); - ui->staticPort->setValue( s->externalPort() ); - ui->proxyButton->setVisible( true ); + m_advancedWidgetUi->staticHostName->setText( s->externalHostname() ); + m_advancedWidgetUi->staticPort->setValue( s->externalPort() ); + m_advancedWidgetUi->proxyButton->setVisible( true ); - ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); - ui->scannerTimeSpinBox->setValue( s->scannerTime() ); - ui->enableEchonestCatalog->setChecked( s->enableEchonestCatalogs() ); + m_collectionWidgetUi->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); + m_collectionWidgetUi->scannerTimeSpinBox->setValue( s->scannerTime() ); + m_collectionWidgetUi->enableEchonestCatalog->setChecked( s->enableEchonestCatalogs() ); - connect( ui->checkBoxWatchForChanges, SIGNAL( clicked( bool ) ), SLOT( updateScanOptionsView() ) ); + connect( m_collectionWidgetUi->checkBoxWatchForChanges, SIGNAL( clicked( bool ) ), SLOT( updateScanOptionsView() ) ); - if ( ui->checkBoxWatchForChanges->isChecked() ) + if ( m_collectionWidgetUi->checkBoxWatchForChanges->isChecked() ) { - ui->scanTimeLabel->show(); - ui->scannerTimeSpinBox->show(); + m_collectionWidgetUi->scanTimeLabel->show(); + m_collectionWidgetUi->scannerTimeSpinBox->show(); } else { - ui->scanTimeLabel->hide(); - ui->scannerTimeSpinBox->hide(); + m_collectionWidgetUi->scanTimeLabel->hide(); + m_collectionWidgetUi->scannerTimeSpinBox->hide(); } foreach ( const QString& dir, TomahawkSettings::instance()->scannerPaths() ) { - ui->dirTree->checkPath( dir, Qt::Checked ); + m_collectionWidgetUi->dirTree->checkPath( dir, Qt::Checked ); } - ui->advancedPage->setMinimumSize( ui->advancedPage->sizeHint() ); - int buttonsWidth = qMax( ui->proxyButton->sizeHint().width(), - ui->aclEntryClearButton->sizeHint().width() ); - ui->proxyButton->setFixedWidth( buttonsWidth ); - ui->aclEntryClearButton->setFixedWidth( buttonsWidth ); + int buttonsWidth = qMax( m_advancedWidgetUi->proxyButton->sizeHint().width(), + m_advancedWidgetUi->aclEntryClearButton->sizeHint().width() ); + m_advancedWidgetUi->proxyButton->setFixedWidth( buttonsWidth ); + m_advancedWidgetUi->aclEntryClearButton->setFixedWidth( buttonsWidth ); +#ifndef Q_OS_MAC + m_advancedWidget->setMinimumSize( m_advancedWidget->sizeHint() ); + m_accountsWidget->setMinimumWidth( 500 ); +#else + m_accountsWidget->setContentsMargins( 6, 6, 6, 6 ); + m_accountsWidgetUi->horizontalLayout->setContentsMargins( 0, 0, 0, 0 ); + m_accountsWidgetUi->installFromFileBtn->setContentsMargins( -4, 0, 0, 0 ); + m_accountsWidget->setMinimumSize( 550, 400 ); + m_accountsWidgetUi->accountsView->setAttribute( Qt::WA_MacShowFocusRect, false ); + + m_collectionWidget->setContentsMargins( 6, 6, 6, 6 ); + m_collectionWidget->setMinimumHeight( m_collectionWidgetUi->verticalLayout->sizeHint().height() + 20 ); + m_collectionWidgetUi->dirTree->setAttribute( Qt::WA_MacShowFocusRect, false ); + + m_advancedWidget->setContentsMargins( 6, 6, 6, 6 ); + m_advancedWidget->setMinimumHeight( m_advancedWidgetUi->verticalLayout->sizeHint().height() ); +#endif // NOW PLAYING // #ifdef Q_WS_MAC @@ -211,72 +219,96 @@ SettingsDialog::SettingsDialog( QWidget *parent ) // ui->checkBoxEnableAdium->hide(); // #endif - connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); - connect( ui->lanOnlyRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); - connect( ui->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); - connect( ui->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); - connect( ui->lanOnlyRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); - connect( ui->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); - connect( ui->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); - connect( ui->enableProxyCheckBox, SIGNAL( toggled(bool) ), SLOT( toggleProxyEnabled() ) ); - connect( this, SIGNAL( rejected() ), SLOT( onRejected() ) ); + m_dialog->addTab( m_accountsWidget, QPixmap( RESPATH "images/account-settings.png" ), tr( "Services" ), tr( "Configure the accounts and services used by Tomahawk " + "to search and retrieve music, find your friends and " + "update your status." ) ); + + m_dialog->addTab( m_collectionWidget, QPixmap( RESPATH "images/music-settings.png" ), tr( "Collection" ), tr( "Manage how Tomahawk finds music on your computer." )); + + m_dialog->addTab( m_advancedWidget, QPixmap( RESPATH "images/advanced-settings.png" ), tr( "Advanced" ), tr( "Configure Tomahawk's advanced settings, including " + "network connectivity settings, browser interaction " + "and more." ) ); + + m_dialog->setCurrentIndex( 0 ); + + connect( m_advancedWidgetUi->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); + connect( m_advancedWidgetUi->lanOnlyRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); + connect( m_advancedWidgetUi->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); + connect( m_advancedWidgetUi->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); + connect( m_advancedWidgetUi->lanOnlyRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); + connect( m_advancedWidgetUi->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); + connect( m_advancedWidgetUi->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); + connect( m_advancedWidgetUi->enableProxyCheckBox, SIGNAL( toggled(bool) ), SLOT( toggleProxyEnabled() ) ); + + connect( m_dialog, SIGNAL( accepted() ), SLOT( saveSettings() ) ); + connect( m_dialog, SIGNAL( rejected() ), SLOT( onRejected() ) ); +} + + +void +SettingsDialog::saveSettings() +{ + qDebug() << Q_FUNC_INFO; + + TomahawkSettings* s = TomahawkSettings::instance(); + + s->setCrashReporterEnabled( m_advancedWidgetUi->checkBoxReporter->checkState() == Qt::Checked ); + s->setHttpEnabled( m_advancedWidgetUi->checkBoxHttp->checkState() == Qt::Checked ); + s->setProxyType( m_advancedWidgetUi->enableProxyCheckBox->isChecked() ? QNetworkProxy::Socks5Proxy : QNetworkProxy::NoProxy ); + s->setExternalAddressMode( m_advancedWidgetUi->upnpRadioButton->isChecked() ? TomahawkSettings::Upnp : ( m_advancedWidgetUi->lanOnlyRadioButton->isChecked() ? TomahawkSettings::Lan : TomahawkSettings::Static ) ); + + s->setExternalHostname( m_advancedWidgetUi->staticHostName->text() ); + s->setExternalPort( m_advancedWidgetUi->staticPort->value() ); + + s->setScannerPaths( m_collectionWidgetUi->dirTree->getCheckedPaths() ); + s->setWatchForChanges( m_collectionWidgetUi->checkBoxWatchForChanges->isChecked() ); + s->setScannerTime( m_collectionWidgetUi->scannerTimeSpinBox->value() ); + s->setEnableEchonestCatalogs( m_collectionWidgetUi->enableEchonestCatalog->isChecked() ); + +// s->setNowPlayingEnabled( ui->checkBoxEnableAdium->isChecked() ); + + s->applyChanges(); + s->sync(); + + if ( m_restartRequired ) + QMessageBox::information( 0, tr( "Information" ), tr( "Some changed settings will not take effect until Tomahawk is restarted" ) ); + + TomahawkUtils::NetworkProxyFactory* proxyFactory = TomahawkUtils::proxyFactory(); + if ( !m_advancedWidgetUi->enableProxyCheckBox->isChecked() ) + { + tDebug() << Q_FUNC_INFO << "Got NoProxy selected"; + proxyFactory->setProxy( QNetworkProxy::NoProxy ); + } + else + { + tDebug() << Q_FUNC_INFO << "Got Socks5Proxy selected"; + proxyFactory->setProxy( QNetworkProxy( QNetworkProxy::Socks5Proxy, s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ) ); + if ( !s->proxyNoProxyHosts().isEmpty() ) + { + tDebug() << Q_FUNC_INFO << "noproxy hosts:" << s->proxyNoProxyHosts(); + tDebug() << Q_FUNC_INFO << "split noproxy line edit is " << s->proxyNoProxyHosts().split( ' ', QString::SkipEmptyParts ); + proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ' ', QString::SkipEmptyParts ) ); + } + } } SettingsDialog::~SettingsDialog() { - qDebug() << Q_FUNC_INFO; - - if ( !m_rejected ) - { - TomahawkSettings* s = TomahawkSettings::instance(); - - s->setCrashReporterEnabled( ui->checkBoxReporter->checkState() == Qt::Checked ); - s->setHttpEnabled( ui->checkBoxHttp->checkState() == Qt::Checked ); - s->setProxyType( ui->enableProxyCheckBox->isChecked() ? QNetworkProxy::Socks5Proxy : QNetworkProxy::NoProxy ); - s->setExternalAddressMode( ui->upnpRadioButton->isChecked() ? TomahawkSettings::Upnp : ( ui->lanOnlyRadioButton->isChecked() ? TomahawkSettings::Lan : TomahawkSettings::Static ) ); - - s->setExternalHostname( ui->staticHostName->text() ); - s->setExternalPort( ui->staticPort->value() ); - - s->setScannerPaths( ui->dirTree->getCheckedPaths() ); - s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() ); - s->setScannerTime( ui->scannerTimeSpinBox->value() ); - s->setEnableEchonestCatalogs( ui->enableEchonestCatalog->isChecked() ); - -// s->setNowPlayingEnabled( ui->checkBoxEnableAdium->isChecked() ); - - s->applyChanges(); - s->sync(); - - if ( m_restartRequired ) - QMessageBox::information( this, tr( "Information" ), tr( "Some changed settings will not take effect until Tomahawk is restarted" ) ); - - TomahawkUtils::NetworkProxyFactory* proxyFactory = TomahawkUtils::proxyFactory(); - if ( !ui->enableProxyCheckBox->isChecked() ) - { - tDebug() << Q_FUNC_INFO << "Got NoProxy selected"; - proxyFactory->setProxy( QNetworkProxy::NoProxy ); - } - else - { - tDebug() << Q_FUNC_INFO << "Got Socks5Proxy selected"; - proxyFactory->setProxy( QNetworkProxy( QNetworkProxy::Socks5Proxy, s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ) ); - if ( !s->proxyNoProxyHosts().isEmpty() ) - { - tDebug() << Q_FUNC_INFO << "noproxy hosts:" << s->proxyNoProxyHosts(); - tDebug() << Q_FUNC_INFO << "split noproxy line edit is " << s->proxyNoProxyHosts().split( ' ', QString::SkipEmptyParts ); - proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ' ', QString::SkipEmptyParts ) ); - } - } - } - else - qDebug() << "Settings dialog cancelled, NOT saving prefs."; - - delete ui; + m_accountsWidget->deleteLater(); + m_collectionWidget->deleteLater(); + m_advancedWidget->deleteLater(); + m_dialog->deleteLater(); } +void +SettingsDialog::show() +{ + m_dialog->setCurrentIndex( 0 ); + m_dialog->show(); +} + void SettingsDialog::serventReady() { @@ -284,73 +316,6 @@ SettingsDialog::serventReady() } -void -SettingsDialog::createIcons() -{ - ensurePolished(); - - m_settingsGroup = new QActionGroup( m_toolBar ); - - QWidget *leftSpacer = new QWidget( m_toolBar ); - leftSpacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - m_toolBar->addWidget( leftSpacer ); - - QAction *accountsAction = new QAction( QIcon( RESPATH "images/account-settings.png" ), - tr( "Services" ), - m_toolBar ); - accountsAction->setCheckable( true ); - accountsAction->setToolTip( tr( "Services
" - "Configure the accounts and services used by Tomahawk " - "to search and retrieve music, find your friends and " - "update your status." ) ); - m_settingsGroup->addAction( accountsAction ); - - QAction *musicAction = new QAction( QIcon( RESPATH "images/music-settings.png" ), - tr( "Collection" ), - m_toolBar ); - musicAction->setCheckable( true ); - musicAction->setToolTip( tr( "Collection
" - "Manage how Tomahawk finds music on your computer." ) ); - m_settingsGroup->addAction( musicAction ); - - - QAction *advancedAction = new QAction( QIcon( RESPATH "images/advanced-settings.png" ), - tr( "Advanced" ), - m_toolBar ); - advancedAction->setCheckable( true ); - advancedAction->setToolTip( tr( "Advanced
" - "Configure Tomahawk's advanced settings, including " - "network connectivity settings, browser interaction " - "and more." ) ); - m_settingsGroup->addAction( advancedAction ); - - m_settingsGroup->setExclusive( true ); - - m_toolBar->addActions( m_settingsGroup->actions() ); - - QWidget *rightSpacer = new QWidget( m_toolBar ); - rightSpacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - m_toolBar->addWidget( rightSpacer ); - - connect( m_settingsGroup, SIGNAL( triggered( QAction * ) ), - this, SLOT( changePage( QAction * ) ) ); - - accountsAction->setChecked( true ); - changePage( accountsAction ); -} - - -void -SettingsDialog::changePage( QAction *action ) -{ - int index = m_settingsGroup->actions().indexOf( action ); - if( ui->stackedWidget->currentIndex() != index ) - { - ui->stackedWidget->setCurrentIndex( index ); - } -} - - void SettingsDialog::onRejected() { @@ -361,11 +326,12 @@ SettingsDialog::onRejected() void SettingsDialog::changeEvent( QEvent *e ) { - QDialog::changeEvent( e ); switch ( e->type() ) { case QEvent::LanguageChange: - ui->retranslateUi( this ); + m_accountsWidgetUi->retranslateUi( m_accountsWidget ); + m_collectionWidgetUi->retranslateUi( m_collectionWidget ); + m_advancedWidgetUi->retranslateUi( m_advancedWidget ); break; default: @@ -386,33 +352,33 @@ SettingsDialog::showProxySettings() void SettingsDialog::toggleRemoteMode() { - ui->staticHostNamePortLabel->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticHostName->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticPort->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticHostNameLabel->setEnabled( ui->staticIpRadioButton->isChecked() ); - ui->staticPortLabel->setEnabled( ui->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticHostNamePortLabel->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticHostName->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticPort->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticHostNameLabel->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); + m_advancedWidgetUi->staticPortLabel->setEnabled( m_advancedWidgetUi->staticIpRadioButton->isChecked() ); } void SettingsDialog::toggleProxyEnabled() { - ui->proxyButton->setEnabled( ui->enableProxyCheckBox->isChecked() ); + m_advancedWidgetUi->proxyButton->setEnabled( m_advancedWidgetUi->enableProxyCheckBox->isChecked() ); } void SettingsDialog::updateScanOptionsView() { - if ( ui->checkBoxWatchForChanges->isChecked() ) + if ( m_collectionWidgetUi->checkBoxWatchForChanges->isChecked() ) { - ui->scanTimeLabel->show(); - ui->scannerTimeSpinBox->show(); + m_collectionWidgetUi->scanTimeLabel->show(); + m_collectionWidgetUi->scannerTimeSpinBox->show(); } else { - ui->scanTimeLabel->hide(); - ui->scannerTimeSpinBox->hide(); + m_collectionWidgetUi->scanTimeLabel->hide(); + m_collectionWidgetUi->scannerTimeSpinBox->hide(); } } @@ -420,7 +386,7 @@ SettingsDialog::updateScanOptionsView() void SettingsDialog::accountsFilterChanged( int ) { - AccountType filter = static_cast< AccountType >( ui->accountsFilterCombo->itemData( ui->accountsFilterCombo->currentIndex() ).toInt() ); + AccountType filter = static_cast< AccountType >( m_accountsWidgetUi->accountsFilterCombo->itemData( m_accountsWidgetUi->accountsFilterCombo->currentIndex() ).toInt() ); m_accountProxy->setFilterType( filter ); } @@ -445,13 +411,13 @@ SettingsDialog::openAccountFactoryConfig( AccountFactory* factory ) } #ifndef Q_OS_MAC - AccountFactoryWrapper dialog( factory, this ); + AccountFactoryWrapper dialog( factory, 0 ); QWeakPointer< AccountFactoryWrapper > watcher( &dialog ); dialog.exec(); #else // on osx a sheet needs to be non-modal - AccountFactoryWrapper* dialog = new AccountFactoryWrapper( factory, this ); + AccountFactoryWrapper* dialog = new AccountFactoryWrapper( factory, 0 ); dialog->show(); #endif } @@ -460,21 +426,21 @@ SettingsDialog::openAccountFactoryConfig( AccountFactory* factory ) void SettingsDialog::createAccountFromFactory( AccountFactory* factory ) { - TomahawkUtils::createAccountFromFactory( factory, this ); + TomahawkUtils::createAccountFromFactory( factory, 0 ); } void SettingsDialog::openAccountConfig( Account* account, bool showDelete ) { - TomahawkUtils::openAccountConfig( account, this, showDelete ); + TomahawkUtils::openAccountConfig( account, 0, showDelete ); } void SettingsDialog::installFromFile() { - const QString resolver = QFileDialog::getOpenFileName( this, tr( "Install resolver from file" ), TomahawkSettings::instance()->scriptDefaultPath() ); + const QString resolver = QFileDialog::getOpenFileName( 0, tr( "Install resolver from file" ), TomahawkSettings::instance()->scriptDefaultPath() ); if( !resolver.isEmpty() ) { @@ -520,7 +486,7 @@ void SettingsDialog::aclEntryClearButtonClicked() { QMessageBox::StandardButton button = QMessageBox::question( - ui->stackedWidget, + 0, tr( "Delete all Access Control entries?" ), tr( "Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to." ), QMessageBox::Ok | QMessageBox::Cancel, @@ -529,7 +495,7 @@ SettingsDialog::aclEntryClearButtonClicked() if ( button == QMessageBox::Ok ) { ACLRegistry::instance()->wipeEntries(); - ui->aclEntryClearButton->setEnabled( false ); + m_advancedWidgetUi->aclEntryClearButton->setEnabled( false ); } } @@ -537,7 +503,7 @@ SettingsDialog::aclEntryClearButtonClicked() void SettingsDialog::scrollTo( const QModelIndex& idx ) { - ui->accountsView->scrollTo( idx, QAbstractItemView::PositionAtBottom ); + m_accountsWidgetUi->accountsView->scrollTo( idx, QAbstractItemView::PositionAtBottom ); } diff --git a/src/SettingsDialog.h b/src/SettingsDialog.h index 88cb934a0..885997fde 100644 --- a/src/SettingsDialog.h +++ b/src/SettingsDialog.h @@ -31,14 +31,16 @@ class AnimatedSpinner; class QListWidgetItem; -class Ui_StackedSettingsDialog; +class Ui_Settings_Accounts; +class Ui_Settings_Collection; +class Ui_Settings_Advanced; class SipPlugin; class ResolversModel; class QNetworkReply; +class QToolbarTabDialog; namespace Ui { - class SettingsDialog; class ProxyDialog; } @@ -68,14 +70,15 @@ private: Ui::ProxyDialog* ui; }; -class SettingsDialog : public QDialog +class SettingsDialog : public QObject { Q_OBJECT public: - explicit SettingsDialog( QWidget* parent = 0 ); + explicit SettingsDialog( QObject* parent = 0 ); ~SettingsDialog(); + void show(); protected: void changeEvent( QEvent* e ); @@ -97,20 +100,26 @@ private slots: void updateScanOptionsView(); - void changePage( QAction *action ); void serventReady(); void aclEntryClearButtonClicked(); - + void requiresRestart(); +private slots: + void saveSettings(); + private: - void createIcons(); + Ui_Settings_Accounts* m_accountsWidgetUi; + QWidget* m_accountsWidget; - Ui_StackedSettingsDialog* ui; + Ui_Settings_Collection* m_collectionWidgetUi; + QWidget* m_collectionWidget; - QToolBar *m_toolBar; - QActionGroup *m_settingsGroup; + Ui_Settings_Advanced* m_advancedWidgetUi; + QWidget* m_advancedWidget; + + QToolbarTabDialog* m_dialog; ProxyDialog m_proxySettings; bool m_rejected; diff --git a/src/Settings_Accounts.ui b/src/Settings_Accounts.ui new file mode 100644 index 000000000..de51ed1f0 --- /dev/null +++ b/src/Settings_Accounts.ui @@ -0,0 +1,82 @@ + + + Settings_Accounts + + + + 0 + 0 + 553 + 439 + + + + Form + + + + 2 + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Filter by capability: + + + + + + + + + + + + true + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + QButton + QWidget +
thirdparty/Qocoa/qbutton.h
+ 1 +
+
+ + +
diff --git a/src/Settings_Advanced.ui b/src/Settings_Advanced.ui new file mode 100644 index 000000000..324bace00 --- /dev/null +++ b/src/Settings_Advanced.ui @@ -0,0 +1,271 @@ + + + Settings_Advanced + + + + 0 + 0 + 469 + 475 + + + + + 0 + 0 + + + + Form + + + + 2 + + + + + Remote Peer Connection Method + + + + + + None (outgoing connections only) + + + + + + + Use UPnP to establish port forward (recommended) + + + + + + + Use static external IP address/host name and port + + + + + + + + 0 + 0 + + + + Set this to your external IP address or host name. Make sure to forward the port to this host! + + + true + + + + + + + + + + 0 + 0 + + + + Static Host Name: + + + + + + + + + + + 0 + 0 + + + + Static Port: + + + + + + + 65535 + + + 50210 + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + SOCKS Proxy + + + + + + Use SOCKS Proxy + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Proxy Settings... + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Other Settings + + + + + + Qt::LeftToRight + + + Allow web browsers to interact with Tomahawk (recommended) + + + true + + + + + + + Qt::LeftToRight + + + Send reports after Tomahawk crashed + + + true + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + Clear All Access Control Entries + + + + + + + + + + + + Qt::Vertical + + + + 20 + 13 + + + + + + + + + diff --git a/src/Settings_Collection.ui b/src/Settings_Collection.ui new file mode 100644 index 000000000..6579d2d51 --- /dev/null +++ b/src/Settings_Collection.ui @@ -0,0 +1,102 @@ + + + Settings_Collection + + + + 0 + 0 + 403 + 370 + + + + + 0 + 0 + + + + Form + + + + 2 + + + + + + + Path to scan for music files: + + + + + + + + + + + + + + The Echo Nest supports keeping track of your catalog metadata + and using it to craft personalized radios. Enabling this option + will allow you (and all your friends) to create automatic playlists + and stations based on your personal taste profile. + + + Upload collection list to The Echo Nest to enable user radio + + + + + + + + 0 + 0 + + + + Watch for changes + + + + + + + + + Time between scans, in seconds: + + + + + + + 60 + + + 999999999 + + + + + + + + + + + + CheckDirTree + QTreeView +
widgets/CheckDirTree.h
+
+
+ + +
diff --git a/src/StackedSettingsDialog.ui b/src/StackedSettingsDialog.ui index af3cfe49d..0377d15dd 100644 --- a/src/StackedSettingsDialog.ui +++ b/src/StackedSettingsDialog.ui @@ -6,8 +6,8 @@ 0 0 - 655 - 500 + 692 + 604 @@ -22,7 +22,7 @@ - 0 + 1 @@ -461,6 +461,12 @@ + buttonBox + + frame_4 + frame_2 + frameNetworkAdvanced + stackedWidget diff --git a/src/TomahawkWindow.cpp b/src/TomahawkWindow.cpp index 8f01a4103..c2a39f628 100644 --- a/src/TomahawkWindow.cpp +++ b/src/TomahawkWindow.cpp @@ -101,6 +101,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) , m_searchWidget( 0 ) , m_audioControls( new AudioControls( this ) ) , m_trayIcon( new TomahawkTrayIcon( this ) ) + , m_settingsDialog( 0 ) , m_audioRetryCounter( 0 ) { setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); @@ -803,8 +804,10 @@ TomahawkWindow::onHistoryForwardAvailable( bool avail ) void TomahawkWindow::showSettingsDialog() { - SettingsDialog win; - win.exec(); + if ( !m_settingsDialog ) + m_settingsDialog = new SettingsDialog; + + m_settingsDialog->show(); } diff --git a/src/TomahawkWindow.h b/src/TomahawkWindow.h index 5191efce1..3289b5f8c 100644 --- a/src/TomahawkWindow.h +++ b/src/TomahawkWindow.h @@ -38,6 +38,7 @@ #include #endif +class SettingsDialog; namespace Tomahawk { namespace Accounts @@ -184,6 +185,7 @@ private: QueueView* m_queueView; AnimatedSplitter* m_sidebar; JobStatusSortModel* m_jobsModel; + SettingsDialog* m_settingsDialog; // Menus and menu actions: Accounts menu QMenuBar *m_menuBar; diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index c148deb59..ecec610b1 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -391,7 +391,9 @@ IF( APPLE ) SET( libSources ${libSources} utils/TomahawkUtils_Mac.mm mac/FileHelpers.mm + thirdparty/Qocoa/qbutton_mac.mm thirdparty/Qocoa/qsearchfield_mac.mm + thirdparty/Qocoa/qtoolbartabdialog_mac.mm widgets/SourceTreePopupDialog_mac.mm ) SET_SOURCE_FILES_PROPERTIES(utils/TomahawkUtils_Mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default") @@ -408,7 +410,7 @@ IF( APPLE ) /System/Library/Frameworks/Security.framework ) ELSE( APPLE ) - SET( libGuiSources ${libGuiSources} thirdparty/Qocoa/qsearchfield.cpp ) + SET( libGuiSources ${libGuiSources} thirdparty/Qocoa/qbutton.cpp thirdparty/Qocoa/qsearchfield.cpp thirdparty/Qocoa/qtoolbartabdialog.cpp ) ENDIF( APPLE ) IF(LIBLASTFM_FOUND) diff --git a/src/libtomahawk/accounts/lastfm/LastFmConfig.cpp b/src/libtomahawk/accounts/lastfm/LastFmConfig.cpp index 6a8c98b63..9d985f156 100644 --- a/src/libtomahawk/accounts/lastfm/LastFmConfig.cpp +++ b/src/libtomahawk/accounts/lastfm/LastFmConfig.cpp @@ -294,7 +294,7 @@ LastFmConfig::onLovedFinished( QNetworkReply* reply ) m_ui->progressBar->setValue( thisPage ); foreach ( lastfm::XmlQuery e, loved.children( "track" ) ) { -// tDebug() << "Found:" << e.children( "artist" ).first()["name"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt(); + tDebug() << "Found:" << e.children( "artist" ).first()["name"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt(); Tomahawk::query_ptr query = Tomahawk::Query::get( e.children( "artist" ).first()["name"].text(), e["name"].text(), QString(), QString(), false ); if ( query.isNull() ) continue; @@ -336,6 +336,8 @@ LastFmConfig::onLovedFinished( QNetworkReply* reply ) bool trackEquality( const Tomahawk::query_ptr& first, const Tomahawk::query_ptr& second ) { + qDebug() << "Comparing:" << first->track() << second->track(); + qDebug() << "==========" << first->artist() << second->artist(); return first->equals( second, true ); } @@ -372,15 +374,20 @@ LastFmConfig::syncLoved() foreach ( const Tomahawk::query_ptr& localLoved, myLoved ) { + qDebug() << "CHECKING FOR LOCAL LOVED ON LAST.FM TOO:" << m_localLoved[ localLoved ].value.toString() << localLoved->track() << localLoved->artist(); QSet< Tomahawk::query_ptr >::const_iterator iter = std::find_if( m_lastfmLoved.begin(), m_lastfmLoved.end(), boost::bind( &trackEquality, _1, boost::ref( localLoved ) ) ); + qDebug() << "Result:" << (iter == m_lastfmLoved.constEnd()); // If we unloved it locally, but it's still loved on last.fm, unlove it if ( m_localLoved[ localLoved ].value.toString() == "false" && iter != m_lastfmLoved.constEnd() ) lastFmToUnlove << localLoved; // If we loved it locally but not loved on last.fm, love it if ( m_localLoved[ localLoved ].value.toString() == "true" && iter == m_lastfmLoved.constEnd() ) + { + qDebug() << "Found Local loved track but not on last.fm!:" << localLoved->track() << localLoved->artist(); lastFmToLove << localLoved; + } } foreach ( const Tomahawk::query_ptr& track, localToLove ) diff --git a/src/libtomahawk/thirdparty/Qocoa/qbutton.cpp b/src/libtomahawk/thirdparty/Qocoa/qbutton.cpp new file mode 100644 index 000000000..0a79e2baf --- /dev/null +++ b/src/libtomahawk/thirdparty/Qocoa/qbutton.cpp @@ -0,0 +1,89 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qbutton.h" + +#include +#include +#include +#include + +class QButtonPrivate : public QObject +{ +public: + QButtonPrivate(QButton *button, QAbstractButton *abstractButton) + : QObject(button), abstractButton(abstractButton) {} + QPointer abstractButton; +}; + +QButton::QButton(QWidget *parent, BezelStyle) : QWidget(parent) +{ + QAbstractButton *button = 0; + if (qobject_cast(parent)) + button = new QToolButton(this); + else + button = new QPushButton(this); + connect(button, SIGNAL(clicked()), + this, SIGNAL(clicked())); + pimpl = new QButtonPrivate(this, button); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(button); +} + +void QButton::setText(const QString &text) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setText(text); +} + +void QButton::setImage(const QPixmap &image) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setIcon(image); +} + +void QButton::setChecked(bool checked) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setChecked(checked); +} + +void QButton::setCheckable(bool checkable) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setCheckable(checkable); +} + +bool QButton::isChecked() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return false; + + return pimpl->abstractButton->isChecked(); +} diff --git a/src/libtomahawk/thirdparty/Qocoa/qbutton.h b/src/libtomahawk/thirdparty/Qocoa/qbutton.h new file mode 100644 index 000000000..591c37ffc --- /dev/null +++ b/src/libtomahawk/thirdparty/Qocoa/qbutton.h @@ -0,0 +1,51 @@ +#ifndef QBUTTON_H +#define QBUTTON_H + +#include +#include + +#include "DllMacro.h" + +class QButtonPrivate; +class DLLEXPORT QButton : public QWidget +{ + Q_OBJECT +public: + // Matches NSBezelStyle + enum BezelStyle { + Rounded = 1, + RegularSquare = 2, + Disclosure = 5, + ShadowlessSquare = 6, + Circular = 7, + TexturedSquare = 8, + HelpButton = 9, + SmallSquare = 10, + TexturedRounded = 11, + RoundRect = 12, + Recessed = 13, + RoundedDisclosure = 14, +#ifdef __MAC_10_7 + Inline = 15 +#endif + }; + + explicit QButton(QWidget *parent, BezelStyle bezelStyle = Rounded); + +public slots: + void setText(const QString &text); + void setImage(const QPixmap &image); + void setChecked(bool checked); + +public: + void setCheckable(bool checkable); + bool isChecked(); + +signals: + void clicked(bool checked = false); + +private: + friend class QButtonPrivate; + QPointer pimpl; +}; +#endif // QBUTTON_H diff --git a/src/libtomahawk/thirdparty/Qocoa/qbutton_mac.mm b/src/libtomahawk/thirdparty/Qocoa/qbutton_mac.mm new file mode 100644 index 000000000..9dfdf9672 --- /dev/null +++ b/src/libtomahawk/thirdparty/Qocoa/qbutton_mac.mm @@ -0,0 +1,232 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qbutton.h" + +#include "qocoa_mac.h" + +#import "Foundation/NSAutoreleasePool.h" +#import "AppKit/NSButton.h" +#import "AppKit/NSFont.h" + +class QButtonPrivate : public QObject +{ +public: + QButtonPrivate(QButton *qButton, NSButton *nsButton, QButton::BezelStyle bezelStyle) + : QObject(qButton), qButton(qButton), nsButton(nsButton) + { + switch(bezelStyle) { + case QButton::Disclosure: + case QButton::Circular: +#ifdef __MAC_10_7 + case QButton::Inline: +#endif + case QButton::RoundedDisclosure: + case QButton::HelpButton: + [nsButton setTitle:@""]; + default: + break; + } + + NSFont* font = 0; + switch(bezelStyle) { + case QButton::RoundRect: + font = [NSFont fontWithName:@"Lucida Grande" size:12]; + break; + + case QButton::Recessed: + font = [NSFont fontWithName:@"Lucida Grande Bold" size:12]; + break; + +#ifdef __MAC_10_7 + case QButton::Inline: + font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; + break; +#endif + + default: + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; + break; + } + [nsButton setFont:font]; + + switch(bezelStyle) { + case QButton::Rounded: + qButton->setMinimumWidth(40); + qButton->setFixedHeight(24); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case QButton::RegularSquare: + case QButton::TexturedSquare: + qButton->setMinimumSize(14, 23); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::ShadowlessSquare: + qButton->setMinimumSize(5, 25); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::SmallSquare: + qButton->setMinimumSize(4, 21); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::TexturedRounded: + qButton->setMinimumSize(10, 22); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::RoundRect: + case QButton::Recessed: + qButton->setMinimumWidth(16); + qButton->setFixedHeight(18); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case QButton::Disclosure: + qButton->setMinimumWidth(13); + qButton->setFixedHeight(13); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case QButton::Circular: + qButton->setMinimumSize(16, 16); + qButton->setMaximumHeight(40); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::HelpButton: + case QButton::RoundedDisclosure: + qButton->setMinimumWidth(22); + qButton->setFixedHeight(22); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; +#ifdef __MAC_10_7 + case QButton::Inline: + qButton->setMinimumWidth(10); + qButton->setFixedHeight(16); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; +#endif + } + + switch(bezelStyle) { + case QButton::Recessed: + [nsButton setButtonType:NSPushOnPushOffButton]; + case QButton::Disclosure: + [nsButton setButtonType:NSOnOffButton]; + default: + [nsButton setButtonType:NSMomentaryPushInButton]; + } + + [nsButton setBezelStyle:bezelStyle]; + } + + void clicked() + { + emit qButton->clicked(qButton->isChecked()); + } + + ~QButtonPrivate() { + [[nsButton target] release]; + [nsButton setTarget:nil]; + } + + QButton *qButton; + NSButton *nsButton; +}; + +@interface QButtonTarget : NSObject +{ +@public + QPointer pimpl; +} +-(void)clicked; +@end + +@implementation QButtonTarget +-(void)clicked { + Q_ASSERT(pimpl); + if (pimpl) + pimpl->clicked(); +} +@end + +QButton::QButton(QWidget *parent, BezelStyle bezelStyle) : QWidget(parent) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSButton *button = [[NSButton alloc] init]; + pimpl = new QButtonPrivate(this, button, bezelStyle); + + QButtonTarget *target = [[QButtonTarget alloc] init]; + target->pimpl = pimpl; + [button setTarget:target]; + + [button setAction:@selector(clicked)]; + + setupLayout(button, this); + + [button release]; + + [pool drain]; +} + +void QButton::setText(const QString &text) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pimpl->nsButton setTitle:fromQString(text)]; + [pool drain]; +} + +void QButton::setImage(const QPixmap &image) +{ + Q_ASSERT(pimpl); + if (pimpl) + [pimpl->nsButton setImage:fromQPixmap(image)]; +} + +void QButton::setChecked(bool checked) +{ + Q_ASSERT(pimpl); + if (pimpl) + [pimpl->nsButton setState:checked]; +} + +void QButton::setCheckable(bool checkable) +{ + const NSInteger cellMask = checkable ? NSChangeBackgroundCellMask : NSNoCellMask; + + Q_ASSERT(pimpl); + if (pimpl) + [[pimpl->nsButton cell] setShowsStateBy:cellMask]; +} + +bool QButton::isChecked() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return false; + + return [pimpl->nsButton state]; +} + +#include "moc_qbutton.cpp" + diff --git a/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h b/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h index ee620a3a5..053f57a06 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h +++ b/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h @@ -32,7 +32,7 @@ static inline NSString* fromQString(const QString &string) { const QByteArray utf8 = string.toUtf8(); const char* cString = utf8.constData(); - return [[NSString alloc] initWithUTF8String:cString]; + return [[[NSString alloc] initWithUTF8String:cString] autorelease]; } static inline QString toQString(NSString *string) @@ -45,7 +45,7 @@ static inline QString toQString(NSString *string) static inline NSImage* fromQPixmap(const QPixmap &pixmap) { CGImageRef cgImage = pixmap.toMacCGImageRef(); - return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; + return [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease]; } static inline void setupLayout(void *cocoaView, QWidget *parent) diff --git a/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog.cpp b/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog.cpp new file mode 100644 index 000000000..4d7419915 --- /dev/null +++ b/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog.cpp @@ -0,0 +1,174 @@ +#include "qtoolbartabdialog.h" + +#include +#include +#include +#include +#include +#include +#include + +class QToolbarTabDialogPrivate : public QObject { + Q_OBJECT +public: + QToolbarTabDialogPrivate(QToolbarTabDialog* qq) : q(qq), layout(0), toolbar(0), rightSpacer(0), stack(0), separator(0), buttons(0), actionGroup(0) {} + +public slots: + void actionTriggered(QAction* action) { + if (dialog.isNull()) + return; + + const int idx = toolbar->actions().indexOf(action); + // There's a left spacer, so we want 1 less + Q_ASSERT(idx > 0); + if (idx < 1) + return; + + stack->setCurrentIndex(idx - 1); + } + + void accepted() { + Q_ASSERT(!dialog.isNull()); + Q_ASSERT(!q.isNull()); + + dialog.data()->hide(); + emit q.data()->accepted(); + } + + void rejected() { + Q_ASSERT(!dialog.isNull()); + Q_ASSERT(!q.isNull()); + + dialog.data()->hide(); + emit q.data()->rejected(); + } + +public: + QWeakPointer dialog; + QWeakPointer q; + + QVBoxLayout* layout; + QToolBar* toolbar; + QAction* rightSpacer; + QStackedWidget* stack; + QFrame* separator; + QDialogButtonBox* buttons; + QActionGroup* actionGroup; + +}; + +QToolbarTabDialog::QToolbarTabDialog() : + QObject(0), + pimpl(new QToolbarTabDialogPrivate(this)) +{ + pimpl->dialog = new QDialog; + pimpl->dialog.data()->setModal(true); + + pimpl->toolbar = new QToolBar(pimpl->dialog.data()); + pimpl->toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + + pimpl->stack = new QStackedWidget(pimpl->dialog.data()); + + pimpl->separator = new QFrame(pimpl->dialog.data()); + pimpl->separator->setFrameShape(QFrame::HLine); + pimpl->separator->setFrameShadow(QFrame::Sunken); + + pimpl->actionGroup = new QActionGroup(pimpl->dialog.data()); + + connect(pimpl->toolbar, SIGNAL(actionTriggered(QAction*)), pimpl.data(), SLOT(actionTriggered(QAction*))); + + pimpl->buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, pimpl->dialog.data()); + connect(pimpl->buttons, SIGNAL(accepted()), pimpl->dialog.data(), SLOT(accept())); + connect(pimpl->buttons, SIGNAL(rejected()), pimpl->dialog.data(), SLOT(reject())); + + connect(pimpl->dialog.data(), SIGNAL(accepted()), pimpl.data(), SLOT(accepted())); + connect(pimpl->dialog.data(), SIGNAL(rejected()), pimpl.data(), SLOT(rejected())); + + QWidget* leftSpacer = new QWidget(pimpl->toolbar); + leftSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + QWidget* rightSpacer = new QWidget(pimpl->toolbar); + rightSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + pimpl->toolbar->addWidget(leftSpacer); + pimpl->rightSpacer = pimpl->toolbar->addWidget(rightSpacer); + + pimpl->layout = new QVBoxLayout; + pimpl->layout->setContentsMargins( 4, 4, 4, 4 ); + pimpl->layout->addWidget(pimpl->toolbar); + pimpl->layout->addWidget(pimpl->separator); + pimpl->layout->addWidget(pimpl->stack); + pimpl->layout->addWidget(pimpl->buttons); + pimpl->dialog.data()->setLayout(pimpl->layout); +} + +QToolbarTabDialog::~QToolbarTabDialog() +{ + if (pimpl && !pimpl->dialog.isNull()) { + delete pimpl->dialog.data(); + } +} + +void QToolbarTabDialog::addTab(QWidget* page, const QPixmap& icon, const QString& label, const QString& tooltip) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + pimpl->toolbar->removeAction(pimpl->rightSpacer); + + QAction* action = new QAction(icon, label, pimpl->toolbar); + action->setCheckable(true); + action->setToolTip(tooltip); + + pimpl->actionGroup->addAction(action); + + pimpl->toolbar->addAction(action); + pimpl->stack->addWidget(page); + + pimpl->toolbar->addAction(pimpl->rightSpacer); +} + +void QToolbarTabDialog::setCurrentIndex(int index) +{ + Q_ASSERT(pimpl); + if (!pimpl || pimpl->dialog.isNull()) + return; + + + Q_ASSERT(index < pimpl->toolbar->actions().length() + 1); + Q_ASSERT(index < pimpl->stack->count()); + if (index < 0 || index > pimpl->toolbar->actions().length()) + return; + if (index > pimpl->stack->count()) + return; + + if (pimpl->stack->currentIndex() != index) + pimpl->stack->setCurrentIndex(index); + + // 1 spacer item before the first action + QAction* toCheck = pimpl->toolbar->actions().at(index + 1); + if (pimpl->actionGroup->checkedAction() != toCheck) + toCheck->setChecked(true); +} + +void QToolbarTabDialog::show() +{ + Q_ASSERT(pimpl); + Q_ASSERT(!pimpl->dialog.isNull()); + if (!pimpl || pimpl->dialog.isNull()) + return; + + pimpl->dialog.data()->show(); +} + +void QToolbarTabDialog::hide() +{ + Q_ASSERT(pimpl); + Q_ASSERT(!pimpl->dialog.isNull()); + if (!pimpl || pimpl->dialog.isNull()) + return; + + pimpl->dialog.data()->hide(); +} + +#include "qtoolbartabdialog.moc" diff --git a/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog.h b/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog.h new file mode 100644 index 000000000..d1e9aee74 --- /dev/null +++ b/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2012 by Leo Franchi + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#ifndef QTOOLBARTABWIDGET_H +#define QTOOLBARTABWIDGET_H + +#include +#include +#include + +#include "DllMacro.h" + +class QToolbarTabDialogPrivate; + +class QAction; + +/** + Dialog with a toolbar that behaves like a tab widget. + + Note that on OS X there are no OK/Cancel buttons, every setting should be applied immediately. + The accepted() signal will be emitted on close/hide regardless. + */ +class DLLEXPORT QToolbarTabDialog : public QObject +{ + Q_OBJECT +public: + QToolbarTabDialog(); + virtual ~QToolbarTabDialog(); + + /* + * If the given widget has a QSizePolicy of Fixed in either direction, the dialog will not be resizable in that + * direction. + */ + void addTab(QWidget* page, const QPixmap& icon, const QString& label, const QString& tooltip = QString()); + + void setCurrentIndex(int index); + + void show(); + void hide(); + +Q_SIGNALS: + void accepted(); + void rejected(); + +private: + QScopedPointer pimpl; + + friend class ::QToolbarTabDialogPrivate; +}; + +#endif // QTOOLBARTABWIDGET_H diff --git a/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog_mac.mm b/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog_mac.mm new file mode 100644 index 000000000..7c5fbced2 --- /dev/null +++ b/src/libtomahawk/thirdparty/Qocoa/qtoolbartabdialog_mac.mm @@ -0,0 +1,461 @@ +/* + Copyright (C) 2012 by Leo Franchi + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#include "qtoolbartabdialog.h" + +#include "qocoa_mac.h" + +#import +#import + +#include +#include +#include +#include +#include +#include + +struct ItemData { + QPixmap icon; + QString text, tooltip; + QMacNativeWidget* nativeWidget; + QWidget* page; +}; + + +namespace { + +static const int TOOLBAR_ITEM_WIDTH = 32; + +CGFloat ToolbarHeightForWindow(NSWindow *window) +{ + CGFloat toolbarHeight = 0.0f; + + NSToolbar *toolbar = toolbar = [window toolbar]; + + if(toolbar && [toolbar isVisible]) + { + NSRect windowFrame = [NSWindow contentRectForFrameRect:[window frame] + styleMask:[window styleMask]]; + toolbarHeight = NSHeight(windowFrame) - NSHeight([[window contentView] frame]); + } + + return toolbarHeight; +} + +}; + +@interface ToolbarDelegate : NSObject +{ + QToolbarTabDialogPrivate *pimpl; +} +// Internal +-(void)setPrivate:(QToolbarTabDialogPrivate*)withPimpl; + +// NSToolbarItem action +-(void)changePanes:(id)sender; + +// NSToolbarDelegate +-(NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted; +-(NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar; +-(NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar; +-(NSArray*) toolbarSelectableItemIdentifiers: (NSToolbar*)toolbar; + +// NSWindowDelegate +-(NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize; +-(void)windowWillClose:(NSNotification *)notification; +@end + +class QToolbarTabDialogPrivate { +public: + QToolbarTabDialogPrivate(QToolbarTabDialog* dialog) : q(dialog), + currentPane(NULL), + minimumWidth(0) + { + } + + ~QToolbarTabDialogPrivate() { + // unset the delegate and toolbar from the window and manually release them + // otherwise, for some reason the old delegate is laying around when we + // create a new NSWindow + [[prefsWindow toolbar] setDelegate:NULL]; + [prefsWindow setToolbar:NULL]; + [prefsWindow release]; + [toolBar release]; + [toolBarDelegate release]; + } + + void calculateSize() { + NSRect windowFrame = [prefsWindow frame]; + + while ([[toolBar visibleItems] count] < [[toolBar items] count]) { + //Each toolbar item is 32x32; we expand by one toolbar item width repeatedly until they all fit + windowFrame.origin.x -= TOOLBAR_ITEM_WIDTH / 2; + windowFrame.size.width += TOOLBAR_ITEM_WIDTH / 2; + + [prefsWindow setFrame:windowFrame display:NO]; + [prefsWindow setMinSize: windowFrame.size]; + } + minimumWidth = windowFrame.size.width; + } + + void showPaneWithIdentifier(NSString* ident) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + const QString identStr = toQString(ident); + Q_ASSERT(items.contains(identStr)); + if (!items.contains(identStr)) + return; + + QWidget* newWidget = items[identStr].nativeWidget; + Q_ASSERT(newWidget); + if (!newWidget) + return; + + QWidget* newPage = items[identStr].page; + Q_ASSERT(newPage); + if (!newPage) + return; + + const NSRect oldFrame = [[prefsWindow contentView] frame]; + + // Clear first responder on window and set a temporary NSView on the window + // while we change the widget out underneath + [prefsWindow makeFirstResponder:nil]; + + NSView *tempView = [[NSView alloc] initWithFrame:[[prefsWindow contentView] frame]]; + [prefsWindow setContentView:tempView]; + [tempView release]; + + QSize sizeToUse = newPage->sizeHint().isNull() ? newPage->size() : newPage->sizeHint(); + sizeToUse.setWidth(qMax(sizeToUse.width(), newPage->minimumWidth())); + sizeToUse.setHeight(qMax(sizeToUse.height(), newPage->minimumHeight())); + + static const int spacing = 4; + + [prefsWindow setMinSize:NSMakeSize(sizeToUse.width(), sizeToUse.height())]; + + // Make room for the new view + NSRect newFrame = [prefsWindow frame]; + newFrame.size.height = sizeToUse.height() + ([prefsWindow frame].size.height - [[prefsWindow contentView] frame].size.height) + spacing; + newFrame.size.width = sizeToUse.width() + spacing; + + // Don't resize the width---only the height, so use the maximum width for any page + // or the same width as before, if the user already resized it to be larger. + newFrame.size.width < minimumWidth ? newFrame.size.width = minimumWidth + : newFrame.size.width = qMax(newFrame.size.width, oldFrame.size.width); + + // Preserve upper left point of window during resize. + newFrame.origin.y += ([[prefsWindow contentView] frame].size.height - sizeToUse.height()) - spacing; + + [prefsWindow setFrame:newFrame display:YES animate:YES]; + + [prefsWindow setContentView: [panes objectForKey:ident]]; + currentPane = ident; + + // Resize the Qt widget immediately as well + resizeCurrentPageToSize([[prefsWindow contentView] frame].size); + + NSSize minSize = [prefsWindow frame].size; + minSize.height -= ToolbarHeightForWindow(prefsWindow); + + [prefsWindow setMinSize:minSize]; + + BOOL canResize = YES; + NSSize maxSize = NSMakeSize(FLT_MAX, FLT_MAX); + + if (newPage->sizePolicy().horizontalPolicy() == QSizePolicy::Fixed) { + canResize = NO; + maxSize.width = sizeToUse.width(); + } + if (newPage->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) { + canResize = NO; + maxSize.height = sizeToUse.height(); + } + + + [prefsWindow setMaxSize:maxSize]; + [prefsWindow setShowsResizeIndicator:canResize]; + + [prefsWindow setTitle:ident]; + [prefsWindow makeFirstResponder:[panes objectForKey:ident]]; + + [pool drain]; + } + + void resizeCurrentPageToSize(NSSize frameSize) { + const QString curPane = toQString(currentPane); + if (items.contains(curPane) && items[curPane].nativeWidget) { + items[curPane].nativeWidget->resize(frameSize.width, frameSize.height); + } + } + + void emitAccepted() { + if (q.isNull()) + return; + + q.data()->accepted(); + } + + QWeakPointer q; + + NSWindow* prefsWindow; + ToolbarDelegate *toolBarDelegate; + QMap items; + + NSMutableDictionary *panes; + NSToolbar *toolBar; + NSString* currentPane; + + int minimumWidth; +}; + + +@implementation ToolbarDelegate + +-(id) init { + if( self = [super init] ) + { + pimpl = nil; + } + + return self; +} + +-(void) setPrivate:(QToolbarTabDialogPrivate *)withPimpl +{ + pimpl = withPimpl; +} + +-(void)changePanes:(id)sender +{ + Q_UNUSED(sender); + if (!pimpl) + return; + + pimpl->showPaneWithIdentifier([pimpl->toolBar selectedItemIdentifier]); +} + +-(NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted +{ + Q_UNUSED(toolbar); + Q_UNUSED(willBeInserted); + if (!pimpl) + return nil; + + NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease]; + const QString identQStr = toQString(itemIdent); + if (pimpl->items.contains(identQStr)) + { + const ItemData& data = pimpl->items[identQStr]; + NSString* label = fromQString(data.text); + + [toolbarItem setLabel:label]; + [toolbarItem setPaletteLabel:label]; + + [toolbarItem setToolTip:fromQString(data.tooltip)]; + [toolbarItem setImage:fromQPixmap(data.icon)]; + + [toolbarItem setTarget: self]; + [toolbarItem setAction: @selector(changePanes:)]; + + } else { + toolbarItem = nil; + } + + return toolbarItem; +} + +-(NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar +{ + Q_UNUSED(toolbar); + if (!pimpl) + return [NSArray array]; + + NSMutableArray* allowedItems = [[[NSMutableArray alloc] init] autorelease]; + + Q_FOREACH( const QString& identQStr, pimpl->items.keys()) + [allowedItems addObject:fromQString(identQStr)]; + + [allowedItems addObjectsFromArray:[NSArray arrayWithObjects:NSToolbarSeparatorItemIdentifier, + NSToolbarSpaceItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarCustomizeToolbarItemIdentifier, nil] ]; + + return allowedItems; +} + + +-(NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar +{ + Q_UNUSED(toolbar); + if (!pimpl) + return [NSArray array]; + + return [[[NSMutableArray alloc] initWithArray:[pimpl->panes allKeys]] autorelease]; + +} + + +-(NSArray*) toolbarSelectableItemIdentifiers: (NSToolbar*)toolbar +{ + Q_UNUSED(toolbar); + if (!pimpl) + return [NSArray array]; + + return [[[NSMutableArray alloc] initWithArray:[pimpl->panes allKeys]] autorelease]; +} + +-(NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize +{ + if (!pimpl) + return frameSize; + + pimpl->resizeCurrentPageToSize([[sender contentView] frame].size); + + return frameSize; +} + +-(void)windowWillClose:(NSNotification *)notification +{ + Q_UNUSED(notification); + + pimpl->emitAccepted(); +} +@end + +QToolbarTabDialog::QToolbarTabDialog() : + QObject(0), + pimpl(new QToolbarTabDialogPrivate(this)) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + pimpl->panes = [[NSMutableDictionary alloc] init]; + + static const int defaultWidth = 350; + static const int defaultHeight = 200; + pimpl->prefsWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, defaultWidth, defaultHeight) + styleMask:NSClosableWindowMask | NSResizableWindowMask | NSTitledWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + + [pimpl->prefsWindow setReleasedWhenClosed:NO]; + [pimpl->prefsWindow setTitle:@"Preferences"]; + + // identifier is some app-unique string, since all toolbars in an app share state. make this unique to this app's preferences window + pimpl->toolBar = [[NSToolbar alloc] initWithIdentifier:[NSString stringWithFormat:@"%@.prefspanel.toolbar", fromQString(QCoreApplication::instance()->applicationName())]]; + [pimpl->toolBar setAllowsUserCustomization: NO]; + [pimpl->toolBar setAutosavesConfiguration: NO]; + [pimpl->toolBar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; + + pimpl->toolBarDelegate = [[ToolbarDelegate alloc] init]; + [pimpl->toolBarDelegate setPrivate:pimpl.data()]; + + [pimpl->prefsWindow setDelegate:pimpl->toolBarDelegate]; + [pimpl->toolBar setDelegate:pimpl->toolBarDelegate]; + + [pimpl->prefsWindow setToolbar:pimpl->toolBar]; + + [pool drain]; +} + +QToolbarTabDialog::~QToolbarTabDialog() +{ +} + +void QToolbarTabDialog::addTab(QWidget* page, const QPixmap& icon, const QString& label, const QString& tooltip) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString* identifier = fromQString(label); + + QMacNativeWidget* nativeWidget = new QMacNativeWidget; + nativeWidget->move(0, 0); + nativeWidget->setPalette(page->palette()); + nativeWidget->setAutoFillBackground(true); + + QVBoxLayout* l = new QVBoxLayout; + l->setContentsMargins(2, 2, 2, 2); + l->setSpacing(0); + page->setAttribute(Qt::WA_LayoutUsesWidgetRect); + l->addWidget(page); + nativeWidget->setLayout(l); + + NSView *nativeView = reinterpret_cast(nativeWidget->winId()); + [nativeView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [nativeView setAutoresizesSubviews:YES]; + + pimpl->minimumWidth = qMax(pimpl->minimumWidth, page->sizeHint().width()); + + nativeWidget->show(); + + ItemData data; + data.icon = icon; + data.text = label; + data.tooltip = tooltip; + data.nativeWidget = nativeWidget; + data.page = page; + pimpl->items.insert(label, data); + + [pimpl->panes setObject:nativeView forKey:identifier]; + + pimpl->showPaneWithIdentifier(identifier); + + [pimpl->toolBar insertItemWithItemIdentifier:identifier atIndex:[[pimpl->toolBar items] count]]; + [pimpl->toolBar setSelectedItemIdentifier:identifier]; + [[pimpl->prefsWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO]; + + pimpl->calculateSize(); + [pool drain]; +} + + +void QToolbarTabDialog::setCurrentIndex(int index) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + [pimpl->toolBar setSelectedItemIdentifier:[[[pimpl->toolBar items] objectAtIndex:index] itemIdentifier]]; + pimpl->showPaneWithIdentifier([[[pimpl->toolBar items] objectAtIndex:index] itemIdentifier]); +} + +void QToolbarTabDialog::show() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + [pimpl->prefsWindow center]; + [pimpl->prefsWindow makeKeyAndOrderFront:nil]; +} + +void QToolbarTabDialog::hide() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + [pimpl->prefsWindow close]; + emit accepted(); +} + +#include "moc_qtoolbartabdialog.cpp"