| AbstractViewLayout.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: AbstractViewLayout.java 2088 2011-03-05 20:36:55Z dmsmith $
21 */
22 package org.crosswire.common.swing.desktop;
23
24 import java.awt.BorderLayout;
25 import java.awt.Component;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Iterator;
29 import java.util.List;
30
31 import javax.swing.JPanel;
32 import javax.swing.event.EventListenerList;
33
34 import org.crosswire.common.swing.GuiUtil;
35 import org.crosswire.common.swing.CWMsg;
36 import org.crosswire.common.swing.desktop.event.ViewEvent;
37 import org.crosswire.common.swing.desktop.event.ViewEventListener;
38
39 /**
40 * Abstract manager of how we layout views.
41 *
42 * @see gnu.lgpl.License for license details.<br>
43 * The copyright to this program is held by it's authors.
44 * @author Joe Walker [joe at eireneh dot com]
45 * @author DM Smith [dmsmith555 at yahoo dot com]
46 */
47 public abstract class AbstractViewLayout implements Viewable {
48 /**
49 * This constructor is protected because it only needs to be seen by the sub
50 * classes
51 */
52 protected AbstractViewLayout() {
53 panel = new JPanel(new BorderLayout());
54 GuiUtil.applyDefaultOrientation(panel);
55 views = new ArrayList<Component>();
56 listenerList = new EventListenerList();
57 }
58
59 /**
60 * Add a view to the set.
61 */
62 public void addView(Component component) {
63 views.add(component);
64 }
65
66 /**
67 * Remove a view from the set.
68 */
69 public void removeView(Component component) {
70 views.remove(component);
71 fireViewRemoved(new ViewEvent(component));
72 }
73
74 /**
75 * Unconditionally remove a view from the set.
76 */
77 protected void forceRemoveView(Component component) {
78 views.remove(component);
79 }
80
81 /**
82 * Get a snapshot of the views as a collection.
83 *
84 * @return the views
85 */
86 public Collection<Component> getViews() {
87 return new ArrayList<Component>(views);
88 }
89
90 /**
91 * Get an iterator of a snapshot of views.
92 *
93 * @return an iterator over the views.
94 */
95 public Iterator<Component> iterator() {
96 return getViews().iterator();
97 }
98
99 /**
100 * Copies all the views from the one layout to the other
101 *
102 * @param other
103 * the other layout
104 */
105 public void moveTo(AbstractViewLayout other) {
106 // Make sure we are copying to something else
107 if (getClass() == other.getClass()) {
108 return;
109 }
110 // Go through the views removing them from the layout
111 // and adding them to the other
112 for (Component view : this) {
113 forceRemoveView(view);
114 other.addView(view);
115 }
116 }
117
118 /**
119 * Close all the views. Note the policy is enforced that one view is kept.
120 * This will keep the last one added.
121 */
122 public void closeAll() {
123 for (Component view : this) {
124 removeView(view);
125 }
126 }
127
128 /**
129 * Close all the views but the one provided.
130 *
131 * @param component
132 * the view that is to remain open.
133 */
134 public void closeOthers(Component component) {
135 for (Component view : this) {
136 if (view != component) {
137 removeView(view);
138 }
139 }
140 }
141
142 /**
143 * Visit every view in the order that they were added.
144 *
145 * @param visitor
146 * The visitor for the view
147 */
148 public void visit(ViewVisitor visitor) {
149 for (Component view : this) {
150 visitor.visitView(view);
151 }
152 }
153
154 /**
155 * Update the title of the view. If the component does not implement
156 * Titleable, then a generated title will be used.
157 *
158 * @param component
159 * the component whose title is to be used
160 */
161 public abstract void updateTitle(Component component);
162
163 /**
164 * Returns the top view. If no view is the top, it returns the first one
165 * added.
166 */
167 public abstract Component getSelected();
168
169 /**
170 * Find the view and select it.
171 *
172 * @param component
173 */
174 public abstract void select(Component component);
175
176 /**
177 * The number of views held by this layout.
178 *
179 * @return the number of views held by this layout
180 */
181 public int getViewCount() {
182 return views.size();
183 }
184
185 /**
186 * Get the view by position. Note that adding and removing views changes the
187 * indexes of the views. Do not use this for iteration as it is not thread
188 * safe.
189 *
190 * @param i
191 * the index of the view
192 * @return the requested view.
193 */
194 public Component getView(int i) {
195 return views.get(i);
196 }
197
198 /**
199 * Get the title from the component, truncating it if necessary. If the
200 * component does not implement Titleable of if the title is empty, then
201 * titles are generated.
202 *
203 * @param component
204 * from whom the title is gotten
205 * @return the title, possibly truncated or generated
206 */
207 protected String getTitle(Component component) {
208 if (component instanceof Titleable) {
209 Titleable view = (Titleable) component;
210 String title = view.getTitle();
211 if (title != null && title.length() > 0) {
212 if (title.length() <= MAX_TITLE_LEN) {
213 return title;
214 }
215 return title.substring(0, MAX_TITLE_LEN - 3) + "...";
216 }
217
218 // should set the title also
219 return generateTitle();
220 }
221 return generateTitle();
222 }
223
224 /**
225 * Generates a generic title
226 *
227 * @return the generated title
228 */
229 private String generateTitle() {
230 // TRANSLATOR: This is the label on a Bible View tab when it is cleared.
231 // {0} is a number to make the label unique.
232 return CWMsg.gettext("Untitled {0}", Integer.valueOf(base++));
233 }
234
235 /**
236 * All parts are put into a panel. This prevents the programmer from having
237 * to change containers.
238 *
239 * @return Returns the panel.
240 */
241 protected JPanel getPanel() {
242 return panel;
243 }
244
245 /**
246 * A constraint that allows the panel to be filled up, stretching
247 * horizontally and vertically.
248 *
249 * @return the constraint
250 */
251 protected Object getConstraint() {
252 return null;
253 }
254
255 /**
256 * Adds a view event listener for notification of any changes to the view.
257 *
258 * @param listener
259 * the listener
260 */
261 public synchronized void addViewEventListener(ViewEventListener listener) {
262 listenerList.add(ViewEventListener.class, listener);
263 }
264
265 /**
266 * Removes a view event listener.
267 *
268 * @param listener
269 * the listener
270 */
271 public synchronized void removeViewEventListener(ViewEventListener listener) {
272 listenerList.remove(ViewEventListener.class, listener);
273 }
274
275 /**
276 * Notify the listeners that the view has been removed.
277 *
278 * @param e
279 * the event
280 * @see EventListenerList
281 */
282 public void fireViewRemoved(ViewEvent e) {
283 // Guaranteed to return a non-null array
284 Object[] listeners = listenerList.getListenerList();
285 // Process the listeners last to first, notifying
286 // those that are interested in this event
287 for (int i = listeners.length - 2; i >= 0; i -= 2) {
288 if (listeners[i] == ViewEventListener.class) {
289 ((ViewEventListener) listeners[i + 1]).viewRemoved(e);
290 }
291 }
292 }
293
294 /**
295 * The list of views.
296 */
297 private List<Component> views;
298
299 /**
300 * The listeners for handling ViewEvent Listeners
301 */
302 private EventListenerList listenerList = new EventListenerList();
303
304 /**
305 * The maximum length of a title before it is abbreviated
306 */
307 private static final int MAX_TITLE_LEN = 30;
308
309 /**
310 * A shared counter for creating unknown titles.
311 */
312 private static int base = 1;
313
314 /**
315 * The panel into which all components are placed.
316 */
317 private JPanel panel;
318
319 }
320