static void html_read_row (htmlNodePtr cur, htmlDocPtr doc, GnmHtmlTableCtxt *tc) { htmlNodePtr ptr; int col = -1; for (ptr = cur->children; ptr != NULL ; ptr = ptr->next) { if (xmlStrEqual (ptr->name, CC2XML ("td")) || xmlStrEqual (ptr->name, CC2XML ("th"))) { GString *buf; xmlBufferPtr a_buf; xmlAttrPtr props; int colspan = 1; int rowspan = 1; GnmCellPos pos; GnmStyle *mstyle; GSList *hrefs = NULL; GnmHLink *lnk = NULL; /* Check whether we need to skip merges from above */ pos.row = tc->row; pos.col = col + 1; while (gnm_sheet_merge_contains_pos (tc->sheet, &pos)) { col++; pos.col++; } /* Do we span across multiple rows or cols? */ props = ptr->properties; while (props) { if (xmlStrEqual (props->name, CC2XML ("colspan")) && props->children) colspan = atoi (CXML2C (props->children->content)); if (xmlStrEqual (props->name, CC2XML ("rowspan")) && props->children) rowspan = atoi (CXML2C (props->children->content)); props = props->next; } if (colspan < 1) colspan = 1; if (rowspan < 1) rowspan = 1; /* Let's figure out the content of the cell */ buf = g_string_new (NULL); a_buf = xmlBufferCreate (); mstyle = gnm_style_new_default (); if (xmlStrEqual (ptr->name, CC2XML ("th"))) gnm_style_set_font_bold (mstyle, TRUE); html_read_content (ptr, buf, mstyle, a_buf, &hrefs, TRUE, doc, tc); if (g_slist_length (hrefs) >= 1 && buf->len > 0) { /* One hyperlink, and text to make it * visible */ char *url; xmlBufferPtr h_buf = xmlBufferCreate (); hrefs = g_slist_reverse (hrefs); htmlNodeDump ( h_buf, doc, (htmlNodePtr)hrefs->data); url = g_strndup ( CXML2C (h_buf->content), h_buf->use); if (strncmp (url, "mailto:", strlen ("mailto:")) == 0) lnk = gnm_hlink_new ( gnm_hlink_email_get_type (), tc->sheet); else lnk = gnm_hlink_new ( gnm_hlink_url_get_type (), tc->sheet); gnm_hlink_set_target (lnk, url); gnm_style_set_hlink (mstyle, lnk); gnm_style_set_font_uline (mstyle, UNDERLINE_SINGLE); gnm_style_set_font_color (mstyle, gnm_color_new_go (GO_COLOR_BLUE)); g_free (url); xmlBufferFree (h_buf); } if (g_slist_length (hrefs) > 1 || buf->len <= 0) { /* Multiple links, * or no text to give hyperlink style, * so put them in a comment */ GSList *l; for (l = hrefs; l != NULL; l = l->next) { htmlNodeDump (a_buf, doc, (htmlNodePtr)l->data); xmlBufferAdd (a_buf, CC2XML ("\n"), -1); } } g_slist_free (hrefs); if (buf->len > 0) { GnmCell *cell = sheet_cell_fetch (tc->sheet, col + 1, tc->row); sheet_style_set_pos (tc->sheet, col + 1, tc->row, mstyle); gnm_cell_set_text (cell, buf->str); } else gnm_style_unref (mstyle); if (a_buf->use > 0) { char *name; name = g_strndup (CXML2C (a_buf->content), a_buf->use); cell_set_comment (tc->sheet, &pos, NULL, name, NULL); g_free (name); } g_string_free (buf, TRUE); xmlBufferFree (a_buf); /* If necessary create the merge */ if (colspan > 1 || rowspan > 1) { GnmRange range; GnmRange *r = ⦥ range_init (r, col + 1, tc->row, col + colspan, tc->row + rowspan - 1); gnm_sheet_merge_add (tc->sheet, r, FALSE, NULL); } col += colspan; } } }
/** * clipboard_paste_region: * @cr: The GnmCellRegion to paste. * @pt: Where to paste the values. * @cc: The context for error handling. * * Pastes the supplied GnmCellRegion (@cr) into the supplied * GnmPasteTarget (@pt). This operation is not undoable. It does not auto grow * the destination if the target is a singleton. This is a simple interface to * paste a region. * * returns : TRUE if there was a problem. **/ gboolean clipboard_paste_region (GnmCellRegion const *cr, GnmPasteTarget const *pt, GOCmdContext *cc) { int repeat_horizontal, repeat_vertical, clearFlags; int dst_cols, dst_rows, src_cols, src_rows; int i, j; GSList *ptr; GnmRange const *r; gboolean has_contents, adjust_merges = TRUE; struct paste_cell_data dat; GnmRange const *merge_src; g_return_val_if_fail (pt != NULL, TRUE); g_return_val_if_fail (cr != NULL, TRUE); /* we do not need any of this fancy stuff when pasting a simple object */ if (cr->cell_content == NULL && cr->styles == NULL && cr->merged == NULL && cr->objects != NULL) { if (pt->paste_flags & (PASTE_COMMENTS | PASTE_OBJECTS)) for (ptr = cr->objects; ptr; ptr = ptr->next) paste_object (pt, ptr->data, pt->range.start.col, pt->range.start.row); return FALSE; } r = &pt->range; dst_cols = range_width (r); dst_rows = range_height (r); src_cols = cr->cols; src_rows = cr->rows; /* If the source is a single cell or a single merge */ /* Treat a target of a single merge specially, don't split the merge */ if ((src_cols == 1 && src_rows == 1) || (g_slist_length (cr->merged) == 1 && (NULL != (merge_src = cr->merged->data)) && range_height (merge_src) == cr->rows && range_width (merge_src) == cr->cols)) { GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start); if (merge != NULL && range_equal (r, merge)) { dst_cols = dst_rows = 1; adjust_merges = FALSE; src_cols = 1; src_rows = 1; } /* Apparently links do not supercede merges */ } else if (pt->paste_flags & PASTE_LINK) adjust_merges = FALSE; has_contents = pt->paste_flags & (PASTE_CONTENTS|PASTE_AS_VALUES|PASTE_LINK); if (pt->paste_flags & PASTE_TRANSPOSE) { int tmp = src_cols; src_cols = src_rows; src_rows = tmp; } if (cr->not_as_contents && (pt->paste_flags & PASTE_CONTENTS)) { go_cmd_context_error_invalid (cc, _("Unable to paste"), _("Contents can only be pasted by value or by link.")); return TRUE; } /* calculate the tiling */ repeat_horizontal = dst_cols/src_cols; if (repeat_horizontal * src_cols != dst_cols) { char *msg = g_strdup_printf ( _("destination does not have an even multiple of source columns (%d vs %d)\n\n" "Try selecting a single cell or an area of the same shape and size."), dst_cols, src_cols); go_cmd_context_error_invalid (cc, _("Unable to paste"), msg); g_free (msg); return TRUE; } repeat_vertical = dst_rows/src_rows; if (repeat_vertical * src_rows != dst_rows) { char *msg = g_strdup_printf ( _("destination does not have an even multiple of source rows (%d vs %d)\n\n" "Try selecting a single cell or an area of the same shape and size."), dst_rows, src_rows); go_cmd_context_error_invalid (cc, _("Unable to paste"), msg); g_free (msg); return TRUE; } if ((pt->range.start.col + dst_cols) > gnm_sheet_get_max_cols (pt->sheet) || (pt->range.start.row + dst_rows) > gnm_sheet_get_max_rows (pt->sheet)) { go_cmd_context_error_invalid (cc, _("Unable to paste"), _("result passes the sheet boundary")); return TRUE; } clearFlags = 0; /* clear the region where we will paste */ if (has_contents) clearFlags = CLEAR_VALUES | CLEAR_NORESPAN; if (pt->paste_flags & PASTE_COMMENTS) clearFlags |= CLEAR_COMMENTS; /* No need to clear the formats. We will paste over top of these. */ /* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */ if (pt->paste_flags & (PASTE_OPER_MASK | PASTE_SKIP_BLANKS)) clearFlags = 0; /* remove merged regions even for operations, or blanks */ if (has_contents && adjust_merges) clearFlags |= CLEAR_MERGES; if (clearFlags != 0) { int const dst_col = pt->range.start.col; int const dst_row = pt->range.start.row; sheet_clear_region (pt->sheet, dst_col, dst_row, dst_col + dst_cols - 1, dst_row + dst_rows - 1, clearFlags, cc); } dat.translate_dates = cr->date_conv && !go_date_conv_equal (cr->date_conv, workbook_date_conv (pt->sheet->workbook)); for (i = 0; i < repeat_horizontal ; i++) for (j = 0; j < repeat_vertical ; j++) { int const left = i * src_cols + pt->range.start.col; int const top = j * src_rows + pt->range.start.row; dat.top_left.col = left; dat.top_left.row = top; dat.rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE; dat.rinfo.origin_sheet = dat.rinfo.target_sheet = pt->sheet; if (pt->paste_flags & PASTE_EXPR_LOCAL_RELOCATE) { dat.rinfo.origin.start = cr->base; dat.rinfo.origin.end.col = cr->base.col + cr->cols - 1; dat.rinfo.origin.end.row = cr->base.row + cr->rows - 1; dat.rinfo.col_offset = left - cr->base.col; dat.rinfo.row_offset = top - cr->base.row; } else { dat.rinfo.origin = pt->range; dat.rinfo.col_offset = 0; dat.rinfo.row_offset = 0; } /* Move the styles on here so we get correct formats before recalc */ if (pt->paste_flags & PASTE_FORMATS) { if (pt->paste_flags & PASTE_TRANSPOSE) sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, (sheet_style_set_list_cb_t) range_transpose, &dat.top_left); else if (pt->paste_flags & PASTE_FLIP_H) { int data = 2 * left + src_cols - 1; sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, (sheet_style_set_list_cb_t) range_flip_h, &data); } else if (pt->paste_flags & PASTE_FLIP_V) { int data = 2 * top + src_rows - 1; sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, (sheet_style_set_list_cb_t) range_flip_v, &data); } else sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, NULL, NULL); } if (has_contents && !(pt->paste_flags & PASTE_DONT_MERGE)) { for (ptr = cr->merged; ptr != NULL ; ptr = ptr->next) { GnmRange tmp = *((GnmRange const *)ptr->data); if (pt->paste_flags & PASTE_TRANSPOSE) { int x; x = tmp.start.col; tmp.start.col = tmp.start.row; tmp.start.row = x; x = tmp.end.col; tmp.end.col = tmp.end.row; tmp.end.row = x; } if (!range_translate (&tmp, pt->sheet, left, top)) gnm_sheet_merge_add (pt->sheet, &tmp, TRUE, cc); } } if (has_contents && (pt->paste_flags & PASTE_LINK)) { paste_link (pt, top, left, cr); continue; } if (has_contents && NULL != cr->cell_content) { dat.pt = pt; dat.cr = cr; g_hash_table_foreach (cr->cell_content, (GHFunc)cb_paste_cell, &dat); } if (pt->paste_flags & (PASTE_COMMENTS | PASTE_OBJECTS)) for (ptr = cr->objects; ptr; ptr = ptr->next) paste_object (pt, ptr->data, left, top); } if (!(pt->paste_flags & PASTE_NO_RECALC)) { if (has_contents) { sheet_region_queue_recalc (pt->sheet, r); sheet_flag_status_update_range (pt->sheet, r); } else sheet_flag_style_update_range (pt->sheet, r); sheet_range_calc_spans (pt->sheet, r, (pt->paste_flags & PASTE_FORMATS) ? GNM_SPANCALC_RE_RENDER : GNM_SPANCALC_RENDER); if (pt->paste_flags & PASTE_UPDATE_ROW_HEIGHT) rows_height_update (pt->sheet, &pt->range, FALSE); sheet_redraw_all (pt->sheet, FALSE); } return FALSE; }