#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../config.h" #include "documentWidget.h" #include "fontpool.h" #include "kdvi_multipage.h" #include "kviewpart.h" #include "performanceMeasurement.h" #include "prefs.h" #include "kprinterwrapper.h" #include "dviWidget.h" #include "optionDialogFontsWidget.h" #include "optionDialogSpecialWidget.h" #include //#define KDVI_MULTIPAGE_DEBUG #ifdef PERFORMANCE_MEASUREMENT // These objects are explained in the file "performanceMeasurement.h" QTime performanceTimer; int performanceFlag = 0; #endif extern "C" { void *init_kdvipart() { KGlobal::locale()->insertCatalogue("kviewshell"); return new KDVIMultiPageFactory; } } KInstance *KDVIMultiPageFactory::s_instance = 0L; KDVIMultiPageFactory::KDVIMultiPageFactory() { } KDVIMultiPageFactory::~KDVIMultiPageFactory() { delete s_instance; s_instance = 0; } KParts::Part *KDVIMultiPageFactory::createPartObject( QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name, const char *, const QStringList & ) { KParts::Part *obj = new KDVIMultiPage(parentWidget, widgetName, parent, name); return obj; } KInstance *KDVIMultiPageFactory::instance() { if (!s_instance) s_instance = new KInstance("kdvi"); return s_instance; } KDVIMultiPage::KDVIMultiPage(QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name) : KMultiPage(parentWidget, widgetName, parent, name), window(0) { #ifdef PERFORMANCE_MEASUREMENT performanceTimer.start(); #endif timer_id = -1; setInstance(KDVIMultiPageFactory::instance()); printer = 0; findDialog = 0; findNextAction = 0; findPrevAction = 0; lastCurrentPage = 0; window = new dviRenderer(scrollView()); // Points to the same object as renderer to avoid downcasting. // FIXME: Remove when the API of the Renderer-class is finished. window->setName("DVI renderer"); setRenderer(window); docInfoAction = new KAction(i18n("Document &Info"), 0, window, SLOT(showInfo()), actionCollection(), "info_dvi"); embedPSAction = new KAction(i18n("Embed External PostScript Files..."), 0, this, SLOT(slotEmbedPostScript()), actionCollection(), "embed_postscript"); if (window->supportsTextSearch()) { findTextAction = KStdAction::find(this, SLOT(showFindTextDialog()), actionCollection(), "find"); findNextAction = KStdAction::findNext(this, SLOT(findNextText()), actionCollection(), "findnext"); findNextAction->setEnabled(false); findPrevAction = KStdAction::findPrev(this, SLOT(findPrevText()), actionCollection(), "findprev"); findPrevAction->setEnabled(false); } copyTextAction = KStdAction::copy(&userSelection, SLOT(copyText()), actionCollection(), "copy_text"); copyTextAction->setEnabled(!userSelection.isEmpty()); connect(&userSelection, SIGNAL(selectionIsNotEmpty(bool)), copyTextAction, SLOT(setEnabled(bool))); selectAllAction = KStdAction::selectAll(this, SLOT(doSelectAll()), actionCollection(), "edit_select_all"); deselectAction = KStdAction::deselect(&userSelection, SLOT(clear()), actionCollection(), "edit_deselect_all"); deselectAction->setEnabled(!userSelection.isEmpty()); connect(&userSelection, SIGNAL(selectionIsNotEmpty(bool)), deselectAction, SLOT(setEnabled(bool))); new KAction(i18n("Enable All Warnings && Messages"), 0, this, SLOT(doEnableWarnings()), actionCollection(), "enable_msgs"); exportPSAction = new KAction(i18n("PostScript..."), 0, this, SLOT(doExportPS()), actionCollection(), "export_postscript"); exportPDFAction = new KAction(i18n("PDF..."), 0, this, SLOT(doExportPDF()), actionCollection(), "export_pdf"); exportTextAction = new KAction(i18n("Text..."), 0, this, SLOT(doExportText()), actionCollection(), "export_text"); KStdAction::tipOfDay(this, SLOT(showTip()), actionCollection(), "help_tipofday"); setXMLFile("kdvi_part.rc"); connect(window, SIGNAL(request_goto_page(int, int)), this, SLOT(goto_page(int, int) ) ); connect(scrollView(), SIGNAL(contentsMoving(int, int)), this, SLOT(contentsMovingInScrollView(int, int)) ); readSettings(); preferencesChanged(); enableActions(false); // Show tip of the day, when the first main window is shown. QTimer::singleShot(0,this,SLOT(showTipOnStart())); } void KDVIMultiPage::slotEmbedPostScript(void) { if (window) { window->embedPostScript(); emit askingToCheckActions(); } } void KDVIMultiPage::setEmbedPostScriptAction(void) { if ((window == 0) || (window->dviFile == 0) || (window->dviFile->numberOfExternalPSFiles == 0)) embedPSAction->setEnabled(false); else embedPSAction->setEnabled(true); } void KDVIMultiPage::slotSave() { // Try to guess the proper ending... QString formats; QString ending; int rindex = m_file.findRev("."); if (rindex == -1) { ending = QString::null; formats = QString::null; } else { ending = m_file.mid(rindex); // e.g. ".dvi" formats = fileFormats().grep(ending).join("\n"); } QString fileName = KFileDialog::getSaveFileName(QString::null, 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 (QFile(fileName).exists()) { int r = KMessageBox::warningYesNo (0, i18n("The file %1\nexists. Do you want to overwrite that file?").arg(fileName), i18n("Overwrite File")); if (r == KMessageBox::No) return; } // TODO: error handling... if ((window != 0) && (window->dviFile != 0) && (window->dviFile->dvi_Data() != 0)) window->dviFile->saveAs(fileName); return; } void KDVIMultiPage::slotSave_defaultFilename() { // TODO: error handling... if ((window != 0) && (window->dviFile != 0)) window->dviFile->saveAs(m_file); return; } bool KDVIMultiPage::isModified() { if ((window == 0) || (window->dviFile == 0) || (window->dviFile->dvi_Data() == 0)) return false; else return window->dviFile->isModified; } KDVIMultiPage::~KDVIMultiPage() { if (timer_id != -1) killTimer(timer_id); timer_id = -1; writeSettings(); Prefs::writeConfig(); delete printer; } void KDVIMultiPage::contentsMovingInScrollView(int x, int y) { Q_UINT16 cpg = getCurrentPageNumber(); if ((cpg != 0) && (window != 0) && (window->dviFile != 0)) emit(pageInfo(window->dviFile->total_pages, cpg-1)); } bool KDVIMultiPage::openFile() { document_history.clear(); emit setStatusBarText(i18n("Loading file %1").arg(m_file)); bool r = window->setFile(m_file); setEmbedPostScriptAction(); if (!r) emit setStatusBarText(QString::null); generateDocumentWidgets(); window->changePageSize(); emit numberOfPages(window->totalPages()); enableActions(r); QString reference = url().ref(); if (!reference.isEmpty()) gotoPage(window->parseReference(reference)); kdError() << "A" << endl; return r; } void KDVIMultiPage::jumpToReference(QString reference) { if (window == 0) return; gotoPage(window->parseReference(reference)); } QStringList KDVIMultiPage::fileFormats() { QStringList r; r << i18n("*.dvi *.DVI|TeX Device Independent Files (*.dvi)"); return r; } void KDVIMultiPage::gotoPage(const anchor &a) { kdError() << "GOTOPAGE pg=" << a.page << ", dist=" << a.distance_from_top_in_inch << endl; if (a.page.isInvalid() || (window == 0)) return; goto_page(a.page-1, a.distance_from_top_in_inch * window->getResolution() + 0.5); } void KDVIMultiPage::gotoPage(int pageNr, int beginSelection, int endSelection ) { #ifdef KDVI_MULTIPAGE_DEBUG kdDebug(4300) << "KDVIMultiPage::gotoPage( pageNr=" << pageNr << ", beginSelection=" << beginSelection <<", endSelection=" << endSelection <<" )" << endl; #endif if (pageNr == 0) { kdError(4300) << "KDVIMultiPage::gotoPage(...) called with pageNr=0" << endl; return; } DocumentPage *pageData = pageCache.getPage(pageNr); if (pageData == 0) { #ifdef DEBUG_DOCUMENTWIDGET kdDebug(4300) << "DocumentWidget::paintEvent: no DocumentPage generated" << endl; #endif return; } QString selectedText(""); for(unsigned int i = beginSelection; i < endSelection; i++) { selectedText += pageData->textLinkList[i].linkText; selectedText += "\n"; } userSelection.set(pageNr, beginSelection, endSelection, selectedText); Q_UINT16 y = pageData->textLinkList[beginSelection].box.bottom(); goto_page(pageNr-1, y); /* document_history.add(pageNr,y); scrollView()->ensureVisible(scrollView()->width()/2, y ); emit pageInfo(window->totalPages(), pageNr ); */ } void KDVIMultiPage::setPaperSize(double w, double h) { window->setPaper(w, h); } void KDVIMultiPage::doSelectAll(void) { switch( widgetList.size() ) { case 0: kdError(4300) << "KDVIMultiPage::doSelectAll(void) while widgetList is empty" << endl; break; case 1: ((DocumentWidget *)widgetList[0])->selectAll(); break; default: if (widgetList.size() < getCurrentPageNumber()) kdError(4300) << "KDVIMultiPage::doSelectAll(void) while widgetList.size()=" << widgetList.size() << "and getCurrentPageNumber()=" << getCurrentPageNumber() << endl; else ((DocumentWidget *)widgetList[getCurrentPageNumber()-1])->selectAll(); } } void KDVIMultiPage::doExportPS(void) { window->exportPS(); } void KDVIMultiPage::doExportPDF(void) { window->exportPDF(); } void KDVIMultiPage::addConfigDialogs(KConfigDialog* configDialog) { static optionDialogFontsWidget* fontConfigWidget = 0; fontConfigWidget = new optionDialogFontsWidget(scrollView()); optionDialogSpecialWidget* specialConfigWidget = new optionDialogSpecialWidget(scrollView()); configDialog->addPage(fontConfigWidget, Prefs::self(), i18n("TeX Fonts"), "fonts"); configDialog->addPage(specialConfigWidget, Prefs::self(), i18n("DVI Specials"), "dvi"); configDialog->setHelp("preferences", "kdvi"); connect(configDialog, SIGNAL(settingsChanged()), this, SLOT(preferencesChanged())); } void KDVIMultiPage::preferencesChanged() { #ifdef KDVI_MULTIPAGE_DEBUG kdDebug(4300) << "preferencesChanged" << endl; #endif int mfmode = Prefs::metafontMode(); bool showPS = Prefs::showPS(); bool useFontHints = Prefs::useFontHints(); window->setPrefs( showPS, Prefs::editorCommand(), mfmode, useFontHints); } bool KDVIMultiPage::print(const QStringList &pages, int current) { // Make sure the KPrinter is available if (printer == 0) { printer = new KPrinter(); if (printer == 0) { kdError(4300) << "Could not allocate printer structure" << endl; return false; } } // Feed the printer with useful defaults and information. printer->setPageSelection( KPrinter::ApplicationSide ); printer->setCurrentPage( current+1 ); printer->setMinMax( 1, window->totalPages() ); printer->setFullPage( true ); // Give a suggestion for the paper orientation. Unfortunately, as of // now, KPrinter then automatically disables the orientation widget, // so that the user cannot change our suggestion here. Thus, we // comment this out for now. /* if (window != 0) if (window->paper_height_in_cm >= window->paper_width_in_cm) printer->setOrientation( KPrinter::Portrait ); else printer->setOrientation( KPrinter::Landscape ); */ // 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. if (pages.isEmpty() == true) printer->setOption( "kde-range", "" ); else { int commaflag = 0; QString range; QStringList::ConstIterator it = pages.begin(); do{ int val = (*it).toUInt()+1; if (commaflag == 1) range += QString(", "); else commaflag = 1; int endval = val; if (it != pages.end()) { QStringList::ConstIterator jt = it; jt++; do{ int val2 = (*jt).toUInt()+1; if (val2 == endval+1) endval++; else break; jt++; } while( jt != pages.end() ); it = jt; } else it++; if (endval == val) range += QString("%1").arg(val); else range += QString("%1-%2").arg(val).arg(endval); } while (it != pages.end() ); printer->setOption( "kde-range", range ); } // Show the printer options requestor if (!printer->setup(scrollView(), i18n("Print %1").arg(m_file.section('/', -1)))) return false; // This funny method call is necessary for the KPrinter to return // proper results in printer->orientation() below. It seems that // KPrinter does some options parsing in that method. ((KDVIPrinterWrapper *)printer)->doPreparePrinting(); if (printer->pageList().isEmpty()) { KMessageBox::error( scrollView(), i18n("The list of pages you selected was empty.\n" "Maybe you made an error in selecting the pages, " "e.g. by giving an invalid range like '7-2'.") ); return false; } // Turn the results of the options requestor into a list arguments // which are used by dvips. QString dvips_options = QString::null; // Print in reverse order. if ( printer->pageOrder() == KPrinter::LastPageFirst ) dvips_options += "-r "; // Print only odd pages. if ( printer->pageSet() == KPrinter::OddPages ) dvips_options += "-A "; // Print only even pages. if ( printer->pageSet() == KPrinter::EvenPages ) dvips_options += "-B "; // We use the printer->pageSize() method to find the printer page // size, and pass that information on to dvips. Unfortunately, dvips // does not understand all of these; what exactly dvips understands, // depends on its configuration files. Consequence: expect problems // with unusual paper sizes. switch( printer->pageSize() ) { case KPrinter::A4: dvips_options += "-t a4 "; break; case KPrinter::B5: dvips_options += "-t b5 "; break; case KPrinter::Letter: dvips_options += "-t letter "; break; case KPrinter::Legal: dvips_options += "-t legal "; break; case KPrinter::Executive: dvips_options += "-t executive "; break; case KPrinter::A0: dvips_options += "-t a0 "; break; case KPrinter::A1: dvips_options += "-t a1 "; break; case KPrinter::A2: dvips_options += "-t a2 "; break; case KPrinter::A3: dvips_options += "-t a3 "; break; case KPrinter::A5: dvips_options += "-t a5 "; break; case KPrinter::A6: dvips_options += "-t a6 "; break; case KPrinter::A7: dvips_options += "-t a7 "; break; case KPrinter::A8: dvips_options += "-t a8 "; break; case KPrinter::A9: dvips_options += "-t a9 "; break; case KPrinter::B0: dvips_options += "-t b0 "; break; case KPrinter::B1: dvips_options += "-t b1 "; break; case KPrinter::B10: dvips_options += "-t b10 "; break; case KPrinter::B2: dvips_options += "-t b2 "; break; case KPrinter::B3: dvips_options += "-t b3 "; break; case KPrinter::B4: dvips_options += "-t b4 "; break; case KPrinter::B6: dvips_options += "-t b6 "; break; case KPrinter::B7: dvips_options += "-t b7 "; break; case KPrinter::B8: dvips_options += "-t b8 "; break; case KPrinter::B9: dvips_options += "-t b9 "; break; case KPrinter::C5E: dvips_options += "-t c5e "; break; case KPrinter::Comm10E: dvips_options += "-t comm10e "; break; case KPrinter::DLE: dvips_options += "-t dle "; break; case KPrinter::Folio: dvips_options += "-t folio "; break; case KPrinter::Ledger: dvips_options += "-t ledger "; break; case KPrinter::Tabloid: dvips_options += "-t tabloid "; break; } // Orientation if ( printer->orientation() == KPrinter::Landscape ) dvips_options += "-t landscape "; // List of pages to print. QValueList pageList = printer->pageList(); dvips_options += "-pp "; int commaflag = 0; for( QValueList::ConstIterator it = pageList.begin(); it != pageList.end(); ++it ) { if (commaflag == 1) dvips_options += QString(","); else commaflag = 1; dvips_options += QString("%1").arg(*it); } // Now print. For that, export the DVI-File to PostScript. Note that // dvips will run concurrently to keep the GUI responsive, keep log // of dvips and allow abort. Giving a non-zero printer argument // means that the dvi-widget will print the file when dvips // terminates, and then delete the output file. KTempFile tf; window->exportPS(tf.name(), dvips_options, printer); // "True" may be a bit euphemistic. However, since dvips runs // concurrently, there is no way of telling the result of the // printing command at this stage. return true; } // Explanation of the timerEvent. // // This is a dreadful hack. The problem we adress with this timer // event is the following: the kviewshell has a KDirWatch object which // looks at the DVI file and calls reload() when the object has // changed. That works very nicely in principle, but in practise, when // TeX runs for several seconds over a complicated file, this does not // work at all. First, most of the time, while TeX is still writing, // the file is invalid. Thus, reload() is very often called when the // DVI file is bad. We solve this problem by checking the file // first. If the file is bad, we do not reload. Second, when the file // finally becomes good, it very often happens that KDirWatch does not // notify us anymore. Whether this is a bug or a side effect of a // feature of KDirWatch, I dare not say. We remedy that problem by // using a timer: when reload() was called on a bad file, we // automatically come back (via the timerEvent() function) every // second and check if the file became good. If so, we stop the // timer. It may well happen that KDirWatch calls us several times // while we are waiting for the file to become good, but that does not // do any harm. // // -- Stefan Kebekus. void KDVIMultiPage::timerEvent( QTimerEvent * ) { #ifdef KDVI_MULTIPAGE_DEBUG kdDebug(4300) << "Timer Event " << endl; #endif reload(); } void KDVIMultiPage::reload() { #ifdef KDVI_MULTIPAGE_DEBUG kdDebug(4300) << "Reload file " << m_file << endl; #endif if (window->correctDVI(m_file)) { killTimer(timer_id); timer_id = -1; bool r = window->setFile(m_file); enableActions(r); emit pageInfo(window->totalPages(), window->curr_page()-1 ); } else { if (timer_id == -1) timer_id = startTimer(1000); } } void KDVIMultiPage::enableActions(bool b) { docInfoAction->setEnabled(b); selectAllAction->setEnabled(b); findTextAction->setEnabled(b); exportPSAction->setEnabled(b); exportPDFAction->setEnabled(b); exportTextAction->setEnabled(b); setEmbedPostScriptAction(); } void KDVIMultiPage::doEnableWarnings(void) { KMessageBox::information (scrollView(), i18n("All messages and warnings will now be shown.")); KMessageBox::enableAllMessages(); KTipDialog::setShowOnStart(true); } void KDVIMultiPage::showTip(void) { KTipDialog::showTip(scrollView(), "kdvi/tips", true); } void KDVIMultiPage::showTipOnStart(void) { KTipDialog::showTip(scrollView(), "kdvi/tips"); } DocumentWidget* KDVIMultiPage::createDocumentWidget() { // TODO: handle different sizes per page. DVIWidget* documentWidget = new DVIWidget(scrollView()->viewport(), scrollView(), getRenderer()->sizeOfPage(/*page+*/1), &pageCache, &userSelection, "singlePageWidget" ); // Handle source links connect(documentWidget, SIGNAL(SRCLink(const QString&,QMouseEvent *, DocumentWidget *)), getRenderer(), SLOT(handleSRCLink(const QString &,QMouseEvent *, DocumentWidget *))); return documentWidget; } #include "kdvi_multipage.moc"