// 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: UserPreferencesDialog.java,v 1.110 2008/10/23 08:14:25 spyromus Exp $
//
package com.salas.bb.dialogs;
import com.jgoodies.binding.adapter.BoundedRangeAdapter;
import com.jgoodies.binding.adapter.ComboBoxAdapter;
import com.jgoodies.binding.adapter.ToggleButtonAdapter;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.value.AbstractValueModel;
import com.jgoodies.binding.value.BufferedValueModel;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.uif.AbstractDialog;
import com.jgoodies.uif.AbstractFrame;
import com.jgoodies.uif.util.SystemUtils;
import com.salas.bb.core.*;
import com.salas.bb.domain.FeedClass;
import com.salas.bb.domain.FeedsSortOrder;
import com.salas.bb.domain.GuideClass;
import com.salas.bb.domain.prefs.StarzPreferences;
import com.salas.bb.domain.prefs.UserPreferences;
import com.salas.bb.domain.prefs.ViewModePreferences;
import com.salas.bb.remixfeeds.prefs.BloggingPreferencesPanel;
import com.salas.bb.sentiments.SentimentsFeature;
import com.salas.bb.twitter.TwitterPreferencesPanel;
import com.salas.bb.utils.Constants;
import com.salas.bb.utils.ResourceID;
import com.salas.bb.utils.SpinnerModelAdapter;
import com.salas.bb.utils.i18n.Strings;
import com.salas.bb.utils.notification.NotificationArea;
import com.salas.bb.utils.uif.*;
import com.salas.bb.views.feeds.IFeedDisplay;
import com.salas.bb.views.feeds.IFeedDisplayConstants;
import com.salas.bb.views.mainframe.MainFrame;
import com.salas.bb.views.settings.FeedRenderingSettings;
import com.salas.bb.views.settings.RenderingSettingsNames;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Builds the User Preferences dialog.
*/
public final class UserPreferencesDialog extends AbstractDialog
{
private final GlobalModel model;
private final Pages pages;
private AdvancedPreferencesPanel pnlAdvanced;
private final AtomicBoolean feedsFilterChanged = new AtomicBoolean(false);
private final AtomicBoolean guidesFilterChanged = new AtomicBoolean(false);
private static final ColorListCellRenderer colorListCellRenderer =
new ColorListCellRenderer(Strings.message("userprefs.tab.feeds.filter.hidden"));
private Class selectedPanelClass;
/**
* Creates user preferences dialog.
*
* @param owner owning frame.
* @param aModel global Model of BlogBridge.
*/
public UserPreferencesDialog(AbstractFrame owner, GlobalModel aModel)
{
super(owner);
this.model = aModel;
pages = new Pages();
}
// Building *************************************************************
/**
* Builds and answers the preference's header.
*
* @return JComponent header of dialog box
*/
protected JComponent buildHeader()
{
return new HeaderPanelExt(
Strings.message("userprefs.dialog.title"),
Strings.message("userprefs.dialog.header"),
IconSource.getIcon(ResourceID.ICON_PREFERENCES));
}
/**
* Builds and answers the preference's content pane.
*
* @return JComponent of content part of the dialog box
*/
protected JComponent buildContent()
{
JPanel content = new JPanel(new BorderLayout());
content.add(buildNavPanel(), BorderLayout.NORTH);
content.add(buildMainPane(), BorderLayout.CENTER);
content.add(buildButtonsBar(), BorderLayout.SOUTH);
return content;
}
private Component buildNavPanel()
{
JComboBox selector = pages.getSelector();
JButton btnPrev = new JButton(Strings.message("userprefs.dialog.nav.prev"));
btnPrev.addActionListener(new MoveSelection(selector, false));
JButton btnNext = new JButton(Strings.message("userprefs.dialog.nav.next"));
btnNext.addActionListener(new MoveSelection(selector, true));
BBFormBuilder builder = new BBFormBuilder("p, 4dlu, p, 2dlu, p");
builder.append(selector);
builder.append(btnPrev);
builder.append(btnNext);
JPanel panel = builder.getPanel();
Insets ins = Borders.DIALOG_BORDER.getBorderInsets(null);
panel.setBorder(BorderFactory.createEmptyBorder(0, 0, ins.bottom, 0));
return panel;
}
/**
* Opens the dialog and chooses the panel.
*
* @param panelClass panel.
*/
public void open(Class panelClass)
{
selectedPanelClass = panelClass;
open();
}
/**
* Moves selection in the combo-box forward/backwards.
*/
private static class MoveSelection implements ActionListener
{
private JComboBox box;
private boolean forward;
/**
* Creates action.
*
* @param box box.
* @param forward direction.
*/
public MoveSelection(JComboBox box, boolean forward)
{
this.box = box;
this.forward = forward;
}
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e)
{
int sel = box.getSelectedIndex();
int siz = box.getItemCount();
if (forward)
{
sel++;
if (sel == siz) sel = 0;
} else
{
sel--;
if (sel < 0) sel = siz - 1;
}
box.setSelectedIndex(sel);
}
}
private JComponent buildButtonsBar()
{
return buildButtonBarWithOKCancelApply();
}
/**
* Builds content pane.
*
* @return pane.
*/
private JComponent buildMainPane()
{
final UserPreferences prefs = model.getUserPreferences();
final StarzPreferences starzPreferences = model.getStarzPreferences();
final FeedRenderingSettings feedRS = model.getGlobalRenderingSettings();
final ValueModel trigger = getTriggerChannel();
GeneralPreferencesPanel generalPanel = new GeneralPreferencesPanel(prefs, feedRS, trigger);
pnlAdvanced = new AdvancedPreferencesPanel(prefs, starzPreferences, feedRS, trigger);
TagsPreferencesPanel tagsPanel = new TagsPreferencesPanel(prefs, model.getServicePreferences(), trigger);
pages.addPage(Strings.message("userprefs.tab.general"), generalPanel);
pages.addPage(Strings.message("userprefs.tab.guides"), new GuidesPanel(feedRS, trigger, guidesFilterChanged));
pages.addPage(Strings.message("userprefs.tab.feeds"), new FeedsPanel(feedRS, prefs, trigger, feedsFilterChanged));
pages.addPage(Strings.message("userprefs.tab.articles"), new ArticlesPanel(prefs, trigger));
pages.addPage(Strings.message("userprefs.tab.tags"), tagsPanel);
pages.addPage(Strings.message("userprefs.tab.readinglists"), new ReadingListsPanel(prefs, trigger));
pages.addPage(Strings.message("userprefs.tab.blogs"),
new BloggingPreferencesPanel(this, trigger, prefs.getBloggingPreferences()));
pages.addPage(Strings.message("userprefs.tab.twitter"),
new TwitterPreferencesPanel(this, trigger, prefs.getTwitterPreferences()));
pages.addPage(Strings.message("userprefs.tab.notifications"), new NotificationsPanel(prefs, trigger));
pages.addPage(Strings.message("userprefs.tab.advanced"), pnlAdvanced);
pages.setBorder(BorderFactory.createEtchedBorder());
// Restore last selected page
if (!pages.selectPageByClass(selectedPanelClass))
{
int i = prefs.getSelectedPrefsPage();
if (i > -1 && i < pages.getSelector().getItemCount()) pages.getSelector().setSelectedIndex(i);
}
return pages;
}
/**
* Opens dialog. We intercept it to save the last visible page index.
*/
public void open()
{
super.open();
// Store last selected page
model.getUserPreferences().setSelectedPrefsPage(pages.getSelector().getSelectedIndex());
}
protected void resizeHook(JComponent comp)
{
int width = SystemUtils.IS_OS_MAC ? 565 : (int)(460 * Constants.SIZE_FACTOR);
int height = SystemUtils.IS_OS_MAC ? 600 : (int)(515 * Constants.SIZE_FACTOR);
comp.setPreferredSize(new Dimension(width, height));
}
// Misc *****************************************************************
/**
* Performs repacking of window on accept.
*/
public void doApply()
{
String msg = checkValidity();
if (msg == null)
{
super.doApply();
if (feedsFilterChanged.get())
{
model.getGuidesSet().invalidateFeedVisibilityCaches();
model.getGuideModel().filterChanged();
}
feedsFilterChanged.set(false);
guidesFilterChanged.set(false);
pack();
GlobalModel.touchPreferences();
} else
{
JOptionPane.showMessageDialog(getParent(), msg, getTitle(), JOptionPane.ERROR_MESSAGE);
}
}
/**
* Checks the validity of the properties.
*
* @return the message or <code>NULL</code> if fine.
*/
private String checkValidity()
{
return pnlAdvanced.checkValidity();
}
/**
* Closes the window.
*/
protected void doCloseWindow()
{
doCancel();
}
/**
* Basic preferences panel.
*/
private static class PreferencesPanel extends JPanel
{
final ValueModel trigger;
/**
* Creates the panel.
*
* @param aTrigger trigger.
*/
public PreferencesPanel(ValueModel aTrigger)
{
trigger = aTrigger;
}
}
/**
* Pages component knows how to register and show titled pages. It has selector sub-component
* based on combo-box (drop-down list).
*/
private static class Pages extends JPanel
{
private JComboBox selector;
/**
* Creates pages control.
*/
public Pages()
{
setLayout(new BorderLayout());
selector = new JComboBox();
selector.addItemListener(new PageSelectedListener());
}
/**
* Returns page selector.
*
* @return page selector.
*/
public JComboBox getSelector()
{
return selector;
}
/**
* Adds page to the selector.
*
* @param title title.
* @param page page.
*/
public void addPage(String title, JComponent page)
{
selector.addItem(new Page(title, page));
}
/**
* Selects the page by its component class.
*
* @param pageClass class.
*
* @return TRUE if the page was found.
*/
public boolean selectPageByClass(Class pageClass)
{
if (pageClass == null) return false;
Page selectedPage = null;
int count = selector.getItemCount();
for (int i = 0; i < count && selectedPage == null; i++)
{
Page page = (Page)selector.getItemAt(i);
if (page.page.getClass() == pageClass) selectedPage = page;
}
selector.setSelectedItem(selectedPage);
return selectedPage != null;
}
/**
* Invoked when the page gets selected.
*
* @param page page.
*/
public void onPageSelected(JComponent page)
{
removeAll();
add(page, BorderLayout.CENTER);
validate();
repaint();
}
/**
* Listens to page selection events and sends them to the main class.
*/
private class PageSelectedListener implements ItemListener
{
/**
* Invoked when another page gets selected.
*
* @param e event.
*/
public void itemStateChanged(ItemEvent e)
{
JComponent page = null;
Page pageItem = (Page)selector.getSelectedItem();
if (pageItem != null) page = pageItem.getPage();
onPageSelected(page);
}
}
/** Single titled page. */
private static class Page
{
private String title;
private JComponent page;
/**
* Creates titled page.
*
* @param title title.
* @param page page.
*/
public Page(String title, JComponent page)
{
this.title = title;
this.page = page;
}
/**
* Returns page title.
*
* @return title.
*/
public String toString()
{
return title;
}
/**
* Returns page component.
*
* @return component.
*/
public JComponent getPage()
{
return page;
}
}
}
/**
* Guides preferences panel.
*/
private static class GuidesPanel extends PreferencesPanel
{
/**
* Creates panel.
*
* @param aFeedRS feed rendering settings.
* @param aTrigger trigger.
* @param filterChanged atomic boolean flag.
*/
public GuidesPanel(FeedRenderingSettings aFeedRS, ValueModel aTrigger, AtomicBoolean filterChanged)
{
super(aTrigger);
JCheckBox chBigIconInGuides = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.guides.use.large.icons"),
aFeedRS, RenderingSettingsNames.IS_BIG_ICON_IN_GUIDES, trigger);
JCheckBox chShowUnreadInGuides = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.guides.show.unread.counter"),
aFeedRS, "showUnreadInGuides", trigger);
JCheckBox chShowIconInGuides = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.guides.show.icon"),
aFeedRS, RenderingSettingsNames.IS_ICON_IN_GUIDES_SHOWING, trigger);
JCheckBox chShowTextInGuides = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.guides.show.name"),
aFeedRS, RenderingSettingsNames.IS_TEXT_IN_GUIDES_SHOWING, trigger);
new MandatoryCheckBoxController(chShowIconInGuides, chShowTextInGuides);
GuideDisplayModeManager gdmm = GuideDisplayModeManager.getInstance();
JComboBox cbFilterRead = createFilterComboBox(GuideClass.READ, gdmm, filterChanged, trigger);
// Layout
BBFormBuilder builder =
new BBFormBuilder("7dlu, 7dlu, 35dlu, 40dlu, 2dlu, 90dlu, 2dlu, p:grow", this);
builder.setDefaultDialogBorder();
builder.setLeadingColumnOffset(1);
builder.appendSeparator(Strings.message("userprefs.tab.feeds.filtering"));
builder.append(Strings.message("userprefs.fully.read"), 3,
CellConstraints.RIGHT, CellConstraints.DEFAULT).setLabelFor(cbFilterRead);
builder.append(cbFilterRead);
builder.nextLine();
builder.appendSeparator(Strings.message("userprefs.tab.guides.show"));
builder.setLeadingColumnOffset(1);
builder.append(chShowIconInGuides, 7);
builder.setLeadingColumnOffset(2);
builder.append(chBigIconInGuides, 6);
builder.setLeadingColumnOffset(1);
builder.append(chShowTextInGuides, 7);
builder.append(chShowUnreadInGuides, 7);
}
}
/**
* Feeds preferences panel.
*/
private static class FeedsPanel extends PreferencesPanel
{
private static final Integer[] SORTING_ORDER = {
FeedsSortOrder.ALPHABETICAL,
FeedsSortOrder.RATING,
FeedsSortOrder.READ,
FeedsSortOrder.INVALIDNESS,
FeedsSortOrder.VISITS
};
private static final String[] SORTING_CLASS_NAMES = {
getFeedSortOrderName(FeedsSortOrder.ALPHABETICAL),
getFeedSortOrderName(FeedsSortOrder.RATING),
getFeedSortOrderName(FeedsSortOrder.READ),
getFeedSortOrderName(FeedsSortOrder.INVALIDNESS),
getFeedSortOrderName(FeedsSortOrder.VISITS)
};
private JLabel lbSortFirstBy;
private JLabel lbSortThenBy;
private JCheckBox chSortingEnabled;
private JComboBox cbSortClass1;
private JCheckBox chReverseSort1;
private JComboBox cbSortClass2;
private JCheckBox chReverseSort2;
/**
* Creates the panel.
*
* @param aFeedRS rendering settings.
* @param aPrefs user preferences.
* @param aTrigger trigger.
* @param filterChanged atomic boolean flag.
*/
public FeedsPanel(FeedRenderingSettings aFeedRS, UserPreferences aPrefs,
ValueModel aTrigger, AtomicBoolean filterChanged)
{
super(aTrigger);
JCheckBox chShowStarz = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.feeds.show.starz"), aFeedRS, "showStarz", trigger);
JCheckBox chShowUnreadInFeeds = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.feeds.show.number.of.unread.articles"),
aFeedRS, "showUnreadInFeeds", trigger);
JCheckBox chShowActivityChart = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.feeds.show.7.day.activity.chart"),
aFeedRS, "showActivityChart", trigger);
FeedDisplayModeManager fdmm = FeedDisplayModeManager.getInstance();
JComboBox cbFilterLowRated = createFilterComboBox(FeedClass.LOW_RATED, fdmm, filterChanged, trigger);
JComboBox cbFilterRead = createFilterComboBox(FeedClass.READ, fdmm, filterChanged, trigger);
JComboBox cbFilterInvalid = createFilterComboBox(FeedClass.INVALID, fdmm, filterChanged, trigger);
JComboBox cbFilterDisabled = createFilterComboBox(FeedClass.DISABLED, fdmm, filterChanged, trigger);
chSortingEnabled = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.feeds.sort"), aPrefs, UserPreferences.PROP_SORTING_ENABLED, trigger);
chSortingEnabled.addActionListener(new SortingEnabledListener());
cbSortClass1 = new JComboBox(new SortClassesAdapter(new BufferedValueModel(
new PropertyAdapter(aPrefs, UserPreferences.PROP_SORT_BY_CLASS_1), trigger)));
cbSortClass2 = new JComboBox(new SortClassesAdapter(new BufferedValueModel(
new PropertyAdapter(aPrefs, UserPreferences.PROP_SORT_BY_CLASS_2), trigger)));
chReverseSort1 = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.feeds.sort.reverse.1"),
aPrefs, UserPreferences.PROP_REVERSED_SORT_BY_CLASS_1, trigger);
chReverseSort2 = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.feeds.sort.reverse.2"),
aPrefs, UserPreferences.PROP_REVERSED_SORT_BY_CLASS_2, trigger);
BBFormBuilder builder =
new BBFormBuilder("7dlu, 40dlu, 2dlu, 40dlu, 2dlu, 90dlu, 2dlu, p:grow", this);
builder.setDefaultDialogBorder();
builder.appendSeparator(Strings.message("userprefs.tab.feeds.sorting"));
builder.setLeadingColumnOffset(1);
builder.append(chSortingEnabled);
lbSortFirstBy = builder.append(Strings.message("userprefs.tab.feeds.sorting.first.by"), 1,
CellConstraints.RIGHT, CellConstraints.DEFAULT);
builder.append(cbSortClass1);
builder.append(chReverseSort1);
builder.nextLine();
lbSortThenBy = builder.append(Strings.message("userprefs.tab.feeds.sorting.then.by"), 3,
CellConstraints.RIGHT, CellConstraints.DEFAULT);
builder.append(cbSortClass2);
builder.append(chReverseSort2);
builder.appendSeparator(Strings.message("userprefs.tab.feeds.filtering"));
builder.append(Strings.message("userprefs.tab.feeds.invalid"), 3,
CellConstraints.RIGHT, CellConstraints.DEFAULT).setLabelFor(cbFilterInvalid);
builder.append(cbFilterInvalid);
builder.nextLine();
builder.append(Strings.message("userprefs.fully.read"), 3,
CellConstraints.RIGHT, CellConstraints.DEFAULT).setLabelFor(cbFilterRead);
builder.append(cbFilterRead);
builder.nextLine();
builder.append(Strings.message("userprefs.tab.feeds.below.starz.threshold"), 3,
CellConstraints.RIGHT, CellConstraints.DEFAULT).setLabelFor(cbFilterLowRated);
builder.append(cbFilterLowRated);
builder.nextLine();
builder.append(Strings.message("userprefs.tab.feeds.disabled"), 3,
CellConstraints.RIGHT, CellConstraints.DEFAULT).setLabelFor(cbFilterDisabled);
builder.append(cbFilterDisabled);
builder.nextLine();
builder.appendSeparator(Strings.message("userprefs.tab.feeds.show"));
builder.append(chShowStarz, 5);
builder.nextLine();
builder.append(chShowUnreadInFeeds, 5);
builder.nextLine();
builder.append(chShowActivityChart, 5);
setSortingEnabled(aPrefs.isSortingEnabled());
}
/**
* Returns the name of the sorting class.
*
* @param clazz class id.
*
* @return the name.
*/
private static String getFeedSortOrderName(int clazz)
{
return (String)FeedsSortOrder.SORTING_CLASS_NAMES.get(clazz);
}
/**
* Enables and disables sorting components.
*
* @param enabled <code>TRUE</code> to enable.
*/
private void setSortingEnabled(boolean enabled)
{
chReverseSort1.setEnabled(enabled);
chReverseSort2.setEnabled(enabled);
cbSortClass1.setEnabled(enabled);
cbSortClass2.setEnabled(enabled);
lbSortFirstBy.setEnabled(enabled);
lbSortThenBy.setEnabled(enabled);
}
/** Listens to checkbox and updates enableness state of related controls. */
private class SortingEnabledListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
boolean enabled = chSortingEnabled.isSelected();
setSortingEnabled(enabled);
}
}
/** Writes class-mask into model, but displays textual names of class-masks. */
private static class SortClassesAdapter extends ComboBoxAdapter
{
public SortClassesAdapter(ValueModel aValueModel)
{
super(SORTING_CLASS_NAMES, aValueModel);
}
public Object getSelectedItem()
{
int clazz = (Integer)super.getSelectedItem();
int index = classToIndex(clazz);
return SORTING_CLASS_NAMES[index];
}
public void setSelectedItem(Object o)
{
int index = classNameToIndex((String)o);
super.setSelectedItem(SORTING_ORDER[index]);
}
private int classToIndex(int clazz)
{
int index = -1;
for (int i = 0; index == -1 && i < SORTING_ORDER.length; i++)
{
index = SORTING_ORDER[i] == clazz ? i : -1;
}
return index;
}
private int classNameToIndex(String className)
{
int index = -1;
for (int i = 0; index == -1 && i < SORTING_CLASS_NAMES.length; i++)
{
index = SORTING_CLASS_NAMES[i].equals(className) ? i : -1;
}
return index;
}
}
}
/**
* Articles preferences panel.
*/
private static class ArticlesPanel extends PreferencesPanel
{
private int oldBriefSentences;
private int oldBriefMaxLength;
/**
* Creates articles panel.
*
* @param aPrefs user preferences.
* @param aTrigger trigger.
*/
public ArticlesPanel(final UserPreferences aPrefs, ValueModel aTrigger)
{
super(aTrigger);
JCheckBox chAutoExpandMini = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.articles.autoexpand"), aPrefs,
UserPreferences.PROP_AUTO_EXPAND_MINI, trigger);
BBFormBuilder builder = new BBFormBuilder("7dlu, p, 4dlu, 15dlu, 17dlu, p:grow", this);
builder.setDefaultDialogBorder();
builder.appendSeparator(Strings.message("userprefs.tab.articles.what.to.display.in.each.mode"));
builder.setLeadingColumnOffset(1);
builder.append(buildViewModesPanel(aPrefs), 5);
builder.setLeadingColumnOffset(0);
builder.appendSeparator(Strings.message("userprefs.tab.articles.brief.mode"));
builder.setLeadingColumnOffset(1);
builder.append(buildBriefModePanel(aPrefs, aTrigger), 5);
builder.setLeadingColumnOffset(0);
builder.appendSeparator(Strings.message("userprefs.tab.articles.pagination"));
builder.setLeadingColumnOffset(1);
builder.append(buildPaginationPanel(aPrefs, aTrigger), 5);
builder.setLeadingColumnOffset(0);
builder.appendSeparator(Strings.message("userprefs.options"));
builder.setLeadingColumnOffset(1);
builder.append(chAutoExpandMini, 5);
// This block must go after the initialization of the other controls because it depends
// on the values being propagated upon triggering.
oldBriefSentences = aPrefs.getBriefSentences();
oldBriefMaxLength = aPrefs.getBriefMaxLength();
aTrigger.addValueChangeListener(new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
if (Boolean.TRUE.equals(evt.getNewValue()) &&
(oldBriefMaxLength != aPrefs.getBriefMaxLength() ||
oldBriefSentences != aPrefs.getBriefSentences()))
{
oldBriefSentences = aPrefs.getBriefSentences();
oldBriefMaxLength = aPrefs.getBriefMaxLength();
MainFrame frame = GlobalController.SINGLETON.getMainFrame();
IFeedDisplay feedView = frame.getArticlesListPanel().getFeedView();
feedView.repaintIfInMode(true);
}
}
});
}
private Component buildBriefModePanel(UserPreferences prefs, ValueModel trigger)
{
SpinnerModelAdapter mdlSentences = new SpinnerModelAdapter(new BoundedRangeAdapter(
new BufferedValueModel(new PropertyAdapter(prefs, UserPreferences.PROP_BRIEF_SENTENCES),
trigger), 0, 1, 10));
JSpinner spSentences = new JSpinner(mdlSentences);
SpinnerModelAdapter mdlMaxLength = new SpinnerModelAdapter(new BoundedRangeAdapter(
new BufferedValueModel(new PropertyAdapter(prefs, UserPreferences.PROP_BRIEF_MAX_LENGTH),
trigger), 0, 100, 1000));
mdlMaxLength.setStepSize(100);
JSpinner spMaxLength = new JSpinner(mdlMaxLength);
BBFormBuilder builder = new BBFormBuilder("p, 4dlu, 25dlu, 4dlu, p, 4dlu, 35dlu, 4dlu, p");
String[] slices = Strings.slices("userprefs.tab.articles.brief.mode.settings");
builder.append(slices[0], 1);
builder.append(spSentences);
builder.append(slices[1], 1);
builder.append(spMaxLength);
builder.append(slices[2], 1);
return builder.getPanel();
}
private Component buildPaginationPanel(UserPreferences prefs, ValueModel trigger)
{
SpinnerModelAdapter mdlPageSize = new SpinnerModelAdapter(new BoundedRangeAdapter(
new BufferedValueModel(new PropertyAdapter(prefs, UserPreferences.PROP_PAGE_SIZE),
trigger), 0, 10, 200));
JSpinner spPageSize = new JSpinner(mdlPageSize);
SpinnerNumberModel model = (SpinnerNumberModel)spPageSize.getModel();
model.setStepSize(10);
BBFormBuilder builder = new BBFormBuilder("p, 4dlu, 25dlu, 4dlu, p");
String[] slices = Strings.slices("userprefs.tab.articles.pagination.settings");
builder.append(slices[0], 1);
builder.append(spPageSize);
builder.append(slices[1], 1);
return builder.getPanel();
}
/**
* Creates view modes panel.
*
* @param aPrefs preferences.
*
* @return panel.
*/
private Component buildViewModesPanel(UserPreferences aPrefs)
{
JCheckBox[][] checks = createCheckBoxes(aPrefs.getViewModePreferences());
BBFormBuilder builder = new BBFormBuilder(
"p, 4dlu, center:35dlu, 4dlu, center:35dlu, 4dlu, center:35dlu");
builder.append("", 1);
builder.append(Strings.message("viewmode.minimal"), 1);
builder.append(Strings.message("viewmode.brief"), 1);
builder.append(Strings.message("viewmode.full"), 1);
String titles[] = {
Strings.message("article.header.author"),
Strings.message("article.header.categories"),
Strings.message("article.header.date"),
Strings.message("article.header.time"),
Strings.message("article.header.pin"),
Strings.message("article.header.colorCode"),
Strings.message("article.header.url")
};
boolean enableness[] = {
true,
true,
true,
true,
true,
SentimentsFeature.isAvailable(),
true
};
for (int i = 0; i < titles.length; i++)
{
boolean en = enableness[i];
String title = titles[i];
builder.append(title, 1).setEnabled(en);
builder.append(checks[i][0], 1);
builder.append(checks[i][1], 1);
builder.append(checks[i][2], 1);
for (int j = 0; j < 3; j++) checks[i][j].setEnabled(en);
}
// Make time checkboxes dependent on date
int date = 3;
int time = 4;
for (int i = 0; i < 3; i++) UifUtilities.setDependency(checks[date][i], checks[time][i]);
return builder.getPanel();
}
/**
* Creates check boxes to use for the form.
*
* @param aPrefs preferences.
*
* @return check boxes.
*/
private JCheckBox[][] createCheckBoxes(ViewModePreferences aPrefs)
{
ViewModePreferences.ViewModeBean[] modes = new ViewModePreferences.ViewModeBean[]
{
new ViewModePreferences.ViewModeBean(aPrefs, IFeedDisplayConstants.MODE_MINIMAL),
new ViewModePreferences.ViewModeBean(aPrefs, IFeedDisplayConstants.MODE_BRIEF),
new ViewModePreferences.ViewModeBean(aPrefs, IFeedDisplayConstants.MODE_FULL)
};
String[] properties = {
ViewModePreferences.AUTHOR_VISIBLE,
ViewModePreferences.CATEGORIES_VISIBLE,
ViewModePreferences.DATE_VISIBLE,
ViewModePreferences.TIME_VISIBLE,
ViewModePreferences.PIN_VISIBLE,
ViewModePreferences.COLOR_CODE_VISIBLE,
ViewModePreferences.URL_VISIBLE
};
JCheckBox[][] checks = new JCheckBox[properties.length][modes.length];
for (int i = 0; i < properties.length; i++)
{
String property = properties[i];
for (int j = 0; j < modes.length; j++)
{
ViewModePreferences.ViewModeBean mode = modes[j];
ButtonModel model = new ToggleButtonAdapter(new BufferedValueModel(
new PropertyAdapter(mode, property), trigger));
checks[i][j] = ComponentsFactory.createCheckBox(null, model);
}
}
return checks;
}
}
/**
* Global notifications preferences.
*/
private static class NotificationsPanel extends PreferencesPanel
{
/**
* Creates panel.
*
* @param prefs preferences.
* @param trigger trigger.
*/
public NotificationsPanel(UserPreferences prefs, ValueModel trigger)
{
super(trigger);
JComponent wording = ComponentsFactory.createWrappedMultilineLabel(
Strings.message("userprefs.tab.notifications.wording"));
JCheckBox chEnabled;
if (NotificationArea.isSupported())
{
chEnabled = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.notifications.enabled"),
prefs, UserPreferences.PROP_NOTIFICATIONS_ENABLED, this.trigger);
} else
{
chEnabled = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.notifications.enabled"));
chEnabled.setEnabled(NotificationArea.isSupported());
}
JCheckBox chSoundOnNoUnread = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.notifications.sound.no.unread"),
prefs, UserPreferences.PROP_SOUND_ON_NO_UNREAD, this.trigger);
JCheckBox chSoundOnNewArticles = ComponentsFactory.createCheckBox(
Strings.message("userprefs.tab.notifications.sound.new.article"),
prefs, UserPreferences.PROP_SOUND_ON_NEW_ARTICLES, this.trigger);
BBFormBuilder builder = new BBFormBuilder("7dlu, p:grow", this);
builder.setDefaultDialogBorder();
builder.append(wording, 2);
builder.setLeadingColumnOffset(1);
builder.appendUnrelatedComponentsGapRow(2);
builder.append(chEnabled);
builder.appendRelatedComponentsGapRow(2);
builder.appendSeparator(Strings.message("userprefs.tab.notifications.sound"));
builder.append(chSoundOnNoUnread);
builder.append(chSoundOnNewArticles);
}
}
/** Converts masked bits into states. */
private static class FilterClassesAdapter extends ColorComboBoxAdapter
{
private static final Color[] COLORS = {
Color.BLACK,
Color.decode("#FF344E"), Color.decode("#FFAF2F"), Color.decode("#F6E12A"), Color.decode("#99DE26"),
Color.decode("#24A4E0"), Color.decode("#F361B4"), Color.decode("#9E9E9E"),
null };
/**
* Creates combo-box adapter for model.
*
* @param aModel model to adopt.
*/
public FilterClassesAdapter(ValueModel aModel)
{
super(COLORS, aModel);
}
}
/**
* Reads and writes data directly to <code>ChannelDisplayModeManager</code>.
*/
private static class DisplayModeModel extends AbstractValueModel
{
private int channelClass;
private AbstractDisplayModeManager feedDMM;
private final AtomicBoolean changedFlag;
/**
* Creates model adapter for the given class.
*
* @param cl class.
* @param dmm display mode manager.
* @param changedFlag changed flag.
*/
public DisplayModeModel(int cl, AbstractDisplayModeManager dmm,
AtomicBoolean changedFlag)
{
channelClass = cl;
feedDMM = dmm;
this.changedFlag = changedFlag;
}
/**
* Takes the value of color from the display mode manager.
*
* @return color.
*/
public Object getValue()
{
return feedDMM.getColor(channelClass);
}
/**
* Sets the value of color to the display mode manager.
*
* @param object color.
*/
public void setValue(Object object)
{
Color oldColor = feedDMM.getColor(channelClass);
Color newColor = (Color)object;
if ((oldColor == null && newColor != null) ||
(oldColor != null && newColor == null)) changedFlag.set(true);
feedDMM.setColor(channelClass, (Color)object);
}
}
/**
* Creates a filter combo-box for a given class.
*
* @param cl class.
* @param dmm display mode manager to operate.
* @param changed changed atomic flag.
* @param trigger trigger to use for data commit.
*
* @return combo-box.
*/
private static JComboBox createFilterComboBox(int cl, AbstractDisplayModeManager dmm, AtomicBoolean changed,
ValueModel trigger)
{
JComboBox box = new JComboBox(new FilterClassesAdapter(new BufferedValueModel(
new DisplayModeModel(cl, dmm, changed),
trigger)));
box.setRenderer(colorListCellRenderer);
return box;
}
}