001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.base.ui;
039:
040: import java.lang.reflect.Method;
041: import java.util.Hashtable;
042: import java.util.LinkedList;
043: import java.util.Iterator;
044: import java.util.Map;
045:
046: import org.millstone.base.terminal.*;
047: import org.millstone.base.terminal.PaintTarget;
048: import org.millstone.base.terminal.KeyMapper;
049: import org.millstone.base.terminal.PaintException;
050:
051: /** Tabsheet component.
052: *
053: * @author IT Mill Ltd.
054: * @version 3.1.1
055: * @since 3.0
056: */
057: public class TabSheet extends AbstractComponentContainer {
058:
059: /** Linked list of component tabs */
060: private LinkedList tabs = new LinkedList();
061:
062: /** Tab -> caption mapping */
063: private Hashtable tabCaptions = new Hashtable();
064:
065: /** Tab -> icon mapping */
066: private Hashtable tabIcons = new Hashtable();
067:
068: /** Selected tab */
069: private Component selected = null;
070: private KeyMapper keyMapper = new KeyMapper();
071:
072: /** Holds value of property tabsHIdden. */
073: private boolean tabsHidden;
074:
075: /** Construct new Tabsheet.
076: * Tabsheet is immediate by default.
077: */
078: public TabSheet() {
079: super ();
080: setImmediate(true);
081: }
082:
083: /** Get component container iterator for going trough all the components in the container.
084: * @return Iterator of the components inside the container.
085: */
086: public Iterator getComponentIterator() {
087: return java.util.Collections.unmodifiableList(tabs).iterator();
088: }
089:
090: /** Remove a component from this container.
091: * @param c The component to be removed.
092: */
093: public void removeComponent(Component c) {
094: if (c != null && tabs.contains(c)) {
095: super .removeComponent(c);
096: keyMapper.remove(c);
097: tabs.remove(c);
098: tabCaptions.remove(c);
099: if (c.equals(selected)) {
100: if (tabs.isEmpty())
101: selected = null;
102: else {
103: selected = (Component) tabs.getFirst();
104: fireSelectedTabChange();
105: }
106: }
107: requestRepaint();
108: }
109: }
110:
111: /** Add a component into this container.
112: * The component is added as a tab where its default tab-caption is
113: * the caption of the component.
114: * @param c The component to be added.
115: */
116: public void addComponent(Component c) {
117: addTab(c, c.getCaption(), getIcon());
118: }
119:
120: /** Add a new tab into TabSheet.
121: * @param c The component to be added onto tab.
122: * @param caption The caption of the tab.
123: * @param icon Set the icon of the tab.
124: */
125: public void addTab(Component c, String caption, Resource icon) {
126: if (c != null) {
127: tabs.addLast(c);
128: tabCaptions.put(c, caption != null ? caption : "");
129: if (icon != null)
130: tabIcons.put(c, icon);
131: if (selected == null) {
132: selected = c;
133: fireSelectedTabChange();
134: }
135: super .addComponent(c);
136: requestRepaint();
137: }
138: }
139:
140: /** Get component UIDL tag.
141: * @return Component UIDL tag as string.
142: */
143: public String getTag() {
144: return "tabsheet";
145: }
146:
147: /** Move all components from another container to this container.
148: * The components are removed from the other container.
149: * @param source The container components are removed from.
150: */
151: public void moveComponentsFrom(ComponentContainer source) {
152: for (Iterator i = source.getComponentIterator(); i.hasNext();) {
153: Component c = (Component) i.next();
154: String caption = null;
155: Resource icon = null;
156: if (TabSheet.class.isAssignableFrom(source.getClass())) {
157: caption = ((TabSheet) source).getTabCaption(c);
158: icon = ((TabSheet) source).getTabIcon(c);
159: }
160: source.removeComponent(c);
161: addTab(c, caption, icon);
162:
163: }
164: }
165:
166: /** Paint the content of this component.
167: * @param event PaintEvent.
168: * @throws PaintException The paint operation failed.
169: */
170: public void paintContent(PaintTarget target) throws PaintException {
171:
172: if (areTabsHidden())
173: target.addAttribute("hidetabs", true);
174:
175: target.startTag("tabs");
176:
177: for (Iterator i = getComponentIterator(); i.hasNext();) {
178: Component c = (Component) i.next();
179: if (!c.isVisible())
180: continue;
181: target.startTag("tab");
182: Resource icon = getTabIcon(c);
183: if (icon != null)
184: target.addAttribute("icon", icon);
185: String caption = getTabCaption(c);
186: if (!c.isEnabled()) {
187: target.addAttribute("disabled", true);
188: }
189:
190: if (caption != null && caption.length() > 0)
191: target.addAttribute("caption", caption);
192: target.addAttribute("key", keyMapper.key(c));
193: if (c.equals(selected)) {
194: target.addAttribute("selected", true);
195: c.paint(target);
196: }
197: target.endTag("tab");
198: }
199:
200: target.endTag("tabs");
201:
202: if (selected != null)
203: target.addVariable(this , "selected", keyMapper
204: .key(selected));
205: }
206:
207: /** Are tabs hidden.
208: * @return Property visibility
209: */
210: public boolean areTabsHidden() {
211: return this .tabsHidden;
212: }
213:
214: /** Setter for property tabsHidden.
215: * @param tabsHidden True if the tabs should be hidden.
216: */
217: public void hideTabs(boolean tabsHidden) {
218: this .tabsHidden = tabsHidden;
219: requestRepaint();
220: }
221:
222: /** Get the caption for a component */
223: public String getTabCaption(Component c) {
224: String caption = (String) tabCaptions.get(c);
225: if (caption == null)
226: caption = "";
227: return caption;
228: }
229:
230: /** Set the caption for a component */
231: public void setTabCaption(Component c, String caption) {
232: tabCaptions.put(c, caption);
233: requestRepaint();
234: }
235:
236: /** Get the icon for a component */
237: public Resource getTabIcon(Component c) {
238: return (Resource) tabIcons.get(c);
239: }
240:
241: /** Set the icon for a component */
242: public void setTabIcon(Component c, Resource icon) {
243: if (icon == null)
244: tabIcons.remove(c);
245: else
246: tabIcons.put(c, icon);
247: requestRepaint();
248: }
249:
250: /** Set the selected tab */
251: public void setSelectedTab(Component c) {
252: if (c != null && tabs.contains(c) && !selected.equals(c)) {
253: selected = c;
254: fireSelectedTabChange();
255: requestRepaint();
256: }
257: }
258:
259: /** Get the selected tab */
260: public Component getSelectedTab() {
261: return selected;
262: }
263:
264: /** Invoked when the value of a variable has changed.
265: * @param event Variable change event containing the information about
266: * the changed variable.
267: */
268: public void changeVariables(Object source, Map variables) {
269: if (variables.containsKey("selected"))
270: setSelectedTab((Component) keyMapper.get((String) variables
271: .get("selected")));
272: }
273:
274: /* Documented in superclass */
275: public void replaceComponent(Component oldComponent,
276: Component newComponent) {
277:
278: // Get the captions
279: String oldCaption = getTabCaption(oldComponent);
280: Resource oldIcon = getTabIcon(oldComponent);
281: String newCaption = getTabCaption(newComponent);
282: Resource newIcon = getTabIcon(newComponent);
283:
284: // Get the locations
285: int oldLocation = -1;
286: int newLocation = -1;
287: int location = 0;
288: for (Iterator i = tabs.iterator(); i.hasNext();) {
289: Component component = (Component) i.next();
290:
291: if (component == oldComponent)
292: oldLocation = location;
293: if (component == newComponent)
294: newLocation = location;
295:
296: location++;
297: }
298:
299: if (oldLocation == -1)
300: addComponent(newComponent);
301: else if (newLocation == -1) {
302: removeComponent(oldComponent);
303: keyMapper.remove(oldComponent);
304: addComponent(newComponent);
305: tabs.remove(newComponent);
306: tabs.add(oldLocation, newComponent);
307: setTabCaption(newComponent, oldCaption);
308: setTabIcon(newComponent, oldIcon);
309: } else {
310: if (oldLocation > newLocation) {
311: tabs.remove(oldComponent);
312: tabs.add(newLocation, oldComponent);
313: tabs.remove(newComponent);
314: tabs.add(oldLocation, newComponent);
315: } else {
316: tabs.remove(newComponent);
317: tabs.add(oldLocation, newComponent);
318: tabs.remove(oldComponent);
319: tabs.add(newLocation, oldComponent);
320: }
321: setTabCaption(newComponent, oldCaption);
322: setTabIcon(newComponent, oldIcon);
323: setTabCaption(oldComponent, newCaption);
324: setTabIcon(oldComponent, newIcon);
325:
326: requestRepaint();
327: }
328: }
329:
330: /* Click event ************************************************ */
331:
332: private static final Method SELECTED_TAB_CHANGE_METHOD;
333: static {
334: try {
335: SELECTED_TAB_CHANGE_METHOD = SelectedTabChangeListener.class
336: .getDeclaredMethod(
337: "selectedTabChange",
338: new Class[] { SelectedTabChangeEvent.class });
339: } catch (java.lang.NoSuchMethodException e) {
340: // This should never happen
341: throw new java.lang.RuntimeException();
342: }
343: }
344:
345: /** Selected Tab Change event. This event is thrown, when the selected tab
346: * in the tab sheet is changed.
347: * @author IT Mill Ltd.
348: * @version 3.1.1
349: * @since 3.0
350: */
351: public class SelectedTabChangeEvent extends Component.Event {
352:
353: /**
354: * Serial generated by eclipse.
355: */
356: private static final long serialVersionUID = 3258129141914940469L;
357:
358: /** New instance of selected tab change event
359: * @param source Source of the event.
360: */
361: public SelectedTabChangeEvent(Component source) {
362: super (source);
363: }
364:
365: /** Select where the event occurred
366: * @return Source of the event.
367: */
368: public Select getSelect() {
369: return (Select) getSource();
370: }
371: }
372:
373: /** Selected Tab Change Event listener
374: * @author IT Mill Ltd.
375: * @version 3.1.1
376: * @since 3.0
377: */
378: public interface SelectedTabChangeListener {
379:
380: /** Visible tab in tab sheet has has been changed.
381: * @param event Selected tab change event.
382: */
383: public void selectedTabChange(SelectedTabChangeEvent event);
384: }
385:
386: /** Add selected tab change listener
387: * @param listener Listener to be added.
388: */
389: public void addListener(SelectedTabChangeListener listener) {
390: addListener(SelectedTabChangeEvent.class, listener,
391: SELECTED_TAB_CHANGE_METHOD);
392: }
393:
394: /** Remove selected tab change listener
395: * @param listener Listener to be removed.
396: */
397: public void removeListener(SelectedTabChangeListener listener) {
398: removeListener(SelectedTabChangeEvent.class, listener,
399: SELECTED_TAB_CHANGE_METHOD);
400: }
401:
402: /** Emit options change event. */
403: protected void fireSelectedTabChange() {
404: fireEvent(new SelectedTabChangeEvent(this));
405: }
406: }
|