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 14KB

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