diff --git a/config.cc b/config.cc index 354e81d0..4c1033b8 100644 --- a/config.cc +++ b/config.cc @@ -122,6 +122,7 @@ Preferences::Preferences(): enableWebPlugins( false ), hideGoldenDictHeader( false ), zoomFactor( 1 ), + helpZoomFactor( 1 ), wordsZoomLevel( 0 ), maxStringsInHistory( 500 ), storeHistory( 1 ), @@ -707,6 +708,9 @@ Class load() throw( exError ) if ( !preferences.namedItem( "zoomFactor" ).isNull() ) c.preferences.zoomFactor = preferences.namedItem( "zoomFactor" ).toElement().text().toDouble(); + if ( !preferences.namedItem( "helpZoomFactor" ).isNull() ) + c.preferences.helpZoomFactor = preferences.namedItem( "helpZoomFactor" ).toElement().text().toDouble(); + if ( !preferences.namedItem( "wordsZoomLevel" ).isNull() ) c.preferences.wordsZoomLevel = preferences.namedItem( "wordsZoomLevel" ).toElement().text().toInt(); @@ -866,6 +870,16 @@ Class load() throw( exError ) if ( !mainWindowGeometry.isNull() ) c.mainWindowGeometry = QByteArray::fromBase64( mainWindowGeometry.toElement().text().toLatin1() ); + QDomNode helpWindowGeometry = root.namedItem( "helpWindowGeometry" ); + + if ( !helpWindowGeometry.isNull() ) + c.helpWindowGeometry = QByteArray::fromBase64( helpWindowGeometry.toElement().text().toLatin1() ); + + QDomNode helpSplitterState = root.namedItem( "helpSplitterState" ); + + if ( !helpSplitterState.isNull() ) + c.helpSplitterState = QByteArray::fromBase64( helpSplitterState.toElement().text().toLatin1() ); + #ifdef Q_OS_WIN QDomNode maximizedMainWindowGeometry = root.namedItem( "maximizedMainWindowGeometry" ); @@ -1480,6 +1494,10 @@ void save( Class const & c ) throw( exError ) opt.appendChild( dd.createTextNode( QString::number( c.preferences.zoomFactor ) ) ); preferences.appendChild( opt ); + opt = dd.createElement( "helpZoomFactor" ); + opt.appendChild( dd.createTextNode( QString::number( c.preferences.helpZoomFactor ) ) ); + preferences.appendChild( opt ); + opt = dd.createElement( "wordsZoomLevel" ); opt.appendChild( dd.createTextNode( QString::number( c.preferences.wordsZoomLevel ) ) ); preferences.appendChild( opt ); @@ -1738,6 +1756,14 @@ void save( Class const & c ) throw( exError ) opt.appendChild( dd.createTextNode( QString::fromLatin1( c.mainWindowGeometry.toBase64() ) ) ); root.appendChild( opt ); + opt = dd.createElement( "helpWindowGeometry" ); + opt.appendChild( dd.createTextNode( QString::fromLatin1( c.helpWindowGeometry.toBase64() ) ) ); + root.appendChild( opt ); + + opt = dd.createElement( "helpSplitterState" ); + opt.appendChild( dd.createTextNode( QString::fromLatin1( c.helpSplitterState.toBase64() ) ) ); + root.appendChild( opt ); + #ifdef Q_OS_WIN { QDomElement maximizedMainWindowGeometry = dd.createElement( "maximizedMainWindowGeometry" ); diff --git a/config.hh b/config.hh index ac24c616..81cf7784 100644 --- a/config.hh +++ b/config.hh @@ -225,6 +225,7 @@ struct Preferences bool hideGoldenDictHeader; qreal zoomFactor; + qreal helpZoomFactor; int wordsZoomLevel; unsigned maxStringsInHistory; @@ -520,6 +521,8 @@ struct Class QByteArray popupWindowGeometry; // Geometry saved by QMainWindow QByteArray dictInfoGeometry; // Geometry of "Dictionary info" window QByteArray inspectorGeometry; // Geometry of WebKit inspector window + QByteArray helpWindowGeometry; // Geometry of help window + QByteArray helpSplitterState; // Geometry of help splitter QString historyExportPath; // Path for export/import history QString resourceSavePath; // Path to save images/audio diff --git a/goldendict.pro b/goldendict.pro index 9c5d1d68..3ec7d034 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -20,9 +20,11 @@ QT += webkit QT += xml QT += network QT += svg +QT += sql CONFIG += exceptions \ rtti \ - stl + stl \ + help OBJECTS_DIR = build UI_DIR = build MOC_DIR = build @@ -139,6 +141,9 @@ unix:!mac { desktops.path = $$PREFIX/share/applications desktops.files = redist/*.desktop INSTALLS += desktops + helps.path = $$PREFIX/share/goldendict/help/ + helps.files = help/*.qch + INSTALLS += helps } mac { TARGET = GoldenDict @@ -171,7 +176,9 @@ mac { QMAKE_POST_LINK = mkdir -p GoldenDict.app/Contents/Frameworks & \ cp -nR $${PWD}/maclibs/lib/ GoldenDict.app/Contents/Frameworks/ & \ mkdir -p GoldenDict.app/Contents/MacOS/locale & \ - cp -R locale/*.qm GoldenDict.app/Contents/MacOS/locale/ + cp -R locale/*.qm GoldenDict.app/Contents/MacOS/locale/ & \ + mkdir -p GoldenDict.app/Contents/MacOS/help & \ + cp -R help/*.qch GoldenDict.app/Contents/MacOS/help/ CONFIG += zim_support } @@ -285,7 +292,8 @@ HEADERS += folding.hh \ dictheadwords.hh \ fulltextsearch.hh \ ftshelpers.hh \ - dictserver.hh + dictserver.hh \ + helpwindow.hh FORMS += groups.ui \ dictgroupwidget.ui \ @@ -405,7 +413,8 @@ SOURCES += folding.cc \ dictheadwords.cc \ fulltextsearch.cc \ ftshelpers.cc \ - dictserver.cc + dictserver.cc \ + helpwindow.cc win32 { FORMS += texttospeechsource.ui diff --git a/help/gdhelp_en.qch b/help/gdhelp_en.qch new file mode 100644 index 00000000..034da710 Binary files /dev/null and b/help/gdhelp_en.qch differ diff --git a/help/gdhelp_ru.qch b/help/gdhelp_ru.qch new file mode 100644 index 00000000..528a836b Binary files /dev/null and b/help/gdhelp_ru.qch differ diff --git a/helpwindow.cc b/helpwindow.cc new file mode 100644 index 00000000..14cfb084 --- /dev/null +++ b/helpwindow.cc @@ -0,0 +1,252 @@ +/* This file is (c) 2014 Abs62 + * Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */ + +#include +#include +#include +#include +#include +#include +#include + +#include "helpwindow.hh" +#include "gddebug.hh" + +namespace Help { + +HelpBrowser::HelpBrowser( QHelpEngineCore * engine, QWidget *parent ) : + QTextBrowser( parent ), + helpEngine( engine ) +{ + connect( this, SIGNAL( anchorClicked( QUrl ) ), this, SLOT( linkClicked( QUrl ) ) ); +} + +void HelpBrowser::showHelpForKeyword( QString const & id ) +{ + if ( helpEngine ) + { + QMap< QString, QUrl > links = helpEngine->linksForIdentifier( id ); + if( !links.isEmpty() ) + setSource( links.constBegin().value() ); + } +} + +QVariant HelpBrowser::loadResource( int type, QUrl const & name ) +{ + QByteArray ba; + if( type < 4 && helpEngine ) + { + QUrl url(name); + if( name.isRelative() ) + url = source().resolved( url ); + + ba = helpEngine->fileData(url ); + } + return ba; +} + +void HelpBrowser::linkClicked( QUrl const & url ) +{ + if( url.scheme() == "http" || url.scheme() == "https" ) + { + QDesktopServices::openUrl( url ); + } + else + setSource( url ); +} + +HelpWindow::HelpWindow( QWidget * parent, Config::Class & cfg_ ) : + QDialog( parent ), + cfg( cfg_ ), + helpEngine( 0 ) +{ + resize( QSize( 600, 450 ) ); + setWindowTitle( tr( "GoldenDict help" ) ); + setWindowFlags( windowFlags() & ~Qt::WindowContextHelpButtonHint ); + + QVBoxLayout * mainLayout = new QVBoxLayout( this ); + setLayout( mainLayout ); + + navToolBar = new QToolBar( this ); + navHome = navToolBar->addAction( QIcon( ":/icons/home.png" ), tr( "Home" ) ); + navToolBar->widgetForAction( navHome )->setObjectName( "helpHomeButton" ); + navBack = navToolBar->addAction( QIcon( ":/icons/previous.png" ), tr( "Back" ) ); + navToolBar->widgetForAction( navBack )->setObjectName( "helpBackButton" ); + navForward = navToolBar->addAction( QIcon( ":/icons/next.png" ), tr( "Forward" ) ); + navToolBar->widgetForAction( navForward )->setObjectName( "helpForwardButton" ); + + navToolBar->addSeparator(); + + zoomInAction = navToolBar->addAction( QIcon( ":/icons/icon32_zoomin" ), tr( "Zoom In" ) ); + navToolBar->widgetForAction( zoomInAction )->setObjectName( "zoomInButton" ); + zoomOutAction = navToolBar->addAction( QIcon( ":/icons/icon32_zoomout" ), tr( "Zoom Out" ) ); + navToolBar->widgetForAction( zoomInAction )->setObjectName( "zoomOutButton" ); + zoomBaseAction = navToolBar->addAction( QIcon( ":/icons/icon32_zoombase" ), tr( "Normal Size" ) ); + navToolBar->widgetForAction( zoomBaseAction )->setObjectName( "zoomBaseButton" ); + + navForward->setEnabled( false ); + navBack->setEnabled( false ); + + mainLayout->addWidget( navToolBar ); + + QString localeName = cfg.preferences.interfaceLanguage; + if( localeName.isEmpty() ) + localeName = QLocale::system().name(); + + helpFile = QDir::toNativeSeparators( Config::getProgramDataDir() + "/help/gdhelp_" + + localeName.left( 2 ) + ".qch" ); + + if( !QFileInfo( helpFile ).isFile() ) + helpFile = QDir::toNativeSeparators( Config::getProgramDataDir() + "/help/gdhelp_en.qch" ); + + helpCollectionFile = QDir::toNativeSeparators( Config::getConfigDir() + "gdhelp.qhc" ); + + helpEngine = new QHelpEngine( helpCollectionFile ); + + if( !helpEngine->setupData() ) + { + gdWarning( "Help engine initialization error: %s", helpEngine->error().toUtf8().data() ); + delete helpEngine; + helpEngine = 0; + } + else + { + if( !helpEngine->registerDocumentation( helpFile ) ) + { + gdWarning( "Help engine set file error: %s", helpEngine->error().toUtf8().data() ); + } + + tabWidget = new QTabWidget( this ); + tabWidget->addTab( helpEngine->contentWidget(), tr( "Content" ) ); + tabWidget->addTab( helpEngine->indexWidget(), tr( "Index" ) ); + + helpBrowser = new HelpBrowser( helpEngine, this ); + + helpBrowser->setOpenLinks( false ); + + connect( helpEngine->contentWidget(), SIGNAL( linkActivated( QUrl ) ), + helpBrowser, SLOT( setSource( QUrl ) ) ); + connect( helpEngine->indexWidget(), SIGNAL( linkActivated( QUrl, QString ) ), + helpBrowser, SLOT( setSource( QUrl ) ) ); + + connect( navHome, SIGNAL( triggered() ), helpBrowser, SLOT( home() ) ); + connect( navForward, SIGNAL( triggered() ), helpBrowser, SLOT( forward() ) ); + connect( navBack, SIGNAL( triggered() ), helpBrowser, SLOT( backward() ) ); + + connect( helpBrowser, SIGNAL( forwardAvailable( bool ) ), + this, SLOT( forwardEnabled( bool ) ) ); + + connect( helpBrowser, SIGNAL( backwardAvailable( bool ) ), + this, SLOT( backwardEnabled( bool ) ) ); + + connect( helpEngine->contentWidget(), SIGNAL( clicked( QModelIndex ) ), + this, SLOT( contentsItemClicked( QModelIndex ) ) ); + + connect( zoomInAction, SIGNAL( triggered( ) ), this, SLOT( zoomIn() ) ); + connect( zoomOutAction, SIGNAL( triggered( ) ), this, SLOT( zoomOut() ) ); + connect( zoomBaseAction, SIGNAL( triggered( ) ), this, SLOT( zoomBase() ) ); + + splitter = new QSplitter( this ); + splitter->addWidget( tabWidget ); + splitter->addWidget( helpBrowser ); + + splitter->setStretchFactor( 0, 1 ); + splitter->setStretchFactor( 1, 4 ); + mainLayout->addWidget( splitter ); + } + + if( !cfg.helpWindowGeometry.isEmpty() ) + restoreGeometry( cfg.helpWindowGeometry ); + if( !cfg.helpSplitterState.isEmpty() ) + splitter->restoreState( cfg.helpSplitterState ); + + QFont f = helpBrowser->font(); + fontSize = f.pointSize(); + if( fontSize < 10 ) + { + fontSize = 10; + f.setPointSize( fontSize ); + helpBrowser->setFont( f ); + } + + applyZoomFactor(); +} + +HelpWindow::~HelpWindow() +{ + if( helpEngine ) + delete helpEngine; + + QFile f( helpCollectionFile ); + f.remove(); +} + +void HelpWindow::reject() +{ + cfg.helpWindowGeometry = saveGeometry(); + cfg.helpSplitterState = splitter->saveState(); + emit needClose(); +} + +void HelpWindow::accept() +{ + cfg.helpWindowGeometry = saveGeometry(); + cfg.helpSplitterState = splitter->saveState(); + emit needClose(); +} + +void HelpWindow::forwardEnabled( bool enabled ) +{ + navForward->setEnabled( enabled ); +} + +void HelpWindow::backwardEnabled( bool enabled ) +{ + navBack->setEnabled( enabled ); +} + +void HelpWindow::contentsItemClicked( QModelIndex const & index ) +{ + QHelpContentItem * item = helpEngine->contentModel()->contentItemAt( index ); + if( !item->url().isEmpty() ) + helpBrowser->setSource( item->url() ); +} + +void HelpWindow::zoomIn() +{ + cfg.preferences.helpZoomFactor += 0.2; + applyZoomFactor(); +} + +void HelpWindow::zoomOut() +{ + cfg.preferences.helpZoomFactor -= 0.2; + applyZoomFactor(); +} + +void HelpWindow::zoomBase() +{ + cfg.preferences.helpZoomFactor = 1; + applyZoomFactor(); +} + +void HelpWindow::applyZoomFactor() +{ + if ( cfg.preferences.helpZoomFactor >= 5 ) + cfg.preferences.helpZoomFactor = 5; + else if ( cfg.preferences.helpZoomFactor <= 0.2 ) + cfg.preferences.helpZoomFactor = 0.2; + + zoomInAction->setEnabled( cfg.preferences.helpZoomFactor < 5 ); + zoomOutAction->setEnabled( cfg.preferences.helpZoomFactor > 0.2 ); + zoomBaseAction->setEnabled( cfg.preferences.helpZoomFactor != 1.0 ); + + if( fontSize > 0 ) + { + QFont f = helpBrowser->font(); + f.setPointSize( fontSize * cfg.preferences.helpZoomFactor ); + helpBrowser->setFont( f ); + } +} + +} // namespace Help diff --git a/helpwindow.hh b/helpwindow.hh new file mode 100644 index 00000000..fcfb2d2d --- /dev/null +++ b/helpwindow.hh @@ -0,0 +1,77 @@ +#ifndef __HELPWINDOW_HH_INCLUDED__ +#define __HELPWINDOW_HH_INCLUDED__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "config.hh" + +namespace Help { + +class HelpBrowser : public QTextBrowser +{ +Q_OBJECT +public: + HelpBrowser( QHelpEngineCore * engine, QWidget *parent ); + void showHelpForKeyword( QString const & id ); +private: + QVariant loadResource( int type, QUrl const & name ); + QHelpEngineCore * helpEngine; + +private slots: + void linkClicked( QUrl const & url ); +}; + + +class HelpWindow : public QDialog +{ +Q_OBJECT + + Config::Class & cfg; + QToolBar * navToolBar; + QHelpEngine * helpEngine; + QTabWidget * tabWidget; + HelpBrowser * helpBrowser; + QAction * navForward, * navBack, * navHome; + QAction * zoomInAction, * zoomOutAction, * zoomBaseAction; + QSplitter * splitter; + + QString helpFile, helpCollectionFile; + + int fontSize; + + void applyZoomFactor(); + +public: + HelpWindow( QWidget * parent, Config::Class & cfg_ ); + ~HelpWindow(); + + QHelpEngine const * getHelpEngine() + { return helpEngine; } + +public slots: + virtual void reject(); + virtual void accept(); + void forwardEnabled( bool enabled ); + void backwardEnabled( bool enabled ); + void contentsItemClicked( QModelIndex const & index ); + + void zoomIn(); + void zoomOut(); + void zoomBase(); + +signals: + void needClose(); +}; + +} // namespace Help + +#endif // __HELPWINDOW_HH_INCLUDED__ diff --git a/icons/home.png b/icons/home.png new file mode 100644 index 00000000..b1c6ae19 Binary files /dev/null and b/icons/home.png differ diff --git a/mainwindow.cc b/mainwindow.cc index d8f3c8a0..d2b08398 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -116,6 +116,7 @@ MainWindow::MainWindow( Config::Class & cfg_ ): , headwordsDlg( 0 ) , ftsIndexing( dictionaries ) , ftsDlg( 0 ) +, helpWindow( 0 ) #ifdef Q_OS_WIN32 , gdAskMessage( 0xFFFFFFFF ) #endif @@ -610,6 +611,8 @@ MainWindow::MainWindow( Config::Class & cfg_ ): this, SLOT( openConfigFolder() ) ); connect( ui.about, SIGNAL( triggered() ), this, SLOT( showAbout() ) ); + connect( ui.showReference, SIGNAL( triggered() ), + this, SLOT( showGDHelp() ) ); connect( groupListInDock, SIGNAL( currentIndexChanged( QString const & ) ), this, SLOT( currentGroupChanged( QString const & ) ) ); @@ -1900,6 +1903,7 @@ void MainWindow::editPreferences() // These parameters are not set in dialog p.zoomFactor = cfg.preferences.zoomFactor; + p.helpZoomFactor = cfg.preferences.helpZoomFactor; p.wordsZoomLevel = cfg.preferences.wordsZoomLevel; p.hideMenubar = cfg.preferences.hideMenubar; p.searchInDock = cfg.preferences.searchInDock; @@ -2702,6 +2706,9 @@ void MainWindow::toggleMainWindow( bool onlyShow ) if( ftsDlg ) ftsDlg->hide(); + + if( helpWindow ) + helpWindow->hide(); } if ( shown ) @@ -4039,6 +4046,29 @@ void MainWindow::closeFullTextSearchDialog() } } +void MainWindow::showGDHelp() +{ + if( !helpWindow ) + helpWindow = new Help::HelpWindow( this, cfg ); + + if( helpWindow->getHelpEngine() ) + { + connect( helpWindow, SIGNAL( needClose() ), this, SLOT( hideGDHelp() ) ); + helpWindow->show(); + } + else + { + delete helpWindow; + helpWindow = 0; + } +} + +void MainWindow::hideGDHelp() +{ + if( helpWindow ) + helpWindow->hide(); +} + #ifdef Q_OS_WIN32 bool MainWindow::handleGDMessage( MSG * message, long * result ) diff --git a/mainwindow.hh b/mainwindow.hh index f94bc270..1fd33704 100644 --- a/mainwindow.hh +++ b/mainwindow.hh @@ -29,6 +29,7 @@ #include "wordlist.hh" #include "dictheadwords.hh" #include "fulltextsearch.hh" +#include "helpwindow.hh" #ifdef Q_WS_X11 #include @@ -168,6 +169,8 @@ private: FTS::FullTextSearchDialog * ftsDlg; + Help::HelpWindow * helpWindow; + /// Applies the qt's stylesheet, given the style's name. void applyQtStyleSheet( QString const & displayStyle, QString const & addonStyle ); @@ -427,6 +430,9 @@ private slots: void showFullTextSearchDialog(); void closeFullTextSearchDialog(); + void showGDHelp(); + void hideGDHelp(); + signals: /// Set optional parts expand mode for all tabs void setExpandOptionalParts( bool expand ); diff --git a/mainwindow.ui b/mainwindow.ui index 7c15d613..71ba3540 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -101,6 +101,8 @@ &Help + + @@ -372,9 +374,6 @@ About GoldenDict - - F1 - QAction::AboutRole @@ -587,6 +586,14 @@ QAction::TextHeuristicRole + + + GoldenDict reference + + + F1 + + diff --git a/resources.qrc b/resources.qrc index 8e9760cb..cc29474e 100644 --- a/resources.qrc +++ b/resources.qrc @@ -10,6 +10,7 @@ icons/addtab.png icons/next.png icons/previous.png + icons/home.png icons/reload.png icons/programicon.png icons/programicon_scan.png