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: PostEditorAdv.java,v 1.10 2007/03/23 12:40:53 spyromus Exp $ // package com.salas.bb.remixfeeds.editor; import com.jgoodies.binding.beans.PropertyAdapter; import com.jgoodies.binding.list.ArrayListModel; import com.jgoodies.binding.value.ValueModel; import com.jgoodies.forms.builder.ButtonBarBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.uif.AbstractDialog; import com.salas.bb.remixfeeds.api.WeblogPost; import com.salas.bb.remixfeeds.prefs.TargetBlog; import com.salas.bb.utils.i18n.Strings; import com.salas.bb.utils.uif.BBFormBuilder; import com.salas.bb.utils.uif.CheckBoxList; import com.toedter.calendar.JDateChooser; import com.toedter.calendar.JTextFieldDateEditor; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.*; import java.util.List; /** * Advanced version of the post editor. */ public class PostEditorAdv extends AbstractPostEditor { protected JButton btnPostAndContinue; protected JEditorPane tfExcerpt; protected JCheckBox chAllowComments; protected JCheckBox chAllowTrackbacks; private JDateChooser pkDate; private JLabel lbDate; private TargetBlog[] targetBlogs; private JCheckBox[] chTargetBlog; private CategoriesLabel[] pcCategories; private ArrayListModel[] categories; private TargetBlog.Category[][] category; private SyntheticCategoryProperty[] categoryProperties; /** * Creates advanced post editor. * * @param frame frame. * @param richText <code>TRUE</code> to show rich text editor. */ public PostEditorAdv(Frame frame, boolean richText) { super(frame, richText); btnPostAndContinue = new JButton(new PostToBlogAction(true)); tfExcerpt = new JEditorPane(); chAllowComments = new JCheckBox(Strings.message("ptb.editor.allow.comments"), true); chAllowTrackbacks = new JCheckBox(Strings.message("ptb.editor.allow.trackbacks"), true); } /** * Sets the list of target blogs. Initializes their category lists and creates checkboxes for the * layout. This method must be called before opening the dialog. * * @param targetBlogs the list of target blogs. * @param selected the list of selected blogs. */ public void setTargetBlogs(TargetBlog[] targetBlogs, TargetBlog[] selected) { int blogs = targetBlogs.length; this.targetBlogs = targetBlogs; java.util.List<TargetBlog> selBlogs = Arrays.asList(selected); // Initialize the arrays pcCategories = new CategoriesLabel[blogs]; chTargetBlog = new JCheckBox[blogs]; categories = new ArrayListModel[blogs]; category = new TargetBlog.Category[blogs][]; categoryProperties = new SyntheticCategoryProperty[blogs]; for (int i = 0; i < targetBlogs.length; i++) { categoryProperties[i] = new SyntheticCategoryProperty(i); TargetBlog blog = targetBlogs[i]; initCategories(blog, i, new TargetBlog.Category[] { blog.getDefaultCategory() }); PropertyAdapter adapter = new PropertyAdapter(categoryProperties[i], SyntheticCategoryProperty.PROP, true); pcCategories[i] = new CategoriesLabel(categories[i], adapter); chTargetBlog[i] = new JCheckBox(blog.getTitle()); if (selBlogs.contains(blog)) { chTargetBlog[i].setSelected(true); setDraft(blog.isDraft()); } } updateTitle(selected); } /** * Initializes the list of categories for a given blog. If the categories aren't * available, loads them and then selects the given default. * * @param blog blog to initialize. * @param index index of the blog to locate the correct categories / category object. * @param cat category to select. */ private void initCategories(final TargetBlog blog, final int index, final TargetBlog.Category[] cat) { // If there are no categories in the blog object yet, let it be the default category. // We'll start loading them right away. TargetBlog.Category[] cats = blog.getCategories(); boolean noCategories = cats.length == 0; if (noCategories) cats = cat; // Populate the list of categories with defaults ArrayListModel categories = this.categories[index]; if (categories == null) { categories = new ArrayListModel(); this.categories[index] = categories; } categories.clear(); categories.addAll(Arrays.asList(cats)); // Select the category setCategories(index, cat); if (noCategories) { // Load categories for this blog blog.loadCategories(new Runnable() { public void run() { initCategories(blog, index, category[index]); } }); } } /** * Sets the categories for a given blog. * * @param index blog index. * @param cat categories. */ private void setCategories(int index, TargetBlog.Category[] cat) { cat = findEqualCategories(categories[index], cat); // Set and fire TargetBlog.Category[] old = category[index]; category[index] = cat; categoryProperties[index].fireChange(old, cat); } /** * Scans the list of given categories and returns the one which is like the given. * * @param categories categories. * @param cat category to find among these in the list. * * @return a category or <code>NULL</code>. */ protected static TargetBlog.Category[] findEqualCategories(ArrayListModel categories, TargetBlog.Category[] cat) { List<TargetBlog.Category> newCats = new ArrayList<TargetBlog.Category>(); if (cat != null) { List<TargetBlog.Category> cats = Arrays.asList(cat); for (Object o : categories) { TargetBlog.Category c = (TargetBlog.Category) o; if (c != null && cats.contains(c)) newCats.add(c); } } return newCats.toArray(new TargetBlog.Category[newCats.size()]); } /** * The hook to add more buttons. * * @param builder builder. */ protected void addCustomButtons(ButtonBarBuilder builder) { builder.addFixed(btnPostAndContinue); builder.addRelatedGap(); } /** * The hook to add more custom panels after the main part. * * @param builder builder. */ protected void addCustomPanels(BBFormBuilder builder) { builder.appendRelatedComponentsGapRow(2); builder.appendRow("50dlu"); builder.append(new JScrollPane(tfExcerpt), 2, CellConstraints.FILL, CellConstraints.FILL); builder.append(buildOptionsPanel(), 2); } /** * Builds the options panel for below the excerpt. * * @return options panel. */ private Component buildOptionsPanel() { BBFormBuilder builder = new BBFormBuilder("p"); for (int i = 0; i < targetBlogs.length; i++) { BBFormBuilder b = new BBFormBuilder("min(p;100dlu), 2dlu, min(p;100dlu)"); b.append(chTargetBlog[i], pcCategories[i]); builder.append(b.getPanel()); } JPanel pnlBlogs = builder.getPanel(); builder = new BBFormBuilder("p"); builder.append(chDraft); builder.append(chAllowComments); builder.append(chAllowTrackbacks); JPanel pnlOptions = builder.getPanel(); builder = new BBFormBuilder(new FormLayout("p, 4dlu:grow, p", "top:p")); builder.append(pnlBlogs, pnlOptions); return builder.getPanel(); } /** * The hook to add custom control elements to the toolbar. * * @param builder builder. */ protected void addCustomControls(BBFormBuilder builder) { pkDate = new JDateChooser(new JTextFieldDateEditor("MM/dd/yyyy", "##/##/####", '_')); pkDate.setDate(new Date()); Component[] cmps = pkDate.getComponents(); for (Component cmp : cmps) if (cmp instanceof JButton) pkDate.remove(cmp); lbDate = new JLabel(Strings.message("ptb.editor.publication.date")); BBFormBuilder b = new BBFormBuilder("p, 2dlu, p"); b.append(lbDate, pkDate); builder.append(b.getPanel()); } /** * Returns the list of indices of all selected target blogs. * * @return list of indices. */ public java.util.List<Integer> getSelectedBlogIndices() { java.util.List<Integer> sel = new ArrayList<Integer>(); for (int i = 0; i < chTargetBlog.length; i++) { if (chTargetBlog[i].isSelected()) sel.add(i); } return sel; } /** * Adds more specific properties before sending. * * @param post post. */ protected void addProperties(WeblogPost post) { post.excerpt = tfExcerpt.getText(); post.allowComments = chAllowComments.isSelected(); post.allowTrackbacks = chAllowTrackbacks.isSelected(); post.dateCreated = pkDate.getDate(); } /** * Returns a blog at a given index. * * @param i index. * * @return blog. */ protected TargetBlog getBlogAt(int i) { return targetBlogs[i]; } /** * Returns the list of categories selected for the given blog. * * @param blogIndex index of blog in list. * * @return categories. */ protected java.util.List<TargetBlog.Category> getCategoriesForBlog(int blogIndex) { return Arrays.asList(category[blogIndex]); } /** * Enables / disables all controls during posting. * * @param en <code>TRUE</code> to enable. */ protected void enableControls(boolean en) { super.enableControls(en); btnPostAndContinue.setEnabled(en); tfExcerpt.setEnabled(en); chAllowComments.setEnabled(en); chAllowTrackbacks.setEnabled(en); for (int i = 0; i < targetBlogs.length; i++) { chTargetBlog[i].setEnabled(en); pcCategories[i].setEnabled(en); } pkDate.setEnabled(en); lbDate.setEnabled(en); } /** * Synthetic property to watch categories in array. */ public class SyntheticCategoryProperty { public static final String PROP = "value"; private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final int index; public SyntheticCategoryProperty(int index) { this.index = index; } public void addPropertyChangeListener(PropertyChangeListener x) { pcs.addPropertyChangeListener(x); } public void removePropertyChangeListener(PropertyChangeListener x) { pcs.removePropertyChangeListener(x); } public Object getValue() { return category[index]; } public void setValue(Object c) { category[index] = (TargetBlog.Category[]) c; } public void fireChange(TargetBlog.Category[] old, TargetBlog.Category[] cat) { pcs.firePropertyChange(PROP, old, cat); } } // ------------------------------------------------------------------------ // Categories picker // ------------------------------------------------------------------------ /** * The label with auto-updatable categories name. */ private class CategoriesLabel extends JLabel { private final Color NORMAL = Color.BLUE; private final ArrayListModel allCategories; private final ValueModel selCategories; /** * Creates a label for selected categories. * * @param allCategories all categories list model to pass to the picker. * @param selCategories selected categories to display and pass to the picker. */ public CategoriesLabel(ArrayListModel allCategories, ValueModel selCategories) { this.allCategories = allCategories; this.selCategories = selCategories; setForeground(NORMAL); setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); updateLabelText(); enableEvents(AWTEvent.MOUSE_EVENT_MASK); } /** Updates the text according to what is selected. */ private void updateLabelText() { String text; TargetBlog.Category[] cs = (TargetBlog.Category[]) selCategories.getValue(); if (cs == null || cs.length == 0) { text = "Uncategorized"; } else if (cs.length == 1) { text = "category: " + cs[0].name; } else { text = cs.length + " categories"; } setText("(" + text + ")"); } @Override protected void processMouseEvent(MouseEvent e) { if (e.getID() == MouseEvent.MOUSE_CLICKED) { CategoriesPicker dlg = new CategoriesPicker(allCategories, (TargetBlog.Category[]) selCategories.getValue()); dlg.open(); if (!dlg.hasBeenCanceled()) { selCategories.setValue(dlg.getSelectedCategories()); updateLabelText(); } } } } /** * Categories picker. */ private class CategoriesPicker extends AbstractDialog { private CategoriesList lstCategories; /** * Creates categories picker component. * * @param categories categories. * @param selected selected categories. */ public CategoriesPicker(ArrayListModel categories, TargetBlog.Category[] selected) { super(PostEditorAdv.this, Strings.message("ptb.editor.categories")); lstCategories = new CategoriesList(categories, selected); } /** * Builds the content pane. * * @return pane. */ protected JComponent buildContent() { JPanel content = new JPanel(new BorderLayout()); content.add(new JScrollPane(lstCategories), BorderLayout.CENTER); content.add(buildButtonBarWithOKCancel(), BorderLayout.SOUTH); return content; } /** * Returns the selected categories. * * @return selected. */ public TargetBlog.Category[] getSelectedCategories() { return lstCategories.getChecked(); } } /** * Categories list control. */ private static class CategoriesList extends CheckBoxList { /** * Creates and initializes categories list. * * @param categories the list of all known categories. * @param selected the list of selected. */ public CategoriesList(ArrayListModel categories, TargetBlog.Category[] selected) { populateList(categories, selected); check(selected); } /** * Returns the list of checked categories. * * @return checked categories. */ public TargetBlog.Category[] getChecked() { java.util.List<TargetBlog.Category> cats = new ArrayList<TargetBlog.Category>(); ListModel model = getModel(); for (int i = 0; i < model.getSize(); i++) { CategoryCheckBox cb = (CategoryCheckBox) model.getElementAt(i); if (cb.isSelected()) cats.add(cb.getCategory()); } return cats.toArray(new TargetBlog.Category[cats.size()]); } /** * Fetches all categories from the model and shows them. * * @param categories categories. * @param selected selected categories. */ private void populateList(ArrayListModel categories, TargetBlog.Category[] selected) { Vector<CategoryCheckBox> wrappedCategories = new Vector<CategoryCheckBox>(categories.size()); for (Object c : categories) { wrappedCategories.add(new CategoryCheckBox((TargetBlog.Category) c)); } for (TargetBlog.Category c : selected) { TargetBlog.Category eqCat = findEqualCategory(categories, c); if (eqCat == null) wrappedCategories.add(new CategoryCheckBox(c)); } setListData(wrappedCategories); } /** * Sets checkmarks for the given categories. * * @param selected the categories to be checked. */ private void check(TargetBlog.Category[] selected) { java.util.List<TargetBlog.Category> cats = Arrays.asList(selected); ListModel model = getModel(); for (int i = 0; i < model.getSize(); i++) { CategoryCheckBox cb = (CategoryCheckBox) model.getElementAt(i); TargetBlog.Category c = cb.getCategory(); if (cats.contains(c)) cb.setSelected(true); } } /** * Custom checkbox for the category. */ private static class CategoryCheckBox extends JCheckBox { private final TargetBlog.Category cat; /** * Creates a checkbox for the category. * * @param cat category. */ public CategoryCheckBox(TargetBlog.Category cat) { this.cat = cat; setText(cat.name); } /** * Returns the assigned category. * * @return category. */ public TargetBlog.Category getCategory() { return cat; } /** * Returns string representation. * * @return string representation. */ public String toString() { return cat.name; } } } }