001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.springframework.richclient.command;
017    
018    import com.jgoodies.forms.layout.ColumnSpec;
019    import com.jgoodies.forms.layout.RowSpec;
020    import com.jgoodies.forms.layout.Size;
021    import org.springframework.richclient.application.ApplicationServicesLocator;
022    import org.springframework.richclient.command.config.CommandButtonConfigurer;
023    import org.springframework.richclient.command.config.CommandConfigurer;
024    import org.springframework.richclient.command.config.CommandFaceDescriptor;
025    import org.springframework.richclient.command.support.ButtonBarGroupContainerPopulator;
026    import org.springframework.richclient.command.support.ButtonStackGroupContainerPopulator;
027    import org.springframework.richclient.command.support.SimpleGroupContainerPopulator;
028    import org.springframework.richclient.command.support.ToggleButtonPopupListener;
029    import org.springframework.richclient.core.UIConstants;
030    import org.springframework.richclient.factory.ButtonFactory;
031    import org.springframework.richclient.factory.MenuFactory;
032    import org.springframework.richclient.util.GuiStandardUtils;
033    import org.springframework.util.Assert;
034    import org.springframework.util.ObjectUtils;
035    import org.springframework.util.StringUtils;
036    
037    import javax.swing.*;
038    import javax.swing.border.Border;
039    import javax.swing.event.EventListenerList;
040    import java.awt.*;
041    import java.util.Collections;
042    import java.util.Iterator;
043    import java.util.List;
044    
045    /**
046     * Implementation of an {@link AbstractCommand} that groups a collection of
047     * commands. This can be used to construct a menu with all kinds of sub menus.
048     *
049     * @author Keith Donald
050     */
051    public class CommandGroup extends AbstractCommand {
052    
053            private EventListenerList listenerList;
054    
055            private GroupMemberList memberList = new GroupMemberList();
056    
057            private CommandRegistry commandRegistry;
058    
059            /**
060             * @see AbstractCommand#AbstractCommand()
061             */
062            public CommandGroup() {
063                    super();
064            }
065    
066            /**
067             * @see AbstractCommand#AbstractCommand(String)
068             */
069            public CommandGroup(String groupId) {
070                    super(groupId);
071            }
072    
073            /**
074             * @see AbstractCommand#AbstractCommand(String, CommandFaceDescriptor)
075             */
076            public CommandGroup(String groupId, CommandFaceDescriptor face) {
077                    super(groupId, face);
078            }
079    
080            /**
081             * Constructor with id for configuration and the {@link CommandRegistry} to use.
082             *
083             * @see AbstractCommand#AbstractCommand(String)
084             */
085            public CommandGroup(String groupId, CommandRegistry commandRegistry) {
086                    super(groupId);
087                    setCommandRegistry(commandRegistry);
088            }
089    
090            /**
091             * @see AbstractCommand#AbstractCommand(String, String)
092             */
093            public CommandGroup(String id, String encodedLabel) {
094                    super(id, encodedLabel);
095            }
096    
097            /**
098             * @see AbstractCommand#AbstractCommand(String, String, Icon, String)
099             */
100            public CommandGroup(String id, String encodedLabel, Icon icon, String caption) {
101                    super(id, encodedLabel, icon, caption);
102            }
103    
104            /**
105             * Creates a command group with a single command member.
106             *
107             * @param member the command to put in the CommandGroup
108             * @return a {@link CommandGroup} which contains the given command.
109             */
110            public static CommandGroup createCommandGroup(AbstractCommand member) {
111                    return createCommandGroup(null, new Object[] { member });
112            }
113    
114            /**
115             * Create a command group which holds all the given members.
116             *
117             * @param members members to add to the group.
118             * @return a {@link CommandGroup} which contains all the members.
119             */
120            public static CommandGroup createCommandGroup(Object[] members) {
121                    return createCommandGroup(null, members, false, null);
122            }
123    
124        /**
125             * Create a command group which holds all the given members.
126             *
127             * @param members members to add to the group.
128             * @return a {@link CommandGroup} which contains all the members.
129             */
130            public static CommandGroup createCommandGroup(List<? extends AbstractCommand> members) {
131                    return createCommandGroup(null, members.toArray(), false, null);
132            }
133    
134        /**
135             * Create a command group which holds all the given members.
136             *
137             * @param groupId the id to configure the group.
138             * @param members members to add to the group.
139             * @return a {@link CommandGroup} which contains all the members.
140             */
141            public static CommandGroup createCommandGroup(String groupId, Object[] members) {
142                    return createCommandGroup(groupId, members, false, null);
143            }
144    
145        /**
146             * Create a command group which holds all the given members.
147             *
148             * @param groupId the id to configure the group.
149             * @param members members to add to the group.
150             * @return a {@link CommandGroup} which contains all the members.
151             */
152            public static CommandGroup createCommandGroup(String groupId, List<? extends AbstractCommand> members) {
153                    return createCommandGroup(groupId, members.toArray(), false, null);
154            }
155    
156            /**
157             * Create a command group which holds all the given members.
158             *
159             * @param groupId the id to configure the group.
160             * @param members members to add to the group.
161             * @param configurer the configurer to use.
162             * @return a {@link CommandGroup} which contains all the members.
163             */
164            public static CommandGroup createCommandGroup(String groupId, Object[] members, CommandConfigurer configurer) {
165                    return createCommandGroup(groupId, members, false, configurer);
166            }
167    
168            public static CommandGroup createExclusiveCommandGroup(Object[] members) {
169                    return createCommandGroup(null, members, true, null);
170            }
171    
172            public static CommandGroup createExclusiveCommandGroup(String groupId, Object[] members) {
173                    return createCommandGroup(groupId, members, true, null);
174            }
175    
176            public static CommandGroup createExclusiveCommandGroup(String groupId, Object[] members,
177                            CommandConfigurer configurer) {
178                    return createCommandGroup(groupId, members, true, configurer);
179            }
180    
181            /**
182             * Creates a command group, configuring the group using the ObjectConfigurer
183             * service (pulling visual configuration properties from an external
184             * source). This method will also auto-configure contained Command members
185             * that have not yet been configured.
186             *
187             * @param groupId id to configure this commandGroup.
188             * @param members members to add to the group.
189             * @return a {@link CommandGroup} that holds the given members.
190             */
191            private static CommandGroup createCommandGroup(final String groupId, final Object[] members,
192                            final boolean exclusive, final CommandConfigurer configurer) {
193                    final CommandConfigurer theConfigurer = (configurer != null) ? configurer
194                                    : (CommandConfigurer) ApplicationServicesLocator.services().getService(CommandConfigurer.class);
195    
196                    final CommandGroupFactoryBean groupFactory = new CommandGroupFactoryBean(groupId, null, theConfigurer, members);
197                    groupFactory.setExclusive(exclusive);
198                    return groupFactory.getCommandGroup();
199            }
200    
201            protected void addInternal(AbstractCommand command) {
202                    this.memberList.add(new SimpleGroupMember(this, command));
203            }
204    
205            protected void addLazyInternal(String commandId) {
206                    this.memberList.add(new LazyGroupMember(this, commandId));
207            }
208    
209            protected void addSeparatorInternal() {
210                    this.memberList.add(new SeparatorGroupMember());
211            }
212    
213            protected void addGlueInternal() {
214                    this.memberList.add(new GlueGroupMember());
215            }
216    
217            protected void addComponentInternal(Component component) {
218                    this.memberList.add(new ComponentGroupMember(component));
219            }
220    
221            public final void setCommandRegistry(CommandRegistry registry) {
222                    if (!ObjectUtils.nullSafeEquals(this.commandRegistry, registry)) {
223    
224                            // @TODO should groups listen to command registration events if
225                            // they've
226                            // got lazy members that haven't been instantiated? Or are
227                            // targetable
228                            // commands lightweight enough?
229                            if (logger.isDebugEnabled()) {
230                                    logger.debug("Setting registry " + registry + " for command group '" + getId() + "'");
231                            }
232                            this.commandRegistry = registry;
233                    }
234            }
235    
236            /**
237             * Enable/disable the entire group.
238             * 
239             * @param enabled enable/disable this group.
240             */
241            public void setEnabled(boolean enabled) {
242                    super.setEnabled(enabled);
243                    for (Iterator members = getMemberList().iterator(); members.hasNext();)
244                    {
245                        GroupMember groupMember = (GroupMember)members.next();
246                        groupMember.setEnabled(enabled);
247                    }
248            }
249    
250            public void setVisible(boolean visible) {
251                    super.setVisible(visible);
252                    getMemberList().setContainersVisible(visible);
253            }
254    
255            protected CommandRegistry getCommandRegistry() {
256                    return commandRegistry;
257            }
258    
259            public void add(AbstractCommand command) {
260                    add(command, true);
261            }
262    
263            public void add(AbstractCommand command, boolean rebuild) {
264                    if (command == null) {
265                            return;
266                    }
267                    if (contains(command)) {
268                            return;
269                    }
270                    getMemberList().append(new SimpleGroupMember(this, command));
271                    rebuildIfNecessary(rebuild);
272            }
273    
274            public void add(String groupMemberPath, AbstractCommand command) {
275                    AbstractCommand c = find(groupMemberPath);
276                    assertIsGroup(groupMemberPath, c);
277                    ((CommandGroup) c).add(command);
278            }
279    
280            private void assertIsGroup(String groupMemberPath, AbstractCommand c) {
281                    Assert.notNull(c, "Command at path '" + groupMemberPath + "' does not exist.");
282                    Assert.isTrue((c instanceof CommandGroup), "Command at path '" + groupMemberPath + "' is not a group.");
283            }
284    
285            public void add(String groupMemberPath, AbstractCommand command, boolean rebuild) {
286                    AbstractCommand c = find(groupMemberPath);
287                    assertIsGroup(groupMemberPath, c);
288                    ((CommandGroup) c).add(command, rebuild);
289            }
290    
291            public void remove(AbstractCommand command) {
292                    remove(command, true);
293            }
294    
295            public void remove(AbstractCommand command, boolean rebuild) {
296                    if (command == null) {
297                            return;
298                    }
299                    ExpansionPointGroupMember expansionPoint = getMemberList().getExpansionPoint();
300                    GroupMember member = expansionPoint.getMemberFor(command.getId());
301                    if (member != null) {
302                            expansionPoint.remove(member);
303                            rebuildIfNecessary(rebuild);
304                    }
305            }
306    
307            public void remove(String groupMemberPath, AbstractCommand command) {
308                    AbstractCommand c = find(groupMemberPath);
309                    assertIsGroup(groupMemberPath, c);
310                    ((CommandGroup) c).remove(command);
311            }
312    
313            public void remove(String groupMemberPath, AbstractCommand command, boolean rebuild) {
314                    AbstractCommand c = find(groupMemberPath);
315                    assertIsGroup(groupMemberPath, c);
316                    ((CommandGroup) c).remove(command, rebuild);
317            }
318    
319            public void addSeparator() {
320                    addSeparator(true);
321            }
322    
323            public void addSeparator(boolean rebuild) {
324                    getMemberList().append(new SeparatorGroupMember());
325                    rebuildIfNecessary(rebuild);
326            }
327    
328            public void addGlue() {
329                    addGlue(true);
330            }
331    
332            public void addGlue(boolean rebuild) {
333                    getMemberList().append(new GlueGroupMember());
334                    rebuildIfNecessary(rebuild);
335            }
336    
337            private void rebuildIfNecessary(boolean rebuild) {
338                    if (rebuild) {
339                            rebuildAllControls();
340                            fireMembersChanged();
341                    }
342            }
343    
344            protected void rebuildAllControls() {
345                    if (logger.isDebugEnabled()) {
346                            logger.debug("Rebuilding all GUI controls for command group '" + getId() + "'");
347                    }
348                    getMemberList().rebuildControls();
349            }
350    
351            protected GroupMemberList getMemberList() {
352                    return memberList;
353            }
354    
355            protected Iterator memberIterator() {
356                    return getMemberList().iterator();
357            }
358    
359            public int size() {
360                    return getMemberCount();
361            }
362    
363            public boolean isAllowedMember(AbstractCommand proposedMember) {
364                    return true;
365            }
366    
367            /**
368             * Search for and return the command contained by this group with the
369             * specified path. Nested paths should be deliniated by the "/" character;
370             * for example, "fileGroup/newGroup/simpleFileCommand". The returned command
371             * may be a group or an action command.
372             *
373             * @param memberPath the path of the command, with nested levels deliniated
374             * by the "/" path separator.
375             * @return the command at the specified member path, or <code>null</code>
376             * if no was command found.
377             */
378            public AbstractCommand find(String memberPath) {
379                    if (logger.isDebugEnabled()) {
380                            logger.debug("Searching for command with nested path '" + memberPath + "'");
381                    }
382                    String[] paths = StringUtils.delimitedListToStringArray(memberPath, "/");
383                    CommandGroup currentGroup = this;
384                    // fileGroup/newGroup/newJavaProject
385                    for (int i = 0; i < paths.length; i++) {
386                            String memberId = paths[i];
387                            if (i < paths.length - 1) {
388                                    // must be a nested group
389                                    currentGroup = currentGroup.findCommandGroupMember(memberId);
390                            }
391                            else {
392                                    // is last path element; can be a group or action
393                                    return currentGroup.findCommandMember(memberId);
394                            }
395                    }
396                    return null;
397            }
398    
399            private CommandGroup findCommandGroupMember(String groupId) {
400                    AbstractCommand c = findCommandMember(groupId);
401                    Assert.isTrue((c instanceof CommandGroup), "Command with id '" + groupId + "' is not a group.");
402                    return (CommandGroup) c;
403            }
404    
405            private AbstractCommand findCommandMember(String commandId) {
406                    Iterator it = memberList.iterator();
407                    while (it.hasNext()) {
408                            GroupMember member = (GroupMember) it.next();
409                            if (member.managesCommand(commandId)) {
410                                    return member.getCommand();
411                            }
412                    }
413                    logger.warn("No command with id '" + commandId + "' is nested within this group (" + getId()
414                                    + "); returning null");
415                    return null;
416            }
417    
418            /**
419             * Executes all the members of this group.
420             */
421            public void execute() {
422                    Iterator it = memberList.iterator();
423                    while (it.hasNext()) {
424                            GroupMember member = (GroupMember) it.next();
425                            member.getCommand().execute();
426                    }
427            }
428    
429            public int getMemberCount() {
430                    return getMemberList().size();
431            }
432    
433            public boolean contains(AbstractCommand command) {
434                    return getMemberList().contains(command);
435            }
436    
437            public void reset() {
438                    ExpansionPointGroupMember expansionPoint = getMemberList().getExpansionPoint();
439                    if (!expansionPoint.isEmpty()) {
440                            expansionPoint.clear();
441                            rebuildIfNecessary(true);
442                    }
443            }
444    
445            public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory,
446                            CommandButtonConfigurer buttonConfigurer) {
447                    return createButton(getDefaultFaceDescriptorId(), buttonFactory, getMenuFactory(), buttonConfigurer);
448            }
449    
450            public AbstractButton createButton(ButtonFactory buttonFactory, MenuFactory menuFactory) {
451                    return createButton(getDefaultFaceDescriptorId(), buttonFactory, menuFactory, getPullDownMenuButtonConfigurer());
452            }
453    
454            public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory, MenuFactory menuFactory) {
455                    return createButton(faceDescriptorId, buttonFactory, menuFactory, getPullDownMenuButtonConfigurer());
456            }
457    
458            public AbstractButton createButton(ButtonFactory buttonFactory, MenuFactory menuFactory,
459                            CommandButtonConfigurer buttonConfigurer) {
460                    return createButton(getDefaultFaceDescriptorId(), buttonFactory, menuFactory, buttonConfigurer);
461            }
462    
463            public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory, MenuFactory menuFactory,
464                            CommandButtonConfigurer buttonConfigurer) {
465                    AbstractButton button = buttonFactory.createToggleButton();
466                    attach(button, buttonConfigurer);
467                    JPopupMenu popup = menuFactory.createPopupMenu();
468                    bindMembers(button, popup, menuFactory, getMenuItemButtonConfigurer());
469                    ToggleButtonPopupListener.bind(button, popup);
470                    return button;
471            }
472    
473            protected CommandButtonConfigurer getPullDownMenuButtonConfigurer() {
474                    return getCommandServices().getPullDownMenuButtonConfigurer();
475            }
476    
477            public JMenuItem createMenuItem(String faceDescriptorId, MenuFactory factory,
478                            CommandButtonConfigurer buttonConfigurer) {
479                    JMenu menu = factory.createMenu();
480                    attach(menu);
481                    bindMembers(menu, menu, factory, buttonConfigurer);
482                    return menu;
483            }
484    
485            public JPopupMenu createPopupMenu() {
486                    return createPopupMenu(getMenuFactory());
487            }
488    
489            public JPopupMenu createPopupMenu(MenuFactory factory) {
490                    JPopupMenu popup = factory.createPopupMenu();
491                    bindMembers(popup, popup, factory, getMenuItemButtonConfigurer());
492                    return popup;
493            }
494    
495            public JComponent createToolBar() {
496                    return createToolBar(getToolBarButtonFactory());
497            }
498    
499            public JComponent createToolBar(ButtonFactory buttonFactory) {
500                    JComponent toolbar = getComponentFactory().createToolBar();
501                    toolbar.setName(getText());
502                    bindMembers(toolbar, toolbar, buttonFactory, getToolBarButtonConfigurer());
503                    toolbar.setEnabled(false);
504                    toolbar.setVisible(true);
505                    return toolbar;
506            }
507    
508            public JMenuBar createMenuBar() {
509                    return createMenuBar(getMenuFactory());
510            }
511    
512            public JMenuBar createMenuBar(MenuFactory factory) {
513                    JMenuBar menubar = factory.createMenuBar();
514                    bindMembers(menubar, menubar, factory, getMenuItemButtonConfigurer());
515                    return menubar;
516            }
517    
518            /**
519             * Create a button bar with buttons for all the commands in this group.
520             *
521             * @return never null
522             */
523            public JComponent createButtonBar() {
524                    return createButtonBar(null);
525            }
526    
527            /**
528             * Create a button bar with buttons for all the commands in this group. Adds
529             * a border top and bottom of 2 spaces.
530             *
531             * @param minimumButtonSize if null, then there is no minimum size
532             *
533             * @return never null
534             */
535            public JComponent createButtonBar(Size minimumButtonSize) {
536                    return createButtonBar(minimumButtonSize, GuiStandardUtils.createTopAndBottomBorder(UIConstants.TWO_SPACES));
537            }
538    
539            /**
540             * Create a button bar with buttons for all the commands in this.
541             *
542             * @param columnSpec Custom columnSpec for each column containing a button,
543             * can be <code>null</code>.
544             * @param rowSpec Custom rowspec for the buttonbar, can be <code>null</code>.
545             * @return never null
546             */
547            public JComponent createButtonBar(final ColumnSpec columnSpec, final RowSpec rowSpec) {
548                    return createButtonBar(columnSpec, rowSpec, null);
549            }
550    
551            /**
552             * Create a button bar with buttons for all the commands in this.
553             *
554             * @param minimumButtonSize if null, then there is no minimum size
555             * @param border if null, then don't use a border
556             *
557             * @return never null
558             */
559            public JComponent createButtonBar(final Size minimumButtonSize, final Border border) {
560                    return createButtonBar(minimumButtonSize == null ? null : new ColumnSpec(minimumButtonSize), null, border);
561            }
562    
563            /**
564             * Create a button bar with buttons for all the commands in this.
565             *
566             * @param columnSpec Custom columnSpec for each column containing a button,
567             * can be <code>null</code>.
568             * @param rowSpec Custom rowspec for the buttonbar, can be <code>null</code>.
569             * @param border if null, then don't use a border
570             *
571             * @return never null
572             */
573            public JComponent createButtonBar(final ColumnSpec columnSpec, final RowSpec rowSpec, final Border border) {
574                    final ButtonBarGroupContainerPopulator container = new ButtonBarGroupContainerPopulator();
575                    container.setColumnSpec(columnSpec);
576                    container.setRowSpec(rowSpec);
577                    addCommandsToGroupContainer(container);
578                    return GuiStandardUtils.attachBorder(container.getButtonBar(), border);
579            }
580    
581            /**
582             * Create a button stack with buttons for all the commands.
583             *
584             * @return never null
585             */
586            public JComponent createButtonStack() {
587                    return createButtonStack(null);
588            }
589    
590            /**
591             * Create a button stack with buttons for all the commands. Adds a border
592             * left and right of 2 spaces.
593             *
594             * @param minimumButtonSize Minimum size of the buttons (can be null)
595             * @return never null
596             */
597            public JComponent createButtonStack(final Size minimumButtonSize) {
598                    return createButtonStack(minimumButtonSize, GuiStandardUtils.createLeftAndRightBorder(UIConstants.TWO_SPACES));
599            }
600    
601            /**
602             * Create a button stack with buttons for all the commands.
603             *
604             * @param minimumButtonSize Minimum size of the buttons (can be
605             * <code>null</code>)
606             * @param border Border to set around the stack.
607             * @return never null
608             */
609            public JComponent createButtonStack(final Size minimumButtonSize, final Border border) {
610                    return createButtonStack(minimumButtonSize == null ? null : new ColumnSpec(minimumButtonSize), null, border);
611            }
612    
613            /**
614             * Create a button stack with buttons for all the commands.
615             *
616             * @param columnSpec Custom columnSpec for the stack, can be
617             * <code>null</code>.
618             * @param rowSpec Custom rowspec for each row containing a button can be
619             * <code>null</code>.
620             * @return never null
621             */
622            public JComponent createButtonStack(final ColumnSpec columnSpec, final RowSpec rowSpec) {
623                    return createButtonStack(columnSpec, rowSpec, null);
624            }
625    
626            /**
627             * Create a button stack with buttons for all the commands.
628             *
629             * @param columnSpec Custom columnSpec for the stack, can be
630             * <code>null</code>.
631             * @param rowSpec Custom rowspec for each row containing a button can be
632             * <code>null</code>.
633             * @param border Border to set around the stack.
634             * @return never null
635             */
636            public JComponent createButtonStack(final ColumnSpec columnSpec, final RowSpec rowSpec, final Border border) {
637                    final ButtonStackGroupContainerPopulator container = new ButtonStackGroupContainerPopulator();
638                    container.setColumnSpec(columnSpec);
639                    container.setRowSpec(rowSpec);
640                    addCommandsToGroupContainer(container);
641                    return GuiStandardUtils.attachBorder(container.getButtonStack(), border);
642            }
643    
644            /**
645             * Create a container with the given GroupContainerPopulator which will hold
646             * the members of this group.
647             *
648             * @param groupContainerPopulator
649             */
650            protected void addCommandsToGroupContainer(final GroupContainerPopulator groupContainerPopulator) {
651                    final Iterator members = getMemberList().iterator();
652    
653                    while (members.hasNext()) {
654                            final GroupMember member = (GroupMember) members.next();
655                            if (member.getCommand() instanceof CommandGroup) {
656                                    member.fill(groupContainerPopulator, getButtonFactory(), getPullDownMenuButtonConfigurer(),
657                                                    Collections.EMPTY_LIST);
658                            }
659                            else {
660                                    member.fill(groupContainerPopulator, getButtonFactory(), getDefaultButtonConfigurer(),
661                                                    Collections.EMPTY_LIST);
662                            }
663                    }
664                    groupContainerPopulator.onPopulated();
665            }
666    
667            private void bindMembers(Object owner, Container memberContainer, Object controlFactory,
668                            CommandButtonConfigurer configurer) {
669                    getMemberList().bindMembers(owner, new SimpleGroupContainerPopulator(memberContainer), controlFactory,
670                                    configurer);
671            }
672    
673            public void addGroupListener(CommandGroupListener l) {
674                    if (listenerList == null) {
675                            listenerList = new EventListenerList();
676                    }
677                    listenerList.add(CommandGroupListener.class, l);
678            }
679    
680            public void removeGroupListener(CommandGroupListener l) {
681                    Assert.notNull(listenerList, "Listener list has not yet been instantiated!");
682                    listenerList.remove(CommandGroupListener.class, l);
683            }
684    
685            protected void fireMembersChanged() {
686                    if (listenerList == null) {
687                            return;
688                    }
689                    CommandGroupEvent event = null;
690                    Object[] listeners = listenerList.getListenerList();
691                    for (int i = listeners.length - 2; i >= 0; i -= 2) {
692                            if (listeners[i] == CommandGroupListener.class) {
693                                    if (event == null) {
694                                            event = new CommandGroupEvent(this);
695                                    }
696                                    ((CommandGroupListener) listeners[i + 1]).membersChanged(event);
697                            }
698                    }
699            }
700    }