/******************************************************************************
*
*
* Notepad2
*
* Edit.c
*   Text File Editing Helper Stuff
*
* See Readme.txt for more information about this source code.
* Please send me your comments to this work.
*
* Distributed under the terms of the GNU General Public License,
* see License.txt for details.
*
*                                              (c) Florian Balmer 1996-2004
*                                                       textview@bluewin.ch
*                                               http://www.flos-freeware.ch
*
*
******************************************************************************/
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <string.h>
#include "notepad2.h"
#include "helpers.h"
#include "dialogs.h"
#include "appreg.h"
#include "scintilla.h"
#include "scilexer.h"
#include "styles.h"
#include "edit.h"
#include "resource.h"


extern HWND  hwndMain;
extern HWND  hwndEdit;
extern HINSTANCE g_hInstance;
extern LPMALLOC  g_lpMalloc;


// Default Codepage and Character Set
extern int iDefaultCodePage;
extern int iDefaultCharSet;


//=============================================================================
//
//  EditCreate()
//
HWND EditCreate(HWND hwndParent)
{

  HWND hwnd;

  hwnd = CreateWindowEx(
           WS_EX_CLIENTEDGE,
           "Scintilla",
           NULL,
           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
           0,0,0,0,
           hwndParent,
           (HMENU)IDC_EDIT,
           g_hInstance,
           NULL);

  SendMessage(hwnd,SCI_SETCODEPAGE,iDefaultCodePage,0);
  SendMessage(hwnd,SCI_SETEOLMODE,SC_EOL_CRLF,0);

  SendMessage(hwnd,SCI_SETMODEVENTMASK,/*SC_MODEVENTMASKALL*/SC_MOD_INSERTTEXT|SC_MOD_DELETETEXT,0);

  SendMessage(hwnd,SCI_USEPOPUP,FALSE,0);

  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_NEXT + (SCMOD_CTRL << 16)),SCI_PARADOWN);
  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_PRIOR + (SCMOD_CTRL << 16)),SCI_PARAUP);
  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_NEXT + ((SCMOD_CTRL | SCMOD_SHIFT) << 16)),SCI_PARADOWNEXTEND);
  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_PRIOR + ((SCMOD_CTRL | SCMOD_SHIFT) << 16)),SCI_PARAUPEXTEND);

  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_HOME + (0 << 16)),SCI_VCHOMEWRAP);
  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_END + (0 << 16)),SCI_LINEENDWRAP);
  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_HOME + (SCMOD_SHIFT << 16)),SCI_VCHOMEWRAPEXTEND);
  SendMessage(hwnd,SCI_ASSIGNCMDKEY,(SCK_END + (SCMOD_SHIFT << 16)),SCI_LINEENDWRAPEXTEND);

  // Init default values for printing
  EditPrintInit();

  return(hwnd);

}


//=============================================================================
//
//  EditSetNewText()
//
void EditSetNewText(HWND hwnd,LPCSTR lpstrText,DWORD cbText)
{

  if (SendMessage(hwnd,SCI_GETREADONLY,0,0))
    SendMessage(hwnd,SCI_SETREADONLY,FALSE,0);

  SendMessage(hwnd,SCI_CANCEL,0,0);
  SendMessage(hwnd,SCI_SETUNDOCOLLECTION,0,0);
  SendMessage(hwnd,SCI_CLEARALL,0,0);
  SendMessage(hwnd,SCI_MARKERDELETEALL,(WPARAM)-1,0);

  if (cbText > 0)
    SendMessage(hwnd,SCI_ADDTEXT,cbText,(LPARAM)lpstrText);

  SendMessage(hwnd,SCI_SETUNDOCOLLECTION,1,0);
  SendMessage(hwnd,EM_EMPTYUNDOBUFFER,0,0);
  SendMessage(hwnd,SCI_SETSAVEPOINT,0,0);
  SendMessage(hwnd,SCI_GOTOPOS,0,0);
  SendMessage(hwnd,SCI_CHOOSECARETX,0,0);

}


//=============================================================================
//
//  EditDetectEOLMode() - moved here to handle Unicode files correctly
//
int EditDetectEOLMode(HWND hwnd,LPCSTR lpData,DWORD cbData)
{
  int iEOLMode = SC_EOL_CRLF;

  char *cp = lpData;
  while (*cp && (*cp != '\x0D' && *cp != '\x0A')) cp++;

  if (*cp == '\x0D' && *(cp+1) != '\x0A')
    iEOLMode = SC_EOL_CR;
  else if (*cp == '\x0A')
    iEOLMode = SC_EOL_LF;

  return (iEOLMode);
}



//=============================================================================
//
//  IsUnicode(), IsUTF8(), IsUTF7()
//
BOOL IsUnicode(const char* pBuffer,int cb,LPBOOL lpbBOM,LPBOOL lpbReverse)
{

  int i = 0xFFFF;

  BOOL bIsTextUnicode;

  BOOL bHasBOM;
  BOOL bHasRBOM;

  if (!pBuffer || cb < 2)
    return FALSE;

  bIsTextUnicode = IsTextUnicode(pBuffer,cb,&i);

  bHasBOM  = (*pBuffer == '\xFF' && *(pBuffer+1) == '\xFE');
  bHasRBOM = (*pBuffer == '\xFE' && *(pBuffer+1) == '\xFF');

  /*
  #define IS_TEXT_UNICODE_ASCII16               0x0001
  #define IS_TEXT_UNICODE_REVERSE_ASCII16       0x0010
  #define IS_TEXT_UNICODE_STATISTICS            0x0002
  #define IS_TEXT_UNICODE_REVERSE_STATISTICS    0x0020
  #define IS_TEXT_UNICODE_CONTROLS              0x0004
  #define IS_TEXT_UNICODE_REVERSE_CONTROLS      0x0040
  #define IS_TEXT_UNICODE_SIGNATURE             0x0008
  #define IS_TEXT_UNICODE_REVERSE_SIGNATURE     0x0080
  #define IS_TEXT_UNICODE_ILLEGAL_CHARS         0x0100
  #define IS_TEXT_UNICODE_ODD_LENGTH            0x0200
  #define IS_TEXT_UNICODE_DBCS_LEADBYTE         0x0400
  #define IS_TEXT_UNICODE_NULL_BYTES            0x1000
  *//*
  {
    char szBuf[512];

    wsprintf(szBuf,
      "IS_TEXT_UNICODE_ASCII16\t\t\t%s\n"\
      "IS_TEXT_UNICODE_REVERSE_ASCII16\t\t%s\n"\
      "IS_TEXT_UNICODE_STATISTICS\t\t%s\n"\
      "IS_TEXT_UNICODE_REVERSE_STATISTICS\t%s\n"\
      "IS_TEXT_UNICODE_CONTROLS\t\t%s\n"\
      "IS_TEXT_UNICODE_REVERSE_CONTROLS\t%s\n"\
      "IS_TEXT_UNICODE_SIGNATURE\t\t%s\n"\
      "IS_TEXT_UNICODE_REVERSE_SIGNATURE\t%s\n"\
      "IS_TEXT_UNICODE_ILLEGAL_CHARS\t\t%s\n"\
      "IS_TEXT_UNICODE_ODD_LENGTH\t\t%s\n"\
      "IS_TEXT_UNICODE_DBCS_LEADBYTE\t\t%s\n"\
      "IS_TEXT_UNICODE_NULL_BYTES\t\t%s\n\n"\
      "bIsTextUnicode\t\t\t\t%i\n\n"\
      "bHasBOM\t\t\t\t\t%i\n"\
      "bHasRBOM\t\t\t\t%i",
      i & IS_TEXT_UNICODE_ASCII16 ? "1" :"-",
      i & IS_TEXT_UNICODE_REVERSE_ASCII16 ? "1" :"-",
      i & IS_TEXT_UNICODE_STATISTICS ? "1" :"-",
      i & IS_TEXT_UNICODE_REVERSE_STATISTICS ? "1" :"-",
      i & IS_TEXT_UNICODE_CONTROLS ? "1" :"-",
      i & IS_TEXT_UNICODE_REVERSE_CONTROLS ? "1" :"-",
      i & IS_TEXT_UNICODE_SIGNATURE ? "1" :"-",
      i & IS_TEXT_UNICODE_REVERSE_SIGNATURE ? "1" :"-",
      i & IS_TEXT_UNICODE_ILLEGAL_CHARS ? "1" :"-",
      i & IS_TEXT_UNICODE_ODD_LENGTH ? "1" :"-",
      i & IS_TEXT_UNICODE_DBCS_LEADBYTE ? "1" :"-",
      i & IS_TEXT_UNICODE_NULL_BYTES ? "1" :"-",
      bIsTextUnicode,bHasBOM,bHasRBOM);

    MessageBox(hwnd,szBuf,"Unicode file guessing",0);
  }*/
  /*
  Andere Variante: IsTextUnicode() einmal auf normal, einmal auf _swab(),
  wenn vorhanden BOM und RBOM selber entfernen

  Auch noch zu Probieren: Einfach konvertieren, und mit lpUsedDefaultChar
  bei WideCharToMultiByte() checken, ob Zeichen nicht interpretiert werden
  konnten -> Hinweis auf non-Unicode-File.
  */

  if (i == 0xFFFF) // i doesn't seem to have been modified ...
    i = 0;

  if (bIsTextUnicode || bHasBOM || bHasRBOM ||
        ((i & (IS_TEXT_UNICODE_UNICODE_MASK | IS_TEXT_UNICODE_REVERSE_MASK)) &&
       !((i & IS_TEXT_UNICODE_UNICODE_MASK) && (i & IS_TEXT_UNICODE_REVERSE_MASK)) &&
        !(i & IS_TEXT_UNICODE_ODD_LENGTH) &&
        !(i & IS_TEXT_UNICODE_ILLEGAL_CHARS && !(i & IS_TEXT_UNICODE_REVERSE_SIGNATURE)))) {

    if (lpbBOM)
      *lpbBOM = (bHasBOM || bHasRBOM ||
        (i & (IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE)))
          ? TRUE : FALSE;

    if (lpbReverse)
      *lpbReverse = (bHasRBOM || (i & IS_TEXT_UNICODE_REVERSE_MASK)) ? TRUE : FALSE;

    return TRUE;
  }

  else

    return FALSE;

}


BOOL IsUTF8(const char* pTest,int nLength)
{
  static int byte_class_table[256] = {
  /*       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  */
  /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 30 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 40 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  /* 80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  /* 90 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  /* A0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  /* B0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  /* C0 */ 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  /* D0 */ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  /* E0 */ 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 7,
  /* F0 */ 9,10,10,10,11, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
  /*       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  */ };

  /* state table */
  typedef enum {
    kSTART = 0,kA,kB,kC,kD,kE,kF,kG,kERROR,kNumOfStates } utf8_state;

  static utf8_state state_table[] = {
  /*                            kSTART, kA,     kB,     kC,     kD,     kE,     kF,     kG,     kERROR */
  /* 0x00-0x7F: 0            */ kSTART, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0x80-0x8F: 1            */ kERROR, kSTART, kA,     kERROR, kA,     kB,     kERROR, kB,     kERROR,
  /* 0x90-0x9f: 2            */ kERROR, kSTART, kA,     kERROR, kA,     kB,     kB,     kERROR, kERROR,
  /* 0xa0-0xbf: 3            */ kERROR, kSTART, kA,     kA,     kERROR, kB,     kB,     kERROR, kERROR,
  /* 0xc0-0xc1, 0xf5-0xff: 4 */ kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xc2-0xdf: 5            */ kA,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xe0: 6                 */ kC,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xe1-0xec, 0xee-0xef: 7 */ kB,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xed: 8                 */ kD,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xf0: 9                 */ kF,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xf1-0xf3: 10           */ kE,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR,
  /* 0xf4: 11                */ kG,     kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR, kERROR };

  #define BYTE_CLASS(b) (byte_class_table[(unsigned char)b])
  #define NEXT_STATE(b,cur) (state_table[(BYTE_CLASS(b) * kNumOfStates) + (cur)])

    utf8_state current = kSTART;
    int i;

    const char* pt = pTest;
    int len = nLength;

    for(i = 0; i < len ; i++, pt++) {

      current = NEXT_STATE(*pt,current);
      if (kERROR == current)
        break; }

    return (current == kSTART) ? TRUE : FALSE;
  }


BOOL IsUTF7(const char* pTest,int nLength)
{
  int i;
  const char *pt = pTest;

  for (i = 0; i < nLength; i++) {
    if (*pt & 0x80 || !*pt)
      return FALSE;
    pt++; }

  return TRUE;
}


#define IsUTF8Signature(p) \
          ((*(p+0) == '\xEF' && *(p+1) == '\xBB' && *(p+2) == '\xBF'))


#define UTF8StringStart(p) \
          (IsUTF8Signature(p)) ? (p+3) : (p)


/* byte length of UTF-8 sequence based on value of first byte.
   for UTF-16 (21-bit space), max. code length is 4, so we only need to look
   at 4 upper bits.
 */
static const INT utf8_lengths[16]=
{
    1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
    0,0,0,0,                /* 1000 to 1011 : not valid */
    2,2,                    /* 1100, 1101 : 2 bytes */
    3,                      /* 1110 : 3 bytes */
    4                       /* 1111 :4 bytes */
};

/*++
Function :
    UTF8_mbslen_bytes [INTERNAL]

    Calculates the byte size of a NULL-terminated UTF-8 string.

Parameters :
    char *utf8_string : string to examine

Return value :
    size (in bytes) of a NULL-terminated UTF-8 string.
    -1 if invalid NULL-terminated UTF-8 string
--*/
static INT UTF8_mbslen_bytes(LPCSTR utf8_string)
{
    INT length=0;
    INT code_size;
    BYTE byte;

    while(*utf8_string)
    {
        byte=(BYTE)*utf8_string;

        if( (byte <= 0xF7) && (0 != (code_size = utf8_lengths[ byte >> 4 ])))
        {
            length+=code_size;
            utf8_string+=code_size;
        }
        else
        {
            /* we got an invalid byte value but need to count it,
               it will be later ignored during the string conversion */
            //WARN("invalid first byte value 0x%02X in UTF-8 sequence!\n",byte);
            length++;
            utf8_string++;
        }
    }
    length++; /* include NULL terminator */
    return length;
}

/*++
Function :
    UTF8_mbslen [INTERNAL]

    Calculates the character size of a NULL-terminated UTF-8 string.

Parameters :
    char *utf8_string : string to examine
    int byte_length : byte size of string

Return value :
    size (in characters) of a UTF-8 string.
    -1 if invalid UTF-8 string
--*/
static INT UTF8_mbslen(LPCSTR source, INT byte_length)
{
    INT wchar_length=0;
    INT code_size;
    BYTE byte;

    while(byte_length > 0)
    {
        byte=(BYTE)*source;

        /* UTF-16 can't encode 5-byte and 6-byte sequences, so maximum value
           for first byte is 11110111. Use lookup table to determine sequence
           length based on upper 4 bits of first byte */
        if ((byte <= 0xF7) && (0 != (code_size=utf8_lengths[ byte >> 4])))
        {
            /* 1 sequence == 1 character */
            wchar_length++;

            if(code_size==4)
                wchar_length++;

            source+=code_size;        /* increment pointer */
            byte_length-=code_size;   /* decrement counter*/
        }
        else
        {
            /*
               unlike UTF8_mbslen_bytes, we ignore the invalid characters.
               we only report the number of valid characters we have encountered
               to match the Windows behavior.
            */
            //WARN("invalid byte 0x%02X in UTF-8 sequence, skipping it!\n",
            //     byte);
            source++;
            byte_length--;
        }
    }
    return wchar_length;
}


//=============================================================================
//
//  EditLoadFile()
//
BOOL EditLoadFile(HWND hwnd,LPCSTR pszFile,BOOL bSkipEncodingDetection,
                  int* iCodePage,int* iEOLMode,BOOL *pbUnicodeErr,BOOL *pbFileTooBig)
{

  HANDLE hFile;

  DWORD  dwFileSize;
  DWORD  dwFileSizeLimit;
  DWORD  dwBufSize;
  BOOL   bReadSuccess;

  LPSTR lpData;
  DWORD cbData;
  //char  *cp;

  BOOL bBOM;
  BOOL bReverse;

  *pbUnicodeErr = FALSE;
  *pbFileTooBig = FALSE;

  hFile = CreateFile(pszFile,
                     GENERIC_READ,
                     FILE_SHARE_READ|FILE_SHARE_WRITE,
                     NULL,
                     OPEN_EXISTING,
                     FILE_ATTRIBUTE_NORMAL,
                     NULL);

  if (hFile == INVALID_HANDLE_VALUE)
    return FALSE;

  // calculate buffer limit
  dwFileSize = GetFileSize(hFile,NULL);
  dwBufSize  = dwFileSize + 10;

  // Check if a warning message should be displayed for large files
  dwFileSizeLimit = RegGetAppIntEx("Settings","FileLoadWarningMB",1);
  if (dwFileSizeLimit != 0 && dwFileSizeLimit * 1024 * 1024 < dwFileSize) {
    if (MsgBox(MBYESNOWARN,IDS_WARNLOADBIGFILE) != IDYES) {
      CloseHandle(hFile);
      *pbFileTooBig = TRUE;
      return FALSE;
    }
  }

  lpData = GlobalAlloc(GPTR,dwBufSize);
  bReadSuccess = ReadFile(hFile,lpData,GlobalSize(lpData)-2,&cbData,NULL);
  CloseHandle(hFile);

  if (!bReadSuccess)
  {
    GlobalFree(lpData);
    return FALSE;
  }

  // default codepage
  *iCodePage = NCP_DEFAULT;

  if (!bSkipEncodingDetection &&
      IsUnicode(lpData,cbData,&bBOM,&bReverse) && !IsUTF8Signature(lpData)) // check for UTF-8 signature
  {
    LPSTR  lpDataUTF8;
    CPINFO cpi;
    UINT   uCP_UTF8;

    *iCodePage = NCP_UNICODE;
    if (bBOM)
      *iCodePage |= NCP_UNICODE_BOM;

    if (bReverse)
    {
      _swab(lpData,lpData,cbData);
      *iCodePage |= NCP_UNICODE_REVERSE;
    }

    // Unicode text is converted to ANSI and not to UTF-8 on Windows 95
    uCP_UTF8 = (GetCPInfo(CP_UTF8, &cpi) || IsValidCodePage(CP_UTF8)) ? CP_UTF8 : CP_ACP;

    lpDataUTF8 = GlobalAlloc(GPTR,(cbData * 3) + 2);
    cbData = WideCharToMultiByte(uCP_UTF8,0,(bBOM) ? (LPWSTR)lpData + 1 : (LPWSTR)lpData,
              (-1),lpDataUTF8,GlobalSize(lpDataUTF8),NULL,NULL);

    if (cbData == 0 && uCP_UTF8 == CP_UTF8)
    {
      cbData = WideCharToMultiByte(CP_ACP,0,(bBOM) ? (LPWSTR)lpData + 1 : (LPWSTR)lpData,
                (-1),lpDataUTF8,GlobalSize(lpDataUTF8),NULL,NULL);
      *pbUnicodeErr = TRUE;
    }

    GlobalFree(lpData);
    SendMessage(hwnd,SCI_SETCODEPAGE,SC_CP_UTF8,0);
    EditSetNewText(hwnd,"",0);
    EditSetNewText(hwnd,lpDataUTF8,cbData-1);
    *iEOLMode = EditDetectEOLMode(hwnd,lpDataUTF8,cbData-1);
    GlobalFree(lpDataUTF8);
  }

  else if (!bSkipEncodingDetection &&
           IsUTF8(lpData,cbData) &&
           ((UTF8_mbslen_bytes(UTF8StringStart(lpData)) - 1 !=
             UTF8_mbslen(UTF8StringStart(lpData),IsUTF8Signature(lpData) ? cbData-3 : cbData)) ||
             IsUTF8Signature(lpData)))
  {
    SendMessage(hwnd,SCI_SETCODEPAGE,SC_CP_UTF8,0);
    EditSetNewText(hwnd,"",0);
    if (IsUTF8Signature(lpData)) {
      EditSetNewText(hwnd,UTF8StringStart(lpData),cbData-3);
      *iEOLMode = EditDetectEOLMode(hwnd,UTF8StringStart(lpData),cbData-3);
      *iCodePage = NCP_UTF8 | NCP_UTF8_SIGN; }
    else {
      EditSetNewText(hwnd,lpData,cbData);
      *iEOLMode = EditDetectEOLMode(hwnd,lpData,cbData);
      *iCodePage = NCP_UTF8; }
    GlobalFree(lpData);
  }

  else
  {
    SendMessage(hwnd,SCI_SETCODEPAGE,iDefaultCodePage,0);
    EditSetNewText(hwnd,"",0);
    EditSetNewText(hwnd,lpData,cbData);
    *iEOLMode = EditDetectEOLMode(hwnd,lpData,cbData);
    *iCodePage = NCP_DEFAULT;
    GlobalFree(lpData);
  }

  return TRUE;

}


//=============================================================================
//
//  EditSaveFile()
//
BOOL EditSaveFile(HWND hwnd,LPCSTR pszFile,int iCodePage,BOOL bSaveCopy)
{

  HANDLE hFile;
  BOOL   bWriteSuccess;

  LPSTR lpData;
  DWORD cbData;
  DWORD dwBytesWritten;

  hFile = CreateFile(pszFile,
                     GENERIC_WRITE,
                     FILE_SHARE_READ|FILE_SHARE_WRITE,
                     NULL,
                     CREATE_ALWAYS,
                     FILE_ATTRIBUTE_NORMAL,
                     NULL);

  // failure could be due to missing attributes (2k/XP)
  if (hFile == INVALID_HANDLE_VALUE)
  {
    DWORD dwAttributes = GetFileAttributes(pszFile);
    if (dwAttributes != INVALID_FILE_ATTRIBUTES)
    {
      dwAttributes = dwAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
      hFile = CreateFile(pszFile,
                        GENERIC_WRITE,
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        NULL,
                        CREATE_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL | dwAttributes,
                        NULL);
    }
  }

  if (hFile == INVALID_HANDLE_VALUE)
    return FALSE;

  // get text
  cbData = SendMessage(hwnd,SCI_GETLENGTH,0,0);
  lpData = GlobalAlloc(GPTR,cbData + 1);
  SendMessage(hwnd,SCI_GETTEXT,GlobalSize(lpData),(LPARAM)lpData);

  if (cbData == 0)
    bWriteSuccess = SetEndOfFile(hFile);

  else
  {

    if (iCodePage & NCP_UNICODE)
    {
      LPWSTR lpDataWide;
      int    cbDataWide;
      CPINFO cpi;
      UINT   uCP_UTF8;

      // UTF-8 text is interpreted as ANSI when saving as Unicode on Windows 95
      uCP_UTF8 = (GetCPInfo(CP_UTF8, &cpi) || IsValidCodePage(CP_UTF8)) ? CP_UTF8 : CP_ACP;

      lpDataWide = GlobalAlloc(GPTR,cbData * 2 + 16);
      cbDataWide = MultiByteToWideChar(uCP_UTF8,0,lpData,cbData,lpDataWide,GlobalSize(lpDataWide));

      if (iCodePage & NCP_UNICODE_BOM) {
        if (iCodePage & NCP_UNICODE_REVERSE)
          WriteFile(hFile,(LPCVOID)"\xFE\xFF",2,&dwBytesWritten,NULL);
        else
          WriteFile(hFile,(LPCVOID)"\xFF\xFE",2,&dwBytesWritten,NULL); }

      if (iCodePage & NCP_UNICODE_REVERSE)
        _swab((char*)lpDataWide,(char*)lpDataWide,cbDataWide * sizeof(WCHAR));

      bWriteSuccess = WriteFile(hFile,lpDataWide,cbDataWide * sizeof(WCHAR),&dwBytesWritten,NULL);

      GlobalFree(lpData);
    }

    else if (iCodePage & NCP_UTF8)
    {
      if (iCodePage & NCP_UTF8_SIGN)
        WriteFile(hFile,(LPCVOID)"\xEF\xBB\xBF",3,&dwBytesWritten,NULL);

      bWriteSuccess = WriteFile(hFile,lpData,cbData,&dwBytesWritten,NULL);
    }

    else // convert text to 8bit
    {
      //LPWSTR lpDataWide = GlobalAlloc(GPTR,cbData * 2 + 16);
      //int    cbDataWide = MultiByteToWideChar(CP_UTF8,0,lpData,cbData,lpDataWide,GlobalSize(lpDataWide));

      //ZeroMemory(lpData,GlobalSize(lpData));
      //cbData = WideCharToMultiByte(CP_ACP,0,lpDataWide,cbDataWide,lpData,GlobalSize(lpData),NULL,NULL);
      //GlobalFree(lpDataWide);

      bWriteSuccess = WriteFile(hFile,lpData,cbData,&dwBytesWritten,NULL);

      GlobalFree(lpData);
    }
  }

  CloseHandle(hFile);

  if (bWriteSuccess)
  {
    if (!bSaveCopy)
      SendMessage(hwnd,SCI_SETSAVEPOINT,0,0);

    return TRUE;
  }

  else
    return FALSE;

}


//=============================================================================
//
//  EditMakeUppercase()
//
void EditMakeUppercase(HWND hwnd)
{
  int cchTextW;
  int iCurPos;
  int iAnchorPos;
  UINT cpEdit;
  int i;
  BOOL bChanged = FALSE;

  if (!IsWindowsNT())
  {
    SendMessage(hwnd,SCI_UPPERCASE,0,0);
    return;
  }

  iCurPos    = SendMessage(hwnd,SCI_GETCURRENTPOS,0,0);
  iAnchorPos = SendMessage(hwnd,SCI_GETANCHOR,0,0);

  if (iCurPos != iAnchorPos)
  {
    if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
    {
      int iSelCount = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0) -
                        SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);

      LPSTR  pszText  = GlobalAlloc(GPTR,(iSelCount)+2);
      LPWSTR pszTextW = GlobalAlloc(GPTR,(iSelCount*2)+2);

      if (pszText == NULL || pszTextW == NULL) {
        GlobalFree(pszText);
        GlobalFree(pszTextW);
        return; }

      SendMessage(hwnd,SCI_GETSELTEXT,0,(LPARAM)pszText);

      cpEdit = SendMessage(hwnd,SCI_GETCODEPAGE,0,0);

      cchTextW = MultiByteToWideChar(cpEdit,0,pszText,iSelCount,pszTextW,GlobalSize(pszTextW) / sizeof(WCHAR));

      for (i = 0; i < cchTextW; i++) {
        if (IsCharLowerW(pszTextW[i])) {
          pszTextW[i] = LOWORD(CharUpperW((LPWSTR)MAKELONG(pszTextW[i],0)));
          bChanged = TRUE; } }

      if (bChanged) {

        WideCharToMultiByte(cpEdit,0,pszTextW,cchTextW,pszText,GlobalSize(pszText),NULL,NULL);
        GlobalFree(pszTextW);

        SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);
        SendMessage(hwnd,SCI_CLEAR,0,0);
        SendMessage(hwnd,SCI_ADDTEXT,(WPARAM)iSelCount,(LPARAM)pszText);
        SendMessage(hwnd,SCI_SETSEL,(WPARAM)iAnchorPos,(LPARAM)iCurPos);
        SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);

        GlobalFree(pszText); }

      else
        GlobalFree(pszTextW);

    }
    else
      MsgBox(MBINFO,IDS_SELRECT);
  }
}


//=============================================================================
//
//  EditMakeLowercase()
//
void EditMakeLowercase(HWND hwnd)
{
  int cchTextW;
  int iCurPos;
  int iAnchorPos;
  UINT cpEdit;
  int i;
  BOOL bChanged = FALSE;

  if (!IsWindowsNT())
  {
    SendMessage(hwnd,SCI_LOWERCASE,0,0);
    return;
  }

  iCurPos    = SendMessage(hwnd,SCI_GETCURRENTPOS,0,0);
  iAnchorPos = SendMessage(hwnd,SCI_GETANCHOR,0,0);

  if (iCurPos != iAnchorPos)
  {
    if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
    {
      int iSelCount = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0) -
                        SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);

      LPSTR  pszText  = GlobalAlloc(GPTR,(iSelCount)+2);
      LPWSTR pszTextW = GlobalAlloc(GPTR,(iSelCount*2)+2);

      if (pszText == NULL || pszTextW == NULL) {
        GlobalFree(pszText);
        GlobalFree(pszTextW);
        return; }

      SendMessage(hwnd,SCI_GETSELTEXT,0,(LPARAM)pszText);

      cpEdit = SendMessage(hwnd,SCI_GETCODEPAGE,0,0);

      cchTextW = MultiByteToWideChar(cpEdit,0,pszText,iSelCount,pszTextW,GlobalSize(pszTextW) / sizeof(WCHAR));

      for (i = 0; i < cchTextW; i++) {
        if (IsCharUpperW(pszTextW[i])) {
          pszTextW[i] = LOWORD(CharLowerW((LPWSTR)MAKELONG(pszTextW[i],0)));
          bChanged = TRUE; } }

      if (bChanged) {

        WideCharToMultiByte(cpEdit,0,pszTextW,cchTextW,pszText,GlobalSize(pszText),NULL,NULL);
        GlobalFree(pszTextW);

        SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);
        SendMessage(hwnd,SCI_CLEAR,0,0);
        SendMessage(hwnd,SCI_ADDTEXT,(WPARAM)iSelCount,(LPARAM)pszText);
        SendMessage(hwnd,SCI_SETSEL,(WPARAM)iAnchorPos,(LPARAM)iCurPos);
        SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);

        GlobalFree(pszText); }

      else
        GlobalFree(pszTextW);

    }
    else
      MsgBox(MBINFO,IDS_SELRECT);
  }
}


//=============================================================================
//
//  EditTabsToSpaces()
//
void EditTabsToSpaces(HWND hwnd,int nTabWidth)
{
  if (!IsWindowsNT())
  {
    int i;
    EDITFINDREPLACE efrTTS = { "\t", "", "", "", SCFIND_REGEXP, 0, 0, 0, hwnd };

    for (i = 0; i < nTabWidth; i++)
      efrTTS.szReplace[i] = ' ';
    efrTTS.szReplace[nTabWidth] = 0;

    // Check if there is any selection... simply use a regular expression replace!
    if (SendMessage(hwnd,SCI_GETSELECTIONEND,0,0) - SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0))
    {
      if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
        EditReplaceAllInSelection(hwnd,&efrTTS,FALSE);

      else
        MsgBox(MBINFO,IDS_SELRECT);
    }
    else
      /*EditReplaceAll(hwnd,&efrTTS,FALSE)*/;
  }

  else
  {
    LPSTR pszText;
    LPWSTR pszTextW;
    int cchTextW;
    int iTextW;
    LPWSTR pszConvW;
    int cchConvW;
    int cchConvM;
    int i,j;
    int iLine;
    int iCurPos;
    int iAnchorPos;
    int iSelStart;
    int iSelEnd;
    int iSelCount;
    UINT cpEdit;
    struct TextRange tr;

    if (SC_SEL_RECTANGLE == SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0)) {
      MsgBox(MBINFO,IDS_SELRECT);
      return; }

    iCurPos    = SendMessage(hwnd,SCI_GETCURRENTPOS,0,0);
    iAnchorPos = SendMessage(hwnd,SCI_GETANCHOR,0,0);

    if (iCurPos == iAnchorPos) /*{
      iSelStart = 0;
      iSelEnd   = SendMessage(hwnd,SCI_GETLENGTH,0,0); }*/
      return;

    else {
      iSelStart = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
      iSelEnd   = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0); }

    iLine = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iSelStart,0);
    iSelStart = SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLine,0);

    iSelCount = iSelEnd - iSelStart;

    pszText = GlobalAlloc(GPTR,(iSelCount)+2);
    if (pszText == NULL)
      return;

    pszTextW = GlobalAlloc(GPTR,(iSelCount*2)+2);
    if (pszTextW == NULL) {
      GlobalFree(pszText);
      return; }

    tr.chrg.cpMin = iSelStart;
    tr.chrg.cpMax = iSelEnd;
    tr.lpstrText = pszText;
    SendMessage(hwnd,SCI_GETTEXTRANGE,0,(LPARAM)&tr);

    cpEdit = SendMessage(hwnd,SCI_GETCODEPAGE,0,0);

    cchTextW = MultiByteToWideChar(cpEdit,0,pszText,iSelCount,pszTextW,GlobalSize(pszTextW) / sizeof(WCHAR));
    GlobalFree(pszText);

    pszConvW = GlobalAlloc(GPTR,cchTextW*sizeof(WCHAR)*nTabWidth+2);
    if (pszConvW == NULL) {
      GlobalFree(pszTextW);
      return; }

    cchConvW = 0;

    // Contributed by Homam
    // Thank you very much!
    i=0;
    for (iTextW = 0; iTextW < cchTextW; iTextW++)
    {
      WCHAR w = pszTextW[iTextW];
      if (w == L'\t') {
        for (j = 0; j < nTabWidth - i % nTabWidth; j++)
          pszConvW[cchConvW++] = L' ';
        i = 0;
      }
      else {
        i++;
        if (w == L'\n' || w == L'\r')
          i = 0;
        pszConvW[cchConvW++] = w;
      }
    }

    GlobalFree(pszTextW);

    if (cchConvW != cchTextW) {
      pszText = GlobalAlloc(GPTR,cchConvW * 3);

      cchConvM = WideCharToMultiByte(cpEdit,0,pszConvW,cchConvW,pszText,GlobalSize(pszText),NULL,NULL);
      GlobalFree(pszConvW);

      if (iAnchorPos > iCurPos) {
        iCurPos = iSelStart;
        iAnchorPos = iSelStart + cchConvM; }
      else {
        iAnchorPos = iSelStart;
        iCurPos = iSelStart + cchConvM; }

      SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);
      SendMessage(hwnd,SCI_SETTARGETSTART,(WPARAM)iSelStart,0);
      SendMessage(hwnd,SCI_SETTARGETEND,(WPARAM)iSelEnd,0);
      SendMessage(hwnd,SCI_REPLACETARGET,(WPARAM)cchConvM,(LPARAM)pszText);
      //SendMessage(hwnd,SCI_CLEAR,0,0);
      //SendMessage(hwnd,SCI_ADDTEXT,(WPARAM)cchConvW,(LPARAM)pszText);
      SendMessage(hwnd,SCI_SETSEL,(WPARAM)iAnchorPos,(LPARAM)iCurPos);
      SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);

      GlobalFree(pszText); }

    else
      GlobalFree(pszConvW);
  }
}


//=============================================================================
//
//  EditSpacesToTabs()
//
void EditSpacesToTabs(HWND hwnd,int nTabWidth)
{
  if (!IsWindowsNT())
  {
    int i;
    EDITFINDREPLACE efrSTT = { "", "\t", "", "", SCFIND_REGEXP, 0, 0, 0, hwnd };

    for (i = 0; i < nTabWidth; i++)
      efrSTT.szFind[i] = ' ';
    efrSTT.szFind[nTabWidth] = 0;

    // Check if there is any selection... simply use a regular expression replace!
    if (SendMessage(hwnd,SCI_GETSELECTIONEND,0,0) - SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0))
    {
      if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
        EditReplaceAllInSelection(hwnd,&efrSTT,FALSE);

      else
        MsgBox(MBINFO,IDS_SELRECT);
    }
    else
      /*EditReplaceAll(hwnd,&efrSTT,FALSE)*/;
  }

  else
  {
    LPSTR pszText;
    LPWSTR pszTextW;
    int cchTextW;
    int iTextW;
    LPWSTR pszConvW;
    int cchConvW;
    int cchConvM;
    int i,j,t;
    int iLine;
    int iCurPos;
    int iAnchorPos;
    int iSelStart;
    int iSelEnd;
    int iSelCount;
    UINT cpEdit;
    struct TextRange tr;
    WCHAR space[64];

    if (SC_SEL_RECTANGLE == SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0)) {
      MsgBox(MBINFO,IDS_SELRECT);
      return; }

    iCurPos    = SendMessage(hwnd,SCI_GETCURRENTPOS,0,0);
    iAnchorPos = SendMessage(hwnd,SCI_GETANCHOR,0,0);

    if (iCurPos == iAnchorPos) /*{
      iSelStart = 0;
      iSelEnd   = SendMessage(hwnd,SCI_GETLENGTH,0,0); }*/
      return;

    else {
      iSelStart = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
      iSelEnd   = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0); }

    iLine = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iSelStart,0);
    iSelStart = SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLine,0);

    iSelCount = iSelEnd - iSelStart;

    pszText = GlobalAlloc(GPTR,(iSelCount)+2);
    if (pszText == NULL)
      return;

    pszTextW = GlobalAlloc(GPTR,(iSelCount*2)+2);
    if (pszTextW == NULL) {
      GlobalFree(pszText);
      return; }

    tr.chrg.cpMin = iSelStart;
    tr.chrg.cpMax = iSelEnd;
    tr.lpstrText = pszText;
    SendMessage(hwnd,SCI_GETTEXTRANGE,0,(LPARAM)&tr);

    cpEdit = SendMessage(hwnd,SCI_GETCODEPAGE,0,0);

    cchTextW = MultiByteToWideChar(cpEdit,0,pszText,iSelCount,pszTextW,GlobalSize(pszTextW) / sizeof(WCHAR));
    GlobalFree(pszText);

    pszConvW = GlobalAlloc(GPTR,cchTextW*sizeof(WCHAR)+2);
    if (pszConvW == NULL) {
      GlobalFree(pszTextW);
      return; }

    cchConvW = 0;

    // Contributed by Homam
    // Thank you very much!
    i = j = 0;
    for (iTextW = 0; iTextW < cchTextW; iTextW++)
    {
      WCHAR w = pszTextW[iTextW];
      if ((w == L' ' || w == L'\t')) {
        space[j++] = w;
        if (j == nTabWidth - i % nTabWidth || w == L'\t') {
          if (j > 1 || pszTextW[iTextW+1] == L' ' || pszTextW[iTextW+1] == L'\t')
            pszConvW[cchConvW++] = L'\t';
          else
            pszConvW[cchConvW++] = w;
          i = j = 0;
        }
      }
      else {
        i += j + 1;
        if (j > 0) {
          //space[j] = '\0';
          for (t = 0; t < j; t++)
            pszConvW[cchConvW++] = space[t];
          j = 0;
        }
        if (w == L'\n' || w == L'\r')
          i = 0;
        pszConvW[cchConvW++] = w;
      }
    }
    if (j > 0) {
      for (t = 0; t < j; t++)
        pszConvW[cchConvW++] = space[t]; }

    GlobalFree(pszTextW);

    if (cchConvW != cchTextW) {
      pszText = GlobalAlloc(GPTR,cchConvW * 3);

      cchConvM = WideCharToMultiByte(cpEdit,0,pszConvW,cchConvW,pszText,GlobalSize(pszText),NULL,NULL);
      GlobalFree(pszConvW);

      if (iAnchorPos > iCurPos) {
        iCurPos = iSelStart;
        iAnchorPos = iSelStart + cchConvM; }
      else {
        iAnchorPos = iSelStart;
        iCurPos = iSelStart + cchConvM; }

      SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);
      SendMessage(hwnd,SCI_SETTARGETSTART,(WPARAM)iSelStart,0);
      SendMessage(hwnd,SCI_SETTARGETEND,(WPARAM)iSelEnd,0);
      SendMessage(hwnd,SCI_REPLACETARGET,(WPARAM)cchConvM,(LPARAM)pszText);
      //SendMessage(hwnd,SCI_CLEAR,0,0);
      //SendMessage(hwnd,SCI_ADDTEXT,(WPARAM)cchConvW,(LPARAM)pszText);
      SendMessage(hwnd,SCI_SETSEL,(WPARAM)iAnchorPos,(LPARAM)iCurPos);
      SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);

      GlobalFree(pszText); }

    else
      GlobalFree(pszConvW);
  }
}


//=============================================================================
//
//  EditStripFirstCharacter()
//
void EditStripFirstCharacter(HWND hwnd)
{
  int iSelStart = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
  int iSelEnd   = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);

  if (iSelStart == iSelEnd) {
    iSelStart = 0;
    iSelEnd   = SendMessage(hwnd,SCI_GETLENGTH,0,0); }

  if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
  {
    int iLine;

    int iLineStart = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iSelStart,0);
    int iLineEnd   = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iSelEnd,0);

    if (iSelStart > SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLineStart,0))
      iLineStart++;

    if (iSelEnd <= SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLineEnd,0))
      iLineEnd--;

    SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);

    for (iLine = iLineStart; iLine <= iLineEnd; iLine++)
    {
      int iPos = SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLine,0);
      if (SendMessage(hwnd,SCI_GETLINEENDPOSITION,(WPARAM)iLine,0)- iPos > 0)
      {
        SendMessage(hwnd,SCI_SETTARGETSTART,(WPARAM)iPos,0);
        SendMessage(hwnd,SCI_SETTARGETEND,
          (WPARAM)SendMessage(hwnd,SCI_POSITIONAFTER,(WPARAM)iPos,0),0);
        SendMessage(hwnd,SCI_REPLACETARGET,0,(LPARAM)"");
      }
    }
    SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);
  }
  else
    MsgBox(MBINFO,IDS_SELRECT);
}


//=============================================================================
//
//  EditStripTrailingBlanks()
//
void EditStripTrailingBlanks(HWND hwnd)
{
  // Check if there is any selection... simply use a regular expression replace!
  if (SendMessage(hwnd,SCI_GETSELECTIONEND,0,0) - SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0))
  {
    if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
    {
      EDITFINDREPLACE efrTrim = { "[ \t]+$", "", "", "", SCFIND_REGEXP, 0, 0, 0, hwnd };
      EditReplaceAllInSelection(hwnd,&efrTrim,FALSE);
    }
    else
      MsgBox(MBINFO,IDS_SELRECT);
  }
  // Code from SciTE...
  else
  {
    int line;
    int maxLines;
    int lineStart;
    int lineEnd;
    int i;
    char ch;

    SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);
    maxLines = SendMessage(hwnd,SCI_GETLINECOUNT,0,0);
    for (line = 0; line < maxLines; line++)
    {
      lineStart = SendMessage(hwnd,SCI_POSITIONFROMLINE,line,0);
      lineEnd = SendMessage(hwnd,SCI_GETLINEENDPOSITION,line,0);
      i = lineEnd-1;
      ch = (char)SendMessage(hwnd,SCI_GETCHARAT,i,0);
      while ((i >= lineStart) && ((ch == ' ') || (ch == '\t')))
      {
        i--;
        ch = (char)SendMessage(hwnd,SCI_GETCHARAT,i,0);
      }
      if (i < (lineEnd-1))
      {
        SendMessage(hwnd,SCI_SETTARGETSTART,i + 1,0);
        SendMessage(hwnd,SCI_SETTARGETEND,lineEnd,0);
        SendMessage(hwnd,SCI_REPLACETARGET,0,(LPARAM)"");
      }
    }
    SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);
  }
}


//=============================================================================
//
//  EditRemoveBlankLines()
//
void EditRemoveBlankLines(HWND hwnd)
{
  int iSelStart = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
  int iSelEnd   = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);

  if (iSelStart == iSelEnd) {
    iSelStart = 0;
    iSelEnd   = SendMessage(hwnd,SCI_GETLENGTH,0,0); }

  if (SC_SEL_RECTANGLE != SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
  {
    int iLine;

    int iLineStart = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iSelStart,0);
    int iLineEnd   = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iSelEnd,0);

    if (iSelStart > SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLineStart,0))
      iLineStart++;

    if (iSelEnd <= SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLineEnd,0))
      iLineEnd--;

    SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);

    for (iLine = iLineStart; iLine <= iLineEnd; )
    {
      int iPos = SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLine,0);
      if (SendMessage(hwnd,SCI_GETLINEENDPOSITION,(WPARAM)iLine,0) == iPos)
      {
        int iPos2 = SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iLine+1,0);
        SendMessage(hwnd,SCI_SETTARGETSTART,(WPARAM)iPos,0);
        SendMessage(hwnd,SCI_SETTARGETEND,(WPARAM)iPos2,0);
        SendMessage(hwnd,SCI_REPLACETARGET,0,(LPARAM)"");
        iLineEnd--;
      }
      else
        iLine++;
    }
    SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);
  }
  else
    MsgBox(MBINFO,IDS_SELRECT);
}


//=============================================================================
//
//  EditJumpTo()
//
void EditJumpTo(HWND hwnd,int iNewLine,int iNewCol)
{
  int iMaxLine = SendMessage(hwnd,SCI_GETLINECOUNT,0,0);

  // Jumpt to end with line set to -1
  if (iNewLine == -1) {
    SendMessage(hwnd,SCI_DOCUMENTEND,0,0);
    return; }

  // Line maximum is iMaxLine
  iNewLine = min(iNewLine,iMaxLine);

  // Column minimum is 1
  iNewCol = max(iNewCol,1);

  if (iNewLine > 0 && iNewLine <= iMaxLine && iNewCol > 0)
  {
    int iNewPos  = SendMessage(hwnd,SCI_POSITIONFROMLINE,(WPARAM)iNewLine-1,0);
    int iLineEndPos = SendMessage(hwnd,SCI_GETLINEENDPOSITION,(WPARAM)iNewLine-1,0);

    while (iNewCol-1 > SendMessage(hwnd,SCI_GETCOLUMN,(WPARAM)iNewPos,0))
    {
      if (iNewPos >= iLineEndPos)
        break;

      iNewPos = SendMessage(hwnd,SCI_POSITIONAFTER,(WPARAM)iNewPos,0);
    }

    iNewPos = min(iNewPos,iLineEndPos);
    SendMessage(hwnd,SCI_GOTOPOS,(WPARAM)iNewPos,0);
    SendMessage(hwnd,SCI_CHOOSECARETX,0,0);
  }
}


//=============================================================================
//
//  EditToggleBookmark()
//
void EditToggleBookmark(HWND hwnd)
{
  int iCurrentPos = SendMessage(hwnd,SCI_GETCURRENTPOS,0,0);
  int iCurrentLine = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iCurrentPos,0);
  int iMarkerBits = SendMessage(hwnd,SCI_MARKERGET,(WPARAM)iCurrentLine,0);

  if (iMarkerBits & 1) // Bit 0 represents the Notepad2 bookmark
    SendMessage(hwnd,SCI_MARKERDELETE,(WPARAM)iCurrentLine,0);
  else
    SendMessage(hwnd,SCI_MARKERADD,(WPARAM)iCurrentLine,0);
}


//=============================================================================
//
//  EditJumpToBookmark()
//
void EditJumpToBookmark(HWND hwnd,BOOL bNext)
{
  int iCurrentPos = SendMessage(hwnd,SCI_GETCURRENTPOS,0,0);
  int iCurrentLine = SendMessage(hwnd,SCI_LINEFROMPOSITION,(WPARAM)iCurrentPos,0);
  int iMaxLine = SendMessage(hwnd,SCI_GETLINECOUNT,0,0) - 1;

  if (bNext)
  {
    int iMarkerLine = -1;

    if (iCurrentLine < iMaxLine)
      iMarkerLine = SendMessage(hwnd,SCI_MARKERNEXT,(WPARAM)iCurrentLine+1,1);

    if (iMarkerLine == -1)
      iMarkerLine = SendMessage(hwnd,SCI_MARKERNEXT,(WPARAM)0,1);

    if (iMarkerLine != -1) {
      SendMessage(hwnd,SCI_GOTOLINE,(WPARAM)iMarkerLine,0);
      SendMessage(hwnd,SCI_CHOOSECARETX,0,0); }
  }

  else
  {
    int iMarkerLine = -1;

    if (iCurrentLine > 0)
      iMarkerLine = SendMessage(hwnd,SCI_MARKERPREVIOUS,(WPARAM)iCurrentLine-1,1);

    if (iMarkerLine == -1)
      iMarkerLine = SendMessage(hwnd,SCI_MARKERPREVIOUS,(WPARAM)iMaxLine,1);

    if (iMarkerLine != -1) {
      SendMessage(hwnd,SCI_GOTOLINE,(WPARAM)iMarkerLine,0);
      SendMessage(hwnd,SCI_CHOOSECARETX,0,0); }
  }
}


//=============================================================================
//
//  EditClearAllBookmarks()
//
void EditClearAllBookmarks(HWND hwnd)
{
  SendMessage(hwnd,SCI_MARKERDELETEALL,(WPARAM)-1,0);
}


//=============================================================================
//
//  EditFindReplaceDlgProcW()
//
BOOL CALLBACK EditFindReplaceDlgProcW(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM lParam)
{

  LPEDITFINDREPLACE lpefr;
  HANDLE hStrings;
  int i;
  char tch[256+32];
  BOOL bCloseDlg;
  BOOL bIsFindDlg;

  CREATEMRULIST mruFind = { sizeof(CREATEMRULIST), 16, 2, HKEY_CURRENT_USER,
                            "Software\\Notepad2\\Recent Find", NULL };
  CREATEMRULIST mruReplace = { sizeof(CREATEMRULIST), 16, 2, HKEY_CURRENT_USER,
                              "Software\\Notepad2\\Recent Replace", NULL };

  static UINT uCPEdit;

  switch(umsg)
  {

    case WM_INITDIALOG:
      {
        int cchSelection;
        char *lpszSelection;
        char *lpsz;

        SetWindowLong(hwnd,DWL_USER,lParam);
        lpefr = (LPEDITFINDREPLACE)lParam;

        // Get the current code page for Unicode conversion
        uCPEdit = SendMessage(lpefr->hwnd,SCI_GETCODEPAGE,0,0);

        // Load MRUs
        hStrings = CreateMRUList(&mruFind);
        for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
        {
          EnumMRUList(hStrings,i,tch,COUNTOF(tch));
          ComboBox_AddStringA2W(CP_UTF8,GetDlgItem(hwnd,IDC_FINDTEXT),tch);
          //SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_ADDSTRING,0,(LPARAM)tch);
        }
        FreeMRUList(hStrings);
        hStrings = CreateMRUList(&mruReplace);
        for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
        {
          EnumMRUList(hStrings,i,tch,COUNTOF(tch));
          ComboBox_AddStringA2W(CP_UTF8,GetDlgItem(hwnd,IDC_REPLACETEXT),tch);
          //SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_ADDSTRING,0,(LPARAM)tch);
        }
        FreeMRUList(hStrings);

        cchSelection = SendMessage(lpefr->hwnd,SCI_GETSELECTIONEND,0,0) -
                       SendMessage(lpefr->hwnd,SCI_GETSELECTIONSTART,0,0);

        if (cchSelection < 255)
        {
          lpszSelection = GlobalAlloc(GPTR,cchSelection+2);
          SendMessage(lpefr->hwnd,SCI_GETSELTEXT,0,(LPARAM)lpszSelection);

          // Check lpszSelection and truncate bad chars
          lpsz = strchr(lpszSelection,13);
          if (lpsz) *lpsz = '\0';

          lpsz = strchr(lpszSelection,10);
          if (lpsz) *lpsz = '\0';

          lpsz = strchr(lpszSelection,9);
          if (lpsz) *lpsz = '\0';

          SetDlgItemTextA2W(uCPEdit,hwnd,IDC_FINDTEXT,lpszSelection);
          GlobalFree(lpszSelection);
        }

        SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_LIMITTEXT,255,0);
        SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_SETEXTENDEDUI,TRUE,0);

        if (!GetWindowTextLengthW(GetDlgItem(hwnd,IDC_FINDTEXT)))
          SetDlgItemTextA2W(CP_UTF8,hwnd,IDC_FINDTEXT,lpefr->szFindUTF8);

        if (GetDlgItem(hwnd,IDC_REPLACETEXT))
        {
          SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_LIMITTEXT,255,0);
          SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_SETEXTENDEDUI,TRUE,0);
          SetDlgItemTextA2W(CP_UTF8,hwnd,IDC_REPLACETEXT,lpefr->szReplaceUTF8);
        }

        if (lpefr->fuFlags & SCFIND_MATCHCASE)
          CheckDlgButton(hwnd,IDC_FINDCASE,BST_CHECKED);

        if (lpefr->fuFlags & SCFIND_WHOLEWORD)
          CheckDlgButton(hwnd,IDC_FINDWORD,BST_CHECKED);

        if (lpefr->fuFlags & SCFIND_WORDSTART)
          CheckDlgButton(hwnd,IDC_FINDSTART,BST_CHECKED);

        if (lpefr->fuFlags & SCFIND_REGEXP)
          CheckDlgButton(hwnd,IDC_FINDREGEXP,BST_CHECKED);

        if (lpefr->bFindUp)
          CheckDlgButton(hwnd,IDC_FINDUP,BST_CHECKED);

        if (GetDlgItem(hwnd,IDC_REPLACE)) {
          if (lpefr->bReplaceClose)
            CheckDlgButton(hwnd,IDC_FINDCLOSE,BST_CHECKED); }
        else {
          if (lpefr->bFindClose)
            CheckDlgButton(hwnd,IDC_FINDCLOSE,BST_CHECKED); }

        CenterDlgInParent(hwnd);
      }
      return TRUE;


    case WM_COMMAND:

      switch(LOWORD(wParam))
      {

        case IDC_FINDTEXT:
        case IDC_REPLACETEXT:
          {
            BOOL bEnable = (GetWindowTextLengthW(GetDlgItem(hwnd,IDC_FINDTEXT)) ||
                            CB_ERR != SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_GETCURSEL,0,0));

            EnableWindow(GetDlgItem(hwnd,IDOK),bEnable);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACE),bEnable);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEALL),bEnable);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEINSEL),bEnable);

            if (HIWORD(wParam) == CBN_CLOSEUP) {
              LONG lSelEnd;
              SendDlgItemMessage(hwnd,LOWORD(wParam),CB_GETEDITSEL,0,(LPARAM)&lSelEnd);
              SendDlgItemMessage(hwnd,LOWORD(wParam),CB_SETEDITSEL,0,MAKELPARAM(lSelEnd,lSelEnd)); }
          }
          break;

        case IDOK:
        case IDC_REPLACE:
        case IDC_REPLACEALL:
        case IDC_REPLACEINSEL:

          (LONG)lpefr = GetWindowLong(hwnd,DWL_USER);

          if (!GetDlgItemTextA2W(uCPEdit,hwnd,IDC_FINDTEXT,lpefr->szFind,COUNTOF(lpefr->szFind)))
          {
            EnableWindow(GetDlgItem(hwnd,IDOK),FALSE);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACE),FALSE);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEALL),FALSE);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEINSEL),FALSE);
            return TRUE;
          }

          if (GetDlgItem(hwnd,IDC_REPLACETEXT))
            GetDlgItemTextA2W(uCPEdit,hwnd,IDC_REPLACETEXT,lpefr->szReplace,COUNTOF(lpefr->szReplace));

          lpefr->fuFlags = 0;

          if (IsDlgButtonChecked(hwnd,IDC_FINDCASE) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_MATCHCASE;

          if (IsDlgButtonChecked(hwnd,IDC_FINDWORD) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_WHOLEWORD;

          if (IsDlgButtonChecked(hwnd,IDC_FINDSTART) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_WORDSTART;

          if (IsDlgButtonChecked(hwnd,IDC_FINDREGEXP) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_REGEXP;

          lpefr->bFindUp = (IsDlgButtonChecked(hwnd,IDC_FINDUP) == BST_CHECKED) ? TRUE : FALSE;

          bIsFindDlg = (GetDlgItem(hwnd,IDC_REPLACE) == NULL);
          if (bIsFindDlg) {
            lpefr->bFindClose = (IsDlgButtonChecked(hwnd,IDC_FINDCLOSE) == BST_CHECKED) ? TRUE : FALSE; }
          else {
            lpefr->bReplaceClose = (IsDlgButtonChecked(hwnd,IDC_FINDCLOSE) == BST_CHECKED) ? TRUE : FALSE; }

          // Save MRUs
          if (lstrlen(lpefr->szFind))
          {
            if (GetDlgItemTextA2W(CP_UTF8,hwnd,IDC_FINDTEXT,lpefr->szFindUTF8,COUNTOF(lpefr->szFindUTF8))) {
              hStrings = CreateMRUList(&mruFind);
              AddMRUString(hStrings,lpefr->szFindUTF8);
              FreeMRUList(hStrings); }
          }
          if (lstrlen(lpefr->szReplace))
          {
            if (GetDlgItemTextA2W(CP_UTF8,hwnd,IDC_REPLACETEXT,lpefr->szReplaceUTF8,COUNTOF(lpefr->szReplaceUTF8))) {
              hStrings = CreateMRUList(&mruReplace);
              AddMRUString(hStrings,lpefr->szReplaceUTF8);
              FreeMRUList(hStrings); }
          }

          if (bIsFindDlg) {
            bCloseDlg = lpefr->bFindClose; }
          else {
            if (LOWORD(wParam) == IDOK)
              bCloseDlg = FALSE;
            else
              bCloseDlg = lpefr->bReplaceClose; }

          if (bCloseDlg)
            EndDialog(hwnd,LOWORD(wParam));

          else {

            // Reload MRUs
            SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_RESETCONTENT,0,0);
            SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_RESETCONTENT,0,0);

            hStrings = CreateMRUList(&mruFind);
            for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
            {
              EnumMRUList(hStrings,i,tch,COUNTOF(tch));
              ComboBox_AddStringA2W(CP_UTF8,GetDlgItem(hwnd,IDC_FINDTEXT),tch);
              //SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_ADDSTRING,0,(LPARAM)tch);
            }
            FreeMRUList(hStrings);
            hStrings = CreateMRUList(&mruReplace);
            for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
            {
              EnumMRUList(hStrings,i,tch,COUNTOF(tch));
              ComboBox_AddStringA2W(CP_UTF8,GetDlgItem(hwnd,IDC_REPLACETEXT),tch);
              //SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_ADDSTRING,0,(LPARAM)tch);
            }
            FreeMRUList(hStrings);

            SetDlgItemTextA2W(CP_UTF8,hwnd,IDC_FINDTEXT,lpefr->szFindUTF8);
            SetDlgItemTextA2W(CP_UTF8,hwnd,IDC_REPLACETEXT,lpefr->szReplaceUTF8);

            SendMessage(hwnd,WM_NEXTDLGCTL,(WPARAM)(GetFocus()),1);

            switch (LOWORD(wParam))
            {
              case IDOK: // find
                if (!lpefr->bFindUp)
                  EditFindNext(lpefr->hwnd,lpefr);
                else
                  EditFindPrev(lpefr->hwnd,lpefr);
                break;

              case IDC_REPLACE:
                EditReplace(lpefr->hwnd,lpefr);
                break;

              case IDC_REPLACEALL:
                EditReplaceAll(lpefr->hwnd,lpefr,TRUE);
                break;

              case IDC_REPLACEINSEL:
                EditReplaceAllInSelection(lpefr->hwnd,lpefr,TRUE);
                break;
            }
          }
          break;


        case IDCANCEL:

          EndDialog(hwnd,IDCANCEL);
          break;

      }

      return TRUE;

  }

  return FALSE;

}


//=============================================================================
//
//  EditFindReplaceDlgProcA()
//
BOOL CALLBACK EditFindReplaceDlgProcA(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM lParam)
{

  LPEDITFINDREPLACE lpefr;
  HANDLE hStrings;
  int i;
  char tch[256+32];
  BOOL bCloseDlg;
  BOOL bIsFindDlg;

  CREATEMRULIST mruFind = { sizeof(CREATEMRULIST), 16, 2, HKEY_CURRENT_USER,
                            "Software\\Notepad2\\Recent Find", NULL };
  CREATEMRULIST mruReplace = { sizeof(CREATEMRULIST), 16, 2, HKEY_CURRENT_USER,
                              "Software\\Notepad2\\Recent Replace", NULL };

  switch(umsg)
  {

    case WM_INITDIALOG:
      {
        int cchSelection;
        char *lpszSelection;
        char *lpsz;

        SetWindowLong(hwnd,DWL_USER,lParam);
        lpefr = (LPEDITFINDREPLACE)lParam;

        // Load MRUs
        hStrings = CreateMRUList(&mruFind);
        for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
        {
          EnumMRUList(hStrings,i,tch,COUNTOF(tch));
          SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_ADDSTRING,0,(LPARAM)tch);
        }
        FreeMRUList(hStrings);
        hStrings = CreateMRUList(&mruReplace);
        for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
        {
          EnumMRUList(hStrings,i,tch,COUNTOF(tch));
          SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_ADDSTRING,0,(LPARAM)tch);
        }
        FreeMRUList(hStrings);

        cchSelection = SendMessage(lpefr->hwnd,SCI_GETSELECTIONEND,0,0) -
                       SendMessage(lpefr->hwnd,SCI_GETSELECTIONSTART,0,0);

        if (cchSelection < 255)
        {
          lpszSelection = GlobalAlloc(GPTR,cchSelection+2);
          SendMessage(lpefr->hwnd,SCI_GETSELTEXT,0,(LPARAM)lpszSelection);

          // Check lpszSelection and truncate bad chars
          lpsz = strchr(lpszSelection,13);
          if (lpsz) *lpsz = '\0';

          lpsz = strchr(lpszSelection,10);
          if (lpsz) *lpsz = '\0';

          lpsz = strchr(lpszSelection,9);
          if (lpsz) *lpsz = '\0';

          SetDlgItemText(hwnd,IDC_FINDTEXT,lpszSelection);
          GlobalFree(lpszSelection);
        }

        SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_LIMITTEXT,255,0);
        SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_SETEXTENDEDUI,TRUE,0);

        if (!GetWindowTextLength(GetDlgItem(hwnd,IDC_FINDTEXT)))
          SetDlgItemText(hwnd,IDC_FINDTEXT,lpefr->szFind);

        if (GetDlgItem(hwnd,IDC_REPLACETEXT))
        {
          SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_LIMITTEXT,255,0);
          SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_SETEXTENDEDUI,TRUE,0);
          SetDlgItemText(hwnd,IDC_REPLACETEXT,lpefr->szReplace);
        }

        if (lpefr->fuFlags & SCFIND_MATCHCASE)
          CheckDlgButton(hwnd,IDC_FINDCASE,BST_CHECKED);

        if (lpefr->fuFlags & SCFIND_WHOLEWORD)
          CheckDlgButton(hwnd,IDC_FINDWORD,BST_CHECKED);

        if (lpefr->fuFlags & SCFIND_WORDSTART)
          CheckDlgButton(hwnd,IDC_FINDSTART,BST_CHECKED);

        if (lpefr->fuFlags & SCFIND_REGEXP)
          CheckDlgButton(hwnd,IDC_FINDREGEXP,BST_CHECKED);

        if (lpefr->bFindUp)
          CheckDlgButton(hwnd,IDC_FINDUP,BST_CHECKED);

        if (GetDlgItem(hwnd,IDC_REPLACE)) {
          if (lpefr->bReplaceClose)
            CheckDlgButton(hwnd,IDC_FINDCLOSE,BST_CHECKED); }
        else {
          if (lpefr->bFindClose)
            CheckDlgButton(hwnd,IDC_FINDCLOSE,BST_CHECKED); }

        CenterDlgInParent(hwnd);
      }
      return TRUE;


    case WM_COMMAND:

      switch(LOWORD(wParam))
      {

        case IDC_FINDTEXT:
        case IDC_REPLACETEXT:
          {
            BOOL bEnable = (GetWindowTextLength(GetDlgItem(hwnd,IDC_FINDTEXT)) ||
                            CB_ERR != SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_GETCURSEL,0,0));

            EnableWindow(GetDlgItem(hwnd,IDOK),bEnable);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACE),bEnable);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEALL),bEnable);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEINSEL),bEnable);

            if (HIWORD(wParam) == CBN_CLOSEUP) {
              LONG lSelEnd;
              SendDlgItemMessage(hwnd,LOWORD(wParam),CB_GETEDITSEL,0,(LPARAM)&lSelEnd);
              SendDlgItemMessage(hwnd,LOWORD(wParam),CB_SETEDITSEL,0,MAKELPARAM(lSelEnd,lSelEnd)); }
          }
          break;

        case IDOK:
        case IDC_REPLACE:
        case IDC_REPLACEALL:
        case IDC_REPLACEINSEL:

          (LONG)lpefr = GetWindowLong(hwnd,DWL_USER);

          if (!GetDlgItemText(hwnd,IDC_FINDTEXT,lpefr->szFind,COUNTOF(lpefr->szFind)))
          {
            EnableWindow(GetDlgItem(hwnd,IDOK),FALSE);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACE),FALSE);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEALL),FALSE);
            EnableWindow(GetDlgItem(hwnd,IDC_REPLACEINSEL),FALSE);
            return TRUE;
          }

          if (GetDlgItem(hwnd,IDC_REPLACETEXT))
            GetDlgItemText(hwnd,IDC_REPLACETEXT,lpefr->szReplace,COUNTOF(lpefr->szReplace));

          lpefr->fuFlags = 0;

          if (IsDlgButtonChecked(hwnd,IDC_FINDCASE) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_MATCHCASE;

          if (IsDlgButtonChecked(hwnd,IDC_FINDWORD) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_WHOLEWORD;

          if (IsDlgButtonChecked(hwnd,IDC_FINDSTART) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_WORDSTART;

          if (IsDlgButtonChecked(hwnd,IDC_FINDREGEXP) == BST_CHECKED)
            lpefr->fuFlags |= SCFIND_REGEXP;

          lpefr->bFindUp = (IsDlgButtonChecked(hwnd,IDC_FINDUP) == BST_CHECKED) ? TRUE : FALSE;

          bIsFindDlg = (GetDlgItem(hwnd,IDC_REPLACE) == NULL);
          if (bIsFindDlg) {
            lpefr->bFindClose = (IsDlgButtonChecked(hwnd,IDC_FINDCLOSE) == BST_CHECKED) ? TRUE : FALSE; }
          else {
            lpefr->bReplaceClose = (IsDlgButtonChecked(hwnd,IDC_FINDCLOSE) == BST_CHECKED) ? TRUE : FALSE; }

          // Save MRUs
          if (lstrlen(lpefr->szFind))
          {
            hStrings = CreateMRUList(&mruFind);
            AddMRUString(hStrings,lpefr->szFind);
            FreeMRUList(hStrings);
          }
          if (lstrlen(lpefr->szReplace))
          {
            hStrings = CreateMRUList(&mruReplace);
            AddMRUString(hStrings,lpefr->szReplace);
            FreeMRUList(hStrings);
          }

          if (bIsFindDlg) {
            bCloseDlg = lpefr->bFindClose; }
          else {
            if (LOWORD(wParam) == IDOK)
              bCloseDlg = FALSE;
            else
              bCloseDlg = lpefr->bReplaceClose; }

          if (bCloseDlg)
            EndDialog(hwnd,LOWORD(wParam));

          else {

            // Reload MRUs
            SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_RESETCONTENT,0,0);
            SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_RESETCONTENT,0,0);

            hStrings = CreateMRUList(&mruFind);
            for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
            {
              EnumMRUList(hStrings,i,tch,COUNTOF(tch));
              SendDlgItemMessage(hwnd,IDC_FINDTEXT,CB_ADDSTRING,0,(LPARAM)tch);
            }
            FreeMRUList(hStrings);
            hStrings = CreateMRUList(&mruReplace);
            for (i = 0; i < EnumMRUList(hStrings,0,NULL,0); i++)
            {
              EnumMRUList(hStrings,i,tch,COUNTOF(tch));
              SendDlgItemMessage(hwnd,IDC_REPLACETEXT,CB_ADDSTRING,0,(LPARAM)tch);
            }
            FreeMRUList(hStrings);

            SetDlgItemText(hwnd,IDC_FINDTEXT,lpefr->szFind);
            SetDlgItemText(hwnd,IDC_REPLACETEXT,lpefr->szReplace);

            SendMessage(hwnd,WM_NEXTDLGCTL,(WPARAM)(GetFocus()),1);

            switch (LOWORD(wParam))
            {
              case IDOK: // find
                if (!lpefr->bFindUp)
                  EditFindNext(lpefr->hwnd,lpefr);
                else
                  EditFindPrev(lpefr->hwnd,lpefr);
                break;

              case IDC_REPLACE:
                EditReplace(lpefr->hwnd,lpefr);
                break;

              case IDC_REPLACEALL:
                EditReplaceAll(lpefr->hwnd,lpefr,TRUE);
                break;

              case IDC_REPLACEINSEL:
                EditReplaceAllInSelection(lpefr->hwnd,lpefr,TRUE);
                break;
            }
          }
          break;


        case IDCANCEL:

          EndDialog(hwnd,IDCANCEL);
          break;

      }

      return TRUE;

  }

  return FALSE;

}


//=============================================================================
//
//  EditFindReplaceDlg()
//
BOOL EditFindReplaceDlg(HWND hwnd,LPCEDITFINDREPLACE lpefr,BOOL bReplace)
{

  int iResult;

  lpefr->hwnd = hwnd;

  if (IsWindowsNT()) {
    iResult = DialogBoxParamW(
                g_hInstance,
                (bReplace) ? MAKEINTRESOURCEW(IDD_REPLACE) : MAKEINTRESOURCEW(IDD_FIND),
                GetParent(hwnd),
                EditFindReplaceDlgProcW,
                (LPARAM) lpefr); }

  else {
    iResult = DialogBoxParamA(
                g_hInstance,
                (bReplace) ? MAKEINTRESOURCE(IDD_REPLACE) : MAKEINTRESOURCE(IDD_FIND),
                GetParent(hwnd),
                EditFindReplaceDlgProcA,
                (LPARAM) lpefr); }

  if (iResult == IDOK) { // IDOK always means "find"
    if (!lpefr->bFindUp)
      return EditFindNext(hwnd,lpefr);
    else
      return EditFindPrev(hwnd,lpefr); }

  if (iResult == IDC_REPLACE)
    return EditReplace(hwnd,lpefr);

  if (iResult == IDC_REPLACEALL)
    return EditReplaceAll(hwnd,lpefr,TRUE);

  if (iResult == IDC_REPLACEINSEL)
    return EditReplaceAllInSelection(hwnd,lpefr,TRUE);

  return FALSE;

}


//=============================================================================
//
//  EditFindNext()
//
BOOL EditFindNext(HWND hwnd,LPCEDITFINDREPLACE lpefr)
{

  struct TextToFind ttf;
  int iPos;

  if (!lstrlen(lpefr->szFind))
    return EditFindReplaceDlg(hwnd,lpefr,FALSE);

  ZeroMemory(&ttf,sizeof(ttf));

  ttf.chrg.cpMin = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);
  ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);
  ttf.lpstrText = lpefr->szFind;

  iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf);

  if (iPos == -1 && ttf.chrg.cpMin > 0)
  {
    // wrapped
    MsgBox(MBINFO,IDS_FIND_WRAPFW);

    ttf.chrg.cpMin = 0;
    iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf);
  }

  if (iPos == -1)
  {
    // notfound
    MsgBox(MBINFO,IDS_NOTFOUND);
    return FALSE;
  }

  SendMessage(hwnd,SCI_SETSEL,ttf.chrgText.cpMin,ttf.chrgText.cpMax);

  return TRUE;

}


//=============================================================================
//
//  EditFindPrev()
//
BOOL EditFindPrev(HWND hwnd,LPCEDITFINDREPLACE lpefr)
{

  struct TextToFind ttf;
  int iPos;
  int iLength;

  if (!lstrlen(lpefr->szFind))
    return EditFindReplaceDlg(hwnd,lpefr,FALSE);

  ZeroMemory(&ttf,sizeof(ttf));

  ttf.chrg.cpMin = max(0,SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0)-1);
  ttf.chrg.cpMax = 0;
  ttf.lpstrText = lpefr->szFind;

  iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf);

  iLength = SendMessage(hwnd,SCI_GETLENGTH,0,0);
  if (iPos == -1 && ttf.chrg.cpMin < iLength)
  {
    // wrapped
    MsgBox(MBINFO,IDS_FIND_WRAPRE);

    ttf.chrg.cpMin = iLength;
    iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf);
  }

  if (iPos == -1)
  {
    // notfound
    MsgBox(MBINFO,IDS_NOTFOUND);
    return FALSE;
  }

  SendMessage(hwnd,SCI_SETSEL,ttf.chrgText.cpMin,ttf.chrgText.cpMax);

  return TRUE;

}


//=============================================================================
//
//  EditReplace()
//
BOOL EditReplace(HWND hwnd,LPCEDITFINDREPLACE lpefr)
{

  struct TextToFind ttf;
  int iPos;
  int iReplaceMsg = (lpefr->fuFlags & SCFIND_REGEXP) ? SCI_REPLACETARGETRE : SCI_REPLACETARGET;

  if (!lstrlen(lpefr->szFind))
    return EditFindReplaceDlg(hwnd,lpefr,TRUE);

  ZeroMemory(&ttf,sizeof(ttf));

  ttf.chrg.cpMin = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0); // Start!
  ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);
  ttf.lpstrText = lpefr->szFind;

  iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf);

  if (iPos == -1 && ttf.chrg.cpMin > 0)
  {
    // wrapped
    MsgBox(MBINFO,IDS_FIND_WRAPFW);

    ttf.chrg.cpMin = 0;
    iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf);
  }

  if (iPos == -1)
  {
    // notfound
    MsgBox(MBINFO,IDS_NOTFOUND);
    return FALSE;
  }

  //SendMessage(hwnd,SCI_SETSEL,ttf.chrgText.cpMin,ttf.chrgText.cpMax);
  SendMessage(hwnd,SCI_SETTARGETSTART,ttf.chrgText.cpMin,0);
  SendMessage(hwnd,SCI_SETTARGETEND,ttf.chrgText.cpMax,0);
  SendMessage(hwnd,iReplaceMsg,(WPARAM)-1,(LPARAM)lpefr->szReplace);

  SendMessage(hwnd,SCI_SETSEL,SendMessage(hwnd,SCI_GETTARGETSTART,0,0),SendMessage(hwnd,SCI_GETTARGETEND,0,0));

  return TRUE;

}


//=============================================================================
//
//  EditReplaceAll()
//
BOOL EditReplaceAll(HWND hwnd,LPCEDITFINDREPLACE lpefr,BOOL bShowInfo)
{

  struct TextToFind ttf;
  int iPos;
  int iCount = 0;
  int iReplaceMsg = (lpefr->fuFlags & SCFIND_REGEXP) ? SCI_REPLACETARGETRE : SCI_REPLACETARGET;
  int iReplaceLen = lstrlen(lpefr->szReplace);

  if (!lstrlen(lpefr->szFind))
    return EditFindReplaceDlg(hwnd,lpefr,TRUE);

  // Show wait cursor...
  SendMessage(hwnd,SCI_SETCURSOR,SC_CURSORWAIT,0);

  ZeroMemory(&ttf,sizeof(ttf));

  ttf.chrg.cpMin = 0;
  ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);
  ttf.lpstrText = lpefr->szFind;

  while ((iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf)) != -1)
  {
    iCount++;

    if (iCount == 1)
      SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);

    SendMessage(hwnd,SCI_SETTARGETSTART,ttf.chrgText.cpMin,0);
    SendMessage(hwnd,SCI_SETTARGETEND,ttf.chrgText.cpMax,0);
    SendMessage(hwnd,iReplaceMsg,(WPARAM)-1,(LPARAM)lpefr->szReplace);

    ttf.chrg.cpMin = SendMessage(hwnd,SCI_GETTARGETEND,0,0);
    ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);

    if (ttf.chrg.cpMin == ttf.chrg.cpMax ||
        (ttf.chrgText.cpMin == ttf.chrgText.cpMax && iReplaceLen == 0)) // check for empty target and empty replace
      break;
  }

  if (iCount)
    SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);

  // Remove wait cursor
  SendMessage(hwnd,SCI_SETCURSOR,SC_CURSORNORMAL,0);

  if (bShowInfo)
    MsgBox(MBINFO,IDS_REPLCOUNT,iCount);

  return TRUE;

}


//=============================================================================
//
//  EditReplaceAllInSelection()
//
BOOL EditReplaceAllInSelection(HWND hwnd,LPCEDITFINDREPLACE lpefr,BOOL bShowInfo)
{

  struct TextToFind ttf;
  int iPos;
  int iCount = 0;
  int iReplaceMsg = (lpefr->fuFlags & SCFIND_REGEXP) ? SCI_REPLACETARGETRE : SCI_REPLACETARGET;
  int iReplaceLen = lstrlen(lpefr->szReplace);
  BOOL fCancel = FALSE;

  if (SC_SEL_RECTANGLE == SendMessage(hwnd,SCI_GETSELECTIONMODE,0,0))
  {
    MsgBox(MBINFO,IDS_SELRECT);
    return FALSE;
  }

  if (!lstrlen(lpefr->szFind))
    return EditFindReplaceDlg(hwnd,lpefr,TRUE);

  // Show wait cursor...
  SendMessage(hwnd,SCI_SETCURSOR,SC_CURSORWAIT,0);

  ZeroMemory(&ttf,sizeof(ttf));

  ttf.chrg.cpMin = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
  ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);
  ttf.lpstrText = lpefr->szFind;

  while ((iPos = SendMessage(hwnd,SCI_FINDTEXT,lpefr->fuFlags,(LPARAM)&ttf)) != -1 && !fCancel)
  {
    if (ttf.chrgText.cpMin >= SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0) &&
        ttf.chrgText.cpMax <= SendMessage(hwnd,SCI_GETSELECTIONEND,0,0))
    {
      iCount++;

      if (iCount == 1)
        SendMessage(hwnd,SCI_BEGINUNDOACTION,0,0);

      SendMessage(hwnd,SCI_SETTARGETSTART,ttf.chrgText.cpMin,0);
      SendMessage(hwnd,SCI_SETTARGETEND,ttf.chrgText.cpMax,0);
      SendMessage(hwnd,iReplaceMsg,(WPARAM)-1,(LPARAM)lpefr->szReplace);

      ttf.chrg.cpMin = SendMessage(hwnd,SCI_GETTARGETEND,0,0);
      ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);
    }

    else
    {
      //ttf.chrg.cpMin = ttf.chrgText.cpMax;
      //ttf.chrg.cpMax = SendMessage(hwnd,SCI_GETLENGTH,0,0);

      // gone across selection, cancel
      fCancel = TRUE;
    }

    if (ttf.chrg.cpMin == ttf.chrg.cpMax ||
        (ttf.chrgText.cpMin == ttf.chrgText.cpMax && iReplaceLen == 0)) // check for empty target and empty replace
      break;
  }

  if (iCount)
    SendMessage(hwnd,SCI_ENDUNDOACTION,0,0);

  // Remove wait cursor
  SendMessage(hwnd,SCI_SETCURSOR,SC_CURSORNORMAL,0);

  if (bShowInfo)
    MsgBox(MBINFO,IDS_REPLCOUNT,iCount);

  return TRUE;

}


//=============================================================================
//
//  EditLinenumDlgProc()
//
BOOL CALLBACK EditLinenumDlgProc(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM lParam)
{

  switch(umsg)
  {

    case WM_INITDIALOG:
      {

        int iCurLine = SendMessage(hwndEdit,SCI_LINEFROMPOSITION,
                         SendMessage(hwndEdit,SCI_GETCURRENTPOS,0,0),0)+1;

        SetDlgItemInt(hwnd,IDC_LINENUM,iCurLine,FALSE);
        SendDlgItemMessage(hwnd,IDC_LINENUM,EM_LIMITTEXT,15,0);

        SendDlgItemMessage(hwnd,IDC_COLNUM,EM_LIMITTEXT,15,0);

        CenterDlgInParent(hwnd);

      }
      return TRUE;


    case WM_COMMAND:

      switch(LOWORD(wParam))
      {

        case IDOK: {

          BOOL fTranslated;
          BOOL fTranslated2;

          int iNewCol;

          int iNewLine = GetDlgItemInt(hwnd,IDC_LINENUM,&fTranslated,FALSE);
          int iMaxLine = SendMessage(hwndEdit,SCI_GETLINECOUNT,0,0);

          if (SendDlgItemMessage(hwnd,IDC_COLNUM,WM_GETTEXTLENGTH,0,0) > 0)
            iNewCol = GetDlgItemInt(hwnd,IDC_COLNUM,&fTranslated2,FALSE);
          else {
            iNewCol = 1;
            fTranslated2 = TRUE; }

          if (!fTranslated || !fTranslated2)
          {
            PostMessage(hwnd,WM_NEXTDLGCTL,(WPARAM)(GetDlgItem(hwnd,(!fTranslated) ? IDC_LINENUM : IDC_COLNUM)),1);
            return TRUE;
          }

          if (iNewLine > 0 && iNewLine <= iMaxLine && iNewCol > 0)
          {
            //int iNewPos  = SendMessage(hwndEdit,SCI_POSITIONFROMLINE,(WPARAM)iNewLine-1,0);
            //int iLineEndPos = SendMessage(hwndEdit,SCI_GETLINEENDPOSITION,(WPARAM)iNewLine-1,0);

            //while (iNewCol-1 > SendMessage(hwndEdit,SCI_GETCOLUMN,(WPARAM)iNewPos,0))
            //{
            //  if (iNewPos >= iLineEndPos)
            //    break;

            //  iNewPos = SendMessage(hwndEdit,SCI_POSITIONAFTER,(WPARAM)iNewPos,0);
            //}

            //iNewPos = min(iNewPos,iLineEndPos);
            //SendMessage(hwndEdit,SCI_GOTOPOS,(WPARAM)iNewPos,0);
            //SendMessage(hwndEdit,SCI_CHOOSECARETX,0,0);

            EditJumpTo(hwndEdit,iNewLine,iNewCol);

            EndDialog(hwnd,IDOK);
          }

          else
            PostMessage(hwnd,WM_NEXTDLGCTL,(WPARAM)(GetDlgItem(hwnd,(!(iNewLine > 0 && iNewLine <= iMaxLine)) ? IDC_LINENUM : IDC_COLNUM)),1);

          }
          break;


        case IDCANCEL:
          EndDialog(hwnd,IDCANCEL);
          break;

      }

      return TRUE;

  }

  return FALSE;

}


//=============================================================================
//
//  EditLinenumDlg()
//
BOOL EditLinenumDlg(HWND hwnd)
{

  if (IDOK == DialogBoxParam(g_hInstance,MAKEINTRESOURCE(IDD_LINENUM),
                             GetParent(hwnd),EditLinenumDlgProc,(LPARAM)hwnd))
    return TRUE;

  else
    return FALSE;

}



///   End of Edit.c   \\\
