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 2 times 
31             // every second.
32 			m_timeout = new Timeout( 500, &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     __gshared cairod.Surface surface;
74 
75     __gshared int width = 470;
76     __gshared 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 * const width = 470;
90 * const height = 470;
91 * auto gd = new GTKWindow();
92 * auto tid = new Thread(() { gd.run("plotcli", width, height); }).start();
93 * auto gg = GGPlotD().put( geomHist2D( aes ) );
94 * gd.draw( gg, width, height );
95 * Thread.sleep( dur!("seconds")( 2 ) ); // sleep for 5 seconds
96 * 
97 * gg = GGPlotD().put( geomPoint( aes ) );
98 * gd.clearWindow();
99 * gd.draw( gg, width, height );
100 * 
101 * // Wait for gtk thread to finish (Window closed)
102 * tid.join();
103 * --------------------
104 */
105 class GTKWindow
106 {
107     import gtk.MainWindow : MainWindow;
108     import gtk.Main : Main;
109     import ggplotd.ggplotd : GGPlotD;
110 
111     this() {
112         string[] args;
113         Main.init(args);
114         sa = new SurfaceArea();
115         sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, 470, 470);
116     }
117 
118     ///
119     void draw(T)( T gg, int width, int height )
120     {
121         // Testing seems to indicate this doesn't need a mutex?
122         sa.width = width;
123         sa.height = height;
124         // Writing to cairo surfaces should be safe. Displayer only reads from it.
125         sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, width, height);
126         gg.drawToSurface( sa.surface, width, height );
127     }
128  
129     ///
130     void clearWindow()
131     {
132         cairod.RGBA colour = cairod.RGBA(1,1,1,1);
133         auto backcontext = cairod.Context(sa.surface);
134         backcontext.setSourceRGBA(colour);
135         backcontext.paint;
136     }
137 
138     /**
139     * Open the window and run the mainloop. This is blocking, due to the mainloop.
140     *
141     * It should be safe to run threaded though
142     */
143     void run(string title, int width = 470, int height = 470)
144     {
145         MainWindow win = new MainWindow(title);
146 
147         win.setDefaultSize( width, height );
148 
149         win.add(sa);
150         sa.show();
151         win.showAll();
152 
153         Main.run();
154     }
155 
156     __gshared SurfaceArea sa;
157 }