зеркало из https://github.com/mozilla/pjs.git
626 строки
15 KiB
C
626 строки
15 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* Created by: Nisheeth Ranjan (nisheeth@netscape.com), April 30, 1998 */
|
|
|
|
/*
|
|
* Implements the abstraction of a layout probe object that gives information about
|
|
* layout elements. No mutilation of the layout elements is allowed by this
|
|
* probe. Each FE uses this API to satisfy requests for layout information.
|
|
*
|
|
*/
|
|
|
|
#include "layprobe.h"
|
|
#include "layout.h"
|
|
#include "xp.h"
|
|
|
|
|
|
typedef struct _lo_ProbeState {
|
|
MWContext *context;
|
|
lo_TopState *top_state;
|
|
lo_DocState *doc_state;
|
|
LO_Element *curr_ele;
|
|
XP_List *table_stack; /* Stack of pointers to parent tables */
|
|
XP_List *cell_stack; /* Stack of pointers to parent cells */
|
|
XP_List *parent_type_stack; /* Stack of type of parents seen till now */
|
|
} lo_ProbeState;
|
|
|
|
/* Private function prototypes */
|
|
static Bool lo_QA_IsValidElement (LO_Element *ele);
|
|
static LO_Element * lo_QA_GetValidNextElement (LO_Element *ele);
|
|
static Bool lo_QA_ElementHasColor( LO_Element * ele);
|
|
static Bool lo_QA_GetColor( LO_Element *ele, LO_Color *color, ColorType type);
|
|
static long lo_QA_RGBColorToLong( LO_Color color );
|
|
|
|
/*
|
|
* Constructor/Destructor for probe
|
|
*
|
|
*/
|
|
|
|
/* Probe state is created. Probe position gets set to "nothing". */
|
|
long LO_QA_CreateProbe( MWContext *context )
|
|
{
|
|
lo_ProbeState *ps = XP_NEW_ZAP( lo_ProbeState );
|
|
|
|
if (ps && context) {
|
|
ps->context = context;
|
|
ps->curr_ele = NULL;
|
|
ps->table_stack = XP_ListNew();
|
|
ps->cell_stack = XP_ListNew();
|
|
ps->parent_type_stack = XP_ListNew();
|
|
ps->top_state = lo_FetchTopState( XP_DOCID(context) );
|
|
if (ps->top_state)
|
|
ps->doc_state = ps->top_state->doc_state;
|
|
}
|
|
|
|
return (long) ps;
|
|
}
|
|
|
|
/* Probe state is destroyed. */
|
|
void LO_QA_DestroyProbe( long probeID )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
|
|
if (ps) {
|
|
ps->context = NULL;
|
|
ps->top_state = NULL;
|
|
ps->doc_state = NULL;
|
|
ps->curr_ele = NULL;
|
|
if (ps->table_stack)
|
|
XP_ListDestroy( ps->table_stack );
|
|
if (ps->cell_stack)
|
|
XP_ListDestroy( ps->cell_stack );
|
|
if (ps->parent_type_stack)
|
|
XP_ListDestroy( ps->parent_type_stack );
|
|
XP_FREE(ps);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Functions to set position of layout probe.
|
|
*
|
|
*/
|
|
|
|
/* Sets probe position to first layout element in document. */
|
|
Bool LO_QA_GotoFirstElement( long probeID )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
LO_Element **line_array;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->doc_state) {
|
|
XP_LOCK_BLOCK ( line_array, LO_Element **, ps->doc_state->line_array );
|
|
if (line_array) {
|
|
ps->curr_ele = line_array[0];
|
|
success = TRUE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
If the probe has just been created, the next element is the first layout element in the document.
|
|
If there is a next element, the function returns TRUE and sets the probe position to the next element.
|
|
If there is no next element, the function returns FALSE and resets the probe position to nothing.
|
|
*/
|
|
Bool LO_QA_GotoNextElement( long probeID )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
LO_Element *next_ele = NULL;
|
|
|
|
if (ps) {
|
|
if (!ps->curr_ele) {
|
|
/* The probe has just been created, so goto first element */
|
|
LO_QA_GotoFirstElement( (long) ps );
|
|
success = TRUE;
|
|
}
|
|
else {
|
|
/* The probe is currently pointing to curr_ele. */
|
|
next_ele = lo_QA_GetValidNextElement (ps->curr_ele);
|
|
if (next_ele) {
|
|
/* A valid next element exists */
|
|
ps->curr_ele = next_ele;
|
|
success = TRUE;
|
|
}
|
|
else {
|
|
/* No valid next element */
|
|
ps->curr_ele = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
Sets probe position and returns TRUE or false based on the following table:
|
|
|
|
Current Probe Position New Probe Position Return value
|
|
---------------------- ------------------ ------------
|
|
Table First cell in table TRUE
|
|
Cell First layout element in cell TRUE
|
|
Layer First layout element in layer TRUE
|
|
Any other element No change FALSE
|
|
*/
|
|
|
|
Bool LO_QA_GotoChildElement( long probeID )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps) {
|
|
if (ps->curr_ele) {
|
|
if (ps->curr_ele->lo_any.type == LO_TABLE) {
|
|
/* Push the table layout element on the table stack */
|
|
XP_ListAddObjectToEnd (ps->table_stack, (void *) ps->curr_ele);
|
|
/* Push the table type on the parent type stack */
|
|
XP_ListAddObjectToEnd (ps->parent_type_stack, (void *) LO_TABLE);
|
|
ps->curr_ele = ps->curr_ele->lo_any.next;
|
|
XP_ASSERT( ps->curr_ele->lo_any.type == LO_CELL);
|
|
success = TRUE;
|
|
}
|
|
else if (ps->curr_ele->lo_any.type == LO_CELL) {
|
|
/* Push the table layout element on the cell stack */
|
|
XP_ListAddObjectToEnd (ps->cell_stack, (void *) ps->curr_ele);
|
|
/* Push the cell type on the parent type stack */
|
|
XP_ListAddObjectToEnd (ps->parent_type_stack, (void *) LO_CELL);
|
|
ps->curr_ele = ps->curr_ele->lo_cell.cell_list;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
Sets probe position and returns TRUE or false based on the following table:
|
|
|
|
Current Probe Position New Probe Position Return value
|
|
---------------------- ------------------ ------------
|
|
Cell within a table Parent Table TRUE
|
|
Element within a cell Parent Cell TRUE
|
|
Element within a layer Parent Layer TRUE
|
|
Any other element No change FALSE
|
|
*/
|
|
|
|
Bool LO_QA_GotoParentElement( long probeID )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps) {
|
|
if (ps->parent_type_stack) {
|
|
/* Pop the parent type off of the parent type stack */
|
|
uint8 parentType = (uint8) XP_ListRemoveEndObject( ps->parent_type_stack );
|
|
switch (parentType) {
|
|
case LO_TABLE: {
|
|
if (ps->table_stack) {
|
|
ps->curr_ele = (LO_Element *) XP_ListRemoveEndObject( ps->table_stack );
|
|
success = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
case LO_CELL: {
|
|
if (ps->cell_stack) {
|
|
ps->curr_ele = (LO_Element *) XP_ListRemoveEndObject( ps->cell_stack );
|
|
success = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
Gets the layout element type that the probe is positioned on.
|
|
Returns FALSE if the probe is not positioned on any element, else returns TRUE.
|
|
*/
|
|
Bool LO_QA_GetElementType( long probeID, int *type )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
XP_ASSERT(lo_QA_IsValidElement( ps->curr_ele ));
|
|
*type = ps->curr_ele->lo_any.type;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
* Functions to return the current layout element's position and dimensions.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
Each of these functions return TRUE if the probe position is set to a layout element,
|
|
otherwise, they return FALSE.
|
|
*/
|
|
Bool LO_QA_GetElementXPosition( long probeID, long *x )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
XP_ASSERT(lo_QA_IsValidElement( ps->curr_ele ));
|
|
*x = ps->curr_ele->lo_any.x;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_GetElementYPosition( long probeID, long *y )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
XP_ASSERT(lo_QA_IsValidElement( ps->curr_ele ));
|
|
*y = ps->curr_ele->lo_any.y;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_GetElementWidth( long probeID, long *width )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
XP_ASSERT(lo_QA_IsValidElement( ps->curr_ele ));
|
|
*width = ps->curr_ele->lo_any.width;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_GetElementHeight( long probeID, long *height )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
XP_ASSERT(lo_QA_IsValidElement( ps->curr_ele ));
|
|
*height = ps->curr_ele->lo_any.height;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
Bool LO_QA_HasURL( long probeID, Bool *hasURL )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
if (ps->curr_ele->lo_any.type == LO_TEXT) {
|
|
if (ps->curr_ele->lo_text.text_attr->attrmask & LO_ATTR_ANCHOR) {
|
|
*hasURL = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
*hasURL = FALSE;
|
|
}
|
|
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_HasText( long probeID, Bool *hasText )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
if (ps->curr_ele->lo_any.type == LO_TEXT)
|
|
*hasText = TRUE;
|
|
else
|
|
*hasText = FALSE;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_HasColor( long probeID, Bool *hasColor )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
XP_ASSERT(lo_QA_IsValidElement( ps->curr_ele ));
|
|
*hasColor = lo_QA_ElementHasColor( ps->curr_ele );
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_HasChild( long probeID, Bool *hasChild )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
if (ps->curr_ele->lo_any.type == LO_TABLE ||
|
|
ps->curr_ele->lo_any.type == LO_CELL)
|
|
*hasChild = TRUE;
|
|
else
|
|
*hasChild = FALSE;
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_HasParent( long probeID, Bool *hasParent )
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->parent_type_stack) {
|
|
if (XP_ListGetEndObject(ps->parent_type_stack)) {
|
|
/* There is stuff pushed on the parent type stack. So, this element has a parent */
|
|
*hasParent = TRUE;
|
|
}
|
|
else {
|
|
*hasParent = FALSE;
|
|
}
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_GetTextLength( long probeID, long *length)
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
if (ps->curr_ele->lo_any.type == LO_TEXT) {
|
|
*length = ps->curr_ele->lo_text.text_len;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
Bool LO_QA_GetText( long probeID, char *text, long length)
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
if (ps->curr_ele->lo_any.type == LO_TEXT) {
|
|
XP_ASSERT(XP_STRLEN((const char *)ps->curr_ele->lo_text.text) == length);
|
|
strncpy(text, (const char *) ps->curr_ele->lo_text.text, length);
|
|
success = TRUE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/* Color Type Elements containing the color type
|
|
---------- ----------------------------------
|
|
LO_QA_BACKGROUND_COLOR LO_TEXT, LO_BULLET, LO_CELL
|
|
LO_QA_FOREGROUND_COLOR LO_TEXT, LO_BULLET
|
|
LO_QA_BORDER_COLOR LO_TABLE
|
|
|
|
Based on the element type and the color type, gets the color of the element
|
|
at which the probe is currently positioned. Returns TRUE if successful,
|
|
FALSE if not.
|
|
*/
|
|
Bool LO_QA_GetColor( long probeID, long *color, ColorType type)
|
|
{
|
|
lo_ProbeState *ps = (lo_ProbeState *) probeID;
|
|
Bool success = FALSE;
|
|
|
|
if (ps && ps->curr_ele) {
|
|
if (lo_QA_ElementHasColor(ps->curr_ele)) {
|
|
LO_Color clr;
|
|
if (lo_QA_GetColor(ps->curr_ele, &clr, type)) {
|
|
*color = lo_QA_RGBColorToLong( clr );
|
|
success = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Private methods. Only called from within this module.
|
|
**/
|
|
|
|
/* Gets a "valid" next element in the layout element list.
|
|
"Valid" elements are all the element types that we want layout outsiders to know about.
|
|
Note: An LO_CELL is NOT a valid next element even though outsiders know about it. This
|
|
is because we want tables to contain cells. Cells can only be gotten at by making a
|
|
LO_QA_GotoChildElement() on a probe that is positioned on a table element.
|
|
*/
|
|
static LO_Element * lo_QA_GetValidNextElement (LO_Element *ele)
|
|
{
|
|
LO_Element *next_ele = NULL;
|
|
|
|
if (ele) {
|
|
Bool found = FALSE;
|
|
next_ele = ele->lo_any.next;
|
|
while (next_ele && !found) {
|
|
switch(next_ele->lo_any.type)
|
|
{
|
|
case LO_TEXT:
|
|
case LO_HRULE:
|
|
case LO_IMAGE:
|
|
case LO_BULLET:
|
|
case LO_TABLE:
|
|
case LO_FORM_ELE:
|
|
case LO_EMBED:
|
|
case LO_JAVA:
|
|
case LO_OBJECT: {
|
|
/* This is a valid element. */
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
default: {
|
|
/* Not a valid element. Try next element */
|
|
next_ele = next_ele->lo_any.next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return next_ele;
|
|
}
|
|
|
|
/* Returns TRUE if ele's type is one of the types defined in layprobe.h */
|
|
static Bool lo_QA_IsValidElement (LO_Element *ele)
|
|
{
|
|
Bool isValid = FALSE;
|
|
if (ele) {
|
|
switch (ele->lo_any.type)
|
|
{
|
|
case LO_TEXT:
|
|
case LO_HRULE:
|
|
case LO_IMAGE:
|
|
case LO_BULLET:
|
|
case LO_TABLE:
|
|
case LO_CELL:
|
|
case LO_FORM_ELE:
|
|
case LO_EMBED:
|
|
case LO_JAVA:
|
|
case LO_OBJECT: {
|
|
isValid = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/* Figure out if ele is an element that has a color property */
|
|
static Bool lo_QA_ElementHasColor( LO_Element * ele)
|
|
{
|
|
Bool hasColor = FALSE;
|
|
if ( ele ) {
|
|
hasColor = (ele->lo_any.type == LO_TEXT ||
|
|
ele->lo_any.type == LO_BULLET ||
|
|
ele->lo_any.type == LO_TABLE ||
|
|
ele->lo_any.type == LO_CELL);
|
|
}
|
|
return hasColor;
|
|
}
|
|
|
|
/* Color Type Elements containing the color type
|
|
---------- ----------------------------------
|
|
LO_QA_BACKGROUND_COLOR LO_TEXT, LO_BULLET, LO_CELL
|
|
LO_QA_FOREGROUND_COLOR LO_TEXT, LO_BULLET
|
|
LO_QA_BORDER_COLOR LO_TABLE
|
|
|
|
Based on the element type and the color type, gets the color of ele.
|
|
Returns TRUE if successful, FALSE if not.
|
|
*/
|
|
static Bool lo_QA_GetColor( LO_Element *ele, LO_Color *color, ColorType type )
|
|
{
|
|
Bool success = FALSE;
|
|
|
|
if (ele) {
|
|
switch (ele->lo_any.type)
|
|
{
|
|
case LO_TEXT:
|
|
if (type == LO_QA_FOREGROUND_COLOR) {
|
|
if (ele->lo_text.text_attr) {
|
|
*color = ele->lo_text.text_attr->fg;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
else if (type == LO_QA_BACKGROUND_COLOR) {
|
|
if (ele->lo_text.text_attr) {
|
|
*color = ele->lo_text.text_attr->bg;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case LO_BULLET:
|
|
if (type == LO_QA_FOREGROUND_COLOR) {
|
|
if (ele->lo_text.text_attr) {
|
|
*color = ele->lo_bullet.text_attr->fg;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
else if (type == LO_QA_BACKGROUND_COLOR) {
|
|
if (ele->lo_text.text_attr) {
|
|
*color = ele->lo_bullet.text_attr->bg;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case LO_TABLE:
|
|
if (type == LO_QA_BORDER_COLOR) {
|
|
*color = ele->lo_table.border_color;
|
|
success = TRUE;
|
|
}
|
|
break;
|
|
case LO_CELL:
|
|
if (type == LO_QA_BACKGROUND_COLOR) {
|
|
if (ele->lo_cell.backdrop.bg_color) {
|
|
*color = *(ele->lo_cell.backdrop.bg_color);
|
|
success = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/* Convert the RGB triplet into a long */
|
|
static long lo_QA_RGBColorToLong( LO_Color color )
|
|
{
|
|
long c = color.red;
|
|
|
|
c = c << 8;
|
|
c += color.green;
|
|
c = c << 8;
|
|
c += color.blue;
|
|
|
|
return c;
|
|
} |