1 /*
2  * Copyright 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.google.gwt.sample.showcase.client.content.text;
17 
18 import com.google.gwt.core.client.GWT;
19 import com.google.gwt.event.dom.client.ChangeEvent;
20 import com.google.gwt.event.dom.client.ChangeHandler;
21 import com.google.gwt.event.dom.client.ClickEvent;
22 import com.google.gwt.event.dom.client.ClickHandler;
23 import com.google.gwt.event.dom.client.KeyUpEvent;
24 import com.google.gwt.event.dom.client.KeyUpHandler;
25 import com.google.gwt.i18n.client.Constants;
26 import com.google.gwt.resources.client.ClientBundle;
27 import com.google.gwt.resources.client.ImageResource;
28 import com.google.gwt.user.client.Window;
29 import com.google.gwt.user.client.ui.Composite;
30 import com.google.gwt.user.client.ui.HorizontalPanel;
31 import com.google.gwt.user.client.ui.Image;
32 import com.google.gwt.user.client.ui.ListBox;
33 import com.google.gwt.user.client.ui.PushButton;
34 import com.google.gwt.user.client.ui.RichTextArea;
35 import com.google.gwt.user.client.ui.ToggleButton;
36 import com.google.gwt.user.client.ui.VerticalPanel;
37 import com.google.gwt.user.client.ui.Widget;
38 
39 /**
40  * A sample toolbar for use with {@link RichTextArea}. It provides a simple UI
41  * for all rich text formatting, dynamically displayed only for the available
42  * functionality.
43  */
44 @SuppressWarnings("deprecation")
45 public class RichTextToolbar extends Composite {
46 
47   /**
48    * This {@link ClientBundle} is used for all the button icons. Using a bundle
49    * allows all of these images to be packed into a single image, which saves a
50    * lot of HTTP requests, drastically improving startup time.
51    */
52   public interface Images extends ClientBundle {
53 
bold()54     ImageResource bold();
55 
createLink()56     ImageResource createLink();
57 
hr()58     ImageResource hr();
59 
indent()60     ImageResource indent();
61 
insertImage()62     ImageResource insertImage();
63 
italic()64     ImageResource italic();
65 
justifyCenter()66     ImageResource justifyCenter();
67 
justifyLeft()68     ImageResource justifyLeft();
69 
justifyRight()70     ImageResource justifyRight();
71 
ol()72     ImageResource ol();
73 
outdent()74     ImageResource outdent();
75 
removeFormat()76     ImageResource removeFormat();
77 
removeLink()78     ImageResource removeLink();
79 
strikeThrough()80     ImageResource strikeThrough();
81 
subscript()82     ImageResource subscript();
83 
superscript()84     ImageResource superscript();
85 
ul()86     ImageResource ul();
87 
underline()88     ImageResource underline();
89   }
90 
91   /**
92    * This {@link Constants} interface is used to make the toolbar's strings
93    * internationalizable.
94    */
95   public interface Strings extends Constants {
96 
black()97     String black();
98 
blue()99     String blue();
100 
bold()101     String bold();
102 
color()103     String color();
104 
createLink()105     String createLink();
106 
font()107     String font();
108 
green()109     String green();
110 
hr()111     String hr();
112 
indent()113     String indent();
114 
insertImage()115     String insertImage();
116 
italic()117     String italic();
118 
justifyCenter()119     String justifyCenter();
120 
justifyLeft()121     String justifyLeft();
122 
justifyRight()123     String justifyRight();
124 
large()125     String large();
126 
medium()127     String medium();
128 
normal()129     String normal();
130 
ol()131     String ol();
132 
outdent()133     String outdent();
134 
red()135     String red();
136 
removeFormat()137     String removeFormat();
138 
removeLink()139     String removeLink();
140 
size()141     String size();
142 
small()143     String small();
144 
strikeThrough()145     String strikeThrough();
146 
subscript()147     String subscript();
148 
superscript()149     String superscript();
150 
ul()151     String ul();
152 
underline()153     String underline();
154 
white()155     String white();
156 
xlarge()157     String xlarge();
158 
xsmall()159     String xsmall();
160 
xxlarge()161     String xxlarge();
162 
xxsmall()163     String xxsmall();
164 
yellow()165     String yellow();
166   }
167 
168   /**
169    * We use an inner EventHandler class to avoid exposing event methods on the
170    * RichTextToolbar itself.
171    */
172   private class EventHandler implements ClickHandler, ChangeHandler,
173       KeyUpHandler {
174 
onChange(ChangeEvent event)175     public void onChange(ChangeEvent event) {
176       Widget sender = (Widget) event.getSource();
177 
178       if (sender == backColors) {
179         basic.setBackColor(backColors.getValue(backColors.getSelectedIndex()));
180         backColors.setSelectedIndex(0);
181       } else if (sender == foreColors) {
182         basic.setForeColor(foreColors.getValue(foreColors.getSelectedIndex()));
183         foreColors.setSelectedIndex(0);
184       } else if (sender == fonts) {
185         basic.setFontName(fonts.getValue(fonts.getSelectedIndex()));
186         fonts.setSelectedIndex(0);
187       } else if (sender == fontSizes) {
188         basic.setFontSize(fontSizesConstants[fontSizes.getSelectedIndex() - 1]);
189         fontSizes.setSelectedIndex(0);
190       }
191     }
192 
onClick(ClickEvent event)193     public void onClick(ClickEvent event) {
194       Widget sender = (Widget) event.getSource();
195 
196       if (sender == bold) {
197         basic.toggleBold();
198       } else if (sender == italic) {
199         basic.toggleItalic();
200       } else if (sender == underline) {
201         basic.toggleUnderline();
202       } else if (sender == subscript) {
203         basic.toggleSubscript();
204       } else if (sender == superscript) {
205         basic.toggleSuperscript();
206       } else if (sender == strikethrough) {
207         extended.toggleStrikethrough();
208       } else if (sender == indent) {
209         extended.rightIndent();
210       } else if (sender == outdent) {
211         extended.leftIndent();
212       } else if (sender == justifyLeft) {
213         basic.setJustification(RichTextArea.Justification.LEFT);
214       } else if (sender == justifyCenter) {
215         basic.setJustification(RichTextArea.Justification.CENTER);
216       } else if (sender == justifyRight) {
217         basic.setJustification(RichTextArea.Justification.RIGHT);
218       } else if (sender == insertImage) {
219         String url = Window.prompt("Enter an image URL:", "http://");
220         if (url != null) {
221           extended.insertImage(url);
222         }
223       } else if (sender == createLink) {
224         String url = Window.prompt("Enter a link URL:", "http://");
225         if (url != null) {
226           extended.createLink(url);
227         }
228       } else if (sender == removeLink) {
229         extended.removeLink();
230       } else if (sender == hr) {
231         extended.insertHorizontalRule();
232       } else if (sender == ol) {
233         extended.insertOrderedList();
234       } else if (sender == ul) {
235         extended.insertUnorderedList();
236       } else if (sender == removeFormat) {
237         extended.removeFormat();
238       } else if (sender == richText) {
239         // We use the RichTextArea's onKeyUp event to update the toolbar status.
240         // This will catch any cases where the user moves the cursur using the
241         // keyboard, or uses one of the browser's built-in keyboard shortcuts.
242         updateStatus();
243       }
244     }
245 
onKeyUp(KeyUpEvent event)246     public void onKeyUp(KeyUpEvent event) {
247       Widget sender = (Widget) event.getSource();
248       if (sender == richText) {
249         // We use the RichTextArea's onKeyUp event to update the toolbar status.
250         // This will catch any cases where the user moves the cursur using the
251         // keyboard, or uses one of the browser's built-in keyboard shortcuts.
252         updateStatus();
253       }
254     }
255   }
256 
257   private static final RichTextArea.FontSize[] fontSizesConstants = new RichTextArea.FontSize[] {
258       RichTextArea.FontSize.XX_SMALL, RichTextArea.FontSize.X_SMALL,
259       RichTextArea.FontSize.SMALL, RichTextArea.FontSize.MEDIUM,
260       RichTextArea.FontSize.LARGE, RichTextArea.FontSize.X_LARGE,
261       RichTextArea.FontSize.XX_LARGE};
262 
263   private Images images = (Images) GWT.create(Images.class);
264   private Strings strings = (Strings) GWT.create(Strings.class);
265   private EventHandler handler = new EventHandler();
266 
267   private RichTextArea richText;
268   private RichTextArea.BasicFormatter basic;
269   private RichTextArea.ExtendedFormatter extended;
270 
271   private VerticalPanel outer = new VerticalPanel();
272   private HorizontalPanel topPanel = new HorizontalPanel();
273   private HorizontalPanel bottomPanel = new HorizontalPanel();
274   private ToggleButton bold;
275   private ToggleButton italic;
276   private ToggleButton underline;
277   private ToggleButton subscript;
278   private ToggleButton superscript;
279   private ToggleButton strikethrough;
280   private PushButton indent;
281   private PushButton outdent;
282   private PushButton justifyLeft;
283   private PushButton justifyCenter;
284   private PushButton justifyRight;
285   private PushButton hr;
286   private PushButton ol;
287   private PushButton ul;
288   private PushButton insertImage;
289   private PushButton createLink;
290   private PushButton removeLink;
291   private PushButton removeFormat;
292 
293   private ListBox backColors;
294   private ListBox foreColors;
295   private ListBox fonts;
296   private ListBox fontSizes;
297 
298   /**
299    * Creates a new toolbar that drives the given rich text area.
300    *
301    * @param richText the rich text area to be controlled
302    */
RichTextToolbar(RichTextArea richText)303   public RichTextToolbar(RichTextArea richText) {
304     this.richText = richText;
305     this.basic = richText.getBasicFormatter();
306     this.extended = richText.getExtendedFormatter();
307 
308     outer.add(topPanel);
309     outer.add(bottomPanel);
310     topPanel.setWidth("100%");
311     bottomPanel.setWidth("100%");
312 
313     initWidget(outer);
314     setStyleName("gwt-RichTextToolbar");
315     richText.addStyleName("hasRichTextToolbar");
316 
317     if (basic != null) {
318       topPanel.add(bold = createToggleButton(images.bold(), strings.bold()));
319       topPanel.add(italic = createToggleButton(images.italic(),
320           strings.italic()));
321       topPanel.add(underline = createToggleButton(images.underline(),
322           strings.underline()));
323       topPanel.add(subscript = createToggleButton(images.subscript(),
324           strings.subscript()));
325       topPanel.add(superscript = createToggleButton(images.superscript(),
326           strings.superscript()));
327       topPanel.add(justifyLeft = createPushButton(images.justifyLeft(),
328           strings.justifyLeft()));
329       topPanel.add(justifyCenter = createPushButton(images.justifyCenter(),
330           strings.justifyCenter()));
331       topPanel.add(justifyRight = createPushButton(images.justifyRight(),
332           strings.justifyRight()));
333     }
334 
335     if (extended != null) {
336       topPanel.add(strikethrough = createToggleButton(images.strikeThrough(),
337           strings.strikeThrough()));
338       topPanel.add(indent = createPushButton(images.indent(), strings.indent()));
339       topPanel.add(outdent = createPushButton(images.outdent(),
340           strings.outdent()));
341       topPanel.add(hr = createPushButton(images.hr(), strings.hr()));
342       topPanel.add(ol = createPushButton(images.ol(), strings.ol()));
343       topPanel.add(ul = createPushButton(images.ul(), strings.ul()));
344       topPanel.add(insertImage = createPushButton(images.insertImage(),
345           strings.insertImage()));
346       topPanel.add(createLink = createPushButton(images.createLink(),
347           strings.createLink()));
348       topPanel.add(removeLink = createPushButton(images.removeLink(),
349           strings.removeLink()));
350       topPanel.add(removeFormat = createPushButton(images.removeFormat(),
351           strings.removeFormat()));
352     }
353 
354     if (basic != null) {
355       bottomPanel.add(backColors = createColorList("Background"));
356       bottomPanel.add(foreColors = createColorList("Foreground"));
357       bottomPanel.add(fonts = createFontList());
358       bottomPanel.add(fontSizes = createFontSizes());
359 
360       // We only use these handlers for updating status, so don't hook them up
361       // unless at least basic editing is supported.
362       richText.addKeyUpHandler(handler);
363       richText.addClickHandler(handler);
364     }
365   }
366 
createColorList(String caption)367   private ListBox createColorList(String caption) {
368     ListBox lb = new ListBox();
369     lb.addChangeHandler(handler);
370     lb.setVisibleItemCount(1);
371 
372     lb.addItem(caption);
373     lb.addItem(strings.white(), "white");
374     lb.addItem(strings.black(), "black");
375     lb.addItem(strings.red(), "red");
376     lb.addItem(strings.green(), "green");
377     lb.addItem(strings.yellow(), "yellow");
378     lb.addItem(strings.blue(), "blue");
379     return lb;
380   }
381 
createFontList()382   private ListBox createFontList() {
383     ListBox lb = new ListBox();
384     lb.addChangeHandler(handler);
385     lb.setVisibleItemCount(1);
386 
387     lb.addItem(strings.font(), "");
388     lb.addItem(strings.normal(), "");
389     lb.addItem("Times New Roman", "Times New Roman");
390     lb.addItem("Arial", "Arial");
391     lb.addItem("Courier New", "Courier New");
392     lb.addItem("Georgia", "Georgia");
393     lb.addItem("Trebuchet", "Trebuchet");
394     lb.addItem("Verdana", "Verdana");
395     return lb;
396   }
397 
createFontSizes()398   private ListBox createFontSizes() {
399     ListBox lb = new ListBox();
400     lb.addChangeHandler(handler);
401     lb.setVisibleItemCount(1);
402 
403     lb.addItem(strings.size());
404     lb.addItem(strings.xxsmall());
405     lb.addItem(strings.xsmall());
406     lb.addItem(strings.small());
407     lb.addItem(strings.medium());
408     lb.addItem(strings.large());
409     lb.addItem(strings.xlarge());
410     lb.addItem(strings.xxlarge());
411     return lb;
412   }
413 
createPushButton(ImageResource img, String tip)414   private PushButton createPushButton(ImageResource img, String tip) {
415     PushButton pb = new PushButton(new Image(img));
416     pb.addClickHandler(handler);
417     pb.setTitle(tip);
418     return pb;
419   }
420 
createToggleButton(ImageResource img, String tip)421   private ToggleButton createToggleButton(ImageResource img, String tip) {
422     ToggleButton tb = new ToggleButton(new Image(img));
423     tb.addClickHandler(handler);
424     tb.setTitle(tip);
425     return tb;
426   }
427 
428   /**
429    * Updates the status of all the stateful buttons.
430    */
updateStatus()431   private void updateStatus() {
432     if (basic != null) {
433       bold.setDown(basic.isBold());
434       italic.setDown(basic.isItalic());
435       underline.setDown(basic.isUnderlined());
436       subscript.setDown(basic.isSubscript());
437       superscript.setDown(basic.isSuperscript());
438     }
439 
440     if (extended != null) {
441       strikethrough.setDown(extended.isStrikethrough());
442     }
443   }
444 }
445