Quantcast

Joining Freeplane and Editing the UI for the Preferences Popup

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Joining Freeplane and Editing the UI for the Preferences Popup

theworldbright
Hello, Freeplane developers. I am currently searching for a open source project to build my skills with. I have been learning Java for around five years now but I have never participated in any open source project. I hope I will be welcome here.

For starters I thought I would change the UI for the preferences window since the JButton’s on the left side did not look that appealing.

Proposed UI Change

Here is the code for OptionPanel.java. Basically I switched out the JButtons for a JTabbedPane and made adjustments to make everything work. The last opened tab is saved on close (through a somewhat hacky method). There is a possibility that the manipulating of the selectedPanel string during opening/closing will cause some problems, as I never got my head around why the panel string came with an appended "OptionPanel." to the start. Furthermore, without the tabStringToIndexMap.containsKey(selectedPanel), there seems to be an annoying null pointer exception that occurs because of the spelling differences between "behaviour" and "behavior."

package org.freeplane.core.resources.components;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultMutableTreeNode;

import org.apache.commons.lang.StringUtils;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.resources.components.IValidator.ValidationResult;
import org.freeplane.core.ui.MenuBuilder;
import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.mode.Controller;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.factories.ButtonBarFactory;
import com.jgoodies.forms.layout.FormLayout;

public class OptionPanel {
        public interface IOptionPanelFeedback {
                void writeProperties(Properties props);
        }

        static final String PREFERENCE_STORAGE_PROPERTY = "OptionPanel_Window_Properties";
        private Vector<IPropertyControl> controls;
        final private IOptionPanelFeedback feedback;
        final private HashMap<String, Integer> tabStringToIndexMap = new HashMap<String, Integer>();
        final private HashMap<Integer, String> tabIndexToStringMap = new HashMap<Integer, String>();
        private String selectedPanel;
        final private JDialog topDialog;

        /**
         * @throws IOException
         */
        public OptionPanel(final JDialog d, final IOptionPanelFeedback feedback) {
                super();
                topDialog = d;
                this.feedback = feedback;
                new OptionPanelBuilder();
        }

        /**
         * This method builds the preferences panel.
         * A list of IPropertyControl is iterated through and
         * if the IPropertyControl is an instance of TabProperty,
         * it creates a new "tab" that can be clicked to reveal the appropriate panel.
         * If the previous selected tab was saved on close,
         * the appropriate tab is reopened.
         *
         * @param controlsTree  This is the data that needs to be built
         */
        public void buildPanel(final DefaultMutableTreeNode controlsTree) {
                final JPanel centralPanel = new JPanel();
                centralPanel.setLayout(new GridLayout(1, 1));
                final JTabbedPane tabbedPane = new JTabbedPane();
                tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
                FormLayout bottomLayout = null;
                DefaultFormBuilder bottomBuilder = null;
                initControls(controlsTree);
                final Iterator<IPropertyControl> iterator = controls.iterator();
                int tabIndex = 0;
                while (iterator.hasNext()) {
                        final IPropertyControl control = iterator.next();
                        if (control instanceof TabProperty) {
                                final TabProperty newTab = (TabProperty) control;
                                bottomLayout = new FormLayout(newTab.getDescription(), "");
                                bottomBuilder = new DefaultFormBuilder(bottomLayout);
                                bottomBuilder.setDefaultDialogBorder();
                                final JScrollPane bottomComponent = new JScrollPane(bottomBuilder.getPanel());
                                UITools.setScrollbarIncrement(bottomComponent);
                                final String tabName = TextUtils.getOptionalText(newTab.getLabel());
                                tabStringToIndexMap.put(tabName, tabIndex);
                                tabIndexToStringMap.put(tabIndex, tabName);
                                tabbedPane.addTab(tabName, bottomComponent);
                                tabIndex++;
                        }
                        else {
                                control.layout(bottomBuilder);
                        }
                }
                tabbedPane.addChangeListener(new ChangeListener() {
                        public void stateChanged(final ChangeEvent event) {
                                final JTabbedPane c = (JTabbedPane) event.getSource();
                                selectedPanel = tabIndexToStringMap.get(c.getSelectedIndex());
                        }
                });
                centralPanel.add(tabbedPane);
                if (selectedPanel != null && tabStringToIndexMap.containsKey(selectedPanel)) {
                        // Without the containsKey call the loading of the tab "behaviour"/"behavior" gives a nullpointer exception
                        tabbedPane.setSelectedIndex(tabStringToIndexMap.get(selectedPanel));
                }
                topDialog.getContentPane().add(centralPanel, BorderLayout.CENTER);
                final JButton cancelButton = new JButton();
                MenuBuilder.setLabelAndMnemonic(cancelButton, TextUtils.getRawText("cancel"));
                cancelButton.addActionListener(new ActionListener() {
                        public void actionPerformed(final ActionEvent arg0) {
                                closeWindow();
                        }
                });
                final JButton okButton = new JButton();
                MenuBuilder.setLabelAndMnemonic(okButton, TextUtils.getRawText("ok"));
                okButton.addActionListener(new ActionListener() {
                        public void actionPerformed(final ActionEvent arg0) {
                                if (validate()) {
                                        closeWindow();
                                        feedback.writeProperties(getOptionProperties());
                                }
                        }
                });
                topDialog.getRootPane().setDefaultButton(okButton);
                topDialog.getContentPane().add(ButtonBarFactory.buildOKCancelBar(cancelButton, okButton), BorderLayout.SOUTH);
        }

        private boolean validate() {
                final Properties properties = getOptionProperties();
                final ValidationResult result = new ValidationResult();
                for (final IValidator validator : Controller.getCurrentController().getOptionValidators()) {
                        result.add(validator.validate(properties));
                }
                if (!result.isValid()) {
                        UITools.errorMessage(formatErrors("OptionPanel.validation_error", result.getErrors()));
                        LogUtils.severe(result.toString());
                }
                else if (result.hasWarnings()) {
                        UITools.informationMessage(formatErrors("OptionPanel.validation_warning", result.getWarnings()));
                        LogUtils.warn(result.toString());
                }
                return result.isValid();
        }

        private String formatErrors(final String key, final ArrayList<String> errors) {
                // TextUtils.format() xml escapes the format arguments - we don't want that
                final MessageFormat formatter = new MessageFormat(TextUtils.getText(key));
                return formatter.format(new Object[] { StringUtils.join(errors.iterator(), "<br>") });
        }

        @SuppressWarnings("unchecked")
        /**
         * This is where the controls are added to the "controls" IProperty Vector
         * @param controlsTree This is the tree that gets built
         */
        private void initControls(final DefaultMutableTreeNode controlsTree) {
                controls = new Vector<IPropertyControl>();
                for (final Enumeration<DefaultMutableTreeNode> i = controlsTree.preorderEnumeration(); i.hasMoreElements();) {
                        final IPropertyControlCreator creator = (IPropertyControlCreator) i.nextElement().getUserObject();
                        if (creator == null) {
                                continue;
                        }
                        final IPropertyControl control = creator.createControl();
                        controls.add(control);
                }
        }

        public void closeWindow() {
                final OptionPanelWindowConfigurationStorage storage = new OptionPanelWindowConfigurationStorage();
                storage.setPanel("OptionPanel." + selectedPanel);
                storage.storeDialogPositions(topDialog, OptionPanel.PREFERENCE_STORAGE_PROPERTY);
                topDialog.setVisible(false);
                topDialog.dispose();
        }

        private Properties getOptionProperties() {
                final Properties p = new Properties();
                for (final IPropertyControl control : controls) {
                        if (control instanceof PropertyBean) {
                                final PropertyBean bean = (PropertyBean) control;
                                final String value = bean.getValue();
                                if (value != null) {
                                        p.setProperty(bean.getName(), value);
                                }
                        }
                }
                return p;
        }

        public void setProperties() {
                for (final IPropertyControl control : controls) {
                        if (control instanceof PropertyBean) {
                                final PropertyBean bean = (PropertyBean) control;
                                final String name = bean.getName();
                                final String value = ResourceController.getResourceController().getProperty(name);
                                bean.setValue(value);
                        }
                }
        }

        void setSelectedPanel(final String panel) {
                if (panel.startsWith("OptionPanel.")) {
                        selectedPanel = panel.substring(12); // 12 Letters in OptionPanel.
                }
        }
}
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Joining Freeplane and Editing the UI for the Preferences Popup

ldmpub
Hi,

Nice to ear that new devloppers are joining the Freeplane team. Welcome!
I'm also trying to learn Java but yet I'm still far form contributing to the projec, I will appreciaed if you can share your experience.

I have no opinion on your technical suggestion but I hope real devloppers will answer you soon.

regads,

Le 26/01/2014 14:48, worldbright [via Freeplane Developer] a écrit :
Hello, Freeplane developers. I am currently searching for a open source project to build my skills with. I have been learning Java for around five years now but I have never participated in any open source project. I hope I will be welcome here.

For starters I thought I would change the UI for the preferences window since the JButton’s on the left side did not look that appealing.

Proposed UI Change

Here is the code for OptionPanel.java. Basically I switched out the JButtons for a JTabbedPane and made adjustments to make everything work. The last opened tab is saved on close (through a somewhat hacky method). There is a possibility that the manipulating of the selectedPanel string during opening/closing will cause some problems, as I never got my head around why the panel string came with an appended "OptionPanel." to the start. Furthermore, without the tabStringToIndexMap.containsKey(selectedPanel), there seems to be an annoying null pointer exception that occurs because of the spelling differences between "behaviour" and "behavior."

package org.freeplane.core.resources.components;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultMutableTreeNode;

import org.apache.commons.lang.StringUtils;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.resources.components.IValidator.ValidationResult;
import org.freeplane.core.ui.MenuBuilder;
import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.mode.Controller;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.factories.ButtonBarFactory;
import com.jgoodies.forms.layout.FormLayout;

public class OptionPanel {
        public interface IOptionPanelFeedback {
                void writeProperties(Properties props);
        }

        static final String PREFERENCE_STORAGE_PROPERTY = "OptionPanel_Window_Properties";
        private Vector<IPropertyControl> controls;
        final private IOptionPanelFeedback feedback;
        final private HashMap<String, Integer> tabStringToIndexMap = new HashMap<String, Integer>();
        final private HashMap<Integer, String> tabIndexToStringMap = new HashMap<Integer, String>();
        private String selectedPanel;
        final private JDialog topDialog;

        /**
         * @throws IOException
         */
        public OptionPanel(final JDialog d, final IOptionPanelFeedback feedback) {
                super();
                topDialog = d;
                this.feedback = feedback;
                new OptionPanelBuilder();
        }

        /**
         * This method builds the preferences panel.
         * A list of IPropertyControl is iterated through and
         * if the IPropertyControl is an instance of TabProperty,
         * it creates a new "tab" that can be clicked to reveal the appropriate panel.
         * If the previous selected tab was saved on close,
         * the appropriate tab is reopened.
         *
         * @param controlsTree  This is the data that needs to be built
         */
        public void buildPanel(final DefaultMutableTreeNode controlsTree) {
                final JPanel centralPanel = new JPanel();
                centralPanel.setLayout(new GridLayout(1, 1));
                final JTabbedPane tabbedPane = new JTabbedPane();
                tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
                FormLayout bottomLayout = null;
                DefaultFormBuilder bottomBuilder = null;
                initControls(controlsTree);
                final Iterator<IPropertyControl> iterator = controls.iterator();
                int tabIndex = 0;
                while (iterator.hasNext()) {
                        final IPropertyControl control = iterator.next();
                        if (control instanceof TabProperty) {
                                final TabProperty newTab = (TabProperty) control;
                                bottomLayout = new FormLayout(newTab.getDescription(), "");
                                bottomBuilder = new DefaultFormBuilder(bottomLayout);
                                bottomBuilder.setDefaultDialogBorder();
                                final JScrollPane bottomComponent = new JScrollPane(bottomBuilder.getPanel());
                                UITools.setScrollbarIncrement(bottomComponent);
                                final String tabName = TextUtils.getOptionalText(newTab.getLabel());
                                tabStringToIndexMap.put(tabName, tabIndex);
                                tabIndexToStringMap.put(tabIndex, tabName);
                                tabbedPane.addTab(tabName, bottomComponent);
                                tabIndex++;
                        }
                        else {
                                control.layout(bottomBuilder);
                        }
                }
                tabbedPane.addChangeListener(new ChangeListener() {
                        public void stateChanged(final ChangeEvent event) {
                                final JTabbedPane c = (JTabbedPane) event.getSource();
                                selectedPanel = tabIndexToStringMap.get(c.getSelectedIndex());
                        }
                });
                centralPanel.add(tabbedPane);
                if (selectedPanel != null && tabStringToIndexMap.containsKey(selectedPanel)) {
                        // Without the containsKey call the loading of the tab "behaviour"/"behavior" gives a nullpointer exception
                        tabbedPane.setSelectedIndex(tabStringToIndexMap.get(selectedPanel));
                }
                topDialog.getContentPane().add(centralPanel, BorderLayout.CENTER);
                final JButton cancelButton = new JButton();
                MenuBuilder.setLabelAndMnemonic(cancelButton, TextUtils.getRawText("cancel"));
                cancelButton.addActionListener(new ActionListener() {
                        public void actionPerformed(final ActionEvent arg0) {
                                closeWindow();
                        }
                });
                final JButton okButton = new JButton();
                MenuBuilder.setLabelAndMnemonic(okButton, TextUtils.getRawText("ok"));
                okButton.addActionListener(new ActionListener() {
                        public void actionPerformed(final ActionEvent arg0) {
                                if (validate()) {
                                        closeWindow();
                                        feedback.writeProperties(getOptionProperties());
                                }
                        }
                });
                topDialog.getRootPane().setDefaultButton(okButton);
                topDialog.getContentPane().add(ButtonBarFactory.buildOKCancelBar(cancelButton, okButton), BorderLayout.SOUTH);
        }

        private boolean validate() {
                final Properties properties = getOptionProperties();
                final ValidationResult result = new ValidationResult();
                for (final IValidator validator : Controller.getCurrentController().getOptionValidators()) {
                        result.add(validator.validate(properties));
                }
                if (!result.isValid()) {
                        UITools.errorMessage(formatErrors("OptionPanel.validation_error", result.getErrors()));
                        LogUtils.severe(result.toString());
                }
                else if (result.hasWarnings()) {
                        UITools.informationMessage(formatErrors("OptionPanel.validation_warning", result.getWarnings()));
                        LogUtils.warn(result.toString());
                }
                return result.isValid();
        }

        private String formatErrors(final String key, final ArrayList<String> errors) {
                // TextUtils.format() xml escapes the format arguments - we don't want that
                final MessageFormat formatter = new MessageFormat(TextUtils.getText(key));
                return formatter.format(new Object[] { StringUtils.join(errors.iterator(), "<br>") });
        }

        @SuppressWarnings("unchecked")
        /**
         * This is where the controls are added to the "controls" IProperty Vector
         * @param controlsTree This is the tree that gets built
         */
        private void initControls(final DefaultMutableTreeNode controlsTree) {
                controls = new Vector<IPropertyControl>();
                for (final Enumeration<DefaultMutableTreeNode> i = controlsTree.preorderEnumeration(); i.hasMoreElements();) {
                        final IPropertyControlCreator creator = (IPropertyControlCreator) i.nextElement().getUserObject();
                        if (creator == null) {
                                continue;
                        }
                        final IPropertyControl control = creator.createControl();
                        controls.add(control);
                }
        }

        public void closeWindow() {
                final OptionPanelWindowConfigurationStorage storage = new OptionPanelWindowConfigurationStorage();
                storage.setPanel("OptionPanel." + selectedPanel);
                storage.storeDialogPositions(topDialog, OptionPanel.PREFERENCE_STORAGE_PROPERTY);
                topDialog.setVisible(false);
                topDialog.dispose();
        }

        private Properties getOptionProperties() {
                final Properties p = new Properties();
                for (final IPropertyControl control : controls) {
                        if (control instanceof PropertyBean) {
                                final PropertyBean bean = (PropertyBean) control;
                                final String value = bean.getValue();
                                if (value != null) {
                                        p.setProperty(bean.getName(), value);
                                }
                        }
                }
                return p;
        }

        public void setProperties() {
                for (final IPropertyControl control : controls) {
                        if (control instanceof PropertyBean) {
                                final PropertyBean bean = (PropertyBean) control;
                                final String name = bean.getName();
                                final String value = ResourceController.getResourceController().getProperty(name);
                                bean.setValue(value);
                        }
                }
        }

        void setSelectedPanel(final String panel) {
                if (panel.startsWith("OptionPanel.")) {
                        selectedPanel = panel.substring(12); // 12 Letters in OptionPanel.
                }
        }
}


If you reply to this email, your message will be added to the discussion below:
http://freeplane-developer.996965.n3.nabble.com/Joining-Freeplane-and-Editing-the-UI-for-the-Preferences-Popup-tp95.html
To start a new topic under Freeplane Developer, email [hidden email]
To unsubscribe from Freeplane Developer, click here.
NAML

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Joining Freeplane and Editing the UI for the Preferences Popup

Dimitry Polivaev
Administrator
In reply to this post by theworldbright
Hello worldbright,

I am delighted by the  new option panel layout and I committed it to our master development branch at https://github.com/freeplane/freeplane . I did very moderate changes in your code, they are committed separately as https://github.com/freeplane/freeplane/commit/cbc6f3ef6fd64f24bb883457b513386f5393bde5

So your changes are part of the Freeplane 1.4.x (because 1.3.x has a feature freeze now).

I would really appreciate if we could discuss the matter and the manner of your next contributions.

1. The matter.

In my opinion a  user post https://sourceforge.net/apps/phpbb/freeplane/viewtopic.php?f=1&t=780#p3975 perfectly sums up what features should come next. There are (ordered from  easiest to the most complex)

- Background images
- More kinds of lines (dotted, stripped...)
- More kinds of nodes (squared, circular...)
- Clone nodes, like in Freemind: A child node will have a lot of parents

If you have any own ideas and preferences free to follow them, just communicate them first so that they can be discussed and you can get early feedback.

2. The manner.

We heavily use git and particularly github for our source code management. It would be easier for me if you could create a github account and fork our repository.  This way you can push all your changes to this repository and submit a so-called "pull request" from your github repository web page.

We also use Skype and TeamViewer for all live discussions. I shall send you my Skype account name in a private mail.

So feel welcome and ask any questions you like to be sure you are on a right way and we are moving to the right direction together.

Best regards,
Dimitry
Loading...