1 /*
2  * This file is part of gtkD.
3  *
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * gtkD is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with gtkD; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
17  */
18 
19 module TTextView;
20 
21 private import gtk.Window;
22 
23 private import gtk.Widget;
24 private import gtk.TextView;
25 private import gtk.TextBuffer;
26 private import gtk.TextIter;
27 private import gtk.VPaned;
28 private import gtk.ScrolledWindow;
29 private import gtk.TextChildAnchor;
30 private import gtk.Button;
31 private import gtk.Menu;
32 private import gtk.MenuItem;
33 private import gtk.HScale;
34 private import gtk.Image;
35 private import gtk.Entry;
36 
37 private import gdk.Bitmap;
38 private import gdk.Pixbuf;
39 
40 private import gtk.ComboBox;
41 
42 private import glib.GException;
43 
44 version(Tango)
45 {
46 	private import tango.io.Stdout;
47 	private import tango.text.convert.Layout;
48 	private import tango.core.Vararg;
49 }
50 else
51 	private import std.stdio;
52 
53 /**
54  * reproduces the gtk-demo TextView
55  * \bug the output shows erros like (DUITest.exe:5012): GLib-GObject-WARNING **: [Invalid UTF-8] gobject.c:882: object class `GtkTextTag' has no property named `l┌E'
56  */
57 
58 public:
59 class TTextView : Window
60 {
61 	TextView view1;
62 	TextBuffer buffer;
63 	TextView view2;
64 
65 	/**
66 	 * ctor. must reactivate the constructor name change loby
67 	 */
68 	this()
69 	{
70 		super("TextView");
71 		setDefaultSize(450,450);
72 		setBorderWidth(0);
73 
74 		createTextViews();
75 
76 		setupWidgets();
77 
78 		createTags(buffer);
79 		insertText(buffer);
80 
81 		attachWidgets(view1);
82 		attachWidgets(view2);
83 
84 		showAll();
85 	}
86 
87 //	bit windowDeleteCallback(Window window, Event event)
88 //	{
89 //		return false;
90 //	}
91 //
92 //	bit windowDestroyCallback(Window window, Event event)
93 //	{
94 //		printf("TTextView windowDestroyCallback\n");
95 //		return false;
96 //	}
97 
98 	/**
99 	 * creates the text view and buffer to use
100 	 */
101 	void createTextViews()
102 	{
103 		view1 = new TextView();
104 		buffer = view1.getBuffer();
105 		view2 = new TextView(buffer);
106 	}
107 
108 	/**
109 	 * sets up the widgets ot this test
110 	 */
111 	void setupWidgets()
112 	{
113 		VPaned vPaned = new VPaned();
114 		vPaned.setBorderWidth(5);
115 		add(vPaned);
116 
117 		ScrolledWindow sw = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
118 		sw.add(view1);
119 		vPaned.add1(sw);
120 		sw = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
121 		sw.add(view2);
122 		vPaned.add2(sw);
123 	}
124 
125 	/**
126 	 * Creates all the text tags to use
127 	 * @param buffer the TextBuffer
128 	 */
129 	void createTags(TextBuffer buffer)
130 	{
131 		Bitmap stipple;
132 
133 		/* Create a bunch of tags. Note that it's also possible to
134 		* create tags with gtk_text_tag_new() then add them to the
135 		* tag table for the buffer, gtk_text_buffer_create_tag() is
136 		* just a convenience function. Also note that you don't have
137 		* to give tags a name; pass NULL for the name to create an
138 		* anonymous tag.
139 		*
140 		* In any real app, another useful optimization would be to create
141 		* a GtkTextTagTable in advance, and reuse the same tag table for
142 		* all the buffers with the same tag set, instead of creating
143 		* new copies of the same tags for every buffer.
144 		*
145 		* Tags are assigned default priorities in order of addition to the
146 		* tag table.	 That is, tags created later that affect the same text
147 		* property affected by an earlier tag will override the earlier
148 		* tag.  You can modify tag priorities with
149 		* gtk_text_tag_set_priority().
150 		*/
151 
152 		buffer.createTag("heading",
153 						"weight", PangoWeight.BOLD,
154 						"size", 15 * PANGO_SCALE);
155     	buffer.createTag("italic",
156     					"style", cast(int)PangoStyle.ITALIC);
157 
158 		buffer.createTag("bold",
159 			      "weight", cast(int)PangoWeight.BOLD);
160 
161 		buffer.createTag("big",
162 			      /* points times the PANGO_SCALE factor */
163 			      "size", 20 * PANGO_SCALE);
164 
165 		buffer.createTag("xx-small",
166 			      "scale", PANGO_SCALE_XX_SMALL);
167 
168 		buffer.createTag("x-large",
169 			      "scale", PANGO_SCALE_X_LARGE);
170 
171 		buffer.createTag("monospace",
172 			      "family", "monospace");
173 
174 		buffer.createTag("blue_foreground",
175 			      "foreground", "blue");
176 
177 		buffer.createTag("red_background",
178 			      "background", "red");
179 
180 enum {
181 	gray50_width = 2,
182 	gray50_height = 2
183 }
184 
185 static string gray50_bits = [0x02, 0x01];
186 
187     	stipple = Bitmap.createFromData(null,   // drawablw
188 					 gray50_bits, gray50_width,
189 					 gray50_height);
190 
191 		buffer.createTag("background_stipple",
192 			      "background_stipple", stipple);
193 
194 		buffer.createTag("foreground_stipple",
195 			      "foreground_stipple", stipple);
196 
197 		buffer.createTag("big_gap_before_line",
198 			      "pixels_above_lines", 30);
199 
200 		buffer.createTag("big_gap_after_line",
201 			      "pixels_below_lines", 30);
202 
203 		buffer.createTag("double_spaced_line",
204 			      "pixels_inside_wrap", 10);
205 
206 		buffer.createTag("not_editable",
207 			      "editable", cast(int)false);
208 
209 		buffer.createTag("word_wrap",
210 			      "wrap_mode", cast(int)WrapMode.WORD);
211 
212 		buffer.createTag("char_wrap",
213 			      "wrap_mode", cast(int)WrapMode.CHAR);
214 
215 		buffer.createTag("no_wrap",
216 			      "wrap_mode", cast(int)WrapMode.NONE);
217 
218 		buffer.createTag("center",
219 			      "justification", cast(int)Justification.JUSTIFY_CENTER);
220 
221 		buffer.createTag("right_justify",
222 			      "justification", cast(int)Justification.JUSTIFY_RIGHT);
223 
224 		buffer.createTag("wide_margins",
225 			      "left_margin", 50, "right_margin", 50);
226 
227 		buffer.createTag("strikethrough",
228 			      "strikethrough", cast(int)true);
229 
230 		buffer.createTag("underline",
231 			      "underline", cast(int)PangoUnderline.SINGLE);
232 
233 		buffer.createTag("double_underline",
234 			      "underline", cast(int)PangoUnderline.DOUBLE);
235 
236 		buffer.createTag("superscript",
237 			      "rise", 10 * PANGO_SCALE,	  /* 10 pixels */
238 			      "size", 8 * PANGO_SCALE);	  /* 8 points */
239 
240 		buffer.createTag("subscript",
241 			      "rise", -10 * PANGO_SCALE,   /* 10 pixels */
242 			      "size", 8 * PANGO_SCALE);	   /* 8 points */
243 
244 
245 		buffer.createTag("rtl_quote",
246 			      "wrap_mode", WrapMode.WORD,
247 			      "direction", TextDirection.RTL,
248 			      "indent", 30,
249 			      "left_margin", 20,
250 			      "right_margin", 20);
251 
252 	}
253 	/**
254 	 * Inserts all the test text into the buffer
255 	 * @param buffer the TextBuffer
256 	 */
257 	void insertText(TextBuffer buffer)
258 	{
259 		//version(Tango) {} else
260 		{
261 		TextIter iter = new TextIter();
262 		TextIter start = new TextIter();
263 		TextIter end = new TextIter();
264 		Pixbuf pixbuf;
265 		Pixbuf scaled;
266 		TextChildAnchor anchor;
267 		string filename;
268 
269 		/* demo_find_file() looks in the the current directory first,
270 		* so you can run gtk-demo without installing GTK, then looks
271 		* in the location where the file is installed.
272 		*/
273 
274 		try
275 		{
276 			pixbuf = new Pixbuf("images/gtk-logo-rgb.gif");
277 
278 			scaled = pixbuf.scaleSimple(32, 32, InterpType.BILINEAR);
279 			pixbuf = pixbuf.scaleSimple(38, 38, InterpType.BILINEAR);
280 		}
281 		catch (Exception)
282 		{
283 			version(Tango) Stdout("Failed to load image file gtk-logo-rgb.gif").newline;
284 			else writef("Failed to load image file gtk-logo-rgb.gif\n");
285 		}
286 
287 		/* get start of buffer; each insertion will revalidate the
288 		* iterator to point to just after the inserted text.
289 		*/
290 
291 		buffer.getIterAtOffset(iter, 0);
292 
293 		buffer.insert(iter, "The text widget can display text with all kinds of nifty attributes. It also supports multiple views of the same buffer; this demo is showing the same buffer in two places.\n\n");
294 
295 		buffer.insertWithTagsByName(iter, "Font styles. ","heading");
296 
297 		buffer.insert(iter, "For example, you can have ");
298 		buffer.insertWithTagsByName(iter, "italic", "italic");
299 		buffer.insert(iter, ", ");
300 		buffer.insertWithTagsByName(iter, "bold", "bold");
301 		buffer.insert(iter, ", or ");
302 		buffer.insertWithTagsByName(iter, "monospace (typewriter)", "monospace");
303 		buffer.insert(iter, ", or ");
304 		buffer.insertWithTagsByName(iter, "big", "big");
305 		buffer.insert(iter, " text. ");
306 		buffer.insert(iter, "It's best not to hardcode specific text sizes; you can use relative sizes as with CSS, such as ");
307 		buffer.insertWithTagsByName(iter, "xx-small", "xx-small");
308 		buffer.insert(iter, " or ");
309 		buffer.insertWithTagsByName(iter, "x-large", "x-large");
310 		buffer.insert(iter, " to ensure that your program properly adapts if the user changes the default font size.\n\n");
311 
312 		buffer.insertWithTagsByName(iter, "Colors. ", "heading");
313 
314 		buffer.insert(iter, "Colors such as ");
315 		buffer.insertWithTagsByName(iter, "a blue foreground", "blue_foreground");
316 		buffer.insert(iter, " or ");
317 		buffer.insertWithTagsByName(iter, "a red background", "red_background");
318 		buffer.insert(iter, " or even ");
319 		buffer.insertWithTagsByName(iter, "a stippled red background",
320 									"red_background", "background_stipple");
321 
322 		buffer.insert(iter, " or ");
323 		buffer.insertWithTagsByName(iter,
324 			"a stippled blue foreground on solid red background",
325 			"blue_foreground",
326 			"red_background",
327 			"foreground_stipple");
328 		buffer.insert(iter, " (select that to read it) can be used.\n\n");
329 
330 		buffer.insertWithTagsByName(iter, "Underline, strikethrough, and rise. ", "heading");
331 
332 		buffer.insertWithTagsByName(iter, "Strikethrough", "strikethrough");
333 		buffer.insert(iter, ", ");
334 		buffer.insertWithTagsByName(iter, "underline", "underline");
335 		buffer.insert(iter, ", ");
336 		buffer.insertWithTagsByName(iter, "double underline", "double_underline");
337 		buffer.insert(iter, ", ");
338 		buffer.insertWithTagsByName(iter, "superscript", "superscript");
339 		buffer.insert(iter, ", and ");
340 		buffer.insertWithTagsByName(iter, "subscript", "subscript");
341 		buffer.insert(iter, " are all supported.\n\n");
342 
343 		buffer.insertWithTagsByName(iter, "Images. ", "heading");
344 
345 		buffer.insert(iter, "The buffer can have images in it: ");
346 		if ( pixbuf !is  null )
347 		{
348 			buffer.insertPixbuf(iter, scaled);
349 			buffer.insertPixbuf(iter, pixbuf);
350 			buffer.insertPixbuf(iter, scaled);
351 		}
352 		else
353 		{
354 			buffer.insert(iter,"Sorry can't find the images");
355 		}
356 		buffer.insert(iter, " for example.\n\n");
357 
358 		buffer.insertWithTagsByName(iter, "Spacing. ", "heading");
359 
360 		buffer.insert(iter, "You can adjust the amount of space before each line.\n");
361 
362 		buffer.insertWithTagsByName(iter, "This line has a whole lot of space before it.\n",
363 					    "big_gap_before_line", "wide_margins");
364 		buffer.insertWithTagsByName(iter,
365 					    "You can also adjust the amount of space after each line; this line has a whole lot of space after it.\n",
366 					    "big_gap_after_line", "wide_margins");
367 
368 		buffer.insertWithTagsByName(iter,
369 					    "You can also adjust the amount of space between wrapped lines; this line has extra space between each wrapped line in the same paragraph. To show off wrapping, some filler text: the quick brown fox jumped over the lazy dog. Blah blah blah blah blah blah blah blah blah.\n",
370 					    "double_spaced_line", "wide_margins");
371 
372 		buffer.insert(iter, "Also note that those lines have extra-wide margins.\n\n");
373 
374 		buffer.insertWithTagsByName(iter, "Editability. ", "heading");
375 
376 		buffer.insertWithTagsByName(iter,
377 					    "This line is 'locked down' and can't be edited by the user - just try it! You can't delete this line.\n\n",
378 					    "not_editable");
379 
380 		buffer.insertWithTagsByName(iter, "Wrapping. ", "heading");
381 
382 		buffer.insert(iter,
383 			  "This line (and most of the others in this buffer) is word-wrapped, using the proper Unicode algorithm. Word wrap should work in all scripts and languages that GTK+ supports. Let's make this a long paragraph to demonstrate: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah\n\n");
384 
385 		buffer.insertWithTagsByName(iter,
386 					    "This line has character-based wrapping, and can wrap between any two character glyphs. Let's make this a long paragraph to demonstrate: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah\n\n",
387 					    "char_wrap");
388 
389 		buffer.insertWithTagsByName(iter,
390 					    "This line has all wrapping turned off, so it makes the horizontal scrollbar appear.\n\n\n",
391 					    "no_wrap");
392 
393 		buffer.insertWithTagsByName(iter, "Justification. ", "heading");
394 
395 		buffer.insertWithTagsByName(iter,
396 					    "\nThis line has center justification.\n", "center");
397 
398 		buffer.insertWithTagsByName(iter,
399 					    "This line has right justification.\n", "right_justify");
400 
401 		buffer.insertWithTagsByName(iter,
402 					    "\nThis line has big wide margins. Text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text.\n",
403 					    "wide_margins");
404 
405 		buffer.insertWithTagsByName(iter, "Internationalization. ", "heading");
406 
407 		buffer.insert(iter,
408 			  "You can put all sorts of Unicode text in the buffer.\n\nGerman (Deutsch S\303\274d) Gr\303\274\303\237 Gott\nGreek (\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254) \316\223\316\265\316\271\316\254 \317\203\316\261\317\202\nHebrew	\327\251\327\234\327\225\327\235\nJapanese (\346\227\245\346\234\254\350\252\236)\n\nThe widget properly handles bidirectional text, word wrapping, DOS/UNIX/Unicode paragraph separators, grapheme boundaries, and so on using the Pango internationalization framework.\n");
409 
410 		buffer.insert(iter, "Here's a word-wrapped quote in a right-to-left language:\n");
411 		buffer.insertWithTagsByName(iter, "\331\210\331\202\330\257 \330\250\330\257\330\243 \330\253\331\204\330\247\330\253 \331\205\331\206 \330\243\331\203\330\253\330\261 \330\247\331\204\331\205\330\244\330\263\330\263\330\247\330\252 \330\252\331\202\330\257\331\205\330\247 \331\201\331\212 \330\264\330\250\331\203\330\251 \330\247\331\203\330\263\331\212\331\210\331\206 \330\250\330\261\330\247\331\205\330\254\331\207\330\247 \331\203\331\205\331\206\330\270\331\205\330\247\330\252 \331\204\330\247 \330\252\330\263\330\271\331\211 \331\204\331\204\330\261\330\250\330\255\330\214 \330\253\331\205 \330\252\330\255\331\210\331\204\330\252 \331\201\331\212 \330\247\331\204\330\263\331\206\331\210\330\247\330\252 \330\247\331\204\330\256\331\205\330\263 \330\247\331\204\331\205\330\247\330\266\331\212\330\251 \330\245\331\204\331\211 \331\205\330\244\330\263\330\263\330\247\330\252 \331\205\330\247\331\204\331\212\330\251 \331\205\331\206\330\270\331\205\330\251\330\214 \331\210\330\250\330\247\330\252\330\252 \330\254\330\262\330\241\330\247 \331\205\331\206 \330\247\331\204\331\206\330\270\330\247\331\205 \330\247\331\204\331\205\330\247\331\204\331\212 \331\201\331\212 \330\250\331\204\330\257\330\247\331\206\331\207\330\247\330\214 \331\210\331\204\331\203\331\206\331\207\330\247 \330\252\330\252\330\256\330\265\330\265 \331\201\331\212 \330\256\330\257\331\205\330\251 \331\202\330\267\330\247\330\271 \330\247\331\204\331\205\330\264\330\261\331\210\330\271\330\247\330\252 \330\247\331\204\330\265\330\272\331\212\330\261\330\251. \331\210\330\243\330\255\330\257 \330\243\331\203\330\253\330\261 \331\207\330\260\331\207 \330\247\331\204\331\205\330\244\330\263\330\263\330\247\330\252 \331\206\330\254\330\247\330\255\330\247 \331\207\331\210 \302\273\330\250\330\247\331\206\331\203\331\210\330\263\331\210\331\204\302\253 \331\201\331\212 \330\250\331\210\331\204\331\212\331\201\331\212\330\247.\n\n",
412 						"rtl_quote");
413 
414 		buffer.insert(iter, "You can put widgets in the buffer: Here's a button: ");
415 		anchor = buffer.createChildAnchor(iter);
416 		buffer.insert(iter, " and a menu: ");
417 		anchor = buffer.createChildAnchor(iter);
418 		buffer.insert(iter, " and a scale: ");
419 		anchor = buffer.createChildAnchor(iter);
420 		buffer.insert(iter, " and an animation: ");
421 		anchor = buffer.createChildAnchor(iter);
422 		buffer.insert(iter, " finally a text entry: ");
423 		anchor = buffer.createChildAnchor(iter);
424 		buffer.insert(iter, ".\n");
425 
426 		buffer.insert(iter, "\n\nThis demo doesn't demonstrate all the GtkTextBuffer features; it leaves out, for example: invisible/hidden text (doesn't work in GTK 2, but planned), tab stops, application-drawn areas on the sides of the widget for displaying breakpoints and such...");
427 
428     	/* Apply word_wrap tag to whole buffer */
429 		buffer.getBounds(start, end);
430 		buffer.applyTagByName("word_wrap", start, end);
431 		}
432 	}
433 
434 	/**
435 	 * Attaches all test widgets to the text view
436 	 * @param view the TextView
437 	 */
438 	void attachWidgets(TextView view)
439 	{
440 		TextIter iter = new TextIter();
441 		TextBuffer buffer;
442 		int i;
443 
444 		buffer = view.getBuffer();
445 
446     	buffer.getStartIter(iter);
447 
448 		i = 0;
449 
450 		while (findAnchor(iter))
451 		{
452 			TextChildAnchor anchor;// = new TextChildAnchor();
453 			Widget widget;
454 			anchor = iter.getChildAnchor();
455 
456 			if (i == 0)
457 			{
458 				Button button = new Button("Click Me");
459 				widget = button;
460 			}
461 			else if (i == 1)
462         	{
463 
464 				ComboBox comboBox = new ComboBox();
465 				comboBox.appendText("Option 1");
466 				comboBox.appendText("Option 2");
467 				comboBox.appendText("Option 3");
468 				widget = comboBox;
469 			}
470 			else if (i == 2)
471 			{
472 				HScale hScale = new HScale(0.0,100.0,5.0);
473 				hScale.setSizeRequest(70, -1);
474 				widget = hScale;
475 			}
476 			else if (i == 3)
477 			{
478 				Image image = new Image("images/floppybuddy.gif");
479 				widget = image;
480 			}
481 			else if (i == 4)
482 			{
483 				widget = new Entry();
484 			}
485 			else
486 			{
487 				widget = null;
488 				//g_assert_not_reached ();
489 			}
490 
491 			if ( widget !is  null )
492 			{
493 				view.addChildAtAnchor(widget,anchor);
494 			}
495 
496 			++i;
497 		}
498 	}
499 
500 	bool findAnchor (TextIter iter)
501 	{
502 		while (iter.forwardChar())
503 		{
504 			if (iter.getChildAnchor() !is  null ) //gtk_text_iter_get_child_anchor (iter))
505 			{
506 				return true;
507 			}
508 		}
509 		return false;
510 	}
511 
512 }