| |
1 /* |
| |
2 * purple |
| |
3 * |
| |
4 * Purple is the legal property of its developers, whose names are too numerous |
| |
5 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
6 * source distribution. |
| |
7 * |
| |
8 * This program is free software; you can redistribute it and/or modify |
| |
9 * it under the terms of the GNU General Public License as published by |
| |
10 * the Free Software Foundation; either version 2 of the License, or |
| |
11 * (at your option) any later version. |
| |
12 * |
| |
13 * This program is distributed in the hope that it will be useful, |
| |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
16 * GNU General Public License for more details. |
| |
17 * |
| |
18 * You should have received a copy of the GNU General Public License |
| |
19 * along with this program; if not, write to the Free Software |
| |
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
| |
21 * |
| |
22 */ |
| |
23 |
| |
24 #include "internal.h" |
| |
25 #include "purplewhiteboard.h" |
| |
26 |
| |
27 #include "purpleprotocolfactory.h" |
| |
28 #include "purplewhiteboarduiops.h" |
| |
29 #include "protocol.h" |
| |
30 |
| |
31 typedef struct { |
| |
32 int state; |
| |
33 |
| |
34 PurpleAccount *account; |
| |
35 char *who; |
| |
36 |
| |
37 /* TODO Remove this and use protocol-specific subclasses. */ |
| |
38 void *proto_data; |
| |
39 |
| |
40 PurpleWhiteboardOps *protocol_ops; |
| |
41 |
| |
42 GList *draw_list; |
| |
43 } PurpleWhiteboardPrivate; |
| |
44 |
| |
45 /* GObject Property enums */ |
| |
46 enum { |
| |
47 PROP_0, |
| |
48 PROP_STATE, |
| |
49 PROP_ACCOUNT, |
| |
50 PROP_WHO, |
| |
51 PROP_DRAW_LIST, |
| |
52 N_PROPERTIES, |
| |
53 }; |
| |
54 static GParamSpec *properties[N_PROPERTIES] = { NULL, }; |
| |
55 |
| |
56 G_DEFINE_TYPE_WITH_PRIVATE(PurpleWhiteboard, purple_whiteboard, G_TYPE_OBJECT) |
| |
57 |
| |
58 /****************************************************************************** |
| |
59 * Globals |
| |
60 *****************************************************************************/ |
| |
61 static GList *wb_list = NULL; |
| |
62 |
| |
63 /****************************************************************************** |
| |
64 * Helpers |
| |
65 *****************************************************************************/ |
| |
66 static void |
| |
67 purple_whiteboard_set_account(PurpleWhiteboard *whiteboard, |
| |
68 PurpleAccount *account) |
| |
69 { |
| |
70 PurpleWhiteboardPrivate *priv = NULL; |
| |
71 |
| |
72 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
73 |
| |
74 if(g_set_object(&priv->account, account)) { |
| |
75 g_object_notify_by_pspec(G_OBJECT(whiteboard), |
| |
76 properties[PROP_ACCOUNT]); |
| |
77 } |
| |
78 } |
| |
79 |
| |
80 static void |
| |
81 purple_whiteboard_set_who(PurpleWhiteboard *whiteboard, const gchar *who) { |
| |
82 PurpleWhiteboardPrivate *priv = NULL; |
| |
83 |
| |
84 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
85 |
| |
86 g_clear_pointer(&priv->who, g_free); |
| |
87 priv->who = g_strdup(who); |
| |
88 |
| |
89 g_object_notify_by_pspec(G_OBJECT(whiteboard), properties[PROP_WHO]); |
| |
90 } |
| |
91 |
| |
92 /****************************************************************************** |
| |
93 * GObject Implementation |
| |
94 *****************************************************************************/ |
| |
95 static void |
| |
96 purple_whiteboard_set_property(GObject *obj, guint param_id, |
| |
97 const GValue *value, GParamSpec *pspec) |
| |
98 { |
| |
99 PurpleWhiteboard *whiteboard = PURPLE_WHITEBOARD(obj); |
| |
100 |
| |
101 switch(param_id) { |
| |
102 case PROP_STATE: |
| |
103 purple_whiteboard_set_state(whiteboard, g_value_get_int(value)); |
| |
104 break; |
| |
105 case PROP_ACCOUNT: |
| |
106 purple_whiteboard_set_account(whiteboard, |
| |
107 g_value_get_object(value)); |
| |
108 break; |
| |
109 case PROP_WHO: |
| |
110 purple_whiteboard_set_who(whiteboard, g_value_get_string(value)); |
| |
111 break; |
| |
112 case PROP_DRAW_LIST: |
| |
113 purple_whiteboard_set_draw_list(whiteboard, |
| |
114 g_value_get_pointer(value)); |
| |
115 break; |
| |
116 default: |
| |
117 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); |
| |
118 break; |
| |
119 } |
| |
120 } |
| |
121 |
| |
122 static void |
| |
123 purple_whiteboard_get_property(GObject *obj, guint param_id, GValue *value, |
| |
124 GParamSpec *pspec) |
| |
125 { |
| |
126 PurpleWhiteboard *whiteboard = PURPLE_WHITEBOARD(obj); |
| |
127 |
| |
128 switch (param_id) { |
| |
129 case PROP_STATE: |
| |
130 g_value_set_int(value, purple_whiteboard_get_state(whiteboard)); |
| |
131 break; |
| |
132 case PROP_ACCOUNT: |
| |
133 g_value_set_object(value, |
| |
134 purple_whiteboard_get_account(whiteboard)); |
| |
135 break; |
| |
136 case PROP_WHO: |
| |
137 g_value_set_string(value, |
| |
138 purple_whiteboard_get_who(whiteboard)); |
| |
139 break; |
| |
140 case PROP_DRAW_LIST: |
| |
141 g_value_set_pointer(value, |
| |
142 purple_whiteboard_get_draw_list(whiteboard)); |
| |
143 break; |
| |
144 default: |
| |
145 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); |
| |
146 break; |
| |
147 } |
| |
148 } |
| |
149 |
| |
150 static void |
| |
151 purple_whiteboard_init(PurpleWhiteboard *whiteboard) { |
| |
152 } |
| |
153 |
| |
154 static void |
| |
155 purple_whiteboard_constructed(GObject *object) { |
| |
156 PurpleWhiteboard *whiteboard = PURPLE_WHITEBOARD(object); |
| |
157 PurpleWhiteboardPrivate *priv = NULL; |
| |
158 PurpleProtocol *protocol = NULL; |
| |
159 |
| |
160 G_OBJECT_CLASS(purple_whiteboard_parent_class)->constructed(object); |
| |
161 |
| |
162 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
163 |
| |
164 protocol = purple_connection_get_protocol( |
| |
165 purple_account_get_connection(priv->account)); |
| |
166 purple_whiteboard_set_protocol_ops(whiteboard, |
| |
167 purple_protocol_get_whiteboard_ops(protocol)); |
| |
168 |
| |
169 /* Start up protocol specifics */ |
| |
170 if(priv->protocol_ops != NULL && priv->protocol_ops->start != NULL) { |
| |
171 priv->protocol_ops->start(whiteboard); |
| |
172 } |
| |
173 |
| |
174 wb_list = g_list_append(wb_list, whiteboard); |
| |
175 } |
| |
176 |
| |
177 static void |
| |
178 purple_whiteboard_finalize(GObject *object) { |
| |
179 PurpleWhiteboard *whiteboard = PURPLE_WHITEBOARD(object); |
| |
180 PurpleWhiteboardPrivate *priv = NULL; |
| |
181 |
| |
182 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
183 |
| |
184 if(whiteboard->ui_data) { |
| |
185 purple_whiteboard_ui_ops_destroy(whiteboard); |
| |
186 } |
| |
187 |
| |
188 /* Do protocol specific session ending procedures */ |
| |
189 if(priv->protocol_ops != NULL && priv->protocol_ops->end != NULL) { |
| |
190 priv->protocol_ops->end(whiteboard); |
| |
191 } |
| |
192 |
| |
193 wb_list = g_list_remove(wb_list, whiteboard); |
| |
194 |
| |
195 g_clear_object(&priv->account); |
| |
196 g_clear_pointer(&priv->who, g_free); |
| |
197 |
| |
198 /* TODO: figure out how we need to clean up the drawlist */ |
| |
199 |
| |
200 G_OBJECT_CLASS(purple_whiteboard_parent_class)->finalize(object); |
| |
201 } |
| |
202 |
| |
203 static void |
| |
204 purple_whiteboard_class_init(PurpleWhiteboardClass *klass) { |
| |
205 GObjectClass *obj_class = G_OBJECT_CLASS(klass); |
| |
206 |
| |
207 obj_class->get_property = purple_whiteboard_get_property; |
| |
208 obj_class->set_property = purple_whiteboard_set_property; |
| |
209 obj_class->finalize = purple_whiteboard_finalize; |
| |
210 obj_class->constructed = purple_whiteboard_constructed; |
| |
211 |
| |
212 properties[PROP_STATE] = g_param_spec_int( |
| |
213 "state", "State", |
| |
214 "State of the whiteboard.", |
| |
215 G_MININT, G_MAXINT, 0, |
| |
216 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); |
| |
217 |
| |
218 properties[PROP_ACCOUNT] = g_param_spec_object( |
| |
219 "account", "Account", |
| |
220 "The whiteboard's account.", PURPLE_TYPE_ACCOUNT, |
| |
221 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
| |
222 |
| |
223 properties[PROP_WHO] = g_param_spec_string( |
| |
224 "who", "Who", |
| |
225 "Who you're drawing with.", NULL, |
| |
226 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
| |
227 |
| |
228 properties[PROP_DRAW_LIST] = g_param_spec_pointer( |
| |
229 "draw-list", "Draw list", |
| |
230 "A list of points to draw to the buddy.", |
| |
231 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); |
| |
232 |
| |
233 g_object_class_install_properties(obj_class, N_PROPERTIES, properties); |
| |
234 } |
| |
235 |
| |
236 /****************************************************************************** |
| |
237 * API |
| |
238 *****************************************************************************/ |
| |
239 void |
| |
240 purple_whiteboard_set_protocol_ops(PurpleWhiteboard *whiteboard, |
| |
241 PurpleWhiteboardOps *ops) |
| |
242 { |
| |
243 PurpleWhiteboardPrivate *priv = NULL; |
| |
244 |
| |
245 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
246 |
| |
247 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
248 |
| |
249 priv->protocol_ops = ops; |
| |
250 } |
| |
251 |
| |
252 PurpleAccount * |
| |
253 purple_whiteboard_get_account(PurpleWhiteboard *whiteboard) { |
| |
254 PurpleWhiteboardPrivate *priv = NULL; |
| |
255 |
| |
256 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), NULL); |
| |
257 |
| |
258 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
259 |
| |
260 return priv->account; |
| |
261 } |
| |
262 |
| |
263 const gchar * |
| |
264 purple_whiteboard_get_who(PurpleWhiteboard *whiteboard) { |
| |
265 PurpleWhiteboardPrivate *priv = NULL; |
| |
266 |
| |
267 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), NULL); |
| |
268 |
| |
269 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
270 |
| |
271 return priv->who; |
| |
272 } |
| |
273 |
| |
274 void |
| |
275 purple_whiteboard_set_state(PurpleWhiteboard *whiteboard, int state) { |
| |
276 PurpleWhiteboardPrivate *priv = NULL; |
| |
277 |
| |
278 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
279 |
| |
280 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
281 |
| |
282 priv->state = state; |
| |
283 |
| |
284 g_object_notify_by_pspec(G_OBJECT(whiteboard), properties[PROP_STATE]); |
| |
285 } |
| |
286 |
| |
287 gint |
| |
288 purple_whiteboard_get_state(PurpleWhiteboard *whiteboard) { |
| |
289 PurpleWhiteboardPrivate *priv = NULL; |
| |
290 |
| |
291 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), -1); |
| |
292 |
| |
293 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
294 |
| |
295 return priv->state; |
| |
296 } |
| |
297 |
| |
298 void |
| |
299 purple_whiteboard_start(PurpleWhiteboard *whiteboard) { |
| |
300 purple_whiteboard_ui_ops_create(whiteboard); |
| |
301 } |
| |
302 |
| |
303 PurpleWhiteboard * |
| |
304 purple_whiteboard_get_session(PurpleAccount *account, const gchar *who) { |
| |
305 PurpleWhiteboard *whiteboard = NULL; |
| |
306 PurpleWhiteboardPrivate *priv = NULL; |
| |
307 GList *l = NULL; |
| |
308 |
| |
309 for(l = wb_list; l != NULL; l = l->next) { |
| |
310 whiteboard = PURPLE_WHITEBOARD(l->data); |
| |
311 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
312 |
| |
313 if(priv->account == account && purple_strequal(priv->who, who)) { |
| |
314 return whiteboard; |
| |
315 } |
| |
316 } |
| |
317 |
| |
318 return NULL; |
| |
319 } |
| |
320 |
| |
321 void |
| |
322 purple_whiteboard_draw_list_destroy(GList *draw_list) { |
| |
323 g_list_free(draw_list); |
| |
324 } |
| |
325 |
| |
326 gboolean |
| |
327 purple_whiteboard_get_dimensions(PurpleWhiteboard *whiteboard, gint *width, |
| |
328 gint *height) |
| |
329 { |
| |
330 PurpleWhiteboardPrivate *priv = NULL; |
| |
331 PurpleWhiteboardOps *protocol_ops = NULL; |
| |
332 |
| |
333 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), FALSE); |
| |
334 |
| |
335 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
336 protocol_ops = priv->protocol_ops; |
| |
337 |
| |
338 if(protocol_ops != NULL && protocol_ops->get_dimensions != NULL) { |
| |
339 protocol_ops->get_dimensions(whiteboard, width, height); |
| |
340 |
| |
341 return TRUE; |
| |
342 } |
| |
343 |
| |
344 return FALSE; |
| |
345 } |
| |
346 |
| |
347 void |
| |
348 purple_whiteboard_set_dimensions(PurpleWhiteboard *whiteboard, gint width, |
| |
349 gint height) |
| |
350 { |
| |
351 purple_whiteboard_ui_ops_set_dimensions(whiteboard, width, height); |
| |
352 } |
| |
353 |
| |
354 void |
| |
355 purple_whiteboard_send_draw_list(PurpleWhiteboard *whiteboard, GList *list) { |
| |
356 PurpleWhiteboardPrivate *priv = NULL; |
| |
357 PurpleWhiteboardOps *protocol_ops = NULL; |
| |
358 |
| |
359 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
360 |
| |
361 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
362 protocol_ops = priv->protocol_ops; |
| |
363 |
| |
364 if(protocol_ops != NULL && protocol_ops->send_draw_list != NULL) { |
| |
365 protocol_ops->send_draw_list(whiteboard, list); |
| |
366 } |
| |
367 } |
| |
368 |
| |
369 void |
| |
370 purple_whiteboard_draw_point(PurpleWhiteboard *whiteboard, gint x, gint y, |
| |
371 gint color, gint size) |
| |
372 { |
| |
373 purple_whiteboard_ui_ops_draw_point(whiteboard, x, y, color, size); |
| |
374 } |
| |
375 |
| |
376 void |
| |
377 purple_whiteboard_draw_line(PurpleWhiteboard *whiteboard, gint x1, gint y1, |
| |
378 gint x2, gint y2, gint color, gint size) |
| |
379 { |
| |
380 purple_whiteboard_ui_ops_draw_line(whiteboard, x1, y1, x2, y2, color, |
| |
381 size); |
| |
382 } |
| |
383 |
| |
384 void |
| |
385 purple_whiteboard_clear(PurpleWhiteboard *whiteboard) { |
| |
386 purple_whiteboard_ui_ops_clear(whiteboard); |
| |
387 } |
| |
388 |
| |
389 void |
| |
390 purple_whiteboard_send_clear(PurpleWhiteboard *whiteboard) { |
| |
391 PurpleWhiteboardPrivate *priv = NULL; |
| |
392 PurpleWhiteboardOps *protocol_ops = NULL; |
| |
393 |
| |
394 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
395 |
| |
396 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
397 protocol_ops = priv->protocol_ops; |
| |
398 |
| |
399 if(protocol_ops != NULL && protocol_ops->clear != NULL) { |
| |
400 protocol_ops->clear(whiteboard); |
| |
401 } |
| |
402 } |
| |
403 |
| |
404 void |
| |
405 purple_whiteboard_send_brush(PurpleWhiteboard *whiteboard, gint size, |
| |
406 gint color) |
| |
407 { |
| |
408 PurpleWhiteboardPrivate *priv = NULL; |
| |
409 PurpleWhiteboardOps *protocol_ops = NULL; |
| |
410 |
| |
411 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
412 |
| |
413 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
414 protocol_ops = priv->protocol_ops; |
| |
415 |
| |
416 if(protocol_ops != NULL && protocol_ops->set_brush != NULL) { |
| |
417 protocol_ops->set_brush(whiteboard, size, color); |
| |
418 } |
| |
419 } |
| |
420 |
| |
421 gboolean |
| |
422 purple_whiteboard_get_brush(PurpleWhiteboard *whiteboard, gint *size, |
| |
423 gint *color) |
| |
424 { |
| |
425 PurpleWhiteboardPrivate *priv = NULL; |
| |
426 PurpleWhiteboardOps *protocol_ops = NULL; |
| |
427 |
| |
428 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), FALSE); |
| |
429 |
| |
430 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
431 protocol_ops = priv->protocol_ops; |
| |
432 |
| |
433 if(protocol_ops != NULL && protocol_ops->get_brush != NULL) { |
| |
434 protocol_ops->get_brush(whiteboard, size, color); |
| |
435 |
| |
436 return TRUE; |
| |
437 } |
| |
438 |
| |
439 return FALSE; |
| |
440 } |
| |
441 |
| |
442 void |
| |
443 purple_whiteboard_set_brush(PurpleWhiteboard *whiteboard, gint size, |
| |
444 gint color) |
| |
445 { |
| |
446 purple_whiteboard_ui_ops_set_brush(whiteboard, size, color); |
| |
447 } |
| |
448 |
| |
449 GList * |
| |
450 purple_whiteboard_get_draw_list(PurpleWhiteboard *whiteboard) { |
| |
451 PurpleWhiteboardPrivate *priv = NULL; |
| |
452 |
| |
453 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), NULL); |
| |
454 |
| |
455 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
456 |
| |
457 return priv->draw_list; |
| |
458 } |
| |
459 |
| |
460 void |
| |
461 purple_whiteboard_set_draw_list(PurpleWhiteboard *whiteboard, |
| |
462 GList* draw_list) |
| |
463 { |
| |
464 PurpleWhiteboardPrivate *priv = NULL; |
| |
465 |
| |
466 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
467 |
| |
468 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
469 priv->draw_list = draw_list; |
| |
470 |
| |
471 g_object_notify_by_pspec(G_OBJECT(whiteboard), properties[PROP_DRAW_LIST]); |
| |
472 } |
| |
473 |
| |
474 void |
| |
475 purple_whiteboard_set_protocol_data(PurpleWhiteboard *whiteboard, |
| |
476 gpointer proto_data) |
| |
477 { |
| |
478 PurpleWhiteboardPrivate *priv = NULL; |
| |
479 |
| |
480 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
481 |
| |
482 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
483 priv->proto_data = proto_data; |
| |
484 } |
| |
485 |
| |
486 gpointer |
| |
487 purple_whiteboard_get_protocol_data(PurpleWhiteboard *whiteboard) { |
| |
488 PurpleWhiteboardPrivate *priv = NULL; |
| |
489 |
| |
490 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), NULL); |
| |
491 |
| |
492 priv = purple_whiteboard_get_instance_private(whiteboard); |
| |
493 |
| |
494 return priv->proto_data; |
| |
495 } |
| |
496 |
| |
497 void |
| |
498 purple_whiteboard_set_ui_data(PurpleWhiteboard *whiteboard, gpointer ui_data) { |
| |
499 g_return_if_fail(PURPLE_IS_WHITEBOARD(whiteboard)); |
| |
500 |
| |
501 whiteboard->ui_data = ui_data; |
| |
502 } |
| |
503 |
| |
504 gpointer |
| |
505 purple_whiteboard_get_ui_data(PurpleWhiteboard *whiteboard) { |
| |
506 g_return_val_if_fail(PURPLE_IS_WHITEBOARD(whiteboard), NULL); |
| |
507 |
| |
508 return whiteboard->ui_data; |
| |
509 } |
| |
510 |
| |
511 PurpleWhiteboard * |
| |
512 purple_whiteboard_new(PurpleAccount *account, const gchar *who, gint state) { |
| |
513 PurpleWhiteboard *whiteboard = NULL; |
| |
514 PurpleProtocol *protocol = NULL; |
| |
515 |
| |
516 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); |
| |
517 g_return_val_if_fail(who != NULL, NULL); |
| |
518 |
| |
519 protocol = purple_protocols_find(purple_account_get_protocol_id(account)); |
| |
520 |
| |
521 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL); |
| |
522 |
| |
523 if(PURPLE_IS_PROTOCOL_FACTORY(protocol)) { |
| |
524 whiteboard = purple_protocol_factory_whiteboard_new( |
| |
525 PURPLE_PROTOCOL_FACTORY(protocol), account, who, state); |
| |
526 } else { |
| |
527 whiteboard = g_object_new(PURPLE_TYPE_WHITEBOARD, |
| |
528 "account", account, |
| |
529 "who", who, |
| |
530 "state", state, |
| |
531 NULL |
| |
532 ); |
| |
533 } |
| |
534 |
| |
535 return whiteboard; |
| |
536 } |
| |
537 |