Posted By

FArcellier on 08/06/11


Tagged

Drawing area GTK+ Cairo


Versions (?)

GTK+ Code Demos : Drawing Area (+ Main)


 / Published in: C
 

Get from the gtk-demo Application. Drawing Area demo

It's stand alone program you can modify and compil. It was missing the main in source to be able to use it.

  1. #include <string.h>
  2. #include <gtk/gtk.h>
  3.  
  4. static GtkWidget *window = NULL;
  5.  
  6. /*!
  7.  * \brief Pixmap to scribble area, to store our scribbles
  8.  */
  9. static cairo_surface_t *surface = NULL;
  10.  
  11. /*!
  12.  * \brief Create a new surface of the appropriate size to store our scribbles
  13.  */
  14. static gboolean scribble_configure_event (GtkWidget*, GdkEventConfigure*, gpointer);
  15.  
  16. /*!
  17.  * \brief Redraw the screen from the surface
  18.  */
  19. static gboolean scribble_expose_event (GtkWidget*, GdkEventExpose*, gpointer);
  20.  
  21. /*!
  22.  * \brief Draw a rectangle on the screen
  23.  */
  24. static void draw_brush (GtkWidget *widget, gdouble x, gdouble y);
  25.  
  26. static gboolean scribble_button_press_event (GtkWidget*, GdkEventButton*, gpointer);
  27. static gboolean scribble_motion_notify_event (GtkWidget*, GdkEventMotion*, gpointer);
  28. static gboolean checkerboard_expose (GtkWidget*, GdkEventExpose*, gpointer);
  29. static void close_window (void);
  30. GtkWidget * do_drawingarea ();
  31.  
  32. /*!
  33.  * \brief Create a new surface of the appropriate size to store our scribbles
  34.  */
  35. static gboolean scribble_configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer data)
  36. {
  37. cairo_t *cr = NULL;
  38.  
  39. if (surface)
  40. {
  41. cairo_surface_destroy (surface);
  42. }
  43.  
  44. surface = gdk_window_create_similar_surface (widget -> window,
  45. CAIRO_CONTENT_COLOR,
  46. widget -> allocation.width,
  47. widget -> allocation.height);
  48.  
  49. /* Initialize the surface to white */
  50. cr = cairo_create (surface);
  51. cairo_set_source_rgb (cr, 1, 1, 1);
  52. cairo_paint (cr);
  53. cairo_destroy (cr);
  54.  
  55. /* We've handled the configure event, no need for further processing. */
  56. return TRUE;
  57. }
  58.  
  59. /*!
  60.  * \brief Redraw the screen from the surface
  61.  */
  62. static gboolean scribble_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
  63. {
  64. cairo_t *cr = NULL;
  65.  
  66. cr = gdk_cairo_create (widget->window);
  67. cairo_set_source_surface (cr, surface, 0, 0);
  68. gdk_cairo_rectangle (cr, &event->area);
  69. cairo_fill (cr);
  70.  
  71. cairo_destroy (cr);
  72.  
  73. return FALSE;
  74. }
  75.  
  76. /*!
  77.  * \brief Draw a rectangle on the screen
  78.  */
  79. static void draw_brush (GtkWidget *widget, gdouble x, gdouble y)
  80. {
  81. GdkRectangle update_rect;
  82. memset(&update_rect, 0, sizeof(GdkRectangle));
  83.  
  84. cairo_t *cr = NULL;
  85.  
  86. update_rect.x = x - 3;
  87. update_rect.y = y - 3;
  88. update_rect.width = 6;
  89. update_rect.height = 6;
  90.  
  91. /* Paint to the surface, where we store our state */
  92. cr = cairo_create (surface);
  93. gdk_cairo_rectangle (cr, &update_rect);
  94. cairo_fill (cr);
  95. cairo_destroy (cr);
  96.  
  97. /* Now invalidate the affected region of the drawing area. */
  98. gdk_window_invalidate_rect (widget->window,
  99. &update_rect,
  100. FALSE);
  101. }
  102.  
  103. static gboolean scribble_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
  104. {
  105. if (surface == NULL)
  106. {
  107. return FALSE; /* Paranoia check, in case we haven't gotten a configure event */
  108. }
  109.  
  110. if (event->button == 1)
  111. {
  112. draw_brush (widget, event->x, event->y);
  113. }
  114.  
  115. /* We've handled the event, stop processing */
  116. return TRUE;
  117. }
  118.  
  119. static gboolean scribble_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, gpointer data)
  120. {
  121. int x = 0, y = 0;
  122. GdkModifierType state = 0;
  123.  
  124. if (surface == NULL)
  125. {
  126. return FALSE; /* paranoia check, in case we haven't gotten a configure event */
  127. }
  128.  
  129. /* This call is very important; it requests the next motion event.
  130.   * If you don't call gdk_window_get_pointer(), you'll only get a single
  131.   * motion event.The reason is that we specified GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events()
  132.   * If we hadn't specified that, we could juste use event->x, event->y as the pointer location.
  133.   * But we'd also get deluged in events.
  134.   * By requesting the next event as we handle the current one, we avoid getting a huge number of
  135.   * events faster than we can cope.
  136.   */
  137. gdk_window_get_pointer (event->window, &x, &y, &state);
  138.  
  139. if (state & GDK_BUTTON1_MASK)
  140. {
  141. draw_brush (widget, x, y);
  142. }
  143.  
  144. /* We've handled it, stop processing */
  145. return TRUE;
  146. }
  147.  
  148. static gboolean checkerboard_expose (GtkWidget *da, GdkEventExpose *event, gpointer data)
  149. {
  150. gint i = 0, j = 0, xcount = 0, ycount = 0;
  151. cairo_t *cr = NULL;
  152.  
  153. #define CHECK_SIZE 10
  154. #define SPACING 2
  155.  
  156. /* At the start of an expose handler, a clip region of event->area is set on the window, and event
  157.   * ->area has been cleared to the widget's background. The docs for gdk_window_begin_paint_region() give
  158.   * more details on how this works.
  159.   */
  160.  
  161. cr = gdk_cairo_create (da -> window);
  162. gdk_cairo_rectangle (cr, &event->area);
  163. cairo_clip(cr);
  164.  
  165. xcount = 0;
  166. i = SPACING;
  167. while (i < da->allocation.width)
  168. {
  169. j = SPACING;
  170. ycount = xcount %2; /* start with even /odd depending on row */
  171. while (j < da->allocation.height)
  172. {
  173. if (ycount % 2)
  174. {
  175. cairo_set_source_rgb (cr, 0.45777, 0, 0.45777);
  176. }
  177. else
  178. {
  179. cairo_set_source_rgb (cr, 1, 1, 1);
  180. }
  181.  
  182. /* If we're outside event->area, this will do nothing.
  183.   * It might be mildly more efficient if we handled
  184.   * the clipping ourselves, but again we're feeling lazy
  185.   */
  186. cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
  187. cairo_fill(cr);
  188.  
  189. j += CHECK_SIZE + SPACING;
  190. ++ycount;
  191. }
  192.  
  193. i += CHECK_SIZE + SPACING;
  194. ++xcount;
  195. }
  196.  
  197. cairo_destroy (cr);
  198.  
  199. /* return TRUE because we've handled this event, so no further
  200.   * processing is required.
  201.   */
  202. return TRUE;
  203. }
  204.  
  205. static void close_window (void)
  206. {
  207. window = NULL;
  208.  
  209. if (surface)
  210. {
  211. g_object_unref (surface);
  212. }
  213.  
  214. surface = NULL;
  215. gtk_main_quit();
  216. }
  217.  
  218. GtkWidget * do_drawingarea ()
  219. {
  220. GtkWidget *frame = NULL, *vbox = NULL, *da = NULL, *label = NULL;
  221.  
  222. if (!window)
  223. {
  224. window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  225. //gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget));
  226. gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
  227.  
  228. g_signal_connect (G_OBJECT(window), "destroy", G_CALLBACK (close_window), NULL);
  229.  
  230. gtk_container_set_border_width (GTK_CONTAINER (window), 8);
  231.  
  232. vbox = gtk_vbox_new (FALSE, 8);
  233. gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
  234. gtk_container_add (GTK_CONTAINER (window), vbox);
  235.  
  236. /*
  237.   * Create the checkerboard area
  238.   */
  239. label = gtk_label_new (NULL);
  240. gtk_label_set_markup (GTK_LABEL (label), "<u>Checkerboard pattern</u>");
  241.  
  242. gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  243.  
  244. frame = gtk_frame_new (NULL);
  245. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  246. gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  247.  
  248. da = gtk_drawing_area_new();
  249. /* Set a minimum size */
  250. gtk_widget_set_size_request (da, 100, 100);
  251.  
  252. gtk_container_add (GTK_CONTAINER (frame), da);
  253.  
  254. g_signal_connect (da, "expose-event", G_CALLBACK (checkerboard_expose), NULL);
  255.  
  256. /*
  257.   * Create the scribble area
  258.   */
  259. label = gtk_label_new (NULL);
  260. gtk_label_set_markup (GTK_LABEL (label), "<u>Scribble area</u>");
  261. gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  262.  
  263. frame = gtk_frame_new (NULL);
  264. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  265. gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  266.  
  267. da = gtk_drawing_area_new();
  268.  
  269. /* Set a minimum size */
  270. gtk_widget_set_size_request (da, 100, 100);
  271.  
  272. gtk_container_add (GTK_CONTAINER (frame), da);
  273.  
  274. /* Signals used to handle backing surface */
  275. g_signal_connect (da, "expose_event", G_CALLBACK (scribble_expose_event), NULL);
  276. g_signal_connect (da, "configure_event", G_CALLBACK (scribble_configure_event), NULL);
  277.  
  278. /* Event signals */
  279. g_signal_connect (da, "motion-notify-event", G_CALLBACK (scribble_motion_notify_event), NULL);
  280. g_signal_connect (da, "button-press-event", G_CALLBACK (scribble_button_press_event), NULL);
  281.  
  282. /* Ask to receive events the drawing area doesn't normally
  283.   * subscribe to
  284.   */
  285. gtk_widget_set_events (da, gtk_widget_get_events (da)
  286. | GDK_LEAVE_NOTIFY_MASK
  287. | GDK_BUTTON_PRESS_MASK
  288. | GDK_POINTER_MOTION_MASK
  289. | GDK_POINTER_MOTION_HINT_MASK);
  290.  
  291. }
  292.  
  293. if (!gtk_widget_get_visible (window))
  294. {
  295. gtk_widget_show_all (window);
  296. }
  297. else
  298. {
  299. gtk_widget_destroy (window);
  300. }
  301.  
  302. return window;
  303. }
  304.  
  305. int main(int argc, char *argv[])
  306. {
  307. gtk_init (&argc, &argv);
  308.  
  309. do_drawingarea();
  310.  
  311. gtk_main();
  312. return 0;
  313. }

Report this snippet  

You need to login to post a comment.