EMMA Coverage Report (generated Sun Apr 20 22:38:01 CEST 2008)
[all classes][net.sf.jomic.comic]

COVERAGE SUMMARY FOR SOURCE FILE [ComicView.java]

nameclass, %method, %block, %line, %
ComicView.java100% (2/2)85%  (57/67)79%  (1786/2247)85%  (402.8/473)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ComicView100% (1/1)85%  (55/65)79%  (1776/2237)85%  (399.8/470)
componentHidden (ComponentEvent): void 0%   (0/1)0%   (0/1)0%   (0/1)
componentShown (ComponentEvent): void 0%   (0/1)0%   (0/1)0%   (0/1)
getGenericComicImageName (int): String 0%   (0/1)0%   (0/90)0%   (0/13)
goFirst (): void 0%   (0/1)0%   (0/4)0%   (0/2)
goNextFew (): void 0%   (0/1)0%   (0/6)0%   (0/2)
goPreviousFew (): void 0%   (0/1)0%   (0/6)0%   (0/2)
scroll (JScrollBar, int): void 0%   (0/1)0%   (0/19)0%   (0/3)
scrollHorizontally (int): void 0%   (0/1)0%   (0/6)0%   (0/2)
scrollVertically (int): void 0%   (0/1)0%   (0/6)0%   (0/2)
setRotateOnlySinglePortraitImages (boolean): void 0%   (0/1)0%   (0/5)0%   (0/2)
assertImageIndexIsValid (int): void 100% (1/1)28%  (11/40)50%  (1.5/3)
setPage (int): void 100% (1/1)43%  (16/37)69%  (2.8/4)
scrollHome (): void 100% (1/1)47%  (57/121)47%  (12.7/27)
getImageIndexForPage (int): int 100% (1/1)56%  (39/70)84%  (8.4/10)
addSheet (int, int): ComicSheet 100% (1/1)75%  (48/64)86%  (6.9/8)
setModel (ComicModel, ProgressFrame, int): void 100% (1/1)79%  (171/217)88%  (31.8/36)
<static initializer> 100% (1/1)80%  (12/15)80%  (0.8/1)
getRenderedImage (int, int): RenderedImage 100% (1/1)81%  (97/120)85%  (23.9/28)
goPrevious (int): void 100% (1/1)82%  (37/45)90%  (8.1/9)
propertyChange (PropertyChangeEvent): void 100% (1/1)83%  (70/84)82%  (13.1/16)
goNext (int): void 100% (1/1)85%  (44/52)90%  (8.1/9)
precacheComicImage (int): void 100% (1/1)85%  (44/52)90%  (8.1/9)
getExpectedViewSize (int, int): Dimension 100% (1/1)87%  (126/145)92%  (37/40)
createImagePanel (): JPanel 100% (1/1)87%  (135/155)93%  (30.8/33)
ComicView (): void 100% (1/1)97%  (158/163)100% (31/31)
getPageText (): String 100% (1/1)98%  (105/107)95%  (19/20)
componentMoved (ComponentEvent): void 100% (1/1)100% (1/1)100% (1/1)
componentResized (ComponentEvent): void 100% (1/1)100% (3/3)100% (2/2)
createColorBox (): ColorBox 100% (1/1)100% (16/16)100% (3/3)
dispose (): void 100% (1/1)100% (11/11)100% (4/4)
getComicImage (): ComicImage 100% (1/1)100% (6/6)100% (1/1)
getComicImage (int): ComicImage 100% (1/1)100% (5/5)100% (1/1)
getCurrentSheetIndex (): int 100% (1/1)100% (5/5)100% (1/1)
getImageCount (): int 100% (1/1)100% (3/3)100% (1/1)
getImageIndex (): int 100% (1/1)100% (7/7)100% (2/2)
getIntFor (boolean): int 100% (1/1)100% (9/9)100% (4/4)
getPage (): int 100% (1/1)100% (5/5)100% (1/1)
getPageCount (): int 100% (1/1)100% (3/3)100% (1/1)
getPageForImageIndex (int): int 100% (1/1)100% (12/12)100% (4/4)
getRenderedImage (): RenderedImage 100% (1/1)100% (49/49)100% (14/14)
getRotation (): int 100% (1/1)100% (4/4)100% (1/1)
getScaleMode (): String 100% (1/1)100% (3/3)100% (1/1)
getSheet (int): ComicSheet 100% (1/1)100% (6/6)100% (1/1)
getSheetForImage (int): ComicSheet 100% (1/1)100% (10/10)100% (2/2)
getSheetForPage (int): ComicSheet 100% (1/1)100% (10/10)100% (2/2)
getSheetIndexForImage (int): int 100% (1/1)100% (18/18)100% (4/4)
getSheetIndexForPage (int): int 100% (1/1)100% (54/54)100% (14/14)
goLast (): void 100% (1/1)100% (7/7)100% (2/2)
goNext (): void 100% (1/1)100% (4/4)100% (2/2)
goPrevious (): void 100% (1/1)100% (4/4)100% (2/2)
isFirst (): boolean 100% (1/1)100% (22/22)100% (4/4)
isLast (): boolean 100% (1/1)100% (31/31)100% (4/4)
isMangaMode (): boolean 100% (1/1)100% (4/4)100% (1/1)
isTwoPageMode (): boolean 100% (1/1)100% (4/4)100% (1/1)
rotateLeft (): void 100% (1/1)100% (9/9)100% (2/2)
rotateRight (): void 100% (1/1)100% (9/9)100% (2/2)
setDefaultCursor (): void 100% (1/1)100% (4/4)100% (2/2)
setImageIndex (int): void 100% (1/1)100% (133/133)100% (27/27)
setModel (ComicModel, ProgressFrame): void 100% (1/1)100% (6/6)100% (2/2)
setScaleMode (String): void 100% (1/1)100% (60/60)100% (17/17)
setSwapLeftAndRightImage (boolean): void 100% (1/1)100% (5/5)100% (2/2)
setTwoPageMode (boolean): void 100% (1/1)100% (5/5)100% (2/2)
setWaitCursor (): void 100% (1/1)100% (5/5)100% (2/2)
updateColorBoxes (): void 100% (1/1)100% (26/26)100% (7/7)
updateDisplay (): void 100% (1/1)100% (28/28)100% (10/10)
     
class ComicView$1100% (1/1)100% (2/2)100% (10/10)100% (3/3)
ComicView$1 (ComicView): void 100% (1/1)100% (6/6)100% (1/1)
run (): void 100% (1/1)100% (4/4)100% (2/2)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2008 Thomas Aglassinger
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16package net.sf.jomic.comic;
17 
18import java.awt.Color;
19import java.awt.Cursor;
20import java.awt.Dimension;
21import java.awt.Graphics2D;
22import java.awt.GridBagConstraints;
23import java.awt.GridBagLayout;
24import java.awt.Point;
25import java.awt.event.ComponentEvent;
26import java.awt.event.ComponentListener;
27import java.awt.image.BufferedImage;
28import java.awt.image.RenderedImage;
29import java.beans.PropertyChangeEvent;
30import java.beans.PropertyChangeListener;
31import java.io.File;
32import java.text.DecimalFormat;
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.Collections;
36import java.util.Iterator;
37import java.util.List;
38 
39import javax.swing.BorderFactory;
40import javax.swing.JPanel;
41import javax.swing.JScrollBar;
42import javax.swing.JScrollPane;
43import javax.swing.JViewport;
44import javax.swing.SwingUtilities;
45 
46import net.sf.jomic.common.ComicSheetRenderSettings;
47import net.sf.jomic.common.PropertyConstants;
48import net.sf.jomic.common.Settings;
49import net.sf.jomic.tools.ColorBox;
50import net.sf.jomic.tools.ErrorTools;
51import net.sf.jomic.tools.FileTools;
52import net.sf.jomic.tools.ImageRenderSettings;
53import net.sf.jomic.tools.ImageTools;
54import net.sf.jomic.tools.LocaleTools;
55import net.sf.jomic.tools.ProgressFrame;
56import net.sf.jomic.tools.StringTools;
57import net.sf.jomic.ui.FullScreenViewer;
58import net.sf.jomic.ui.RenderedImageView;
59import net.sf.wraplog.Logger;
60 
61/**
62 *  Panel to show one page or sheet of a comic. Depending on the structure of the images and the
63 *  value of <code>getTwoPageMode()</code>, this can show one or two images:
64 *  <ul>
65 *    <li> With <code>getTwoPageMode()</code> returning <code>false</code>, always show 1 image
66 *    <li> For the frontpage, always show 1 image
67 *    <li> With the image being wider than high, show 1 image
68 *    <li> With two consecutive images being higher than wide and <code>getTwoPageMode()</code>
69 *    returning <code>true</code>, show 2 images.
70 *  </ul>
71 *  For more details about this algorithm, refer to the <a
72 *  href="http://jomic.sourceforge.net/developer-guide.html">Developer Guide</a> .
73 *
74 * @author    Thomas Aglassinger
75 */
76public class ComicView extends JScrollPane implements ComponentListener, PropertyChangeListener
77{
78    private ColorBox bottomBox;
79    private ComicCache comicCache;
80    private ErrorTools errorTools;
81    private int imageCount;
82    private int imageIndex;
83    private RenderedImageView imagePane;
84    private ImageTools imageTools;
85    private ColorBox leftBox;
86    private LocaleTools localeTools;
87    private Logger logger;
88    private ComicModel model;
89    private Color oldFillColor;
90 
91    /**
92     *  Number of the last page.
93     */
94    private int pageCount;
95    private JPanel pane;
96    private ProgressFrame progressFrame;
97 
98    private boolean propertyChangeListenerAdded;
99 
100    /**
101     *  Properties that cause ComicView to refresh if changed.
102     */
103    private String[] refreshCausingProperties;
104    private ColorBox rightBox;
105    private String scaleMode;
106    private Settings settings;
107 
108    /**
109     *  List containing ComicSheets for two page mode.
110     */
111    private List sheetList;
112 
113    private StringTools stringTools;
114    private ColorBox topBox;
115 
116    /**
117     *  Creates a new, empty view.
118     *
119     * @see    #setModel(ComicModel, ProgressFrame)
120     */
121    public ComicView() {
122        logger = Logger.getLogger(ComicView.class);
123        settings = Settings.instance();
124        comicCache = ComicCache.instance();
125        errorTools = ErrorTools.instance();
126        imageTools = ImageTools.instance();
127        localeTools = LocaleTools.instance();
128        stringTools = StringTools.instance();
129 
130        imagePane = new RenderedImageView();
131        pane = new JPanel();
132        topBox = createColorBox();
133        bottomBox = createColorBox();
134        leftBox = createColorBox();
135        rightBox = createColorBox();
136 
137        // Specify which property changes will cause a refresh
138        refreshCausingProperties = new String[]{
139                PropertyConstants.BLUR_MODE,
140                PropertyConstants.BLUR_RADIUS,
141                PropertyConstants.BLUR_THRESHOLD,
142                ImageRenderSettings.FILL_COLOR,
143                ImageRenderSettings.ROTATION,
144                ImageRenderSettings.SCALE_MODE,
145                ComicSheetRenderSettings.SWAP_LEFT_AND_RIGHT_IMAGE,
146                PropertyConstants.SHOW_TOOLBAR,
147                ComicSheetRenderSettings.SHOW_TWO_PAGES,
148                PropertyConstants.USE_BLUR
149                };
150        Arrays.sort(refreshCausingProperties);
151 
152        // Use GridBagLayout to center the image
153        GridBagConstraints constraints = new GridBagConstraints();
154 
155        pane.setLayout(new GridBagLayout());
156        constraints.gridx = 0;
157        constraints.gridy = 0;
158        constraints.anchor = GridBagConstraints.PAGE_START;
159        pane.add(imagePane, constraints);
160        setViewportView(pane);
161        setViewportView(createImagePanel());
162        scaleMode = ImageTools.SCALE_ACTUAL;
163        getVerticalScrollBar().setUnitIncrement(settings.getScrollCount());
164        getHorizontalScrollBar().setUnitIncrement(settings.getScrollCount());
165        settings.addPropertyChangeListener(this);
166        propertyChangeListenerAdded = true;
167        addComponentListener(this);
168    }
169 
170    public void setImageIndex(int newImageIndex) {
171        assertImageIndexIsValid(newImageIndex);
172        if (logger.isDebugEnabled()) {
173            if (isTwoPageMode()) {
174                int sheetIndex = getSheetIndexForImage(newImageIndex);
175                ComicSheet sheet = getSheet(sheetIndex);
176                String sheetText = "(left=" + sheet.getLeftImageIndex();
177 
178                if (sheet.hasRightImage()) {
179                    sheetText += ",right=" + sheet.getRightImageIndex();
180                }
181                sheetText += ")";
182                logger.debug("set sheet to " + sheetIndex + ", " + sheetText);
183            } else {
184                logger.debug("set imageIndex to " + newImageIndex);
185            }
186        }
187        imageIndex = newImageIndex;
188        settings.setMostRecentPage(getPage());
189        updateDisplay();
190 
191        // Precache next sheet or image
192        if (isTwoPageMode()) {
193            int nextSheetIndex = getSheetIndexForImage(imageIndex) + 1;
194 
195            if (nextSheetIndex < sheetList.size()) {
196                ComicSheet nextSheet = getSheet(nextSheetIndex);
197 
198                precacheComicImage(nextSheet.getLeftImageIndex());
199                if (nextSheet.hasRightImage()) {
200                    precacheComicImage(nextSheet.getRightImageIndex());
201                }
202            }
203        } else {
204            int nextImageIndex = imageIndex + 1;
205 
206            if (nextImageIndex < getImageCount()) {
207                precacheComicImage(nextImageIndex);
208            }
209        }
210    }
211 
212    /**
213     *  Set <code>newModel</code> to be the new model from which the images should be fetched, and
214     *  show front page.
215     */
216    public void setModel(ComicModel newModel, ProgressFrame newProgressFrame) {
217        setModel(newModel, newProgressFrame, 0);
218    }
219 
220    /**
221     *  Set <code>newModel</code> to be the new model from which the images should be fetched, and
222     *  show page number <code>newPage</code>. If there a no enough pages to go to <code>newPage</code>
223     *  , log a warning an go to front page.
224     */
225    public void setModel(ComicModel newModel, ProgressFrame newProgressFrame, int newPage) {
226        assert newModel != null;
227        assert newPage >= 0 : "newPage=" + newPage;
228 
229        ComicSheet lastSheet;
230 
231        // Page number the next sheet starts at.
232        pageCount = 0;
233 
234        model = newModel;
235        imageCount = model.getImageCount();
236        progressFrame = newProgressFrame;
237 
238        if (progressFrame != null) {
239            progressFrame.setNote(localeTools.getMessage("panels.comic.progress.layingOutPages"));
240        }
241 
242        sheetList = new ArrayList(imageCount);
243        lastSheet = null;
244 
245        // Front page always uses own sheet.
246        if (model.getComicImage(0).isDoublePage()) {
247            addSheet(2, 0);
248        } else {
249            addSheet(1, 0);
250        }
251 
252        for (int i = 1; i < model.getImageCount(); i += 1) {
253            ComicImage image = model.getComicImage(i);
254            boolean isDoublePage = image.isDoublePage();
255 
256            if (lastSheet == null) {
257                if (isDoublePage) {
258                    addSheet(2, i);
259                } else {
260                    lastSheet = addSheet(1, i);
261                }
262            } else {
263                if (isDoublePage) {
264                    addSheet(2, i);
265                } else {
266                    lastSheet.setRightImageIndex(i);
267                    pageCount += 1;
268                    if (logger.isDebugEnabled()) {
269                        logger.debug("pageCount <- " + pageCount);
270                    }
271                }
272                lastSheet = null;
273            }
274            if (progressFrame != null) {
275                progressFrame.setProgress(model.computeProgress(ComicModel.TASK_LAYOUT, i));
276            }
277        }
278        if (logger.isInfoEnabled()) {
279            logger.info("comic has " + pageCount + " pages, " + model.getImageCount()
280                    + " images, and " + sheetList.size() + " sheets");
281        }
282 
283        if (newPage < pageCount) {
284            setPage(newPage);
285        } else {
286            logger.warn("cannot go to page " + (newPage + 1) + " because comic has only "
287                    + (pageCount + 1) + " pages; reverting to front page");
288            goFirst();
289        }
290    }
291 
292    public void setPage(int newPage) {
293        assert newPage >= 0;
294        assert newPage < pageCount : "newPage=" + newPage + ", pageCount=" + pageCount;
295        setImageIndex(getImageIndexForPage(newPage));
296    }
297 
298    public void setRotateOnlySinglePortraitImages(boolean newRotate) {
299        settings.setRotateOnlySinglePortraitImages(newRotate);
300    }
301 
302    public void setScaleMode(String newScaleMode) {
303        imageTools.assertIsValidScaleMode(newScaleMode);
304        scaleMode = newScaleMode;
305        if (scaleMode.equals(ImageTools.SCALE_ACTUAL)) {
306            setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
307            setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
308        } else if (scaleMode.equals(ImageTools.SCALE_FIT)) {
309            setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
310            setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER);
311        } else if (scaleMode.equals(ImageTools.SCALE_HEIGHT)) {
312            setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
313            setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER);
314        } else if (scaleMode.equals(ImageTools.SCALE_WIDTH)) {
315            setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
316            setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
317        }
318        if (model != null) {
319            updateDisplay();
320        }
321    }
322 
323    public void setSwapLeftAndRightImage(boolean newMangaMode) {
324        settings.setSwapLeftAndRightImage(newMangaMode);
325    }
326 
327    /**
328     *  Set two page mode.
329     *
330     * @param  newMode  <code>false</code> means that only a single image should be show, <code>true</code>
331     *      means that two "lean" images should be used to show two pages at once.
332     */
333    public void setTwoPageMode(boolean newMode) {
334        settings.setTwoPageMode(newMode);
335    }
336 
337    private void setDefaultCursor() {
338        setCursor(Cursor.getDefaultCursor());
339    }
340 
341    private void setWaitCursor() {
342        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
343    }
344 
345    public ComicImage getComicImage() {
346        return model.getComicImage(getImageIndex());
347    }
348 
349    public ComicImage getComicImage(int somePage) {
350        return model.getComicImage(somePage);
351    }
352 
353    /**
354     *  Get a generic name for the image at <code>index</code>, for example 17.jpg or 043+044.png.
355     */
356    public String getGenericComicImageName(int index) {
357        String result;
358        ComicImage image = model.getComicImage(index);
359        ComicSheet sheet = getSheetForImage(index);
360        int page = sheet.getPage() + 1;
361        DecimalFormat format = stringTools.getLeadingZeroFormat(getPageCount() + 2);
362 
363        if (image.isDoublePage()) {
364            result = format.format(page) + "+" + format.format(page + 1);
365        } else {
366            if (sheet.getLeftImageIndex() == index) {
367                result = format.format(page);
368            } else {
369                assert sheet.getRightImageIndex() == index;
370                result = format.format(page + 1);
371            }
372        }
373 
374        String suffix = FileTools.instance().getSuffix(image.getName());
375 
376        result += "." + suffix;
377        return result;
378    }
379 
380    /**
381     *  The number of images.
382     */
383    public int getImageCount() {
384        return imageCount;
385    }
386 
387    /**
388     *  Get the index of the image currently viewed.
389     */
390    // TODO: Clarify the result if the current sheet contains 2 images.
391    public int getImageIndex() {
392        assertImageIndexIsValid(imageIndex);
393        // TODO: Consider manga mode for export.
394        return imageIndex;
395    }
396 
397    /**
398     *  Get the number of the current page; if showing two pages, get the number of the left page.
399     */
400    public int getPage() {
401        return getPageForImageIndex(getImageIndex());
402    }
403 
404    /**
405     *  Get the number of pages (as perceived by the end user). This can differ from the the number
406     *  of images. For example, a double image counts as 2 pages.
407     */
408    public int getPageCount() {
409        return pageCount;
410    }
411 
412    /**
413     *  Get a human readable description of the page(s) currently viewed.
414     */
415    public String getPageText() {
416        String result;
417 
418        if (model != null) {
419            ComicSheet sheet = getSheet(getCurrentSheetIndex());
420            boolean leftIsDoubleImage = getComicImage(sheet.getLeftImageIndex()).isDoublePage();
421            int leftPage = sheet.getPage() + 1;
422            int leftNumber = leftPage;
423            int rightNumber = leftPage;
424            boolean showTwoPageNumbers;
425 
426            if (leftIsDoubleImage || (isTwoPageMode() && sheet.hasRightImage())) {
427                // We are really showing two pages right  now.
428                if (isMangaMode()) {
429                    leftNumber += 1;
430                } else {
431                    rightNumber += 1;
432                }
433                showTwoPageNumbers = true;
434            } else {
435                if (sheet.getLeftImageIndex() != getImageIndex()) {
436                    // The current page is the right one of the current sheet.
437                    leftNumber += 1;
438                }
439                showTwoPageNumbers = false;
440            }
441 
442            if (showTwoPageNumbers) {
443                result = localeTools.getMessage("panels.comic.pageText.double",
444                        new Object[]{new Integer(leftNumber), new Integer(rightNumber), new Integer(pageCount)});
445            } else {
446                result = localeTools.getMessage("panels.comic.pageText.single",
447                        new Object[]{new Integer(leftNumber), new Integer(pageCount)});
448            }
449        } else {
450            result = "";
451        }
452        return result;
453    }
454 
455    /**
456     *  Render the image to fit in the current JViewport while applying Settings.
457     *
458     * @see    JViewport
459     * @see    Settings
460     */
461    public RenderedImage getRenderedImage() {
462        RenderedImage result = null;
463        JViewport port = getViewport();
464        int viewWidth = port.getWidth();
465        int viewHeight = port.getHeight();
466 
467        if ((viewWidth > 2) && (viewHeight > 2)) {
468            Dimension expectedViewSize = getExpectedViewSize(viewWidth, viewHeight);
469            int expectedViewWidth = expectedViewSize.width;
470            int expectedViewHeight = expectedViewSize.height;
471 
472            if ((viewWidth != expectedViewWidth) || (viewHeight != expectedViewHeight)) {
473                // FIXME: move side-effect causing getViewport().setSize() and related code to updateDisplay().
474                getViewport().setSize(expectedViewSize);
475                viewWidth = expectedViewWidth;
476                viewHeight = expectedViewHeight;
477            }
478            result = getRenderedImage(viewWidth, viewHeight);
479        }
480        return result;
481    }
482 
483    /**
484     *  Get the current rotation.
485     *
486     * @return    one of ImageTools.ROTATE_*
487     * @see       ImageTools#ROTATE_NONE
488     * @see       ImageTools#ROTATE_CLOCKWISE
489     * @see       ImageTools#ROTATE_COUNTERCLOCKWISE
490     * @see       ImageTools#ROTATE_UPSIDE_DOWN
491     */
492    public int getRotation() {
493        return settings.getRotation();
494    }
495 
496    public String getScaleMode() {
497        return scaleMode;
498    }
499 
500    public ComicSheet getSheetForPage(int somePage) {
501        int index = getSheetIndexForPage(somePage);
502 
503        return (ComicSheet) sheetList.get(index);
504    }
505 
506    /**
507     *  Is the current page or sheet the first one?
508     */
509    public boolean isFirst() {
510        boolean result;
511 
512        if (isTwoPageMode()) {
513            result = getSheetIndexForImage(imageIndex) == 0;
514        } else {
515            result = imageIndex == 0;
516        }
517        return result;
518    }
519 
520    /**
521     *  Is the current page or sheet the last one?
522     */
523    public boolean isLast() {
524        boolean result;
525 
526        if (isTwoPageMode()) {
527            result = getSheetIndexForImage(imageIndex) == sheetList.size() - 1;
528        } else {
529            result = imageIndex == imageCount - 1;
530        }
531        return result;
532    }
533 
534    public boolean isMangaMode() {
535        return settings.getSwapLeftAndRightImage();
536    }
537 
538    public boolean isTwoPageMode() {
539        return settings.getTwoPageMode();
540    }
541 
542    int getCurrentSheetIndex() {
543        return getSheetIndexForImage(getImageIndex());
544    }
545 
546    ComicSheet getSheet(int sheetIndex) {
547        return (ComicSheet) sheetList.get(sheetIndex);
548    }
549 
550    private Dimension getExpectedViewSize(int viewWidth, int viewHeight) {
551        Dimension result;
552        int sheetWidth;
553        int sheetHeight;
554 
555        if (isTwoPageMode()) {
556            ComicSheet comicSheet = getSheetForPage(getPage());
557            int leftImageIndex = comicSheet.getLeftImageIndex();
558            ComicImage leftImage = getComicImage(leftImageIndex);
559            int leftImageWidth = leftImage.getWidth();
560            int leftImageHeight = leftImage.getHeight();
561            int rightImageWidth;
562            int rightImageHeight;
563 
564            if (comicSheet.hasRightImage()) {
565                int rightImageIndex = comicSheet.getRightImageIndex();
566                ComicImage rightImage = getComicImage(rightImageIndex);
567 
568                rightImageWidth = rightImage.getWidth();
569                rightImageHeight = rightImage.getHeight();
570            } else {
571                rightImageWidth = 0;
572                rightImageHeight = 0;
573            }
574 
575            sheetWidth = leftImageWidth + rightImageWidth;
576            sheetHeight = Math.max(leftImageHeight, rightImageHeight);
577        } else {
578            sheetWidth = getComicImage().getWidth();
579            sheetHeight = getComicImage().getHeight();
580        }
581 
582        if ((getRotation() == ImageTools.ROTATE_CLOCKWISE) || getRotation() == ImageTools.ROTATE_COUNTERCLOCKWISE) {
583            int swapSize;
584 
585            swapSize = sheetWidth;
586            sheetWidth = sheetHeight;
587            sheetHeight = swapSize;
588        }
589 
590        int areaWidth;
591        int areaHeight;
592 
593        if (getScaleMode().equals(ImageTools.SCALE_ACTUAL)) {
594            areaWidth = sheetWidth;
595            areaHeight = sheetHeight;
596        } else if (getScaleMode().equals(ImageTools.SCALE_FIT)) {
597            areaWidth = viewWidth;
598            areaHeight = viewHeight;
599        } else if (getScaleMode().equals(ImageTools.SCALE_HEIGHT)) {
600            areaWidth = Integer.MAX_VALUE;
601            areaHeight = viewHeight;
602        } else if (getScaleMode().equals(ImageTools.SCALE_WIDTH)) {
603            areaWidth = viewWidth;
604            areaHeight = Integer.MAX_VALUE;
605        } else {
606            areaWidth = 0;
607            areaHeight = 0;
608            assert false : "scaleMode=" + getScaleMode();
609        }
610        result = imageTools.getSqueezedDimension(areaWidth, areaHeight, sheetWidth, sheetHeight, settings.getScaleMode());
611        return result;
612    }
613 
614    private int getImageIndexForPage(int newPage) {
615        assert newPage >= 0;
616        assert newPage < pageCount;
617        ComicSheet sheet = getSheetForPage(newPage);
618        int imageIndexForPage = sheet.getLeftImageIndex();
619 
620        if (sheet.getPage() != newPage) {
621            boolean leftIsDoubleImage = getComicImage(sheet.getLeftImageIndex()).isDoublePage();
622 
623            assert sheet.hasRightImage() || leftIsDoubleImage
624                    : "newPage=" + newPage + ", sheet.page=" + sheet.getPage()
625                    + ", leftIsDoubleImage=" + leftIsDoubleImage;
626            if (!leftIsDoubleImage) {
627                imageIndexForPage = sheet.getRightImageIndex();
628            }
629        }
630        return imageIndexForPage;
631    }
632 
633    private int getIntFor(boolean some) {
634        // TODO: Isn't there any shorter way to convert a boolean to an int (not counting the "?" operator)
635        int result;
636 
637        if (some) {
638            result = 1;
639        } else {
640            result = 0;
641        }
642        return result;
643    }
644 
645    private int getPageForImageIndex(int index) {
646        assertImageIndexIsValid(index);
647 
648        ComicSheet sheet = getSheetForImage(index);
649        int result = sheet.getPage();
650 
651        return result;
652    }
653 
654    private RenderedImage getRenderedImage(int viewWidth, int viewHeight) {
655        BufferedImage result = null;
656 
657        if (Boolean.getBoolean(PropertyConstants.SYSTEM_PROPERTY_PREFIX + PropertyConstants.TEST_BEEP_ON_REPAINT)) {
658            // TODO: jomicTools.beep();
659        }
660        if (isVisible()) {
661            ComicSheet comicSheet = getSheetForPage(getPage());
662            int leftImageIndex = comicSheet.getLeftImageIndex();
663            RenderedImage leftImage = getComicImage(leftImageIndex).getImage();
664            RenderedImage rightImage = null;
665 
666            result = new BufferedImage(viewWidth, viewHeight, BufferedImage.TYPE_INT_ARGB);
667 
668            Graphics2D g2d = (Graphics2D) result.getGraphics();
669 
670            try {
671                if (comicSheet.hasRightImage()) {
672                    int rightImageIndex = comicSheet.getRightImageIndex();
673 
674                    rightImage = getComicImage(rightImageIndex).getImage();
675                }
676 
677                if (settings.useBlur()) {
678                    String blurMode = settings.getBlurMode();
679 
680                    leftImage = imageTools.getBluredImage(leftImage, blurMode);
681                    if (rightImage != null) {
682                        rightImage = imageTools.getBluredImage(rightImage, blurMode);
683                    }
684                }
685 
686                ComicSheetLayout renderer = new ComicSheetLayout();
687                ComicSheetRenderSettings renderSettings = new ComicSheetRenderSettings();
688 
689                renderSettings.setRotation(getRotation());
690                renderSettings.setRotateOnlySinglePortraitImages(settings.getRotateOnlySinglePortraitImages());
691                renderSettings.setScaleMode(settings.getScaleMode());
692                renderSettings.setSwapLeftAndRightImage(settings.getSwapLeftAndRightImage());
693                renderSettings.setTwoPageMode(settings.getTwoPageMode());
694 
695                renderer.renderTo(g2d, viewWidth, viewHeight, getImageIndex(), comicSheet,
696                        leftImage, rightImage, renderSettings);
697            } finally {
698                g2d.dispose();
699            }
700        }
701        return result;
702    }
703 
704    private ComicSheet getSheetForImage(int someImageIndex) {
705        int index = getSheetIndexForImage(someImageIndex);
706 
707        return (ComicSheet) sheetList.get(index);
708    }
709 
710    /**
711     *  Get the sheet on which the image <code>someImageIndex</code> is placed.
712     */
713    private int getSheetIndexForImage(int someImageIndex) {
714        int result = 0;
715        ComicSheet targetSheet = new ComicSheet(Integer.MAX_VALUE, someImageIndex);
716 
717        result = Collections.binarySearch(sheetList, targetSheet, new ComicSheetindexOverlapComparator());
718 
719        return result;
720    }
721 
722    /**
723     *  Get the sheet on which the page <code>somePage</code> is placed.
724     */
725    private int getSheetIndexForPage(int somePage) {
726        int result = 0;
727        boolean found = false;
728        Iterator rider = sheetList.iterator();
729 
730        // TODO: use binary search
731        while (rider.hasNext() && !found) {
732            ComicSheet sheet = (ComicSheet) rider.next();
733 
734            int sheetPage = sheet.getPage();
735            int leftImageIndex = sheet.getLeftImageIndex();
736            ComicImage leftComicImage = getComicImage(leftImageIndex);
737            boolean hasTwoPages = sheet.hasRightImage() || leftComicImage.isDoublePage();
738 
739            if ((sheetPage == somePage) || (hasTwoPages && (sheetPage + 1 == somePage))) {
740                found = true;
741            } else {
742                result += 1;
743            }
744        }
745        return result;
746    }
747 
748    public void componentHidden(ComponentEvent event) {
749        // Do nothing.
750    }
751 
752    public void componentMoved(ComponentEvent event) {
753        // Do nothing.
754    }
755 
756    public void componentResized(ComponentEvent event) {
757        updateDisplay();
758    }
759 
760    public void componentShown(ComponentEvent event) {
761        // Do nothing.
762    }
763 
764    public void dispose() {
765        if (propertyChangeListenerAdded) {
766            settings.removePropertyChangeListener(this);
767        }
768        removeComponentListener(this);
769    }
770 
771    /**
772     *  Go to first page.
773     */
774    public void goFirst() {
775        setImageIndex(0);
776    }
777 
778    /**
779     *  Go to last page.
780     */
781    public void goLast() {
782        setImageIndex(imageCount - 1);
783    }
784 
785    /**
786     *  Go to next page or sheet.
787     */
788    public void goNext() {
789        goNext(1);
790    }
791 
792    /**
793     *  Go a few pages/sheet forward.
794     *
795     * @see    Settings#getFew()
796     */
797    public void goNextFew() {
798        goNext(settings.getFew());
799    }
800 
801    /**
802     *  Go <code>count</code> pages or sheets backwards.
803     */
804    public void goPrevious(int count) {
805        assert !isFirst();
806        assert count > 0;
807        if (isTwoPageMode()) {
808            int previousSheetIndex = getCurrentSheetIndex() - count;
809            ComicSheet nextSheet = getSheet(Math.max(previousSheetIndex, 0));
810 
811            setImageIndex(nextSheet.getLeftImageIndex());
812        } else {
813            setImageIndex(Math.max(getImageIndex() - count, 0));
814        }
815    }
816 
817    /**
818     *  Go to previous page or sheet.
819     */
820    public void goPrevious() {
821        goPrevious(1);
822    }
823 
824 
825    /**
826     *  Go a few pages/sheets backwards.
827     *
828     * @see    Settings#getFew()
829     */
830    public void goPreviousFew() {
831        goPrevious(settings.getFew());
832    }
833 
834    public void propertyChange(PropertyChangeEvent event) {
835        try {
836            assert event != null;
837            String propertyName = event.getPropertyName();
838 
839            assert propertyName != null;
840            String oldValue = (String) event.getOldValue();
841            String value = (String) event.getNewValue();
842            boolean refreshComicView = stringTools.equalsAnyOf(refreshCausingProperties, propertyName);
843 
844            // if necessary, refresh the comic view
845            if (refreshComicView) {
846                Runnable updateRunner =
847                    new Runnable()
848                    {
849                        public void run() {
850                            updateDisplay();
851                        }
852                    };
853 
854                SwingUtilities.invokeLater(updateRunner);
855            } else {
856                if (logger.isDebugEnabled()) {
857                    logger.debug("ignoring change of property " + stringTools.sourced(propertyName)
858                            + " from " + stringTools.sourced(oldValue)
859                            + " to " + stringTools.sourced(value));
860                }
861            }
862        } catch (Throwable error) {
863            errorTools.showError(event, error);
864        }
865    }
866 
867    public void rotateLeft() {
868        settings.setRotation(imageTools.getLeftRotation(getRotation()));
869    }
870 
871    public void rotateRight() {
872        settings.setRotation(imageTools.getRightRotation(getRotation()));
873    }
874 
875    /**
876     *  Scroll to start of page. Normally this means the left top corner, unless the left and right
877     *  image are swapped, in which case it means the top right corner.
878     */
879    public void scrollHome() {
880        Point home;
881        boolean useTop;
882        boolean useLeft;
883        JViewport viewPort = getViewport();
884        int viewWidth = viewPort.getWidth();
885        int viewHeight = viewPort.getHeight();
886        boolean rotatedCounterClockwise = getRotation() == ImageTools.ROTATE_COUNTERCLOCKWISE;
887        boolean rotatedClockwise = getRotation() == ImageTools.ROTATE_CLOCKWISE;
888 
889        if (rotatedClockwise) {
890            useLeft = false;
891            useTop = true;
892        } else if (rotatedCounterClockwise) {
893            useLeft = true;
894            useTop = false;
895        } else if (getRotation() == ImageTools.ROTATE_NONE) {
896            useLeft = true;
897            useTop = true;
898        } else if (getRotation() == ImageTools.ROTATE_UPSIDE_DOWN) {
899            useLeft = false;
900            useTop = false;
901        } else {
902            assert false : "rotation=" + getRotation();
903            useLeft = true;
904            useTop = true;
905        }
906 
907        if (isMangaMode()) {
908            if (rotatedClockwise || rotatedCounterClockwise) {
909                useTop = !useTop;
910            } else {
911                useLeft = !useLeft;
912            }
913        }
914 
915        home = new Point(viewWidth * getIntFor(!useLeft), viewHeight * getIntFor(!useTop));
916        viewPort.setViewPosition(home);
917    }
918 
919    public void scrollHorizontally(final int notches) {
920        scroll(getHorizontalScrollBar(), notches);
921    }
922 
923    public void scrollVertically(final int notches) {
924        scroll(getVerticalScrollBar(), notches);
925    }
926 
927    public void updateDisplay() {
928        if (!FullScreenViewer.instance().isVisible()) {
929            setWaitCursor();
930            try {
931                if (model != null) {
932                    RenderedImage renderedImage = getRenderedImage();
933 
934                    if (renderedImage != null) {
935                        imagePane.set(renderedImage);
936                    }
937                }
938                updateColorBoxes();
939            } finally {
940                setDefaultCursor();
941            }
942        }
943    }
944 
945    /**
946     *  Add a new sheet with the left image identified by <code>newLeftImageIndex</code>. After
947     *  that, increment <code>pageCount</code> by <code>pageIncrement</code>.
948     */
949    private ComicSheet addSheet(int pageIncrement, int newLeftImageIndex) {
950        assert (pageIncrement == 1) || (pageIncrement == 2) : "pageIncrement=" + pageIncrement;
951        assert newLeftImageIndex >= 0;
952        ComicSheet result = new ComicSheet(pageCount, newLeftImageIndex);
953 
954        sheetList.add(result);
955        pageCount += pageIncrement;
956        if (logger.isDebugEnabled()) {
957            logger.debug("pageCount <- " + pageCount);
958        }
959        return result;
960    }
961 
962    private void assertImageIndexIsValid(int someImageIndex) {
963        assert someImageIndex >= 0 : "someImageIndex=" + someImageIndex;
964        assert someImageIndex < imageCount : "someImageIndex=" + someImageIndex + ", imageIndexCount=" + imageCount;
965    }
966 
967    private ColorBox createColorBox() {
968        ColorBox result = new ColorBox(settings.getFillColor());
969 
970        result.setPreferredSize(new Dimension(0, 0));
971        return result;
972    }
973 
974    private JPanel createImagePanel() {
975        assert topBox != null;
976        assert bottomBox != null;
977        assert leftBox != null;
978        assert rightBox != null;
979        assert imagePane != null;
980        JPanel result = new JPanel(new GridBagLayout());
981        GridBagConstraints c = new GridBagConstraints();
982 
983        c.gridx = 0;
984        c.gridy = 0;
985        c.gridwidth = GridBagConstraints.REMAINDER;
986        c.weightx = 1.0;
987        c.weighty = 1.0;
988        c.fill = GridBagConstraints.BOTH;
989        result.add(topBox, c);
990        c.gridy += 1;
991        c.gridwidth = 1;
992        c.weighty = 0.0;
993        result.add(leftBox, c);
994        c.gridx += 1;
995        c.weightx = 0.0;
996        c.fill = GridBagConstraints.NONE;
997        result.add(imagePane, c);
998        c.gridx += 1;
999        c.weightx = 1.0;
1000        c.fill = GridBagConstraints.BOTH;
1001        result.add(rightBox, c);
1002        c.gridx = 0;
1003        c.gridy += 1;
1004        c.weighty = 1.0;
1005        c.gridwidth = GridBagConstraints.REMAINDER;
1006        result.add(bottomBox, c);
1007        result.setBorder(BorderFactory.createEmptyBorder());
1008        return result;
1009    }
1010 
1011    /**
1012     *  Go <code>count</code> pages or sheets forward.
1013     */
1014    private void goNext(int count) {
1015        assert !isLast();
1016        assert count > 0;
1017        if (isTwoPageMode()) {
1018            int nextSheetIndex = getCurrentSheetIndex() + count;
1019            ComicSheet nextSheet = getSheet(Math.min(nextSheetIndex, sheetList.size() - 1));
1020 
1021            setImageIndex(nextSheet.getLeftImageIndex());
1022        } else {
1023            setImageIndex(Math.min(getImageIndex() + count, imageCount - 1));
1024        }
1025    }
1026 
1027    private void precacheComicImage(int index) {
1028        assert index >= 0;
1029        assert index < model.getImageCount();
1030 
1031        if (logger.isDebugEnabled()) {
1032            logger.debug("precaching image " + index);
1033        }
1034 
1035        // Compute a thumbnail to make sure all pixels are "touched"
1036        // and transferred into the system's or CPU's  memory cache.
1037        ComicImage comicImage = getComicImage(index);
1038        File imageFile = comicImage.getFile();
1039 
1040        PrecachedImageInCacheListener precachedListener = new PrecachedImageInCacheListener();
1041 
1042        comicCache.getThumbnail(imageFile, precachedListener);
1043    }
1044 
1045    private void scroll(final JScrollBar bar, final int notches) {
1046        assert bar != null;
1047        bar.setValue(bar.getValue() + notches * settings.getScrollCount());
1048    }
1049 
1050    private void updateColorBoxes() {
1051        Color fillColor = settings.getFillColor();
1052 
1053        if (!fillColor.equals(oldFillColor)) {
1054            topBox.setColor(fillColor);
1055            leftBox.setColor(fillColor);
1056            rightBox.setColor(fillColor);
1057            bottomBox.setColor(fillColor);
1058        }
1059    }
1060}

[all classes][net.sf.jomic.comic]
EMMA 2.0.4217 (C) Vladimir Roubtsov