001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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: * Michael Williamson (eclipse-bugs@magnaworks.com) - patch (see Bugzilla #92545)
011: *
012: *******************************************************************************/package org.eclipse.ui.forms.widgets;
013:
014: import java.util.Hashtable;
015:
016: import org.eclipse.core.runtime.Assert;
017: import org.eclipse.swt.SWT;
018: import org.eclipse.swt.events.PaintEvent;
019: import org.eclipse.swt.graphics.Color;
020: import org.eclipse.swt.graphics.GC;
021: import org.eclipse.swt.graphics.Image;
022: import org.eclipse.swt.graphics.Point;
023: import org.eclipse.swt.graphics.Rectangle;
024: import org.eclipse.swt.widgets.Composite;
025: import org.eclipse.swt.widgets.Control;
026: import org.eclipse.swt.widgets.Event;
027: import org.eclipse.swt.widgets.Listener;
028: import org.eclipse.swt.widgets.Text;
029: import org.eclipse.ui.internal.forms.widgets.FormImages;
030: import org.eclipse.ui.internal.forms.widgets.FormUtil;
031:
032: /**
033: * A variation of the expandable composite that adds optional description below
034: * the title. Section is often used as a basic building block in forms because
035: * it provides for logical grouping of information.
036: * <p>
037: * In case of the TITLE_BAR style, Section renders the title bar in a way
038: * compatible with the rest of the workbench. Since it is a widget, all the
039: * colors must be supplied directly. When created by the form toolkit, these
040: * colors are supplied by the toolkit. The toolkit initializes these colors
041: * based on the system colors. For this reason, it is recommended to create the
042: * section by the toolkit instead of through its own constructor.
043: * <p>
044: * Since 3.1, it is possible to set a control to be used for section
045: * description. If used, <code>DESCRIPTION</code> style should not be set. A
046: * typical way to take advantage of the new method is to set an instance of
047: * <code>FormText</code> to provide for hyperlinks and images in the
048: * description area.
049: *
050: * @since 3.0
051: */
052: public class Section extends ExpandableComposite {
053: /**
054: * Description style. If used, description will be rendered below the title.
055: */
056: public static final int DESCRIPTION = 1 << 7;
057:
058: private Control descriptionControl;
059:
060: private Control separator;
061:
062: private Hashtable titleColors;
063:
064: private static final String COLOR_BG = "bg"; //$NON-NLS-1$
065:
066: private static final String COLOR_GBG = "gbg"; //$NON-NLS-1$
067:
068: private static final String COLOR_BORDER = "border"; //$NON-NLS-1$
069:
070: /**
071: * Creates a new section instance in the provided parent.
072: *
073: * @param parent
074: * the parent composite
075: * @param style
076: * the style to use
077: */
078: public Section(Composite parent, int style) {
079: this (parent, SWT.NULL, style);
080: }
081:
082: Section(Composite parent, int cstyle, int style) {
083: super (parent, cstyle | getBackgroundStyle(style), style);
084: int rtl = cstyle & SWT.RIGHT_TO_LEFT;
085: if ((style & DESCRIPTION) != 0) {
086: descriptionControl = new Text(this , SWT.READ_ONLY
087: | SWT.WRAP | rtl);
088: }
089: if ((style & TITLE_BAR) != 0) {
090: Listener listener = new Listener() {
091: public void handleEvent(Event e) {
092: Image image = Section.super .getBackgroundImage();
093: if (image != null) {
094: FormImages.getInstance().markFinished(image);
095: }
096: Section.super .setBackgroundImage(null);
097: }
098: };
099: addListener(SWT.Dispose, listener);
100: addListener(SWT.Resize, listener);
101: }
102: }
103:
104: private static int getBackgroundStyle(int estyle) {
105: return ((estyle & TITLE_BAR) != 0) ? SWT.NO_BACKGROUND
106: : SWT.NULL;
107: }
108:
109: protected void internalSetExpanded(boolean expanded) {
110: super .internalSetExpanded(expanded);
111: if ((getExpansionStyle() & TITLE_BAR) != 0) {
112: if (!expanded)
113: super .setBackgroundImage(null);
114: }
115: reflow();
116: }
117:
118: /**
119: * Reflows this section and all the parents up the hierarchy until a
120: * ScrolledForm is reached.
121: */
122: protected void reflow() {
123: Composite c = this ;
124: while (c != null) {
125: c.setRedraw(false);
126: c = c.getParent();
127: if (c instanceof ScrolledForm) {
128: break;
129: }
130: }
131: c = this ;
132: while (c != null) {
133: c.layout(true);
134: c = c.getParent();
135: if (c instanceof ScrolledForm) {
136: ((ScrolledForm) c).reflow(true);
137: break;
138: }
139: }
140: c = this ;
141: while (c != null) {
142: c.setRedraw(true);
143: c = c.getParent();
144: if (c instanceof ScrolledForm) {
145: break;
146: }
147: }
148: }
149:
150: /**
151: * Sets the description text. Has no effect if DESCRIPTION style was not
152: * used to create the control.
153: *
154: * @param description
155: */
156: public void setDescription(String description) {
157: if (descriptionControl instanceof Text)
158: ((Text) descriptionControl).setText(description);
159: }
160:
161: /**
162: * Returns the current description text.
163: *
164: * @return description text or <code>null</code> if DESCRIPTION style was
165: * not used to create the control.
166: */
167: public String getDescription() {
168: if (descriptionControl instanceof Text)
169: return ((Text) descriptionControl).getText();
170: return null;
171: }
172:
173: /**
174: * Sets the separator control of this section. The separator must not be
175: * <samp>null </samp> and must be a direct child of this container. If
176: * defined, separator will be placed below the title text and will remain
177: * visible regardless of the expansion state.
178: *
179: * @param separator
180: * the separator that will be placed below the title text.
181: */
182: public void setSeparatorControl(Control separator) {
183: Assert.isTrue(separator != null
184: && separator.getParent().equals(this ));
185: this .separator = separator;
186: }
187:
188: /**
189: * Returns the control that is used as a separator betweeen the title and
190: * the client, or <samp>null </samp> if not set.
191: *
192: * @return separator control or <samp>null </samp> if not set.
193: */
194: public Control getSeparatorControl() {
195: return separator;
196: }
197:
198: /**
199: * Sets the background of the section.
200: *
201: * @param bg
202: * the new background
203: */
204: public void setBackground(Color bg) {
205: super .setBackground(bg);
206: if (descriptionControl != null
207: && (getExpansionStyle() & DESCRIPTION) != 0)
208: descriptionControl.setBackground(bg);
209: }
210:
211: /**
212: * Sets the foreground of the section.
213: *
214: * @param fg
215: * the new foreground.
216: */
217: public void setForeground(Color fg) {
218: super .setForeground(fg);
219: if (descriptionControl != null
220: && (getExpansionStyle() & DESCRIPTION) != 0)
221: descriptionControl.setForeground(fg);
222: }
223:
224: /**
225: * Returns the control used to render the description. In 3.1, this method
226: * was promoted to public.
227: *
228: * @return description control or <code>null</code> if DESCRIPTION style
229: * was not used to create the control and description control was
230: * not set by the client.
231: * @see #setDescriptionControl(org.eclipse.swt.widgets.Control)
232: */
233: public Control getDescriptionControl() {
234: return descriptionControl;
235: }
236:
237: /**
238: * Sets the description control of this section. The control must not be
239: * <samp>null</samp> and must be a direct child of this container. If
240: * defined, contol will be placed below the title text and the separator and
241: * will be hidden int he collapsed state.
242: * <p>
243: * This method and <code>DESCRIPTION</code> style are mutually exclusive.
244: * Use the method only if you want to create the description control
245: * yourself.
246: *
247: * @since 3.1
248: * @param descriptionControl
249: * the control that will be placed below the title text.
250: */
251: public void setDescriptionControl(Control descriptionControl) {
252: Assert.isTrue((getExpansionStyle() & DESCRIPTION) == 0);
253: Assert.isTrue(descriptionControl != null
254: && descriptionControl.getParent().equals(this ));
255: this .descriptionControl = descriptionControl;
256: }
257:
258: /**
259: * Sets the color of the title bar border when TITLE_BAR style is used.
260: *
261: * @param color
262: * the title bar border color
263: */
264: public void setTitleBarBorderColor(Color color) {
265: putTitleBarColor(COLOR_BORDER, color);
266: }
267:
268: /**
269: * Sets the color of the title bar background when TITLE_BAR style is used.
270: * This color is used as a starting color for the vertical gradient.
271: *
272: * @param color
273: * the title bar border background
274: */
275: public void setTitleBarBackground(Color color) {
276: putTitleBarColor(COLOR_BG, color);
277: }
278:
279: /**
280: * Sets the color of the title bar gradient background when TITLE_BAR style
281: * is used. This color is used at the height where title controls end
282: * (toggle, tool bar).
283: *
284: * @param color
285: * the title bar gradient background
286: */
287: public void setTitleBarGradientBackground(Color color) {
288: putTitleBarColor(COLOR_GBG, color);
289: }
290:
291: /**
292: * Returns the title bar border color when TITLE_BAR style is used.
293: *
294: * @return the title bar border color
295: */
296: public Color getTitleBarBorderColor() {
297: if (titleColors == null)
298: return null;
299: return (Color) titleColors.get(COLOR_BORDER);
300: }
301:
302: /**
303: * Returns the title bar gradient background color when TITLE_BAR style is
304: * used.
305: *
306: * @return the title bar gradient background
307: */
308: public Color getTitleBarGradientBackground() {
309: if (titleColors == null)
310: return null;
311: if ((getExpansionStyle() & SHORT_TITLE_BAR) != 0)
312: return getBackground();
313: return (Color) titleColors.get(COLOR_GBG);
314: }
315:
316: /**
317: * Returns the title bar background when TITLE_BAR style is used.
318: *
319: * @return the title bar background
320: */
321: public Color getTitleBarBackground() {
322: if (titleColors == null)
323: return null;
324: return (Color) titleColors.get(COLOR_BG);
325: }
326:
327: private void putTitleBarColor(String key, Color color) {
328: if (color == null)
329: return;
330: if (titleColors == null)
331: titleColors = new Hashtable();
332: titleColors.put(key, color);
333: }
334:
335: protected void onPaint(PaintEvent e) {
336: Color bg = null;
337: Color fg = null;
338: Color border = null;
339:
340: GC gc = e.gc;
341: Image buffer = null;
342: Rectangle bounds = getClientArea();
343:
344: if ((getExpansionStyle() & TITLE_BAR) != 0) {
345: buffer = new Image(getDisplay(), bounds.width,
346: bounds.height);
347: buffer.setBackground(getBackground());
348: gc = new GC(buffer);
349: }
350: if (titleColors != null) {
351: bg = (Color) titleColors.get(COLOR_BG);
352: fg = getTitleBarForeground();
353: border = (Color) titleColors.get(COLOR_BORDER);
354: }
355: if (bg == null)
356: bg = getBackground();
357: if (fg == null)
358: fg = getForeground();
359: if (border == null)
360: border = fg;
361: int theight = 0;
362: int gradientheight = 0;
363: int tvmargin = IGAP;
364: if ((getExpansionStyle() & TITLE_BAR) != 0) {
365: Point tsize = null;
366: Point tcsize = null;
367: if (toggle != null)
368: tsize = toggle.getSize();
369: int twidth = bounds.width - marginWidth - marginWidth;
370: if (tsize != null)
371: twidth -= tsize.x + IGAP;
372: if (getTextClient() != null)
373: tcsize = getTextClient().getSize();
374: if (tcsize != null)
375: twidth -= tcsize.x + IGAP;
376: Point size = textLabel.getSize();
377: if (tsize != null)
378: theight += Math.max(theight, tsize.y);
379: gradientheight = theight;
380: if (tcsize != null) {
381: theight = Math.max(theight, tcsize.y);
382: }
383: theight = Math.max(theight, size.y);
384: gradientheight = Math.max(gradientheight, size.y);
385: theight += tvmargin + tvmargin;
386: gradientheight += tvmargin + tvmargin;
387: } else {
388: theight = 5;
389: }
390: if ((getExpansionStyle() & TITLE_BAR) != 0) {
391: if (getBackgroundImage() == null)
392: updateHeaderImage(bg, bounds, gradientheight, theight);
393: gc.setBackground(getBackground());
394: gc.fillRectangle(bounds.x, bounds.y, bounds.width,
395: bounds.height);
396: drawBackground(gc, bounds.x, bounds.y, bounds.width,
397: theight);
398: if (marginWidth > 0) {
399: // fix up margins
400: gc.setBackground(getBackground());
401: gc.fillRectangle(0, 0, marginWidth, theight);
402: gc.fillRectangle(bounds.x + bounds.width - marginWidth,
403: 0, marginWidth, theight);
404: }
405: } else if (isExpanded()) {
406: gc.setForeground(bg);
407: gc.setBackground(getBackground());
408: gc.fillGradientRectangle(marginWidth, marginHeight,
409: bounds.width - marginWidth - marginWidth, theight,
410: true);
411: }
412: gc.setBackground(getBackground());
413: FormUtil.setAntialias(gc, SWT.ON);
414: // repair the upper left corner
415: gc.fillPolygon(new int[] { marginWidth, marginHeight,
416: marginWidth, marginHeight + 2, marginWidth + 2,
417: marginHeight });
418: // repair the upper right corner
419: gc.fillPolygon(new int[] { bounds.width - marginWidth - 3,
420: marginHeight, bounds.width - marginWidth, marginHeight,
421: bounds.width - marginWidth, marginHeight + 3 });
422: gc.setForeground(border);
423: if (isExpanded() || (getExpansionStyle() & TITLE_BAR) != 0) {
424: // top left curve
425: gc.drawLine(marginWidth, marginHeight + 2, marginWidth + 2,
426: marginHeight);
427: // top edge
428: gc.drawLine(marginWidth + 2, marginHeight, bounds.width
429: - marginWidth - 3, marginHeight);
430: // top right curve
431: gc.drawLine(bounds.width - marginWidth - 3, marginHeight,
432: bounds.width - marginWidth - 1, marginHeight + 2);
433: } else {
434: // collapsed short title bar
435: // top edge
436: gc.drawLine(marginWidth, marginHeight, bounds.width - 1,
437: marginHeight);
438: }
439: if ((getExpansionStyle() & TITLE_BAR) != 0 || isExpanded()) {
440: // left vertical edge gradient
441: gc.fillGradientRectangle(marginWidth, marginHeight + 2, 1,
442: gradientheight - 2, true);
443: // right vertical edge gradient
444: gc.fillGradientRectangle(bounds.width - marginWidth - 1,
445: marginHeight + 2, 1, gradientheight - 2, true);
446: }
447: if ((getExpansionStyle() & TITLE_BAR) != 0) {
448: // New in 3.3 - edge treatmant
449: gc.setForeground(getDisplay().getSystemColor(
450: SWT.COLOR_WHITE));
451: gc.drawPolyline(new int[] { marginWidth + 1,
452: marginHeight + gradientheight - 1, marginWidth + 1,
453: marginHeight + 2, marginWidth + 2,
454: marginHeight + 2, marginWidth + 2,
455: marginHeight + 1, bounds.width - marginWidth - 3,
456: marginHeight + 1, bounds.width - marginWidth - 3,
457: marginHeight + 2, bounds.width - marginWidth - 2,
458: marginHeight + 2, bounds.width - marginWidth - 2,
459: marginHeight + gradientheight - 1 });
460: }
461: if (buffer != null) {
462: gc.dispose();
463: e.gc.drawImage(buffer, 0, 0);
464: buffer.dispose();
465: }
466: }
467:
468: private void updateHeaderImage(Color bg, Rectangle bounds,
469: int theight, int realtheight) {
470: Image image = FormImages.getInstance().getGradient(
471: getDisplay(), getBackground(), bg, realtheight,
472: theight, marginHeight);
473: super .setBackgroundImage(image);
474: }
475:
476: /**
477: * Background image is used for the title gradient - does nothing.
478: */
479: public final void setBackgroundImage(Image image) {
480: }
481: }
|