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 TestDrawingArea;
20 
21 //debug = trace;
22 
23 private import gtk.VBox;
24 private import gdk.Font;
25 private import pango.PgContext;
26 private import pango.PgLayout;
27 private import gdk.ImageGdk;
28 
29 version(Tango) private import tango.io.Stdout;
30 version(Tango) private import tango.stdc.stdio;
31 else private import std.stdio;
32 
33 version(Tango) private import tango.math.Math;
34 else private import std.math;
35 
36 private import gtk.Widget;
37 private import gtk.MenuItem;
38 private import gtk.ComboBox;
39 private import gtk.Menu;
40 private import gtk.Adjustment;
41 private import gtk.HBox;
42 private import gdk.Pixbuf;
43 //private import gdk.Point;
44 private import gdk.Color;
45 private import gdk.Drawable;
46 private import gdk.GC;
47 private import pango.PgFontDescription;
48 
49 private import gtk.DrawingArea;
50 private import gtk.Image;
51 private import gtk.SpinButton;
52 
53 private import gdk.Event;
54 
55 private import gdk.Color;
56 
57 //private import event.Event;
58 
59 
60 /**
61  * This tests the gtkD drawing area widget
62  */
63 class TestDrawingArea : VBox
64 {
65 
66 	this()
67 	{
68 		debug(Tango) Stdout("TestDrawingArea.this() 1").newline;
69 		super(false,4);
70 
71 		debug(Tango) Stdout("TestDrawingArea.this() 2").newline;
72 		TestDrawing drawingArea = new TestDrawing();
73 
74 		debug(Tango) Stdout("TestDrawingArea.this() 3").newline;
75 
76 		ComboBox gcOptions = new ComboBox();
77 		gcOptions.appendText("GC COPY");
78 		gcOptions.appendText("GC INVERT");
79 		gcOptions.appendText("GC XOR");
80 		gcOptions.appendText("GC CLEAR");
81 		gcOptions.appendText("GC AND");
82 		gcOptions.appendText("GC AND_REVERSE");
83 		gcOptions.appendText("GC AND_INVERT");
84 		gcOptions.appendText("GC NOOP");
85 		gcOptions.appendText("GC OR");
86 		gcOptions.appendText("GC EQUIV");
87 		gcOptions.appendText("GC OR_REVERSE");
88 		gcOptions.appendText("GC COPY_INVERT");
89 		gcOptions.appendText("GC OR_INVERT");
90 		gcOptions.appendText("GC NAND");
91 		gcOptions.appendText("GC NOR");
92 		gcOptions.appendText("GC SET");
93 		gcOptions.setActive(1);
94 		gcOptions.addOnChanged(&drawingArea.onCGOptionsChanged);
95 
96 		debug(Tango) Stdout("TestDrawingArea.this() 4").newline;
97 
98 		ComboBox primOption = new ComboBox();
99 		primOption.appendText("Filled Arc");
100 		primOption.appendText("Arc");
101 		primOption.appendText("Line");
102 		primOption.appendText("Point");
103 		primOption.appendText("Rectangle");
104 		primOption.appendText("Filled Rectangle");
105 		primOption.appendText("Text");
106 		primOption.appendText("Pango text");
107 		primOption.appendText("Image");
108 		primOption.appendText("Polygon");
109 		primOption.setActive(0);
110 		primOption.addOnChanged(&drawingArea.onPrimOptionChanged);
111 
112 		debug(Tango) Stdout("TestDrawingArea.this() 5").newline;
113 
114 		packStart(drawingArea,true,true,0);
115 
116 		HBox hbox = new HBox(false,4);
117 		hbox.packStart(gcOptions,false,false,2);
118 		hbox.packStart(primOption,false,false,2);
119 		hbox.packStart(drawingArea.spin,false,false,2);
120 		hbox.packStart(drawingArea.backSpin,false,false,2);
121 
122 		debug(Tango) Stdout("TestDrawingArea.this() 6").newline;
123 
124 		packStart(hbox, false, false, 0);
125 
126 		debug(Tango) Stdout("TestDrawingArea.this() END").newline;
127 	}
128 
129 
130 	class TestDrawing : DrawingArea
131 	{
132 
133 
134 		GdkFunction gcFunction = GdkFunction.INVERT;
135 		Color paintColor;
136 		Color black;
137 
138 		int width;
139 		int height;
140 
141 		bool buttonIsDown;
142 
143 		string primitiveType;
144 		PgFontDescription font;
145 		Image image;
146 		Pixbuf scaledPixbuf;
147 
148 		SpinButton spin;
149 		SpinButton backSpin;
150 		static GdkPoint[] polygonStar = [
151 			{0,4},
152 			{1,1},
153 			{4,0},
154 			{1,-1},
155 			{0,-4},
156 			{-1,-1},
157 			{-4,0},
158 			{-1,1}
159 			];
160 
161 		this()
162 		{
163 			debug(Tango) Stdout("TestDrawing.this() 1").newline;
164 
165 			setSizeRequest(333,333);
166 			width = getWidth();
167 			height = getHeight();
168 
169 			debug(Tango) Stdout("TestDrawing.this() 2").newline;
170 
171 			primitiveType = "Filled Arc";
172 			font = PgFontDescription.fromString("Courier 48");
173 
174 			image = new Image("../../../images/gtkDlogo_a_small.png");
175 			scaledPixbuf = image.getPixbuf();
176 			if (scaledPixbuf is null)
177 			{
178 				version(Tango) Stdout("\nFailed to load image gtkDlogo_a_small.png").newline;
179 				else printf("\nFailed to load image file gtkDlogo_a_small.png\n");
180 			}
181 
182 			paintColor = new Color(cast(ubyte)0,cast(ubyte)0,cast(ubyte)0);
183 			black = new Color(cast(ubyte)0,cast(ubyte)0,cast(ubyte)0);
184 
185 			spin = new SpinButton(new Adjustment(30, 1, 400, 1, 10, 0),1,0);
186 			sizeSpinChanged(spin);
187 			spin.addOnValueChanged(&sizeSpinChanged);
188 			backSpin = new SpinButton(new Adjustment(5, 4, 100, 1, 10, 0),1,0);
189 			backSpin.addOnValueChanged(&backSpinChanged);
190 
191 
192 
193 			addOnExpose(&exposeCallback);
194 //			addOnMap(&mapCallback);
195 			addOnMotionNotify(&onMotionNotify);
196 			addOnSizeAllocate(&onSizeAllocate);
197 			addOnButtonPress(&onButtonPress);
198 			addOnButtonRelease(&onButtonRelease);
199 		}
200 
201 		void onSizeAllocate(GtkAllocation* allocation, Widget widget)
202 		{
203 			width = allocation.width;
204 			height = allocation.height;
205 		}
206 
207 
208 		public bool onButtonPress(GdkEventButton* event, Widget widget)
209 		{
210 			debug(trace) version(Tango) Stdout("button DOWN").newline;
211 			else writefln("button DOWN");
212 			if ( event.button == 1 )
213 			{
214 				debug(trace) version(Tango) Stdout("Button 1 down").newline;
215 				else writefln("Button 1 down");
216 				buttonIsDown = true;
217 				Drawable d = getWindow();
218 				GC gc = new GC(d);
219 				gc.setForeground(new Color(cast(ubyte)0,cast(ubyte)0,cast(ubyte)0));
220 				gc.setFunction(gcFunction);
221 
222 				drawPrimitive(gc, d, cast(int)event.x, cast(int)event.y); //event.getX(), event.getY());
223 
224 				gc.setForeground(black);
225 				gc.setFunction(GdkFunction.COPY);
226 			}
227 			return false;
228 		}
229 
230 		public bool onButtonRelease(GdkEventButton* event, Widget widget)
231 		{
232 			debug(trace) version(Tango) Stdout("button UP").newline;
233 			else writefln("button UP");
234 			if ( event.button == 1 )
235 			{
236 				debug(trace) version(Tango) Stdout("button 1 UP").newline;
237 				else writefln("Button 1 UP");
238 				buttonIsDown = false;
239 			}
240 			return false;
241 		}
242 
243 
244 		public bool mapCallback(Widget d, Event event)
245 		{
246 			//printf("MAP CALLBACK\n");
247 			return false;
248 		}
249 
250 		/**
251 		 * This will be called from the expose event call back.
252 		 * \bug this is called on get or loose focus - review
253 		 */
254 		public bool exposeCallback(GdkEventExpose* event, Widget widget)
255 		{
256 			//printf("testWindow.exposed ----------------------------- \n");
257 			drawPoints(getWindow());
258 			return true;
259 		}
260 		public bool noExposeCallback(Widget widget)
261 		{
262 			//printf("testWindow.noExposed ----------------------------- \n");
263 			return true;
264 		}
265 
266 
267 		public bool onMotionNotify(GdkEventMotion* event, Widget widget)
268 		{
269 			//printf("testWindow.mouseMoved ----------------------------- \n");
270 			if ( buttonIsDown )
271 			{
272 				Drawable d = getWindow();
273 				GC gc = new GC(d);
274 				gc.setForeground(paintColor);
275 				gc.setFunction(gcFunction);
276 
277 				drawPrimitive(gc, d, cast(int)event.x, cast(int)event.y);
278 				//d.drawPoint(event.getX(),event.getY());
279 				//d.drawArc(true,event.getX()-2,event.getY()-2,4,4,0,64*360);
280 				//d.drawRectangle(true,event.getX()-2,event.getY()-2,4,4);
281 
282 				gc.setForeground(black);
283 				gc.setFunction(GdkFunction.COPY);
284 			}
285 			return true;
286 		}
287 
288 		public bool mouseButtonReleaseCallback(Widget widget, GdkEventButton event)
289 		{
290 			//printf("testWindow.buttonReleased ----------------------------- \n");
291 			return true;
292 		}
293 
294 		static int backSpinCount = 0;
295 
296 		public void backSpinChanged(SpinButton spinButton)
297 		{
298 
299 			debug(trace) version(Tango) Stdout.format("backSpinChanged - entry {}", ++backSpinCount).newline;
300 			else writefln("backSpinChanged - entry %s", ++backSpinCount);
301 			drawPoints(getWindow());
302 			GC gc = new GC(getWindow());
303 			debug(trace) version(Tango) Stdout("backSpinChanged - exit").newline;
304 			else writefln("backSpinChanged - exit");
305 		}
306 
307 		public void sizeSpinChanged(SpinButton spinButton)
308 		{
309 			if ( !(scaledPixbuf is null))
310 			{
311 				int width = spinButton.getValueAsInt();
312 				scaledPixbuf = image.getPixbuf();
313 
314 				float ww = width * scaledPixbuf.getWidth() / 30;
315 				float hh = width * scaledPixbuf.getHeight() / 30;
316 
317 				scaledPixbuf = scaledPixbuf.scaleSimple(cast(int)ww, cast(int)hh, GdkInterpType.HYPER);
318 			}
319 		}
320 
321 
322 
323 		public void drawPrimitive(GC gc, Drawable d, int x, int y)
324 		{
325 			int width = spin.getValueAsInt();
326 			int height = width * 3 / 4;
327 			debug(trace) version(Tango) Stdout.format("primitiveType = {}", primitiveType).newline;
328 			else writefln("primitiveType = %s", primitiveType);
329 			switch ( primitiveType )
330 			{
331 				case "Arc":
332 					d.drawArc(gc, false,x-width/2,y-width/2,width,width,0,64*360);
333 					break;
334 
335 				case "Filled Arc":
336 					d.drawArc(gc, true,x-width/4,y-width/4,width/2,width/2,0,64*360);
337 					break;
338 
339 				case "Line":
340 					d.drawLine(gc, x, y, x+width, y);
341 					break;
342 
343 				case "Point":
344 					d.drawPoint(gc, x, y);
345 					break;
346 
347 				case "Rectangle":
348 					d.drawRectangle(gc, false, x-width/2, y-width/4, width, height);
349 					break;
350 
351 				case "Filled Rectangle":
352 					d.drawRectangle(gc, true, x-width/2, y-width/4, width, height);
353 					break;
354 
355 				case "Text":
356 					Font font = new Font("FreeMono 12");
357 					debug(trace) version(Tango) Stdout.format("Text font = {}", font).newline;
358 					else writefln("Text font = %s", font);
359 					d.drawString( font, gc,x, y, "gtkD toolkit");
360 					break;
361 
362 				case "Pango text":
363 					//PgContext pc = getPangoContext();
364 					//PgLayout l = new PgLayout(pc);
365 					PgLayout l = createPangoLayout("GtkD with D");
366 					//l.setText("GtkD with D");
367 					//PgFontDescription fd = l.getFontDescription();
368 					PgFontDescription fd = new PgFontDescription("Sans", width);
369 					l.setFontDescription(fd);
370 
371 					d.drawLayout(gc, x,y,l);
372 					break;
373 
374 				case "Image":
375 					if ( !(scaledPixbuf is null))
376 					{
377 						scaledPixbuf.renderToDrawable( d, gc,
378 						//d.getDrawableStruct(), gc.getGCStruct(),
379 							0, 0, x, y,
380 							scaledPixbuf.getWidth(), scaledPixbuf.getHeight(),
381 							GdkRgbDither.NONE, 0, 0
382 							);
383 					}
384 					break;
385 
386 				case "Polygon":
387 					GdkPoint[] pol1;
388 					pol1.length = polygonStar.length;
389 					for ( int scale = 10 ; scale<= 300; scale+=15)
390 					{
391 						foreach(int i , ref GdkPoint p ; polygonStar)
392 						{
393 							pol1[i].x = p.x*scale/2+x;
394 							pol1[i].y = p.y*scale/2+y;
395 						}
396 						d.drawPolygon(gc, true , pol1);
397 					}
398 					break;
399 
400 				default:
401 					d.drawArc(gc, true,x-2,y-2,4,4,0,64*360);
402 					break;
403 			}
404 		}
405 
406 		private void drawPoints(Drawable d)
407 		{
408 			int square = backSpin.getValueAsInt();
409 			int totalcount = 0;
410 			int count = 0;
411 			Color color = new Color();
412 			GC gc = new GC(d);
413 			int width = this.width;
414 			int height = this.height;
415 			int x = 0;
416 			int y = 0;
417 
418 			debug(trace) version(Tango) Stdout.format("w,h = {} {}",width ,height).newline;
419 			else writefln("w,h = %s %s",width ,height);
420 
421 			debug(trace) version(Tango) Stdout.format("w,h = {} {}",width ,height).newline;
422 			else writefln("w,h = %s %s",width ,height);
423 
424 			float dx = 256.0 / width;
425 			float dy = 256.0 / height ;
426 			float xx;
427 			float yy;
428 			while ( x < width || y <height )
429 			{
430 				xx = x * dx;
431 				yy = y * dy;
432 				color.set8(	cast(ubyte)xx,
433 							cast(ubyte)yy,
434 							cast(ubyte)(sqrt((xx*xx)+(yy*yy))));
435 				gc.setForeground(color);
436 				if ( square > 1 )
437 				{
438 					//d.drawPoint(gc, x,y);
439 					d.drawRectangle(gc, true,x,y,square,square);
440 					//d.foo();
441 					//d.drawArc(gc, 1, 10, 10, 100, 100, 45, 60);
442 				}
443 				else
444 				{
445 					d.drawPoint(gc, x,y);
446 				}
447 				x +=square;
448 				if  ( x > width)
449 				{
450 					if ( y>height)
451 					{
452 						//y=0;
453 					}
454 					else
455 					{
456 						x = 0;
457 						y+=square;
458 					}
459 				}
460 				++totalcount;
461 			}
462 			gc.setForeground(black);
463 			delete color;
464 		}
465 
466 		void onCGOptionsChanged(ComboBox comboBox)
467 		{
468 			debug(trace) version(Tango) Stdout.format("gcOptions = {}", comboBox.getActiveText()).newline;
469 			else writefln("gcOptions = %s", comboBox.getActiveText());
470 			switch ( comboBox.getActiveText() )
471 			{
472 				case "GC COPY": gcFunction = GdkFunction.COPY; break;
473 				case "GC INVERT": gcFunction = GdkFunction.INVERT; break;
474 				case "GC XOR": gcFunction = GdkFunction.XOR; break;
475 				case "GC CLEAR": gcFunction = GdkFunction.CLEAR; break;
476 				case "GC AND": gcFunction = GdkFunction.AND; break;
477 				case "GC AND_REVERSE": gcFunction = GdkFunction.AND_REVERSE; break;
478 				case "GC AND_INVERT": gcFunction = GdkFunction.AND_INVERT; break;
479 				case "GC NOOP": gcFunction = GdkFunction.NOOP; break;
480 				case "GC OR": gcFunction = GdkFunction.OR; break;
481 				case "GC EQUIV": gcFunction = GdkFunction.EQUIV; break;
482 				case "GC OR_REVERSE": gcFunction = GdkFunction.OR_REVERSE; break;
483 				case "GC COPY_INVERT": gcFunction = GdkFunction.COPY_INVERT; break;
484 				case "GC OR_INVERT": gcFunction = GdkFunction.OR_INVERT; break;
485 				case "GC NAND": gcFunction = GdkFunction.NAND; break;
486 				case "GC NOR": gcFunction = GdkFunction.NOR; break;
487 				case "GC SET": gcFunction = GdkFunction.SET; break;
488 				default: gcFunction = GdkFunction.INVERT; break;
489 			}
490 		}
491 
492 		void onPrimOptionChanged(ComboBox comboBox)
493 		{
494 			primitiveType = comboBox.getActiveText();
495 		}
496 
497 //
498 //		void primitive(MenuItem item)
499 //		{
500 //			printf("TestDrawing.primitive item command = %.*s\n", item.getActionCommand().toString());
501 //			primitiveType.set(item.getActionCommand().dup);
502 //		}
503 
504 	}
505 
506 }
507