#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentWidget.h" #include "marklist.h" #include "tableOfContents.h" #include "kprintDialogPage_pageoptions.h" #include "kvsprefs.h" #include "kmultipage.h" #include "pageNumber.h" #include "renderedDocumentPagePrinter.h" #include "searchWidget.h" #include "textBox.h" #include "zoomlimits.h" //#define DEBUG_KMULTIPAGE KMultiPage::KMultiPage(TQWidget *parentWidget, const char *widgetName, TQObject *parent, const char *name) : DCOPObject("kmultipage"), KParts::ReadOnlyPart(parent, name) { // For reasons which I don't understand, the initialization of the // DCOPObject above does not work properly, the name is ignored. It // works fine if we repeat the name here. -- Stefan Kebekus // This is because of the virtual inheritance. Get rid of it (but it's BC, and this is a lib...) -- DF setObjId("kmultipage"); parentWdg = parentWidget; lastCurrentPage = 0; timer_id = -1; searchInProgress = false; TQVBox* verticalBox = new TQVBox(parentWidget); verticalBox->setFocusPolicy(TQ_StrongFocus); setWidget(verticalBox); splitterWidget = new TQSplitter(verticalBox, widgetName); splitterWidget->setOpaqueResize(false); splitterWidget->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding); // Create SideBar sideBar = new TQToolBox(splitterWidget, "sidebar"); // Create ContentsList tableOfContents = new TableOfContents(sideBar); sideBar->addItem(tableOfContents, TQIconSet(SmallIcon("contents")), i18n("Contents")); connect(tableOfContents, TQT_SIGNAL(gotoPage(const Anchor&)), this, TQT_SLOT(gotoPage(const Anchor&))); // Create MarkList _markList = new MarkList(sideBar, "marklist"); sideBar->addItem(_markList, TQIconSet(SmallIcon("thumbnail")), i18n("Thumbnails")); // Restore state of the sidebar sideBar->setCurrentItem(sideBar->item(KVSPrefs::sideBarItem())); splitterWidget->setResizeMode(sideBar, TQSplitter::KeepSize); connect(_markList, TQT_SIGNAL(selected(const PageNumber&)), this, TQT_SLOT(gotoPage(const PageNumber&))); _scrollView = new PageView(splitterWidget, widgetName); // Create Search Panel searchWidget = new SearchWidget(verticalBox); searchWidget->hide(); connect(searchWidget, TQT_SIGNAL(findNextText()), this, TQT_SLOT(findNextText())); connect(searchWidget, TQT_SIGNAL(findPrevText()), this, TQT_SLOT(findPrevText())); sideBar->setMinimumWidth(80); sideBar->setMaximumWidth(300); connect(_scrollView, TQT_SIGNAL(currentPageChanged(const PageNumber&)), this, TQT_SLOT(setCurrentPageNumber(const PageNumber&))); connect(_scrollView, TQT_SIGNAL(viewSizeChanged(const TQSize&)), scrollView(), TQT_SLOT(calculateCurrentPageNumber())); connect(_scrollView, TQT_SIGNAL(wheelEventReceived(TQWheelEvent *)), this, TQT_SLOT(wheelEvent(TQWheelEvent*))); connect(this, TQT_SIGNAL(enableMoveTool(bool)), _scrollView, TQT_SLOT(slotEnableMoveTool(bool))); splitterWidget->setCollapsible(sideBar, false); splitterWidget->setSizes(KVSPrefs::guiLayout()); connect(searchWidget, TQT_SIGNAL(searchEnabled(bool)), this, TQT_SIGNAL(searchEnabled(bool))); connect(searchWidget, TQT_SIGNAL(stopSearch()), this, TQT_SLOT(stopSearch())); } KMultiPage::~KMultiPage() { writeSettings(); if (timer_id != -1) killTimer(timer_id); delete pageCache; } void KMultiPage::readSettings() { } void KMultiPage::writeSettings() { // Save TOC tqlayout tableOfContents->writeSettings(); KVSPrefs::setGuiLayout(splitterWidget->sizes()); // Save state of the sidebar KVSPrefs::setSideBarItem(sideBar->indexOf(sideBar->currentItem())); KVSPrefs::writeConfig(); } TQString KMultiPage::name_of_current_file() { return m_file; } bool KMultiPage::is_file_loaded(const TQString& filename) { return (filename == m_file); } void KMultiPage::slotSave_defaultFilename() { slotSave(); } void KMultiPage::slotSave() { // Try to guess the proper ending... TQString formats; TQString ending; int rindex = m_file.findRev("."); if (rindex == -1) { ending = TQString(); formats = TQString(); } else { ending = m_file.mid(rindex); // e.g. ".dvi" formats = fileFormats().grep(ending).join("\n"); } TQString fileName = KFileDialog::getSaveFileName(TQString(), formats, 0, i18n("Save File As")); if (fileName.isEmpty()) return; // Add the ending to the filename. I hope the user likes it that // way. if (!ending.isEmpty() && fileName.find(ending) == -1) fileName = fileName+ending; if (TQFile(fileName).exists()) { int r = KMessageBox::warningContinueCancel (0, i18n("The file %1\nexists. Shall I overwrite that file?").tqarg(fileName), i18n("Overwrite File"), i18n("Overwrite")); if (r == KMessageBox::Cancel) return; } KIO::Job *job = KIO::file_copy( KURL( m_file ), KURL( fileName ), 0600, true, false, true ); connect( job, TQT_SIGNAL( result( KIO::Job * ) ), this, TQT_SLOT( slotIOJobFinished ( KIO::Job * ) ) ); return; } void KMultiPage::setFile(bool) { return; } bool KMultiPage::closeURL() { #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::closeURL()" << endl; #endif if (renderer.isNull()) return false; // Clear navigation history. document_history.clear(); // Close the file. renderer->setFile(TQString(), KURL()); renderer->clear(); // Delete Page Widgets. widgetList.setAutoDelete(true); widgetList.resize(0); widgetList.setAutoDelete(false); // Update ScrollView. scrollView()->layoutPages(); enableActions(false); // Clear Thumbnail List. markList()->clear(); // Clear Table of Contents tableOfContents->clear(); // Clear Status Bar emit setStatusBarText(TQString()); return true; } void KMultiPage::slotIOJobFinished ( KIO::Job *job ) { if ( job->error() ) job->showErrorDialog( 0L ); } void KMultiPage::slotShowScrollbars(bool status) { _scrollView->slotShowScrollbars(status); } void KMultiPage::slotShowSidebar(bool show) { if (show) sideBar->show(); else sideBar->hide(); } void KMultiPage::slotShowThumbnails(bool show) { markList()->slotShowThumbnails(show); } void KMultiPage::slotSetFullPage(bool fullpage) { _scrollView->setFullScreenMode(fullpage); if (fullpage) slotShowSidebar(false); } void KMultiPage::preferencesChanged() { // We need to read the config options otherwise the KVSPrefs-object would // not be syncronized between the kviewpart and the kmultipage. KVSPrefs::self()->readConfig(); slotShowThumbnails(KVSPrefs::showThumbnails()); // if we are in overviewmode and the number of columns or rows has changed if (scrollView()->overviewMode() && (scrollView()->getNrColumns() != KVSPrefs::overviewModeColumns() || scrollView()->getNrRows() != KVSPrefs::overviewModeRows())) { setViewMode(KVSPrefs::EnumViewMode::Overview); } if (KVSPrefs::changeColors() && KVSPrefs::renderMode() == KVSPrefs::EnumRenderMode::Paper) renderer->setAccessibleBackground(true, KVSPrefs::paperColor()); else renderer->setAccessibleBackground(false); renderModeChanged(); } void KMultiPage::setViewMode(int mode) { #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::setViewMode(" << mode << ")" << endl; #endif // Save the current page number because when we are changing the columns // and rows in the scrollview the currently shown Page probably out of view. PageNumber currentPage = currentPageNumber(); // Save viewMode for future uses of KViewShell switch (mode) { case KVSPrefs::EnumViewMode::SinglePage: KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::SinglePage); // Don't do anything if the view mode is already set if ((scrollView()->getNrColumns() == 1) && (scrollView()->getNrRows() == 1) && (scrollView()->isContinuous() == false)) return; scrollView()->setNrColumns(1); scrollView()->setNrRows(1); scrollView()->setContinuousViewMode(false); // We scroll the view to the top, so that top and not the bottom // of the visible page is shown. scrollView()->scrollTop(); break; case KVSPrefs::EnumViewMode::ContinuousFacing: KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::ContinuousFacing); // Don't do anything if the view mode is already set if ((scrollView()->getNrColumns() == 2) && (scrollView()->getNrRows() == 1) && (scrollView()->isContinuous() == true)) return; scrollView()->setNrColumns(2); scrollView()->setNrRows(1); scrollView()->setContinuousViewMode(true); break; case KVSPrefs::EnumViewMode::Overview: KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::Overview); // Don't do anything if the view mode is already set if ((scrollView()->getNrColumns() == KVSPrefs::overviewModeColumns()) && (scrollView()->getNrRows() == KVSPrefs::overviewModeRows()) && (scrollView()->isContinuous() == false)) return; scrollView()->setNrColumns(KVSPrefs::overviewModeColumns()); scrollView()->setNrRows(KVSPrefs::overviewModeRows()); scrollView()->setContinuousViewMode(false); // We scroll the view to the top, so that top and not the bottom // of the visible tableau is shown. scrollView()->scrollTop(); break; default: //KVSPrefs::EnumViewMode::Continuous KVSPrefs::setViewMode(KVSPrefs::EnumViewMode::Continuous); // Don't do anything if the view mode is already set if ((scrollView()->getNrColumns() == 1) && (scrollView()->getNrRows() == 1) && (scrollView()->isContinuous() == true)) return; scrollView()->setNrColumns(1); scrollView()->setNrRows(1); scrollView()->setContinuousViewMode(true); } generateDocumentWidgets(currentPage); KVSPrefs::writeConfig(); emit viewModeChanged(); } void KMultiPage::initializePageCache() { pageCache = new DocumentPageCache(); } DocumentWidget* KMultiPage::createDocumentWidget() { DocumentWidget* documentWidget = new DocumentWidget(scrollView()->viewport(), scrollView(), pageCache, "singlePageWidget"); connect(documentWidget, TQT_SIGNAL(clearSelection()), this, TQT_SLOT(clearSelection())); connect(this, TQT_SIGNAL(enableMoveTool(bool)), documentWidget, TQT_SLOT(slotEnableMoveTool(bool))); return documentWidget; } void KMultiPage::generateDocumentWidgets(const PageNumber& _startPage) { PageNumber startPage = _startPage; #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::generateDocumentWidgets(" << startPage << ")" << endl; #endif // Do nothing if no document is loaded. if (getRenderer().isNull() || getRenderer()->isEmpty()) return; // This function is only called with an invalid pagenumber, when // the file has been loaded or reloaded. bool reload = !startPage.isValid(); if (reload) { // Find the number of the current page, for later use. startPage = currentPageNumber(); } // Make sure that startPage is in the permissible range. if (startPage < 1) startPage = 1; if (startPage > numberOfPages()) startPage = numberOfPages(); unsigned int tableauStartPage = startPage; // Find out how many widgets are needed, and resize the widgetList accordingly. widgetList.setAutoDelete(true); TQ_UINT16 oldwidgetListSize = widgetList.size(); if (numberOfPages() == 0) widgetList.resize(0); else { switch (KVSPrefs::viewMode()) { case KVSPrefs::EnumViewMode::SinglePage: widgetList.resize(1); break; case KVSPrefs::EnumViewMode::Overview: { // Calculate the number of pages shown in overview mode. unsigned int visiblePages = KVSPrefs::overviewModeColumns() * KVSPrefs::overviewModeRows(); // Calculate the number of the first page in the tableau. tableauStartPage = startPage - ((startPage - 1) % visiblePages); // We cannot have more widgets then pages in the document. visiblePages = TQMIN(visiblePages, numberOfPages() - tableauStartPage + 1); if (widgetList.size() != visiblePages) widgetList.resize(visiblePages); break; } default: // In KVS_Continuous and KVS_ContinuousFacing all pages in the document are shown. widgetList.resize(numberOfPages()); } } bool isWidgetListResized = (widgetList.size() != oldwidgetListSize); widgetList.setAutoDelete(false); // If the widgetList is empty, there is nothing left to do. if (widgetList.size() == 0) { scrollView()->addChild(&widgetList); return; } // Allocate DocumentWidget structures so that all entries of // widgetList point to a valid DocumentWidget. DocumentWidget *documentWidget; for(TQ_UINT16 i=0; ishow(); connect(documentWidget, TQT_SIGNAL(localLink(const TQString &)), this, TQT_SLOT(handleLocalLink(const TQString &))); connect(documentWidget, TQT_SIGNAL(setStatusBarText(const TQString&)), this, TQT_SIGNAL(setStatusBarText(const TQString&)) ); } } // Set the page numbers for the newly allocated widgets. How this is // done depends on the viewMode. if (KVSPrefs::viewMode() == KVSPrefs::EnumViewMode::SinglePage) { // In KVS_SinglePage mode, any number between 1 and the maximum // number of pages is acceptable. If an acceptable value is found, // nothing is done, and otherwise '1' is set as a default. documentWidget = widgetList[0]; if (documentWidget != 0) { // Paranoia safety check documentWidget->setPageNumber(startPage); documentWidget->update(); } else kdError(4300) << "Zero-Pointer in widgetList in KMultiPage::generateDocumentWidgets()" << endl; } else { // In all other modes, the widgets will be numbered continuously, // starting from firstShownPage. for(TQ_UINT16 i=0; isetPageNumber(i+tableauStartPage); else documentWidget->setPageNumber(i+1); } else kdError(4300) << "Zero-Pointer in widgetList in KMultiPage::generateDocumentWidgets()" << endl; } } // Make the changes in the widgetList known to the scrollview. so // that the scrollview may update its contents. scrollView()->addChild(&widgetList); // If the number of widgets has changed, or the viewmode has been changed the widget // that displays the current page may not be visible anymore. Bring it back into focus. if (isWidgetListResized || !reload) gotoPage(startPage); } bool KMultiPage::gotoPage(const PageNumber& page) { return gotoPage(page, 0, true); } bool KMultiPage::gotoPage(const PageNumber& page, int y, bool isLink) { #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::gotoPage()" << endl; #endif if (widgetList.size() == 0) { kdError(4300) << "KMultiPage::gotoPage(" << page << ", y) called, but widgetList is empty" << endl; return false; } if (!page.isValid()) { kdDebug(1223) << "KMultiPage::gotoPage(" << page << ") invalid pageNumber." << endl; return false; } if (isLink) document_history.add(page, y); DocumentWidget* pageWidget; // If we are in overview viewmode if (KVSPrefs::viewMode() == KVSPrefs::EnumViewMode::Overview) { unsigned int visiblePages = KVSPrefs::overviewModeColumns() * KVSPrefs::overviewModeRows(); // Pagenumber of the first visibile Page in the current tableau unsigned int firstPage = ((DocumentWidget*)widgetList[0])->getPageNumber(); // Pagenumber of the first page in the new tableau. unsigned int tableauStartPage = page + 1 - (page % visiblePages); // If these numbers arn't equal "page" is not in the current tableu. if (firstPage != tableauStartPage) // widgets need to be updated { if ((numberOfPages() - tableauStartPage + 1 < visiblePages) || (widgetList.size() < visiblePages)) { // resize widgetList // the pages are also set correctly by "generateDocumentWidgets" generateDocumentWidgets(tableauStartPage); } else { // "page" is not shown in the scrollview, so we have to switch widgets. // Here we don't need to resize the widgetList. for (unsigned int i = 0; i < widgetList.size(); i++) { pageWidget = (DocumentWidget*)(widgetList[i]); if (pageWidget != 0) pageWidget->setPageNumber(tableauStartPage + i); } scrollView()->layoutPages(); } } // move scrollview to "page". // Make the widget pageWidget visible in the scrollview. Somehow this // doesn't seem to trigger the signal contentsMoved in the // TQScrollview, so that we better call setCurrentPage() ourselves. pageWidget = (DocumentWidget*)(widgetList[page % visiblePages]); scrollView()->moveViewportToWidget(pageWidget, y); // Set current page number. setCurrentPageNumber(page); return true; } else if (widgetList.size() == 1) { // If the widget list contains only a single element, then either // the document contains only one page, or we are in "single page" // view mode. In either case, we set the page number of the single // widget to 'page' pageWidget = (DocumentWidget*)(widgetList[0]); // Paranoia security check if (pageWidget == 0) { kdError(4300) << "KMultiPage::goto_Page() called with widgetList.size() == 1, but widgetList[0] == 0" << endl; return false; } if (pageCache->sizeOfPageInPixel(currentPageNumber()) == pageCache->sizeOfPageInPixel(page)) { // We are rendering the page before we switch the widget to the new page. // To make a smooth transition. We only do this if the size of the current and new page are equal, // otherwise we would have to render the page twice, if autozoom is enabled. pageCache->getPage(page); } pageWidget->setPageNumber(page); scrollView()->layoutPages(); scrollView()->moveViewportToWidget(pageWidget, y); } else { // There are multiple widgets, then we are either in the // "Continuous" or in the "Continouous-Facing" view mode. In that // case, we find the widget which is supposed to display page // 'page' and move the scrollview to make it visible // Paranoia security checks if (widgetList.size() < page) { kdError(4300) << "KMultiPage::goto_Page(page,y ) called with widgetList.size()=" << widgetList.size() << ", and page=" << page << endl; return false; } pageWidget = (DocumentWidget*)(widgetList[page-1]); if (pageWidget == 0) { kdError(4300) << "KMultiPage::goto_Page() called with widgetList.size() > 1, but widgetList[page] == 0" << endl; return false; } scrollView()->moveViewportToWidget(pageWidget, y); } if (isLink && y != 0) pageWidget->flash(y); // Set current page number. setCurrentPageNumber(page); return true; } void KMultiPage::handleLocalLink(const TQString &linkText) { #ifdef DEBUG_SPECIAL kdDebug(4300) << "hit: local link to " << linkText << endl; #endif if (renderer.isNull()) { kdError(4300) << "KMultiPage::handleLocalLink( " << linkText << " ) called, but renderer==0" << endl; return; } TQString locallink; if (linkText[0] == '#' ) locallink = linkText.mid(1); // Drop the '#' at the beginning else locallink = linkText; Anchor anch = renderer->findAnchor(locallink); if (anch.isValid()) gotoPage(anch); else { if (linkText[0] != '#' ) { // We could in principle use KIO::Netaccess::run() here, but // it is perhaps not a very good idea to allow a DVI-file to // specify arbitrary commands, such as "rm -rvf /". Using // the kfmclient seems to be MUCH safer. TQUrl DVI_Url(m_file); TQUrl Link_Url(DVI_Url, linkText, true); TQStringList args; args << "openURL"; args << Link_Url.toString(); kapp->kdeinitExec("kfmclient", args); } } } void KMultiPage::setCurrentPageNumber(const PageNumber& page) { #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::setCurrentPageNumber()" << endl; #endif if (page != currentPageNumber()) { markList()->setCurrentPageNumber(page); emit pageInfo(numberOfPages(), currentPageNumber()); } } PageNumber KMultiPage::currentPageNumber() { return markList()->currentPageNumber(); } void KMultiPage::doGoBack() { HistoryItem *it = document_history.back(); if (it != 0) gotoPage(it->page, it->ypos, false); // Do not add a history item. else kdDebug(4300) << "Faulty return -- bad history buffer" << endl; return; } void KMultiPage::doGoForward() { HistoryItem *it = document_history.forward(); if (it != 0) gotoPage(it->page, it->ypos, false); // Do not add a history item. else kdDebug(4300) << "Faulty return -- bad history buffer" << endl; return; } void KMultiPage::renderModeChanged() { pageCache->clear(); generateDocumentWidgets(); scrollView()->layoutPages(); for (TQ_UINT16 i=0; i < widgetList.size(); i++) { DocumentWidget* documentWidget = widgetList[i]; if (documentWidget == 0) continue; documentWidget->update(); } markList()->repaintThumbnails(); } void KMultiPage::repaintAllVisibleWidgets() { #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::repaintAllVisibleWidgets()" << endl; #endif bool everResized = false; // Go through the list of widgets and resize them, if necessary for(TQ_UINT16 i=0; isizeOfPageInPixel(documentWidget->getPageNumber()); if (pageSize != documentWidget->pageSize()) { documentWidget->setPageSize(pageSize); everResized = true; } } // If at least one widget was resized, all widgets should be // re-aligned. This will automatically update all necessary // widgets. if (everResized == true) scrollView()->layoutPages(true); } double KMultiPage::setZoom(double zoom) { #ifdef DEBUG_KMULTIPAGE kdDebug(1233) << "KMultiPage::setZoom(" << zoom << ")" << endl; #endif if (zoom < ZoomLimits::MinZoom/1000.0) zoom = ZoomLimits::MinZoom/1000.0; if (zoom > ZoomLimits::MaxZoom/1000.0) zoom = ZoomLimits::MaxZoom/1000.0; pageCache->setResolution(TQPaintDevice::x11AppDpiX()*zoom); emit zoomChanged(); return zoom; } void KMultiPage::print() { // Paranoid safety checks if (renderer.isNull()) return; if (renderer->isEmpty()) return; // Allocate the printer structure KPrinter *printer = getPrinter(); if (printer == 0) return; // initialize the printer using the print dialog if ( printer->setup(parentWdg, i18n("Print %1").tqarg(m_file.section('/', -1))) ) { // Now do the printing. TQValueList pageList = printer->pageList(); if (pageList.isEmpty()) printer->abort(); else { printer->setCreator("kviewshell"); printer->setDocName(m_file); RenderedDocumentPagePrinter rdpp(printer); // Obtain papersize information that is required to perform // the resizing and centering, if this is wanted by the user. Length paperWidth, paperHeight; TQPaintDeviceMetrics pdm(printer); paperWidth.setLength_in_mm(pdm.widthMM()); paperHeight.setLength_in_mm(pdm.heightMM()); TQValueList::ConstIterator it = pageList.begin(); while (true) { SimplePageSize paper_s(paperWidth, paperHeight); // Printing usually takes a while. This is to keep the GUI // updated. tqApp->processEvents(); TQPainter *paint = rdpp.getPainter(); if (paint != 0) { // Before drawing the page, we figure out the zoom-value, // taking the "page sizes and placement" options from the // printer dialog into account double factual_zoom = 1.0; // Obtain pagesize information that is required to perform the // resizing and centering, if this is wanted by the user. SimplePageSize page_s = sizeOfPage(*it); paint->save(); // Rotate the page, if appropriate. By default, page // rotation is enabled. This is also hardcoded into // KPrintDialogPage_PageOptions.cpp if ((page_s.isPortrait() != paper_s.isPortrait()) && (printer->option( "kde-kviewshell-rotatepage" ) != "false")) { paint->rotate(-90); paint->translate(-printer->resolution()*paperHeight.getLength_in_inch(), 0.0); paper_s = paper_s.rotate90(); } double suggested_zoom = page_s.zoomToFitInto(paper_s); // By default, "shrink page" and "expand page" are off. This // is also hardcoded into KPrintDialogPage_PageOptions.cpp if ((suggested_zoom < 1.0) && (printer->option( "kde-kviewshell-shrinkpage" ) == "true")) factual_zoom = suggested_zoom; if ((suggested_zoom > 1.0) && (printer->option( "kde-kviewshell-expandpage" ) == "true")) factual_zoom = suggested_zoom; Length delX, delY; // By default, "center page" is on. This is also hardcoded // into KPrintDialogPage_PageOptions.cpp if (printer->option( "kde-kviewshell-centerpage" ) != "false") { delX = (paper_s.width() - page_s.width()*factual_zoom)/2.0; delY = (paper_s.height() - page_s.height()*factual_zoom)/2.0; } // Now draw the page. rdpp.setPageNumber(*it); double resolution = factual_zoom*printer->resolution(); paint->translate(resolution*delX.getLength_in_inch(), resolution*delY.getLength_in_inch()); renderer->drawPage(resolution, &rdpp); paint->restore(); } ++it; if ((it == pageList.end()) || (printer->aborted() == true)) break; printer->newPage(); } // At this point the rdpp is destructed. The last page is then // printed. } } delete printer; } void KMultiPage::setRenderer(DocumentRenderer* _renderer) { renderer = _renderer; // Initialize documentPageCache. initializePageCache(); pageCache->setRenderer(renderer); _markList->setPageCache(pageCache); // Clear widget list. widgetList.resize(0); // Relay signals. connect(renderer, TQT_SIGNAL(setStatusBarText(const TQString&)), this, TQT_SIGNAL(setStatusBarText(const TQString&))); connect(pageCache, TQT_SIGNAL(paperSizeChanged()), this, TQT_SLOT(renderModeChanged())); connect(pageCache, TQT_SIGNAL(textSelected(bool)), this, TQT_SIGNAL(textSelected(bool))); connect(renderer, TQT_SIGNAL(documentIsChanged()), this, TQT_SLOT(renderModeChanged())); connect(this, TQT_SIGNAL(zoomChanged()), this, TQT_SLOT(repaintAllVisibleWidgets())); } void KMultiPage::updateWidgetSize(const PageNumber& pageNumber) { for(TQ_UINT16 i=0; igetPageNumber() == pageNumber) { // Resize, if necessary TQSize pageSize = pageCache->sizeOfPageInPixel(documentWidget->getPageNumber()); if (pageSize != documentWidget->pageSize()) { documentWidget->setPageSize(pageSize); scrollView()->layoutPages(); } // We have just one widget per page. break; } } // Update marklist markList()->updateWidgetSize(pageNumber); } PageNumber KMultiPage::widestPage() const { Length maxWidth; PageNumber pageNumber = 1; for (int i = 1; i <= numberOfPages(); i++) { Length width = pageCache->sizeOfPage(i).width(); if (width > maxWidth) { maxWidth = width; pageNumber = i; } } return pageNumber; } double KMultiPage::zoomForWidthColumns(unsigned int viewportWidth) const { Length maxLeftColumnWidth; Length maxRightColumnWidth; Length maxWidth; PageNumber widestPageLeft; PageNumber widestPageRight; for (int i = 1; i <= numberOfPages(); i++) { Length width = pageCache->sizeOfPage(i).width(); if ( i % 2 == 0) // page is in left column { if (width > maxLeftColumnWidth) { maxLeftColumnWidth = width; widestPageLeft = i; } } if ( i % 2 == 1) // page is in right column { if (width > maxRightColumnWidth) maxRightColumnWidth = width; widestPageRight = i; } } double ratio = maxLeftColumnWidth / (maxLeftColumnWidth + maxRightColumnWidth); // This number is the amount of space the left column should occupy in the viewport. unsigned int leftTargetWidth = (unsigned int)(ratio * viewportWidth); return pageCache->sizeOfPage(widestPageLeft).zoomForWidth(leftTargetWidth); } double KMultiPage::calculateFitToHeightZoomValue() { PageNumber pageNumber = 1; // See below, in the documentation of the method "calculatefitToWidthZoomLevel" // for an explanation of the complicated calculation we are doing here. int columns = scrollView()->getNrColumns(); int rows = scrollView()->getNrRows(); int continuousViewmode = scrollView()->isContinuous(); bool fullScreenMode = scrollView()->fullScreenMode(); if (columns == 1 && rows == 1 && !continuousViewmode) // single page mode { pageNumber = currentPageNumber(); if (!pageNumber.isValid()) pageNumber = 1; } int pageDistance = scrollView()->distanceBetweenPages(); if (columns == 1 && rows == 1 && !continuousViewmode && fullScreenMode) { // In Single Page Fullscreen Mode we want to fit the page to the // window without a margin around it. pageDistance = 0; } int targetViewportHeight = scrollView()->viewportSize(0,0).height(); int targetPageHeight = (targetViewportHeight - rows*pageDistance)/rows; int targetPageWidth = (int)(targetPageHeight * pageCache->sizeOfPage(pageNumber).aspectRatio() ); int targetViewportWidth = targetPageWidth * columns + (columns+1)*pageDistance; targetViewportHeight = scrollView()->viewportSize(targetViewportWidth, targetViewportHeight).height(); targetPageHeight = (targetViewportHeight - rows*pageDistance)/rows; return pageCache->sizeOfPage(pageNumber).zoomForHeight(targetPageHeight); } double KMultiPage::calculateFitToWidthZoomValue() { PageNumber pageNumber = 1; int columns = scrollView()->getNrColumns(); int rows = scrollView()->getNrRows(); int continuousViewmode = scrollView()->isContinuous(); bool fullScreenMode = scrollView()->fullScreenMode(); if (columns == 1 && rows == 1 && !continuousViewmode) // single page mode { // To calculate the zoom level in single page mode we need the size // of the current page. When a new document is opened this function // is called while the currentPageNumber is invalid. We use the size // of the first page of the document in this case. pageNumber = currentPageNumber(); if (!pageNumber.isValid()) pageNumber = 1; } if (columns == 1 && rows == 1 && continuousViewmode) // continuous viewmode { pageNumber = widestPage(); if (!pageNumber.isValid()) pageNumber = 1; } // rows should be 1 for Single Page Viewmode, // the number of Pages in Continuous Viewmode // and number of Pages/2 in Continuous-Facing Viewmode if (continuousViewmode) rows = (int)(ceil(numberOfPages() / (double)columns)); int pageDistance = scrollView()->distanceBetweenPages(); if (columns == 1 && rows == 1 && !continuousViewmode && fullScreenMode) { // In Single Page Fullscreen Mode we want to fit the page to the // window without a margin around it. pageDistance = 0; } // There is a slight complication here... if we just take the width // of the viewport and scale the contents by a factor x so that it // fits the viewport exactly, then, depending on chosen papersize // (landscape, etc.), the contents may be higher than the viewport // and the TQScrollview may or may not insert a scrollbar at the // right. If the scrollbar appears, then the usable width of the // viewport becomes smaller, and scaling by x does not really fit // the (now smaller page) anymore. // Calculate the width and height of the view, disregarding the // possible complications with scrollbars, e.g. assuming the maximal // space is available. // width of the widget excluding possible scrollbars int targetViewportWidth = scrollView()->viewportSize(0,0).width(); // maximal width of a single page int targetPageWidth = (targetViewportWidth - (columns+1) * pageDistance) / columns; // maximal height of a single page int targetPageHeight = (int)(targetPageWidth/pageCache->sizeOfPage(pageNumber).aspectRatio()); // FIXME: this is only correct if all pages in the document have the same height int targetViewportHeight = rows * targetPageHeight + (rows+1) * pageDistance; // Think again, this time use only the area which is really // acessible (which, in case that targetWidth targetHeight don't fit // the viewport, is really smaller because of the scrollbars). targetViewportWidth = scrollView()->viewportSize(targetViewportWidth, targetViewportHeight).width(); if (columns == 2 && continuousViewmode) // continuous facing { // TODO Generalize this for more than 2 columns return zoomForWidthColumns(targetViewportWidth - (columns+1) * pageDistance); } // maximal width of a single page (now the scrollbars are taken into account) targetPageWidth = (targetViewportWidth - (columns+1) * pageDistance) / columns; return pageCache->sizeOfPage(pageNumber).zoomForWidth(targetPageWidth); } void KMultiPage::prevPage() { TQ_UINT8 cols = scrollView()->getNrColumns(); TQ_UINT8 rows = scrollView()->getNrRows(); PageNumber np = 1; if (cols*rows < currentPageNumber()) { np = currentPageNumber() - cols*rows; } gotoPage(np); } void KMultiPage::nextPage() { TQ_UINT8 cols = scrollView()->getNrColumns(); TQ_UINT8 rows = scrollView()->getNrRows(); PageNumber np = TQMIN(currentPageNumber() + cols*rows, (TQ_UINT16)numberOfPages()); gotoPage(np); } void KMultiPage::firstPage() { gotoPage(1); } void KMultiPage::lastPage() { gotoPage(numberOfPages()); } void KMultiPage::scroll(TQ_INT32 deltaInPixel) { TQScrollBar* scrollBar = scrollView()->verticalScrollBar(); if (scrollBar == 0) { kdError(4300) << "KMultiPage::scroll called without scrollBar" << endl; return; } if (deltaInPixel < 0) { if (scrollBar->value() == scrollBar->minValue()) { if ( (currentPageNumber() == 1) || (changePageDelayTimer.isActive()) ) return; if (scrollView()->isContinuous()) return; changePageDelayTimer.stop(); prevPage(); scrollView()->setContentsPos(scrollView()->contentsX(), scrollBar->maxValue()); return; } } if (deltaInPixel > 0) { if (scrollBar->value() == scrollBar->maxValue()) { if ( (currentPageNumber() == numberOfPages()) || (changePageDelayTimer.isActive()) ) return; if (scrollView()->isContinuous()) return; changePageDelayTimer.stop(); nextPage(); scrollView()->setContentsPos(scrollView()->contentsX(), 0); return; } } scrollBar->setValue(scrollBar->value() + deltaInPixel); if ( (scrollBar->value() == scrollBar->maxValue()) || (scrollBar->value() == scrollBar->minValue()) ) changePageDelayTimer.start(200,true); else changePageDelayTimer.stop(); } void KMultiPage::scrollUp() { TQScrollBar* scrollBar = scrollView()->verticalScrollBar(); if (scrollBar == 0) return; scroll(-scrollBar->lineStep()); } void KMultiPage::scrollDown() { TQScrollBar* scrollBar = scrollView()->verticalScrollBar(); if (scrollBar == 0) return; scroll(scrollBar->lineStep()); } void KMultiPage::scrollLeft() { TQScrollBar* scrollBar = scrollView()->horizontalScrollBar(); if (scrollBar) scrollBar->subtractLine(); } void KMultiPage::scrollRight() { TQScrollBar* scrollBar = scrollView()->horizontalScrollBar(); if (scrollBar) scrollBar->addLine(); } void KMultiPage::scrollUpPage() { TQScrollBar* scrollBar = scrollView()->verticalScrollBar(); if (scrollBar) scrollBar->subtractPage(); } void KMultiPage::scrollDownPage() { TQScrollBar* scrollBar = scrollView()->verticalScrollBar(); if (scrollBar) scrollBar->addPage(); } void KMultiPage::scrollLeftPage() { TQScrollBar* scrollBar = scrollView()->horizontalScrollBar(); if (scrollBar) scrollBar->subtractPage(); } void KMultiPage::scrollRightPage() { TQScrollBar* scrollBar = scrollView()->horizontalScrollBar(); if (scrollBar) scrollBar->addPage(); } void KMultiPage::readDown() { PageView* sv = scrollView(); if (sv->atBottom()) { if (sv->isContinuous()) return; if (currentPageNumber() == numberOfPages()) return; nextPage(); sv->setContentsPos(sv->contentsX(), 0); } else sv->readDown(); } void KMultiPage::readUp() { PageView* sv = scrollView(); if (sv->atTop()) { if (sv->isContinuous()) return; if (currentPageNumber() == 1) return; prevPage(); sv->setContentsPos(sv->contentsX(), sv->contentsHeight()); } else sv->readUp(); } void KMultiPage::jumpToReference(const TQString& reference) { if (renderer.isNull()) return; gotoPage(renderer->parseReference(reference)); } void KMultiPage::gotoPage(const Anchor &a) { if (!a.page.isValid() || (renderer.isNull())) return; gotoPage(a.page, (int)(a.distance_from_top.getLength_in_inch()*pageCache->getResolution() + 0.5), true); } void KMultiPage::gotoPage(const TextSelection& selection) { if (selection.isEmpty()) { kdError(4300) << "KMultiPage::gotoPage(...) called with empty TextSelection." << endl; return; } RenderedDocumentPage* pageData = pageCache->getPage(selection.getPageNumber()); if (pageData == 0) { #ifdef DEBUG_DOCUMENTWIDGET kdDebug(4300) << "DocumentWidget::paintEvent: no documentPage generated" << endl; #endif return; } switch (widgetList.size()) { case 0: kdError(4300) << "KMultiPage::select() while widgetList is empty" << endl; break; case 1: ((DocumentWidget*)widgetList[0])->select(selection); break; default: if (widgetList.size() < currentPageNumber()) kdError(4300) << "KMultiPage::select() while widgetList.size()=" << widgetList.size() << "and currentPageNumber()=" << currentPageNumber() << endl; else ((DocumentWidget*)widgetList[selection.getPageNumber() - 1])->select(selection); } unsigned int y = pageData->textBoxList[selection.getSelectedTextStart()].box.top(); gotoPage(selection.getPageNumber(), y, false); } void KMultiPage::doSelectAll() { switch( widgetList.size() ) { case 0: kdError(4300) << "KMultiPage::doSelectAll() while widgetList is empty" << endl; break; case 1: ((DocumentWidget *)widgetList[0])->selectAll(); break; default: if (widgetList.size() < currentPageNumber()) kdError(4300) << "KMultiPage::doSelectAll() while widgetList.size()=" << widgetList.size() << "and currentPageNumber()=" << currentPageNumber() << endl; else ((DocumentWidget *)widgetList[currentPageNumber()-1])->selectAll(); } } void KMultiPage::showFindTextDialog() { if ((renderer.isNull()) || (renderer->supportsTextSearch() == false)) return; searchWidget->show(); searchWidget->setFocus(); } void KMultiPage::stopSearch() { if (searchInProgress) { // stop the search searchInProgress = false; } else searchWidget->hide(); } void KMultiPage::findNextText() { #ifdef KDVI_MULTIPAGE_DEBUG kdDebug(4300) << "KMultiPage::findNextText() called" << endl; #endif searchInProgress = true; // Used to remember if the documentPage we use is from the cache. // If not we need to delete it manually to avoid a memory leak. bool cachedPage = false; TQString searchText = searchWidget->getText(); if (searchText.isEmpty()) { kdError(4300) << "KMultiPage::findNextText() called when search text was empty" << endl; return; } bool case_sensitive = searchWidget->caseSensitive(); // Find the page and text position on the page where the search will // start. If nothing is selected, we start at the beginning of the // current page. Otherwise, start after the selected text. TODO: // Optimize this to get a better 'user feeling' TQ_UINT16 startingPage; TQ_UINT16 startingTextItem; TextSelection userSelection = pageCache->selectedText(); if (userSelection.isEmpty()) { startingPage = currentPageNumber(); startingTextItem = 0; } else { startingPage = userSelection.getPageNumber(); startingTextItem = userSelection.getSelectedTextEnd()+1; } TextSelection foundSelection; RenderedDocumentPagePixmap* searchPage = 0; for(unsigned int i = 0; i < numberOfPages(); i++) { unsigned int pageNumber = (i + startingPage - 1) % numberOfPages() + 1; if (!searchInProgress) { // Interrupt the search setStatusBarText(i18n("Search interrupted")); if (!cachedPage) delete searchPage; return; } if (i != 0) { setStatusBarText(i18n("Search page %1 of %2").tqarg(pageNumber).tqarg(numberOfPages())); kapp->processEvents(); } // Check if we already have a rendered version of the page in the cache. As we are only interested in the // text we don't care about the page size. if (pageCache->isPageCached(pageNumber)) { // If the last search page used was created locally, we need to destroy it if (!cachedPage) delete searchPage; searchPage = pageCache->getPage(pageNumber); cachedPage = true; } else { // If the page is not in the cache we draw a small version of it, since this is faster. // We only create a new searchPage if we need to, otherwise reuse the existing one. if (!searchPage || cachedPage) searchPage = new RenderedDocumentPagePixmap(); cachedPage = false; searchPage->resize(1,1); searchPage->setPageNumber(pageNumber); renderer->getText(searchPage); } // If there is no text in the current page, try the next one. if (searchPage->textBoxList.size() == 0) continue; foundSelection = searchPage->find(searchText, startingTextItem, case_sensitive); if (foundSelection.isEmpty()) { // In the next page, start search again at the beginning. startingTextItem = 0; clearSelection(); if (pageNumber == numberOfPages()) { int answ = KMessageBox::questionYesNo(scrollView(), i18n("The search string %1 could not be found by the " "end of the document. Should the search be restarted from the beginning " "of the document?").tqarg(searchText), i18n("Text Not Found"), KStdGuiItem::cont(), KStdGuiItem::cancel()); if (answ != KMessageBox::Yes) { setStatusBarText(TQString()); searchInProgress = false; if (!cachedPage) delete searchPage; return; } } } else { pageCache->selectText(foundSelection); gotoPage(pageCache->selectedText()); setStatusBarText(TQString()); searchInProgress = false; if (!cachedPage) delete searchPage; return; } } KMessageBox::sorry(scrollView(), i18n("The search string %1 could not be found.").tqarg(searchText)); setStatusBarText(TQString()); searchInProgress = false; if (!cachedPage) delete searchPage; } void KMultiPage::findPrevText() { #ifdef KDVI_MULTIPAGE_DEBUG kdDebug(4300) << "KMultiPage::findPrevText() called" << endl; #endif searchInProgress = true; // Used to remember if the documentPage we use is from the cache. // If not we need to delete it manually to avoid a memory leak. bool cachedPage = false; TQString searchText = searchWidget->getText(); if (searchText.isEmpty()) { kdError(4300) << "KMultiPage::findPrevText() called when search text was empty" << endl; return; } bool case_sensitive = searchWidget->caseSensitive(); // Find the page and text position on the page where the search will // start. If nothing is selected, we start at the beginning of the // current page. Otherwise, start after the selected text. TODO: // Optimize this to get a better 'user feeling' unsigned int startingPage; int startingTextItem; TextSelection userSelection = pageCache->selectedText(); if (userSelection.isEmpty()) { startingPage = currentPageNumber(); startingTextItem = -1; } else { startingPage = userSelection.getPageNumber(); startingTextItem = userSelection.getSelectedTextStart()-1; } TextSelection foundSelection; RenderedDocumentPagePixmap* searchPage = 0; for(unsigned int i = 0; i < numberOfPages(); i++) { int pageNumber = startingPage - i; if (pageNumber <= 0) pageNumber += numberOfPages(); if (!searchInProgress) { // Interrupt the search setStatusBarText(i18n("Search interrupted")); if (!cachedPage) delete searchPage; return; } if (i != 0) { setStatusBarText(i18n("Search page %1 of %2").tqarg(pageNumber).tqarg(numberOfPages())); kapp->processEvents(); } // Check if we already have a rendered version of the page in the cache. As we are only interested in the // text we don't care about the page size. if (pageCache->isPageCached(pageNumber)) { // If the last search page used was created locally, we need to destroy it if (!cachedPage) delete searchPage; searchPage = pageCache->getPage(pageNumber); cachedPage = true; } else { // If the page is not in the cache we draw a small version of it, since this is faster. // We only create a new searchPage if we need to, otherwise reuse the existing one. if (!searchPage || cachedPage) searchPage = new RenderedDocumentPagePixmap(); cachedPage = false; searchPage->resize(1,1); searchPage->setPageNumber(pageNumber); renderer->getText(searchPage); } // If there is no text in the current page, try the next one. if (searchPage->textBoxList.size() == 0) continue; foundSelection = searchPage->findRev(searchText, startingTextItem, case_sensitive); if (foundSelection.isEmpty()) { // In the next page, start search again at the beginning. startingTextItem = -1; clearSelection(); if (pageNumber == 1) { int answ = KMessageBox::questionYesNo(scrollView(), i18n("The search string %1 could not be found by the " "beginning of the document. Should the search be restarted from the end " "of the document?").tqarg(searchText), i18n("Text Not Found"), KStdGuiItem::cont(), KStdGuiItem::cancel()); if (answ != KMessageBox::Yes) { setStatusBarText(TQString()); searchInProgress = false; if (!cachedPage) delete searchPage; return; } } } else { pageCache->selectText(foundSelection); gotoPage(pageCache->selectedText()); setStatusBarText(TQString()); searchInProgress = false; if (!cachedPage) delete searchPage; return; } } KMessageBox::sorry(scrollView(), i18n("The search string %1 could not be found.").tqarg(searchText)); setStatusBarText(TQString()); searchInProgress = false; if (!cachedPage) delete searchPage; } void KMultiPage::clearSelection() { PageNumber page = pageCache->selectedText().getPageNumber(); if (!page.isValid()) return; // Clear selection pageCache->deselectText(); // Now we need to update the widget which contained the selection switch(widgetList.size()) { case 0: kdError(4300) << "KMultiPage::clearSelection() while widgetList is empty" << endl; break; case 1: widgetList[0]->update(); break; default: for (unsigned int i = 0; i < widgetList.size(); i++) { DocumentWidget* pageWidget = (DocumentWidget*)widgetList[i]; if (pageWidget->getPageNumber() == page) { pageWidget->update(); break; } } } } void KMultiPage::copyText() { pageCache->selectedText().copyText(); } void KMultiPage::timerEvent( TQTimerEvent * ) { #ifdef KMULTIPAGE_DEBUG kdDebug(4300) << "Timer Event " << endl; #endif reload(); } void KMultiPage::reload() { #ifdef KMULTIPAGE_DEBUG kdDebug(4300) << "Reload file " << m_file << endl; #endif if (renderer.isNull()) { kdError() << "KMultiPage::reload() called, but no renderer was set" << endl; return; } if (renderer->isValidFile(m_file)) { pageCache->clear(); pageCache->deselectText(); document_history.clear(); emit setStatusBarText(i18n("Reloading file %1").tqarg(m_file)); TQ_INT32 pg = currentPageNumber(); killTimer(timer_id); timer_id = -1; bool r = renderer->setFile(m_file, m_url); generateDocumentWidgets(); // Set Table of Contents tableOfContents->setContents(renderer->getBookmarks()); // Adjust number of widgets in the thumbnail sidebar markList()->clear(); markList()->setNumberOfPages(numberOfPages(), KVSPrefs::showThumbnails()); setCurrentPageNumber(pg); setFile(r); emit setStatusBarText(TQString()); } else { if (timer_id == -1) timer_id = startTimer(1000); } } bool KMultiPage::openFile() { if (renderer.isNull()) { kdError(4300) << "KMultiPage::openFile() called when no renderer was set" << endl; return false; } pageCache->deselectText(); document_history.clear(); pageCache->clear(); emit setStatusBarText(i18n("Loading file %1").tqarg(m_file)); bool r = renderer->setFile(m_file, m_url); if (r) { setCurrentPageNumber(1); generateDocumentWidgets(); // Set number of widgets in the thumbnail sidebar markList()->clear(); markList()->setNumberOfPages(numberOfPages(), KVSPrefs::showThumbnails()); TQString reference = url().ref(); if (!reference.isEmpty()) gotoPage(renderer->parseReference(reference)); // Set Table of Contents tableOfContents->setContents(renderer->getBookmarks()); } else m_file = TQString(); setFile(r); // Clear Statusbar emit setStatusBarText(TQString()); return r; } bool KMultiPage::openURL(const TQString &filename, const KURL &base_url) { m_file = filename; m_url = base_url; bool success = openFile(); if (success) setCurrentPageNumber(1); return success; } void KMultiPage::enableActions(bool fileLoaded) { Q_UNUSED(fileLoaded); } void KMultiPage::wheelEvent(TQWheelEvent *e) { TQScrollBar *sb = scrollView()->verticalScrollBar(); if (sb == 0) return; // Zoom in/out if (e->state() & ControlButton) { if (e->delta() < 0) emit zoomOut(); else emit zoomIn(); return; } TQ_INT32 pxl = -(e->delta()*sb->lineStep())/60; if (pxl == 0) { if (e->delta() > 0) pxl = -1; else pxl = 1; } // Faster scrolling if (e->state() & ShiftButton) pxl *= 10; scroll(pxl); } KPrinter *KMultiPage::getPrinter(bool enablePageSizeFeatures) { // Allocate a new KPrinter structure, if necessary KPrinter *printer = new KPrinter(true); if (printer == 0) { kdError(1223) << "KMultiPage::getPrinter(..): Cannot allocate new KPrinter structure" << endl; return 0; } // Allocate a new KPrintDialogPage structure and add it to the // printer, if the kmultipage implementation requests that if (enablePageSizeFeatures == true) { KPrintDialogPage_PageOptions *pageOptions = new KPrintDialogPage_PageOptions(); if (pageOptions == 0) { kdError(1223) << "KMultiPage::getPrinter(..): Cannot allocate new KPrintDialogPage_PageOptions structure" << endl; delete printer; return 0; } printer->addDialogPage( pageOptions ); } // Feed the printer with useful defaults and information. printer->setPageSelection( KPrinter::ApplicationSide ); printer->setCurrentPage( currentPageNumber() ); printer->setMinMax( 1, numberOfPages() ); printer->setFullPage( true ); // If pages are marked, give a list of marked pages to the // printer. We try to be smart and optimize the list by using ranges // ("5-11") wherever possible. The user will be tankful for // that. Complicated? Yeah, but that's life. TQValueList selectedPageNo = selectedPages(); if (selectedPageNo.isEmpty() == true) printer->setOption( "kde-range", "" ); else { int commaflag = 0; TQString range; TQValueList::ConstIterator it = selectedPageNo.begin(); do{ int val = *it; if (commaflag == 1) range += TQString(", "); else commaflag = 1; int endval = val; if (it != selectedPageNo.end()) { TQValueList::ConstIterator jt = it; jt++; do{ int val2 = *jt; if (val2 == endval+1) endval++; else break; jt++; } while( jt != selectedPageNo.end() ); it = jt; } else it++; if (endval == val) range += TQString("%1").tqarg(val); else range += TQString("%1-%2").tqarg(val).tqarg(endval); } while (it != selectedPageNo.end() ); printer->setOption( "kde-range", range ); } return printer; } void KMultiPage::doExportText() { // Generate a suggestion for a reasonable file name TQString suggestedName = url().filename(); suggestedName = suggestedName.left(suggestedName.find(".")) + ".txt"; TQString fileName = KFileDialog::getSaveFileName(suggestedName, i18n("*.txt|Plain Text (Latin 1) (*.txt)"), scrollView(), i18n("Export File As")); if (fileName.isEmpty()) return; TQFileInfo finfo(fileName); if (finfo.exists()) { int r = KMessageBox::warningContinueCancel (scrollView(), i18n("The file %1\nexists. Do you want to overwrite that file?").tqarg(fileName), i18n("Overwrite File"), i18n("Overwrite")); if (r == KMessageBox::Cancel) return; } TQFile textFile(fileName); textFile.open(IO_WriteOnly); TQTextStream stream(&textFile); TQProgressDialog progress(i18n("Exporting to text..."), i18n("Abort"), renderer->totalPages(), scrollView(), "export_text_progress", true); progress.setMinimumDuration(300); RenderedDocumentPagePixmap dummyPage; dummyPage.resize(1, 1); for(unsigned int page = 1; page <= renderer->totalPages(); page++) { progress.setProgress(page); tqApp->processEvents(); if (progress.wasCancelled()) break; dummyPage.setPageNumber(page); // We gracefully ignore any errors (bad file, etc.) renderer->getText(&dummyPage); for(unsigned int i = 0; i < dummyPage.textBoxList.size(); i++) { // We try to detect newlines if (i > 0) { // Like all our textalgorithmns this currently assumes left to right text. // TODO: make this more generic. But we first would need to guess the corrent // orientation. if (dummyPage.textBoxList[i].box.top() > dummyPage.textBoxList[i-1].box.bottom() && dummyPage.textBoxList[i].box.x() < dummyPage.textBoxList[i-1].box.x()) { stream << "\n"; } } stream << dummyPage.textBoxList[i].text; } // Send newline after each page. stream << "\n"; } // Switch off the progress dialog, etc. progress.setProgress(renderer->totalPages()); return; } void KMultiPage::slotEnableMoveTool(bool enable) { emit enableMoveTool(enable); } #include "kmultipage.moc"