1 /**
2  * Widget module.
3  *
4  * License:
5  *     MIT. See LICENSE for full details.
6  */
7 module tkd.widget.text;
8 
9 /**
10  * Imports.
11  */
12 import std.conv;
13 import tkd.element.uielement;
14 import tkd.image.image;
15 import tkd.widget.common.border;
16 import tkd.widget.common.color;
17 import tkd.widget.common.font;
18 import tkd.widget.common.height;
19 import tkd.widget.common.relief;
20 import tkd.widget.common.width;
21 import tkd.widget.common.xscrollcommand;
22 import tkd.widget.common.xview;
23 import tkd.widget.common.yscrollcommand;
24 import tkd.widget.common.yview;
25 import tkd.widget.textwrapmode;
26 import tkd.widget.widget;
27 
28 /**
29  * A text widget displays one or more lines of text and allows that text to be 
30  * edited. Text widgets support embedded widgets or embedded images.
31  *
32  * Example:
33  * ---
34  * auto text = new Text()
35  * 	.appendText("Text")
36  * 	.pack();
37  * ---
38  *
39  * Common_Commands:
40  *     These are injected common commands that can also be used with this widget.
41  *     $(P
42  *         $(LINK2 ./common/border.html, Border) $(BR)
43  *         $(LINK2 ./common/color.html, Color) $(BR)
44  *         $(LINK2 ./common/font.html, Font) $(BR)
45  *         $(LINK2 ./common/height.html, Height) $(BR)
46  *         $(LINK2 ./common/relief.html, Relief) $(BR)
47  *         $(LINK2 ./common/width.html, Width) $(BR)
48  *         $(LINK2 ./common/xscrollcommand.html, XScrollCommand) $(BR)
49  *         $(LINK2 ./common/xview.html, XView) $(BR)
50  *         $(LINK2 ./common/yscrollcommand.html, YScrollCommand) $(BR)
51  *         $(LINK2 ./common/yview.html, YView) $(BR)
52  *     )
53  *
54  * Additional_Events:
55  *     Additional events that can also be bound to using the $(LINK2 ../element/uielement.html#UiElement.bind, bind) method.
56  *     $(P
57  *         <<Clear>>,
58  *         <<Copy>>,
59  *         <<Cut>>,
60  *         <<Modified>>,
61  *         <<Paste>>,
62  *         <<PasteSelection>>,
63  *         <<PrevWindow>>,
64  *         <<Redo>>,
65  *         <<Selection>>,
66  *         <<Undo>>,
67  *         <Alt-Key>,
68  *         <B1-Enter>,
69  *         <B1-Leave>,
70  *         <B1-Motion>,
71  *         <B2-Motion>,
72  *         <Button-1>,
73  *         <Button-2>,
74  *         <Button-4>,
75  *         <Button-5>,
76  *         <ButtonRelease-1>,
77  *         <Control-Button-1>,
78  *         <Control-Key-Down>,
79  *         <Control-Key-End>,
80  *         <Control-Key-Home>,
81  *         <Control-Key-Left>,
82  *         <Control-Key-Next>,
83  *         <Control-Key-Prior>,
84  *         <Control-Key-Right>,
85  *         <Control-Key-Tab>,
86  *         <Control-Key-Up>,
87  *         <Control-Key-a>,
88  *         <Control-Key-b>,
89  *         <Control-Key-backslash>,
90  *         <Control-Key-d>,
91  *         <Control-Key-e>,
92  *         <Control-Key-f>,
93  *         <Control-Key-h>,
94  *         <Control-Key-i>,
95  *         <Control-Key-k>,
96  *         <Control-Key-n>,
97  *         <Control-Key-o>,
98  *         <Control-Key-p>,
99  *         <Control-Key-slash>,
100  *         <Control-Key-space>,
101  *         <Control-Key-t>,
102  *         <Control-Key>,
103  *         <Control-Shift-Key-Down>,
104  *         <Control-Shift-Key-End>,
105  *         <Control-Shift-Key-Home>,
106  *         <Control-Shift-Key-Left>,
107  *         <Control-Shift-Key-Right>,
108  *         <Control-Shift-Key-Tab>,
109  *         <Control-Shift-Key-Up>,
110  *         <Control-Shift-Key-space>,
111  *         <Double-Button-1>,
112  *         <Double-Shift-Button-1>,
113  *         <Key-BackSpace>,
114  *         <Key-Delete>,
115  *         <Key-Down>,
116  *         <Key-End>,
117  *         <Key-Escape>,
118  *         <Key-F10>,
119  *         <Key-Home>,
120  *         <Key-Insert>,
121  *         <Key-KP_Enter>,
122  *         <Key-Left>,
123  *         <Key-Next>,
124  *         <Key-Prior>,
125  *         <Key-Return>,
126  *         <Key-Right>,
127  *         <Key-Select>,
128  *         <Key-Tab>,
129  *         <Key-Up>,
130  *         <Key>,
131  *         <Meta-Key-BackSpace>,
132  *         <Meta-Key-Delete>,
133  *         <Meta-Key-b>,
134  *         <Meta-Key-d>,
135  *         <Meta-Key-f>,
136  *         <Meta-Key-greater>,
137  *         <Meta-Key-less>,
138  *         <Meta-Key>,
139  *         <MouseWheel>,
140  *         <Shift-Button-1>,
141  *         <Shift-Key-Down>,
142  *         <Shift-Key-End>,
143  *         <Shift-Key-Home>,
144  *         <Shift-Key-Left>,
145  *         <Shift-Key-Next>,
146  *         <Shift-Key-Prior>,
147  *         <Shift-Key-Right>,
148  *         <Shift-Key-Select>,
149  *         <Shift-Key-Tab>,
150  *         <Shift-Key-Up>,
151  *         <Triple-Button-1>,
152  *         <Triple-Shift-Button-1>,
153  *     )
154  *
155  * See_Also:
156  *     $(LINK2 ./widget.html, tkd.widget.widget)
157  */
158 class Text : Widget, IXScrollable!(Text), IYScrollable!(Text)
159 {
160 	/**
161 	 * Construct the widget.
162 	 *
163 	 * Params:
164 	 *     parent = The parent of this widget.
165 	 *
166 	 * See_Also:
167 	 *     $(LINK2 ../element/uielement.html, tkd.element.UiElement) $(BR)
168 	 */
169 	public this(UiElement parent = null)
170 	{
171 		super(parent);
172 		this._elementId = "text";
173 
174 		this._tk.eval("text %s -highlightthickness 0", this.id);
175 
176 		this.setUndoSupport(true);
177 		this.setUndoLevels(25);
178 		this.setWrapMode(TextWrapMode.word);
179 	}
180 
181 	/**
182 	 * Set the amount of padding in the text widget.
183 	 *
184 	 * Params:
185 	 *     padding = The amount of padding in the text widget.
186 	 *
187 	 * Returns:
188 	 *     This widget to aid method chaining.
189 	 */
190 	public auto setPadding(this T)(int padding)
191 	{
192 		this._tk.eval("%s configure -padx %s -pady %s", this.id, padding, padding);
193 
194 		return cast(T) this;
195 	}
196 
197 	/**
198 	 * Set if the widget is readonly or not.
199 	 *
200 	 * Params:
201 	 *     readOnly = Flag to toggle readonly state.
202 	 *
203 	 * Returns:
204 	 *     This widget to aid method chaining.
205 	 */
206 	public auto setReadOnly(this T)(bool readOnly = true)
207 	{
208 		if(readOnly)
209 		{
210 			this._tk.eval("%s configure -state disabled", this.id);
211 		}
212 		else
213 		{
214 			this._tk.eval("%s configure -state normal", this.id);
215 		}
216 
217 		return cast(T) this;
218 	}
219 
220 	/**
221 	 * Enable or disable undo support.
222 	 *
223 	 * Params:
224 	 *     enable = True to enable undo support, false to disable it.
225 	 *
226 	 * Returns:
227 	 *     This widget to aid method chaining.
228 	 */
229 	public auto setUndoSupport(this T)(bool enable)
230 	{
231 		this._tk.eval("%s configure -undo %s", this.id, enable);
232 
233 		return cast(T) this;
234 	}
235 
236 	/**
237 	 * Set the number of undo levels the widget will support.
238 	 *
239 	 * Params:
240 	 *     undoLevels = The number of undo levels the widget will support.
241 	 *
242 	 * Returns:
243 	 *     This widget to aid method chaining.
244 	 */
245 	public auto setUndoLevels(this T)(int undoLevels)
246 	{
247 		this._tk.eval("%s configure -maxundo %s", this.id, undoLevels);
248 
249 		return cast(T) this;
250 	}
251 
252 	/**
253 	 * Set the wrap mode of the text.
254 	 *
255 	 * Params:
256 	 *     mode = The mode to wrap the text with.
257 	 *
258 	 * Returns:
259 	 *     This widget to aid method chaining.
260 	 *
261 	 * See_Also:
262 	 *     $(LINK2 ./textwrapmode.html, tkd.widget.textwrapmode)
263 	 */
264 	public auto setWrapMode(this T)(string mode)
265 	{
266 		this._tk.eval("%s configure -wrap %s", this.id, mode);
267 
268 		return cast(T) this;
269 	}
270 
271 	/**
272 	 * Appends text to the widget.
273 	 *
274 	 * Params:
275 	 *     text = The text to append.
276 	 *
277 	 * Returns:
278 	 *     This widget to aid method chaining.
279 	 */
280 	public auto appendText(this T)(string text)
281 	{
282 		// String concatenation is used to build the script here instead of 
283 		// using format specifiers to enable supporting input which includes 
284 		// Tcl/Tk reserved characters and elements that could be construed as 
285 		// format specifiers.
286 		string script = std.conv.text(this.id, ` insert end "`, this._tk.escape(text), `"`);
287 		this._tk.eval(script);
288 
289 		return cast(T) this;
290 	}
291 
292 	/**
293 	 * Inserts text into the widget at a specified line and character index.
294 	 *
295 	 * Params:
296 	 *     line = The line at which to insert the text. Indexes start at 1.
297 	 *     character = The character at which to insert the text. Indexes start at 0.
298 	 *     text = The text to insert.
299 	 *
300 	 * Returns:
301 	 *     This widget to aid method chaining.
302 	 */
303 	public auto insertText(this T)(int line, int character, string text)
304 	{
305 		// String concatenation is used to build the script here instead of 
306 		// using format specifiers to enable supporting input which includes 
307 		// Tcl/Tk reserved characters and elements that could be construed as 
308 		// format specifiers.
309 		string script = std.conv.text(this.id, ` insert `, line, `.`, character, ` "`, this._tk.escape(text), `"`);
310 		this._tk.eval(script);
311 
312 		return cast(T) this;
313 	}
314 
315 	/**
316 	 * Get the text from the widget.
317 	 *
318 	 * Returns:
319 	 *     The text from the widget.
320 	 */
321 	public string getText(this T)()
322 	{
323 		this._tk.eval("%s get 0.0 end", this.id);
324 
325 		return this._tk.getResult!(string);
326 	}
327 
328 	/**
329 	 * Delete text from the widget.
330 	 *
331 	 * Params:
332 	 *     fromLine = The line from which to start deleting. Indexes start at 1.
333 	 *     fromChar = The character from which to start deleting. Indexes start at 0.
334 	 *     toLine = The line up to (but not including) which to delete. Indexes start at 1.
335 	 *     toChar = The character up to (but not including) which to delete. Indexes start at 0.
336 	 *
337 	 * Returns:
338 	 *     This widget to aid method chaining.
339 	 */
340 	public auto deleteText(this T)(int fromLine, int fromChar, int toLine, int toChar)
341 	{
342 		this._tk.eval("%s delete %s.%s %s.%s", this.id, fromLine, fromChar, toLine, toChar);
343 
344 		return cast(T) this;
345 	}
346 
347 	/**
348 	 * Delete all content from the widget.
349 	 *
350 	 * Returns:
351 	 *     This widget to aid method chaining.
352 	 */
353 	public auto clear(this T)()
354 	{
355 		this._tk.eval("%s delete 0.0 end", this.id);
356 
357 		return cast(T) this;
358 	}
359 
360 	/**
361 	 * Embed a widget into the text.
362 	 *
363 	 * Params:
364 	 *     line = The line at which to insert the text. Indexes start at 1.
365 	 *     character = The character at which to insert the text. Indexes start at 0.
366 	 *     widget = The widget to embed.
367 	 *     padding = The amount of padding around the widget.
368 	 *
369 	 * Returns:
370 	 *     This widget to aid method chaining.
371 	 */
372 	public auto embedWidget(this T)(int line, int character, Widget widget, int padding = 0)
373 	{
374 		this._tk.eval("%s window create %s.%s -window %s -align center -padx %s -pady %s", this.id, line, character, widget.id, padding, padding);
375 
376 		return cast(T) this;
377 	}
378 
379 	/**
380 	 * Embed an image into the text.
381 	 *
382 	 * Params:
383 	 *     line = The line at which to insert the text. Indexes start at 1.
384 	 *     character = The character at which to insert the text. Indexes start at 0.
385 	 *     image = The image to embed.
386 	 *     padding = The amount of padding around the widget.
387 	 *
388 	 * Returns:
389 	 *     This widget to aid method chaining.
390 	 */
391 	public auto embedImage(this T)(int line, int character, Image image, int padding = 0)
392 	{
393 		this._tk.eval("%s image create %s.%s -image %s -align center -padx %s -pady %s", this.id, line, character, image.id, padding, padding);
394 
395 		return cast(T) this;
396 	}
397 
398 	/**
399 	 * Undo the last edit to the widget. This only applied to the widget if 
400 	 * undo is enabled.
401 	 *
402 	 * Returns:
403 	 *     This widget to aid method chaining.
404 	 */
405 	public auto undo(this T)()
406 	{
407 		this._tk.eval("%s edit undo", this.id);
408 
409 		return cast(T) this;
410 	}
411 
412 	/**
413 	 * Redo the last edit to the widget. This only applied to the widget if 
414 	 * undo is enabled.
415 	 *
416 	 * Returns:
417 	 *     This widget to aid method chaining.
418 	 */
419 	public auto redo(this T)()
420 	{
421 		this._tk.eval("%s edit redo", this.id);
422 
423 		return cast(T) this;
424 	}
425 
426 	/**
427 	 * Clear all undo's. This only applied to the widget if 
428 	 * undo is enabled.
429 	 *
430 	 * Returns:
431 	 *     This widget to aid method chaining.
432 	 */
433 	public auto resetUndo(this T)()
434 	{
435 		this._tk.eval("%s edit reset", this.id);
436 
437 		return cast(T) this;
438 	}
439 
440 	/**
441 	 * See a particular text index. The text widget automatically scrolls to 
442 	 * see the passed indexes.
443 	 *
444 	 * Params:
445 	 *     line = The line to see. Indexes start at 1.
446 	 *     character = The character to see. Indexes start at 0.
447 	 *
448 	 * Returns:
449 	 *     This widget to aid method chaining.
450 	 */
451 	public auto seeText(this T)(int line, int character = 0)
452 	{
453 		this._tk.eval("%s see %s.%s", this.id, line, character);
454 
455 		return cast(T) this;
456 	}
457 
458 	/**
459 	 * Cut the selected text to the clipboard.
460 	 *
461 	 * Returns:
462 	 *     This widget to aid method chaining.
463 	 */
464 	public auto cutText(this T)()
465 	{
466 		this._tk.eval("tk_textCut %s", this.id);
467 
468 		return cast(T) this;
469 	}
470 
471 	/**
472 	 * Copy the selected text to the clipboard.
473 	 *
474 	 * Returns:
475 	 *     This widget to aid method chaining.
476 	 */
477 	public auto copyText(this T)()
478 	{
479 		this._tk.eval("tk_textCopy %s", this.id);
480 
481 		return cast(T) this;
482 	}
483 
484 	/**
485 	 * Paste the selected text from the clipboard at the cursor position.
486 	 *
487 	 * Returns:
488 	 *     This widget to aid method chaining.
489 	 */
490 	public auto pasteText(this T)()
491 	{
492 		this._tk.eval("tk_textPaste %s", this.id);
493 
494 		return cast(T) this;
495 	}
496 
497 	/**
498 	 * Mixin common commands.
499 	 */
500 	mixin Border;
501 	mixin Color;
502 	mixin Font;
503 	mixin Height;
504 	mixin Relief;
505 	mixin Width;
506 	mixin XScrollCommand!(Text);
507 	mixin XView;
508 	mixin YScrollCommand!(Text);
509 	mixin YView;
510 }