//========================================================================
//
// XpdfWidget.h
//
// Copyright 2009-2021 Glyph & Cog, LLC
//
//========================================================================

//! \mainpage
//!
//! XpdfWidget is a PDF viewer widget class for Qt.
//! <br><br>
//! <a href="changes.html">Change history</a>
//! <br><br>
//! Copyright 2009-2021 Glyph & Cog, LLC

//! \file

#ifndef XPDFWIDGET_H
#define XPDFWIDGET_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <QAbstractScrollArea>

class QMutex;
class QTimer;
#if XPDFWIDGET_PRINTING
class QPrinter;
class QPrintDialog;
#endif

class GString;
class PDFDoc;
class QtPDFCore;

//------------------------------------------------------------------------


/*! Opaque handle used to represent an outline node. */
typedef void *XpdfOutlineHandle;

/*! Opaque handle used to represent a layer. */
typedef void *XpdfLayerHandle;

/*! Opaque handle used to represent a layer display order tree node. */
typedef void *XpdfLayerOrderHandle;

/*! Opaque handle used to represent an annotation. */
typedef void *XpdfAnnotHandle;

/*! Opaque handle used to represent a form field. */
typedef void *XpdfFormFieldHandle;

/*! Opaque handle used to represent a PDF document. */
typedef void *XpdfDocHandle;

//------------------------------------------------------------------------

/*! Text search result, returned by XpdfWidget::findAll(). */
struct XpdfFindResult {

  //! \cond PROTECTED
  XpdfFindResult(int pageA, double xMinA, double yMinA,
		 double xMaxA, double yMaxA)
    : page(pageA), xMin(xMinA), yMin(yMinA), xMax(xMaxA), yMax(yMaxA) {}
  XpdfFindResult(): page(0), xMin(0), yMin(0), xMax(0), yMax(0) {}
  //! \endcond

  /*! Page number. */
  int page;

  /*! \name Bounding box. */
  ///@{
  double xMin, yMin, xMax, yMax;
  ///@}
};

//------------------------------------------------------------------------

//! A PDF viewer widget class for Qt.
class XpdfWidget: public QAbstractScrollArea {
  Q_OBJECT

public:

  //! Error codes returned by certain XpdfViewer functions.
  enum ErrorCode {
    pdfOk               =    0,	//!< no error
    pdfErrOpenFile      =    1,	//!< couldn't open the PDF file
    pdfErrBadCatalog    =    2,	//!< couldn't read the page catalog
    pdfErrDamaged       =    3,	//!< PDF file was damaged and couldn't be
				//!<   repaired
    pdfErrEncrypted     =    4,	//!< file was encrypted and password was
				//!<   incorrect or not supplied
    pdfErrHighlightFile =    5,	//!< nonexistent or invalid highlight file
    pdfErrBadPrinter    =    6,	//!< invalid printer
    pdfErrPrinting      =    7,	//!< error during printing
    pdfErrPermission    =    8,	//!< PDF file doesn't allow that operation
    pdfErrBadPageNum    =    9,	//!< invalid page number
    pdfErrFileIO        =   10,	//!< file I/O error
    pdfErrNoHandle      = 1001,	//!< NULL object handle
    pdfErrOutOfMemory   = 1002,	//!< out of memory
    pdfErrBusy          = 1003,	//!< PDF component is busy
    pdfErrBadArg        = 1004	//!< invalid argument
  };

  //! Display modes, to be passed to XpdfWidget::setDisplayMode().
  enum DisplayMode {
    pdfDisplaySingle,			//!< single page
    pdfDisplayContinuous,		//!< pages stacked vertically
    pdfDisplaySideBySideSingle,		//!< two facing pages
    pdfDisplaySideBySideContinuous,	//!< facing pages, stacked vertically
    pdfDisplayHorizontalContinuous	//!< pages stacked horizontally
  };

  //! \name Zoom values
  //! Special values for XpdfWidget::setZoom() / XpdfWidget::getZoom()
  //@{
  static const int zoomToPage  = -1;	//!< zoom to fit whole page
  static const int zoomToWidth = -2;	//!< zoom to fit page width
  static const int zoomToHeight = -3;	//!< zoom to fit page height
  //@}

  //! \name Find flags
  //! Flags to be passed to XpdfWidget::find()
  //@{
  //! search backward from the starting point
  static const int findBackward      = 0x00000001;
  //! perform a case-sensitive search (default is case-insensitive)
  static const int findCaseSensitive = 0x00000002;
  //! start searching from the previous search result
  static const int findNext          = 0x00000004;
  //! limit the search to the current page
  static const int findOnePageOnly   = 0x00000008;
  //! limit the search to whole words
  static const int findWholeWord     = 0x00000010;
  //@}

  //! Initialize the XpdfWidget class, reading a configuration file.
  //! If \a configFileName is non-empty, the specified file is
  //! tried first.  If \a configFileName is empty, or the file
  //! doesn't exist, the default location is tried (\c <exe-dir>/xpdfrc
  //! on Windows; \c ~/\c .xpdfrc on Unix).
  //!
  //! This function must be called before any other XpdfWidget functions
  //! (including the constructor).  It will be called automatically
  //! (with a NULL \a configFileName) if it hasn't already been
  //! called when the first XpdfWidget function is used.
  static void init(const QString &configFileName = QString());

  //! Process a configuration command, i.e., one line of an xpdfrc file.
  //! Note that this applies globally to all XpdfWidget instances.
  static void setConfig(const QString &command);

  //! The XpdfWidget constructor.
  //! \param paperColor the paper background color (which should generally
  //!        be left as white)
  //! \param matteColor the matte color displayed between pages, and around
  //!        pages that are smaller than the window
  //! \param reverseVideo sets reverse video at startup
  //! \param parentA the parent QWidget
  XpdfWidget(const QColor &paperColor = QColor(0xff, 0xff, 0xff),
	     const QColor &matteColor = QColor(0x80, 0x80, 0x80),
	     bool reverseVideo = false, QWidget *parentA = 0);

  //! The XpdfWidget constructor.
  //! \param paperColor the paper background color (which should generally
  //!        be left as white)
  //! \param matteColor the matte color displayed between pages, and around
  //!        pages that are smaller than the window
  //! \param reverseVideo sets reverse video at startup
  //! \param parentA the parent QWidget
  //! This version has the \a parent argument first so it works correctly
  //! with Qt Designer.
  XpdfWidget(QWidget *parentA,
	     const QColor &paperColor = QColor(0xff, 0xff, 0xff),
	     const QColor &matteColor = QColor(0x80, 0x80, 0x80),
	     bool reverseVideo = false);

  //! Destroys the XpdfWidget.
  virtual ~XpdfWidget();

  //! Control handling of hyperlinks.
  //! If enabled, the viewer will follow hyperlinks when clicked with the
  //! left mouse button.  If disabled, the viewer will ignore left mouse
  //! button clicks on hyperlinks.  The default is enabled.
  void enableHyperlinks(bool on);

  //! Control handling of external hyperlinks.
  //! This setting allows disabling external hyperlinks (links that
  //! open another PDF file, or that involve a URL), independent of
  //! internal hyperlinks (links to locations within the same PDF
  //! file).  This setting is ignored if all hyperlinks are disabled
  //! (enableHyperlinks(false)).  The default is enabled.
  void enableExternalHyperlinks(bool on);

  //! Control handling of text selection.
  //! If enabled, the viewer will allow the user to select rectangular
  //! regions of text when the user drags with the left mouse button.  If
  //! disabled, dragging with the left mouse button is ignored.  The
  //! default is enabled.
  void enableSelect(bool on);

  //! Control mouse panning.
  //! If enabled, dragging with the middle mouse button pressed will pan
  //! the page.  If disabled, the middle button is ignored.  The default
  //! is enabled.
  void enablePan(bool on);

  //! Control touchscreen panning.
  //! If enabled, QPanGestures are recognized and used to pan the PDF
  //! view.
  void enableTouchPan(bool on);

  //! Control touchscreen zooming.
  //! If enabled QPinchGestures are recognized and used to pinch-zoom
  //! the PDF view.
  void enableTouchZoom(bool on);

  //! Control keypress passthrough.
  //! If enabled, XpdfWidget will pass keypress events through to the
  //! keyPress signal, with no other processing.  If disabled, XpdfWidget
  //! will implement some built-in key bindings.  The default is disabled.
  void setKeyPassthrough(bool on) { keyPassthrough = on; }

  //! Control mouse event passthrough.
  //! If enabled, XpdfWidget will pass mouse events through to the
  //! mousePress/mouseRelease signals, with no other processing.  If
  //! disabled, XpdfWidget will implement some built-in mouse handling
  //! (in addition to sending the signals).  The default is disabled.
  void setMousePassthrough(bool on) { mousePassthrough = on; }

  //! Control the password dialog.
  //! If enabled, the viewer will show a password dialog for encrypted
  //! files; if disabled, it will simply return \c pdfErrEncrypted unless
  //! the correct password is passed to \c pdfLoadFileWithPassword.  The
  //! default is enabled.
  void showPasswordDialog(bool showDlg);

  //! Set the matte color, i.e., the color used for background outside
  //! the actual page area.  The default is a medium gray.
  void setMatteColor(const QColor &matteColor);

  //! Turn reverse video mode on/off.  The default is off.
  void setReverseVideo(bool reverse);

  //! Set the cursor.  The requested cursor will only be displayed in
  //! the viewport (not in the scrollbars).
  void setCursor(const QCursor &cursor);

  //! Reset to the default cursor.
  void unsetCursor();

  //! Load a PDF file and display its first page.
  //! \param fileName the PDF file to load
  //! \param password a string to be tried first as the owner password
  //!        and then as the user password
  //! \return \c pdfOk if successful; an error code, otherwise
  ErrorCode loadFile(const QString &fileName,
		     const QString &password = "");

  //! Load a PDF file from a memory buffer and display its first page.
  //! \param buffer the PDF file in memory
  //! \param bufferLength length of \a buffer
  //! \param password a string to be tried first as the owner password
  //!        and then as the user password
  //! \return \c pdfOk if successful; an error code otherwise
  ErrorCode loadMem(const char *buffer, unsigned int bufferLength,
		    const QString &password = "");

  //! Load a PDF file and return a handle.

  //! This function can be safely called from a non-GUI thread.  Use
  //! XpdfWidget::loadDoc (on the GUI thread) to load the document
  //! handle into the viewer.  The handle returned in *\c docPtr
  //! should be passed to either XpdfWidget::loadDoc or
  //! XpdfWidget::freeDoc.
  //!
  //! Calling XpdfWidget::readDoc + XpdfWidget::loadDoc is equivalent
  //! to calling XpdfWidget::loadFile.  The difference is that readDoc
  //! can be called on a background thread to avoid stalling the user
  //! interface.
  //! \param docPtr the PDF document handle will be returned her
  //! \param fileName the PDF file to load
  //! \param password a string to be tried first as the owner password
  //!        and then as the user password
  //! \return \c pdfOk if successful; an error code, otherwise
  ErrorCode readDoc(XpdfDocHandle *docPtr,
		    const QString &fileName,
		    const QString &password = "");

  //! Load a PDF document and display its first page.
  //! This function displays a PDF document handle created by
  //! XpdfWidget::readDoc.  The document handle should not be used for
  //! anything else after calling this function.
  //! \return \c pdfOk if successful; an error code, otherwise
  ErrorCode loadDoc(XpdfDocHandle doc);

  //! Free a PDF document.
  //! This function frees a PDF document handle created by
  //! XpdfWidget::readDoc.  It should only be called if the document
  //! is not going to be displayed.  That is: after calling
  //! XpdfWidget::readDoc, you should call either XpdfWidget::loadDoc
  //! or XpdfWidget::freeDoc.  The document handle should not be used
  //! for anything else after calling this function.
  void freeDoc(XpdfDocHandle doc);

  //! Reload the current PDF file.
  //! This reloads the current PDF file, maintaining the zoom and
  //! scroll position (if possible).  This only works if the PDF file
  //! was loaded from a file (i.e., with XpdfWidget::loadFile, not
  //! with XpdfWidget::loadMem).
  //! \return \c pdfOk if successful; an error code otherwise
  ErrorCode reload();

  //! Close the currently open PDF file (if any).
  //! Calling this function is optional - the current PDF file will be
  //! automatically closed if XpdfWidget::loadFile or XpdfWidget::loadMem
  //! is called.
  void closeFile();

  //! Save the PDF file with another name.
  //! \param fileName the file to be written
  //! \return \c pdfOk if successful; an error code otherwise
  ErrorCode saveAs(const QString &fileName);

  //! Get the file name of the currently open PDF file.
  QString getFileName() const;

  //! Returns true if there is currently a PDF file open.
  bool hasOpenDocument() const;

  //! Return the number of pages in the currently open PDF file.
  //! Returns -1 if no file is open.
  int getNumPages() const;

  //! Return the currently displayed page number.
  //! Returns -1 if no file is open.
  int getCurrentPage() const;

  //! Return the page number corresponding to the middle of the
  //! window.
  int getMidPage() const;

  //! Display the specified page.
  void gotoPage(int pageNum);

  //! Display the first page.
  //! This is equivalent to \code
  //!     gotoPage(1)
  //! \endcode
  void gotoFirstPage();

  //! Display the last page.
  //! This is equivalent to \code
  //!     gotoPage(getNumPages())
  //! \endcode
  void gotoLastPage();

  //! Display the next page.
  void gotoNextPage(bool scrollToTop = true);

  //! Display the previous page.
  void gotoPreviousPage(bool scrollToTop = true);

  //! Go to a named destination.
  bool gotoNamedDestination(const QString &dest);

  //! Go forward along the history list.
  void goForward();

  //! Go backward along the history list.
  void goBackward();

  //! Scroll one screen up.
  void scrollPageUp();

  //! Scroll one screen down.
  void scrollPageDown();

  //! Scroll the page so that the top-left corner of the window is
  //! (\a xx,\a yy) pixels from the top-left corner of the PDF page.
  void scrollTo(int xx, int yy);

  //! Scroll the page by (\a dx,\a dy) pixels.  If \a dx is positive,
  //! scrolls right; if \a dx is negative, scrolls left.  Similarly,
  //! positive and negative values of \a dy scroll down and up,
  //! respectively.
  void scrollBy(int dx, int dy);

  //! Return the current scroll position x coordinate.
  int getScrollX() const;

  //! Return the current scroll position y coordinate.
  int getScrollY() const;

  //! Change the zoom factor.
  //! This can be a percentage factor (where 100 means 72 dpi) or one of
  //! the special values, XpdfWidget::zoomToPage or XpdfWidget::zoomToWidth.
  void setZoom(double zoom);

  //! Return the current zoom factor.
  //! This can be a percentage factor or one of the special values,
  //! XpdfWidget::zoomToPage or XpdfWidget::zoomToWidth.
  double getZoom() const;

  //! Return the current zoom factor as a percentage.
  //! If the zoom is set to XpdfWidget::zoomToPage or
  //! XpdfWidget::zoomToWidth, returns the computed zoom percentage
  //! for the specified page, based on the current window size.
  double getZoomPercent(int page = 1) const;

  //! Zoom in to the specified rectangle.
  //! The coordinate system is the same one used by
  //! XpdfWidget::getCurrentSelection.  This function will set the zoom
  //! factor and scroll position so that the specified rectangle just fits
  //! in the window.
  void zoomToRect(int page, double xMin, double yMin,
		  double xMax, double yMax);

  //! Set the zoom factor, while maintaining the current center.
  //! Accepts the same zoom values as XpdfWidget::setZoom.
  void zoomCentered(double zoom);

  //! Zoom so that the current page(s) fill the window width.
  //! Maintains the vertical center.
  void zoomToCurrentWidth();

  //! Change the page rotation.
  //! \param rotate rotation angle in degrees - must be 0, 90, 180, or 270
  void setRotate(int rotate);

  //! Return the current page rotation.
  //! The angle can be 0, 90, 180, or 270.
  int getRotate() const;

  //! Set continuous or single-page view mode.
  //! Deprecated: this is equivalent to calling setDisplayMode() with
  //! pdfDisplaySingle or pdfDisplayContinuous.
  //! \param continuous true for continous view mode, false for single-page
  //!        view mode
  void setContinuousMode(bool continuous);

  //! Return true if the viewer is in continuous view mode, or false
  //! if it is in any other mode.  Deprecated: see getDisplayMode().
  bool getContinuousMode() const;

  //! Set the display mode.
  void setDisplayMode(DisplayMode mode);

  //! Return the current display mode.
  DisplayMode getDisplayMode();

  //! Returns true if the mouse is currently over a hyperlink.
  bool mouseOverLink();

  //! Returns true if the specified coordinates are inside a
  //! hyperlink.  Note: This function expects PDF coordinates, not window
  //! coordinates.
  bool onLink(int page, double xx, double yy);

  //! Get destination information for the hyperlink at the specified
  //! page and coordinates.  If there is a link at the specified
  //! point, returns a string suitable for displaying to a user;
  //! otherwise returns an empty string.  Note: This function expects
  //! PDF coordinates, not window coordinates.
  QString getLinkInfo(int page, double xx, double yy);

  //! Get destination information for the hyperlink under the mouse.
  //! If the mouse is currently over a hyperlink, return an info
  //! string (same as with getLinkInfo()); otherwise return an empty
  //! string.
  QString getMouseLinkInfo();

  //! Activate the link (if any) at the specified page and coordinates.
  //! Returns true if successful.  Note: This function expects PDF
  //! coordinates, not window coordinates.
  bool gotoLinkAt(int page, double xx, double yy);

  //! Check for an annotation containing the specified point.
  //! Returns NULL if there is no annotation at this point.  Note:
  //! This function expects PDF coordinates, not window coordinates.
  XpdfAnnotHandle onAnnot(int page, double xx, double yy);

  //! Get the annotation type.
  QString getAnnotType(XpdfAnnotHandle annot);

  //! Get the annotation's content.
  //! Usage of this depends on the annotation type.
  QString getAnnotContent(XpdfAnnotHandle annot);

  //! Check for a form field containing the specified point.
  //! Returns NULL if there is no annotation at this point.  Note:
  //! This function expects PDF coordinates, not window coordinates.
  XpdfFormFieldHandle onFormField(int page, double xx, double yy);

  //! Get the form field's type.
  QString getFormFieldType(XpdfFormFieldHandle field);

  //! Get the form field's name.
  QString getFormFieldName(XpdfFormFieldHandle field);

  //! Get the form field's content.
  //! Usage of this depends on the field type.
  QString getFormFieldValue(XpdfFormFieldHandle field);

  //! Get the form field's bounding box.
  void getFormFieldBBox(XpdfFormFieldHandle field, int *pageNum,
			double *xMin, double *yMin,
			double *xMax, double *yMax);

  //! Convert window coordinates to PDF coordinates.  Returns true if
  //! successful, i.e., if the specified point falls on a PDF page.
  bool convertWindowToPDFCoords(int winX, int winY,
				int *page, double *pdfX, double *pdfY);

  //! Convert PDF coordinates to window coordinates.
  void convertPDFToWindowCoords(int page, double pdfX, double pdfY,
				int *winX, int *winY);

  //! Enable or disable window redraws.
  //! This is useful, e.g., for avoiding extra redraws during window
  //! resizing.  Deprecated -- this just calls setUpdatesEnabled().
  void enableRedraw(bool enable);

  //! Return the coordinates of the specified page box.
  //! \param page the page number
  //! \param box the requested page box - one of "media", "crop",
  //!        "bleed", "trim", or "art" (\a box is not case-sensitive)
  //! \param *xMin returns the minimum x coordinate of the box
  //! \param *yMin returns the minimum y coordinate of the box
  //! \param *xMax returns the maximum x coordinate of the box
  //! \param *yMax returns the maximum y coordinate of the box
  //!
  //! All coordinates are in points (1 point = 1/72 inch).
  void getPageBox(int page, const QString &box,
		  double *xMin, double *yMin, double *xMax, double *yMax) const;

  //! Return the width of the specified page.
  //! This function returns the crop box width, measured in points
  //! (1 point = 1/72 inch).
  double getPageWidth(int page) const;

  //! Return the height of the specified page.
  //! This function returns the crop box height, measured in points
  //! (1 point = 1/72 inch).
  double getPageHeight(int page) const;

  //! Get the default rotation for the specified page.
  //! This is the default viewing rotation specified in the PDF file -
  //! it will be one of 0, 90, 180, or 270.
  int getPageRotation(int page) const;

  //! Check to see if there is a selection.
  //! Returns true if there is a currently active selection.
  bool hasSelection();

  //! Returns the current selection.
  //! If there is a currently active selection, sets *\a page, (*\a x0,*\a y0),
  //! and (*\a x1,*\a y1) to the page number and upper-left and lower-right
  //! coordinates, respectively, and returns true.  If there is no selection,
  //! returns false.
  bool getCurrentSelection(int *page, double *x0, double *y0,
			   double *x1, double *y1) const;

  //! Set the selection.
  //! Sets the current selection to the rectangle with upper-left corner
  //! (\a x0,\a y0) and lower-right corner (\a x1,\a y1) on \a page.
  void setCurrentSelection(int page, double x0, double y0,
			   double x1, double y1);

  //! Clear the selection.
  void clearSelection();

  //! Check for block selection mode.
  //! Returns true if the current selection mode is block.
  bool isBlockSelectMode();

  //! Check for linear selection mode.
  //! Returns true if the current selection mode is linear.
  bool isLinearSelectMode();

  //! Set block selection mode.
  //! In this mode, the selection is a simple rectangle.  Any part of
  //! the page can be selected, regardless of the content on the page.
  void setBlockSelectMode();

  //! Set linear selection mode.
  //! In this mode, the selection follows text.  Non-text regions
  //! cannot be selected.
  void setLinearSelectMode();

  //! Set the selection color.
  void setSelectionColor(const QColor &selectionColor);


  //! Force a complete redraw.
  void forceRedraw();

#if XPDFWIDGET_PRINTING
  //! Checks to see if printing is allowed.
  //! This function returns false if the currently displayed PDF file
  //! is encrypted and does not allow printing (or if no PDF file is
  //! currently open).  The owner password can be used to circumvent
  //! this: if a valid owner password was supplied to
  //! XpdfWidget::loadFile, this function will always return true.  If
  //! this function returns false, the printing functions will return
  //! an error.
  bool okToPrint() const;

  //! Print the currently displayed PDF file.
  //! Prints the currently displayed PDF file.  If \a showDialog is
  //! true, displays the Qt print dialog, and uses the printer
  //! selected by the user.  If \a showDialog is false, prints to the
  //! default printer without showing any dialogs.
  ErrorCode print(bool showDialog);

  //! Print the currently displayed PDF file.
  //! Prints the currently displayed PDF file to \a prt.
  ErrorCode print(QPrinter *prt);

  //! Cancel an in-progress print job.  This should be called in
  //! response to a printStatus signal.
  void cancelPrint() { printCanceled = true; }

  void updatePrintStatus(int nextPage, int firstPage, int lastPage);
  bool isPrintCanceled() { return printCanceled; }

  //! Set the horizontal and vertical print resolution, in dots per
  //! inch (DPI).  The horizontal and vertical resolutions are
  //! typically the same.  (There are exceptions, such as some chart
  //! printers.)
  void setPrintDPI(int hDPI, int vDPI);
#endif // XPDFWIDGET_PRINTING

  //! Convert a page to a color image.
  //! This function converts the page number \a page to a 24-bit RGB
  //! bitmap, at a resolution of \a dpi dots per inch.
  QImage convertPageToImage(int page, double dpi);

  //! Convert a rectangular region of a page to a color image.
  //! This function converts a rectangular region, defined by
  //! corners (\a x0,\a y0) and (\a x1,\a y1), of page number \a page
  //! to a 24-bit RGB bitmap, at a resolution of \a dpi dots per inch.
  QImage convertRegionToImage(int page, double x0, double y0,
			      double x1, double y1, double dpi);

  //! Retrieve an embedded thumbnail image.
  //! This function returns the embedded thumbnail image for the
  //! specified page, or a null image if there is no embedded
  //! thumbnail.  This function does not do any rasterization -- it
  //! only returns a non-null image if there is an embedded thumbnail
  //! in the PDF file.
  QImage getThumbnail(int page);

  //! Checks to see if text extraction is allowed.
  //! This function returns false if the currently displayed PDF file
  //! is encrypted and does not allow extraction of text (or if no PDF
  //! file is currently open).  The owner password can be used to
  //! circumvent this: if a valid owner password was supplied to
  //! XpdfWidget::loadFile, this function will always return true.
  //! If this function returns false, the text extraction functions will
  //! not return any text.
  bool okToExtractText() const;

  //! Set the encoding to use for text extraction.
  //! The following encodings are predefined:
  //!   - \c "Latin1": ISO-8859-1 (this is the default value)
  //!   - \c "ASCII7": 7-bit ASCII
  //!   - \c "UTF-8": Unicode in UTF-8 format
  //!   - \c "UCS-2": Unicode in UCS-2 format
  //!
  //! Additional encodings can be defined via the xpdfrc config file.
  void setTextEncoding(const QString &encodingName);

  //! Extract text from a region of a page.
  //! This function extracts and returns text from the rectangular
  //! region, defined by corners (\a x0,\a y0) and (\a x1,\a y1), of
  //! page number \a page.  The coordinates returned by
  //! XpdfWidget::getCurrentSelection may be passed directly to this
  //! function.  Returns an empty string if no file is open or if
  //! text extraction is not allowed.
  QString extractText(int page, double x0, double y0,
		      double x1, double y1);

  //! Get the currently selected text.
  //! Returns an empty string if there is no selection (or if there is
  //! no text in the selected region).
  QString getSelectedText();

  //! Copy the current selection to the clipboard.
  void copySelection();

  //! Find a text string.
  //! This function searches for a Unicode text string.  Starts
  //! searching after (before, if searching backward) the current
  //! selection (if there is a selection), or at the top (bottom,
  //! if searching backward) of the current page (if there is no
  //! selection).  The \a flags argument consists of zero or more
  //! of the following, or-ed together:
  //!   - \c findBackward - search backward from the starting point
  //!   - \c findCaseSensitive - perform a case-sensitive search
  //!     (default is case-insensitive)
  //!   - \c findNext - start searching from the previous search result
  //!   - \c findOnePageOnly - limit the search to the current page
  //!   - \c findWholeWord - limit the search to whole words
  bool find(const QString &text, int flags = 0);

  //! Find all occurrences of a text string.
  //! This function searches for a Unicode text string, in pages
  //! \a firstPage .. \a lastPage.  The \a flags argument consists of
  //! zero or more of the following, or-ed together:
  //!   - \c findCaseSensitive - perform a case-sensitive search
  //!     (default is case-insensitive)
  //!   - \c findWholeWord - limit the search to whole words
  //! Returns a list of search results.
  QVector<XpdfFindResult> findAll(const QString &text, int firstPage,
				  int lastPage, int flags = 0);

  //! Check if the PDF file has page labels.
  //! Return true if the currently open PDF file has page labels.
  bool hasPageLabels();

  //! Convert a page number to a page label.
  //! Return the page label for page number \a pageNum.  Returns an
  //! empty string if the page number is invalid or if the PDF file
  //! doesn't have page labels.
  QString getPageLabelFromPageNum(int pageNum);

  //! Convert a page label to a page number.
  //! Return the page number for \a pageLabel.  Returns -1 if there is
  //! no matching page label or if the PDF file doesn't have page
  //! labels.
  int getPageNumFromPageLabel(QString pageLabel);

  //! Return the number of children of an outline tree node.
  //! This function returns the number of children of node \a outline,
  //! or the number of root outline entries if \a outline is \c NULL.
  int getOutlineNumChildren(XpdfOutlineHandle outline);

  //! Return a child of an outline tree node.
  //! This function returns the \a idx 'th child of node \a outline,
  //! or the \a idx 'th root entry if \a outline is \c NULL.
  XpdfOutlineHandle getOutlineChild(XpdfOutlineHandle outline, int idx);

  //! Return the parent of an outline tree node.
  //! This function returns the parent of node \a outline, or NULL if
  //! \a outline is a root item.
  XpdfOutlineHandle getOutlineParent(XpdfOutlineHandle outline);

  //! Get the title of an outline tree node.
  //! This function returns the title of node \a outline.
  QString getOutlineTitle(XpdfOutlineHandle outline);

  //! Return true if the specified outline entry starts open.
  bool getOutlineStartsOpen(XpdfOutlineHandle outline);

  //! Return the target page number for the specified outline entry.
  int getOutlineTargetPage(XpdfOutlineHandle outline);

  //! Jump to the target of the specified outline entry.
  void gotoOutlineTarget(XpdfOutlineHandle outline);

  //! Return the number of layers in the PDF file.
  //! Note that a PDF file can have zero or more layers.
  int getNumLayers() const;

  //! Get a layer handle.
  //! This function returns a handle for the \a idx 'th layer.
  XpdfLayerHandle getLayer(int idx) const;

  //! Get the name of a layer.
  //! This function returns the title of \a layer.
  QString getLayerName(XpdfLayerHandle layer) const;

  //! Get the visibility state of a layer.
  //! Returns true if the layer is currently visible, false if not.
  bool getLayerVisibility(XpdfLayerHandle layer) const;

  //! Set the visibility state of a layer.
  //! \param layer the layer handle
  //! \param visibility the new state - true for visible, false for not
  //!        visible
  void setLayerVisibility(XpdfLayerHandle layer, bool visibility);

  //! Get the suggested state for this layer in viewing mode.
  //! This function returns one of:
  //!   -  1: on
  //!   -  0: off
  //!   - -1: unset
  int getLayerViewState(XpdfLayerHandle layer) const;

  //! Get the suggested state for this layer in printing mode.
  //! This function returns one of:
  //!   -  1: on
  //!   -  0: off
  //!   - -1: unset
  int getLayerPrintState(XpdfLayerHandle layer) const;

  //! Get the root of the layer display order tree.
  XpdfLayerOrderHandle getLayerOrderRoot() const;

  //! Check the type of a layer display order tree node.
  //! Returns true if the specified node of the layer display order
  //! tree is a name; false if the node is a layer.
  bool getLayerOrderIsName(XpdfLayerOrderHandle order) const;

  //! Get the name of a layer display order tree node.
  //! This should only be called if getLayerOrderIsName returns true.
  QString getLayerOrderName(XpdfLayerOrderHandle order) const;

  //! Get the layer associated with a layer display order tree node.
  XpdfLayerHandle getLayerOrderLayer(XpdfLayerOrderHandle order);

  //! Returns the number of children attached to a layer display order
  //! tree node.
  int getLayerOrderNumChildren(XpdfLayerOrderHandle order);

  //! Returns the \a idx 'th child of a layer display order tree node.
  XpdfLayerOrderHandle getLayerOrderChild(XpdfLayerOrderHandle order, int idx);

  //! Return the parent of a layer display order tree node.
  //! This function returns the parent of node \a order, or NULL if \a
  //! order is the root node.
  XpdfLayerOrderHandle getLayerOrderParent(XpdfLayerOrderHandle order);

  //! Return the number of embedded files in the current PDF file.
  int getNumEmbeddedFiles();

  //! Return the name of the \a idx 'th embedded file.
  QString getEmbeddedFileName(int idx);

  //! Save the \a idx 'th embedded file with the specified file name.
  //! Returns true if successful.
  bool saveEmbeddedFile(int idx, QString fileName);

  //--- for internal use

  //! \cond

  virtual QSize sizeHint() const;
  QtPDFCore *getCore() { return core; }

  //! \endcond

signals:

  //! This signal is emitted whenever the viewer displays a new page.
  //! It can be triggered by user actions (e.g., the PageDown button),
  //! or program control (e.g., the gotoNextPage function).
  //! \param pageNum - the new page number
  void pageChange(int pageNum);

  //! This signal is emitted whenever the page shown at the middle of
  //! the window changes.
  //! It is similar to XpdfWidget::pageChange, except that it reflects
  //! the page shown at the middle of the window (instead of the page
  //! at the top of the window).
  void midPageChange(int pageNum);

  //! This signal is emitted just before a PDF file is loaded.
  void preLoad();

  //! This signal is emitted just after a PDF file is loaded.
  void postLoad();

  //! This signal is emitted whenever a key is pressed.
  void keyPress(QKeyEvent *e);

  //! This signal is emitted whenever a mouse button is pressed.
  void mousePress(QMouseEvent *e);

  //! This signal is emitted whenever a mouse button is released.
  void mouseRelease(QMouseEvent *e);

  //! This signal is emitted whenever a mouse button is clicked.
  //! A click is defined as a button press and release within a short
  //! distance of each other (a few pixels).  The sequence of signals
  //! is: mousePress, possibly a few mouseMoves, mouseRelease, and
  //! then mouseClick.
  void mouseClick(QMouseEvent *e);

  //! This signal is emitted whenever a mouse button is double-clicked.
  //! Double clicks are two clicks within a few pixels of each other,
  //! and within a certain time of each other.  The sequence of
  //! signals is: mousePress, mouseRelease, mouseClick, mousePress,
  //! mouseRelease, mouseDoubleClick.  There may also be mouseMoves
  //! between the presses and releases.
  void mouseDoubleClick(QMouseEvent *e);

  //! This signal is emitted whenever a mouse button is triple-clicked.
  //! Triple clicks are three clicks within a few pixels of each
  //! other, and within a certain time of each other.  The sequence of
  //! signals is: mousePress, mouseRelease, mouseClick, mousePress,
  //! mouseRelease, mouseDoubleClick, mousePress, mouseRelease,
  //! mouseTripleClick.  There may also be mouseMoves between the
  //! presses and releases.
  void mouseTripleClick(QMouseEvent *e);

  //! This signal is emitted whenever the mouse pointer is moved.
  void mouseMove(QMouseEvent *e);

  //! This signal is emitted whenever a mouse wheel is clicked.
  void mouseWheel(QWheelEvent *e);

  //! This signal is emitted whenever the user clicks on a hyperlink.
  //! \param linkType the type of link - one of:
  //!          - \c "goto": a link to another page in the same PDF
  //!            file - \a dest is empty; \a page is the destination
  //!            page number
  //!          - \c "pdf": a link to another PDF file - \a dest is the
  //!            target PDF file; \a page is 0
  //!          - \c "launch": an arbitrary command to be run - \a dest
  //!            is the command; \a page is 0
  //!          - \c "url": a URL link - \a dest is the URL; \a page is 0
  //!          - \c "named": a "named action" link - \a dest is the
  //!            action (see the PDF spec for details); \a page is 0
  //!          - \c "unknown": an unknown link type - \a dest is empty;
  //!            \a page is 0
  //! \param dest destination string
  //! \param page destination page number
  void linkClick(const QString &linkType, const QString &dest, int page);

  //! This signal is emitted when the user selects an area.
  //! Use XpdfWidget::getCurrentSelection to retrieve the selection.
  void selectDone();

  //! This signal is emitted whenever the widget is repainted.  \a
  //! finished is true if the painted view is complete, or false if
  //! this was an incremental update, i.e., if the view is still being
  //! rendered.
  void paintDone(bool finished);

  //! This signal is emitted when the widget is resized.
  void resized();


#if XPDFWIDGET_PRINTING
  //! This signal is called before each page is spooled, and after the
  //! last page is spooled.  It is typically used to update a print
  //! status dialog.  \a nextPage is the next page to be printed.
  //! \a firstPage and \a lastPage specify the range of pages being
  //! printed.
  void printStatus(int nextPage, int firstPage, int lastPage);
#endif

  //! \cond PROTECTED

  void tileDone();

  //! \endcond

protected:

  //! \cond PROTECTED

  virtual void paintEvent(QPaintEvent *eventA);
  virtual void resizeEvent(QResizeEvent *eventA);
  virtual void scrollContentsBy(int dx, int dy);
  virtual void keyPressEvent(QKeyEvent *e);
  virtual void mousePressEvent(QMouseEvent *e);
  virtual void mouseReleaseEvent(QMouseEvent *e);
  virtual void mouseMoveEvent(QMouseEvent *e);
  virtual void wheelEvent(QWheelEvent *e);
  virtual bool eventFilter(QObject *obj, QEvent *event);

  //! \endcond

private slots:

  void tick();

private:

  void setup(const QColor &paperColor, const QColor &matteColor,
	     bool reverseVideo);
  static void updateCbk(void *data, GString *fileName,
			int pageNum, int numPages,
			const char *linkLabel);
  static void midPageChangedCbk(void *data, int pageNum);
  static void preLoadCbk(void *data);
  static void postLoadCbk(void *data);
  static void linkCbk(void *data, const char *type,
		      const char *dest, int page);
  static void selectDoneCbk(void *data);
  static void paintDoneCbk(void *data, bool finished);
  static void tileDoneCbk(void *data);

  friend class XpdfViewer;
  bool getLinkTarget(int page, double xx, double yy,
		     QString &targetFileName, int &targetPage,
		     QString &targetDest);

#if XPDFWIDGET_PRINTING
  QPrinter *printerForDialog;
  QPrintDialog *printDialog;
  int printHDPI, printVDPI;
  bool printCanceled;
#endif

  static QMutex initMutex;

  QtPDFCore *core;
  double scaleFactor;

  bool keyPassthrough;
  bool mousePassthrough;
  int lastMousePressX[3], lastMousePressY[3];
  ulong lastMousePressTime[3];
  bool lastMouseEventWasPress;

  bool touchPanEnabled;
  bool touchZoomEnabled;
  double pinchZoomStart;

  QTimer *tickTimer;
};

#endif
