Java tutorial
// 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: ArticleListPanel.java,v 1.52 2008/04/02 14:31:28 spyromus Exp $ // package com.salas.bb.views; import com.jgoodies.binding.beans.PropertyAdapter; import com.jgoodies.binding.value.ValueHolder; import com.jgoodies.binding.value.ValueModel; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.uif.action.ActionManager; import com.jgoodies.uif.action.ToggleAction; import com.jgoodies.uif.builder.ToolBarBuilder; import com.jgoodies.uif.util.SystemUtils; import com.salas.bb.core.ControllerAdapter; import com.salas.bb.core.GlobalController; import com.salas.bb.core.GlobalModel; import com.salas.bb.core.actions.ActionsTable; import com.salas.bb.domain.IArticle; import com.salas.bb.domain.IFeed; import com.salas.bb.domain.IFeedListener; import com.salas.bb.domain.prefs.UserPreferences; import com.salas.bb.sentiments.SentimentsFeature; import com.salas.bb.utils.i18n.Strings; import com.salas.bb.utils.uif.CoolInternalFrame; import com.salas.bb.utils.uif.JComboBoxActionItem; import com.salas.bb.utils.uif.JumplessScrollPane; import com.salas.bb.views.feeds.CompositeFeedDisplay; import com.salas.bb.views.feeds.IFeedDisplay; import com.salas.bb.views.settings.HTMLFeedDisplayConfig; import com.salas.bb.views.settings.ImageFeedDisplayConfig; import com.salas.bb.views.settings.RenderingManager; import com.salas.bb.views.settings.RenderingSettingsNames; import javax.swing.*; import java.awt.*; import java.awt.event.MouseWheelEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.MessageFormat; /** * Display and work on the Articles of the selected Channel. */ public final class ArticleListPanel extends CoolInternalFrame implements PropertyChangeListener { private final IFeedDisplay feedDisplay; private final JToolBar subToolBar; /** Page number model. */ private ValueModel pageModel; /** * Constructs panel with list of articles for selected channel. */ public ArticleListPanel() { super(Strings.message("panel.articles")); setPreferredSize(new Dimension(300, 100)); RenderingManager.addPropertyChangeListener(RenderingSettingsNames.THEME, this); // Register own controller listener GlobalController.SINGLETON.addControllerListener(new ControllerListener()); PropertyChangeListener articleViewChangeHandler = new ArticleViewChangeHandler(); GlobalModel.SINGLETON.getGlobalRenderingSettings().addPropertyChangeListener("articleViewMode", articleViewChangeHandler); // Set the sub-toolbar (right justified in the CoolInternalFrame) pageModel = new ValueHolder(0); pageModel.addValueChangeListener(new PageModelListener()); ValueModel pageCountModel = new ValueHolder(0); subToolBar = createSubtoolbar(); setHeaderControl(subToolBar); // Create the list that will contain the channels. Uses a custom // renderer. HTMLFeedDisplayConfig htmlConfig = new HTMLFeedDisplayConfig(); ImageFeedDisplayConfig imageConfig = new ImageFeedDisplayConfig(); RenderingManager.addPropertyChangeListener(htmlConfig.getRenderingManagerListener()); RenderingManager.addPropertyChangeListener(imageConfig.getRenderingManagerListener()); // Get page size user preferences and subscribe to updates UserPreferences preferences = GlobalModel.SINGLETON.getUserPreferences(); PropertyAdapter paPageSize = new PropertyAdapter(preferences, UserPreferences.PROP_PAGE_SIZE, true); paPageSize.addPropertyChangeListener(new PageSizeListener()); feedDisplay = new CompositeFeedDisplay(htmlConfig, imageConfig, pageModel, pageCountModel); feedDisplay.setPageSize(preferences.getPageSize()); // Setup data-adapter FeedDisplayAdapter adapter = new FeedDisplayAdapter(feedDisplay); GlobalController.SINGLETON.addControllerListener(adapter); // Create scroll pane and put list in it JScrollPane listSP = new JumplessScrollPane(feedDisplay.getComponent()); listSP.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); listSP.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); listSP.setFocusable(false); listSP.setBorder(null); // Register viewport to enable correct scrolling feedDisplay.setViewport(listSP.getViewport()); JPanel content = new JPanel(new BorderLayout()); content.add(new PagingPanel(pageModel, pageCountModel), BorderLayout.NORTH); content.add(listSP, BorderLayout.CENTER); // Register scroll pane setContent(content); setFeedTitle(null); } /** * Returns articles list component. * * @return articles list. */ public IFeedDisplay getFeedView() { return feedDisplay; } /** * Creates a sub-toolbar with two models. * * @return sub-toolbar. */ private JToolBar createSubtoolbar() { final JToolBar toolbar = new JToolBar(); toolbar.setRollover(true); toolbar.setAlignmentY(0.0f); toolbar.setOpaque(false); toolbar.setBorder(null); toolbar.setBorderPainted(false); ToolBarBuilder subTBbldr = new ToolBarBuilder(toolbar); final JComboBox comboBox = new JComboBox(); // Indexes of items in this list should correspond to the values of // filter setting from FeedRenderingSettings class (FILTER_XYZ constants) createNAddComboBoxAction(comboBox, ActionsTable.CMD_ARTICLE_SHOW_ALL_TB); createNAddComboBoxAction(comboBox, ActionsTable.CMD_ARTICLE_SHOW_UNREAD_TB); createNAddComboBoxAction(comboBox, ActionsTable.CMD_ARTICLE_SHOW_PINNED_TB); if (SentimentsFeature.isAvailable()) { createNAddComboBoxAction(comboBox, ActionsTable.CMD_ARTICLE_SHOW_POSITIVE_TB); createNAddComboBoxAction(comboBox, ActionsTable.CMD_ARTICLE_SHOW_NEGATIVE_TB); createNAddComboBoxAction(comboBox, ActionsTable.CMD_ARTICLE_SHOW_NON_NEGATIVE_TB); } int filter = Math.min(RenderingManager.getArticleFilter(), comboBox.getItemCount() - 1); comboBox.addActionListener(JComboBoxActionItem.getComboBoxListener()); comboBox.setOpaque(!SystemUtils.IS_OS_MAC); comboBox.setFocusable(false); comboBox.setSelectedIndex(filter); // Listen to changes of FILTER property and update the selection RenderingManager.addPropertyChangeListener(RenderingSettingsNames.ARTICLE_FILTER, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { comboBox.setSelectedIndex(RenderingManager.getArticleFilter()); } }); JPanel comboboxPanel = new JPanel(new FormLayout("10px, p", SystemUtils.IS_OS_MAC ? "1px, p" : "0, 20px")); CellConstraints cc = new CellConstraints(); comboboxPanel.add(comboBox, cc.xy(2, 2)); // Mac OS Aqua look has a bug with JComboBox sizing depending on the font size // The report: http://lists.apple.com/archives/Java-dev/2005/Jan/msg00510.html // and the answer: http://lists.apple.com/archives/Java-dev/2005/Jan/msg00511.html // // The page with screenshots of how the Quaqua library deals with the JComboBox // look through the custom component look painting (not a native component): // http://www.randelshofer.ch/quaqua/guide/jcombobox.html if (!SystemUtils.IS_OS_MAC) { comboBox.setFont(new Font("Tahoma", Font.PLAIN, 10)); } comboboxPanel.setOpaque(false); subTBbldr.add(comboboxPanel); subTBbldr.addGap(); subTBbldr.add(new ViewModeSelector(GlobalModel.SINGLETON.getViewModeValueModel())); return toolbar; } private void createNAddComboBoxAction(JComboBox box, String cmdcode) { JToggleButton newitem = new JComboBoxActionItem((ToggleAction) ActionManager.get(cmdcode)); box.addItem(newitem); } /** * Called when the theme changes. * * @param evt event object. */ public void propertyChange(PropertyChangeEvent evt) { updateUI(); } // Changes title text. private void setFeedTitle(final IFeed feed) { String text = (feed == null ? Strings.message("panel.articles.no.feed.selected") : feed.getTitle()); if (text == null) text = Strings.message("untitled"); setSubtitle(MessageFormat.format(Strings.message("panel.in"), text)); } /** * Listens for <code>GlobalController</code> events in order to get when channel selected. This * information is necessary to set correct articles list title. */ private final class ControllerListener extends ControllerAdapter implements IFeedListener { private IFeed selected; /** * Invoked after application changes the feed. * * @param feed feed to which we are switching. */ public void feedSelected(final IFeed feed) { if (feed != selected) { if (selected != null) selected.removeListener(this); selected = feed; if (selected != null) selected.addListener(this); // Set the first page pageModel.setValue(0); setFeedTitle(selected); } } /** * Invoked when new article has been added to the feed. * * @param feed feed. * @param article article. */ public void articleAdded(IFeed feed, IArticle article) { } /** * Invoked when the article has been removed from the feed. * * @param feed feed. * @param article article. */ public void articleRemoved(IFeed feed, IArticle article) { } /** * Invoked when the property of the feed has been changed. * * @param feed feed. * @param property property of the feed. * @param oldValue old property value. * @param newValue new property value. */ public void propertyChanged(final IFeed feed, String property, Object oldValue, Object newValue) { if (property.equals(IFeed.PROP_TITLE)) { SwingUtilities.invokeLater(new Runnable() { public void run() { setFeedTitle(feed); } }); } } } /** * Listener of property changes in article view. */ private class ArticleViewChangeHandler implements PropertyChangeListener { /** * Repaints sub-toolbar on any changes. * * @param e property change event object. */ public void propertyChange(PropertyChangeEvent e) { subToolBar.repaint(); } } /** * Looks at the page model and notifies the feed display on changes in page number. */ private class PageModelListener implements PropertyChangeListener { /** * Invoked when the page number changes. * * @param evt event. */ public void propertyChange(PropertyChangeEvent evt) { feedDisplay.setPage((Integer) evt.getNewValue()); } } /** * Looks at the page size model. */ private class PageSizeListener implements PropertyChangeListener { /** * Invoked when the page number changes. * * @param evt event. */ public void propertyChange(PropertyChangeEvent evt) { if (PropertyAdapter.PROPERTYNAME_VALUE.equals(evt.getPropertyName())) { feedDisplay.setPageSize((Integer) evt.getNewValue()); } } } /** * Paging panel that comes out when the paging is available. */ private class PagingPanel extends JPanel implements PropertyChangeListener { private final ValueModel pageModel; private final ValueModel pageCountModel; private JLabel lbInfo; /** * Creates the panel. * * @param pageModel page number model. * @param pageCountModel page count model. */ public PagingPanel(ValueModel pageModel, ValueModel pageCountModel) { this.pageModel = pageModel; this.pageCountModel = pageCountModel; lbInfo = new JLabel(); RenderingManager.addPropertyChangeListener(RenderingSettingsNames.THEME, this); PagingControl paging = new PagingControl(pageModel, pageCountModel); setLayout(new FormLayout("4dlu, p, 7dlu:grow, p, 4dlu", "2dlu, p, 2dlu")); CellConstraints cc = new CellConstraints(); add(lbInfo, cc.xy(2, 2)); add(paging, cc.xy(4, 2)); // Bind the panel pageModel.addValueChangeListener(this); pageCountModel.addValueChangeListener(this); onPageConfigChange(); onThemeChange(); enableEvents(AWTEvent.MOUSE_WHEEL_EVENT_MASK); } /** * Updates the fonts and colors. */ private void onThemeChange() { lbInfo.setFont(RenderingManager.getArticleTitleFont()); setBackground(Color.decode("#9cc9ff")); } /** * Updates the text of the info. */ private void onPageConfigChange() { int pages = (Integer) pageCountModel.getValue(); setVisible(pages > 1); String[] msgs = Strings.slices("pagination.page.of"); lbInfo.setText(msgs[0] + " " + ((Integer) pageModel.getValue() + 1) + " " + msgs[1] + " " + pages); } /** * Invoked when the page number of the number of pages changes. * * @param evt event. */ public void propertyChange(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); if (ValueHolder.PROPERTYNAME_VALUE.equals(prop)) { onPageConfigChange(); } else if (RenderingSettingsNames.THEME.equals(prop)) { onThemeChange(); } } @Override protected void processMouseWheelEvent(MouseWheelEvent e) { feedDisplay.getComponent().dispatchEvent(e); } } }