View Javadoc

1   package com.explosion.expf.menusandtools.menu.segmented;
2   
3   import java.awt.Component;
4   import java.awt.Container;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import javax.swing.JPopupMenu;
11  
12  import com.explosion.expf.menusandtools.menu.ExpMenuItem;
13  import com.explosion.expf.menusandtools.menu.InvalidOrUnknownSegmentException;
14  import com.explosion.expf.menusandtools.menu.MenuException;
15  
16  
17  /* =============================================================================
18   *       
19   *     Copyright 2004 Stephen Cowx
20   *
21   *     Licensed under the Apache License, Version 2.0 (the "License");
22   *     you may not use this file except in compliance with the License.
23   *     You may obtain a copy of the License at
24   *
25   *     http://www.apache.org/licenses/LICENSE-2.0
26   *
27   *     Unless required by applicable law or agreed to in writing, software
28   *     distributed under the License is distributed on an "AS IS" BASIS,
29   *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30   *     See the License for the specific language governing permissions and
31   *     limitations under the License.
32   * 
33   * =============================================================================
34   */
35  
36  /***
37   * Code hole for duplicate code between menu components with incompatible heirarchies 
38   * @author Stephen Cowx
39   * Created on 09-Mar-2005
40   */
41  public class SegmentedMenuHelper
42  {
43      private static org.apache.log4j.Logger log = org.apache.log4j.LogManager.getLogger(SegmentedMenuHelper.class);
44      
45      private boolean firstTaken = false;
46      private boolean lastTaken = false;
47      private List segments = new ArrayList();
48      private Map segmentIndexes = new HashMap();
49      private Map segmentItemCounts = new HashMap();
50      private ExpSegmentedMenu theMenu;
51      private Container menuComponent;
52      
53      /***
54       * @param theMenu
55       * @param menuComponent
56       */
57      public SegmentedMenuHelper(ExpSegmentedMenu expSegmentedMenu, Container jMenuComponent)
58      {
59          super();
60          this.theMenu = expSegmentedMenu;
61          this.menuComponent = jMenuComponent;
62      }
63      /***
64       * Creates a new segment in this menu, and adds a separator unless it is the first or only segment in which case it doesn;t add a separator
65       * The separator for each segment always comes BEFORE the items in the segment with the exception of the Top segment, which has no separator
66       * If they are 
67       */
68      public ExpMenuSegment createNewSegment(int relativePositionOfSegmentOnMenu) throws Exception
69      {
70        ExpMenuSegment segment = new ExpMenuSegment(theMenu);
71        segmentItemCounts.put(segment, new Integer(0));
72        int segmentIndex = 0;
73        
74        if (relativePositionOfSegmentOnMenu == ExpMenuSegment.ALWAYS_FIRST_SEGMENT)
75        {
76          if (firstTaken)
77            throw new MenuException("Relative position ALWAYS_FIRST_SEGMENT is already being used on menu " + menuComponent.getName() + ".");
78  
79          firstTaken = true;
80          segmentIndex = 0;
81        }
82        else if (relativePositionOfSegmentOnMenu == ExpMenuSegment.ANY_POSITION)
83        {
84          if (segments.size() == 0)
85          {
86          	segmentIndex = 0;
87          }
88          else if (segments.size() == 1)
89          {
90              if (lastTaken)
91              {
92              	segmentIndex = 0;
93              }
94              else
95              {
96              	segmentIndex = 1;    
97              }
98          }
99          else if (segments.size() > 1)
100         {
101             if (lastTaken)
102             {
103             	segmentIndex = segments.size()-1;//second to last  
104             }
105             else
106             {
107             	segmentIndex = segments.size();//last
108             }
109         }
110       }
111       else if (relativePositionOfSegmentOnMenu == ExpMenuSegment.ALWAYS_LAST_SEGMENT)
112       {
113            if (lastTaken)
114               throw new MenuException("Relative position ALWAYS_LAST_SEGMENT is already being used on menu " + menuComponent.getName() + ".");
115 
116   	      lastTaken = true;
117   	      if (segments.size() == 0)
118   	      {
119   	         segmentIndex = 0;
120   	      }
121   	      else if (segments.size() == 1)
122   	      {
123   	      	 segmentIndex = 1;
124   	      }
125   	      else
126   	      {
127   	      	segmentIndex = segments.size();//last
128   	      }
129       }
130       
131       /* Add the segment to segments */
132       segmentIndexes.put(segment, new Integer(segmentIndex));
133       segments.add(segmentIndex,segment);
134       
135       /* The segments will now be in the right order.  it will be a good idea to 
136        * set their segmentIndexes now.
137        */
138       log.debug("Segment "+segment.hashCode() + " added : " + relativePositionOfSegmentOnMenu + " to " + this.hashCode());
139       ExpMenuSegment segmentToSeparate= null;
140       for (int i=0;i<segments.size();i++)
141       {
142           ExpMenuSegment theSegment = (ExpMenuSegment) segments.get(i);
143           int theSegmentIndex = ((Integer) segmentIndexes.get(theSegment)).intValue();
144           log.debug("Changing " + theSegment.hashCode() + " position from "+theSegmentIndex+" to "+ i);
145           segmentIndexes.put(theSegment, new Integer(i));
146 //        if the segment moves from being the first to the second, then insert a separator above it
147           if ( theSegmentIndex == 0 && i == 1)
148           {
149               segmentToSeparate = theSegment;
150           }
151           
152       }
153       
154       if (segmentToSeparate != null)
155       {
156           addElementToSegment(segmentToSeparate, null, true);
157       }
158       
159       /***
160        * Add in the separator if it is not the first component
161        * The separator for each segment always comes BEFORE the items in the segment
162        */
163      if (relativePositionOfSegmentOnMenu != ExpMenuSegment.ALWAYS_FIRST_SEGMENT && segmentIndex != 0)
164       	addElementToSegment(segment, null);
165       
166       return segment;
167     }
168     
169     /***
170      * Inserts and element at the end of the segment 
171      * @see com.explosion.expf.menusandtools.menu.segmented.ExpSegmentedMenu#addItemToSegment(com.explosion.expf.menusandtools.menu.ExpMenuSegment, java.awt.Component)
172      * @param segment
173      * @param item
174      * @throws InvalidOrUnknownSegmentException
175      */
176     public void addElementToSegment(ExpMenuSegment segment, Component item) throws InvalidOrUnknownSegmentException
177     {
178         addElementToSegment(segment, item, true);
179     }
180     
181     /***
182      * Inserts and element at the end of the segment if atEnd is true otherwise it inserts it at the beginning
183      * @see com.explosion.expf.menusandtools.menu.segmented.ExpSegmentedMenu#addItemToSegment(com.explosion.expf.menusandtools.menu.ExpMenuSegment, java.awt.Component)
184      * @param segment
185      * @param item
186      * @throws InvalidOrUnknownSegmentException
187      */
188     private void addElementToSegment(ExpMenuSegment segment, Component item, boolean atEnd) throws InvalidOrUnknownSegmentException
189     {
190         int insertionIndex = getInsertionIndex(segment, atEnd);
191         
192         log.debug("Segment " + segment.hashCode() + " is number " +(Integer) segmentIndexes.get(segment)
193                + " of " + segments.size() + " in "+this.hashCode()+", it has  "
194 			   +(Integer) segmentItemCounts.get(segment)
195 			   +" items and "+(item != null && item instanceof ExpMenuItem ? ((ExpMenuItem)item).getText() : "item") +" will be inserted at "+insertionIndex);
196 			    
197         if (item != null)
198             menuComponent.add(item,insertionIndex);
199         else
200             menuComponent.add(new JPopupMenu.Separator(), insertionIndex);
201         
202         /* Update the count for that item */
203         int segmentItemCount = ((Integer) segmentItemCounts.get(segment)).intValue();
204         segmentItemCounts.put(segment,new Integer(segmentItemCount+1));
205      }
206     
207     /***
208      * Returns the insertionIndex for this new item
209      */
210      private int getInsertionIndex(ExpMenuSegment segment, boolean atEnd) throws InvalidOrUnknownSegmentException
211      {
212          int insertionIndex = 0;
213          Integer segmentIndex = (Integer) segmentIndexes.get(segment);
214          
215          if (segmentIndex == null)
216              throw new InvalidOrUnknownSegmentException("The ExpMenuSegment you are using was not created using this menu.  You cannot use segments from another ExpSegmented menu in the context of this one");
217          
218          log.debug("Building up insertion index");
219          for (int i=0;i<=segmentIndex.intValue();i++)
220          {
221              if (!atEnd && i==segmentIndex.intValue())
222              {
223                  insertionIndex += 1;
224                  break;
225              }
226              ExpMenuSegment theSegment = (ExpMenuSegment) segments.get(i);
227              Integer segmentItemCount = (Integer) segmentItemCounts.get(theSegment);
228              log.debug(theSegment.hashCode() + " has " + segmentItemCount.intValue() + " items");
229              insertionIndex += segmentItemCount.intValue();
230          }
231          log.debug("InsertionIndex is " + insertionIndex);
232          return insertionIndex;
233      }
234      
235      /***
236       * Removes all the elements except the separator from this menu 
237       * @param segment
238       * @param item
239       * @throws InvalidOrUnknownSegmentException
240       */
241      public void removeSegment(ExpMenuSegment segment) throws InvalidOrUnknownSegmentException
242      {
243         log.debug("Removing segment " + segment.hashCode());
244      	//last ione, we will go backwards to the first one so that the indexes don;t get screwed
245      	int startIndex = getInsertionIndex(segment,true)-1;
246      	int endIndex = startIndex - ((Integer) segmentItemCounts.get(segment)).intValue();
247         for (int i=startIndex; i>endIndex; i--)
248         {
249         	int itemCount = ((Integer) segmentItemCounts.get(segment)).intValue();
250         	//log.debug("Segment " + segment.hashCode() + " is number " +(Integer) segmentIndexes.get(segment)
251             //        + " of " + segments.size() + " in "+this.hashCode()+", it has  "
252      		//	   + itemCount + " items");
253      			    
254         	this.menuComponent.remove(i);
255         	segmentItemCounts.put(segment, new Integer(--itemCount));
256         }
257         
258         this.segments.remove(segment);
259         this.segmentIndexes.remove(segment);
260         this.segmentItemCounts.remove(segment);
261         
262      }
263      
264      /***
265       * Returns the first segment in this helper or null if there isn't one
266       * @return
267       */
268      public ExpMenuSegment getFirstSegment()
269      {
270      	if (segments == null)
271      		return null;
272      	if (segments.size() < 1)
273      		return null;
274      	
275      	return (ExpMenuSegment) segments.get(0);
276      }
277      
278      /***
279       * Returns the last segment in this helper or null if there isn't one
280       * @return
281       */
282      public ExpMenuSegment getLastSegment()
283      {
284      	if (segments == null)
285      		return null;
286      	if (segments.size() < 1)
287      		return null;
288      	
289      	return (ExpMenuSegment) segments.get(segments.size() -1);
290      }
291 }