Announcement Announcement Module
Collapse
No announcement yet.
Multiple value Property and JLists Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Multiple value Property and JLists

    Hi, In my domain object I have mutiple value property. I want to display a avaiblable values and selected values on my form, how do I do that?



    For example



    Class BusinessUnit {

    String name;

    .......

    List employees;

    }



    I want to have two JLists side-by-side (standard windows available-selected lists) to add (>> ) and remove (<<) the employess for this BusinessUnit. On this form page I would only have these selection lists.



    Thanks



    Amad

  • #2
    Amad,

    I'm pasting a class (a form page) where I do almost exactly this... in the left column is a "entity explorer tree" (it's hierarchical but it could be a list) where the user may select properties [aka columns] as part of building a ad-hoc query. In the right column is a List of columns the user has selected. The tree is bound to the "Entity" property on the backing form object, and the columnViewList model is bound to the "selectedProperties" property. In between the list panes are add and remove buttons. There is also a up/down button as well for manipulating the order of the selected column list.

    Code:
    public class QuerySelectPropertiesForm extends AbstractFormPage &#123;
        public static final String PAGE_ID = "selectProperties";
    
        private EntityExplorerTree entityExplorerTree;
    
        private JList columnViewList;
    
        private JButton addButton;
    
        private JButton removeButton;
    
        private JButton upButton;
    
        private JButton downButton;
    
        public QuerySelectPropertiesForm&#40;FormModel formModel&#41; &#123;
            super&#40;formModel, PAGE_ID&#41;;
        &#125;
    
        protected Entity getEntity&#40;&#41; &#123;
            return &#40;Entity&#41;getValue&#40;Query.TOP_LEVEL_ENTITY_PROPERTY&#41;;
        &#125;
    
        protected ValueModel getEntityHolder&#40;&#41; &#123;
            return getValueModel&#40;Query.TOP_LEVEL_ENTITY_PROPERTY&#41;;
        &#125;
    
        protected JList getList&#40;&#41; &#123;
            return columnViewList;
        &#125;
    
        protected List getListModel&#40;&#41; &#123;
            return &#40;List&#41;getValue&#40;Query.SELECTED_PROPERTIES&#41;;
        &#125;
    
        protected JComponent createControl&#40;&#41; &#123;
            JPanel contentPane = new JPanel&#40;&#41;;
            contentPane.setLayout&#40;new BoxLayout&#40;contentPane, BoxLayout.X_AXIS&#41;&#41;;
            contentPane.add&#40;createTreePanel&#40;&#41;&#41;;
            contentPane.add&#40;createColumnViewPanel&#40;&#41;&#41;;
            return contentPane;
        &#125;
    
        private JComponent createTreePanel&#40;&#41; &#123;
            ComponentFactory factory = getComponentFactory&#40;&#41;;
    
            initializeEntityExplorerTree&#40;&#41;;
            JComponent treeScrollPane = entityExplorerTree.getControl&#40;&#41;;
            treeScrollPane.setPreferredSize&#40;new Dimension&#40;175, 250&#41;&#41;;
    
            JLabel availableProperties = factory.createLabelFor&#40;
                    "label.availableProperties", treeScrollPane&#41;;
    
            JPanel treePane = new JPanel&#40;new BorderLayout&#40;&#41;&#41;;
            treePane.add&#40;availableProperties, BorderLayout.NORTH&#41;;
            treePane.add&#40;treeScrollPane, BorderLayout.CENTER&#41;;
            treePane.add&#40;createAddRemoveButtonPanel&#40;&#41;, BorderLayout.EAST&#41;;
            return treePane;
        &#125;
    
        private void initializeEntityExplorerTree&#40;&#41; &#123;
            entityExplorerTree = new EntityExplorerTree&#40;getEntityHolder&#40;&#41;&#41;;
            entityExplorerTree.addSelectionListener&#40;new TreeSelectionListener&#40;&#41; &#123;
                public void valueChanged&#40;TreeSelectionEvent e&#41; &#123;
                    onEntitySelection&#40;entityExplorerTree.getLastSelectedObject&#40;&#41;&#41;;
                &#125;
            &#125;&#41;;
            addFormValueListener&#40;Query.TOP_LEVEL_ENTITY_PROPERTY, new ValueListener&#40;&#41; &#123;
                public void valueChanged&#40;&#41; &#123;
                    getListModel&#40;&#41;.clear&#40;&#41;;
                &#125;
            &#125;&#41;;
        &#125;
    
        protected void onEntitySelection&#40;PersistentMetadataObject o&#41; &#123;
            if &#40;o != null&#41; &#123;
                addButton.setEnabled&#40;true&#41;;
                SwingUtilities.getRootPane&#40;addButton&#41;.setDefaultButton&#40;addButton&#41;;
            &#125;
            else &#123;
                addButton.setEnabled&#40;false&#41;;
            &#125;
        &#125;
    
        private JComponent createAddRemoveButtonPanel&#40;&#41; &#123;
            ComponentFactory factory = getComponentFactory&#40;&#41;;
    
            addButton = createAddButton&#40;&#41;;
            removeButton = createRemoveButton&#40;&#41;;
    
            JComponent panel = GuiStandardUtils
                    .createCommandButtonColumn&#40;new JButton&#91;&#93; &#123; addButton,
                            removeButton &#125;&#41;;
            panel.setBorder&#40;GuiStandardUtils
                    .createLeftAndRightBorder&#40;UIConstants.ONE_SPACE&#41;&#41;;
            return panel;
        &#125;
    
        private JButton createAddButton&#40;&#41; &#123;
            JButton addButton = getComponentFactory&#40;&#41;.createButton&#40;"button.add"&#41;;
            addButton.setEnabled&#40;false&#41;;
            addButton.addActionListener&#40;new ActionListener&#40;&#41; &#123;
                public void actionPerformed&#40;ActionEvent e&#41; &#123;
                    onAdd&#40;entityExplorerTree.getSelectionPaths&#40;&#41;&#41;;
                &#125;
            &#125;&#41;;
            return addButton;
        &#125;
    
        protected void onAdd&#40;TreePath&#91;&#93; paths&#41; &#123;
            for &#40;int i = 0; i < paths.length; i++&#41; &#123;
                TreePath path = paths&#91;i&#93;;
                DefaultMutableTreeNode node = &#40;DefaultMutableTreeNode&#41;path
                        .getLastPathComponent&#40;&#41;;
                PersistentMetadataObject o = &#40;PersistentMetadataObject&#41;node
                        .getUserObject&#40;&#41;;
                if &#40;o.isRootEntity&#40;&#41;&#41; &#123;
                    getListModel&#40;&#41;.add&#40;new HqlSelectedEntity&#40;&#40;Entity&#41;o&#41;&#41;;
                &#125;
                else if &#40;o.isProperty&#40;&#41;&#41; &#123;
                    getListModel&#40;&#41;.add&#40;new HqlSelectedProperty&#40;&#40;Property&#41;o&#41;&#41;;
                &#125;
                entityExplorerTree.clearSelection&#40;&#41;;
            &#125;
        &#125;
    
        private JButton createRemoveButton&#40;&#41; &#123;
            JButton removeButton = getComponentFactory&#40;&#41;.createButton&#40;
                    "button.remove"&#41;;
            removeButton.setEnabled&#40;false&#41;;
            removeButton.addActionListener&#40;new ActionListener&#40;&#41; &#123;
                public void actionPerformed&#40;ActionEvent e&#41; &#123;
                    int&#91;&#93; indices = columnViewList.getSelectedIndices&#40;&#41;;
                    Arrays.sort&#40;indices&#41;;
                    for &#40;int i = 0; i < indices.length; i++&#41; &#123;
                        getListModel&#40;&#41;.remove&#40;indices&#91;i&#93; - i&#41;;
                    &#125;
                &#125;
            &#125;&#41;;
            return removeButton;
        &#125;
    
        private JComponent createColumnViewPanel&#40;&#41; &#123;
            this.columnViewList = createColumnViewList&#40;&#41;;
            JLabel selectedProperties = getComponentFactory&#40;&#41;.createLabelFor&#40;
                    "label.selectedProperties", columnViewList&#41;;
    
            JScrollPane sp = new JScrollPane&#40;columnViewList&#41;;
            sp.setPreferredSize&#40;new Dimension&#40;150, entityExplorerTree.getControl&#40;&#41;
                    .getPreferredSize&#40;&#41;.height&#41;&#41;;
    
            JPanel listPane = new JPanel&#40;new BorderLayout&#40;&#41;&#41;;
            listPane.add&#40;selectedProperties, BorderLayout.NORTH&#41;;
            listPane.add&#40;sp, BorderLayout.CENTER&#41;;
            listPane.add&#40;
                    new ListItemUpDownButtonPanel&#40;columnViewList&#41;.getControl&#40;&#41;,
                    BorderLayout.EAST&#41;;
            return listPane;
        &#125;
    
        private JList createColumnViewList&#40;&#41; &#123;
            JList columnViewList = new JList&#40;&#41;;
            final ListListModel columnViewListModel = &#40;ListListModel&#41;getFormModel&#40;&#41;
                    .createBoundListModel&#40;Query.SELECTED_PROPERTIES&#41;;
            columnViewList.setModel&#40;columnViewListModel&#41;;
            columnViewList
                    .setCellRenderer&#40;PersistentMetadataRenderers.LIST_CELL_RENDERER&#41;;
            columnViewList.addListSelectionListener&#40;new ListSelectionListener&#40;&#41; &#123;
                public void valueChanged&#40;ListSelectionEvent e&#41; &#123;
                    if &#40;!e.getValueIsAdjusting&#40;&#41;&#41; &#123;
                        if &#40;getList&#40;&#41;.isSelectionEmpty&#40;&#41;&#41; &#123;
                            removeButton.setEnabled&#40;false&#41;;
                        &#125;
                        else &#123;
                            removeButton.setEnabled&#40;true&#41;;
                        &#125;
                    &#125;
                &#125;
            &#125;&#41;;
            return columnViewList;
        &#125;
    
    &#125;

    Comment


    • #3
      Thanks Keith, just a related question do you have this form on a dialog, reason I am asking because I wanted to put it on Panel, as one of the pages in compound form. I want to use tabbedPane with Apply Revert buttons, I know there is that TabbedDialogPage, basically want to similar thing but not on a dialog just Panel with TabbedPanel and button bar which is also Guarded. I am not sure how to structure this. Any help would be appreciated.

      Thanks

      Amad

      Comment


      • #4
        Amad,

        The cool thing is this form page actually *is* a panel; more specifically, it's a ControlFactory that produces a JPanel. So I can stick this thing on a dialog or part of tab pane, or whatever.

        TabbedDialogPages can be reused in other places besides dialogs, though I admit they are intended to be used generally in conjunction with the TitledApplicationDialog to allow for multiple dialog pages, switchable via tabs, typically with an OK and Cancel button as part of an edit transaction (and a title / message area at the top.) Of course, this is customizable, as the pages themselves are independent of the dialog or other control they are contained on.

        If you just want plain old TabPanes, maybe just consider just using JTabbedPane. There is a ComponentFactory method that makes them easier to create. But I'd still consider using TabbedDialogPage as it makes it easy to add individual forms, where each form is generally a single tab and edits a subset of the properties of the backing compound form object.

        As far as button bars, this is where the command framework commands in handy. Command groups can produce button bars, in addition to popup menus. Commands are also all implement Guarded. So for example, in my application I have a button bar as part of a form page on a dialog that displays several buttons where each button executes an ActionCommand or ToggleCommand (or maybe a pull-down button menu if it it's a group.) I can use the same command group to produce a popup menu with exactly the same commands, only this time they're realized as menu items. It's good user interface design: typically the button bar will be positioned below the object it allows manipulation of, and the popup menu will display when you right click on the object.

        Hope this helps!

        Comment


        • #5
          Thanks Keith, now I am using TabbedDialogPage to get my Tabbed forms. But shouldn't there be a CompositeFormPage and then a concrete implementation
          TabbedFormPage? I mean its TabbedDialogPage is working for me but I just don't like to use dialog pages in my JPanel even though it just in the name. Also having a TabbedFormPage with button bar would be suitable for dialogs or can also be used for other panels. Like in my case where i have navigation tree on the left and selected domain object's properties (as TabbedPane) on the left. Right now I have to sort of work indirectly through this TabbedDialogPage and put it on a panel with a button bar (Apply Revert Help). If we had something in form package that does that then we can also use it for dialogs, similar to FormBackedDialogPage ??

          Let me know what do you think.

          Amad

          Comment


          • #6
            now I am using TabbedDialogPage to get my Tabbed forms. But shouldn't there be a CompositeFormPage and then a concrete implementation
            TabbedFormPage? I mean its TabbedDialogPage is working for me but I just don't like to use dialog pages in my JPanel even though it just in the name.
            Amad,
            could you please explain what it is about TabbedDialogPage that you don't like? I'm not sure I understand why you think we need a CompositeFormPage / TabbedFormPage. Perhaps if you could submit some code showing how you are using the form classes I'll catch on.

            Right now I have to sort of work indirectly through this TabbedDialogPage and put it on a panel with a button bar (Apply Revert Help).
            This is exactly how TabbedDialogPage is intended to be used. Is it simply that you want a class that provides a message area and set of buttons similar to TitledPageApplicationDialog but which can be used as a JPanel rather that a JDialog? This is something which we will certinly be adding support for ASAP.

            Ollie

            Comment


            • #7
              could you please explain what it is about TabbedDialogPage that you don't like?
              Ollie,
              In principal nothing it works form but as Keith had mentioned that it was implemented mainly keeping dialogs in mind. Thats why I think we should seperate this composite page functionality into may be some composite form page. But as I said it could be just this *Dilaog* throwing me off.

              Is it simply that you want a class that provides a message area and set of buttons similar to TitledPageApplicationDialog but which can be used as a JPanel rather that a JDialog?
              Yes indeed. How soon are we talking about? If not if you can give me some pointers i tihink Iwill figure it out.


              Amad

              Comment


              • #8
                In principal nothing it works form but as Keith had mentioned that it was implemented mainly keeping dialogs in mind. Thats why I think we should seperate this composite page functionality into may be some composite form page. But as I said it could be just this *Dilaog* throwing me off.
                OK. this is what i thought. Don't worry about the *Dialog* there is nothing about DialogPage, TabbedDialogPage etc. that is specific to JDialog. Perhapse we are going to have to make the names clear? Though once we start putting some better documentation in things should get clearer. I hope...


                As far as a time frame for having a JPanel based implementation of TitledPageApplicationDialog I can probably do something quite soon (next week). But if you felt adventurous perhaps you should have a go and post your code? If you look in ApplicatonDialog and TitledDialog you should get a fair idea of how to do the implementation.

                Ollie

                Comment


                • #9
                  Ollie following are two classes that I have created inorder to meet my requirements. Note I don't need messageArea pane for Panel.

                  1. ApplicationPanel Class
                  Code:
                  public abstract class ApplicationPanel extends AbstractControlFactory implements Guarded &#123;
                  
                      protected final Log logger = LogFactory.getLog&#40;getClass&#40;&#41;&#41;;
                  
                      protected static final String DEFAULT_FINISH_KEY = "applyCommand";
                  
                      protected static final String DEFAULT_CANCEL_KEY = "revertCommand";
                  
                      protected static final String DEFAULT_HELP_KEY = "helpCommand";
                  
                      private boolean defaultEnabled = false;
                  
                      private ActionCommand finishCommand;
                  
                      private ActionCommand cancelCommand;
                  
                      private ActionCommand helpCommand;
                  
                      private CommandGroup panelCommandGroup;
                  
                      /**
                       * Should the finish button be enabled by default?
                       *
                       * @param enabled
                       *            true or false
                       */
                      public void setDefaultEnabled&#40;boolean enabled&#41; &#123;
                          this.defaultEnabled = enabled;
                      &#125;
                  
                      public boolean isEnabled&#40;&#41; &#123;
                          if &#40;isControlCreated&#40;&#41;&#41; &#123;
                              return finishCommand.isEnabled&#40;&#41;;
                          &#125;
                          else &#123;
                              return false;
                          &#125;
                      &#125;
                  
                      public void setEnabled&#40;boolean enabled&#41; &#123;
                          setFinishEnabled&#40;enabled&#41;;
                      &#125;
                  
                      protected void setFinishEnabled&#40;boolean enabled&#41; &#123;
                          if &#40;isControlCreated&#40;&#41;&#41; &#123;
                              finishCommand.setEnabled&#40;enabled&#41;;
                          &#125;
                      &#125;
                  
                      protected JComponent createControl&#40;&#41; &#123;
                          initStandardCommands&#40;&#41;;
                          JPanel panel = new JPanel&#40;&#41;;
                          panel.setLayout&#40;new BorderLayout&#40;&#41;&#41;;
                          JComponent contentPane = createContentPane&#40;&#41;;
                          GuiStandardUtils.attachBorder&#40;contentPane, Borders.EMPTY_BORDER&#41;;
                          panel.add&#40;contentPane&#41;;
                          panel.add&#40;createButtonBar&#40;&#41;, BorderLayout.SOUTH&#41;;
                          return panel;
                      &#125;
                  
                      private void initStandardCommands&#40;&#41; &#123;
                          finishCommand = new ActionCommand&#40;getFinishFaceConfigurationKey&#40;&#41;&#41; &#123;
                              public void doExecuteCommand&#40;&#41; &#123;
                                  try &#123;
                                      boolean result = onFinish&#40;&#41;;
                                      if &#40;result&#41; &#123;
                                          /** @todo May be some notification */
                                      &#125;
                                  &#125;
                                  catch &#40;Exception e&#41; &#123;
                                      logger.warn&#40;"Exception occured executing panel finish command.", e&#41;;
                                      onFinishException&#40;e&#41;;
                                  &#125;
                              &#125;
                          &#125;;
                          finishCommand.setEnabled&#40;defaultEnabled&#41;;
                  
                          cancelCommand = new ActionCommand&#40;getCancelFaceConfigurationKey&#40;&#41;&#41; &#123;
                              public void doExecuteCommand&#40;&#41; &#123;
                                  onCancel&#40;&#41;;
                              &#125;
                          &#125;;
                  
                          helpCommand = new ActionCommand&#40;getHelpFaceConfigurationKey&#40;&#41;&#41; &#123;
                              public void doExecuteCommand&#40;&#41; &#123;
                                  onHelp&#40;&#41;;
                              &#125;
                          &#125;;
                  
                      &#125;
                  
                      protected void onFinishException&#40;Exception e&#41; &#123;
                          String exceptionMessage;
                          if &#40;e instanceof MessageSourceResolvable&#41; &#123;
                              exceptionMessage = getMessageSourceAccessor&#40;&#41;.getMessage&#40;&#40;MessageSourceResolvable&#41;e&#41;;
                          &#125;
                          else &#123;
                              exceptionMessage = e.getLocalizedMessage&#40;&#41;;
                          &#125;
                          if &#40;!StringUtils.hasText&#40;exceptionMessage&#41;&#41; &#123;
                              String defaultMessage =
                                  "Unable to finish; an application exception occured.\nPlease contact your administrator.";
                              exceptionMessage = getMessageSourceAccessor&#40;&#41;.getMessage&#40;
                                  "applicationPanel.defaultFinishException", defaultMessage&#41;;
                          &#125;
                          JOptionPane.showMessageDialog&#40;getControl&#40;&#41;, exceptionMessage, getApplicationName&#40;&#41;,
                                                        JOptionPane.ERROR_MESSAGE&#41;;
                      &#125;
                  
                      protected String getFinishFaceConfigurationKey&#40;&#41; &#123;
                          return DEFAULT_FINISH_KEY;
                      &#125;
                  
                      protected String getCancelFaceConfigurationKey&#40;&#41; &#123;
                          return DEFAULT_CANCEL_KEY;
                      &#125;
                  
                      protected String getHelpFaceConfigurationKey&#40;&#41; &#123;
                          return DEFAULT_HELP_KEY;
                      &#125;
                  
                      /**
                       * Return a standardized row of command buttons, right-justified and all of
                       * the same size, with Apply as the default button, and no mnemonics used, as
                       * per the Java Look and Feel guidelines.
                       */
                      protected JComponent createButtonBar&#40;&#41; &#123;
                          this.panelCommandGroup = CommandGroup.createCommandGroup&#40;null, getCommandGroupMembers&#40;&#41;&#41;;
                          JComponent buttonBar = this.panelCommandGroup.createButtonBar&#40;&#41;;
                          GuiStandardUtils.attachBorder&#40;buttonBar, Borders.DIALOG_BORDER&#41;;
                          return buttonBar;
                      &#125;
                  
                      protected ActionCommand getFinishCommand&#40;&#41; &#123;
                          return finishCommand;
                      &#125;
                  
                      protected ActionCommand getCancelCommand&#40;&#41; &#123;
                          return cancelCommand;
                      &#125;
                  
                      protected ActionCommand getHelpCommand&#40;&#41; &#123;
                          return helpCommand;
                      &#125;
                  
                      /**
                       * Template getter method to return the commands to populate the panel
                       * button bar.
                       *
                       * @return The array of commands &#40;may also be a separator or glue identifier&#41;
                       */
                      protected Object&#91;&#93; getCommandGroupMembers&#40;&#41; &#123;
                          return new AbstractCommand&#91;&#93; &#123;
                              getFinishCommand&#40;&#41;, getCancelCommand&#40;&#41;, getHelpCommand&#40;&#41;&#125;;
                      &#125;
                  
                      protected void onHelp&#40;&#41; &#123;
                  
                      &#125;
                  
                      /**
                       * Template lifecycle method invoked after the dialog control is
                       * initialized.
                       */
                      protected void onInitialized&#40;&#41; &#123;
                      &#125;
                  
                      /**
                       * Return the GUI which allows the user to manipulate the business objects
                       * related to this panel; this GUI will be placed above the <code>OK</code>
                       * and <code>Cancel</code> buttons, in a standard manner.
                       */
                      protected abstract JComponent createContentPane&#40;&#41;;
                  
                      /**
                       * Request invocation of the action taken when the user hits the
                       * <code>Apply</code> &#40;finish&#41; button.
                       *
                       * @return true if action completed succesfully; false otherwise.
                       */
                      protected abstract boolean onFinish&#40;&#41;;
                  
                      /**
                       * Request invocation of the action taken when the user hits the
                       * <code>Cancel</code> &#40;cancel&#41; button.
                       *
                       */
                      protected abstract void onCancel&#40;&#41;;
                  &#125;
                  2. PageApplicationPanel Class
                  Code:
                  public abstract class PageApplictionPanel extends ApplicationPanel implements
                      PropertyChangeListener &#123;
                  
                      private DialogPage dialogPage;
                  
                      public PageApplictionPanel&#40;DialogPage dialogPage&#41; &#123;
                          super&#40;&#41;;
                          setDialogPage&#40;dialogPage&#41;;
                      &#125;
                  
                      protected DialogPage getDialogPage&#40;&#41; &#123;
                          return dialogPage;
                      &#125;
                  
                      private void setDialogPage&#40;DialogPage dialogPage&#41; &#123;
                          this.dialogPage = dialogPage;
                      &#125;
                  
                      protected JComponent createContentPane&#40;&#41; &#123;
                          dialogPage.addPropertyChangeListener&#40;this&#41;;
                          return dialogPage.getControl&#40;&#41;;
                      &#125;
                  
                      public void propertyChange&#40;PropertyChangeEvent e&#41; &#123;
                          if &#40;DialogPage.PAGE_COMPLETE_PROPERTY.equals&#40;e.getPropertyName&#40;&#41;&#41;&#41; &#123;
                              setEnabled&#40;dialogPage.isPageComplete&#40;&#41;&#41;;
                          &#125;
                      &#125;
                  Usage :
                  Code:
                          PageApplictionPanel pageApplictionPanel = new PageApplictionPanel&#40;tabbedPage&#41; &#123;
                              protected boolean onFinish&#40;&#41; &#123;
                                  globalUserFormModel.commit&#40;&#41;;
                                  return true;
                              &#125;
                  
                              protected void onCancel&#40;&#41; &#123;
                                  globalUserFormModel.revert&#40;&#41;; // Note &#58; this doesn't currently work If I have two list boxes and I am copying from one &#40;avilable&#41; to &#40;selected&#41; other..
                              &#125;
                          &#125;;
                  Only problem I have now is from Keith's class (above pasted) there is no way of reverting since we are adding value to list directly on Add action. There is some BufferedList stuff, I wonder if that can help me here.? Anyone?

                  Amad

                  Comment


                  • #10
                    Only problem I have now is from Keith's class (above pasted) there is no way of reverting since we are adding value to list directly on Add action. There is some BufferedList stuff, I wonder if that can help me here.? Anyone?
                    That's a bug Amad thanks for spotting it. I seems that BufferedCollectionValueModel was not doing the revert. I've fixed and added a test in CVS.,

                    Comment

                    Working...
                    X