// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 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: CompositeFeedDisplay.java,v 1.32 2008/02/28 15:59:46 spyromus Exp $ // package com.salas.bb.views.feeds; import com.jgoodies.binding.value.ValueModel; import com.salas.bb.domain.*; import com.salas.bb.utils.uif.VertialScrollablePanel; import com.salas.bb.views.feeds.html.HTMLFeedDisplay; import com.salas.bb.views.feeds.html.IHTMLFeedDisplayConfig; import com.salas.bb.views.feeds.image.IImageFeedDisplayConfig; import com.salas.bb.views.feeds.image.ImageFeedDisplay; import com.salas.bb.views.feeds.twitter.TwitterFeedDisplay; import javax.swing.*; import java.awt.*; import java.net.URL; import java.util.logging.Logger; /** * Composite feed display analyzes feed type and shows appropriate feed display. */ public class CompositeFeedDisplay extends AbstractFeedDisplay { private final IHTMLFeedDisplayConfig htmlDisplayConfig; private final IImageFeedDisplayConfig imageDisplayConfig; private IFeedDisplayListener displayListener; private IFeedListener feedListener; private AbstractFeedDisplay currentDisplay; private IFeed currentFeed; private FeedType currentFeedType; private ScrollablePanel scrollableView; private boolean initializedDisplay; /** Page model with the number of pages. Updated by the FeedDisplayModel. */ private final ValueModel pageCountModel; /** Page model to update when the page changes. */ private final ValueModel pageModel; private int pageSize; /** * Creates composite feed display. * * @param aHtmlConfig HTML configuration. * @param aImageConfig Image configuration. * @param pageModel page model to update when the page changes. * @param pageCountModel page model to update with the number of pages. */ public CompositeFeedDisplay(IHTMLFeedDisplayConfig aHtmlConfig, IImageFeedDisplayConfig aImageConfig, ValueModel pageModel, ValueModel pageCountModel) { super(null, null, pageCountModel); this.pageCountModel = pageCountModel; this.pageModel = pageModel; initializedDisplay = false; htmlDisplayConfig = aHtmlConfig; imageDisplayConfig = aImageConfig; displayListener = new DelegatingDisplayListener(); feedListener = new FeedListener(); enableEvents(AWTEvent.KEY_EVENT_MASK); scrollableView = new ScrollablePanel(new BorderLayout()); } /** * Returns <code>TRUE</code> during firing the article(s) selection event, so that it's possible to learn if it's * the source of this event. This is particularily useful when it's necessary to skip sending the event back to the * display. * * @return <code>TRUE</code> if current event has come from this component. */ @Override public boolean isArticleSelectionSource() { return currentDisplay != null && currentDisplay.isArticleSelectionSource(); } /** * Sets the viewport which will be used for showing this component. * * @param aViewport viewport. */ @Override public void setViewport(JViewport aViewport) { super.setViewport(aViewport); scrollableView.setViewport(aViewport); } @Override protected void cycleViewMode(boolean global, boolean forward) { if (currentDisplay != null) currentDisplay.cycleViewMode(global, forward); } /** * Returns displayable feed view component. * * @return displayable feed view component. */ public JComponent getComponent() { return scrollableView; } /** Requests focus for this display. */ public void focus() { if (currentDisplay == null) super.requestFocus(); else currentDisplay.focus(); } /** * Returns currently selected text in currently selected article. * * @return text. */ public String getSelectedText() { return currentDisplay == null ? super.getSelectedText() : currentDisplay.getSelectedText(); } /** * Repaints all highlights in all visible articles. */ public void repaintHighlights() { if (currentDisplay != null) currentDisplay.repaintHighlights(); } /** * Repaints all sentiments color codes. */ public void repaintSentimentsColorCodes() { if (currentDisplay != null) currentDisplay.repaintSentimentsColorCodes(); } /** * Orders view to select and show article if it can be visible. * * @param article article to select. */ public void selectArticle(IArticle article) { if (currentDisplay != null) currentDisplay.selectArticle(article); } /** * Repaints article text if is currently in the given mode. * * @param briefMode <code>TRUE</code> for brief mode, otherwise -- full mode. */ public void repaintIfInMode(boolean briefMode) { if (currentDisplay != null) currentDisplay.repaintIfInMode(briefMode); } /** * Orders to select next article. * * @param mode mode of selection. * * @return <code>TRUE</code> if article has been selected. */ public boolean selectNextArticle(int mode) { return currentDisplay != null && currentDisplay.selectNextArticle(mode); } /** * Orders to select next article. * * @param mode mode of selection. * * @return <code>TRUE</code> if article has been selected. */ public boolean selectFirstArticle(int mode) { return currentDisplay != null && currentDisplay.selectFirstArticle(mode); } /** * Orders to select previous article. * * @param mode mode of selection. * * @return <code>TRUE</code> if article has been selected. */ public boolean selectPreviousArticle(int mode) { return currentDisplay != null && currentDisplay.selectPreviousArticle(mode); } /** * Orders to select last article. * * @param mode mode of selection. * * @return <code>TRUE</code> if article has been selected. */ public boolean selectLastArticle(int mode) { return currentDisplay != null && currentDisplay.selectLastArticle(mode); } /** * Sets the feed which is required to be displayed. * * @param feed the feed. */ public void setFeed(IFeed feed) { FeedType feedType = feed == null ? null : feed.getType(); if (currentFeed != null) { currentFeed.removeListener(feedListener); } currentFeed = feed; if (currentFeed != null) { currentFeed.addListener(feedListener); } if (feedType != currentFeedType || !initializedDisplay) { installNewDisplay(feedType); currentFeedType = feedType; initializedDisplay = true; } if (currentDisplay != null) currentDisplay.setFeed(feed); } @Override public void setPage(int page) { if (currentDisplay != null) currentDisplay.setPage(page); } @Override public void setPageSize(int size) { pageSize = size; if (currentDisplay != null) currentDisplay.setPageSize(size); } /** * Deinstalls old display and installs new one. * * @param aFeedType feed type. */ private void installNewDisplay(FeedType aFeedType) { if (currentDisplay != null) { scrollableView.unsetDisplay(); currentDisplay.prepareForDismiss(); currentDisplay.removeListener(displayListener); } currentDisplay = createNewDisplay(aFeedType); if (currentDisplay != null) { scrollableView.setDisplay(currentDisplay); Color bgColor = currentDisplay.getConfig().getDisplayBGColor(); scrollableView.setBackground(bgColor); if (viewport != null) viewport.setBackground(bgColor); currentDisplay.addListener(displayListener); } } /** * Creates display according to feed type. * * @param aFeedType feed type. * * @return display. */ private AbstractFeedDisplay createNewDisplay(FeedType aFeedType) { AbstractFeedDisplay display = null; if (aFeedType != null) { switch(aFeedType.getType()) { case FeedType.TYPE_TWITTER: display = new TwitterFeedDisplay(htmlDisplayConfig, pageModel, pageCountModel); break; case FeedType.TYPE_TEXT: display = new HTMLFeedDisplay(htmlDisplayConfig, pageModel, pageCountModel); break; case FeedType.TYPE_IMAGE: display = new ImageFeedDisplay(imageDisplayConfig, pageModel, pageCountModel); break; } } if (display != null && pageSize != 0) display.setPageSize(pageSize); return display != null ? display : new NoFeedDisplay(htmlDisplayConfig, pageCountModel); } /** Releases all links and resources and prepares itself to be garbage collected. */ public void prepareForDismiss() { } /** * Returns current logger. * * @return logger object. */ protected Logger getLogger() { return null; } /** * Creates new article display for addition to the display. * * @param aArticle article to create display for. * * @return display. */ protected IArticleDisplay createNewArticleDisplay(IArticle aArticle) { return null; } /** Delegate scrolling to the component. */ public void scrollRectToVisible(Rectangle aRect) { scrollableView.scrollTo(aRect); } /** * Invoked when selected feed type changes. */ private void onFeedTypeChange() { if (currentFeedType != currentFeed.getType()) { SwingUtilities.invokeLater(new Runnable() { public void run() { setFeed(currentFeed); } }); } } @Override protected void onConfigPropertyChange(String name) { if (currentDisplay != null) currentDisplay.onConfigPropertyChange(name); } /** * Listens to changes of selected feed type. */ private class FeedListener extends FeedAdapter { /** * Called when information in feed changed. * * @param feed feed. * @param property property of the feed. * @param oldValue old property value. * @param newValue new property value. */ public void propertyChanged(IFeed feed, String property, Object oldValue, Object newValue) { if (IFeed.PROP_TYPE.equals(property)) { onFeedTypeChange(); } else if (IFeed.PROP_ASCENDING_SORTING.equals(property)) { onConfigPropertyChange(IFeedDisplayConfig.SORT_ORDER); } } } /** * Listener refiring the envent to the others. */ private class DelegatingDisplayListener implements IFeedDisplayListener { /** * Invoked when user selects article or article is selected as result of direct invocation of * {@link com.salas.bb.views.feeds.IFeedDisplay#selectArticle(com.salas.bb.domain.IArticle)} * method. * * @param lead lead article. * @param selectedArticles all selected articles. */ public void articleSelected(IArticle lead, IArticle[] selectedArticles) { fireArticleSelected(lead, selectedArticles); } /** * Invoked when user clicks on some link at the article text or header. The expected behaviour * is openning the link in browser. * * @param link link clicked. */ public void linkClicked(URL link) { fireLinkClicked(link); } /** * Invoked when user hovers some link with mouse pointer. * * @param link link hovered or <code>NULL</code> if previously hovered link is no longer * hovered. */ public void linkHovered(URL link) { fireLinkHovered(link); } /** * Invoked when user clicks on some quick-link to the other feed. * * @param feed feed to select. */ public void feedJumpLinkClicked(IFeed feed) { fireFeedJumpLinkClicked(feed); } /** Invoked when the user made something to zoom content in. */ public void onZoomIn() { fireZoomIn(); } /** Invoked when the user made something to zoom the content out. */ public void onZoomOut() { fireZoomOut(); } } /** * Panel with custom scrollable properties. */ protected static class ScrollablePanel extends VertialScrollablePanel implements IScrollContoller { private IFeedDisplay currentDisplay; private JViewport viewport; /** * Create a new buffered JPanel with the specified layout manager * * @param layout the LayoutManager to use */ public ScrollablePanel(LayoutManager layout) { super(layout); } /** * Forwards the <code>scrollRectToVisible()</code> message to the <code>JComponent</code>'s * parent. Components that can service the request, such as <code>JViewport</code>, override * this method and perform the scrolling. * * @param aRect the visible <code>Rectangle</code> * * @see javax.swing.JViewport */ public void scrollRectToVisible(Rectangle aRect) { } /** * Makes scrolling to the given place. * * @param aRect the visible <code>Rectangle</code> * * @see javax.swing.JViewport */ public void scrollTo(Rectangle aRect) { super.scrollRectToVisible(aRect); } /** * Sets the display. * * @param cdisplay display. */ public void setDisplay(IFeedDisplay cdisplay) { if (currentDisplay != null) unsetDisplay(); currentDisplay = cdisplay; JComponent display = currentDisplay.getComponent(); display.setVisible(false); currentDisplay.setViewport(viewport); add(display, BorderLayout.NORTH); display.setVisible(true); } /** * Unsets the display. */ public void unsetDisplay() { if (currentDisplay == null) return; JComponent d = currentDisplay.getComponent(); d.setVisible(false); remove(d); currentDisplay = null; } @Override public boolean requestFocusInWindow() { boolean focusing = super.requestFocusInWindow(); if (currentDisplay != null) currentDisplay.focus(); return focusing; } /** * Sets a viewport to use for displays. * * @param viewport viewport. */ public void setViewport(JViewport viewport) { this.viewport = viewport; } } }