You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PreviewPanel.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.awt.viewer;
  19. import java.awt.Color;
  20. import java.awt.Dimension;
  21. import java.awt.GridLayout;
  22. import java.awt.Point;
  23. import java.awt.Toolkit;
  24. import java.awt.event.MouseEvent;
  25. import java.awt.event.MouseListener;
  26. import java.awt.event.MouseMotionListener;
  27. import java.awt.geom.Rectangle2D;
  28. import javax.swing.JLabel;
  29. import javax.swing.JOptionPane;
  30. import javax.swing.JPanel;
  31. import javax.swing.JScrollPane;
  32. import javax.swing.JViewport;
  33. import javax.swing.SwingUtilities;
  34. import javax.swing.border.EmptyBorder;
  35. import org.apache.xmlgraphics.util.UnitConv;
  36. import org.apache.fop.apps.FOPException;
  37. import org.apache.fop.apps.FOUserAgent;
  38. import org.apache.fop.apps.MimeConstants;
  39. import org.apache.fop.area.PageViewport;
  40. import org.apache.fop.render.awt.AWTRenderer;
  41. /**
  42. * <p>Holds a scrollpane with the rendered page(s) and handles actions performed
  43. * to alter the display of the page.
  44. * </p>
  45. * <p>Use PreviewPanel when you want to embed a preview in your own application
  46. * with your own controls. Use PreviewDialog when you want to use the standard
  47. * Fop controls.
  48. * </p>
  49. * <p>In order to embed a PreviewPanel in your own app, create your own renderer,
  50. * and your own agent. In order to support reloads, you may also implement your
  51. * own Renderable extension or the default InputHandler. Setting the Renderable
  52. * to null works fine though.
  53. * Then call setPreviewDialogDisplayed(false) to hide the
  54. * default dialog. Finally create a preview panel with the agent, renderable and
  55. * renderer and add it to your gui:
  56. * </p>
  57. * <pre>
  58. * FopFactory fopFactory = FopFactory.newInstance();
  59. * AWTRenderer renderer = new AWTRenderer();
  60. * FOUserAgent agent = fopFactory.newFOUserAgent();
  61. * agent.setRendererOverride(renderer);
  62. * renderer.setPreviewDialogDisplayed(false);
  63. * renderer.setUserAgent(agent);
  64. * renderer.setUserAgent(agent);
  65. * previewPanel = new PreviewPanel(agent, null, renderer);
  66. * previewPanel = new PreviewPanel(ua);
  67. * myGui.add(previewPanel);
  68. * </pre>
  69. *
  70. * In order to set options and display a page do:
  71. * <pre>
  72. * renderer.clearViewportList();
  73. * // build report xml here
  74. * reload(); // optional if setting changed
  75. * </pre>
  76. *
  77. * If you wan't to change settings, don't call reload. A good example is
  78. * to set the page to fill the screen and set the scrolling mode:
  79. * <pre>
  80. * double scale = previewPanel.getScaleToFitWindow();
  81. * previewPanel.setScaleFactor(scale);
  82. * previewPanel.setDisplayMode(PreviewPanel.CONTINUOUS);
  83. * </pre>
  84. */
  85. public class PreviewPanel extends JPanel {
  86. /** Constant for setting single page display. */
  87. public static final int SINGLE = 1;
  88. /** Constant for setting continuous page display. */
  89. public static final int CONTINUOUS = 2;
  90. /** Constant for displaying even/odd pages side by side in continuous form. */
  91. public static final int CONT_FACING = 3;
  92. /** The number of pixels left empty at the top bottom and sides of the page. */
  93. private static final int BORDER_SPACING = 10;
  94. /** The main display area */
  95. private JScrollPane previewArea;
  96. /** The AWT renderer - often shared with PreviewDialog */
  97. private AWTRenderer renderer;
  98. /** The FOUserAgent associated with this panel - often shared with PreviewDialog */
  99. protected FOUserAgent foUserAgent;
  100. /**
  101. * Renderable instance that can be used to reload and re-render a document after
  102. * modifications.
  103. */
  104. protected Renderable renderable;
  105. /** The number of the page which is currently selected */
  106. private int currentPage = 0;
  107. /** The index of the first page displayed on screen. */
  108. private int firstPage = 0;
  109. /** The number of pages concurrently displayed on screen. */
  110. private int pageRange = 1;
  111. /** The display mode. One of SINGLE, CONTINUOUS or CONT_FACING. */
  112. private int displayMode = SINGLE;
  113. /** The component(s) that hold the rendered page(s) */
  114. private ImageProxyPanel[] pagePanels = null;
  115. /**
  116. * Panel showing the page panels in a grid. Usually the dimensions
  117. * of the grid are 1x1, nx1 or nx2.
  118. */
  119. private JPanel gridPanel = null;
  120. /** Asynchronous reloader thread, used when reload() method is called. */
  121. private Reloader reloader;
  122. /**
  123. * Allows any mouse drag on the page area to scroll the display window.
  124. */
  125. private ViewportScroller scroller;
  126. /**
  127. * Creates a new PreviewPanel instance.
  128. * @param foUserAgent the user agent
  129. * @param renderable the Renderable instance that is used to reload/re-render a document
  130. * after modifications.
  131. * @param renderer the AWT Renderer instance to paint with
  132. */
  133. public PreviewPanel(FOUserAgent foUserAgent, Renderable renderable, AWTRenderer renderer) {
  134. super(new GridLayout(1, 1));
  135. this.renderable = renderable;
  136. this.renderer = renderer;
  137. this.foUserAgent = foUserAgent;
  138. //Override target resolution for the computer screen
  139. this.foUserAgent.setTargetResolution(Toolkit.getDefaultToolkit().getScreenResolution());
  140. gridPanel = new JPanel();
  141. gridPanel.setLayout(new GridLayout(0, 1)); // rows, cols
  142. previewArea = new JScrollPane(gridPanel);
  143. previewArea.getViewport().setBackground(Color.gray);
  144. // FIXME should add scroll wheel support here at some point.
  145. scroller = new ViewportScroller(previewArea.getViewport());
  146. previewArea.addMouseListener(scroller);
  147. previewArea.addMouseMotionListener(scroller);
  148. previewArea.setMinimumSize(new Dimension(50, 50));
  149. add(previewArea);
  150. }
  151. /**
  152. * @return the currently visible page
  153. */
  154. public int getPage() {
  155. return currentPage;
  156. }
  157. /**
  158. * Selects the given page, displays it on screen and notifies
  159. * listeners about the change in selection.
  160. * @param number the page number
  161. */
  162. public void setPage(int number) {
  163. if (displayMode == CONTINUOUS || displayMode == CONT_FACING) {
  164. currentPage = number;
  165. gridPanel.scrollRectToVisible(pagePanels[currentPage].getBounds());
  166. } else { // single page mode
  167. currentPage = number;
  168. firstPage = currentPage;
  169. }
  170. showPage();
  171. }
  172. /**
  173. * Sets the display mode.
  174. * @param mode One of SINGLE, CONTINUOUS or CONT_FACING.
  175. */
  176. public void setDisplayMode(int mode) {
  177. if (mode != displayMode) {
  178. displayMode = mode;
  179. gridPanel.setLayout(new GridLayout(0, displayMode == CONT_FACING ? 2 : 1));
  180. reload();
  181. }
  182. }
  183. /**
  184. * Returns the display mode.
  185. * @return mode One of SINGLE, CONTINUOUS or CONT_FACING.
  186. */
  187. public int getDisplayMode() {
  188. return displayMode;
  189. }
  190. /**
  191. * Reloads and reformats document.
  192. */
  193. public synchronized void reload() {
  194. if (reloader == null || !reloader.isAlive()) {
  195. reloader = new Reloader();
  196. reloader.start();
  197. }
  198. }
  199. /**
  200. * Allows a (yet) simple visual debug of the document.
  201. */
  202. void debug() {
  203. renderer.debug = !renderer.debug;
  204. reload();
  205. }
  206. /**
  207. * Allows any mouse drag on the page area to scroll the display window.
  208. */
  209. private class ViewportScroller implements MouseListener, MouseMotionListener {
  210. /** The viewport to be scrolled */
  211. private final JViewport viewport;
  212. /** Starting position of a mouse drag - X co-ordinate */
  213. private int startPosX = 0;
  214. /** Starting position of a mouse drag - Y co-ordinate */
  215. private int startPosY = 0;
  216. ViewportScroller(JViewport vp) {
  217. viewport = vp;
  218. }
  219. // ***** MouseMotionListener *****
  220. public synchronized void mouseDragged(MouseEvent e) {
  221. if (viewport == null) {
  222. return;
  223. }
  224. int x = e.getX();
  225. int y = e.getY();
  226. int xmove = x - startPosX;
  227. int ymove = y - startPosY;
  228. int viewWidth = viewport.getExtentSize().width;
  229. int viewHeight = viewport.getExtentSize().height;
  230. int imageWidth = viewport.getViewSize().width;
  231. int imageHeight = viewport.getViewSize().height;
  232. Point viewPoint = viewport.getViewPosition();
  233. int viewX = Math.max(0, Math.min(imageWidth - viewWidth, viewPoint.x - xmove));
  234. int viewY = Math.max(0, Math.min(imageHeight - viewHeight, viewPoint.y - ymove));
  235. viewport.setViewPosition(new Point(viewX, viewY));
  236. startPosX = x;
  237. startPosY = y;
  238. }
  239. public void mouseMoved(MouseEvent e) { }
  240. // ***** MouseListener *****
  241. public void mousePressed(MouseEvent e) {
  242. startPosX = e.getX();
  243. startPosY = e.getY();
  244. }
  245. public void mouseExited(MouseEvent e) { }
  246. public void mouseEntered(MouseEvent e) { }
  247. public void mouseClicked(MouseEvent e) { }
  248. public void mouseReleased(MouseEvent e) { }
  249. }
  250. /**
  251. * This class is used to reload document in a thread safe way.
  252. */
  253. private class Reloader extends Thread {
  254. public void run() {
  255. if (!renderer.isRenderingDone()) {
  256. // do not allow the reloading while FOP is still rendering
  257. JOptionPane.showMessageDialog(previewArea,
  258. "Cannot perform the requested operation until "
  259. + "all page are rendered. Please wait",
  260. "Please wait ", 1 /* INFORMATION_MESSAGE */);
  261. return;
  262. }
  263. pagePanels = null;
  264. int savedCurrentPage = currentPage;
  265. currentPage = 0;
  266. gridPanel.removeAll();
  267. switch(displayMode) {
  268. case CONT_FACING:
  269. // This page intentionally left blank
  270. // Makes 0th/1st page on rhs
  271. gridPanel.add(new JLabel(""));
  272. case CONTINUOUS:
  273. currentPage = 0;
  274. firstPage = 0;
  275. pageRange = renderer.getNumberOfPages();
  276. break;
  277. case SINGLE:
  278. default:
  279. currentPage = 0;
  280. firstPage = 0;
  281. pageRange = 1;
  282. break;
  283. }
  284. pagePanels = new ImageProxyPanel[pageRange];
  285. for (int pg = 0; pg < pageRange; pg++) {
  286. pagePanels[pg] = new ImageProxyPanel(renderer, pg + firstPage);
  287. pagePanels[pg].setBorder(new EmptyBorder(
  288. BORDER_SPACING, BORDER_SPACING, BORDER_SPACING, BORDER_SPACING));
  289. gridPanel.add(pagePanels[pg]);
  290. }
  291. try {
  292. if (renderable != null) {
  293. renderer.clearViewportList();
  294. renderable.renderTo(foUserAgent, MimeConstants.MIME_FOP_AWT_PREVIEW);
  295. }
  296. } catch (FOPException e) {
  297. e.printStackTrace();
  298. // FIXME Should show exception in gui - was reportException(e);
  299. }
  300. setPage(savedCurrentPage);
  301. }
  302. }
  303. /**
  304. * Scales page image
  305. * @param scale [0;1]
  306. */
  307. public void setScaleFactor(double scale) {
  308. renderer.setScaleFactor(scale);
  309. reload();
  310. }
  311. /**
  312. * Returns the scale factor required in order to fit either the current
  313. * page within the current window or to fit two adjacent pages within
  314. * the display if the displaymode is continuous.
  315. * @return the requested scale factor
  316. * @throws FOPException in case of an error while fetching the PageViewport
  317. */
  318. public double getScaleToFitWindow() throws FOPException {
  319. Dimension extents = previewArea.getViewport().getExtentSize();
  320. return getScaleToFit(extents.getWidth() - 2 * BORDER_SPACING,
  321. extents.getHeight() - 2 * BORDER_SPACING);
  322. }
  323. /**
  324. * As getScaleToFitWindow, but ignoring the Y axis.
  325. * @return the requested scale factor
  326. * @throws FOPException in case of an error while fetching the PageViewport
  327. */
  328. public double getScaleToFitWidth() throws FOPException {
  329. Dimension extents = previewArea.getViewport().getExtentSize();
  330. return getScaleToFit(extents.getWidth() - 2 * BORDER_SPACING, Double.MAX_VALUE);
  331. }
  332. /**
  333. * Returns the scale factor required in order to fit either the current page or
  334. * two adjacent pages within a window of the given height and width, depending
  335. * on the display mode. In order to ignore either dimension,
  336. * just specify it as Double.MAX_VALUE.
  337. * @param viewWidth width of the view
  338. * @param viewHeight height of the view
  339. * @return the requested scale factor
  340. * @throws FOPException in case of an error while fetching the PageViewport
  341. */
  342. public double getScaleToFit(double viewWidth, double viewHeight) throws FOPException {
  343. PageViewport pageViewport = renderer.getPageViewport(currentPage);
  344. Rectangle2D pageSize = pageViewport.getViewArea();
  345. float screenResolution = Toolkit.getDefaultToolkit().getScreenResolution();
  346. float screenFactor = screenResolution / UnitConv.IN2PT;
  347. double widthScale = viewWidth / (pageSize.getWidth() / 1000f) / screenFactor;
  348. double heightScale = viewHeight / (pageSize.getHeight() / 1000f) / screenFactor;
  349. return Math.min(displayMode == CONT_FACING ? widthScale / 2 : widthScale, heightScale);
  350. }
  351. /** Starts rendering process and shows the current page. */
  352. public synchronized void showPage() {
  353. ShowPageImage viewer = new ShowPageImage();
  354. if (SwingUtilities.isEventDispatchThread()) {
  355. viewer.run();
  356. } else {
  357. SwingUtilities.invokeLater(viewer);
  358. }
  359. }
  360. /** This class is used to render the page image in a thread safe way. */
  361. private class ShowPageImage implements Runnable {
  362. /**
  363. * The run method that does the actual rendering of the viewed page
  364. */
  365. public void run() {
  366. for (int pg = firstPage; pg < firstPage + pageRange; pg++) {
  367. pagePanels[pg - firstPage].setPage(pg);
  368. }
  369. revalidate();
  370. }
  371. }
  372. }