Vidalia  0.2.15
ConfigDialog.cpp
Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
00007 **  including this file, may be copied, modified, propagated, or distributed 
00008 **  except according to the terms described in the LICENSE file.
00009 */
00010 
00011 /* 
00012 ** \file ConfigDialog.cpp
00013 ** \brief Contains a series of Vidalia and Tor configuration pages
00014 */
00015 
00016 #include "ConfigDialog.h"
00017 #include "GeneralPage.h"
00018 #include "NetworkPage.h"
00019 #include "ServerPage.h"
00020 #include "AdvancedPage.h"
00021 #include "AppearancePage.h"
00022 #include "ServicePage.h"
00023 #include "VMessageBox.h"
00024 #include "ServerSettings.h"
00025 #include "NetworkSettings.h"
00026 #include "Vidalia.h"
00027 
00028 #include "html.h"
00029 
00030 
00031 /* Images for toolbar icons */
00032 #define IMAGE_GENERAL       ":/images/32x32/preferences-system.png"
00033 #define IMAGE_NETWORK       ":/images/32x32/preferences-system-network.png"
00034 #define IMAGE_SERVER        ":/images/32x32/preferences-system-network-sharing.png"
00035 #define IMAGE_APPEARANCE    ":/images/32x32/preferences-desktop-locale.png"
00036 #define IMAGE_ADVANCED      ":/images/32x32/applications-system.png"
00037 #define IMAGE_HELP          ":/images/32x32/system-help.png"
00038 #define IMAGE_SERVICE       ":/images/32x32/services.png"
00039 
00040 
00041 /** Constructor */
00042 ConfigDialog::ConfigDialog(QWidget* parent)
00043 : VidaliaWindow("ConfigDialog", parent)
00044 {
00045   /* Invoke the Qt Designer generated QObject setup routine */
00046   ui.setupUi(this);
00047  
00048   /* Override the QDialogButtonBox button text so we can use our own
00049    * translations. */
00050   QPushButton *button = ui.buttonBox->button(QDialogButtonBox::Ok);
00051   if (button) {
00052     Vidalia::createShortcut(QKeySequence(Qt::Key_Return), 
00053                             this, button, SLOT(click()));
00054   }
00055   button = ui.buttonBox->button(QDialogButtonBox::Cancel);
00056   if (button) {
00057     Vidalia::createShortcut("Esc", this, button, SLOT(click()));
00058     Vidalia::createShortcut("Ctrl+W", this, button, SLOT(click()));
00059   }
00060   
00061   /* Connect the button box signals to the appropriate slots */
00062   connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(saveChanges()));
00063   connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(close()));
00064   connect(ui.buttonBox, SIGNAL(helpRequested()), this, SLOT(help()));
00065   connect(Vidalia::torControl(), SIGNAL(authenticated()),
00066                            this, SLOT(applyChanges()));
00067 
00068   /* Used to connect restartTor signals */
00069   AdvancedPage *advancedPage;
00070   /* Create the config pages and actions */
00071   QActionGroup *grp = new QActionGroup(this);
00072   GeneralPage *generalPage = new GeneralPage(ui.stackPages);
00073   ui.stackPages->add(generalPage,
00074                      createPageAction(QIcon(IMAGE_GENERAL),
00075                                       tr("General"), "General", grp));
00076   connect(generalPage, SIGNAL(checkForUpdates()),
00077           this, SLOT(onCheckForUpdates()));
00078 
00079   ui.stackPages->add(new NetworkPage(ui.stackPages),
00080                      createPageAction(QIcon(IMAGE_NETWORK),
00081                                       tr("Network"), "Network", grp));
00082 
00083   ui.stackPages->add(new ServerPage(ui.stackPages),
00084                      createPageAction(QIcon(IMAGE_SERVER),
00085                                       tr("Sharing"), "Sharing", grp));
00086 
00087   ui.stackPages->add(new ServicePage(ui.stackPages),
00088                      createPageAction(QIcon(IMAGE_SERVICE),
00089                                       tr("Services"), "Services", grp));
00090 
00091   ui.stackPages->add(new AppearancePage(ui.stackPages),
00092                      createPageAction(QIcon(IMAGE_APPEARANCE),
00093                                       tr("Appearance"), "Appearance", grp));
00094 
00095   ui.stackPages->add(advancedPage = new AdvancedPage(ui.stackPages),
00096                      createPageAction(QIcon(IMAGE_ADVANCED),
00097                                       tr("Advanced"), "Advanced", grp));
00098 
00099   connect(advancedPage, SIGNAL(restartTor()), this, SIGNAL(restartTor()));
00100 
00101   foreach (ConfigPage *page, ui.stackPages->pages()) {
00102     connect(page, SIGNAL(helpRequested(QString)),
00103             this, SLOT(help(QString)));
00104   }
00105 
00106   /* Create the toolbar */
00107   ui.toolBar->addActions(grp->actions());
00108   ui.toolBar->addSeparator();
00109   connect(grp, SIGNAL(triggered(QAction *)), 
00110           ui.stackPages, SLOT(showPage(QAction *)));
00111   
00112   /* Create and bind the Help button */
00113   QAction *helpAct = new QAction(QIcon(IMAGE_HELP), tr("Help"), ui.toolBar);
00114   helpAct->setData("Help");
00115   addAction(helpAct, SLOT(help()));
00116 
00117   /* Select the first action */
00118   grp->actions()[0]->setChecked(true);
00119 
00120 #if defined(Q_WS_WIN)
00121   helpAct->setShortcut(QString("F1"));
00122 #else
00123   helpAct->setShortcut(QString("Ctrl+?"));
00124 #endif
00125 }
00126 
00127 /** Creates a new action associated with a config page. */
00128 QAction*
00129 ConfigDialog::createPageAction(const QIcon &img, const QString &text, 
00130                                const QString &data, QActionGroup *group)
00131 {
00132   QAction *action = new QAction(img, text, group);
00133   action->setData(data);
00134   action->setCheckable(true);
00135   return action;
00136 }
00137 
00138 /** Adds the given action to the toolbar and hooks its triggered() signal to
00139  * the specified slot (if given). */
00140 void
00141 ConfigDialog::addAction(QAction *action, const char *slot)
00142 {
00143   ui.toolBar->addAction(action);
00144   connect(action, SIGNAL(triggered()), this, slot);
00145 }
00146 
00147 /** Shows the config dialog with focus set to the given page. */
00148 void
00149 ConfigDialog::showWindow(Page page)
00150 {
00151   /* Load saved settings */
00152   loadSettings();
00153   /* Show the dialog. */
00154   VidaliaWindow::showWindow();
00155   /* Set the focus to the specified page. */
00156   ui.stackPages->setCurrentIndex((int)page);
00157 }
00158 
00159 /** Called when the user changes the UI translation. */
00160 void
00161 ConfigDialog::retranslateUi()
00162 {
00163   ui.retranslateUi(this);
00164   foreach (ConfigPage *page, ui.stackPages->pages()) {
00165     page->retranslateUi();
00166   }
00167   foreach (QAction *action, ui.toolBar->actions()) {
00168     action->setText(tr(qPrintable(action->data().toString()), "ConfigDialog"));
00169   }
00170   ui.buttonBox->setStandardButtons(ui.buttonBox->standardButtons());
00171 }
00172 
00173 /** Loads the saved ConfigDialog settings. */
00174 void
00175 ConfigDialog::loadSettings()
00176 {
00177   /* Call each config page's load() method to load its data */
00178   foreach (ConfigPage *page, ui.stackPages->pages()) {
00179     page->load();
00180   }
00181 }
00182 
00183 /** Saves changes made to settings. If Tor is running and Vidalia is
00184  * connected, we will also attempt to apply the changes to Tor. */
00185 void
00186 ConfigDialog::saveChanges()
00187 {
00188   QString errmsg;
00189   
00190   /* Call each config page's save() method to save its data */
00191   foreach (ConfigPage *page, ui.stackPages->pages()) {
00192     if (!page->save(errmsg)) {
00193       /* Display the offending page */
00194       ui.stackPages->setCurrentPage(page);
00195       
00196       /* Show the user what went wrong */
00197       VMessageBox::warning(this, 
00198         tr("Error Saving Settings"), 
00199         p(tr("Vidalia was unable to save your %1 settings.")
00200              .arg(tr(qPrintable(page->title()), "ConfigDialog"))) + p(errmsg),
00201         VMessageBox::Ok);
00202 
00203       /* Don't process the rest of the pages */
00204       return;
00205     }
00206   }
00207   if (Vidalia::torControl()->isConnected())
00208     applyChanges();
00209   else
00210     close();
00211 }
00212 
00213 /** Called after Vidalia has authenticated to Tor and applies any changes
00214  * made since the last time they were applied. */
00215 void
00216 ConfigDialog::applyChanges()
00217 {
00218   QString errmsg;
00219   bool appliedChanges = false;
00220 
00221   foreach (ConfigPage *page, ui.stackPages->pages()) {
00222     if (!page->changedSinceLastApply())
00223       continue;
00224     if (!page->apply(errmsg)) {
00225       /* Failed to apply the changes to Tor */
00226       int ret = VMessageBox::warning(this,
00227                   tr("Error Applying Settings"),
00228                   p(tr("Vidalia was unable to apply your %1 settings "
00229                        "to Tor.").arg(page->title()))
00230                     + p(errmsg),
00231                   VMessageBox::ShowSettings|VMessageBox::Default,
00232                   VMessageBox::Cancel|VMessageBox::Escape);
00233       if (ret == VMessageBox::ShowSettings) {
00234         /* Show the user the page with the bad settings */
00235         showWindow();
00236         ui.stackPages->setCurrentPage(page);
00237       } else {
00238         /* The user clicked 'Cancel', so revert the failed settings */
00239         page->revert();
00240         close();
00241       }
00242       return;
00243     }
00244     appliedChanges = true;
00245   }
00246   if (appliedChanges)
00247     saveConf();      
00248   close();
00249 }
00250 
00251 /** Sends Tor a SAVECONF to write its configuration to disk. If the SAVECONF
00252  * is successful, then all settings are considered to be applied. */
00253 void
00254 ConfigDialog::saveConf()
00255 {
00256   TorControl *tc = Vidalia::torControl();
00257   if (tc->saveConf()) {
00258     ServerSettings(tc).setChanged(false);
00259     NetworkSettings(tc).setChanged(false);
00260     TorSettings(tc).setChanged(false);
00261   }
00262 }
00263 
00264 /** Shows help information for whichever settings page the user is currently
00265  * viewing. */
00266 void
00267 ConfigDialog::help()
00268 {
00269   Page currentPage = static_cast<Page>(ui.stackPages->currentIndex());
00270   
00271   switch (currentPage) {
00272     case Network:
00273       help("config.network"); break;
00274     case Server:
00275       help("server"); break;
00276     case Appearance:
00277       help("config.appearance"); break;
00278     case Advanced:
00279       help("config.advanced"); break;
00280     case Service:
00281       help("config.services"); break;
00282     default:
00283       help("config.general"); break;
00284   }
00285 }
00286 
00287 /** Stub method that relays the checkForUpdates() signal from the General
00288  * settings page to the owner of the config dialog (MainWindow). */
00289 void
00290 ConfigDialog::onCheckForUpdates()
00291 {
00292   emit checkForUpdates();
00293 }
00294 
00295 
00296 /** Called when a ConfigPage in the dialog requests help on a specific
00297  * <b>topic</b>. */
00298 void
00299 ConfigDialog::help(const QString &topic)
00300 {
00301   emit helpRequested(topic);
00302 }
00303