gecko-dev/lib/layout/edtutil.cpp

6484 строки
194 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.
*/
//
// Public interface and shared subsystem data.
//
#ifdef EDITOR
#include "editor.h"
#include "rosetta.h"
typedef struct PA_AmpEsc_struct {
char *str;
char value;
intn len;
} PA_AmpEsc;
#ifndef XP_MAC
static PA_AmpEsc PA_AmpEscapes[] = {
{"lt", '<', 2},
{"LT", '<', 2},
{"gt", '>', 2},
{"GT", '>', 2},
{"amp", '&', 3},
{"AMP", '&', 3},
{"quot", '\"', 4},
{"QUOT", '\"', 4},
{"nbsp", '\240', 4},
{"reg", '\256', 3},
{"REG", '\256', 3},
{"copy", '\251', 4},
{"COPY", '\251', 4},
{"iexcl", '\241', 5},
{"cent", '\242', 4},
{"pound", '\243', 5},
{"curren", '\244', 6},
#ifdef XP_WIN
{"euro", '\200', 4},
#endif
{"yen", '\245', 3},
{"brvbar", '\246', 6},
{"sect", '\247', 4},
{"uml", '\250', 3},
{"ordf", '\252', 4},
{"laquo", '\253', 5},
{"not", '\254', 3},
{"shy", '\255', 3},
{"macr", '\257', 4},
{"deg", '\260', 3},
{"plusmn", '\261', 6},
{"sup2", '\262', 4},
{"sup3", '\263', 4},
{"acute", '\264', 5},
{"micro", '\265', 5},
{"para", '\266', 4},
{"middot", '\267', 6},
{"cedil", '\270', 5},
{"sup1", '\271', 4},
{"ordm", '\272', 4},
{"raquo", '\273', 5},
{"frac14", '\274', 6},
{"frac12", '\275', 6},
{"frac34", '\276', 6},
{"iquest", '\277', 6},
{"Agrave", '\300', 6},
{"Aacute", '\301', 6},
{"Acirc", '\302', 5},
{"Atilde", '\303', 6},
{"Auml", '\304', 4},
{"Aring", '\305', 5},
{"AElig", '\306', 5},
{"Ccedil", '\307', 6},
{"Egrave", '\310', 6},
{"Eacute", '\311', 6},
{"Ecirc", '\312', 5},
{"Euml", '\313', 4},
{"Igrave", '\314', 6},
{"Iacute", '\315', 6},
{"Icirc", '\316', 5},
{"Iuml", '\317', 4},
{"ETH", '\320', 3},
{"Ntilde", '\321', 6},
{"Ograve", '\322', 6},
{"Oacute", '\323', 6},
{"Ocirc", '\324', 5},
{"Otilde", '\325', 6},
{"Ouml", '\326', 4},
{"times", '\327', 5},
{"Oslash", '\330', 6},
{"Ugrave", '\331', 6},
{"Uacute", '\332', 6},
{"Ucirc", '\333', 5},
{"Uuml", '\334', 4},
{"Yacute", '\335', 6},
{"THORN", '\336', 5},
{"szlig", '\337', 5},
{"agrave", '\340', 6},
{"aacute", '\341', 6},
{"acirc", '\342', 5},
{"atilde", '\343', 6},
{"auml", '\344', 4},
{"aring", '\345', 5},
{"aelig", '\346', 5},
{"ccedil", '\347', 6},
{"egrave", '\350', 6},
{"eacute", '\351', 6},
{"ecirc", '\352', 5},
{"euml", '\353', 4},
{"igrave", '\354', 6},
{"iacute", '\355', 6},
{"icirc", '\356', 5},
{"iuml", '\357', 4},
{"eth", '\360', 3},
{"ntilde", '\361', 6},
{"ograve", '\362', 6},
{"oacute", '\363', 6},
{"ocirc", '\364', 5},
{"otilde", '\365', 6},
{"ouml", '\366', 4},
{"divide", '\367', 6},
{"oslash", '\370', 6},
{"ugrave", '\371', 6},
{"uacute", '\372', 6},
{"ucirc", '\373', 5},
{"uuml", '\374', 4},
{"yacute", '\375', 6},
{"thorn", '\376', 5},
{"yuml", '\377', 4},
{NULL, '\0', 0},
};
#else /* ! XP_MAC */
/* Entities encoded in MacRoman. */
static PA_AmpEsc PA_AmpEscapes[] = {
{"lt", '<', 2},
{"LT", '<', 2},
{"gt", '>', 2},
{"GT", '>', 2},
{"amp", '&', 3},
{"AMP", '&', 3},
{"quot", '\"', 4},
{"QUOT", '\"', 4},
{"nbsp", '\007', 4},
{"reg", '\250', 3},
{"REG", '\250', 3},
{"copy", '\251', 4},
{"COPY", '\251', 4},
{"iexcl", '\301', 5},
{"cent", '\242', 4},
{"pound", '\243', 5},
#if 0 /* before MacOS 8.5 0xdb is curren, but it changed to euro in MacOS 8.5 */
{"curren", '\333', 6},
#else
{"euro", '\333', 4},
#endif
{"yen", '\264', 3},
/*
* Navigator Gold currently inverts this table in such a way that
* ASCII characters (less than 128) get converted to the names
* listed here. For things like ampersand (&amp;) this is the
* right thing to do, but for this one (brvbar), it isn't since
* both broken vertical bar and vertical bar are mapped to the same
* character by the Latin-1 to Mac Roman table.
*
* Punt for now. This needs to be fixed later. -- erik
*/
/* {"brvbar", '\174', 6}, */
{"sect", '\244', 4},
{"uml", '\254', 3},
{"ordf", '\273', 4},
{"laquo", '\307', 5},
{"not", '\302', 3},
{"shy", '\320', 3},
{"macr", '\370', 4},
{"deg", '\241', 3},
{"plusmn", '\261', 6},
/* {"sup2", '\62', 4}, see comment above */
/* {"sup3", '\63', 4}, see comment above */
{"acute", '\253', 5},
{"micro", '\265', 5},
{"para", '\246', 4},
{"middot", '\341', 6},
{"cedil", '\374', 5},
/* {"sup1", '\61', 4}, see comment above */
{"ordm", '\274', 4},
{"raquo", '\310', 5},
{"frac14", '\271', 6},
{"frac12", '\270', 6},
{"frac34", '\262', 6},
{"iquest", '\300', 6},
{"Agrave", '\313', 6},
{"Aacute", '\347', 6},
{"Acirc", '\345', 5},
{"Atilde", '\314', 6},
{"Auml", '\200', 4},
{"Aring", '\201', 5},
{"AElig", '\256', 5},
{"Ccedil", '\202', 6},
{"Egrave", '\351', 6},
{"Eacute", '\203', 6},
{"Ecirc", '\346', 5},
{"Euml", '\350', 4},
{"Igrave", '\355', 6},
{"Iacute", '\352', 6},
{"Icirc", '\353', 5},
{"Iuml", '\354', 4},
{"ETH", '\334', 3}, /* Icelandic MacRoman: ETH ('D' w/horiz bar) */
{"Ntilde", '\204', 6},
{"Ograve", '\361', 6},
{"Oacute", '\356', 6},
{"Ocirc", '\357', 5},
{"Otilde", '\315', 6},
{"Ouml", '\205', 4},
/* {"times", '\170', 5}, see comment above */
{"Oslash", '\257', 6},
{"Ugrave", '\364', 6},
{"Uacute", '\362', 6},
{"Ucirc", '\363', 5},
{"Uuml", '\206', 4},
{"Yacute", '\240', 6}, /* Icelandic MacRoman: Yacute */
{"THORN", '\336', 5}, /* Icelandic MacRoman: THORN (kinda like 'P') */
{"szlig", '\247', 5},
{"agrave", '\210', 6},
{"aacute", '\207', 6},
{"acirc", '\211', 5},
{"atilde", '\213', 6},
{"auml", '\212', 4},
{"aring", '\214', 5},
{"aelig", '\276', 5},
{"ccedil", '\215', 6},
{"egrave", '\217', 6},
{"eacute", '\216', 6},
{"ecirc", '\220', 5},
{"euml", '\221', 4},
{"igrave", '\223', 6},
{"iacute", '\222', 6},
{"icirc", '\224', 5},
{"iuml", '\225', 4},
{"eth", '\335', 3}, /* Icelandic MacRoman: eth ('d' w/horiz bar) */
{"ntilde", '\226', 6},
{"ograve", '\230', 6},
{"oacute", '\227', 6},
{"ocirc", '\231', 5},
{"otilde", '\233', 6},
{"ouml", '\232', 4},
{"divide", '\326', 6},
{"oslash", '\277', 6},
{"ugrave", '\235', 6},
{"uacute", '\234', 6},
{"ucirc", '\236', 5},
{"uuml", '\237', 4},
{"yacute", '\340', 6}, /* Icelandic MacRoman: yacute */
{"thorn", '\337', 5}, /* Icelandic MacRoman: thorn (kinda like 'p') */
{"yuml", '\330', 4},
{NULL, '\0', 0},
};
#endif
// For XP Strings
extern "C" {
#include "xpgetstr.h"
#define WANT_ENUM_STRING_IDS
#include "allxpstr.h"
#undef WANT_ENUM_STRING_IDS
}
#include "prefapi.h"
#include "secnav.h"
#include "xp_str.h"
char* edt_CopyFromHuge(int16 /*csid*/, XP_HUGE_CHAR_PTR text, int32 length, int32* pOutLength){
int len2 = length;
#ifdef XP_WIN16
const int32 kMax = 65530;
XP_Bool bTruncated = FALSE;
if ( len2 > kMax ) {
XP_TRACE(("Truncating huge data. original size: %d bytes", len2));
bTruncated = TRUE;
len2 = kMax;
}
#endif
char* pData = (char*) XP_ALLOC(len2 + 1);
if ( pData == NULL ) return NULL;
for(int32 i = 0; i < len2; i++ ){
// No XP way of transfering from HUGE to normal
pData[i] = text[i];
}
#ifdef XP_WIN16
if ( bTruncated ) {
int nboc = INTL_NthByteOfChar(csid, pData, (int)(len2));
// nboc is one-based. Stupid!
if ( nboc > 1 ){
len2 -= (nboc - 1);
}
if ( len2 < 0 ) {
len2 = 0;
}
XP_TRACE(("Final size: %d bytes", len2));
}
#endif
pData[len2] = '\0';
if ( pOutLength ) {
*pOutLength = len2;
}
return pData;
}
void edt_StripAtHashOrQuestionMark(char *pUrl)
{
if( !pUrl )
return;
char *pStart = XP_STRRCHR(pUrl, '/');
if(!pStart)
pStart = pUrl;
char * pSuffix = XP_STRCHR(pStart, '?');
if(pSuffix)
*pSuffix = '\0';
pSuffix = XP_STRRCHR(pStart, '#');
if(pSuffix)
*pSuffix = '\0';
}
char *edt_StripUsernamePassword(char *pUrl) {
int iType = NET_URL_Type(pUrl);
if (iType != FTP_TYPE_URL &&
iType != HTTP_TYPE_URL &&
iType != SECURE_HTTP_TYPE_URL) {
return XP_STRDUP(pUrl);
}
char *pRet = NULL;
if (NET_ParseUploadURL(pUrl,&pRet,NULL,NULL) && pRet) {
return pRet;
}
else {
return XP_STRDUP(pUrl);
}
}
// Always returns newly allocated memory or NULL.
PRIVATE char *edt_remove_trailing_slash(const char *pStr) {
if (pStr) {
char *pNew = XP_STRDUP(pStr);
if (pNew && *pNew) {
// Only do strlen() if pNew is non-NULL.
int iLen = XP_STRLEN(pNew);
// Kill trailing slash.
if (pNew[iLen-1] == '/') {
pNew[iLen-1] = '\0';
}
}
return pNew;
}
else {
return NULL;
}
}
// Just wrappers around the XP_* functions. The only difference is that
// these functions will work if the directory ends in a trailing slash,
// avoiding obscure bugs on Win16.
XP_Dir edt_OpenDir(const char * name, XP_FileType type) {
XP_ASSERT(type == xpURL);
char *pNew = edt_remove_trailing_slash(name);
XP_Dir ret = XP_OpenDir(pNew,type);
XP_FREEIF(pNew);
return ret;
}
int edt_MakeDirectory(const char* name, XP_FileType type) {
XP_ASSERT(type == xpURL);
char *pNew = edt_remove_trailing_slash(name);
int ret = XP_MakeDirectory(pNew,type);
XP_FREEIF(pNew);
return ret;
}
int edt_RemoveDirectory(const char *name, XP_FileType type) {
XP_ASSERT(type == xpURL);
char *pNew = edt_remove_trailing_slash(name);
int ret = XP_RemoveDirectory(pNew,type);
XP_FREEIF(pNew);
return ret;
}
// Recursively kill everything under pNameArg, which is in xpURL format.
void edt_RemoveDirectoryR(char *pNameArg) {
// non-NULL and non-zero-length.
if (!(pNameArg && *pNameArg))
return;
// Make sure name ends in slash.
char *pName = XP_STRDUP(pNameArg);
if (!pName) {
return;
}
if (pName[XP_STRLEN(pName)-1] != '/') {
StrAllocCat(pName,"/");
}
XP_Dir dir = edt_OpenDir(pName, xpURL);
if (dir) {
XP_DirEntryStruct *entry = NULL;
for (entry = XP_ReadDir(dir); entry; entry = XP_ReadDir(dir)) {
// Don't touch "." or ".."
if (entry->d_name && XP_STRCMP(entry->d_name,".") && XP_STRCMP(entry->d_name,"..")) {
// Create filename inside directory.
char *subName = XP_STRDUP(pName);
if (entry->d_name[0] == '/') {
// skip the slash.
StrAllocCat(subName,entry->d_name + 1);
}
else {
StrAllocCat(subName,entry->d_name);
}
// Recurse, will do nothing if not a directory.
edt_RemoveDirectoryR(subName);
// If subName is a directory, it will already be gone, so this does nothing.
XP_FileRemove(subName,xpURL);
XP_FREEIF(subName);
}
}
XP_CloseDir(dir);
edt_RemoveDirectory(pName,xpURL);
}
XP_FREE(pName);
}
// Timer
void CEditTimerCallback (void * closure){
if (closure) {
CEditTimer* pTimer = (CEditTimer*) closure;
pTimer->Callback();
}
}
CEditTimer::CEditTimer(){
m_timeoutEnabled = FALSE;
m_timerID = NULL;
}
CEditTimer::~CEditTimer(){
Cancel();
}
void CEditTimer::Callback() {
m_timeoutEnabled = FALSE;
OnCallback();
}
void CEditTimer::Cancel(){
if ( m_timeoutEnabled ) {
FE_ClearTimeout(m_timerID);
m_timeoutEnabled = FALSE;
m_timerID = NULL;
}
}
void CEditTimer::OnCallback() {}
void CEditTimer::SetTimeout(uint32 msecs){
Cancel();
m_timerID = FE_SetTimeout(&CEditTimerCallback, this, msecs);
m_timeoutEnabled = TRUE;
}
// Color with a defined/undefined boolean
ED_Color::ED_Color() {
m_bDefined = TRUE;
m_color.red = 0;
m_color.green = 0;
m_color.blue = 0;
}
ED_Color::ED_Color(LO_Color& pLoColor) {
m_bDefined = TRUE;
m_color = pLoColor;
}
static int ED_Color_Clip(int c) {
if ( c < 0 ) {
XP_ASSERT(FALSE);
c = 0;
}
if ( c > 255 ) {
XP_ASSERT(FALSE);
c = 255;
}
return c;
}
ED_Color::ED_Color(int r, int g, int b) {
m_bDefined = TRUE;
m_color.red = ED_Color_Clip(r);
m_color.green = ED_Color_Clip(g);
m_color.blue = ED_Color_Clip(b);
}
ED_Color::ED_Color(int32 rgb) {
if ( rgb == -1 ) {
m_bDefined = FALSE;
m_color.red = 0;
m_color.green = 0;
m_color.blue = 0;
}
else {
m_bDefined = TRUE;
m_color.red = (rgb >> 16) & 255;
m_color.green = (rgb >> 8) & 255;
m_color.blue = rgb & 255;
}
}
ED_Color::ED_Color(LO_Color* pLoColor) {
if ( pLoColor ) {
m_color.red = ED_Color_Clip(pLoColor->red);
m_color.green = ED_Color_Clip(pLoColor->green);
m_color.blue = ED_Color_Clip(pLoColor->blue);
m_bDefined = TRUE;
}
else {
m_bDefined = FALSE;
}
}
XP_Bool ED_Color::operator==(const ED_Color& other) const {
return m_bDefined == other.m_bDefined &&
(!m_bDefined || m_color.red == other.m_color.red &&
m_color.green == other.m_color.green &&
m_color.blue == other.m_color.blue );
}
XP_Bool ED_Color::operator!=(const ED_Color& other) const {
return ! operator==(other);
}
XP_Bool ED_Color::IsDefined() { return m_bDefined; }
int ED_Color::Red() { XP_ASSERT(m_bDefined); return m_color.red; }
int ED_Color::Green() { XP_ASSERT(m_bDefined); return m_color.green; }
int ED_Color::Blue() { XP_ASSERT(m_bDefined); return m_color.blue; }
LO_Color ED_Color::GetLOColor() {XP_ASSERT(m_bDefined); return m_color; }
long ED_Color::GetAsLong() {
if ( ! m_bDefined ) return -1;
return (((long)m_color.red << 16) | ((long)m_color.green << 8) | (long)m_color.blue);
}
void
ED_Color::SetUndefined() {
m_bDefined = FALSE;
m_color.red = 0;
m_color.green = 0;
m_color.blue = 0;
}
ED_Color
ED_Color::GetUndefined()
{
ED_Color undef;
undef.SetUndefined();
return undef;
}
//-----------------------------------------------------------------------------
// Helper functions.
//-----------------------------------------------------------------------------
//
// Convert a tagtype to a string
//
static char* TagString(int32 tagType)
{
switch(tagType)
{
case P_UNKNOWN:
return "";
case P_TEXT:
return "text";
case P_TITLE:
return PT_TITLE;
case P_INDEX:
return PT_INDEX;
case P_BASE:
return PT_BASE;
case P_LINK:
return PT_LINK;
case P_HEADER_1:
return PT_HEADER_1;
case P_HEADER_2:
return PT_HEADER_2;
case P_HEADER_3:
return PT_HEADER_3;
case P_HEADER_4:
return PT_HEADER_4;
case P_HEADER_5:
return PT_HEADER_5;
case P_HEADER_6:
return PT_HEADER_6;
case P_ANCHOR:
return PT_ANCHOR;
case P_PARAGRAPH:
return PT_PARAGRAPH;
case P_ADDRESS:
return PT_ADDRESS;
case P_IMAGE:
return PT_IMAGE;
case P_NEW_IMAGE:
return PT_NEW_IMAGE;
case P_PLAIN_TEXT:
return PT_PLAIN_TEXT;
case P_PLAIN_PIECE:
return PT_PLAIN_PIECE;
case P_PREFORMAT:
return PT_PREFORMAT;
case P_LISTING_TEXT:
return PT_LISTING_TEXT;
case P_UNUM_LIST:
return PT_UNUM_LIST;
case P_NUM_LIST:
return PT_NUM_LIST;
case P_MENU:
return PT_MENU;
case P_DIRECTORY:
return PT_DIRECTORY;
case P_LIST_ITEM:
return PT_LIST_ITEM;
case P_DESC_LIST:
return PT_DESC_LIST;
case P_DESC_TITLE:
return PT_DESC_TITLE;
case P_DESC_TEXT:
return PT_DESC_TEXT;
case P_STRIKEOUT:
return PT_STRIKEOUT;
case P_STRIKE:
return PT_STRIKE;
case P_UNDERLINE:
return PT_UNDERLINE;
case P_FIXED:
return PT_FIXED;
case P_CENTER:
return PT_CENTER;
case P_EMBED:
return PT_EMBED;
case P_SUBDOC:
return PT_SUBDOC;
case P_CELL:
return PT_CELL;
case P_TABLE:
return PT_TABLE;
case P_CAPTION:
return PT_CAPTION;
case P_TABLE_ROW:
return PT_TABLE_ROW;
case P_TABLE_HEADER:
return PT_TABLE_HEADER;
case P_TABLE_DATA:
return PT_TABLE_DATA;
case P_BOLD:
return PT_BOLD;
case P_ITALIC:
return PT_ITALIC;
case P_EMPHASIZED:
return PT_EMPHASIZED;
case P_STRONG:
return PT_STRONG;
case P_CODE:
return PT_CODE;
case P_SAMPLE:
return PT_SAMPLE;
case P_KEYBOARD:
return PT_KEYBOARD;
case P_VARIABLE:
return PT_VARIABLE;
case P_CITATION:
return PT_CITATION;
case P_BLOCKQUOTE:
return PT_BLOCKQUOTE;
case P_FORM:
return PT_FORM;
case P_INPUT:
return PT_INPUT;
case P_SELECT:
return PT_SELECT;
case P_OPTION:
return PT_OPTION;
case P_TEXTAREA:
return PT_TEXTAREA;
case P_HRULE:
return PT_HRULE;
case P_BODY:
return PT_BODY;
case P_COLORMAP:
return PT_COLORMAP;
case P_HYPE:
return PT_HYPE;
case P_META:
return PT_META;
case P_LINEBREAK:
return PT_LINEBREAK;
case P_WORDBREAK:
return PT_WORDBREAK;
case P_NOBREAK:
return PT_NOBREAK;
case P_BASEFONT:
return PT_BASEFONT;
case P_FONT:
return PT_FONT;
case P_BLINK:
return PT_BLINK;
case P_BIG:
return PT_BIG;
case P_SMALL:
return PT_SMALL;
case P_SUPER:
return PT_SUPER;
case P_SUB:
return PT_SUB;
case P_GRID:
return PT_GRID;
case P_GRID_CELL:
return PT_GRID_CELL;
case P_NOGRIDS:
return PT_NOGRIDS;
case P_JAVA_APPLET:
return PT_JAVA_APPLET;
case P_MAP:
return PT_MAP;
case P_AREA:
return PT_AREA;
case P_DIVISION:
return PT_DIVISION;
case P_KEYGEN:
return PT_KEYGEN;
case P_MAX:
return "HTML";
case P_SCRIPT:
return PT_SCRIPT;
case P_NOEMBED:
return PT_NOEMBED;
case P_SERVER:
return PT_SERVER;
case P_PARAM:
return PT_PARAM;
case P_MULTICOLUMN:
return PT_MULTICOLUMN;
case P_SPACER:
return PT_SPACER;
case P_NOSCRIPT:
return PT_NOSCRIPT;
case P_HEAD:
return PT_HEAD;
case P_HTML:
return PT_HTML;
case P_CERTIFICATE:
return PT_CERTIFICATE;
case P_MQUOTE:
return PT_MQUOTE;
case P_STYLE:
return PT_STYLE;
case P_LAYER:
return PT_LAYER;
case P_ILAYER:
return PT_ILAYER;
case P_OBJECT:
return PT_OBJECT;
case P_SPAN:
return PT_SPAN;
case P_SPELL:
return PT_SPELL;
case P_INLINEINPUT:
return PT_INLINEINPUT;
case P_INLINEINPUTTHICK:
return PT_INLINEINPUTTHICK;
case P_INLINEINPUTDOTTED:
return PT_INLINEINPUTDOTTED;
case P_NSDT:
return PT_NSDT;
case P_NSCP_CLOSE:
return PT_NSCP_OPEN;
case P_NSCP_OPEN:
return PT_NSDT;
case P_NSCP_REBLOCK:
return PT_NSCP_REBLOCK;
case P_BLOCK:
return "block"; // PT_BLOCK
case P_NOLAYER:
return PT_NOLAYER; // (?) jrm 97/03/08 according to instructions below.
case P_BUILTIN:
return PT_BUILTIN;
default:
// If we get here, then it's a new tag that's been added to lib\libparse\pa_tags.h
// The fix is to add this new tag to the case statement above.
XP_ASSERT(FALSE);
return "UNKNOWN";
}
}
const int kTagBufferSize = 40;
static char tagBuffer[kTagBufferSize];
char* EDT_UpperCase(char* tagString)
{
int i = 0;
if ( tagString ) {
for ( i = 0; i < kTagBufferSize-1 && tagString[i] != '\0'; i++ ) {
tagBuffer[i] = XP_TO_UPPER(tagString[i]);
}
}
tagBuffer[i] = '\0';
return tagBuffer;
}
char *EDT_TagString(int32 tagType){
return EDT_UpperCase(TagString(tagType));
}
char* EDT_AlignString(ED_Alignment align){
if ( align < ED_ALIGN_CENTER ) {
XP_ASSERT(FALSE);
align = ED_ALIGN_CENTER;
}
if ( align > ED_ALIGN_ABSTOP ) {
XP_ASSERT(FALSE);
align = ED_ALIGN_ABSTOP;
}
return EDT_UpperCase(lo_alignStrings[align]);
}
ED_TextFormat edt_TagType2TextFormat( TagType t ){
switch(t){
case P_BOLD:
case P_STRONG:
return TF_BOLD;
case P_ITALIC:
case P_EMPHASIZED:
case P_VARIABLE:
case P_CITATION:
return TF_ITALIC;
case P_FIXED:
case P_CODE:
case P_KEYBOARD:
case P_SAMPLE:
return TF_FIXED;
case P_SUPER:
return TF_SUPER;
case P_SUB:
return TF_SUB;
case P_NOBREAK:
return TF_NOBREAK;
case P_STRIKEOUT:
return TF_STRIKEOUT;
case P_UNDERLINE:
return TF_UNDERLINE;
case P_BLINK:
return TF_BLINK;
case P_SERVER:
return TF_SERVER;
case P_SCRIPT:
return TF_SCRIPT;
case P_STYLE:
return TF_STYLE;
case P_SPELL:
return TF_SPELL;
case P_INLINEINPUT:
return TF_INLINEINPUT;
case P_INLINEINPUTTHICK:
return TF_INLINEINPUTTHICK;
case P_INLINEINPUTDOTTED:
return TF_INLINEINPUTDOTTED;
}
return TF_NONE;
}
static int workBufSize = 0;
static char* workBuf = 0;
char *edt_WorkBuf( int iSize ){
if( iSize > workBufSize ){
if( workBuf != 0 ){
delete[] workBuf;
}
workBuf = new char[ iSize ];
}
return workBuf;
}
char *edt_QuoteString( char* pString ){
int iLen = 0;
char *p = pString;
while( p && *p ){
if( *p == '"' ){
iLen += 6; // &quot;
}
else {
iLen++;
}
p++;
}
iLen++;
char *pRet = edt_WorkBuf( iLen+1 );
p = pRet;
while( pString && *pString ){
if( *pString == '"' ){
strcpy( p, "&quot;" );
p += 6;
}
else {
*p++ = *pString;
}
pString++;
}
*p = 0;
return pRet;
}
char *edt_MakeParamString( char* pString ){
int iLen = 0;
char *p = pString;
while( p && *p ){
if( *p == '"' ){
iLen += 6; // &quot;
}
else {
iLen++;
}
p++;
}
iLen += 3; // the beginning and ending quote and the trailing 0.
char *pRet = edt_WorkBuf( iLen+1 );
p = pRet;
if( pString && *pString == '`' ){
do {
*p++ = *pString++;
} while( *pString && *pString != '`' );
*p++ = '`';
*p = 0;
}
else {
*p = '"';
p++;
while( pString && *pString ){
if( *pString == '"' ){
strcpy( p, "&quot;" );
p += 6;
}
else {
*p++ = *pString;
}
pString++;
}
*p++ = '"';
*p = 0;
}
return pRet;
}
char *edt_FetchParamString( PA_Tag *pTag, char* param, int16 win_csid ){
PA_Block buff;
char *str;
char *retVal = 0;
buff = PA_FetchParamValue(pTag, param, win_csid);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
lo_StripTextWhitespace(str, XP_STRLEN(str));
if( str ) {
retVal = XP_STRDUP( str );
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
return retVal;
}
XP_Bool edt_FetchParamBoolExist( PA_Tag *pTag, char* param, int16 csid ){
PA_Block buff;
buff = PA_FetchParamValue(pTag, param, csid);
if (buff != NULL){
PA_FREE(buff);
return TRUE;
}
else {
return FALSE;
}
}
XP_Bool edt_FetchDimension( PA_Tag *pTag, char* param,
int32 *pValue, XP_Bool *pPercent,
int32 nDefaultValue, XP_Bool bDefaultPercent, int16 csid ){
PA_Block buff;
char *str;
int32 value;
// Fill in defaults
*pValue = nDefaultValue;
*pPercent = bDefaultPercent;
buff = PA_FetchParamValue(pTag, param, csid);
XP_Bool result = buff != NULL;
if( buff != NULL )
{
PA_LOCK(str, char *, buff);
if( str != 0 ){
value = XP_ATOI(str);
*pValue = value;
*pPercent = (NULL != XP_STRCHR(str, '%'));
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
return result;
}
ED_Alignment edt_FetchParamAlignment( PA_Tag* pTag, ED_Alignment eDefault, XP_Bool bVAlign, int16 csid ){
PA_Block buff;
char *str;
ED_Alignment retVal = eDefault;
buff = PA_FetchParamValue(pTag, bVAlign ? PARAM_VALIGN : PARAM_ALIGN, csid);
if (buff != NULL)
{
XP_Bool floating;
floating = FALSE;
PA_LOCK(str, char *, buff);
lo_StripTextWhitespace(str, XP_STRLEN(str));
// LTNOTE: this is a hack. We should do a better job maping these
// could cause problems in the future.
retVal = (ED_Alignment) lo_EvalAlignParam(str, &floating);
PA_UNLOCK(buff);
PA_FREE(buff);
}
return retVal;
}
int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue, int16 csid ){
return edt_FetchParamInt(pTag, param, defValue, defValue, csid);
}
int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue, int32 defValueIfParamButNoValue, int16 csid ){
PA_Block buff;
char *str;
int32 retVal = defValue;
buff = PA_FetchParamValue(pTag, param, csid);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
if( str != 0 ){
if ( *str == '\0' ) { /* They mentioned the param, but gave to value. */
retVal = defValueIfParamButNoValue;
}
else {
retVal = XP_ATOI(str);
}
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
return retVal;
}
ED_Color edt_FetchParamColor( PA_Tag *pTag, char* param, int16 csid ){
PA_Block buff = PA_FetchParamValue(pTag, param, csid);
ED_Color retVal;
retVal.SetUndefined();
if (buff != NULL)
{
char *color_str;
PA_LOCK(color_str, char *, buff);
lo_StripTextWhitespace(color_str, XP_STRLEN(color_str));
uint8 red, green, blue;
LO_ParseRGB(color_str, &red, &green, &blue);
retVal = EDT_RGB(red,green,blue);
PA_UNLOCK(buff);
PA_FREE(buff);
}
return retVal;
}
PRIVATE
XP_Bool edt_NameInList( char* pName, char** ppNameList ){
int i = 0;
while( ppNameList && ppNameList[i] ){
if( XP_STRCASECMP( pName, ppNameList[i]) == 0 ){
return TRUE;
}
i++;
}
return FALSE;
}
char* edt_FetchParamExtras( PA_Tag *pTag, char**ppKnownParams, int16 win_csid ){
char** ppNames;
char** ppValues;
char* pRet = 0;
int i = 0;
char *pSpace = "";
int iMax;
iMax = PA_FetchAllNameValues( pTag, &ppNames, &ppValues, win_csid );
while( i < iMax ){
if( !edt_NameInList( ppNames[i], ppKnownParams ) ){
if( ppValues[i] ){
pRet = PR_sprintf_append( pRet, "%s%s=%s",
pSpace, ppNames[i], edt_MakeParamString( ppValues[i] ) );
}
else {
pRet = PR_sprintf_append( pRet, "%s%s", pSpace, ppNames[i] );
}
pSpace = " ";
}
i++;
}
i = 0;
while( i < iMax ){
if( ppNames[i] ) XP_FREE( ppNames[i] );
if( ppValues[i] ) XP_FREE( ppValues[i] );
i++;
}
if( ppNames ) XP_FREE( ppNames );
if( ppValues ) XP_FREE( ppValues );
return pRet;
}
// Override existing.
void edt_FetchParamString2(PA_Tag* pTag, char* param, char*& data, int16 win_csid){
char* result = edt_FetchParamString( pTag, param, win_csid );
if ( result ) {
if ( data ){
XP_FREE(data);
}
data = result;
}
}
// Override existing.
void edt_FetchParamColor2( PA_Tag *pTag, char* param, ED_Color& data, int16 csid ){
ED_Color result = edt_FetchParamColor(pTag, param, csid);
if ( result.IsDefined() ) {
data = result;
}
}
// Append to existing extras. To Do: Have new versions of a parameter overwrite the old ones.
void edt_FetchParamExtras2( PA_Tag *pTag, char**ppKnownParams, char*& data, int16 win_csid ){
char* result = edt_FetchParamExtras( pTag, ppKnownParams, win_csid );
if ( result ) {
if ( data ) {
data = PR_sprintf_append(data, " %s", result);
XP_FREE(result);
}
else {
data = result;
}
}
}
XP_Bool edt_ReplaceParamValue( PA_Tag *pTag, char * pName, char * pValue, int16 csid ){
XP_ASSERT(pName);
XP_ASSERT(pTag);
char** ppNames;
char** ppValues;
char* pNew = 0;
int i = 0;
char *pSpace = "";
int iMax;
XP_Bool bFound = FALSE;
iMax = PA_FetchAllNameValues( pTag, &ppNames, &ppValues, csid );
while( i < iMax ){
if( XP_STRCASECMP( pName, ppNames[i]) == 0 ){
bFound = TRUE;
if( pValue){
pNew = PR_sprintf_append( pNew, "%s%s=%s",
pSpace, pName, edt_MakeParamString( pValue ) );
}
} else {
if( ppValues[i] ){
pNew = PR_sprintf_append( pNew, "%s%s=%s",
pSpace, ppNames[i], edt_MakeParamString( ppValues[i] ) );
}
else {
pNew = PR_sprintf_append( pNew, "%s%s", pSpace, ppNames[i] );
}
pSpace = " ";
}
i++;
}
// Add value at end if not found
if( !bFound && pValue ){
pNew = PR_sprintf_append( pNew, "%s%s=%s", pSpace, pName, pValue );
}
// Terminate
pNew = PR_sprintf_append( pNew, "%s", ">" );
// Cleanup memory
i = 0;
while( i < iMax ){
if( ppNames[i] ) XP_FREE( ppNames[i] );
if( ppValues[i] ) XP_FREE( ppValues[i] );
i++;
}
if( ppNames ) XP_FREE( ppNames );
if( ppValues ) XP_FREE( ppValues );
// Now replace with new data
PA_Block buff;
char *locked_buff;
int iLen;
iLen = XP_STRLEN(pNew);
buff = (PA_Block)PA_ALLOC((iLen+1) * sizeof(char));
if (buff != NULL) {
PA_LOCK(locked_buff, char *, buff);
XP_BCOPY(pNew, locked_buff, iLen);
locked_buff[iLen] = '\0';
PA_UNLOCK(buff);
if( pTag->data ) XP_FREE(pTag->data);
pTag->data = buff;
pTag->data_len = iLen;
} else {
// LTNOTE: out of memory, should throw an exception
}
return bFound;
}
LO_Color* edt_MakeLoColor(ED_Color c) {
if( !c.IsDefined()){
return 0;
}
LO_Color *pColor = XP_NEW( LO_Color );
pColor->red = EDT_RED(c);
pColor->green = EDT_GREEN(c);
pColor->blue = EDT_BLUE(c);
return pColor;
}
void edt_SetLoColor( ED_Color c, LO_Color *pColor ){
if( !c.IsDefined()){
XP_ASSERT(FALSE);
return;
}
pColor->red = EDT_RED(c);
pColor->green = EDT_GREEN(c);
pColor->blue = EDT_BLUE(c);
}
#define CHARSET_SIZE 256
static PA_AmpEsc *ed_escapes[CHARSET_SIZE];
void edt_InitEscapes(int16 /*csid*/, XP_Bool bQuoteHiBits){
PA_AmpEsc *pEsc = PA_AmpEscapes;
XP_BZERO( ed_escapes, CHARSET_SIZE * sizeof( PA_AmpEsc* ) );
while( pEsc->value ){
int ch = 0xff & (int) pEsc->value;
if( ed_escapes[ ch ] == 0 ){
if ( ch == '&' || ch == '<' || ch == '>'
|| (NON_BREAKING_SPACE == ((char) ch))
|| (ch >= 128 && bQuoteHiBits ) ) {
ed_escapes[ ch ] = pEsc;
}
}
pEsc++;
}
}
void edt_PrintWithEscapes( CPrintState *ps, char *p, XP_Bool bBreakLines ){
int csid = ps->m_pBuffer->GetRAMCharSetID();
char *pBegin;
PA_AmpEsc *pEsc;
// break the lines after 70 characters if we can, but don't really do it
// in the formatted case.
while( p && *p ){
pBegin = p;
while( *p && ps->m_iCharPos < 70 && ed_escapes[(unsigned char)*p] == 0
&& ((unsigned char) *p) != ((unsigned char) NON_BREAKING_SPACE) ){
char* p2 = INTL_NextChar(csid, p);
int charWidth = p2 - p;
ps->m_iCharPos += charWidth;
p = p2;
}
while( *p && *p != ' ' && ed_escapes[(unsigned char)*p] == 0
&& ((unsigned char) *p) != ((unsigned char) NON_BREAKING_SPACE)){
char* p2 = INTL_NextChar(csid, p);
int charWidth = p2 - p;
ps->m_iCharPos += charWidth;
p = p2;
}
ps->m_pOut->Write( pBegin, p-pBegin);
if( *p && *p == ' ' && ps->m_iCharPos >= 70 ){
if( bBreakLines ){
ps->m_pOut->Write( "\n", 1 );
}
else {
ps->m_pOut->Write( " ", 1 );
}
ps->m_iCharPos = 0;
}
else if( *p && (pEsc = ed_escapes[(unsigned char)*p]) != 0 ){
ps->m_pOut->Write( "&",1 );
ps->m_pOut->Write( pEsc->str, pEsc->len );
ps->m_pOut->Write( ";",1 );
ps->m_iCharPos += pEsc->len+2;
}
else if (((unsigned char) *p) == ((unsigned char) NON_BREAKING_SPACE) ){
const char* nbsp = INTL_NonBreakingSpace(csid);
int nbsp_len = XP_STRLEN(nbsp);
ps->m_pOut->Write( (char*) nbsp, XP_STRLEN(nbsp));
}
// move past the space or special char
if( *p ){
p = INTL_NextChar(csid, p);
}
}
}
char *edt_LocalName( char *pURL ){
char *pDest = FE_URLToLocalName( pURL );
#if 0
if( pDest && FE_EditorPrefConvertFileCaseOnWrite( ) ){
char *url_ptr = pDest;
while ( *url_ptr != '\0' ){
*url_ptr = XP_TO_LOWER(*url_ptr);
url_ptr++;
}
}
#endif
return pDest;
}
PA_Block PA_strdup( char* s ){
char *new_str;
PA_Block new_buff;
if( s == 0 ){
return 0;
}
new_buff = (PA_Block)PA_ALLOC(XP_STRLEN(s) + 1);
if (new_buff != NULL)
{
PA_LOCK(new_str, char *, new_buff);
XP_STRCPY(new_str, s);
PA_UNLOCK(new_buff);
}
return new_buff;
}
void edt_SetTagData( PA_Tag* pTag, char* pTagData){
PA_Block buff;
char *locked_buff;
int iLen;
iLen = XP_STRLEN(pTagData);
buff = (PA_Block)PA_ALLOC((iLen+1) * sizeof(char));
if (buff != NULL)
{
PA_LOCK(locked_buff, char *, buff);
XP_BCOPY(pTagData, locked_buff, iLen);
locked_buff[iLen] = '\0';
PA_UNLOCK(buff);
}
else {
// LTNOTE: out of memory, should throw an exception
return;
}
pTag->data = buff;
pTag->data_len = iLen;
pTag->next = NULL;
return;
}
//
// Create a tag and add it to the list
//
void edt_AddTag( PA_Tag*& pStart, PA_Tag*& pEnd, TagType t, XP_Bool bIsEnd,
char *pTagData ){
PA_Tag *pTag = XP_NEW( PA_Tag );
XP_BZERO( pTag, sizeof( PA_Tag ) );
pTag->type = t;
pTag->is_end = bIsEnd;
edt_SetTagData(pTag, pTagData ? pTagData : ">" );
if( pStart == 0 ){
pStart = pTag;
}
if( pEnd ){
pEnd->next = pTag;
}
pEnd = pTag;
}
//-----------------------------------------------------------------------------
// CEditPosition
//-----------------------------------------------------------------------------
void CEditPositionComparable::CalcPosition( TXP_GrowableArray_int32* pA,
CEditPosition *pPos ){
CEditElement *pElement, *pParent, *pSib;
int i;
pA->Empty();
pA->Add( pPos->Offset() );
pElement = pPos->Element();
if( pElement ){
while( (pParent = pElement->GetParent()) != NULL ){
i = 0;
pSib = pParent->GetChild();
while( pSib != pElement ){
pSib = pSib->GetNextSibling();
i++;
}
pA->Add(i);
pElement = pParent;
}
}
}
int CEditPositionComparable::Compare( CEditPosition *pPos ){
TXP_GrowableArray_int32 toArray;
CalcPosition( &toArray, pPos );
int32 i = m_Array.Size();
int32 iTo = toArray.Size();
int32 iDiff;
while( i && iTo ){
iDiff = toArray[--iTo] - m_Array[--i];
if( iDiff != 0 ) {
return (iDiff > 0 ? 1 : -1 );
}
}
if( i == 0 && iTo == 0){
return 0;
}
iDiff = iTo - i;
return (iDiff > 0 ? 1 : -1 );
}
// CEditInsertPoint
CEditInsertPoint::CEditInsertPoint() {
m_pElement = 0;
m_iPos = 0;
m_bStickyAfter = FALSE;
}
CEditInsertPoint::CEditInsertPoint(CEditLeafElement* pElement, ElementOffset iPos) {
m_pElement = pElement;
m_iPos = iPos;
m_bStickyAfter = FALSE;
}
CEditInsertPoint::CEditInsertPoint(CEditElement* pElement, ElementOffset iPos){
m_pElement = pElement->Leaf();
m_iPos = iPos;
m_bStickyAfter = FALSE;
}
CEditInsertPoint::CEditInsertPoint(CEditElement* pElement, ElementOffset iPos, XP_Bool bStickyAfter){
m_pElement = pElement->Leaf();
m_iPos = iPos;
m_bStickyAfter = bStickyAfter;
}
XP_Bool CEditInsertPoint::operator==(const CEditInsertPoint& other ) {
return m_pElement == other.m_pElement && m_iPos == other.m_iPos;
}
XP_Bool CEditInsertPoint::operator!=(const CEditInsertPoint& other ) {
return ! (*this == other);
}
XP_Bool CEditInsertPoint::operator<(const CEditInsertPoint& other ) {
return Compare(other) < 0;
}
XP_Bool CEditInsertPoint::operator<=(const CEditInsertPoint& other ) {
return Compare(other) <= 0;
}
XP_Bool CEditInsertPoint::operator>=(const CEditInsertPoint& other ) {
return Compare(other) >= 0;
}
XP_Bool CEditInsertPoint::operator>(const CEditInsertPoint& other ) {
return Compare(other) > 0;
}
CEditElement* CEditInsertPoint::FindNonEmptyElement( CEditElement *pStartElement ){
CEditElement* pOldElement = NULL;
while ( pStartElement && pStartElement != pOldElement ) {
pOldElement = pStartElement;
if( !pStartElement->IsLeaf() ){
pStartElement = pStartElement->PreviousLeaf();
}
else if( pStartElement->Leaf()->GetLayoutElement() == 0 ){
pStartElement = pStartElement->PreviousLeaf();
}
}
return pStartElement;
}
int CEditInsertPoint::Compare(const CEditInsertPoint& other ) {
XP_Bool bAIsBreak = FALSE;
LO_Element* pA = m_pElement->GetLayoutElement();
if ( ! pA ) {
bAIsBreak = TRUE;
pA = FindNonEmptyElement(m_pElement)->Leaf()->GetLayoutElement();
}
XP_Bool bBIsBreak = FALSE;
LO_Element* pB = other.m_pElement->GetLayoutElement();
if ( ! pB ) {
bBIsBreak = TRUE;
pB = FindNonEmptyElement(other.m_pElement)->Leaf()->GetLayoutElement();
}
if ( !pA || !pB ) {
XP_ASSERT(FALSE); // Phantom insert points.
return 0;
}
int32 aIndex = pA->lo_any.ele_id;
int32 bIndex = pB->lo_any.ele_id;
if ( aIndex < bIndex ) {
return -1;
}
else if ( aIndex == bIndex ) {
// Same element. Compare positions.
if ( m_iPos < other.m_iPos ) {
return -1;
}
else if ( m_iPos == other.m_iPos ) {
if ( bAIsBreak < bBIsBreak ) {
return -1;
}
else if ( bAIsBreak == bBIsBreak ) {
return 0;
}
else {
return 1;
}
}
else {
return 1;
}
}
else {
return 1;
}
}
XP_Bool CEditInsertPoint::IsDenormalizedVersionOf(const CEditInsertPoint& other){
return other.m_iPos == 0 &&
m_iPos == m_pElement->GetLen() &&
m_pElement->NextLeaf() == other.m_pElement;
}
XP_Bool CEditInsertPoint::IsStartOfElement() {
return m_iPos <= 0;
}
XP_Bool CEditInsertPoint::IsEndOfElement() {
return m_iPos >= m_pElement->GetLen();
}
XP_Bool CEditInsertPoint::IsStartOfContainer() {
return IsStartOfElement()
&& m_pElement->PreviousLeafInContainer() == NULL;
}
XP_Bool CEditInsertPoint::IsEndOfContainer() {
return IsEndOfElement()
&& m_pElement->LeafInContainerAfter() == NULL;
}
XP_Bool CEditInsertPoint::IsStartOfDocument(){
return IsStartOfElement() &&
m_pElement->PreviousLeaf() == NULL;
}
XP_Bool CEditInsertPoint::IsEndOfDocument(){
return m_pElement->IsEndOfDocument();
}
XP_Bool CEditInsertPoint::GapWithBothSidesAllowed(){
XP_Bool bResult = FALSE;
XP_Bool bAllowBothSidesOfGap = m_pElement->AllowBothSidesOfGap();
if ( bAllowBothSidesOfGap
&& IsEndOfElement()
&& !IsEndOfContainer() ) {
bResult = TRUE;
}
else if ( IsStartOfElement() && ! IsStartOfContainer() ) {
if ( bAllowBothSidesOfGap ) {
bResult = TRUE;
}
else {
CEditLeafElement* pPrev = m_pElement->PreviousLeaf();
if ( pPrev && pPrev->AllowBothSidesOfGap() ) {
bResult = TRUE;
}
}
}
return bResult;
}
XP_Bool CEditInsertPoint::IsLineBreak(){
return IsHardLineBreak() || IsSoftLineBreak();
}
XP_Bool CEditInsertPoint::IsSoftLineBreak(){
XP_Bool result = FALSE;
CEditTextElement* pText = CEditTextElement::Cast(m_pElement);
if ( pText ){
int iOffset;
LO_Element* pLOElement;
if ( pText->GetLOElementAndOffset(m_iPos, m_bStickyAfter,
pLOElement, iOffset) ){
if ( pLOElement->type == LO_LINEFEED ){
result = TRUE;
}
}
}
return result;
}
XP_Bool CEditInsertPoint::IsHardLineBreak(){
return IsStartOfElement() && m_pElement->CausesBreakBefore()
|| IsEndOfElement() && m_pElement->CausesBreakAfter();
}
XP_Bool CEditInsertPoint::IsSpace() {
XP_Bool result = FALSE;
if ( m_pElement->IsA(P_TEXT) ) {
if ( m_iPos == m_pElement->GetLen() ){
CEditLeafElement *pNext = m_pElement->TextInContainerAfter();
if( pNext
&& pNext->IsA(P_TEXT)
&& pNext->Text()->GetLen() != 0
&& pNext->Text()->GetText()[0] == ' ') {
result = TRUE;
}
}
else if ( m_pElement->Text()->GetText()[m_iPos] == ' ' ) {
result = TRUE;
}
}
return result;
}
XP_Bool CEditInsertPoint::IsSpaceBeforeOrAfter() {
XP_Bool result = IsSpace();
if ( !result ) {
CEditInsertPoint before = PreviousPosition();
if ( before != *this ) {
result = before.IsSpace();
}
}
return result;
}
CEditInsertPoint CEditInsertPoint::NextPosition(){
CEditInsertPoint result;
m_pElement->NextPosition(m_iPos, result.m_pElement, result.m_iPos);
return result;
}
CEditInsertPoint CEditInsertPoint::PreviousPosition(){
CEditInsertPoint result;
m_pElement->PrevPosition(m_iPos, result.m_pElement, result.m_iPos);
// Work around what is probably a bug in PrevPosition.
if ( m_iPos == 0 && result.m_iPos == result.m_pElement->GetLen() ) {
result.m_pElement->PrevPosition(result.m_iPos, result.m_pElement, result.m_iPos);
}
return result;
}
#ifdef DEBUG
void CEditInsertPoint::Print(IStreamOut& stream) {
stream.Printf("0x%08lx.%d%s", m_pElement, m_iPos, m_bStickyAfter ? "+" : "" );
}
#endif
// CEditSelection
CEditSelection::CEditSelection(){
m_bFromStart = FALSE;
}
CEditSelection::CEditSelection(CEditElement* pStart, intn iStartPos,
CEditElement* pEnd, intn iEndPos, XP_Bool fromStart)
: m_start(pStart, iStartPos), m_end(pEnd, iEndPos), m_bFromStart(fromStart)
{
}
CEditSelection::CEditSelection(const CEditInsertPoint& start,
const CEditInsertPoint& end, XP_Bool fromStart)
: m_start(start), m_end(end), m_bFromStart(fromStart)
{
}
XP_Bool CEditSelection::operator==(const CEditSelection& other ){
return m_start == other.m_start &&
m_end == other.m_end && m_bFromStart == other.m_bFromStart;
}
XP_Bool CEditSelection::operator!=(const CEditSelection& other ){
return ! (*this == other);
}
XP_Bool CEditSelection::EqualRange(CEditSelection& other){
return m_start == other.m_start && m_end == other.m_end;
}
XP_Bool CEditSelection::IsInsertPoint() {
/* It's an insert point if the two edges are equal, OR if the
* start is at the end of an element, and the
* end is at the start of an element, and
* the two elements are next to each other,
* and they're in the same container. (Whew!)
*/
return m_start == m_end ||
( m_start.IsEndOfElement() &&
m_end.IsStartOfElement() &&
m_start.m_pElement->LeafInContainerAfter() == m_end.m_pElement
);
}
CEditInsertPoint* CEditSelection::GetEdge(XP_Bool bEnd){
if ( bEnd ) return &m_end;
else return &m_start;
}
CEditInsertPoint* CEditSelection::GetActiveEdge(){
return GetEdge(!m_bFromStart);
}
CEditInsertPoint* CEditSelection::GetAnchorEdge(){
return GetEdge(m_bFromStart);
}
XP_Bool CEditSelection::Intersects(CEditSelection& other) {
return m_start < other.m_end && other.m_start < m_end;
}
XP_Bool CEditSelection::Contains(CEditInsertPoint& point) {
return m_start <= point && point < m_end;
}
XP_Bool CEditSelection::Contains(CEditSelection& other) {
return ContainsStart(other) && other.m_end <= m_end;
}
XP_Bool CEditSelection::ContainsStart(CEditSelection& other) {
return Contains(other.m_start);
}
XP_Bool CEditSelection::ContainsEnd(CEditSelection& other) {
return Contains(other.m_end) || other.m_end == m_end;
}
XP_Bool CEditSelection::ContainsEdge(CEditSelection& other, XP_Bool bEnd){
if ( bEnd )
return ContainsEnd(other);
else
return ContainsStart(other);
}
XP_Bool CEditSelection::CrossesOver(CEditSelection& other) {
XP_Bool bContainsStart = ContainsStart(other);
XP_Bool bContainsEnd = ContainsEnd(other);
return bContainsStart ^ bContainsEnd;
}
XP_Bool CEditSelection::ClipTo(CEditSelection& other) {
// True if the result is defined. No change to this if it isn't
XP_Bool intersects = Intersects(other);
if ( intersects ) {
if ( m_start < other.m_start ) {
m_start = other.m_start;
}
if ( other.m_end < m_end ) {
m_end = other.m_end;
}
}
return intersects;
}
CEditElement* CEditSelection::GetCommonAncestor(){
return m_start.m_pElement->GetCommonAncestor(m_end.m_pElement);
}
XP_Bool CEditSelection::CrossesSubDocBoundary(){
// The intent of this method is "If this selection were cut, would the
// document become malformed?"
// A document could be malformed if the selection crosses the table
// boundary, or if it would leave a table without it's protective
// buffer of containers.
XP_Bool result = FALSE;
if ( ! IsInsertPoint() ){
CEditElement* pStartCell = m_start.m_pElement->GetSubDocOrLayerSkipRoot();
CEditElement* pEndCell = m_end.m_pElement->GetSubDocOrLayerSkipRoot();
CEditElement* pClosedEndCell = GetClosedEndContainer()->GetSubDocOrLayerSkipRoot();
result = pStartCell != pEndCell || pStartCell != pClosedEndCell;
if ( result ) {
// This is only OK if the selection spans an entire table.
Bool bStartBad = FALSE;
Bool bEndBad = FALSE;
if ( pStartCell ) {
CEditSelection all;
pStartCell->GetTableOrLayerIgnoreSubdoc()->GetAll(all);
bStartBad = ! Contains(all);
}
if ( pEndCell ) {
CEditSelection all;
pEndCell->GetTableOrLayerIgnoreSubdoc()->GetAll(all);
bEndBad = ! Contains(all);
}
result = bStartBad || bEndBad;
}
else if ( pStartCell && pEndCell != pClosedEndCell ) {
// If the selection spans an entire cell, but not an entire table, that's bad
CEditSelection all;
pStartCell->GetTableOrLayerIgnoreSubdoc()->GetAll(all);
result = *this != all;
}
else {
// One further check: If the selection starts at the start of
// a paragraph, and the selection ends at the end of a paragraph
// and the previous item is a table, and the
// next item is not a container, then we can't cut. The reason is
// that tables need to have containers after them.
// Note that m_end is one more than is selected.
if ( m_start.IsStartOfContainer() &&
m_end.IsStartOfContainer() ) {
// The selection spans whole containers.
CEditContainerElement* pStartContainer = GetStartContainer();
CEditElement* pPreviousSib = pStartContainer->GetPreviousSibling();
if ( pPreviousSib && pPreviousSib->IsTable() ) {
CEditContainerElement* pEndContainer = GetClosedEndContainer();
CEditElement* pNextSib = pEndContainer->GetNextSibling();
if ( !pNextSib || pNextSib->IsEndContainer() || pNextSib->IsTable() ) {
result = TRUE;
}
}
}
}
}
return result;
}
CEditSubDocElement* CEditSelection::GetEffectiveSubDoc(XP_Bool bEnd){
CEditInsertPoint* pEdge = GetEdge(bEnd);
CEditSubDocElement* pSubDoc = pEdge->m_pElement->GetSubDoc();
while ( pSubDoc ) {
// If the edge is actually the edge of a subdoc, skip out of the subdoc.
CEditSelection wholeSubDoc;
pSubDoc->GetAll(wholeSubDoc);
// If this is a wholeSubDoc Selection, bump up
if ( *this != wholeSubDoc ) {
if ( *pEdge != *wholeSubDoc.GetEdge(bEnd) )
break;
// If the other end of the selection is is in this same subdoc, stop
if ( wholeSubDoc.ContainsEdge(*this, !bEnd) )
break;
}
CEditElement* pSubDocParent = pSubDoc->GetParent();
if ( !pSubDocParent )
break;
CEditSubDocElement* pParentSubDoc = pSubDocParent->GetSubDoc();
if ( ! pParentSubDoc )
break;
pSubDoc = pParentSubDoc;
}
return pSubDoc;
}
XP_Bool CEditSelection::ExpandToNotCrossStructures(){
XP_Bool bChanged = FALSE;
if ( ! IsInsertPoint() ){
CEditElement* pGrandfatherStart = m_start.m_pElement->GetParent()->GetParent();
CEditElement* pGrandfatherEnd;
CEditElement* pEffectiveEndContianer;
if ( m_end.IsEndOfDocument() || EndsAtStartOfContainer()) {
pEffectiveEndContianer = m_end.m_pElement->PreviousLeaf()->GetParent();
}
else {
pEffectiveEndContianer = m_end.m_pElement->GetParent();
}
pGrandfatherEnd = pEffectiveEndContianer->GetParent();
if ( pGrandfatherStart != pGrandfatherEnd ){
bChanged = TRUE;
CEditElement* pAncestor = pGrandfatherStart->GetCommonAncestor(pGrandfatherEnd);
if ( ! pAncestor ) {
XP_ASSERT(FALSE);
return FALSE;
}
// If we're in a table, we need to return the whole table.
if ( pAncestor->IsTableRow() ) {
pAncestor = pAncestor->GetTable();
if ( ! pAncestor ) {
XP_ASSERT(FALSE);
return FALSE;
}
}
if ( pAncestor->IsTable() ) {
pAncestor->GetAll(*this);
return TRUE;
}
CEditLeafElement* pCleanStart = pAncestor->GetChildContaining(m_start.m_pElement)->GetFirstMostChild()->Leaf();
CEditLeafElement* pCleanEnd = pAncestor->GetChildContaining(pEffectiveEndContianer)->GetLastMostChild()->NextLeaf();
m_start.m_pElement = pCleanStart;
m_start.m_iPos = 0;
if ( pCleanEnd ) {
m_end.m_pElement = pCleanEnd;
m_end.m_iPos = 0;
}
else {
XP_ASSERT(FALSE); // Somehow we've lost the end of the document element.
pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->Leaf();
m_end.m_pElement = pCleanEnd;
m_end.m_iPos = pCleanEnd->Leaf()->GetLen();
}
}
}
return bChanged;
}
void CEditSelection::ExpandToBeCutable(){
// Very similar to ExpandToNotCrossDocumentStructures, except that
// the result is always Cutable.
if ( ! IsInsertPoint() ){
CEditElement* pGrandfatherStart = m_start.m_pElement->GetParent()->GetParent();
CEditElement* pGrandfatherEnd;
CEditElement* pEffectiveEndContianer;
if ( m_end.IsEndOfDocument()) {
pEffectiveEndContianer = m_end.m_pElement->PreviousLeaf()->GetParent();
}
else {
pEffectiveEndContianer = m_end.m_pElement->GetParent();
}
pGrandfatherEnd = pEffectiveEndContianer->GetParent();
if ( pGrandfatherStart != pGrandfatherEnd ){
CEditElement* pAncestor = pGrandfatherStart->GetCommonAncestor(pGrandfatherEnd);
if ( ! pAncestor ) {
XP_ASSERT(FALSE);
return;
}
// If we're in a table, we need to return the whole table.
if ( pAncestor->IsTableRow() ) {
pAncestor = pAncestor->GetTable();
if ( ! pAncestor ) {
XP_ASSERT(FALSE);
return;
}
}
if ( pAncestor->IsTable() ) {
pAncestor->GetAll(*this);
return;
}
CEditLeafElement* pCleanStart = pAncestor->GetChildContaining(m_start.m_pElement)->GetFirstMostChild()->Leaf();
CEditLeafElement* pCleanEnd = pAncestor->GetChildContaining(pEffectiveEndContianer)->GetLastMostChild()->NextLeaf();
m_start.m_pElement = pCleanStart;
m_start.m_iPos = 0;
if ( pCleanEnd ) {
m_end.m_pElement = pCleanEnd;
m_end.m_iPos = 0;
}
else {
XP_ASSERT(FALSE); // Somehow we've lost the end of the document element.
pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->Leaf();
m_end.m_pElement = pCleanEnd;
m_end.m_iPos = pCleanEnd->Leaf()->GetLen();
}
}
}
}
void CEditSelection::ExpandToIncludeFragileSpaces() {
// Expand the selection to include spaces that will be
// trimmed if we did a cut of the original selection.
if ( ! IsInsertPoint() ){
CEditInsertPoint before = m_start.PreviousPosition();
// Either we backed up one position, or we hit the beginning
// of the document.
if ( before == m_start || before.IsSpace() ) {
if ( m_end.IsSpace() ) {
if ( ! m_end.m_pElement->InFormattedText() ) {
m_end = m_end.NextPosition();
}
}
}
}
}
void CEditSelection::ExpandToEncloseWholeContainers(){
XP_Bool bWasInsertPoint = IsInsertPoint();
CEditLeafElement* pCleanStart = m_start.m_pElement->GetParent()->GetFirstMostChild()->Leaf();
m_start.m_pElement = pCleanStart;
m_start.m_iPos = 0;
if ( !m_end.IsStartOfContainer() || bWasInsertPoint ) {
CEditLeafElement* pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->NextLeaf();
if ( pCleanEnd ) {
m_end.m_pElement = pCleanEnd;
m_end.m_iPos = 0;
}
else {
XP_ASSERT(FALSE); // Somehow we've lost the end of the document element.
pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->Leaf();
m_end.m_pElement = pCleanEnd;
m_end.m_iPos = pCleanEnd->Leaf()->GetLen();
}
}
}
XP_Bool CEditSelection::EndsAtStartOfContainer() {
return m_end.IsStartOfContainer();
}
XP_Bool CEditSelection::StartsAtEndOfContainer() {
return m_start.IsEndOfContainer();
}
XP_Bool CEditSelection::AnyLeavesSelected(){
// FALSE if insert point or container end.
return ! (IsInsertPoint() || IsContainerEnd());
}
XP_Bool CEditSelection::IsContainerEnd(){
XP_Bool result = FALSE;
if ( ! IsInsertPoint() && EndsAtStartOfContainer()
&& StartsAtEndOfContainer() ) {
CEditContainerElement* pStartContainer = m_start.m_pElement->FindContainer();
CEditContainerElement* pEndContainer = m_end.m_pElement->FindContainer();
if ( pStartContainer && pEndContainer ) {
CEditContainerElement* pNextContainer =
CEditContainerElement::Cast(pStartContainer->FindNextElement(&CEditElement::FindContainer, 0));
if ( pNextContainer == pEndContainer) {
result = TRUE;
}
}
}
return result;
}
void CEditSelection::ExcludeLastDocumentContainerEnd() {
// Useful for cut & delete, where you don't replace the final container mark
if ( ContainsLastDocumentContainerEnd() ){
CEditLeafElement* pEnd = m_end.m_pElement;
pEnd = pEnd->PreviousLeaf();
if ( pEnd ) {
m_end.m_pElement = pEnd;
m_end.m_iPos = pEnd->Leaf()->GetLen();
if ( m_start > m_end ) {
m_start = m_end;
}
}
}
}
XP_Bool CEditSelection::ContainsLastDocumentContainerEnd() {
// Useful for cut & delete, where you don't replace the final container mark
XP_Bool bResult = FALSE;
CEditLeafElement* pEnd = m_end.m_pElement;
if ( pEnd && pEnd->GetElementType() == eEndElement ){
bResult = TRUE;
}
return bResult;
}
CEditContainerElement* CEditSelection::GetStartContainer() {
CEditElement* pParent = m_start.m_pElement->GetParent();
if ( pParent && pParent->IsContainer() ) return pParent->Container();
else return NULL;
}
CEditContainerElement* CEditSelection::GetClosedEndContainer() {
CEditElement* pEnd = m_end.m_pElement;
if ( m_end.IsStartOfContainer() ) {
// Back up one
CEditElement* pPrev = pEnd->PreviousLeaf();
if ( pPrev ) {
pEnd = pPrev;
}
else pEnd = m_start.m_pElement;
}
CEditElement* pParent = pEnd->GetParent();
if ( pParent && pParent->IsContainer() ) return pParent->Container();
else return NULL;
}
#ifdef DEBUG
void CEditSelection::Print(IStreamOut& stream){
stream.Printf("start ");
m_start.Print(stream);
stream.Printf(" end ");
m_end.Print(stream);
stream.Printf(" FromStart %ld", (long)m_bFromStart);
}
#endif
// Persistent selections
CPersistentEditInsertPoint::CPersistentEditInsertPoint()
{
m_index = -1;
m_bStickyAfter = TRUE;
}
CPersistentEditInsertPoint::CPersistentEditInsertPoint(ElementIndex index)
: m_index(index), m_bStickyAfter(TRUE)
{}
CPersistentEditInsertPoint::CPersistentEditInsertPoint(ElementIndex index, XP_Bool bStickyAfter)
: m_index(index), m_bStickyAfter(bStickyAfter)
{
}
XP_Bool CPersistentEditInsertPoint::operator==(const CPersistentEditInsertPoint& other ) {
return m_index == other.m_index;
}
XP_Bool CPersistentEditInsertPoint::operator!=(const CPersistentEditInsertPoint& other ) {
return ! (*this == other);
}
XP_Bool CPersistentEditInsertPoint::operator<(const CPersistentEditInsertPoint& other ) {
return m_index < other.m_index;
}
XP_Bool CPersistentEditInsertPoint::operator<=(const CPersistentEditInsertPoint& other ) {
return *this < other || *this == other;
}
XP_Bool CPersistentEditInsertPoint::operator>=(const CPersistentEditInsertPoint& other ) {
return ! (*this < other);
}
XP_Bool CPersistentEditInsertPoint::operator>(const CPersistentEditInsertPoint& other ) {
return ! (*this <= other);
}
#ifdef DEBUG
void CPersistentEditInsertPoint::Print(IStreamOut& stream) {
stream.Printf("%ld%s", (long)m_index, m_bStickyAfter ? "+" : "" );
}
#endif
void CPersistentEditInsertPoint::ComputeDifference(
CPersistentEditInsertPoint& other, CPersistentEditInsertPoint& delta){
// delta = this - other;
delta.m_index = m_index - other.m_index;
}
void CPersistentEditInsertPoint::AddRelative(
CPersistentEditInsertPoint& delta, CPersistentEditInsertPoint& result){
// result = this + delta;
result.m_index = m_index + delta.m_index;
}
XP_Bool CPersistentEditInsertPoint::IsEqualUI(
const CPersistentEditInsertPoint& other ) {
return m_index == other.m_index && m_bStickyAfter == other.m_bStickyAfter;
}
// class CPersistentEditSelection
CPersistentEditSelection::CPersistentEditSelection()
{
m_bFromStart = FALSE;
}
CPersistentEditSelection::CPersistentEditSelection(const CPersistentEditInsertPoint& start, const CPersistentEditInsertPoint& end)
: m_start(start), m_end(end)
{
m_bFromStart = FALSE;
}
ElementIndex CPersistentEditSelection::GetCount() {
return m_end.m_index - m_start.m_index;
}
XP_Bool CPersistentEditSelection::IsInsertPoint(){
return m_start == m_end;
}
XP_Bool CPersistentEditSelection::operator==(const CPersistentEditSelection& other ) {
return SelectedRangeEqual(other) && m_bFromStart == other.m_bFromStart;
}
XP_Bool CPersistentEditSelection::operator!=(const CPersistentEditSelection& other ) {
return ! ( *this == other );
}
XP_Bool CPersistentEditSelection::SelectedRangeEqual(const CPersistentEditSelection& other ) {
return m_start == other.m_start && m_end == other.m_end;
}
CPersistentEditInsertPoint* CPersistentEditSelection::GetEdge(XP_Bool bEnd){
return bEnd ? &m_end : &m_start;
}
#ifdef DEBUG
void CPersistentEditSelection::Print(IStreamOut& stream) {
stream.Printf("start ");
m_start.Print(stream);
stream.Printf(" end ");
m_end.Print(stream);
stream.Printf(" FromStart %ld", (long)m_bFromStart);
}
#endif
// Used for undo
// change this by the same way that original was changed into modified.
void CPersistentEditSelection::Map(CPersistentEditSelection& original,
CPersistentEditSelection& modified){
CPersistentEditSelection delta;
CPersistentEditSelection result;
modified.m_start.ComputeDifference(original.m_start, delta.m_start);
modified.m_end.ComputeDifference(original.m_end, delta.m_end);
m_start.AddRelative(delta.m_start, result.m_start);
m_end.AddRelative(delta.m_end, result.m_end);
*this = result;
}
//
// Call this constructor with a maximum number, a series of bits and
// BIT_ARRAY_END for example: CBitArray a(100, 1 ,4 9 ,7, BIT_ARRAY_END);
//
CBitArray::CBitArray(long n, int iFirst, ...) {
m_Bits = 0;
size = 0;
int i;
if( n ) {
SetSize(n);
}
va_list stack;
#ifdef OSF1
va_start( stack, n );
#else
va_start( stack, iFirst );
(*this)[iFirst] = 1;
#endif
while( (i = va_arg(stack,int)) != BIT_ARRAY_END ){
(*this)[i] = 1;
}
}
void CBitArray::SetSize(long n){
// On Win16 we can't have a size larger that int.
XP_ASSERT(size < (1L << (16 + 3)));
uint8 *oldBits = m_Bits;
m_Bits = new uint8[n/8+1];
memset(m_Bits,0,(int) (n/8+1));
if(oldBits){
memcpy(m_Bits,oldBits,(int) (min(n,size)/8+1));
delete oldBits;
}
size = n;
}
void CBitArray::ClearAll(){
memset(m_Bits,0, (int) (size/8+1) );
}
/* CLM: Helper to gray UI items not allowed when inside Java Script
* Note that the return value is FALSE if partial selection,
* allowing the non-script text to be changed
* Current Font Size, Color, and Character attributes will suppress
* setting other attributes, so it is OK to call these when mixed
*/
#ifdef USE_SCRIPT
XP_Bool EDT_IsJavaScript(MWContext * pMWContext)
{
if(!pMWContext) return FALSE;
XP_Bool bRetVal = FALSE;
EDT_CharacterData * pData = EDT_GetCharacterData(pMWContext);
if( pData) {
bRetVal = ( (0 != (pData->mask & TF_SERVER)) &&
(0 != (pData->values & TF_SERVER)) ) ||
( (0 != (pData->mask & TF_STYLE)) &&
(0 != (pData->values & TF_STYLE)) ) ||
( (0 != (pData->mask & TF_SCRIPT )) &&
(0 != (pData->values & TF_SCRIPT)) );
EDT_FreeCharacterData(pData);
}
return bRetVal;
}
#else
XP_Bool EDT_IsJavaScript(MWContext *)
{
return FALSE;
}
#endif
/* Helper to use for enabling Character properties
* (Bold, Italic, etc., but DONT use for clearing (TF_NONE)
* or setting Java Script (Server or Client)
* Tests for:
* 1. Good edit buffer and not blocked because of some action,
* 2. Caret or selection is NOT entirely within Java Script,
* 3. Caret or selection has some text or is mixed selection
* (thus FALSE if single non-text object is selected)
*/
XP_Bool EDT_CanSetCharacterAttribute(MWContext * pMWContext)
{
if( !pMWContext || pMWContext->waitingMode || EDT_IsBlocked(pMWContext) ){
return FALSE;
}
ED_ElementType type = EDT_GetCurrentElementType(pMWContext);
return ( (type == ED_ELEMENT_TEXT || type == ED_ELEMENT_SELECTION ||
type >= ED_ELEMENT_TABLE) && !EDT_IsJavaScript(pMWContext) );
}
// TODO: Should probably go through all containers in a selection
XP_Bool EDT_IsPreformat(MWContext *pMWContext)
{
GET_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer) FALSE;
CEditInsertPoint ip;
pEditBuffer->GetInsertPoint(ip);
if( ip.m_pElement )
{
CEditContainerElement *pContainer = ip.m_pElement->FindContainer();
if( pContainer )
return pContainer->GetType() == P_PREFORMAT;
}
return FALSE;
}
// XP string defines for FontFaces
extern "C" {
#include "xpgetstr.h"
#define WANT_ENUM_STRING_IDS
#include "allxpstr.h"
#undef WANT_ENUM_STRING_IDS
}
int iFontFaces[] = {
XP_NSFONT_DEFAULT,
XP_NSFONT_FIXED,
XP_NSFONT_ARIAL,
XP_NSFONT_TIMES,
XP_NSFONT_COURIER
};
// IDs for the list of fonts written to the tags
// MUST CORRESPOND IN NUMBER AND LOCATION TO iFontFaces!
int iFontFaceTags[] = {
0, 0,
XP_NSFONTLIST_ARIAL,
XP_NSFONTLIST_TIMES,
XP_NSFONTLIST_COURIER
};
#define ED_NSFONT_MAX ((sizeof(iFontFaceTags))/sizeof(int))
// Cache the lists of font faces that we show to user
// and list of tags (no shown to user)
static char * pFontFaces = NULL;
static char * pFontFaceTags = NULL;
char *EDT_GetFontFaces()
{
if( pFontFaces ){
return pFontFaces;
}
intn iSize = 512;
int iCur = 0;
pFontFaces = (char*)XP_ALLOC( iSize );
if( !pFontFaces )
return NULL;
pFontFaces[0] = 0;
pFontFaces[1] = 0;
for( int i = 0; i < ED_NSFONT_MAX; i++ ){
char *pFont = XP_GetString(iFontFaces[i]);
if( pFont && *pFont ){
int iLen = XP_STRLEN( pFont );
if( iCur + iLen + 2 > iSize ){
iSize += 512;
pFontFaces = (char*)XP_REALLOC( pFontFaces, iSize );
if( ! pFontFaces ){
return NULL;
}
}
XP_STRCPY( &pFontFaces[iCur], pFont );
iCur += iLen+1;
}
}
// Append extra '\0' at end
pFontFaces[iCur] = 0;
return pFontFaces;
}
char *EDT_GetFontFaceTags()
{
if( pFontFaceTags ){
return pFontFaceTags;
}
intn iSize = 2024;
pFontFaceTags = (char*)XP_ALLOC( iSize );
if( !pFontFaceTags )
return NULL;
// First 2 strings are ignored - map to default proportional and fixed
XP_STRCPY(pFontFaceTags, " ");
XP_STRCPY(&pFontFaceTags[2], " ");
int iCur = 4; // Start at 4th character - after above strings
pFontFaceTags[5] = 0; // Add extra 0 in case there are no more strings
for( int i = 2; i < ED_NSFONT_MAX; i++ ){
char *pFont = XP_GetString(iFontFaceTags[i]);
if( pFont && *pFont ){
int iLen = XP_STRLEN( pFont );
if( iCur + iLen + 2 > iSize ){
iSize += 2024;
pFontFaceTags = (char*)XP_REALLOC( pFontFaceTags, iSize );
if( ! pFontFaceTags ){
return NULL;
}
}
XP_STRCPY( &pFontFaceTags[iCur], pFont );
iCur += iLen+1;
}
}
// Append extra '\0' at end
pFontFaceTags[iCur] = 0;
return pFontFaceTags;
}
#define EDT_CLEAR_BIT(x,tf) x &= ~tf
#define EDT_SET_BIT(x,tf) x |= tf
void EDT_SetFontFace(MWContext * pMWContext, EDT_CharacterData * pCharacterData,
int iFontIndex, char * pFontFace )
{
// Must have one or the other
if( pMWContext == NULL && pCharacterData == NULL )
return;
#if defined(XP_WIN) || defined(XP_OS2)
// Windows may give us this if click off of the font combobox
if( (long)pFontFace == -1L )
return;
#endif
EDT_CharacterData * pData;
XP_Bool bCanCopy;
XP_Bool bHaveData;
// Only 0 and 1 have any meaning now that XP groups are "automatic"
if( iFontIndex < 0 || iFontIndex > 1 ){
iFontIndex = -1;
}
if( pCharacterData ){
// Use supplied structure
pData = pCharacterData;
bHaveData = TRUE;
// If pointer to existing data is same as new, don't copy it
bCanCopy = pData->pFontFace != pFontFace;
// We will respect the TF_FONT_FACE bit in existing data,
// but we are also interested in FIXED if we are doing
if( pData->mask & TF_FONT_FACE ){
EDT_SET_BIT(pData->mask, TF_FIXED);
}
} else {
// No data supplied, get from current selection or at caret
bHaveData = FALSE;
pData = EDT_GetCharacterData(pMWContext);
if( !pData )
return;
bCanCopy = TRUE;
// Set the bit to tell that we are interested in font face
// and fixed width
pData->mask = TF_FONT_FACE | TF_FIXED;
}
// Remove any existing font face data if not same as new data
if( pData->pFontFace && bCanCopy){
XP_FREEIF(pData->pFontFace);
}
// This will force using whatever user string was supplied
if( pFontFace ){
// So it will be skipped in test below
iFontIndex = -1;
if( bCanCopy) {
// Translate into XP font "group" if face is found in any group
char *pTranslated = EDT_TranslateToXPFontFace(pFontFace);
if( *pTranslated == '_' ){
// First character may have signal for separator - skip over it
pTranslated++;
}
// Detect the 1st two items -- the Default Variable and Fixed Width fonts
if( 0 == XP_STRCMP(pTranslated, XP_GetString(iFontFaces[0])) ){
iFontIndex = 0;
} else if( 0 == XP_STRCMP(pTranslated, XP_GetString(iFontFaces[1])) ){
iFontIndex = 1;
} else {
// Copy the font face - either same as pFontFace or the entire group if translated
pData->pFontFace = XP_STRDUP(pTranslated);
// Set font face tag - note that it is set
EDT_SET_BIT(pData->values, TF_FONT_FACE);
// We can't be fixed width if here
EDT_CLEAR_BIT(pData->values, TF_FIXED);
}
}
}
if( iFontIndex == 0 || iFontIndex == 1 ){
// No font face tag will be set
EDT_CLEAR_BIT(pData->values, TF_FONT_FACE);
if( iFontIndex == 1){
// Fixed Width
EDT_SET_BIT(pData->values, TF_FIXED);
} else {
// Default proportional font
EDT_CLEAR_BIT(pData->values, TF_FIXED);
}
}
if( pMWContext ){
EDT_SetCharacterData(pMWContext, pData);
}
// Free only the data we obtained
if( !bHaveData ){
EDT_FreeCharacterData(pData);
}
}
char * EDT_TranslateToXPFontFace( char * pFontFace )
{
char * pFontFaceTags = EDT_GetFontFaceTags();
if( pFontFaceTags ){
char * pFace = pFontFace;
// Skip over initial separator signal
if( *pFace == '_' ){
pFace++;
}
// Skip over first two strings (each contains " ")
char * pTag = pFontFaceTags+4;
int iLen;
int i = 0;
while( (iLen = XP_STRLEN(pTag)) > 0 ) {
char * pComma;
char * pTagFace = pTag;
// Scan for comma separating the next font in group
while( (pComma = XP_STRCHR(pTagFace,',')) != 0 ){
*pComma = '\0';
int iFaceLen = XP_STRLEN(pTagFace);
XP_Bool bFound = (0 == XP_STRCMP(pTagFace, pFace));
*pComma = ',';
if( bFound ){
// We found the font in the list - we're done
return pTag;
}
pTagFace += iFaceLen+1;
}
// Check one more time - either no commas found or
// pFace is on the last font in the group
if( 0 == XP_STRCMP(pTagFace, pFace) ){
return pTag;
}
pTag += iLen+1;
i++;
}
}
// Return exactly what we were passed if not found in font groups
return pFontFace;
}
// Cache the list of colors that we show to user
//static char * pFontColors = NULL;
// Use this array instead of pFontColor list
// This replaces the XP_Strings colors
uint8 NSColors[MAX_NS_COLORS][3] =
{ { 255,255,255 },
{ 204,204,204 },
{ 192,192,192 },
{ 153,153,153 },
{ 102,102,102 },
{ 51,51,51 },
{ 0,0,0 },
{ 255,204,204 },
{ 255,102,102 },
{ 255,0,0 },
{ 204,0,0 },
{ 153,0,0 },
{ 102,0,0 },
{ 51,0,0 },
{ 255,204,153 },
{ 255,204,51 },
{ 255,153,0 },
{ 255,102,0 },
{ 204,102,0 },
{ 153,51,0 },
{ 102,51,0 },
{ 255,255,153 },
{ 255,255,102 },
{ 255,204,102 },
{ 255,204,51 },
{ 204,153,51 },
{ 153,102,51 },
{ 102,51,51 },
{ 255,255,204 },
{ 255,255,153 },
{ 255,255,0 },
{ 255,204,0 },
{ 153,153,0 },
{ 102,102,0 },
{ 51,51,0 },
{ 153,255,153 },
{ 102,255,153 },
{ 51,255,51 },
{ 0,204,0 },
{ 0,153,0 },
{ 0,102,0 },
{ 0,51,0 },
{ 153,255,255 },
{ 51,255,255 },
{ 102,204,204 },
{ 0,204,204 },
{ 51,153,153 },
{ 51,102,102 },
{ 0,51,51 },
{ 204,255,255 },
{ 102,255,255 },
{ 51,204,255 },
{ 51,102,255 },
{ 51,51,255 },
{ 0,0,153 },
{ 0,0,102 },
{ 204,204,255 },
{ 153,153,204 },
{ 102,102,204 },
{ 102,51,255 },
{ 102,0,204 },
{ 51,51,153 },
{ 51,0,153 },
{ 255,204,255 },
{ 255,153,255 },
{ 204,102,204 },
{ 204,51,204 },
{ 153,51,102 },
{ 102,51,102 },
{ 51,0,51 } };
void EDT_GetNSColor(intn iIndex, LO_Color * pLoColor)
{
if( pLoColor && iIndex < MAX_NS_COLORS )
{
pLoColor->red = NSColors[iIndex][0];
pLoColor->green = NSColors[iIndex][1];
pLoColor->blue = NSColors[iIndex][2];
}
}
/* Return list of font colors in format: "r,g,b,ColorName" where colors for r,g,b
* are decimal strings in range 0-255
*/
#if defined(XP_UNIX)
char *EDT_GetFontColors()
{
return NULL;
#if 0
if( pFontColors )
return pFontColors;
intn iSize = 512;
int iCur = 0;
pFontColors = (char*)XP_ALLOC( iSize );
if( !pFontColors )
return NULL;
pFontColors[0] = 0;
pFontColors[1] = 0;
int nIDColor = XP_NSCOLOR_BASE;
while( nIDColor < XP_NSCOLOR_END){
char *pColor = XP_GetString(nIDColor);
if( pColor && *pColor ){
int iLen = XP_STRLEN( pColor );
if( iCur + iLen + 2 > iSize ){
iSize += 512;
pFontColors = (char*)XP_REALLOC( pFontColors, iSize );
if( ! pFontColors ){
return NULL;
}
}
XP_STRCPY( &pFontColors[iCur], pColor );
iCur += iLen+1;
}
// Next color ID
nIDColor++;
}
// Append extra '\0' at end
pFontColors[iCur] = 0;
return pFontColors;
#endif
}
#endif
char * EDT_ParseColorString(LO_Color * pLoColor, char * pColorString)
{
XP_ASSERT(pLoColor);
XP_ASSERT(pColorString);
char *pComma = XP_STRCHR(pColorString, ',');
if(!pComma) return pColorString;
*pComma = '\0';
pLoColor->red = (uint8)atoi(pColorString);
*pComma = ',';
char *pColor = pComma+1;
pComma = XP_STRCHR(pColor, ',');
if(!pComma) return pColor;
*pComma = '\0';
pLoColor->green = (uint8)atoi(pColor);
*pComma = ',';
pColor = pComma+1;
pComma = XP_STRCHR(pColor, ',');
// If no comma found, assume color string
// doesn't have HTML-HEX format and
// all thats left is the blue value
if(pComma)
*pComma = '\0';
pLoColor->blue = (uint8)atoi(pColor);
if(pComma)
{
*pComma = ',';
pColor = pComma+1;
// Return rest of string, assumned to be Hex representation
return pColor;
}
// Supply a hex translation
static char pHexColor[16];
XP_SPRINTF(pHexColor, "#%02X%02X%02X", pLoColor->red, pLoColor->green, pLoColor->blue);
return pHexColor;
}
/* Scan our list of colors and return index of matching color
* or -1 if no match found. We NEVER return 0 (default color)
*/
int EDT_GetMatchingFontColorIndex(LO_Color * pLOColor)
{
for( intn i = 0; i < MAX_NS_COLORS; i++ )
{
if( NSColors[i][0] == pLOColor->red &&
NSColors[i][1] == pLOColor->green &&
NSColors[i][2] == pLOColor->blue ){
return (i+1);
}
}
return -1;
}
#ifdef DEBUG
// Automated test routine hook
const char* CEditTestManager::m_kTrigger = "$$$Test";
CEditTestManager::CEditTestManager(CEditBuffer* pBuffer)
: m_pBuffer(pBuffer),
m_bTesting(FALSE),
m_iTriggerIndex(0),
m_pSaveBuffer(0),
m_pTempFileURL(0)
{
#ifdef XP_WIN32
// 5/28/98 ??? This is crashing -- Why???
// _CrtMemCheckpoint( &m_state ); // In theorey, avoid measuring the data before we were created.
#endif
PowerOnTest();
}
void CEditTestManager::DumpMemoryDelta() {
m_pBuffer->Trim(); // Clear out undo/redo log to simplify memory statistics.
#ifdef XP_WIN32
_CrtMemDumpAllObjectsSince( &m_state );
_CrtMemCheckpoint( &m_state );
#else
XP_TRACE(("Not supported on this OS.\n"));
#endif
}
XP_Bool CEditTestManager::Key(char key) {
XP_Bool result = m_bTesting;
if ( ! m_bTesting ){
if ( key == m_kTrigger[m_iTriggerIndex] ) {
m_iTriggerIndex++;
if ( m_kTrigger[m_iTriggerIndex] == '\0' ) {
m_bTesting = TRUE;
XP_TRACE(("Testing on! Type # of test, or Q to quit."));
}
}
else {
m_iTriggerIndex = 0;
}
}
else {
int bQuitTesting = FALSE;
m_bTesting = FALSE; // So when the tests type, it doesn't cause a recursion into the test code.
intn bTestResult = -1;
if ( key >= 'A' && key <= 'Z' ) key = key + ('a' - 'A');
switch (key) {
case 'q':
bQuitTesting = TRUE;
XP_TRACE(("Quitting test mode."));
break;
case 'a':
DumpLoElements();
break;
case 'b':
VerifyLoElements();
break;
case 'c':
DumpDocumentContents();
break;
case 'd':
DumpMemoryDelta();
break;
case 'g':
GetDocTempDir();
break;
case '!':
PasteINTL();
break;
case 'w':
PasteINTLText();
break;
case 'x':
CopyDocumentToBuffer();
break;
case 'y':
CopyBufferToDocument();
break;
#ifdef EDITOR_JAVA
case 'j':
DumpPlugins();
break;
case 'k':
PerformFirstPlugin();
break;
case 'l':
PerformPluginByName();
break;
case 'm':
PerformFirstEncoder();
break;
case 'n':
PerformPreOpen();
break;
#endif
case 's':
SaveToTempFile();
break;
case 't':
RemoveTempFile();
break;
case 'v':
TestHTMLPaste();
break;
case '0':
bTestResult = EveryNavigationKeyEverywhereTest();
break;
case '1':
bTestResult = ArrowTest();
break;
case '2':
bTestResult = BackspaceKeyTest();
break;
case '3':
bTestResult = DeleteKeyTest();
break;
case '4':
bTestResult = ZeroDocumentCursorTest();
break;
case '5':
bTestResult = OneCharDocumentCursorTest();
break;
case '6':
bTestResult = BoldTest(10); // OneDayTest(1);
break;
case '7':
m_pBuffer->m_bSkipValidation = TRUE;
bTestResult = OneDayTest(100);
m_pBuffer->m_bSkipValidation = FALSE;
break;
case '8':
bTestResult = TextFETest();
break;
case '?':
XP_TRACE(("? - type this help\n"
"Q - quit.\n"
"a - print lo-elements.\n"
"b - verify lo-elements.\n"
"c - print document contents (elements, undo history, etc.)\n"
"d - dump memory usage delta.\n"
"g - get doc temp directory. \n"
));
#ifdef EDITOR_JAVA
XP_TRACE(("j - show Composer Plugin and Image Encoder information.\n"
"k - run the first composer plugin.\n"
"l - run a composer plugin by name.\n"
"m - run the first image encoder.\n"
"n - test the EDT_PreOpen call.\n"
));
#endif
XP_TRACE(("q - test HTML Paste Quoted.\n"
));
XP_TRACE(("s - save doc to temporary file.\n"
));
XP_TRACE(("t - remove saved temporary file.\n"
));
XP_TRACE(("x - save document state.\n"
"y - restore document state.\n"
));
XP_TRACE((
"0 - navigation key crash test.\n"
"1 - arrow completeness test.\n"
"2 - destructive backspace test.\n"
"3 - destructive delete test.\n"
"4 - empty document cursor test.\n"
"5 - one character document cursor test.\n"
"6 - one day document test - simulate editing for one whole day 1 cycle.\n"
"7 - one day document test - simulate editing for one whole day 500 cycle.\n"
"8 - write buffer to out.txt as plain text.\n"
));
break;
default:
XP_TRACE(("Type ? for help, Type # of test, or Q to quit."));
break;
}
if ( ! bQuitTesting )
{
m_bTesting = TRUE;
}
if ( bTestResult >= 0 ){
XP_TRACE(("Test %s", bTestResult ? "Passed": "Failed"));
}
}
return result;
}
void CEditTestManager::PowerOnTest() {
// Test some things that have to be tested by code. This method is
// called whenever the editor starts up in debug mode. Don't take too long.
// Test that we know about all the existing tags.
for(int i = 0; i < P_MAX; i++ ){
EDT_TagString(i);
}
}
void CEditTestManager::DumpLoElements() {
lo_PrintLayout(m_pBuffer->m_pContext);
}
void CEditTestManager::VerifyLoElements() {
lo_VerifyLayout(m_pBuffer->m_pContext);
}
void CEditTestManager::DumpDocumentContents(){
CStreamOutMemory buffer;
m_pBuffer->printState.Reset( &buffer, m_pBuffer );
m_pBuffer->DebugPrintTree( m_pBuffer->m_pRoot );
m_pBuffer->DebugPrintState(buffer);
TraceLargeString(buffer.GetText());
}
void TraceLargeString(char* b){
// have to dump one line at a time. XP_TRACE has a 512 char limit on Windows.
while ( *b != '\0' ) {
char* b2 = b;
while ( *b2 != '\0' && *b2 != '\n'){
b2++;
}
char old = *b2;
*b2 = '\0';
XP_TRACE(("%s", b));
*b2 = old;
b = b2 + 1;
if ( old == '\0' ) break;
}
}
void CEditTestManager::PasteINTL(){
unsigned char testStringJIS[] = {'J','I','S',' ','[',27,'$','B',30,21,']',' ',0};
unsigned char testStringEUC1[] = {'E','U','C',' ','[', 0xb0, 0};
unsigned char tesrStringEUC2[] = {0xa1,']',' ',0};
unsigned char testStringSJIS[] = {'S','J','I','S',' ','[',0x88,0x9f,']',0};
MWContext* pContext = m_pBuffer->m_pContext;
EDT_PasteQuoteBegin(pContext, TRUE);
EDT_PasteQuoteINTL(pContext, (char*) testStringJIS, CS_JIS);
EDT_PasteQuoteINTL(pContext, (char*) testStringEUC1, CS_EUCJP);
EDT_PasteQuoteINTL(pContext, (char*) tesrStringEUC2, CS_EUCJP);
EDT_PasteQuoteINTL(pContext, (char*) testStringSJIS, CS_SJIS);
EDT_PasteQuoteEnd(pContext);
}
void CEditTestManager::PasteINTLText(){
unsigned char testStringJIS[] = {'J','I','S',' ','[',27,'$','B',30,21,']',' ',0};
unsigned char testStringEUC[] = {'E','U','C',' ','[', 0xb0, 0xa1,']',' ',0};
unsigned char testStringSJIS[] = {'S','J','I','S',' ','[',0x88,0x9f,']',0};
MWContext* pContext = m_pBuffer->m_pContext;
EDT_PasteQuoteBegin(pContext, FALSE);
EDT_PasteQuoteINTL(pContext, (char*) testStringJIS, CS_JIS);
EDT_PasteQuoteINTL(pContext, (char*) testStringEUC, CS_EUCJP);
EDT_PasteQuoteINTL(pContext, (char*) testStringSJIS, CS_SJIS);
EDT_PasteQuoteEnd(pContext);
}
void CEditTestManager::CopyDocumentToBuffer(){
if ( m_pSaveBuffer){
delete[] m_pSaveBuffer;
m_pSaveBuffer = 0;
}
m_pBuffer->WriteToBuffer(&m_pSaveBuffer, TRUE);
XP_TRACE(("Buffer size: %d bytes", XP_STRLEN(m_pSaveBuffer)));
}
void CEditTestManager::CopyBufferToDocument(){
if ( m_pSaveBuffer == NULL ) {
XP_TRACE(("The save buffer is empty."));
}
else {
m_pBuffer->ReadFromBuffer(m_pSaveBuffer);
}
}
static const char* kChunkName[EDT_NA_COUNT] = {
"character", "word", "line edge", "document", "updown"
};
XP_Bool CEditTestManager::ZeroDocumentCursorTest(){
XP_TRACE(("ZeroDocumentCursorTest"));
// [selection][chunk][direction][edge]
const ElementIndex kExpectedResults[2][EDT_NA_COUNT][2][2] =
{ // no selection
{
{{ 0, 0}, {0, 0} }, // EDT_NA_CHARACTER
{{ 0, 0}, {0, 0} }, // EDT_NA_WORD
{{ 0, 0}, {0, 0} }, // EDT_NA_LINEEDGE
{{ 0, 0}, {0, 0} }, // EDT_NA_DOCUMENT
{{ 0, 0}, {0, 0} } // EDT_NA_UPDOWN
},
// selection
{
{{ 0, 0}, {0, 1} }, // EDT_NA_CHARACTER
{{ 0, 0}, {0, 1} }, // EDT_NA_WORD
{{ 0, 0}, {0, 1} }, // EDT_NA_LINEEDGE
{{ 0, 0}, {0, 1} }, // EDT_NA_DOCUMENT
{{ 0, 0}, {0, 1} } // EDT_NA_UPDOWN
}
};
XP_Bool result = TRUE;
// Clear everything from existing document
EDT_SelectAll(m_pBuffer->m_pContext);
EDT_DeleteChar(m_pBuffer->m_pContext);
CPersistentEditInsertPoint zero(0,FALSE);
// Verify the cursor does the right thing for every possible cursor
for ( int select = 0; select < 2; select++ ) {
for ( int chunk = 0; chunk < EDT_NA_COUNT; chunk++ ) {
for ( int direction = 0; direction < 2; direction++ ) {
m_pBuffer->SetInsertPoint(zero);
m_pBuffer->NavigateChunk(select, chunk, direction);
CPersistentEditSelection selection;
m_pBuffer->GetSelection(selection);
for ( int edge = 0; edge < 2; edge++ ) {
ElementIndex expected = kExpectedResults[select][chunk][direction][edge];
ElementIndex actual = selection.GetEdge(edge)->m_index;
if ( expected != actual ){
result = FALSE;
XP_TRACE(("selection: %s chunk: %s direction: %s edge: %s. Expected %d got %d",
select ? "TRUE" : "FALSE",
kChunkName[chunk],
direction ? "forward" : "back",
edge ? "end" : "start",
expected,
actual));
}
}
}
}
}
return result;
}
XP_Bool CEditTestManager::OneCharDocumentCursorTest(){
XP_TRACE(("OneCharDocumentCursorTest"));
// [startPos][selection][chunk][direction][edge]
const ElementIndex kExpectedResults[2][2][EDT_NA_COUNT][2][2] =
{
// position 0
{ // no selection
{
{{ 0, 0}, {1, 1} }, // EDT_NA_CHARACTER
{{ 0, 0}, {1, 1} }, // EDT_NA_WORD
{{ 0, 0}, {1, 1} }, // EDT_NA_LINEEDGE
{{ 0, 0}, {1, 1} }, // EDT_NA_DOCUMENT
{{ 0, 0}, {0, 0} } // EDT_NA_UPDOWN
},
// selection
{
{{ 0, 0}, {0, 1} }, // EDT_NA_CHARACTER
{{ 0, 0}, {0, 1} }, // EDT_NA_WORD
{{ 0, 0}, {0, 2} }, // EDT_NA_LINEEDGE
{{ 0, 0}, {0, 2} }, // EDT_NA_DOCUMENT
{{ 0, 0}, {0, 2} } // EDT_NA_UPDOWN
}
},
// position 1
{ // no selection
{
{{ 0, 0}, {1, 1} }, // EDT_NA_CHARACTER
{{ 0, 0}, {1, 1} }, // EDT_NA_WORD
{{ 0, 0}, {1, 1} }, // EDT_NA_LINEEDGE
{{ 0, 0}, {1, 1} }, // EDT_NA_DOCUMENT
{{ 1, 1}, {1, 1} } // EDT_NA_UPDOWN
},
// selection
{
{{ 0, 1}, {1, 2} }, // EDT_NA_CHARACTER
{{ 0, 1}, {1, 2} }, // EDT_NA_WORD
{{ 0, 1}, {1, 2} }, // EDT_NA_LINEEDGE
{{ 0, 1}, {1, 2} }, // EDT_NA_DOCUMENT
{{ 0, 1}, {1, 2} } // EDT_NA_UPDOWN
}
}
};
XP_Bool result = TRUE;
// Clear everything from existing document
EDT_SelectAll(m_pBuffer->m_pContext);
EDT_DeleteChar(m_pBuffer->m_pContext);
m_pBuffer->InsertChar( 'X', FALSE );
// Verify the cursor does the right thing for every possible cursor
for ( int startPos = 0; startPos < 2; startPos++ ){
CPersistentEditInsertPoint p(startPos,FALSE);
for ( int select = 0; select < 2; select++ ) {
for ( int chunk = 0; chunk < EDT_NA_COUNT; chunk++ ) {
for ( int direction = 0; direction < 2; direction++ ) {
m_pBuffer->SetInsertPoint(p);
m_pBuffer->NavigateChunk(select, chunk, direction);
CPersistentEditSelection selection;
m_pBuffer->GetSelection(selection);
for ( int edge = 0; edge < 2; edge++ ) {
ElementIndex expected = kExpectedResults[startPos][select][chunk][direction][edge];
ElementIndex actual = selection.GetEdge(edge)->m_index;
if ( expected != actual ){
result = FALSE;
XP_TRACE(("position: %d selection: %s chunk: %s direction: %s edge: %s. Expected %d got %d",
startPos,
select ? "TRUE" : "FALSE",
kChunkName[chunk],
direction ? "forward" : "back",
edge ? "end" : "start",
expected,
actual));
}
}
}
}
}
}
return result;
}
XP_Bool CEditTestManager::OneDayTest(int32 rounds) {
char* kTitle = "One Day Test\n";
XP_TRACE(("%s", kTitle));
EDT_SelectAll(m_pBuffer->m_pContext);
EDT_DeleteChar(m_pBuffer->m_pContext);
EDT_PasteText(m_pBuffer->m_pContext, kTitle);
for( int32 i = 0; i < rounds; i++ ) {
XP_TRACE(("Round %d", i));
const char* kTestString = "All work and no play makes Jack a dull boy.\nRedrum.\n";
char c;
for ( int j = 0;
(c = kTestString[j]) != '\0';
j++ ) {
if ( c == '\n' || c == '\r') {
EDT_ReturnKey(m_pBuffer->m_pContext);
}
else {
EDT_KeyDown(m_pBuffer->m_pContext, c,0,0);
}
}
// And delete a little.
for ( int k = 0; k < 8; k++ ) {
EDT_DeletePreviousChar(m_pBuffer->m_pContext);
}
}
XP_TRACE(("End of %s.", kTitle));
return TRUE;
}
static void TextFETestDone(PrintSetup *setup) {
XP_FileClose(setup->out);
XP_TRACE(("Finished TextFETest."));
}
XP_Bool CEditTestManager::TextFETest() {
PrintSetup setup;
char *_srcName = 0;
char *srcName = 0;
char *srcURL = 0;
char *destName = 0;
XP_File destFile = 0;
URL_Struct *srcURLS = 0;
XP_Bool ret = FALSE;
ED_FileError result;
//// Write the edit buffer to a temporary file as HTML.
// Get the filename of a temporary file.
#ifdef XP_MAC
_srcName = WH_TempName(xpFileToPost,"ns");
#else
_srcName = WH_TempName(xpTemporary,"ns");
#endif
if (!_srcName)
goto CLEAN_UP;
// Is this necessary for some Mac magic to work?
srcName = WH_FilePlatformName(_srcName);
if (!srcName)
goto CLEAN_UP;
srcURL = XP_PlatformFileToURL(srcName);
if (!srcURL)
goto CLEAN_UP;
result = EDT_SaveFile(m_pBuffer->m_pContext,
srcURL,srcURL,
FALSE,FALSE,FALSE);
if (result != ED_ERROR_NONE)
goto CLEAN_UP;
XP_TRACE(("Created %s",srcURL));
// The converted plain text will go to "out.txt".
destName = XP_STRDUP("out.txt");
if (!destName)
goto CLEAN_UP;
destFile = XP_FileOpen(destName,xpFileToPost,XP_FILE_WRITE_BIN);
if (!destFile)
goto CLEAN_UP;
//// Call XL_Translate text to convert src to dest, TextFETestDone will be called on
// completion.
srcURLS = NET_CreateURLStruct(srcURL,NET_DONT_RELOAD);
if (!srcURLS) {
XP_FileClose(destFile);
goto CLEAN_UP;
}
XL_InitializeTextSetup(&setup);
setup.url = srcURLS;
setup.completion = TextFETestDone;
setup.out = destFile;
// Seems that passed in MWcontext is only used to copy some info when making the
// temporary text context.
XL_TranslateText(m_pBuffer->m_pContext,srcURLS,&setup);
ret = TRUE;
// Nasty, a goto.
CLEAN_UP:
if (_srcName)
XP_FREE(_srcName);
if (srcName)
XP_FREE(srcName);
if (srcURL)
XP_FREE(srcURL);
if (destName)
XP_FREE(destName);
return ret;
}
XP_Bool CEditTestManager::BoldTest(int32 rounds) {
char* kTitle = "Bold Test\n";
XP_TRACE(("%s", kTitle));
EDT_SelectAll(m_pBuffer->m_pContext);
EDT_DeleteChar(m_pBuffer->m_pContext);
EDT_PasteText(m_pBuffer->m_pContext, kTitle);
EDT_SelectAll(m_pBuffer->m_pContext);
for( int32 i = 0; i < rounds; i++ ) {
XP_TRACE(("Round %d", i));
EDT_CharacterData* normal = EDT_GetCharacterData( m_pBuffer->m_pContext );
EDT_CharacterData* bold = EDT_GetCharacterData( m_pBuffer->m_pContext );
bold->mask = TF_BOLD;
bold->values = TF_BOLD;
EDT_SetCharacterData(m_pBuffer->m_pContext, bold);
EDT_SetCharacterData(m_pBuffer->m_pContext, normal);
EDT_FreeCharacterData(normal);
EDT_FreeCharacterData(bold);
}
XP_TRACE(("End of %s.", kTitle));
return TRUE;
}
XP_Bool CEditTestManager::EveryNavigationKeyEverywhereTest() {
// Does navigation work from every position?
XP_Bool result;
result = NavigateChunkCrashTest() && UArrowTest(FALSE) && UArrowTest(TRUE)
&& DArrowTest(FALSE) && DArrowTest(TRUE);
XP_TRACE(("Done.\n"));
return result;
}
XP_Bool CEditTestManager::ArrowTest() {
// Does the navigation move smoothly through the document?
XP_Bool result;
result = LArrowTest(FALSE) || LArrowTest(TRUE) || RArrowTest(FALSE)
|| RArrowTest(TRUE) || UArrowTest(TRUE) || DArrowTest(TRUE);
#if 0
// Too many false positives -- need to know about up/down at first/last line
result |= UArrowTest(FALSE);
result |= DArrowTest(FALSE);
#endif
XP_TRACE(("Done.\n"));
return result;
}
void CEditTestManager::GetWholeDocumentSelection(CPersistentEditSelection& selection){
CEditSelection wholeDocument;
m_pBuffer->m_pRoot->GetAll(wholeDocument);
selection = m_pBuffer->EphemeralToPersistent(wholeDocument);
// Ignore the last 2 positions -- it's the EOF marker
selection.m_end.m_index -= 2;
}
XP_Bool CEditTestManager::NavigateChunkCrashTest(){
for ( int select = 0; select < 2; select++ ) {
for ( int chunk = 0; chunk < EDT_NA_COUNT; chunk++ ) {
for ( int direction = 0; direction < 2; direction++ ) {
if ( !NavChunkCrashTest(select, chunk, direction) )
return FALSE;
}
}
}
return TRUE;
}
XP_Bool CEditTestManager::NavChunkCrashTest(XP_Bool bSelect, int chunk, XP_Bool bDirection){
XP_Bool bResult = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p;
XP_TRACE(("NavChunkCrashTest bSelect = %d chunk = %d bDirection = %d\n", bSelect, chunk, bDirection));
for ( p = w.m_start;
p.m_index < w.m_end.m_index;
p.m_index++ ) {
m_pBuffer->SetInsertPoint(p);
m_pBuffer->NavigateChunk(bSelect, chunk, bDirection);
}
return bResult;
}
XP_Bool CEditTestManager::RArrowTest(XP_Bool bSelect){
XP_Bool result = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p;
XP_TRACE(("Right Arrow%s\n", bSelect ? " with shift key" : ""));
for ( p = w.m_start;
p.m_index < w.m_end.m_index;
p.m_index++ ) {
m_pBuffer->SetInsertPoint(p);
m_pBuffer->NavigateChunk(bSelect, LO_NA_CHARACTER, TRUE);
CPersistentEditSelection s2;
m_pBuffer->GetSelection(s2);
if ( (bSelect == s2.IsInsertPoint()) ||
s2.m_end.m_index != p.m_index + 1 ) {
XP_TRACE(("%d should be %d was %d\n", p.m_index,
p.m_index + 1, s2.m_end.m_index));
result = FALSE;
}
}
return result;
}
XP_Bool CEditTestManager::LArrowTest(XP_Bool bSelect){
XP_Bool result = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p(0,TRUE);
XP_TRACE(("Left Arrow Test%s\n", bSelect ? " with shift key" : ""));
for ( p.m_index = w.m_end.m_index - 1;
p.m_index > w.m_start.m_index;
p.m_index-- ) {
CEditSelection startSelection;
CPersistentEditSelection s2;
p.m_bStickyAfter = TRUE;
m_pBuffer->SetInsertPoint(p);
m_pBuffer->GetSelection(startSelection);
if ( startSelection.m_start.GapWithBothSidesAllowed() && ! bSelect ) {
// XP_TRACE(("%d - gap with both sides allowed.", p.m_index));
// Test that SetInsertPoint did the right thing.
m_pBuffer->GetSelection(s2);
if ( ! s2.IsInsertPoint() || !s2.m_start.IsEqualUI(p) ) {
XP_TRACE(("SetInsertPoint at %d should be %d.%d was %d.%d\n", p.m_index,
p.m_index, p.m_bStickyAfter,
s2.m_start.m_index, s2.m_start.m_bStickyAfter));
result = FALSE;
}
// Test hump over soft break
m_pBuffer->NavigateChunk(bSelect, LO_NA_CHARACTER, FALSE);
m_pBuffer->GetSelection(s2);
CPersistentEditInsertPoint expected = p;
expected.m_bStickyAfter = FALSE;
if ( ! s2.IsInsertPoint() || !s2.m_start.IsEqualUI(expected) ) {
XP_TRACE(("gap at %d should be %d.%d was %d.%d\n", p.m_index,
expected.m_index, expected.m_bStickyAfter,
s2.m_start.m_index, s2.m_start.m_bStickyAfter));
result = FALSE;
}
p.m_bStickyAfter = FALSE;
m_pBuffer->SetInsertPoint(p);
}
m_pBuffer->NavigateChunk(bSelect, LO_NA_CHARACTER, FALSE);
m_pBuffer->GetSelection(s2);
if ( bSelect == s2.IsInsertPoint() ) {
XP_TRACE(("%d Wrong type of selection. Expected %d.%d was %d.%d\n", p.m_index,
p.m_index, p.m_bStickyAfter,
s2.m_start.m_index, s2.m_start.m_bStickyAfter));
result = FALSE;
}
else if ( s2.m_start.m_index != p.m_index - 1 ) {
XP_TRACE(("%d wrong edge position. Should be %d.%d was %d.%d\n", p.m_index,
p.m_index-1, p.m_bStickyAfter,
s2.m_start.m_index, s2.m_start.m_bStickyAfter));
result = FALSE;
}
}
return result;
}
XP_Bool CEditTestManager::UArrowTest(XP_Bool bSelect){
XP_Bool result = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p;
XP_TRACE(("Up Arrow Test%s\n", bSelect ? " with shift key" : ""));
for ( p = w.m_end.m_index - 1;
p.m_index > w.m_start.m_index;
p.m_index-- ) {
m_pBuffer->SetInsertPoint(p);
CPersistentEditSelection s;
m_pBuffer->GetSelection(s);
m_pBuffer->ClearMove();
m_pBuffer->UpDown(bSelect, FALSE);
CPersistentEditSelection s2;
m_pBuffer->GetSelection(s2);
if ( s2 == s ) {
XP_TRACE(("%d didn't change selection\n", p.m_index));
result = FALSE;
}
}
return result;
}
XP_Bool CEditTestManager::DArrowTest(XP_Bool bSelect){
XP_Bool result = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p;
XP_TRACE(("Down Arrow Test%s\n", bSelect ? " with shift key" : ""));
for ( p = w.m_start.m_index;
p.m_index < w.m_end.m_index;
p.m_index++ ) {
m_pBuffer->SetInsertPoint(p);
CPersistentEditSelection s;
m_pBuffer->GetSelection(s);
m_pBuffer->ClearMove();
m_pBuffer->UpDown(bSelect, TRUE);
CPersistentEditSelection s2;
m_pBuffer->GetSelection(s2);
if ( s2 == s ) {
XP_TRACE(("%d didn't change selection\n", p.m_index));
result = FALSE;
}
}
return result;
}
XP_Bool CEditTestManager::BackspaceKeyTest(){
XP_Bool result = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p;
XP_TRACE(("DeleteKeyTest"));
p = w.m_end;
p.m_index--;
m_pBuffer->SetInsertPoint(p);
for ( p = w.m_start.m_index;
p.m_index < w.m_end.m_index;
p.m_index++ ) {
m_pBuffer->DeletePreviousChar();
}
return result;
}
XP_Bool CEditTestManager::DeleteKeyTest(){
XP_Bool result = TRUE;
CPersistentEditSelection w;
GetWholeDocumentSelection(w);
CPersistentEditInsertPoint p;
XP_TRACE(("DeleteKeyTest"));
m_pBuffer->SetInsertPoint(w.m_start);
for ( p = w.m_start.m_index;
p.m_index < w.m_end.m_index;
p.m_index++ ) {
m_pBuffer->DeleteNextChar();
}
return result;
}
XP_Bool CEditTestManager::Backspace() {
if ( ! m_bTesting ){
m_iTriggerIndex--;
if (m_iTriggerIndex < 0 ) m_iTriggerIndex = 0;
}
return m_bTesting;
}
XP_Bool CEditTestManager::ReturnKey() {
if ( ! m_bTesting ){
m_iTriggerIndex = 0;
}
return m_bTesting;
}
#ifdef EDITOR_JAVA
void CEditTestManager::DumpPlugins(){
XP_TRACE(("Dump Plugin database."));
int32 numCategories = EDT_NumberOfPluginCategories();
XP_TRACE(("EDT_NumberOfPluginCategories: %d", numCategories));
for(int category = 0; category < numCategories; category++ ) {
char* pcsCategoryName = EDT_GetPluginCategoryName(category);
XP_TRACE(("EDT_GetPluginCategoryName(%d) = \"%s\"", category, pcsCategoryName));
int plugins = EDT_NumberOfPlugins(category);
XP_TRACE(("EDT_NumberOfPlugins(%d) = %d", category, plugins));
for(int plugin = 0; plugin < plugins; plugin++){
char* pcsPluginName = EDT_GetPluginName(category, plugin);
XP_TRACE(("EDT_GetPluginName(%d, %d) = \"%s\"", category, plugin, pcsPluginName));
char* pcsPluginMenuHelp = EDT_GetPluginMenuHelp(category, plugin);
XP_TRACE(("EDT_GetPluginMenuHelp(%d, %d) = \"%s\"", category, plugin, pcsPluginMenuHelp));
}
}
XP_TRACE(("Dump Image Encoder database."));
int encoders = EDT_NumberOfEncoders();
XP_TRACE(("EDT_NumberOfEncoders() = %d", encoders));
for(int encoder = 0; encoder < encoders; encoder++){
char* pcsEncoderName = EDT_GetEncoderName(encoder);
XP_TRACE(("EDT_GetEncoderName(%d) = \"%s\"", encoder, pcsEncoderName));
char* pcsEncoderFileExtension = EDT_GetEncoderFileExtension(encoder);
XP_TRACE(("EDT_GetEncoderFileExtension(%d) = \"%s\"", encoder, pcsEncoderFileExtension));
char* pcsEncoderFileType = EDT_GetEncoderFileType(encoder);
XP_TRACE(("EDT_GetEncoderFileType(%d) = \"%s\"", encoder, pcsEncoderFileType));
XP_TRACE(("EDT_GetEncoderNeedsQuantizedSource(%d) = \"%d\"", encoder, EDT_GetEncoderNeedsQuantizedSource(encoder)));
char* pcsEncoderMenuHelp = EDT_GetEncoderMenuHelp(encoder);
XP_TRACE(("EDT_GetEncoderMenuHelp(%d) = \"%s\"", encoder, pcsEncoderMenuHelp));
}
}
#ifndef XP_OS2
static
#else
extern "OPTLINK"
#endif
void edt_TestImageEncoderCallbackFn(EDT_ImageEncoderStatus status, void* hook) {
XP_TRACE(("edt_TestImageEncoderCallbackFn Status: %d hook: %08x", status, hook));
}
void CEditTestManager::PerformFirstPlugin(){
if ( EDT_NumberOfPluginCategories() <= 0 ) {
XP_TRACE(("EDT_NumberOfPluginCategories: %d", EDT_NumberOfPluginCategories()));
return;
}
if ( EDT_NumberOfPlugins(0) <= 0 ) {
XP_TRACE(("EDT_NumberOfPlugins(0): %d", EDT_NumberOfPlugins(0)));
return;
}
XP_Bool bItem = EDT_PerformPlugin(m_pBuffer->m_pContext, 0, 0, edt_TestImageEncoderCallbackFn, (void*) 0x1234);
XP_TRACE(("EDT_PerformPlugin: returned %d\n", bItem));
}
void CEditTestManager::PerformPluginByName(){
XP_TRACE(("Calling dummyPlugin.does.not.exist"));
XP_Bool bResult = EDT_PerformPluginByClassName(m_pBuffer->m_pContext, "dummyPlugin.does.not.exist",
edt_TestImageEncoderCallbackFn, (void*) 12);
XP_TRACE(("EDT_PerformPluginByClassName returned %d\n", bResult));
XP_TRACE(("Calling netscape.test.plugin.composer.EditRaw"));
bResult = EDT_PerformPluginByClassName(m_pBuffer->m_pContext, "netscape.test.plugin.composer.EditRaw",
edt_TestImageEncoderCallbackFn, (void*) 13);
XP_TRACE(("EDT_PerformPluginByClassName returned %d\n", bResult));
}
void CEditTestManager::PerformFirstEncoder(){
if ( EDT_NumberOfEncoders() <= 0 ) {
XP_TRACE(("EDT_NumberOfEncoders: %d", EDT_NumberOfEncoders()));
return;
}
char** ppPixels = (char**) XP_ALLOC(sizeof(char*));
char* pPixels = (char*) XP_ALLOC(3);
ppPixels[0] = pPixels;
pPixels[0] = 1;
pPixels[1] = 2;
pPixels[2] = 3;
XP_TRACE(("EDT_StartEncoder: starting\n"));
XP_Bool bItem = EDT_StartEncoder(m_pBuffer->m_pContext, 0, 1, 1, ppPixels,
"C:\\junk.txt", edt_TestImageEncoderCallbackFn, (void*) 0xfeedface);
XP_TRACE(("EDT_StartEncoder: returned %d\n", bItem));
XP_FREE(pPixels);
XP_FREE(ppPixels);
}
#ifndef XP_OS2
static
#else
extern "OPTLINK"
#endif
void edt_TestPreOpenCallbackFn(XP_Bool bUserCancled, char* pURL, void* hook) {
XP_TRACE(("edt_TestPreOpenCallbackFn bUserCancled: %s url: %s hook: %08x", bUserCancled ? "TRUE" : "FALSE",
pURL ? pURL : "null", hook));
}
void CEditTestManager::PerformPreOpen(){
XP_TRACE(("Calling preOpen."));
EDT_PreOpen(m_pBuffer->m_pContext,"http://PerformPreOpen",
edt_TestPreOpenCallbackFn, (void*) 13);
}
#endif // EDITOR_JAVA
void CEditTestManager::TestHTMLPaste(){
EDT_PasteQuoteBegin(m_pBuffer->m_pContext, TRUE);
EDT_PasteQuote(m_pBuffer->m_pContext, "<b>This is bold.</b>");
EDT_PasteQuote(m_pBuffer->m_pContext, "<i>This is italic.</i>");
EDT_PasteQuoteEnd(m_pBuffer->m_pContext);
}
void CEditTestManager::SaveToTempFile() {
EDT_SaveToTempFile(m_pBuffer->m_pContext,(EDT_SaveToTempCallbackFn) SaveToTempFileCB,(void *)m_pBuffer->m_pContext);
}
void CEditTestManager::SaveToTempFileCB(char *pFileURL,void *hook) {
XP_ASSERT(hook);
MWContext *pContext = (MWContext *)hook;
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
CEditTestManager *pManager = pEditBuffer->m_pTestManager;
if (!pManager) {
XP_ASSERT(0);
return;
}
XP_FREEIF(pManager->m_pTempFileURL);
if (pFileURL) {
pManager->m_pTempFileURL = XP_STRDUP(pFileURL);
XP_TRACE(("Wrote to temp file %s",pFileURL));
}
else
{
XP_TRACE(("Failed to write temp file."));
}
}
void CEditTestManager::RemoveTempFile() {
if (m_pTempFileURL) {
XP_TRACE(("Removing file %s",m_pTempFileURL));
EDT_RemoveTempFile(m_pBuffer->m_pContext,m_pTempFileURL);
XP_FREEIF(m_pTempFileURL);
}
else {
XP_TRACE(("No temp file has been saved"));
}
}
void CEditTestManager::GetDocTempDir() {
CEditCommandLog *pLog = CGlobalHistoryGroup::GetGlobalHistoryGroup()->GetLog(m_pBuffer);
if (!pLog) {
return;
}
char *pFile = pLog->GetDocTempDir();
if (pFile) {
XP_TRACE(("DocTempDir is %s",pFile));
XP_FREE(pFile);
}
else {
XP_TRACE(("No document temp dir was created."));
}
}
#endif // DEBUG
//-----------------------------------------------------------------------------
// CParseState / CPrintState
//-----------------------------------------------------------------------------
#define PRE_BODY 0
#define IN_BODY 1
#define POST_BODY 2
CParseState::CParseState()
:m_bInTitle(FALSE),
m_iDocPart(0),
m_inJavaScript(0),
m_baseFontSize(0),
m_pNextText(0),
m_pJavaScript(0),
m_pPostBody(0){
}
XP_Bool CParseState::InBody(){
return m_iDocPart == IN_BODY;
}
void CParseState::StartBody(){
m_iDocPart = IN_BODY;
}
void CParseState::EndBody(){
m_iDocPart = POST_BODY;
}
CStreamOutMemory* CParseState::GetStream(){
if ( m_iDocPart < POST_BODY ) {
// m_pJavaScript is mis-named. It's actually
// a bag we hold all our unprocessed head tags in.
if( m_pJavaScript == 0 ){
m_pJavaScript = new CStreamOutMemory();
}
return m_pJavaScript;
}
else {
if( m_pPostBody == 0 ){
m_pPostBody = new CStreamOutMemory();
}
return m_pPostBody;
}
}
CParseState::~CParseState() {
Free(m_pJavaScript);
Free(m_pPostBody);
delete m_pNextText;
}
void CParseState::Free(CStreamOutMemory*& pStream){
if ( pStream ) {
// COutMemoryStreams don't delete their text.
XP_HUGE_CHAR_PTR pData = pStream->GetText();
if ( pData ) {
XP_HUGE_FREE(pData);
}
delete pStream;
pStream = NULL;
}
}
void CParseState::Reset(){
bLastWasSpace = TRUE; // at the beginning of the document
// we should ignore leading spaces
m_baseFontSize = 3;
m_formatTypeStack.Reset();
m_formatTextStack.Reset();
m_iDocPart = 0;
m_bInTitle = FALSE;
m_inJavaScript = 0;
m_pJavaScript = 0;
delete m_pNextText;
// force the compiler to use the non-stream constructor
m_pNextText = new CEditTextElement((CEditElement*)0,0);
}
//-----------------------------------------------------------------------------
// CPrintState
//-----------------------------------------------------------------------------
void CPrintState::Reset( IStreamOut* pOut, CEditBuffer *pBuffer ){
m_pOut = pOut;
m_iCharPos = 0;
m_bTextLast = FALSE;
m_iLevel = 0;
m_pBuffer = pBuffer;
m_bEncodeSelectionAsComment = FALSE;
}
void CPrintState::PrintSelectionComment(XP_Bool bEnd, XP_Bool bStickyAfter){
if ( !bEnd ) {
m_pBuffer->PasteHTMLHook(this);
}
m_pOut->Printf("<!-- %s%s -->", bEnd ? EDT_SELECTION_END_COMMENT : EDT_SELECTION_START_COMMENT,
bStickyAfter ? "+" : "");
}
XP_Bool CPrintState::ShouldPrintSelectionComments(CEditLeafElement* pElement){
return m_bEncodeSelectionAsComment && (
ShouldPrintSelectionComment(pElement, FALSE)
|| ShouldPrintSelectionComment(pElement, TRUE));
}
XP_Bool CPrintState::ShouldPrintSelectionComment(CEditLeafElement* pElement, XP_Bool bEnd){
return m_bEncodeSelectionAsComment && m_selection.GetEdge(bEnd)->m_pElement == pElement;
}
//-----------------------------------------------------------------------------
// CEditLinkManager
//-----------------------------------------------------------------------------
EDT_HREFData* ED_Link::GetData(){
EDT_HREFData* pData = EDT_NewHREFData();
pData->pURL = XP_STRDUP( hrefStr );
if( pExtra ){
pData->pExtra = XP_STRDUP( pExtra );
}
else {
pData->pExtra = 0;
}
return pData;
}
CEditLinkManager::CEditLinkManager(){
}
ED_Link* CEditLinkManager::MakeLink( char *pHREF, char *pExtra, intn iRefCount ){
ED_Link *pNewLink = XP_NEW( ED_Link );
pNewLink->iRefCount = iRefCount;
pNewLink->pLinkManager = this;
pNewLink->hrefStr = XP_STRDUP( pHREF );
pNewLink->bAdjusted = FALSE;
if( pExtra ){
pNewLink->pExtra = XP_STRDUP( pExtra );
}
else {
pNewLink->pExtra = 0;
}
return pNewLink;
}
// When a target name changes, call this to change links to that target
XP_Bool CEditLinkManager::FixupLinksToTarget(char *pOldName, char *pNewName)
{
// Check if both strings exist and are different
if( !pOldName || !pNewName || 0 == XP_STRCMP(pOldName, pNewName) )
return FALSE;
XP_Bool bResult = TRUE;
for (int i = 0; i < m_links.Size(); i++ )
{
ED_Link *pLink;
if( (pLink = m_links[i]) != 0 && *pLink->hrefStr == '#' )
{
// Point to 1 character past the #
char *pTarget = pLink->hrefStr + 1;
intn iNewLen = XP_STRLEN(pNewName);
if( 0 == XP_STRCMP(pTarget, pOldName) )
{
if( iNewLen > (intn)XP_STRLEN(pOldName) )
{
// New name is longer - allocate more space
XP_FREE(pLink->hrefStr);
pLink->hrefStr = (char*)XP_ALLOC(iNewLen + 2);
if( pLink->hrefStr )
{
*pLink->hrefStr = '#';
pTarget = pLink->hrefStr + 1;
}
}
XP_STRCPY(pTarget, pNewName);
bResult = TRUE;
}
}
}
return bResult;
}
void CEditLinkManager::AdjustAllLinks( char *pOldURL, char* pNewURL, ED_HREFList *badLinks ){
int i;
for (i = 0; i < m_links.Size(); i++ ){
ED_Link *pLink;
if( (pLink = m_links[i]) != 0 ){
AdjustLink(&pLink->hrefStr,pOldURL,pNewURL,badLinks);
}
}
}
// Used for adjusting links, images, etc.
// pNewBase can be NULL, the other args shouldn't be.
//
// Change *ppURL so that the URL will still point to the same location
// after changing the base URL.
// If there is no way to adjust ppURL, ppURL will not change and we add it to badLinks.
// E.g. if ppURL relative to pOldBase points to a local file and
// pNewBase is a remote site, we can't adjust the URL.
void CEditLinkManager::AdjustLink(char **ppURL,char *pOldBase, char *pNewBase, ED_HREFList *badLinks){
if (!ppURL || !*ppURL) {
return;
}
if ((*ppURL)[0] == '#' || (*ppURL)[0] == '`') {
// do nothing, but not an error.
return;
}
// Will make url
// absolute if the source and dest are on the same machine, just different
// drives. If on the same drive, will make a relative URL.
// If on different drives or different machine, leave URL alone, return FALSE.
// Make absolute relative to source URL
char *pAbsURL = NET_MakeAbsoluteURL( pOldBase, *ppURL );
if (!pAbsURL) {
// Add to list of bad links.
AddHREFUnique(badLinks,*ppURL);
return;
}
char *pNewURL = NULL;
if( pNewBase ){
// Try to make relative to dest URL.
int result = NET_MakeRelativeURL( pNewBase, pAbsURL, &pNewURL );
// If we can't make it relative.
if (result != NET_URL_SAME_DIRECTORY &&
result != NET_URL_SAME_DEVICE) {
if (NET_URL_Type(pAbsURL) == FILE_TYPE_URL &&
NET_URL_Type(pNewBase) != FILE_TYPE_URL) {
// No point in adjusting URL or making relative, local file is not
// visible to a remote machine. So don't change anything.
XP_FREEIF(pAbsURL);
XP_FREEIF(pNewURL);
// Add to list of bad links.
AddHREFUnique(badLinks,*ppURL);
return;
}
// else pNewURL will be a copy of pAbsURL, so just continue.
// I.e. we have made the URL absolute.
}
// result is same directory or same device.
else if (result == NET_URL_SAME_DEVICE && (*ppURL)[0] == '/') {
// Absolute pathing. Don't force it to be relative. It'll work in the
// new location.
XP_FREEIF(pAbsURL);
XP_FREEIF(pNewURL);
return;
}
// More aggressive absolute pathing. If the new relative url goes all the
// way up to the root, just make it start from the root instead of having a
// bunch of ../../../ Bug 50500.
// E.g. pAbsURL = http://host/images/bob.gif,
// pNewBase = http://host/dir1/dir2/dir3/doc.html
// instead of setting pNewURL to
// ../../../images/bob.gif, use
// /images/bob.gif
else if (result == NET_URL_SAME_DEVICE) {
// Count sets of ../ at the beginning of pNewURL.
int dotDotCount = 0;
char *pSeek = pNewURL;
while (pSeek && *pSeek && !XP_STRNCMP(pSeek,"../",3)) {
dotDotCount++;
pSeek +=3;
}
char *basePath = NET_ParseURL(pNewBase,GET_PATH_PART);
if (!basePath) {
XP_ASSERT(0);
return;
}
/* Find location of drive separator */
char *basePtr = (char*) XP_STRCHR(basePath, '|');
if (!basePtr) {
basePtr = basePath;
// basePtr now points to first '/' in pNewBase before directories
// Count number of subdirectories in pNewBase.
int subDirCount = -1; // Don't count first slash.
while (basePtr && *basePtr) {
if (*basePtr == '/') {
subDirCount++;
}
basePtr++;
}
XP_FREE(basePath);
if (dotDotCount >= subDirCount && dotDotCount > 0) {
if (dotDotCount > subDirCount) {
// Error, Net_MakeRelativeURL added too many ../
XP_ASSERT(0);
}
// pSeek is pointing to first char after all the ../
char *pTemp = XP_STRDUP(pSeek - 1); // Move back to get the slash.
XP_FREE(pNewURL);
pNewURL = pTemp;
}
}
else {
// there is a drive separator, so this trick won't work.
// use pNewURL as is.
XP_FREE(basePath);
}
}
} else {
// No new base is supplied - so we just return absolute URL
XP_FREEIF(*ppURL);
*ppURL = pAbsURL;
return;
}
// else pNewURL is a correct relative URL. Or an absolute URL if result == NET_URL_FAIL.
XP_FREEIF( pAbsURL );
XP_FREEIF(*ppURL);
*ppURL = pNewURL;
}
ED_LinkId CEditLinkManager::Add( char *pHREF, char *pExtra ){
int i = 0;
int lastFree = -1;
while( i < m_links.Size() ){
ED_Link* pLink = m_links[i];
if( pLink != 0 ){
// need a more intelegent way of comparing HREFs
if( XP_STRCMP( pLink->hrefStr, pHREF) == 0 ) {
// Links the same. How about pExtra ?
if ( (!!pExtra == !!pLink->pExtra)
&& ((pExtra &&
XP_STRCMP( pExtra, pLink->pExtra ) == 0 )
|| pExtra == 0 ) ){
pLink->iRefCount++;
return pLink;
}
}
}
else {
lastFree = i;
}
i++;
}
ED_Link *pNewLink = MakeLink( pHREF, pExtra );
//
// Store it.
//
if( lastFree != -1 ){
m_links[lastFree] = pNewLink;
}
else {
lastFree = m_links.Add( pNewLink );
}
pNewLink->linkOffset = lastFree;
return pNewLink;
}
void CEditLinkManager::Free(ED_LinkId id){
if( --id->iRefCount == 0 ){
m_links[ id->linkOffset ] = 0;
XP_FREEIF( id->pExtra );
XP_FREEIF( id->hrefStr );
XP_FREE( id );
}
}
void CEditLinkManager::AddHREFUnique(ED_HREFList *badLinks,char *pURL) {
for (int n = 0; n < badLinks->Size(); n++) {
// Just comparing links for equality by string comparison.
if (!XP_STRCMP((*badLinks)[n],pURL)) {
// Already in list.
return;
}
}
badLinks->Add(XP_STRDUP(pURL));
}
XP_Bool EDT_SelectTableElement(MWContext *pMWContext, int32 x, int32 y,
LO_Element *pLoElement,
ED_HitType iHitType,
XP_Bool bModifierKeyPressed,
XP_Bool bExtendSelection)
{
GET_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer) 0;
return pEditBuffer->SelectTableElement(x, y, pLoElement, iHitType,
bModifierKeyPressed, bExtendSelection);
}
ED_HitType EDT_ExtendTableCellSelection(MWContext *pMWContext, int32 x, int32 y)
{
GET_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer) ED_HIT_NONE;
return pEditBuffer->ExtendTableCellSelection(x, y);
}
// Dynamic Sizing Object
CSizingObject::CSizingObject() :
m_pBuffer(0),
m_pLoElement(0),
m_iStyle(0),
m_iXOrigin(0),
m_iYOrigin(0),
m_iXMouseOffset(0),
m_iYMouseOffset(0),
m_iStartWidth(0),
m_iStartHeight(0),
m_iViewWidth(0),
m_iViewHeight(0),
m_bWidthPercent(0),
m_bHeightPercent(0),
m_bPercentOriginal(0),
m_bFirstTime(1),
m_iAddCols(0),
m_iAddRows(0),
m_bCenterSizing(0)
{
}
XP_Bool CSizingObject::Create(CEditBuffer *pBuffer,
LO_Element *pLoElement,
int iSizingStyle,
int32 xVal, int32 yVal,
XP_Bool bModifierKeyPressed, XP_Rect *pRect){
XP_ASSERT(pBuffer);
XP_ASSERT(pRect);
XP_ASSERT(iSizingStyle);
m_pBuffer = pBuffer;
m_pLoElement = pLoElement;
m_iStyle = iSizingStyle;
if( !m_pLoElement )
{
#ifdef LAYERS
m_pLoElement = LO_XYToElement(pBuffer->m_pContext, xVal, yVal, 0);
#else
m_pLoElement = LO_XYToElement(pBuffer->m_pContext, xVal, yVal);
#endif
}
if( !m_pLoElement )
{
return FALSE;
}
// Get view window size for "100%" sizing
FE_GetDocAndWindowPosition(pBuffer->m_pContext, &m_iXOrigin, &m_iYOrigin, &m_iViewWidth, &m_iViewHeight);
if( !m_iViewWidth || !m_iViewHeight )
{
return FALSE;
}
// Get extra margin
int32 iMarginWidth;
int32 iMarginHeight;
LO_GetDocumentMargins(pBuffer->m_pContext, &iMarginWidth, &iMarginHeight);
m_iViewWidth -= 2 * iMarginWidth;
m_iViewHeight -= 2 * iMarginHeight;
if( m_pLoElement->lo_any.type == LO_CELL )
{
LO_Element *pElement = NULL;
LO_Element *pLastElement = NULL;
// When resizing a column, we must find a cell that doesn't have COLSPAN > 1
if( m_iStyle == ED_SIZE_RIGHT && lo_GetColSpan(m_pLoElement) > 1 )
{
int32 iRightEdge = m_pLoElement->lo_cell.x + m_pLoElement->lo_cell.width;
pElement = lo_GetFirstAndLastCellsInTable(m_pBuffer->m_pContext, m_pLoElement, &pLastElement);
if( pElement )
{
do {
if( pElement && pElement->lo_any.type == LO_CELL &&
(pElement->lo_cell.x + pElement->lo_cell.width) == iRightEdge &&
lo_GetColSpan(pElement) == 1 )
{
m_pLoElement = pElement;
break;
}
if( pElement != pLastElement )
pElement = pElement->lo_any.next;
}
while( pElement != pLastElement );
}
}
if( m_iStyle == ED_SIZE_BOTTOM && lo_GetRowSpan(m_pLoElement) > 1 )
{
int32 iBottomEdge = m_pLoElement->lo_cell.y + m_pLoElement->lo_cell.height;
if( !pElement )
pElement = lo_GetFirstAndLastCellsInTable(m_pBuffer->m_pContext, m_pLoElement, &pLastElement);
if( pElement )
{
do {
if( pElement && pElement->lo_any.type == LO_CELL &&
(pElement->lo_cell.y + pElement->lo_cell.height) == iBottomEdge &&
lo_GetRowSpan(pElement) == 1 )
{
m_pLoElement = pElement;
break;
}
if( pElement != pLastElement )
pElement = pElement->lo_any.next;
}
while( pElement != pLastElement );
}
}
}
LO_Any *pAny = &(m_pLoElement->lo_any);
m_iParentWidth = 0;
// Check for sizing of table cell or nested table
if( pAny->type == LO_TABLE )
{
// Check for possible nested table by finding if it has a parent cell
LO_CellStruct *pParentCell = lo_GetParentCell(m_pBuffer->m_pContext, m_pLoElement);
if( pParentCell )
{
// Width = that of cell containing table
m_iParentWidth = pParentCell->width;
m_iWidthMsgID = XP_EDT_PERCENT_CELL;
}
}
else if( pAny->type == LO_CELL )
{
//
LO_TableStruct *pParentTable = lo_GetParentTable(m_pBuffer->m_pContext, m_pLoElement);
if( pParentTable )
{
// Get the width usable for percent calculation (minus borders and cell spacing)
m_iParentWidth = lo_CalcTableWidthForPercentMode(m_pLoElement);
m_iWidthMsgID = XP_EDT_PERCENT_TABLE;
}
}
if( m_iParentWidth == 0 )
{
m_iParentWidth = m_iViewWidth;
m_iWidthMsgID = XP_EDT_PERCENT_WINDOW;
}
// SHOULD WE DO THIS???
m_pBuffer->MoveAndHideCaretInTable(m_pLoElement);
// Calculate the rect in View's coordinates;
// ASSUMES PIXELS ONLY -- NO CONVERSION DONE
m_Rect.left = pAny->x + pAny->x_offset - m_iXOrigin;
m_Rect.top = pAny->y + pAny->y_offset - m_iYOrigin;
// Recent default value of flag used
// for image status string, e.g.: (% of original width)
m_bPercentOriginal = FALSE;
// Get percent-mode flag for edit element and adjust table/cell data
switch ( m_pLoElement->type )
{
case LO_IMAGE:
{
// Get the edit element
CEditElement * pElement = m_pLoElement->lo_any.edit_element;
if( !pElement ) return FALSE;
if( pElement->IsIcon() )
{
// All "unknown" tags are handled here
pElement->GetWidth(&m_bWidthPercent, &m_iStartWidth);
pElement->GetHeight(&m_bHeightPercent, &m_iStartHeight);
} else if( pElement->IsImage() )
{
EDT_ImageData * pImageData = pElement->Image()->GetImageData();
//pBuffer->GetImageData();
if( pImageData )
{
m_bWidthPercent = pImageData->bWidthPercent;
m_bHeightPercent = pImageData->bHeightPercent;
// Save the original height and width of the image
// if we have it
if( pImageData->iOriginalWidth )
{
m_iStartWidth = pImageData->iOriginalWidth;
m_iStartHeight = pImageData->iOriginalHeight;
// Constrain relative to "original" dimensions
m_bPercentOriginal = TRUE;
}
EDT_FreeImageData(pImageData);
}
// LO_ImageStruct includes border -- adjust
m_Rect.left += ((LO_ImageStruct*)pAny)->border_width;
m_Rect.top += ((LO_ImageStruct*)pAny)->border_width;
}
break;
}
case LO_HRULE:
{
// Get the edit element
CEditElement * pElement = m_pLoElement->lo_any.edit_element;
if( !pElement ) return FALSE;
EDT_HorizRuleData * pHData = pElement->HorizRule()->GetData(); //pBuffer->GetHorizRuleData();
if( pHData )
{
m_bWidthPercent = pHData->bWidthPercent;
if( pHData->align == ED_ALIGN_CENTER )
m_bCenterSizing = TRUE;
//Note: we will get width and "size" (height) in pixels below
m_iStartHeight = pHData->size;
EDT_FreeHorizRuleData(pHData);
}
break;
}
case LO_TABLE:
{
CEditTableElement *pTable =
(CEditTableElement*)edt_GetTableElementFromLO_Element( m_pLoElement, LO_TABLE );
if( pTable )
{
EDT_TableData *pTableData = pTable->GetData();
if( pTableData )
{
switch( iSizingStyle )
{
case ED_SIZE_RIGHT:
// Use width determined by layout
m_bWidthPercent = pTableData->bWidthPercent;
if( m_bWidthPercent )
{
m_iStartWidth = (pAny->width * 100) / m_iParentWidth;
} else {
m_iStartWidth = pAny->width;
}
break;
case ED_SIZE_BOTTOM:
// Use Height determined by layout
m_bHeightPercent = pTableData->bHeightPercent;
if( m_bHeightPercent )
{
m_iStartHeight = (pAny->height * 100) / m_iViewHeight;
} else {
m_iStartHeight = pAny->height;
}
break;
case ED_SIZE_ADD_COLS:
// When adding columns, show selection only
// around "new" columns, so rect.left = table->right
m_Rect.left += pAny->width;
m_Rect.right = m_Rect.left + 1;
m_Rect.bottom = m_Rect.top + pAny->height;
m_iStartHeight = pAny->height;
break;
case ED_SIZE_ADD_ROWS:
// As with columns, rect is around "new" rows
m_Rect.top += pAny->height;
m_Rect.bottom = m_Rect.top + 1;
m_Rect.right = m_Rect.left + pAny->width;
m_iStartWidth = pAny->width;
break;
}
EDT_FreeTableData(pTableData);
}
}
break;
}
case LO_CELL:
{
CEditTableCellElement *pCell =
(CEditTableCellElement*)edt_GetTableElementFromLO_Element( m_pLoElement, LO_CELL );
if( pCell )
{
EDT_TableCellData * pCellData = pCell->GetData(0);
if( pCellData )
{
m_bWidthPercent = pCellData->bWidthPercent;
m_bHeightPercent = pCellData->bHeightPercent;
EDT_FreeTableCellData(pCellData);
if( iSizingStyle == ED_SIZE_RIGHT )
{
if( m_bWidthPercent )
{
// IMPORTANT: The "width" we use for cell sizing INCLUDES the
// border and padding values (as does the LO_Element value),
// This is NOT the same as the HTML "WIDTH" param or the Editor's
// pCellData->width. Same goes for Height
m_iStartWidth = (pAny->width * 100) / m_iParentWidth;
} else {
// Use width determined by layout
m_iStartWidth = pAny->width;
}
}
else
{
if( m_bHeightPercent )
{
m_iStartHeight = (pAny->height * 100) / m_iViewHeight;
} else {
m_iStartHeight = pAny->height;
}
}
// We will select the entire row or column, so get size of table
LO_TableStruct * pLoTable = lo_GetParentTable(pBuffer->m_pContext, m_pLoElement);
if( pLoTable )
{
int32 iAdjust = min(pLoTable->border_width, 4 - pLoTable->inter_cell_space);
if( iSizingStyle == ED_SIZE_RIGHT )
{
// Prefered top and bottom is just inside table border
m_Rect.top = pLoTable->y + pLoTable->y_offset - m_iYOrigin + pLoTable->border_width;
m_Rect.bottom = m_Rect.top + pLoTable->height - 2*pLoTable->border_width;
// Back off top and bottom if intercell space is too small
if( pLoTable->inter_cell_space < 4 && iAdjust > 0 )
{
m_Rect.top -= iAdjust;
m_Rect.bottom += iAdjust;
}
m_Rect.right = m_Rect.left + pAny->width;
}
else
{
// Prefered left and right is just inside table border
m_Rect.left = pLoTable->x + pLoTable->x_offset - m_iXOrigin + pLoTable->border_width;
m_Rect.right = m_Rect.left + pLoTable->width - 2*pLoTable->border_width;
// Back off if intercell space is too small
if( pLoTable->inter_cell_space < 4 && iAdjust > 0 )
{
m_Rect.left -= iAdjust;
m_Rect.right += iAdjust;
}
m_Rect.bottom = m_Rect.top + pAny->height;
}
}
}
}
break;
}
}
if( !m_bWidthPercent )
{
// We aren't doing percent, so set message ID for pixels
m_iWidthMsgID = XP_EDT_PIXELS;
}
// Finish calculating object rect if not set for table sizing above
if( iSizingStyle != ED_SIZE_ADD_COLS && iSizingStyle != ED_SIZE_ADD_ROWS &&
m_pLoElement->type != LO_CELL )
{
m_Rect.right = m_Rect.left + pAny->width;
m_Rect.bottom = m_Rect.top + pAny->height;
}
// If we didn't set above, use current size or minimum of 1
if( m_iStartWidth == 0 )
{
m_iStartWidth = max(1, pAny->width);
}
if( m_iStartHeight == 0 )
{
m_iStartHeight = max(1, pAny->height);
}
// Calculate the offset of actual object edge to
// the location of the mouse cursor
switch( iSizingStyle )
{
case ED_SIZE_LEFT:
m_iXMouseOffset = m_Rect.left - pAny->x_offset + m_iXOrigin - xVal;
break;
case ED_SIZE_RIGHT:
m_iXMouseOffset = m_Rect.right - pAny->x_offset + m_iXOrigin - xVal;
break;
case ED_SIZE_TOP:
m_iYMouseOffset = m_Rect.top - pAny->y_offset + m_iYOrigin - yVal;
break;
case ED_SIZE_BOTTOM:
m_iYMouseOffset = m_Rect.bottom - pAny->y_offset + m_iYOrigin - yVal;
break;
}
// Call this now primarily to start the
// status line display of sizing information
GetSizingRect(xVal, yVal, bModifierKeyPressed, &m_Rect);
//NOTE: If above offsets are correct, then m_Rect shouldn't change
// during call to GetSizingRect
if( pRect )
*pRect = m_Rect;
return TRUE;
}
#define EDT_NEW_ROW_HEIGHT 24
#define EDT_NEW_COL_WIDTH 24
PRIVATE
void CalcAddRowRect(int32 iRows, XP_Rect *pTableRect, XP_Rect *pRect)
{
pRect->left = pTableRect->left + 6;
pRect->right = pTableRect->right - 6;
pRect->top = pTableRect->top + (iRows * EDT_NEW_ROW_HEIGHT) + 2;
pRect->bottom = pRect->top;
}
PRIVATE
void CalcAddColRect(int32 iCols, XP_Rect *pTableRect, XP_Rect *pRect)
{
pRect->top = pTableRect->top + 6;
pRect->bottom = pTableRect->bottom - 6;
pRect->left = pTableRect->left + (iCols * EDT_NEW_COL_WIDTH) + 2;
pRect->right = pRect->left;
}
// Corner must be > any single side value
#define EDT_IS_SIZING_CORNER(style) (style == ED_SIZE_TOP_LEFT || \
style == ED_SIZE_TOP_RIGHT || \
style == ED_SIZE_BOTTOM_LEFT || \
style == ED_SIZE_BOTTOM_RIGHT )
XP_Bool CSizingObject::GetSizingRect(int32 xVal, int32 yVal, XP_Bool bModifierKeyPressed, XP_Rect *pRect)
{
XP_ASSERT(pRect);
// Adjust input X an Y by amount calculated in Create
// so we resize as if cursor started exactly on the correct edge
// instead of just close to it
xVal += m_iXMouseOffset;
yVal += m_iYMouseOffset;
int i;
// We never allow lock aspect for table sizing
// Instead, modifier is used to distinguish between sizing the last column or row
// and sizing the whole table
// For non-table objects, we lock aspect ratio if Modifier Key IS NOT pressed
XP_Bool bLockAspectRatio = m_pLoElement->type != LO_TABLE && m_pLoElement->type != LO_CELL &&
EDT_IS_SIZING_CORNER(m_iStyle) && !bModifierKeyPressed;
int32 iViewX = xVal - m_iXOrigin;
int32 iViewY = yVal - m_iYOrigin;
// Calculate the new rectangle
// And don't allow dragging past opposite side:
XP_Rect new_rect = m_Rect;
int32 iAmountMoved = 0;
if( m_iStyle == ED_SIZE_RIGHT )
iAmountMoved = max(m_Rect.left, iViewX) - m_Rect.right;
else if( m_iStyle == ED_SIZE_LEFT )
iAmountMoved = m_Rect.left - min(m_Rect.right, iViewX);
if( m_bWidthPercent )
{
// In % mode, limit largest value to get 100% of width or height
// Calc. the pixel value corresponding to just under 101% width or height,
// so roundoff will always allow achieving 100%
int32 iFullWidth = ((101 * m_iParentWidth) / 100) - 1;
int32 iFullHeight = ((101 * m_iViewHeight) / 100) - 1;
if( m_iStyle & ED_SIZE_TOP )
{
new_rect.top = min(m_Rect.bottom, max(iViewY,m_Rect.bottom-iFullHeight));
}
if( m_iStyle & ED_SIZE_BOTTOM )
{
new_rect.bottom = max(m_Rect.top, min(iViewY,m_Rect.top+iFullHeight));
}
if( m_iStyle & ED_SIZE_LEFT )
{
if( m_bCenterSizing )
{
new_rect.left -= iAmountMoved;
new_rect.right += iAmountMoved;
}
else
new_rect.left = min(m_Rect.right, max(iViewX,m_Rect.right-iFullWidth));
}
if( m_iStyle & ED_SIZE_RIGHT )
{
if( m_bCenterSizing )
{
new_rect.left -= iAmountMoved;
new_rect.right += iAmountMoved;
}
else
new_rect.right = max(m_Rect.left, iViewX);
}
} else {
if( m_iStyle & ED_SIZE_TOP )
{
new_rect.top = min(m_Rect.bottom, iViewY);
}
if( m_iStyle & ED_SIZE_BOTTOM || m_iStyle == ED_SIZE_ADD_ROWS )
{
new_rect.bottom = max(m_Rect.top, iViewY);
}
if( m_iStyle & ED_SIZE_LEFT )
{
if( m_bCenterSizing )
{
new_rect.left -= iAmountMoved;
new_rect.right += iAmountMoved;
}
else
new_rect.left = min(m_Rect.right, iViewX);
}
if( m_iStyle & ED_SIZE_RIGHT || m_iStyle == ED_SIZE_ADD_COLS )
{
if( m_bCenterSizing )
{
new_rect.left -= iAmountMoved;
new_rect.right += iAmountMoved;
}
else
new_rect.right = max(m_Rect.left, iViewX);
}
}
int iNewWidth = new_rect.right - new_rect.left;
int iNewHeight = new_rect.bottom - new_rect.top;
// When sizing both width and height,
// fixup coordinates to lock aspect ratio
//
if(bLockAspectRatio)
{
int iLockedWidth = (iNewHeight * m_iStartWidth) / m_iStartHeight;
int iLockedHeight = (iNewWidth * m_iStartHeight) / m_iStartWidth;
if( iNewWidth < iLockedWidth )
{
// New width is too small to match new height - adjust
if( m_iStyle & ED_SIZE_LEFT ){
new_rect.left = new_rect.right - iLockedWidth;
} else {
new_rect.right = new_rect.left + iLockedWidth;
}
} else if( iNewHeight < iLockedHeight )
{
// New height is too small to match new width
if( m_iStyle & ED_SIZE_TOP )
{
new_rect.top = new_rect.bottom - iLockedHeight;
} else {
new_rect.bottom = new_rect.top + iLockedHeight;
}
}
}
// Return new rect to caller
*pRect = new_rect;
XP_Bool bRectChanged = new_rect.top != m_Rect.top ||
new_rect.bottom != m_Rect.bottom ||
new_rect.left != m_Rect.left ||
new_rect.right != m_Rect.right;
// Continue if rect is different from last rect requested
// or its the first time here (so status is displayed on initial mouse down)
if( bRectChanged || m_bFirstTime )
{
m_Rect = new_rect;
// Display status message with data for user:
char pMsg[512] = "";
char pPercentOfWhat[256] = "";
int32 iWidth = m_Rect.right - m_Rect.left;
int32 iHeight = m_Rect.bottom - m_Rect.top;
int32 iPercent = 0;
XP_Bool bDoWidth = (m_iStyle & ED_SIZE_LEFT) || (m_iStyle & ED_SIZE_RIGHT);
XP_Bool bDoHeight = (m_iStyle & ED_SIZE_TOP) || (m_iStyle & ED_SIZE_BOTTOM);
// Show only dimension(s) for whats changing: (Width vs. Height vs both)
// First part of status shows: "Width = xx[pixesl|%of window width]"
// and/or similar string for Height.
//
if( bDoWidth )
{
iPercent = (iWidth * 100 ) / m_iStartWidth;
if(m_bWidthPercent)
{
// Convert to % format
iWidth = (iWidth * 100) / m_iParentWidth;
}
else
{
// Tables are problematic. We use full cell width, including padding and borders,
// because it much easier to calculate percent mode values,
// but the HTML WIDTH and HEIGHT params only measure CONTENT area
// Should we display the same content size value when resizing?
// (That's what user sees in the Width and Height values in the property dialogs)
}
// "Width = x"
PR_snprintf(pMsg, 128, XP_GetString(XP_EDT_WIDTH_EQUALS), iWidth);
// "pixels" or "% of window"
strcat(pMsg, XP_GetString(m_iWidthMsgID));
// The % of original [width] and/or [height] string
// Current logic assumes constraining aspect ratio when sizing corners,
// so separate Width, Height percentages are not shown
XP_STRCPY(pPercentOfWhat, XP_GetString(XP_EDT_WIDTH));
}
if( bDoHeight )
{
// Since corners are constrained to aspect ratio,
// just use Width's calculation if already done
if( !bDoWidth )
{
iPercent = (iHeight * 100 ) / m_iStartHeight;
}
if(m_bHeightPercent)
{
iHeight = (iHeight * 100) / m_iViewHeight;
}
// "Height = x"
PR_snprintf(pMsg+XP_STRLEN(pMsg), 128,
XP_GetString(XP_EDT_HEIGHT_EQUALS), iHeight);
// "pixels" or "% of window"
strcat(pMsg, XP_GetString(m_bHeightPercent ? (int)XP_EDT_PERCENT_WINDOW :
(int)XP_EDT_PIXELS));
if( bDoWidth )
{
strcat(pPercentOfWhat, XP_GetString(XP_EDT_AND));
}
strcat(pPercentOfWhat, XP_GetString(XP_EDT_HEIGHT));
}
if( bDoWidth || bDoHeight )
{
// Build string to report relative size change:
// (% of [original|previous] [width | height | width and height])
PR_snprintf(pMsg+XP_STRLEN(pMsg), 128,
XP_GetString(m_bPercentOriginal ? (int)XP_EDT_PERCENT_ORIGINAL :
(int)XP_EDT_PERCENT_PREVIOUS),
iPercent, pPercentOfWhat);
} else if( m_iStyle == ED_SIZE_ADD_COLS )
{
int iAddCols = max(1, iNewWidth / EDT_NEW_COL_WIDTH);
PR_snprintf(pMsg, 128, XP_GetString(XP_EDT_ADD_COLUMNS_STATUS), iAddCols);
XP_Rect rect;
// Erase all existing lines
// NOTE: We can't draw/erase just the "new" line because
// we may skip lines if mouse is moved fast, causing a mess
for( i=1; i< m_iAddCols; i++ )
{
CalcAddColRect(i, &new_rect, &rect);
if( bRectChanged )
FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
}
// Draw all new lines based on new number of columns
for( i=1; i< iAddCols; i++ )
{
CalcAddColRect(i, &new_rect, &rect);
if( bRectChanged )
FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, FALSE);
}
m_iAddCols = iAddCols;
} else if( m_iStyle == ED_SIZE_ADD_ROWS )
{
int iAddRows = max(1, iNewHeight / EDT_NEW_ROW_HEIGHT);
PR_snprintf(pMsg, 128, XP_GetString(XP_EDT_ADD_ROWS_STATUS), iAddRows);
XP_Rect rect;
// Erase all existing lines
for( i=1; i< m_iAddRows; i++ )
{
CalcAddRowRect(i, &new_rect, &rect);
if( bRectChanged )
FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
}
// Draw all new lines based on new number of columns
for( i=1; i< iAddRows; i++ )
{
CalcAddRowRect(i, &new_rect, &rect);
if( bRectChanged )
FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, FALSE);
}
m_iAddRows = iAddRows;
}
FE_Progress(m_pBuffer->m_pContext, pMsg);
m_bFirstTime = FALSE;
// Indicate that rect changed
return bRectChanged;
}
// Rect is same as last time
return FALSE;
}
void CSizingObject::ResizeObject()
{
// Erase visual feedback when adding rows or columns
EraseAddRowsOrCols();
int32 iWidth=0, iWidthPixels=0, iHeightPixels=0, iHeight=0;
// Get the element being sized
CEditLeafElement *pElement = (CEditLeafElement*)(m_pLoElement->lo_any.edit_element);
if( !(m_iStyle == ED_SIZE_ADD_ROWS || m_iStyle == ED_SIZE_ADD_COLS) )
{
iWidthPixels = m_Rect.right - m_Rect.left;
iHeightPixels = m_Rect.bottom - m_Rect.top;
// Convert to percent if that's what we are using
// Note that we do not change that mode when returning new size
if( m_bWidthPercent )
{
iWidth = (iWidthPixels * 100) / m_iParentWidth;
} else {
iWidth = iWidthPixels;
}
if( m_bHeightPercent )
{
iHeight = (iHeightPixels * 100) / m_iViewHeight;
} else {
iHeight = iHeightPixels;
}
}
// Change the appropriate element's width and/or height
switch ( m_pLoElement->type )
{
case LO_IMAGE:
{
if( pElement && pElement->IsIcon() )
{
// TODO: ALL "UNKNOWN" TAGS ARE HANDLED HERE
// Only change dimension if we were sizing it
if( !(m_iStyle & ED_SIZE_LEFT ||
m_iStyle & ED_SIZE_RIGHT) )
{
iWidth = 0; // Signal to not change width...
}
if( !(m_iStyle & ED_SIZE_TOP ||
m_iStyle & ED_SIZE_BOTTOM) )
{
iHeight = 0; // ...or height
}
if( iWidth > 0 || iHeight > 0 )
{
m_pBuffer->BeginBatchChanges(kSetUnknownTagDataCommandID);
pElement->SetSize(m_bWidthPercent, iWidth, m_bHeightPercent, iHeight);
m_pBuffer->EndBatchChanges();
}
m_pBuffer->Relayout(pElement, 0);
} else if( pElement && pElement->IsImage() )
{
EDT_ImageData * pImageData = pElement->Image()->GetImageData();
if( pImageData )
{
// Only change dimension if we were sizing it
if( m_iStyle & ED_SIZE_TOP ||
m_iStyle & ED_SIZE_BOTTOM )
{
pImageData->iHeight = iHeight;
}
if( m_iStyle & ED_SIZE_LEFT ||
m_iStyle & ED_SIZE_RIGHT )
{
pImageData->iWidth = iWidth;
}
m_pBuffer->BeginBatchChanges(kSetImageDataCommandID);
// We are not changing image URL, so third param
// (keep-images-with-doc) shouldn't matter
pElement->Image()->SetImageData(pImageData);
edt_FreeImageData(pImageData);
m_pBuffer->Relayout(pElement,0);
m_pBuffer->EndBatchChanges();
}
}
break;
}
case LO_HRULE:
{
if( pElement )
{
EDT_HorizRuleData * pHData = pElement->HorizRule()->GetData(); //m_pBuffer->GetHorizRuleData();
if( pHData )
{
// Only change dimension if we were sizing it
if( m_iStyle & ED_SIZE_TOP ||
m_iStyle & ED_SIZE_BOTTOM )
{
pHData->size = iHeight;
}
if( m_iStyle & ED_SIZE_LEFT ||
m_iStyle & ED_SIZE_RIGHT )
{
pHData->iWidth = iWidth;
}
m_pBuffer->BeginBatchChanges(kSetHorizRuleDataCommandID);
pElement->HorizRule()->SetData(pHData);
EDT_FreeHorizRuleData(pHData);
m_pBuffer->Relayout(pElement,0);
m_pBuffer->EndBatchChanges();
}
}
break;
}
case LO_TABLE:
{
CEditTableElement *pTable =
(CEditTableElement*)edt_GetTableElementFromLO_Element( m_pLoElement, LO_TABLE );
if( pTable )
{
if( m_iStyle == ED_SIZE_ADD_ROWS ||
m_iStyle == ED_SIZE_ADD_COLS )
{
m_pBuffer->BeginBatchChanges(m_iStyle == ED_SIZE_ADD_COLS ?
kInsertTableColumnCommandID : kInsertTableRowCommandID);
// Be sure caret is already in the table,
// but don't show caret or scroll window
m_pBuffer->MoveAndHideCaretInTable(m_pLoElement);
// Move to last cell of table
while(m_pBuffer->NextTableCell())
;
if( m_iStyle == ED_SIZE_ADD_ROWS )
m_pBuffer->InsertTableRows(NULL, TRUE, m_iAddRows);
else
m_pBuffer->InsertTableColumns(NULL, TRUE, m_iAddCols);
// InsertTable... will relayout
//m_pBuffer->Relayout(pTable, 0);
m_pBuffer->EndBatchChanges();
}
else
{
// Sizing table width or height
EDT_TableData * pTableData = pTable->GetData();
XP_Bool bChangeWidth = FALSE;
XP_Bool bChangeHeight = FALSE;
if( pTableData )
{
XP_Bool bChangeTable = FALSE;
if( (m_iStyle & ED_SIZE_RIGHT) && iWidth != m_iStartWidth )
{
pTableData->bWidthDefined = TRUE;
pTableData->iWidth = iWidth;
pTableData->iWidthPixels = iWidthPixels;
bChangeWidth = TRUE;
}
if( (m_iStyle & ED_SIZE_BOTTOM) && iHeight != m_iStartHeight )
{
pTableData->bHeightDefined = TRUE;
pTableData->iHeight = iHeight;
pTableData->iHeightPixels = iHeightPixels;
bChangeHeight = TRUE;
}
if( bChangeWidth || bChangeHeight )
{
m_pBuffer->BeginBatchChanges(kSetTableDataCommandID);
pTable->SetData(pTableData);
// Use this instead of Relayout to properly
// adjust sizing params before relayout, then restore after
m_pBuffer->ResizeTable(pTable, bChangeWidth, bChangeHeight);
m_pBuffer->EndBatchChanges();
}
EDT_FreeTableData(pTableData);
}
}
}
break;
}
case LO_CELL:
{
CEditTableCellElement *pCell =
(CEditTableCellElement*)edt_GetTableElementFromLO_Element( m_pLoElement, LO_CELL );
if( pCell )
{
CEditTableElement *pTable = pCell->GetParentTable();
// Should never happen!
if( !pTable )
break;
XP_Bool bChangeWidth = FALSE;
XP_Bool bChangeHeight = FALSE;
m_pBuffer->BeginBatchChanges(kSetTableCellDataCommandID);
if( (m_iStyle & ED_SIZE_RIGHT) && iWidth != m_iStartWidth )
{
bChangeWidth = TRUE;
EDT_TableCellData *pData = pCell->GetData();
if( pData )
{
pData->bWidthDefined = TRUE;
pData->iWidthPixels = iWidthPixels;
// Resize all cells sharing or spanning the right border
pCell->SetColumnWidthRight(pTable, m_pLoElement, pData);
EDT_FreeTableCellData(pData);
}
}
if( (m_iStyle & ED_SIZE_BOTTOM) && iHeight != m_iStartHeight )
{
bChangeHeight = TRUE;
EDT_TableCellData *pData = pCell->GetData();
if( pData )
{
pData->bHeightDefined = TRUE;
pData->iHeightPixels = iHeightPixels;
// Resize all cells sharing or spanning the bottom border
pCell->SetRowHeightBottom(pTable, m_pLoElement, pData);
EDT_FreeTableCellData(pData);
}
}
if( bChangeWidth || bChangeHeight )
{
// Relayout the entire table
// (This modifies current table and cell params
// to facilitate resizing, then restores after Relayout)
m_pBuffer->ResizeTableCell(pTable, bChangeWidth, bChangeHeight);
}
m_pBuffer->EndBatchChanges();
}
break;
}
}
}
void CSizingObject::EraseAddRowsOrCols()
{
int i;
XP_Rect rect;
if( m_iStyle == ED_SIZE_ADD_COLS )
{
// Erase all existing lines
// NOTE: We can't draw/erase just the "new" line because
// we may skip lines if mouse is moved fast, causing a mess
for( i=1; i< m_iAddCols; i++ )
{
CalcAddColRect(i, &m_Rect, &rect);
FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
}
} else if( m_iStyle == ED_SIZE_ADD_ROWS )
{
// Erase all existing lines
for( i=1; i< m_iAddRows; i++ )
{
CalcAddRowRect(i, &m_Rect, &rect);
FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
}
}
}
CSizingObject::~CSizingObject()
{
// Assure that next comparison to a new rect
// will be different
m_Rect.left = -1;
FE_Progress(m_pBuffer->m_pContext, "");
// Don't leave object in selected state
m_pBuffer->ClearSelection(TRUE, FALSE);
}
///////////////////////////////////////////////////
// Relayout timer ... doesn't quite work perfectly.
CRelayoutTimer::CRelayoutTimer(){
m_pBuffer = NULL;
}
void CRelayoutTimer::SetEditBuffer(CEditBuffer* pBuffer){
m_pBuffer = pBuffer;
}
void CRelayoutTimer::OnCallback() {
m_pBuffer->Relayout(m_pStartElement, m_iOffset);
}
void CRelayoutTimer::Flush() {
if (IsTimeoutEnabled() ) {
Cancel();
OnCallback();
}
}
void CRelayoutTimer::Relayout( CEditElement *pStartElement, int iOffset)
{
const uint32 kRelayoutDelay = 50;
XP_Bool bSetValues = TRUE;
XP_Bool bInTable = pStartElement->GetTableIgnoreSubdoc() != NULL;
if ( ! bInTable ) {
Flush();
m_pBuffer->Relayout(pStartElement, iOffset);
return;
}
if ( IsTimeoutEnabled() ) {
Cancel();
if ( pStartElement != m_pStartElement ) {
OnCallback(); // Do saved relayout now.
}
else {
bSetValues = FALSE;
}
}
if ( bSetValues) {
m_pStartElement = pStartElement;
m_iOffset = iOffset;
}
// Put the character into the LO_Element string so that the two data structures are consistent.
if (pStartElement->IsText() ){
CEditTextElement* pTextElement = pStartElement->Text();
LO_TextStruct* pLoText = NULL;
int iLoOffset = 0;
if ( pTextElement->GetLOTextAndOffset(iOffset, FALSE, pLoText, iLoOffset) ) {
if ( pLoText ) {
int32 textLen = pLoText->text_len;
char *pText;
if (textLen <= 0 ){
PA_FREE(pLoText->text);
pLoText->text = (PA_Block) PA_ALLOC(2); // char plus zero terminated.
}
else {
pLoText->text = (PA_Block) PA_REALLOC(pLoText->text, textLen + 2);
}
PA_LOCK(pText, char *, pLoText->text);
int32 bytesToMove = textLen - iLoOffset;
if ( bytesToMove > 0){
XP_MEMMOVE(pText + iLoOffset +1, pText + iLoOffset, bytesToMove);
}
pText[iLoOffset] = pTextElement->GetText()[iOffset];
pText[textLen+1] = '\0';
// XP_TRACE(("pText=%08x %s %d\n", pText, pText, textLen));
PA_UNLOCK(pLoText->text);
textLen++;
pLoText->text_len = (int16) textLen;
}
}
}
SetTimeout(kRelayoutDelay);
}
CAutoSaveTimer::CAutoSaveTimer() {
m_pBuffer = NULL;
m_iMinutes = 0;
m_bSuspended = FALSE;
m_bCalledWhileSuspended = FALSE;
}
void CAutoSaveTimer::SetEditBuffer(CEditBuffer* pBuffer){
m_pBuffer = pBuffer;
// Sanity check, is this an OK Buffer?
XP_ASSERT(m_pBuffer->m_pRoot);
}
void CAutoSaveTimer::OnCallback() {
if( m_bSuspended ){
m_bCalledWhileSuspended = TRUE;
} else {
// Sanity check -- is this an OK Buffer?
if ( ! m_pBuffer || ! m_pBuffer->m_pRoot ) {
XP_ASSERT(FALSE);
return;
}
// Skip autosave if currently suspended
m_pBuffer->AutoSaveCallback();
Restart();
}
}
void CAutoSaveTimer::Restart() {
#ifdef DEBUG_AUTO_SAVE
const int32 kMillisecondsPerMinute = 6000L; //60000L; // * 1000 / 10;
#else
const int32 kMillisecondsPerMinute = 60000L;
#endif
if ( m_iMinutes > 0 ) {
SetTimeout(m_iMinutes * kMillisecondsPerMinute);
}
}
void CAutoSaveTimer::SetPeriod(int32 minutes){
Cancel();
m_iMinutes = minutes;
Restart();
}
int32 CAutoSaveTimer::GetPeriod(){
return m_iMinutes;
}
void CAutoSaveTimer::Suspend(){
m_bSuspended = TRUE;
}
void CAutoSaveTimer::Resume(){
m_bSuspended = FALSE;
// We need to restart the timer ONLY
// if we were called during a suspended period
if( m_bCalledWhileSuspended ){
m_bCalledWhileSuspended = FALSE;
} else {
Restart();
}
}
CEditDocState::CEditDocState() {
m_pBuffer = NULL;
m_version = 0;
}
CEditDocState::~CEditDocState() {
if (m_pBuffer) {
XP_HUGE_FREE(m_pBuffer);
}
}
#ifdef DEBUG
void CEditDocState::Print(IStreamOut& stream) {
if (!m_pBuffer) return;
stream.Printf("%d bytes", XP_STRLEN(m_pBuffer));
// have to dump one line at a time. XP_TRACE has a 512 char limit on Windows.
char* b = m_pBuffer;
while ( *b != '\0' ) {
char* b2 = b;
while ( *b2 != '\0' && *b2 != '\n'){
b2++;
}
char old = *b2;
*b2 = '\0';
stream.Printf("%s\n", b);
*b2 = old;
b = b2 + 1;
if ( old == '\0' ) break;
}
}
#endif
/* Take the document for current context and change appropriate
* params to make it look like a "Untitled" new document
* Allows loading any document as a "Template"
*/
void EDT_ConvertCurrentDocToNewDoc(MWContext * pMWContext)
{
GET_WRITABLE_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer);
pEditBuffer->ConvertCurrentDocToNewDoc();
}
char * EDT_GetFilename(char * pURL, XP_Bool bMustHaveExtension)
{
// No string or its empty
if( !pURL || !*pURL ){
return NULL;
}
// Isolate just the filename of source URL
char * pFilename = XP_STRRCHR(pURL, '/');
// Maybe its a local filename?
if(!pFilename){
pFilename = XP_STRRCHR(pURL, '\\');
}
// Or just after host or drive letter?
if(!pFilename){
pFilename = XP_STRCHR(pURL, ':');
}
if(pFilename){
// Filename starts just after "/"
pFilename++;
} else {
// Use what we have - maybe not a full URL spec?
pFilename = pURL;
}
char * pResult = NULL;
if( pFilename && *pFilename ){
// Don't include any stuff after filename
char * ques = XP_STRCHR(pFilename, '?');
char * hash = XP_STRCHR(pFilename, '#');
if(ques){
*ques = '\0';
}
if(hash){
*hash = '\0';
}
// Check if we must have a period to be considered a filename
// This allows text without "." to be considered a subdirectory, not filename
if( *pFilename ){
if( !bMustHaveExtension ||
(bMustHaveExtension && XP_STRCHR(pFilename, '.')) ){
// Copy just the filename if we have one
pResult = XP_STRDUP(pFilename);
}
}
// set the values back
if(ques){
*ques = '?';
}
if(hash){
*hash = '#';
}
}
return pResult;
}
char * EDT_ReplaceFilename(char * pBaseURL, char * pURL, XP_Bool bMustHaveExtension)
{
char * pResult = NULL;
if( !pBaseURL || !*pBaseURL ){
return NULL;
}
// Find the start of filename in base -
// copy to new string
// **** We overload the meaning of bMustHaveExtension to
// also cause any filename on the base to have an extension,
// else any name without trailing slash, such as //chainsaw/mydir",
// will be "corrected" by adding trailing slash
if( bMustHaveExtension ){
char * pBaseFilename = EDT_GetFilename(pBaseURL, bMustHaveExtension);
int iLen = XP_STRLEN(pBaseURL);
if( !pBaseFilename && pBaseURL[iLen-1] != '/' ){
// No trailing slash found, copy string and append one
pResult = (char*)XP_ALLOC(iLen+1);
if( !pResult ){
return NULL;
}
XP_STRCPY(pResult, pBaseURL);
pResult[iLen] = '/';
pResult[iLen+1] = '\0';
}
XP_FREEIF(pBaseFilename);
}
if( !pResult ){
char * pBaseEnd = XP_STRRCHR(pBaseURL, '/');
if( !pBaseEnd )
pBaseEnd = XP_STRCHR(pBaseURL, ':');
if( !pBaseEnd )
return NULL;
pBaseEnd++;
char save_char = *pBaseEnd;
*pBaseEnd = '\0';
// Allocate result and copy the base part
StrAllocCat(pResult, pBaseURL);
// Restore character
*pBaseEnd = save_char;
}
if( pResult && pURL ){
// Get just the filename of source URL
char * pFilename = EDT_GetFilename(pURL, bMustHaveExtension);
if( pFilename ){
// Append the filename to the base
StrAllocCat(pResult, pFilename);
XP_FREE(pFilename);
}
}
//Note: If no pURL, pBaseURL is returned stripped of its filename
return pResult;
}
#if defined(SingleSignon)
static XP_Bool GetSingleSignonData(MWContext * pMWContext, char *pLocation, char **ppUsername, char **ppPassword)
{
if( !pLocation )
return FALSE;
char *pUsernameSingleSignon = NULL;
char *pPasswordSingleSignon = NULL;
SI_RestoreOldSignonDataFromBrowser(pMWContext, pLocation, FALSE,
&pUsernameSingleSignon, &pPasswordSingleSignon);
if (pUsernameSingleSignon)
{
if( ppUsername )
*ppUsername = pUsernameSingleSignon;
else
XP_FREE(pUsernameSingleSignon);
if( ppPassword )
*ppPassword = pPasswordSingleSignon;
else if( pPasswordSingleSignon )
XP_FREE(pPasswordSingleSignon);
return TRUE;
}
return FALSE;
}
#endif
char * EDT_GetDefaultPublishURL(MWContext * pMWContext, char **ppFilename, char **ppUsername, char **ppPassword)
{
XP_ASSERT(pMWContext);
char * pURL = LO_GetBaseURL(pMWContext);
if( !pURL ){
return NULL;
}
XP_Bool bLastPublishFailed = EDT_IsSameURL(CEditSaveObject::m_pFailedPublishUrl, pURL, NULL, NULL);
if( ppFilename ){
if( EDT_IS_NEW_DOCUMENT(pMWContext) ){
// New page: empty filename
*ppFilename = NULL;
} else {
// Extract current filename part
*ppFilename = EDT_GetFilename(pURL, TRUE);
// On win16 force to lower case.
#ifdef XP_WIN16
char *pConvert = *ppFilename;
while (pConvert && *pConvert) {
*pConvert = XP_TO_LOWER(*pConvert);
pConvert++;
}
#endif
}
}
// Default: Clear password
if( ppPassword ){
*ppPassword = NULL;
}
#if defined(SingleSignon)
// Check if we saved a username/password for this URL
// (We will find it only if we are editing a remote URL)
if( GetSingleSignonData(pMWContext, pURL, ppUsername, ppPassword) )
// If we found a name, we assume the location part of
// URL is correct, so just return that
return EDT_ReplaceFilename(pURL, NULL, TRUE);
#endif
if( !bLastPublishFailed && !EDT_IS_NEW_DOCUMENT(pMWContext) ){
int iType = NET_URL_Type(pURL);
if( iType == FTP_TYPE_URL ||
iType == HTTP_TYPE_URL ||
iType == SECURE_HTTP_TYPE_URL ) {
// We are editing a remote page, so try to return it to that location
// (We don't know the password)
// Truncate current URL at the filename
return EDT_ReplaceFilename(pURL, NULL, TRUE);
}
}
char *pPrefURL = NULL;
if( bLastPublishFailed ){
// use the last location saved - this will be the location we failed at before
PREF_CopyCharPref("editor.publish_last_loc", &pPrefURL);
} else {
// Use the last successfully-used location saved in preferences
PREF_CopyCharPref("editor.publish_history_0", &pPrefURL);
}
// Be sure to check for empty pref string
if( !pPrefURL || !*pPrefURL ){
XP_FREEIF(pPrefURL);
// No last-used location -- use the default location
PREF_CopyCharPref("editor.publish_location", &pPrefURL);
}
char * pLocation = 0;
char * pUsername = ppUsername ? *ppUsername : 0;
NET_ParseUploadURL( pPrefURL, &pLocation, &pUsername, NULL );
#if defined(SingleSignon)
// Check if we saved a username/password for the pref location
if(! GetSingleSignonData(pMWContext, pLocation, ppUsername, ppPassword) )
#endif
{
if (ppUsername)
*ppUsername = pUsername;
}
XP_FREEIF(pPrefURL);
return pLocation;
}
//
// Keep this localized here. These two functions are the only ones
// that need to know this value.
// NOTE: CALLER MUST FREE THE RETURNED STRINGS
#define MAX_PUBLISH_LOCATIONS 20
#ifdef XP_WIN
XP_Bool
EDT_GetPublishingHistory(MWContext *pMWContext,
unsigned n,
char** ppLocation,
char** ppUsername,
char** ppPassword)
#else
XP_Bool
EDT_GetPublishingHistory(unsigned n,
char** ppLocation,
char** ppUsername,
char** ppPassword)
#endif
{
char prefname[32];
char* prefvalue = NULL;
if (n >= MAX_PUBLISH_LOCATIONS)
return FALSE;
XP_SPRINTF(prefname, "editor.publish_history_%d", n);
if( PREF_CopyCharPref(prefname, &prefvalue) == -1 ||
prefvalue == NULL || prefvalue[0] == '\0')
return FALSE;
if (ppLocation != NULL)
*ppLocation = NULL; /* else NET_ParseUploadURL() will try to free! */
if (ppUsername != NULL)
*ppUsername = NULL;
XP_Bool return_value;
return_value = NET_ParseUploadURL(prefvalue, ppLocation, ppUsername, NULL);
if (!return_value)
{
XP_FREE(prefvalue);
return FALSE;
}
if (ppPassword != NULL)
{
#ifdef XP_WIN
#if defined(SingleSignon)
// Check if we saved a username/password for this URL via SingleSignon
GetSingleSignonData(pMWContext, *ppLocation, ppUsername, ppPassword);
#endif
#endif
}
XP_FREEIF(prefvalue);
return TRUE;
}
void edt_SyncPublishingHistory(MWContext *pMWContext)
{
char prefname[32];
char* prefvalue = NULL;
char* pLastLoc = NULL;
int i;
/*
* Last location should be set when you start a publish.
* Now copy this to the top (first) of the history.
* Abort if pref doesn't exist or is empty
*/
if( PREF_CopyCharPref("editor.publish_last_loc", &pLastLoc) == -1 ||
!pLastLoc || !*pLastLoc )
return;
/*
* First scan the list to find pref that matches new item
*/
for (i = 0; i < (MAX_PUBLISH_LOCATIONS - 1); i++) {
XP_SPRINTF(prefname, "editor.publish_history_%d", i);
if( PREF_CopyCharPref(prefname, &prefvalue) == -1 ||
!prefvalue || !*prefvalue ||
XP_STRCMP(prefvalue, pLastLoc) == 0) {
//
// No history or this history matches the last publish location.
//
break;
}
XP_FREEIF(prefvalue);
}
XP_FREEIF(prefvalue);
/*
* Now shift everything up a slot.
*/
for (; i > 0; i--) {
XP_SPRINTF(prefname, "editor.publish_history_%d", i-1);
PREF_CopyCharPref(prefname, &prefvalue);
XP_SPRINTF(prefname, "editor.publish_history_%d", i);
PREF_SetCharPref(prefname, prefvalue);
XP_FREEIF(prefvalue);
}
/*
* Now set the zero'th one.
*/
PREF_SetCharPref("editor.publish_history_0", pLastLoc);
XP_FREEIF(pLastLoc);
}
XP_Bool EDT_GetUserDefaultPublishData(MWContext *pMWContext, char **ppLocation, char **ppUsername, char **ppPassword)
{
char * pDefaultLocation = NULL;
PREF_CopyCharPref("editor.publish_location", &pDefaultLocation);
if( !pDefaultLocation){
return FALSE;
}
char *pLocation = NULL;
char *pUsername = NULL;
// Parse the preference string to extract Username
NET_ParseUploadURL( pDefaultLocation, &pLocation, &pUsername, NULL );
#if defined(SingleSignon)
// Check if we saved a username/password for the pref location
if( !GetSingleSignonData(pMWContext, *ppLocation, ppUsername, ppPassword) )
#endif
{
if (ppUsername)
*ppUsername = pUsername;
else
XP_FREEIF(pUsername);
}
if( ppLocation )
*ppLocation = pLocation;
else
XP_FREEIF(pLocation);
return TRUE;
}
XP_Bool EDT_GetNetcenterPublishData(MWContext *pMWContext, char **ppLocation, char **ppUsername, char **ppPassword)
{
//TODO: FINISH THIS
return FALSE;
}
// Cache the Edit History data
static char** ppEditHistoryURLs = NULL;
static char** ppEditHistoryTitles = NULL;
// NOTE: Caller must NOT free the returned strings - they are locally cached
XP_Bool
EDT_GetEditHistory(MWContext * pMWContext, unsigned n,
char** ppUrl, char** ppTitle)
{
char prefname[32];
unsigned i;
// if context is NULL, bail!
if( !pMWContext )
return FALSE;
// Initialize list if not done so already
if( !ppEditHistoryURLs )
EDT_SyncEditHistory(pMWContext);
if( !ppEditHistoryURLs )
return FALSE;
// Check if current URL is matches the requested or more recent items in the list
if( pMWContext )
{
History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
if( pEntry && pEntry->address )
{
for( i = 0; i <= n; i++ ){
XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
if( ppEditHistoryURLs[i] &&
EDT_IsSameURL(pEntry->address, ppEditHistoryURLs[i], 0, 0) )
{
// The current doc was found, so skip over it
n++;
break;
}
}
}
}
// Default in case we don't set the location
if( ppUrl )
*ppUrl = NULL;
if( n >= MAX_EDIT_HISTORY_LOCATIONS )
return FALSE;
if( !ppEditHistoryURLs[n] )
return FALSE;
// Return URL and Title to caller
if( ppUrl )
*ppUrl = ppEditHistoryURLs[n];
if( ppTitle )
*ppTitle = ppEditHistoryTitles[n];
return TRUE;
}
static char pEmpty[] = "";
void
EDT_SyncEditHistory(MWContext * pMWContext)
{
char prefname[32];
char* prefvalue = NULL;
char* pTitle = pEmpty;
int i;
if(!pMWContext)
return;
// We are not in a happy state if there's no history item
History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
if(!pEntry || !pEntry->address || !*pEntry->address )
return;
// Test for new document URL: file://Untitled and ignore it
if( !XP_STRCMP(XP_GetString(XP_EDIT_NEW_DOC_NAME), pEntry->address) ||
!XP_STRCMP(XP_GetString(XP_EDIT_NEW_DOC_URL), pEntry->address))
{
return;
}
// Initialize the local arrays from the current preference list
if( !ppEditHistoryURLs )
{
ppEditHistoryURLs = (char**)XP_ALLOC( MAX_EDIT_HISTORY_LOCATIONS * sizeof(char*) );
if( !ppEditHistoryURLs )
return;
ppEditHistoryTitles = (char**)XP_ALLOC( MAX_EDIT_HISTORY_LOCATIONS * sizeof(char*) );
if( !ppEditHistoryTitles )
{
XP_FREEIF(ppEditHistoryURLs);
return;
}
for( i = 0; i < MAX_EDIT_HISTORY_LOCATIONS; i++ )
{
XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
if(PREF_CopyCharPref(prefname, &prefvalue) != -1 &&
prefvalue && *prefvalue )
{
ppEditHistoryURLs[i] = prefvalue;
prefvalue = NULL;
} else {
ppEditHistoryURLs[i] = NULL;
XP_FREEIF(prefvalue);
}
XP_SPRINTF(prefname, "editor.url_history.TITLE_%d", i);
if(PREF_CopyCharPref(prefname, &prefvalue) != -1 &&
prefvalue && *prefvalue )
{
ppEditHistoryTitles[i] = prefvalue;
prefvalue = NULL;
} else {
ppEditHistoryTitles[i] = NULL;
XP_FREEIF(prefvalue);
}
}
}
// We failed to initialize - quit
if( !ppEditHistoryURLs )
return;
if( pEntry->title && *pEntry->title ){
pTitle = pEntry->title;
}
/*
* First scan the list to find pref URL that matches current page
*/
for (i = 0; i < (MAX_EDIT_HISTORY_LOCATIONS - 1); i++)
{
XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
if( !ppEditHistoryURLs[i] ||
EDT_IsSameURL(ppEditHistoryURLs[i], pEntry->address, 0, 0) ) {
//
// No history or this history matches the current last-edited URL
//
break;
}
}
// Now i = the index to last-edited URL or last possible URL in the list
if( i == (MAX_EDIT_HISTORY_LOCATIONS-1) )
{
// We will be replacing the last item, so delete it
// so delete it
XP_FREEIF(ppEditHistoryURLs[i]);
XP_FREEIF(ppEditHistoryTitles[i]);
}
/*
* Now shift everything one item.
*/
for (; i > 0; i--)
{
XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
// Not sure if this can handle null string, so lets not try!
PREF_SetCharPref(prefname, ppEditHistoryURLs[i-1] ? ppEditHistoryURLs[i-1] : pEmpty);
ppEditHistoryURLs[i] = ppEditHistoryURLs[i-1];
XP_SPRINTF(prefname, "editor.url_history.TITLE_%d", i);
PREF_SetCharPref(prefname, ppEditHistoryTitles[i-1] ? ppEditHistoryTitles[i-1] : pEmpty);
ppEditHistoryTitles[i] = ppEditHistoryTitles[i-1];
}
/*
* Now set the zero'th one -- the current URL
*/
PREF_SetCharPref("editor.url_history.URL_0", pEntry->address);
PREF_SetCharPref("editor.url_history.TITLE_0", pTitle);
ppEditHistoryURLs[0] = XP_STRDUP(pEntry->address);
if( pTitle && *pTitle )
{
ppEditHistoryTitles[0] = XP_STRDUP(pTitle);
} else {
ppEditHistoryTitles[0] = NULL;
}
}
void edt_UpdateEditHistoryTitle(MWContext * pMWContext, char * pTitle)
{
if( pMWContext )
{
History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
if(pEntry && pEntry->address && !*pEntry->address)
{
for (int i = 0; i < MAX_EDIT_HISTORY_LOCATIONS; i++)
{
char prefname[32];
XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
if( ppEditHistoryURLs[i] &&
EDT_IsSameURL(ppEditHistoryURLs[i], pEntry->address, 0, 0) )
{
XP_FREEIF(ppEditHistoryTitles[i]);
if( pTitle )
ppEditHistoryTitles[i] = pTitle;
// Also update the preference value
char prefname[32];
XP_SPRINTF(prefname, "editor.url_history.TITLE_%d", i);
// Not sure if this can handle null string, so lets not try!
PREF_SetCharPref(prefname, pTitle ? pTitle : pEmpty);
return;
}
}
}
}
}
char * EDT_GetPageTitleFromFilename(char * pFilename)
{
char * pTitle = NULL;
if( pFilename ){
// Get Full "filename" and we don't need an extension
pTitle = EDT_GetFilename(pFilename, FALSE);
if( pTitle ){
// Don't include extension
char * period = XP_STRCHR(pTitle, '.');
if(period){
// Truncate at the beginning of extension
*period = '\0';
}
}
}
return pTitle;
}
XP_Bool EDT_IsImageURL(char *pImageURL)
{
// Check for all known image file extensions
// without regard for case
if( pImageURL && *pImageURL &&
(strcasestr(pImageURL, ".jpeg") ||
strcasestr(pImageURL, ".jpg") ||
strcasestr(pImageURL, ".gif") ||
strcasestr(pImageURL, ".png") ||
strcasestr(pImageURL, ".xbmp")) )
{
return TRUE;
}
return FALSE;
}
static int StrcmpModuloSlashes(const char* s1, const char* s2)
{
if (!s1)
return -1;
if (!s2)
return 1;
while(1)
{
if (*s1 != *s2 || *s1 == 0)
return *s1 - *s2;
if (*s1 == '/')
{
while (*(++s1) == '/')
;
while (*(++s2) == '/')
;
continue;
}
++s1;
++s2;
}
}
// True if both urls are the same, ignores any username/password
// information. Does caseless comparison for file:// URLs
// on windows and mac.
// On Unix, collapses consecutive multiple slashes.
// url1 and url2 are relative to base1 and base2, respectively.
// If url1 or url2 is already absolute, base1 or base2 can
// be passed in as NULL.
XP_Bool EDT_IsSameURL(char *url1,char *url2,char *base1,char *base2) {
// Check for NULL or empty strings.
if (!url1 || !url2 || !*url1 || !*url2) {
return FALSE;
}
// Used passed in base URLs if provided.
char *pAbs1 = NULL;
char *pAbs2 = NULL;
if (base1) {
pAbs1 = NET_MakeAbsoluteURL(base1,url1);
}
if (base2) {
pAbs2 = NET_MakeAbsoluteURL(base2,url2);
}
// Strip any username/password info from urls in making comparison if
// they are http, https, or ftp.
char *pUrl1 = edt_StripUsernamePassword(pAbs1 ? pAbs1 : url1);
char *pUrl2 = edt_StripUsernamePassword(pAbs2 ? pAbs2 : url2);
edt_StripAtHashOrQuestionMark(pUrl1);
edt_StripAtHashOrQuestionMark(pUrl2);
XP_Bool bRetVal = FALSE;
if (pUrl1 && pUrl2) {
//char *pBaseUrl1 = NET_ParseURL(pUrl1, GET_PATH_PART);
//char *pBaseUrl2 = NET_ParseURL(pUrl2, GET_PATH_PART);
#if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2)
// Strip off Target (named Anchors)
// Local file URLs are equivalent if they differ only
// by case.
if (NET_URL_Type(pUrl1) == FILE_TYPE_URL) {
bRetVal = (XP_STRCASECMP( pUrl1, pUrl2 ) == 0);
}
else {
bRetVal = (XP_STRCMP( pUrl1, pUrl2 ) == 0 );
}
#else
// Use regular strcmp.
bRetVal = (StrcmpModuloSlashes( pUrl1, pUrl2 ) == 0 );
#endif
}
XP_FREEIF(pUrl1);
XP_FREEIF(pUrl2);
XP_FREEIF(pAbs1);
XP_FREEIF(pAbs2);
return bRetVal;
}
// Replace the current selection with supplied text
void EDT_ReplaceText(MWContext *pContext, char * pReplaceText, XP_Bool bReplaceAll,
char *pTextToLookFor, XP_Bool bCaseless, XP_Bool bBackward, XP_Bool bDoWrap)
{
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->ReplaceLoop( pReplaceText, bReplaceAll,
pTextToLookFor, bCaseless, bBackward, bDoWrap );
}
// Block of functions moved from EDITOR.CPP for Win16 Build
// NOT USED???
#ifdef FIND_REPLACE
XP_Bool EDT_FindAndReplace(MWContext *pContext,
EDT_FindAndReplaceData *pData ){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
return pEditBuffer->FindAndReplace( pData );
}
#endif // FIND_REPLACE
// Table Sizing, Selection, Add Row/Col interface
ED_HitType EDT_GetTableHitRegion(MWContext *pContext, int32 xVal, int32 yVal,
LO_Element **ppElement, XP_Bool bModifierKeyPressed)
{
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) ED_HIT_NONE;
return pEditBuffer->GetTableHitRegion(xVal, yVal, ppElement, bModifierKeyPressed);
}
ED_HitType EDT_GetSelectedTableElement(MWContext *pContext, LO_Element **ppElement){
return EDT_GetTableHitRegion(pContext, 0, 0, ppElement, FALSE);
}
// Object Sizer
ED_SizeStyle EDT_CanSizeObject(MWContext *pContext, LO_Element *pElement,
int32 xVal, int32 yVal){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
return pEditBuffer->CanSizeObject(pElement, xVal, yVal);
}
XP_Bool EDT_IsSizing(MWContext *pContext){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
return pEditBuffer->IsSizing();
}
ED_SizeStyle EDT_StartSizing(MWContext *pContext, LO_Element *pElement, int32 xVal, int32 yVal,
XP_Bool bModifierKeyPressed, XP_Rect *pRect){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
return pEditBuffer->StartSizing(pElement, xVal, yVal, bModifierKeyPressed, pRect);
}
XP_Bool EDT_GetSizingRect(MWContext *pContext, int32 xVal, int32 yVal,
XP_Bool bModifierKeyPressed, XP_Rect *pRect){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
return pEditBuffer->GetSizingRect(xVal, yVal, bModifierKeyPressed, pRect);
}
void EDT_EndSizing(MWContext *pContext){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->EndSizing();
}
void EDT_CancelSizing(MWContext *pContext){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->CancelSizing();
}
ED_BufferOffset EDT_GetInsertPointOffset( MWContext *pContext ){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
CPersistentEditInsertPoint p;
pEditBuffer->GetInsertPoint( p );
return (ED_BufferOffset) p.m_index;
}
void EDT_SetInsertPointToOffset( MWContext *pContext, ED_BufferOffset i,
int32 iLen ){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
CPersistentEditInsertPoint p((ElementIndex)i);
if( iLen != 0 ){
CPersistentEditInsertPoint p1((ElementIndex)i+iLen);
CPersistentEditSelection perSel;
perSel = CPersistentEditSelection( p, p1 );
CEditSelection sel = pEditBuffer->PersistentToEphemeral( perSel );
pEditBuffer->SetSelection( sel );
}
else {
pEditBuffer->SetInsertPoint( p );
}
}
void EDT_OffsetToLayoutElement( MWContext *pContext, ED_BufferOffset i,
LO_Element * *element, int32 *caretPos){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
CPersistentEditInsertPoint p((ElementIndex)i);
CEditInsertPoint eP = pEditBuffer->PersistentToEphemeral(p);
*element = eP.m_pElement->GetLayoutElement();
*caretPos = eP.m_iPos;
}
ED_BufferOffset EDT_LayoutElementToOffset( MWContext *pContext,
LO_Element *element, int32 caretPos){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) -1;
if (element->type != LO_TEXT)
return -1;
CEditInsertPoint insertPoint(element->lo_text.edit_element, caretPos);
CPersistentEditInsertPoint p = pEditBuffer->EphemeralToPersistent(insertPoint);
return p.m_index;
}
LO_TextBlock* EDT_GetTextBlock(MWContext *, LO_Element *le)
{
LO_TextBlock *block;
CEditTextElement *pText;
block = NULL;
if( ( le->type == LO_TEXT ) && ( le->lo_any.edit_element != NULL )
&& le->lo_any.edit_element->IsA( P_TEXT ) ){
pText = le->lo_any.edit_element->Text();
block = pText->GetTextBlock();
}
return block;
}
void EDT_GetSelectionOffsets(MWContext *pContext, ED_BufferOffset* pStart, ED_BufferOffset* pEnd){
if ( ! pStart || !pEnd ) {
XP_ASSERT(FALSE);
return;
}
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
CPersistentEditSelection s;
pEditBuffer->GetSelection( s );
*pStart = (ED_BufferOffset) s.m_start.m_index;
*pEnd = (ED_BufferOffset) s.m_end.m_index;
}
XP_Bool EDT_SelectFirstMisspelledWord( MWContext *pContext ){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
return pEditBuffer->FindNextMisspelledWord( TRUE, TRUE, 0 );
}
XP_Bool EDT_SelectNextMisspelledWord( MWContext *pContext ){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
return pEditBuffer->FindNextMisspelledWord( FALSE, TRUE, 0 );
}
void EDT_ReplaceMisspelledWord( MWContext *pContext, char* pOldWord,
char*pNewWord, XP_Bool bAll ){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->BeginBatchChanges(kPasteTextCommandID);
pEditBuffer->IterateMisspelledWords( CEditBuffer::EMSW_REPLACE,
pOldWord, pNewWord, bAll );
pEditBuffer->EndBatchChanges();
}
void EDT_IgnoreMisspelledWord( MWContext *pContext, char* pOldWord,
XP_Bool bAll ){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->IterateMisspelledWords( CEditBuffer::EMSW_IGNORE,
pOldWord, 0, bAll );
}
XP_HUGE_CHAR_PTR EDT_GetPositionalText( MWContext* pContext ){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
return pEditBuffer->GetPositionalText();
}
void EDT_SetCharacterDataAtOffset( MWContext *pContext, EDT_CharacterData *pData,
ED_BufferOffset iBufOffset, int32 iLen ){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->SetCharacterDataAtOffset(pData, iBufOffset, iLen );
}
void EDT_SetRefresh( MWContext* pContext, XP_Bool bRefreshOn ){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
pEditBuffer->SetRefresh( bRefreshOn );
}
// Warning this deletes (and recreates) the CEditBuffer if we ChangeEncoding
XP_Bool EDT_SetEncoding(MWContext* pContext, int16 csid){
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
return pEditBuffer->SetEncoding(csid);
}
/*
* Extract the Extra HTML string from the ED_Element pointer in an image struct
* (ED_Element is a void* to external users)
* Caller must XP_FREE result
*/
char * EDT_GetExtraHTML_FromImage(LO_ImageStruct *pImage){
if( pImage ){
if( pImage->edit_element == 0 ){
// We don't have an edit element - must be from Navigator,
// so we have to pick out stuff that would end up in CEditImageElement's extra data
// BUT the original PA_TAG data is no longer around.
// The only item we will grab is the USEMAP data
// TODO: Get other associated data such as JavaScript?
// To do that, we need a new function to reconstruct a tag from LO_ImageStruct
if ( pImage->image_attr && pImage->image_attr->usemap_name ){
char * pRet = 0;
pRet = PR_sprintf_append( pRet, "USEMAP=");
pRet = PR_sprintf_append( pRet, pImage->image_attr->usemap_name);
return pRet;
}
}else if( pImage->edit_element->IsImage() ){
EDT_ImageData * pImageData = ((CEditImageElement*)pImage->edit_element)->GetImageData();
if( pImageData->pExtra && *pImageData->pExtra ){
return XP_STRDUP(pImageData->pExtra);
}
EDT_FreeImageData(pImageData);
}
}
return NULL;
}
PRBool EDT_EncryptState( MWContext *pContext ){
CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
if (pEditBuffer == NULL) return PR_FALSE;
return pEditBuffer->m_bEncrypt;
}
void EDT_EncryptToggle( MWContext *pContext ){
CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
if (pEditBuffer == NULL) return;
pEditBuffer->m_bEncrypt = pEditBuffer->m_bEncrypt ? PR_FALSE : PR_TRUE;
}
void EDT_EncryptSet( MWContext *pContext ){
CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
if (pEditBuffer == NULL) return;
pEditBuffer->m_bEncrypt = PR_TRUE;
}
void EDT_EncryptReset( MWContext *pContext ){
CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
if (pEditBuffer == NULL) return;
pEditBuffer->m_bEncrypt = PR_FALSE;
}
XP_Bool EDT_AdjustTableRectForCaption(LO_TableStruct *pTable, XP_Rect *pRect)
{
if( !pTable || !pRect )
return FALSE;
// If table has a caption, we want highlighting to surround the caption as well
LO_Element *pElement = (LO_Element*)pTable;
// Find the first cell
while( pElement && pElement->type != LO_CELL )
pElement = pElement->lo_any.next;
if( pElement )
{
XP_Bool bCaptionBeforeTable = pElement->lo_cell.isCaption;
XP_Bool bCaptionAfterTable = FALSE;
if( !bCaptionBeforeTable )
{
// Search for a caption at the end
do {
if( pElement && pElement->type == LO_CELL &&
pElement->lo_cell.isCaption )
{
bCaptionAfterTable = TRUE;
break;
}
pElement = pElement->lo_any.next;
}
while( pElement && pElement->type != LO_LINEFEED );
}
if( bCaptionBeforeTable || bCaptionAfterTable )
{
// This is the full height, including caption
int32 iHeight = pTable->line_height + pTable->border_bottom_width + pTable->inter_cell_space;
// Adjust top or bottom to include caption area
if( bCaptionBeforeTable )
pRect->top = pRect->bottom - iHeight;
else
pRect->bottom = pRect->top + iHeight;
return TRUE;
}
}
return FALSE;
}
void EDT_SavePublishUsername(MWContext *pContext, char *pAddress, char *pUsername)
{
// Save the location and username given to us by NetLib
// when it prompts for a username and password
if( pContext && pAddress && *pAddress && pUsername )
{
GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
// Copy the address and strip off the filename from the supplied URL
char *pNewLocation = EDT_ReplaceFilename(pAddress, 0, TRUE);
if( pNewLocation )
{
char *pPrefLocation = 0;
// Insert the username into the location string and save in prefs
if( NET_MakeUploadURL(&pPrefLocation, pNewLocation, pUsername, 0) )
PREF_SetCharPref("editor.publish_last_loc", pPrefLocation);
XP_FREEIF(pPrefLocation);
XP_FREE(pNewLocation);
}
}
}
#endif // EDITOR