Page 4 of 5

Re: Status of source code changes suggested in November 2014

Posted: Sat Sep 12, 2015 3:12 pm
by kcarmody
Incorporating the changes recently suggested by Claudio, I'm again submitting the following changes, almost all of which relate to the RichEditBox control. I've developed a patch, based on Claudio's current patch (patch 6), which incorporates these changes. My patch should be installed on top of Claudio's patch.

Individual modified source files are at http://kevincarmody.com/hmg/, and a zip of files in the patch is at http://kevincarmody.com/hmg/HmgChangeProposal.zip.

This patch includes an overhauled Rich Edit demo, which uses all the source code changes (except for the HasNonAnsiChars property and the SelPasteSpecial method). The new Rich Edit demo is at http://kevincarmody.com/hmg/SAMPLES/Con ... chEditBox/, including the executable at http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.exe.

New function GetRichEditFileType( cFile, lUtf8Test ) --> nFileType

This function returns a file type (RTF, ANSI, UTF-16, UTF-8) which can be used in RichEditLoadFile and RichEditSaveFile methods of RichEditBox control.

http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 52, 512-578

Code: Select all

// Following line added by Kevin Carmody, September 2015
#include "fileio.ch"

Code: Select all

// Following function added by Kevin Carmody, September 2015

*-----------------------------------------------------------------------------*
FUNCTION GetRichEditFileType ( cFile, lUtf8Test )
*-----------------------------------------------------------------------------*

LOCAL hFile    := FOPEN( cFile, FO_READ )
LOCAL cBuffer  := SPACE( 5 )
LOCAL nBufRead := 0
LOCAL nType    := 0

/*
  The following code block tests whether an umnarked text file contains
  valid UTF-8 text with non-ASCII characters.
*/

LOCAL bIsUtf8NonAscii := {||
   LOCAL lUtf8NonAscii := .N.
   LOCAL cPartial := ''
      cBuffer  := SPACE( 0x400 )
      nBufRead := 1
      BEGIN SEQUENCE
         WHILE nBufRead > 0
            nBufRead := FREAD( hFile, @cBuffer, 0x400 )
            IF nBufRead > 0 .AND. HMG_IsUtf8( cPartial + cBuffer, .N., .Y., @cPartial )
               lUtf8NonAscii := .Y.
               BREAK
            ENDIF
         ENDDO
         IF ! EMPTY( cPartial )
            lUtf8NonAscii := .N.
         ENDIF
      END SEQUENCE
   RETURN lUtf8NonAscii
   }

   BEGIN SEQUENCE

      IF hFile < 0
         BREAK
      ENDIF
      nBufRead := FREAD( hFile, @cBuffer, 5 )
      DO CASE
      CASE nBufRead >= 5 .AND. LEFT( cBuffer, 5 ) == "{\rtf"
         nType := RICHEDITFILE_RTF
      CASE nBufRead >= 3 .AND. LEFT( cBuffer, 3 ) == E"\xEF\xBB\xBF"
         nType := RICHEDITFILE_UTF8
      CASE nBufRead >= 2 .AND. LEFT( cBuffer, 2 ) == E"\xFF\xFE"
         nType := RICHEDITFILE_UTF16LE
      CASE nBufRead >= 2 .AND. LEFT( cBuffer, 2 ) == E"\xFE\xFF"
         nType := RICHEDITFILE_UTF16BE
      CASE ! EMPTY( lUtf8Test ) .AND. bIsUtf8NonAscii:EVAL( )
         nType := RICHEDITFILE_UTF8
      OTHERWISE
         nType := RICHEDITFILE_ANSI
      ENDCASE

   END SEQUENCE

   FCLOSE( hFile )

RETURN nType   
This function is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - line 880

Code: Select all

   LOCAL nFormat        := GETRICHEDITFILETYPE(cFileName, .Y.)
New function Utf16ByteSwap( cInFile, cOutFile )

This function supports the UTF-16 BE (big endian) file type for the RichEditLoadFile and RichEditSaveFile methods of RichEditBox control.

http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 52, 583-627

Code: Select all

// Following line added by Kevin Carmody, September 2015
#include "fileio.ch"

Code: Select all

// Following function added by Kevin Carmody, September 2015

*-----------------------------------------------------------------------------*
FUNCTION Utf16ByteSwap( cInFile, cOutFile )
*-----------------------------------------------------------------------------*

LOCAL hInFile   := FOPEN( cInFile , FO_READ )
LOCAL hOutFile  := FCREATE( cOutFile )
LOCAL cInBuffer := SPACE( 0x400 )
LOCAL nBufRead  := 1
LOCAL lSuccess  := .N.
LOCAL cOutBuffer, cBytePair, nBufWrite, nByte

BEGIN SEQUENCE

   IF hInFile < 0
      BREAK
   ENDIF
   IF hOutFile < 0
      BREAK
   ENDIF
   WHILE nBufRead > 0
      cOutBuffer := ""
      nBufRead   := FREAD( hInFile, @cInBuffer, 0x400 )
      IF nBufRead > 0
         FOR nByte := 1 TO nBufRead STEP 2
            cBytePair  := SUBSTR( cInBuffer, nByte, 2 )
            cOutBuffer += RIGHT( cBytePair, 1 ) + LEFT( cBytePair, 1 )
         NEXT
         nBufWrite := FWRITE( hOutFile, cOutBuffer )
         IF nBufWrite < nBufRead
            BREAK
         ENDIF
      ENDIF
   ENDDO
   lSuccess := .Y.
    
END SEQUENCE

FCLOSE( hInFile )
FCLOSE( hOutFile )

RETURN lSuccess
This function is used in RichEditBox_RichEditLoadFile() and RichEditBox_RichEditSaveFile() - see below.

New function HMG_IsNonASCII( cString )

This function determines whether a string contains any non-ASCII characters.

http://kevincarmody.com/hmg/SOURCE/h_UNICODE_String.prg - lines 262-280

Code: Select all

// Following function added by Kevin Carmody, September 2015

FUNCTION HMG_IsNonASCII( cString )
/*
   This function determines whether a string contains one or more
   non-ASCII characters.
*/

LOCAL lNonASCII := .F.
LOCAL cChar

   BEGIN SEQUENCE
      FOR EACH cChar IN cString
         IF cChar >= CHR( 0x80 )
            lNonASCII := .T.
            BREAK
         ENDIF
      NEXT
   END SEQUENCE

RETURN lNonASCII
This function is used in RichEditBox_HasNonAsciiChars() - see below.

New function HMG_UTF8IsNonANSI( cUtf8Str )

This function determines whether a UTF-8 string contains any non-ANSI characters.

http://kevincarmody.com/hmg/SOURCE/h_UNICODE_String.prg - lines 285-369

Code: Select all

// Following function added by Kevin Carmody, September 2015

FUNCTION HMG_UTF8IsNonANSI( cUtf8Str )
/*
   This function determines whether a UTF-8 string contains one or more
   non-ANSI characters.  It does not check whether the string is valid UTF-8.
*/

LOCAL aAnsiTrans := { ;
   0x20AC, ; // ANSI 0x80 - EURO SIGN
   0x201A, ; // ANSI 0x82 - SINGLE LOW-9 QUOTATION MARK
   0x0192, ; // ANSI 0x83 - LATIN SMALL LETTER F WITH HOOK
   0x201E, ; // ANSI 0x84 - DOUBLE LOW-9 QUOTATION MARK
   0x2026, ; // ANSI 0x85 - HORIZONTAL ELLIPSIS
   0x2020, ; // ANSI 0x86 - DAGGER
   0x2021, ; // ANSI 0x87 - DOUBLE DAGGER
   0x02C6, ; // ANSI 0x88 - MODIFIER LETTER CIRCUMFLEX ACCENT
   0x2030, ; // ANSI 0x89 - PER MILLE SIGN
   0x0160, ; // ANSI 0x8A - LATIN CAPITAL LETTER S WITH CARON
   0x2039, ; // ANSI 0x8B - SINGLE LEFT-POINTING ANGLE QUOTATION MARK
   0x0152, ; // ANSI 0x8C - LATIN CAPITAL LIGATURE OE
   0x017D, ; // ANSI 0x8E - LATIN CAPITAL LETTER Z WITH CARON
   0x2018, ; // ANSI 0x91 - LEFT SINGLE QUOTATION MARK
   0x2019, ; // ANSI 0x92 - RIGHT SINGLE QUOTATION MARK
   0x201C, ; // ANSI 0x93 - LEFT DOUBLE QUOTATION MARK
   0x201D, ; // ANSI 0x94 - RIGHT DOUBLE QUOTATION MARK
   0x2022, ; // ANSI 0x95 - BULLET
   0x2013, ; // ANSI 0x96 - EN DASH
   0x2014, ; // ANSI 0x97 - EM DASH
   0x02DC, ; // ANSI 0x98 - SMALL TILDE
   0x2122, ; // ANSI 0x99 - TRADE MARK SIGN
   0x0161, ; // ANSI 0x9A - LATIN SMALL LETTER S WITH CARON
   0x203A, ; // ANSI 0x9B - SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
   0x0153, ; // ANSI 0x9C - LATIN SMALL LIGATURE OE
   0x017E, ; // ANSI 0x9E - LATIN SMALL LETTER Z WITH CARON
   0x0178  } // ANSI 0x9F - LATIN CAPITAL LETTER Y WITH DIAERESIS
LOCAL aAnsiSkip  := { ;
   0x81, ;
   0x8D, ;
   0x8F, ;
   0x90, ;
   0x9D  }

LOCAL lNonANSI := .F.
LOCAL nOctets  := 0
LOCAL cChar, nChar, nCode

   BEGIN SEQUENCE

      FOR EACH cChar IN cUtf8Str

         nChar := HB_BCODE( cChar )

         IF nOctets != 0

            --nOctets
            nCode := HB_BITOR( HB_BITSHIFT( nCode, 6 ), HB_BITAND( nChar, 0x3F ) )
            IF nOctets == 0
               DO CASE
               CASE nCode >= 0x100
                  IF ASCAN( aAnsiTrans, nCode ) == 0
                     lNonANSI := .T.
                  ENDIF
               CASE nCode >= 0xA0
               CASE nCode >= 0x80
                  IF ASCAN( aAnsiSkip, nCode ) == 0
                     lNonANSI := .T.
                  ENDIF                  
               ENDCASE
            ENDIF

         ELSEIF HB_BITAND( nChar, 0x80 ) != 0

            DO WHILE HB_BITAND( nChar, 0x80 ) != 0
               nChar := HB_BITAND( HB_BITSHIFT ( nChar, 1 ), 0xFF )
               ++nOctets
            ENDDO
            --nOctets
            nCode := HB_BITAND( HB_BCODE( cChar ), HB_BITSHIFT( 1, nOctets ) - 1 )

         ENDIF

      NEXT

   END SEQUENCE

RETURN lNonANSI
This function is used in RichEditBox_HasNonAnsiChars() - see below.

Enhanced function DoMethod ( Arg1 , Arg2 , Arg3 , Arg4 , Arg5 , Arg6 , Arg7 , Arg8 , Arg9 )

Enabled this function to return a value from a RichEditBox method.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - line 8869

Code: Select all

Function DoMethod ( Arg1 , Arg2 , Arg3 , Arg4 , Arg5 , Arg6 , Arg7 , Arg8 , Arg9 )
// Following line modified by Kevin Carmody, September 2015
Local xData, i, hWnd
http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 8881-8882

Code: Select all

// Following 2 lines modified by Kevin Carmody, September 2015
IF _RichEditBox_DoMethod ( @xData, Arg1 , Arg2 , Arg3 , Arg4 , Arg5 , Arg6 , Arg7 , Arg8 , Arg9 ) == .T.
   Return xData
ENDIF
http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - line 10422

Code: Select all

// Following line modified by Kevin Carmody, September 2015
Function _RichEditBox_DoMethod ( xData, Arg1 , Arg2 , Arg3 , Arg4 , Arg5 , Arg6 , Arg7 , Arg8 , Arg9 )
The return value is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - lines 906

Code: Select all

      IF ! wMain.ebDoc.RICHEDITLOADFILE(cFileName, .N., nFormat)
The return value is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - lines 993

Code: Select all

      IF ! wMain.ebDoc.RICHEDITSAVEFILE(cFileName, .N., nFormat)
Enhanced function HMG_IsUTF8( cString, lAllowASCII, lAllowPartial, cPartial )

This function now has three optional arguments:
  • lAllowASCII allows the input string to be all ASCII
  • lAllowPartial allows the input string to end with an incomplete UTF-8 sequence
  • cPartial (passed by reference) is set to the incomplete UTF-8 sequence at the end of the input string, or the empty string
The last two arguments are useful when the string is an input file buffer.

http://kevincarmody.com/hmg/SOURCE/h_UNICODE_String.prg - lines 175-257

Code: Select all

// Following function modified by Kevin Carmody, September 2015

FUNCTION HMG_IsUTF8( cString, lAllowASCII, lAllowPartial, cPartial ) // code from Harbour Project
/* 
  Modeled after HB_STRISUTF8 in \src\rtl\strutf8.c in Harbour source.
  This Harbour code has two bugs:
    - does not allow input to be all ASCII
    - allows code point above 0x10FFFF
  This function corrects these bugs.
  This function returns .F. if cString contains any invalid UTF-8.
  If the optional argument lAllowASCII is .T., cString may be all ASCII.
     Otherwise cString must contain one or more non-ASCII chars.
  If the optional argument lAllowPartial is .T., cString may end with an 
     unfinished UTF-8 byte sequence, which is passed back through cPartial, 
     which is otherwise set to the empty string. This is useful when cString 
     is a file buffer.
*/
LOCAL lASCII  := .T.
LOCAL lUTF8   := .T.
LOCAL nOctets := 0
LOCAL cChar, nChar, nCode

   IF lAllowASCII == NIL
      lAllowASCII := .F.
   ENDIF
   IF lAllowPartial == NIL
      lAllowPartial := .F.
   ENDIF

   BEGIN SEQUENCE

      FOR EACH cChar IN cString

         nChar := HB_BCODE( cChar )

         IF nOctets != 0

            IF HB_BITAND( nChar, 0xC0 ) != 0x80
               lUTF8 := .F.
               BREAK
            ENDIF
            --nOctets
            nCode := HB_BITOR( HB_BITSHIFT( nCode, 6 ), HB_BITAND( nChar, 0x3F ) )
            IF nOctets == 0 .AND. nCode > 0x10FFFF
               lUTF8 := .F.
               BREAK
            ENDIF

         ELSEIF HB_BITAND( nChar, 0x80 ) != 0

            lASCII := .F.
            DO WHILE HB_BITAND( nChar, 0x80 ) != 0
               nChar := HB_BITAND( HB_BITSHIFT ( nChar, 1 ), 0xFF )
               ++nOctets
            ENDDO
            --nOctets
            IF nOctets == 0
               lUTF8 := .F.
               BREAK
            ENDIF
            nCode := HB_BITAND( HB_BCODE( cChar ), HB_BITSHIFT( 1, nOctets ) - 1 )

         ENDIF

      NEXT

   END SEQUENCE

   IF nOctets > 0
      IF lAllowPartial
         cPartial := RIGHT( cString, nOctets )
      ELSE
         lUTF8 := .F.
      ENDIF
   ELSE
      IF lAllowPartial
         cPartial := ''
      ENDIF
   ENDIF

   IF ! lAllowASCII .AND. lASCII
      lUTF8 := .F.
   ENDIF

RETURN lUTF8
This function is used in http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - see above.

Code: Select all

            IF nBufRead > 0 .AND. HMG_IsUtf8( cPartial + cBuffer, .N., .Y., @cPartial )
Fixed RichEditBox_StreamIn( hWndControl, cFileName, lSelection, nDataFormat ) --> lSuccess

This function now skips over a byte order mark (BOM) in a Unicode text file if one is present.

http://kevincarmody.com/hmg/SOURCE/c_richeditbox.c - lines 190-192, 199-203, 216-233

Code: Select all

//        RichEditBox_StreamIn ( hWndControl, cFileName, lSelection, nDataFormat ) --> return lSuccess
HB_FUNC ( RICHEDITBOX_STREAMIN )
{
   HWND       hWndControl = (HWND)   HMG_parnl (1);
   TCHAR     *cFileName   = (TCHAR*) HMG_parc (2);
   BOOL       lSelection  = (BOOL)   hb_parl  (3);
   LONG       nDataFormat = (LONG)   hb_parnl (4);
   HANDLE     hFile;
   // Following 3 lines added by Kevin Carmody, September 2015
   BYTE       bUtf8Bom[3]; 
   BYTE       bUtf16Bom[2]; 
   DWORD      dwRead;
   EDITSTREAM es;
   LONG       Format;

   switch( nDataFormat )
   {
   // Comments in this switch block modified by Kevin Carmody, September 2015
      case 1:   Format = SF_TEXT; break; // ANSI or UTF-8 with BOM or mixed (UTF-8 BOM removed, overlong UTF-8 accepted, invalid UTF-8 read as ANSI)
      case 2:   Format = ( CP_UTF8 << 16 ) | SF_USECODEPAGE | SF_TEXT; break; // UTF-8 without BOM (BOM not removed)
      case 3:   Format = SF_TEXT | SF_UNICODE; break; // UTF-16 LE without BOM (BOM not removed)
      case 4:   Format = SF_RTF;  break;
      // case 5, UTF-8 RTF, removed by Kevin Carmody, September 2015, because it can never occur
      default:  Format = SF_RTF; break;
   }

   if ( lSelection )
        Format = Format | SFF_SELECTION;

   if( ( hFile = CreateFile (cFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL )) == INVALID_HANDLE_VALUE )
   {   hb_retl (FALSE);
       return;
   }

   // Following switch block added by Kevin Carmody, September 2015
   switch( nDataFormat )
   {
      case 1:   break;
      case 2:   
         if ( ! ReadFile (hFile, bUtf8Bom, 3, &dwRead, NULL) ) // read past BOM if present
            hb_retl (FALSE);
         if ( ! ( dwRead == 3 && bUtf8Bom[0] == 0xEF && bUtf8Bom[1] == 0xBB && bUtf8Bom[2] == 0xBF ) )
            SetFilePointer (hFile, 0, 0, FILE_BEGIN);
         break;
      case 3:
         if ( ! ReadFile (hFile, bUtf16Bom, 2, &dwRead, NULL) ) // read past BOM if present
            hb_retl (FALSE);
         if ( ! ( dwRead == 2 && bUtf16Bom[0] == 0xFF && bUtf16Bom[1] == 0xFE ) )
            SetFilePointer (hFile, 0, 0, FILE_BEGIN);
         break;
      case 4:   break;
      default:  break;
   }
   es.pfnCallback = EditStreamCallbackRead;
   es.dwCookie    = (DWORD_PTR) hFile;
   es.dwError     = 0;

   SendMessage ( hWndControl, EM_STREAMIN, (WPARAM) Format, (LPARAM) &es );

   CloseHandle (hFile);

   if( es.dwError )
      hb_retl (FALSE);
   else
      hb_retl (TRUE);
}
This function is called by RichEditBox_RtfTxtLoadFile() - see below.

Fixed RichEditBox_StreamOut( hWndControl, cFileName, lSelection, nDataFormat ) --> lSuccess

This function now writes a byte order mark (BOM) to a Unicode text file.

http://kevincarmody.com/hmg/SOURCE/c_richeditbox.c - lines 269-271, 278-282, 295-302

Code: Select all

//        RichEditBox_StreamOut ( hWndControl, cFileName, lSelection, nDataFormat ) --> return lSuccess
HB_FUNC ( RICHEDITBOX_STREAMOUT )
{
   HWND       hWndControl = (HWND)   HMG_parnl (1);
   TCHAR     *cFileName   = (TCHAR*) HMG_parc (2);
   BOOL       lSelection  = (BOOL)   hb_parl  (3);
   LONG       nDataFormat = (LONG)   hb_parnl (4);
   HANDLE     hFile;
   // Following 3 lines added by Kevin Carmody, September 2015
   BYTE       bUtf8Bom[3]  = {0xEF, 0xBB, 0xBF};
   BYTE       bUtf16Bom[2] = {0xFF, 0xFE}; 
   DWORD      dwWritten;
   EDITSTREAM es;
   LONG       Format;

   switch( nDataFormat )
   {
   // Comments in this switch block modified by Kevin Carmody, September 2015
      case 1:   Format = SF_TEXT; break; // ANSI (non-ANSI characters converted to question marks)
      case 2:   Format = ( CP_UTF8 << 16 ) | SF_USECODEPAGE | SF_TEXT; break; // UTF-8 without BOM
      case 3:   Format = SF_TEXT | SF_UNICODE; break; // UTF-16 LE without BOM
      case 4:   Format = SF_RTF;  break;
      // case 5, UTF-8 RTF, removed by Kevin Carmody, September 2015, because it can never occur
      default:  Format = SF_RTF; break;
   }

   if ( lSelection )
        Format = Format | SFF_SELECTION;

   if( ( hFile = CreateFile (cFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE )
   {   hb_retl (FALSE);
       return;
   }

   // Following switch block added by Kevin Carmody, September 2015
   switch( nDataFormat )
   {
      case 1:   break;
      case 2:   WriteFile( hFile, bUtf8Bom, 3, &dwWritten, NULL ); break; // write UTF-8 BOM at head of file
      case 3:   WriteFile( hFile, bUtf16Bom, 2, &dwWritten, NULL ); break; // write UTF-16 LE BOM at head of file
      case 4:   break;
      default:  break;
   }
   es.pfnCallback = EditStreamCallbackWrite;
   es.dwCookie    = (DWORD_PTR) hFile;
   es.dwError     = 0;

   SendMessage ( hWndControl, EM_STREAMOUT, (WPARAM) Format, (LPARAM) &es );
   
   CloseHandle (hFile);

   if( es.dwError )
      hb_retl (FALSE);
   else
      hb_retl (TRUE);
}
This function is used in RichEditBox_RtfTxtSaveFile() - see below.

Fixed HMG_UTF8InsertBOM ( cString )

Deleted spurious insertion of '/' between BOM and text.

http://kevincarmody.com/hmg/SOURCE/h_UNICODE_String.prg - line 167

Code: Select all

// Following function modified by Kevin Carmody, September 2015

FUNCTION HMG_UTF8InsertBOM ( cString )
   IF HMG_IsUTF8WithBOM (cString) == .F.
      cString := UTF8_BOM + cString
   ENDIF
RETURN cString
New constants RICHEDITFILE_ANSI etc.

These constants are RTF and TXT file types returned by the new function GetRichEditFileType() and can be used by the RichEditLoadFile and RichEditSaveFile methods of the RichEditBox control.

http://kevincarmody.com/hmg/INCLUDE/i_richeditbox.ch - lines 161-169

Code: Select all

// Following 5 #defines added by Kevin Carmody, September 2015

*****************
*   File type   *
*****************

#define RICHEDITFILE_ANSI      1   // ANSI text file
#define RICHEDITFILE_UTF8      2   // UTF-8 text file
#define RICHEDITFILE_UTF16LE   3   // UTF-16 LE (little endian) text file
#define RICHEDITFILE_RTF       4   // RTF file
#define RICHEDITFILE_UTF16BE   5   // UTF-16 BE (big endian) text file
These constants are also used in the Rich Edit demo - http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - lines 883-904 and lines 976-992.

New RichEditBox property HasNonAsciiChars (read only)

This property detects whether a rich edit control contains non-ASCII Unicode characters.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10266-10268, at end of _RichEditBox_GetProperty()

Code: Select all

   // Following case added by Kevin Carmody, September 2015
   CASE Arg3 == "HASNONASCIICHARS"
        xData  := RichEditBox_HasNonAsciiChars ( hWndControl )
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - line 164

Code: Select all

;; // Following line modified by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFTextMode,AutoURLDetect,Zoom,SelectRange,CaretPos,Value,GetSelectText,GetTextLength,ViewRect,HasNonAsciiChars,HasNonAnsiChars\> => GetProperty ( <"w">, \<"c"\> , \<"p"\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 424-430

Code: Select all

// Following function added by Kevin Carmody, September 2015

/*
  The following function tests for the presence of non-ASCII characters in 
  a rich edit control.  It would be better to know if the control contained
  non-ANSI characters, but since some ANSI characters are not in the Latin-1 
  Supplement, testing for non-ANSI characters is inefficient for large 
  documents.
*/

*-----------------------------------------------------------------------------*
FUNCTION RichEditBox_HasNonAsciiChars( hWndControl )
*-----------------------------------------------------------------------------*

LOCAL cBuffer := RichEditBox_GetText( hWndControl, .N. )

RETURN HMG_IsNonASCII( cBuffer, .N. )
This property is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - line 945

Code: Select all

   LOCAL lUnicode := wMain.ebDoc.HASNONASCIICHARS
New RichEditBox property HasNonAnsiChars (read only)

This property detects whether a rich edit control contains non-ANSI Unicode characters.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10266-10268, at end of _RichEditBox_GetProperty()

Code: Select all

   // Following case added by Kevin Carmody, September 2015
   CASE Arg3 == "HASNONANSICHARS"
        xData  := RichEditBox_HasNonAnsiChars ( hWndControl )
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - line 164

Code: Select all

;; // Following line modified by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFTextMode,AutoURLDetect,Zoom,SelectRange,CaretPos,Value,GetSelectText,GetTextLength,ViewRect,HasNonAsciiChars,HasNonAnsiChars\> => GetProperty ( <"w">, \<"c"\> , \<"p"\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 424-430

Code: Select all

// Following function added by Kevin Carmody, September 2015

/*
  The following function tests for the presence of non-ANSI characters in 
  a rich edit control.  It is slower than RichEditBox_HasNonAsciiChars
  but does not reject any Unicode characters that are in ANSI.
*/

*-----------------------------------------------------------------------------*
FUNCTION RichEditBox_HasNonAnsiChars( hWndControl )
*-----------------------------------------------------------------------------*

LOCAL cBuffer := RichEditBox_GetText( hWndControl, .N. )

RETURN HMG_UTF8IsNonANSI( cBuffer )
This property is not used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg, but could be used on line 945 in place of HASNONASCIICHARS. In this case, it would be slower but would catch non-ASCII ANSI characters that could be successfully written to an ANSI file.

Code: Select all

   LOCAL lUnicode := wMain.ebDoc.HASNONANSICHARS
New RichEditBox method RichEditLoadFile( cFile, lSelection, nType ) --> lSuccess

This method is a synonym for the RtfLoadFile method. It now uses the file type returned by GetRichEditFileType(). It skips over a byte order mark in a Unicode text file, and it supports UTF-16 BE (big endian Unicode text file).

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10445-10446

Code: Select all

   // Following case modified by Kevin Carmody, September 2015
   CASE Arg3 == "RTFLOADFILE" .OR. Arg3 == "RICHEDITLOADFILE"
        xData := RichEditBox_RichEditLoadFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 172-173, 175

Code: Select all

;; // Following 2 lines modified by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 452-484

Code: Select all

// Following function added by Kevin Carmody, September 2015

*-----------------------------------------------------------------------------*
FUNCTION RichEditBox_RichEditLoadFile( hWndControl, cFile, lSelection, nType )
*-----------------------------------------------------------------------------*

LOCAL lSuccess := .N.
LOCAL cTempFile

   IF ! HB_ISLOGICAL( lSelection )
      lSelection := .N.
   ENDIF
   IF ! HB_ISNUMERIC( nType )
      nType := RICHEDITFILE_RTF
   ENDIF

   IF lSelection
      RichEditBox_UnSelectAll( hWndControl )
   ENDIF
   IF RichEditBox_RTFLoadResourceFile( hWndControl, cFile, lSelection )
      lSuccess := .T.
   ELSE
      IF nType == RICHEDITFILE_UTF16BE
         cTempFile := GETTEMPFOLDER() + "_RichEditLoadFile.txt"
         lSuccess  := Utf16ByteSwap( cFile, cTempFile )
         IF lSuccess
            lSuccess := RichEditBox_StreamIn( hWndControl, cTempFile, lSelection, RICHEDITFILE_UTF16LE )
         ENDIF
         DELETE FILE ( cTempFile )
      ELSE
         lSuccess := RichEditBox_StreamIn( hWndControl, cFile, lSelection, nType )
      ENDIF
   ENDIF

RETURN lSuccess
This method is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - line 906

Code: Select all

      IF ! wMain.ebDoc.RICHEDITLOADFILE(cFileName, .N., nFormat)
New RichEditBox method RichEditSaveFile( cFile, lSelection, nType ) --> lSuccess

This method is a synonym for the RtfSaveFile method. It now uses the file type returned by GetRichEditFileType(), writes a byte order mark to a Unicode text file, and supports UTF-16 BE (big endian Unicode text file).

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10450-10451

Code: Select all

   // Following case modified by Kevin Carmody, September 2015
   CASE Arg3 == "RTFSAVEFILE" .OR. Arg3 == "RICHEDITSAVEFILE"
        xData := RichEditBox_RichEditSaveFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 172-173, 175

Code: Select all

;; // Following 2 lines modified by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 482-507

Code: Select all

// Following function added by Kevin Carmody, September 2015

*-----------------------------------------------------------------------------*
FUNCTION RichEditBox_RichEditSaveFile( hWndControl, cFile, lSelection, nType )
*-----------------------------------------------------------------------------*

LOCAL lSuccess := .N.
LOCAL cTempFile

   IF ! HB_ISLOGICAL( lSelection )
      lSelection := .N.
   ENDIF
   IF ! HB_ISNUMERIC( nType )
      nType := RICHEDITFILE_RTF
   ENDIF

   IF nType == RICHEDITFILE_UTF16BE
      cTempFile := GETTEMPFOLDER() + "_RichEditLoadFile.txt"
      lSuccess := RichEditBox_StreamOut( hWndControl, cTempFile, lSelection, RICHEDITFILE_UTF16LE )
      IF lSuccess
         lSuccess  := Utf16ByteSwap( cTempFile, cFile )
      ENDIF
      DELETE FILE ( cTempFile )
   ELSE
      lSuccess := RichEditBox_StreamOut( hWndControl, cFile, lSelection, nType )
   ENDIF

RETURN lSuccess
This method is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - line 993

Code: Select all

      IF ! wMain.ebDoc.RICHEDITSAVEFILE(cFileName, .N., nFormat)
New RichEditBox method SelPasteSpecial

Paste special - needs documentation of clipboard format argument.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10463-10465

Code: Select all

   // Following case added by Kevin Carmody, September 2015
   CASE Arg3 == HMG_UPPER ("SelPasteSpecial")
        RichEditBox_PasteSpecial ( hWndControl, Arg4 )
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - line 179

Code: Select all

;; // Following line added by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:SelPasteSpecial\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
This method is not used in the Rich Edit demo.

Enhanced RichEditBox method RtfLoadFile( cFile, lSelection, nType ) --> lSuccess

This method is a synonym for the RichEditLoadFile method described above.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10445-10446

Code: Select all

   // Following case modified by Kevin Carmody, September 2015
   CASE Arg3 == "RTFLOADFILE" .OR. Arg3 == "RICHEDITLOADFILE"
        xData := RichEditBox_RichEditLoadFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 172-173, 175

Code: Select all

;; // Following 2 lines modified by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
This method is not used in the Rich Edit demo.

Enhanced RichEditBox method RtfSaveFile( cFile, lSelection, nType ) --> lSuccess

This method is a synonym for the RichEditSaveFile method described above.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 10445-10447

Code: Select all

   // Following case modified by Kevin Carmody, September 2015
   CASE Arg3 == "RTFSAVEFILE" .OR. Arg3 == "RICHEDITSAVEFILE"
        xData := RichEditBox_RichEditSaveFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 172-173, 175

Code: Select all

// Following 2 lines modified by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, September 2015
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RichEditLoadFile,RichEditSaveFile\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
This method is not used in the Rich Edit demo.

Overhauled the Rich Edit demo

The rich edit demo now has the following features:
  • Main menu
  • List of recently used files
  • Resizable windows
  • Ctrl-B, Ctrl-I, Ctrl-U supported
  • File name in title, file name and page in status bar
  • Modified flag, caps lock, num lock, insert status on status bar
  • Window size, font name and size, file locations, file filters, recently used file names stored in registry
  • Paragraph numbering
  • Read and write text files
  • Many other enhancements
The compiled executable is at http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.exe.

The rich edit demo uses all the enhancements described above (except the HasNonAnsiChars property, SelPasteSpecial method, and HMG_UTF8InsertBOM function).

Source files changed: Source files added: Source files deleted:

Re: Status of source code changes suggested in November 2014

Posted: Sat Sep 12, 2015 5:12 pm
by srvet_claudio
Thanks Kevin.

Re: Status of source code changes suggested in November 2014

Posted: Mon Sep 14, 2015 5:15 am
by bpd2000
Thanks Mr. Kevin for your contribution

Re: Status of source code changes suggested in November 2014

Posted: Mon Sep 14, 2015 6:53 am
by serge_girard
Thanks Kevin for these very usefull changes!

Serge