• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kviewshell

kmultipage.cpp

Go to the documentation of this file.
00001 #include <config.h>
00002 
00003 #include <kaction.h>
00004 #include <kapplication.h>
00005 #include <kdebug.h>
00006 #include <kfiledialog.h>
00007 #include <kiconloader.h>
00008 #include <kio/job.h>
00009 #include <klocale.h>
00010 #include <kmessagebox.h>
00011 #include <kprinter.h>
00012 #include <kstdaction.h>
00013 #include <qobject.h>
00014 #include <qlayout.h>
00015 #include <qpaintdevicemetrics.h>
00016 #include <qprogressdialog.h>
00017 #include <qsplitter.h>
00018 #include <qurl.h>
00019 #include <qtoolbox.h>
00020 #include <qvbox.h>
00021 
00022 #include "documentWidget.h"
00023 #include "marklist.h"
00024 #include "tableOfContents.h"
00025 #include "kprintDialogPage_pageoptions.h"
00026 #include "kvsprefs.h"
00027 #include "kmultipage.h"
00028 #include "pageNumber.h"
00029 #include "renderedDocumentPagePrinter.h"
00030 #include "searchWidget.h"
00031 #include "textBox.h"
00032 #include "zoomlimits.h"
00033 
00034 
00035 //#define DEBUG_KMULTIPAGE
00036 
00037 KMultiPage::KMultiPage(QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name)
00038   : DCOPObject("kmultipage"), KParts::ReadOnlyPart(parent, name)
00039 {
00040   // For reasons which I don't understand, the initialization of the
00041   // DCOPObject above does not work properly, the name is ignored. It
00042   // works fine if we repeat the name here. -- Stefan Kebekus
00043   // This is because of the virtual inheritance. Get rid of it (but it's BC, and this is a lib...) -- DF
00044   setObjId("kmultipage");
00045 
00046   parentWdg = parentWidget;
00047   lastCurrentPage = 0;
00048   timer_id = -1;
00049   searchInProgress = false;
00050   
00051   QVBox* verticalBox = new QVBox(parentWidget);
00052   verticalBox->setFocusPolicy(QWidget::StrongFocus);
00053   setWidget(verticalBox);
00054   
00055   splitterWidget = new QSplitter(verticalBox, widgetName);
00056   splitterWidget->setOpaqueResize(false);
00057   splitterWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
00058   
00059   // Create SideBar
00060   sideBar = new QToolBox(splitterWidget, "sidebar");
00061   
00062   // Create ContentsList
00063   tableOfContents = new TableOfContents(sideBar);
00064   sideBar->addItem(tableOfContents, QIconSet(SmallIcon("contents")), i18n("Contents"));
00065 
00066   connect(tableOfContents, SIGNAL(gotoPage(const Anchor&)), this, SLOT(gotoPage(const Anchor&)));
00067   
00068   // Create MarkList
00069   _markList = new MarkList(sideBar, "marklist");
00070   sideBar->addItem(_markList, QIconSet(SmallIcon("thumbnail")), i18n("Thumbnails"));
00071 
00072   // Restore state of the sidebar
00073   sideBar->setCurrentItem(sideBar->item(KVSPrefs::sideBarItem()));
00074 
00075   splitterWidget->setResizeMode(sideBar, QSplitter::KeepSize);
00076 
00077   connect(_markList, SIGNAL(selected(const PageNumber&)), this, SLOT(gotoPage(const PageNumber&)));
00078 
00079   _scrollView = new PageView(splitterWidget, widgetName);
00080 
00081   // Create Search Panel
00082   searchWidget = new SearchWidget(verticalBox);
00083   searchWidget->hide();
00084   connect(searchWidget, SIGNAL(findNextText()), this, SLOT(findNextText()));
00085   connect(searchWidget, SIGNAL(findPrevText()), this, SLOT(findPrevText()));
00086 
00087   sideBar->setMinimumWidth(80);
00088   sideBar->setMaximumWidth(300);
00089 
00090   connect(_scrollView, SIGNAL(currentPageChanged(const PageNumber&)), this, SLOT(setCurrentPageNumber(const PageNumber&)));
00091   connect(_scrollView, SIGNAL(viewSizeChanged(const QSize&)), scrollView(), SLOT(calculateCurrentPageNumber()));
00092   connect(_scrollView, SIGNAL(wheelEventReceived(QWheelEvent *)), this, SLOT(wheelEvent(QWheelEvent*)));
00093 
00094   connect(this, SIGNAL(enableMoveTool(bool)), _scrollView, SLOT(slotEnableMoveTool(bool)));
00095 
00096   splitterWidget->setCollapsible(sideBar, false);
00097   splitterWidget->setSizes(KVSPrefs::guiLayout());
00098 
00099   connect(searchWidget, SIGNAL(searchEnabled(bool)), this, SIGNAL(searchEnabled(bool)));
00100   connect(searchWidget, SIGNAL(stopSearch()), this, SLOT(stopSearch()));
00101 }
00102 
00103 
00104 KMultiPage::~KMultiPage()
00105 {
00106   writeSettings();
00107 
00108   if (timer_id != -1)
00109     killTimer(timer_id);
00110 
00111   delete pageCache;
00112 }
00113 
00114 void KMultiPage::readSettings()
00115 {
00116 }
00117 
00118 void KMultiPage::writeSettings()
00119 {
00120   // Save TOC layout
00121   tableOfContents->writeSettings();
00122 
00123   KVSPrefs::setGuiLayout(splitterWidget->sizes());
00124   // Save state of the sidebar
00125   KVSPrefs::setSideBarItem(sideBar->indexOf(sideBar->currentItem()));
00126   KVSPrefs::writeConfig();
00127 }
00128 
00129 QString KMultiPage::name_of_current_file()
00130 {
00131   return m_file;
00132 }
00133 
00134 bool KMultiPage::is_file_loaded(const QString& filename)
00135 {
00136   return (filename == m_file);
00137 }
00138 
00139 void KMultiPage::slotSave_defaultFilename()
00140 {
00141   slotSave();
00142 }
00143 
00144 void KMultiPage::slotSave()
00145 {
00146   // Try to guess the proper ending...
00147   QString formats;
00148   QString ending;
00149   int rindex = m_file.findRev(".");
00150   if (rindex == -1) {
00151     ending = QString::null;
00152     formats = QString::null;
00153   } else {
00154     ending = m_file.mid(rindex); // e.g. ".dvi"
00155     formats = fileFormats().grep(ending).join("\n");
00156   }
00157 
00158   QString fileName = KFileDialog::getSaveFileName(QString::null, formats, 0, i18n("Save File As"));
00159 
00160   if (fileName.isEmpty())
00161     return;
00162 
00163   // Add the ending to the filename. I hope the user likes it that
00164   // way.
00165   if (!ending.isEmpty() && fileName.find(ending) == -1)
00166     fileName = fileName+ending;
00167 
00168   if (QFile(fileName).exists()) {
00169     int r = KMessageBox::warningContinueCancel (0, i18n("The file %1\nexists. Shall I overwrite that file?").arg(fileName),
00170                        i18n("Overwrite File"), i18n("Overwrite"));
00171     if (r == KMessageBox::Cancel)
00172       return;
00173   }
00174 
00175   KIO::Job *job = KIO::file_copy( KURL( m_file ), KURL( fileName ), 0600, true, false, true );
00176   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotIOJobFinished ( KIO::Job * ) ) );
00177 
00178   return;
00179 }
00180 
00181 
00182 void KMultiPage::setFile(bool)
00183 {
00184   return;
00185 }
00186 
00187 
00188 bool KMultiPage::closeURL()
00189 {
00190 #ifdef DEBUG_KMULTIPAGE
00191   kdDebug(1233) << "KMultiPage::closeURL()" << endl;
00192 #endif
00193 
00194   if (renderer.isNull())
00195     return false;
00196   
00197   // Clear navigation history.
00198   document_history.clear();
00199   
00200   // Close the file.
00201   renderer->setFile(QString::null, KURL());
00202   renderer->clear();
00203   
00204   // Delete Page Widgets.
00205   widgetList.setAutoDelete(true);
00206   widgetList.resize(0);
00207   widgetList.setAutoDelete(false);
00208   
00209   // Update ScrollView.
00210   scrollView()->layoutPages();
00211   enableActions(false);
00212   
00213   // Clear Thumbnail List.
00214   markList()->clear();
00215   
00216   // Clear Table of Contents
00217   tableOfContents->clear();
00218   
00219   // Clear Status Bar
00220   emit setStatusBarText(QString::null);
00221 
00222   return true;
00223 }
00224 
00225 void KMultiPage::slotIOJobFinished ( KIO::Job *job )
00226 {
00227   if ( job->error() )
00228     job->showErrorDialog( 0L );
00229 }
00230 
00231 void KMultiPage::slotShowScrollbars(bool status)
00232 {
00233   _scrollView->slotShowScrollbars(status);
00234 }
00235 
00236 void KMultiPage::slotShowSidebar(bool show)
00237 {
00238   if (show)
00239     sideBar->show();
00240   else
00241     sideBar->hide();
00242 }
00243 
00244 void KMultiPage::slotShowThumbnails(bool show)
00245 {
00246   markList()->slotShowThumbnails(show);
00247 }
00248 
00249 void KMultiPage::slotSetFullPage(bool fullpage)
00250 {
00251   _scrollView->setFullScreenMode(fullpage);
00252   if (fullpage)
00253     slotShowSidebar(false);
00254 }
00255 
00256 void KMultiPage::preferencesChanged()
00257 {
00258   // We need to read the config options otherwise the KVSPrefs-object would
00259   // not be syncronized between the kviewpart and the kmultipage.
00260   KVSPrefs::self()->readConfig();
00261 
00262   slotShowThumbnails(KVSPrefs::showThumbnails());
00263 
00264   // if we are in overviewmode and the number of columns or rows has changed
00265   if (scrollView()->overviewMode() &&
00266       (scrollView()->getNrColumns() != KVSPrefs::overviewModeColumns() ||
00267        scrollView()->getNrRows() != KVSPrefs::overviewModeRows()))
00268   {
00269     setViewMode(KVSPrefs::EnumViewMode::Overview);
00270   }
00271 
00272   if (KVSPrefs::changeColors() && KVSPrefs::renderMode() == KVSPrefs::EnumRenderMode::Paper)
00273     renderer->setAccessibleBackground(true, KVSPrefs::paperColor());
00274   else
00275     renderer->setAccessibleBackground(false);
00276 
00277   renderModeChanged();
00278 }
00279 
00280 void KMultiPage::setViewMode(int mode)
00281 {
00282 #ifdef DEBUG_KMULTIPAGE
00283   kdDebug(1233) << "KMultiPage::setViewMode(" << mode << ")" << endl;
00284 #endif
00285   // Save the current page number because when we are changing the columns
00286   // and rows in the scrollview the currently shown Page probably out of view.
00287   PageNumber currentPage = currentPageNumber();
00288 
00289   // Save viewMode for future uses of KViewShell
00290   switch (mode) 
00291   {
00292     case KVSPrefs::EnumViewMode::SinglePage:
00293       KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::SinglePage);
00294 
00295       // Don't do anything if the view mode is already set
00296       if ((scrollView()->getNrColumns() == 1) && (scrollView()->getNrRows() == 1) && (scrollView()->isContinuous() == false))
00297         return;
00298       
00299       scrollView()->setNrColumns(1);
00300       scrollView()->setNrRows(1);
00301       scrollView()->setContinuousViewMode(false);
00302       // We scroll the view to the top, so that top and not the bottom
00303       // of the visible page is shown.
00304       scrollView()->scrollTop();
00305       break;
00306     case KVSPrefs::EnumViewMode::ContinuousFacing:
00307       KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::ContinuousFacing);
00308 
00309       // Don't do anything if the view mode is already set
00310       if ((scrollView()->getNrColumns() == 2) && (scrollView()->getNrRows() == 1) && (scrollView()->isContinuous() == true))
00311         return;
00312 
00313       scrollView()->setNrColumns(2);
00314       scrollView()->setNrRows(1);
00315       scrollView()->setContinuousViewMode(true);
00316       break;
00317     case KVSPrefs::EnumViewMode::Overview:
00318       KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::Overview);
00319 
00320       // Don't do anything if the view mode is already set
00321       if ((scrollView()->getNrColumns() == KVSPrefs::overviewModeColumns()) && (scrollView()->getNrRows() == KVSPrefs::overviewModeRows()) && (scrollView()->isContinuous() == false))
00322         return;
00323 
00324       scrollView()->setNrColumns(KVSPrefs::overviewModeColumns());
00325       scrollView()->setNrRows(KVSPrefs::overviewModeRows());
00326       scrollView()->setContinuousViewMode(false);
00327       // We scroll the view to the top, so that top and not the bottom
00328       // of the visible tableau is shown.
00329       scrollView()->scrollTop();
00330       break;
00331     default:  //KVSPrefs::EnumViewMode::Continuous
00332       KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::Continuous);
00333 
00334       // Don't do anything if the view mode is already set
00335       if ((scrollView()->getNrColumns() == 1) && (scrollView()->getNrRows() == 1) && (scrollView()->isContinuous() == true))
00336         return;
00337       
00338       scrollView()->setNrColumns(1);
00339       scrollView()->setNrRows(1);
00340       scrollView()->setContinuousViewMode(true);
00341   }
00342   generateDocumentWidgets(currentPage);
00343   KVSPrefs::writeConfig();
00344   emit viewModeChanged();
00345 }
00346 
00347 void KMultiPage::initializePageCache()
00348 {
00349   pageCache = new DocumentPageCache();
00350 }
00351 
00352 DocumentWidget* KMultiPage::createDocumentWidget()
00353 {
00354   DocumentWidget* documentWidget = new DocumentWidget(scrollView()->viewport(), scrollView(), pageCache, "singlePageWidget");
00355   connect(documentWidget, SIGNAL(clearSelection()), this, SLOT(clearSelection()));
00356   connect(this, SIGNAL(enableMoveTool(bool)), documentWidget, SLOT(slotEnableMoveTool(bool)));
00357   return documentWidget;
00358 }
00359 
00360 
00361 void KMultiPage::generateDocumentWidgets(const PageNumber& _startPage)
00362 {
00363   PageNumber startPage = _startPage;
00364 #ifdef DEBUG_KMULTIPAGE
00365   kdDebug(1233) << "KMultiPage::generateDocumentWidgets(" << startPage << ")" << endl;
00366 #endif
00367 
00368   // Do nothing if no document is loaded.
00369   if (getRenderer().isNull() || getRenderer()->isEmpty())
00370     return;
00371 
00372   // This function is only called with an invalid pagenumber, when
00373   // the file has been loaded or reloaded.
00374   bool reload = !startPage.isValid();
00375 
00376   if (reload)
00377   {
00378     // Find the number of the current page, for later use.
00379     startPage = currentPageNumber();
00380   }
00381 
00382   // Make sure that startPage is in the permissible range.
00383   if (startPage < 1)
00384     startPage = 1;
00385   if (startPage > numberOfPages())
00386     startPage = numberOfPages();
00387 
00388   unsigned int tableauStartPage = startPage;
00389 
00390   // Find out how many widgets are needed, and resize the widgetList accordingly.
00391   widgetList.setAutoDelete(true);
00392   Q_UINT16 oldwidgetListSize = widgetList.size();
00393   if (numberOfPages() == 0)
00394     widgetList.resize(0);
00395   else
00396   {
00397     switch (KVSPrefs::viewMode())
00398     {
00399       case KVSPrefs::EnumViewMode::SinglePage:
00400         widgetList.resize(1);
00401         break;
00402       case KVSPrefs::EnumViewMode::Overview:
00403       {
00404         // Calculate the number of pages shown in overview mode.
00405         unsigned int visiblePages = KVSPrefs::overviewModeColumns() * KVSPrefs::overviewModeRows();
00406         // Calculate the number of the first page in the tableau.
00407         tableauStartPage = startPage - ((startPage - 1) % visiblePages);
00408         // We cannot have more widgets then pages in the document.
00409         visiblePages = QMIN(visiblePages, numberOfPages() - tableauStartPage + 1);
00410         if (widgetList.size() != visiblePages)
00411           widgetList.resize(visiblePages);
00412         break;
00413       }
00414       default:
00415         // In KVS_Continuous and KVS_ContinuousFacing all pages in the document are shown.
00416         widgetList.resize(numberOfPages());
00417     }
00418   }
00419   bool isWidgetListResized = (widgetList.size() != oldwidgetListSize);
00420   widgetList.setAutoDelete(false);
00421 
00422   // If the widgetList is empty, there is nothing left to do.
00423   if (widgetList.size() == 0) {
00424     scrollView()->addChild(&widgetList);
00425     return;
00426   }
00427 
00428   // Allocate DocumentWidget structures so that all entries of
00429   // widgetList point to a valid DocumentWidget.
00430   DocumentWidget *documentWidget;
00431   for(Q_UINT16 i=0; i<widgetList.size(); i++) {
00432     documentWidget = widgetList[i];
00433     if (documentWidget == 0) {
00434       documentWidget = createDocumentWidget();
00435 
00436       widgetList.insert(i, documentWidget);
00437       documentWidget->show();
00438 
00439       connect(documentWidget, SIGNAL(localLink(const QString &)), this, SLOT(handleLocalLink(const QString &)));
00440       connect(documentWidget, SIGNAL(setStatusBarText(const QString&)), this, SIGNAL(setStatusBarText(const QString&)) );
00441     }
00442   }
00443 
00444   // Set the page numbers for the newly allocated widgets. How this is
00445   // done depends on the viewMode.
00446   if (KVSPrefs::viewMode() == KVSPrefs::EnumViewMode::SinglePage) {
00447     // In KVS_SinglePage mode, any number between 1 and the maximum
00448     // number of pages is acceptable. If an acceptable value is found,
00449     // nothing is done, and otherwise '1' is set as a default.
00450     documentWidget = widgetList[0];
00451     if (documentWidget != 0) { // Paranoia safety check
00452       documentWidget->setPageNumber(startPage);
00453       documentWidget->update();
00454     } else
00455       kdError(4300) << "Zero-Pointer in widgetList in KMultiPage::generateDocumentWidgets()" << endl;
00456   } else {
00457     // In all other modes, the widgets will be numbered continuously,
00458     // starting from firstShownPage.
00459     for(Q_UINT16 i=0; i<widgetList.size(); i++) {
00460       documentWidget = widgetList[i];
00461       if (documentWidget != 0) // Paranoia safety check
00462       {
00463         if (KVSPrefs::viewMode() == KVSPrefs::EnumViewMode::Overview)
00464           documentWidget->setPageNumber(i+tableauStartPage);
00465         else
00466           documentWidget->setPageNumber(i+1);
00467       }
00468       else
00469         kdError(4300) << "Zero-Pointer in widgetList in KMultiPage::generateDocumentWidgets()" << endl;
00470     }
00471   }
00472 
00473   // Make the changes in the widgetList known to the scrollview. so
00474   // that the scrollview may update its contents.
00475   scrollView()->addChild(&widgetList);
00476 
00477   // If the number of widgets has changed, or the viewmode has been changed the widget 
00478   // that displays the current page may not be visible anymore. Bring it back into focus.
00479   if (isWidgetListResized || !reload)
00480     gotoPage(startPage);
00481 }
00482 
00483 
00484 bool KMultiPage::gotoPage(const PageNumber& page)
00485 {
00486   return gotoPage(page, 0, true);
00487 }
00488 
00489 
00490 bool KMultiPage::gotoPage(const PageNumber& page, int y, bool isLink)
00491 {
00492 #ifdef DEBUG_KMULTIPAGE
00493   kdDebug(1233) << "KMultiPage::gotoPage()" << endl;
00494 #endif
00495 
00496   if (widgetList.size() == 0) {
00497     kdError(4300) << "KMultiPage::gotoPage(" << page << ", y) called, but widgetList is empty" << endl;
00498     return false;
00499   }
00500 
00501   if (!page.isValid())
00502   {
00503     kdDebug(1223) << "KMultiPage::gotoPage(" << page << ") invalid pageNumber." << endl;
00504     return false;
00505   }
00506 
00507   if (isLink)
00508     document_history.add(page, y);
00509 
00510   DocumentWidget* pageWidget;
00511 
00512   // If we are in overview viewmode
00513   if (KVSPrefs::viewMode() == KVSPrefs::EnumViewMode::Overview)
00514   {
00515     unsigned int visiblePages = KVSPrefs::overviewModeColumns() * KVSPrefs::overviewModeRows();
00516     // Pagenumber of the first visibile Page in the current tableau
00517     unsigned int firstPage = ((DocumentWidget*)widgetList[0])->getPageNumber();
00518     // Pagenumber of the first page in the new tableau.
00519     unsigned int tableauStartPage = page + 1 - (page % visiblePages);
00520     // If these numbers arn't equal "page" is not in the current tableu.
00521     if (firstPage != tableauStartPage) // widgets need to be updated
00522     {
00523       if ((numberOfPages() - tableauStartPage + 1 < visiblePages) || (widgetList.size() < visiblePages))
00524       {
00525         // resize widgetList
00526         // the pages are also set correctly by "generateDocumentWidgets"
00527         generateDocumentWidgets(tableauStartPage);
00528       }
00529       else
00530       {
00531         // "page" is not shown in the scrollview, so we have to switch widgets.
00532         // Here we don't need to resize the widgetList.
00533         for (unsigned int i = 0; i < widgetList.size(); i++)
00534         {
00535           pageWidget = (DocumentWidget*)(widgetList[i]);
00536           if (pageWidget != 0)
00537             pageWidget->setPageNumber(tableauStartPage + i);
00538         }
00539         scrollView()->layoutPages();
00540       }
00541     }
00542     // move scrollview to "page".
00543     // Make the widget pageWidget visible in the scrollview. Somehow this
00544     // doesn't seem to trigger the signal contentsMoved in the
00545     // QScrollview, so that we better call setCurrentPage() ourselves.
00546     pageWidget = (DocumentWidget*)(widgetList[page % visiblePages]);
00547 
00548     scrollView()->moveViewportToWidget(pageWidget, y);
00549 
00550     // Set current page number.
00551     setCurrentPageNumber(page);
00552 
00553     return true;
00554   }
00555   else if (widgetList.size() == 1)
00556   {
00557     // If the widget list contains only a single element, then either
00558     // the document contains only one page, or we are in "single page"
00559     // view mode. In either case, we set the page number of the single
00560     // widget to 'page'
00561     pageWidget = (DocumentWidget*)(widgetList[0]);
00562 
00563     // Paranoia security check
00564     if (pageWidget == 0) {
00565       kdError(4300) << "KMultiPage::goto_Page() called with widgetList.size() == 1, but widgetList[0] == 0" << endl;
00566       return false;
00567     }
00568 
00569     if (pageCache->sizeOfPageInPixel(currentPageNumber()) == pageCache->sizeOfPageInPixel(page))
00570     {
00571       // We are rendering the page before we switch the widget to the new page.
00572       // To make a smooth transition. We only do this if the size of the current and new page are equal,
00573       // otherwise we would have to render the page twice, if autozoom is enabled.
00574       pageCache->getPage(page);
00575     }
00576 
00577     pageWidget->setPageNumber(page);
00578     scrollView()->layoutPages();
00579     scrollView()->moveViewportToWidget(pageWidget, y);
00580   } else {
00581     // There are multiple widgets, then we are either in the
00582     // "Continuous" or in the "Continouous-Facing" view mode. In that
00583     // case, we find the widget which is supposed to display page
00584     // 'page' and move the scrollview to make it visible
00585 
00586     // Paranoia security checks
00587     if (widgetList.size() < page) {
00588       kdError(4300) << "KMultiPage::goto_Page(page,y ) called with widgetList.size()=" << widgetList.size() << ", and page=" << page << endl;
00589       return false;
00590     }
00591     pageWidget = (DocumentWidget*)(widgetList[page-1]);
00592     if (pageWidget == 0) {
00593       kdError(4300) << "KMultiPage::goto_Page() called with widgetList.size() > 1, but widgetList[page] == 0" << endl;
00594       return false;
00595     }
00596 
00597     scrollView()->moveViewportToWidget(pageWidget, y);
00598   }
00599 
00600   if (isLink && y != 0)
00601     pageWidget->flash(y);
00602 
00603   // Set current page number.
00604   setCurrentPageNumber(page);
00605   return true;
00606 }
00607 
00608 
00609 void KMultiPage::handleLocalLink(const QString &linkText)
00610 {
00611 #ifdef DEBUG_SPECIAL
00612   kdDebug(4300) << "hit: local link to " << linkText << endl;
00613 #endif
00614 
00615   if (renderer.isNull()) {
00616     kdError(4300) << "KMultiPage::handleLocalLink( " << linkText << " ) called, but renderer==0" << endl;
00617     return;
00618   }
00619 
00620   QString locallink;
00621   if (linkText[0] == '#' )
00622     locallink = linkText.mid(1); // Drop the '#' at the beginning
00623   else
00624     locallink = linkText;
00625 
00626   Anchor anch = renderer->findAnchor(locallink);
00627 
00628   if (anch.isValid())
00629     gotoPage(anch);
00630   else {
00631     if (linkText[0] != '#' ) {
00632       // We could in principle use KIO::Netaccess::run() here, but
00633       // it is perhaps not a very good idea to allow a DVI-file to
00634       // specify arbitrary commands, such as "rm -rvf /". Using
00635       // the kfmclient seems to be MUCH safer.
00636       QUrl DVI_Url(m_file);
00637       QUrl Link_Url(DVI_Url, linkText, true);
00638 
00639       QStringList args;
00640       args << "openURL";
00641       args << Link_Url.toString();
00642       kapp->kdeinitExec("kfmclient", args);
00643     }
00644   }
00645 }
00646 
00647 void KMultiPage::setCurrentPageNumber(const PageNumber& page)
00648 {
00649 #ifdef DEBUG_KMULTIPAGE
00650   kdDebug(1233) << "KMultiPage::setCurrentPageNumber()" << endl;
00651 #endif
00652 
00653   if (page != currentPageNumber())
00654   {
00655     markList()->setCurrentPageNumber(page);
00656     emit pageInfo(numberOfPages(), currentPageNumber());
00657   }
00658 }
00659 
00660 PageNumber KMultiPage::currentPageNumber()
00661 {
00662   return markList()->currentPageNumber();
00663 }
00664 
00665 void KMultiPage::doGoBack()
00666 {
00667   HistoryItem *it = document_history.back();
00668   if (it != 0)
00669     gotoPage(it->page, it->ypos, false); // Do not add a history item.
00670   else
00671     kdDebug(4300) << "Faulty return -- bad history buffer" << endl;
00672   return;
00673 }
00674 
00675 
00676 void KMultiPage::doGoForward()
00677 {
00678   HistoryItem *it = document_history.forward();
00679   if (it != 0)
00680     gotoPage(it->page, it->ypos, false); // Do not add a history item.
00681   else
00682     kdDebug(4300) << "Faulty return -- bad history buffer" << endl;
00683   return;
00684 }
00685 
00686 
00687 void KMultiPage::renderModeChanged()
00688 {
00689   pageCache->clear();
00690 
00691   generateDocumentWidgets();
00692   scrollView()->layoutPages();
00693 
00694   for (Q_UINT16 i=0; i < widgetList.size(); i++)
00695   {
00696     DocumentWidget* documentWidget = widgetList[i];
00697     if (documentWidget == 0)
00698       continue;
00699 
00700     documentWidget->update();
00701   }
00702 
00703   markList()->repaintThumbnails();
00704 }
00705 
00706 
00707 void KMultiPage::repaintAllVisibleWidgets()
00708 {
00709 #ifdef DEBUG_KMULTIPAGE
00710   kdDebug(1233) << "KMultiPage::repaintAllVisibleWidgets()" << endl;
00711 #endif
00712 
00713   bool everResized = false;
00714 
00715   // Go through the list of widgets and resize them, if necessary
00716   for(Q_UINT16 i=0; i<widgetList.size(); i++)
00717   {
00718     DocumentWidget* documentWidget = widgetList[i];
00719     if (documentWidget == 0)
00720       continue;
00721 
00722     // Resize, if necessary
00723     QSize pageSize = pageCache->sizeOfPageInPixel(documentWidget->getPageNumber());
00724     if (pageSize != documentWidget->pageSize())
00725     {
00726       documentWidget->setPageSize(pageSize);
00727       everResized = true;
00728     }
00729   }
00730 
00731   // If at least one widget was resized, all widgets should be
00732   // re-aligned. This will automatically update all necessary
00733   // widgets.
00734   if (everResized == true)
00735     scrollView()->layoutPages(true);
00736 }
00737 
00738 
00739 double KMultiPage::setZoom(double zoom)
00740 {
00741 #ifdef DEBUG_KMULTIPAGE
00742   kdDebug(1233) << "KMultiPage::setZoom(" << zoom << ")" << endl;
00743 #endif
00744 
00745   if (zoom < ZoomLimits::MinZoom/1000.0)
00746     zoom = ZoomLimits::MinZoom/1000.0;
00747   if (zoom > ZoomLimits::MaxZoom/1000.0)
00748     zoom = ZoomLimits::MaxZoom/1000.0;
00749 
00750   pageCache->setResolution(QPaintDevice::x11AppDpiX()*zoom);
00751   emit zoomChanged();
00752   return zoom;
00753 }
00754 
00755 
00756 void KMultiPage::print()
00757 {
00758   // Paranoid safety checks
00759   if (renderer.isNull())
00760     return;
00761   if (renderer->isEmpty())
00762     return;
00763 
00764   // Allocate the printer structure
00765   KPrinter *printer = getPrinter();
00766   if (printer == 0)
00767     return;
00768 
00769   // initialize the printer using the print dialog
00770   if ( printer->setup(parentWdg, i18n("Print %1").arg(m_file.section('/', -1))) ) {    
00771     // Now do the printing. 
00772     QValueList<int> pageList = printer->pageList();
00773     if (pageList.isEmpty()) 
00774       printer->abort();
00775     else {
00776       printer->setCreator("kviewshell");
00777       printer->setDocName(m_file);
00778       RenderedDocumentPagePrinter rdpp(printer);
00779       
00780       // Obtain papersize information that is required to perform
00781       // the resizing and centering, if this is wanted by the user.
00782       Length paperWidth, paperHeight;
00783       QPaintDeviceMetrics pdm(printer);
00784       paperWidth.setLength_in_mm(pdm.widthMM());
00785       paperHeight.setLength_in_mm(pdm.heightMM());
00786       
00787       QValueList<int>::ConstIterator it = pageList.begin();
00788       while (true) {
00789     SimplePageSize paper_s(paperWidth, paperHeight);
00790 
00791     // Printing usually takes a while. This is to keep the GUI
00792     // updated.
00793     qApp->processEvents();
00794     
00795     QPainter *paint = rdpp.getPainter();
00796     if (paint != 0) {
00797       // Before drawing the page, we figure out the zoom-value,
00798       // taking the "page sizes and placement" options from the
00799       // printer dialog into account
00800       double factual_zoom = 1.0;
00801       
00802       // Obtain pagesize information that is required to perform the
00803       // resizing and centering, if this is wanted by the user.
00804       SimplePageSize page_s = sizeOfPage(*it);
00805       
00806       paint->save();
00807 
00808       // Rotate the page, if appropriate. By default, page
00809       // rotation is enabled. This is also hardcoded into
00810       // KPrintDialogPage_PageOptions.cpp
00811       if ((page_s.isPortrait() != paper_s.isPortrait()) && (printer->option( "kde-kviewshell-rotatepage" ) != "false")) {
00812         paint->rotate(-90);
00813         paint->translate(-printer->resolution()*paperHeight.getLength_in_inch(), 0.0);
00814         paper_s = paper_s.rotate90();
00815       }
00816 
00817       double suggested_zoom = page_s.zoomToFitInto(paper_s);
00818       
00819       // By default, "shrink page" and "expand page" are off. This
00820       // is also hardcoded into KPrintDialogPage_PageOptions.cpp
00821       if ((suggested_zoom < 1.0) && (printer->option( "kde-kviewshell-shrinkpage" ) == "true")) 
00822         factual_zoom = suggested_zoom;
00823       if ((suggested_zoom > 1.0) && (printer->option( "kde-kviewshell-expandpage" ) == "true")) 
00824         factual_zoom = suggested_zoom;
00825       
00826       Length delX, delY;
00827       // By default, "center page" is on. This is also hardcoded
00828       // into KPrintDialogPage_PageOptions.cpp
00829       if (printer->option( "kde-kviewshell-centerpage" ) != "false") {
00830         delX = (paper_s.width() - page_s.width()*factual_zoom)/2.0;
00831         delY = (paper_s.height() - page_s.height()*factual_zoom)/2.0;
00832       }
00833       
00834       // Now draw the page.
00835       rdpp.setPageNumber(*it);
00836       
00837       double resolution = factual_zoom*printer->resolution();
00838       
00839       paint->translate(resolution*delX.getLength_in_inch(), resolution*delY.getLength_in_inch());
00840       renderer->drawPage(resolution, &rdpp);
00841       paint->restore();
00842     }
00843     ++it;
00844     if ((it == pageList.end()) || (printer->aborted() == true))
00845       break;
00846     
00847     printer->newPage();
00848       }
00849       // At this point the rdpp is destructed. The last page is then
00850       // printed.
00851     }
00852   }
00853   delete printer;
00854 }
00855 
00856 
00857 void KMultiPage::setRenderer(DocumentRenderer* _renderer)
00858 {
00859   renderer = _renderer;
00860 
00861   // Initialize documentPageCache.
00862   initializePageCache();
00863   pageCache->setRenderer(renderer);
00864 
00865   _markList->setPageCache(pageCache);
00866 
00867   // Clear widget list.
00868   widgetList.resize(0);
00869 
00870   // Relay signals.
00871   connect(renderer, SIGNAL(setStatusBarText(const QString&)), this, SIGNAL(setStatusBarText(const QString&)));
00872   connect(pageCache, SIGNAL(paperSizeChanged()), this, SLOT(renderModeChanged()));
00873   connect(pageCache, SIGNAL(textSelected(bool)), this, SIGNAL(textSelected(bool)));
00874   connect(renderer, SIGNAL(documentIsChanged()), this, SLOT(renderModeChanged()));
00875   connect(this, SIGNAL(zoomChanged()), this, SLOT(repaintAllVisibleWidgets()));
00876 }
00877 
00878 
00879 void KMultiPage::updateWidgetSize(const PageNumber& pageNumber)
00880 {
00881   for(Q_UINT16 i=0; i<widgetList.size(); i++)
00882   {
00883     DocumentWidget* documentWidget = widgetList[i];
00884     if (documentWidget == 0)
00885       continue;
00886 
00887     if (documentWidget->getPageNumber() == pageNumber)
00888     {
00889       // Resize, if necessary
00890       QSize pageSize = pageCache->sizeOfPageInPixel(documentWidget->getPageNumber());
00891       if (pageSize != documentWidget->pageSize())
00892       {
00893         documentWidget->setPageSize(pageSize);
00894         scrollView()->layoutPages();
00895       }
00896       // We have just one widget per page.
00897       break;
00898     }
00899   }
00900 
00901   // Update marklist
00902   markList()->updateWidgetSize(pageNumber);
00903 }
00904 
00905 
00906 PageNumber KMultiPage::widestPage() const
00907 {
00908   Length maxWidth;
00909   PageNumber pageNumber = 1;
00910 
00911   for (int i = 1; i <= numberOfPages(); i++)
00912   {
00913     Length width = pageCache->sizeOfPage(i).width();
00914 
00915     if (width > maxWidth)
00916     {
00917       maxWidth = width;
00918       pageNumber = i;
00919     }
00920   }
00921 
00922   return pageNumber;
00923 }
00924 
00925 double KMultiPage::zoomForWidthColumns(unsigned int viewportWidth) const
00926 {
00927   Length maxLeftColumnWidth;
00928   Length maxRightColumnWidth;
00929   Length maxWidth;
00930 
00931   PageNumber widestPageLeft;
00932   PageNumber widestPageRight;
00933 
00934   for (int i = 1; i <= numberOfPages(); i++)
00935   {
00936     Length width = pageCache->sizeOfPage(i).width();
00937 
00938     if ( i % 2 == 0) // page is in left column
00939     {
00940       if (width > maxLeftColumnWidth)
00941       {
00942         maxLeftColumnWidth = width;
00943         widestPageLeft = i;
00944       }
00945     }
00946 
00947     if ( i % 2 == 1) // page is in right column
00948     {
00949       if (width > maxRightColumnWidth)
00950         maxRightColumnWidth = width;
00951         widestPageRight = i;
00952     }
00953   }
00954 
00955   double ratio =  maxLeftColumnWidth / (maxLeftColumnWidth + maxRightColumnWidth);
00956 
00957   // This number is the amount of space the left column should occupy in the viewport.
00958   unsigned int leftTargetWidth = (unsigned int)(ratio * viewportWidth);
00959 
00960   return pageCache->sizeOfPage(widestPageLeft).zoomForWidth(leftTargetWidth);
00961 }
00962 
00963 double KMultiPage::calculateFitToHeightZoomValue()
00964 {
00965   PageNumber pageNumber = 1;
00966 
00967   // See below, in the documentation of the method "calculatefitToWidthZoomLevel"
00968   // for an explanation of the complicated calculation we are doing here.
00969   int columns = scrollView()->getNrColumns();
00970   int rows = scrollView()->getNrRows();
00971   int continuousViewmode = scrollView()->isContinuous();
00972   bool fullScreenMode = scrollView()->fullScreenMode();
00973 
00974   if (columns == 1 && rows == 1 && !continuousViewmode) // single page mode
00975   {
00976     pageNumber = currentPageNumber();
00977     if (!pageNumber.isValid())
00978       pageNumber = 1;
00979   }
00980 
00981   int pageDistance = scrollView()->distanceBetweenPages();
00982   if (columns == 1 && rows == 1 && !continuousViewmode && fullScreenMode)
00983   {
00984     // In Single Page Fullscreen Mode we want to fit the page to the
00985     // window without a margin around it.
00986     pageDistance = 0;
00987   }
00988 
00989   int targetViewportHeight = scrollView()->viewportSize(0,0).height();
00990   int targetPageHeight = (targetViewportHeight - rows*pageDistance)/rows;
00991   int targetPageWidth  = (int)(targetPageHeight * pageCache->sizeOfPage(pageNumber).aspectRatio() );
00992   int targetViewportWidth = targetPageWidth * columns + (columns+1)*pageDistance;
00993   targetViewportHeight = scrollView()->viewportSize(targetViewportWidth, targetViewportHeight).height();
00994   targetPageHeight = (targetViewportHeight - rows*pageDistance)/rows;
00995 
00996   return pageCache->sizeOfPage(pageNumber).zoomForHeight(targetPageHeight);
00997 }
00998 
00999 
01000 double KMultiPage::calculateFitToWidthZoomValue()
01001 {
01002   PageNumber pageNumber = 1;
01003 
01004   int columns = scrollView()->getNrColumns();
01005   int rows = scrollView()->getNrRows();
01006   int continuousViewmode = scrollView()->isContinuous();
01007   bool fullScreenMode = scrollView()->fullScreenMode();
01008 
01009   if (columns == 1 && rows == 1 && !continuousViewmode) // single page mode
01010   {
01011     // To calculate the zoom level in single page mode we need the size
01012     // of the current page. When a new document is opened this function
01013     // is called while the currentPageNumber is invalid. We use the size
01014     // of the first page of the document in this case.
01015     pageNumber = currentPageNumber();
01016     if (!pageNumber.isValid())
01017       pageNumber = 1;
01018   }
01019 
01020   if (columns == 1 && rows == 1 && continuousViewmode) // continuous viewmode
01021   {
01022     pageNumber = widestPage();
01023     if (!pageNumber.isValid())
01024       pageNumber = 1;
01025   }
01026 
01027   // rows should be 1 for Single Page Viewmode,
01028   // the number of Pages in Continuous Viewmode
01029   // and number of Pages/2 in Continuous-Facing Viewmode
01030   if (continuousViewmode)
01031     rows = (int)(ceil(numberOfPages() / (double)columns));
01032 
01033   int pageDistance = scrollView()->distanceBetweenPages();
01034   if (columns == 1 && rows == 1 && !continuousViewmode && fullScreenMode)
01035   {
01036     // In Single Page Fullscreen Mode we want to fit the page to the
01037     // window without a margin around it.
01038     pageDistance = 0;
01039   }
01040   // There is a slight complication here... if we just take the width
01041   // of the viewport and scale the contents by a factor x so that it
01042   // fits the viewport exactly, then, depending on chosen papersize
01043   // (landscape, etc.), the contents may be higher than the viewport
01044   // and the QScrollview may or may not insert a scrollbar at the
01045   // right. If the scrollbar appears, then the usable width of the
01046   // viewport becomes smaller, and scaling by x does not really fit
01047   // the (now smaller page) anymore.
01048 
01049   // Calculate the width and height of the view, disregarding the
01050   // possible complications with scrollbars, e.g. assuming the maximal
01051   // space is available.
01052 
01053   // width of the widget excluding possible scrollbars
01054   int targetViewportWidth  = scrollView()->viewportSize(0,0).width();
01055 
01056   // maximal width of a single page
01057   int targetPageWidth = (targetViewportWidth - (columns+1) * pageDistance) / columns;
01058 
01059   // maximal height of a single page
01060   int targetPageHeight = (int)(targetPageWidth/pageCache->sizeOfPage(pageNumber).aspectRatio());
01061   // FIXME: this is only correct if all pages in the document have the same height
01062   int targetViewportHeight = rows * targetPageHeight + (rows+1) * pageDistance;
01063 
01064   // Think again, this time use only the area which is really
01065   // acessible (which, in case that targetWidth targetHeight don't fit
01066   // the viewport, is really smaller because of the scrollbars).
01067   targetViewportWidth = scrollView()->viewportSize(targetViewportWidth, targetViewportHeight).width();
01068 
01069   if (columns == 2 && continuousViewmode) // continuous facing
01070   {
01071     // TODO Generalize this for more than 2 columns
01072     return zoomForWidthColumns(targetViewportWidth - (columns+1) * pageDistance);
01073   }
01074 
01075   // maximal width of a single page (now the scrollbars are taken into account)
01076   targetPageWidth = (targetViewportWidth - (columns+1) * pageDistance) / columns;
01077 
01078   return pageCache->sizeOfPage(pageNumber).zoomForWidth(targetPageWidth);
01079 }
01080 
01081 
01082 void KMultiPage::prevPage()
01083 {
01084   Q_UINT8 cols = scrollView()->getNrColumns();
01085   Q_UINT8 rows = scrollView()->getNrRows();
01086 
01087   PageNumber np = 1;
01088   if (cols*rows < currentPageNumber())
01089   {
01090     np = currentPageNumber() - cols*rows;
01091   }
01092 
01093   gotoPage(np);
01094 }
01095 
01096 
01097 void KMultiPage::nextPage()
01098 {
01099   Q_UINT8 cols = scrollView()->getNrColumns();
01100   Q_UINT8 rows = scrollView()->getNrRows();
01101 
01102   PageNumber np = QMIN(currentPageNumber() + cols*rows, (Q_UINT16)numberOfPages());
01103 
01104   gotoPage(np);
01105 }
01106 
01107 
01108 void KMultiPage::firstPage()
01109 {
01110   gotoPage(1);
01111 }
01112 
01113 
01114 void KMultiPage::lastPage()
01115 {
01116   gotoPage(numberOfPages());
01117 }
01118 
01119 
01120 void KMultiPage::scroll(Q_INT32 deltaInPixel)
01121 {
01122   QScrollBar* scrollBar = scrollView()->verticalScrollBar();
01123   if (scrollBar == 0) {
01124     kdError(4300) << "KMultiPage::scroll called without scrollBar" << endl;
01125     return;
01126   }
01127 
01128   if (deltaInPixel < 0) {
01129     if (scrollBar->value() == scrollBar->minValue()) {
01130       if ( (currentPageNumber() == 1) || (changePageDelayTimer.isActive()) )
01131         return;
01132 
01133       if (scrollView()->isContinuous())
01134         return;
01135 
01136       changePageDelayTimer.stop();
01137       prevPage();
01138 
01139       scrollView()->setContentsPos(scrollView()->contentsX(), scrollBar->maxValue());
01140       return;
01141     }
01142   }
01143 
01144   if (deltaInPixel > 0) {
01145     if (scrollBar->value() == scrollBar->maxValue()) {
01146       if ( (currentPageNumber() == numberOfPages()) || (changePageDelayTimer.isActive()) )
01147         return;
01148 
01149       if (scrollView()->isContinuous())
01150         return;
01151 
01152       changePageDelayTimer.stop();
01153       nextPage();
01154 
01155       scrollView()->setContentsPos(scrollView()->contentsX(), 0);
01156       return;
01157     }
01158   }
01159 
01160   scrollBar->setValue(scrollBar->value() + deltaInPixel);
01161 
01162   if ( (scrollBar->value() == scrollBar->maxValue()) || (scrollBar->value() == scrollBar->minValue()) )
01163     changePageDelayTimer.start(200,true);
01164   else
01165     changePageDelayTimer.stop();
01166 }
01167 
01168 
01169 void KMultiPage::scrollUp()
01170 {
01171   QScrollBar* scrollBar = scrollView()->verticalScrollBar();
01172   if (scrollBar == 0)
01173     return;
01174 
01175   scroll(-scrollBar->lineStep());
01176 }
01177 
01178 
01179 void KMultiPage::scrollDown()
01180 {
01181   QScrollBar* scrollBar = scrollView()->verticalScrollBar();
01182   if (scrollBar == 0)
01183     return;
01184 
01185   scroll(scrollBar->lineStep());
01186 }
01187 
01188 void KMultiPage::scrollLeft()
01189 {
01190   QScrollBar* scrollBar = scrollView()->horizontalScrollBar();
01191   if (scrollBar)
01192     scrollBar->subtractLine();
01193 }
01194 
01195 
01196 void KMultiPage::scrollRight()
01197 {
01198   QScrollBar* scrollBar = scrollView()->horizontalScrollBar();
01199   if (scrollBar)
01200     scrollBar->addLine();
01201 }
01202 
01203 
01204 void KMultiPage::scrollUpPage()
01205 {
01206   QScrollBar* scrollBar = scrollView()->verticalScrollBar();
01207   if (scrollBar)
01208     scrollBar->subtractPage();
01209 }
01210 
01211 
01212 void KMultiPage::scrollDownPage()
01213 {
01214   QScrollBar* scrollBar = scrollView()->verticalScrollBar();
01215   if (scrollBar)
01216     scrollBar->addPage();
01217 }
01218 
01219 
01220 void KMultiPage::scrollLeftPage()
01221 {
01222   QScrollBar* scrollBar = scrollView()->horizontalScrollBar();
01223   if (scrollBar)
01224     scrollBar->subtractPage();
01225 }
01226 
01227 
01228 void KMultiPage::scrollRightPage()
01229 {
01230   QScrollBar* scrollBar = scrollView()->horizontalScrollBar();
01231   if (scrollBar)
01232     scrollBar->addPage();
01233 }
01234 
01235 
01236 void KMultiPage::readDown()
01237 {
01238   PageView* sv = scrollView();
01239 
01240   if (sv->atBottom())
01241   {
01242     if (sv->isContinuous())
01243       return;
01244 
01245     if (currentPageNumber() == numberOfPages())
01246       return;
01247 
01248     nextPage();
01249     sv->setContentsPos(sv->contentsX(), 0);
01250   }
01251   else
01252     sv->readDown();
01253 }
01254 
01255 
01256 void KMultiPage::readUp()
01257 {
01258   PageView* sv = scrollView();
01259 
01260   if (sv->atTop())
01261   {
01262     if (sv->isContinuous())
01263       return;
01264 
01265     if (currentPageNumber() == 1)
01266       return;
01267 
01268     prevPage();
01269     sv->setContentsPos(sv->contentsX(),  sv->contentsHeight());
01270   }
01271   else
01272     sv->readUp();
01273 }
01274 
01275 
01276 void KMultiPage::jumpToReference(const QString& reference)
01277 {
01278   if (renderer.isNull())
01279     return;
01280   
01281   gotoPage(renderer->parseReference(reference));
01282 }
01283 
01284 
01285 void KMultiPage::gotoPage(const Anchor &a)
01286 {
01287   if (!a.page.isValid() || (renderer.isNull()))
01288     return;
01289 
01290   gotoPage(a.page, (int)(a.distance_from_top.getLength_in_inch()*pageCache->getResolution() + 0.5), true);
01291 }
01292 
01293 
01294 void KMultiPage::gotoPage(const TextSelection& selection)
01295 {
01296   if (selection.isEmpty())
01297   {
01298     kdError(4300) << "KMultiPage::gotoPage(...) called with empty TextSelection." << endl;
01299     return;
01300   }
01301 
01302   RenderedDocumentPage* pageData = pageCache->getPage(selection.getPageNumber());
01303 
01304   if (pageData == 0) {
01305 #ifdef DEBUG_DOCUMENTWIDGET
01306     kdDebug(4300) << "DocumentWidget::paintEvent: no documentPage generated" << endl;
01307 #endif
01308     return;
01309   }
01310 
01311   switch (widgetList.size())
01312   {
01313     case 0:
01314       kdError(4300) << "KMultiPage::select() while widgetList is empty" << endl;
01315       break;
01316     case 1:
01317       ((DocumentWidget*)widgetList[0])->select(selection);
01318       break;
01319     default:
01320       if (widgetList.size() < currentPageNumber())
01321         kdError(4300) << "KMultiPage::select() while widgetList.size()=" << widgetList.size() << "and currentPageNumber()=" << currentPageNumber() << endl;
01322       else
01323         ((DocumentWidget*)widgetList[selection.getPageNumber() - 1])->select(selection);
01324   }
01325 
01326   unsigned int y = pageData->textBoxList[selection.getSelectedTextStart()].box.top();
01327   gotoPage(selection.getPageNumber(), y, false);
01328 }
01329 
01330 
01331 void KMultiPage::doSelectAll()
01332 {
01333   switch( widgetList.size() ) {
01334   case 0:
01335     kdError(4300) << "KMultiPage::doSelectAll() while widgetList is empty" << endl;
01336     break;
01337   case 1:
01338     ((DocumentWidget *)widgetList[0])->selectAll();
01339     break;
01340   default:
01341     if (widgetList.size() < currentPageNumber())
01342       kdError(4300) << "KMultiPage::doSelectAll() while widgetList.size()=" << widgetList.size() << "and currentPageNumber()=" << currentPageNumber() << endl;
01343     else
01344       ((DocumentWidget *)widgetList[currentPageNumber()-1])->selectAll();
01345   }
01346 }
01347 
01348 
01349 
01350 void  KMultiPage::showFindTextDialog()
01351 {
01352   if ((renderer.isNull()) || (renderer->supportsTextSearch() == false))
01353     return;
01354 
01355   searchWidget->show();
01356   searchWidget->setFocus();
01357 }
01358 
01359 void KMultiPage::stopSearch()
01360 {
01361   if (searchInProgress)
01362   {
01363     // stop the search
01364     searchInProgress = false;
01365   }
01366   else
01367     searchWidget->hide();
01368 }
01369 
01370 void KMultiPage::findNextText()
01371 {
01372 #ifdef KDVI_MULTIPAGE_DEBUG
01373   kdDebug(4300) << "KMultiPage::findNextText() called" << endl;
01374 #endif
01375 
01376   searchInProgress = true;
01377 
01378   // Used to remember if the documentPage we use is from the cache.
01379   // If not we need to delete it manually to avoid a memory leak.
01380   bool cachedPage = false;
01381 
01382   QString searchText = searchWidget->getText();
01383 
01384   if (searchText.isEmpty())
01385   {
01386     kdError(4300) << "KMultiPage::findNextText() called when search text was empty" << endl;
01387     return;
01388   }
01389 
01390   bool case_sensitive = searchWidget->caseSensitive();
01391 
01392   // Find the page and text position on the page where the search will
01393   // start. If nothing is selected, we start at the beginning of the
01394   // current page. Otherwise, start after the selected text.  TODO:
01395   // Optimize this to get a better 'user feeling'
01396   Q_UINT16 startingPage;
01397   Q_UINT16 startingTextItem;
01398 
01399   TextSelection userSelection = pageCache->selectedText();
01400   if (userSelection.isEmpty())
01401   {
01402     startingPage     = currentPageNumber();
01403     startingTextItem = 0;
01404   }
01405   else
01406   {
01407     startingPage     = userSelection.getPageNumber();
01408     startingTextItem = userSelection.getSelectedTextEnd()+1;
01409   }
01410 
01411   TextSelection foundSelection;
01412 
01413   RenderedDocumentPagePixmap* searchPage = 0;
01414 
01415   for(unsigned int i = 0; i < numberOfPages(); i++)
01416   {
01417     unsigned int pageNumber = (i + startingPage - 1) % numberOfPages() + 1;
01418 
01419     if (!searchInProgress)
01420     {
01421       // Interrupt the search
01422       setStatusBarText(i18n("Search interrupted"));
01423       if (!cachedPage)
01424         delete searchPage;
01425       return;
01426     }
01427 
01428     if (i != 0)
01429     {
01430       setStatusBarText(i18n("Search page %1 of %2").arg(pageNumber).arg(numberOfPages()));
01431       kapp->processEvents();
01432     }
01433 
01434     // Check if we already have a rendered version of the page in the cache. As we are only interested in the
01435     // text we don't care about the page size.
01436     if (pageCache->isPageCached(pageNumber))
01437     {
01438       // If the last search page used was created locally, we need to destroy it
01439       if (!cachedPage)
01440         delete searchPage;
01441 
01442       searchPage = pageCache->getPage(pageNumber);
01443       cachedPage = true;
01444     }
01445     else
01446     {
01447       // If the page is not in the cache we draw a small version of it, since this is faster.
01448 
01449       // We only create a new searchPage if we need to, otherwise reuse the existing one.
01450       if (!searchPage || cachedPage)
01451         searchPage = new RenderedDocumentPagePixmap();
01452 
01453       cachedPage = false;
01454 
01455       searchPage->resize(1,1);
01456       searchPage->setPageNumber(pageNumber);
01457       renderer->getText(searchPage);
01458     }
01459 
01460     // If there is no text in the current page, try the next one.
01461     if (searchPage->textBoxList.size() == 0)
01462       continue;
01463 
01464     foundSelection = searchPage->find(searchText, startingTextItem, case_sensitive);
01465 
01466     if (foundSelection.isEmpty())
01467     {
01468       // In the next page, start search again at the beginning.
01469       startingTextItem = 0;
01470       clearSelection();
01471 
01472       if (pageNumber == numberOfPages())
01473       {
01474         int answ = KMessageBox::questionYesNo(scrollView(),
01475                    i18n("<qt>The search string <strong>%1</strong> could not be found by the "
01476                         "end of the document. Should the search be restarted from the beginning "
01477                         "of the document?</qt>").arg(searchText),
01478                    i18n("Text Not Found"), KStdGuiItem::cont(), KStdGuiItem::cancel());
01479 
01480         if (answ != KMessageBox::Yes)
01481         {
01482           setStatusBarText(QString::null);
01483           searchInProgress = false;
01484           if (!cachedPage)
01485             delete searchPage;
01486           return;
01487         }
01488       }
01489     }
01490     else
01491     {
01492       pageCache->selectText(foundSelection);
01493       gotoPage(pageCache->selectedText());
01494       setStatusBarText(QString::null);
01495       searchInProgress = false;
01496       if (!cachedPage)
01497         delete searchPage;
01498       return;
01499     }
01500   }
01501 
01502   KMessageBox::sorry(scrollView(), i18n("<qt>The search string <strong>%1</strong> could not be found.</qt>").arg(searchText));
01503   setStatusBarText(QString::null);
01504   searchInProgress = false;
01505   if (!cachedPage)
01506     delete searchPage;
01507 }
01508 
01509 
01510 void KMultiPage::findPrevText()
01511 {
01512 #ifdef KDVI_MULTIPAGE_DEBUG
01513   kdDebug(4300) << "KMultiPage::findPrevText() called" << endl;
01514 #endif
01515 
01516   searchInProgress = true;
01517 
01518   // Used to remember if the documentPage we use is from the cache.
01519   // If not we need to delete it manually to avoid a memory leak.
01520   bool cachedPage = false;
01521 
01522   QString searchText = searchWidget->getText();
01523 
01524   if (searchText.isEmpty())
01525   {
01526     kdError(4300) << "KMultiPage::findPrevText() called when search text was empty" << endl;
01527     return;
01528   }
01529 
01530   bool case_sensitive = searchWidget->caseSensitive();
01531 
01532   // Find the page and text position on the page where the search will
01533   // start. If nothing is selected, we start at the beginning of the
01534   // current page. Otherwise, start after the selected text.  TODO:
01535   // Optimize this to get a better 'user feeling'
01536   unsigned int startingPage;
01537   int startingTextItem;
01538 
01539   TextSelection userSelection = pageCache->selectedText();
01540   if (userSelection.isEmpty())
01541   {
01542     startingPage     = currentPageNumber();
01543     startingTextItem = -1;
01544   }
01545   else
01546   {
01547     startingPage     = userSelection.getPageNumber();
01548     startingTextItem = userSelection.getSelectedTextStart()-1;
01549   }
01550 
01551   TextSelection foundSelection;
01552 
01553   RenderedDocumentPagePixmap* searchPage = 0;
01554 
01555   for(unsigned int i = 0; i < numberOfPages(); i++)
01556   {
01557     int pageNumber = startingPage - i;
01558     if (pageNumber <= 0)
01559       pageNumber += numberOfPages();
01560 
01561     if (!searchInProgress)
01562     {
01563       // Interrupt the search
01564       setStatusBarText(i18n("Search interrupted"));
01565       if (!cachedPage)
01566         delete searchPage;
01567       return;
01568     }
01569 
01570     if (i != 0)
01571     {
01572       setStatusBarText(i18n("Search page %1 of %2").arg(pageNumber).arg(numberOfPages()));
01573       kapp->processEvents();
01574     }
01575 
01576     // Check if we already have a rendered version of the page in the cache. As we are only interested in the
01577     // text we don't care about the page size.
01578     if (pageCache->isPageCached(pageNumber))
01579     {
01580       // If the last search page used was created locally, we need to destroy it
01581       if (!cachedPage)
01582         delete searchPage;
01583 
01584       searchPage = pageCache->getPage(pageNumber);
01585       cachedPage = true;
01586     }
01587     else
01588     {
01589       // If the page is not in the cache we draw a small version of it, since this is faster.
01590 
01591       // We only create a new searchPage if we need to, otherwise reuse the existing one.
01592       if (!searchPage || cachedPage)
01593         searchPage = new RenderedDocumentPagePixmap();
01594 
01595       cachedPage = false;
01596 
01597       searchPage->resize(1,1);
01598       searchPage->setPageNumber(pageNumber);
01599       renderer->getText(searchPage);
01600     }
01601 
01602     // If there is no text in the current page, try the next one.
01603     if (searchPage->textBoxList.size() == 0)
01604       continue;
01605 
01606     foundSelection = searchPage->findRev(searchText, startingTextItem, case_sensitive);
01607 
01608     if (foundSelection.isEmpty())
01609     {
01610       // In the next page, start search again at the beginning.
01611       startingTextItem = -1;
01612       clearSelection();
01613 
01614       if (pageNumber == 1)
01615       {
01616         int answ = KMessageBox::questionYesNo(scrollView(),
01617                   i18n("<qt>The search string <strong>%1</strong> could not be found by the "
01618                         "beginning of the document. Should the search be restarted from the end "
01619                         "of the document?</qt>").arg(searchText),
01620                   i18n("Text Not Found"), KStdGuiItem::cont(), KStdGuiItem::cancel());
01621 
01622         if (answ != KMessageBox::Yes)
01623         {
01624           setStatusBarText(QString::null);
01625           searchInProgress = false;
01626           if (!cachedPage)
01627             delete searchPage;
01628           return;
01629         }
01630       }
01631     }
01632     else
01633     {
01634       pageCache->selectText(foundSelection);
01635       gotoPage(pageCache->selectedText());
01636       setStatusBarText(QString::null);
01637       searchInProgress = false;
01638       if (!cachedPage)
01639         delete searchPage;
01640       return;
01641     }
01642   }
01643 
01644   KMessageBox::sorry(scrollView(), i18n("<qt>The search string <strong>%1</strong> could not be found.</qt>").arg(searchText));
01645   setStatusBarText(QString::null);
01646   searchInProgress = false;
01647   if (!cachedPage)
01648     delete searchPage;
01649 }
01650 
01651 
01652 void KMultiPage::clearSelection()
01653 {
01654   PageNumber page = pageCache->selectedText().getPageNumber();
01655 
01656   if (!page.isValid())
01657     return;
01658 
01659   // Clear selection
01660   pageCache->deselectText();
01661 
01662   // Now we need to update the widget which contained the selection
01663   switch(widgetList.size())
01664   {
01665     case 0:
01666       kdError(4300) << "KMultiPage::clearSelection() while widgetList is empty" << endl;
01667       break;
01668     case 1:
01669       widgetList[0]->update();
01670       break;
01671     default:
01672       for (unsigned int i = 0; i < widgetList.size(); i++)
01673       {
01674         DocumentWidget* pageWidget = (DocumentWidget*)widgetList[i];
01675         if (pageWidget->getPageNumber() == page)
01676         {
01677           pageWidget->update();
01678           break;
01679         }
01680       }
01681   }
01682 }
01683 
01684 void KMultiPage::copyText()
01685 {
01686   pageCache->selectedText().copyText();
01687 }
01688 
01689 void KMultiPage::timerEvent( QTimerEvent * )
01690 {
01691 #ifdef KMULTIPAGE_DEBUG
01692   kdDebug(4300) << "Timer Event " << endl;
01693 #endif
01694   reload();
01695 }
01696 
01697 
01698 void KMultiPage::reload()
01699 {
01700 #ifdef KMULTIPAGE_DEBUG
01701   kdDebug(4300) << "Reload file " << m_file << endl;
01702 #endif
01703   
01704   if (renderer.isNull()) {
01705     kdError() << "KMultiPage::reload() called, but no renderer was set" << endl;
01706     return;
01707   }
01708   
01709   if (renderer->isValidFile(m_file)) {
01710     pageCache->clear();
01711     pageCache->deselectText();
01712     document_history.clear();
01713     emit setStatusBarText(i18n("Reloading file %1").arg(m_file));
01714     Q_INT32 pg = currentPageNumber();
01715 
01716     killTimer(timer_id);
01717     timer_id = -1;
01718     bool r = renderer->setFile(m_file, m_url);
01719     
01720     generateDocumentWidgets();
01721 
01722     // Set Table of Contents
01723     tableOfContents->setContents(renderer->getBookmarks());
01724 
01725     // Adjust number of widgets in the thumbnail sidebar
01726     markList()->clear();
01727     markList()->setNumberOfPages(numberOfPages(), KVSPrefs::showThumbnails());
01728 
01729     setCurrentPageNumber(pg);
01730     setFile(r);
01731     emit setStatusBarText(QString::null);
01732   } else {
01733     if (timer_id == -1)
01734       timer_id = startTimer(1000);
01735   }
01736 }
01737 
01738 
01739 bool KMultiPage::openFile()
01740 {
01741   if (renderer.isNull()) {
01742     kdError(4300) << "KMultiPage::openFile() called when no renderer was set" << endl;
01743     return false;
01744   }
01745 
01746   pageCache->deselectText();
01747   document_history.clear();
01748   pageCache->clear();
01749   emit setStatusBarText(i18n("Loading file %1").arg(m_file));
01750 
01751   bool r = renderer->setFile(m_file, m_url);
01752 
01753   if (r) {
01754     setCurrentPageNumber(1);
01755     generateDocumentWidgets();
01756 
01757     // Set number of widgets in the thumbnail sidebar
01758     markList()->clear();
01759     markList()->setNumberOfPages(numberOfPages(), KVSPrefs::showThumbnails());
01760     
01761     QString reference = url().ref();
01762     if (!reference.isEmpty())
01763       gotoPage(renderer->parseReference(reference));
01764     
01765     // Set Table of Contents
01766     tableOfContents->setContents(renderer->getBookmarks());
01767   } else
01768     m_file = QString::null;
01769 
01770   
01771   setFile(r);
01772   
01773   // Clear Statusbar
01774   emit setStatusBarText(QString::null);
01775   return r;
01776 }
01777 
01778 
01779 bool KMultiPage::openURL(const QString &filename, const KURL &base_url)
01780 {
01781   m_file = filename;
01782   m_url = base_url;
01783 
01784   bool success = openFile();
01785   if (success)
01786     setCurrentPageNumber(1);
01787 
01788   return success;
01789 }
01790 
01791 
01792 void KMultiPage::enableActions(bool fileLoaded)
01793 {
01794   Q_UNUSED(fileLoaded);
01795 }
01796 
01797 void KMultiPage::wheelEvent(QWheelEvent *e)
01798 {
01799   QScrollBar *sb = scrollView()->verticalScrollBar();
01800   if (sb == 0)
01801     return;
01802 
01803   // Zoom in/out
01804   if (e->state() & ControlButton)
01805   {
01806     if (e->delta() < 0)
01807       emit zoomOut();
01808     else
01809       emit zoomIn();
01810     return;
01811   }
01812 
01813   Q_INT32 pxl = -(e->delta()*sb->lineStep())/60;
01814   if (pxl == 0)
01815   {
01816     if (e->delta() > 0)
01817       pxl = -1;
01818     else
01819       pxl = 1;
01820   }
01821 
01822   // Faster scrolling
01823   if (e->state() & ShiftButton)
01824     pxl *= 10;
01825 
01826   scroll(pxl);
01827 }
01828 
01829 
01830 KPrinter *KMultiPage::getPrinter(bool enablePageSizeFeatures)
01831 {
01832     // Allocate a new KPrinter structure, if necessary
01833   KPrinter *printer = new KPrinter(true);
01834   if (printer == 0) {
01835     kdError(1223) << "KMultiPage::getPrinter(..): Cannot allocate new KPrinter structure" << endl;
01836     return 0;
01837   }
01838   
01839   // Allocate a new KPrintDialogPage structure and add it to the
01840   // printer, if the kmultipage implementation requests that
01841   if (enablePageSizeFeatures == true) {
01842     KPrintDialogPage_PageOptions *pageOptions = new KPrintDialogPage_PageOptions();
01843     if (pageOptions == 0) {
01844       kdError(1223) << "KMultiPage::getPrinter(..): Cannot allocate new KPrintDialogPage_PageOptions structure" << endl;
01845       delete printer;
01846       return 0;
01847     }
01848     printer->addDialogPage( pageOptions );
01849   }
01850   
01851   // Feed the printer with useful defaults and information.
01852   printer->setPageSelection( KPrinter::ApplicationSide );
01853   printer->setCurrentPage( currentPageNumber() );
01854   printer->setMinMax( 1, numberOfPages() );
01855   printer->setFullPage( true );
01856   
01857   // If pages are marked, give a list of marked pages to the
01858   // printer. We try to be smart and optimize the list by using ranges
01859   // ("5-11") wherever possible. The user will be tankful for
01860   // that. Complicated? Yeah, but that's life.
01861   QValueList<int> selectedPageNo = selectedPages();
01862   if (selectedPageNo.isEmpty() == true)
01863     printer->setOption( "kde-range", "" );
01864   else {
01865     int commaflag = 0;
01866     QString range;
01867     QValueList<int>::ConstIterator it = selectedPageNo.begin();
01868     do{
01869       int val = *it;
01870       if (commaflag == 1)
01871     range +=  QString(", ");
01872       else
01873     commaflag = 1;
01874       int endval = val;
01875       if (it != selectedPageNo.end()) {
01876     QValueList<int>::ConstIterator jt = it;
01877     jt++;
01878     do{
01879       int val2 = *jt;
01880       if (val2 == endval+1)
01881         endval++;
01882       else
01883         break;
01884       jt++;
01885     } while( jt != selectedPageNo.end() );
01886     it = jt;
01887       } else
01888     it++;
01889       if (endval == val)
01890     range +=  QString("%1").arg(val);
01891       else
01892     range +=  QString("%1-%2").arg(val).arg(endval);
01893     } while (it != selectedPageNo.end() );
01894     printer->setOption( "kde-range", range );
01895   }
01896   
01897   return printer;  
01898 }
01899 
01900 void KMultiPage::doExportText()
01901 {
01902   // Generate a suggestion for a reasonable file name
01903   QString suggestedName = url().filename();
01904   suggestedName = suggestedName.left(suggestedName.find(".")) + ".txt";
01905 
01906   QString fileName = KFileDialog::getSaveFileName(suggestedName, i18n("*.txt|Plain Text (Latin 1) (*.txt)"), scrollView(), i18n("Export File As"));
01907 
01908   if (fileName.isEmpty())
01909     return;
01910 
01911   QFileInfo finfo(fileName);
01912   if (finfo.exists())
01913   {
01914     int r = KMessageBox::warningContinueCancel (scrollView(),
01915                 i18n("The file %1\nexists. Do you want to overwrite that file?").arg(fileName),
01916                 i18n("Overwrite File"), i18n("Overwrite"));
01917 
01918     if (r == KMessageBox::Cancel)
01919       return;
01920   }
01921 
01922   QFile textFile(fileName);
01923   textFile.open(IO_WriteOnly);
01924   QTextStream stream(&textFile);
01925 
01926   QProgressDialog progress(i18n("Exporting to text..."), i18n("Abort"), renderer->totalPages(),
01927                            scrollView(), "export_text_progress", true);
01928   progress.setMinimumDuration(300);
01929 
01930   RenderedDocumentPagePixmap dummyPage;
01931   dummyPage.resize(1, 1);
01932 
01933   for(unsigned int page = 1; page <= renderer->totalPages(); page++)
01934   {
01935     progress.setProgress(page);
01936     qApp->processEvents();
01937 
01938     if (progress.wasCancelled())
01939       break;
01940 
01941     dummyPage.setPageNumber(page);
01942     // We gracefully ignore any errors (bad file, etc.)
01943     renderer->getText(&dummyPage);
01944 
01945     for(unsigned int i = 0; i < dummyPage.textBoxList.size(); i++)
01946     {
01947       // We try to detect newlines
01948       if (i > 0)
01949       {
01950         // Like all our textalgorithmns this currently assumes left to right text.
01951         // TODO: make this more generic. But we first would need to guess the corrent
01952         // orientation.
01953         if (dummyPage.textBoxList[i].box.top() > dummyPage.textBoxList[i-1].box.bottom() &&
01954             dummyPage.textBoxList[i].box.x() < dummyPage.textBoxList[i-1].box.x())
01955         {
01956           stream << "\n";
01957         }
01958       }
01959       stream << dummyPage.textBoxList[i].text;
01960     }
01961 
01962     // Send newline after each page.
01963     stream << "\n";
01964   }
01965 
01966   // Switch off the progress dialog, etc.
01967   progress.setProgress(renderer->totalPages());
01968   return;
01969 }
01970 
01971 void KMultiPage::slotEnableMoveTool(bool enable)
01972 {
01973   emit enableMoveTool(enable);
01974 }
01975 
01976 #include "kmultipage.moc"

kviewshell

Skip menu "kviewshell"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • kviewshell
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal