001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.sample.kitchensink.client;
017:
018: import com.google.gwt.core.client.GWT;
019: import com.google.gwt.i18n.client.Constants;
020: import com.google.gwt.user.client.Window;
021: import com.google.gwt.user.client.ui.AbstractImagePrototype;
022: import com.google.gwt.user.client.ui.ChangeListener;
023: import com.google.gwt.user.client.ui.ClickListener;
024: import com.google.gwt.user.client.ui.Composite;
025: import com.google.gwt.user.client.ui.HorizontalPanel;
026: import com.google.gwt.user.client.ui.ImageBundle;
027: import com.google.gwt.user.client.ui.KeyboardListener;
028: import com.google.gwt.user.client.ui.ListBox;
029: import com.google.gwt.user.client.ui.PushButton;
030: import com.google.gwt.user.client.ui.RichTextArea;
031: import com.google.gwt.user.client.ui.ToggleButton;
032: import com.google.gwt.user.client.ui.VerticalPanel;
033: import com.google.gwt.user.client.ui.Widget;
034:
035: /**
036: * A sample toolbar for use with {@link RichTextArea}. It provides a simple UI
037: * for all rich text formatting, dynamically displayed only for the available
038: * functionality.
039: */
040: public class RichTextToolbar extends Composite {
041:
042: /**
043: * This {@link ImageBundle} is used for all the button icons. Using an image
044: * bundle allows all of these images to be packed into a single image, which
045: * saves a lot of HTTP requests, drastically improving startup time.
046: */
047: public interface Images extends ImageBundle {
048:
049: /**
050: * @gwt.resource bold.gif
051: */
052: AbstractImagePrototype bold();
053:
054: /**
055: * @gwt.resource createLink.gif
056: */
057: AbstractImagePrototype createLink();
058:
059: /**
060: * @gwt.resource hr.gif
061: */
062: AbstractImagePrototype hr();
063:
064: /**
065: * @gwt.resource indent.gif
066: */
067: AbstractImagePrototype indent();
068:
069: /**
070: * @gwt.resource insertImage.gif
071: */
072: AbstractImagePrototype insertImage();
073:
074: /**
075: * @gwt.resource italic.gif
076: */
077: AbstractImagePrototype italic();
078:
079: /**
080: * @gwt.resource justifyCenter.gif
081: */
082: AbstractImagePrototype justifyCenter();
083:
084: /**
085: * @gwt.resource justifyLeft.gif
086: */
087: AbstractImagePrototype justifyLeft();
088:
089: /**
090: * @gwt.resource justifyRight.gif
091: */
092: AbstractImagePrototype justifyRight();
093:
094: /**
095: * @gwt.resource ol.gif
096: */
097: AbstractImagePrototype ol();
098:
099: /**
100: * @gwt.resource outdent.gif
101: */
102: AbstractImagePrototype outdent();
103:
104: /**
105: * @gwt.resource removeFormat.gif
106: */
107: AbstractImagePrototype removeFormat();
108:
109: /**
110: * @gwt.resource removeLink.gif
111: */
112: AbstractImagePrototype removeLink();
113:
114: /**
115: * @gwt.resource strikeThrough.gif
116: */
117: AbstractImagePrototype strikeThrough();
118:
119: /**
120: * @gwt.resource subscript.gif
121: */
122: AbstractImagePrototype subscript();
123:
124: /**
125: * @gwt.resource superscript.gif
126: */
127: AbstractImagePrototype super script();
128:
129: /**
130: * @gwt.resource ul.gif
131: */
132: AbstractImagePrototype ul();
133:
134: /**
135: * @gwt.resource underline.gif
136: */
137: AbstractImagePrototype underline();
138: }
139:
140: /**
141: * This {@link Constants} interface is used to make the toolbar's strings
142: * internationalizable.
143: */
144: public interface Strings extends Constants {
145:
146: String black();
147:
148: String blue();
149:
150: String bold();
151:
152: String color();
153:
154: String createLink();
155:
156: String font();
157:
158: String green();
159:
160: String hr();
161:
162: String indent();
163:
164: String insertImage();
165:
166: String italic();
167:
168: String justifyCenter();
169:
170: String justifyLeft();
171:
172: String justifyRight();
173:
174: String large();
175:
176: String medium();
177:
178: String normal();
179:
180: String ol();
181:
182: String outdent();
183:
184: String red();
185:
186: String removeFormat();
187:
188: String removeLink();
189:
190: String size();
191:
192: String small();
193:
194: String strikeThrough();
195:
196: String subscript();
197:
198: String super script();
199:
200: String ul();
201:
202: String underline();
203:
204: String white();
205:
206: String xlarge();
207:
208: String xsmall();
209:
210: String xxlarge();
211:
212: String xxsmall();
213:
214: String yellow();
215: }
216:
217: /**
218: * We use an inner EventListener class to avoid exposing event methods on the
219: * RichTextToolbar itself.
220: */
221: private class EventListener implements ClickListener,
222: ChangeListener, KeyboardListener {
223:
224: public void onChange(Widget sender) {
225: if (sender == backColors) {
226: basic.setBackColor(backColors.getValue(backColors
227: .getSelectedIndex()));
228: backColors.setSelectedIndex(0);
229: } else if (sender == foreColors) {
230: basic.setForeColor(foreColors.getValue(foreColors
231: .getSelectedIndex()));
232: foreColors.setSelectedIndex(0);
233: } else if (sender == fonts) {
234: basic.setFontName(fonts.getValue(fonts
235: .getSelectedIndex()));
236: fonts.setSelectedIndex(0);
237: } else if (sender == fontSizes) {
238: basic.setFontSize(fontSizesConstants[fontSizes
239: .getSelectedIndex() - 1]);
240: fontSizes.setSelectedIndex(0);
241: }
242: }
243:
244: public void onClick(Widget sender) {
245: if (sender == bold) {
246: basic.toggleBold();
247: } else if (sender == italic) {
248: basic.toggleItalic();
249: } else if (sender == underline) {
250: basic.toggleUnderline();
251: } else if (sender == subscript) {
252: basic.toggleSubscript();
253: } else if (sender == super script) {
254: basic.toggleSuperscript();
255: } else if (sender == strikethrough) {
256: extended.toggleStrikethrough();
257: } else if (sender == indent) {
258: extended.rightIndent();
259: } else if (sender == outdent) {
260: extended.leftIndent();
261: } else if (sender == justifyLeft) {
262: basic.setJustification(RichTextArea.Justification.LEFT);
263: } else if (sender == justifyCenter) {
264: basic
265: .setJustification(RichTextArea.Justification.CENTER);
266: } else if (sender == justifyRight) {
267: basic
268: .setJustification(RichTextArea.Justification.RIGHT);
269: } else if (sender == insertImage) {
270: String url = Window.prompt("Enter an image URL:",
271: "http://");
272: if (url != null) {
273: extended.insertImage(url);
274: }
275: } else if (sender == createLink) {
276: String url = Window.prompt("Enter a link URL:",
277: "http://");
278: if (url != null) {
279: extended.createLink(url);
280: }
281: } else if (sender == removeLink) {
282: extended.removeLink();
283: } else if (sender == hr) {
284: extended.insertHorizontalRule();
285: } else if (sender == ol) {
286: extended.insertOrderedList();
287: } else if (sender == ul) {
288: extended.insertUnorderedList();
289: } else if (sender == removeFormat) {
290: extended.removeFormat();
291: } else if (sender == richText) {
292: // We use the RichTextArea's onKeyUp event to update the toolbar status.
293: // This will catch any cases where the user moves the cursur using the
294: // keyboard, or uses one of the browser's built-in keyboard shortcuts.
295: updateStatus();
296: }
297: }
298:
299: public void onKeyDown(Widget sender, char keyCode, int modifiers) {
300: }
301:
302: public void onKeyPress(Widget sender, char keyCode,
303: int modifiers) {
304: }
305:
306: public void onKeyUp(Widget sender, char keyCode, int modifiers) {
307: if (sender == richText) {
308: // We use the RichTextArea's onKeyUp event to update the toolbar status.
309: // This will catch any cases where the user moves the cursur using the
310: // keyboard, or uses one of the browser's built-in keyboard shortcuts.
311: updateStatus();
312: }
313: }
314: }
315:
316: private static final RichTextArea.FontSize[] fontSizesConstants = new RichTextArea.FontSize[] {
317: RichTextArea.FontSize.XX_SMALL,
318: RichTextArea.FontSize.X_SMALL, RichTextArea.FontSize.SMALL,
319: RichTextArea.FontSize.MEDIUM, RichTextArea.FontSize.LARGE,
320: RichTextArea.FontSize.X_LARGE,
321: RichTextArea.FontSize.XX_LARGE };
322:
323: private Images images = GWT.create(Images.class);
324: private Strings strings = GWT.create(Strings.class);
325: private EventListener listener = new EventListener();
326:
327: private RichTextArea richText;
328: private RichTextArea.BasicFormatter basic;
329: private RichTextArea.ExtendedFormatter extended;
330:
331: private VerticalPanel outer = new VerticalPanel();
332: private HorizontalPanel topPanel = new HorizontalPanel();
333: private HorizontalPanel bottomPanel = new HorizontalPanel();
334: private ToggleButton bold;
335: private ToggleButton italic;
336: private ToggleButton underline;
337: private ToggleButton subscript;
338: private ToggleButton super script;
339: private ToggleButton strikethrough;
340: private PushButton indent;
341: private PushButton outdent;
342: private PushButton justifyLeft;
343: private PushButton justifyCenter;
344: private PushButton justifyRight;
345: private PushButton hr;
346: private PushButton ol;
347: private PushButton ul;
348: private PushButton insertImage;
349: private PushButton createLink;
350: private PushButton removeLink;
351: private PushButton removeFormat;
352:
353: private ListBox backColors;
354: private ListBox foreColors;
355: private ListBox fonts;
356: private ListBox fontSizes;
357:
358: /**
359: * Creates a new toolbar that drives the given rich text area.
360: *
361: * @param richText the rich text area to be controlled
362: */
363: public RichTextToolbar(RichTextArea richText) {
364: this .richText = richText;
365: this .basic = richText.getBasicFormatter();
366: this .extended = richText.getExtendedFormatter();
367:
368: outer.add(topPanel);
369: outer.add(bottomPanel);
370: topPanel.setWidth("100%");
371: bottomPanel.setWidth("100%");
372:
373: initWidget(outer);
374: setStyleName("gwt-RichTextToolbar");
375:
376: if (basic != null) {
377: topPanel.add(bold = createToggleButton(images.bold(),
378: strings.bold()));
379: topPanel.add(italic = createToggleButton(images.italic(),
380: strings.italic()));
381: topPanel.add(underline = createToggleButton(images
382: .underline(), strings.underline()));
383: topPanel.add(subscript = createToggleButton(images
384: .subscript(), strings.subscript()));
385: topPanel.add(super script = createToggleButton(images
386: .super script(), strings.super script()));
387: topPanel.add(justifyLeft = createPushButton(images
388: .justifyLeft(), strings.justifyLeft()));
389: topPanel.add(justifyCenter = createPushButton(images
390: .justifyCenter(), strings.justifyCenter()));
391: topPanel.add(justifyRight = createPushButton(images
392: .justifyRight(), strings.justifyRight()));
393: }
394:
395: if (extended != null) {
396: topPanel.add(strikethrough = createToggleButton(images
397: .strikeThrough(), strings.strikeThrough()));
398: topPanel.add(indent = createPushButton(images.indent(),
399: strings.indent()));
400: topPanel.add(outdent = createPushButton(images.outdent(),
401: strings.outdent()));
402: topPanel.add(hr = createPushButton(images.hr(), strings
403: .hr()));
404: topPanel.add(ol = createPushButton(images.ol(), strings
405: .ol()));
406: topPanel.add(ul = createPushButton(images.ul(), strings
407: .ul()));
408: topPanel.add(insertImage = createPushButton(images
409: .insertImage(), strings.insertImage()));
410: topPanel.add(createLink = createPushButton(images
411: .createLink(), strings.createLink()));
412: topPanel.add(removeLink = createPushButton(images
413: .removeLink(), strings.removeLink()));
414: topPanel.add(removeFormat = createPushButton(images
415: .removeFormat(), strings.removeFormat()));
416: }
417:
418: if (basic != null) {
419: bottomPanel.add(backColors = createColorList("Background"));
420: bottomPanel.add(foreColors = createColorList("Foreground"));
421: bottomPanel.add(fonts = createFontList());
422: bottomPanel.add(fontSizes = createFontSizes());
423:
424: // We only use these listeners for updating status, so don't hook them up
425: // unless at least basic editing is supported.
426: richText.addKeyboardListener(listener);
427: richText.addClickListener(listener);
428: }
429: }
430:
431: private ListBox createColorList(String caption) {
432: ListBox lb = new ListBox();
433: lb.addChangeListener(listener);
434: lb.setVisibleItemCount(1);
435:
436: lb.addItem(caption);
437: lb.addItem(strings.white(), "white");
438: lb.addItem(strings.black(), "black");
439: lb.addItem(strings.red(), "red");
440: lb.addItem(strings.green(), "green");
441: lb.addItem(strings.yellow(), "yellow");
442: lb.addItem(strings.blue(), "blue");
443: return lb;
444: }
445:
446: private ListBox createFontList() {
447: ListBox lb = new ListBox();
448: lb.addChangeListener(listener);
449: lb.setVisibleItemCount(1);
450:
451: lb.addItem(strings.font(), "");
452: lb.addItem(strings.normal(), "");
453: lb.addItem("Times New Roman", "Times New Roman");
454: lb.addItem("Arial", "Arial");
455: lb.addItem("Courier New", "Courier New");
456: lb.addItem("Georgia", "Georgia");
457: lb.addItem("Trebuchet", "Trebuchet");
458: lb.addItem("Verdana", "Verdana");
459: return lb;
460: }
461:
462: private ListBox createFontSizes() {
463: ListBox lb = new ListBox();
464: lb.addChangeListener(listener);
465: lb.setVisibleItemCount(1);
466:
467: lb.addItem(strings.size());
468: lb.addItem(strings.xxsmall());
469: lb.addItem(strings.xsmall());
470: lb.addItem(strings.small());
471: lb.addItem(strings.medium());
472: lb.addItem(strings.large());
473: lb.addItem(strings.xlarge());
474: lb.addItem(strings.xxlarge());
475: return lb;
476: }
477:
478: private PushButton createPushButton(AbstractImagePrototype img,
479: String tip) {
480: PushButton pb = new PushButton(img.createImage());
481: pb.addClickListener(listener);
482: pb.setTitle(tip);
483: return pb;
484: }
485:
486: private ToggleButton createToggleButton(AbstractImagePrototype img,
487: String tip) {
488: ToggleButton tb = new ToggleButton(img.createImage());
489: tb.addClickListener(listener);
490: tb.setTitle(tip);
491: return tb;
492: }
493:
494: /**
495: * Updates the status of all the stateful buttons.
496: */
497: private void updateStatus() {
498: if (basic != null) {
499: bold.setDown(basic.isBold());
500: italic.setDown(basic.isItalic());
501: underline.setDown(basic.isUnderlined());
502: subscript.setDown(basic.isSubscript());
503: superscript.setDown(basic.isSuperscript());
504: }
505:
506: if (extended != null) {
507: strikethrough.setDown(extended.isStrikethrough());
508: }
509: }
510: }
|