// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2007 by R. Pito Salas // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software Foundation; // either version 2 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with this program; // if not, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: JumplessViewport.java,v 1.5 2007/07/19 11:02:30 spyromus Exp $ // package com.salas.bb.utils.uif; import javax.swing.*; import java.awt.*; import java.awt.event.ComponentEvent; /** * A viewport component that saves the position relative to items of the list * in the view. */ public class JumplessViewport extends JViewport { private JumplessScrollPosition sp; /** * Creates a viewport. */ public JumplessViewport() { // Either BACKINGSTORE_SCROLL_MODE or SIMPLE_SCROLL_MODE is // suitable for flicker-less scrolling setScrollMode(BACKINGSTORE_SCROLL_MODE); } @Override protected ViewListener createViewListener() { return new CustomViewListener(); } @Override protected LayoutManager createLayoutManager() { return new CustomViewportLayout(); } @Override public void setViewPosition(Point p) { // Calculate current position in relative to objects in the list // Get the list component Point pos = getViewPosition(); Component view = getView(); if (view != null) { Component list = view.getComponentAt(pos); if (list != null) { // Get the list item under the given coordinates Component panel = list.getComponentAt(pos); if (panel != null) { // Convert coordinates into the item space Point panelPos = SwingUtilities.convertPoint(list, pos, panel); int panelHeight = panel.getHeight(); // Calculate the offset relative to the head or the tail of the item boolean head = true; int offset = panelPos.y; if (offset > panelHeight / 2) { head = false; offset = panelHeight - offset; } // Record it for the future reference setStoredPosition(panel, head, offset); } } } super.setViewPosition(p); } /** * Sets the stored position. * * @param panel panel. * @param head <code>TRUE</code> to count offset from the head. * @param offset number of pixels to offset from the top or bottom. */ public void setStoredPosition(Component panel, boolean head, int offset) { sp = new JumplessScrollPosition(panel, head, offset); } /** * Forgets the stored position. Useful when changing the content at once. */ public void resetStoredPosition() { sp = null; } /** * Resize listener that updates the viewing position so that * no jumps are visible. */ private class CustomViewListener extends ViewListener { @Override public void componentResized(ComponentEvent e) { // There's nothing here intentionally } } /** * View port layout that omits recalculation of y-coordinate of the view window. */ private class CustomViewportLayout extends ViewportLayout { private int oldHeight = 0; @Override public void layoutContainer(Container parent) { JViewport vp = (JViewport)parent; Component view = vp.getView(); Scrollable scrollableView = null; if (view == null) return; if (view instanceof Scrollable) scrollableView = (Scrollable)view; view.validate(); Dimension viewPrefSize = view.getPreferredSize(); Dimension vpSize = vp.getSize(); Dimension extentSize = vp.toViewCoordinates(vpSize); Dimension viewSize = new Dimension(viewPrefSize); if (scrollableView != null) { if (scrollableView.getScrollableTracksViewportWidth()) { viewSize.width = vpSize.width; } if (scrollableView.getScrollableTracksViewportHeight()) { viewSize.height = vpSize.height; } } Point viewPosition = vp.getViewPosition(); if (scrollableView == null || vp.getParent() == null || vp.getParent().getComponentOrientation().isLeftToRight()) { if ((viewPosition.x + extentSize.width) > viewSize.width) { viewPosition.x = Math.max(0, viewSize.width - extentSize.width); } } else { if (extentSize.width > viewSize.width) { viewPosition.x = viewSize.width - extentSize.width; } else { viewPosition.x = Math.max(0, Math.min(viewSize.width - extentSize.width, viewPosition.x)); } } // Update y-coord if (sp != null && viewPrefSize.height != oldHeight) { // Find the coordinate of the saved position in the new layout Component list = view.getComponentAt(0, 0); if (list != null) { Component panel = sp.getAnchor(); int offset = sp.isHead() ? sp.getYOffset() : panel.getHeight() - sp.getYOffset(); Point point = SwingUtilities.convertPoint(panel, 0, offset, list); // See if there's a blank space going to be visible after the last // panel, and make so that it isn't if ((point.y + extentSize.height) > viewSize.height) { point.y = Math.max(0, viewSize.height - extentSize.height); } // Change the position viewPosition.y = point.y; } } if (scrollableView == null) { if ((viewPosition.x == 0) && (vpSize.width > viewPrefSize.width)) { viewSize.width = vpSize.width; } if ((viewPosition.y == 0) && (vpSize.height > viewPrefSize.height)) { viewSize.height = vpSize.height; } } oldHeight = viewPrefSize.height; vp.setViewPosition(viewPosition); vp.setViewSize(viewSize); } } }