/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "xp.h" #include "layout.h" #include "laylayer.h" #include "libi18n.h" #include "xlate.h" #include "layers.h" #define IL_CLIENT /* XXXM12N Defined by Image Library clients */ #include "libimg.h" /* Image Library public API. */ #include "timing.h" #ifdef PROFILE #pragma profile on #endif #ifdef XP_MAC # ifdef XP_TRACE # undef XP_TRACE # endif # define XP_TRACE(X) #else #ifndef XP_TRACE # define XP_TRACE(X) fprintf X #endif #endif /* XP_MAC */ /* * BUGBUG LAYERS: This is a hack that is currently being used for selection * but might be useful elsewhere. Essentially, for extending the selected * text, we know exactly how much we want to draw (using FE_DisplaySubtext). * Rather than ask for a synchronous refresh of the area and relying on * LO_RefreshArea to do the correct drawing (actually, it can't do the * correct drawing since it doesn't draw sub-strings), we temporarily replace * the painter_func for the layer in question to one that just does the * required drawing. We still do a synchronous refresh, but the temporary * painter_func is called instead of the one that calls LO_RefreshArea. */ typedef struct LO_SelectionHackStruct { MWContext *context; LO_TextStruct *text; int32 start_pos; int32 end_pos; Bool need_bg; } LO_SelectionHackStruct; static void lo_selection_hack_painter_func(void *drawable, CL_Layer *layer, FE_Region update_region) { LO_SelectionHackStruct *hack = (LO_SelectionHackStruct *)CL_GetLayerClientData(layer); FE_SetDrawable(hack->context, drawable); /* For layers, we don't ever need to clear the background */ FE_DisplaySubtext(hack->context, FE_VIEW, hack->text, hack->start_pos, hack->end_pos, FALSE); FE_SetDrawable(hack->context, NULL); } static void lo_NormalizeStartAndEndOfSelection(LO_TextStruct *text, int32 *start, int32 *end) { int32 p1, p2; char *string; int n; int16 charset; if (text->ele_attrmask & LO_ELE_SELECTED) { if (text->sel_start < 0) { p1 = 0; } else { p1 = text->sel_start; } if (text->sel_end < 0) { p2 = text->text_len - 1; } else { p2 = text->sel_end; } PA_LOCK(string, char *, text->text); /* * find beginning of first character */ charset = text->text_attr->charset; switch (n = INTL_NthByteOfChar(charset, string, (int)(p1+1))) { case 0: case 1: break; default: p1 -= (n - 1); if (p1 < 0) { p1 = 0; } break; } if (text->sel_start >= 0) { text->sel_start = p1; } /* * find end of last character */ switch (n = INTL_NthByteOfChar(charset, string, (int)(p2+1))) { case 0: break; default: p2 -= (n - 1); if (p2 < 0) { p2 = 0; } /* fall through */ case 1: p2 += INTL_IsLeadByte(charset, string[p2]); if (p2 > text->text_len - 1) { p2 = text->text_len - 1; } break; } if (text->sel_end >= 0) { text->sel_end = p2; } *start = p1; *end = p2; PA_UNLOCK(text->text); } } #ifdef DEBUG void LO_DUMP_RECT(XP_Rect *rect) { XP_TRACE(("Compositing rectangle: [%3d,%3d] %d x %d\n", rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top)); } #endif /* * BUGBUG LAYERS: Since we know that lo_DisplaySubtext is only done * for selection, we generate a synchronous composite. This might * not be the right thing to do: maybe we should have an additional * argument to the LO_Display routines */ void lo_DisplaySubtext(MWContext *context, LO_TextStruct *text, int32 start_pos, int32 end_pos, Bool need_bg, CL_Layer *sel_layer) { lo_NormalizeStartAndEndOfSelection(text, &start_pos, &end_pos); /* If we have to draw, go through the compositor */ if (context->compositor) { XP_Rect rect; CL_Layer *layer; int32 x_offset, y_offset; if (!sel_layer) layer = CL_FindLayer(context->compositor, LO_BODY_LAYER_NAME); else layer = sel_layer; FE_GetTextFrame(context, text, start_pos, end_pos, &rect); lo_GetLayerXYShift(layer, &x_offset, &y_offset); XP_OffsetRect(&rect, -x_offset, -y_offset); CL_UpdateLayerRect(context->compositor, layer, &rect, PR_FALSE); return; } else /* For layers, we don't ever need to clear the background */ FE_DisplaySubtext(context, FE_VIEW, text, start_pos, end_pos, FALSE); } void lo_DisplayText(MWContext *context, LO_TextStruct *text, Bool need_bg) { int32 p1, p2; LO_TextAttr tmp_attr; LO_TextAttr *hold_attr; TIMING_STOPCLOCK_OBJECT("layout:blank-screen", context, "displaying text"); /* Blinking text elements are placed in a separate layer */ if (context->compositor && text->text_attr->attrmask & LO_ATTR_BLINK) { if (! (text->ele_attrmask & LO_ELE_DRAWN)) { /* XXX - If the BLINK is in a layer, we need to create the blink layer as a child of that layer, not as a child of the _BODY layer. */ CL_Layer *body_layer = CL_FindLayer(context->compositor, LO_BODY_LAYER_NAME); lo_CreateBlinkLayer(context, text, body_layer); text->ele_attrmask |= LO_ELE_DRAWN; } /* All blink text drawing is handled by the blink layer's painter function. Don't do any drawing here. */ return; } if (text->ele_attrmask & LO_ELE_SELECTED) { lo_NormalizeStartAndEndOfSelection(text, &p1, &p2); if (p1 > 0) { /* For layers we don't ever need to clear the background */ FE_DisplaySubtext(context, FE_VIEW, text, 0, (p1 - 1), FALSE); } lo_CopyTextAttr(text->text_attr, &tmp_attr); tmp_attr.fg.red = text->text_attr->bg.red; tmp_attr.fg.green = text->text_attr->bg.green; tmp_attr.fg.blue = text->text_attr->bg.blue; tmp_attr.bg.red = text->text_attr->fg.red; tmp_attr.bg.green = text->text_attr->fg.green; tmp_attr.bg.blue = text->text_attr->fg.blue; /* lo_CopyTextAttr doesn't copy the FE_Data. In * this case, however, we need to copy the FE_Data * because otherwise the front end will * synthesize a new copy. This assumes that * the FE_Data doesn't care about font color. */ tmp_attr.FE_Data = text->text_attr->FE_Data; hold_attr = text->text_attr; text->text_attr = &tmp_attr; /* For layers we don't ever need to clear the background */ FE_DisplaySubtext(context, FE_VIEW, text, p1, p2, FALSE); text->text_attr = hold_attr; if (p2 < (text->text_len - 1)) { /* For layers we don't ever need to clear the background */ FE_DisplaySubtext(context, FE_VIEW, text, (p2 + 1), (text->text_len - 1), FALSE); } } else { /* For layers we don't ever need to clear the background */ FE_DisplayText(context, FE_VIEW, text, FALSE); } } void lo_DisplayEmbed(MWContext *context, LO_EmbedStruct *embed) { CL_Layer *layer; if (! context->compositor) { FE_DisplayEmbed(context, FE_VIEW, embed); return; } /* Don't ever display hidden embeds */ if (embed->objTag.ele_attrmask & LO_ELE_HIDDEN) return; layer = embed->objTag.layer; XP_ASSERT(layer); if (! layer) /* Paranoia */ return; if (!(embed->objTag.ele_attrmask & LO_ELE_DRAWN)) { /* Move layer to new position */ CL_MoveLayer(layer, embed->objTag.x + embed->objTag.x_offset, embed->objTag.y + embed->objTag.y_offset); CL_SetLayerHidden(layer, PR_FALSE); embed->objTag.ele_attrmask |= LO_ELE_DRAWN; } } void lo_DisplayBuiltin(MWContext *context, LO_BuiltinStruct *builtin) { CL_Layer *layer; /* need to deal with layers here XXX */ if (! context->compositor) { FE_DisplayBuiltin(context, FE_VIEW, builtin); return; } layer = builtin->layer; XP_ASSERT(layer); if (! layer) /* Paranoia */ return; if (!(builtin->ele_attrmask & LO_ELE_DRAWN)) { /* Move layer to new position */ CL_MoveLayer(layer, builtin->x + builtin->x_offset, builtin->y + builtin->y_offset); CL_SetLayerHidden(layer, PR_FALSE); builtin->ele_attrmask |= LO_ELE_DRAWN; } } #ifdef JAVA void lo_DisplayJavaApp(MWContext *context, LO_JavaAppStruct *java_app) { CL_Layer *layer; if (! context->compositor) { FE_DisplayJavaApp(context, FE_VIEW, java_app); return; } layer = java_app->objTag.layer; XP_ASSERT(layer); if (! layer) /* Paranoia */ return; if (!(java_app->objTag.ele_attrmask & LO_ELE_DRAWN)) { /* Move layer to new position */ CL_MoveLayer(layer, java_app->objTag.x + java_app->objTag.x_offset, java_app->objTag.y + java_app->objTag.y_offset); /* Now that layer is layed out to its final position, we can display it */ CL_SetLayerHidden(layer, PR_FALSE); java_app->objTag.ele_attrmask |= LO_ELE_DRAWN; } } #endif /* Make the image layer visible and draw the image border. Should only be called if there is a compositor. */ static void lo_DisplayImage(MWContext *context, LO_ImageStruct *image) { int bw = image->border_width; XP_ASSERT(context->compositor); /* Allow the compositor to start drawing the image layer. */ lo_ActivateImageLayer(context, image); /* The image border, if present, draws in the parent of the image layer. */ if (bw) { int x = image->x + image->x_offset + bw; int y = image->y + image->y_offset + bw; FE_DisplayBorder(context, FE_VIEW, x - bw, y - bw, image->width + 2 * bw, image->height + 2 * bw, bw, &image->text_attr->fg, LO_SOLID); } } void lo_DisplayImageWithoutCompositor(MWContext *context, LO_ImageStruct *image) { int bw = image->border_width; int x = image->x + image->x_offset + bw; int y = image->y + image->y_offset + bw; int width = image->width; int height = image->height; /* This routine should only be called in the absence of a compositor. */ XP_ASSERT(!context->compositor); /* Handle the TextFE. */ if (context->type == MWContextText) { XL_DisplayTextImage(context, FE_VIEW, image); return; } if (image->is_icon) { IL_DisplayIcon(context->img_cx, image->icon_number, x, y); } else { int cnv_x = context->convertPixX; int cnv_y = context->convertPixY; IL_DisplaySubImage(image->image_req, x/cnv_x, y/cnv_y, 0, 0, width/cnv_x, height/cnv_y); } if (bw) FE_DisplayBorder(context, FE_VIEW, x - bw, y - bw, width + 2 * bw, height + 2 * bw, bw, &image->text_attr->fg, LO_SOLID); } void lo_DisplaySubImageWithoutCompositor(MWContext *context, LO_ImageStruct *image, int32 x, int32 y, uint32 width, uint32 height) { int bw = image->border_width; int x_pos = image->x + image->x_offset + bw; int y_pos = image->y + image->y_offset + bw; int sub_x, sub_y, sub_w, sub_h; /* This routine should only be called in the absence of a compositor. */ XP_ASSERT(!context->compositor); /* Handle the TextFE. */ if (context->type == MWContextText) { XL_DisplayTextImage(context, FE_VIEW, image); return; } if (x < x_pos) { sub_x = x_pos; sub_w = width + x - x_pos; } else { sub_x = x; sub_w = width; } if (y < y_pos) { sub_y = y_pos; sub_h = height + y - y_pos; } else { sub_y = y; sub_h = height; } if (x + width > x_pos + image->width) sub_w += (x_pos + image->width - x - width); if (y + height > y_pos + image->height) sub_h += (y_pos + image->height - y - height); if (sub_w > 0 && sub_h > 0) { if (image->is_icon) { IL_DisplayIcon(context->img_cx, image->icon_number, x_pos, y_pos); } else { int cnv_x = context->convertPixX; int cnv_y = context->convertPixY; IL_DisplaySubImage(image->image_req, x_pos/cnv_x, y_pos/cnv_y, (sub_x - x_pos)/cnv_x, (sub_y - y_pos)/cnv_y, sub_w/cnv_x, sub_h/cnv_y); } } if (bw) FE_DisplayBorder(context, FE_VIEW, x - bw, y - bw, width + 2 * bw, height + 2 * bw, bw, &image->text_attr->fg, LO_SOLID); } /* Should only be called in the absence of a compositor. */ void lo_ClipImage(MWContext *context, LO_ImageStruct *image, int32 x, int32 y, uint32 width, uint32 height) { int32 sub_x, sub_y; uint32 sub_w, sub_h; /* * If the two don't overlap, do nothing. */ if (((int32)(image->x + image->x_offset + image->width + 2*image->border_width) < x)|| ((int32)(x + width) < (int32)(image->x + image->x_offset))|| ((int32)(image->y + image->y_offset + image->height + 2*image->border_width) < y)|| ((int32)(y + height) < (int32)(image->y + image->y_offset))) { return; } if ((image->x + image->x_offset) >= x) { sub_x = image->x + image->x_offset; } else { sub_x = x; } if ((int32)(image->x + image->x_offset + image->width + 2*image->border_width) <= (int32)(x + width)) { sub_w = image->x + image->x_offset + image->width + 2*image->border_width - sub_x; } else { sub_w = x + width - sub_x; } if ((image->y + image->y_offset) >= y) { sub_y = image->y + image->y_offset; } else { sub_y = y; } if ((int32)(image->y + image->y_offset + image->height + 2*image->border_width) <= (int32)(y + height)) { sub_h = image->y + image->y_offset + image->height + 2*image->border_width - sub_y; } else { sub_h = y + height - sub_y; } if (((int32)sub_w >= (int32)(image->width - 2))&& ((int32)sub_h >= (int32)(image->height - 2))) { lo_DisplayImageWithoutCompositor(context, (LO_ImageStruct *)image); } else { lo_DisplaySubImageWithoutCompositor(context, (LO_ImageStruct *)image, sub_x, sub_y, sub_w, sub_h); } } void lo_DisplayEdge(MWContext *context, LO_EdgeStruct *edge) { if (edge->visible != FALSE) { FE_DisplayEdge(context, FE_VIEW, edge); } } void lo_DisplayTable(MWContext *context, LO_TableStruct *table) { FE_DisplayTable(context, FE_VIEW, table); } void lo_DisplaySubDoc(MWContext *context, LO_SubDocStruct *subdoc) { FE_DisplaySubDoc(context, FE_VIEW, subdoc); } void lo_DisplayCell(MWContext *context, LO_CellStruct *cell) { /* If this cell is empty, bail */ if (cell->cell_list == NULL && cell->cell_float_list == NULL) return; if (context->compositor && cell->cell_bg_layer) CL_SetLayerHidden(cell->cell_bg_layer, PR_FALSE); FE_DisplayCell(context, FE_VIEW, cell); } void lo_DisplayLineFeed(MWContext *context, LO_LinefeedStruct *lfeed, Bool need_bg) { LO_TextAttr tmp_attr; LO_TextAttr *hold_attr; if (lfeed->ele_attrmask & LO_ELE_SELECTED) { lo_CopyTextAttr(lfeed->text_attr, &tmp_attr); tmp_attr.fg.red = lfeed->text_attr->bg.red; tmp_attr.fg.green = lfeed->text_attr->bg.green; tmp_attr.fg.blue = lfeed->text_attr->bg.blue; tmp_attr.bg.red = lfeed->text_attr->fg.red; tmp_attr.bg.green = lfeed->text_attr->fg.green; tmp_attr.bg.blue = lfeed->text_attr->fg.blue; hold_attr = lfeed->text_attr; lfeed->text_attr = &tmp_attr; FE_DisplayLineFeed(context, FE_VIEW, lfeed, (XP_Bool)need_bg); lfeed->text_attr = hold_attr; } else { FE_DisplayLineFeed(context, FE_VIEW, lfeed, (XP_Bool)need_bg); } } void lo_DisplayHR(MWContext *context, LO_HorizRuleStruct *hrule) { FE_DisplayHR(context, FE_VIEW, hrule); } void lo_DisplayBullet(MWContext *context, LO_BulletStruct *bullet) { FE_DisplayBullet(context, FE_VIEW, bullet); } void lo_DisplayFormElement(MWContext *context, LO_FormElementStruct *form_element) { CL_Layer *layer; if (! context->compositor) { FE_DisplayFormElement(context, FE_VIEW, form_element); return; } layer = form_element->layer; XP_ASSERT(layer); if (! layer) /* Paranoia */ return; if (!(form_element->ele_attrmask & LO_ELE_DRAWN)) { /* Move layer to new position */ CL_MoveLayer(layer, form_element->x + form_element->x_offset + form_element->border_horiz_space, form_element->y + form_element->y_offset + form_element->border_vert_space); CL_SetLayerHidden(layer, PR_FALSE); form_element->ele_attrmask |= LO_ELE_DRAWN; } } void lo_DisplayElement(MWContext *context, LO_Element *tptr, int32 base_x, int32 base_y, int32 x, int32 y, uint32 width, uint32 height) { LO_Any *any = &tptr->lo_any; XP_Rect bbox; lo_GetElementBbox(tptr, &bbox); XP_OffsetRect(&bbox, base_x, base_y); if (bbox.top >= (int32)(y + height)) return; if (bbox.bottom <= y ) return; if (bbox.left >= (int32)(x + width)) return; if (bbox.right <= x ) return; /* Temporarily translate to new coordinate system */ any->x += base_x; any->y += base_y; switch (tptr->type) { case LO_TEXT: if (tptr->lo_text.text != NULL) lo_DisplayText(context, (LO_TextStruct *)tptr, FALSE); break; case LO_LINEFEED: lo_DisplayLineFeed(context, (LO_LinefeedStruct *)tptr, FALSE); break; case LO_HRULE: lo_DisplayHR(context, (LO_HorizRuleStruct *)tptr); break; case LO_FORM_ELE: lo_DisplayFormElement(context, (LO_FormElementStruct *)tptr); break; case LO_BULLET: lo_DisplayBullet(context, (LO_BulletStruct *)tptr); break; case LO_IMAGE: if (context->compositor) { /* Allow the compositor to start drawing the image. */ lo_DisplayImage(context, (LO_ImageStruct *)tptr); } else { /* There is no compositor, so draw the image directly, with the appropriate clip. */ lo_ClipImage(context, (LO_ImageStruct *)tptr, (x + base_x), (y + base_y), width, height); } break; case LO_TABLE: lo_DisplayTable(context, (LO_TableStruct *)tptr); break; case LO_EMBED: lo_DisplayEmbed(context, (LO_EmbedStruct *)tptr); break; case LO_BUILTIN: lo_DisplayBuiltin(context, (LO_BuiltinStruct *)tptr); break; #ifdef JAVA case LO_JAVA: lo_DisplayJavaApp(context, (LO_JavaAppStruct *)tptr); break; #endif case LO_EDGE: lo_DisplayEdge(context, (LO_EdgeStruct *)tptr); break; case LO_CELL: /* If this cell is a container for an inflow layer (and therefore not a table cell), don't descend into the cell because the layer's painter function will handle its display. */ if (((LO_CellStruct*)tptr)->cell_inflow_layer) break; /* cmanske: Order changed - display cell contents FIRST * so selection feedback near cell border is not wiped out * TODO: This doesn't work for images, which are display * asynchronously. Need to add "observer" to image display */ lo_DisplayCellContents(context, (LO_CellStruct *)tptr, base_x, base_y, x, y, width, height); lo_DisplayCell(context, (LO_CellStruct *)tptr); break; case LO_SUBDOC: { LO_SubDocStruct *subdoc; int32 new_x, new_y; uint32 new_width, new_height; lo_DocState *sub_state; subdoc = (LO_SubDocStruct *)tptr; sub_state = (lo_DocState *)subdoc->state; if (sub_state == NULL) { break; } lo_DisplaySubDoc(context, subdoc); new_x = subdoc->x; new_y = subdoc->y; new_width = subdoc->x_offset + subdoc->width; new_height = subdoc->y_offset + subdoc->height; new_x = new_x - subdoc->x; new_y = new_y - subdoc->y; sub_state->base_x = subdoc->x + subdoc->x_offset + subdoc->border_width; sub_state->base_y = subdoc->y + subdoc->y_offset + subdoc->border_width; lo_RefreshDocumentArea(context, sub_state, new_x, new_y, new_width, new_height); } break; case LO_PARAGRAPH: case LO_CENTER: case LO_MULTICOLUMN: case LO_LIST: case LO_DESCTITLE: case LO_DESCTEXT: case LO_BLOCKQUOTE: case LO_HEADING: case LO_SPAN: /* all non-display, doc-state mutating elements. */ break; case LO_TEXTBLOCK: break; case LO_FLOAT: case LO_LAYER: case LO_SPACER: break; default: XP_TRACE(("lo_DisplayElement(%p) skip element type = %d\n", tptr, tptr->type)); break; } /* Restore original element's coordinates */ any->x -= base_x; any->y -= base_y; } #ifdef PROFILE #pragma profile off #endif