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.Pixbuf;
38 
39 private import gtk.ComboBoxText;
40 
41 private import glib.GException;
42 
43 version(Tango)
44 {
45 	private import tango.io.Stdout;
46 	private import tango.text.convert.Layout;
47 	private import tango.core.Vararg;
48 }
49 else
50 	private import std.stdio;
51 
52 /**
53  * reproduces the gtk-demo TextView
54  * \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'
55  */
56 
57 public:
58 class TTextView : Window
59 {
60 	TextView view1;
61 	TextBuffer buffer;
62 	TextView view2;
63 
64 	/**
65 	 * ctor. must reactivate the constructor name change loby
66 	 */
67 	this()
68 	{
69 		super("TextView");
70 		setDefaultSize(450,450);
71 		setBorderWidth(0);
72 
73 		createTextViews();
74 
75 		setupWidgets();
76 
77 		createTags(buffer);
78 		insertText(buffer);
79 
80 		attachWidgets(view1);
81 		attachWidgets(view2);
82 
83 		showAll();
84 	}
85 
86 //	bit windowDeleteCallback(Window window, Event event)
87 //	{
88 //		return false;
89 //	}
90 //
91 //	bit windowDestroyCallback(Window window, Event event)
92 //	{
93 //		printf("TTextView windowDestroyCallback\n");
94 //		return false;
95 //	}
96 
97 	/**
98 	 * creates the text view and buffer to use
99 	 */
100 	void createTextViews()
101 	{
102 		view1 = new TextView();
103 		buffer = view1.getBuffer();
104 		view2 = new TextView(buffer);
105 	}
106 
107 	/**
108 	 * sets up the widgets ot this test
109 	 */
110 	void setupWidgets()
111 	{
112 		VPaned vPaned = new VPaned();
113 		vPaned.setBorderWidth(5);
114 		add(vPaned);
115 
116 		ScrolledWindow sw = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
117 		sw.add(view1);
118 		vPaned.add1(sw);
119 		sw = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
120 		sw.add(view2);
121 		vPaned.add2(sw);
122 	}
123 
124 	/**
125 	 * Creates all the text tags to use
126 	 * @param buffer the TextBuffer
127 	 */
128 	void createTags(TextBuffer buffer)
129 	{
130 		/* Create a bunch of tags. Note that it's also possible to
131 		* create tags with gtk_text_tag_new() then add them to the
132 		* tag table for the buffer, gtk_text_buffer_create_tag() is
133 		* just a convenience function. Also note that you don't have
134 		* to give tags a name; pass NULL for the name to create an
135 		* anonymous tag.
136 		*
137 		* In any real app, another useful optimization would be to create
138 		* a GtkTextTagTable in advance, and reuse the same tag table for
139 		* all the buffers with the same tag set, instead of creating
140 		* new copies of the same tags for every buffer.
141 		*
142 		* Tags are assigned default priorities in order of addition to the
143 		* tag table.	 That is, tags created later that affect the same text
144 		* property affected by an earlier tag will override the earlier
145 		* tag.  You can modify tag priorities with
146 		* gtk_text_tag_set_priority().
147 		*/
148 
149 		buffer.createTag("heading",
150 						"weight", PangoWeight.BOLD,
151 						"size", 15 * PANGO_SCALE);
152     	buffer.createTag("italic",
153     					"style", cast(int)PangoStyle.ITALIC);
154 
155 		buffer.createTag("bold",
156 			      "weight", cast(int)PangoWeight.BOLD);
157 
158 		buffer.createTag("big",
159 			      /* points times the PANGO_SCALE factor */
160 			      "size", 20 * PANGO_SCALE);
161 
162 		buffer.createTag("xx-small",
163 			      "scale", PANGO_SCALE_XX_SMALL);
164 
165 		buffer.createTag("x-large",
166 			      "scale", PANGO_SCALE_X_LARGE);
167 
168 		buffer.createTag("monospace",
169 			      "family", "monospace");
170 
171 		buffer.createTag("blue_foreground",
172 			      "foreground", "blue");
173 
174 		buffer.createTag("red_background",
175 			      "background", "red");
176 
177 		buffer.createTag("big_gap_before_line",
178 			      "pixels_above_lines", 30);
179 
180 		buffer.createTag("big_gap_after_line",
181 			      "pixels_below_lines", 30);
182 
183 		buffer.createTag("double_spaced_line",
184 			      "pixels_inside_wrap", 10);
185 
186 		buffer.createTag("not_editable",
187 			      "editable", cast(int)false);
188 
189 		buffer.createTag("word_wrap",
190 			      "wrap_mode", cast(int)WrapMode.WORD);
191 
192 		buffer.createTag("char_wrap",
193 			      "wrap_mode", cast(int)WrapMode.CHAR);
194 
195 		buffer.createTag("no_wrap",
196 			      "wrap_mode", cast(int)WrapMode.NONE);
197 
198 		buffer.createTag("center",
199 			      "justification", cast(int)Justification.JUSTIFY_CENTER);
200 
201 		buffer.createTag("right_justify",
202 			      "justification", cast(int)Justification.JUSTIFY_RIGHT);
203 
204 		buffer.createTag("wide_margins",
205 			      "left_margin", 50, "right_margin", 50);
206 
207 		buffer.createTag("strikethrough",
208 			      "strikethrough", cast(int)true);
209 
210 		buffer.createTag("underline",
211 			      "underline", cast(int)PangoUnderline.SINGLE);
212 
213 		buffer.createTag("double_underline",
214 			      "underline", cast(int)PangoUnderline.DOUBLE);
215 
216 		buffer.createTag("superscript",
217 			      "rise", 10 * PANGO_SCALE,	  /* 10 pixels */
218 			      "size", 8 * PANGO_SCALE);	  /* 8 points */
219 
220 		buffer.createTag("subscript",
221 			      "rise", -10 * PANGO_SCALE,   /* 10 pixels */
222 			      "size", 8 * PANGO_SCALE);	   /* 8 points */
223 
224 
225 		buffer.createTag("rtl_quote",
226 			      "wrap_mode", WrapMode.WORD,
227 			      "direction", TextDirection.RTL,
228 			      "indent", 30,
229 			      "left_margin", 20,
230 			      "right_margin", 20);
231 
232 	}
233 	/**
234 	 * Inserts all the test text into the buffer
235 	 * @param buffer the TextBuffer
236 	 */
237 	void insertText(TextBuffer buffer)
238 	{
239 		//version(Tango) {} else
240 		{
241 		TextIter iter = new TextIter();
242 		TextIter start = new TextIter();
243 		TextIter end = new TextIter();
244 		Pixbuf pixbuf;
245 		Pixbuf scaled;
246 		TextChildAnchor anchor;
247 		string filename;
248 
249 		/* demo_find_file() looks in the the current directory first,
250 		* so you can run gtk-demo without installing GTK, then looks
251 		* in the location where the file is installed.
252 		*/
253 
254 		try
255 		{
256 			pixbuf = new Pixbuf("images/gtk-logo-rgb.gif");
257 
258 			scaled = pixbuf.scaleSimple(32, 32, InterpType.BILINEAR);
259 			pixbuf = pixbuf.scaleSimple(38, 38, InterpType.BILINEAR);
260 		}
261 		catch (Exception)
262 		{
263 			version(Tango) Stdout("Failed to load image file gtk-logo-rgb.gif").newline;
264 			else writef("Failed to load image file gtk-logo-rgb.gif\n");
265 		}
266 
267 		/* get start of buffer; each insertion will revalidate the
268 		* iterator to point to just after the inserted text.
269 		*/
270 
271 		buffer.getIterAtOffset(iter, 0);
272 
273 		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");
274 
275 		buffer.insertWithTagsByName(iter, "Font styles. ","heading");
276 
277 		buffer.insert(iter, "For example, you can have ");
278 		buffer.insertWithTagsByName(iter, "italic", "italic");
279 		buffer.insert(iter, ", ");
280 		buffer.insertWithTagsByName(iter, "bold", "bold");
281 		buffer.insert(iter, ", or ");
282 		buffer.insertWithTagsByName(iter, "monospace (typewriter)", "monospace");
283 		buffer.insert(iter, ", or ");
284 		buffer.insertWithTagsByName(iter, "big", "big");
285 		buffer.insert(iter, " text. ");
286 		buffer.insert(iter, "It's best not to hardcode specific text sizes; you can use relative sizes as with CSS, such as ");
287 		buffer.insertWithTagsByName(iter, "xx-small", "xx-small");
288 		buffer.insert(iter, " or ");
289 		buffer.insertWithTagsByName(iter, "x-large", "x-large");
290 		buffer.insert(iter, " to ensure that your program properly adapts if the user changes the default font size.\n\n");
291 
292 		buffer.insertWithTagsByName(iter, "Colors. ", "heading");
293 
294 		buffer.insert(iter, "Colors such as ");
295 		buffer.insertWithTagsByName(iter, "a blue foreground", "blue_foreground");
296 		buffer.insert(iter, " or ");
297 		buffer.insertWithTagsByName(iter, "a red background", "red_background");
298 		buffer.insert(iter, " can be used.\n\n");
299 
300 		buffer.insertWithTagsByName(iter, "Underline, strikethrough, and rise. ", "heading");
301 
302 		buffer.insertWithTagsByName(iter, "Strikethrough", "strikethrough");
303 		buffer.insert(iter, ", ");
304 		buffer.insertWithTagsByName(iter, "underline", "underline");
305 		buffer.insert(iter, ", ");
306 		buffer.insertWithTagsByName(iter, "double underline", "double_underline");
307 		buffer.insert(iter, ", ");
308 		buffer.insertWithTagsByName(iter, "superscript", "superscript");
309 		buffer.insert(iter, ", and ");
310 		buffer.insertWithTagsByName(iter, "subscript", "subscript");
311 		buffer.insert(iter, " are all supported.\n\n");
312 
313 		buffer.insertWithTagsByName(iter, "Images. ", "heading");
314 
315 		buffer.insert(iter, "The buffer can have images in it: ");
316 		if ( pixbuf !is  null )
317 		{
318 			buffer.insertPixbuf(iter, scaled);
319 			buffer.insertPixbuf(iter, pixbuf);
320 			buffer.insertPixbuf(iter, scaled);
321 		}
322 		else
323 		{
324 			buffer.insert(iter,"Sorry can't find the images");
325 		}
326 		buffer.insert(iter, " for example.\n\n");
327 
328 		buffer.insertWithTagsByName(iter, "Spacing. ", "heading");
329 
330 		buffer.insert(iter, "You can adjust the amount of space before each line.\n");
331 
332 		buffer.insertWithTagsByName(iter, "This line has a whole lot of space before it.\n",
333 					    "big_gap_before_line", "wide_margins");
334 		buffer.insertWithTagsByName(iter,
335 					    "You can also adjust the amount of space after each line; this line has a whole lot of space after it.\n",
336 					    "big_gap_after_line", "wide_margins");
337 
338 		buffer.insertWithTagsByName(iter,
339 					    "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",
340 					    "double_spaced_line", "wide_margins");
341 
342 		buffer.insert(iter, "Also note that those lines have extra-wide margins.\n\n");
343 
344 		buffer.insertWithTagsByName(iter, "Editability. ", "heading");
345 
346 		buffer.insertWithTagsByName(iter,
347 					    "This line is 'locked down' and can't be edited by the user - just try it! You can't delete this line.\n\n",
348 					    "not_editable");
349 
350 		buffer.insertWithTagsByName(iter, "Wrapping. ", "heading");
351 
352 		buffer.insert(iter,
353 			  "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");
354 
355 		buffer.insertWithTagsByName(iter,
356 					    "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",
357 					    "char_wrap");
358 
359 		buffer.insertWithTagsByName(iter,
360 					    "This line has all wrapping turned off, so it makes the horizontal scrollbar appear.\n\n\n",
361 					    "no_wrap");
362 
363 		buffer.insertWithTagsByName(iter, "Justification. ", "heading");
364 
365 		buffer.insertWithTagsByName(iter,
366 					    "\nThis line has center justification.\n", "center");
367 
368 		buffer.insertWithTagsByName(iter,
369 					    "This line has right justification.\n", "right_justify");
370 
371 		buffer.insertWithTagsByName(iter,
372 					    "\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",
373 					    "wide_margins");
374 
375 		buffer.insertWithTagsByName(iter, "Internationalization. ", "heading");
376 
377 		buffer.insert(iter,
378 			  "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");
379 
380 		buffer.insert(iter, "Here's a word-wrapped quote in a right-to-left language:\n");
381 		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",
382 						"rtl_quote");
383 
384 		buffer.insert(iter, "You can put widgets in the buffer: Here's a button: ");
385 		anchor = buffer.createChildAnchor(iter);
386 		buffer.insert(iter, " and a menu: ");
387 		anchor = buffer.createChildAnchor(iter);
388 		buffer.insert(iter, " and a scale: ");
389 		anchor = buffer.createChildAnchor(iter);
390 		buffer.insert(iter, " and an animation: ");
391 		anchor = buffer.createChildAnchor(iter);
392 		buffer.insert(iter, " finally a text entry: ");
393 		anchor = buffer.createChildAnchor(iter);
394 		buffer.insert(iter, ".\n");
395 
396 		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...");
397 
398     	/* Apply word_wrap tag to whole buffer */
399 		buffer.getBounds(start, end);
400 		buffer.applyTagByName("word_wrap", start, end);
401 		}
402 	}
403 
404 	/**
405 	 * Attaches all test widgets to the text view
406 	 * @param view the TextView
407 	 */
408 	void attachWidgets(TextView view)
409 	{
410 		TextIter iter = new TextIter();
411 		TextBuffer buffer;
412 		int i;
413 
414 		buffer = view.getBuffer();
415 
416     	buffer.getStartIter(iter);
417 
418 		i = 0;
419 
420 		while (findAnchor(iter))
421 		{
422 			TextChildAnchor anchor;// = new TextChildAnchor();
423 			Widget widget;
424 			anchor = iter.getChildAnchor();
425 
426 			if (i == 0)
427 			{
428 				Button button = new Button("Click Me");
429 				widget = button;
430 			}
431 			else if (i == 1)
432         	{
433 
434 				ComboBoxText comboBox = new ComboBoxText();
435 				comboBox.appendText("Option 1");
436 				comboBox.appendText("Option 2");
437 				comboBox.appendText("Option 3");
438 				widget = comboBox;
439 			}
440 			else if (i == 2)
441 			{
442 				HScale hScale = new HScale(0.0,100.0,5.0);
443 				hScale.setSizeRequest(70, -1);
444 				widget = hScale;
445 			}
446 			else if (i == 3)
447 			{
448 				Image image = new Image("images/floppybuddy.gif");
449 				widget = image;
450 			}
451 			else if (i == 4)
452 			{
453 				widget = new Entry();
454 			}
455 			else
456 			{
457 				widget = null;
458 				//g_assert_not_reached ();
459 			}
460 
461 			if ( widget !is  null )
462 			{
463 				view.addChildAtAnchor(widget,anchor);
464 			}
465 
466 			++i;
467 		}
468 	}
469 
470 	bool findAnchor (TextIter iter)
471 	{
472 		while (iter.forwardChar())
473 		{
474 			if (iter.getChildAnchor() !is  null ) //gtk_text_iter_get_child_anchor (iter))
475 			{
476 				return true;
477 			}
478 		}
479 		return false;
480 	}
481 
482 }