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