1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 6 * as published by the Free Software Foundation. 7 * 8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 9 * 10 * $Id: prefs-dialog.cc 13295 2012-05-09 17:37:14Z jordan $ 11 */ 12 13#include <cassert> 14#include <iostream> 15 16#include <QCheckBox> 17#include <QComboBox> 18#include <QCoreApplication> 19#include <QDialogButtonBox> 20#include <QDoubleSpinBox> 21#include <QFileIconProvider> 22#include <QFileInfo> 23#include <QHBoxLayout> 24#include <QHttp> 25#include <QIcon> 26#include <QLabel> 27#include <QLineEdit> 28#include <QList> 29#include <QMessageBox> 30#include <QPushButton> 31#include <QSpinBox> 32#include <QStyle> 33#include <QTabWidget> 34#include <QTime> 35#include <QTimeEdit> 36#include <QTimer> 37#include <QVBoxLayout> 38 39#include "formatter.h" 40#include "hig.h" 41#include "prefs.h" 42#include "prefs-dialog.h" 43#include "session.h" 44#include "utils.h" 45 46/*** 47**** 48***/ 49 50namespace 51{ 52 const char * PREF_KEY( "pref-key" ); 53}; 54 55void 56PrefsDialog :: checkBoxToggled( bool checked ) 57{ 58 const int key( sender( )->property( PREF_KEY ).toInt( ) ); 59 setPref( key, checked ); 60} 61 62QCheckBox * 63PrefsDialog :: checkBoxNew( const QString& text, int key ) 64{ 65 QCheckBox * box = new QCheckBox( text ); 66 box->setChecked( myPrefs.getBool( key ) ); 67 box->setProperty( PREF_KEY, key ); 68 connect( box, SIGNAL(toggled(bool)), this, SLOT(checkBoxToggled(bool))); 69 myWidgets.insert( key, box ); 70 return box; 71} 72 73void 74PrefsDialog :: enableBuddyWhenChecked( QCheckBox * box, QWidget * buddy ) 75{ 76 connect( box, SIGNAL(toggled(bool)), buddy, SLOT(setEnabled(bool)) ); 77 buddy->setEnabled( box->isChecked( ) ); 78} 79 80void 81PrefsDialog :: spinBoxEditingFinished() 82{ 83 const QObject * spin = sender(); 84 const int key = spin->property( PREF_KEY ).toInt( ); 85 const QDoubleSpinBox * d = qobject_cast<const QDoubleSpinBox*>( spin ); 86 if( d ) 87 setPref( key, d->value( ) ); 88 else 89 setPref( key, qobject_cast<const QSpinBox*>(spin)->value( ) ); 90} 91 92QSpinBox * 93PrefsDialog :: spinBoxNew( int key, int low, int high, int step ) 94{ 95 QSpinBox * spin = new QSpinBox( ); 96 spin->setRange( low, high ); 97 spin->setSingleStep( step ); 98 spin->setValue( myPrefs.getInt( key ) ); 99 spin->setProperty( PREF_KEY, key ); 100 connect( spin, SIGNAL(editingFinished()), this, SLOT(spinBoxEditingFinished())); 101 myWidgets.insert( key, spin ); 102 return spin; 103} 104 105QDoubleSpinBox * 106PrefsDialog :: doubleSpinBoxNew( int key, double low, double high, double step, int decimals ) 107{ 108 QDoubleSpinBox * spin = new QDoubleSpinBox( ); 109 spin->setRange( low, high ); 110 spin->setSingleStep( step ); 111 spin->setDecimals( decimals ); 112 spin->setValue( myPrefs.getDouble( key ) ); 113 spin->setProperty( PREF_KEY, key ); 114 connect( spin, SIGNAL(editingFinished()), this, SLOT(spinBoxEditingFinished())); 115 myWidgets.insert( key, spin ); 116 return spin; 117} 118 119void 120PrefsDialog :: timeEditingFinished( ) 121{ 122 QTimeEdit * e = qobject_cast<QTimeEdit*>(sender()); 123 if( e ) 124 { 125 const int key( e->property( PREF_KEY ).toInt( ) ); 126 const QTime time( e->time( ) ); 127 const int seconds( QTime().secsTo( time ) ); 128std::cerr << "setPref to " << (seconds/60) << " minutes" << std::endl; 129 setPref( key, seconds / 60 ); 130 } 131} 132QTimeEdit* 133PrefsDialog :: timeEditNew( int key ) 134{ 135 const int minutes( myPrefs.getInt( key ) ); 136 QTimeEdit * e = new QTimeEdit( ); 137 e->setDisplayFormat( QString::fromAscii( "hh:mm" ) ); 138 e->setProperty( PREF_KEY, key ); 139 e->setTime( QTime().addSecs( minutes * 60 ) ); 140 myWidgets.insert( key, e ); 141 connect( e, SIGNAL(editingFinished()), this, SLOT(timeEditingFinished()) ); 142 return e; 143} 144 145void 146PrefsDialog :: lineEditingFinished( ) 147{ 148 QLineEdit * e = qobject_cast<QLineEdit*>(sender()); 149 if( e && e->isModified( ) ) 150 { 151 const int key( e->property( PREF_KEY ).toInt( ) ); 152 const QString text( e->text() ); 153 setPref( key, text ); 154 } 155} 156QLineEdit* 157PrefsDialog :: lineEditNew( int key, int echoMode ) 158{ 159 QLineEdit * e = new QLineEdit( myPrefs.getString( key ) ); 160 e->setProperty( PREF_KEY, key ); 161 e->setEchoMode( QLineEdit::EchoMode( echoMode ) ); 162 myWidgets.insert( key, e ); 163 connect( e, SIGNAL(editingFinished()), this, SLOT(lineEditingFinished()) ); 164 return e; 165} 166 167/*** 168**** 169***/ 170 171QWidget * 172PrefsDialog :: createWebTab( Session& session ) 173{ 174 HIG * hig = new HIG( this ); 175 hig->addSectionTitle( tr( "Web Client" ) ); 176 QWidget * w; 177 QHBoxLayout * h = new QHBoxLayout( ); 178 QPushButton * b = new QPushButton( tr( "&Open web client" ) ); 179 connect( b, SIGNAL(clicked()), &session, SLOT(launchWebInterface()) ); 180 h->addWidget( b, 0, Qt::AlignRight ); 181 QWidget * l = checkBoxNew( tr( "&Enable web client" ), Prefs::RPC_ENABLED ); 182 myUnsupportedWhenRemote << l; 183 hig->addRow( l, h, 0 ); 184 l = hig->addRow( tr( "Listening &port:" ), w = spinBoxNew( Prefs::RPC_PORT, 0, 65535, 1 ) ); 185 myWebWidgets << l << w; 186 hig->addWideControl( w = checkBoxNew( tr( "Use &authentication" ), Prefs::RPC_AUTH_REQUIRED ) ); 187 myWebWidgets << w; 188 l = hig->addRow( tr( "&Username:" ), w = lineEditNew( Prefs::RPC_USERNAME ) ); 189 myWebAuthWidgets << l << w; 190 l = hig->addRow( tr( "Pass&word:" ), w = lineEditNew( Prefs::RPC_PASSWORD, QLineEdit::Password ) ); 191 myWebAuthWidgets << l << w; 192 hig->addWideControl( w = checkBoxNew( tr( "Only allow these IP a&ddresses to connect:" ), Prefs::RPC_WHITELIST_ENABLED ) ); 193 myWebWidgets << w; 194 l = hig->addRow( tr( "Addresses:" ), w = lineEditNew( Prefs::RPC_WHITELIST ) ); 195 myWebWhitelistWidgets << l << w; 196 myUnsupportedWhenRemote << myWebWidgets << myWebAuthWidgets << myWebWhitelistWidgets; 197 hig->finish( ); 198 return hig; 199} 200 201/*** 202**** 203***/ 204 205void 206PrefsDialog :: altSpeedDaysEdited( int i ) 207{ 208 const int value = qobject_cast<QComboBox*>(sender())->itemData(i).toInt(); 209 setPref( Prefs::ALT_SPEED_LIMIT_TIME_DAY, value ); 210} 211 212 213QWidget * 214PrefsDialog :: createSpeedTab( ) 215{ 216 QWidget *l, *r; 217 HIG * hig = new HIG( this ); 218 hig->addSectionTitle( tr( "Speed Limits" ) ); 219 const QString speed_K_str = Formatter::unitStr( Formatter::SPEED, Formatter::KB ); 220 221 l = checkBoxNew( tr( "Limit &download speed (%1):" ).arg( speed_K_str ), Prefs::DSPEED_ENABLED ); 222 r = spinBoxNew( Prefs::DSPEED, 0, INT_MAX, 5 ); 223 hig->addRow( l, r ); 224 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r ); 225 226 l = checkBoxNew( tr( "Limit &upload speed (%1):" ).arg( speed_K_str ), Prefs::USPEED_ENABLED ); 227 r = spinBoxNew( Prefs::USPEED, 0, INT_MAX, 5 ); 228 hig->addRow( l, r ); 229 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r ); 230 231 hig->addSectionDivider( ); 232 QHBoxLayout * h = new QHBoxLayout; 233 h->setSpacing( HIG :: PAD ); 234 QLabel * label = new QLabel; 235 label->setPixmap( QPixmap( QString::fromAscii( ":/icons/alt-limit-off.png" ) ) ); 236 label->setAlignment( Qt::AlignLeft|Qt::AlignVCenter ); 237 h->addWidget( label ); 238 label = new QLabel( tr( "Alternative Speed Limits" ) ); 239 label->setStyleSheet( QString::fromAscii( "font: bold" ) ); 240 label->setAlignment( Qt::AlignLeft|Qt::AlignVCenter ); 241 h->addWidget( label ); 242 hig->addSectionTitle( h ); 243 244 QString s = tr( "<small>Override normal speed limits manually or at scheduled times</small>" ); 245 hig->addWideControl( new QLabel( s ) ); 246 247 s = tr( "Limit do&wnload speed (%1):" ).arg( speed_K_str ); 248 r = spinBoxNew( Prefs :: ALT_SPEED_LIMIT_DOWN, 0, INT_MAX, 5 ); 249 hig->addRow( s, r ); 250 251 s = tr( "Limit u&pload speed (%1):" ).arg( speed_K_str ); 252 r = spinBoxNew( Prefs :: ALT_SPEED_LIMIT_UP, 0, INT_MAX, 5 ); 253 hig->addRow( s, r ); 254 255 QCheckBox * c = checkBoxNew( tr( "&Scheduled times:" ), Prefs::ALT_SPEED_LIMIT_TIME_ENABLED ); 256 h = new QHBoxLayout( ); 257 h->setSpacing( HIG::PAD ); 258 QWidget * w = timeEditNew( Prefs :: ALT_SPEED_LIMIT_TIME_BEGIN ); 259 h->addWidget( w, 1 ); 260 mySchedWidgets << w; 261 QLabel * nd = new QLabel( tr("&to") ); 262 h->addWidget( nd ); 263 mySchedWidgets << nd; 264 w = timeEditNew( Prefs :: ALT_SPEED_LIMIT_TIME_END ); 265 nd->setBuddy( w ); 266 h->addWidget( w, 1 ); 267 mySchedWidgets << w; 268 hig->addRow( c, h, 0 ); 269 270 s = tr( "&On days:" ); 271 QComboBox * box = new QComboBox; 272 const QIcon noIcon; 273 box->addItem( noIcon, tr( "Every Day" ), QVariant( TR_SCHED_ALL ) ); 274 box->addItem( noIcon, tr( "Weekdays" ), QVariant( TR_SCHED_WEEKDAY ) ); 275 box->addItem( noIcon, tr( "Weekends" ), QVariant( TR_SCHED_WEEKEND ) ); 276 box->addItem( noIcon, tr( "Sunday" ), QVariant( TR_SCHED_SUN ) ); 277 box->addItem( noIcon, tr( "Monday" ), QVariant( TR_SCHED_MON ) ); 278 box->addItem( noIcon, tr( "Tuesday" ), QVariant( TR_SCHED_TUES ) ); 279 box->addItem( noIcon, tr( "Wednesday" ), QVariant( TR_SCHED_WED ) ); 280 box->addItem( noIcon, tr( "Thursday" ), QVariant( TR_SCHED_THURS ) ); 281 box->addItem( noIcon, tr( "Friday" ), QVariant( TR_SCHED_FRI ) ); 282 box->addItem( noIcon, tr( "Saturday" ), QVariant( TR_SCHED_SAT ) ); 283 box->setCurrentIndex( box->findData( myPrefs.getInt( Prefs :: ALT_SPEED_LIMIT_TIME_DAY ) ) ); 284 connect( box, SIGNAL(activated(int)), this, SLOT(altSpeedDaysEdited(int)) ); 285 w = hig->addRow( s, box ); 286 mySchedWidgets << w << box; 287 288 hig->finish( ); 289 return hig; 290} 291 292/*** 293**** 294***/ 295 296QWidget * 297PrefsDialog :: createDesktopTab( ) 298{ 299 HIG * hig = new HIG( this ); 300 hig->addSectionTitle( tr( "Desktop" ) ); 301 302 hig->addWideControl( checkBoxNew( tr( "Show Transmission icon in the ¬ification area" ), Prefs::SHOW_TRAY_ICON ) ); 303 hig->addWideControl( checkBoxNew( tr( "Show &popup notifications" ), Prefs::SHOW_DESKTOP_NOTIFICATION ) ); 304 305 hig->finish( ); 306 return hig; 307} 308 309/*** 310**** 311***/ 312 313void 314PrefsDialog :: onPortTested( bool isOpen ) 315{ 316 myPortButton->setEnabled( true ); 317 myWidgets[Prefs::PEER_PORT]->setEnabled( true ); 318 myPortLabel->setText( isOpen ? tr( "Port is <b>open</b>" ) 319 : tr( "Port is <b>closed</b>" ) ); 320} 321 322void 323PrefsDialog :: onPortTest( ) 324{ 325 myPortLabel->setText( tr( "Testing TCP Port..." ) ); 326 myPortButton->setEnabled( false ); 327 myWidgets[Prefs::PEER_PORT]->setEnabled( false ); 328 mySession.portTest( ); 329} 330 331QWidget * 332PrefsDialog :: createNetworkTab( ) 333{ 334 HIG * hig = new HIG( this ); 335 hig->addSectionTitle( tr( "Incoming Peers" ) ); 336 337 QSpinBox * s = spinBoxNew( Prefs::PEER_PORT, 1, 65535, 1 ); 338 QHBoxLayout * h = new QHBoxLayout( ); 339 QPushButton * b = myPortButton = new QPushButton( tr( "Te&st Port" ) ); 340 QLabel * l = myPortLabel = new QLabel( tr( "Status unknown" ) ); 341 h->addWidget( l ); 342 h->addSpacing( HIG :: PAD_BIG ); 343 h->addWidget( b ); 344 h->setStretchFactor( l, 1 ); 345 connect( b, SIGNAL(clicked(bool)), this, SLOT(onPortTest())); 346 connect( &mySession, SIGNAL(portTested(bool)), this, SLOT(onPortTested(bool))); 347 348 hig->addRow( tr( "&Port for incoming connections:" ), s ); 349 hig->addRow( QString(), h, 0 ); 350 hig->addWideControl( checkBoxNew( tr( "Pick a &random port every time Transmission is started" ), Prefs :: PEER_PORT_RANDOM_ON_START ) ); 351 hig->addWideControl( checkBoxNew( tr( "Use UPnP or NAT-PMP port &forwarding from my router" ), Prefs::PORT_FORWARDING ) ); 352 353 hig->addSectionDivider( ); 354 hig->addSectionTitle( tr( "Limits" ) ); 355 hig->addRow( tr( "Maximum peers per &torrent:" ), spinBoxNew( Prefs::PEER_LIMIT_TORRENT, 1, FD_SETSIZE, 5 ) ); 356 hig->addRow( tr( "Maximum peers &overall:" ), spinBoxNew( Prefs::PEER_LIMIT_GLOBAL, 1, FD_SETSIZE, 5 ) ); 357 358 hig->addSectionDivider( ); 359 hig->addSectionTitle( tr( "Options" ) ); 360 361 QWidget * w; 362 hig->addWideControl( w = checkBoxNew( tr( "Enable &uTP for peer connections" ), Prefs::UTP_ENABLED ) ); 363 w->setToolTip( tr( "uTP is a tool for reducing network congestion." ) ); 364 365 hig->finish( ); 366 return hig; 367} 368 369/*** 370**** 371***/ 372 373void 374PrefsDialog :: onBlocklistDialogDestroyed( QObject * o ) 375{ 376 Q_UNUSED( o ); 377 378 myBlocklistDialog = 0; 379} 380 381void 382PrefsDialog :: onUpdateBlocklistCancelled( ) 383{ 384 disconnect( &mySession, SIGNAL(blocklistUpdated(int)), this, SLOT(onBlocklistUpdated(int))) ; 385 myBlocklistDialog->deleteLater( ); 386} 387 388void 389PrefsDialog :: onBlocklistUpdated( int n ) 390{ 391 myBlocklistDialog->setText( tr( "<b>Update succeeded!</b><p>Blocklist now has %Ln rules.", 0, n ) ); 392 myBlocklistDialog->setTextFormat( Qt::RichText ); 393} 394 395void 396PrefsDialog :: onUpdateBlocklistClicked( ) 397{ 398 myBlocklistDialog = new QMessageBox( QMessageBox::Information, 399 QString(), 400 tr( "<b>Update Blocklist</b><p>Getting new blocklist..." ), 401 QMessageBox::Close, 402 this ); 403 connect( myBlocklistDialog, SIGNAL(rejected()), this, SLOT(onUpdateBlocklistCancelled()) ); 404 connect( &mySession, SIGNAL(blocklistUpdated(int)), this, SLOT(onBlocklistUpdated(int))) ; 405 myBlocklistDialog->show( ); 406 mySession.updateBlocklist( ); 407} 408 409void 410PrefsDialog :: encryptionEdited( int i ) 411{ 412 const int value( qobject_cast<QComboBox*>(sender())->itemData(i).toInt( ) ); 413 setPref( Prefs::ENCRYPTION, value ); 414} 415 416QWidget * 417PrefsDialog :: createPrivacyTab( ) 418{ 419 QWidget * w; 420 HIG * hig = new HIG( this ); 421 422 hig->addSectionTitle( tr( "Blocklist" ) ); 423 424 QWidget * l = checkBoxNew( tr("Enable &blocklist:"), Prefs::BLOCKLIST_ENABLED ); 425 QWidget * e = lineEditNew( Prefs::BLOCKLIST_URL ); 426 myBlockWidgets << e; 427 hig->addRow( l, e ); 428 429 l = myBlocklistLabel = new QLabel( ); 430 myBlockWidgets << l; 431 w = new QPushButton( tr( "&Update" ) ); 432 connect( w, SIGNAL(clicked(bool)), this, SLOT(onUpdateBlocklistClicked())); 433 myBlockWidgets << w; 434 QHBoxLayout * h = new QHBoxLayout( ); 435 h->addWidget( l ); 436 h->addStretch( 1 ); 437 h->addWidget( w ); 438 hig->addWideControl( h ); 439 440 l = checkBoxNew( tr( "Enable &automatic updates" ), Prefs::BLOCKLIST_UPDATES_ENABLED ); 441 myBlockWidgets << l; 442 hig->addWideControl( l ); 443 444 hig->addSectionDivider( ); 445 hig->addSectionTitle( tr( "Privacy" ) ); 446 447 QComboBox * box = new QComboBox( ); 448 box->addItem( tr( "Allow encryption" ), 0 ); 449 box->addItem( tr( "Prefer encryption" ), 1 ); 450 box->addItem( tr( "Require encryption" ), 2 ); 451 myWidgets.insert( Prefs :: ENCRYPTION, box ); 452 connect( box, SIGNAL(activated(int)), this, SLOT(encryptionEdited(int))); 453 454 hig->addRow( tr( "&Encryption mode:" ), box ); 455 hig->addWideControl( w = checkBoxNew( tr( "Use PE&X to find more peers" ), Prefs::PEX_ENABLED ) ); 456 w->setToolTip( tr( "PEX is a tool for exchanging peer lists with the peers you're connected to." ) ); 457 hig->addWideControl( w = checkBoxNew( tr( "Use &DHT to find more peers" ), Prefs::DHT_ENABLED ) ); 458 w->setToolTip( tr( "DHT is a tool for finding peers without a tracker." ) ); 459 hig->addWideControl( w = checkBoxNew( tr( "Use &Local Peer Discovery to find more peers" ), Prefs::LPD_ENABLED ) ); 460 w->setToolTip( tr( "LPD is a tool for finding peers on your local network." ) ); 461 462 hig->finish( ); 463 updateBlocklistLabel( ); 464 return hig; 465} 466 467/*** 468**** 469***/ 470 471void 472PrefsDialog :: onScriptClicked( void ) 473{ 474 const QString title = tr( "Select \"Torrent Done\" Script" ); 475 const QString myPath = myPrefs.getString( Prefs::SCRIPT_TORRENT_DONE_FILENAME ); 476 const QString path = Utils::remoteFileChooser( this, title, myPath, false, mySession.isServer() ); 477 478 if( !path.isEmpty() ) 479 onLocationSelected( path, Prefs::SCRIPT_TORRENT_DONE_FILENAME ); 480} 481 482void 483PrefsDialog :: onIncompleteClicked( void ) 484{ 485 const QString title = tr( "Select Incomplete Directory" ); 486 const QString myPath = myPrefs.getString( Prefs::INCOMPLETE_DIR ); 487 const QString path = Utils::remoteFileChooser( this, title, myPath, true, mySession.isServer() ); 488 489 if( !path.isEmpty() ) 490 onLocationSelected( path, Prefs::INCOMPLETE_DIR ); 491} 492 493void 494PrefsDialog :: onWatchClicked( void ) 495{ 496 const QString title = tr( "Select Watch Directory" ); 497 const QString myPath = myPrefs.getString( Prefs::DIR_WATCH ); 498 const QString path = Utils::remoteFileChooser( this, title, myPath, true, true ); 499 500 if( !path.isEmpty() ) 501 onLocationSelected( path, Prefs::DIR_WATCH ); 502} 503 504void 505PrefsDialog :: onDestinationClicked( void ) 506{ 507 const QString title = tr( "Select Destination" ); 508 const QString myPath = myPrefs.getString( Prefs::DOWNLOAD_DIR ); 509 const QString path = Utils::remoteFileChooser( this, title, myPath, true, mySession.isServer() ); 510 511 if( !path.isEmpty() ) 512 onLocationSelected( path, Prefs::DOWNLOAD_DIR ); 513} 514 515void 516PrefsDialog :: onLocationSelected( const QString& path, int key ) 517{ 518 setPref( key, path ); 519} 520 521QWidget * 522PrefsDialog :: createTorrentsTab( ) 523{ 524 const int iconSize( style( )->pixelMetric( QStyle :: PM_SmallIconSize ) ); 525 const QFileIconProvider iconProvider; 526 const QIcon folderIcon = iconProvider.icon( QFileIconProvider::Folder ); 527 const QPixmap folderPixmap = folderIcon.pixmap( iconSize ); 528 const QIcon fileIcon = iconProvider.icon( QFileIconProvider::File ); 529 const QPixmap filePixmap = fileIcon.pixmap( iconSize ); 530 531 QWidget *l, *r; 532 HIG * hig = new HIG( this ); 533 hig->addSectionTitle( tr( "Adding" ) ); 534 535 hig->addWideControl( checkBoxNew( tr( "Show &options dialog" ), Prefs::OPTIONS_PROMPT ) ); 536 hig->addWideControl( checkBoxNew( tr( "&Start when added" ), Prefs::START ) ); 537 hig->addWideControl( checkBoxNew( tr( "Mo&ve .torrent file to the trash" ), Prefs::TRASH_ORIGINAL ) ); 538 539 l = checkBoxNew( tr( "Automatically &add torrents from:" ), Prefs::DIR_WATCH_ENABLED ); 540 QPushButton * b = myWatchButton = new QPushButton; 541 b->setIcon( folderPixmap ); 542 b->setStyleSheet( QString::fromAscii( "text-align: left; padding-left: 5; padding-right: 5" ) ); 543 connect( b, SIGNAL(clicked(bool)), this, SLOT(onWatchClicked(void)) ); 544 hig->addRow( l, b ); 545 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), b ); 546 547 hig->addSectionDivider( ); 548 hig->addSectionTitle( tr( "Seeding Limits" ) ); 549 550 l = checkBoxNew( tr( "Stop seeding at &ratio:" ), Prefs::RATIO_ENABLED ); 551 r = doubleSpinBoxNew( Prefs::RATIO, 0, INT_MAX, 0.5, 2 ); 552 hig->addRow( l, r ); 553 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r ); 554 555 l = checkBoxNew( tr( "Stop seeding if idle for &N minutes:" ), Prefs::IDLE_LIMIT_ENABLED ); 556 r = spinBoxNew( Prefs::IDLE_LIMIT, 1, INT_MAX, 5 ); 557 hig->addRow( l, r ); 558 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r ); 559 560 hig->finish( ); 561 return hig; 562} 563 564QWidget * 565PrefsDialog :: createDownloadTab( ) 566{ 567 const int iconSize( style( )->pixelMetric( QStyle :: PM_SmallIconSize ) ); 568 const QFileIconProvider iconProvider; 569 const QIcon folderIcon = iconProvider.icon( QFileIconProvider::Folder ); 570 const QPixmap folderPixmap = folderIcon.pixmap( iconSize ); 571 const QIcon fileIcon = iconProvider.icon( QFileIconProvider::File ); 572 const QPixmap filePixmap = fileIcon.pixmap( iconSize ); 573 574 QWidget *l; 575 HIG * hig = new HIG( this ); 576 hig->addSectionTitle( tr( "Location" ) ); 577 578 QPushButton * b = myDestinationButton = new QPushButton; 579 b->setIcon( folderPixmap ); 580 b->setStyleSheet( QString::fromAscii( "text-align: left; padding-left: 5; padding-right: 5" ) ); 581 connect( b, SIGNAL(clicked(bool)), this, SLOT(onDestinationClicked(void)) ); 582 hig->addRow( tr( "Save to &Location:" ), b ); 583 584 hig->addSectionDivider( ); 585 hig->addSectionTitle( tr( "Queue" ) ); 586 587 hig->addRow( tr( "Maximum active &downloads:" ), spinBoxNew( Prefs::DOWNLOAD_QUEUE_SIZE, 1, INT_MAX, 1 ) ); 588 hig->addRow( tr( "Downloads sharing data in the last N minutes are &active:" ), spinBoxNew( Prefs::QUEUE_STALLED_MINUTES, 1, INT_MAX, 10 ) ); 589 590 hig->addSectionDivider( ); 591 hig->addSectionTitle( tr( "Incomplete" ) ); 592 593 hig->addWideControl( checkBoxNew( tr( "Append \".&part\" to incomplete files' names" ), Prefs::RENAME_PARTIAL_FILES ) ); 594 595 l = myIncompleteCheckbox = checkBoxNew( tr( "Keep &incomplete files in:" ), Prefs::INCOMPLETE_DIR_ENABLED ); 596 b = myIncompleteButton = new QPushButton; 597 b->setIcon( folderPixmap ); 598 b->setStyleSheet( QString::fromAscii( "text-align: left; padding-left: 5; padding-right: 5" ) ); 599 connect( b, SIGNAL(clicked(bool)), this, SLOT(onIncompleteClicked(void)) ); 600 hig->addRow( myIncompleteCheckbox, b ); 601 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), b ); 602 603 l = myTorrentDoneScriptCheckbox = checkBoxNew( tr( "Call scrip&t when torrent is completed:" ), Prefs::SCRIPT_TORRENT_DONE_ENABLED ); 604 b = myTorrentDoneScriptButton = new QPushButton; 605 b->setIcon( filePixmap ); 606 b->setStyleSheet( QString::fromAscii( "text-align: left; padding-left: 5; padding-right: 5" ) ); 607 connect( b, SIGNAL(clicked(bool)), this, SLOT(onScriptClicked(void)) ); 608 hig->addRow( myTorrentDoneScriptCheckbox, b ); 609 enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), b ); 610 611 hig->finish( ); 612 return hig; 613} 614 615/*** 616**** 617***/ 618 619PrefsDialog :: PrefsDialog( Session& session, Prefs& prefs, QWidget * parent ): 620 QDialog( parent ), 621 myIsServer( session.isServer( ) ), 622 mySession( session ), 623 myPrefs( prefs ), 624 myLayout( new QVBoxLayout( this ) ) 625{ 626 setWindowTitle( tr( "Transmission Preferences" ) ); 627 628 QTabWidget * t = new QTabWidget( this ); 629 t->addTab( createTorrentsTab( ), tr( "Torrents" ) ); 630 t->addTab( createDownloadTab( ), tr( "Download" ) ); 631 t->addTab( createSpeedTab( ), tr( "Speed" ) ); 632 t->addTab( createPrivacyTab( ), tr( "Privacy" ) ); 633 t->addTab( createNetworkTab( ), tr( "Network" ) ); 634 t->addTab( createDesktopTab( ), tr( "Desktop" ) ); 635 t->addTab( createWebTab( session ), tr( "Web" ) ); 636 myLayout->addWidget( t ); 637 638 QDialogButtonBox * buttons = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this ); 639 connect( buttons, SIGNAL(rejected()), this, SLOT(close()) ); // "close" triggers rejected 640 myLayout->addWidget( buttons ); 641 QWidget::setAttribute( Qt::WA_DeleteOnClose, true ); 642 643 connect( &mySession, SIGNAL(sessionUpdated()), this, SLOT(sessionUpdated())); 644 645 QList<int> keys; 646 keys << Prefs :: RPC_ENABLED 647 << Prefs :: ALT_SPEED_LIMIT_ENABLED 648 << Prefs :: ALT_SPEED_LIMIT_TIME_ENABLED 649 << Prefs :: ENCRYPTION 650 << Prefs :: BLOCKLIST_ENABLED 651 << Prefs :: DIR_WATCH 652 << Prefs :: DOWNLOAD_DIR 653 << Prefs :: INCOMPLETE_DIR 654 << Prefs :: INCOMPLETE_DIR_ENABLED 655 << Prefs :: SCRIPT_TORRENT_DONE_FILENAME; 656 foreach( int key, keys ) 657 refreshPref( key ); 658 659 // if it's a remote session, disable the preferences 660 // that don't work in remote sessions 661 if( !myIsServer ) { 662 foreach( QWidget * w, myUnsupportedWhenRemote ) { 663 w->setToolTip( tr( "Not supported by remote sessions" ) ); 664 w->setEnabled( false ); 665 } 666 } 667} 668 669PrefsDialog :: ~PrefsDialog( ) 670{ 671} 672 673void 674PrefsDialog :: setPref( int key, const QVariant& v ) 675{ 676 myPrefs.set( key, v ); 677 refreshPref( key ); 678} 679 680/*** 681**** 682***/ 683 684void 685PrefsDialog :: sessionUpdated( ) 686{ 687 updateBlocklistLabel( ); 688} 689 690void 691PrefsDialog :: updateBlocklistLabel( ) 692{ 693 const int n = mySession.blocklistSize( ); 694 myBlocklistLabel->setText( tr( "<i>Blocklist contains %Ln rules</i>", 0, n ) ); 695} 696 697void 698PrefsDialog :: refreshPref( int key ) 699{ 700 switch( key ) 701 { 702 case Prefs :: RPC_ENABLED: 703 case Prefs :: RPC_WHITELIST_ENABLED: 704 case Prefs :: RPC_AUTH_REQUIRED: { 705 const bool enabled( myPrefs.getBool( Prefs::RPC_ENABLED ) ); 706 const bool whitelist( myPrefs.getBool( Prefs::RPC_WHITELIST_ENABLED ) ); 707 const bool auth( myPrefs.getBool( Prefs::RPC_AUTH_REQUIRED ) ); 708 foreach( QWidget * w, myWebWhitelistWidgets ) w->setEnabled( enabled && whitelist ); 709 foreach( QWidget * w, myWebAuthWidgets ) w->setEnabled( enabled && auth ); 710 foreach( QWidget * w, myWebWidgets ) w->setEnabled( enabled ); 711 break; 712 } 713 714 case Prefs :: ALT_SPEED_LIMIT_TIME_ENABLED: { 715 const bool enabled = myPrefs.getBool( key ); 716 foreach( QWidget * w, mySchedWidgets ) w->setEnabled( enabled ); 717 break; 718 } 719 720 case Prefs :: BLOCKLIST_ENABLED: { 721 const bool enabled = myPrefs.getBool( key ); 722 foreach( QWidget * w, myBlockWidgets ) w->setEnabled( enabled ); 723 break; 724 } 725 726 case Prefs :: DIR_WATCH: 727 myWatchButton->setText( QFileInfo(myPrefs.getString(Prefs::DIR_WATCH)).fileName() ); 728 break; 729 730 case Prefs :: SCRIPT_TORRENT_DONE_FILENAME: { 731 const QString path( myPrefs.getString( key ) ); 732 myTorrentDoneScriptButton->setText( QFileInfo(path).fileName() ); 733 break; 734 } 735 736 case Prefs :: PEER_PORT: 737 myPortLabel->setText( tr( "Status unknown" ) ); 738 myPortButton->setEnabled( true ); 739 break; 740 741 case Prefs :: DOWNLOAD_DIR: { 742 QString path( myPrefs.getString( key ) ); 743 myDestinationButton->setText( QFileInfo(path).fileName() ); 744 break; 745 } 746 747 case Prefs :: INCOMPLETE_DIR: { 748 QString path( myPrefs.getString( key ) ); 749 myIncompleteButton->setText( QFileInfo(path).fileName() ); 750 break; 751 } 752 753 case Prefs :: INCOMPLETE_DIR_ENABLED: { 754 const bool enabled = myPrefs.getBool( key ); 755 myIncompleteButton->setEnabled( enabled ); 756 break; 757 } 758 759 default: 760 break; 761 } 762 763 key2widget_t::iterator it( myWidgets.find( key ) ); 764 if( it != myWidgets.end( ) ) 765 { 766 QWidget * w( it.value( ) ); 767 QCheckBox * checkBox; 768 QSpinBox * spin; 769 QDoubleSpinBox * doubleSpin; 770 QTimeEdit * timeEdit; 771 QLineEdit * lineEdit; 772 773 if(( checkBox = qobject_cast<QCheckBox*>(w))) 774 { 775 checkBox->setChecked( myPrefs.getBool( key ) ); 776 } 777 else if(( spin = qobject_cast<QSpinBox*>(w))) 778 { 779 spin->setValue( myPrefs.getInt( key ) ); 780 } 781 else if(( doubleSpin = qobject_cast<QDoubleSpinBox*>(w))) 782 { 783 doubleSpin->setValue( myPrefs.getDouble( key ) ); 784 } 785 else if(( timeEdit = qobject_cast<QTimeEdit*>(w))) 786 { 787 const int minutes( myPrefs.getInt( key ) ); 788 timeEdit->setTime( QTime().addSecs( minutes * 60 ) ); 789 } 790 else if(( lineEdit = qobject_cast<QLineEdit*>(w))) 791 { 792 lineEdit->setText( myPrefs.getString( key ) ); 793 } 794 else if( key == Prefs::ENCRYPTION ) 795 { 796 QComboBox * comboBox( qobject_cast<QComboBox*>( w ) ); 797 const int index = comboBox->findData( myPrefs.getInt( key ) ); 798 comboBox->setCurrentIndex( index ); 799 } 800 } 801} 802 803bool 804PrefsDialog :: isAllowed( int key ) const 805{ 806 Q_UNUSED( key ); 807 808 return true; 809} 810