1 module ggplotd.gtk;
2 
3 version(ggplotdGTK):
4 
5 import cairod = cairo.cairo;
6 import gtk.DrawingArea : DrawingArea;
7 
8 class SurfaceArea : DrawingArea
9 {
10     import gtkc = cairo.Context;
11     import gtk.Widget : GtkAllocation, Widget;
12     import glib.Timeout : Scoped, Timeout;
13 
14     public:
15     this()
16 	{
17         surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, width, height);
18 		//Attach our expose callback, which will draw the window.
19 		addOnDraw(&drawCallback);
20 	}
21 
22 protected:
23 	//Override default signal handler:
24 	bool drawCallback(Scoped!(gtkc.Context) cr, Widget widget)
25 	{
26         import gtkp = cairo.Pattern;
27 
28 		if ( m_timeout is null )
29 		{
30 			// Create a new timeout that will ask the window to be drawn 10 times 
31             // every second.
32 			m_timeout = new Timeout( 100, &onElapsed, false );
33 		}
34 
35 		// This is where we draw on the window
36 		GtkAllocation size;
37 
38 		getAllocation(size);
39 
40         /*
41            Surface to pattern. Scale it appropiatly and then use setSource.
42            */
43         cairod.SurfacePattern pattern = new cairod.SurfacePattern( surface );
44         cairod.Matrix matrix;
45         import std.conv : to;
46         matrix.initScale( width.to!double/size.width, height.to!double/size.height );
47         pattern.setMatrix( matrix );
48 
49         /*
50            gtk-d cairo interface and cairod both wrap the same C cairo_pattern_t
51            so this cast should be save.
52         */
53         gtkp.Pattern gtkPattern = new gtkp.Pattern(cast (gtkp.cairo_pattern_t*) pattern.nativePointer);
54 
55         cr.setSource(gtkPattern);
56         cr.paint();
57 		return true;
58 	}
59 
60 	bool onElapsed()
61 	{
62 		//force our program to redraw the entire clock once per every second.
63 		GtkAllocation area;
64 		getAllocation(area);
65 
66 		queueDrawArea(area.x, area.y, area.width, area.height);
67 		
68 		return true;
69 	}
70 
71 	Timeout m_timeout;
72 
73     cairod.Surface surface;
74 
75     int width = 470;
76     int height = 470;
77 }
78 
79 import core.thread;
80 /**
81 * Helper class to open a GTK window and draw to it
82 *
83 * Examples:
84 * --------------------
85 * // Code to create the aes here
86 * // ...
87 *
88 * // Start gtk window.
89 * auto gd = new GTKWindow();
90 * auto tid = new Thread(() { gd.run("plotcli"); }).start();
91 * auto gg = GGPlotD().put( geomHist3D( aes ) );
92 * gd.drawGG( gg, 470, 470 );
93 * Thread.sleep( dur!("seconds")( 2 ) ); // sleep for 5 seconds
94 * 
95 * gg = GGPlotD().put( geomPoint( aes ) );
96 * gd.clearWindow();
97 * gd.draw( gg, 470, 470 );
98 * 
99 * // Wait for gtk thread to finish (Window closed)
100 * tid.join();
101 * --------------------
102 */
103 class GTKWindow
104 {
105     import gtk.MainWindow : MainWindow;
106     import gtk.Main : Main;
107     import ggplotd.ggplotd : GGPlotD;
108 
109     this() {
110         string[] args;
111         Main.init(args);
112         sa = new SurfaceArea();
113         sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, 470, 470);
114     }
115 
116     ///
117     void draw(T)( T gg, int width, int height )
118     {
119         // Testing seems to indicate this doesn't need a mutex?
120         sa.width = width;
121         sa.height = height;
122         // Writing to cairo surfaces should be safe. Displayer only reads from it.
123         sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, width, height);
124         gg.drawToSurface( sa.surface, width, height );
125     }
126  
127     ///
128     void clearWindow()
129     {
130         cairod.RGBA colour = cairod.RGBA(1,1,1,1);
131         auto backcontext = cairod.Context(sa.surface);
132         backcontext.setSourceRGBA(colour);
133         backcontext.paint;
134     }
135 
136     /**
137     * Open the window and run the mainloop. This is blocking, due to the mainloop.
138     *
139     * It should be safe to run threaded though
140     */
141     void run(string title)
142     {
143         MainWindow win = new MainWindow(title);
144 
145         win.setDefaultSize( 250, 250 );
146 
147         //c.surface = surface;
148         win.add(sa);
149         sa.show();
150         win.showAll();
151 
152         Main.run();
153     }
154 
155     SurfaceArea sa;
156 }