00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <QDomDocument>
00018 #include <QDir>
00019 #include <vidalia.h>
00020
00021 #include "helpbrowser.h"
00022
00023
00024 #define LEFT_PANE_INDEX 0
00025 #define NO_STRETCH 0
00026 #define MINIMUM_PANE_SIZE 1
00027
00028
00029 #define ELEMENT_CONTENTS "Contents"
00030 #define ELEMENT_TOPIC "Topic"
00031 #define ATTRIBUTE_TOPIC_ID "id"
00032 #define ATTRIBUTE_TOPIC_HTML "html"
00033 #define ATTRIBUTE_TOPIC_NAME "name"
00034 #define ATTRIBUTE_TOPIC_SECTION "section"
00035
00036
00037 #define ROLE_TOPIC_ID Qt::UserRole
00038 #define ROLE_TOPIC_QRC_PATH (Qt::UserRole+1)
00039
00040
00041
00042 HelpBrowser::HelpBrowser(QWidget *parent)
00043 : VidaliaWindow("HelpBrowser", parent)
00044 {
00045 VidaliaSettings settings;
00046
00047
00048 ui.setupUi(this);
00049 #if defined(Q_WS_MAC)
00050 ui.actionHome->setShortcut(QString("Shift+Ctrl+H"));
00051 #endif
00052
00053
00054 ui.actionClose->setShortcut(QString("Esc"));
00055 Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger()));
00056
00057
00058 ui.frmFind->setHidden(true);
00059
00060
00061
00062 QList<int> sizes;
00063 sizes.append(MINIMUM_PANE_SIZE);
00064 sizes.append(MINIMUM_PANE_SIZE);
00065 ui.splitter->setSizes(sizes);
00066 ui.splitter->setStretchFactor(LEFT_PANE_INDEX, NO_STRETCH);
00067
00068 connect(ui.treeContents,
00069 SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
00070 this, SLOT(contentsItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
00071
00072 connect(ui.treeSearch,
00073 SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
00074 this, SLOT(searchItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
00075
00076
00077 connect(ui.actionHome, SIGNAL(triggered()), ui.txtBrowser, SLOT(home()));
00078 connect(ui.actionBack, SIGNAL(triggered()), ui.txtBrowser, SLOT(backward()));
00079 connect(ui.actionForward, SIGNAL(triggered()), ui.txtBrowser, SLOT(forward()));
00080 connect(ui.txtBrowser, SIGNAL(backwardAvailable(bool)),
00081 ui.actionBack, SLOT(setEnabled(bool)));
00082 connect(ui.txtBrowser, SIGNAL(forwardAvailable(bool)),
00083 ui.actionForward, SLOT(setEnabled(bool)));
00084 connect(ui.btnFindNext, SIGNAL(clicked()), this, SLOT(findNext()));
00085 connect(ui.btnFindPrev, SIGNAL(clicked()), this, SLOT(findPrev()));
00086 connect(ui.btnSearch, SIGNAL(clicked()), this, SLOT(search()));
00087
00088
00089 loadContentsFromXml(":/help/" + language() + "/contents.xml");
00090
00091
00092 ui.treeContents->setCurrentItem(ui.treeContents->topLevelItem(0));
00093 ui.treeContents->setItemExpanded(ui.treeContents->topLevelItem(0), true);
00094 }
00095
00096
00097
00098 QString
00099 HelpBrowser::language()
00100 {
00101 QString lang = Vidalia::language();
00102 if (!QDir(":/help/" + lang).exists())
00103 lang = "en";
00104 return lang;
00105 }
00106
00107
00108 void
00109 HelpBrowser::loadContentsFromXml(QString xmlFile)
00110 {
00111 QString errorString;
00112 QFile file(xmlFile);
00113 QDomDocument document;
00114
00115
00116 if (!document.setContent(&file, true, &errorString)) {
00117 ui.txtBrowser->setPlainText(tr("Error Loading Help Contents: ")+errorString);
00118 return;
00119 }
00120
00121 if (!loadContents(&document, errorString)) {
00122 ui.txtBrowser->setPlainText(tr("Error Loading Help Contents: ")+errorString);
00123 return;
00124 }
00125 }
00126
00127
00128 bool
00129 HelpBrowser::loadContents(const QDomDocument *document, QString &errorString)
00130 {
00131
00132 QDomElement root = document->documentElement();
00133 if (root.tagName() != ELEMENT_CONTENTS) {
00134 errorString = tr("Supplied XML file is not a valid Contents document.");
00135 return false;
00136 }
00137 _elementList << root;
00138
00139
00140 QTreeWidgetItem *home = createTopicTreeItem(root, 0);
00141 ui.treeContents->addTopLevelItem(home);
00142
00143
00144 QDomElement child = root.firstChildElement(ELEMENT_TOPIC);
00145 while (!child.isNull()) {
00146 parseHelpTopic(child, home);
00147 child = child.nextSiblingElement(ELEMENT_TOPIC);
00148 }
00149 return true;
00150 }
00151
00152
00153 void
00154 HelpBrowser::parseHelpTopic(const QDomElement &topicElement,
00155 QTreeWidgetItem *parent)
00156 {
00157
00158 if (isValidTopicElement(topicElement)) {
00159
00160 _elementList << topicElement;
00161
00162
00163 QTreeWidgetItem *topic = createTopicTreeItem(topicElement, parent);
00164
00165
00166 QDomElement child = topicElement.firstChildElement(ELEMENT_TOPIC);
00167 while (!child.isNull()) {
00168 parseHelpTopic(child, topic);
00169 child = child.nextSiblingElement(ELEMENT_TOPIC);
00170 }
00171 }
00172 }
00173
00174
00175 bool
00176 HelpBrowser::isValidTopicElement(const QDomElement &topicElement)
00177 {
00178 return (topicElement.hasAttribute(ATTRIBUTE_TOPIC_ID) &&
00179 topicElement.hasAttribute(ATTRIBUTE_TOPIC_NAME) &&
00180 topicElement.hasAttribute(ATTRIBUTE_TOPIC_HTML));
00181 }
00182
00183
00184
00185
00186 QString
00187 HelpBrowser::getResourcePath(const QDomElement &topicElement)
00188 {
00189 QString link = language() + "/" + topicElement.attribute(ATTRIBUTE_TOPIC_HTML);
00190 if (topicElement.hasAttribute(ATTRIBUTE_TOPIC_SECTION)) {
00191 link += "#" + topicElement.attribute(ATTRIBUTE_TOPIC_SECTION);
00192 }
00193 return link;
00194 }
00195
00196
00197 QTreeWidgetItem*
00198 HelpBrowser::createTopicTreeItem(const QDomElement &topicElement,
00199 QTreeWidgetItem *parent)
00200 {
00201 QTreeWidgetItem *topic = new QTreeWidgetItem(parent);
00202 topic->setText(0, topicElement.attribute(ATTRIBUTE_TOPIC_NAME));
00203 topic->setData(0, ROLE_TOPIC_ID, topicElement.attribute(ATTRIBUTE_TOPIC_ID));
00204 topic->setData(0, ROLE_TOPIC_QRC_PATH, getResourcePath(topicElement));
00205 return topic;
00206 }
00207
00208
00209 void
00210 HelpBrowser::contentsItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev)
00211 {
00212 QList<QTreeWidgetItem *> selected = ui.treeSearch->selectedItems();
00213
00214 if (!selected.isEmpty()) {
00215 ui.treeSearch->setItemSelected(selected[0], false);
00216 }
00217 currentItemChanged(current, prev);
00218 }
00219
00220
00221 void
00222 HelpBrowser::searchItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev)
00223 {
00224 QList<QTreeWidgetItem *> selected = ui.treeContents->selectedItems();
00225
00226 if (!selected.isEmpty()) {
00227 ui.treeContents->setItemSelected(selected[0], false);
00228 }
00229
00230
00231 currentItemChanged(current, prev);
00232
00233
00234 QTextCursor found;
00235 QTextDocument::FindFlags flags = QTextDocument::FindWholeWords;
00236 found = ui.txtBrowser->document()->find(_lastSearch, 0, flags);
00237 if (!found.isNull()) {
00238 ui.txtBrowser->setTextCursor(found);
00239 }
00240 }
00241
00242
00243 void
00244 HelpBrowser::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev)
00245 {
00246 Q_UNUSED(prev);
00247 if (current) {
00248 ui.txtBrowser->setSource(QUrl(current->data(0,
00249 ROLE_TOPIC_QRC_PATH).toString()));
00250 }
00251 _foundBefore = false;
00252 }
00253
00254
00255
00256 QTreeWidgetItem*
00257 HelpBrowser::findTopicItem(QTreeWidgetItem *startItem, QString topic)
00258 {
00259
00260 if (!startItem)
00261 return 0;
00262
00263
00264 QString subtopic = topic.mid(0, topic.indexOf(".")).toLower();
00265
00266
00267 for (int i = 0; i < startItem->childCount(); i++) {
00268 QTreeWidgetItem *item = startItem->child(i);
00269
00270 if (subtopic == item->data(0, ROLE_TOPIC_ID).toString().toLower()) {
00271
00272 ui.treeContents->setItemExpanded(item, true);
00273 if (!topic.contains(".")) {
00274
00275 return item;
00276 }
00277
00278 return findTopicItem(item, topic.mid(topic.indexOf(".")+1));
00279 }
00280 }
00281 return 0;
00282 }
00283
00284
00285
00286 void
00287 HelpBrowser::showTopic(QString topic)
00288 {
00289
00290 QTreeWidgetItem *item =
00291 findTopicItem(ui.treeContents->topLevelItem(0), topic);
00292
00293 if (item) {
00294
00295
00296 QTreeWidgetItem* selected = ui.treeContents->selectedItems()[0];
00297 if (selected) {
00298 ui.treeContents->setItemSelected(selected, false);
00299 }
00300 ui.treeContents->setItemExpanded(ui.treeContents->topLevelItem(0), true);
00301 ui.treeContents->setItemSelected(item, true);
00302 currentItemChanged(item, selected);
00303 }
00304 }
00305
00306
00307 void
00308 HelpBrowser::findNext()
00309 {
00310 find(true);
00311 }
00312
00313
00314 void
00315 HelpBrowser::findPrev()
00316 {
00317 find(false);
00318 }
00319
00320
00321
00322
00323
00324 void
00325 HelpBrowser::find(bool forward)
00326 {
00327
00328 if (ui.lineFind->text().isEmpty()) {
00329 return;
00330 }
00331
00332 QTextDocument::FindFlags flags = 0;
00333 QTextCursor cursor = ui.txtBrowser->textCursor();
00334 QString searchPhrase = ui.lineFind->text();
00335
00336
00337 this->statusBar()->clearMessage();
00338
00339
00340 if (!forward) {
00341 flags |= QTextDocument::FindBackward;
00342 }
00343 if (ui.chkbxMatchCase->isChecked()) {
00344 flags |= QTextDocument::FindCaseSensitively;
00345 }
00346 if (ui.chkbxWholePhrase->isChecked()) {
00347 flags |= QTextDocument::FindWholeWords;
00348 }
00349
00350
00351 if (searchPhrase != _lastFind) {
00352 _foundBefore = false;
00353 }
00354 _lastFind = searchPhrase;
00355
00356
00357 if (!cursor.hasSelection()) {
00358 if (forward) {
00359 cursor.movePosition(QTextCursor::Start);
00360 } else {
00361 cursor.movePosition(QTextCursor::End);
00362 }
00363 ui.txtBrowser->setTextCursor(cursor);
00364 }
00365
00366
00367 QTextCursor found;
00368 found = ui.txtBrowser->document()->find(searchPhrase, cursor, flags);
00369
00370
00371 if (!found.isNull()) {
00372 ui.txtBrowser->setTextCursor(found);
00373
00374 } else {
00375 if (_foundBefore) {
00376 if (forward)
00377 this->statusBar()->showMessage(tr("Search reached end of document"));
00378 else
00379 this->statusBar()->showMessage(tr("Search reached start of document"));
00380 } else {
00381 this->statusBar()->showMessage(tr("Text not found in document"));
00382 }
00383 }
00384
00385
00386 _foundBefore |= !found.isNull();
00387 }
00388
00389
00390
00391
00392
00393 void
00394 HelpBrowser::search()
00395 {
00396
00397 ui.treeSearch->clear();
00398
00399
00400 if (ui.lineSearch->text().isEmpty()) {
00401 return;
00402 }
00403
00404 HelpTextBrowser browser;
00405 QTextCursor found;
00406 QTextDocument::FindFlags flags = QTextDocument::FindWholeWords;
00407
00408 _lastSearch = ui.lineSearch->text();
00409
00410
00411 for (int i=0; i < _elementList.size(); ++i) {
00412
00413 browser.setSource(QUrl(getResourcePath(_elementList[i])));
00414
00415
00416 found = browser.document()->find(ui.lineSearch->text(), 0, flags);
00417
00418
00419 if (!found.isNull()) {
00420 ui.treeSearch->addTopLevelItem(createTopicTreeItem(_elementList[i], 0));
00421 }
00422 }
00423
00424
00425 this->statusBar()->showMessage(tr("Found %1 results")
00426 .arg(ui.treeSearch->topLevelItemCount()));
00427 }
00428
00429
00430 void
00431 HelpBrowser::showWindow(QString topic)
00432 {
00433
00434
00435 VidaliaWindow::showWindow();
00436
00437
00438 if (!topic.isEmpty()) {
00439 showTopic(topic);
00440 }
00441 }
00442