Compilation: prefer glib functions over goffice equivalents
[gnumeric.git] / plugins / excel / ms-obj.c
blobf1b595224db212085c08cffc435871145bb3da58
1 /* vim: set sw=8: */
3 /*
4 * ms-obj.c: MS Excel Object support for Gnumeric
6 * Authors:
7 * Jody Goldberg (jody@gnome.org)
8 * Michael Meeks (michael@ximian.com)
10 * (C) 1998-2001 Michael Meeks
11 * (C) 2002-2005 Jody Goldberg
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of the
16 * License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
26 * USA
29 #include <gnumeric-config.h>
30 #include <gnumeric.h>
31 #include <string.h>
33 #include "boot.h"
34 #include "ms-obj.h"
35 #include "ms-chart.h"
36 #include "ms-escher.h"
37 #include "ms-excel-util.h"
38 #include "ms-formula-write.h"
40 #include <expr.h>
41 #include <parse-util.h>
42 #include <sheet-object-widget.h>
44 #include <gsf/gsf-utils.h>
45 #include <stdio.h>
47 #define GR_END 0x00
48 #define GR_MACRO 0x04
49 #define GR_COMMAND_BUTTON 0x05
50 #define GR_GROUP 0x06
51 #define GR_CLIPBOARD_FORMAT 0x07
52 #define GR_PICTURE_OPTIONS 0x08
53 #define GR_PICTURE_FORMULA 0x09
54 #define GR_CHECKBOX_LINK 0x0A
55 #define GR_RADIO_BUTTON 0x0B
56 #define GR_SCROLLBAR 0x0C
57 #define GR_NOTE_STRUCTURE 0x0D
58 #define GR_SCROLLBAR_FORMULA 0x0E
59 #define GR_GROUP_BOX_DATA 0x0F
60 #define GR_EDIT_CONTROL_DATA 0x10
61 #define GR_RADIO_BUTTON_DATA 0x11
62 #define GR_CHECKBOX_DATA 0x12
63 #define GR_LISTBOX_DATA 0x13
64 #define GR_CHECKBOX_FORMULA 0x14
65 #define GR_COMMON_OBJ_DATA 0x15
67 MSObjAttr *
68 ms_obj_attr_new_flag (MSObjAttrID id)
70 MSObjAttr *res = g_new (MSObjAttr, 1);
72 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == 0, NULL);
74 /* be anal about constness */
75 *((MSObjAttrID *)&(res->id)) = id;
76 res->v.v_ptr = NULL;
77 return res;
80 MSObjAttr *
81 ms_obj_attr_new_uint (MSObjAttrID id, guint32 val)
83 MSObjAttr *res = g_new (MSObjAttr, 1);
85 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_INT_MASK, NULL);
87 /* be anal about constness */
88 *((MSObjAttrID *)&(res->id)) = id;
89 res->v.v_uint = val;
90 return res;
92 MSObjAttr *
93 ms_obj_attr_new_ptr (MSObjAttrID id, gpointer val)
95 MSObjAttr *res = g_new (MSObjAttr, 1);
97 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_PTR_MASK, NULL);
99 /* be anal about constness */
100 *((MSObjAttrID *)&(res->id)) = id;
101 res->v.v_ptr = val;
102 return res;
105 MSObjAttr *
106 ms_obj_attr_new_array (MSObjAttrID id, GArray *array)
108 MSObjAttr *res = g_new (MSObjAttr, 1);
110 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_GARRAY_MASK, NULL);
112 /* be anal about constness */
113 *((MSObjAttrID *)&(res->id)) = id;
114 res->v.v_array = array;
115 return res;
118 MSObjAttr *
119 ms_obj_attr_new_expr (MSObjAttrID id, GnmExprTop const *texpr)
121 MSObjAttr *res = g_new (MSObjAttr, 1);
123 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_EXPR_MASK, NULL);
125 /* be anal about constness */
126 *((MSObjAttrID *)&(res->id)) = id;
127 res->v.v_texpr = texpr;
128 return res;
131 MSObjAttr *
132 ms_obj_attr_new_markup (MSObjAttrID id, PangoAttrList *markup)
134 MSObjAttr *res = g_new (MSObjAttr, 1);
136 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_PANGO_ATTR_LIST_MASK, NULL);
138 /* be anal about constness */
139 *((MSObjAttrID *)&(res->id)) = id;
140 res->v.v_markup = markup;
141 pango_attr_list_ref (markup);
142 return res;
145 MSObjAttr *
146 ms_obj_attr_new_gobject (MSObjAttrID id, GObject *object)
148 MSObjAttr *res = g_new (MSObjAttr, 1);
150 g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_GOBJECT_MASK, NULL);
152 *((MSObjAttrID *)&(res->id)) = id;
153 res->v.v_object = object;
154 g_object_ref (object);
155 return res;
158 guint32
159 ms_obj_attr_get_uint (MSObjAttrBag *attrs, MSObjAttrID id, guint32 default_value)
161 MSObjAttr *attr;
163 g_return_val_if_fail (attrs != NULL, default_value);
164 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_INT_MASK, default_value);
166 attr = ms_obj_attr_bag_lookup (attrs, id);
167 if (attr == NULL)
168 return default_value;
169 return attr->v.v_uint;
172 gint32
173 ms_obj_attr_get_int (MSObjAttrBag *attrs, MSObjAttrID id, gint32 default_value)
175 MSObjAttr *attr;
177 g_return_val_if_fail (attrs != NULL, default_value);
178 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_INT_MASK, default_value);
180 attr = ms_obj_attr_bag_lookup (attrs, id);
181 if (attr == NULL)
182 return default_value;
183 return attr->v.v_int;
186 gboolean
187 ms_obj_attr_get_ptr (MSObjAttrBag *attrs, MSObjAttrID id,
188 gpointer *res, gboolean steal)
190 MSObjAttr *attr;
192 g_return_val_if_fail (attrs != NULL, FALSE);
193 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_PTR_MASK, FALSE);
195 if (NULL == (attr = ms_obj_attr_bag_lookup (attrs, id)))
196 return FALSE;
198 *res = attr->v.v_ptr;
199 if (steal)
200 attr->v.v_ptr = NULL;
202 return TRUE;
205 GArray *
206 ms_obj_attr_get_array (MSObjAttrBag *attrs, MSObjAttrID id,
207 GArray *default_value, gboolean steal)
209 MSObjAttr *attr;
210 GArray *res;
212 g_return_val_if_fail (attrs != NULL, default_value);
213 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_GARRAY_MASK, default_value);
215 attr = ms_obj_attr_bag_lookup (attrs, id);
216 if (attr == NULL)
217 return default_value;
218 res = attr->v.v_array;
219 if (steal)
220 attr->v.v_array = NULL;
221 return res;
224 GnmExprTop const *
225 ms_obj_attr_get_expr (MSObjAttrBag *attrs, MSObjAttrID id,
226 GnmExprTop const *default_value, gboolean steal)
228 MSObjAttr *attr;
229 GnmExprTop const *res;
231 g_return_val_if_fail (attrs != NULL, default_value);
232 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_EXPR_MASK, default_value);
234 attr = ms_obj_attr_bag_lookup (attrs, id);
235 if (attr == NULL)
236 return default_value;
237 res = attr->v.v_texpr;
238 if (steal)
239 attr->v.v_texpr = NULL;
240 return res;
243 PangoAttrList *
244 ms_obj_attr_get_markup (MSObjAttrBag *attrs, MSObjAttrID id,
245 PangoAttrList *default_value, gboolean steal)
247 MSObjAttr *attr;
248 PangoAttrList *res;
250 g_return_val_if_fail (attrs != NULL, default_value);
251 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_PANGO_ATTR_LIST_MASK, default_value);
253 attr = ms_obj_attr_bag_lookup (attrs, id);
254 if (attr == NULL)
255 return default_value;
256 res = attr->v.v_markup;
257 if (steal)
258 attr->v.v_markup = NULL;
259 return res;
262 GObject *
263 ms_obj_attr_get_gobject (MSObjAttrBag *attrs, MSObjAttrID id)
265 MSObjAttr *attr;
267 g_return_val_if_fail (attrs != NULL, NULL);
268 g_return_val_if_fail (id & MS_OBJ_ATTR_IS_GOBJECT_MASK, NULL);
270 attr = ms_obj_attr_bag_lookup (attrs, id);
271 if (attr == NULL)
272 return NULL;
273 return attr->v.v_object;
276 static void
277 ms_obj_attr_destroy (MSObjAttr *attr)
279 if (attr != NULL) {
280 if ((attr->id & MS_OBJ_ATTR_IS_PTR_MASK) &&
281 attr->v.v_ptr != NULL) {
282 g_free (attr->v.v_ptr);
283 attr->v.v_ptr = NULL;
284 } else if ((attr->id & MS_OBJ_ATTR_IS_GARRAY_MASK) &&
285 attr->v.v_array != NULL) {
286 g_array_free (attr->v.v_array, TRUE);
287 attr->v.v_array = NULL;
288 } else if ((attr->id & MS_OBJ_ATTR_IS_EXPR_MASK) &&
289 attr->v.v_texpr != NULL) {
290 gnm_expr_top_unref (attr->v.v_texpr);
291 attr->v.v_texpr = NULL;
292 } else if ((attr->id & MS_OBJ_ATTR_IS_PANGO_ATTR_LIST_MASK) &&
293 attr->v.v_markup != NULL) {
294 pango_attr_list_unref (attr->v.v_markup);
295 attr->v.v_markup = NULL;
296 } else if ((attr->id & MS_OBJ_ATTR_IS_GOBJECT_MASK) &&
297 attr->v.v_object != NULL) {
298 g_object_unref (attr->v.v_object);
299 attr->v.v_object = NULL;
301 g_free (attr);
305 static guint
306 cb_ms_obj_attr_hash (gconstpointer key)
308 return ((MSObjAttr const *)key)->id;
311 static gint
312 cb_ms_obj_attr_cmp (gconstpointer a, gconstpointer b)
314 return ((MSObjAttr const *)a)->id == ((MSObjAttr const *)b)->id;
317 MSObjAttrBag *
318 ms_obj_attr_bag_new (void)
320 return g_hash_table_new (cb_ms_obj_attr_hash, cb_ms_obj_attr_cmp);
323 static void
324 cb_ms_obj_attr_destroy (gpointer key, gpointer value, gpointer ignored)
326 ms_obj_attr_destroy (value);
328 void
329 ms_obj_attr_bag_destroy (MSObjAttrBag *attrs)
331 if (attrs != NULL) {
332 g_hash_table_foreach (attrs, cb_ms_obj_attr_destroy, NULL);
333 g_hash_table_destroy (attrs);
338 void
339 ms_obj_attr_bag_insert (MSObjAttrBag *attrs, MSObjAttr *attr)
341 g_return_if_fail (!g_hash_table_lookup (attrs, attr));
342 g_hash_table_insert (attrs, attr, attr);
345 MSObjAttr *
346 ms_obj_attr_bag_lookup (MSObjAttrBag *attrs, MSObjAttrID id)
348 if (attrs != NULL) {
349 MSObjAttr attr = {0, {0}};
350 *((MSObjAttrID *)&(attr.id)) = id;
351 return g_hash_table_lookup (attrs, &attr);
353 return NULL;
356 /********************************************************************************/
358 MSObj *
359 ms_obj_new (MSObjAttrBag *attrs)
361 MSObj *obj = g_new0 (MSObj, 1);
363 obj->excel_type = (unsigned)-1; /* Set to undefined */
364 obj->excel_type_name = NULL;
365 obj->id = -1;
366 obj->gnum_obj = NULL;
367 obj->attrs = (attrs != NULL) ? attrs : ms_obj_attr_bag_new ();
368 obj->auto_combo = FALSE;
369 obj->is_linked = FALSE;
370 obj->comment_pos.col = obj->comment_pos.row = -1;
372 return obj;
375 void
376 ms_obj_delete (MSObj *obj)
378 if (obj) {
379 if (obj->gnum_obj) {
380 g_object_unref (obj->gnum_obj);
381 obj->gnum_obj = NULL;
383 if (obj->attrs) {
384 ms_obj_attr_bag_destroy (obj->attrs);
385 obj->attrs = NULL;
387 g_free (obj);
391 char *
392 ms_read_TXO (BiffQuery *q, MSContainer *c, PangoAttrList **markup)
394 static char const * const orientations [] = {
395 "Left to right",
396 "Top to Bottom",
397 "Bottom to Top on Side",
398 "Top to Bottom on Side"
400 static char const * const haligns [] = {
401 "At left", "Horizontally centered",
402 "At right", "Horizontally justified"
404 static char const * const valigns [] = {
405 "At top", "Vertically centered",
406 "At bottom", "Vertically justified"
409 guint16 options, orient, text_len;
410 int halign, valign;
411 char *text;
412 guint16 op;
413 GString *accum;
414 gboolean continue_seen = FALSE;
416 *markup = NULL;
418 XL_CHECK_CONDITION_VAL (q->length >= 14, g_strdup (""));
420 options = GSF_LE_GET_GUINT16 (q->data);
421 orient = GSF_LE_GET_GUINT16 (q->data + 2);
422 text_len = GSF_LE_GET_GUINT16 (q->data + 10);
423 /* guint16 const num_formats = GSF_LE_GET_GUINT16 (q->data + 12);*/
424 halign = (options >> 1) & 0x7;
425 valign = (options >> 4) & 0x7;
427 if (text_len == 0)
428 return NULL;
430 accum = g_string_new ("");
431 while (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
432 gboolean use_utf16;
433 guint maxlen;
435 continue_seen = TRUE;
436 ms_biff_query_next (q);
437 if (q->length == 0)
438 continue;
440 use_utf16 = q->data[0] != 0;
441 maxlen = (q->length - 1) / (use_utf16 ? 2 : 1);
442 text = excel_get_chars (c->importer,
443 q->data + 1, MIN (text_len, maxlen), use_utf16, NULL);
444 g_string_append (accum, text);
445 g_free (text);
446 if (text_len <= maxlen)
447 break;
448 text_len -= maxlen;
450 text = g_string_free (accum, FALSE);
451 if (continue_seen) {
452 if (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
453 ms_biff_query_next (q);
454 *markup = ms_container_read_markup (c, q->data, q->length,
455 text);
456 } else {
457 g_warning ("Unusual, TXO text with no formatting has 0x%x @ 0x%lx",
458 op, (long)q->streamPos);
460 } else {
461 g_warning ("TXO len of %d but no continue", text_len);
464 #ifndef NO_DEBUG_EXCEL
465 if (ms_excel_object_debug > 0) {
466 char const *o_msg = (orient <= 3) ? orientations[orient] : "unknown orientation";
467 char const *h_msg = (1 <= halign && halign <= 4) ? haligns[halign-1] : "unknown h-align";
468 char const *v_msg = (1 <= valign && valign <= 4) ? valigns[valign-1] : "unknown v-align";
470 g_printerr ("{ TextObject\n");
471 g_printerr ("Text '%s'\n", text);
472 g_printerr ("is %s(%d), %s(%d) & %s(%d);\n",
473 o_msg, orient, h_msg, halign, v_msg, valign);
474 g_printerr ("}; /* TextObject */\n");
476 #endif
477 return text;
480 #ifndef NO_DEBUG_EXCEL
481 static void
482 ms_obj_dump (guint8 const *data, int len, int data_left, char const *name)
484 if (ms_excel_object_debug < 2)
485 return;
487 g_printerr ("{ %s \n", name);
488 if (len+4 > data_left) {
489 g_printerr ("/* invalid length %d (0x%x) > %d(0x%x)*/\n",
490 len+4, len+4, data_left, data_left);
491 len = data_left - 4;
493 if (ms_excel_object_debug > 2)
494 gsf_mem_dump (data, len+4);
495 g_printerr ("}; /* %s */\n", name);
497 #else
498 #define ms_obj_dump (data, len, data_left, name) do { } while (0)
499 #endif
501 static guint8 const *
502 ms_obj_read_expr (MSObj *obj, MSObjAttrID id, MSContainer *c,
503 guint8 const *data, guint8 const *last)
505 guint16 len;
506 GnmExprTop const *ref;
508 if (ms_excel_object_debug > 2)
509 gsf_mem_dump (data, last-data);
511 /* <u16 length> <u32 calcid?> <var expr> */
512 g_return_val_if_fail ((data + 2) <= last, NULL);
513 len = GSF_LE_GET_GUINT16 (data);
515 /* looks like they sometimes skip the calc id if there is expr */
516 if (len == 0 && (data + 2) == last)
517 return data + 2;
519 g_return_val_if_fail ((data + 6 + len) <= last, NULL);
520 if (NULL == (ref = ms_container_parse_expr (c, data + 6, len)))
521 return NULL;
522 ms_obj_attr_bag_insert (obj->attrs,
523 ms_obj_attr_new_expr (id, ref));
524 return data + 6 + len;
528 static gboolean
529 read_pre_biff8_read_text (BiffQuery *q, MSContainer *c, MSObj *obj,
530 guint8 const *first,
531 unsigned len, unsigned txo_len)
533 PangoAttrList *markup = NULL;
534 GByteArray *markup_data = NULL;
535 char *str;
536 unsigned remaining;
537 guint16 op;
539 if (first == NULL)
540 return TRUE;
542 remaining = q->data + q->length - first;
544 /* CONTINUE handling here is very odd.
545 * If the text needs CONTINUEs but the markup does not, the markup is
546 * stored at the end of the OBJ rather than after the text. */
547 if (txo_len > 0 && txo_len < remaining) {
548 markup_data = g_byte_array_new ();
549 g_byte_array_append (markup_data, q->data + q->length - txo_len, txo_len);
550 remaining -= txo_len;
553 str = excel_get_chars (c->importer, first, MIN (remaining, len), FALSE, NULL);
554 if (len > remaining) {
555 GString *accum = g_string_new (str);
556 g_free (str);
557 len -= remaining;
558 while (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
559 ms_biff_query_next (q);
560 str = excel_get_chars (c->importer, q->data,
561 MIN (q->length, len), FALSE, NULL);
562 g_string_append (accum, str);
563 g_free (str);
564 if (len < q->length)
565 break;
566 len -= q->length;
568 str = g_string_free (accum, FALSE);
569 if (len > q->length) {
570 g_free (str);
571 return TRUE;
573 first = q->data + len;
574 } else
575 first += len;
576 if (((first - q->data) & 1))
577 first++; /* pad to word bound */
579 ms_obj_attr_bag_insert (obj->attrs,
580 ms_obj_attr_new_ptr (MS_OBJ_ATTR_TEXT, str));
582 if (NULL != markup_data) {
583 markup = ms_container_read_markup (c, markup_data->data, markup_data->len,
584 str);
585 g_byte_array_free (markup_data, TRUE);
586 } else if (txo_len > 0) {
587 remaining = q->data + q->length - first;
588 if (txo_len > remaining) {
589 GByteArray *accum = g_byte_array_new ();
590 g_byte_array_append (accum, first, remaining);
591 txo_len -= remaining;
592 while (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
593 ms_biff_query_next (q);
594 g_byte_array_append (accum, q->data, MIN (q->length, txo_len));
595 if (txo_len <= q->length)
596 break;
597 txo_len -= q->length;
599 first = q->data + txo_len;
600 markup = ms_container_read_markup (c, accum->data, accum->len,
601 str);
602 g_byte_array_free (accum, TRUE);
603 } else {
604 markup = ms_container_read_markup (c, first, txo_len,
605 str);
606 first += txo_len;
609 if (NULL != markup) {
610 ms_obj_attr_bag_insert (obj->attrs,
611 ms_obj_attr_new_markup (MS_OBJ_ATTR_MARKUP, markup));
612 pango_attr_list_unref (markup);
615 return FALSE;
618 static guint8 const *
619 read_pre_biff8_read_expr (BiffQuery *q, MSContainer *c, MSObj *obj,
620 guint8 const *data, unsigned total_len) /* including extras */
622 if (total_len <= 0)
623 return data;
624 XL_CHECK_CONDITION_VAL (total_len <= q->length - (data - q->data), data);
626 ms_obj_read_expr (obj, MS_OBJ_ATTR_LINKED_TO_CELL, c,
627 data, data + total_len);
628 data += total_len; /* use total_len not the stated expression len */
629 if (((data - q->data) & 1) && data < q->data + q->length)
630 data++; /* pad to word bound */
631 return data;
634 static guint8 const *
635 read_pre_biff8_read_name_and_fmla (BiffQuery *q, MSContainer *c, MSObj *obj,
636 gboolean has_name, unsigned offset)
638 guint8 const *data;
639 gboolean fmla_len;
641 XL_CHECK_CONDITION_VAL (q->length >= 28, NULL);
642 fmla_len = GSF_LE_GET_GUINT16 (q->data+26);
643 XL_CHECK_CONDITION_VAL (q->length >= offset + 2 + fmla_len, NULL);
645 data = q->data + offset;
647 if (has_name) {
648 guint8 const *last = q->data + q->length;
649 unsigned len = *data++;
650 char *str;
652 g_return_val_if_fail (last - data >= len, NULL);
654 str = excel_get_chars (c->importer, data, len, FALSE, NULL);
655 data += len;
656 if (((data - q->data) & 1) && data < last)
657 data++; /* pad to word bound */
659 ms_obj_attr_bag_insert (obj->attrs,
660 ms_obj_attr_new_ptr (MS_OBJ_ATTR_OBJ_NAME, str));
662 return read_pre_biff8_read_expr (q, c, obj, data, fmla_len);
665 static gboolean
666 ms_obj_read_pre_biff8_obj (BiffQuery *q, MSContainer *c, MSObj *obj)
668 guint8 const *last = q->data + q->length;
669 guint16 peek_op, tmp, len;
670 unsigned txo_len, if_empty;
671 guint8 const *data;
672 gboolean has_name;
673 guint8 *anchor;
675 XL_CHECK_CONDITION_VAL (q->length >= 26, TRUE);
677 has_name = (q->length >= 32 &&
678 GSF_LE_GET_GUINT16 (q->data+30) != 0); /* undocumented */
679 #if 0
680 guint16 const flags = GSF_LE_GET_GUINT16(q->data+8);
681 #endif
682 anchor = g_malloc (MS_ANCHOR_SIZE);
683 memcpy (anchor, q->data+8, MS_ANCHOR_SIZE);
684 ms_obj_attr_bag_insert (obj->attrs,
685 ms_obj_attr_new_ptr (MS_OBJ_ATTR_ANCHOR, anchor));
687 obj->excel_type = GSF_LE_GET_GUINT16(q->data + 4);
688 obj->id = GSF_LE_GET_GUINT32(q->data + 6);
690 switch (obj->excel_type) {
691 case 0: /* group */
692 break;
693 case MSOT_LINE:
694 XL_CHECK_CONDITION_VAL (q->data + 41 <= last, TRUE);
695 tmp = GSF_LE_GET_GUINT8 (q->data+38) & 0x0F;
696 if (tmp > 0)
697 ms_obj_attr_bag_insert (obj->attrs,
698 ms_obj_attr_new_uint (MS_OBJ_ATTR_ARROW_END, tmp));
699 ms_obj_attr_bag_insert (obj->attrs,
700 ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
701 0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
702 tmp = GSF_LE_GET_GUINT8 (q->data+35);
703 ms_obj_attr_bag_insert (obj->attrs,
704 ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_STYLE,
705 ((tmp == 0xff) ? 0 : tmp+1)));
707 tmp = GSF_LE_GET_GUINT8 (q->data+40);
708 if (tmp == 1 || tmp == 2)
709 ms_obj_attr_bag_insert (obj->attrs,
710 ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_H));
711 if (tmp >= 2)
712 ms_obj_attr_bag_insert (obj->attrs,
713 ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_V));
714 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name,
715 (obj->excel_type == MSOT_LINE) ? 42 : 44);
716 break;
718 case MSOT_RECTANGLE:
719 case MSOT_OVAL:
720 case MSOT_ARC:
721 case MSOT_TEXTBOX:
722 XL_CHECK_CONDITION_VAL (q->data + 36 <= last, TRUE);
723 ms_obj_attr_bag_insert (obj->attrs,
724 ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_BACKGROUND,
725 0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
726 ms_obj_attr_bag_insert (obj->attrs,
727 ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
728 0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
729 if (GSF_LE_GET_GUINT8 (q->data+36) == 0)
730 ms_obj_attr_bag_insert (obj->attrs,
731 ms_obj_attr_new_flag (MS_OBJ_ATTR_UNFILLED));
733 tmp = GSF_LE_GET_GUINT8 (q->data+39);
734 ms_obj_attr_bag_insert (obj->attrs,
735 ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_STYLE,
736 ((tmp == 0xff) ? 0 : tmp+1)));
737 ms_obj_attr_bag_insert (obj->attrs,
738 ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
739 0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
740 ms_obj_attr_bag_insert (obj->attrs,
741 ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_WIDTH,
742 GSF_LE_GET_GUINT8 (q->data+40) * 256));
744 if (obj->excel_type == MSOT_TEXTBOX) {
745 g_return_val_if_fail (q->data + 52 <= last, TRUE);
746 len = GSF_LE_GET_GUINT16 (q->data + 44);
747 txo_len = GSF_LE_GET_GUINT16 (q->data + 48);
748 if_empty = GSF_LE_GET_GUINT16 (q->data + 50);
750 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
751 if (read_pre_biff8_read_text (q, c, obj, data, len, txo_len))
752 return TRUE;
753 if (txo_len == 0)
754 ms_obj_attr_bag_insert (obj->attrs,
755 ms_obj_attr_new_markup (MS_OBJ_ATTR_MARKUP,
756 ms_container_get_markup (c, if_empty)));
757 } else
758 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 44);
759 break;
761 case MSOT_CHART:
762 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 62);
763 break;
765 case MSOT_BUTTON:
766 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
767 break;
768 case MSOT_PICTURE:
769 /* 50 uint16 cbPictFmla, 60 name len, name, fmla (respect cbMacro), fmla (cbPictFmla) */
770 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 60);
771 break;
773 case MSOT_POLYGON:
774 /* 66 name len, name, fmla (respect cbMacro) */
775 ms_obj_attr_bag_insert (obj->attrs,
776 ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
777 0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
778 ms_obj_attr_bag_insert (obj->attrs,
779 ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
780 0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
782 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 66);
784 if (ms_biff_query_peek_next (q, &peek_op) &&
785 peek_op == BIFF_COORDLIST) {
786 unsigned i, n;
787 guint tmp;
788 GArray *array;
790 ms_biff_query_next (q);
791 n = q->length / 2;
792 array = g_array_set_size (
793 g_array_new (FALSE, FALSE, sizeof (double)), n + 2);
795 for (i = 0; i < n ; i++) {
796 tmp = GSF_LE_GET_GUINT16 (q->data + 2*i);
797 g_array_index (array, double, i) = (double)tmp/ 16384.;
799 g_array_index (array, double, i) = g_array_index (array, double, 0);
800 g_array_index (array, double, i+1) = g_array_index (array, double, 1);
801 ms_obj_attr_bag_insert (obj->attrs,
802 ms_obj_attr_new_array (MS_OBJ_ATTR_POLYGON_COORDS, array));
804 break;
806 case MSOT_CHECKBOX:
807 /* 76 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbtext, text */
808 break;
809 case MSOT_OPTION: /* option button */
810 /* 88 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbtext, text */
811 break;
812 case MSOT_EDIT:
813 /* 70 name len, name, fmla (respect cbMacro), cbtext, text */
814 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
815 break;
816 case MSOT_LABEL:
817 /* 70 name len, name, fmla (respect cbMacro), cbtext, text */
818 len = GSF_LE_GET_GUINT16 (q->data + 44);
819 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
820 if (read_pre_biff8_read_text (q, c, obj, data, len, 16))
821 return TRUE;
822 break;
823 case MSOT_DIALOG: /* dialog frame */
824 /* 70 name len, name, fmla (respect cbMacro) */
825 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
826 break;
827 case MSOT_SPINNER:
828 case MSOT_SCROLLBAR:
829 /* 68 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2 */
830 ms_obj_attr_bag_insert (obj->attrs,
831 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
832 GSF_LE_GET_GUINT16 (q->data+48)));
833 ms_obj_attr_bag_insert (obj->attrs,
834 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
835 GSF_LE_GET_GUINT16 (q->data+50)));
836 ms_obj_attr_bag_insert (obj->attrs,
837 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
838 GSF_LE_GET_GUINT16 (q->data+52)));
839 ms_obj_attr_bag_insert (obj->attrs,
840 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
841 GSF_LE_GET_GUINT16 (q->data+54)));
842 ms_obj_attr_bag_insert (obj->attrs,
843 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
844 GSF_LE_GET_GUINT16 (q->data+56)));
845 ms_obj_attr_bag_insert (obj->attrs,
846 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_HORIZ,
847 GSF_LE_GET_GUINT16 (q->data+58)));
850 guint8 const *last = q->data + q->length;
851 guint8 const *ptr = q->data + 64;
853 ptr += 1 + *ptr; /* object name */
854 if ((ptr - q->data) & 1) ptr++; /* align on word */
855 if (ptr >= last) break;
857 ptr += 2 + GSF_LE_GET_GUINT16 (ptr); /* the macro */
858 if ((ptr - q->data) & 1) ptr++; /* align on word */
859 if (ptr >= last) break;
861 (void) ms_obj_read_expr (obj, MS_OBJ_ATTR_LINKED_TO_CELL, c,
862 ptr, last);
864 break;
865 case MSOT_LIST:
866 /* 88 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbfmla3, fmla3 */
867 break;
868 case MSOT_GROUP:
869 /* 82 name len, name, fmla (respect cbMacro), cbtext, text */
870 data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 82);
871 break;
872 case MSOT_COMBO:
873 /* 110 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbfmla3, fmla3 */
874 obj->auto_combo =
875 (GSF_LE_GET_GUINT16 (q->data + 8) & 0x8000) ? TRUE : FALSE;
876 break;
877 default :
878 break;
881 if (obj->excel_type == MSOT_PICTURE) { /* picture */
882 guint16 op;
883 if (ms_biff_query_peek_next (q, &op) && op == BIFF_IMDATA) {
884 GdkPixbuf *pixbuf;
886 ms_biff_query_next (q);
887 pixbuf = excel_read_IMDATA (q, FALSE);
888 if (pixbuf) {
889 ms_obj_attr_bag_insert (obj->attrs,
890 ms_obj_attr_new_gobject
891 (MS_OBJ_ATTR_IMDATA,
892 G_OBJECT (pixbuf)));
893 g_object_unref (pixbuf);
897 return FALSE;
900 static void
901 ms_obj_map_forms_obj (MSObj *obj, MSContainer *c,
902 guint8 const *data, guint8 const *last)
904 static struct {
905 char const *key;
906 unsigned excel_type;
907 gboolean has_result_link;
908 gboolean has_source_link; /* requires has_result_link */
909 } const map_forms [] = {
910 { "ScrollBar.1", MSOT_SCROLLBAR, TRUE, FALSE },
911 { "CheckBox.1", MSOT_CHECKBOX, TRUE, FALSE },
912 { "TextBox.1", MSOT_TEXTBOX, FALSE, FALSE },
913 { "CommandButton.1", MSOT_BUTTON, FALSE, FALSE },
914 { "OptionButton.1", MSOT_OPTION, TRUE, FALSE },
915 { "ListBox.1", MSOT_LIST, TRUE, TRUE },
916 { "ComboBox.1", MSOT_COMBO, TRUE, TRUE },
917 { "ToggleButton.1", MSOT_TOGGLE, TRUE, FALSE },
918 { "SpinButton.1", MSOT_SPINNER, TRUE, FALSE },
919 { "Label.1", MSOT_LABEL, FALSE, FALSE },
920 { "Image.1", MSOT_PICTURE, FALSE, FALSE }
922 int i;
923 char *type;
924 guint32 len;
926 if (last - data < 16)
927 return;
928 type = excel_get_text (c->importer, data + 16,
929 GSF_LE_GET_GUINT16 (data + 14),
930 &len, NULL, last - data);
931 if (NULL == type || strncmp (type, "Forms.", 6)) {
932 g_free (type);
933 return;
936 #ifndef NO_DEBUG_EXCEL
937 if (ms_excel_object_debug > 0) {
938 g_printerr ("'%s' = %d\n", type, len);
939 if (ms_excel_object_debug > 4)
940 gsf_mem_dump (data, last-data);
942 #endif
944 for (i = G_N_ELEMENTS (map_forms); i-- > 0 ; )
945 if (map_forms [i].excel_type > 0 &&
946 !strcmp (type+6, map_forms[i].key))
947 break;
949 g_free (type);
951 if (i < 0)
952 return;
953 obj->excel_type = map_forms [i].excel_type;
954 #ifndef NO_DEBUG_EXCEL
955 if (ms_excel_object_debug > 0)
956 g_printerr ("found = %s\n", map_forms[i].key);
957 #endif
959 if (map_forms [i].has_result_link) {
960 /* round to word length */
961 data = ms_obj_read_expr (obj, MS_OBJ_ATTR_LINKED_TO_CELL, c,
962 data + 16 + len + (len &1) + 14, last);
963 if (NULL != data && map_forms [i].has_source_link)
964 ms_obj_read_expr (obj, MS_OBJ_ATTR_INPUT_FROM, c,
965 data+3, last);
969 static gboolean
970 ms_obj_read_biff8_obj (BiffQuery *q, MSContainer *c, MSObj *obj)
972 guint8 *data;
973 gint32 data_len_left;
974 gboolean hit_end = FALSE;
975 gboolean next_biff_record_maybe_imdata = FALSE;
977 g_return_val_if_fail (q, TRUE);
978 g_return_val_if_fail (q->opcode == BIFF_OBJ, TRUE);
980 data = q->data;
981 data_len_left = q->length;
983 #if 0
984 ms_biff_query_dump (q);
985 #endif
987 /* Scan through the pseudo BIFF substream */
988 while (data_len_left >= 4 && !hit_end) {
989 guint16 const record_type = GSF_LE_GET_GUINT16(data);
991 /* All the sub-records seem to have this layout
992 * 2001/Mar/29 JEG : liars. Ok not all records have this
993 * layout. Create a list box. It seems to do something
994 * unique. It acts like an end, and has no length specified.
996 guint16 len = GSF_LE_GET_GUINT16(data+2);
998 // The would-be "len" field has different meaning for
999 // GR_LISTBOX_DATA.
1000 if (record_type != GR_LISTBOX_DATA)
1001 XL_CHECK_CONDITION_VAL (data_len_left >= 4 + len, TRUE);
1003 /* 1st record must be COMMON_OBJ*/
1004 XL_CHECK_CONDITION_VAL (obj->excel_type >= 0 ||
1005 record_type == GR_COMMON_OBJ_DATA,
1006 TRUE);
1008 switch (record_type) {
1009 case GR_END:
1010 XL_CHECK_CONDITION_VAL (len == 0, TRUE);
1011 /* ms_obj_dump (data, len, data_len_left, "ObjEnd"); */
1012 hit_end = TRUE;
1013 break;
1015 case GR_MACRO :
1016 ms_obj_read_expr (obj, MS_OBJ_ATTR_MACRO_EXPR, c,
1017 data+4, data + 4 + len);
1018 ms_obj_dump (data, len, data_len_left, "MacroObject");
1019 break;
1021 case GR_COMMAND_BUTTON :
1022 ms_obj_dump (data, len, data_len_left, "CommandButton");
1023 break;
1025 case GR_GROUP :
1026 ms_obj_dump (data, len, data_len_left, "Group");
1027 break;
1029 case GR_CLIPBOARD_FORMAT :
1030 ms_obj_dump (data, len, data_len_left, "ClipboardFmt");
1031 break;
1033 case GR_PICTURE_OPTIONS :
1034 if (len == 2) {
1035 guint16 opt = GSF_LE_GET_GUINT16 (data + 4);
1037 obj->is_linked = (opt & 0x2) ? TRUE : FALSE;
1038 #ifndef NO_DEBUG_EXCEL
1039 if (ms_excel_object_debug >= 1) {
1040 g_printerr ("{ /* PictOpt */\n");
1041 g_printerr ("value = %x;\n", opt);
1042 g_printerr ("}; /* PictOpt */\n");
1044 #endif
1045 } else {
1046 /* no docs on this so be careful */
1047 g_warning ("PictOpt record with size other than 2");
1050 next_biff_record_maybe_imdata = TRUE;
1051 break;
1053 case GR_PICTURE_FORMULA :
1054 /* Check for form objects stored here for no apparent reason */
1055 if (obj->excel_type == 8)
1056 ms_obj_map_forms_obj (obj, c, data+4, data+4+len);
1057 break;
1059 case GR_CHECKBOX_LINK :
1060 ms_obj_dump (data, len, data_len_left, "CheckboxLink");
1061 break;
1063 case GR_RADIO_BUTTON :
1064 ms_obj_dump (data, len, data_len_left, "RadioButton");
1065 break;
1067 case GR_SCROLLBAR :
1068 XL_CHECK_CONDITION_VAL (data_len_left >= 20, TRUE);
1069 ms_obj_attr_bag_insert (obj->attrs,
1070 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
1071 GSF_LE_GET_GUINT16 (data+8)));
1072 ms_obj_attr_bag_insert (obj->attrs,
1073 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
1074 GSF_LE_GET_GUINT16 (data+10)));
1075 ms_obj_attr_bag_insert (obj->attrs,
1076 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
1077 GSF_LE_GET_GUINT16 (data+12)));
1078 ms_obj_attr_bag_insert (obj->attrs,
1079 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
1080 GSF_LE_GET_GUINT16 (data+14)));
1081 ms_obj_attr_bag_insert (obj->attrs,
1082 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
1083 GSF_LE_GET_GUINT16 (data+16)));
1084 ms_obj_attr_bag_insert (obj->attrs,
1085 ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_HORIZ,
1086 GSF_LE_GET_GUINT16 (data+18)));
1087 ms_obj_dump (data, len, data_len_left, "ScrollBar");
1088 break;
1090 case GR_NOTE_STRUCTURE :
1091 ms_obj_dump (data, len, data_len_left, "Note");
1092 break;
1094 case GR_SCROLLBAR_FORMULA :
1095 ms_obj_read_expr (obj, MS_OBJ_ATTR_LINKED_TO_CELL, c,
1096 data+4, data + 4 + len);
1097 ms_obj_dump (data, len, data_len_left, "ScrollbarFmla");
1098 break;
1100 case GR_GROUP_BOX_DATA :
1101 ms_obj_dump (data, len, data_len_left, "GroupBoxData");
1102 break;
1104 case GR_EDIT_CONTROL_DATA :
1105 ms_obj_dump (data, len, data_len_left, "EditCtrlData");
1106 break;
1108 case GR_RADIO_BUTTON_DATA :
1109 ms_obj_dump (data, len, data_len_left, "RadioData");
1110 break;
1112 case GR_CHECKBOX_DATA :
1113 ms_obj_dump (data, len, data_len_left, "CheckBoxData");
1114 break;
1116 case GR_LISTBOX_DATA :
1117 if (!obj->auto_combo)
1118 ms_obj_read_expr (obj, MS_OBJ_ATTR_INPUT_FROM, c,
1119 data+6, data + data_len_left);
1121 /* UNDOCUMENTED :
1122 * It seems as if list box data does not conform to
1123 * the docs. It acts like an end and has no size.
1125 hit_end = TRUE;
1126 len = data_len_left - 4;
1128 #warning "We should import selection too, see ms_objv8_write_list_data"
1130 ms_obj_dump (data, len, data_len_left, "ListBoxData");
1131 break;
1133 case GR_CHECKBOX_FORMULA :
1134 ms_obj_read_expr (obj, MS_OBJ_ATTR_LINKED_TO_CELL, c,
1135 data+4, data + 4 + len);
1136 ms_obj_dump (data, len, data_len_left, "CheckBoxFmla");
1137 break;
1139 case GR_COMMON_OBJ_DATA : {
1140 guint16 options;
1142 XL_CHECK_CONDITION_VAL (data_len_left >= 10, TRUE);
1144 options = GSF_LE_GET_GUINT16 (data+8);
1146 /* Multiple objects in 1 record ?? */
1147 XL_CHECK_CONDITION_VAL (obj->excel_type == -1, TRUE);
1149 obj->excel_type = GSF_LE_GET_GUINT16(data+4);
1150 obj->id = GSF_LE_GET_GUINT16(data+6);
1152 // "P" flag in FtCmo.
1153 obj->auto_combo =
1154 (obj->excel_type == 0x14) && (options & 0x100);
1156 #ifndef NO_DEBUG_EXCEL
1157 /* only print when debug is enabled */
1158 if (ms_excel_object_debug == 0)
1159 break;
1161 g_printerr ("OBJECT TYPE = %d, id = %d;\n", obj->excel_type, obj->id);
1162 if (options&0x0001)
1163 g_printerr ("Locked;\n");
1164 if (options&0x0010)
1165 g_printerr ("Printable;\n");
1166 if (options&0x2000)
1167 g_printerr ("AutoFilled;\n");
1168 if (options&0x4000)
1169 g_printerr ("AutoLines;\n");
1171 if (ms_excel_object_debug > 4) {
1172 /* According to the docs this should not fail
1173 * but there appears to be a flag at 0x200 for
1174 * scrollbars and 0x100 for combos associated
1175 * with filters. */
1176 if ((options & 0x9eee) != 0)
1177 g_printerr ("Unknown option flag : %x;\n",
1178 options & 0x9eee);
1180 #endif
1182 break;
1184 default:
1185 g_printerr ("ERROR : Unknown Obj record 0x%x len 0x%x dll %d;\n",
1186 record_type, len, data_len_left);
1189 if (data_len_left < len+4)
1190 g_printerr ("record len %d (0x%x) > %d\n", len+4, len+4, data_len_left);
1192 /* FIXME : We need a structure akin to the escher code to do this properly */
1193 for (data_len_left -= len+4; data_len_left < 0; ) {
1194 guint16 peek_op;
1196 g_printerr ("deficit of %d\n", data_len_left);
1198 /* FIXME : what do we expect here ??
1199 * I've seen what seem to be embedded drawings
1200 * but I am not sure what is embedding what.
1202 if (!ms_biff_query_peek_next (q, &peek_op) ||
1203 (peek_op != BIFF_CONTINUE &&
1204 peek_op != BIFF_MS_O_DRAWING &&
1205 peek_op != BIFF_TXO &&
1206 peek_op != BIFF_OBJ)) {
1207 g_printerr ("0x%x vs 0x%x\n", q->opcode, peek_op);
1208 return TRUE;
1211 ms_biff_query_next (q);
1212 data_len_left += q->length;
1213 g_printerr ("merged in 0x%x with len %d\n", q->opcode, q->length);
1215 data = q->data + q->length - data_len_left;
1218 /* The ftEnd record should have been the last */
1219 if (data_len_left > 0) {
1220 g_printerr("OBJ : unexpected extra data after Object End record;\n");
1221 gsf_mem_dump (data, data_len_left);
1222 return TRUE;
1225 /* Catch underflow too */
1226 XL_CHECK_CONDITION_VAL (data_len_left == 0, TRUE);
1228 /* FIXME : Throw away the IMDATA that may follow.
1229 * I am not sure when the IMDATA does follow, or how to display it,
1230 * but very careful in case it is not there. */
1231 if (next_biff_record_maybe_imdata) {
1232 guint16 op;
1234 if (ms_biff_query_peek_next (q, &op) && op == BIFF_IMDATA) {
1235 GdkPixbuf *pixbuf;
1237 ms_biff_query_next (q);
1238 pixbuf = excel_read_IMDATA (q, FALSE);
1239 if (pixbuf)
1240 g_object_unref (pixbuf);
1244 return FALSE;
1248 * ms_read_OBJ :
1249 * @q: The biff record to start with.
1250 * @c: The object's container
1251 * @attrs: an optional hash of object attributes.
1253 * This function takes ownership of attrs.
1255 * Returns: %TRUE on success.
1257 gboolean
1258 ms_read_OBJ (BiffQuery *q, MSContainer *c, MSObjAttrBag *attrs)
1260 static char const * const object_type_names[] = {
1261 "Group", /* 0x00 */
1262 "Line", /* 0x01 */
1263 "Rectangle", /* 0x02 */
1264 "Oval", /* 0x03 */
1265 "Arc", /* 0x04 */
1266 "Chart", /* 0x05 */
1267 "TextBox", /* 0x06 */
1268 "Button", /* 0x07 */
1269 "Picture", /* 0x08 */
1270 "Polygon", /* 0x09 */
1271 NULL, /* 0x0A */
1272 "CheckBox", /* 0x0B */
1273 "Option", /* 0x0C */
1274 "Edit", /* 0x0D */
1275 "Label", /* 0x0E */
1276 "Dialog", /* 0x0F */
1277 "Spinner", /* 0x10 */
1278 "Scroll", /* 0x11 */
1279 "List", /* 0x12 */
1280 "Group", /* 0x13 */
1281 "Combo", /* 0x14 */
1282 NULL, NULL, NULL, NULL, /* 0x15 - 0x18 */
1283 "Comment", /* 0x19 */
1284 NULL, NULL, NULL, NULL, /* 0x1A - 0x1D */
1285 "MS Drawing" /* 0x1E */
1288 gboolean errors;
1289 MSObj *obj;
1291 /* no decent docs for this */
1292 if (c->importer->ver <= MS_BIFF_V4)
1293 return FALSE;
1295 #ifndef NO_DEBUG_EXCEL
1296 if (ms_excel_object_debug > 0)
1297 g_printerr ("{ /* OBJ start */\n");
1298 #endif
1299 obj = ms_obj_new (attrs);
1300 /* When called from escher (@attrs != NULL) use the biff8 variant.
1301 * When embdedded directly in the stream (@attrs == NULL) the OBJ
1302 * record appears to be in the old format. (#546887) */
1303 errors = (NULL != attrs)
1304 ? ms_obj_read_biff8_obj (q, c, obj)
1305 : ms_obj_read_pre_biff8_obj (q, c, obj);
1307 if (errors) {
1308 #ifndef NO_DEBUG_EXCEL
1309 if (ms_excel_object_debug > 0)
1310 g_printerr ("}; /* OBJ error 1 */\n");
1311 #endif
1312 ms_obj_delete (obj);
1313 return TRUE;
1316 obj->excel_type_name = NULL;
1317 if ((size_t)obj->excel_type < G_N_ELEMENTS (object_type_names))
1318 obj->excel_type_name = object_type_names[obj->excel_type];
1319 if (obj->excel_type_name == NULL)
1320 obj->excel_type_name = "Unknown";
1322 #ifndef NO_DEBUG_EXCEL
1323 if (ms_excel_object_debug > 0) {
1324 g_printerr ("Object (%d) is a '%s'\n", obj->id, obj->excel_type_name);
1325 g_printerr ("}; /* OBJ end */\n");
1327 #endif
1329 if (c->vtbl->create_obj != NULL)
1330 obj->gnum_obj = c->vtbl->create_obj (c, obj);
1332 /* Chart, There should be a BOF next */
1333 if (obj->excel_type == 0x5) {
1334 if (ms_excel_chart_read_BOF (q, c, obj->gnum_obj)) {
1335 ms_obj_delete (obj);
1336 return TRUE;
1340 ms_container_add_obj (c, obj);
1342 return FALSE;
1345 /**********************************************************************/
1347 void
1348 ms_objv8_write_common (BiffPut *bp, int id, int type, guint16 flags)
1350 guint8 buf[22];
1351 GSF_LE_SET_GUINT16 (buf + 0, 0x15); /* common record */
1352 GSF_LE_SET_GUINT16 (buf + 2, 0x12); /* len 0x12 */
1353 GSF_LE_SET_GUINT16 (buf + 4, type);
1354 GSF_LE_SET_GUINT16 (buf + 6, id);
1355 GSF_LE_SET_GUINT16 (buf + 8, flags);
1357 GSF_LE_SET_GUINT32 (buf + 10, 0);
1358 /* docs say 0, but n the wild this is some sort of pointer ?*/
1359 GSF_LE_SET_GUINT32 (buf + 14, 0);
1360 GSF_LE_SET_GUINT32 (buf + 18, 0);
1361 ms_biff_put_var_write (bp, buf, sizeof buf);
1364 void
1365 ms_objv8_write_scrollbar_old (BiffPut *bp)
1367 /* no docs, but some guesses. See above */
1368 static guint8 const data[] = {
1369 0x0c, 0,
1370 0x14, 0,
1371 0, 0,
1372 0, 0,
1373 0, 0, /* value */
1374 0, 0, /* min */
1375 0x64, 0,/* max */
1376 1, 0, /* increment */
1377 0xa, 0, /* page */
1378 0, 0,
1379 0x10, 0,
1380 1, 0
1382 ms_biff_put_var_write (bp, data, sizeof data);
1385 void
1386 ms_objv8_write_listbox (BiffPut *bp, guint8 lct, gboolean filtered)
1388 static guint8 const data[] = {
1389 0x13, 0, // GR_LISTBOX_DATA
1390 0xee, 0x1f, /* totally contradicts docs, see above */
1391 0, 0, // Formula?
1392 3, 0, // # lines
1393 0, 0, // Nothing selected
1394 1, // Flags (fUseCB)
1395 6, // lct
1396 0, 0, // iEdit
1397 2, 0, 8, 0, 0x40, 0, 0, 0, 0, 0 // LbsDropData
1399 guint8 buf[sizeof data];
1400 memcpy (buf, data, sizeof data);
1401 if (filtered)
1402 GSF_LE_SET_GUINT16 (buf + 14, 0xa);
1403 GSF_LE_SET_GUINT8 (buf + 11, lct);
1404 ms_biff_put_var_write (bp, buf, sizeof data);
1407 void
1408 ms_objv8_write_note (BiffPut *bp)
1410 static guint8 const data[] = {
1411 0x0d, 0, /* Note */
1412 0x16, 0, /* length 0x16 */
1413 #if 0
1414 /* no idea, and no docs */
1415 54 80 79 64 08 0a 77 4f b3 d2 6b 26 88 2a 22 1a 00 00 10 00 00 00
1416 46 2d 5a 01 10 5c e7 46 9b 97 e2 7e 49 7f 08 b8 00 00 bf 00 08 00
1417 #endif
1418 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1420 guint8 buf[sizeof data];
1422 memcpy (buf, data, sizeof data);
1423 ms_biff_put_var_write (bp, buf, sizeof data);
1426 static void
1427 ms_objv8_write_checkbox_data (BiffPut *bp, gboolean active)
1429 char cboxdata[12];
1431 GSF_LE_SET_GUINT16 (cboxdata, GR_CHECKBOX_DATA);
1432 GSF_LE_SET_GUINT16 (cboxdata + 2, sizeof (cboxdata) - 4);
1433 GSF_LE_SET_GUINT16 (cboxdata + 4, active);
1434 GSF_LE_SET_GUINT16 (cboxdata + 6, 0); /* accel */
1435 GSF_LE_SET_GUINT16 (cboxdata + 8, 0); /* reserved */
1436 GSF_LE_SET_GUINT16 (cboxdata + 10, 2); /* 3d display. */
1437 ms_biff_put_var_write (bp, cboxdata, sizeof cboxdata);
1440 static void
1441 ms_objv8_write_checkbox_link (BiffPut *bp, gboolean active)
1443 char data[16];
1445 GSF_LE_SET_GUINT16 (data, GR_CHECKBOX_LINK);
1446 GSF_LE_SET_GUINT16 (data + 2, sizeof (data) - 4);
1447 GSF_LE_SET_GUINT16 (data + 4, active); /* ? */
1448 GSF_LE_SET_GUINT16 (data + 6, 0x12b0); /* ? */
1449 GSF_LE_SET_GUINT16 (data + 8, 0x01ce); /* ? */
1450 GSF_LE_SET_GUINT16 (data + 10, 0);
1451 GSF_LE_SET_GUINT16 (data + 12, 0);
1452 GSF_LE_SET_GUINT16 (data + 14, 2); /* style? */
1453 ms_biff_put_var_write (bp, data, sizeof data);
1456 static void
1457 ms_objv8_write_link_fmla (BiffPut *bp, guint16 typ,
1458 ExcelWriteSheet *esheet,
1459 GnmExprTop const *texpr)
1461 char hfmla[10];
1462 unsigned pos, end_pos;
1463 guint16 fmla_len;
1465 pos = bp->curpos;
1466 GSF_LE_SET_GUINT16 (hfmla, typ);
1467 GSF_LE_SET_GUINT16 (hfmla + 2, 0); /* record len */
1468 GSF_LE_SET_GUINT16 (hfmla + 4, 0); /* formula len */
1469 GSF_LE_SET_GUINT32 (hfmla + 6, 0); /* calcid? */
1470 ms_biff_put_var_write (bp, hfmla, sizeof hfmla);
1471 fmla_len = excel_write_formula (esheet->ewb,
1472 texpr,
1473 esheet->gnum_sheet, 0, 0,
1474 EXCEL_CALLED_FROM_OBJ);
1475 if (fmla_len & 1)
1476 ms_biff_put_var_write (bp, "", 1);
1477 end_pos = bp->curpos;
1478 ms_biff_put_var_seekto (bp, pos);
1479 GSF_LE_SET_GUINT16 (hfmla + 2, (fmla_len + 7) & ~1);
1480 GSF_LE_SET_GUINT16 (hfmla + 4, fmla_len);
1481 ms_biff_put_var_write (bp, hfmla, sizeof hfmla);
1482 ms_biff_put_var_seekto (bp, end_pos);
1485 static void
1486 ms_objv8_write_macro_fmla (BiffPut *bp,
1487 ExcelWriteSheet *esheet,
1488 GnmExprTop const *texpr)
1490 char hfmla[10];
1491 unsigned pos, end_pos;
1492 guint16 fmla_len;
1494 pos = bp->curpos;
1495 GSF_LE_SET_GUINT16 (hfmla, GR_MACRO);
1496 GSF_LE_SET_GUINT16 (hfmla + 2, 0); /* record len */
1497 GSF_LE_SET_GUINT16 (hfmla + 4, 0); /* formula len */
1498 GSF_LE_SET_GUINT32 (hfmla + 6, 0); /* calcid? */
1499 ms_biff_put_var_write (bp, hfmla, sizeof hfmla);
1500 fmla_len = excel_write_formula (esheet->ewb,
1501 texpr,
1502 esheet->gnum_sheet, 0, 0,
1503 EXCEL_CALLED_FROM_OBJ);
1504 if (fmla_len & 1)
1505 ms_biff_put_var_write (bp, "", 1);
1506 end_pos = bp->curpos;
1507 ms_biff_put_var_seekto (bp, pos);
1508 GSF_LE_SET_GUINT16 (hfmla + 2, (fmla_len + 7) & ~1);
1509 GSF_LE_SET_GUINT16 (hfmla + 4, fmla_len);
1510 ms_biff_put_var_write (bp, hfmla, sizeof hfmla);
1511 ms_biff_put_var_seekto (bp, end_pos);
1514 static void
1515 ms_objv8_write_macro_ref (BiffPut *bp,
1516 ExcelWriteSheet *esheet,
1517 GnmNamedExpr *macro_nexpr)
1519 GnmExprTop const *texpr =
1520 gnm_expr_top_new
1521 (gnm_expr_new_name (macro_nexpr,
1522 esheet->gnum_sheet,
1523 NULL));
1524 ms_objv8_write_macro_fmla (bp, esheet, texpr);
1525 gnm_expr_top_unref (texpr);
1528 void
1529 ms_objv8_write_checkbox (BiffPut *bp,
1530 gboolean active,
1531 ExcelWriteSheet *esheet,
1532 GnmExprTop const *link_texpr,
1533 GnmNamedExpr *macro_nexpr)
1535 ms_objv8_write_checkbox_link (bp, active);
1536 if (link_texpr)
1537 ms_objv8_write_link_fmla (bp, GR_CHECKBOX_FORMULA,
1538 esheet, link_texpr);
1539 if (0 && macro_nexpr)
1540 ms_objv8_write_macro_ref (bp, esheet, macro_nexpr);
1541 ms_objv8_write_checkbox_data (bp, active);
1544 static void
1545 ms_objv8_write_radiobutton_rec (BiffPut *bp)
1547 char rb[10];
1549 GSF_LE_SET_GUINT16 (rb, GR_RADIO_BUTTON);
1550 GSF_LE_SET_GUINT16 (rb + 2, sizeof (rb) - 4);
1551 GSF_LE_SET_GUINT32 (rb + 4, 0); /* ignore */
1552 GSF_LE_SET_GUINT16 (rb + 8, 0); /* ignore */
1553 ms_biff_put_var_write (bp, rb, sizeof rb);
1556 static void
1557 ms_objv8_write_radiobutton_data (BiffPut *bp, guint16 nobj, gboolean first)
1559 char rb[8];
1561 GSF_LE_SET_GUINT16 (rb, GR_RADIO_BUTTON_DATA);
1562 GSF_LE_SET_GUINT16 (rb + 2, sizeof (rb) - 4);
1563 GSF_LE_SET_GUINT16 (rb + 4, nobj);
1564 GSF_LE_SET_GUINT16 (rb + 6, !!first);
1565 ms_biff_put_var_write (bp, rb, sizeof rb);
1568 void
1569 ms_objv8_write_radiobutton (BiffPut *bp,
1570 gboolean active,
1571 ExcelWriteSheet *esheet,
1572 GnmExprTop const *link_texpr,
1573 GnmNamedExpr *macro_nexpr)
1575 ms_objv8_write_checkbox_link (bp, active);
1576 ms_objv8_write_radiobutton_rec (bp);
1577 if (link_texpr)
1578 ms_objv8_write_link_fmla (bp, GR_CHECKBOX_FORMULA,
1579 esheet, link_texpr);
1580 if (0 && macro_nexpr)
1581 ms_objv8_write_macro_ref (bp, esheet, macro_nexpr);
1582 ms_objv8_write_checkbox_data (bp, active);
1583 ms_objv8_write_radiobutton_data (bp, 0, TRUE);
1586 static void
1587 ms_objv8_write_adjustment (BiffPut *bp,
1588 GtkAdjustment *adj, gboolean horiz)
1590 char data[24];
1592 GSF_LE_SET_GUINT16 (data, GR_SCROLLBAR);
1593 GSF_LE_SET_GUINT16 (data + 2, sizeof (data) - 4);
1594 GSF_LE_SET_GUINT32 (data + 4, 0); /* Unused */
1595 #define SQUEEZE(f) ((guint16)CLAMP(f, -32768, 32767))
1596 GSF_LE_SET_GUINT16 (data + 8, SQUEEZE (gtk_adjustment_get_value (adj)));
1597 GSF_LE_SET_GINT16 (data + 10, SQUEEZE (gtk_adjustment_get_lower (adj)));
1598 GSF_LE_SET_GINT16 (data + 12, SQUEEZE (gtk_adjustment_get_upper (adj) + gtk_adjustment_get_step_increment (adj)));
1599 GSF_LE_SET_GINT16 (data + 14, SQUEEZE (gtk_adjustment_get_step_increment (adj)));
1600 GSF_LE_SET_GINT16 (data + 16, SQUEEZE (gtk_adjustment_get_page_increment (adj)));
1601 #undef SQUEEZE
1602 GSF_LE_SET_GINT16 (data + 18, !!horiz);
1603 GSF_LE_SET_GINT16 (data + 20, 15); /* widget in pixels */
1604 GSF_LE_SET_GINT16 (data + 22, 0x0001); /* draw */
1606 ms_biff_put_var_write (bp, data, sizeof data);
1609 void
1610 ms_objv8_write_spinbutton (BiffPut *bp,
1611 ExcelWriteSheet *esheet,
1612 GtkAdjustment *adj, gboolean horiz,
1613 GnmExprTop const *link_texpr,
1614 GnmNamedExpr *macro_nexpr)
1616 ms_objv8_write_adjustment (bp, adj, horiz);
1617 if (link_texpr)
1618 ms_objv8_write_link_fmla (bp, GR_SCROLLBAR_FORMULA,
1619 esheet, link_texpr);
1620 if (0 && macro_nexpr)
1621 ms_objv8_write_macro_ref (bp, esheet, macro_nexpr);
1624 void
1625 ms_objv8_write_scrollbar (BiffPut *bp,
1626 ExcelWriteSheet *esheet,
1627 GtkAdjustment *adj, gboolean horiz,
1628 GnmExprTop const *link_texpr,
1629 GnmNamedExpr *macro_nexpr)
1631 ms_objv8_write_adjustment (bp, adj, horiz);
1632 if (link_texpr)
1633 ms_objv8_write_link_fmla (bp, GR_SCROLLBAR_FORMULA,
1634 esheet, link_texpr);
1635 if (0 && macro_nexpr)
1636 ms_objv8_write_macro_ref (bp, esheet, macro_nexpr);
1639 static void
1640 ms_objv8_write_list_data (BiffPut *bp,
1641 ExcelWriteSheet *esheet,
1642 GnmExprTop const *texpr,
1643 guint16 N, guint16 selected)
1645 char hfmla[12];
1646 char data[8];
1647 unsigned pos, end_pos;
1648 guint16 fmla_len;
1649 guint16 ui;
1650 guint16 style = 0;
1651 guint8 *selection;
1653 pos = bp->curpos;
1654 GSF_LE_SET_GUINT16 (hfmla, GR_LISTBOX_DATA);
1655 GSF_LE_SET_GUINT16 (hfmla + 2, 0x1fcc); /* ??? */
1656 GSF_LE_SET_GUINT16 (hfmla + 4, 0); /* record len */
1657 GSF_LE_SET_GUINT16 (hfmla + 6, 0); /* formula len */
1658 GSF_LE_SET_GUINT32 (hfmla + 8, 0); /* calcid? */
1659 ms_biff_put_var_write (bp, hfmla, sizeof hfmla);
1660 if (texpr) {
1661 fmla_len = excel_write_formula (esheet->ewb,
1662 texpr,
1663 esheet->gnum_sheet, 0, 0,
1664 EXCEL_CALLED_FROM_OBJ);
1665 if (fmla_len & 1)
1666 ms_biff_put_var_write (bp, "", 1);
1667 GSF_LE_SET_GUINT16 (hfmla + 6, fmla_len);
1668 } else {
1669 /* Needs testing. */
1670 ms_biff_put_var_write (bp, "\0", 2);
1671 fmla_len = 0;
1674 end_pos = bp->curpos;
1675 ms_biff_put_var_seekto (bp, pos);
1676 GSF_LE_SET_GUINT16 (hfmla + 4, (fmla_len + 7) & ~1);
1677 ms_biff_put_var_write (bp, hfmla, sizeof hfmla);
1678 ms_biff_put_var_seekto (bp, end_pos);
1680 selection = g_new0 (char, N);
1681 for (ui = 0; ui < N; ui++)
1682 selection[ui] = (ui + 1 == selected);
1684 GSF_LE_SET_GUINT16 (data, N);
1685 GSF_LE_SET_GUINT16 (data + 2, selected); /* iSel */
1686 GSF_LE_SET_GUINT16 (data + 4, style);
1687 GSF_LE_SET_GUINT16 (data + 6, 0); /* edit object id */
1688 ms_biff_put_var_write (bp, data, sizeof data);
1689 ms_biff_put_var_write (bp, selection, N);
1691 g_free (selection);
1694 void
1695 ms_objv8_write_list (BiffPut *bp,
1696 ExcelWriteSheet *esheet,
1697 GtkAdjustment *adj,
1698 GnmExprTop const *res_texpr,
1699 GnmExprTop const *data_texpr,
1700 GnmNamedExpr *macro_nexpr)
1702 ms_objv8_write_adjustment (bp, adj, FALSE);
1703 if (res_texpr)
1704 ms_objv8_write_link_fmla (bp, GR_SCROLLBAR_FORMULA,
1705 esheet, res_texpr);
1706 if (0 && macro_nexpr)
1707 ms_objv8_write_macro_ref (bp, esheet, macro_nexpr);
1708 ms_objv8_write_list_data (bp, esheet, data_texpr,
1709 (guint16)gtk_adjustment_get_upper (adj) - 1,
1710 (guint16)gtk_adjustment_get_value (adj));
1713 void
1714 ms_objv8_write_button (BiffPut *bp,
1715 ExcelWriteSheet *esheet,
1716 GnmNamedExpr *macro_nexpr)
1718 if (0 && macro_nexpr)
1719 ms_objv8_write_macro_ref (bp, esheet, macro_nexpr);