home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / edtutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  194.5 KB  |  6,272 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20. //
  21. // Public interface and shared subsystem data.
  22. //
  23.  
  24. #ifdef EDITOR
  25.  
  26. #include "editor.h"
  27.  
  28. typedef struct PA_AmpEsc_struct {
  29.         char *str;
  30.         char value;
  31.         intn len;
  32. } PA_AmpEsc;
  33.  
  34. #ifndef XP_MAC
  35. static PA_AmpEsc PA_AmpEscapes[] = {
  36.     {"lt", '<', 2},
  37.     {"LT", '<', 2},
  38.     {"gt", '>', 2},
  39.     {"GT", '>', 2},
  40.     {"amp", '&', 3},
  41.     {"AMP", '&', 3},
  42.     {"quot", '\"', 4},
  43.     {"QUOT", '\"', 4},
  44.     {"nbsp", '\240', 4},
  45.     {"reg", '\256', 3},
  46.     {"REG", '\256', 3},
  47.     {"copy", '\251', 4},
  48.     {"COPY", '\251', 4},
  49.  
  50.     {"iexcl", '\241', 5},
  51.     {"cent", '\242', 4},
  52.     {"pound", '\243', 5},
  53.     {"curren", '\244', 6},
  54.     {"yen", '\245', 3},
  55.     {"brvbar", '\246', 6},
  56.     {"sect", '\247', 4},
  57.  
  58.     {"uml", '\250', 3},
  59.     {"ordf", '\252', 4},
  60.     {"laquo", '\253', 5},
  61.     {"not", '\254', 3},
  62.     {"shy", '\255', 3},
  63.     {"macr", '\257', 4},
  64.  
  65.     {"deg", '\260', 3},
  66.     {"plusmn", '\261', 6},
  67.     {"sup2", '\262', 4},
  68.     {"sup3", '\263', 4},
  69.     {"acute", '\264', 5},
  70.     {"micro", '\265', 5},
  71.     {"para", '\266', 4},
  72.     {"middot", '\267', 6},
  73.  
  74.     {"cedil", '\270', 5},
  75.     {"sup1", '\271', 4},
  76.     {"ordm", '\272', 4},
  77.     {"raquo", '\273', 5},
  78.     {"frac14", '\274', 6},
  79.     {"frac12", '\275', 6},
  80.     {"frac34", '\276', 6},
  81.     {"iquest", '\277', 6},
  82.  
  83.     {"Agrave", '\300', 6},
  84.     {"Aacute", '\301', 6},
  85.     {"Acirc", '\302', 5},
  86.     {"Atilde", '\303', 6},
  87.     {"Auml", '\304', 4},
  88.     {"Aring", '\305', 5},
  89.     {"AElig", '\306', 5},
  90.     {"Ccedil", '\307', 6},
  91.  
  92.     {"Egrave", '\310', 6},
  93.     {"Eacute", '\311', 6},
  94.     {"Ecirc", '\312', 5},
  95.     {"Euml", '\313', 4},
  96.     {"Igrave", '\314', 6},
  97.     {"Iacute", '\315', 6},
  98.     {"Icirc", '\316', 5},
  99.     {"Iuml", '\317', 4},
  100.  
  101.     {"ETH", '\320', 3},
  102.     {"Ntilde", '\321', 6},
  103.     {"Ograve", '\322', 6},
  104.     {"Oacute", '\323', 6},
  105.     {"Ocirc", '\324', 5},
  106.     {"Otilde", '\325', 6},
  107.     {"Ouml", '\326', 4},
  108.     {"times", '\327', 5},
  109.  
  110.     {"Oslash", '\330', 6},
  111.     {"Ugrave", '\331', 6},
  112.     {"Uacute", '\332', 6},
  113.     {"Ucirc", '\333', 5},
  114.     {"Uuml", '\334', 4},
  115.     {"Yacute", '\335', 6},
  116.     {"THORN", '\336', 5},
  117.     {"szlig", '\337', 5},
  118.  
  119.     {"agrave", '\340', 6},
  120.     {"aacute", '\341', 6},
  121.     {"acirc", '\342', 5},
  122.     {"atilde", '\343', 6},
  123.     {"auml", '\344', 4},
  124.     {"aring", '\345', 5},
  125.     {"aelig", '\346', 5},
  126.     {"ccedil", '\347', 6},
  127.  
  128.     {"egrave", '\350', 6},
  129.     {"eacute", '\351', 6},
  130.     {"ecirc", '\352', 5},
  131.     {"euml", '\353', 4},
  132.     {"igrave", '\354', 6},
  133.     {"iacute", '\355', 6},
  134.     {"icirc", '\356', 5},
  135.     {"iuml", '\357', 4},
  136.  
  137.     {"eth", '\360', 3},
  138.     {"ntilde", '\361', 6},
  139.     {"ograve", '\362', 6},
  140.     {"oacute", '\363', 6},
  141.     {"ocirc", '\364', 5},
  142.     {"otilde", '\365', 6},
  143.     {"ouml", '\366', 4},
  144.     {"divide", '\367', 6},
  145.  
  146.     {"oslash", '\370', 6},
  147.     {"ugrave", '\371', 6},
  148.     {"uacute", '\372', 6},
  149.     {"ucirc", '\373', 5},
  150.     {"uuml", '\374', 4},
  151.     {"yacute", '\375', 6},
  152.     {"thorn", '\376', 5},
  153.     {"yuml", '\377', 4},
  154.  
  155.     {NULL, '\0', 0},
  156. };
  157. #else /* ! XP_MAC */
  158.                             /* Entities encoded in MacRoman.  */
  159. static PA_AmpEsc PA_AmpEscapes[] = {
  160.     {"lt", '<', 2},
  161.     {"LT", '<', 2},
  162.     {"gt", '>', 2},
  163.     {"GT", '>', 2},
  164.     {"amp", '&', 3},
  165.     {"AMP", '&', 3},
  166.     {"quot", '\"', 4},
  167.     {"QUOT", '\"', 4},
  168.     {"nbsp", '\007', 4},
  169.     {"reg", '\250', 3},
  170.     {"REG", '\250', 3},
  171.     {"copy", '\251', 4},
  172.     {"COPY", '\251', 4},
  173.  
  174.     {"iexcl", '\301', 5},
  175.     {"cent", '\242', 4},
  176.     {"pound", '\243', 5},
  177.     {"curren", '\333', 6},
  178.     {"yen", '\264', 3},
  179.  
  180.     /*
  181.      * Navigator Gold currently inverts this table in such a way that
  182.      * ASCII characters (less than 128) get converted to the names
  183.      * listed here.  For things like ampersand (&) this is the
  184.      * right thing to do, but for this one (brvbar), it isn't since
  185.      * both broken vertical bar and vertical bar are mapped to the same
  186.      * character by the Latin-1 to Mac Roman table.
  187.      *
  188.      * Punt for now. This needs to be fixed later. -- erik
  189.      */
  190.     /* {"brvbar", '\174', 6}, */
  191.  
  192.     {"sect", '\244', 4},
  193.  
  194.     {"uml", '\254', 3},
  195.     {"ordf", '\273', 4},
  196.     {"laquo", '\307', 5},
  197.     {"not", '\302', 3},
  198.     {"shy", '\320', 3},
  199.     {"macr", '\370', 4},
  200.  
  201.     {"deg", '\241', 3},
  202.     {"plusmn", '\261', 6},
  203.     /* {"sup2", '\62', 4}, see comment above */
  204.     /* {"sup3", '\63', 4}, see comment above */
  205.     {"acute", '\253', 5},
  206.     {"micro", '\265', 5},
  207.     {"para", '\246', 4},
  208.     {"middot", '\341', 6},
  209.  
  210.     {"cedil", '\374', 5},
  211.     /* {"sup1", '\61', 4}, see comment above */
  212.     {"ordm", '\274', 4},
  213.     {"raquo", '\310', 5},
  214.     {"frac14", '\271', 6},
  215.     {"frac12", '\270', 6},
  216.     {"frac34", '\262', 6},
  217.     {"iquest", '\300', 6},
  218.  
  219.     {"Agrave", '\313', 6},
  220.     {"Aacute", '\347', 6},
  221.     {"Acirc", '\345', 5},
  222.     {"Atilde", '\314', 6},
  223.     {"Auml", '\200', 4},
  224.     {"Aring", '\201', 5},
  225.     {"AElig", '\256', 5},
  226.     {"Ccedil", '\202', 6},
  227.  
  228.     {"Egrave", '\351', 6},
  229.     {"Eacute", '\203', 6},
  230.     {"Ecirc", '\346', 5},
  231.     {"Euml", '\350', 4},
  232.     {"Igrave", '\355', 6},
  233.     {"Iacute", '\352', 6},
  234.     {"Icirc", '\353', 5},
  235.     {"Iuml", '\354', 4},
  236.  
  237.     {"ETH", '\334', 3},            /* Icelandic MacRoman: ETH ('D' w/horiz bar) */
  238.     {"Ntilde", '\204', 6},
  239.     {"Ograve", '\361', 6},
  240.     {"Oacute", '\356', 6},
  241.     {"Ocirc", '\357', 5},
  242.     {"Otilde", '\315', 6},
  243.     {"Ouml", '\205', 4},
  244.     /* {"times", '\170', 5}, see comment above */
  245.  
  246.     {"Oslash", '\257', 6},
  247.     {"Ugrave", '\364', 6},
  248.     {"Uacute", '\362', 6},
  249.     {"Ucirc", '\363', 5},
  250.     {"Uuml", '\206', 4},
  251.     {"Yacute", '\240', 6},        /* Icelandic MacRoman: Yacute */
  252.     {"THORN", '\336', 5},        /* Icelandic MacRoman: THORN (kinda like 'P') */
  253.     {"szlig", '\247', 5},
  254.  
  255.     {"agrave", '\210', 6},
  256.     {"aacute", '\207', 6},
  257.     {"acirc", '\211', 5},
  258.     {"atilde", '\213', 6},
  259.     {"auml", '\212', 4},
  260.     {"aring", '\214', 5},
  261.     {"aelig", '\276', 5},
  262.     {"ccedil", '\215', 6},
  263.  
  264.     {"egrave", '\217', 6},
  265.     {"eacute", '\216', 6},
  266.     {"ecirc", '\220', 5},
  267.     {"euml", '\221', 4},
  268.     {"igrave", '\223', 6},
  269.     {"iacute", '\222', 6},
  270.     {"icirc", '\224', 5},
  271.     {"iuml", '\225', 4},
  272.  
  273.     {"eth", '\335', 3},        /* Icelandic MacRoman: eth ('d' w/horiz bar) */
  274.     {"ntilde", '\226', 6},
  275.     {"ograve", '\230', 6},
  276.     {"oacute", '\227', 6},
  277.     {"ocirc", '\231', 5},
  278.     {"otilde", '\233', 6},
  279.     {"ouml", '\232', 4},
  280.     {"divide", '\326', 6},
  281.  
  282.     {"oslash", '\277', 6},
  283.     {"ugrave", '\235', 6},
  284.     {"uacute", '\234', 6},
  285.     {"ucirc", '\236', 5},
  286.     {"uuml", '\237', 4},
  287.     {"yacute", '\340', 6},        /* Icelandic MacRoman: yacute */
  288.     {"thorn", '\337', 5},        /* Icelandic MacRoman: thorn (kinda like 'p') */
  289.     {"yuml", '\330', 4},
  290.  
  291.     {NULL, '\0', 0},
  292. };
  293.  
  294. #endif
  295.  
  296.  
  297. // For XP Strings
  298. extern "C" {
  299. #include "xpgetstr.h"
  300. #define WANT_ENUM_STRING_IDS
  301. #include "allxpstr.h"
  302. #undef WANT_ENUM_STRING_IDS
  303. }
  304. #include "prefapi.h"
  305. #include "secnav.h"
  306. #include "xp_str.h"
  307.  
  308. char* edt_CopyFromHuge(int16 /*csid*/, XP_HUGE_CHAR_PTR text, int32 length, int32* pOutLength){
  309.     int len2 = length;
  310.  
  311. #ifdef XP_WIN16
  312.     const int32 kMax = 65530;
  313.     XP_Bool bTruncated = FALSE;
  314.     if ( len2 > kMax ) {
  315.         XP_TRACE(("Truncating huge data. original size: %d bytes", len2));
  316.         bTruncated = TRUE;
  317.         len2 = kMax;
  318.     }
  319. #endif
  320.  
  321.     char* pData = (char*) XP_ALLOC(len2 + 1);
  322.     if ( pData == NULL ) return NULL;
  323.     for(int32 i = 0; i < len2; i++ ){
  324.         // No XP way of transfering from HUGE to normal
  325.         pData[i] = text[i];
  326.     }
  327.  
  328. #ifdef XP_WIN16
  329.     if ( bTruncated ) {
  330.         int nboc = INTL_NthByteOfChar(csid, pData, (int)(len2));
  331.         // nboc is one-based. Stupid!
  332.         if ( nboc > 1 ){
  333.             len2 -= (nboc - 1);
  334.         }
  335.         if ( len2 < 0 ) {
  336.             len2 = 0;
  337.         }
  338.         XP_TRACE(("Final size: %d bytes", len2));
  339.     }
  340. #endif
  341.  
  342.     pData[len2] = '\0';
  343.     if ( pOutLength ) {
  344.         *pOutLength = len2;
  345.     }
  346.     return pData;
  347. }
  348.  
  349. void edt_StripAtHashOrQuestionMark(char *pUrl)
  350. {
  351.     if( !pUrl )
  352.         return;
  353.  
  354.     char *pStart = XP_STRRCHR(pUrl, '/');
  355.  
  356.     if(!pStart)
  357.         pStart = pUrl;
  358.     
  359.     char * pSuffix = XP_STRCHR(pStart, '?');
  360.  
  361.     if(pSuffix)
  362.         *pSuffix = '\0';
  363.  
  364.     pSuffix = XP_STRRCHR(pStart, '#');
  365.     if(pSuffix)
  366.         *pSuffix = '\0';
  367. }
  368.  
  369. char *edt_StripUsernamePassword(char *pUrl) {
  370.   int iType = NET_URL_Type(pUrl);
  371.   if (iType != FTP_TYPE_URL &&
  372.       iType != HTTP_TYPE_URL &&
  373.       iType != SECURE_HTTP_TYPE_URL) {
  374.     return XP_STRDUP(pUrl);
  375.   }
  376.  
  377.   char *pRet = NULL;
  378.   if (NET_ParseUploadURL(pUrl,&pRet,NULL,NULL) && pRet) {
  379.     return pRet;
  380.   }
  381.   else {
  382.     return XP_STRDUP(pUrl);
  383.   }
  384. }
  385.  
  386.  
  387. // Always returns newly allocated memory or NULL.
  388. PRIVATE char *edt_remove_trailing_slash(const char *pStr) {
  389.   if (pStr) {
  390.     char *pNew = XP_STRDUP(pStr);
  391.     if (pNew && *pNew) {
  392.       // Only do strlen() if pNew is non-NULL.
  393.       int iLen = XP_STRLEN(pNew);
  394.  
  395.       // Kill trailing slash.
  396.       if (pNew[iLen-1] == '/') {
  397.         pNew[iLen-1] = '\0';
  398.       }
  399.     }
  400.     return pNew;
  401.   }
  402.   else {
  403.     return NULL;
  404.   }
  405. }
  406.  
  407.  
  408.  
  409. // Just wrappers around the XP_* functions.  The only difference is that
  410. // these functions will work if the directory ends in a trailing slash,
  411. // avoiding obscure bugs on Win16.
  412. XP_Dir edt_OpenDir(const char * name, XP_FileType type) {
  413.   XP_ASSERT(type == xpURL);
  414.   char *pNew = edt_remove_trailing_slash(name);
  415.   XP_Dir ret = XP_OpenDir(pNew,type);
  416.   XP_FREEIF(pNew);
  417.   return ret;
  418. }
  419.  
  420. int edt_MakeDirectory(const char* name, XP_FileType type) {
  421.   XP_ASSERT(type == xpURL);
  422.   char *pNew = edt_remove_trailing_slash(name);
  423.   int ret = XP_MakeDirectory(pNew,type);
  424.   XP_FREEIF(pNew);
  425.   return ret;
  426. }
  427.  
  428. int edt_RemoveDirectory(const char *name, XP_FileType type) {
  429.   XP_ASSERT(type == xpURL);
  430.   char *pNew = edt_remove_trailing_slash(name);
  431.   int ret = XP_RemoveDirectory(pNew,type);
  432.   XP_FREEIF(pNew);
  433.   return ret;
  434. }
  435.  
  436.  
  437.  
  438. // Recursively kill everything under pNameArg, which is in xpURL format.
  439. void edt_RemoveDirectoryR(char *pNameArg) {
  440.   // non-NULL and non-zero-length.
  441.   if (!(pNameArg && *pNameArg))
  442.     return;
  443.  
  444.   // Make sure name ends in slash.
  445.   char *pName = XP_STRDUP(pNameArg);
  446.   if (!pName) {
  447.     return; 
  448.   }
  449.   if (pName[XP_STRLEN(pName)-1] != '/') {
  450.     StrAllocCat(pName,"/");
  451.   }
  452.  
  453.  
  454.   XP_Dir dir = edt_OpenDir(pName, xpURL);
  455.   if (dir) {
  456.     XP_DirEntryStruct *entry = NULL;
  457.     for (entry = XP_ReadDir(dir); entry; entry = XP_ReadDir(dir)) {
  458.       // Don't touch "." or ".."
  459.       if (entry->d_name && XP_STRCMP(entry->d_name,".") && XP_STRCMP(entry->d_name,"..")) {
  460.  
  461.         // Create filename inside directory.
  462.         char *subName = XP_STRDUP(pName);
  463.         if (entry->d_name[0] == '/') {
  464.           // skip the slash.
  465.           StrAllocCat(subName,entry->d_name + 1);
  466.         }
  467.         else {
  468.           StrAllocCat(subName,entry->d_name);
  469.         }
  470.  
  471.         // Recurse, will do nothing if not a directory.
  472.         edt_RemoveDirectoryR(subName);
  473.         // If subName is a directory, it will already be gone, so this does nothing.
  474.         XP_FileRemove(subName,xpURL);
  475.  
  476.  
  477.         XP_FREEIF(subName);
  478.       }
  479.     }
  480.                 
  481.     XP_CloseDir(dir); 
  482.     edt_RemoveDirectory(pName,xpURL);
  483.   }  
  484.  
  485.  
  486.   XP_FREE(pName);
  487. }
  488.  
  489.  
  490. // Timer
  491.  
  492.  
  493. void CEditTimerCallback (void * closure){
  494.     if (closure) {
  495.         CEditTimer* pTimer = (CEditTimer*) closure;
  496.         pTimer->Callback();
  497.     }
  498. }
  499.  
  500. CEditTimer::CEditTimer(){
  501.     m_timeoutEnabled = FALSE;
  502.     m_timerID = NULL;
  503. }
  504.  
  505. CEditTimer::~CEditTimer(){
  506.     Cancel();
  507. }
  508.  
  509. void CEditTimer::Callback() {
  510.     m_timeoutEnabled = FALSE;
  511.     OnCallback();
  512. }
  513.  
  514. void CEditTimer::Cancel(){
  515.     if ( m_timeoutEnabled ) {
  516.         FE_ClearTimeout(m_timerID);
  517.         m_timeoutEnabled = FALSE;
  518.         m_timerID = NULL;
  519.     }
  520. }
  521.  
  522. void CEditTimer::OnCallback() {}
  523.  
  524. void CEditTimer::SetTimeout(uint32 msecs){
  525.     Cancel();
  526.     m_timerID = FE_SetTimeout(&CEditTimerCallback, this, msecs);
  527.     m_timeoutEnabled = TRUE;
  528. }
  529.  
  530. // Color with a defined/undefined boolean
  531.  
  532. ED_Color::ED_Color() {
  533.     m_bDefined = TRUE;
  534.     m_color.red = 0;
  535.     m_color.green = 0;
  536.     m_color.blue = 0;
  537. }
  538. ED_Color::ED_Color(LO_Color& pLoColor) {
  539.     m_bDefined = TRUE;
  540.     m_color = pLoColor;
  541. }
  542.  
  543. static int ED_Color_Clip(int c) {
  544.     if ( c < 0 ) {
  545.         XP_ASSERT(FALSE);
  546.         c = 0;
  547.     }
  548.     if ( c > 255 ) {
  549.         XP_ASSERT(FALSE);
  550.         c = 255;
  551.     }
  552.     return c;
  553. }
  554.  
  555. ED_Color::ED_Color(int r, int g, int b) {
  556.     m_bDefined = TRUE;
  557.     m_color.red = ED_Color_Clip(r);
  558.     m_color.green = ED_Color_Clip(g);
  559.     m_color.blue = ED_Color_Clip(b);
  560. }
  561.  
  562. ED_Color::ED_Color(int32 rgb) {
  563.     if ( rgb == -1 ) {
  564.         m_bDefined = FALSE;
  565.         m_color.red = 0;
  566.         m_color.green = 0;
  567.         m_color.blue = 0;
  568.     }
  569.     else {
  570.         m_bDefined = TRUE;
  571.         m_color.red = (rgb >> 16) & 255;
  572.         m_color.green = (rgb >> 8) & 255;
  573.         m_color.blue = rgb & 255;
  574.     }
  575. }
  576.  
  577. ED_Color::ED_Color(LO_Color* pLoColor) {
  578.     if ( pLoColor ) {
  579.         m_color.red = ED_Color_Clip(pLoColor->red);
  580.         m_color.green = ED_Color_Clip(pLoColor->green);
  581.         m_color.blue = ED_Color_Clip(pLoColor->blue);
  582.         m_bDefined = TRUE;
  583.     }
  584.     else {
  585.         m_bDefined = FALSE;
  586.     }
  587. }
  588.  
  589. XP_Bool ED_Color::operator==(const ED_Color& other) const {
  590.     return m_bDefined == other.m_bDefined &&
  591.         (!m_bDefined || m_color.red == other.m_color.red &&
  592.         m_color.green == other.m_color.green &&
  593.         m_color.blue == other.m_color.blue );
  594. }
  595.  
  596. XP_Bool ED_Color::operator!=(const ED_Color& other) const {
  597.     return ! operator==(other);
  598. }
  599.  
  600. XP_Bool ED_Color::IsDefined() { return m_bDefined; }
  601. int ED_Color::Red() { XP_ASSERT(m_bDefined); return m_color.red; }
  602. int ED_Color::Green() { XP_ASSERT(m_bDefined); return m_color.green; }
  603. int ED_Color::Blue() { XP_ASSERT(m_bDefined); return m_color.blue; }
  604. LO_Color ED_Color::GetLOColor() {XP_ASSERT(m_bDefined); return m_color; }
  605.  
  606. long ED_Color::GetAsLong() {
  607.     if ( ! m_bDefined ) return -1;
  608.     return (((long)m_color.red << 16) | ((long)m_color.green << 8) | (long)m_color.blue);
  609. }
  610.  
  611. void
  612. ED_Color::SetUndefined() {
  613.     m_bDefined = FALSE;
  614.     m_color.red = 0;
  615.     m_color.green = 0;
  616.     m_color.blue = 0;
  617. }
  618.  
  619. ED_Color 
  620. ED_Color::GetUndefined()
  621. {
  622.     ED_Color undef;
  623.     undef.SetUndefined();
  624.     return undef;
  625. }
  626.  
  627. //-----------------------------------------------------------------------------
  628. // Helper functions.
  629. //-----------------------------------------------------------------------------
  630.  
  631. //
  632. // Convert a tagtype to a string
  633. //
  634.  
  635. static char* TagString(int32 tagType)
  636. {
  637.     switch(tagType)
  638.     {
  639.         case P_UNKNOWN:
  640.             return "";
  641.         case P_TEXT:
  642.             return "text";
  643.         case P_TITLE:
  644.             return PT_TITLE;
  645.         case P_INDEX:
  646.             return PT_INDEX;
  647.         case P_BASE:
  648.             return PT_BASE;
  649.         case P_LINK:
  650.             return PT_LINK;
  651.         case P_HEADER_1:
  652.             return PT_HEADER_1;
  653.         case P_HEADER_2:
  654.             return PT_HEADER_2;
  655.         case P_HEADER_3:
  656.             return PT_HEADER_3;
  657.         case P_HEADER_4:
  658.             return PT_HEADER_4;
  659.         case P_HEADER_5:
  660.             return PT_HEADER_5;
  661.         case P_HEADER_6:
  662.             return PT_HEADER_6;
  663.         case P_ANCHOR:
  664.             return PT_ANCHOR;
  665.         case P_PARAGRAPH:
  666.             return PT_PARAGRAPH;
  667.         case P_ADDRESS:
  668.             return PT_ADDRESS;
  669.         case P_IMAGE:
  670.             return PT_IMAGE;
  671.         case P_NEW_IMAGE:
  672.             return PT_NEW_IMAGE;
  673.         case P_PLAIN_TEXT:
  674.             return PT_PLAIN_TEXT;
  675.         case P_PLAIN_PIECE:
  676.             return PT_PLAIN_PIECE;
  677.         case P_PREFORMAT:
  678.             return PT_PREFORMAT;
  679.         case P_LISTING_TEXT:
  680.             return PT_LISTING_TEXT;
  681.         case P_UNUM_LIST:
  682.             return PT_UNUM_LIST;
  683.         case P_NUM_LIST:
  684.             return PT_NUM_LIST;
  685.         case P_MENU:
  686.             return PT_MENU;
  687.         case P_DIRECTORY:
  688.             return PT_DIRECTORY;
  689.         case P_LIST_ITEM:
  690.             return PT_LIST_ITEM;
  691.         case P_DESC_LIST:
  692.             return PT_DESC_LIST;
  693.         case P_DESC_TITLE:
  694.             return PT_DESC_TITLE;
  695.         case P_DESC_TEXT:
  696.             return PT_DESC_TEXT;
  697.         case P_STRIKEOUT:
  698.             return PT_STRIKEOUT;
  699.         case P_STRIKE:
  700.             return PT_STRIKE;
  701.         case P_UNDERLINE:
  702.             return PT_UNDERLINE;
  703.         case P_FIXED:
  704.             return PT_FIXED;
  705.         case P_CENTER:
  706.             return PT_CENTER;
  707.         case P_EMBED:
  708.             return PT_EMBED;
  709.         case P_SUBDOC:
  710.             return PT_SUBDOC;
  711.         case P_CELL:
  712.             return PT_CELL;
  713.         case P_TABLE:
  714.             return PT_TABLE;
  715.         case P_CAPTION:
  716.             return PT_CAPTION;
  717.         case P_TABLE_ROW:
  718.             return PT_TABLE_ROW;
  719.         case P_TABLE_HEADER:
  720.             return PT_TABLE_HEADER;
  721.         case P_TABLE_DATA:
  722.             return PT_TABLE_DATA;
  723.         case P_BOLD:
  724.             return PT_BOLD;
  725.         case P_ITALIC:
  726.             return PT_ITALIC;
  727.         case P_EMPHASIZED:
  728.             return PT_EMPHASIZED;
  729.         case P_STRONG:
  730.             return PT_STRONG;
  731.         case P_CODE:
  732.             return PT_CODE;
  733.         case P_SAMPLE:
  734.             return PT_SAMPLE;
  735.         case P_KEYBOARD:
  736.             return PT_KEYBOARD;
  737.         case P_VARIABLE:
  738.             return PT_VARIABLE;
  739.         case P_CITATION:
  740.             return PT_CITATION;
  741.         case P_BLOCKQUOTE:
  742.             return PT_BLOCKQUOTE;
  743.         case P_FORM:
  744.             return PT_FORM;
  745.         case P_INPUT:
  746.             return PT_INPUT;
  747.         case P_SELECT:
  748.             return PT_SELECT;
  749.         case P_OPTION:
  750.             return PT_OPTION;
  751.         case P_TEXTAREA:
  752.             return PT_TEXTAREA;
  753.         case P_HRULE:
  754.             return PT_HRULE;
  755.         case P_BODY:
  756.             return PT_BODY;
  757.         case P_COLORMAP:
  758.             return PT_COLORMAP;
  759.         case P_HYPE:
  760.             return PT_HYPE;
  761.         case P_META:
  762.             return PT_META;
  763.         case P_LINEBREAK:
  764.             return PT_LINEBREAK;
  765.         case P_WORDBREAK:
  766.             return PT_WORDBREAK;
  767.         case P_NOBREAK:
  768.             return PT_NOBREAK;
  769.         case P_BASEFONT:
  770.             return PT_BASEFONT;
  771.         case P_FONT:
  772.             return PT_FONT;
  773.         case P_BLINK:
  774.             return PT_BLINK;
  775.         case P_BIG:
  776.             return PT_BIG;
  777.         case P_SMALL:
  778.             return PT_SMALL;
  779.         case P_SUPER:
  780.             return PT_SUPER;
  781.         case P_SUB:
  782.             return PT_SUB;
  783.         case P_GRID:
  784.             return PT_GRID;
  785.         case P_GRID_CELL:
  786.             return PT_GRID_CELL;
  787.         case P_NOGRIDS:
  788.             return PT_NOGRIDS;
  789.         case P_JAVA_APPLET:
  790.             return PT_JAVA_APPLET;
  791.         case P_MAP:
  792.             return PT_MAP;
  793.         case P_AREA:
  794.             return PT_AREA;
  795.         case P_DIVISION:
  796.             return PT_DIVISION;
  797.         case P_KEYGEN:
  798.             return PT_KEYGEN;
  799.         case P_MAX:
  800.             return "HTML";
  801.         case P_SCRIPT:
  802.             return PT_SCRIPT;
  803.         case P_NOEMBED:
  804.             return PT_NOEMBED;
  805.         case P_SERVER:
  806.             return PT_SERVER;
  807.         case P_PARAM:
  808.             return PT_PARAM;
  809.         case P_MULTICOLUMN:
  810.             return PT_MULTICOLUMN;
  811.         case P_SPACER:
  812.             return PT_SPACER;
  813.         case P_NOSCRIPT:
  814.             return PT_NOSCRIPT;
  815.         case P_HEAD:
  816.             return PT_HEAD;
  817.         case P_HTML:
  818.             return PT_HTML;
  819.         case P_CERTIFICATE:
  820.             return PT_CERTIFICATE;
  821.         case P_MQUOTE:
  822.             return PT_MQUOTE;
  823.         case P_STYLE:
  824.             return PT_STYLE;
  825.         case P_LAYER:
  826.             return PT_LAYER;
  827.         case P_ILAYER:
  828.             return PT_ILAYER;
  829.         case P_OBJECT:
  830.             return PT_OBJECT;
  831.         case P_SPAN:
  832.             return PT_SPAN;
  833.         case P_SPELL:
  834.             return PT_SPELL;
  835.         case P_INLINEINPUT:
  836.             return PT_INLINEINPUT;
  837.         case P_INLINEINPUTTHICK:
  838.             return PT_INLINEINPUTTHICK;
  839.         case P_INLINEINPUTDOTTED:
  840.             return PT_INLINEINPUTDOTTED;
  841.         case P_NSDT:
  842.             return PT_NSDT;
  843.         case P_NSCP_CLOSE:
  844.             return PT_NSCP_OPEN;
  845.         case P_NSCP_OPEN:
  846.             return PT_NSDT;
  847.         case P_NSCP_REBLOCK:
  848.             return PT_NSCP_REBLOCK;
  849.         case P_BLOCK:
  850.             return "block"; // PT_BLOCK 
  851.         case P_NOLAYER:
  852.             return PT_NOLAYER; // (?) jrm 97/03/08 according to instructions below.
  853.         default:
  854.             // If we get here, then it's a new tag that's been added to lib\libparse\pa_tags.h
  855.             // The fix is to add this new tag to the case statement above.
  856.             XP_ASSERT(FALSE);
  857.             return "UNKNOWN";
  858.     }
  859. }
  860.  
  861. const int kTagBufferSize = 40;
  862. static char tagBuffer[kTagBufferSize];
  863.  
  864. char* EDT_UpperCase(char* tagString)
  865. {
  866.     int i = 0;
  867.     if ( tagString ) {
  868.         for ( i = 0; i < kTagBufferSize-1 && tagString[i] != '\0'; i++ ) {
  869.             tagBuffer[i] = XP_TO_UPPER(tagString[i]);
  870.         }
  871.     }
  872.     tagBuffer[i] = '\0';
  873.     return tagBuffer;
  874. }
  875.  
  876. char *EDT_TagString(int32 tagType){
  877.     return EDT_UpperCase(TagString(tagType));
  878. }
  879.  
  880. char* EDT_AlignString(ED_Alignment align){
  881.     if ( align < ED_ALIGN_CENTER ) {
  882.         XP_ASSERT(FALSE);
  883.         align = ED_ALIGN_CENTER;
  884.     }
  885.     if ( align > ED_ALIGN_ABSTOP ) {
  886.         XP_ASSERT(FALSE);
  887.         align = ED_ALIGN_ABSTOP;
  888.     }
  889.     return EDT_UpperCase(lo_alignStrings[align]);
  890. }
  891.  
  892. ED_TextFormat edt_TagType2TextFormat( TagType t ){
  893.     switch(t){
  894.     case P_BOLD:
  895.     case P_STRONG:
  896.         return TF_BOLD;
  897.  
  898.     case P_ITALIC:
  899.     case P_EMPHASIZED:
  900.     case P_VARIABLE:
  901.     case P_CITATION:
  902.         return TF_ITALIC;
  903.  
  904.     case P_FIXED:
  905.     case P_CODE:
  906.     case P_KEYBOARD:
  907.     case P_SAMPLE:
  908.         return TF_FIXED;
  909.  
  910.     case P_SUPER:
  911.         return TF_SUPER;
  912.  
  913.     case P_SUB:
  914.         return TF_SUB;
  915.  
  916.     case P_NOBREAK:
  917.         return TF_NOBREAK;
  918.  
  919.     case P_STRIKEOUT:
  920.         return TF_STRIKEOUT;
  921.  
  922.     case P_UNDERLINE:
  923.         return TF_UNDERLINE;
  924.  
  925.     case P_BLINK:
  926.         return TF_BLINK;
  927.  
  928.     case P_SERVER:
  929.         return TF_SERVER;
  930.  
  931.     case P_SCRIPT:
  932.         return TF_SCRIPT;
  933.  
  934.     case P_STYLE:
  935.         return TF_STYLE;
  936.  
  937.     case P_SPELL:
  938.         return TF_SPELL;
  939.  
  940.     case P_INLINEINPUT:
  941.         return TF_INLINEINPUT;
  942.  
  943.     case P_INLINEINPUTTHICK:
  944.         return TF_INLINEINPUTTHICK;
  945.  
  946.     case P_INLINEINPUTDOTTED:
  947.         return TF_INLINEINPUTDOTTED;
  948.     }
  949.     return TF_NONE;
  950. }
  951.  
  952. static int workBufSize = 0;
  953. static char* workBuf = 0;
  954.  
  955. char *edt_WorkBuf( int iSize ){
  956.     if( iSize > workBufSize ){
  957.         if( workBuf != 0 ){
  958.             delete[] workBuf;
  959.         }
  960.         workBuf = new char[ iSize ];
  961.     }
  962.     return workBuf;
  963. }
  964.  
  965. char *edt_QuoteString( char* pString ){
  966.     int iLen = 0;
  967.     char *p = pString;
  968.     while( p && *p  ){
  969.         if( *p == '"' ){
  970.             iLen += 6;    // "
  971.         }
  972.         else {
  973.             iLen++;
  974.         }
  975.         p++;
  976.     }
  977.     iLen++;
  978.     
  979.     char *pRet = edt_WorkBuf( iLen+1 );
  980.     p = pRet;
  981.     while( pString && *pString ){
  982.         if( *pString == '"' ){
  983.             strcpy( p, """ );
  984.             p += 6;
  985.         }
  986.         else {
  987.             *p++ = *pString;
  988.         }
  989.         pString++;
  990.     }
  991.     *p = 0;
  992.     return pRet;
  993. }
  994.  
  995.  
  996. char *edt_MakeParamString( char* pString ){
  997.     int iLen = 0;
  998.     char *p = pString;
  999.     while( p && *p  ){
  1000.         if( *p == '"' ){
  1001.             iLen += 6;    // "
  1002.         }
  1003.         else {
  1004.             iLen++;
  1005.         }
  1006.         p++;
  1007.     }
  1008.     iLen += 3;      // the beginning and ending quote and the trailing 0.
  1009.     
  1010.     char *pRet = edt_WorkBuf( iLen+1 );
  1011.     p = pRet;
  1012.     if( pString && *pString == '`' ){
  1013.         do {
  1014.             *p++ = *pString++;
  1015.         } while( *pString && *pString != '`' );
  1016.         *p++ = '`';
  1017.         *p = 0;
  1018.     }
  1019.     else {
  1020.         *p = '"';
  1021.         p++;
  1022.         while( pString && *pString ){
  1023.             if( *pString == '"' ){
  1024.                 strcpy( p, """ );
  1025.                 p += 6;
  1026.             }
  1027.             else {
  1028.                 *p++ = *pString;
  1029.             }
  1030.             pString++;
  1031.         }
  1032.         *p++ = '"';
  1033.         *p = 0;
  1034.     }
  1035.     return pRet;
  1036. }
  1037.  
  1038. char *edt_FetchParamString( PA_Tag *pTag, char* param, int16 win_csid ){
  1039.     PA_Block buff;
  1040.     char *str;
  1041.     char *retVal = 0;
  1042.  
  1043.     buff = PA_FetchParamValue(pTag, param, win_csid);
  1044.     if (buff != NULL)
  1045.     {
  1046.         PA_LOCK(str, char *, buff);
  1047.         lo_StripTextWhitespace(str, XP_STRLEN(str));
  1048.         if( str ) {
  1049.             retVal = XP_STRDUP( str );
  1050.         }
  1051.         PA_UNLOCK(buff);
  1052.         PA_FREE(buff);
  1053.     }
  1054.     return retVal;
  1055. }
  1056.  
  1057. XP_Bool edt_FetchParamBoolExist( PA_Tag *pTag, char* param, int16 csid ){
  1058.     PA_Block buff;
  1059.  
  1060.     buff = PA_FetchParamValue(pTag, param, csid);
  1061.     if (buff != NULL){
  1062.         PA_FREE(buff);
  1063.         return TRUE;
  1064.     }
  1065.     else {
  1066.         return FALSE;
  1067.     }
  1068. }
  1069.  
  1070. XP_Bool edt_FetchDimension( PA_Tag *pTag, char* param, 
  1071.                          int32 *pValue, XP_Bool *pPercent,
  1072.                          int32 nDefaultValue, XP_Bool bDefaultPercent, int16 csid ){
  1073.     PA_Block buff;
  1074.     char *str;
  1075.     int32   value;
  1076.  
  1077.     // Fill in defaults
  1078.     *pValue = nDefaultValue;
  1079.     *pPercent = bDefaultPercent;
  1080.     
  1081.     buff = PA_FetchParamValue(pTag, param, csid);
  1082.     XP_Bool result = buff != NULL;
  1083.     if( buff != NULL )
  1084.     {
  1085.         PA_LOCK(str, char *, buff);
  1086.         if( str != 0 ){
  1087.             value = XP_ATOI(str);
  1088.             *pValue = value;
  1089.             *pPercent = (NULL != XP_STRCHR(str, '%'));
  1090.         }
  1091.         PA_UNLOCK(buff);
  1092.         PA_FREE(buff);
  1093.     }
  1094.     return result;
  1095. }
  1096.     
  1097. ED_Alignment edt_FetchParamAlignment( PA_Tag* pTag, ED_Alignment eDefault, XP_Bool bVAlign, int16 csid ){
  1098.     PA_Block buff;
  1099.     char *str;
  1100.     ED_Alignment retVal = eDefault;
  1101.  
  1102.     buff = PA_FetchParamValue(pTag, bVAlign ? PARAM_VALIGN : PARAM_ALIGN, csid);
  1103.     if (buff != NULL)
  1104.     {
  1105.         XP_Bool floating;
  1106.  
  1107.         floating = FALSE;
  1108.         PA_LOCK(str, char *, buff);
  1109.         lo_StripTextWhitespace(str, XP_STRLEN(str));
  1110.         // LTNOTE: this is a hack.  We should do a better job maping these
  1111.         //  could cause problems in the future.
  1112.         retVal = (ED_Alignment) lo_EvalAlignParam(str, &floating);
  1113.         PA_UNLOCK(buff);
  1114.         PA_FREE(buff);
  1115.     }
  1116.     return retVal;
  1117. }
  1118.  
  1119. int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue, int16 csid ){
  1120.     return edt_FetchParamInt(pTag, param, defValue, defValue, csid);
  1121. }
  1122.  
  1123. int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue, int32 defValueIfParamButNoValue, int16 csid ){
  1124.     PA_Block buff;
  1125.     char *str;
  1126.     int32 retVal = defValue;
  1127.  
  1128.     buff = PA_FetchParamValue(pTag, param, csid);
  1129.     if (buff != NULL)
  1130.     {
  1131.         PA_LOCK(str, char *, buff);
  1132.         if( str != 0 ){
  1133.             if ( *str == '\0' ) { /* They mentioned the param, but gave to value. */
  1134.                 retVal = defValueIfParamButNoValue;
  1135.             }
  1136.             else {
  1137.                 retVal = XP_ATOI(str);
  1138.             }
  1139.         }
  1140.         PA_UNLOCK(buff);
  1141.         PA_FREE(buff);
  1142.     }
  1143.     return retVal;
  1144. }
  1145.  
  1146. ED_Color edt_FetchParamColor( PA_Tag *pTag, char* param, int16 csid ){
  1147.     PA_Block buff = PA_FetchParamValue(pTag, param, csid);
  1148.     ED_Color retVal;
  1149.     retVal.SetUndefined();
  1150.     if (buff != NULL)
  1151.     {
  1152.         char *color_str;
  1153.         PA_LOCK(color_str, char *, buff);
  1154.         lo_StripTextWhitespace(color_str, XP_STRLEN(color_str));
  1155.         uint8 red, green, blue;
  1156.         LO_ParseRGB(color_str, &red, &green, &blue);
  1157.         retVal = EDT_RGB(red,green,blue);
  1158.         PA_UNLOCK(buff);
  1159.         PA_FREE(buff);
  1160.     }
  1161.     return retVal;
  1162. }
  1163.  
  1164. PRIVATE
  1165. XP_Bool edt_NameInList( char* pName, char** ppNameList ){
  1166.     int i = 0;
  1167.     while( ppNameList && ppNameList[i] ){
  1168.         if( XP_STRCASECMP( pName, ppNameList[i]) == 0 ){
  1169.             return TRUE;
  1170.         }
  1171.         i++;
  1172.     }
  1173.     return FALSE;
  1174. }
  1175.  
  1176. char* edt_FetchParamExtras( PA_Tag *pTag, char**ppKnownParams, int16 win_csid ){
  1177.     char** ppNames;
  1178.     char** ppValues;
  1179.     char* pRet = 0;
  1180.     int i = 0;
  1181.     char *pSpace = "";
  1182.     int iMax;
  1183.  
  1184.     iMax = PA_FetchAllNameValues( pTag, &ppNames, &ppValues, win_csid );
  1185.  
  1186.     while( i < iMax ){
  1187.         if( !edt_NameInList( ppNames[i], ppKnownParams ) ){
  1188.             if( ppValues[i] ){
  1189.                 pRet = PR_sprintf_append( pRet, "%s%s=%s",
  1190.                     pSpace, ppNames[i], edt_MakeParamString( ppValues[i] ) );
  1191.             }
  1192.             else {
  1193.                 pRet = PR_sprintf_append( pRet, "%s%s", pSpace, ppNames[i] );
  1194.             }
  1195.             pSpace = " ";
  1196.         }
  1197.         i++;
  1198.     }
  1199.  
  1200.     i = 0;
  1201.     while( i < iMax ){
  1202.         if( ppNames[i] ) XP_FREE( ppNames[i] );
  1203.         if( ppValues[i] ) XP_FREE( ppValues[i] );
  1204.         i++;
  1205.     }
  1206.  
  1207.     if( ppNames ) XP_FREE( ppNames );
  1208.     if( ppValues ) XP_FREE( ppValues );
  1209.  
  1210.     return pRet;
  1211. }
  1212.  
  1213. // Override existing.
  1214. void edt_FetchParamString2(PA_Tag* pTag, char* param, char*& data, int16 win_csid){
  1215.     char* result = edt_FetchParamString( pTag, param, win_csid );
  1216.     if ( result ) {
  1217.         if ( data ){
  1218.             XP_FREE(data);
  1219.         }
  1220.         data = result;
  1221.     }
  1222. }
  1223.  
  1224. // Override existing.
  1225. void edt_FetchParamColor2( PA_Tag *pTag, char* param, ED_Color& data, int16 csid ){
  1226.     ED_Color result = edt_FetchParamColor(pTag, param, csid);
  1227.     if ( result.IsDefined() ) {
  1228.         data = result;
  1229.     }
  1230. }
  1231.  
  1232. // Append to existing extras. To Do: Have new versions of a parameter overwrite the old ones.
  1233. void edt_FetchParamExtras2( PA_Tag *pTag, char**ppKnownParams, char*& data, int16 win_csid ){
  1234.     char* result = edt_FetchParamExtras( pTag, ppKnownParams, win_csid );
  1235.     if ( result ) {
  1236.         if ( data ) {
  1237.             data = PR_sprintf_append(data, " %s", result);
  1238.             XP_FREE(result);
  1239.         }
  1240.         else {
  1241.             data = result;
  1242.         }
  1243.     }
  1244. }
  1245.  
  1246. XP_Bool edt_ReplaceParamValue( PA_Tag *pTag, char * pName, char * pValue, int16 csid ){
  1247.     XP_ASSERT(pName);
  1248.     XP_ASSERT(pTag);
  1249.  
  1250.     char** ppNames;
  1251.     char** ppValues;
  1252.     char* pNew = 0;
  1253.     int i = 0;
  1254.     char *pSpace = "";
  1255.     int iMax;
  1256.     XP_Bool bFound = FALSE;
  1257.     
  1258.     iMax = PA_FetchAllNameValues( pTag, &ppNames, &ppValues, csid );
  1259.  
  1260.     while( i < iMax ){
  1261.         if( XP_STRCASECMP( pName, ppNames[i]) == 0 ){
  1262.             bFound = TRUE;
  1263.             if( pValue){
  1264.                 pNew = PR_sprintf_append( pNew, "%s%s=%s",
  1265.                     pSpace, pName, edt_MakeParamString( pValue ) );
  1266.             }
  1267.         } else {
  1268.             if( ppValues[i] ){
  1269.                 pNew = PR_sprintf_append( pNew, "%s%s=%s",
  1270.                     pSpace, ppNames[i], edt_MakeParamString( ppValues[i] ) );
  1271.             }
  1272.             else {
  1273.                 pNew = PR_sprintf_append( pNew, "%s%s", pSpace, ppNames[i] );
  1274.             }
  1275.             pSpace = " ";
  1276.         }
  1277.         i++;
  1278.     }
  1279.     // Add value at end if not found
  1280.     if( !bFound && pValue ){
  1281.         pNew = PR_sprintf_append( pNew, "%s%s=%s", pSpace, pName, pValue );
  1282.     }
  1283.     // Terminate
  1284.     pNew = PR_sprintf_append( pNew, "%s", ">" );
  1285.  
  1286.     // Cleanup memory
  1287.     i = 0;
  1288.     while( i < iMax ){
  1289.         if( ppNames[i] ) XP_FREE( ppNames[i] );
  1290.         if( ppValues[i] ) XP_FREE( ppValues[i] );
  1291.         i++;
  1292.     }
  1293.  
  1294.     if( ppNames ) XP_FREE( ppNames );
  1295.     if( ppValues ) XP_FREE( ppValues );
  1296.     
  1297.     // Now replace with new data
  1298.  
  1299.     PA_Block buff;
  1300.     char *locked_buff;
  1301.     int iLen;
  1302.  
  1303.     iLen = XP_STRLEN(pNew);
  1304.     buff = (PA_Block)PA_ALLOC((iLen+1) * sizeof(char));
  1305.     if (buff != NULL) {
  1306.         PA_LOCK(locked_buff, char *, buff);
  1307.         XP_BCOPY(pNew, locked_buff, iLen);
  1308.         locked_buff[iLen] = '\0';
  1309.         PA_UNLOCK(buff);
  1310.  
  1311.         if( pTag->data ) XP_FREE(pTag->data);
  1312.         pTag->data = buff;
  1313.         pTag->data_len = iLen;
  1314.     } else { 
  1315.         // LTNOTE: out of memory, should throw an exception
  1316.     }
  1317.     return bFound;
  1318. }
  1319.  
  1320. LO_Color* edt_MakeLoColor(ED_Color c) {
  1321.     if( !c.IsDefined()){
  1322.         return 0;
  1323.     }
  1324.     LO_Color *pColor = XP_NEW( LO_Color );
  1325.     pColor->red = EDT_RED(c);
  1326.     pColor->green = EDT_GREEN(c);
  1327.     pColor->blue = EDT_BLUE(c);
  1328.     return pColor;
  1329. }
  1330.  
  1331. void edt_SetLoColor( ED_Color c, LO_Color *pColor ){
  1332.     if( !c.IsDefined()){
  1333.         XP_ASSERT(FALSE);
  1334.         return;
  1335.     }
  1336.     pColor->red = EDT_RED(c);
  1337.     pColor->green = EDT_GREEN(c);
  1338.     pColor->blue = EDT_BLUE(c);
  1339. }
  1340.  
  1341.  
  1342. #define CHARSET_SIZE    256
  1343. static PA_AmpEsc *ed_escapes[CHARSET_SIZE];
  1344.  
  1345. void edt_InitEscapes(int16 /*csid*/, XP_Bool bQuoteHiBits){
  1346.     PA_AmpEsc *pEsc = PA_AmpEscapes;
  1347.  
  1348.     XP_BZERO( ed_escapes, CHARSET_SIZE * sizeof( PA_AmpEsc* ) );
  1349.  
  1350.     while( pEsc->value ){
  1351.         int ch = 0xff & (int) pEsc->value;
  1352.         if( ed_escapes[ ch ] == 0 ){
  1353.             if ( ch == '&' || ch == '<'
  1354.                 || (NON_BREAKING_SPACE == ((char) ch))            
  1355.                 || (ch > 128 && bQuoteHiBits ) ) {
  1356.                    ed_escapes[ ch ] = pEsc;
  1357.             }
  1358.         }
  1359.         pEsc++;
  1360.     }
  1361. }
  1362.  
  1363. void edt_PrintWithEscapes( CPrintState *ps, char *p, XP_Bool bBreakLines ){
  1364.     int csid = ps->m_pBuffer->GetRAMCharSetID();
  1365.     char *pBegin;
  1366.     PA_AmpEsc *pEsc;
  1367.  
  1368.     // break the lines after 70 characters if we can, but don't really do it 
  1369.     // in the formatted case.
  1370.     while( p && *p ){
  1371.         pBegin = p;
  1372.         while( *p && ps->m_iCharPos < 70 && ed_escapes[(unsigned char)*p] == 0
  1373.             && ((unsigned char) *p) != ((unsigned char) NON_BREAKING_SPACE) ){
  1374.             char* p2 = INTL_NextChar(csid, p);
  1375.             int charWidth = p2 - p;
  1376.             ps->m_iCharPos += charWidth;
  1377.             p = p2;
  1378.         }
  1379.         while( *p && *p != ' ' && ed_escapes[(unsigned char)*p] == 0
  1380.             && ((unsigned char) *p) != ((unsigned char) NON_BREAKING_SPACE)){
  1381.             char* p2 = INTL_NextChar(csid, p);
  1382.             int charWidth = p2 - p;
  1383.             ps->m_iCharPos += charWidth;
  1384.             p = p2;
  1385.         }
  1386.         ps->m_pOut->Write( pBegin, p-pBegin);
  1387.  
  1388.         if( *p && *p == ' ' && ps->m_iCharPos >= 70 ){
  1389.             if( bBreakLines ){
  1390.                 ps->m_pOut->Write( "\n", 1 );
  1391.             }
  1392.             else {
  1393.                 ps->m_pOut->Write( " ", 1 );
  1394.             }
  1395.             ps->m_iCharPos = 0;
  1396.         }
  1397.         else if( *p && (pEsc = ed_escapes[(unsigned char)*p]) != 0 ){
  1398.             ps->m_pOut->Write( "&",1 );
  1399.             ps->m_pOut->Write( pEsc->str, pEsc->len );
  1400.             ps->m_pOut->Write( ";",1 );
  1401.             ps->m_iCharPos += pEsc->len+2;
  1402.         }
  1403.         else if (((unsigned char) *p) == ((unsigned char) NON_BREAKING_SPACE) ){
  1404.             const char* nbsp = INTL_NonBreakingSpace(csid);
  1405.             int nbsp_len = XP_STRLEN(nbsp);
  1406.             ps->m_pOut->Write( (char*) nbsp, XP_STRLEN(nbsp));
  1407.         }
  1408.  
  1409.         // move past the space or special char
  1410.         if( *p ){
  1411.             p = INTL_NextChar(csid, p);
  1412.         }
  1413.     }
  1414. }
  1415.  
  1416. char *edt_LocalName( char *pURL ){
  1417.     char *pDest = FE_URLToLocalName( pURL );
  1418. #if 0
  1419.     if( pDest && FE_EditorPrefConvertFileCaseOnWrite( ) ){
  1420.         char *url_ptr = pDest;
  1421.         while ( *url_ptr != '\0' ){
  1422.             *url_ptr = XP_TO_LOWER(*url_ptr); 
  1423.             url_ptr++;
  1424.         }
  1425.     }
  1426. #endif
  1427.     return pDest;
  1428. }
  1429.  
  1430. PA_Block PA_strdup( char* s ){
  1431.     char *new_str;
  1432.     PA_Block new_buff;
  1433.  
  1434.     if( s == 0 ){
  1435.         return 0;
  1436.     }
  1437.  
  1438.     new_buff = (PA_Block)PA_ALLOC(XP_STRLEN(s) + 1);
  1439.     if (new_buff != NULL)
  1440.     {
  1441.         PA_LOCK(new_str, char *, new_buff);
  1442.         XP_STRCPY(new_str, s);
  1443.         PA_UNLOCK(new_buff);
  1444.     }
  1445.     return new_buff;
  1446. }
  1447.  
  1448. void edt_SetTagData( PA_Tag* pTag, char* pTagData){
  1449.     PA_Block buff;
  1450.     char *locked_buff;
  1451.     int iLen;
  1452.  
  1453.     iLen = XP_STRLEN(pTagData);
  1454.     buff = (PA_Block)PA_ALLOC((iLen+1) * sizeof(char));
  1455.     if (buff != NULL)
  1456.     {
  1457.         PA_LOCK(locked_buff, char *, buff);
  1458.         XP_BCOPY(pTagData, locked_buff, iLen);
  1459.         locked_buff[iLen] = '\0';
  1460.         PA_UNLOCK(buff);
  1461.     }
  1462.     else { 
  1463.         // LTNOTE: out of memory, should throw an exception
  1464.         return;
  1465.     }
  1466.     pTag->data = buff;
  1467.     pTag->data_len = iLen;
  1468.     pTag->next = NULL;
  1469.     return;
  1470. }
  1471.  
  1472. //
  1473. // Create a tag and add it to the list
  1474. //
  1475. void edt_AddTag( PA_Tag*& pStart, PA_Tag*& pEnd, TagType t, XP_Bool bIsEnd,
  1476.         char *pTagData  ){
  1477.     PA_Tag *pTag = XP_NEW( PA_Tag );
  1478.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  1479.     pTag->type = t;
  1480.     pTag->is_end = bIsEnd;
  1481.     edt_SetTagData(pTag, pTagData ? pTagData : ">" );
  1482.     if( pStart == 0 ){
  1483.         pStart = pTag;
  1484.     }
  1485.     if( pEnd ){
  1486.         pEnd->next = pTag;
  1487.     }
  1488.     pEnd = pTag;
  1489. }
  1490.  
  1491.  
  1492.  
  1493. //-----------------------------------------------------------------------------
  1494. // CEditPosition
  1495. //-----------------------------------------------------------------------------
  1496.  
  1497. void CEditPositionComparable::CalcPosition( TXP_GrowableArray_int32* pA, 
  1498.             CEditPosition *pPos ){
  1499.  
  1500.     CEditElement *pElement, *pParent, *pSib;
  1501.     int i;
  1502.  
  1503.     pA->Empty();
  1504.     pA->Add( pPos->Offset() );
  1505.     pElement = pPos->Element();
  1506.     if( pElement ){
  1507.         while( (pParent = pElement->GetParent()) != NULL ){
  1508.             i = 0;
  1509.             pSib = pParent->GetChild();        
  1510.             while( pSib != pElement ){
  1511.                 pSib = pSib->GetNextSibling();
  1512.                 i++;
  1513.             }
  1514.             pA->Add(i);
  1515.             pElement = pParent;
  1516.         }
  1517.     }
  1518. }
  1519.  
  1520. int CEditPositionComparable::Compare( CEditPosition *pPos ){
  1521.     TXP_GrowableArray_int32 toArray;
  1522.  
  1523.     CalcPosition( &toArray, pPos );
  1524.  
  1525.     int32 i = m_Array.Size();
  1526.     int32 iTo = toArray.Size();
  1527.     int32 iDiff;
  1528.  
  1529.     while( i && iTo ){
  1530.         iDiff = toArray[--iTo] - m_Array[--i];
  1531.         if( iDiff != 0 ) {
  1532.             return (iDiff > 0 ? 1 : -1 );
  1533.         }
  1534.     }
  1535.     if( i == 0 && iTo == 0){
  1536.         return 0;
  1537.     }
  1538.     iDiff = iTo - i;
  1539.     return (iDiff > 0 ? 1 : -1 );
  1540. }
  1541.  
  1542.  
  1543. // CEditInsertPoint
  1544.  
  1545. CEditInsertPoint::CEditInsertPoint() {
  1546.     m_pElement = 0;
  1547.     m_iPos = 0;
  1548.     m_bStickyAfter = FALSE;
  1549. }
  1550.  
  1551. CEditInsertPoint::CEditInsertPoint(CEditLeafElement* pElement, ElementOffset iPos) {
  1552.     m_pElement = pElement;
  1553.     m_iPos = iPos;
  1554.     m_bStickyAfter = FALSE;
  1555. }
  1556.  
  1557. CEditInsertPoint::CEditInsertPoint(CEditElement* pElement, ElementOffset iPos){
  1558.     m_pElement = pElement->Leaf();
  1559.     m_iPos = iPos;
  1560.     m_bStickyAfter = FALSE;
  1561. }
  1562.  
  1563. CEditInsertPoint::CEditInsertPoint(CEditElement* pElement, ElementOffset iPos, XP_Bool bStickyAfter){
  1564.     m_pElement = pElement->Leaf();
  1565.     m_iPos = iPos;
  1566.     m_bStickyAfter = bStickyAfter;
  1567. }
  1568.  
  1569. XP_Bool CEditInsertPoint::operator==(const CEditInsertPoint& other ) {
  1570.     return m_pElement == other.m_pElement && m_iPos == other.m_iPos;
  1571. }
  1572.  
  1573. XP_Bool CEditInsertPoint::operator!=(const CEditInsertPoint& other ) {
  1574.     return ! (*this == other);
  1575. }
  1576.  
  1577. XP_Bool CEditInsertPoint::operator<(const CEditInsertPoint& other ) {
  1578.     return Compare(other) < 0;
  1579. }
  1580.  
  1581. XP_Bool CEditInsertPoint::operator<=(const CEditInsertPoint& other ) {
  1582.     return Compare(other) <= 0;
  1583. }
  1584.  
  1585. XP_Bool CEditInsertPoint::operator>=(const CEditInsertPoint& other ) {
  1586.     return Compare(other) >= 0;
  1587. }
  1588.  
  1589. XP_Bool CEditInsertPoint::operator>(const CEditInsertPoint& other ) {
  1590.     return Compare(other) > 0;
  1591. }
  1592.  
  1593. CEditElement* CEditInsertPoint::FindNonEmptyElement( CEditElement *pStartElement ){
  1594.     CEditElement* pOldElement = NULL;
  1595.     while ( pStartElement && pStartElement != pOldElement ) {
  1596.         pOldElement = pStartElement;
  1597.         if( !pStartElement->IsLeaf() ){
  1598.             pStartElement = pStartElement->PreviousLeaf();
  1599.         }
  1600.         else if( pStartElement->Leaf()->GetLayoutElement() == 0 ){
  1601.            pStartElement = pStartElement->PreviousLeaf();
  1602.         }
  1603.     }
  1604.     return pStartElement;
  1605. }
  1606.  
  1607. int CEditInsertPoint::Compare(const CEditInsertPoint& other ) {
  1608.     XP_Bool bAIsBreak = FALSE;
  1609.     LO_Element* pA = m_pElement->GetLayoutElement();
  1610.     if ( ! pA ) {
  1611.         bAIsBreak = TRUE;
  1612.         pA = FindNonEmptyElement(m_pElement)->Leaf()->GetLayoutElement();
  1613.     }
  1614.     XP_Bool bBIsBreak = FALSE;
  1615.     LO_Element* pB = other.m_pElement->GetLayoutElement();
  1616.     if ( ! pB ) {
  1617.         bBIsBreak = TRUE;
  1618.         pB = FindNonEmptyElement(other.m_pElement)->Leaf()->GetLayoutElement();
  1619.     }
  1620.     if ( !pA || !pB ) {
  1621.         XP_ASSERT(FALSE); // Phantom insert points.
  1622.         return 0;
  1623.     }
  1624.     int32 aIndex = pA->lo_any.ele_id;
  1625.     int32 bIndex = pB->lo_any.ele_id;
  1626.     if ( aIndex < bIndex ) {
  1627.         return -1;
  1628.     }
  1629.     else if ( aIndex == bIndex ) {
  1630.         // Same element. Compare positions.
  1631.         if ( m_iPos < other.m_iPos ) {
  1632.             return -1;
  1633.         }
  1634.         else if ( m_iPos == other.m_iPos ) {
  1635.             if ( bAIsBreak < bBIsBreak ) {
  1636.                 return -1;
  1637.             }
  1638.             else if ( bAIsBreak == bBIsBreak ) {
  1639.                 return 0;
  1640.             }
  1641.             else {
  1642.                 return 1;
  1643.             }
  1644.         }
  1645.         else {
  1646.             return 1;
  1647.         }
  1648.     }
  1649.     else {
  1650.         return 1;
  1651.     }
  1652. }
  1653.  
  1654. XP_Bool CEditInsertPoint::IsDenormalizedVersionOf(const CEditInsertPoint& other){
  1655.     return other.m_iPos == 0 &&
  1656.         m_iPos == m_pElement->GetLen() &&
  1657.         m_pElement->NextLeaf() == other.m_pElement;
  1658. }
  1659.  
  1660. XP_Bool CEditInsertPoint::IsStartOfElement() {
  1661.     return m_iPos <= 0;
  1662. }
  1663.  
  1664. XP_Bool CEditInsertPoint::IsEndOfElement() {
  1665.     return m_iPos >= m_pElement->GetLen();
  1666. }
  1667.  
  1668. XP_Bool CEditInsertPoint::IsStartOfContainer() {
  1669.     return IsStartOfElement()
  1670.         && m_pElement->PreviousLeafInContainer() == NULL;
  1671. }
  1672.  
  1673. XP_Bool CEditInsertPoint::IsEndOfContainer() {
  1674.     return IsEndOfElement()
  1675.         && m_pElement->LeafInContainerAfter() == NULL;
  1676. }
  1677.  
  1678. XP_Bool CEditInsertPoint::IsStartOfDocument(){
  1679.     return IsStartOfElement() &&
  1680.         m_pElement->PreviousLeaf() == NULL;
  1681. }
  1682.  
  1683. XP_Bool CEditInsertPoint::IsEndOfDocument(){
  1684.     return m_pElement->IsEndOfDocument();
  1685. }
  1686.  
  1687. XP_Bool CEditInsertPoint::GapWithBothSidesAllowed(){
  1688.     XP_Bool bResult = FALSE;
  1689.     XP_Bool bAllowBothSidesOfGap = m_pElement->AllowBothSidesOfGap();
  1690.     if ( bAllowBothSidesOfGap
  1691.             && IsEndOfElement()
  1692.             && !IsEndOfContainer() ) {
  1693.         bResult = TRUE;
  1694.     }
  1695.     else if ( IsStartOfElement() && ! IsStartOfContainer() ) {
  1696.         if ( bAllowBothSidesOfGap ) {
  1697.             bResult = TRUE;
  1698.         }
  1699.         else {
  1700.             CEditLeafElement* pPrev = m_pElement->PreviousLeaf();
  1701.             if ( pPrev && pPrev->AllowBothSidesOfGap() ) {
  1702.                 bResult = TRUE;
  1703.             }
  1704.         }
  1705.     }
  1706.     return bResult;
  1707. }
  1708.  
  1709. XP_Bool CEditInsertPoint::IsLineBreak(){
  1710.     return IsHardLineBreak() || IsSoftLineBreak();
  1711. }
  1712.  
  1713. XP_Bool CEditInsertPoint::IsSoftLineBreak(){
  1714.     XP_Bool result = FALSE;
  1715.     CEditTextElement* pText = CEditTextElement::Cast(m_pElement);
  1716.     if ( pText ){
  1717.         int iOffset;
  1718.         LO_Element* pLOElement;
  1719.         if ( pText->GetLOElementAndOffset(m_iPos, m_bStickyAfter,
  1720.                 pLOElement, iOffset) ){
  1721.             if ( pLOElement->type == LO_LINEFEED ){
  1722.                 result = TRUE;
  1723.             }
  1724.         }
  1725.     }
  1726.     return result;
  1727. }
  1728.  
  1729. XP_Bool CEditInsertPoint::IsHardLineBreak(){
  1730.     return IsStartOfElement() && m_pElement->CausesBreakBefore()
  1731.         || IsEndOfElement() && m_pElement->CausesBreakAfter();
  1732. }
  1733.  
  1734. XP_Bool CEditInsertPoint::IsSpace() {
  1735.     XP_Bool result = FALSE;
  1736.     if ( m_pElement->IsA(P_TEXT) ) {
  1737.         if ( m_iPos == m_pElement->GetLen() ){
  1738.             CEditLeafElement *pNext = m_pElement->TextInContainerAfter();
  1739.             if( pNext
  1740.                 && pNext->IsA(P_TEXT)
  1741.                 && pNext->Text()->GetLen() != 0
  1742.                 && pNext->Text()->GetText()[0] == ' ') {
  1743.                 result = TRUE;
  1744.             }
  1745.         }
  1746.         else if ( m_pElement->Text()->GetText()[m_iPos] == ' ' ) {
  1747.             result = TRUE;
  1748.         }
  1749.     }
  1750.     return result;
  1751. }
  1752.  
  1753. XP_Bool CEditInsertPoint::IsSpaceBeforeOrAfter() {
  1754.     XP_Bool result = IsSpace();
  1755.     if ( !result ) {
  1756.         CEditInsertPoint before = PreviousPosition();
  1757.         if ( before != *this ) {
  1758.             result = before.IsSpace();
  1759.         }
  1760.     }
  1761.     return result;
  1762. }
  1763.  
  1764. CEditInsertPoint CEditInsertPoint::NextPosition(){
  1765.     CEditInsertPoint result;
  1766.     m_pElement->NextPosition(m_iPos, result.m_pElement, result.m_iPos);
  1767.     return result;
  1768. }
  1769.  
  1770. CEditInsertPoint CEditInsertPoint::PreviousPosition(){
  1771.     CEditInsertPoint result;
  1772.     m_pElement->PrevPosition(m_iPos, result.m_pElement, result.m_iPos);
  1773.     // Work around what is probably a bug in PrevPosition.
  1774.     if ( m_iPos == 0 && result.m_iPos == result.m_pElement->GetLen() ) {
  1775.         result.m_pElement->PrevPosition(result.m_iPos, result.m_pElement, result.m_iPos);
  1776.     }
  1777.     return result;
  1778. }
  1779.  
  1780. #ifdef DEBUG
  1781. void CEditInsertPoint::Print(IStreamOut& stream) {
  1782.     stream.Printf("0x%08lx.%d%s", m_pElement, m_iPos, m_bStickyAfter ? "+" : "" );
  1783. }
  1784. #endif
  1785.  
  1786.  
  1787.  
  1788. // CEditSelection
  1789.  
  1790. CEditSelection::CEditSelection(){
  1791.     m_bFromStart = FALSE;
  1792. }
  1793.  
  1794. CEditSelection::CEditSelection(CEditElement* pStart, intn iStartPos,
  1795.     CEditElement* pEnd, intn iEndPos, XP_Bool fromStart)
  1796.     : m_start(pStart, iStartPos), m_end(pEnd, iEndPos), m_bFromStart(fromStart)
  1797. {
  1798. }
  1799.  
  1800. CEditSelection::CEditSelection(const CEditInsertPoint& start,
  1801.     const CEditInsertPoint& end, XP_Bool fromStart)
  1802.     : m_start(start), m_end(end), m_bFromStart(fromStart)
  1803. {
  1804. }
  1805.  
  1806. XP_Bool CEditSelection::operator==(const CEditSelection& other ){
  1807.     return m_start == other.m_start &&
  1808.     m_end == other.m_end && m_bFromStart == other.m_bFromStart;
  1809. }
  1810.  
  1811. XP_Bool CEditSelection::operator!=(const CEditSelection& other ){
  1812.     return ! (*this == other);
  1813. }
  1814.  
  1815. XP_Bool CEditSelection::EqualRange(CEditSelection& other){
  1816.     return m_start == other.m_start && m_end == other.m_end;
  1817. }
  1818.  
  1819. XP_Bool CEditSelection::IsInsertPoint() {
  1820.     /* It's an insert point if the two edges are equal, OR if the
  1821.      * start is at the end of an element, and the
  1822.      * end is at the start of an element, and
  1823.      * the two elements are next to each other,
  1824.      * and they're in the same container. (Whew!)
  1825.      */
  1826.     return m_start == m_end ||
  1827.         ( m_start.IsEndOfElement() &&
  1828.           m_end.IsStartOfElement() &&
  1829.           m_start.m_pElement->LeafInContainerAfter() == m_end.m_pElement
  1830.         );
  1831. }
  1832.  
  1833. CEditInsertPoint* CEditSelection::GetEdge(XP_Bool bEnd){
  1834.     if ( bEnd ) return &m_end;
  1835.     else return &m_start;
  1836. }
  1837.  
  1838. CEditInsertPoint* CEditSelection::GetActiveEdge(){
  1839.      return GetEdge(!m_bFromStart);
  1840. }
  1841.  
  1842. CEditInsertPoint* CEditSelection::GetAnchorEdge(){
  1843.     return GetEdge(m_bFromStart);
  1844. }
  1845.  
  1846. XP_Bool CEditSelection::Intersects(CEditSelection& other) {
  1847.     return m_start < other.m_end && other.m_start < m_end;
  1848. }
  1849.  
  1850. XP_Bool CEditSelection::Contains(CEditInsertPoint& point) {
  1851.     return m_start <= point && point < m_end;
  1852. }
  1853.  
  1854. XP_Bool CEditSelection::Contains(CEditSelection& other) {
  1855.     return ContainsStart(other) && other.m_end <= m_end;
  1856. }
  1857.  
  1858. XP_Bool CEditSelection::ContainsStart(CEditSelection& other) {
  1859.     return Contains(other.m_start);
  1860. }
  1861.  
  1862. XP_Bool CEditSelection::ContainsEnd(CEditSelection& other) {
  1863.     return Contains(other.m_end) || other.m_end == m_end;
  1864. }
  1865.  
  1866. XP_Bool CEditSelection::ContainsEdge(CEditSelection& other, XP_Bool bEnd){
  1867.     if ( bEnd )
  1868.         return ContainsEnd(other);
  1869.     else
  1870.         return ContainsStart(other);
  1871. }
  1872.  
  1873. XP_Bool CEditSelection::CrossesOver(CEditSelection& other) {
  1874.     XP_Bool bContainsStart = ContainsStart(other);
  1875.     XP_Bool bContainsEnd = ContainsEnd(other);
  1876.     return bContainsStart ^ bContainsEnd;
  1877. }
  1878.  
  1879. XP_Bool CEditSelection::ClipTo(CEditSelection& other) {
  1880.     // True if the result is defined. No change to this if it isn't
  1881.     XP_Bool intersects = Intersects(other);
  1882.     if ( intersects ) {
  1883.         if ( m_start < other.m_start ) {
  1884.             m_start = other.m_start;
  1885.         }
  1886.         if ( other.m_end < m_end ) {
  1887.             m_end = other.m_end;
  1888.         }
  1889.     }
  1890.     return intersects;
  1891. }
  1892.  
  1893. CEditElement* CEditSelection::GetCommonAncestor(){
  1894.     return m_start.m_pElement->GetCommonAncestor(m_end.m_pElement);
  1895. }
  1896.  
  1897. XP_Bool CEditSelection::CrossesSubDocBoundary(){
  1898.     // The intent of this method is "If this selection were cut, would the
  1899.     // document become malformed?"
  1900.  
  1901.     // A document could be malformed if the selection crosses the table
  1902.     // boundary, or if it would leave a table without it's protective
  1903.     // buffer of containers.
  1904.  
  1905.     XP_Bool result = FALSE;
  1906.     if ( ! IsInsertPoint() ){
  1907.         CEditElement* pStartCell = m_start.m_pElement->GetSubDocOrLayerSkipRoot();
  1908.         CEditElement* pEndCell = m_end.m_pElement->GetSubDocOrLayerSkipRoot();
  1909.         CEditElement* pClosedEndCell = GetClosedEndContainer()->GetSubDocOrLayerSkipRoot();
  1910.         result = pStartCell != pEndCell || pStartCell != pClosedEndCell;
  1911.         if ( result ) {
  1912.             // This is only OK if the selection spans an entire table.
  1913.             Bool bStartBad = FALSE;
  1914.             Bool bEndBad = FALSE;
  1915.             if ( pStartCell ) {
  1916.                 CEditSelection all;
  1917.                 pStartCell->GetTableOrLayerIgnoreSubdoc()->GetAll(all);
  1918.                 bStartBad = ! Contains(all);
  1919.             }
  1920.  
  1921.             if ( pEndCell ) {
  1922.                 CEditSelection all;
  1923.                 pEndCell->GetTableOrLayerIgnoreSubdoc()->GetAll(all);
  1924.                 bEndBad = ! Contains(all);
  1925.             }
  1926.             result = bStartBad || bEndBad;
  1927.         }
  1928.         else if ( pStartCell && pEndCell != pClosedEndCell ) {
  1929.             // If the selection spans an entire cell, but not an entire table, that's bad
  1930.             CEditSelection all;
  1931.             pStartCell->GetTableOrLayerIgnoreSubdoc()->GetAll(all);
  1932.             result = *this != all;
  1933.         }
  1934.         else {
  1935.             // One further check: If the selection starts at the start of
  1936.             // a paragraph, and the selection ends at the end of a paragraph
  1937.             // and the previous item is a table, and the
  1938.             // next item is not a container, then we can't cut. The reason is
  1939.             // that tables need to have containers after them.
  1940.  
  1941.             // Note that m_end is one more than is selected.
  1942.             if ( m_start.IsStartOfContainer() &&
  1943.                 m_end.IsStartOfContainer() ) {
  1944.                 // The selection spans whole containers.
  1945.                 CEditContainerElement* pStartContainer = GetStartContainer();
  1946.                 CEditElement* pPreviousSib = pStartContainer->GetPreviousSibling();
  1947.                 
  1948.                 if ( pPreviousSib && pPreviousSib->IsTable() ) {
  1949.                     CEditContainerElement* pEndContainer = GetClosedEndContainer();
  1950.                     CEditElement* pNextSib = pEndContainer->GetNextSibling();
  1951.                     if ( !pNextSib || pNextSib->IsEndContainer() || pNextSib->IsTable() ) {
  1952.                         result = TRUE;
  1953.                     }
  1954.                 }
  1955.             }
  1956.         }
  1957.     }
  1958.     return result;
  1959. }
  1960.  
  1961. CEditSubDocElement* CEditSelection::GetEffectiveSubDoc(XP_Bool bEnd){
  1962.     CEditInsertPoint* pEdge = GetEdge(bEnd);
  1963.     CEditSubDocElement* pSubDoc = pEdge->m_pElement->GetSubDoc();
  1964.     while ( pSubDoc ) {
  1965.         // If the edge is actually the edge of a subdoc, skip out of the subdoc.
  1966.         CEditSelection wholeSubDoc;
  1967.         pSubDoc->GetAll(wholeSubDoc);
  1968.         // If this is a wholeSubDoc Selection, bump up
  1969.         if ( *this != wholeSubDoc ) {
  1970.             if ( *pEdge != *wholeSubDoc.GetEdge(bEnd) )
  1971.                 break;
  1972.             // If the other end of the selection is is in this same subdoc, stop
  1973.             if ( wholeSubDoc.ContainsEdge(*this, !bEnd) )
  1974.                 break;
  1975.         }
  1976.         CEditElement* pSubDocParent = pSubDoc->GetParent();
  1977.         if ( !pSubDocParent )
  1978.             break;
  1979.         CEditSubDocElement* pParentSubDoc = pSubDocParent->GetSubDoc();
  1980.         if ( ! pParentSubDoc )
  1981.             break;
  1982.         pSubDoc = pParentSubDoc;
  1983.     }
  1984.     return pSubDoc;
  1985. }
  1986.  
  1987. XP_Bool CEditSelection::ExpandToNotCrossStructures(){
  1988.     XP_Bool bChanged = FALSE;
  1989.     if ( ! IsInsertPoint() ){
  1990.         CEditElement* pGrandfatherStart = m_start.m_pElement->GetParent()->GetParent();
  1991.         CEditElement* pGrandfatherEnd;
  1992.         CEditElement* pEffectiveEndContianer;
  1993.         if ( m_end.IsEndOfDocument() || EndsAtStartOfContainer()) {
  1994.             pEffectiveEndContianer = m_end.m_pElement->PreviousLeaf()->GetParent();
  1995.         }
  1996.         else {
  1997.             pEffectiveEndContianer = m_end.m_pElement->GetParent();
  1998.         }
  1999.         pGrandfatherEnd = pEffectiveEndContianer->GetParent();
  2000.         if ( pGrandfatherStart != pGrandfatherEnd ){
  2001.             bChanged = TRUE;
  2002.             CEditElement* pAncestor = pGrandfatherStart->GetCommonAncestor(pGrandfatherEnd);
  2003.             if ( ! pAncestor ) {
  2004.                 XP_ASSERT(FALSE);
  2005.                 return FALSE;
  2006.             }
  2007.             // If we're in a table, we need to return the whole table.
  2008.             if ( pAncestor->IsTableRow() ) {
  2009.                 pAncestor = pAncestor->GetTable();
  2010.                 if ( ! pAncestor ) {
  2011.                     XP_ASSERT(FALSE);
  2012.                     return FALSE;
  2013.                 }
  2014.             }
  2015.             if ( pAncestor->IsTable() ) {
  2016.                 pAncestor->GetAll(*this);
  2017.                 return TRUE;
  2018.             }
  2019.             CEditLeafElement* pCleanStart = pAncestor->GetChildContaining(m_start.m_pElement)->GetFirstMostChild()->Leaf();
  2020.             CEditLeafElement* pCleanEnd = pAncestor->GetChildContaining(pEffectiveEndContianer)->GetLastMostChild()->NextLeaf();
  2021.             m_start.m_pElement = pCleanStart;
  2022.             m_start.m_iPos = 0;
  2023.             if ( pCleanEnd ) {
  2024.                 m_end.m_pElement = pCleanEnd;
  2025.                 m_end.m_iPos = 0;
  2026.             }
  2027.             else {
  2028.                 XP_ASSERT(FALSE);   // Somehow we've lost the end of the document element.
  2029.                 pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->Leaf();
  2030.                 m_end.m_pElement = pCleanEnd;
  2031.                 m_end.m_iPos = pCleanEnd->Leaf()->GetLen();
  2032.             }
  2033.         }
  2034.     }
  2035.     return bChanged;
  2036. }
  2037.  
  2038. void CEditSelection::ExpandToBeCutable(){
  2039.     // Very similar to ExpandToNotCrossDocumentStructures, except that
  2040.     // the result is always Cutable.
  2041.     if ( ! IsInsertPoint() ){
  2042.         CEditElement* pGrandfatherStart = m_start.m_pElement->GetParent()->GetParent();
  2043.         CEditElement* pGrandfatherEnd;
  2044.         CEditElement* pEffectiveEndContianer;
  2045.         if ( m_end.IsEndOfDocument()) {
  2046.             pEffectiveEndContianer = m_end.m_pElement->PreviousLeaf()->GetParent();
  2047.         }
  2048.         else {
  2049.             pEffectiveEndContianer = m_end.m_pElement->GetParent();
  2050.         }
  2051.         pGrandfatherEnd = pEffectiveEndContianer->GetParent();
  2052.         if ( pGrandfatherStart != pGrandfatherEnd ){
  2053.             CEditElement* pAncestor = pGrandfatherStart->GetCommonAncestor(pGrandfatherEnd);
  2054.             if ( ! pAncestor ) {
  2055.                 XP_ASSERT(FALSE);
  2056.                 return;
  2057.             }
  2058.             // If we're in a table, we need to return the whole table.
  2059.             if ( pAncestor->IsTableRow() ) {
  2060.                 pAncestor = pAncestor->GetTable();
  2061.                 if ( ! pAncestor ) {
  2062.                     XP_ASSERT(FALSE);
  2063.                     return;
  2064.                 }
  2065.             }
  2066.             if ( pAncestor->IsTable() ) {
  2067.                 pAncestor->GetAll(*this);
  2068.                 return;
  2069.             }
  2070.             CEditLeafElement* pCleanStart = pAncestor->GetChildContaining(m_start.m_pElement)->GetFirstMostChild()->Leaf();
  2071.             CEditLeafElement* pCleanEnd = pAncestor->GetChildContaining(pEffectiveEndContianer)->GetLastMostChild()->NextLeaf();
  2072.             m_start.m_pElement = pCleanStart;
  2073.             m_start.m_iPos = 0;
  2074.             if ( pCleanEnd ) {
  2075.                 m_end.m_pElement = pCleanEnd;
  2076.                 m_end.m_iPos = 0;
  2077.             }
  2078.             else {
  2079.                 XP_ASSERT(FALSE);   // Somehow we've lost the end of the document element.
  2080.                 pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->Leaf();
  2081.                 m_end.m_pElement = pCleanEnd;
  2082.                 m_end.m_iPos = pCleanEnd->Leaf()->GetLen();
  2083.             }
  2084.         }
  2085.     }
  2086. }
  2087.  
  2088. void CEditSelection::ExpandToIncludeFragileSpaces() {
  2089.     // Expand the selection to include spaces that will be
  2090.     // trimmed if we did a cut of the original selection.
  2091.     if ( ! IsInsertPoint() ){
  2092.         CEditInsertPoint before = m_start.PreviousPosition();
  2093.         // Either we backed up one position, or we hit the beginning
  2094.         // of the document.
  2095.         if ( before == m_start || before.IsSpace() ) {
  2096.             if ( m_end.IsSpace() ) {
  2097.                 if ( ! m_end.m_pElement->InFormattedText() ) {
  2098.                     m_end = m_end.NextPosition();
  2099.                 }
  2100.             }
  2101.         }
  2102.     }
  2103. }
  2104.  
  2105. void CEditSelection::ExpandToEncloseWholeContainers(){
  2106.     XP_Bool bWasInsertPoint = IsInsertPoint();
  2107.     CEditLeafElement* pCleanStart = m_start.m_pElement->GetParent()->GetFirstMostChild()->Leaf();
  2108.     m_start.m_pElement = pCleanStart;
  2109.     m_start.m_iPos = 0;
  2110.     if ( !m_end.IsStartOfContainer() || bWasInsertPoint ) {
  2111.         CEditLeafElement* pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->NextLeaf();
  2112.         if ( pCleanEnd ) {
  2113.             m_end.m_pElement = pCleanEnd;
  2114.             m_end.m_iPos = 0;
  2115.         }
  2116.         else {
  2117.             XP_ASSERT(FALSE);   // Somehow we've lost the end of the document element.
  2118.             pCleanEnd = m_end.m_pElement->GetParent()->GetLastMostChild()->Leaf();
  2119.             m_end.m_pElement = pCleanEnd;
  2120.             m_end.m_iPos = pCleanEnd->Leaf()->GetLen();
  2121.         }
  2122.     }
  2123. }
  2124.  
  2125.  
  2126. XP_Bool CEditSelection::EndsAtStartOfContainer() {
  2127.     return m_end.IsStartOfContainer();
  2128. }
  2129.  
  2130. XP_Bool CEditSelection::StartsAtEndOfContainer() {
  2131.     return m_start.IsEndOfContainer();
  2132. }
  2133.  
  2134. XP_Bool CEditSelection::AnyLeavesSelected(){
  2135.     // FALSE if insert point or container end.
  2136.     return ! (IsInsertPoint() || IsContainerEnd());
  2137. }
  2138.  
  2139. XP_Bool CEditSelection::IsContainerEnd(){
  2140.     XP_Bool result = FALSE;
  2141.     if ( ! IsInsertPoint() && EndsAtStartOfContainer()
  2142.         && StartsAtEndOfContainer() ) {
  2143.         CEditContainerElement* pStartContainer = m_start.m_pElement->FindContainer();
  2144.         CEditContainerElement* pEndContainer = m_end.m_pElement->FindContainer();
  2145.         if ( pStartContainer && pEndContainer ) {
  2146.             CEditContainerElement* pNextContainer =
  2147.                 CEditContainerElement::Cast(pStartContainer->FindNextElement(&CEditElement::FindContainer, 0));
  2148.             if ( pNextContainer == pEndContainer) {
  2149.                 result = TRUE;
  2150.             }
  2151.         }
  2152.     }
  2153.     return result;
  2154. }
  2155.  
  2156. void CEditSelection::ExcludeLastDocumentContainerEnd() {
  2157.     // Useful for cut & delete, where you don't replace the final container mark
  2158.     if ( ContainsLastDocumentContainerEnd() ){
  2159.         CEditLeafElement* pEnd = m_end.m_pElement;
  2160.         pEnd = pEnd->PreviousLeaf();
  2161.         if ( pEnd ) {
  2162.             m_end.m_pElement = pEnd;
  2163.             m_end.m_iPos = pEnd->Leaf()->GetLen();
  2164.             if ( m_start > m_end ) {
  2165.                 m_start = m_end;
  2166.             }
  2167.         }
  2168.     }
  2169. }
  2170.  
  2171. XP_Bool CEditSelection::ContainsLastDocumentContainerEnd() {
  2172.     // Useful for cut & delete, where you don't replace the final container mark
  2173.     XP_Bool bResult = FALSE;
  2174.     CEditLeafElement* pEnd = m_end.m_pElement;
  2175.     if ( pEnd && pEnd->GetElementType() == eEndElement ){
  2176.         bResult = TRUE;
  2177.     }
  2178.     return bResult;
  2179. }
  2180.  
  2181. CEditContainerElement* CEditSelection::GetStartContainer() {
  2182.     CEditElement* pParent = m_start.m_pElement->GetParent();
  2183.     if ( pParent && pParent->IsContainer() ) return pParent->Container();
  2184.     else return NULL;
  2185. }
  2186.  
  2187. CEditContainerElement* CEditSelection::GetClosedEndContainer() {
  2188.     CEditElement* pEnd = m_end.m_pElement;
  2189.     if ( m_end.IsStartOfContainer() ) {
  2190.         // Back up one
  2191.         CEditElement* pPrev = pEnd->PreviousLeaf();
  2192.         if ( pPrev ) {
  2193.             pEnd = pPrev;
  2194.         }
  2195.         else pEnd = m_start.m_pElement;
  2196.     }
  2197.     CEditElement* pParent = pEnd->GetParent();
  2198.     if ( pParent && pParent->IsContainer() ) return pParent->Container();
  2199.     else return NULL;
  2200. }
  2201.  
  2202. #ifdef DEBUG
  2203. void CEditSelection::Print(IStreamOut& stream){
  2204.     stream.Printf("start ");
  2205.     m_start.Print(stream);
  2206.     stream.Printf(" end ");
  2207.     m_end.Print(stream);
  2208.     stream.Printf(" FromStart %ld", (long)m_bFromStart);
  2209. }
  2210. #endif
  2211.  
  2212. // Persistent selections
  2213. CPersistentEditInsertPoint::CPersistentEditInsertPoint()
  2214. {
  2215.     m_index = -1;
  2216.     m_bStickyAfter = TRUE;
  2217. }
  2218.  
  2219. CPersistentEditInsertPoint::CPersistentEditInsertPoint(ElementIndex index)
  2220.     : m_index(index), m_bStickyAfter(TRUE)
  2221. {}
  2222.  
  2223. CPersistentEditInsertPoint::CPersistentEditInsertPoint(ElementIndex index, XP_Bool bStickyAfter)
  2224.     : m_index(index), m_bStickyAfter(bStickyAfter)
  2225. {
  2226. }
  2227.  
  2228. XP_Bool CPersistentEditInsertPoint::operator==(const CPersistentEditInsertPoint& other ) {
  2229.     return m_index == other.m_index;
  2230. }
  2231.  
  2232. XP_Bool CPersistentEditInsertPoint::operator!=(const CPersistentEditInsertPoint& other ) {
  2233.     return ! (*this == other);
  2234. }
  2235.  
  2236. XP_Bool CPersistentEditInsertPoint::operator<(const CPersistentEditInsertPoint& other ) {
  2237.     return m_index < other.m_index;
  2238. }
  2239.  
  2240. XP_Bool CPersistentEditInsertPoint::operator<=(const CPersistentEditInsertPoint& other ) {
  2241.     return *this < other || *this == other;
  2242. }
  2243.  
  2244. XP_Bool CPersistentEditInsertPoint::operator>=(const CPersistentEditInsertPoint& other ) {
  2245.     return ! (*this < other);
  2246. }
  2247.  
  2248. XP_Bool CPersistentEditInsertPoint::operator>(const CPersistentEditInsertPoint& other ) {
  2249.     return ! (*this <= other);
  2250. }
  2251.  
  2252. #ifdef DEBUG
  2253. void CPersistentEditInsertPoint::Print(IStreamOut& stream) {
  2254.     stream.Printf("%ld%s", (long)m_index, m_bStickyAfter ? "+" : "" );
  2255. }
  2256. #endif
  2257.  
  2258. void CPersistentEditInsertPoint::ComputeDifference(
  2259.     CPersistentEditInsertPoint& other, CPersistentEditInsertPoint& delta){
  2260.     // delta = this - other;
  2261.     delta.m_index = m_index - other.m_index;
  2262. }
  2263.  
  2264. void CPersistentEditInsertPoint::AddRelative(
  2265.     CPersistentEditInsertPoint& delta, CPersistentEditInsertPoint& result){
  2266.     // result = this + delta;
  2267.     result.m_index = m_index + delta.m_index;
  2268. }
  2269.  
  2270. XP_Bool CPersistentEditInsertPoint::IsEqualUI(
  2271.     const CPersistentEditInsertPoint& other ) {
  2272.     return m_index == other.m_index && m_bStickyAfter == other.m_bStickyAfter;
  2273. }
  2274.  
  2275. // class CPersistentEditSelection
  2276.  
  2277. CPersistentEditSelection::CPersistentEditSelection()
  2278. {
  2279.     m_bFromStart = FALSE;
  2280. }
  2281.  
  2282. CPersistentEditSelection::CPersistentEditSelection(const CPersistentEditInsertPoint& start, const CPersistentEditInsertPoint& end)
  2283.     : m_start(start), m_end(end)
  2284. {
  2285.     m_bFromStart = FALSE;
  2286. }
  2287.  
  2288. ElementIndex CPersistentEditSelection::GetCount() {
  2289.     return m_end.m_index - m_start.m_index;
  2290. }
  2291.  
  2292. XP_Bool CPersistentEditSelection::IsInsertPoint(){
  2293.     return m_start == m_end;
  2294. }
  2295.  
  2296. XP_Bool CPersistentEditSelection::operator==(const CPersistentEditSelection& other ) {
  2297.     return SelectedRangeEqual(other) && m_bFromStart == other.m_bFromStart;
  2298. }
  2299.  
  2300. XP_Bool CPersistentEditSelection::operator!=(const CPersistentEditSelection& other ) {
  2301.     return ! ( *this == other );
  2302. }
  2303.  
  2304. XP_Bool CPersistentEditSelection::SelectedRangeEqual(const CPersistentEditSelection& other ) {
  2305.     return m_start == other.m_start && m_end == other.m_end;
  2306. }
  2307.  
  2308. CPersistentEditInsertPoint* CPersistentEditSelection::GetEdge(XP_Bool bEnd){
  2309.     return bEnd ? &m_end : &m_start;
  2310.  
  2311. #ifdef DEBUG
  2312. void CPersistentEditSelection::Print(IStreamOut& stream) {
  2313.     stream.Printf("start ");
  2314.     m_start.Print(stream);
  2315.     stream.Printf(" end ");
  2316.     m_end.Print(stream);
  2317.     stream.Printf(" FromStart %ld", (long)m_bFromStart);
  2318. }
  2319. #endif
  2320.  
  2321. // Used for undo
  2322. // change this by the same way that original was changed into modified.
  2323. void CPersistentEditSelection::Map(CPersistentEditSelection& original,
  2324.     CPersistentEditSelection& modified){
  2325.     CPersistentEditSelection delta;
  2326.     CPersistentEditSelection result;
  2327.     modified.m_start.ComputeDifference(original.m_start, delta.m_start);
  2328.     modified.m_end.ComputeDifference(original.m_end, delta.m_end);
  2329.     m_start.AddRelative(delta.m_start, result.m_start);
  2330.     m_end.AddRelative(delta.m_end, result.m_end);
  2331.     *this = result;
  2332. }
  2333.  
  2334. //
  2335. //  Call this constructor with a maximum number, a series of bits and 
  2336. //  BIT_ARRAY_END for example: CBitArray a(100, 1 ,4 9 ,7, BIT_ARRAY_END);
  2337. // 
  2338. CBitArray::CBitArray(long n, int iFirst, ...) {
  2339.  
  2340.     m_Bits = 0;
  2341.     size = 0;
  2342.  
  2343.     int i;
  2344.  
  2345.     if( n ) {
  2346.       SetSize(n);
  2347.     }
  2348.     va_list stack;
  2349. #ifdef OSF1
  2350.     va_start( stack, n );
  2351. #else
  2352.     va_start( stack, iFirst );
  2353.  
  2354.     (*this)[iFirst] = 1;
  2355. #endif
  2356.  
  2357.     while( (i = va_arg(stack,int)) != BIT_ARRAY_END ){
  2358.        (*this)[i] = 1;
  2359.     }
  2360. }
  2361.  
  2362. void CBitArray::SetSize(long n){
  2363.     // On Win16 we can't have a size larger that int.
  2364.   XP_ASSERT(size < (1L << (16 + 3)));
  2365.   uint8 *oldBits = m_Bits;
  2366.   m_Bits = new uint8[n/8+1];
  2367.   memset(m_Bits,0,(int) (n/8+1));
  2368.   if(oldBits){
  2369.      memcpy(m_Bits,oldBits,(int) (min(n,size)/8+1));
  2370.      delete oldBits;
  2371.   }
  2372.   size = n;
  2373. }
  2374.  
  2375. void CBitArray::ClearAll(){
  2376.   memset(m_Bits,0, (int) (size/8+1) );
  2377. }
  2378.  
  2379. /* CLM: Helper to gray UI items not allowed when inside Java Script
  2380.  * Note that the return value is FALSE if partial selection, 
  2381.  *   allowing the non-script text to be changed
  2382.  * Current Font Size, Color, and Character attributes will suppress
  2383.  *   setting other attributes, so it is OK to call these when mixed
  2384. */
  2385. #ifdef USE_SCRIPT
  2386.  
  2387. XP_Bool EDT_IsJavaScript(MWContext * pMWContext)
  2388. {
  2389.     if(!pMWContext) return FALSE;
  2390.     XP_Bool bRetVal = FALSE;
  2391.     EDT_CharacterData * pData = EDT_GetCharacterData(pMWContext);
  2392.     if( pData) {
  2393.         bRetVal = ( (0 != (pData->mask & TF_SERVER)) &&
  2394.                     (0 != (pData->values & TF_SERVER)) ) ||
  2395.                   ( (0 != (pData->mask & TF_STYLE)) &&
  2396.                     (0 != (pData->values & TF_STYLE)) ) ||
  2397.                   ( (0 != (pData->mask & TF_SCRIPT )) &&
  2398.                     (0 != (pData->values & TF_SCRIPT)) );
  2399.         EDT_FreeCharacterData(pData);
  2400.     }
  2401.     return bRetVal;
  2402. }
  2403.  
  2404. #else
  2405.  
  2406. XP_Bool EDT_IsJavaScript(MWContext *)
  2407. {
  2408.     return FALSE;
  2409. }
  2410.  
  2411. #endif
  2412.  
  2413. /* Helper to use for enabling Character properties 
  2414.  * (Bold, Italic, etc., but DONT use for clearing (TF_NONE)
  2415.  *  or setting Java Script (Server or Client)
  2416.  * Tests for:
  2417.  *   1. Good edit buffer and not blocked because of some action,
  2418.  *   2. Caret or selection is NOT entirely within Java Script, 
  2419.  *   3. Caret or selection has some text or is mixed selection
  2420.  *      (thus FALSE if single non-text object is selected)
  2421. */
  2422. XP_Bool EDT_CanSetCharacterAttribute(MWContext * pMWContext)
  2423. {
  2424.     if( !pMWContext || pMWContext->waitingMode || EDT_IsBlocked(pMWContext) ){
  2425.         return FALSE;
  2426.     }
  2427.     ED_ElementType type = EDT_GetCurrentElementType(pMWContext);
  2428.     return ( (type == ED_ELEMENT_TEXT || type == ED_ELEMENT_SELECTION ||
  2429.               type >= ED_ELEMENT_TABLE) && !EDT_IsJavaScript(pMWContext) );
  2430. }
  2431.  
  2432. // XP string defines for FontFaces
  2433.  
  2434. extern "C" {
  2435. #include "xpgetstr.h"
  2436. #define WANT_ENUM_STRING_IDS
  2437. #include "allxpstr.h"
  2438. #undef WANT_ENUM_STRING_IDS
  2439. }
  2440.  
  2441. int iFontFaces[] = {
  2442.     XP_NSFONT_DEFAULT,
  2443.     XP_NSFONT_FIXED,
  2444.     XP_NSFONT_ARIAL,
  2445.     XP_NSFONT_TIMES,
  2446.     XP_NSFONT_COURIER
  2447. };
  2448.  
  2449. // IDs for the list of fonts written to the tags
  2450. // MUST CORRESPOND IN NUMBER AND LOCATION TO iFontFaces!
  2451. int iFontFaceTags[] = {
  2452.     0, 0,
  2453.     XP_NSFONTLIST_ARIAL,
  2454.     XP_NSFONTLIST_TIMES,
  2455.     XP_NSFONTLIST_COURIER
  2456. };
  2457. #define ED_NSFONT_MAX ((sizeof(iFontFaceTags))/sizeof(int))
  2458.  
  2459. // Cache the lists of font faces that we show to user
  2460. //  and list of tags (no shown to user)
  2461. static char * pFontFaces = NULL;
  2462. static char * pFontFaceTags = NULL;
  2463.  
  2464. char *EDT_GetFontFaces()
  2465. {
  2466.     if( pFontFaces ){
  2467.         return pFontFaces;
  2468.     }
  2469.     intn iSize = 512;
  2470.     int iCur = 0;
  2471.     pFontFaces = (char*)XP_ALLOC( iSize );
  2472.  
  2473.     if( !pFontFaces )
  2474.         return NULL;
  2475.  
  2476.     pFontFaces[0] = 0;
  2477.     pFontFaces[1] = 0;
  2478.     
  2479.     for( int i = 0; i < ED_NSFONT_MAX; i++ ){
  2480.         char *pFont = XP_GetString(iFontFaces[i]);
  2481.  
  2482.         if( pFont && *pFont ){
  2483.             int iLen = XP_STRLEN( pFont );
  2484.             if( iCur + iLen + 2 > iSize ){
  2485.                 iSize += 512;
  2486.                 pFontFaces = (char*)XP_REALLOC( pFontFaces, iSize );
  2487.  
  2488.                 if( ! pFontFaces ){
  2489.                     return NULL;
  2490.                 }
  2491.             }
  2492.             XP_STRCPY( &pFontFaces[iCur], pFont );
  2493.             iCur += iLen+1;
  2494.         }
  2495.     }
  2496.     // Append extra '\0' at end
  2497.     pFontFaces[iCur] = 0;
  2498.  
  2499.     return pFontFaces;
  2500. }
  2501.  
  2502. char *EDT_GetFontFaceTags()
  2503. {
  2504.     if( pFontFaceTags ){
  2505.         return pFontFaceTags;
  2506.     }
  2507.     intn iSize = 2024;
  2508.     pFontFaceTags = (char*)XP_ALLOC( iSize );
  2509.  
  2510.     if( !pFontFaceTags )
  2511.         return NULL;
  2512.  
  2513.     // First 2 strings are ignored - map to default proportional and fixed
  2514.     XP_STRCPY(pFontFaceTags, " ");
  2515.     XP_STRCPY(&pFontFaceTags[2], " ");
  2516.     int iCur = 4; // Start at 4th character - after above strings
  2517.     pFontFaceTags[5] = 0; // Add extra 0 in case there are no more strings
  2518.  
  2519.     for( int i = 2; i < ED_NSFONT_MAX; i++ ){
  2520.         char *pFont = XP_GetString(iFontFaceTags[i]);
  2521.  
  2522.         if( pFont && *pFont ){
  2523.             int iLen = XP_STRLEN( pFont );
  2524.             if( iCur + iLen + 2 > iSize ){
  2525.                 iSize += 2024;
  2526.                 pFontFaceTags = (char*)XP_REALLOC( pFontFaceTags, iSize );
  2527.  
  2528.                 if( ! pFontFaceTags ){
  2529.                     return NULL;
  2530.                 }
  2531.             }
  2532.             XP_STRCPY( &pFontFaceTags[iCur], pFont );
  2533.             iCur += iLen+1;
  2534.         }
  2535.     }
  2536.     // Append extra '\0' at end
  2537.     pFontFaceTags[iCur] = 0;
  2538.  
  2539.     return pFontFaceTags;
  2540. }
  2541.  
  2542. #define EDT_CLEAR_BIT(x,tf) x &= ~tf
  2543. #define EDT_SET_BIT(x,tf)   x |= tf
  2544.  
  2545. void EDT_SetFontFace(MWContext * pMWContext, EDT_CharacterData * pCharacterData,
  2546.                      int iFontIndex, char * pFontFace )
  2547. {
  2548.     // Must have one or the other
  2549.     if( pMWContext == NULL && pCharacterData == NULL )
  2550.         return;
  2551. #if defined(XP_WIN) || defined(XP_OS2)
  2552.     // Windows may give us this if click off of the font combobox
  2553.     if( (long)pFontFace == -1L )
  2554.         return;
  2555. #endif
  2556.  
  2557.     EDT_CharacterData * pData;
  2558.     XP_Bool bCanCopy;
  2559.     XP_Bool bHaveData;
  2560.  
  2561.     // Only 0 and 1 have any meaning now that XP groups are "automatic"
  2562.     if( iFontIndex < 0 || iFontIndex > 1 ){
  2563.         iFontIndex = -1;
  2564.     }
  2565.     if( pCharacterData ){
  2566.         // Use supplied structure
  2567.         pData = pCharacterData;
  2568.         bHaveData = TRUE;
  2569.         // If pointer to existing data is same as new, don't copy it
  2570.         bCanCopy = pData->pFontFace != pFontFace;
  2571.         // We will respect the TF_FONT_FACE bit in existing data,
  2572.         //  but we are also interested in FIXED if we are doing
  2573.         if( pData->mask & TF_FONT_FACE ){
  2574.             EDT_SET_BIT(pData->mask, TF_FIXED);
  2575.         }
  2576.     } else {
  2577.         // No data supplied, get from current selection or at caret
  2578.         bHaveData = FALSE;
  2579.         pData = EDT_GetCharacterData(pMWContext);
  2580.         if( !pData )
  2581.             return;
  2582.         bCanCopy = TRUE;
  2583.         // Set the bit to tell that we are interested in font face
  2584.         //  and fixed width
  2585.         pData->mask = TF_FONT_FACE | TF_FIXED;
  2586.     }
  2587.  
  2588.     // Remove any existing font face data if not same as new data
  2589.     if( pData->pFontFace && bCanCopy){
  2590.         XP_FREEIF(pData->pFontFace);
  2591.     }
  2592.  
  2593.     // This will force using whatever user string was supplied
  2594.     if( pFontFace ){
  2595.         // So it will be skipped in test below
  2596.         iFontIndex = -1;
  2597.         if( bCanCopy) {
  2598.             // Translate into XP font "group" if face is found in any group
  2599.             char *pTranslated = EDT_TranslateToXPFontFace(pFontFace);
  2600.             if( *pTranslated == '_' ){
  2601.                 // First character may have signal for separator - skip over it
  2602.                 pTranslated++;
  2603.             }            
  2604.             // Detect the 1st two items -- the Default Variable and Fixed Width fonts
  2605.             if( 0 == XP_STRCMP(pTranslated, XP_GetString(iFontFaces[0])) ){
  2606.                 iFontIndex = 0;
  2607.             } else if( 0 == XP_STRCMP(pTranslated,  XP_GetString(iFontFaces[1])) ){
  2608.                 iFontIndex = 1;
  2609.             } else {
  2610.                 // Copy the font face - either same as pFontFace or the entire group if translated
  2611.                 pData->pFontFace = XP_STRDUP(pTranslated);
  2612.                 // Set font face tag - note that it is set
  2613.                 EDT_SET_BIT(pData->values, TF_FONT_FACE);
  2614.                 // We can't be fixed width if here
  2615.                 EDT_CLEAR_BIT(pData->values, TF_FIXED);
  2616.             }
  2617.         }
  2618.     } 
  2619.  
  2620.     if( iFontIndex == 0 || iFontIndex == 1 ){
  2621.         // No font face tag will be set
  2622.         EDT_CLEAR_BIT(pData->values, TF_FONT_FACE);
  2623.         
  2624.         if( iFontIndex == 1){
  2625.             // Fixed Width
  2626.             EDT_SET_BIT(pData->values, TF_FIXED);
  2627.         } else {
  2628.             // Default proportional font
  2629.             EDT_CLEAR_BIT(pData->values, TF_FIXED);
  2630.         }
  2631.     }
  2632.  
  2633.     if( pMWContext ){
  2634.         EDT_SetCharacterData(pMWContext, pData);
  2635.     }
  2636.     // Free only the data we obtained
  2637.     if( !bHaveData ){
  2638.         EDT_FreeCharacterData(pData);
  2639.     }
  2640. }
  2641.  
  2642. char * EDT_TranslateToXPFontFace( char * pFontFace )
  2643. {
  2644.     char * pFontFaceTags = EDT_GetFontFaceTags();
  2645.     if( pFontFaceTags ){
  2646.         char * pFace = pFontFace;
  2647.         // Skip over initial separator signal
  2648.         if( *pFace == '_' ){
  2649.             pFace++;
  2650.         }
  2651.         // Skip over first two strings (each contains " ")
  2652.         char * pTag = pFontFaceTags+4;
  2653.         int iLen;    
  2654.         int i = 0;
  2655.         while( (iLen = XP_STRLEN(pTag)) > 0 ) {
  2656.             char * pComma;
  2657.             char * pTagFace = pTag;
  2658.             // Scan for comma separating the next font in group
  2659.             while( (pComma = XP_STRCHR(pTagFace,',')) != 0 ){
  2660.                 *pComma = '\0';
  2661.                 int iFaceLen = XP_STRLEN(pTagFace);
  2662.                 XP_Bool bFound = (0 == XP_STRCMP(pTagFace, pFace));
  2663.                 *pComma = ',';
  2664.                 if( bFound ){
  2665.                     // We found the font in the list - we're done
  2666.                     return pTag;
  2667.                 }
  2668.                 pTagFace += iFaceLen+1;
  2669.             }
  2670.             // Check one more time - either no commas found or 
  2671.             //  pFace is on the last font in the group
  2672.             if( 0 == XP_STRCMP(pTagFace, pFace) ){
  2673.                 return pTag;
  2674.             }
  2675.     
  2676.             pTag += iLen+1;
  2677.             i++;
  2678.         }
  2679.     }
  2680.     // Return exactly what we were passed if not found in font groups    
  2681.     return pFontFace;
  2682. }
  2683.  
  2684.  
  2685. // Cache the list of colors that we show to user
  2686. //static char * pFontColors = NULL;
  2687.  
  2688. // Use this array instead of pFontColor list
  2689. // This replaces the XP_Strings colors
  2690. uint8 NSColors[MAX_NS_COLORS][3] = 
  2691. { { 255,255,255 },
  2692.   { 204,204,204 },
  2693.   { 192,192,192 },
  2694.   { 153,153,153 },
  2695.   { 102,102,102 },
  2696.   { 51,51,51 },   
  2697.   { 0,0,0 },      
  2698.   { 255,204,204 },
  2699.   { 255,102,102 },
  2700.   { 255,0,0 },    
  2701.   { 204,0,0 },    
  2702.   { 153,0,0 },    
  2703.   { 102,0,0 },    
  2704.   { 51,0,0 },     
  2705.   { 255,204,153 },
  2706.   { 255,204,51 }, 
  2707.   { 255,153,0 },  
  2708.   { 255,102,0 },  
  2709.   { 204,102,0 },  
  2710.   { 153,51,0 },   
  2711.   { 102,51,0 },   
  2712.   { 255,255,153 },
  2713.   { 255,255,102 },
  2714.   { 255,204,102 },
  2715.   { 255,204,51 }, 
  2716.   { 204,153,51 }, 
  2717.   { 153,102,51 }, 
  2718.   { 102,51,51 },  
  2719.   { 255,255,204 },
  2720.   { 255,255,153 },
  2721.   { 255,255,0 },  
  2722.   { 255,204,0 },  
  2723.   { 153,153,0 },  
  2724.   { 102,102,0 },  
  2725.   { 51,51,0 },    
  2726.   { 153,255,153 },
  2727.   { 102,255,153 },
  2728.   { 51,255,51 },  
  2729.   { 0,204,0 },    
  2730.   { 0,153,0 },    
  2731.   { 0,102,0 },    
  2732.   { 0,51,0 },     
  2733.   { 153,255,255 },
  2734.   { 51,255,255 }, 
  2735.   { 102,204,204 },
  2736.   { 0,204,204 },  
  2737.   { 51,153,153 }, 
  2738.   { 51,102,102 }, 
  2739.   { 0,51,51 },    
  2740.   { 204,255,255 },
  2741.   { 102,255,255 },
  2742.   { 51,204,255 }, 
  2743.   { 51,102,255 }, 
  2744.   { 51,51,255 },  
  2745.   { 0,0,153 },    
  2746.   { 0,0,102 },    
  2747.   { 204,204,255 },
  2748.   { 153,153,204 },
  2749.   { 102,102,204 },
  2750.   { 102,51,255 }, 
  2751.   { 102,0,204 },  
  2752.   { 51,51,153 },  
  2753.   { 51,0,153 },   
  2754.   { 255,204,255 },
  2755.   { 255,153,255 },
  2756.   { 204,102,204 },
  2757.   { 204,51,204 }, 
  2758.   { 153,51,102 }, 
  2759.   { 102,51,102 }, 
  2760.   { 51,0,51 } };    
  2761.  
  2762. void EDT_GetNSColor(intn iIndex, LO_Color * pLoColor)
  2763. {
  2764.     if( pLoColor && iIndex < MAX_NS_COLORS )
  2765.     {
  2766.         pLoColor->red = NSColors[iIndex][0];
  2767.         pLoColor->green = NSColors[iIndex][1];
  2768.         pLoColor->blue = NSColors[iIndex][2];
  2769.     }
  2770. }
  2771.  
  2772. /* Return list of font colors in format: "r,g,b,ColorName" where colors for r,g,b
  2773.  *  are decimal strings in range 0-255
  2774. */
  2775. #if defined(XP_UNIX)
  2776. char *EDT_GetFontColors()
  2777. {
  2778.     return NULL;
  2779.  
  2780. #if 0
  2781.     if( pFontColors )
  2782.         return pFontColors;
  2783.  
  2784.     intn iSize = 512;
  2785.     int iCur = 0;
  2786.     pFontColors = (char*)XP_ALLOC( iSize );
  2787.  
  2788.     if( !pFontColors )
  2789.         return NULL;
  2790.  
  2791.     pFontColors[0] = 0;
  2792.     pFontColors[1] = 0;
  2793.     
  2794.     int nIDColor = XP_NSCOLOR_BASE;
  2795.     while( nIDColor < XP_NSCOLOR_END){
  2796.         char *pColor = XP_GetString(nIDColor);
  2797.  
  2798.         if( pColor && *pColor ){
  2799.             int iLen = XP_STRLEN( pColor );
  2800.             if( iCur + iLen + 2 > iSize ){
  2801.                 iSize += 512;
  2802.                 pFontColors = (char*)XP_REALLOC( pFontColors, iSize );
  2803.  
  2804.                 if( ! pFontColors ){
  2805.                     return NULL;
  2806.                 }
  2807.             }
  2808.             XP_STRCPY( &pFontColors[iCur], pColor );
  2809.             iCur += iLen+1;
  2810.         }
  2811.         // Next color ID
  2812.         nIDColor++;
  2813.     }
  2814.     // Append extra '\0' at end
  2815.     pFontColors[iCur] = 0;
  2816.     return pFontColors;
  2817. #endif
  2818. }
  2819. #endif
  2820.  
  2821. char * EDT_ParseColorString(LO_Color * pLoColor, char * pColorString)
  2822. {
  2823.     XP_ASSERT(pLoColor);
  2824.     XP_ASSERT(pColorString);
  2825.  
  2826.     char *pComma = XP_STRCHR(pColorString, ',');
  2827.     if(!pComma) return pColorString;
  2828.  
  2829.     *pComma = '\0';
  2830.     pLoColor->red = (uint8)atoi(pColorString);
  2831.     *pComma = ',';
  2832.     char *pColor = pComma+1;
  2833.  
  2834.     pComma = XP_STRCHR(pColor, ',');
  2835.     if(!pComma) return pColor;
  2836.     *pComma = '\0';
  2837.     pLoColor->green = (uint8)atoi(pColor);
  2838.     *pComma = ',';
  2839.     pColor = pComma+1;
  2840.  
  2841.     pComma = XP_STRCHR(pColor, ',');
  2842.     // If no comma found, assume color string
  2843.     //  doesn't have HTML-HEX format and 
  2844.     //  all thats left is the blue value
  2845.     if(pComma) 
  2846.         *pComma = '\0';
  2847.     pLoColor->blue = (uint8)atoi(pColor);
  2848.     if(pComma) 
  2849.     {
  2850.         *pComma = ',';
  2851.         pColor = pComma+1;
  2852.         // Return rest of string, assumned to be Hex representation
  2853.         return pColor;
  2854.     }
  2855.  
  2856.     // Supply a hex translation
  2857.     static char pHexColor[16];
  2858.     XP_SPRINTF(pHexColor, "#%02X%02X%02X", pLoColor->red, pLoColor->green, pLoColor->blue);
  2859.     return pHexColor;
  2860. }
  2861.  
  2862. /* Scan our list of colors and return index of matching color
  2863.  *   or -1 if no match found. We NEVER return 0 (default color)
  2864. */
  2865. int EDT_GetMatchingFontColorIndex(LO_Color * pLOColor)
  2866. {
  2867.     for( intn i = 0; i < MAX_NS_COLORS; i++ )
  2868.     {
  2869.         if( NSColors[i][0] == pLOColor->red &&
  2870.             NSColors[i][1] == pLOColor->green &&
  2871.             NSColors[i][2] == pLOColor->blue ){
  2872.             return (i+1);
  2873.         }
  2874.     }
  2875.     return -1;
  2876. }
  2877.  
  2878.  
  2879. #ifdef DEBUG
  2880.  
  2881. // Automated test routine hook
  2882.  
  2883. const char* CEditTestManager::m_kTrigger = "$$$Test";
  2884.  
  2885. CEditTestManager::CEditTestManager(CEditBuffer* pBuffer)
  2886.     : m_pBuffer(pBuffer),
  2887.       m_bTesting(FALSE),
  2888.       m_iTriggerIndex(0),
  2889.       m_pSaveBuffer(0),
  2890.       m_pTempFileURL(0)
  2891. {
  2892. #ifdef XP_WIN32
  2893.     _CrtMemCheckpoint( &m_state ); // In theorey, avoid measuring the data before we were created.
  2894. #endif
  2895.  
  2896.     PowerOnTest();
  2897. }
  2898.  
  2899. void CEditTestManager::DumpMemoryDelta() {
  2900.     m_pBuffer->Trim(); // Clear out undo/redo log to simplify memory statistics.
  2901. #ifdef XP_WIN32
  2902.     _CrtMemDumpAllObjectsSince( &m_state );
  2903.     _CrtMemCheckpoint( &m_state );
  2904. #else
  2905.     XP_TRACE(("Not supported on this OS.\n"));
  2906. #endif
  2907. }
  2908.  
  2909. XP_Bool CEditTestManager::Key(char key) {
  2910.     XP_Bool result = m_bTesting;
  2911.     if ( ! m_bTesting ){
  2912.         if ( key == m_kTrigger[m_iTriggerIndex] ) {
  2913.             m_iTriggerIndex++;
  2914.             if ( m_kTrigger[m_iTriggerIndex] == '\0' ) {
  2915.                 m_bTesting = TRUE;
  2916.                 XP_TRACE(("Testing on! Type # of test, or Q to quit."));
  2917.             }
  2918.         }
  2919.         else {
  2920.             m_iTriggerIndex = 0;
  2921.         }
  2922.     }
  2923.     else {
  2924.         int bQuitTesting = FALSE;
  2925.         m_bTesting = FALSE; // So when the tests type, it doesn't cause a recursion into the test code.
  2926.         intn bTestResult = -1;
  2927.         if ( key >= 'A' && key <= 'Z' ) key = key + ('a' - 'A');
  2928.         switch (key) {
  2929.             case 'q':
  2930.                 bQuitTesting = TRUE;
  2931.                 XP_TRACE(("Quitting test mode."));
  2932.                 break;
  2933.             case 'a':
  2934.                 DumpLoElements();
  2935.                 break;
  2936.             case 'b':
  2937.                 VerifyLoElements();
  2938.                 break;
  2939.             case 'c':
  2940.                 DumpDocumentContents();
  2941.                 break;
  2942.             case 'd':
  2943.                 DumpMemoryDelta();
  2944.                 break;
  2945.             case 'g':
  2946.                 GetDocTempDir();
  2947.                 break;            
  2948.             case '!':
  2949.                 PasteINTL();
  2950.                 break;
  2951.             case 'w':
  2952.                 PasteINTLText();
  2953.                 break;
  2954.  
  2955.             case 'x':
  2956.                 CopyDocumentToBuffer();
  2957.                 break;
  2958.             case 'y':
  2959.                 CopyBufferToDocument();
  2960.                 break;
  2961. #ifdef EDITOR_JAVA
  2962.             case 'j':
  2963.                 DumpPlugins();
  2964.                 break;
  2965.             case 'k':
  2966.                 PerformFirstPlugin();
  2967.                 break;
  2968.             case 'l':
  2969.                 PerformPluginByName();
  2970.                 break;
  2971.             case 'm':
  2972.                 PerformFirstEncoder();
  2973.                 break;
  2974.             case 'n':
  2975.                 PerformPreOpen();
  2976.                 break;
  2977. #endif
  2978.             case 's':
  2979.                 SaveToTempFile();
  2980.                 break;
  2981.             case 't':
  2982.                 RemoveTempFile();
  2983.                 break;
  2984.             case 'v':
  2985.                 TestHTMLPaste();
  2986.                 break;
  2987.             case '0':
  2988.                 bTestResult = EveryNavigationKeyEverywhereTest();
  2989.                 break;
  2990.             case '1':
  2991.                 bTestResult = ArrowTest();
  2992.                 break;
  2993.             case '2':
  2994.                 bTestResult = BackspaceKeyTest();
  2995.                 break;
  2996.             case '3':
  2997.                 bTestResult = DeleteKeyTest();
  2998.                 break;
  2999.             case '4':
  3000.                 bTestResult = ZeroDocumentCursorTest();
  3001.                 break;
  3002.             case '5':
  3003.                 bTestResult = OneCharDocumentCursorTest();
  3004.                 break;
  3005.             case '6':
  3006.                 bTestResult = BoldTest(10); // OneDayTest(1);
  3007.                 break;
  3008.             case '7':
  3009.                     m_pBuffer->m_bSkipValidation = TRUE;
  3010.                     bTestResult = OneDayTest(100);
  3011.                     m_pBuffer->m_bSkipValidation = FALSE;
  3012.                 break;
  3013.            case '8':
  3014.                 bTestResult = TextFETest();
  3015.                 break;
  3016.              case '?':
  3017.                  XP_TRACE(("? - type this help\n"
  3018.                     "Q - quit.\n"
  3019.                     "a - print lo-elements.\n"
  3020.                     "b - verify lo-elements.\n"
  3021.                     "c - print document contents (elements, undo history, etc.)\n"
  3022.                     "d - dump memory usage delta.\n"
  3023.                     "g - get doc temp directory. \n"
  3024.                     ));
  3025. #ifdef EDITOR_JAVA
  3026.                  XP_TRACE(("j - show Composer Plugin and Image Encoder information.\n"
  3027.                      "k - run the first composer plugin.\n"
  3028.                      "l - run a composer plugin by name.\n"
  3029.                      "m - run the first image encoder.\n"
  3030.                      "n - test the EDT_PreOpen call.\n"
  3031.                     ));
  3032. #endif
  3033.                  XP_TRACE(("q - test HTML Paste Quoted.\n"
  3034.                     ));
  3035.                  XP_TRACE(("s - save doc to temporary file.\n"
  3036.                     ));
  3037.                  XP_TRACE(("t - remove saved temporary file.\n"
  3038.                     ));
  3039.                  XP_TRACE(("x - save document state.\n"
  3040.                     "y - restore document state.\n"
  3041.                     ));
  3042.                  XP_TRACE((
  3043.                     "0 - navigation key crash test.\n"
  3044.                     "1 - arrow completeness test.\n"
  3045.                     "2 - destructive backspace test.\n"
  3046.                     "3 - destructive delete test.\n"
  3047.                     "4 - empty document cursor test.\n"
  3048.                     "5 - one character document cursor test.\n"
  3049.                     "6 - one day document test - simulate editing for one whole day 1 cycle.\n"
  3050.                     "7 - one day document test - simulate editing for one whole day 500 cycle.\n"
  3051.                      "8 - write buffer to out.txt as plain text.\n"
  3052.                    ));
  3053.                break;
  3054.             default:
  3055.                  XP_TRACE(("Type ? for help, Type # of test, or Q to quit."));
  3056.                  break;
  3057.         }
  3058.         if ( ! bQuitTesting )
  3059.         {
  3060.             m_bTesting = TRUE;
  3061.         }
  3062.         if ( bTestResult >= 0 ){
  3063.             XP_TRACE(("Test %s", bTestResult ? "Passed": "Failed"));
  3064.         }
  3065.     }
  3066.     return result;
  3067. }
  3068.  
  3069. void CEditTestManager::PowerOnTest() {
  3070.     // Test some things that have to be tested by code. This method is
  3071.     // called whenever the editor starts up in debug mode. Don't take too long.
  3072.     
  3073.     // Test that we know about all the existing tags.
  3074.     for(int i = 0; i < P_MAX; i++ ){
  3075.         EDT_TagString(i);
  3076.     }
  3077. }
  3078.  
  3079. void CEditTestManager::DumpLoElements() {
  3080.     lo_PrintLayout(m_pBuffer->m_pContext);
  3081. }
  3082.  
  3083. void CEditTestManager::VerifyLoElements() {
  3084.     lo_VerifyLayout(m_pBuffer->m_pContext);
  3085. }
  3086.  
  3087. void CEditTestManager::DumpDocumentContents(){
  3088.     CStreamOutMemory buffer;
  3089.     m_pBuffer->printState.Reset( &buffer, m_pBuffer );
  3090.     m_pBuffer->DebugPrintTree( m_pBuffer->m_pRoot );
  3091.     m_pBuffer->DebugPrintState(buffer);
  3092.     TraceLargeString(buffer.GetText());
  3093. }
  3094.  
  3095. void TraceLargeString(char* b){
  3096.     // have to dump one line at a time. XP_TRACE has a 512 char limit on Windows.
  3097.     while ( *b != '\0' ) {
  3098.         char* b2 = b;
  3099.         while ( *b2 != '\0' && *b2 != '\n'){
  3100.             b2++;
  3101.         }
  3102.         char old = *b2;
  3103.         *b2 = '\0';
  3104.         XP_TRACE(("%s", b));
  3105.         *b2 = old;
  3106.         b = b2 + 1;
  3107.         if ( old == '\0' ) break;
  3108.     }
  3109. }
  3110.  
  3111. void CEditTestManager::PasteINTL(){
  3112.     unsigned char testStringJIS[] = {'J','I','S',' ','[',27,'$','B',30,21,']',' ',0};
  3113.     unsigned char testStringEUC1[] = {'E','U','C',' ','[', 0xb0, 0};
  3114.     unsigned char tesrStringEUC2[] = {0xa1,']',' ',0};
  3115.     unsigned char testStringSJIS[] = {'S','J','I','S',' ','[',0x88,0x9f,']',0};
  3116.     
  3117.     MWContext* pContext = m_pBuffer->m_pContext;
  3118.     EDT_PasteQuoteBegin(pContext, TRUE);
  3119.     EDT_PasteQuoteINTL(pContext, (char*) testStringJIS, CS_JIS);
  3120.     EDT_PasteQuoteINTL(pContext, (char*) testStringEUC1, CS_EUCJP);
  3121.     EDT_PasteQuoteINTL(pContext, (char*) tesrStringEUC2, CS_EUCJP);
  3122.     EDT_PasteQuoteINTL(pContext, (char*) testStringSJIS, CS_SJIS);
  3123.     EDT_PasteQuoteEnd(pContext);
  3124. }
  3125.  
  3126. void CEditTestManager::PasteINTLText(){
  3127.     unsigned char testStringJIS[] = {'J','I','S',' ','[',27,'$','B',30,21,']',' ',0};
  3128.     unsigned char testStringEUC[] = {'E','U','C',' ','[', 0xb0, 0xa1,']',' ',0};
  3129.     unsigned char testStringSJIS[] = {'S','J','I','S',' ','[',0x88,0x9f,']',0};
  3130.     
  3131.     MWContext* pContext = m_pBuffer->m_pContext;
  3132.     EDT_PasteQuoteBegin(pContext, FALSE);
  3133.     EDT_PasteQuoteINTL(pContext, (char*) testStringJIS, CS_JIS);
  3134.     EDT_PasteQuoteINTL(pContext, (char*) testStringEUC, CS_EUCJP);
  3135.     EDT_PasteQuoteINTL(pContext, (char*) testStringSJIS, CS_SJIS);
  3136.     EDT_PasteQuoteEnd(pContext);
  3137. }
  3138.  
  3139. void CEditTestManager::CopyDocumentToBuffer(){
  3140.     if ( m_pSaveBuffer){
  3141.         delete[] m_pSaveBuffer;
  3142.         m_pSaveBuffer = 0;
  3143.     }
  3144.     m_pBuffer->WriteToBuffer(&m_pSaveBuffer, TRUE);
  3145.     XP_TRACE(("Buffer size: %d bytes", XP_STRLEN(m_pSaveBuffer)));
  3146. }
  3147.  
  3148. void CEditTestManager::CopyBufferToDocument(){
  3149.     if ( m_pSaveBuffer == NULL ) {
  3150.         XP_TRACE(("The save buffer is empty."));
  3151.     }
  3152.     else {
  3153.         m_pBuffer->ReadFromBuffer(m_pSaveBuffer);
  3154.     }
  3155. }
  3156.  
  3157. static const char* kChunkName[EDT_NA_COUNT] = {
  3158.     "character", "word", "line edge", "document", "updown"
  3159. };
  3160.  
  3161. XP_Bool CEditTestManager::ZeroDocumentCursorTest(){
  3162.     XP_TRACE(("ZeroDocumentCursorTest"));
  3163.     // [selection][chunk][direction][edge]
  3164.     const ElementIndex kExpectedResults[2][EDT_NA_COUNT][2][2] =
  3165.     {   // no selection
  3166.         {
  3167.             {{ 0, 0}, {0, 0} }, // EDT_NA_CHARACTER
  3168.             {{ 0, 0}, {0, 0} }, // EDT_NA_WORD
  3169.             {{ 0, 0}, {0, 0} }, // EDT_NA_LINEEDGE
  3170.             {{ 0, 0}, {0, 0} }, // EDT_NA_DOCUMENT
  3171.             {{ 0, 0}, {0, 0} }  // EDT_NA_UPDOWN
  3172.         },
  3173.         // selection
  3174.         {
  3175.             {{ 0, 0}, {0, 1} }, // EDT_NA_CHARACTER
  3176.             {{ 0, 0}, {0, 1} }, // EDT_NA_WORD
  3177.             {{ 0, 0}, {0, 1} }, // EDT_NA_LINEEDGE
  3178.             {{ 0, 0}, {0, 1} }, // EDT_NA_DOCUMENT
  3179.             {{ 0, 0}, {0, 1} }  // EDT_NA_UPDOWN
  3180.         }
  3181.     };
  3182.  
  3183.     XP_Bool result = TRUE;
  3184.     // Clear everything from existing document
  3185.     EDT_SelectAll(m_pBuffer->m_pContext);
  3186.     EDT_DeleteChar(m_pBuffer->m_pContext);
  3187.     CPersistentEditInsertPoint zero(0,FALSE);
  3188.     // Verify the cursor does the right thing for every possible cursor
  3189.     for ( int select = 0; select < 2; select++ ) {
  3190.         for ( int chunk = 0; chunk < EDT_NA_COUNT; chunk++ ) {
  3191.             for ( int direction = 0; direction < 2; direction++ ) {
  3192.                 m_pBuffer->SetInsertPoint(zero);
  3193.                 m_pBuffer->NavigateChunk(select, chunk, direction);
  3194.                 CPersistentEditSelection selection;
  3195.                 m_pBuffer->GetSelection(selection);
  3196.                 for ( int edge = 0; edge < 2; edge++ ) {
  3197.                     ElementIndex expected = kExpectedResults[select][chunk][direction][edge];
  3198.                     ElementIndex actual = selection.GetEdge(edge)->m_index;
  3199.                     if ( expected != actual ){
  3200.                         result = FALSE;
  3201.                         XP_TRACE(("selection: %s chunk: %s direction: %s edge: %s. Expected %d got %d",
  3202.                             select ? "TRUE" : "FALSE",
  3203.                             kChunkName[chunk],
  3204.                             direction ? "forward" : "back",
  3205.                             edge ? "end" : "start",
  3206.                             expected,
  3207.                             actual));
  3208.                     }
  3209.                 }
  3210.             }
  3211.         }
  3212.     }
  3213.     return result;
  3214. }
  3215.  
  3216.  
  3217. XP_Bool CEditTestManager::OneCharDocumentCursorTest(){
  3218.     XP_TRACE(("OneCharDocumentCursorTest"));
  3219.     // [startPos][selection][chunk][direction][edge]
  3220.     const ElementIndex kExpectedResults[2][2][EDT_NA_COUNT][2][2] =
  3221.     {
  3222.         // position 0
  3223.         {   // no selection
  3224.             {
  3225.                 {{ 0, 0}, {1, 1} }, // EDT_NA_CHARACTER
  3226.                 {{ 0, 0}, {1, 1} }, // EDT_NA_WORD
  3227.                 {{ 0, 0}, {1, 1} }, // EDT_NA_LINEEDGE
  3228.                 {{ 0, 0}, {1, 1} }, // EDT_NA_DOCUMENT
  3229.                 {{ 0, 0}, {0, 0} }  // EDT_NA_UPDOWN
  3230.             },
  3231.             // selection
  3232.             {
  3233.                 {{ 0, 0}, {0, 1} }, // EDT_NA_CHARACTER
  3234.                 {{ 0, 0}, {0, 1} }, // EDT_NA_WORD
  3235.                 {{ 0, 0}, {0, 2} }, // EDT_NA_LINEEDGE
  3236.                 {{ 0, 0}, {0, 2} }, // EDT_NA_DOCUMENT
  3237.                 {{ 0, 0}, {0, 2} }  // EDT_NA_UPDOWN
  3238.             }
  3239.         },
  3240.         // position 1
  3241.         {   // no selection
  3242.             {
  3243.                 {{ 0, 0}, {1, 1} }, // EDT_NA_CHARACTER
  3244.                 {{ 0, 0}, {1, 1} }, // EDT_NA_WORD
  3245.                 {{ 0, 0}, {1, 1} }, // EDT_NA_LINEEDGE
  3246.                 {{ 0, 0}, {1, 1} }, // EDT_NA_DOCUMENT
  3247.                 {{ 1, 1}, {1, 1} }  // EDT_NA_UPDOWN
  3248.             },
  3249.             // selection
  3250.             {
  3251.                 {{ 0, 1}, {1, 2} }, // EDT_NA_CHARACTER
  3252.                 {{ 0, 1}, {1, 2} }, // EDT_NA_WORD
  3253.                 {{ 0, 1}, {1, 2} }, // EDT_NA_LINEEDGE
  3254.                 {{ 0, 1}, {1, 2} }, // EDT_NA_DOCUMENT
  3255.                 {{ 0, 1}, {1, 2} }  // EDT_NA_UPDOWN
  3256.             }
  3257.         }
  3258.     };
  3259.  
  3260.     XP_Bool result = TRUE;
  3261.     // Clear everything from existing document
  3262.     EDT_SelectAll(m_pBuffer->m_pContext);
  3263.     EDT_DeleteChar(m_pBuffer->m_pContext);
  3264.     m_pBuffer->InsertChar( 'X', FALSE );
  3265.     // Verify the cursor does the right thing for every possible cursor
  3266.     for ( int startPos = 0; startPos < 2; startPos++ ){
  3267.         CPersistentEditInsertPoint p(startPos,FALSE);
  3268.         for ( int select = 0; select < 2; select++ ) {
  3269.             for ( int chunk = 0; chunk < EDT_NA_COUNT; chunk++ ) {
  3270.                 for ( int direction = 0; direction < 2; direction++ ) {
  3271.                     m_pBuffer->SetInsertPoint(p);
  3272.                     m_pBuffer->NavigateChunk(select, chunk, direction);
  3273.                     CPersistentEditSelection selection;
  3274.                     m_pBuffer->GetSelection(selection);
  3275.                     for ( int edge = 0; edge < 2; edge++ ) {
  3276.                         ElementIndex expected = kExpectedResults[startPos][select][chunk][direction][edge];
  3277.                         ElementIndex actual = selection.GetEdge(edge)->m_index;
  3278.                         if ( expected != actual ){
  3279.                             result = FALSE;
  3280.                             XP_TRACE(("position: %d selection: %s chunk: %s direction: %s edge: %s. Expected %d got %d",
  3281.                                 startPos,
  3282.                                 select ? "TRUE" : "FALSE",
  3283.                                 kChunkName[chunk],
  3284.                                 direction ? "forward" : "back",
  3285.                                 edge ? "end" : "start",
  3286.                                 expected,
  3287.                                 actual));
  3288.                         }
  3289.                     }
  3290.                 }
  3291.             }
  3292.         }
  3293.     }
  3294.     return result;
  3295. }
  3296.  
  3297. XP_Bool CEditTestManager::OneDayTest(int32 rounds) {
  3298.     char* kTitle = "One Day Test\n";
  3299.     XP_TRACE(("%s", kTitle));
  3300.     EDT_SelectAll(m_pBuffer->m_pContext);
  3301.     EDT_DeleteChar(m_pBuffer->m_pContext);
  3302.     EDT_PasteText(m_pBuffer->m_pContext, kTitle);
  3303.     for( int32 i = 0; i < rounds; i++ ) {
  3304.         XP_TRACE(("Round %d", i));
  3305.         const char* kTestString = "All work and no play makes Jack a dull boy.\nRedrum.\n";
  3306.         char c;
  3307.         for ( int j = 0;
  3308.               (c = kTestString[j]) != '\0';
  3309.             j++ ) {
  3310.             if ( c == '\n' || c == '\r') {
  3311.                 EDT_ReturnKey(m_pBuffer->m_pContext);
  3312.             }
  3313.             else {
  3314.                 EDT_KeyDown(m_pBuffer->m_pContext, c,0,0);
  3315.             }
  3316.         }
  3317.         // And delete a little.
  3318.         for ( int k = 0; k < 8; k++ ) {
  3319.             EDT_DeletePreviousChar(m_pBuffer->m_pContext);
  3320.         }
  3321.     }
  3322.     XP_TRACE(("End of %s.", kTitle));
  3323.     return TRUE;
  3324. }
  3325.  
  3326. static void TextFETestDone(PrintSetup *setup) {
  3327.     XP_FileClose(setup->out);
  3328.     XP_TRACE(("Finished TextFETest."));
  3329. }
  3330.  
  3331.  
  3332. XP_Bool CEditTestManager::TextFETest() {
  3333.     PrintSetup setup;
  3334.     char *_srcName = 0;
  3335.     char *srcName = 0;
  3336.     char *srcURL = 0;
  3337.     char *destName = 0;
  3338.     XP_File destFile = 0;
  3339.     URL_Struct *srcURLS = 0;
  3340.     XP_Bool ret = FALSE;
  3341.     ED_FileError result;
  3342.  
  3343.     ////  Write the edit buffer to a temporary file as HTML.
  3344.     // Get the filename of a temporary file.
  3345. #ifdef XP_MAC
  3346.     _srcName = WH_TempName(xpFileToPost,"ns");
  3347. #else
  3348.     _srcName = WH_TempName(xpTemporary,"ns");
  3349. #endif
  3350.     if (!_srcName)
  3351.         goto CLEAN_UP;
  3352.  
  3353.     // Is this necessary for some Mac magic to work?
  3354.     srcName = WH_FilePlatformName(_srcName);
  3355.     if (!srcName)
  3356.         goto CLEAN_UP;
  3357.  
  3358.     srcURL = XP_PlatformFileToURL(srcName);
  3359.     if (!srcURL)
  3360.         goto CLEAN_UP;
  3361.  
  3362.     result = EDT_SaveFile(m_pBuffer->m_pContext,
  3363.                                         srcURL,srcURL,
  3364.                                         FALSE,FALSE,FALSE);
  3365.     if (result != ED_ERROR_NONE)
  3366.         goto CLEAN_UP;
  3367.     XP_TRACE(("Created %s",srcURL));
  3368.  
  3369.  
  3370.     // The converted plain text will go to "out.txt".
  3371.     destName = XP_STRDUP("out.txt");
  3372.     if (!destName)
  3373.         goto CLEAN_UP;
  3374.  
  3375.     destFile = XP_FileOpen(destName,xpFileToPost,XP_FILE_WRITE_BIN);
  3376.     if (!destFile)
  3377.         goto CLEAN_UP;
  3378.  
  3379.  
  3380.     //// Call XL_Translate text to convert src to dest, TextFETestDone will be called on
  3381.     // completion.
  3382.     srcURLS = NET_CreateURLStruct(srcURL,NET_DONT_RELOAD);
  3383.     if (!srcURLS) {
  3384.         XP_FileClose(destFile);
  3385.         goto CLEAN_UP;
  3386.     }
  3387.  
  3388.     XL_InitializeTextSetup(&setup);
  3389.     setup.url = srcURLS;
  3390.     setup.completion = TextFETestDone;
  3391.     setup.out = destFile;
  3392.  
  3393.     // Seems that passed in MWcontext is only used to copy some info when making the
  3394.     // temporary text context.
  3395.     XL_TranslateText(m_pBuffer->m_pContext,srcURLS,&setup);
  3396.     ret = TRUE;
  3397.  
  3398.  
  3399. // Nasty, a goto.
  3400. CLEAN_UP:
  3401.     if (_srcName)
  3402.         XP_FREE(_srcName);
  3403.     if (srcName)
  3404.         XP_FREE(srcName);
  3405.     if (srcURL)
  3406.         XP_FREE(srcURL);
  3407.     if (destName)
  3408.         XP_FREE(destName);
  3409.  
  3410.     return ret;
  3411. }
  3412.  
  3413. XP_Bool CEditTestManager::BoldTest(int32 rounds) {
  3414.     char* kTitle = "Bold Test\n";
  3415.     XP_TRACE(("%s", kTitle));
  3416.     EDT_SelectAll(m_pBuffer->m_pContext);
  3417.     EDT_DeleteChar(m_pBuffer->m_pContext);
  3418.     EDT_PasteText(m_pBuffer->m_pContext, kTitle);
  3419.     EDT_SelectAll(m_pBuffer->m_pContext);
  3420.     for( int32 i = 0; i < rounds; i++ ) {
  3421.         XP_TRACE(("Round %d", i));
  3422.         EDT_CharacterData* normal = EDT_GetCharacterData( m_pBuffer->m_pContext );
  3423.         EDT_CharacterData* bold = EDT_GetCharacterData( m_pBuffer->m_pContext );
  3424.         bold->mask = TF_BOLD;
  3425.         bold->values = TF_BOLD;
  3426.         EDT_SetCharacterData(m_pBuffer->m_pContext, bold);
  3427.         EDT_SetCharacterData(m_pBuffer->m_pContext, normal);
  3428.         EDT_FreeCharacterData(normal);
  3429.         EDT_FreeCharacterData(bold);
  3430.     }
  3431.     XP_TRACE(("End of %s.", kTitle));
  3432.     return TRUE;
  3433. }
  3434.  
  3435. XP_Bool CEditTestManager::EveryNavigationKeyEverywhereTest() {
  3436.     // Does navigation work from every position?
  3437.     XP_Bool result;
  3438.     result = NavigateChunkCrashTest() && UArrowTest(FALSE) && UArrowTest(TRUE) 
  3439.                 && DArrowTest(FALSE) && DArrowTest(TRUE);
  3440.     XP_TRACE(("Done.\n"));
  3441.     return result;
  3442. }
  3443.  
  3444. XP_Bool CEditTestManager::ArrowTest() {
  3445.     // Does the navigation move smoothly through the document?
  3446.     XP_Bool result;
  3447.     result = LArrowTest(FALSE) || LArrowTest(TRUE) || RArrowTest(FALSE)
  3448.                 || RArrowTest(TRUE) || UArrowTest(TRUE) || DArrowTest(TRUE);
  3449. #if 0
  3450.     // Too many false positives -- need to know about up/down at first/last line
  3451.     result |= UArrowTest(FALSE);
  3452.     result |= DArrowTest(FALSE);
  3453. #endif
  3454.     XP_TRACE(("Done.\n"));
  3455.     return result;
  3456. }
  3457.  
  3458. void CEditTestManager::GetWholeDocumentSelection(CPersistentEditSelection& selection){
  3459.     CEditSelection wholeDocument;
  3460.     m_pBuffer->m_pRoot->GetAll(wholeDocument);
  3461.     selection = m_pBuffer->EphemeralToPersistent(wholeDocument);
  3462.     // Ignore the last 2 positions -- it's the EOF marker
  3463.     selection.m_end.m_index -= 2;
  3464. }
  3465.  
  3466. XP_Bool CEditTestManager::NavigateChunkCrashTest(){
  3467.     for ( int select = 0; select < 2; select++ ) {
  3468.         for ( int chunk = 0; chunk < EDT_NA_COUNT; chunk++ ) {
  3469.             for ( int direction = 0; direction < 2; direction++ ) {
  3470.                 if ( !NavChunkCrashTest(select, chunk, direction) )
  3471.                     return FALSE;
  3472.             }
  3473.         }
  3474.     }
  3475.     return TRUE;
  3476. }
  3477.  
  3478. XP_Bool CEditTestManager::NavChunkCrashTest(XP_Bool bSelect, int chunk, XP_Bool bDirection){
  3479.     XP_Bool bResult = TRUE;
  3480.     CPersistentEditSelection w;
  3481.     GetWholeDocumentSelection(w);
  3482.     CPersistentEditInsertPoint p;
  3483.     XP_TRACE(("NavChunkCrashTest bSelect = %d chunk = %d bDirection = %d\n", bSelect, chunk, bDirection));
  3484.  
  3485.     for ( p = w.m_start;
  3486.         p.m_index < w.m_end.m_index;
  3487.         p.m_index++ ) {
  3488.         m_pBuffer->SetInsertPoint(p);
  3489.         m_pBuffer->NavigateChunk(bSelect, chunk, bDirection);
  3490.     }
  3491.     return bResult;
  3492. }
  3493.  
  3494. XP_Bool CEditTestManager::RArrowTest(XP_Bool bSelect){
  3495.     XP_Bool result = TRUE;
  3496.     CPersistentEditSelection w;
  3497.     GetWholeDocumentSelection(w);
  3498.     CPersistentEditInsertPoint p;
  3499.     XP_TRACE(("Right Arrow%s\n", bSelect ? " with shift key" : ""));
  3500.  
  3501.     for ( p = w.m_start;
  3502.         p.m_index < w.m_end.m_index;
  3503.         p.m_index++ ) {
  3504.         m_pBuffer->SetInsertPoint(p);
  3505.         m_pBuffer->NavigateChunk(bSelect, LO_NA_CHARACTER, TRUE);
  3506.         CPersistentEditSelection s2;
  3507.         m_pBuffer->GetSelection(s2);
  3508.         if ( (bSelect == s2.IsInsertPoint()) ||
  3509.             s2.m_end.m_index != p.m_index + 1 ) {
  3510.             XP_TRACE(("%d should be %d was %d\n", p.m_index,
  3511.                 p.m_index + 1, s2.m_end.m_index));
  3512.             result = FALSE;
  3513.         }
  3514.     }
  3515.     return result;
  3516. }
  3517.  
  3518. XP_Bool CEditTestManager::LArrowTest(XP_Bool bSelect){
  3519.     XP_Bool result = TRUE;
  3520.     CPersistentEditSelection w;
  3521.     GetWholeDocumentSelection(w);
  3522.     CPersistentEditInsertPoint p(0,TRUE);
  3523.     XP_TRACE(("Left Arrow Test%s\n", bSelect ? " with shift key" : ""));
  3524.  
  3525.     for ( p.m_index = w.m_end.m_index - 1;
  3526.         p.m_index > w.m_start.m_index;
  3527.         p.m_index-- ) {
  3528.  
  3529.         CEditSelection startSelection;
  3530.         CPersistentEditSelection s2;
  3531.  
  3532.         p.m_bStickyAfter = TRUE;
  3533.         m_pBuffer->SetInsertPoint(p);
  3534.  
  3535.         m_pBuffer->GetSelection(startSelection);
  3536.         if ( startSelection.m_start.GapWithBothSidesAllowed() && ! bSelect ) {
  3537.             // XP_TRACE(("%d - gap with both sides allowed.", p.m_index));
  3538.             // Test that SetInsertPoint did the right thing.
  3539.             m_pBuffer->GetSelection(s2);
  3540.             if ( ! s2.IsInsertPoint() || !s2.m_start.IsEqualUI(p) ) {
  3541.                 XP_TRACE(("SetInsertPoint at %d should be %d.%d was %d.%d\n", p.m_index,
  3542.                     p.m_index, p.m_bStickyAfter,
  3543.                     s2.m_start.m_index, s2.m_start.m_bStickyAfter));
  3544.                 result = FALSE;
  3545.             }
  3546.             // Test hump over soft break
  3547.             m_pBuffer->NavigateChunk(bSelect, LO_NA_CHARACTER, FALSE);
  3548.             m_pBuffer->GetSelection(s2);
  3549.             CPersistentEditInsertPoint expected = p;
  3550.             expected.m_bStickyAfter = FALSE;
  3551.             if ( ! s2.IsInsertPoint() || !s2.m_start.IsEqualUI(expected) ) {
  3552.                 XP_TRACE(("gap at %d should be %d.%d was %d.%d\n", p.m_index,
  3553.                     expected.m_index, expected.m_bStickyAfter,
  3554.                     s2.m_start.m_index, s2.m_start.m_bStickyAfter));
  3555.                 result = FALSE;
  3556.             }
  3557.             p.m_bStickyAfter = FALSE;
  3558.             m_pBuffer->SetInsertPoint(p);
  3559.         }
  3560.         m_pBuffer->NavigateChunk(bSelect, LO_NA_CHARACTER, FALSE);
  3561.         m_pBuffer->GetSelection(s2);
  3562.         if ( bSelect == s2.IsInsertPoint() ) {
  3563.             XP_TRACE(("%d Wrong type of selection. Expected %d.%d was %d.%d\n", p.m_index,
  3564.                 p.m_index, p.m_bStickyAfter,
  3565.                 s2.m_start.m_index, s2.m_start.m_bStickyAfter));
  3566.             result = FALSE;
  3567.         }
  3568.         else if ( s2.m_start.m_index != p.m_index - 1 ) {
  3569.             XP_TRACE(("%d wrong edge position. Should be %d.%d was %d.%d\n", p.m_index,
  3570.                 p.m_index-1, p.m_bStickyAfter,
  3571.                 s2.m_start.m_index, s2.m_start.m_bStickyAfter));
  3572.             result = FALSE;
  3573.         }
  3574.     }
  3575.     return result;
  3576. }
  3577.  
  3578. XP_Bool CEditTestManager::UArrowTest(XP_Bool bSelect){
  3579.     XP_Bool result = TRUE;
  3580.     CPersistentEditSelection w;
  3581.     GetWholeDocumentSelection(w);
  3582.     CPersistentEditInsertPoint p;
  3583.     XP_TRACE(("Up Arrow Test%s\n", bSelect ? " with shift key" : ""));
  3584.  
  3585.     for ( p = w.m_end.m_index - 1;
  3586.         p.m_index > w.m_start.m_index;
  3587.         p.m_index-- ) {
  3588.         m_pBuffer->SetInsertPoint(p);
  3589.         CPersistentEditSelection s;
  3590.         m_pBuffer->GetSelection(s);
  3591.         m_pBuffer->ClearMove();
  3592.         m_pBuffer->UpDown(bSelect, FALSE);
  3593.         CPersistentEditSelection s2;
  3594.         m_pBuffer->GetSelection(s2);
  3595.         if ( s2 == s ) {
  3596.             XP_TRACE(("%d didn't change selection\n", p.m_index));
  3597.             result = FALSE;
  3598.         }
  3599.     }
  3600.     return result;
  3601. }
  3602.  
  3603.  
  3604. XP_Bool CEditTestManager::DArrowTest(XP_Bool bSelect){
  3605.     XP_Bool result = TRUE;
  3606.     CPersistentEditSelection w;
  3607.     GetWholeDocumentSelection(w);
  3608.     CPersistentEditInsertPoint p;
  3609.     XP_TRACE(("Down Arrow Test%s\n", bSelect ? " with shift key" : ""));
  3610.  
  3611.     for ( p = w.m_start.m_index;
  3612.         p.m_index < w.m_end.m_index;
  3613.         p.m_index++ ) {
  3614.         m_pBuffer->SetInsertPoint(p);
  3615.         CPersistentEditSelection s;
  3616.         m_pBuffer->GetSelection(s);
  3617.         m_pBuffer->ClearMove();
  3618.         m_pBuffer->UpDown(bSelect, TRUE);
  3619.         CPersistentEditSelection s2;
  3620.         m_pBuffer->GetSelection(s2);
  3621.         if ( s2 == s ) {
  3622.             XP_TRACE(("%d didn't change selection\n", p.m_index));
  3623.             result = FALSE;
  3624.         }
  3625.     }
  3626.     return result;
  3627. }
  3628.  
  3629. XP_Bool CEditTestManager::BackspaceKeyTest(){
  3630.     XP_Bool result = TRUE;
  3631.     CPersistentEditSelection w;
  3632.     GetWholeDocumentSelection(w);
  3633.     CPersistentEditInsertPoint p;
  3634.     XP_TRACE(("DeleteKeyTest"));
  3635.     p = w.m_end;
  3636.     p.m_index--;
  3637.     m_pBuffer->SetInsertPoint(p);
  3638.     for ( p = w.m_start.m_index;
  3639.         p.m_index < w.m_end.m_index;
  3640.         p.m_index++ ) {
  3641.         m_pBuffer->DeletePreviousChar();
  3642.     }
  3643.     return result;
  3644. }
  3645.  
  3646. XP_Bool CEditTestManager::DeleteKeyTest(){
  3647.     XP_Bool result = TRUE;
  3648.     CPersistentEditSelection w;
  3649.     GetWholeDocumentSelection(w);
  3650.     CPersistentEditInsertPoint p;
  3651.     XP_TRACE(("DeleteKeyTest"));
  3652.  
  3653.     m_pBuffer->SetInsertPoint(w.m_start);
  3654.     for ( p = w.m_start.m_index;
  3655.         p.m_index < w.m_end.m_index;
  3656.         p.m_index++ ) {
  3657.         m_pBuffer->DeleteNextChar();
  3658.     }
  3659.     return result;
  3660. }
  3661.  
  3662. XP_Bool CEditTestManager::Backspace() {
  3663.     if ( ! m_bTesting ){
  3664.         m_iTriggerIndex--;
  3665.         if (m_iTriggerIndex < 0 ) m_iTriggerIndex = 0;
  3666.     }
  3667.     return m_bTesting;
  3668. }
  3669.  
  3670. XP_Bool CEditTestManager::ReturnKey() {
  3671.     if ( ! m_bTesting ){
  3672.         m_iTriggerIndex = 0;
  3673.     }
  3674.     return m_bTesting;
  3675. }
  3676.  
  3677. #ifdef EDITOR_JAVA
  3678.  
  3679. void CEditTestManager::DumpPlugins(){
  3680.     XP_TRACE(("Dump Plugin database."));
  3681.     int32 numCategories = EDT_NumberOfPluginCategories();
  3682.     XP_TRACE(("EDT_NumberOfPluginCategories: %d", numCategories));
  3683.     for(int category = 0; category < numCategories; category++ ) {
  3684.         char* pcsCategoryName = EDT_GetPluginCategoryName(category);
  3685.         XP_TRACE(("EDT_GetPluginCategoryName(%d) = \"%s\"", category, pcsCategoryName));
  3686.         int plugins = EDT_NumberOfPlugins(category);
  3687.         XP_TRACE(("EDT_NumberOfPlugins(%d) = %d", category, plugins));
  3688.         for(int plugin = 0; plugin < plugins; plugin++){
  3689.             char* pcsPluginName = EDT_GetPluginName(category, plugin);
  3690.             XP_TRACE(("EDT_GetPluginName(%d, %d) = \"%s\"", category, plugin, pcsPluginName));
  3691.             char* pcsPluginMenuHelp = EDT_GetPluginMenuHelp(category, plugin);
  3692.             XP_TRACE(("EDT_GetPluginMenuHelp(%d, %d) = \"%s\"", category, plugin, pcsPluginMenuHelp));
  3693.         }
  3694.     }
  3695.     XP_TRACE(("Dump Image Encoder database."));
  3696.     int encoders = EDT_NumberOfEncoders();
  3697.     XP_TRACE(("EDT_NumberOfEncoders() = %d", encoders));
  3698.     for(int encoder = 0; encoder < encoders; encoder++){
  3699.         char* pcsEncoderName = EDT_GetEncoderName(encoder);
  3700.         XP_TRACE(("EDT_GetEncoderName(%d) = \"%s\"", encoder, pcsEncoderName));
  3701.  
  3702.         char* pcsEncoderFileExtension = EDT_GetEncoderFileExtension(encoder);
  3703.         XP_TRACE(("EDT_GetEncoderFileExtension(%d) = \"%s\"", encoder, pcsEncoderFileExtension));
  3704.  
  3705.         char* pcsEncoderFileType = EDT_GetEncoderFileType(encoder);
  3706.         XP_TRACE(("EDT_GetEncoderFileType(%d) = \"%s\"", encoder, pcsEncoderFileType));
  3707.  
  3708.         XP_TRACE(("EDT_GetEncoderNeedsQuantizedSource(%d) = \"%d\"", encoder, EDT_GetEncoderNeedsQuantizedSource(encoder)));
  3709.  
  3710.         char* pcsEncoderMenuHelp = EDT_GetEncoderMenuHelp(encoder);
  3711.         XP_TRACE(("EDT_GetEncoderMenuHelp(%d) = \"%s\"", encoder, pcsEncoderMenuHelp));
  3712.  
  3713.     }
  3714. }
  3715.  
  3716. #ifndef XP_OS2
  3717. static 
  3718. #else
  3719. extern "OPTLINK"
  3720. #endif
  3721. void edt_TestImageEncoderCallbackFn(EDT_ImageEncoderStatus status, void* hook) {
  3722.     XP_TRACE(("edt_TestImageEncoderCallbackFn Status: %d hook: %08x", status, hook));
  3723. }
  3724.  
  3725. void CEditTestManager::PerformFirstPlugin(){
  3726.     if ( EDT_NumberOfPluginCategories() <= 0 ) {
  3727.         XP_TRACE(("EDT_NumberOfPluginCategories: %d", EDT_NumberOfPluginCategories()));
  3728.         return;
  3729.     }
  3730.     if ( EDT_NumberOfPlugins(0) <= 0 ) {
  3731.         XP_TRACE(("EDT_NumberOfPlugins(0): %d", EDT_NumberOfPlugins(0)));
  3732.         return;
  3733.     }
  3734.     XP_Bool bItem = EDT_PerformPlugin(m_pBuffer->m_pContext, 0, 0, edt_TestImageEncoderCallbackFn, (void*) 0x1234);
  3735.     XP_TRACE(("EDT_PerformPlugin: returned %d\n", bItem));
  3736. }
  3737.  
  3738. void CEditTestManager::PerformPluginByName(){
  3739.     XP_TRACE(("Calling dummyPlugin.does.not.exist"));
  3740.     XP_Bool bResult = EDT_PerformPluginByClassName(m_pBuffer->m_pContext, "dummyPlugin.does.not.exist",
  3741.         edt_TestImageEncoderCallbackFn, (void*) 12);
  3742.     XP_TRACE(("EDT_PerformPluginByClassName returned %d\n", bResult));
  3743.     XP_TRACE(("Calling netscape.test.plugin.composer.EditRaw"));
  3744.     bResult = EDT_PerformPluginByClassName(m_pBuffer->m_pContext, "netscape.test.plugin.composer.EditRaw",
  3745.          edt_TestImageEncoderCallbackFn, (void*) 13);
  3746.     XP_TRACE(("EDT_PerformPluginByClassName returned %d\n", bResult));
  3747. }
  3748.  
  3749. void CEditTestManager::PerformFirstEncoder(){
  3750.     if ( EDT_NumberOfEncoders() <= 0 ) {
  3751.         XP_TRACE(("EDT_NumberOfEncoders: %d", EDT_NumberOfEncoders()));
  3752.         return;
  3753.     }
  3754.     char** ppPixels = (char**) XP_ALLOC(sizeof(char*));
  3755.     char* pPixels = (char*) XP_ALLOC(3);
  3756.     ppPixels[0] = pPixels;
  3757.     pPixels[0] = 1;
  3758.     pPixels[1] = 2;
  3759.     pPixels[2] = 3;
  3760.     XP_TRACE(("EDT_StartEncoder: starting\n"));
  3761.     XP_Bool bItem = EDT_StartEncoder(m_pBuffer->m_pContext, 0, 1, 1, ppPixels,
  3762.         "C:\\junk.txt", edt_TestImageEncoderCallbackFn, (void*) 0xfeedface);
  3763.     XP_TRACE(("EDT_StartEncoder: returned %d\n", bItem));
  3764.     XP_FREE(pPixels);
  3765.     XP_FREE(ppPixels);
  3766. }
  3767.  
  3768. #ifndef XP_OS2
  3769. static 
  3770. #else
  3771. extern "OPTLINK"
  3772. #endif
  3773. void edt_TestPreOpenCallbackFn(XP_Bool bUserCancled, char* pURL, void* hook) {
  3774.     XP_TRACE(("edt_TestPreOpenCallbackFn bUserCancled: %s url: %s hook: %08x", bUserCancled ? "TRUE" : "FALSE",
  3775.         pURL ? pURL : "null", hook));
  3776. }
  3777.  
  3778. void CEditTestManager::PerformPreOpen(){
  3779.     XP_TRACE(("Calling preOpen."));
  3780.     EDT_PreOpen(m_pBuffer->m_pContext,"http://PerformPreOpen",
  3781.         edt_TestPreOpenCallbackFn, (void*) 13);
  3782. }
  3783.  
  3784. #endif // EDITOR_JAVA
  3785.  
  3786. void CEditTestManager::TestHTMLPaste(){
  3787.     EDT_PasteQuoteBegin(m_pBuffer->m_pContext, TRUE);
  3788.     EDT_PasteQuote(m_pBuffer->m_pContext, "<b>This is bold.</b>");
  3789.     EDT_PasteQuote(m_pBuffer->m_pContext, "<i>This is italic.</i>");
  3790.     EDT_PasteQuoteEnd(m_pBuffer->m_pContext);
  3791. }
  3792.  
  3793. void CEditTestManager::SaveToTempFile() {
  3794.   EDT_SaveToTempFile(m_pBuffer->m_pContext,(EDT_SaveToTempCallbackFn) SaveToTempFileCB,(void *)m_pBuffer->m_pContext);
  3795. }
  3796.  
  3797. void CEditTestManager::SaveToTempFileCB(char *pFileURL,void *hook) {
  3798.   XP_ASSERT(hook);
  3799.   MWContext *pContext = (MWContext *)hook;
  3800.   GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  3801.  
  3802.   CEditTestManager *pManager = pEditBuffer->m_pTestManager;
  3803.   if (!pManager) {
  3804.     XP_ASSERT(0);
  3805.     return;
  3806.   }
  3807.   XP_FREEIF(pManager->m_pTempFileURL);
  3808.   if (pFileURL) {
  3809.     pManager->m_pTempFileURL = XP_STRDUP(pFileURL);
  3810.     XP_TRACE(("Wrote to temp file %s",pFileURL));
  3811.   }
  3812.   else 
  3813.   {
  3814.     XP_TRACE(("Failed to write temp file."));
  3815.   }
  3816. }
  3817.  
  3818. void CEditTestManager::RemoveTempFile() {
  3819.   if (m_pTempFileURL) {
  3820.     XP_TRACE(("Removing file %s",m_pTempFileURL));
  3821.     EDT_RemoveTempFile(m_pBuffer->m_pContext,m_pTempFileURL);
  3822.     XP_FREEIF(m_pTempFileURL);
  3823.   }
  3824.   else {
  3825.     XP_TRACE(("No temp file has been saved"));
  3826.   }
  3827. }
  3828.  
  3829. void CEditTestManager::GetDocTempDir() {
  3830.   CEditCommandLog *pLog = CGlobalHistoryGroup::GetGlobalHistoryGroup()->GetLog(m_pBuffer);
  3831.   if (!pLog) {
  3832.     return;
  3833.   }
  3834.  
  3835.   char *pFile = pLog->GetDocTempDir();
  3836.   if (pFile) {
  3837.     XP_TRACE(("DocTempDir is %s",pFile));
  3838.     XP_FREE(pFile);
  3839.   }
  3840.   else {
  3841.     XP_TRACE(("No document temp dir was created."));
  3842.   }
  3843. }
  3844. #endif // DEBUG
  3845.  
  3846. //-----------------------------------------------------------------------------
  3847. // CParseState / CPrintState
  3848. //-----------------------------------------------------------------------------
  3849. #define PRE_BODY 0
  3850. #define IN_BODY 1
  3851. #define POST_BODY 2
  3852.  
  3853. CParseState::CParseState()
  3854.     :m_bInTitle(FALSE),
  3855.     m_iDocPart(0),
  3856.     m_inJavaScript(0),
  3857.     m_baseFontSize(0),
  3858.     m_pNextText(0),
  3859.     m_pJavaScript(0),
  3860.     m_pPostBody(0){
  3861. }
  3862.  
  3863. XP_Bool CParseState::InBody(){
  3864.     return m_iDocPart == IN_BODY;
  3865. }
  3866.  
  3867. void CParseState::StartBody(){
  3868.     m_iDocPart = IN_BODY;
  3869. }
  3870.  
  3871. void CParseState::EndBody(){
  3872.     m_iDocPart = POST_BODY;
  3873. }
  3874.  
  3875. CStreamOutMemory* CParseState::GetStream(){
  3876.     if ( m_iDocPart < POST_BODY ) {
  3877.         // m_pJavaScript is mis-named. It's actually
  3878.         // a bag we hold all our unprocessed head tags in.
  3879.         if( m_pJavaScript == 0 ){
  3880.             m_pJavaScript = new CStreamOutMemory();
  3881.         }
  3882.         return m_pJavaScript;
  3883.     }
  3884.     else {
  3885.         if( m_pPostBody == 0 ){
  3886.             m_pPostBody = new CStreamOutMemory();
  3887.         }
  3888.         return m_pPostBody;
  3889.     }
  3890. }
  3891.  
  3892. CParseState::~CParseState() {
  3893.     Free(m_pJavaScript);
  3894.     Free(m_pPostBody);
  3895.     delete m_pNextText;
  3896. }
  3897.  
  3898. void CParseState::Free(CStreamOutMemory*& pStream){
  3899.     if ( pStream ) {
  3900.         // COutMemoryStreams don't delete their text.
  3901.         XP_HUGE_CHAR_PTR pData = pStream->GetText();
  3902.         if ( pData ) {
  3903.             XP_HUGE_FREE(pData);
  3904.         }
  3905.         delete pStream;
  3906.         pStream = NULL;
  3907.     }
  3908. }
  3909.  
  3910. void CParseState::Reset(){
  3911.     bLastWasSpace = TRUE;               // at the beginning of the document
  3912.                                         //  we should ignore leading spaces
  3913.     m_baseFontSize = 3;
  3914.     m_formatTypeStack.Reset();
  3915.     m_formatTextStack.Reset();
  3916.     m_iDocPart = 0;
  3917.     m_bInTitle = FALSE;
  3918.     m_inJavaScript = 0;
  3919.     m_pJavaScript = 0;
  3920.     delete m_pNextText;
  3921.     // force the compiler to use the non-stream constructor
  3922.     m_pNextText = new CEditTextElement((CEditElement*)0,0);
  3923. }
  3924.  
  3925. //-----------------------------------------------------------------------------
  3926. //  CPrintState
  3927. //-----------------------------------------------------------------------------
  3928. void CPrintState::Reset( IStreamOut* pOut, CEditBuffer *pBuffer ){
  3929.     m_pOut = pOut;
  3930.     m_iCharPos = 0;
  3931.     m_bTextLast = FALSE;
  3932.     m_iLevel = 0;
  3933.     m_pBuffer = pBuffer;
  3934.     m_bEncodeSelectionAsComment = FALSE;
  3935. }
  3936.  
  3937. void CPrintState::PrintSelectionComment(XP_Bool bEnd, XP_Bool bStickyAfter){
  3938.     if ( !bEnd ) {
  3939.         m_pBuffer->PasteHTMLHook(this);
  3940.     }
  3941.     m_pOut->Printf("<!-- %s%s -->", bEnd ? EDT_SELECTION_END_COMMENT : EDT_SELECTION_START_COMMENT,
  3942.         bStickyAfter ? "+" : "");
  3943. }
  3944.  
  3945. XP_Bool CPrintState::ShouldPrintSelectionComments(CEditLeafElement* pElement){
  3946.     return m_bEncodeSelectionAsComment && (
  3947.         ShouldPrintSelectionComment(pElement, FALSE)
  3948.             || ShouldPrintSelectionComment(pElement, TRUE));
  3949. }
  3950.  
  3951. XP_Bool CPrintState::ShouldPrintSelectionComment(CEditLeafElement* pElement, XP_Bool bEnd){
  3952.     return m_bEncodeSelectionAsComment && m_selection.GetEdge(bEnd)->m_pElement == pElement;
  3953. }
  3954.  
  3955. //-----------------------------------------------------------------------------
  3956. // CEditLinkManager
  3957. //-----------------------------------------------------------------------------
  3958. EDT_HREFData* ED_Link::GetData(){
  3959.     EDT_HREFData* pData = EDT_NewHREFData();
  3960.     pData->pURL = XP_STRDUP( hrefStr );
  3961.     if( pExtra ){
  3962.         pData->pExtra = XP_STRDUP( pExtra );
  3963.     }
  3964.     else {
  3965.         pData->pExtra = 0;
  3966.     }
  3967.     return pData;
  3968. }
  3969.  
  3970. CEditLinkManager::CEditLinkManager(){
  3971. }
  3972.  
  3973. ED_Link* CEditLinkManager::MakeLink( char *pHREF, char *pExtra, intn iRefCount ){
  3974.  
  3975.     ED_Link *pNewLink = XP_NEW( ED_Link );
  3976.     pNewLink->iRefCount = iRefCount;
  3977.     pNewLink->pLinkManager = this;
  3978.     pNewLink->hrefStr = XP_STRDUP( pHREF );
  3979.     pNewLink->bAdjusted = FALSE;
  3980.     if( pExtra ){
  3981.         pNewLink->pExtra = XP_STRDUP( pExtra );
  3982.     }
  3983.     else {
  3984.         pNewLink->pExtra = 0;
  3985.     }
  3986.     return pNewLink;
  3987. }
  3988.  
  3989. void CEditLinkManager::AdjustAllLinks( char *pOldURL, char* pNewURL, ED_HREFList *badLinks ){
  3990.     int i;
  3991.     for (i = 0; i < m_links.Size(); i++ ){
  3992.         ED_Link *pLink;
  3993.         if( (pLink = m_links[i]) != 0 ){
  3994.             AdjustLink(&pLink->hrefStr,pOldURL,pNewURL,badLinks);
  3995.         }
  3996.     }
  3997. }
  3998.  
  3999. // Used for adjusting links, images, etc.
  4000. // pNewBase can be NULL, the other args shouldn't be.
  4001. //
  4002. // Change  *ppURL so that the URL will still point to the same location
  4003. // after changing the base URL.
  4004. // If there is no way to adjust ppURL, ppURL will not change and we add it to badLinks.
  4005. // E.g. if ppURL relative to pOldBase points to a local file and
  4006. // pNewBase is a remote site, we can't adjust the URL.
  4007. void CEditLinkManager::AdjustLink(char **ppURL,char *pOldBase, char *pNewBase, ED_HREFList *badLinks){
  4008.  
  4009.   if (!ppURL || !*ppURL) {
  4010.     return;
  4011.   }
  4012.  
  4013.   if ((*ppURL)[0] == '#' || (*ppURL)[0] == '`') {
  4014.     // do nothing, but not an error.
  4015.     return;
  4016.   }
  4017.  
  4018.   // Will make url
  4019.   // absolute if the source and dest are on the same machine, just different
  4020.   // drives.  If on the same drive, will make a relative URL.
  4021.   // If on different drives or different machine, leave URL alone, return FALSE.
  4022.  
  4023.   // Make absolute relative to source URL
  4024.   char *pAbsURL = NET_MakeAbsoluteURL( pOldBase, *ppURL );
  4025.   if (!pAbsURL) {
  4026.     // Add to list of bad links.
  4027.     AddHREFUnique(badLinks,*ppURL);
  4028.     return;
  4029.   }
  4030.   char *pNewURL = NULL;
  4031.  
  4032.   if( pNewBase ){
  4033.     // Try to make relative to dest URL.
  4034.     int result = NET_MakeRelativeURL( pNewBase, pAbsURL, &pNewURL );
  4035.  
  4036.     // If we can't make it relative.
  4037.     if (result != NET_URL_SAME_DIRECTORY &&
  4038.         result != NET_URL_SAME_DEVICE) {
  4039.  
  4040.       if (NET_URL_Type(pAbsURL) == FILE_TYPE_URL &&
  4041.           NET_URL_Type(pNewBase) != FILE_TYPE_URL) {
  4042.         // No point in adjusting URL or making relative, local file is not
  4043.         // visible to a remote machine.  So don't change anything.
  4044.         XP_FREEIF(pAbsURL);
  4045.         XP_FREEIF(pNewURL);
  4046.   
  4047.         // Add to list of bad links.
  4048.         AddHREFUnique(badLinks,*ppURL);
  4049.         return;
  4050.       }
  4051.       // else pNewURL will be a copy of pAbsURL, so just continue.
  4052.       // I.e. we have made the URL absolute.
  4053.     }
  4054.     // result is same directory or same device.
  4055.     else if (result == NET_URL_SAME_DEVICE && (*ppURL)[0] == '/') {
  4056.       // Absolute pathing.  Don't force it to be relative.  It'll work in the 
  4057.       // new location.
  4058.       XP_FREEIF(pAbsURL);
  4059.       XP_FREEIF(pNewURL);
  4060.       return;
  4061.     }
  4062.     // More aggressive absolute pathing.  If the new relative url goes all the
  4063.     // way up to the root, just make it start from the root instead of having a 
  4064.     // bunch of ../../../   Bug 50500.
  4065.     // E.g. pAbsURL  = http://host/images/bob.gif,
  4066.     //      pNewBase = http://host/dir1/dir2/dir3/doc.html
  4067.     // instead of setting pNewURL to 
  4068.     //                 ../../../images/bob.gif, use
  4069.     //                 /images/bob.gif
  4070.     else if (result == NET_URL_SAME_DEVICE) {
  4071.       // Count sets of ../ at the beginning of pNewURL.
  4072.       int dotDotCount = 0;
  4073.       char *pSeek = pNewURL;
  4074.       while (pSeek && *pSeek && !XP_STRNCMP(pSeek,"../",3)) {
  4075.         dotDotCount++;
  4076.         pSeek +=3;
  4077.       }
  4078.  
  4079.       char *basePath = NET_ParseURL(pNewBase,GET_PATH_PART);
  4080.       if (!basePath) {
  4081.         XP_ASSERT(0);
  4082.         return; 
  4083.       }
  4084.  
  4085.       /* Find location of drive separator */
  4086.       char *basePtr = (char*) XP_STRCHR(basePath, '|');
  4087.       if (!basePtr) {
  4088.         basePtr = basePath;
  4089.         // basePtr now points to first '/' in pNewBase before directories
  4090.  
  4091.         // Count number of subdirectories in pNewBase.
  4092.         int subDirCount = -1; // Don't count first slash.
  4093.         while (basePtr && *basePtr) {
  4094.           if (*basePtr == '/') {
  4095.             subDirCount++;
  4096.           }
  4097.           basePtr++;
  4098.         }
  4099.         XP_FREE(basePath);
  4100.  
  4101.         if (dotDotCount >= subDirCount && dotDotCount > 0) {
  4102.           if (dotDotCount > subDirCount) {
  4103.             // Error, Net_MakeRelativeURL added too many ../
  4104.             XP_ASSERT(0);
  4105.           }
  4106.  
  4107.           // pSeek is pointing to first char after all the ../ 
  4108.           char *pTemp = XP_STRDUP(pSeek - 1); // Move back to get the slash.
  4109.           XP_FREE(pNewURL);
  4110.           pNewURL = pTemp;
  4111.         }
  4112.       }
  4113.       else {
  4114.       // there is a drive separator, so this trick won't work.  
  4115.       // use pNewURL as is.
  4116.         XP_FREE(basePath);
  4117.       }
  4118.     }
  4119.   } else {
  4120.     // No new base is supplied - so we just return absolute URL
  4121.     XP_FREEIF(*ppURL);
  4122.     *ppURL = pAbsURL;
  4123.     return;
  4124.   }
  4125.   
  4126.   // else pNewURL is a correct relative URL.  Or an absolute URL if result == NET_URL_FAIL.
  4127.   
  4128.   XP_FREEIF( pAbsURL );
  4129.   XP_FREEIF(*ppURL);
  4130.   *ppURL = pNewURL;
  4131. }
  4132.  
  4133. ED_LinkId CEditLinkManager::Add( char *pHREF, char *pExtra ){
  4134.     int i = 0;
  4135.     int lastFree = -1;
  4136.     while( i < m_links.Size() ){
  4137.         ED_Link* pLink = m_links[i];
  4138.         if( pLink != 0 ){
  4139.             // need a more intelegent way of comparing HREFs
  4140.             if( XP_STRCMP( pLink->hrefStr, pHREF) == 0 ) {
  4141.                 // Links the same. How about pExtra ?
  4142.                 if ( (!!pExtra == !!pLink->pExtra)
  4143.                     && ((pExtra &&
  4144.                             XP_STRCMP( pExtra, pLink->pExtra ) == 0 )
  4145.                        || pExtra == 0 ) ){
  4146.                     pLink->iRefCount++;
  4147.                     return pLink;
  4148.                 }
  4149.             }
  4150.         }
  4151.         else {
  4152.             lastFree = i;
  4153.         }
  4154.         i++;
  4155.     }
  4156.  
  4157.     ED_Link *pNewLink = MakeLink( pHREF, pExtra );
  4158.  
  4159.     //
  4160.     // Store it.
  4161.     //
  4162.     if( lastFree != -1 ){
  4163.         m_links[lastFree] = pNewLink;
  4164.     }
  4165.     else {
  4166.         lastFree = m_links.Add( pNewLink );
  4167.     }
  4168.     pNewLink->linkOffset = lastFree;
  4169.     return pNewLink;
  4170. }
  4171.  
  4172. void CEditLinkManager::Free(ED_LinkId id){
  4173.     if( --id->iRefCount == 0 ){
  4174.         m_links[ id->linkOffset ] = 0;
  4175.         XP_FREEIF( id->pExtra );
  4176.         XP_FREEIF( id->hrefStr );
  4177.         XP_FREE( id );
  4178.     }
  4179. }
  4180.  
  4181. void CEditLinkManager::AddHREFUnique(ED_HREFList *badLinks,char *pURL) {
  4182.   for (int n = 0; n < badLinks->Size(); n++) {
  4183.     // Just comparing links for equality by string comparison.
  4184.     if (!XP_STRCMP((*badLinks)[n],pURL)) {
  4185.       // Already in list.
  4186.       return;
  4187.     }
  4188.   }
  4189.   badLinks->Add(XP_STRDUP(pURL));
  4190. }
  4191.  
  4192. XP_Bool EDT_SelectTableElement(MWContext  *pMWContext, int32 x, int32 y, 
  4193.                                LO_Element *pLoElement, 
  4194.                                ED_HitType  iHitType, 
  4195.                                XP_Bool     bModifierKeyPressed,
  4196.                                XP_Bool     bExtendSelection)
  4197. {
  4198.     GET_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer) 0;
  4199.     return pEditBuffer->SelectTableElement(x, y, pLoElement, iHitType, 
  4200.                                            bModifierKeyPressed, bExtendSelection);
  4201. }
  4202.  
  4203. ED_HitType EDT_ExtendTableCellSelection(MWContext *pMWContext, int32 x, int32 y)
  4204. {
  4205.     GET_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer) ED_HIT_NONE;
  4206.     return pEditBuffer->ExtendTableCellSelection(x, y);
  4207. }
  4208.  
  4209. // Dynamic Sizing Object
  4210.  
  4211. CSizingObject::CSizingObject() :
  4212.         m_pBuffer(0),
  4213.         m_pLoElement(0),
  4214.         m_iStyle(0),
  4215.         m_iXOrigin(0),
  4216.         m_iYOrigin(0),
  4217.         m_iStartWidth(0),
  4218.         m_iStartHeight(0),
  4219.         m_iViewWidth(0),
  4220.         m_iViewHeight(0),
  4221.         m_bWidthPercent(0),
  4222.         m_bHeightPercent(0),
  4223.         m_bPercentOriginal(0),
  4224.         m_iAddCols(0),
  4225.         m_iAddRows(0)
  4226. {
  4227. }
  4228.  
  4229. XP_Bool CSizingObject::Create(CEditBuffer *pBuffer,
  4230.                               LO_Element *pLoElement,
  4231.                               int iSizingStyle,
  4232.                               int32 xVal, int32 yVal,
  4233.                               XP_Bool /*bLockAspect*/, XP_Rect *pRect){
  4234.     XP_ASSERT(pBuffer);
  4235.     XP_ASSERT(pRect);
  4236.     XP_ASSERT(iSizingStyle);
  4237.     m_pBuffer = pBuffer;
  4238.     m_pLoElement = pLoElement;
  4239.     m_iStyle = iSizingStyle;
  4240.  
  4241.     if( !pLoElement )
  4242.     {
  4243. #ifdef LAYERS
  4244.         pLoElement = LO_XYToElement(pBuffer->m_pContext, xVal, yVal, 0);
  4245. #else
  4246.         pLoElement = LO_XYToElement(pBuffer->m_pContext, xVal, yVal);
  4247. #endif
  4248.     }
  4249.     if( !pLoElement )
  4250.     {
  4251.         return FALSE;
  4252.     }
  4253.  
  4254.     // Get view window size for "100%" sizing
  4255.     FE_GetDocAndWindowPosition(pBuffer->m_pContext, &m_iXOrigin, &m_iYOrigin, &m_iViewWidth, &m_iViewHeight);
  4256.     if( !m_iViewWidth || !m_iViewHeight )
  4257.     {
  4258.         return FALSE;
  4259.     }
  4260.     // Get extra margin
  4261.     int32 iMarginWidth;
  4262.     int32 iMarginHeight;
  4263.     LO_GetDocumentMargins(pBuffer->m_pContext, &iMarginWidth, &iMarginHeight);
  4264.     m_iViewWidth -= 2 * iMarginWidth;
  4265.     m_iViewHeight -= 2 * iMarginHeight;
  4266.     LO_Any *pAny = &(pLoElement->lo_any);
  4267.     int32 iSelectX = pAny->x + pAny->x_offset;
  4268.     int32 iSelectY = pAny->y + pAny->y_offset;
  4269.  
  4270.     m_iParentWidth = 0;
  4271.     // Check for sizing of table cell or nested table
  4272.     if( pAny->type == LO_TABLE )
  4273.     {
  4274.         // Check for possible nested table by finding if it has a parent cell
  4275.         LO_CellStruct *pParentCell = lo_GetParentCell(m_pBuffer->m_pContext, pLoElement); 
  4276.         if( pParentCell )
  4277.         {
  4278.             // Width = that of cell containing table
  4279.             m_iParentWidth = pParentCell->width;
  4280.             m_iWidthMsgID = XP_EDT_PERCENT_CELL;
  4281.         }
  4282.         iSelectX = pAny->x;
  4283.         iSelectY = pAny->y;
  4284.     } 
  4285.     else if( pAny->type == LO_CELL )
  4286.     {
  4287.         // 
  4288.         LO_TableStruct *pParentTable = lo_GetParentTable(m_pBuffer->m_pContext, pLoElement);
  4289.         if( pParentTable )
  4290.         {
  4291.             // Get the width usable for percent calculation (minus borders and cell spacing)
  4292.             m_iParentWidth = lo_CalcTableWidthForPercentMode(pLoElement);
  4293.             m_iWidthMsgID = XP_EDT_PERCENT_TABLE;
  4294.         }
  4295.         iSelectX = pAny->x;
  4296.         iSelectY = pAny->y;
  4297.     } else {
  4298.         // If not a table or cell, 
  4299.         //   select the item of interest to make it the current element
  4300.         // Select at its CENTER to avoid setting caret
  4301.         //   if real X,Y is near the left or right edge
  4302.         iSelectX += pAny->width/2;
  4303.         iSelectY += pAny->height/2;
  4304.     }
  4305.     
  4306.     if( m_iParentWidth == 0 )
  4307.     {
  4308.         m_iParentWidth = m_iViewWidth;
  4309.         m_iWidthMsgID = XP_EDT_PERCENT_WINDOW;
  4310.     }
  4311.     
  4312.     //m_pBuffer->MoveAndHideCaretInTable(pLoElement);
  4313.  
  4314.     // Calculate the rect in View's coordinates;
  4315.     // ASSUMES PIXELS ONLY -- NO CONVERSION DONE
  4316.     m_Rect.left = pAny->x + pAny->x_offset - m_iXOrigin;
  4317.     m_Rect.top = pAny->y + pAny->y_offset - m_iYOrigin;
  4318.  
  4319.     // Recent default value of flag used
  4320.     //  for image status string, e.g.: (% of original width)
  4321.     m_bPercentOriginal = FALSE;
  4322.  
  4323.     // Get percent-mode flag for edit element and adjust table/cell data
  4324.     switch ( m_pLoElement->type )
  4325.     {
  4326.         case LO_IMAGE:
  4327.         {
  4328.             // Get the edit element
  4329.             CEditElement * pElement = m_pLoElement->lo_any.edit_element;
  4330.             if( !pElement ) return FALSE;
  4331.  
  4332.             if( pElement->IsIcon() )
  4333.             {
  4334.                 // All "unknown" tags are handled here
  4335.                 pElement->GetWidth(&m_bWidthPercent, &m_iStartWidth);
  4336.                 pElement->GetHeight(&m_bHeightPercent, &m_iStartHeight);
  4337.             } else if( pElement->IsImage() )
  4338.             {
  4339.                 EDT_ImageData * pImageData = pElement->Image()->GetImageData();
  4340.                 //pBuffer->GetImageData();
  4341.                 if( pImageData )
  4342.                 {
  4343.                     m_bWidthPercent = pImageData->bWidthPercent;
  4344.                     m_bHeightPercent = pImageData->bHeightPercent;
  4345.  
  4346.                     // Save the original height and width of the image
  4347.                     //  if we have it
  4348.                     if( pImageData->iOriginalWidth )
  4349.                     {
  4350.                         m_iStartWidth = pImageData->iOriginalWidth;
  4351.                         m_iStartHeight = pImageData->iOriginalHeight;
  4352.                         // Constrain relative to "original" dimensions
  4353.                         m_bPercentOriginal = TRUE;
  4354.                     }
  4355.                     EDT_FreeImageData(pImageData);
  4356.                 }
  4357.                 // LO_ImageStruct includes border -- adjust
  4358.                 m_Rect.left += ((LO_ImageStruct*)pAny)->border_width;
  4359.                 m_Rect.top += ((LO_ImageStruct*)pAny)->border_width;
  4360.             }
  4361.             break;
  4362.         }
  4363.         case LO_HRULE: 
  4364.         {
  4365.             // Get the edit element
  4366.             CEditElement * pElement = m_pLoElement->lo_any.edit_element;
  4367.             if( !pElement ) return FALSE;
  4368.  
  4369.             EDT_HorizRuleData * pHData = pElement->HorizRule()->GetData(); //pBuffer->GetHorizRuleData();
  4370.             if( pHData )
  4371.             {
  4372.                 m_bWidthPercent = pHData->bWidthPercent;
  4373.                 //Note: we will get width and "size" (height) in pixels below
  4374.                 m_iStartHeight = pHData->size;
  4375.                 EDT_FreeHorizRuleData(pHData);
  4376.             }
  4377.             break;
  4378.         }
  4379.         case LO_TABLE:
  4380.         {
  4381.             CEditTableElement *pTable = 
  4382.                 (CEditTableElement*)m_pBuffer->GetTableElementFromLO_Element( m_pLoElement, LO_TABLE );
  4383.             if( pTable )
  4384.             {
  4385.                 EDT_TableData *pTableData = pTable->GetData();
  4386.                 if( pTableData )
  4387.                 {
  4388.                     switch( iSizingStyle )
  4389.                     {
  4390.                         case ED_SIZE_RIGHT:
  4391.                             // Use width determined by layout
  4392.                             m_bWidthPercent = pTableData->bWidthPercent;
  4393.                             if( m_bWidthPercent )
  4394.                             {
  4395.                                 m_iStartWidth = (pAny->width * 100) / m_iParentWidth;
  4396.                             } else {
  4397.                                 m_iStartWidth = pAny->width;
  4398.                             }
  4399.                             break;
  4400.                         case ED_SIZE_BOTTOM:
  4401.                             // Use Height determined by layout
  4402.                             m_bHeightPercent = pTableData->bHeightPercent;
  4403.                             if( m_bHeightPercent )
  4404.                             {
  4405.                                 m_iStartHeight = (pAny->height * 100) / m_iViewHeight;
  4406.                             } else {
  4407.                                 m_iStartHeight = pAny->height;
  4408.                             }
  4409.                             break;
  4410.                         case ED_SIZE_ADD_COLS:
  4411.                             // When adding columns, show selection only
  4412.                             //  around "new" columns, so rect.left = table->right
  4413.                             m_Rect.left += pAny->width;
  4414.                             m_Rect.right = m_Rect.left + 1;
  4415.                             m_Rect.bottom = m_Rect.top + pAny->height;
  4416.                             m_iStartHeight = pAny->height;
  4417.                             break;
  4418.  
  4419.                         case ED_SIZE_ADD_ROWS:
  4420.                             // As with columns, rect is around "new" rows
  4421.                             m_Rect.top += pAny->height;
  4422.                             m_Rect.bottom = m_Rect.top + 1;
  4423.                             m_Rect.right = m_Rect.left + pAny->width;
  4424.                             m_iStartWidth = pAny->width;
  4425.                             break;
  4426.                     }
  4427.                     EDT_FreeTableData(pTableData);
  4428.                 }
  4429.             }
  4430.             break;
  4431.         }
  4432.         case LO_CELL:
  4433.         {
  4434.             CEditTableCellElement *pCell = 
  4435.                 (CEditTableCellElement*)m_pBuffer->GetTableElementFromLO_Element( m_pLoElement, LO_CELL );
  4436.             if( pCell )
  4437.             {
  4438.                 EDT_TableCellData * pCellData = pCell->GetData(0);
  4439.                 if( pCellData )
  4440.                 {
  4441.                     m_bWidthPercent = pCellData->bWidthPercent;
  4442.                     m_bHeightPercent = pCellData->bHeightPercent;
  4443.                     EDT_FreeTableCellData(pCellData);
  4444.  
  4445.                     if( iSizingStyle == ED_SIZE_RIGHT )
  4446.                     {
  4447.                         if( m_bWidthPercent )
  4448.                         {
  4449.                             m_iStartWidth = (pAny->width * 100) / m_iParentWidth;
  4450.                         } else {
  4451.                             // Use width determined by layout
  4452.                             m_iStartWidth = pAny->width; // pCellData->iWidth;
  4453.                         }
  4454.                     } 
  4455.                     else 
  4456.                     {
  4457.                         if( m_bHeightPercent )
  4458.                         {
  4459.                             m_iStartHeight = (pAny->height * 100) / m_iViewHeight;
  4460.                         } else {
  4461.                             m_iStartHeight = pAny->height;
  4462.                         }
  4463.                     }                
  4464.  
  4465.                     // We will select the entire row or column, so get size of table
  4466.                     LO_TableStruct * pLoTable = lo_GetParentTable(pBuffer->m_pContext, m_pLoElement);
  4467.                     if( pLoTable )
  4468.                     {
  4469.                         int32 iAdjust = min(pLoTable->border_width, 4 - pLoTable->inter_cell_space);
  4470.                         if( iSizingStyle == ED_SIZE_RIGHT )
  4471.                         {
  4472.                             // Prefered top and bottom is just inside table border
  4473.                             m_Rect.top = pLoTable->y + pLoTable->y_offset - m_iYOrigin + pLoTable->border_width;
  4474.                             m_Rect.bottom = m_Rect.top + pLoTable->height - 2*pLoTable->border_width;
  4475.                     
  4476.                             // Back off top and bottom if intercell space is too small
  4477.                             if( pLoTable->inter_cell_space < 4 && iAdjust > 0 )
  4478.                             {
  4479.                                 m_Rect.top -= iAdjust;
  4480.                                 m_Rect.bottom += iAdjust;
  4481.                             }
  4482.                             m_Rect.right = m_Rect.left + pAny->width;
  4483.                         } 
  4484.                         else 
  4485.                         {
  4486.                             // Prefered left and right is just inside table border
  4487.                             m_Rect.left = pLoTable->x + pLoTable->x_offset - m_iXOrigin + pLoTable->border_width;
  4488.                             m_Rect.right = m_Rect.left + pLoTable->width - 2*pLoTable->border_width;
  4489.                     
  4490.                             // Back off if intercell space is too small
  4491.                             if( pLoTable->inter_cell_space < 4 && iAdjust > 0 )
  4492.                             {
  4493.                                 m_Rect.left -= iAdjust;
  4494.                                 m_Rect.right += iAdjust;
  4495.                             }
  4496.                             m_Rect.bottom = m_Rect.top + pAny->height;
  4497.                         }
  4498.                     }
  4499.                 }
  4500.             }
  4501.             break;
  4502.         }
  4503.     }
  4504.  
  4505.     XP_TRACE(("Start Sizing: m_iStartWidth = %d", m_iStartWidth));
  4506.  
  4507.     if( !m_bWidthPercent )
  4508.     {
  4509.         // We aren't doing percent, so set message ID for pixels
  4510.         m_iWidthMsgID = XP_EDT_PIXELS;
  4511.     }
  4512.  
  4513.     // Finish calculating object rect if not set for table sizing above
  4514.     if( iSizingStyle != ED_SIZE_ADD_COLS && iSizingStyle != ED_SIZE_ADD_ROWS &&
  4515.         m_pLoElement->type != LO_CELL )
  4516.     {
  4517.         m_Rect.right = m_Rect.left + pAny->width;
  4518.         m_Rect.bottom = m_Rect.top + pAny->height;
  4519.     }
  4520.     *pRect = m_Rect;
  4521.  
  4522.     // If we didn't set above, use current size or minimum of 1
  4523.     if( m_iStartWidth == 0 )
  4524.     {
  4525.         m_iStartWidth = max(1, pAny->width);
  4526.     }
  4527.     if( m_iStartHeight == 0 )
  4528.     {
  4529.         m_iStartHeight = max(1, pAny->height);
  4530.     }
  4531.     return TRUE;
  4532. }
  4533.  
  4534. #define  EDT_NEW_ROW_HEIGHT  24
  4535. #define  EDT_NEW_COL_WIDTH   24
  4536.  
  4537. PRIVATE
  4538. void CalcAddRowRect(int32 iRows, XP_Rect *pTableRect, XP_Rect *pRect)
  4539. {
  4540.     pRect->left = pTableRect->left + 6;
  4541.     pRect->right = pTableRect->right - 6;
  4542.     pRect->top = pTableRect->top + (iRows * EDT_NEW_ROW_HEIGHT) + 2;
  4543.     pRect->bottom = pRect->top;
  4544. }
  4545.  
  4546. PRIVATE
  4547. void CalcAddColRect(int32 iCols, XP_Rect *pTableRect, XP_Rect *pRect)
  4548. {
  4549.     pRect->top = pTableRect->top + 6;
  4550.     pRect->bottom = pTableRect->bottom - 6;
  4551.     pRect->left = pTableRect->left + (iCols * EDT_NEW_COL_WIDTH) + 2;
  4552.     pRect->right = pRect->left;
  4553. }
  4554.  
  4555. // Corner must be > any single side value
  4556. #define  EDT_IS_SIZING_CORNER(style)   (style == ED_SIZE_TOP_LEFT || \
  4557.                                         style == ED_SIZE_TOP_RIGHT || \
  4558.                                         style == ED_SIZE_BOTTOM_LEFT || \
  4559.                                         style == ED_SIZE_BOTTOM_RIGHT )
  4560.  
  4561. XP_Bool CSizingObject::GetSizingRect(int32 xVal, int32 yVal, XP_Bool bLockAspect, XP_Rect *pRect)
  4562. {
  4563.     XP_ASSERT(pRect);
  4564.     int i;
  4565.     XP_Bool bLockAspectRatio = EDT_IS_SIZING_CORNER(m_iStyle) && bLockAspect;
  4566.  
  4567.     int32 iViewX = xVal - m_iXOrigin;
  4568.     int32 iViewY = yVal - m_iYOrigin;
  4569.  
  4570.     // Calculate the new rectangle
  4571.     // And don't allow dragging past opposite side:
  4572.     XP_Rect new_rect = m_Rect;
  4573.  
  4574.     if( m_bWidthPercent )
  4575.     {
  4576.         // In % mode, limit largest value to get 100% of width or height
  4577.         // Calc. the pixel value corresponding to just under 101% width or height,
  4578.         //  so roundoff will always allow achieving 100%
  4579.         int32 iFullWidth = ((101 * m_iParentWidth) / 100) - 1;
  4580.         int32 iFullHeight = ((101 * m_iViewHeight) / 100) - 1;
  4581.  
  4582.         if( m_iStyle & ED_SIZE_TOP )
  4583.         {
  4584.             new_rect.top = min(m_Rect.bottom, max(iViewY,m_Rect.bottom-iFullHeight));
  4585.         }
  4586.         if( m_iStyle & ED_SIZE_BOTTOM )
  4587.         {
  4588.             new_rect.bottom = max(m_Rect.top, min(iViewY,m_Rect.top+iFullHeight));
  4589.         }
  4590.         if( m_iStyle & ED_SIZE_LEFT )
  4591.         {
  4592.             new_rect.left = min(m_Rect.right, max(iViewX,m_Rect.right-iFullWidth));
  4593.         }
  4594.         if( m_iStyle & ED_SIZE_RIGHT )
  4595.         {
  4596.             new_rect.right = max(m_Rect.left, iViewX); //(m_pLoElement->type == LO_CELL) ? iViewX : min(iViewX,m_Rect.left+iFullWidth));
  4597.         }
  4598.     } else {
  4599.         if( m_iStyle & ED_SIZE_TOP )
  4600.         {
  4601.             new_rect.top = min(m_Rect.bottom, iViewY);
  4602.         }
  4603.         if( m_iStyle & ED_SIZE_BOTTOM || m_iStyle == ED_SIZE_ADD_ROWS )
  4604.         {
  4605.             new_rect.bottom = max(m_Rect.top, iViewY);
  4606.         }
  4607.         if( m_iStyle & ED_SIZE_LEFT )
  4608.         {
  4609.             new_rect.left = min(m_Rect.right, iViewX);
  4610.         }
  4611.         if( m_iStyle & ED_SIZE_RIGHT  || m_iStyle == ED_SIZE_ADD_COLS )
  4612.         {
  4613.             new_rect.right = max(m_Rect.left, iViewX);
  4614.         }
  4615.     }
  4616.     int iNewWidth = new_rect.right - new_rect.left;
  4617.     int iNewHeight = new_rect.bottom - new_rect.top;
  4618.  
  4619.     // When sizing both width and height,
  4620.     //   fixup coordinates to lock aspect ratio
  4621.     //
  4622.     if(bLockAspectRatio)
  4623.     {
  4624.         int iLockedWidth = (iNewHeight * m_iStartWidth) / m_iStartHeight;
  4625.         int iLockedHeight = (iNewWidth * m_iStartHeight) / m_iStartWidth;
  4626.  
  4627.         if( iNewWidth < iLockedWidth )
  4628.         {
  4629.             // New width is too small to match new height - adjust
  4630.             if( m_iStyle & ED_SIZE_LEFT ){
  4631.                 new_rect.left = new_rect.right - iLockedWidth;
  4632.             } else {
  4633.                 new_rect.right = new_rect.left + iLockedWidth;
  4634.             }
  4635.         } else if( iNewHeight < iLockedHeight )
  4636.         {
  4637.             // New height is too small to match new width
  4638.             if( m_iStyle & ED_SIZE_TOP )
  4639.             {
  4640.                 new_rect.top = new_rect.bottom - iLockedHeight;
  4641.             } else {
  4642.                 new_rect.bottom = new_rect.top + iLockedHeight;
  4643.             }
  4644.         }
  4645.     }
  4646.  
  4647.     // Return new rect to caller
  4648.     *pRect = new_rect;
  4649.  
  4650.     // Check if rect is different from last rect requested
  4651.     if( new_rect.top != m_Rect.top ||
  4652.         new_rect.bottom != m_Rect.bottom ||
  4653.         new_rect.left != m_Rect.left ||
  4654.         new_rect.right != m_Rect.right )
  4655.     {
  4656.         m_Rect = new_rect;
  4657.  
  4658.         // Display status message with data for user:
  4659.         char   pMsg[512] = "";
  4660.         char   pPercentOfWhat[256] = "";
  4661.         int32  iWidth = m_Rect.right - m_Rect.left;
  4662.         int32  iHeight = m_Rect.bottom - m_Rect.top;
  4663.         int32  iPercent;
  4664.  
  4665.         XP_Bool bDoWidth = (m_iStyle & ED_SIZE_LEFT) || (m_iStyle & ED_SIZE_RIGHT);
  4666.         XP_Bool bDoHeight = (m_iStyle & ED_SIZE_TOP) || (m_iStyle & ED_SIZE_BOTTOM);
  4667.  
  4668.         // Show only dimension(s) for whats changing: (Width vs. Height vs both)
  4669.         // First part of status shows: "Width = xx[pixesl|%of window width]"
  4670.         //  and/or similar string for Height.
  4671.         //
  4672.         if( bDoWidth )
  4673.         {
  4674.             if(m_bWidthPercent)
  4675.             {
  4676.                 // Convert to % format
  4677.                 iWidth = (iWidth * 100) / m_iParentWidth;
  4678.             }
  4679.             iPercent = (iWidth * 100 ) / m_iStartWidth;
  4680.  
  4681.             // "Width = x"
  4682.             PR_snprintf(pMsg, 128, XP_GetString(XP_EDT_WIDTH_EQUALS), iWidth);
  4683.             // "pixels" or "% of window"
  4684.  
  4685.             strcat(pMsg, XP_GetString(m_iWidthMsgID));
  4686.  
  4687.             // The % of original [width] and/or [height] string
  4688.             // Current logic assumes constaining aspect ratio when sizing corners,
  4689.             //   so separate Width, Height percentages are not shown
  4690.             XP_STRCPY(pPercentOfWhat, XP_GetString(XP_EDT_WIDTH));
  4691.         }
  4692.         if( bDoHeight )
  4693.         {
  4694.             if(m_bHeightPercent)
  4695.             {
  4696.                 iHeight = (iHeight * 100) / m_iViewHeight;
  4697.             }
  4698.             // Since corners are constrained to aspect ratio,
  4699.             //  just use Width's calculation if already done
  4700.             if( !bDoWidth )
  4701.             {
  4702.                 iPercent = (iHeight * 100 ) / m_iStartHeight;
  4703.             }
  4704.  
  4705.             // "Height = x"
  4706.             PR_snprintf(pMsg+XP_STRLEN(pMsg), 128,
  4707.                         XP_GetString(XP_EDT_HEIGHT_EQUALS), iHeight);
  4708.             // "pixels" or "% of window"
  4709.             strcat(pMsg, XP_GetString(m_bHeightPercent ? (int)XP_EDT_PERCENT_WINDOW :
  4710.                                                          (int)XP_EDT_PIXELS));
  4711.  
  4712.             if( bDoWidth )
  4713.             {
  4714.                 strcat(pPercentOfWhat, XP_GetString(XP_EDT_AND));
  4715.             }
  4716.             strcat(pPercentOfWhat, XP_GetString(XP_EDT_HEIGHT));
  4717.         }
  4718.         if( bDoWidth || bDoHeight )
  4719.         {
  4720.             // Build string to report relative size change:
  4721.             //  (% of [original|previous] [width | height | width and height])
  4722.             PR_snprintf(pMsg+XP_STRLEN(pMsg), 128,
  4723.                         XP_GetString(m_bPercentOriginal ? (int)XP_EDT_PERCENT_ORIGINAL :
  4724.                                                           (int)XP_EDT_PERCENT_PREVIOUS),
  4725.                         iPercent, pPercentOfWhat);
  4726.         } else if( m_iStyle == ED_SIZE_ADD_COLS )
  4727.         {
  4728.             int iAddCols = iNewWidth / EDT_NEW_COL_WIDTH;
  4729.             PR_snprintf(pMsg, 128, XP_GetString(XP_EDT_ADD_COLUMNS), iAddCols);
  4730.             XP_Rect rect;
  4731.  
  4732.             // Erase all existing lines
  4733.             // NOTE: We can't draw/erase just the "new" line because 
  4734.             //   we may skip lines if mouse is moved fast, causing a mess
  4735.             for( i=1; i< m_iAddCols; i++ )
  4736.             {
  4737.                 CalcAddColRect(i, &new_rect, &rect);
  4738.                 FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
  4739.             }
  4740.             // Draw all new lines based on new number of columns
  4741.             for( i=1; i< iAddCols; i++ )
  4742.             {
  4743.                 CalcAddColRect(i, &new_rect, &rect);
  4744.                 FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, FALSE);
  4745.             }
  4746.             m_iAddCols = iAddCols;
  4747.         } else if(  m_iStyle == ED_SIZE_ADD_ROWS )
  4748.         {
  4749.             int iAddRows = iNewHeight / EDT_NEW_ROW_HEIGHT;
  4750.             PR_snprintf(pMsg, 128, XP_GetString(XP_EDT_ADD_ROWS), iAddRows);
  4751.             XP_Rect rect;
  4752.             
  4753.             // Erase all existing lines
  4754.             for( i=1; i< m_iAddRows; i++ )
  4755.             {
  4756.                 CalcAddRowRect(i, &new_rect, &rect);
  4757.                 FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
  4758.             }
  4759.             // Draw all new lines based on new number of columns
  4760.             for( i=1; i< iAddRows; i++ )
  4761.             {
  4762.                 CalcAddRowRect(i, &new_rect, &rect);
  4763.                 FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, FALSE);
  4764.             }
  4765.             m_iAddRows = iAddRows;
  4766.         }
  4767.  
  4768.         FE_Progress(m_pBuffer->m_pContext, pMsg);
  4769.  
  4770.         // Indicate that rect changed
  4771.         return TRUE;
  4772.     }
  4773.     // Rect is same as last time
  4774.     return FALSE;
  4775. }
  4776.  
  4777. void CSizingObject::ResizeObject()
  4778. {
  4779.     // Erase visual feedback when adding rows or columns
  4780.     EraseAddRowsOrCols();
  4781.     int32 iWidth, iWidthPixels, iHeight;
  4782.  
  4783.     // Get the element being sized (except table or cell - obtained below)
  4784.     CEditLeafElement *pElement = (CEditLeafElement*)(m_pLoElement->lo_any.edit_element);
  4785.  
  4786.     if( !(m_iStyle == ED_SIZE_ADD_ROWS || m_iStyle == ED_SIZE_ADD_COLS) )
  4787.     {
  4788.  
  4789.         iWidthPixels = m_Rect.right - m_Rect.left;
  4790.         iHeight = m_Rect.bottom - m_Rect.top;
  4791.  
  4792.         // Convert to percent if that's what we are using
  4793.         // Note that we do not change that mode when returning new size
  4794.         if( m_bWidthPercent )
  4795.         {
  4796.             iWidth = (iWidthPixels * 100) / m_iParentWidth;
  4797.         } else {
  4798.             iWidth = iWidthPixels;        
  4799.         }
  4800.         if( m_bHeightPercent )
  4801.         {
  4802.             iHeight = (iHeight * 100) / m_iViewHeight;
  4803.         }
  4804.     }
  4805.  
  4806.     // Change the appropriate element's width and/or height
  4807.     switch ( m_pLoElement->type )
  4808.     {
  4809.         case LO_IMAGE:
  4810.         {
  4811.             if( pElement && pElement->IsIcon() )
  4812.             {
  4813.                 // TODO: ALL "UNKNOWN" TAGS ARE HANDLED HERE
  4814.                 // Only change dimension if we were sizing it
  4815.                 if( !(m_iStyle & ED_SIZE_LEFT ||
  4816.                       m_iStyle & ED_SIZE_RIGHT) )
  4817.                 {
  4818.                     iWidth = 0; // Signal to not change width...
  4819.                 }
  4820.                 if( !(m_iStyle & ED_SIZE_TOP ||
  4821.                       m_iStyle & ED_SIZE_BOTTOM) )
  4822.                 {
  4823.                     iHeight = 0; // ...or height
  4824.                 }
  4825.                 if( iWidth > 0 || iHeight > 0 )
  4826.                 {
  4827.                     m_pBuffer->BeginBatchChanges(kSetUnknownTagDataCommandID);
  4828.                     pElement->SetSize(m_bWidthPercent, iWidth, m_bHeightPercent, iHeight);
  4829.                     m_pBuffer->EndBatchChanges();
  4830.                 }
  4831.                 m_pBuffer->Relayout(pElement, 0);
  4832.             } else if( pElement && pElement->IsImage() )
  4833.             {
  4834.                 EDT_ImageData * pImageData = pElement->Image()->GetImageData();
  4835.                 if( pImageData )
  4836.                 {
  4837.  
  4838.                     // Only change dimension if we were sizing it
  4839.                     if( m_iStyle & ED_SIZE_TOP ||
  4840.                         m_iStyle & ED_SIZE_BOTTOM )
  4841.                     {
  4842.                         pImageData->iHeight = iHeight;
  4843.                     }
  4844.                     if( m_iStyle & ED_SIZE_LEFT ||
  4845.                         m_iStyle & ED_SIZE_RIGHT )
  4846.                     {
  4847.                         pImageData->iWidth = iWidth;
  4848.                     }
  4849.                     m_pBuffer->BeginBatchChanges(kSetImageDataCommandID);
  4850.                     // We are not changing image URL, so third param
  4851.                     //   (keep-images-with-doc) shouldn't matter
  4852.                     pElement->Image()->SetImageData(pImageData);
  4853.                     edt_FreeImageData(pImageData);
  4854.                     m_pBuffer->Relayout(pElement,0);
  4855.                     m_pBuffer->EndBatchChanges();
  4856.                }
  4857.             }
  4858.             break;
  4859.         }
  4860.         case LO_HRULE:
  4861.         {
  4862.             if( pElement )
  4863.             {
  4864.                 EDT_HorizRuleData * pHData = pElement->HorizRule()->GetData(); //m_pBuffer->GetHorizRuleData();
  4865.                 if( pHData )
  4866.                 {
  4867.                     // Only change dimension if we were sizing it
  4868.                     if( m_iStyle & ED_SIZE_TOP ||
  4869.                         m_iStyle & ED_SIZE_BOTTOM )
  4870.                     {
  4871.                         pHData->size = iHeight;
  4872.                     }
  4873.                     if( m_iStyle & ED_SIZE_LEFT ||
  4874.                         m_iStyle & ED_SIZE_RIGHT )
  4875.                     {
  4876.                         pHData->iWidth = iWidth;
  4877.                     }
  4878.                     m_pBuffer->BeginBatchChanges(kSetHorizRuleDataCommandID);
  4879.                     pElement->HorizRule()->SetData(pHData);
  4880.                     EDT_FreeHorizRuleData(pHData);
  4881.                     m_pBuffer->Relayout(pElement,0);
  4882.                     m_pBuffer->EndBatchChanges();
  4883.                 }
  4884.             }
  4885.             break;
  4886.         }
  4887.         case LO_TABLE:
  4888.         {
  4889.             CEditTableElement *pTable = 
  4890.                 (CEditTableElement*)m_pBuffer->GetTableElementFromLO_Element( m_pLoElement, LO_TABLE );
  4891.             if( pTable )
  4892.             {
  4893.                 if( m_iStyle == ED_SIZE_ADD_ROWS ||
  4894.                     m_iStyle == ED_SIZE_ADD_COLS )
  4895.                 {
  4896.                     // Be sure caret is already in the table,
  4897.                     //  but don't show caret or scroll window
  4898.                     m_pBuffer->MoveAndHideCaretInTable(m_pLoElement);
  4899.  
  4900.                     // Move to last cell of table
  4901.                     // (1ST param unused, 2nd = Forward, 3rd = NextRow index ptr
  4902.                     while(m_pBuffer->NextTableCell(FALSE, TRUE, 0))
  4903.                         ;
  4904.                     // Note: These functions handles Begin/EndBatchChanges
  4905.                     // TODO: CHANGE THIS???
  4906.                     if( m_iStyle == ED_SIZE_ADD_ROWS )
  4907.                         m_pBuffer->InsertTableRows(NULL, TRUE, m_iAddRows);
  4908.                     else
  4909.                         m_pBuffer->InsertTableColumns(NULL, TRUE, m_iAddCols);
  4910.                     
  4911.                     // InsertTable... will relayout
  4912.                     //m_pBuffer->Relayout(pTable, 0);
  4913.                 }
  4914.                 else
  4915.                 {
  4916.                     // Sizing table width
  4917.                     EDT_TableData * pTableData = pTable->GetData();
  4918.                     if( pTableData && iWidth != m_iStartWidth )
  4919.                     {
  4920.                         pTableData->bWidthDefined = TRUE;
  4921.                         pTableData->iWidth = iWidth;
  4922.                         pTableData->iWidthPixels = iWidthPixels;
  4923.                         m_pBuffer->BeginBatchChanges(kSetTableDataCommandID);
  4924.                         pTable->SetData(pTableData);
  4925.                         EDT_FreeTableData(pTableData);
  4926.                         m_pBuffer->Relayout(pTable, 0);
  4927.                         m_pBuffer->EndBatchChanges();
  4928.  
  4929. XP_TRACE(("End Sizing: iWidth = %d", iWidth));
  4930.                     }
  4931.                 }
  4932.             }
  4933.             break;
  4934.         }
  4935.         case LO_CELL:
  4936.         {
  4937.             CEditTableCellElement *pCell = 
  4938.                 (CEditTableCellElement*)m_pBuffer->GetTableElementFromLO_Element( m_pLoElement, LO_CELL );
  4939.             if( pCell )
  4940.             {
  4941.                 EDT_TableCellData * pCellData = pCell->GetData(0);
  4942.                 if( pCellData && iWidth != m_iStartWidth )
  4943.                 {
  4944.                     pCellData->bWidthDefined = TRUE;
  4945.                     pCellData->iWidth = iWidth;
  4946.                     pCellData->iWidthPixels = iWidthPixels;
  4947.                     m_pBuffer->BeginBatchChanges(kSetTableCellDataCommandID);
  4948.                     pCell->SetData(pCellData);
  4949.                     EDT_FreeTableCellData(pCellData);
  4950.  
  4951.                     // Get change in pixels: + means we increased width
  4952.                     int32 iDelta = iWidthPixels - m_pLoElement->lo_any.width;
  4953.                     int32 iNewWidth;
  4954.  
  4955.                     // Column sizing is complicated by the fact that first cell in column
  4956.                     //  may not be in the 1st row because of COLSPAN.
  4957.                     //  We must be sure 1st cell sharing RIGHT edge is resized as well,
  4958.                     LO_TableStruct *pLoTable = lo_GetParentTable(NULL, m_pLoElement);
  4959.                     LO_Element *pLoElement = (LO_Element*)pLoTable;
  4960.  
  4961.                     // Stop when we reach the cell we were sizing
  4962.                     while (pLoElement && pLoElement != m_pLoElement)
  4963.                     {
  4964.                         if( pLoElement->type == LO_CELL &&
  4965.                             ((pLoElement->lo_cell.x + pLoElement->lo_cell.width) ==
  4966.                              (m_pLoElement->lo_cell.x + m_pLoElement->lo_cell.width)) )
  4967.                         {
  4968.                             // We found a different cell in first row - get Edit element
  4969.                             CEditTableCellElement *pFirstCell = 
  4970.                                 (CEditTableCellElement*)m_pBuffer->GetTableElementFromLO_Element( pLoElement, LO_CELL );
  4971.                             if( pFirstCell )
  4972.                             {
  4973.                                 pCellData = pFirstCell->GetData(0);
  4974.                                 if( pCellData )
  4975.                                 {
  4976.                                     iNewWidth = pLoElement->lo_cell.width + iDelta;
  4977.                                     if( pCellData->bWidthPercent )
  4978.                                     {
  4979.                                         int32 iTableWidth = lo_CalcTableWidthForPercentMode(pLoElement);
  4980.                                         pCellData->iWidth = (iNewWidth * 100) / iTableWidth;                                
  4981.                                     } else {
  4982.                                         pCellData->iWidth = iNewWidth;
  4983.                                     }
  4984.                                     pCellData->iWidthPixels = iNewWidth;
  4985.                                     
  4986.                                     pCellData->bWidthDefined = TRUE;
  4987.                                     pFirstCell->SetData(pCellData);
  4988.                                     EDT_FreeTableCellData(pCellData);
  4989.                                     //Note: We don't need to change width in pLoElement
  4990.                                     //  since we will relayout entire table, where new widths are determined
  4991.                                 }
  4992.                             }
  4993.                             break;
  4994.                         }
  4995.                         pLoElement = pLoElement->lo_any.next;
  4996.                     }
  4997.                    
  4998.                     // Also adjust width of entire table by same amount,
  4999.                     //   else changing columns "fights" the user
  5000.                     CEditTableElement *pTable = pCell->GetParentTable();
  5001.  
  5002.                     if( pLoTable && pTable )
  5003.                     {
  5004.                         EDT_TableData * pTableData = pTable->GetData();
  5005.                         if( pTableData )
  5006.                         {
  5007.                             pTableData->bWidthDefined = TRUE;
  5008.                             iNewWidth = pLoTable->width + iDelta;
  5009.  
  5010.                             if( pTableData->bWidthPercent )
  5011.                             {
  5012.                                 // This may be > 100 %, but layout will redistibute sizes correctly
  5013.                                 // TODO: Should we warn the user and convert to "pixel" mode?
  5014.                                 //       or just convert to pixel mode automatically?
  5015.                                 pTableData->iWidth = (iNewWidth * 100) / pLoTable->width;
  5016.  
  5017.                                 // Automatically convert to ABSOLUTE PIXELS if new % is > 100%
  5018.                                 if( pTableData->iWidth > 100 )
  5019.                                 {
  5020.                                     pTableData->iWidth = iNewWidth;
  5021.                                     pTableData->bWidthPercent = FALSE;   
  5022.                                 }
  5023.                             } else {
  5024.                                 pTableData->iWidth = iNewWidth;
  5025.                             }
  5026.                             pTableData->iWidthPixels = iNewWidth;
  5027.                             pTable->SetData(pTableData);
  5028.                             EDT_FreeTableData(pTableData);
  5029.                         }
  5030.                     }
  5031.  
  5032.                     // Relayout the entire table
  5033.                     m_pBuffer->Relayout(pTable, 0);
  5034.                     m_pBuffer->EndBatchChanges();
  5035.                 }
  5036.             }
  5037.             break;
  5038.         }
  5039.     }
  5040. }
  5041.  
  5042. void CSizingObject::EraseAddRowsOrCols()
  5043. {
  5044.     int i;
  5045.     XP_Rect rect;
  5046.     if( m_iStyle == ED_SIZE_ADD_COLS )
  5047.     {
  5048.         // Erase all existing lines
  5049.         // NOTE: We can't draw/erase just the "new" line because 
  5050.         //   we may skip lines if mouse is moved fast, causing a mess
  5051.         for( i=1; i< m_iAddCols; i++ )
  5052.         {
  5053.             CalcAddColRect(i, &m_Rect, &rect);
  5054.             FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
  5055.         }
  5056.     } else if(  m_iStyle == ED_SIZE_ADD_ROWS )
  5057.     {
  5058.         // Erase all existing lines
  5059.         for( i=1; i< m_iAddRows; i++ )
  5060.         {
  5061.             CalcAddRowRect(i, &m_Rect, &rect);
  5062.             FE_DisplayAddRowOrColBorder(m_pBuffer->m_pContext, &rect, TRUE);
  5063.         }
  5064.     }
  5065. }
  5066.  
  5067. CSizingObject::~CSizingObject()
  5068. {
  5069.     // Assure that next comparison to a new rect
  5070.     //   will be different
  5071.     m_Rect.left = -1;
  5072.     FE_Progress(m_pBuffer->m_pContext, "");
  5073.  
  5074.     // Don't leave object in selected state
  5075.     m_pBuffer->ClearSelection(TRUE, FALSE);
  5076. }
  5077. ///////////////////////////////////////////////////
  5078.  
  5079. // Relayout timer ... doesn't quite work perfectly.
  5080.  
  5081. CRelayoutTimer::CRelayoutTimer(){
  5082.     m_pBuffer = NULL;
  5083. }
  5084.  
  5085. void CRelayoutTimer::SetEditBuffer(CEditBuffer* pBuffer){
  5086.     m_pBuffer = pBuffer;
  5087. }
  5088.  
  5089. void CRelayoutTimer::OnCallback() {
  5090.     m_pBuffer->Relayout(m_pStartElement, m_iOffset);
  5091. }
  5092.  
  5093. void CRelayoutTimer::Flush() {
  5094.     if (IsTimeoutEnabled() ) {
  5095.         Cancel();
  5096.         OnCallback();
  5097.     }
  5098. }
  5099.  
  5100. void CRelayoutTimer::Relayout( CEditElement *pStartElement, int iOffset)
  5101. {
  5102.     const uint32 kRelayoutDelay = 50;
  5103.     XP_Bool bSetValues = TRUE;
  5104.     XP_Bool bInTable = pStartElement->GetTableIgnoreSubdoc() != NULL;
  5105.  
  5106.     if ( ! bInTable ) {
  5107.         Flush();
  5108.         m_pBuffer->Relayout(pStartElement, iOffset);
  5109.         return;
  5110.     }
  5111.     if ( IsTimeoutEnabled() ) {
  5112.         Cancel();
  5113.         if ( pStartElement != m_pStartElement ) {
  5114.             OnCallback(); // Do saved relayout now.
  5115.         }
  5116.         else {
  5117.             bSetValues = FALSE;
  5118.         }
  5119.     }
  5120.     if ( bSetValues) {
  5121.         m_pStartElement = pStartElement;
  5122.         m_iOffset = iOffset;
  5123.     }
  5124.  
  5125.     // Put the character into the LO_Element string so that the two data structures are consistent.
  5126.     if (pStartElement->IsText() ){
  5127.         CEditTextElement* pTextElement = pStartElement->Text();
  5128.         LO_TextStruct* pLoText = NULL;
  5129.         int iLoOffset = 0;
  5130.         if ( pTextElement->GetLOTextAndOffset(iOffset, FALSE, pLoText, iLoOffset) ) {
  5131.             if ( pLoText ) {
  5132.                 int32 textLen = pLoText->text_len;
  5133.                 char *pText;
  5134.                 if (textLen <= 0 ){
  5135.                     PA_FREE(pLoText->text);
  5136.                     pLoText->text = (PA_Block) PA_ALLOC(2); // char plus zero terminated.
  5137.                 }
  5138.                 else {
  5139.                     pLoText->text = (PA_Block) PA_REALLOC(pLoText->text, textLen + 2);
  5140.                 }
  5141.                 PA_LOCK(pText, char *, pLoText->text);
  5142.                 int32 bytesToMove = textLen - iLoOffset;
  5143.                 if ( bytesToMove > 0){
  5144.                     XP_MEMMOVE(pText + iLoOffset +1, pText + iLoOffset, bytesToMove);
  5145.                 }
  5146.                 pText[iLoOffset] = pTextElement->GetText()[iOffset];
  5147.                 pText[textLen+1] = '\0';
  5148.                 // XP_TRACE(("pText=%08x %s %d\n", pText, pText, textLen));
  5149.                 PA_UNLOCK(pLoText->text);
  5150.                 textLen++;
  5151.                 pLoText->text_len = (int16) textLen;
  5152.             }
  5153.         }
  5154.     }
  5155.     SetTimeout(kRelayoutDelay);
  5156. }
  5157.  
  5158. CAutoSaveTimer::CAutoSaveTimer() {
  5159.      m_pBuffer = NULL;
  5160.      m_iMinutes = 0;
  5161.      m_bSuspended = FALSE;
  5162.      m_bCalledWhileSuspended = FALSE;
  5163. }
  5164.  
  5165. void CAutoSaveTimer::SetEditBuffer(CEditBuffer* pBuffer){
  5166.     m_pBuffer = pBuffer;
  5167.     // Sanity check, is this an OK Buffer?
  5168.     XP_ASSERT(m_pBuffer->m_pRoot);
  5169. }
  5170.  
  5171. void CAutoSaveTimer::OnCallback() {
  5172.     if( m_bSuspended ){
  5173.         m_bCalledWhileSuspended = TRUE;
  5174.     } else {
  5175.         // Sanity check -- is this an OK Buffer?
  5176.         if ( ! m_pBuffer || ! m_pBuffer->m_pRoot ) {
  5177.             XP_ASSERT(FALSE);
  5178.             return;
  5179.         }
  5180.         // Skip autosave if currently suspended
  5181.         m_pBuffer->AutoSaveCallback();
  5182.         Restart();
  5183.     }
  5184. }
  5185.  
  5186. void CAutoSaveTimer::Restart() {
  5187. #ifdef DEBUG_AUTO_SAVE
  5188.     const int32 kMillisecondsPerMinute = 6000L; //60000L; // * 1000 / 10;
  5189. #else
  5190.     const int32 kMillisecondsPerMinute = 60000L;
  5191. #endif
  5192.     if ( m_iMinutes > 0 ) {
  5193.         SetTimeout(m_iMinutes * kMillisecondsPerMinute);
  5194.     }
  5195. }
  5196.  
  5197. void CAutoSaveTimer::SetPeriod(int32 minutes){
  5198.     Cancel();
  5199.     m_iMinutes = minutes;
  5200.     Restart();
  5201. }
  5202.  
  5203. int32 CAutoSaveTimer::GetPeriod(){
  5204.     return m_iMinutes;
  5205. }
  5206.  
  5207. void CAutoSaveTimer::Suspend(){
  5208.     m_bSuspended = TRUE;
  5209. }
  5210.  
  5211. void CAutoSaveTimer::Resume(){
  5212.     m_bSuspended = FALSE;
  5213.     // We need to restart the timer ONLY
  5214.     //  if we were called during a suspended period
  5215.     if( m_bCalledWhileSuspended ){
  5216.         m_bCalledWhileSuspended = FALSE;
  5217.     } else {
  5218.         Restart();
  5219.     }
  5220. }
  5221.  
  5222. CEditDocState::CEditDocState() {
  5223.   m_pBuffer = NULL;
  5224.   m_version = 0;
  5225. }
  5226.  
  5227. CEditDocState::~CEditDocState() {
  5228.   if (m_pBuffer) {
  5229.     XP_HUGE_FREE(m_pBuffer);
  5230.   }
  5231. }
  5232.  
  5233. #ifdef DEBUG
  5234. void CEditDocState::Print(IStreamOut& stream) {
  5235.   if (!m_pBuffer) return;
  5236.  
  5237.   stream.Printf("%d bytes", XP_STRLEN(m_pBuffer));
  5238.   // have to dump one line at a time. XP_TRACE has a 512 char limit on Windows.
  5239.   char* b = m_pBuffer;
  5240.   while ( *b != '\0' ) {
  5241.       char* b2 = b;
  5242.       while ( *b2 != '\0' && *b2 != '\n'){
  5243.           b2++;
  5244.       }
  5245.       char old = *b2;
  5246.       *b2 = '\0';
  5247.       stream.Printf("%s\n", b);
  5248.       *b2 = old;
  5249.       b = b2 + 1;
  5250.       if ( old == '\0' ) break;
  5251.   }
  5252. }
  5253. #endif
  5254.  
  5255.  
  5256.  
  5257. /* Take the document for current context and change appropriate
  5258.  *   params to make it look like a "Untitled" new document
  5259.  * Allows loading any document as a "Template"
  5260.  */
  5261. void EDT_ConvertCurrentDocToNewDoc(MWContext * pMWContext)
  5262. {
  5263.     XP_ASSERT(pMWContext);
  5264.     if( !pMWContext ){
  5265.         return;
  5266.     }
  5267.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer);
  5268.  
  5269.     char * pUntitled = XP_GetString(XP_EDIT_NEW_DOC_NAME);
  5270.  
  5271.     // Traverse all links and images and change URLs to absolute
  5272.     //   since we will be destroying our current base doc URL
  5273.     EDT_ImageData *pImageData;
  5274.     char *pAbsolute = NULL;
  5275.     EDT_PageData *pPageData = pEditBuffer->GetPageData();
  5276.     if( !pPageData){
  5277.         return;
  5278.     }
  5279.     
  5280.     // Should be the same as pEntry->Address???
  5281.     char *pBaseURL = LO_GetBaseURL(pMWContext);
  5282.  
  5283.     // Call Java Plugin hook for pages to be closed,
  5284.     //  BUT only if it really was an lockable source
  5285.     int iType = NET_URL_Type(pBaseURL);
  5286.     if( iType == FTP_TYPE_URL ||
  5287.         iType == HTTP_TYPE_URL ||
  5288.         iType == SECURE_HTTP_TYPE_URL ||
  5289.         iType == FILE_TYPE_URL ){
  5290.         EDT_PreClose(pMWContext, pBaseURL, NULL, NULL);
  5291.     }
  5292.  
  5293.     // Walk the tree and find all HREFs.
  5294.     CEditElement *pLeaf = pEditBuffer->m_pRoot->FindNextElement( 
  5295.                                   &CEditElement::FindLeafAll,0 );
  5296.     // First sweep, mark all HREFs as not adjusted.
  5297.     while (pLeaf) {
  5298.         pEditBuffer->linkManager.SetAdjusted(pLeaf->Leaf()->GetHREF(),FALSE);
  5299.         pLeaf = pLeaf->FindNextElement(&CEditElement::FindLeafAll,0 );
  5300.     }
  5301.     // Second sweep, actually adjust the HREFs.
  5302.     pLeaf = pEditBuffer->m_pRoot->FindNextElement( 
  5303.             &CEditElement::FindLeafAll,0 );
  5304.     while (pLeaf) {
  5305.         ED_LinkId linkId = pLeaf->Leaf()->GetHREF();
  5306.         if (linkId && !pEditBuffer->linkManager.GetAdjusted(linkId)) {
  5307.             pEditBuffer->linkManager.AdjustLink(linkId, pBaseURL, NULL, NULL);          
  5308.             pEditBuffer->linkManager.SetAdjusted(linkId,TRUE);
  5309.         }
  5310.         pLeaf = pLeaf->FindNextElement(&CEditElement::FindLeafAll,0 );
  5311.     }
  5312.  
  5313.     // Regular images.
  5314.     CEditElement *pImage = pEditBuffer->m_pRoot->FindNextElement( 
  5315.                                    &CEditElement::FindImage, 0 );
  5316.     while( pImage ){
  5317.         pImageData = pImage->Image()->GetImageData();
  5318.         if( pImageData ){
  5319.             if( pImageData->pSrc && *pImageData->pSrc ){
  5320.                 char * pOld = XP_STRDUP(pImageData->pSrc);
  5321.                 pAbsolute = NET_MakeAbsoluteURL( pBaseURL, pImageData->pSrc );
  5322.                 if( pAbsolute ){
  5323.                     XP_FREE(pImageData->pSrc);
  5324.                     pImageData->pSrc = pAbsolute;
  5325.                 }
  5326.              }
  5327.              if( pImageData->pLowSrc && *pImageData->pLowSrc){
  5328.                 pAbsolute = NET_MakeAbsoluteURL( pBaseURL, pImageData->pLowSrc );
  5329.                 if( pAbsolute ){
  5330.                     XP_FREE(pImageData->pLowSrc);
  5331.                     pImageData->pLowSrc = pAbsolute;
  5332.                 }
  5333.             }    
  5334.             pImage->Image()->SetImageData( pImageData );
  5335.             edt_FreeImageData( pImageData );
  5336.         }
  5337.         pImage = pImage->FindNextElement( &CEditElement::FindImage, 0 );
  5338.     }
  5339.  
  5340.     // If there is a background Image, make it absolute also
  5341.     if( pPageData->pBackgroundImage && *pPageData->pBackgroundImage){
  5342.         pAbsolute = NET_MakeAbsoluteURL( pBaseURL, pPageData->pBackgroundImage );
  5343.         if( pAbsolute ){
  5344.             XP_FREE(pPageData->pBackgroundImage);
  5345.             pPageData->pBackgroundImage = pAbsolute;
  5346.         }
  5347.     }
  5348.  
  5349.     // Change context's "title" string
  5350.     XP_FREEIF(pMWContext->title);
  5351.     pMWContext->title = NULL;
  5352.     pMWContext->is_new_document = TRUE;
  5353.  
  5354.     // Change the history entry data
  5355.     History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
  5356.     if(pEntry ){
  5357.         XP_FREEIF(pEntry->address);
  5358.         pEntry->address = XP_STRDUP(pUntitled);
  5359.         XP_FREEIF(pEntry->title);
  5360.     }
  5361.     // Layout uses this as the base URL for all links and images
  5362.     LO_SetBaseURL( pMWContext, pUntitled );
  5363.  
  5364.     // Cleat the old title in page data
  5365.     XP_FREEIF(pPageData->pTitle); 
  5366.  
  5367.     // This will set new background image,
  5368.     //   call FE_SetDocTitle() with new "file://Untitled" string,
  5369.     //   and refresh the layout of entire doc
  5370.     pEditBuffer->SetPageData(pPageData);
  5371.  
  5372.     pEditBuffer->FreePageData(pPageData);
  5373. }
  5374.  
  5375. char * EDT_GetFilename(char * pURL, XP_Bool bMustHaveExtension)
  5376. {
  5377.     // No string or its empty
  5378.     if( !pURL || !*pURL ){
  5379.         return NULL;
  5380.     }
  5381.     
  5382.     // Isolate just the filename of source URL
  5383.     char * pFilename = XP_STRRCHR(pURL, '/');
  5384.  
  5385.     // Maybe its a local filename?
  5386.     if(!pFilename){
  5387.         pFilename = XP_STRRCHR(pURL, '\\');
  5388.     }
  5389.  
  5390.     // Or just after host or drive letter?
  5391.     if(!pFilename){
  5392.         pFilename = XP_STRCHR(pURL, ':');
  5393.     }
  5394.  
  5395.     if(pFilename){
  5396.         // Filename starts just after "/"
  5397.         pFilename++;
  5398.     } else {
  5399.         // Use what we have - maybe not a full URL spec?
  5400.         pFilename = pURL;
  5401.     }
  5402.     char * pResult = NULL;
  5403.  
  5404.     if( pFilename && *pFilename ){
  5405.         // Don't include any stuff after filename
  5406.         char * ques = XP_STRCHR(pFilename, '?');
  5407.         char * hash = XP_STRCHR(pFilename, '#');
  5408.         if(ques){
  5409.             *ques = '\0';
  5410.         }
  5411.         if(hash){
  5412.             *hash = '\0';
  5413.         }
  5414.  
  5415.         // Check if we must have a period to be considered a filename
  5416.         // This allow text without "." to be considered a subdirectory, not filename
  5417.         if( *pFilename ){
  5418.             if( !bMustHaveExtension || 
  5419.                  (bMustHaveExtension && XP_STRCHR(pFilename, '.')) ){
  5420.                 // Copy just the filename if we have one
  5421.                 pResult = XP_STRDUP(pFilename);
  5422.             }
  5423.         }
  5424.  
  5425.         // set the values back 
  5426.         if(ques){
  5427.             *ques = '?';
  5428.         }
  5429.         if(hash){
  5430.             *hash = '#';
  5431.         }
  5432.     }    
  5433.     return pResult;
  5434. }
  5435.  
  5436.  char * EDT_ReplaceFilename(char * pBaseURL, char * pURL, XP_Bool bMustHaveExtension)
  5437. {
  5438.     char * pResult = NULL;
  5439.     if( !pBaseURL || !*pBaseURL ){
  5440.         return NULL;
  5441.     }
  5442.     // Find the start of filename in base - 
  5443.     // copy to new string
  5444.     // **** We overload the meaning of bMustHaveExtension to 
  5445.     //      also cause any filename on the base to have an extension,
  5446.     //      else any name without trailing slash, such as //chainsaw/mydir",
  5447.     //      will be "corrected" by adding trailing slash
  5448.     if( bMustHaveExtension ){
  5449.         char * pBaseFilename = EDT_GetFilename(pBaseURL, bMustHaveExtension);
  5450.         int iLen = XP_STRLEN(pBaseURL);
  5451.         if( !pBaseFilename && pBaseURL[iLen-1] != '/' ){
  5452.             // No trailing slash found, copy string and append one
  5453.             pResult = (char*)XP_ALLOC(iLen+1);
  5454.             if( !pResult ){
  5455.                 return NULL;
  5456.             }
  5457.             XP_STRCPY(pResult, pBaseURL);
  5458.             pResult[iLen] = '/';
  5459.             pResult[iLen+1] = '\0';
  5460.         }
  5461.         XP_FREEIF(pBaseFilename);
  5462.     }
  5463.     if( !pResult ){
  5464.         char * pBaseEnd = XP_STRRCHR(pBaseURL, '/');
  5465.  
  5466.         if( !pBaseEnd )
  5467.             pBaseEnd = XP_STRCHR(pBaseURL, ':');
  5468.         if( !pBaseEnd )
  5469.             return NULL;
  5470.  
  5471.         pBaseEnd++;
  5472.         char save_char = *pBaseEnd;
  5473.         *pBaseEnd = '\0';
  5474.  
  5475.         // Allocate result and copy the base part
  5476.         StrAllocCat(pResult, pBaseURL);
  5477.  
  5478.         // Restore character
  5479.         *pBaseEnd = save_char;
  5480.     }
  5481.     if( pResult && pURL ){
  5482.         // Get just the filename of source URL
  5483.         char * pFilename = EDT_GetFilename(pURL, bMustHaveExtension);
  5484.         if( pFilename ){
  5485.             // Append the filename to the base
  5486.             StrAllocCat(pResult, pFilename);
  5487.             XP_FREE(pFilename);
  5488.         }
  5489.     }
  5490.  
  5491.     //Note: If no pURL, pBaseURL is returned stripped of its filename
  5492.     return pResult;        
  5493. }
  5494.  
  5495. char * EDT_GetDefaultPublishURL(MWContext * pMWContext, char **ppFilename, char **ppUserName, char **ppPassword)
  5496. {
  5497.     XP_ASSERT(pMWContext);
  5498.     char * pURL = LO_GetBaseURL(pMWContext);
  5499.     if( !pURL ){
  5500.         return NULL;
  5501.     }
  5502.     XP_Bool bLastPublishFailed = EDT_IsSameURL(CEditSaveObject::m_pFailedPublishUrl, pURL, NULL, NULL);
  5503.  
  5504.     if( ppFilename ){
  5505.         if( EDT_IS_NEW_DOCUMENT(pMWContext) ){
  5506.             // New page: empty filename
  5507.             *ppFilename = NULL;
  5508.         } else {
  5509.             // Extract current filename part
  5510.             *ppFilename = EDT_GetFilename(pURL, TRUE);
  5511.  
  5512.             // On win16 force to lower case.
  5513. #ifdef XP_WIN16
  5514.             char *pConvert = *ppFilename;
  5515.             while (pConvert && *pConvert) {
  5516.               *pConvert = XP_TO_LOWER(*pConvert);
  5517.               pConvert++;
  5518.             }
  5519. #endif
  5520.         }
  5521.     }
  5522.     // Default: Clear password
  5523.     if( ppPassword ){
  5524.         *ppPassword = NULL;
  5525.     }
  5526.  
  5527.     if( !bLastPublishFailed && !EDT_IS_NEW_DOCUMENT(pMWContext) ){
  5528.         int iType = NET_URL_Type(pURL);
  5529.         if( iType == FTP_TYPE_URL ||
  5530.             iType == HTTP_TYPE_URL ||
  5531.             iType == SECURE_HTTP_TYPE_URL ) {
  5532.             // We are editing a remote page, so try to return it to that location
  5533.             // (We don't know the password)
  5534.             // Truncate current URL at the filename
  5535.             return EDT_ReplaceFilename(pURL, NULL, TRUE);
  5536.         }
  5537.     }
  5538.  
  5539.     char *pPrefURL = NULL;
  5540.     XP_Bool bUseDefault = FALSE;
  5541.  
  5542.     if( bLastPublishFailed ){
  5543.         // The publish destination should be saved to this at start of publishing (thus may not be "correct")
  5544.         // The destination should also be saved to the first history pref (editor.publish_history_0) 
  5545.         //  but only if publishing succeeded
  5546.         PREF_CopyCharPref("editor.publish_last_loc", &pPrefURL);
  5547.     } else {
  5548.         // Use the last successfully-used location saved in preferences
  5549.         PREF_CopyCharPref("editor.publish_history_0", &pPrefURL);
  5550.     }
  5551.     // Be sure to check for empty pref string
  5552.     if( !pPrefURL || !*pPrefURL ){
  5553.         XP_FREEIF(pPrefURL);
  5554.         // No last-used location -- use the default location
  5555.         PREF_CopyCharPref("editor.publish_location", &pPrefURL);
  5556.         bUseDefault = TRUE;
  5557.     }   
  5558.  
  5559.     char * pLocation = 0;
  5560.     char * pUserName = ppUserName ? *ppUserName : 0;
  5561.     
  5562.     // Parse the preference string to extract Username
  5563.     //  (password is separate for security munging)
  5564.     NET_ParseUploadURL( pPrefURL, &pLocation, &pUserName, NULL );
  5565.  
  5566.     if (ppUserName)
  5567.         *ppUserName = pUserName;
  5568.  
  5569.     if( ppPassword ){
  5570.         // Get the corrsponding password saved
  5571.         char * pPassword = NULL;
  5572.         if( bLastPublishFailed ){
  5573.         } else if( bUseDefault ){
  5574.             PREF_CopyCharPref("editor.publish_password", &pPassword);
  5575.         } else {
  5576.             PREF_CopyCharPref("editor.publish_password_0", &pPassword);
  5577.         }
  5578.  
  5579.         if( pPassword && *pPassword ){
  5580.             *ppPassword = SECNAV_UnMungeString(pPassword);
  5581.             XP_FREE(pPassword);
  5582.         }
  5583.     }
  5584.     XP_FREEIF(pPrefURL);
  5585.  
  5586.     return pLocation;
  5587. }
  5588.  
  5589. //
  5590. //    Keep this localized here. These two functions are the only ones
  5591. //    that need to know this value.
  5592. //    NOTE: CALLER MUST FREE THE RETURNED STRINGS
  5593. #define MAX_PUBLISH_LOCATIONS 20
  5594.  
  5595. XP_Bool
  5596. EDT_GetPublishingHistory(unsigned n,
  5597.                          char** location_r,
  5598.                          char** username_r,
  5599.                          char** password_r)
  5600. {
  5601.     char  prefname[32];
  5602.     char* prefvalue = NULL;
  5603.  
  5604.     if (n >= MAX_PUBLISH_LOCATIONS)
  5605.         return FALSE;
  5606.  
  5607.     XP_SPRINTF(prefname, "editor.publish_history_%d", n);
  5608.  
  5609.     if( PREF_CopyCharPref(prefname, &prefvalue) == -1 ||
  5610.         prefvalue == NULL || prefvalue[0] == '\0')
  5611.         return FALSE;
  5612.  
  5613.     if (location_r != NULL)
  5614.         *location_r = NULL; /* else NET_ParseUploadURL() will try to free! */
  5615.     if (username_r != NULL)
  5616.         *username_r = NULL;
  5617.  
  5618.     XP_Bool return_value;
  5619.     return_value = NET_ParseUploadURL(prefvalue, location_r, username_r, NULL);
  5620.     XP_FREE(prefvalue); // done with this now.
  5621.  
  5622.     if (!return_value)
  5623.         return FALSE;
  5624.  
  5625.     if (password_r != NULL) {
  5626.         XP_SPRINTF(prefname, "editor.publish_password_%d", n);
  5627.  
  5628.         if (PREF_CopyCharPref(prefname, &prefvalue) != -1 && prefvalue && *prefvalue) {
  5629.             *password_r = SECNAV_UnMungeString(prefvalue);
  5630.         } else {
  5631.             *password_r = NULL;
  5632.         }
  5633.         if (prefvalue != NULL)
  5634.             XP_FREE(prefvalue);
  5635.     }
  5636.     return TRUE;
  5637. }
  5638.  
  5639. void
  5640. EDT_SyncPublishingHistory()
  5641. {
  5642.     char  prefname[32];
  5643.     char* prefvalue = NULL;
  5644.     char* pLastLoc = NULL;
  5645.     char* pLastPass = NULL;
  5646.     int   i;
  5647.     
  5648.     /*
  5649.      *    Last location should be set when you start a publish.
  5650.      *    Now copy this to the top (first) of the history.
  5651.      *    Abort if pref doesn't exist or is empty
  5652.      */
  5653.     if( PREF_CopyCharPref("editor.publish_last_loc", &pLastLoc) == -1 ||
  5654.         !pLastLoc || !*pLastLoc )
  5655.         return;
  5656.     PREF_CopyCharPref("editor.publish_last_pass", &pLastPass);
  5657.     
  5658.     /*
  5659.      *    First scan the list to find pref that matches new item
  5660.      */
  5661.     for (i = 0; i < (MAX_PUBLISH_LOCATIONS - 1); i++) {
  5662.         XP_SPRINTF(prefname, "editor.publish_history_%d", i);
  5663.         if( PREF_CopyCharPref(prefname, &prefvalue) == -1 ||
  5664.             !prefvalue || !*prefvalue ||
  5665.             XP_STRCMP(prefvalue, pLastLoc) == 0) {
  5666.             //
  5667.             //    No history or this history matches the last publish location.
  5668.             //
  5669.             break;
  5670.         }
  5671.         XP_FREEIF(prefvalue);
  5672.     }
  5673.     XP_FREEIF(prefvalue);
  5674.     
  5675.     /*
  5676.      *    Now shift everything up a slot.
  5677.      */
  5678.     for (; i > 0; i--) {
  5679.         XP_SPRINTF(prefname, "editor.publish_history_%d", i-1);
  5680.         PREF_CopyCharPref(prefname, &prefvalue);
  5681.         XP_SPRINTF(prefname, "editor.publish_history_%d", i);
  5682.         PREF_SetCharPref(prefname, prefvalue);
  5683.         XP_FREEIF(prefvalue);
  5684.         
  5685.         XP_SPRINTF(prefname, "editor.publish_password_%d", i-1);
  5686.         PREF_CopyCharPref(prefname, &prefvalue);
  5687.         XP_SPRINTF(prefname, "editor.publish_password_%d", i);
  5688.         PREF_SetCharPref(prefname, prefvalue);
  5689.         XP_FREEIF(prefvalue);
  5690.     }
  5691.     
  5692.     /*
  5693.      *    Now set the zero'th one.
  5694.      */
  5695.     PREF_SetCharPref("editor.publish_history_0", pLastLoc);
  5696.     PREF_SetCharPref("editor.publish_password_0", pLastPass);
  5697.     
  5698.     XP_FREEIF(pLastLoc);
  5699.     XP_FREEIF(pLastPass);
  5700. }
  5701.  
  5702. //
  5703. // This was placed in EDT.H so front ends can use it as well
  5704. // #define MAX_EDIT_HISTORY_LOCATIONS 10 
  5705.  
  5706. // Cache the Edit History data
  5707. static char** ppEditHistoryURLs = NULL;
  5708. static char** ppEditHistoryTitles = NULL;
  5709.  
  5710. //    NOTE: Caller must NOT free the returned strings - they are locally cached
  5711. XP_Bool
  5712. EDT_GetEditHistory(MWContext * pMWContext, unsigned n,
  5713.                    char** ppUrl, char** ppTitle)
  5714. {
  5715.     char  prefname[32];
  5716.     unsigned i;
  5717.     
  5718.     // if context is NULL, bail!
  5719.     if( !pMWContext )
  5720.         return FALSE;
  5721.     
  5722.     // Initialize list if not done so already
  5723.     if( !ppEditHistoryURLs )
  5724.         EDT_SyncEditHistory(pMWContext);
  5725.  
  5726.     // Check if current URL is matches the requested or more recent items in the list
  5727.     if( pMWContext )
  5728.     {
  5729.         History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
  5730.         if( pEntry && pEntry->address )
  5731.         {
  5732.             for( i = 0; i <= n; i++ ){
  5733.                 XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
  5734.                 if( ppEditHistoryURLs[i] && 
  5735.                     EDT_IsSameURL(pEntry->address, ppEditHistoryURLs[i], 0, 0) )
  5736.                 {
  5737.                     // The current doc was found, so skip over it
  5738.                     n++;
  5739.                     break;
  5740.                 }        
  5741.             }
  5742.         }
  5743.     }
  5744.     // Default in case we don't set the location
  5745.     if( ppUrl )
  5746.         *ppUrl = NULL;
  5747.     
  5748.     if( n >= MAX_EDIT_HISTORY_LOCATIONS )
  5749.         return FALSE;
  5750.  
  5751.     if( !ppEditHistoryURLs[n] )
  5752.         return FALSE;
  5753.  
  5754.     // Return URL and Title to caller
  5755.     if( ppUrl )
  5756.         *ppUrl = ppEditHistoryURLs[n];
  5757.  
  5758.     if( ppTitle )
  5759.         *ppTitle = ppEditHistoryTitles[n];
  5760.  
  5761.     return TRUE;
  5762. }
  5763.  
  5764. static char  pEmpty[] = ""; 
  5765.  
  5766. void
  5767. EDT_SyncEditHistory(MWContext * pMWContext)
  5768. {
  5769.     char  prefname[32];
  5770.     char* prefvalue = NULL;
  5771.     char* pTitle = pEmpty;
  5772.     int   i;
  5773.     
  5774.     if(!pMWContext)
  5775.         return;
  5776.  
  5777.     // Initialize the local arrays from the current preference list
  5778.     if( !ppEditHistoryURLs )
  5779.     {
  5780.         ppEditHistoryURLs = (char**)XP_ALLOC( MAX_EDIT_HISTORY_LOCATIONS * sizeof(char*) );
  5781.         if( !ppEditHistoryURLs )
  5782.             return;
  5783.  
  5784.         ppEditHistoryTitles = (char**)XP_ALLOC( MAX_EDIT_HISTORY_LOCATIONS * sizeof(char*) );
  5785.         if( !ppEditHistoryTitles )
  5786.         {
  5787.             XP_FREEIF(ppEditHistoryURLs);
  5788.             return;
  5789.         }
  5790.  
  5791.         for( i = 0; i < MAX_EDIT_HISTORY_LOCATIONS; i++ )
  5792.         {
  5793.             XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
  5794.             if(PREF_CopyCharPref(prefname, &prefvalue) != -1 &&
  5795.                prefvalue && *prefvalue )
  5796.             {
  5797.                 ppEditHistoryURLs[i] = prefvalue;
  5798.                 prefvalue = NULL;
  5799.  
  5800.             } else {
  5801.                 ppEditHistoryURLs[i] = NULL;
  5802.                 XP_FREEIF(prefvalue);
  5803.             }
  5804.  
  5805.             XP_SPRINTF(prefname, "editor.url_history.TITLE_%d", i);
  5806.             if(PREF_CopyCharPref(prefname, &prefvalue) != -1 &&
  5807.                prefvalue && *prefvalue )
  5808.             {
  5809.                 ppEditHistoryTitles[i] = prefvalue;
  5810.                 prefvalue = NULL;
  5811.             } else {
  5812.                 ppEditHistoryTitles[i] = NULL;
  5813.                 XP_FREEIF(prefvalue);
  5814.             }
  5815.         }
  5816.     }
  5817.  
  5818.     History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
  5819.     if(pEntry )
  5820.     {
  5821.         // Must have an address
  5822.         if( !pEntry->address || !*pEntry->address )
  5823.             return;
  5824.  
  5825.         // Test for new document URL: file://Untitled and ignore it
  5826.         if( !XP_STRCMP(XP_GetString(XP_EDIT_NEW_DOC_NAME), pEntry->address) ||
  5827.             !XP_STRCMP(XP_GetString(XP_EDIT_NEW_DOC_URL), pEntry->address))
  5828.         {
  5829.             return;
  5830.         }
  5831.  
  5832.         if( pEntry->title && *pEntry->title ){
  5833.             pTitle = pEntry->title;
  5834.         }
  5835.     }
  5836.  
  5837.     /*
  5838.      *    First scan the list to find pref URL that matches current page
  5839.      */
  5840.     for (i = 0; i < (MAX_EDIT_HISTORY_LOCATIONS - 1); i++)
  5841.     {
  5842.         XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
  5843.         if( !ppEditHistoryURLs[i] ||
  5844.             EDT_IsSameURL(ppEditHistoryURLs[i], pEntry->address, 0, 0) ) {
  5845.             //
  5846.             //    No history or this history matches the current last-edited URL
  5847.             //
  5848.             break;
  5849.         }
  5850.     }
  5851.     // Now i = the index to last-edited URL or last possible URL in the list
  5852.     if( i == (MAX_EDIT_HISTORY_LOCATIONS-1) )
  5853.     {
  5854.         // We will be replacing the last item, so delete it
  5855.         //   so delete it
  5856.         XP_FREEIF(ppEditHistoryURLs[i]);
  5857.         XP_FREEIF(ppEditHistoryTitles[i]);
  5858.     }
  5859.     
  5860.     /*
  5861.      *    Now shift everything one item.
  5862.      */
  5863.     for (; i > 0; i--)
  5864.     {
  5865.         XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
  5866.         // Not sure if this can handle null string, so lets not try!
  5867.         PREF_SetCharPref(prefname, ppEditHistoryURLs[i-1] ? ppEditHistoryURLs[i-1] : pEmpty);
  5868.         
  5869.         ppEditHistoryURLs[i] = ppEditHistoryURLs[i-1];        
  5870.  
  5871.         XP_SPRINTF(prefname, "editor.url_history.TITLE_%d", i);
  5872.         PREF_SetCharPref(prefname, ppEditHistoryTitles[i-1] ? ppEditHistoryTitles[i-1] : pEmpty);
  5873.  
  5874.         ppEditHistoryTitles[i] = ppEditHistoryTitles[i-1];        
  5875.     }
  5876.     
  5877.     /*
  5878.      *    Now set the zero'th one -- the current URL
  5879.      */
  5880.     PREF_SetCharPref("editor.url_history.URL_0", pEntry->address);
  5881.     PREF_SetCharPref("editor.url_history.TITLE_0", pTitle);
  5882.  
  5883.     ppEditHistoryURLs[0] = XP_STRDUP(pEntry->address);        
  5884.  
  5885.  
  5886.     if( pTitle && *pTitle )
  5887.     {
  5888.         ppEditHistoryTitles[0] = XP_STRDUP(pTitle);
  5889.     } else {
  5890.         ppEditHistoryTitles[0] = NULL;
  5891.     }        
  5892. }
  5893.  
  5894. void edt_UpdateEditHistoryTitle(MWContext * pMWContext, char * pTitle)
  5895. {
  5896.     if( pMWContext )
  5897.     {
  5898.         History_entry * pEntry = SHIST_GetCurrent(&(pMWContext->hist));
  5899.         if(pEntry && pEntry->address && !*pEntry->address)
  5900.         {
  5901.             for (int i = 0; i < MAX_EDIT_HISTORY_LOCATIONS; i++)
  5902.             {
  5903.                 char  prefname[32];
  5904.                 XP_SPRINTF(prefname, "editor.url_history.URL_%d", i);
  5905.                 if( ppEditHistoryURLs[i] &&
  5906.                     EDT_IsSameURL(ppEditHistoryURLs[i], pEntry->address, 0, 0) )
  5907.                 {
  5908.                     XP_FREEIF(ppEditHistoryTitles[i]);
  5909.                     if( pTitle )
  5910.                         ppEditHistoryTitles[i] = pTitle;
  5911.  
  5912.                     // Also update the preference value
  5913.                     char  prefname[32];
  5914.                     XP_SPRINTF(prefname, "editor.url_history.TITLE_%d", i);
  5915.                 
  5916.                     // Not sure if this can handle null string, so lets not try!
  5917.                     PREF_SetCharPref(prefname, pTitle ? pTitle : pEmpty);
  5918.                     return;
  5919.                 }
  5920.             }
  5921.         }
  5922.     }
  5923. }
  5924.  
  5925. char * EDT_GetPageTitleFromFilename(char * pFilename)
  5926. {
  5927.     char * pTitle = NULL;
  5928.     if( pFilename ){
  5929.         // Get Full "filename" and we don't need an extension
  5930.         pTitle = EDT_GetFilename(pFilename, FALSE);
  5931.     
  5932.         if( pTitle ){
  5933.             // Don't include extension
  5934.             char * period = XP_STRCHR(pTitle, '.');
  5935.             if(period){
  5936.                 // Truncate at the beginning of extension
  5937.                 *period = '\0';
  5938.             }
  5939.         }
  5940.     }
  5941.     return pTitle;
  5942. }
  5943.  
  5944. // True if both urls are the same, ignores any username/password
  5945. // information.  Does caseless comparison for file:// URLs 
  5946. // on windows and mac.
  5947. // url1 and url2 are relative to base1 and base2, respectively.
  5948. // If url1 or url2 is already absolute, base1 or base2 can 
  5949. // be passed in as NULL.
  5950. XP_Bool EDT_IsSameURL(char *url1,char *url2,char *base1,char *base2) {
  5951.   // Check for NULL or empty strings.
  5952.   if (!url1 || !url2 || !*url1 || !*url2) {
  5953.     return FALSE;
  5954.   }
  5955.  
  5956.   // Used passed in base URLs if provided.
  5957.   char *pAbs1 = NULL;
  5958.   char *pAbs2 = NULL;
  5959.   if (base1) {
  5960.     pAbs1 = NET_MakeAbsoluteURL(base1,url1);
  5961.   }
  5962.   if (base2) {
  5963.     pAbs2 = NET_MakeAbsoluteURL(base2,url2);
  5964.   }
  5965.   // Strip any username/password info from urls in making comparison if
  5966.   // they are http, https, or ftp.
  5967.   char *pUrl1 = edt_StripUsernamePassword(pAbs1 ? pAbs1 : url1);
  5968.   char *pUrl2 = edt_StripUsernamePassword(pAbs2 ? pAbs2 : url2);
  5969.   edt_StripAtHashOrQuestionMark(pUrl1);
  5970.   edt_StripAtHashOrQuestionMark(pUrl2);
  5971.  
  5972.   XP_Bool bRetVal = FALSE;
  5973.   if (pUrl1 && pUrl2) {
  5974.     char *pBaseUrl1 = NET_ParseURL(pUrl1, GET_PATH_PART);
  5975.     char *pBaseUrl2 = NET_ParseURL(pUrl2, GET_PATH_PART);
  5976. #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2)
  5977.     // Strip off Target (named Anchors)
  5978.     // Local file URLs are equivalent if they differ only 
  5979.     // by case.
  5980.     if (NET_URL_Type(pUrl1) == FILE_TYPE_URL) {
  5981.       bRetVal = (XP_STRCASECMP( pUrl1, pUrl2 ) == 0);
  5982.     }
  5983.     else {
  5984.       bRetVal = (XP_STRCMP( pUrl1, pUrl2 ) == 0 );
  5985.     }
  5986. #else
  5987.     // Use regular strcmp.
  5988.     bRetVal = (XP_STRCMP( pUrl1, pUrl2 ) == 0 );
  5989. #endif
  5990.   }
  5991.   
  5992.   XP_FREEIF(pUrl1);
  5993.   XP_FREEIF(pUrl2);
  5994.   XP_FREEIF(pAbs1);
  5995.   XP_FREEIF(pAbs2);
  5996.   return bRetVal;
  5997. }
  5998.  
  5999. // Replace the current selection with supplied text
  6000. void EDT_ReplaceText(MWContext *pContext, char * pReplaceText, XP_Bool bReplaceAll,
  6001.                     char *pTextToLookFor, XP_Bool bCaseless, XP_Bool bBackward, XP_Bool bDoWrap)
  6002. {
  6003.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6004.     pEditBuffer->ReplaceLoop( pReplaceText, bReplaceAll, 
  6005.                               pTextToLookFor, bCaseless, bBackward, bDoWrap );
  6006. }
  6007.  
  6008. // Block of functions moved from EDITOR.CPP for Win16 Build
  6009. // NOT USED???
  6010. #ifdef FIND_REPLACE
  6011.  
  6012. XP_Bool EDT_FindAndReplace(MWContext *pContext,
  6013.                 EDT_FindAndReplaceData *pData ){
  6014.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
  6015.     return pEditBuffer->FindAndReplace( pData );
  6016.  
  6017. }
  6018. #endif  // FIND_REPLACE
  6019.  
  6020. // Table Sizing, Selection, Add Row/Col interface
  6021. ED_HitType EDT_GetTableHitRegion(MWContext *pContext, int32 xVal, int32 yVal, 
  6022.                                  LO_Element **ppElement, XP_Bool bModifierKeyPressed)
  6023. {
  6024.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) ED_HIT_NONE;
  6025.     return pEditBuffer->GetTableHitRegion(xVal, yVal, ppElement, bModifierKeyPressed);
  6026. }
  6027.  
  6028. ED_HitType EDT_GetSelectedTableElement(MWContext *pContext, LO_Element **ppElement){
  6029.     return EDT_GetTableHitRegion(pContext, 0, 0, ppElement, FALSE);
  6030. }
  6031.  
  6032. // Object Sizer
  6033. ED_SizeStyle EDT_CanSizeObject(MWContext *pContext, LO_Element *pElement,
  6034.                       int32 xVal, int32 yVal){
  6035.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
  6036.     return pEditBuffer->CanSizeObject(pElement, xVal, yVal);
  6037. }
  6038.  
  6039. XP_Bool EDT_IsSizing(MWContext *pContext){
  6040.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
  6041.     return pEditBuffer->IsSizing();
  6042. }
  6043.  
  6044. ED_SizeStyle EDT_StartSizing(MWContext *pContext, LO_Element *pElement, int32 xVal, int32 yVal,
  6045.                  XP_Bool bLockAspect, XP_Rect *pRect){
  6046.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
  6047.     return pEditBuffer->StartSizing(pElement, xVal, yVal, bLockAspect, pRect);
  6048. }
  6049.  
  6050. XP_Bool EDT_GetSizingRect(MWContext *pContext, int32 xVal, int32 yVal,
  6051.                        XP_Bool bLockAspect, XP_Rect *pRect){
  6052.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
  6053.     return pEditBuffer->GetSizingRect(xVal, yVal, bLockAspect, pRect);
  6054. }
  6055.  
  6056. void EDT_EndSizing(MWContext *pContext){
  6057.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6058.     pEditBuffer->EndSizing();
  6059. }
  6060.  
  6061. void EDT_CancelSizing(MWContext *pContext){
  6062.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6063.     pEditBuffer->CancelSizing();
  6064. }
  6065.  
  6066. ED_BufferOffset EDT_GetInsertPointOffset( MWContext *pContext ){
  6067.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
  6068.     CPersistentEditInsertPoint p;
  6069.     pEditBuffer->GetInsertPoint( p );
  6070.     return (ED_BufferOffset) p.m_index;
  6071. }
  6072.  
  6073. void EDT_SetInsertPointToOffset( MWContext *pContext, ED_BufferOffset i,
  6074.         int32 iLen ){
  6075.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6076.     CPersistentEditInsertPoint p((ElementIndex)i);
  6077.  
  6078.     if( iLen != 0 ){
  6079.         CPersistentEditInsertPoint p1((ElementIndex)i+iLen);
  6080.         CPersistentEditSelection perSel;
  6081.         perSel = CPersistentEditSelection( p, p1 );
  6082.         CEditSelection sel = pEditBuffer->PersistentToEphemeral( perSel );
  6083.         pEditBuffer->SetSelection( sel );
  6084.     }
  6085.     else {
  6086.         pEditBuffer->SetInsertPoint( p  );
  6087.     }
  6088. }
  6089.  
  6090. void EDT_OffsetToLayoutElement( MWContext *pContext, ED_BufferOffset i,
  6091.         LO_Element * *element, int32 *caretPos){
  6092.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6093.     CPersistentEditInsertPoint p((ElementIndex)i);
  6094.  
  6095.     CEditInsertPoint eP = pEditBuffer->PersistentToEphemeral(p);
  6096.     *element = eP.m_pElement->GetLayoutElement();
  6097.     *caretPos = eP.m_iPos;
  6098. }
  6099.  
  6100. ED_BufferOffset EDT_LayoutElementToOffset( MWContext *pContext,
  6101.         LO_Element *element, int32 caretPos){
  6102.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) -1;
  6103.     
  6104.     if (element->type != LO_TEXT)
  6105.         return -1;
  6106.         
  6107.     CEditInsertPoint insertPoint(element->lo_text.edit_element, caretPos);
  6108.     
  6109.     CPersistentEditInsertPoint p = pEditBuffer->EphemeralToPersistent(insertPoint);
  6110.     
  6111.     return p.m_index;
  6112. }
  6113.  
  6114. LO_TextBlock* EDT_GetTextBlock(MWContext *, LO_Element *le)
  6115. {
  6116.     LO_TextBlock *block;
  6117.     CEditTextElement *pText;
  6118.     
  6119.     block = NULL;
  6120.     
  6121.     if( ( le->type == LO_TEXT ) && ( le->lo_any.edit_element != NULL )
  6122.             && le->lo_any.edit_element->IsA( P_TEXT ) ){
  6123.         pText = le->lo_any.edit_element->Text();
  6124.         block = pText->GetTextBlock();
  6125.     }
  6126.     
  6127.     return block;
  6128. }
  6129.  
  6130. void EDT_GetSelectionOffsets(MWContext *pContext, ED_BufferOffset* pStart, ED_BufferOffset* pEnd){
  6131.     if ( ! pStart || !pEnd ) {
  6132.         XP_ASSERT(FALSE);
  6133.         return;
  6134.     }
  6135.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6136.     CPersistentEditSelection s;
  6137.     pEditBuffer->GetSelection( s );
  6138.     *pStart = (ED_BufferOffset) s.m_start.m_index;
  6139.     *pEnd = (ED_BufferOffset) s.m_end.m_index;
  6140. }
  6141.  
  6142. XP_Bool EDT_SelectFirstMisspelledWord( MWContext *pContext ){
  6143.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
  6144.     return pEditBuffer->FindNextMisspelledWord( TRUE, TRUE, 0 );
  6145. }
  6146.  
  6147. XP_Bool EDT_SelectNextMisspelledWord( MWContext *pContext ){
  6148.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
  6149.     return pEditBuffer->FindNextMisspelledWord( FALSE, TRUE, 0 );
  6150. }
  6151.  
  6152. void EDT_ReplaceMisspelledWord( MWContext *pContext, char* pOldWord, 
  6153.         char*pNewWord, XP_Bool bAll ){
  6154.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6155.     pEditBuffer->BeginBatchChanges(kPasteTextCommandID);
  6156.     pEditBuffer->IterateMisspelledWords( CEditBuffer::EMSW_REPLACE, 
  6157.             pOldWord, pNewWord, bAll );
  6158.     pEditBuffer->EndBatchChanges();
  6159. }
  6160.  
  6161. void EDT_IgnoreMisspelledWord( MWContext *pContext, char* pOldWord, 
  6162.         XP_Bool bAll ){
  6163.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6164.     pEditBuffer->IterateMisspelledWords( CEditBuffer::EMSW_IGNORE, 
  6165.             pOldWord, 0, bAll );
  6166. }
  6167.  
  6168.  
  6169. XP_HUGE_CHAR_PTR EDT_GetPositionalText( MWContext* pContext ){
  6170.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) 0;
  6171.     return pEditBuffer->GetPositionalText();
  6172. }
  6173.  
  6174. void EDT_SetCharacterDataAtOffset( MWContext *pContext, EDT_CharacterData *pData,
  6175.         ED_BufferOffset iBufOffset, int32 iLen ){
  6176.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6177.     pEditBuffer->SetCharacterDataAtOffset(pData, iBufOffset, iLen );
  6178. }
  6179.  
  6180. void EDT_SetRefresh( MWContext* pContext, XP_Bool bRefreshOn ){
  6181.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  6182.     pEditBuffer->SetRefresh( bRefreshOn );
  6183. }
  6184.  
  6185.  
  6186. // Warning this deletes (and recreates) the CEditBuffer.
  6187. XP_Bool EDT_SetEncoding(MWContext* pContext, int16 csid){
  6188.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
  6189.     XP_Bool bDoIt = TRUE;
  6190. //  if ( pEditBuffer->HasEncoding() ) {
  6191.     char* pMessage = XP_GetString(XP_EDT_I18N_HAS_CHARSET);
  6192.     if ( pMessage ) {
  6193.         bDoIt = FE_Confirm(pContext, pMessage);
  6194.     }
  6195.     else {
  6196.         XP_ASSERT(0);
  6197.     }
  6198.  
  6199.     if ( bDoIt ) {
  6200.         pEditBuffer->ChangeEncoding(csid);
  6201.     }
  6202.     return bDoIt;
  6203. }
  6204. // End of Block of functions moved from EDITOR.CPP for Win16 Build
  6205.  
  6206. /*
  6207.  * Extract the Extra HTML string from the ED_Element pointer in an image struct
  6208.  * (ED_Element is a void* to external users)
  6209.  * Caller must XP_FREE result
  6210.  */
  6211. char * EDT_GetExtraHTML_FromImage(LO_ImageStruct *pImage){
  6212.     if( pImage ){
  6213.         if( pImage->edit_element == 0 ){
  6214.             // We don't have an edit element - must be from Navigator,
  6215.             // so we have to pick out stuff that would end up in CEditImageElement's extra data
  6216.             // BUT the original PA_TAG data is no longer around.
  6217.             // The only item we will grab is the USEMAP data
  6218.             // TODO: Get other associated data such as JavaScript?
  6219.             //       To do that, we need a new function to reconstruct a tag from LO_ImageStruct
  6220.  
  6221.             if ( pImage->image_attr && pImage->image_attr->usemap_name ){
  6222.                 char * pRet = 0;
  6223.                 pRet = PR_sprintf_append( pRet, "USEMAP=");
  6224.                 pRet = PR_sprintf_append( pRet, pImage->image_attr->usemap_name);
  6225.                 return pRet;
  6226.             }
  6227.         }else if( pImage->edit_element->IsImage() ){
  6228.             EDT_ImageData * pImageData = ((CEditImageElement*)pImage->edit_element)->GetImageData();
  6229.             if( pImageData->pExtra && *pImageData->pExtra ){
  6230.                 return XP_STRDUP(pImageData->pExtra);
  6231.             }
  6232.             EDT_FreeImageData(pImageData);
  6233.         }
  6234.     }
  6235.     return NULL;
  6236. }
  6237.  
  6238. PRBool EDT_EncryptState( MWContext *pContext ){
  6239.     CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
  6240.  
  6241.     if (pEditBuffer == NULL) return PR_FALSE;
  6242.  
  6243.     return pEditBuffer->m_bEncrypt;
  6244. }
  6245.  
  6246. void EDT_EncryptToggle( MWContext *pContext ){
  6247.     CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
  6248.  
  6249.     if (pEditBuffer == NULL) return;
  6250.  
  6251.      pEditBuffer->m_bEncrypt = pEditBuffer->m_bEncrypt ? PR_FALSE :  PR_TRUE;
  6252. }
  6253.  
  6254. void EDT_EncryptSet( MWContext *pContext ){
  6255.     CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
  6256.  
  6257.     if (pEditBuffer == NULL) return;
  6258.  
  6259.     pEditBuffer->m_bEncrypt = PR_TRUE;
  6260. }
  6261.  
  6262. void EDT_EncryptReset( MWContext *pContext ){
  6263.     CEditBuffer *pEditBuffer = LO_GetEDBuffer( pContext );
  6264.  
  6265.     if (pEditBuffer == NULL) return;
  6266.  
  6267.     pEditBuffer->m_bEncrypt = PR_FALSE;
  6268. }
  6269.  
  6270. #endif // EDITOR
  6271.