001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.forms;
011:
012: import java.util.Enumeration;
013: import java.util.Hashtable;
014: import java.util.Iterator;
015:
016: import org.eclipse.jface.viewers.*;
017: import org.eclipse.swt.SWT;
018: import org.eclipse.swt.custom.BusyIndicator;
019: import org.eclipse.swt.widgets.*;
020: import org.eclipse.ui.forms.widgets.ScrolledPageBook;
021:
022: /**
023: * This managed form part handles the 'details' portion of the
024: * 'master/details' block. It has a page book that manages pages
025: * of details registered for the current selection.
026: * <p>By default, details part accepts any number of pages.
027: * If dynamic page provider is registered, this number may
028: * be excessive. To avoid running out of steam (by creating
029: * a large number of pages with widgets on each), maximum
030: * number of pages can be set to some reasonable value (e.g. 10).
031: * When this number is reached, old pages (those created first)
032: * will be removed and disposed as new ones are added. If
033: * the disposed pages are needed again after that, they
034: * will be created again.
035: *
036: * @since 3.0
037: */
038: public final class DetailsPart implements IFormPart,
039: IPartSelectionListener {
040: private IManagedForm managedForm;
041: private ScrolledPageBook pageBook;
042: private IFormPart masterPart;
043: private IStructuredSelection currentSelection;
044: private Hashtable pages;
045: private IDetailsPageProvider pageProvider;
046: private int pageLimit = Integer.MAX_VALUE;
047:
048: private static class PageBag {
049: private static int counter;
050: private int ticket;
051: private IDetailsPage page;
052: private boolean fixed;
053:
054: public PageBag(IDetailsPage page, boolean fixed) {
055: this .page = page;
056: this .fixed = fixed;
057: this .ticket = ++counter;
058: }
059:
060: public int getTicket() {
061: return ticket;
062: }
063:
064: public IDetailsPage getPage() {
065: return page;
066: }
067:
068: public void dispose() {
069: page.dispose();
070: page = null;
071: }
072:
073: public boolean isDisposed() {
074: return page == null;
075: }
076:
077: public boolean isFixed() {
078: return fixed;
079: }
080:
081: public static int getCurrentTicket() {
082: return counter;
083: }
084: }
085:
086: /**
087: * Creates a details part by wrapping the provided page book.
088: * @param mform the parent form
089: * @param pageBook the page book to wrap
090: */
091: public DetailsPart(IManagedForm mform, ScrolledPageBook pageBook) {
092: this .pageBook = pageBook;
093: pages = new Hashtable();
094: initialize(mform);
095: }
096:
097: /**
098: * Creates a new details part in the provided form by creating
099: * the page book.
100: * @param mform the parent form
101: * @param parent the composite to create the page book in
102: * @param style the style for the page book
103: */
104: public DetailsPart(IManagedForm mform, Composite parent, int style) {
105: this (mform, mform.getToolkit().createPageBook(parent,
106: style | SWT.V_SCROLL | SWT.H_SCROLL));
107: }
108:
109: /**
110: * Registers the details page to be used for all the objects of
111: * the provided object class.
112: * @param objectClass an object of type 'java.lang.Class' to be used
113: * as a key for the provided page
114: * @param page the page to show for objects of the provided object class
115: */
116: public void registerPage(Object objectClass, IDetailsPage page) {
117: registerPage(objectClass, page, true);
118: }
119:
120: private void registerPage(Object objectClass, IDetailsPage page,
121: boolean fixed) {
122: pages.put(objectClass, new PageBag(page, fixed));
123: page.initialize(managedForm);
124: }
125:
126: /**
127: * Sets the dynamic page provider. The dynamic provider can return
128: * different pages for objects of the same class based on their state.
129: * @param provider the provider to use
130: */
131: public void setPageProvider(IDetailsPageProvider provider) {
132: this .pageProvider = provider;
133: }
134:
135: /**
136: * Commits the part by committing the current page.
137: * @param onSave <code>true</code> if commit is requested as a result
138: * of the 'save' action, <code>false</code> otherwise.
139: */
140: public void commit(boolean onSave) {
141: IDetailsPage page = getCurrentPage();
142: if (page != null)
143: page.commit(onSave);
144: }
145:
146: /**
147: * Returns the current page visible in the part.
148: * @return the current page
149: */
150: public IDetailsPage getCurrentPage() {
151: Control control = pageBook.getCurrentPage();
152: if (control != null) {
153: Object data = control.getData();
154: if (data instanceof IDetailsPage)
155: return (IDetailsPage) data;
156: }
157: return null;
158: }
159:
160: /*
161: * (non-Javadoc)
162: *
163: * @see org.eclipse.ui.forms.IFormPart#dispose()
164: */
165: public void dispose() {
166: for (Enumeration enm = pages.elements(); enm.hasMoreElements();) {
167: PageBag pageBag = (PageBag) enm.nextElement();
168: pageBag.dispose();
169: }
170: }
171:
172: /*
173: * (non-Javadoc)
174: *
175: * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
176: */
177: public void initialize(IManagedForm form) {
178: this .managedForm = form;
179: }
180:
181: /**
182: * Tests if the currently visible page is dirty.
183: * @return <code>true</code> if the page is dirty, <code>false</code> otherwise.
184: */
185: public boolean isDirty() {
186: IDetailsPage page = getCurrentPage();
187: if (page != null)
188: return page.isDirty();
189: return false;
190: }
191:
192: /**
193: * Tests if the currently visible page is stale and needs refreshing.
194: * @return <code>true</code> if the page is stale, <code>false</code> otherwise.
195: */
196: public boolean isStale() {
197: IDetailsPage page = getCurrentPage();
198: if (page != null)
199: return page.isStale();
200: return false;
201: }
202:
203: /**
204: * Refreshes the current page.
205: */
206: public void refresh() {
207: IDetailsPage page = getCurrentPage();
208: if (page != null)
209: page.refresh();
210: }
211:
212: /**
213: * Sets the focus to the currently visible page.
214: */
215: public void setFocus() {
216: IDetailsPage page = getCurrentPage();
217: if (page != null)
218: page.setFocus();
219: }
220:
221: /*
222: * (non-Javadoc)
223: *
224: * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
225: */
226: public boolean setFormInput(Object input) {
227: return false;
228: }
229:
230: /*
231: * (non-Javadoc)
232: *
233: * @see org.eclipse.ui.forms.IPartSelectionListener#selectionChanged(org.eclipse.ui.forms.IFormPart,
234: * org.eclipse.jface.viewers.ISelection)
235: */
236: public void selectionChanged(IFormPart part, ISelection selection) {
237: this .masterPart = part;
238: if (currentSelection != null) {
239: }
240: if (selection instanceof IStructuredSelection)
241: currentSelection = (IStructuredSelection) selection;
242: else
243: currentSelection = null;
244: update();
245: }
246:
247: private void update() {
248: Object key = null;
249: if (currentSelection != null) {
250: for (Iterator iter = currentSelection.iterator(); iter
251: .hasNext();) {
252: Object obj = iter.next();
253: if (key == null)
254: key = getKey(obj);
255: else if (getKey(obj).equals(key) == false) {
256: key = null;
257: break;
258: }
259: }
260: }
261: showPage(key);
262: }
263:
264: private Object getKey(Object object) {
265: if (pageProvider != null) {
266: Object key = pageProvider.getPageKey(object);
267: if (key != null)
268: return key;
269: }
270: return object.getClass();
271: }
272:
273: private void showPage(final Object key) {
274: checkLimit();
275: final IDetailsPage oldPage = getCurrentPage();
276: if (key != null) {
277: PageBag pageBag = (PageBag) pages.get(key);
278: IDetailsPage page = pageBag != null ? pageBag.getPage()
279: : null;
280: if (page == null) {
281: // try to get the page dynamically from the provider
282: if (pageProvider != null) {
283: page = pageProvider.getPage(key);
284: if (page != null) {
285: registerPage(key, page, false);
286: }
287: }
288: }
289: if (page != null) {
290: final IDetailsPage fpage = page;
291: BusyIndicator.showWhile(pageBook.getDisplay(),
292: new Runnable() {
293: public void run() {
294: if (!pageBook.hasPage(key)) {
295: Composite parent = pageBook
296: .createPage(key);
297: fpage.createContents(parent);
298: parent.setData(fpage);
299: }
300: //commit the current page
301: if (oldPage != null
302: && oldPage.isDirty())
303: oldPage.commit(false);
304: //refresh the new page
305: if (fpage.isStale())
306: fpage.refresh();
307: fpage.selectionChanged(masterPart,
308: currentSelection);
309: pageBook.showPage(key);
310: }
311: });
312: return;
313: }
314: }
315: // If we are switching from an old page to nothing,
316: // don't loose data
317: if (oldPage != null && oldPage.isDirty())
318: oldPage.commit(false);
319: pageBook.showEmptyPage();
320: }
321:
322: private void checkLimit() {
323: if (pages.size() <= getPageLimit())
324: return;
325: // overflow
326: int currentTicket = PageBag.getCurrentTicket();
327: int cutoffTicket = currentTicket - getPageLimit();
328: for (Enumeration enm = pages.keys(); enm.hasMoreElements();) {
329: Object key = enm.nextElement();
330: PageBag pageBag = (PageBag) pages.get(key);
331: if (pageBag.getTicket() <= cutoffTicket) {
332: // candidate - see if it is active and not fixed
333: if (!pageBag.isFixed()
334: && !pageBag.getPage().equals(getCurrentPage())) {
335: // drop it
336: pageBag.dispose();
337: pages.remove(key);
338: pageBook.removePage(key, false);
339: }
340: }
341: }
342: }
343:
344: /**
345: * Returns the maximum number of pages that should be
346: * maintained in this part. When an attempt is made to
347: * add more pages, old pages are removed and disposed
348: * based on the order of creation (the oldest pages
349: * are removed). The exception is made for the
350: * page that should otherwise be disposed but is
351: * currently active.
352: * @return maximum number of pages for this part
353: */
354: public int getPageLimit() {
355: return pageLimit;
356: }
357:
358: /**
359: * Sets the page limit for this part.
360: * @see #getPageLimit()
361: * @param pageLimit the maximum number of pages that
362: * should be maintained in this part.
363: */
364: public void setPageLimit(int pageLimit) {
365: this.pageLimit = pageLimit;
366: checkLimit();
367: }
368: }
|