// 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: ActionsMonitor.java,v 1.60 2008/04/01 08:20:09 spyromus Exp $ // package com.salas.bb.core.actions; import com.salas.bb.core.ControllerAdapter; import com.salas.bb.core.GlobalController; import com.salas.bb.core.GlobalModel; import com.salas.bb.core.actions.article.*; import com.salas.bb.core.actions.feed.*; import com.salas.bb.core.actions.guide.*; import com.salas.bb.discovery.actions.DiscoverInArticlesAction; import com.salas.bb.discovery.actions.DiscoverInFeedsAction; import com.salas.bb.domain.*; import com.salas.bb.remixfeeds.PostToBlogAction; import com.salas.bb.search.SearchAction; import com.salas.bb.service.ShowServiceDialogAction; import com.salas.bb.service.sync.SyncFullAction; import com.salas.bb.service.sync.SyncInAction; import com.salas.bb.service.sync.SyncOutAction; import com.salas.bb.tags.SelectiveShowTagsAction; import com.salas.bb.tags.ShowArticleTagsAction; import com.salas.bb.tags.ShowFeedTagsAction; import com.salas.bb.twitter.TweetThisAction; import com.salas.bb.utils.ConnectionState; import com.salas.bb.utils.uif.UifUtilities; import com.salas.bb.views.feeds.image.SaveImageAction; import com.salas.bb.whatshot.WhatsHotAction; import javax.swing.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; /** * Listens for events and enables / disables actions. */ public final class ActionsMonitor extends ControllerAdapter { private final ConnectionState connectionState; private final IFeedListener taggingFeedListener; private boolean initFinished; private IGuide selectedGuide; private IFeed selectedFeed; private IArticle selectedArticle; /** * Creates actions monitor. * * @param aController controller. * @param aConnectionState connection state interface. */ private ActionsMonitor(GlobalController aController, ConnectionState aConnectionState) { connectionState = aConnectionState; taggingFeedListener = new TaggingFeedListener(); aController.addControllerListener(this); if (aController.isInitializationFinished()) initializationFinished(); connectionState.addPropertyChangeListener(new ConnectionStateListener()); GlobalModel model = aController.getModel(); guideSelected(model == null ? null : model.getSelectedGuide()); feedSelected(model == null ? null : model.getSelectedFeed()); articleSelected(model == null ? null : model.getSelectedArticle()); } /** * Starts monitoring the application state. * * @param aController controller. * @param aConnectionState connection state interface. */ public static void start(GlobalController aController, ConnectionState aConnectionState) { new ActionsMonitor(aController, aConnectionState); } /** * Invoked after application changes the guide. * * @param guide guide to with we have switched. */ public synchronized void guideSelected(IGuide guide) { selectedGuide = guide; if (UifUtilities.isEDT()) { updateGuideActions(selectedGuide, initFinished); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { updateGuideActions(selectedGuide, initFinished); } }); } } /** * Invoked when article selected / deselected. * * @param article article selected or NULL if deselected. */ public synchronized void articleSelected(IArticle article) { selectedArticle = article; if (UifUtilities.isEDT()) { updateArticleActions(selectedArticle, initFinished); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { updateArticleActions(selectedArticle, initFinished); } }); } } /** * Invoked after application changes the feed. * * @param feed feed to which we have switched. */ public void feedSelected(IFeed feed) { if (selectedFeed != null) selectedFeed.removeListener(taggingFeedListener); selectedFeed = feed; if (selectedFeed != null) selectedFeed.addListener(taggingFeedListener); if (UifUtilities.isEDT()) { updateFeedActions(selectedFeed, initFinished); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { updateFeedActions(selectedFeed, initFinished); } }); } } /** * Invoked after application finishes initialization of data. */ public synchronized void initializationFinished() { initFinished = true; if (UifUtilities.isEDT()) { initFinished0(); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { initFinished0(); } }); } } /** * Called when initialization is reported to be finished. */ private void initFinished0() { updateGuideActions(selectedGuide, initFinished); updateFeedActions(selectedFeed, initFinished); // Enabled if initialization finished AddGuideAction.getInstance().setEnabled(true); AddDirectFeedAction.getInstance().setEnabled(true); AddSmartFeedAction.getInstance().setEnabled(true); ImportGuidesAction.getInstance().setEnabled(true); SortGuidesByTitleAction.getInstance().setEnabled(true); SearchAction.getInstance().setEnabled(true); WhatsHotAction.getInstance().setEnabled(true); DatabaseCompactAction.getInstance().setEnabled(true); DatabaseBackupAction.getInstance().setEnabled(true); } /** * Updates guides actions' enable state. * * @param aSelectedGuide guide, which is currently selected. * @param aInitFinished <code>TRUE</code> when initialization finished. */ private void updateGuideActions(IGuide aSelectedGuide, boolean aInitFinished) { final boolean b = aSelectedGuide != null && aInitFinished; final GuidesSet cgs = GlobalModel.SINGLETON.getGuidesSet(); final int index = b ? cgs.indexOf(aSelectedGuide) : -1; final int size = cgs.getGuidesCount(); final boolean hasChannels = b && aSelectedGuide.getFeedsCount() > 0; // Enable actions below when guide selected GuidePropertiesAction.getInstance().setEnabled(b); DeleteGuideAction.getInstance().setEnabled(b); UpdateGuideAction.getInstance().setEnabled(b); UpdateAllGuidesAction.getInstance().setEnabled(size > 0); MarkGuideReadAction.getInstance().setEnabled(hasChannels); MarkGuideUnreadAction.getInstance().setEnabled(hasChannels); MarkAllGuidesReadAction.getInstance().setEnabled(size > 0); MarkAllGuidesUnreadAction.getInstance().setEnabled(size > 0); // Enable merging of guides if there's more than one guide MergeGuidesAction.getInstance().setEnabled(b && size > 1); // Enable actions below when guides are present final boolean hasGuides = aInitFinished && (size > 0); GotoNextArticleAction.getInstance().setEnabled(hasGuides); GotoNextUnreadAction.getInstance().setEnabled(hasGuides); GotoNextUnreadInNextFeedAction.getInstance().setEnabled(hasGuides); GotoPreviousArticleAction.getInstance().setEnabled(hasGuides); GotoPreviousUnreadAction.getInstance().setEnabled(hasGuides); ExportGuidesAction.getInstance().setEnabled(hasGuides); GotoNextGuideWithUnreadAction.getInstance().setEnabled(hasGuides); PostToBlogAction.update(); FeedLinkPostToBlogAction.update(); } /** * Updates feeds actions' enable state. * * @param aSelectedFeed selected feed. * @param aInitFinished <code>TRUE</code> if initialization has already finished. */ private void updateFeedActions(IFeed aSelectedFeed, boolean aInitFinished) { // Enable actions below only when channel selected final boolean b = aSelectedFeed != null; final boolean bInited = b && aInitFinished; MarkFeedAsReadAction.getInstance().setEnabled(bInited); MarkFeedAsUnreadAction.getInstance().setEnabled(bInited); DeleteFeedAction.getInstance().setEnabled(bInited); ShowFeedPropertiesAction.getInstance().setEnabled(bInited); RatingLowAction.getInstance().setEnabled(bInited); RatingHighAction.getInstance().setEnabled(bInited); UpdateSelectedFeedsAction.getInstance().setEnabled(bInited); DiscoverInFeedsAction.getInstance().setEnabled(bInited); PostToBlogAction.update(); FeedLinkPostToBlogAction.update(); // Enables opening of home page in browser only if // URL is specified. boolean isDirect = aSelectedFeed instanceof DirectFeed; OpenBlogHomeAction.getInstance().setEnabled(b && isDirect && ((DirectFeed)aSelectedFeed).getSiteURL() != null); updateTaggingActions(); // Updating title of Delete command accroding to the state and nature of a selected feed int mode = DeleteFeedAction.DELETE; if (aSelectedFeed != null && (aSelectedFeed instanceof DirectFeed) && selectedGuide != null && !selectedGuide.hasDirectLinkWith(aSelectedFeed)) { mode = ((DirectFeed)aSelectedFeed).isDisabled() ? DeleteFeedAction.ENABLE : DeleteFeedAction.DISABLE; } DeleteFeedAction.getInstance().setMode(mode); CycleViewModeForwardAction.getInstance().setEnabled(bInited); CycleViewModeBackwardAction.getInstance().setEnabled(bInited); } /** * Updates articles actions' enable state. * * @param aSelectedArticle selected article. * @param aInitFinished <code>TRUE</code> if initialization has already finished. */ private void updateArticleActions(IArticle aSelectedArticle, boolean aInitFinished) { final boolean b = aSelectedArticle != null; final boolean bInited = b && aInitFinished; ShowArticlePropertiesAction.getInstance().setEnabled(b); final boolean articleUrlPresent = (b && aSelectedArticle.getLink() != null); BrowseArticleAction.getInstance().setEnabled(articleUrlPresent); ArticleLinkCopyAction.getInstance().setEnabled(articleUrlPresent); ArticleLinkSendAction.getInstance().setEnabled(articleUrlPresent); MarkArticleReadAction.getInstance().setEnabled(bInited); MarkArticleUnreadAction.getInstance().setEnabled(bInited); PinUnpinArticleAction.getInstance().setEnabled(bInited); PostToBlogAction.update(); TweetThisAction.getInstance().setEnabled(bInited); FeedLinkPostToBlogAction.update(); DiscoverInArticlesAction.getInstance().setEnabled(bInited); SaveImageAction.getInstance().setEnabled(b); updateTaggingActions(); } /** * Updates the state of all tagging actions. */ private void updateTaggingActions() { boolean taggableArticle = selectedArticle != null && (selectedArticle instanceof StandardArticle) && ((StandardArticle)selectedArticle).getTaggableLink() != null; boolean taggableFeed = selectedFeed != null && (selectedFeed instanceof DirectFeed) && ((DirectFeed)selectedFeed).getTaggableLink() != null; ShowArticleTagsAction.getInstance().setEnabled(taggableArticle); ShowFeedTagsAction.getInstance().setEnabled(taggableFeed); SelectiveShowTagsAction.getInstance().setEnabled(taggableFeed || taggableArticle); } /** * Called when connection gets online-offline. * * @param isOnline <code>TRUE</code> if currently online. */ private void onConnectionChange(boolean isOnline) { } /** * Called when the service becomes available or not available. * * @param isServiceAccessible <code>TRUE</code> if the service is available. */ private void onServiceAvailabilityChange(final boolean isServiceAccessible) { SwingUtilities.invokeLater(new Runnable() { public void run() { CheckForUpdatesAction.getInstance().setEnabled(isServiceAccessible); SendFeedbackAction.getInstance().setEnabled(isServiceAccessible); ShowServiceDialogAction.getInstance().setEnabled(isServiceAccessible); // Synchronization actions SyncInAction.getInstance().setEnabled(isServiceAccessible); SyncOutAction.getInstance().setEnabled(isServiceAccessible); SyncFullAction.getInstance().setEnabled(isServiceAccessible); } }); } /** * Listens to changes in the feed and updates the tagging options. */ private class TaggingFeedListener 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 (DirectFeed.PROP_XML_URL.equals(property)) updateTaggingActions(); } } /** * Connection state listener. */ private class ConnectionStateListener implements PropertyChangeListener { /** * Invoked when connection state changes. * * @param evt preoperty change event. */ public void propertyChange(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); if (ConnectionState.PROP_ONLINE.equals(prop)) { onConnectionChange(connectionState.isOnline()); } else { onServiceAvailabilityChange(connectionState.isServiceAccessible()); } } } }