78
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2006 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Thomas Graber <d4rkdragon@gmail.com>
|
|
12 *******************************************************************************/
|
|
13 module examples.texteditor.TextEditor;
|
|
14
|
|
15 import dwt.DWT;
|
|
16 import dwt.custom.ExtendedModifyEvent;
|
|
17 import dwt.custom.ExtendedModifyListener;
|
|
18 import dwt.custom.StyleRange;
|
|
19 import dwt.custom.StyledText;
|
|
20 import dwt.events.DisposeEvent;
|
|
21 import dwt.events.DisposeListener;
|
|
22 import dwt.events.SelectionAdapter;
|
|
23 import dwt.events.SelectionEvent;
|
|
24 import dwt.graphics.Color;
|
|
25 import dwt.graphics.Font;
|
|
26 import dwt.graphics.FontData;
|
|
27 import dwt.graphics.Point;
|
|
28 import dwt.graphics.RGB;
|
|
29 import dwt.layout.GridData;
|
|
30 import dwt.layout.GridLayout;
|
|
31 import dwt.widgets.Display;
|
|
32 import dwt.widgets.FontDialog;
|
|
33 import dwt.widgets.Menu;
|
|
34 import dwt.widgets.MenuItem;
|
|
35 import dwt.widgets.Shell;
|
|
36 import dwt.widgets.ToolBar;
|
|
37 import dwt.widgets.ToolItem;
|
|
38 import dwt.widgets.Widget;
|
|
39 import dwt.dwthelper.ResourceBundle;
|
|
40 import dwt.dwthelper.utils;
|
|
41
|
|
42 import tango.util.collection.ArraySeq;
|
|
43
|
|
44 import examples.texteditor.Images;
|
|
45
|
|
46 version( JIVE ){
|
|
47 import jive.stacktrace;
|
|
48 }
|
|
49
|
|
50 /**
|
|
51 */
|
|
52 public class TextEditor {
|
|
53 Shell shell;
|
|
54 ToolBar toolBar;
|
|
55 StyledText text;
|
|
56 Images images;
|
|
57 alias ArraySeq!(StyleRange) StyleCache;
|
|
58 StyleCache cachedStyles;
|
|
59
|
|
60 Color RED = null;
|
|
61 Color BLUE = null;
|
|
62 Color GREEN = null;
|
|
63 Font font = null;
|
|
64 ToolItem boldButton, italicButton, underlineButton, strikeoutButton;
|
|
65
|
|
66 //string resources
|
|
67 static ResourceBundle resources;
|
|
68 private static const char[] resourceData = import( "dwtexamples.texteditor.examples_texteditor.properties" );
|
|
69
|
|
70 /*
|
|
71 * static ctor
|
|
72 */
|
|
73 static this()
|
|
74 {
|
|
75 resources = ResourceBundle.getBundleFromData( resourceData );
|
|
76 }
|
|
77
|
|
78 /*
|
|
79 * ctor
|
|
80 */
|
|
81 this() {
|
|
82 images = new Images();
|
|
83 }
|
|
84
|
|
85 /*
|
|
86 * creates edit menu
|
|
87 */
|
|
88 Menu createEditMenu() {
|
|
89 Menu bar = shell.getMenuBar ();
|
|
90 Menu menu = new Menu (bar);
|
|
91
|
|
92 MenuItem item = new MenuItem (menu, DWT.PUSH);
|
|
93 item.setText (resources.getString("Cut_menuitem"));
|
|
94 item.setAccelerator(DWT.MOD1 + 'X');
|
|
95 item.addSelectionListener(new class() SelectionAdapter {
|
|
96 public void widgetSelected(SelectionEvent event) {
|
|
97 handleCutCopy();
|
|
98 text.cut();
|
|
99 }
|
|
100 });
|
|
101 item = new MenuItem (menu, DWT.PUSH);
|
|
102 item.setText (resources.getString("Copy_menuitem"));
|
|
103 item.setAccelerator(DWT.MOD1 + 'C');
|
|
104 item.addSelectionListener(new class() SelectionAdapter {
|
|
105 public void widgetSelected(SelectionEvent event) {
|
|
106 handleCutCopy();
|
|
107 text.copy();
|
|
108 }
|
|
109 });
|
|
110 item = new MenuItem (menu, DWT.PUSH);
|
|
111 item.setText (resources.getString("Paste_menuitem"));
|
|
112 item.setAccelerator(DWT.MOD1 + 'V');
|
|
113 item.addSelectionListener(new class() SelectionAdapter {
|
|
114 public void widgetSelected(SelectionEvent event) {
|
|
115 text.paste();
|
|
116 }
|
|
117 });
|
|
118 new MenuItem (menu, DWT.SEPARATOR);
|
|
119 item = new MenuItem (menu, DWT.PUSH);
|
|
120 item.setText (resources.getString("Font_menuitem"));
|
|
121 item.addSelectionListener(new class() SelectionAdapter {
|
|
122 public void widgetSelected(SelectionEvent event) {
|
|
123 setFont();
|
|
124 }
|
|
125 });
|
|
126 return menu;
|
|
127 }
|
|
128
|
|
129 /*
|
|
130 * creates file menu
|
|
131 */
|
|
132 Menu createFileMenu() {
|
|
133 Menu bar = shell.getMenuBar ();
|
|
134 Menu menu = new Menu (bar);
|
|
135
|
|
136 MenuItem item = new MenuItem (menu, DWT.PUSH);
|
|
137 item.setText (resources.getString("Exit_menuitem"));
|
|
138 item.addSelectionListener(new class() SelectionAdapter {
|
|
139 public void widgetSelected(SelectionEvent event) {
|
|
140 shell.close ();
|
|
141 }
|
|
142 });
|
|
143
|
|
144 return menu;
|
|
145 }
|
|
146
|
|
147 /*
|
|
148 * Set a style
|
|
149 */
|
|
150 void setStyle(Widget widget) {
|
|
151 Point sel = text.getSelectionRange();
|
|
152 if ((sel is null) || (sel.y is 0)) return;
|
|
153 StyleRange style;
|
|
154 for (int i = sel.x; i<sel.x+sel.y; i++) {
|
|
155 StyleRange range = text.getStyleRangeAtOffset(i);
|
|
156 if (range !is null) {
|
|
157 style = cast(StyleRange)range.clone();
|
|
158 style.start = i;
|
|
159 style.length = 1;
|
|
160 } else {
|
|
161 style = new StyleRange(i, 1, null, null, DWT.NORMAL);
|
|
162 }
|
|
163 if (widget is boldButton) {
|
|
164 style.fontStyle ^= DWT.BOLD;
|
|
165 } else if (widget is italicButton) {
|
|
166 style.fontStyle ^= DWT.ITALIC;
|
|
167 } else if (widget is underlineButton) {
|
|
168 style.underline = !style.underline;
|
|
169 } else if (widget is strikeoutButton) {
|
|
170 style.strikeout = !style.strikeout;
|
|
171 }
|
|
172 text.setStyleRange(style);
|
|
173 }
|
|
174 text.setSelectionRange(sel.x + sel.y, 0);
|
|
175 }
|
|
176
|
|
177 /*
|
|
178 * Clear all style data for the selected text.
|
|
179 */
|
|
180 void clear() {
|
|
181 Point sel = text.getSelectionRange();
|
|
182 if (sel.y !is 0) {
|
|
183 StyleRange style;
|
|
184 style = new StyleRange(sel.x, sel.y, null, null, DWT.NORMAL);
|
|
185 text.setStyleRange(style);
|
|
186 }
|
|
187 text.setSelectionRange(sel.x + sel.y, 0);
|
|
188 }
|
|
189
|
|
190 /*
|
|
191 * Set the foreground color for the selected text.
|
|
192 */
|
|
193 void fgColor(Color fg) {
|
|
194 Point sel = text.getSelectionRange();
|
|
195 if ((sel is null) || (sel.y is 0)) return;
|
|
196 StyleRange style, range;
|
|
197 for (int i = sel.x; i<sel.x+sel.y; i++) {
|
|
198 range = text.getStyleRangeAtOffset(i);
|
|
199 if (range !is null) {
|
|
200 style = cast(StyleRange)range.clone();
|
|
201 style.start = i;
|
|
202 style.length = 1;
|
|
203 style.foreground = fg;
|
|
204 } else {
|
|
205 style = new StyleRange (i, 1, fg, null, DWT.NORMAL);
|
|
206 }
|
|
207 text.setStyleRange(style);
|
|
208 }
|
|
209 text.setSelectionRange(sel.x + sel.y, 0);
|
|
210 }
|
|
211
|
|
212 /*
|
|
213 * creates menu bar
|
|
214 */
|
|
215 void createMenuBar () {
|
|
216 Menu bar = new Menu (shell, DWT.BAR);
|
|
217 shell.setMenuBar (bar);
|
|
218
|
|
219 MenuItem fileItem = new MenuItem (bar, DWT.CASCADE);
|
|
220 fileItem.setText (resources.getString("File_menuitem"));
|
|
221 fileItem.setMenu (createFileMenu ());
|
|
222
|
|
223 MenuItem editItem = new MenuItem (bar, DWT.CASCADE);
|
|
224 editItem.setText (resources.getString("Edit_menuitem"));
|
|
225 editItem.setMenu (createEditMenu ());
|
|
226 }
|
|
227
|
|
228 /*
|
|
229 * creates shell
|
|
230 */
|
|
231 void createShell (Display display) {
|
|
232 shell = new Shell (display);
|
|
233 shell.setText (resources.getString("Window_title"));
|
|
234 images.loadAll (display);
|
|
235 GridLayout layout = new GridLayout();
|
|
236 layout.numColumns = 1;
|
|
237 shell.setLayout(layout);
|
|
238 shell.addDisposeListener (new class() DisposeListener {
|
|
239 public void widgetDisposed (DisposeEvent e) {
|
|
240 if (font !is null) font.dispose();
|
|
241 images.freeAll ();
|
|
242 RED.dispose();
|
|
243 GREEN.dispose();
|
|
244 BLUE.dispose();
|
|
245 }
|
|
246 });
|
|
247 }
|
|
248
|
|
249 /*
|
|
250 * creates styled text widget
|
|
251 */
|
|
252 void createStyledText() {
|
|
253 initializeColors();
|
|
254 text = new StyledText (shell, DWT.BORDER | DWT.MULTI | DWT.V_SCROLL | DWT.H_SCROLL);
|
|
255 GridData spec = new GridData();
|
|
256 spec.horizontalAlignment = GridData.FILL;
|
|
257 spec.grabExcessHorizontalSpace = true;
|
|
258 spec.verticalAlignment = GridData.FILL;
|
|
259 spec.grabExcessVerticalSpace = true;
|
|
260 text.setLayoutData(spec);
|
|
261 text.addExtendedModifyListener(new class() ExtendedModifyListener {
|
|
262 public void modifyText(ExtendedModifyEvent e) {
|
|
263 handleExtendedModify(e);
|
|
264 }
|
|
265 });
|
|
266 }
|
|
267
|
|
268 /*
|
|
269 * creates tool bar
|
|
270 */
|
|
271 void createToolBar() {
|
|
272 toolBar = new ToolBar(shell, DWT.NONE);
|
|
273 SelectionAdapter listener = new class() SelectionAdapter {
|
|
274 public void widgetSelected(SelectionEvent event) {
|
|
275 setStyle (event.widget);
|
|
276 }
|
|
277 };
|
|
278 boldButton = new ToolItem(toolBar, DWT.CHECK);
|
|
279 boldButton.setImage(images.Bold);
|
|
280 boldButton.setToolTipText(resources.getString("Bold"));
|
|
281 boldButton.addSelectionListener(listener);
|
|
282 italicButton = new ToolItem(toolBar, DWT.CHECK);
|
|
283 italicButton.setImage(images.Italic);
|
|
284 italicButton.setToolTipText(resources.getString("Italic"));
|
|
285 italicButton.addSelectionListener(listener);
|
|
286 underlineButton = new ToolItem(toolBar, DWT.CHECK);
|
|
287 underlineButton.setImage(images.Underline);
|
|
288 underlineButton.setToolTipText(resources.getString("Underline"));
|
|
289 underlineButton.addSelectionListener(listener);
|
|
290 strikeoutButton = new ToolItem(toolBar, DWT.CHECK);
|
|
291 strikeoutButton.setImage(images.Strikeout);
|
|
292 strikeoutButton.setToolTipText(resources.getString("Strikeout"));
|
|
293 strikeoutButton.addSelectionListener(listener);
|
|
294
|
|
295 ToolItem item = new ToolItem(toolBar, DWT.SEPARATOR);
|
|
296 item = new ToolItem(toolBar, DWT.PUSH);
|
|
297 item.setImage(images.Red);
|
|
298 item.addSelectionListener(new class() SelectionAdapter {
|
|
299 public void widgetSelected(SelectionEvent event) {
|
|
300 fgColor(RED);
|
|
301 }
|
|
302 });
|
|
303 item = new ToolItem(toolBar, DWT.PUSH);
|
|
304 item.setImage(images.Green);
|
|
305 item.addSelectionListener(new class() SelectionAdapter {
|
|
306 public void widgetSelected(SelectionEvent event) {
|
|
307 fgColor(GREEN);
|
|
308 }
|
|
309 });
|
|
310 item = new ToolItem(toolBar, DWT.PUSH);
|
|
311 item.setImage(images.Blue);
|
|
312 item.addSelectionListener(new class() SelectionAdapter {
|
|
313 public void widgetSelected(SelectionEvent event) {
|
|
314 fgColor(BLUE);
|
|
315 }
|
|
316 });
|
|
317 item = new ToolItem(toolBar, DWT.SEPARATOR);
|
|
318 item = new ToolItem(toolBar, DWT.PUSH);
|
|
319 item.setImage(images.Erase);
|
|
320 item.addSelectionListener(new class() SelectionAdapter {
|
|
321 public void widgetSelected(SelectionEvent event) {
|
|
322 clear();
|
|
323 }
|
|
324 });
|
|
325 }
|
|
326
|
|
327 /*
|
|
328 * Cache the style information for text that has been cut or copied.
|
|
329 */
|
|
330 void handleCutCopy() {
|
|
331 // Save the cut/copied style info so that during paste we will maintain
|
|
332 // the style information. Cut/copied text is put in the clipboard in
|
|
333 // RTF format, but is not pasted in RTF format. The other way to
|
|
334 // handle the pasting of styles would be to access the Clipboard directly and
|
|
335 // parse the RTF text.
|
|
336 cachedStyles = new StyleCache();
|
|
337 Point sel = text.getSelectionRange();
|
|
338 int startX = sel.x;
|
|
339 for (int i=sel.x; i<=sel.x+sel.y-1; i++) {
|
|
340 StyleRange style = text.getStyleRangeAtOffset(i);
|
|
341 if (style !is null) {
|
|
342 style.start = style.start - startX;
|
|
343 if (cachedStyles.toArray().length > 0) {
|
|
344 StyleRange lastStyle = cachedStyles.tail();
|
|
345 if (lastStyle.similarTo(style) && lastStyle.start + lastStyle.length is style.start) {
|
|
346 lastStyle.length++;
|
|
347 } else {
|
|
348 cachedStyles.append(style);
|
|
349 }
|
|
350 } else {
|
|
351 cachedStyles.append(style);
|
|
352 }
|
|
353 }
|
|
354 }
|
|
355 }
|
|
356
|
|
357 /*
|
|
358 * handle modify
|
|
359 */
|
|
360 void handleExtendedModify(ExtendedModifyEvent event) {
|
|
361 if (event.length is 0) return;
|
|
362 StyleRange style;
|
|
363 //PORTING event.length is char count, but it needs to decide on codepoint count
|
|
364 auto cont = text.getTextRange(event.start, event.length);
|
|
365 if ( codepointCount(cont) is 1 || cont == text.getLineDelimiter()) {
|
|
366 // Have the new text take on the style of the text to its right (during
|
|
367 // typing) if no style information is active.
|
|
368 int caretOffset = text.getCaretOffset();
|
|
369 style = null;
|
|
370 if (caretOffset < text.getCharCount()) style = text.getStyleRangeAtOffset(caretOffset);
|
|
371 if (style !is null) {
|
|
372 style = cast(StyleRange) style.clone ();
|
|
373 style.start = event.start;
|
|
374 style.length = event.length;
|
|
375 } else {
|
|
376 style = new StyleRange(event.start, event.length, null, null, DWT.NORMAL);
|
|
377 }
|
|
378 if (boldButton.getSelection()) style.fontStyle |= DWT.BOLD;
|
|
379 if (italicButton.getSelection()) style.fontStyle |= DWT.ITALIC;
|
|
380 style.underline = underlineButton.getSelection();
|
|
381 style.strikeout = strikeoutButton.getSelection();
|
|
382 if (!style.isUnstyled()) text.setStyleRange(style);
|
|
383 } else {
|
|
384 // paste occurring, have text take on the styles it had when it was
|
|
385 // cut/copied
|
|
386 foreach (style; cachedStyles) {
|
|
387 StyleRange newStyle = cast(StyleRange)style.clone();
|
|
388 newStyle.start = style.start + event.start;
|
|
389 text.setStyleRange(newStyle);
|
|
390 }
|
|
391 }
|
|
392 }
|
|
393
|
|
394 /*
|
|
395 * opens the shell
|
|
396 */
|
|
397 public Shell open (Display display) {
|
|
398 createShell (display);
|
|
399 createMenuBar ();
|
|
400 createToolBar ();
|
|
401 createStyledText ();
|
|
402 shell.setSize(500, 300);
|
|
403 shell.open ();
|
|
404 return shell;
|
|
405 }
|
|
406
|
|
407 /*
|
|
408 * set the font for styled text widget
|
|
409 */
|
|
410 void setFont() {
|
|
411 FontDialog fontDialog = new FontDialog(shell);
|
|
412 fontDialog.setFontList((text.getFont()).getFontData());
|
|
413 FontData fontData = fontDialog.open();
|
|
414 if (fontData !is null) {
|
|
415 Font newFont = new Font(shell.getDisplay(), fontData);
|
|
416 text.setFont(newFont);
|
|
417 if (font !is null) font.dispose();
|
|
418 font = newFont;
|
|
419 }
|
|
420 }
|
|
421
|
|
422 /*
|
|
423 * initialize the colors
|
|
424 */
|
|
425 void initializeColors() {
|
|
426 Display display = Display.getDefault();
|
|
427 RED = new Color (display, new RGB(255,0,0));
|
|
428 BLUE = new Color (display, new RGB(0,0,255));
|
|
429 GREEN = new Color (display, new RGB(0,255,0));
|
|
430 }
|
|
431 }
|
|
432
|
|
433 /*
|
|
434 * main function
|
|
435 */
|
|
436 public void main (char[][] args) {
|
|
437 Display display = new Display ();
|
|
438 TextEditor example = new TextEditor ();
|
|
439 Shell shell = example.open (display);
|
|
440 while (!shell.isDisposed ())
|
|
441 if (!display.readAndDispatch ()) display.sleep ();
|
|
442 display.dispose ();
|
|
443 }
|