// 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: RenderingManager.java,v 1.8 2008/04/08 08:06:18 spyromus Exp $
//
package com.salas.bb.views.settings;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
/**
* Manages rendering settings and their changes. <p/>Manager can give to everyone interested current rendering settings
* at any given moment of time. Also if someone wants to listen for updates of current settings it can register as
* listener of all or particular properties and be updated.
*/
public class RenderingManager implements PropertyChangeListener
{
private static final RenderingManager INSTANCE = new RenderingManager();
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private FeedRenderingSettings globalSettings;
private FeedRenderingSettings localSettings;
// Biased Font cache. Simply to avoid having to instantiate lots and lots of Font objects
private static Font cacheArticleBodyFont;
private static Font cacheArticleDateFont;
private static Font cacheDividerTextFont;
private static Font cacheUnreadTitleFont;
private static Font cacheReadTitleFont;
private static Font cacheArticleTitleFont;
// Highlight Colors
private Color lastSearchKeywordHC;
private Color lastUnregBlogLinkHC;
private Color lastRegBlogLinksHC;
// Highlights Painters
private Highlighter.HighlightPainter searchKeywordsHP;
private Highlighter.HighlightPainter unregBlogLinksHP;
private Highlighter.HighlightPainter regBlogLinksHP;
/**
* Returns pre-initialized object.
*
* @return instance.
*/
public static RenderingManager getInstance()
{
return INSTANCE;
}
private RenderingManager()
{
resetFontCache();
}
/**
* Clear the saved Fonts from the private Font cache.
*/
private void resetFontCache()
{
cacheArticleBodyFont = null;
cacheDividerTextFont = null;
cacheArticleDateFont = null;
cacheReadTitleFont = null;
cacheUnreadTitleFont = null;
cacheArticleTitleFont = null;
}
// --- Events -------------------------------------------------------------
/**
* Registers settings property change listener.
*
* @param l
* listener.
*/
public static void addPropertyChangeListener(PropertyChangeListener l)
{
INSTANCE.pcs.addPropertyChangeListener(l);
}
/**
* Register settings property change listener for specific property.
*
* @param property
* property.
* @param l
* listener.
*/
public static void addPropertyChangeListener(String property, PropertyChangeListener l)
{
INSTANCE.pcs.addPropertyChangeListener(property, l);
}
/**
* Removes settings property change listener.
*
* @param l
* listener.
*/
public static void removePropertyChangeListener(PropertyChangeListener l)
{
INSTANCE.pcs.removePropertyChangeListener(l);
}
/**
* Removes setting property change listener registered for specific property.
*
* @param property
* property.
* @param l
* listener.
*/
public void removePropertyChangeListener(String property, PropertyChangeListener l)
{
pcs.removePropertyChangeListener(property, l);
}
/**
* This method is called when global or local settings are changed.
*
* @param evt
* A PropertyChangeEvent object describing the event source and the property that has changed.
*/
public void propertyChange(PropertyChangeEvent evt)
{
final Object source = evt.getSource();
final boolean equalToGlobalSettings;
// Reset the font Cache when theme or font bias changes
final String prop = evt.getPropertyName();
if (RenderingSettingsNames.THEME.equals(prop)|| RenderingSettingsNames.ARTICLE_FONT_BIAS.equals(prop))
{
resetFontCache();
}
// Dispatch the property change to listeners
synchronized (this)
{
equalToGlobalSettings = source == globalSettings;
}
if ((equalToGlobalSettings && localSettings == null) || (source == localSettings))
{
// Respond to global settings changes only when there's not local
// settings installed.
pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
}
/**
* Fires differences in properties between the settings.
*
* @param oldSettings
* old settings.
* @param newSettings
* new settings.
*/
private void fireDifference(final FeedRenderingSettings oldSettings, final FeedRenderingSettings newSettings)
{
for (int i = 0; i < RenderingSettingsNames.KEYS.length; i++)
{
final String key = RenderingSettingsNames.KEYS[i];
if (newSettings != null)
{
newSettings.fireChange(key, oldSettings == null ? null : oldSettings.get(key), newSettings.get(key));
}
}
}
// ------------------------------------------------------------------------
/**
* Registers global setting object. If we don't have local settings object yet then global settings will be
* registered as local too.
*
* @param settings
* new global settings.
*/
public static void setGlobalSettings(FeedRenderingSettings settings)
{
INSTANCE.setGlobalSettings0(settings);
}
/**
* Registers global setting object. If we don't have local settings object yet then global settings will be
* registered as local too.
*
* @param settings
* new global settings.
*/
private synchronized void setGlobalSettings0(FeedRenderingSettings settings)
{
// We don't need to deal with exact copy
if (this.globalSettings == settings) return;
// If we have already global settings defined
// then unregister from them
if (this.globalSettings != null) this.globalSettings.removeListener(this);
final FeedRenderingSettings oldSettings = this.globalSettings;
this.globalSettings = settings;
if (this.globalSettings != null) this.globalSettings.addListener(this);
// Check the existence of local settings
if (this.localSettings == null)
{
// no local settings - register global as local
this.localSettings = this.globalSettings;
} else
{
// present - register global as parent
this.localSettings.setParent(this.globalSettings);
}
fireDifference(oldSettings, settings);
}
// ------------------------------------------------------------------------
/**
* Returns TRUE if grouping of articles is enabled.
*
* @return TRUE if enabled.
*/
public static boolean isGroupingEnabled()
{
return INSTANCE.localSettings.isGroupingEnabled();
}
/**
* Returns TRUE if showing of empty groups is allowed.
*
* @return TRUE if showing is allowed.
*/
public static boolean isShowEmptyGroups()
{
return INSTANCE.localSettings.isShowEmptyGroups();
}
/**
* Returns TRUE if date of the article creation should be showed.
*
* @return TRUE if date showing is enabled.
*/
public static boolean isArticleDateShowing()
{
return INSTANCE.localSettings.isArticleDateShowing();
}
/**
* Returns TRUE if browsing in brief mode.
*
* @return TRUE for brief mode.
*/
public static boolean isBriefMode()
{
return INSTANCE.localSettings.getArticleViewMode() == FeedRenderingSettings.VIEW_MODE_BRIEF;
}
/**
* Returns view mode of articles.
*
* @return view mode.
*
* @see FeedRenderingSettings#VIEW_MODE_FULL
* @see FeedRenderingSettings#VIEW_MODE_BRIEF
* @see FeedRenderingSettings#VIEW_MODE_MINIMAL
*/
public static int getArticleViewMode()
{
return INSTANCE.localSettings.getArticleViewMode();
}
/**
* Returns font for rendering of article body. Use value in cache if something is there
*
* @return font for article body text.
*/
public static Font getArticleBodyFont()
{
if (cacheArticleBodyFont == null) {
cacheArticleBodyFont = INSTANCE.localSettings.getArticleBodyFont();
}
return cacheArticleBodyFont;
}
/**
* Returns font for rendering of article title.
*
* @return font for rendering of article title.
*/
public static Font getArticleTitleFont()
{
if (cacheArticleTitleFont == null)
{
cacheArticleTitleFont = INSTANCE.localSettings.getArticleTitleFont();
}
return cacheArticleTitleFont;
}
/**
* Returns font for rendering of article date.
*
* @return font.
*/
public static Font getArticleDateFont()
{
if (cacheArticleDateFont == null)
{
cacheArticleDateFont = INSTANCE.localSettings.getArticleDateFont();
}
return cacheArticleDateFont;
}
/**
* Returns font for rendering of groups divider title text. Use cache
*
* @return font.
*/
public static Font getDividerTextFont()
{
if (cacheDividerTextFont == null)
{
cacheDividerTextFont = INSTANCE.localSettings.getDividerTextFont();
}
return cacheDividerTextFont;
}
/**
* Returns color for selected article background.
*
* @return color of background.
*/
public static Color getSelectedArticleBackground()
{
return INSTANCE.localSettings.getSelectedArticleBackground();
}
/**
* Returns color for normal article background.
*
* @return color of background.
*/
public static Color getArticleBackground()
{
return INSTANCE.localSettings.getArticleBackground();
}
/**
* Returns color of article body background.
*
* @return color of background.
*/
public static Color getArticleBodyBackground()
{
return INSTANCE.localSettings.getArticleBodyBackground();
}
/**
* Returns color for article title background.
*
* @return color of background.
*/
public static Color getArticleTitleBackground()
{
return INSTANCE.localSettings.getArticleTitleBackground();
}
/**
* Returns grouping divider background color.
*
* @return color of background.
*/
public static Color getGroupingDividerColor()
{
return INSTANCE.localSettings.getGroupingDividerColor();
}
/**
* Returns TRUE if we should suppress rendering of old articles.
*
* @return TRUE to suppress.
*/
public static boolean isSuppressingOlderThan()
{
return INSTANCE.localSettings.isSuppressingOlderThan();
}
/**
* Returns number of days starting from which artiles should not be displayed.
*
* @return number of days.
*/
public static int getSuppressOlderThan()
{
return INSTANCE.localSettings.getSuppressOlderThan();
}
/**
* Returns TRUE if ascending sorting is enabled.
*
* @return TRUE to sort articles ascending.
*/
public static boolean isSortingAscending()
{
return INSTANCE.localSettings.isSortingAscending();
}
/**
* Returns number of characters for limiting of articles size while in brief mode.
*
* @return number of chars.
*/
public static int getArticleSizeLimit()
{
return INSTANCE.localSettings.getArticleSizeLimit();
}
/**
* Returns Article Filtering code.
*
* @return filtering code.
*/
public static int getArticleFilter()
{
return INSTANCE.localSettings.getArticleFilter();
}
/**
* Returns current font for titles of unread articles.
*
* @return current unread article title font.
*/
public static Font getUnreadArticleTitleFont()
{
if (cacheUnreadTitleFont == null)
{
cacheUnreadTitleFont = getArticleTitleFont().deriveFont(Font.BOLD);
}
return cacheUnreadTitleFont;
}
/**
* Returns current font for titles of read articles.
*
* @return current read article title font.
*/
public static Font getReadArticleTitleFont()
{
if (cacheReadTitleFont == null)
{
cacheReadTitleFont = getArticleTitleFont().deriveFont(Font.PLAIN);
}
return cacheReadTitleFont;
}
/**
* Returns painter for search keyword highlights.
*
* @return painter.
*/
public static synchronized Highlighter.HighlightPainter getSearchKeywordsHighlightPainter()
{
// Create new highlighter of search keywords only of it was not created yet or
// the color changed.
Color color = getSerachKeywordHighlightBackground();
if (INSTANCE.searchKeywordsHP == null || !INSTANCE.lastSearchKeywordHC.equals(color))
{
INSTANCE.lastSearchKeywordHC = color;
INSTANCE.searchKeywordsHP = new DefaultHighlighter.DefaultHighlightPainter(color);
}
return INSTANCE.searchKeywordsHP;
}
/**
* Returns the background color for the search highlight.
*
* @return color.
*/
public static Color getSerachKeywordHighlightBackground()
{
return Color.YELLOW;
}
/**
* Returns color of unregistered blog links.
*
* @return color.
*/
public static synchronized Color getUnregisteredBlogLinkColor()
{
return INSTANCE.localSettings.getUnregisteredBlogLinkColor();
}
/**
* Returns color of registered blog links.
*
* @return color.
*/
public static synchronized Color getRegisteredBlogLinkColor()
{
return INSTANCE.localSettings.getRegisteredBlogLinkColor();
}
/**
* Returns painter for discovered and unregistered URL's.
*
* @return painter.
*/
public static synchronized Highlighter.HighlightPainter getUnregisteredUrlsHighlightPainter()
{
// Create new highlighter of unrgistered BlogLinks only of it was not created yet or
// the color changed.
Color currentColor = INSTANCE.localSettings.getUnregisteredBlogLinkColor();
if (INSTANCE.unregBlogLinksHP == null || !INSTANCE.lastUnregBlogLinkHC.equals(currentColor))
{
INSTANCE.lastUnregBlogLinkHC = currentColor;
INSTANCE.unregBlogLinksHP = new DefaultHighlighter.DefaultHighlightPainter(INSTANCE.lastUnregBlogLinkHC);
}
return INSTANCE.unregBlogLinksHP;
}
/**
* Returns painter for discovered and registered URL's.
*
* @return painter.
*/
public static synchronized Highlighter.HighlightPainter getRegisteredUrlsHighlightPainter()
{
// Create new highlighter of unrgistered BlogLinks only of it was not created yet or
// the color changed.
Color currentColor = INSTANCE.localSettings.getRegisteredBlogLinkColor();
if (INSTANCE.regBlogLinksHP == null || !INSTANCE.lastRegBlogLinksHC.equals(currentColor))
{
INSTANCE.lastRegBlogLinksHC = currentColor;
INSTANCE.regBlogLinksHP = new DefaultHighlighter.DefaultHighlightPainter(INSTANCE.lastRegBlogLinksHC);
}
return INSTANCE.regBlogLinksHP;
}
/**
* Returns value of flag for displaying of full article titles.
*
* @return <code>TRUE</code> for on.
*/
public static boolean isDisplayingFullTitles()
{
return INSTANCE.localSettings.isDisplayingFullTitles();
}
/**
* Returns color of article title.
*
* @param aSelected
* TRUE if color of selected article title needed.
*
* @return color.
*/
public static Color getArticleTitleColor(boolean aSelected)
{
return INSTANCE.localSettings.getArticleTitleColor(aSelected);
}
/**
* Returns color of article text.
*
* @param aSelected
* TRUE if color of selected article text needed.
*
* @return color.
*/
public static Color getArticleTextColor(boolean aSelected)
{
return INSTANCE.localSettings.getArticleTextColor(aSelected);
}
/**
* Returns color of article date.
*
* @param aSelected
* TRUE if color of selected article date needed.
*
* @return color.
*/
public static Color getArticleDateColor(boolean aSelected)
{
return INSTANCE.localSettings.getArticleDateColor(aSelected);
}
/**
* Returns color for feed name in the header of articles list panel.
*
* @return color for the feed name.
*/
public static Color getArticleListFeedNameForeground()
{
return INSTANCE.localSettings.getArticleListFeedNameForeground();
}
/**
* Returns font for feed name in the header of articles list panel.
*
* @return font for the feed name.
*/
public static Font getArticleListFeedNameFont()
{
return INSTANCE.localSettings.getArticleListFeedNameFont();
}
/**
* Returns background color for feeds list.
*
* @param alternating
* TRUE if alternating color required.
*
* @return color.
*/
public static Color getFeedsListBackground(boolean alternating)
{
return INSTANCE.localSettings.getFeedsListBackground(alternating);
}
/**
* Returns selected background color for feeds list.
*
* @return color.
*/
public static Color getFeedsListSelectedBackground()
{
return INSTANCE.localSettings.getFeedsListSelectedBackground();
}
/**
* Returns foreground color for feeds list.
*
* @param selected
* TRUE if selected color required.
*
* @return color.
*/
public static Color getFeedsListForeground(boolean selected)
{
return INSTANCE.localSettings.getFeedsListForeground(selected);
}
/**
* Returns whether to show starz in feeds area.
*
* @return TRUE if starz should be shown in feeds.
*/
public static boolean isShowStarz()
{
return INSTANCE.localSettings.isShowStarz();
}
/**
* Returns whether to show unread count in feeds area.
*
* @return TRUE if unread count should be shown in feeds.
*/
public static boolean isShowUnreadInFeeds()
{
return INSTANCE.localSettings.isShowUnreadInFeeds();
}
/**
* Returns whether to activity chart in feeds area.
*
* @return TRUE if activity chart should be be shown in feeds.
*/
public static boolean isShowActivityChart()
{
return INSTANCE.localSettings.isShowActivityChart();
}
/**
* Returns whether to show icons in guides area.
*
* @return TRUE if icons should be shown in guides.
*/
public static boolean isShowIconInGuides()
{
return INSTANCE.localSettings.isShowIconInGuides();
}
/**
* Returns whether to show text in guides area.
*
* @return TRUE if text should be shown in guides.
*/
public static boolean isShowTextInGuides()
{
return INSTANCE.localSettings.isShowTextInGuides();
}
/**
* Returns whether to show unread count in guides area.
*
* @return TRUE if unread count should be shown in guides.
*/
public static boolean isShowUnreadInGuides()
{
return INSTANCE.localSettings.isShowUnreadInGuides();
}
/**
* Returns whether to show big or small icon in guides area.
*
* @return TRUE to show big icons.
*/
public static boolean isBigIconInGuides()
{
return INSTANCE.localSettings.isBigIconInGuides();
}
}