From 3133bbc63a2d32dac638db58fa13e966488e88b5 Mon Sep 17 00:00:00 2001 From: tpearson Date: Mon, 15 Feb 2010 18:23:18 +0000 Subject: Added abandoned KDE3 version of kchmviewer git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kchmviewer@1090662 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/kchmmainwindow.cpp | 1298 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1298 insertions(+) create mode 100644 src/kchmmainwindow.cpp (limited to 'src/kchmmainwindow.cpp') diff --git a/src/kchmmainwindow.cpp b/src/kchmmainwindow.cpp new file mode 100644 index 0000000..13417f1 --- /dev/null +++ b/src/kchmmainwindow.cpp @@ -0,0 +1,1298 @@ +/*************************************************************************** + * Copyright (C) 2004-2007 by Georgy Yunaev, gyunaev@ulduzsoft.com * + * Please do not use email address above for bug reports; see * + * the README file * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "kde-qt.h" + +#include +#include + +#include "libchmfile.h" +#include "libchmfileimpl.h" +#include "libchmurlfactory.h" + +#include "kchmmainwindow.h" +#include "kchmconfig.h" +#include "kchmindexwindow.h" +#include "kchmsearchwindow.h" +#include "kchmbookmarkwindow.h" +#include "kchmtreeviewitem.h" +#include "kchmsearchtoolbar.h" +#include "kchmsettings.h" +#include "kchmsetupdialog.h" +#include "iconstorage.h" +#include "kchmnavtoolbar.h" +#include "kchmviewwindow.h" +#include "kchmviewwindowmgr.h" +#include "kchmkeyeventfilter.h" +#include "kchmcontentswindow.h" + +#if !defined (USE_KDE) + #include "kqrunprocess.h" +#endif + +#include "kchmmainwindow.moc" + + +KCHMMainWindow::KCHMMainWindow() + : KQMainWindow ( 0, "KCHMMainWindow", WDestructiveClose ) +{ + const unsigned int WND_X_SIZE = 700; + const unsigned int WND_Y_SIZE = 500; + const unsigned int SPLT_X_SIZE = 200; + + m_FirstTimeShow = true; + m_chmFile = 0; + + m_indexWindow = 0; + m_searchWindow = 0; + m_contentsWindow = 0; + m_viewWindowMgr = 0; + + m_tabContextPage = -1; + m_tabIndexPage = -1; + m_tabSearchPage = -1; + m_tabBookmarkPage = -1; + + setupSignals(); + + m_currentSettings = new KCHMSettings; + + // Create the initial layout - a splitter with tab window in left, and text browser in right + m_windowSplitter = new QSplitter(this); + m_tabWidget = new KQTabWidget( m_windowSplitter ); + m_viewWindowMgr = new KCHMViewWindowMgr( m_windowSplitter ); + + m_bookmarkWindow = new KCHMBookmarkWindow (m_tabWidget); + + // Add the tabs + m_tabWidget->addTab( m_bookmarkWindow, i18n("Bookmarks") ); + + setupToolbarsAndMenu(); + + setCentralWidget( m_windowSplitter ); + + QValueList sizes; + sizes.push_back (SPLT_X_SIZE); + sizes.push_back (WND_X_SIZE - SPLT_X_SIZE); + m_windowSplitter->setSizes (sizes); + + resize (WND_X_SIZE, WND_Y_SIZE); + +#if defined (ENABLE_AUTOTEST_SUPPORT) + m_autoteststate = STATE_OFF; + m_useShortAutotest = false; +#endif /* defined (ENABLE_AUTOTEST_SUPPORT) */ + + QAccel * accel = new QAccel( this ); + accel->connectItem ( accel->insertItem ( Key_F11 ), this, SLOT ( slotToggleFullScreenMode() ) ); + accel->connectItem ( accel->insertItem ( CTRL + Key_1), this, SLOT ( slotActivateContentTab() ) ); + accel->connectItem ( accel->insertItem ( CTRL + Key_2), this, SLOT ( slotActivateIndexTab() ) ); + accel->connectItem ( accel->insertItem ( CTRL + Key_3), this, SLOT ( slotActivateSearchTab() ) ); + accel->connectItem ( accel->insertItem ( CTRL + Key_4), this, SLOT ( slotActivateBookmarkTab() ) ); + accel->connectItem ( accel->insertItem ( Key_F3 ), m_searchToolbar, SLOT ( onBtnNextSearchResult() ) ); + + statusBar()->show(); + setIcon( *gIconStorage.getApplicationIcon() ); + + m_aboutDlgMenuText = i18n( "%1 version %2\n\nCopyright (C) George Yunaev," + "gyunaev@ulduzsoft.com, 2004-2007\nhttp://www.kchmviewer.net\n\n" + "Licensed under GNU GPL license.\n\n" + "Please check my another project, http://www.transientmail.com - temporary " + "e-mail address, which expires automatically." ) + . arg(APP_NAME) . arg(APP_VERSION); +} + + +KCHMMainWindow::~KCHMMainWindow() +{ +} + + +void KCHMMainWindow::slotOpenMenuItemActivated() +{ +#if defined (USE_KDE) + QString fn = KFileDialog::getOpenFileName( appConfig.m_lastOpenedDir, i18n("*.chm|Compressed Help Manual (*.chm)"), this); +#else + QString fn = QFileDialog::getOpenFileName( appConfig.m_lastOpenedDir, i18n("Compressed Help Manual (*.chm)"), this); +#endif + + if ( !fn.isEmpty() ) + loadChmFile( fn ); + else + { + if ( !m_chmFile ) + exit (1); + + statusBar()->message( i18n("Loading aborted"), 2000 ); + } +} + + +bool KCHMMainWindow::loadChmFile ( const QString &fileName, bool call_open_page ) +{ + LCHMFile * new_chmfile = new LCHMFile(); + + if ( new_chmfile->loadFile( fileName ) ) + { + // The new file is opened, so we can close the old one + if ( m_chmFile ) + { + closeChmFile( ); + delete m_chmFile; + } + + m_chmFile = new_chmfile; + + // Show current encoding in status bar + showInStatusBar( tr("Detected chm file charset: %1") . arg(m_chmFile->currentEncoding()->language) ); + + // Make the file name absolute; we'll need it later + QDir qd; + qd.setPath (fileName); + m_chmFilename = qd.absPath(); + + // Qt's 'dirname' does not work well + QFileInfo qf ( m_chmFilename ); + appConfig.m_lastOpenedDir = qf.dirPath(true); + + // Order the tabulations + int number_of_pages = 0; + + if ( m_chmFile->hasTableOfContents() ) + m_tabContextPage = number_of_pages++; + else + m_tabContextPage = -1; + + if ( m_chmFile->hasIndexTable() ) + m_tabIndexPage = number_of_pages++; + else + m_tabIndexPage = -1; + + // We always show search + m_tabSearchPage = number_of_pages++; + + m_tabBookmarkPage = number_of_pages; + + showOrHideContextWindow( m_tabContextPage ); + showOrHideIndexWindow( m_tabIndexPage ); + showOrHideSearchWindow( m_tabSearchPage ); + + m_bookmarkWindow->invalidate(); + m_navToolbar->updateIconStatus( false, false ); + m_viewWindowMgr->invalidate(); + refreshCurrentBrowser(); + + if ( m_currentSettings->loadSettings (fileName) ) + { + const LCHMTextEncoding * encoding = + m_chmFile->impl()->lookupByLCID( m_currentSettings->m_activeencodinglcid ); + + m_tabWidget->setCurrentPage( m_currentSettings->m_activetabsystem ); + + if ( encoding ) + { + m_chmFile->setCurrentEncoding( encoding ); + m_searchToolbar->setChosenEncodingInMenu( encoding ); + } + + if ( m_searchWindow ) + m_searchWindow->restoreSettings (m_currentSettings->m_searchhistory); + + m_bookmarkWindow->restoreSettings (m_currentSettings->m_bookmarks); + + if ( call_open_page ) + { + m_viewWindowMgr->restoreSettings( m_currentSettings->m_viewwindows ); + m_viewWindowMgr->setCurrentPage( m_currentSettings->m_activetabwindow ); + } + + // Restore the main window size + QValueList sizes; + sizes.push_back( m_currentSettings->m_window_size_splitter ); + sizes.push_back( m_currentSettings->m_window_size_x - m_currentSettings->m_window_size_splitter ); + + m_windowSplitter->setSizes( sizes ); + resize( m_currentSettings->m_window_size_x, m_currentSettings->m_window_size_y ); + } + else + { + m_tabWidget->setCurrentPage (0); + m_searchToolbar->setChosenEncodingInMenu( m_chmFile->currentEncoding() ); + + if ( call_open_page ) + openPage( m_chmFile->homeUrl() ); + } + + m_searchToolbar->setEnabled (true); + appConfig.addFileToHistory( m_chmFilename ); + updateHistoryMenu(); + return true; + } + else + { + QMessageBox mbox( + i18n("%1 - failed to load the chm file") . arg(APP_NAME), + i18n("Unable to load the chm file %1") . arg(fileName), + QMessageBox::Critical, + QMessageBox::Ok, + QMessageBox::NoButton, + QMessageBox::NoButton); + mbox.exec(); + + statusBar()->message( + i18n("Could not load file %1").arg(fileName), + 2000 ); + delete new_chmfile; + return false; + } +} + + +void KCHMMainWindow::slotPrintMenuItemActivated() +{ + currentBrowser()->printCurrentPage(); +} + + +void KCHMMainWindow::slotAboutMenuItemActivated() +{ + QString caption = i18n( "About %1" ) . arg(APP_NAME); + QString text = m_aboutDlgMenuText; + + // It is quite funny that the argument order differs +#if defined (USE_KDE) + KMessageBox::about( this, text, caption ); +#else + QMessageBox::about( this, caption, text ); +#endif +} + + +void KCHMMainWindow::slotAboutQtMenuItemActivated() +{ + QMessageBox::aboutQt( this, APP_NAME); +} + +void KCHMMainWindow::refreshCurrentBrowser( ) +{ + QString title = m_chmFile->title(); + + if ( !title ) + title = APP_NAME; + // KDE adds application name automatically, so we don't need it here +#if !defined (USE_KDE) + else + title = (QString) APP_NAME + " - " + title; +#endif + + setCaption ( title ); + + currentBrowser()->invalidate(); + + if ( m_contentsWindow ) + m_contentsWindow->refillTableOfContents(); +} + +void KCHMMainWindow::slotOnTreeClicked( QListViewItem * item ) +{ + bool unused; + + if ( !item ) + return; + + KCHMIndTocItem * treeitem = (KCHMIndTocItem*) item; + slotLinkClicked( treeitem->getUrl(), unused ); +} + + +void KCHMMainWindow::slotLinkClicked ( const QString & link, bool& follow_link ) +{ + if ( link.isEmpty() ) + return; + + if ( gKeyEventFilter.isShiftPressed() ) + { + openPage( link, OPF_NEW_TAB | OPF_CONTENT_TREE ); + follow_link = false; + } + else if ( gKeyEventFilter.isCtrlPressed() ) + { + openPage( link, OPF_NEW_TAB | OPF_BACKGROUND ); + follow_link = false; + } + else + // If the openPage failed, we do not need to follow the link. + follow_link = openPage( link, OPF_CONTENT_TREE | OPF_ADD2HISTORY ); +} + + +bool KCHMMainWindow::openPage( const QString & srcurl, unsigned int flags ) +{ + QString p1, p2, url = srcurl; + + if ( url == "/" ) + url = m_chmFile->homeUrl(); + + if ( LCHMUrlFactory::isRemoteURL (url, p1) ) + { + switch ( appConfig.m_onExternalLinkClick ) + { + case KCHMConfig::ACTION_DONT_OPEN: + break; + + case KCHMConfig::ACTION_ASK_USER: + if ( QMessageBox::question(this, + i18n("%1 - remote link clicked - %2") . arg(APP_NAME) . arg(p1), + i18n("A remote link %1 will start the external program to open it.\n\nDo you want to continue?").arg( url ), + i18n("&Yes"), i18n("&No"), + QString::null, 0, 1 ) ) + return false; + + // no break! should continue to open. + + case KCHMConfig::ACTION_ALWAYS_OPEN: + { +#if defined (USE_KDE) + new KRun ( url ); +#else + run_process( appConfig.m_QtBrowserPath, url ); +#endif + } + break; + } + + return false; // do not change the current page. + } + + // Filter the URLs which do not need to be opened at all by Qt version + if ( LCHMUrlFactory::isJavascriptURL (url) ) + { + QMessageBox::information( this, + i18n( "%1 - JavsScript link clicked") . arg(APP_NAME), + i18n( "You have clicked a JavaScript link.\nTo prevent security-related issues JavaScript URLs are disabled in CHM files.") ); + + return false; + } + + if ( LCHMUrlFactory::isNewChmURL (url, p1, p2) && p1 != m_chmFilename ) + { + if ( QMessageBox::question( this, + i18n( "%1 - link to a new CHM file clicked"). arg(APP_NAME), + i18n( "You have clicked a link, which leads to a new CHM file %1.\nThe current file will be closed.\n\nDo you want to continue?").arg( p1 ), + i18n( "&Yes" ), i18n( "&No" ), + QString::null, 0, 1 ) ) + return false; + + // Because chm file always contain relative link, and current filename is not changed, + // we need to form a new path + QFileInfo qfi( m_chmFilename ); + QString newfilename = qfi.dirPath(true) + "/" + p1; + + QStringList event_args; + event_args.push_back( newfilename ); + event_args.push_back( p2 ); // url + + qApp->postEvent( this, new KCHMUserEvent( "loadAndOpen", event_args ) ); + return false; + } + + KCHMViewWindow * vwnd = currentBrowser(); + if ( flags & OPF_NEW_TAB ) + vwnd = m_viewWindowMgr->addNewTab( !(flags & OPF_BACKGROUND) ); + + // Store current page and position to add it to history if we change it + int hist_scrollpos = currentBrowser()->getScrollbarPosition(); + QString hist_url = currentBrowser()->getOpenedPage(); + + if ( vwnd->openUrl (url) ) + { + // Open all the tree items to show current item (if needed) + if ( (flags & OPF_CONTENT_TREE) != 0 ) + locateInContentTree( vwnd->getOpenedPage() ); + + if ( flags & OPF_ADD2HISTORY ) + currentBrowser()->addNavigationHistory( hist_url, hist_scrollpos ); + } + + return true; +} + + +void KCHMMainWindow::showEvent( QShowEvent * ) +{ + if ( !m_FirstTimeShow ) + return; + + m_FirstTimeShow = false; + + if ( !parseCmdLineArgs( ) ) + { + if ( appConfig.m_LoadLatestFileOnStartup && appConfig.m_History.size() > 0 ) + { + if ( loadChmFile ( appConfig.m_History[0] ) ) + return; + } + + emit slotOpenMenuItemActivated(); + } +} + +void KCHMMainWindow::setupToolbarsAndMenu( ) +{ + // Create a 'file' toolbar + QToolBar * toolbar = new QToolBar(this); + + toolbar->setLabel( i18n( "File Operations") ); + + QPixmap iconFileOpen (*gIconStorage.getToolbarPixmap(KCHMIconStorage::fileopen)); + QToolButton * fileOpen = new QToolButton (iconFileOpen, + i18n( "Open File" ), + QString::null, + this, + SLOT( slotOpenMenuItemActivated() ), + toolbar); + + QString fileOpenText = i18n( "Click this button to open an existing chm file." ); + QWhatsThis::add( fileOpen, fileOpenText ); + + QPixmap iconFilePrint (*gIconStorage.getToolbarPixmap(KCHMIconStorage::print)); + QToolButton * filePrint = new QToolButton (iconFilePrint, + i18n( "Print File" ), + QString::null, + this, + SLOT( slotPrintMenuItemActivated() ), + toolbar); + + QString filePrintText = i18n( "Click this button to print the current page"); + QWhatsThis::add( filePrint, filePrintText ); + + // Setup the menu + KQPopupMenu * file = new KQPopupMenu( this ); + menuBar()->insertItem( i18n( "&File"), file ); + + int id; + id = file->insertItem ( iconFileOpen, i18n( "&Open..."), this, SLOT( slotOpenMenuItemActivated() ), CTRL+Key_O ); + file->setWhatsThis( id, fileOpenText ); + + id = file->insertItem( iconFilePrint, i18n( "&Print..."), this, SLOT( slotPrintMenuItemActivated() ), CTRL+Key_P ); + file->setWhatsThis( id, filePrintText ); + + file->insertSeparator(); + id = file->insertItem ( i18n( "E&xtract CHM content..."), this, SLOT( slotExtractCHM() ) ); + file->setWhatsThis( id, i18n( "Click this button to extract the whole CHM file content into a specific directory") ); + file->insertSeparator(); + + m_menuHistory = new KQPopupMenu( file ); + connect ( m_menuHistory, SIGNAL( activated(int) ), this, SLOT ( slotHistoryMenuItemActivated(int) )); + + file->insertItem( i18n( "&Recent files"), m_menuHistory ); + + file->insertSeparator(); + file->insertItem( i18n( "&Quit"), qApp, SLOT( closeAllWindows() ), CTRL+Key_Q ); + + KQPopupMenu * menu_edit = new KQPopupMenu( this ); + menuBar()->insertItem( i18n( "&Edit"), menu_edit ); + + menu_edit->insertItem ( i18n( "&Copy"), this, SLOT( slotBrowserCopy()), CTRL+Key_C ); + menu_edit->insertItem ( i18n( "&Select all"), this, SLOT( slotBrowserSelectAll()), CTRL+Key_A ); + + menu_edit->insertSeparator(); + + // KCHMNavToolbar + m_navToolbar = new KCHMNavToolbar( this ); + + // KCHMSearchToolbar also adds 'view' menu + m_searchToolbar = new KCHMSearchAndViewToolbar (this); + + // m_bookmarkWindow adds 'Bookmarks' menu + m_bookmarkWindow->createMenu( this ); + + // m_viewWindowMgr creates 'Window' menu + m_viewWindowMgr->createMenu( this ); + + KQPopupMenu * settings = new KQPopupMenu( this ); + menuBar()->insertItem( i18n( "&Settings"), settings ); + settings->insertItem( i18n( "&Change settings..."), this, SLOT( slotChangeSettingsMenuItemActivated() )); + +#if defined(USE_KDE) + QPopupMenu *help = helpMenu( m_aboutDlgMenuText ); +#else + KQPopupMenu * help = new KQPopupMenu( this ); + help->insertItem( i18n( "&About"), this, SLOT( slotAboutMenuItemActivated() ), Key_F1 ); + help->insertItem( i18n( "About &Qt"), this, SLOT( slotAboutQtMenuItemActivated() )); + help->insertSeparator(); + help->insertItem( i18n( "What's &This"), this, SLOT(whatsThis()), SHIFT+Key_F1 ); +#endif + + menuBar()->insertItem( i18n( "&Help"), help ); + updateHistoryMenu(); +} + +void KCHMMainWindow::slotAddBookmark( ) +{ + emit m_bookmarkWindow->onAddBookmarkPressed (); +} + +void KCHMMainWindow::setTextEncoding( const LCHMTextEncoding * encoding ) +{ + m_chmFile->setCurrentEncoding( encoding ); + m_searchToolbar->setChosenEncodingInMenu( encoding ); + + // Because updateView() will call view->invalidate(), which clears the view->getOpenedPage(), + // we have to make a copy of it. + QString url = currentBrowser()->getOpenedPage(); + + // Regenerate the content and index trees + refreshCurrentBrowser(); + + currentBrowser()->openUrl( url ); +} + +void KCHMMainWindow::closeChmFile( ) +{ + // Prepare the settings + if ( appConfig.m_HistoryStoreExtra ) + { + m_currentSettings->m_activeencodinglcid = m_chmFile->currentEncoding()->winlcid; + m_currentSettings->m_activetabsystem = m_tabWidget->currentPageIndex( ); + m_currentSettings->m_activetabwindow = m_viewWindowMgr->currentPageIndex( ); + + m_currentSettings->m_window_size_x = width(); + m_currentSettings->m_window_size_y = height(); + m_currentSettings->m_window_size_splitter = m_windowSplitter->sizes()[0]; + + if ( m_searchWindow ) + m_searchWindow->saveSettings (m_currentSettings->m_searchhistory); + + m_bookmarkWindow->saveSettings( m_currentSettings->m_bookmarks ); + + m_viewWindowMgr->saveSettings( m_currentSettings->m_viewwindows ); + + m_currentSettings->saveSettings( ); + } + + appConfig.save(); +} + + +void KCHMMainWindow::closeEvent ( QCloseEvent * e ) +{ + // Save the settings if we have something opened + if ( m_chmFile ) + { + closeChmFile( ); + delete m_chmFile; + m_chmFile = 0; + } + + QMainWindow::closeEvent ( e ); +} + +bool KCHMMainWindow::parseCmdLineArgs( ) +{ + QString filename = QString::null, search_query = QString::null; + QString search_index = QString::null, search_bookmark = QString::null; + bool do_autotest = false; + +#if defined (USE_KDE) + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if ( args->isSet("autotestmode") ) + do_autotest = true; + + if ( args->isSet("shortautotestmode") ) + do_autotest = m_useShortAutotest = true; + + search_query = args->getOption ("search"); + search_index = args->getOption ("sindex"); + + if ( args->count() > 0 ) + filename = args->arg(0); +#else + // argv[0] in Qt is still a program name + for ( int i = 1; i < qApp->argc(); i++ ) + { + if ( !strcmp (qApp->argv()[i], "--autotestmode") ) + do_autotest = m_useShortAutotest = true; + else if ( !strcmp (qApp->argv()[i], "--shortautotestmode") ) + do_autotest = true; + else if ( !strcmp (qApp->argv()[i], "--search") ) + search_query = qApp->argv()[++i]; + else if ( !strcmp (qApp->argv()[i], "--sindex") ) + search_index = qApp->argv()[++i]; + else if ( !strcmp (qApp->argv()[i], "-h") || !strcmp (qApp->argv()[i], "--help") ) + { + fprintf (stderr, "Usage: %s [chmfile]\n", qApp->argv()[0]); + exit (1); + } + else + filename = qApp->argv()[i]; + } +#endif + + if ( !filename.isEmpty() ) + { + if ( !loadChmFile( QString::fromLocal8Bit( filename )) ) + return true; // skip the latest checks, but do not exit from the program + + if ( !search_index.isEmpty() ) + { + QStringList event_args; + event_args.push_back( search_index ); + qApp->postEvent( this, new KCHMUserEvent( "findInIndex", event_args ) ); + } + + if ( !search_query.isEmpty() ) + { + QStringList event_args; + event_args.push_back( search_query ); + qApp->postEvent( this, new KCHMUserEvent( "searchQuery", event_args ) ); + } + + if ( do_autotest ) + { +#if defined (ENABLE_AUTOTEST_SUPPORT) + if ( filename.isEmpty() ) + qFatal ("Could not use Auto Test mode without a chm file!"); + + m_autoteststate = STATE_INITIAL; + showMinimized (); + runAutoTest(); +#else + qFatal ("Auto Test mode support is not compiled in."); +#endif /* defined (ENABLE_AUTOTEST_SUPPORT) */ + } + return true; + } + + return false; +} + +void KCHMMainWindow::slotBrowserSelectAll( ) +{ + currentBrowser()->clipSelectAll(); +} + +void KCHMMainWindow::slotBrowserCopy( ) +{ + currentBrowser()->clipCopy(); +} + +void KCHMMainWindow::slotChangeSettingsMenuItemActivated() +{ + KCHMSetupDialog dlg ( this ); + + // Set up the parameters + dlg.m_radioOnBeginOpenDialog->setChecked ( !appConfig.m_LoadLatestFileOnStartup ); + dlg.m_radioOnBeginOpenLast->setChecked ( appConfig.m_LoadLatestFileOnStartup ); + dlg.m_historySize->setValue ( appConfig.m_HistorySize ); + dlg.m_rememberHistoryInfo->setChecked ( appConfig.m_HistoryStoreExtra ); + + dlg.m_radioExtLinkOpenAlways->setChecked ( appConfig.m_onExternalLinkClick == KCHMConfig::ACTION_ALWAYS_OPEN ); + dlg.m_radioExtLinkAsk->setChecked ( appConfig.m_onExternalLinkClick == KCHMConfig::ACTION_ASK_USER ); + dlg.m_radioExtLinkOpenNever->setChecked ( appConfig.m_onExternalLinkClick == KCHMConfig::ACTION_DONT_OPEN ); + + dlg.m_radioNewChmOpenAlways->setChecked ( appConfig.m_onNewChmClick == KCHMConfig::ACTION_ALWAYS_OPEN ); + dlg.m_radioNewChmAsk->setChecked ( appConfig.m_onNewChmClick == KCHMConfig::ACTION_ASK_USER ); + dlg.m_radioNewChmOpenNever->setChecked ( appConfig.m_onNewChmClick == KCHMConfig::ACTION_DONT_OPEN ); + +#if defined (USE_KDE) + dlg.m_groupQtsettings->setEnabled ( false ); + dlg.m_groupKDEsettings->setEnabled ( true ); +#else + dlg.m_groupQtsettings->setEnabled ( true ); + dlg.m_groupKDEsettings->setEnabled ( false ); +#endif + + dlg.m_qtBrowserPath->setText ( appConfig.m_QtBrowserPath ); + dlg.m_radioUseQtextBrowser->setChecked ( appConfig.m_kdeUseQTextBrowser ); + dlg.m_radioUseKHTMLPart->setChecked ( !appConfig.m_kdeUseQTextBrowser ); + + dlg.m_enableJS->setChecked ( appConfig.m_kdeEnableJS ); + dlg.m_enablePlugins->setChecked ( appConfig.m_kdeEnablePlugins ); + dlg.m_enableJava->setChecked ( appConfig.m_kdeEnableJava ); + dlg.m_enableRefresh->setChecked ( appConfig.m_kdeEnableRefresh ); + + dlg.m_advExternalProgramName->setText( appConfig.m_advExternalEditorPath ); + dlg.m_advViewSourceExternal->setChecked ( !appConfig.m_advUseInternalEditor ); + dlg.m_advViewSourceInternal->setChecked ( appConfig.m_advUseInternalEditor ); + + // Search engine + dlg.m_useSearchEngineInternal->setChecked( appConfig.m_useSearchEngine == KCHMConfig::SEARCH_USE_CHM ); + dlg.m_useSearchEngineNew->setChecked( appConfig.m_useSearchEngine == KCHMConfig::SEARCH_USE_MINE ); + + // Connect buddies + dlg.m_labelUseSearchEngineInternal->setBuddy( dlg.m_useSearchEngineInternal ); + dlg.m_labelUseSearchEngineNew->setBuddy( dlg.m_useSearchEngineNew ); + + if ( dlg.exec() == QDialog::Accepted ) + { + appConfig.m_LoadLatestFileOnStartup = dlg.m_radioOnBeginOpenLast->isChecked(); + appConfig.m_HistorySize = dlg.m_historySize->value(); + appConfig.m_HistoryStoreExtra = dlg.m_rememberHistoryInfo->isChecked(); + + if ( dlg.m_radioExtLinkOpenAlways->isChecked () ) + appConfig.m_onExternalLinkClick = KCHMConfig::ACTION_ALWAYS_OPEN; + else if ( dlg.m_radioExtLinkAsk->isChecked () ) + appConfig.m_onExternalLinkClick = KCHMConfig::ACTION_ASK_USER; + else + appConfig.m_onExternalLinkClick = KCHMConfig::ACTION_DONT_OPEN; + + if ( dlg.m_radioNewChmOpenAlways->isChecked () ) + appConfig.m_onNewChmClick = KCHMConfig::ACTION_ALWAYS_OPEN; + else if ( dlg.m_radioNewChmAsk->isChecked () ) + appConfig.m_onNewChmClick = KCHMConfig::ACTION_ASK_USER; + else + appConfig.m_onNewChmClick = KCHMConfig::ACTION_DONT_OPEN; + + appConfig.m_QtBrowserPath = dlg.m_qtBrowserPath->text(); + + // Check the changes + bool need_restart = false; + + if ( appConfig.m_kdeEnableJS != dlg.m_enableJS->isChecked() ) + { + need_restart = true; + appConfig.m_kdeEnableJS = dlg.m_enableJS->isChecked(); + } + + if ( appConfig.m_kdeEnablePlugins != dlg.m_enablePlugins->isChecked() ) + { + need_restart = true; + appConfig.m_kdeEnablePlugins = dlg.m_enablePlugins->isChecked(); + } + + if ( appConfig.m_kdeEnableJava != dlg.m_enableJava->isChecked() ) + { + need_restart = true; + appConfig.m_kdeEnableJava = dlg.m_enableJava->isChecked(); + } + + if ( appConfig.m_kdeEnableRefresh != dlg.m_enableRefresh->isChecked() ) + { + need_restart = true; + appConfig.m_kdeEnableRefresh = dlg.m_enableRefresh->isChecked(); + } + + if ( appConfig.m_kdeUseQTextBrowser != dlg.m_radioUseQtextBrowser->isChecked() ) + { + need_restart = true; + appConfig.m_kdeUseQTextBrowser = dlg.m_radioUseQtextBrowser->isChecked(); + } + + if ( dlg.m_useSearchEngineNew->isChecked() + && appConfig.m_useSearchEngine == KCHMConfig::SEARCH_USE_CHM ) + { + appConfig.m_useSearchEngine = KCHMConfig::SEARCH_USE_MINE; + m_searchWindow->invalidate(); + } + + if ( dlg.m_useSearchEngineInternal->isChecked() + && appConfig.m_useSearchEngine == KCHMConfig::SEARCH_USE_MINE ) + { + appConfig.m_useSearchEngine = KCHMConfig::SEARCH_USE_CHM; + m_searchWindow->invalidate(); + } + + appConfig.m_advExternalEditorPath = dlg.m_advExternalProgramName->text(); + appConfig.m_advUseInternalEditor = dlg.m_advViewSourceExternal->isChecked(); + appConfig.m_advUseInternalEditor = dlg.m_advViewSourceInternal->isChecked(); + + appConfig.save(); + + if ( need_restart ) + QMessageBox::information( + this, + APP_NAME, + i18n( "Changing browser view options or search engine used\n" + "requires restarting the application to take effect." ) ); + } +} + + +void KCHMMainWindow::setupSignals( ) +{ +#if defined(HAVE_SIGACTION) + struct sigaction sa; + memset ((char *)&sa, 0, sizeof(sa)); + sigemptyset (&sa.sa_mask); + sigaddset (&sa.sa_mask, SIGCHLD); + +#ifdef SA_RESTART + sa.sa_flags = SA_RESTART; +#endif + + sa.sa_handler = SIG_IGN; + sigaction (SIGCHLD, &sa, (struct sigaction *)0); +#else /* !HAVE_SIGACTION */ + signal (SIGCHLD, SIG_IGN); +#endif /* HAVE_SIGACTION */ +} + +void KCHMMainWindow::slotHistoryMenuItemActivated( int item ) +{ + if ( item < 0 || item >= (signed) appConfig.m_History.size() ) + qFatal ("KCHMMainWindow::slotHistoryMenuItemActivated: bad history menu id %d", item); + + QString filename = appConfig.m_History[item]; + + // remove it, so it will be added again at the history top, and will not shitf anything. +// appConfig.m_History.remove ( appConfig.m_History.begin() + item); + loadChmFile ( filename ); +} + +void KCHMMainWindow::updateHistoryMenu() +{ + m_menuHistory->clear (); + + for ( int i = appConfig.m_History.size() - 1; i >= 0; i-- ) + m_menuHistory->insertItem( appConfig.m_History[i], i ); +} + +void KCHMMainWindow::slotActivateContentTab( ) +{ + if ( m_tabContextPage != -1 ) + m_tabWidget->setCurrentPage( m_tabContextPage ); +} + +void KCHMMainWindow::slotActivateIndexTab( ) +{ + if ( m_tabIndexPage != -1 ) + m_tabWidget->setCurrentPage( m_tabIndexPage ); +} + +void KCHMMainWindow::slotActivateSearchTab( ) +{ + if ( m_tabSearchPage != -1 ) + m_tabWidget->setCurrentPage( m_tabSearchPage ); +} + +void KCHMMainWindow::slotActivateBookmarkTab( ) +{ + m_tabWidget->setCurrentPage( m_tabBookmarkPage ); +} + +void KCHMMainWindow::showOrHideContextWindow( int tabindex ) +{ + if ( tabindex == -1 ) + { + if ( m_contentsWindow ) + { + m_tabWidget->removePage (m_contentsWindow); + delete m_contentsWindow; + m_contentsWindow = 0; + } + } + else + { + if ( !m_contentsWindow ) + { + m_contentsWindow = new KCHMContentsWindow( m_tabWidget ); + + // Handle clicking on m_contentsWindow element + connect( m_contentsWindow, + SIGNAL( clicked( QListViewItem* ) ), + this, + SLOT( slotOnTreeClicked( QListViewItem* ) ) ); + + connect( m_contentsWindow, + SIGNAL( doubleClicked ( QListViewItem *, const QPoint &, int ) ), + this, + SLOT( slotOnTreeDoubleClicked ( QListViewItem *, const QPoint &, int ) ) ); + + m_tabWidget->insertTab (m_contentsWindow, i18n( "Contents" ), tabindex); + } + } +} + +void KCHMMainWindow::showOrHideIndexWindow( int tabindex ) +{ + // Test whether to show/invalidate the index window + if ( tabindex == -1 ) + { + if ( m_indexWindow ) + { + m_tabWidget->removePage (m_indexWindow); + delete m_indexWindow; + m_indexWindow = 0; + } + } + else + { + if ( !m_indexWindow ) + { + m_indexWindow = new KCHMIndexWindow (m_tabWidget); + m_tabWidget->insertTab (m_indexWindow, i18n( "Index" ), tabindex); + } + else + m_indexWindow->invalidate(); + } +} + +void KCHMMainWindow::showOrHideSearchWindow( int tabindex ) +{ + if ( tabindex == -1 ) + { + if ( m_searchWindow ) + { + m_tabWidget->removePage (m_searchWindow); + delete m_searchWindow; + m_searchWindow = 0; + } + } + else + { + if ( !m_searchWindow ) + { + m_searchWindow = new KCHMSearchWindow (m_tabWidget); + m_tabWidget->insertTab (m_searchWindow, i18n( "Search" ), tabindex); + } + else + m_searchWindow->invalidate(); + } +} + +void KCHMMainWindow::slotEnableFullScreenMode( bool enable ) +{ + if ( enable ) + { + if ( !isFullScreen() ) + { + showFullScreen (); + menuBar()->hide(); + statusBar()->hide(); + } + } + else + { + if ( isFullScreen() ) + { + showNormal (); + menuBar()->show(); + statusBar()->show(); + } + } +} + +void KCHMMainWindow::slotShowContentsWindow( bool show ) +{ + if ( show ) + m_tabWidget->show(); + else + m_tabWidget->hide(); +} + +void KCHMMainWindow::slotToggleFullScreenMode( ) +{ + slotEnableFullScreenMode( !isFullScreen() ); +} + +void KCHMMainWindow::slotLocateInContentWindow( ) +{ + // There may be no content tab at all + if ( !m_contentsWindow || m_tabContextPage == -1 ) + return; + + // Activate a content tab + m_tabWidget->setCurrentPage( m_tabContextPage ); + + if ( m_contentsWindow ) + { + // Open all the tree items to show current item (if needed) + KCHMIndTocItem * treeitem = m_contentsWindow->getTreeItem( currentBrowser()->getOpenedPage() ); + + if ( treeitem ) + { + KCHMIndTocItem * itemparent = treeitem; + + while ( (itemparent = (KCHMIndTocItem*) itemparent->parent()) != 0 ) + itemparent->setOpen(true); + + m_contentsWindow->setCurrentItem (treeitem); + m_contentsWindow->ensureItemVisible (treeitem); + } + else + statusBar()->message( i18n( "Could not locate opened topic in content window"), 2000 ); + } +} + +void KCHMMainWindow::slotExtractCHM( ) +{ + QStringList files; + +#if defined (USE_KDE) + QString outdir = KFileDialog::getExistingDirectory ( + QString::null, + this, + i18n("Choose a directory to store CHM content") ); +#else + QString outdir = QFileDialog::getExistingDirectory ( + QString::null, + this, + 0, + i18n("Choose a directory to store CHM content"), + TRUE ); +#endif + + if ( outdir.isEmpty() ) + return; + + outdir += "/"; + + // Enumerate all the files in archive + if ( !m_chmFile || !m_chmFile->enumerateFiles( &files ) ) + return; + + KQProgressModalDialog progress( i18n("Extracting CHM content"), i18n("Extracting files..."), i18n("Abort"), files.size(), this ); + + for ( unsigned int i = 0; i < files.size(); i++ ) + { + progress.setProgress( i ); + + if ( (i % 3) == 0 ) + { + qApp->processEvents(); + + if ( progress.wasCancelled() ) + break; + } + + // Extract the file + QByteArray buf; + + if ( m_chmFile->getFileContentAsBinary( &buf, files[i] ) ) + { + // Split filename to get the list of subdirectories + QStringList dirs = QStringList::split( '/', files[i] ); + + // Walk through the list of subdirectories, and create them if needed + // dirlevel is used to detect extra .. and prevent overwriting files + // outside the directory (like creating the file images/../../../../../etc/passwd + unsigned int i, dirlevel = 0; + QStringList dirlist; + + for ( i = 0; i < dirs.size() - 1; i++ ) + { + // Skip .. which lead too far above + if ( dirs[i] == ".." ) + { + if ( dirlevel > 0 ) + { + dirlevel--; + dirlist.pop_back(); + } + } + else + { + dirlist.push_back( dirs[i] ); + + QDir dir ( outdir + dirlist.join( "/" ) ); + if ( !dir.exists() ) + { + if ( !dir.mkdir( dir.path() ) ) + qWarning( "Could not create subdir %s\n", dir.path().ascii() ); + } + } + } + + QString filename = outdir + dirlist.join( "/" ) + "/" + dirs[i]; + QFile wf( filename ); + if ( !wf.open( IO_WriteOnly ) ) + { + qWarning( "Could not write file %s\n", filename.ascii() ); + continue; + } + + wf. writeBlock( buf ); + wf.close(); + } + else + qWarning( "Could not get file %s\n", files[i].ascii() ); + } + + progress.setProgress( files.size() ); +} + +KCHMViewWindow * KCHMMainWindow::currentBrowser( ) const +{ + return m_viewWindowMgr->current(); +} + +void KCHMMainWindow::slotOpenPageInNewTab( ) +{ + openPage( currentBrowser()->getNewTabLink(), OPF_NEW_TAB | OPF_CONTENT_TREE ); +} + +void KCHMMainWindow::slotOpenPageInNewBackgroundTab( ) +{ + openPage( currentBrowser()->getNewTabLink(), OPF_NEW_TAB | OPF_BACKGROUND ); +} + +void KCHMMainWindow::slotBrowserChanged( KCHMViewWindow * newbrowser ) +{ + locateInContentTree( newbrowser->getOpenedPage() ); +} + +void KCHMMainWindow::locateInContentTree( const QString & url ) +{ + if ( !m_contentsWindow ) + return; + + KCHMIndTocItem * treeitem = m_contentsWindow->getTreeItem( url ); + + if ( treeitem ) + { + KCHMIndTocItem * itemparent = treeitem; + while ( (itemparent = (KCHMIndTocItem*) itemparent->parent()) != 0 ) + itemparent->setOpen(true); + + m_contentsWindow->setCurrentItem (treeitem); + m_contentsWindow->ensureItemVisible (treeitem); + } +} + +void KCHMMainWindow::slotOnTreeDoubleClicked( QListViewItem * item, const QPoint &, int ) +{ + // Open/close only existing item which have children + if ( !item || item->childCount() == 0 ) + return; + + item->setOpen( !item->isOpen() ); + item->repaint(); +} + +bool KCHMMainWindow::event( QEvent * e ) +{ + if ( e->type() == QEvent::User ) + return handleUserEvent( (KCHMUserEvent*) e ); + + return QWidget::event( e ); +} + +bool KCHMMainWindow::handleUserEvent( const KCHMUserEvent * event ) +{ + if ( event->m_action == "loadAndOpen" ) + { + if ( event->m_args.size() != 1 && event->m_args.size() != 2 ) + qFatal("handleUserEvent: event loadAndOpen must receive 1 or 2 args"); + + QString chmfile = event->m_args[0]; + QString openurl = event->m_args.size() > 1 ? event->m_args[1] : "/"; + + return loadChmFile( chmfile, false ) && openPage( openurl ); + } + else if ( event->m_action == "openPage" ) + { + if ( event->m_args.size() != 1 ) + qFatal("handleUserEvent: event openPage must receive 1 arg"); + + return openPage( event->m_args[0] ); + } + else if ( event->m_action == "findInIndex" ) + { + if ( event->m_args.size() != 1 ) + qFatal( "handleUserEvent: event searchQuery must receive 1 arg" ); + + if ( m_tabIndexPage == -1 ) + return false; + + slotActivateIndexTab(); + m_indexWindow->search( event->m_args[0] ); + return true; + } + else if ( event->m_action == "searchQuery" ) + { + if ( event->m_args.size() != 1 ) + qFatal( "handleUserEvent: event searchQuery must receive 1 arg" ); + + if ( m_tabSearchPage == -1 ) + return false; + + slotActivateSearchTab(); + m_searchWindow->execSearchQueryInGui( event->m_args[0] ); + return true; + } + else + qWarning( "Unknown user event received: %s", event->m_action.ascii() ); + + return false; +} + + +#if defined (ENABLE_AUTOTEST_SUPPORT) +void KCHMMainWindow::runAutoTest() +{ + KCHMIndTocItem * item; + + switch (m_autoteststate) + { + case STATE_INITIAL: + if ( m_contentsWindow && !m_useShortAutotest ) + { + m_autotestlistiterator = QListViewItemIterator (m_contentsWindow); + m_autoteststate = STATE_CONTENTS_OPENNEXTPAGE; + } + else + m_autoteststate = STATE_OPEN_INDEX; + + QTimer::singleShot (500, this, SLOT(runAutoTest()) ); + break; // allow to finish the initialization sequence + + case STATE_CONTENTS_OPENNEXTPAGE: + if ( (item = (KCHMIndTocItem *) m_autotestlistiterator.current()) != 0 ) + { + openPage( item->getUrl(), OPF_CONTENT_TREE | OPF_ADD2HISTORY ); + m_autotestlistiterator++; + } + else + m_autoteststate = STATE_OPEN_INDEX; + + QTimer::singleShot (50, this, SLOT(runAutoTest()) ); + break; + + case STATE_OPEN_INDEX: + if ( m_indexWindow ) + m_tabWidget->setCurrentPage (1); + + m_autoteststate = STATE_SHUTDOWN; + QTimer::singleShot (500, this, SLOT(runAutoTest()) ); + break; + + case STATE_SHUTDOWN: + qApp->quit(); + break; + + default: + break; + } +} +#endif /* defined (ENABLE_AUTOTEST_SUPPORT) */ + + +//TODO: KDE: xmlgui +//TODO: Search result highlighting -- cgit v1.2.3