📄 dw_page.c
字号:
* * + do request_repaint for added stuff. But what level of * granularity? reinstate the update_begin/update_end cycle? Do at * line granularity when making a new line (but risk paint * inconsistency)? Do at word granularity but take on region overhead * on every word (optimize away if outside visible rectangle)? * * + better formatting, including justification & floats * *//* * Returns the x offset (the indentation plus any offset needed for * centering or right justification) for the line. The offset returned * is relative to the page (i.e. add allocation.x0 to get the dw * toplevel coordinate. */static gint Dw_page_line_x_offset(DwPage * page, DwPageLine * line){ gint x; if (line->first) x = page->words[line->first_word].style->left_indent_first; else x = page->words[line->first_word].style->left_indent_rest; return x;}/* * ? */static void Dw_page_add_line(DwPage * page){ gint num_lines; DwPageLine *line; num_lines = page->num_lines; a_List_add(page->lines, num_lines, sizeof(*page->lines), page->num_lines_max); line = &page->lines[num_lines]; line->first_word = page->current_word; line->last_word = page->current_word; if (num_lines == 0) { line->y_top = 0; } else { line->y_top = page->lines[num_lines - 1].y_top + page->lines[num_lines - 1].y_ascent + page->lines[num_lines - 1].y_descent + page->lines[num_lines - 1].y_space; } line->x_size = 0; line->y_ascent = 6; line->y_descent = 3; line->y_space = 0; line->hard = FALSE; line->first = (num_lines == 0 || (page->lines[num_lines - 1].hard && page->lines[num_lines - 1].y_space > 0)); page->num_lines++;}/* * Allocate a new word in a page structure. This routine is where word * wrapping happens. The newly allocated word has the x_size, x_space, * y_ascent, and y_descent fields filled in, but no others. */static DwPageWord *Dw_page_new_word(DwPage * page, gint x_size, gint y_ascent, gint y_descent, DwStyle * style){ DwPageLine *line; DwPageWord *word; DwStyle *style2; gboolean existing; gboolean new_line; gint width, set_width, nw, nl; line = NULL; word = NULL; new_line = FALSE; existing = FALSE; /* Is it an existing word? */ if (x_size == -1) existing = TRUE; if (existing) { word = &page->words[page->current_word]; style2 = word->style; x_size = word->x_size; y_ascent = word->y_ascent; y_descent = word->y_descent; } else { style2 = style; } nl = page->num_lines; if (nl == 0) { new_line = TRUE; } else { line = &page->lines[nl - 1]; if (line->hard) { new_line = TRUE; } else { /* Calculate width of new line and see if it exceeds page width. */ nw = line->last_word; if (nw > line->first_word) { set_width = page->avail_width - style2->right_indent; if (line->first) set_width -= style2->left_indent_first; else set_width -= style2->left_indent_rest; width = line->x_size + page->words[nw - 1].x_space + x_size; if (width > set_width) new_line = TRUE; } } if (new_line) page->real_width = MAX(page->real_width, line->x_size + Dw_page_line_x_offset(page, line)); } if (new_line) { Dw_page_add_line(page); line = &page->lines[nl]; } nw = line->last_word; /* Add a new word */ if (!existing) { a_List_add(page->words, nw, sizeof(*page->words), page->num_words_max); word = &page->words[nw]; word->x_size = x_size; word->x_space = 0; word->y_ascent = y_ascent; word->y_descent = y_descent; /* Just in case the page only has one word */ if (page->num_words == 0) page->real_width = x_size; page->num_words++; } /* Apply the attributes to the line */ if (nw - line->first_word) line->x_size += page->words[nw - 1].x_space; line->x_size += x_size; line->y_ascent = MAX(line->y_ascent, y_ascent); line->y_descent = MAX(line->y_descent, y_descent); /* update the width requisitions */ if (nw == line->first_word && line->first) page->last_line_max_width = style2->left_indent_first; else if (nw == line->first_word) page->last_line_max_width += page->words[line->first_word].x_space; else page->last_line_max_width += page->words[nw - 1].x_space; page->last_line_max_width += x_size; page->current_word++; line->last_word++; return word;}/* Performance is not as good as it could be - it could only rewrap if * it needs to (should be a cheap test: the line is ok if it fits * within the page->width and either it's hard or the width plus the * width of the first word on the next line (plus the spacing of the * last word) exceed the page->width). This would save on memory * allocation too. * * But hey, it works! */static void Dw_page_calc_widget_size(DwPage * page, DwPageWord * word){ DwRequisition requisition; gint req_height, alloc_height; a_Dw_widget_size_request(word->content.widget.widget, &requisition); if (word->content.widget.rel_width == -1) word->x_size = requisition.width; else word->x_size = word->content.widget.rel_width * page->avail_width; if (word->content.widget.rel_height == -1) { word->y_ascent = requisition.ascent; word->y_descent = requisition.descent; } else { /* todo: re-think about it */ req_height = requisition.ascent + requisition.descent; if (req_height) { alloc_height = word->content.widget.rel_height * (page->avail_ascent + page->avail_descent); word->y_ascent = requisition.ascent * alloc_height / req_height; word->y_descent = requisition.descent * alloc_height / req_height; } else { word->y_ascent = 0; word->y_descent = 0; } }}/* * Rewrap the page. * There are basically two times we'll want to do this: * either when the viewport is resized, or when the size changes on one * of the child widgets. */static void Dw_page_rewrap(DwPage * page){ DwPageLine *old_lines; DwWidget *widget; gint old_num_lines; gint line_index, word_index; gint nl; DwPageLine *old_line; DwPageWord *word; widget = DW_WIDGET(page); old_lines = page->lines; old_num_lines = page->num_lines; //g_print(">>> Dw_page_rewrap (%d x %d x %d)\n", //page->avail_width, page->avail_ascent, page->avail_descent); /* Initialize the lines structure of the page. */ page->num_lines = 0; page->num_lines_max = 16; page->lines = g_new(DwPageLine, page->num_lines_max); page->current_word = 0; page->real_width = 0; /* Iterate over the old lines, adding the words to the page structure. */ for (line_index = 0; line_index < old_num_lines; line_index++) { old_line = &(old_lines[line_index]); /* Here, we're actually relying on the invariant that a soft line * is never followed by an empty hard line. */ if (old_line->last_word == old_line->first_word && old_line->hard) { Dw_page_add_line(page); } for (word_index = old_line->first_word; word_index < old_line->last_word; word_index++) { word = &page->words[word_index]; if (word->content_type == DW_PAGE_CONTENT_WIDGET) Dw_page_calc_widget_size(page, word); Dw_page_new_word(page, -1, -1, -1, NULL); if (word->content_type == DW_PAGE_CONTENT_ANCHOR) { Dw_widget_set_anchor(widget, word->content.anchor, page->lines[page->num_lines - 1].y_top); } } if (old_line->hard) { a_Dw_page_linebreak(page); page->lines[page->num_lines - 1].y_space = old_line->y_space; } } g_free(old_lines); /* The last line hasn't been compared to real_width yet */ nl = page->num_lines; if (nl > 0) page->real_width = MAX(page->real_width, page->lines[nl - 1].x_size + Dw_page_line_x_offset(page, &(page->lines[nl - 1])));}/* * Paint a line * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!) * - area is used always (ev. set it to event->area) * - event is only used when is_expose */static void Dw_page_draw_line(DwPage * page, DwPageLine * line, DwRectangle * area, GdkEventExpose * event){ DwWidget *widget; DwPageWord *word; gint word_index; gint x_cursor, y_cursor; gint diff; GdkColormap *colormap; gint uline_width; DwWidget *child; DwRectangle child_area; GdkWindow *window; /* Here's an idea on how to optimize this routine to minimize the number * of calls to gdk_draw_string: * * Copy the text from the words into a buffer, adding a new word * only if: the attributes match, and the spacing is either zero or * equal to the width of ' '. In the latter case, copy a " " into * the buffer. Then draw the buffer. */ widget = DW_WIDGET(page); window = widget->window; colormap = gtk_widget_get_colormap(widget->viewport); x_cursor = Dw_widget_x_world_to_viewport(widget, widget->allocation.x + Dw_page_line_x_offset(page, line)); y_cursor = Dw_widget_y_world_to_viewport(widget, widget->allocation.y + line->y_top + line->y_ascent); for (word_index = line->first_word; word_index < line->last_word; word_index++) { word = &page->words[word_index]; diff = 0; switch (word->content_type) { case DW_PAGE_CONTENT_TEXT: /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed */ if (word->style->SubSup == TEXT_SUB) diff = word->y_ascent / 2; else if (word->style->SubSup == TEXT_SUP) diff -= word->y_ascent / 3; gdk_draw_string(window, word->style->font->font, word->style->color->gc, x_cursor, y_cursor + diff, word->content.text); if (word->style->uline >= 0) { uline_width = word->x_size; if (word_index + 1 < line->last_word && word->style->uline == page->words[word_index + 1].style->uline) uline_width += word->x_space; gdk_draw_line(window, word->style->color->gc, x_cursor, y_cursor + 1 + diff, x_cursor + uline_width - 1, y_cursor + 1 + diff); } if (word->style->strike >= 0) { uline_width = word->x_size; if (word_index + 1 < line->last_word && word->style->strike == page->words[word_index + 1].style->strike) uline_width += word->x_space; gdk_draw_line(window, word->style->color->gc, x_cursor, y_cursor - word->y_ascent / 2 + diff, x_cursor + uline_width - 1, y_cursor - word->y_ascent / 2 + diff); } break; case DW_PAGE_CONTENT_WIDGET: child = word->content.widget.widget; if (Dw_widget_intersect(child, area, &child_area)) { //g_print ("Drawing widget %p\n", child); a_Dw_widget_draw(child, &child_area, event); } break; case DW_PAGE_CONTENT_ANCHOR: /* nothing - an anchor isn't seen */ /* BUG: sometimes anchors have x_space; * we subtract that just in case --EG */ x_cursor -= word->x_size + word->x_space; break; } x_cursor += word->x_size + word->x_space; }}/* * Find the first line index that includes y, relative to top of widget. */static gint Dw_page_find_line_index(DwPage * page, gint y){ gint max_index = page->num_lines - 1; gint step, index, low = 0; step = (page->num_lines + 1) >> 1; while (step > 1) { index = low + step; if (index <= max_index && page->lines[index].y_top < y) low = index; step = (step + 1) >> 1; } if (low < max_index && page->lines[low + 1].y_top < y) low++; /* This new routine returns the line number between * (y_top) and (y_top + y_ascent + y_descent + y_space) : * the space _below_ the line is considered part of the line. * Old routine returned line number between * (y_top - previous_line->y_space) and (y_top + y_ascent + y_descent) : * the space _above_ the line was considered part of the line. * This is important for Dw_page_find_link() --EG */ return low;}/* * Draw the actual lines, starting at (x, y) in toplevel Dw coords. * (former Dw_page_expose_lines) */static void Dw_page_draw(DwWidget * widget, DwRectangle * area, GdkEventExpose * event){ DwPage *page; gint line_index; DwPageLine *line; //gint x1; page = DW_PAGE(widget); line_index = Dw_page_find_line_index(page, area->y); for (; line_index < page->num_lines; line_index++) { line = &(page->lines[line_index]); if (line->y_top >= area->y + area->height) break; Dw_page_draw_line(page, line, area, event); }}/* * Find a link given a coordinate location relative to the window */static gint Dw_page_find_link(DwPage * page, gint x, gint y){ gint line_index, word_index; gint x_cursor, last_x_cursor; gint x1, y1; /* coordinates relative to page */ gint x0, y0; /* coordinates relative to word */ DwPageLine *line; DwPageWord *word; DwPageShape *shape; int dx, dy; int num_shape; guint map; /* x1 = x - DW_WIDGET(page)->allocation.x; * y1 = y - DW_WIDGET(page)->allocation.y; */ x1 = x; y1 = y; if ((line_index = Dw_page_find_line_index(page, y1)) >= page->num_lines) return -1; line = &page->lines[line_index]; if (line->y_top + line->y_ascent + line->y_descent <= y1) return -1; x0 = x1; y0 = y1 - line->y_top; page->x_click = -1; x_cursor = Dw_page_line_x_offset(page, line); for (word_index = line->first_word; word_index < line->last_word; word_index++) { word = &page->words[word_index]; last_x_cursor = x_cursor; x_cursor += word->x_size + word->x_space; if (last_x_cursor <= x1 && x_cursor > x1) { if (word->style->link >= 0) { /* todo: DW_STYLE_HAS_MAP will move elsewhere */ if (word->style->flags & DW_STYLE_HAS_MAP) { page->x_click = x0; page->y_click = y0; } else page->x_click = -1; return word->style->link; } else if (word->style->flags & DW_STYLE_HAS_MAP) { map = word->style->map; for (num_shape = 0; num_shape < page->num_shapes; num_shape++) { shape = &(page->shapes[num_shape]); if (shape->map == map) { if (shape->type == DW_PAGE_SHAPE_CIRCLE) { dx = shape->data.circle.x - x0; dy = shape->data.circle.y - y0; if (shape->data.circle.r2 >= (dx * dx + dy * dy)) return shape->link; } else if (shape->type == DW_PAGE_SHAPE_RECT) { if (x0 > shape->data.rect.left && x0 < shape->data.rect.right && y0 > shape->data.rect.top && y0 < shape->data.rect.bottom) return shape->link; } else if (shape->type == DW_PAGE_SHAPE_POLY) { if (gdk_region_point_in(shape->data.poly, x0, y0))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -