RichEditBox handling of Unicode text files now fixed

HMG Unicode versions 3.1.x related

Moderator: Rathinagiri

User avatar
srvet_claudio
Posts: 2193
Joined: Thu Feb 25, 2010 8:43 pm
Location: Uruguay
Contact:

Re: RichEditBox handling of Unicode text files now fixed

Post by srvet_claudio »

kcarmody wrote: I will put together a posting like this with my changes. There are several dozen changes, so it will be a big posting and will take a while to put together.
Thanks Kevin.
Best regards.
Dr. Claudio Soto
(from Uruguay)
http://srvet.blogspot.com
User avatar
kcarmody
Posts: 152
Joined: Tue Oct 07, 2014 11:13 am
Contact:

Re: RichEditBox handling of Unicode text files now fixed

Post by kcarmody »

RichEditBox changes by Kevin Carmody, November 2014

I'm submitting the following changes, all relating to the RichEditBox control. I've developed a patch, based on Claudio's current patch (patch 3), which incorporates these changes. This 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/HMG.zip.

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

New manifest constants RTFTXTFILE_ANSI etc.

These constants are RTF and TXT file types returned by the new function GetRtfTxtFileType() and can be used by the RtfTxtLoadFile and RtfTxtSaveFile methods of the RichEditBox control.

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

Code: Select all

// Following 5 #defines added by Kevin Carmody, November 2014

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

#define RTFTXTFILE_ANSI      1   // ANSI text file
#define RTFTXTFILE_UTF8      2   // UTF-8 text file
#define RTFTXTFILE_UTF16LE   3   // UTF-16 LE (little endian) text file
#define RTFTXTFILE_RTF       4   // RTF file
#define RTFTXTFILE_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 882-903 and lines 975-991.

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

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

http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 508-576

Code: Select all

// Following function added by Kevin Carmody, November 2014

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

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

/*
  The following code block tests for the presence of non-ASCII characters in 
  an unmarked text file.  It assumes that such characters may be ANSI or 
  parts of UTF-8 sequences.  In UTF-8 mode, Windows reads bytes which are not 
  part of a valid UTF-8 sequence as ANSI.  It would be better to know if the 
  file contained UTF-8 sequences for non-ANSI characters, but since some ANSI 
  characters are not in the Latin-1 Supplement, testing for non-ANSI UTF-8 
  sequences is inefficient for large files.
*/

LOCAL bHasUnicodeChars := {||
   LOCAL lHasUnicode := .N.
   LOCAL cChar, cStr
   cBuffer  := SPACE( 0x400 )
   nBufRead := 1
   BEGIN SEQUENCE
      WHILE nBufRead > 0
         nBufRead := FREAD( hFile, @cBuffer, 0x400 )
         IF nBufRead > 0
            cStr := LEFT( cBuffer, nBufRead )
            FOR EACH cChar IN cStr
               IF cChar >= CHR(0x80)
                  lHasUnicode := .Y.
                  BREAK
               ENDIF
            NEXT
         ENDIF
      ENDDO
   END SEQUENCE
   RETURN lHasUnicode
   }

BEGIN SEQUENCE

   IF hFile < 0
      BREAK
   ENDIF
   nBufRead := FREAD( hFile, @cBuffer, 5 )
   DO CASE
   CASE nBufRead >= 5 .AND. LEFT( cBuffer, 5 ) == "{\rtf"
      nType := RTFTXTFILE_RTF
   CASE nBufRead >= 3 .AND. LEFT( cBuffer, 3 ) == E"\xEF\xBB\xBF"
      nType := RTFTXTFILE_UTF8
   CASE nBufRead >= 2 .AND. LEFT( cBuffer, 2 ) == E"\xFF\xFE"
      nType := RTFTXTFILE_UTF16LE
   CASE nBufRead >= 2 .AND. LEFT( cBuffer, 2 ) == E"\xFE\xFF"
      nType := RTFTXTFILE_UTF16BE
   CASE ! EMPTY( lUtf8Test ) .AND. bHasUnicodeChars:EVAL( )
      nType := RTFTXTFILE_UTF8
   OTHERWISE
      nType := RTFTXTFILE_ANSI
   ENDCASE

END SEQUENCE

FCLOSE( hFile )

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

Code: Select all

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

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

http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 579-621

Code: Select all

// Following function added by Kevin Carmody, November 2014

*-----------------------------------------------------------------------------*
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_RtfTxtLoadFile() and RichEditBox_RtfTxtSaveFile() - see below.

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 226-291

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, November 2014
   BYTE       bUtf8Bom[3]; 
   BYTE       bUtf16Bom[2]; 
   DWORD      dwRead;
   EDITSTREAM es;
   LONG       Format;

   switch( nDataFormat )
   {
   // Comments in this switch block modified by Kevin Carmody, November 2014
      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, November 2014, 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, November 2014
   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 305-360

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, November 2014
   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, November 2014
      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, November 2014, 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, November 2014
   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.

New RichEditBox property HasUnicodeChars (read only)

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

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

Code: Select all

   // Following case added by Kevin Carmody, November 2014
   CASE Arg3 == "HASUNICODECHARS"
        xData  := RichEditBox_HasUnicodeChars ( hWndControl )
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 153-154

Code: Select all

;; // Following line modified by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFTextMode,AutoURLDetect,Zoom,SelectRange,CaretPos,Value,GetSelectText,GetTextLength,ViewRect,HasUnicodeChars\> => GetProperty ( <"w">, \<"c"\> , \<"p"\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 508-576

Code: Select all

// Following function added by Kevin Carmody, November 2014

/*
  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_HasUnicodeChars( hWndControl )
*-----------------------------------------------------------------------------*

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

FOR EACH cChar IN cBuffer
   IF cChar >= CHR(0x80)
      lHasUnicode := .Y.
   ENDIF
NEXT

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

Code: Select all

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

This method is a synonym for the RtfLoadFile method. It now uses the file type returned by GetRtfTxtFileType(). 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 9467-9470

Code: Select all

   // Following case modified by Kevin Carmody, November 2014
   CASE Arg3 == "RTFLOADFILE" .OR. Arg3 == "RTFTXTLOADFILE"
        xData := RichEditBox_RtfTxtLoadFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 161-165

Code: Select all

;; // Following 2 lines modified by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 441-475

Code: Select all

// Following function added by Kevin Carmody, November 2014

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

LOCAL lSuccess := .N.
LOCAL cTempFile

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

IF lSelection
   RichEditBox_UnSelectAll( hWndControl )
ENDIF
IF RichEditBox_RTFLoadResourceFile( hWndControl, cFile, lSelection )
   lSuccess := .T.
ELSE
   IF nType == RTFTXTFILE_UTF16BE
      cTempFile := GETTEMPFOLDER() + "_RtfTxtLoadFile.txt"
      lSuccess  := Utf16ByteSwap( cFile, cTempFile )
      IF lSuccess
         lSuccess := RichEditBox_StreamIn( hWndControl, cTempFile, lSelection, RTFTXTFILE_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 905

Code: Select all

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

This method is a synonym for the RtfSaveFile method. It now uses the file type returned by GetRtfTxtFileType(), 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 9472-9475

Code: Select all

   // Following case modified by Kevin Carmody, November 2014
   CASE Arg3 == "RTFSAVEFILE" .OR. Arg3 == "RTFTXTSAVEFILE"
        xData := RichEditBox_RtfTxtSaveFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 161-165

Code: Select all

;; // Following 2 lines modified by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> ) ;;
http://kevincarmody.com/hmg/SOURCE/h_richeditbox.prg - lines 478-505

Code: Select all

// Following function added by Kevin Carmody, November 2014

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

LOCAL lSuccess := .N.
LOCAL cTempFile

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

IF nType == RTFTXTFILE_UTF16BE
   cTempFile := GETTEMPFOLDER() + "_RtfTxtLoadFile.txt"
   lSuccess := RichEditBox_StreamOut( hWndControl, cTempFile, lSelection, RTFTXTFILE_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 992

Code: Select all

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

Paste special - needs documentation of clipboard format argument.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 9485-9488

Code: Select all

   // Following case added by Kevin Carmody, November 2014
   CASE Arg3 == HMG_UPPER ("SelPasteSpecial")
        RichEditBox_PasteSpecial ( hWndControl, Arg4 )
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 168-169

Code: Select all

;; // Following line added by Kevin Carmody, November 2014
#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 RtfTxtLoadFile method described above.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 9467-9470

Code: Select all

   // Following case modified by Kevin Carmody, November 2014
   CASE Arg3 == "RTFLOADFILE" .OR. Arg3 == "RTFTXTLOADFILE"
        xData := RichEditBox_RtfTxtLoadFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 161-165

Code: Select all

;; // Following 2 lines modified by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>,\<arg2\>,\<arg3\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\>, \<arg3\> ) ;;
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<arg1\>,\<arg2\>) => DoMethod ( <"w">, \<"c"\> , \<"p"\> , \<arg1\> , \<arg2\> ) ;;
;; // Following line added by Kevin Carmody, November 2014
#xtranslate <w>. \<c\> . \<p:RTFLoadFile,RTFSaveFile,RtfTxtLoadFile,RtfTxtSaveFile\> (\<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 RtfTxtSaveFile method described above.

http://kevincarmody.com/hmg/SOURCE/h_controlmisc.prg - lines 9472-9475

Code: Select all

   // Following case modified by Kevin Carmody, November 2014
   CASE Arg3 == "RTFSAVEFILE" .OR. Arg3 == "RTFTXTSAVEFILE"
        xData := RichEditBox_RtfTxtSaveFile ( hWndControl, Arg4, Arg5, Arg6 )   // by default save in SF_RTF format
        RetVal := .T.
http://kevincarmody.com/hmg/INCLUDE/i_window.ch - lines 161-165

Code: Select all

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

Enhanced function GetFile( [aFilter], [cTitle], [cIniFolder], [lMultiSelect], [lNoChangeDir], [nFilterIndex] ) --> cFileName | aFileNames


Added nFilterInddex argument to control the default file filter. See viewtopic.php?f=8&t=4009

http://kevincarmody.com/hmg/SOURCE/c_dialogs.c - lines 173-174, 188-190, 221-222

Code: Select all

HB_FUNC ( C_GETFILE )
{
   #define _MAXMULTISELECTFILE   1024
   OPENFILENAME ofn;
   TCHAR buffer    [ _MAXMULTISELECTFILE  *  MAX_PATH ];
   TCHAR cFullName [ _MAXMULTISELECTFILE ] [ MAX_PATH ];
   TCHAR cCurDir   [ MAX_PATH ];
   TCHAR cFileName [ MAX_PATH ];
   // Following line added by Kevin Carmody, November 2014
   int iFilterIndex = 1;        
   int iPosition    = 0;
   int iNumSelected = 0;

   int flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;

   memset ((void*)&buffer, 0, sizeof(buffer));

   if ( hb_parl(4) )
      flags = flags | OFN_ALLOWMULTISELECT ;

   if ( hb_parl(5) )
      flags = flags | OFN_NOCHANGEDIR ;

   // Following if statement added by Kevin Carmody, November 2014
   if ( hb_parni(6) )
     iFilterIndex = hb_parni(6) ;


//******************************************************************************************************//   
// ofn.lpstrFilter = A buffer containing pairs of null-terminated filter strings. 
//                   The last string in the buffer must be terminated by two NULL characters.
// The following code converts a ANSI (CHAR) filter string into a UNICODE (UNSIGNED INT) filter string
   
   #define _MAX_FILTER 5*1024
   INT i, j=0, cont=0;
   CHAR *p = (CHAR*) hb_parc(1);
   TCHAR Filter [ _MAX_FILTER ];
   memset((void*) &Filter, 0, sizeof(Filter));
   
   for (i=0; *p != '\0'; i++)
   {  cont = cont + strlen (p) + 1;
      if (cont < _MAX_FILTER)
      {   lstrcpy ( &Filter[j], HMG_CHAR_TO_WCHAR(p) );
          j = j + lstrlen ( HMG_CHAR_TO_WCHAR (p) ) + 1;
          p = p + strlen (p) + 1;
      }
      else
         break;
   }
//**********************************************************************//   

   memset( (void*) &ofn, 0, sizeof( OPENFILENAME ) );

   ofn.lStructSize      = sizeof(OPENFILENAME);
   ofn.hwndOwner        = GetActiveWindow();
   ofn.lpstrFilter      = (LPCTSTR) &Filter;
   // Following statement modified by Kevin Carmody, November 2014
   ofn.nFilterIndex     = (DWORD) iFilterIndex;
   ofn.lpstrFile        = (LPTSTR) &buffer;
   ofn.nMaxFile         = sizeof(buffer) / sizeof(TCHAR);
   ofn.lpstrInitialDir  = (LPCTSTR) HMG_parc(3); 
   ofn.lpstrTitle       = (LPCTSTR) HMG_parc(2);
   ofn.Flags            = flags;

   if( GetOpenFileName (&ofn) )
   {
      if ( ofn.nFileExtension !=0 )
         HMG_retc (ofn.lpstrFile);
      else
      {
         wsprintf ( cCurDir, _TEXT("%s"), &buffer [iPosition] );
         iPosition = iPosition + lstrlen (cCurDir) + 1;
         do
         {
            iNumSelected ++;
            wsprintf ( cFileName, _TEXT("%s"), &buffer [iPosition] );
            iPosition = iPosition + lstrlen (cFileName) + 1;
            wsprintf ( cFullName [iNumSelected], _TEXT("%s\\%s"), cCurDir, cFileName);
         }
         while (( lstrlen(cFileName) !=0 ) && ( iNumSelected < _MAXMULTISELECTFILE ));

         if (iNumSelected > 1)
         {
            hb_reta (iNumSelected - 1);
            for (i = 1; i < iNumSelected; i++)
               HMG_storvc (cFullName[i], -1, i );
         }
         else
            HMG_retc( &buffer[0] );
      }
   }
   else
      HMG_retc( _TEXT("") );
}
http://kevincarmody.com/hmg/SOURCE/h_dialogs.prg - lines 93-94, 123-126, 129-130, 132-133

Code: Select all

*-----------------------------------------------------------------------------*
// Following line modified by Kevin Carmody, November 2014
Function GetFile( aFilter, title, cIniFolder, multiselect, nochangedir, nFilterIndex )
*-----------------------------------------------------------------------------*
local c := ''
local cfiles := ''
local fileslist := {}
local n
Local i

  IF aFilter == Nil
    aFilter := {}
  EndIf

   IF title == NIL
      title := ""
   ENDIF   

   IF cIniFolder == NIL
      cIniFolder := ""
   ENDIF
   
  FOR n:=1 TO HMG_LEN(aFilter)
      c :=  c + aFilter[n][1] + CHR(0) + aFilter[n][2] + CHR(0)
  NEXT
   c :=  c + CHR(0)
   
  if valtype(multiselect) == 'U'
    multiselect := .f.
  endif

  // Following if block added by Kevin Carmody, November 2014
  if nFilterIndex == NIL
    nFilterIndex := 1
  endif

  if .not. multiselect
    // Following line modified by Kevin Carmody, November 2014
    Return ( C_GetFile ( c , title, cIniFolder, multiselect, nochangedir, nFilterIndex ) )
  else
    // Following line modified by Kevin Carmody, November 2014
    cfiles := C_GetFile ( c , title, cIniFolder, multiselect, nochangedir, nFilterIndex )

    if HMG_LEN( cfiles ) > 0
      if valtype( cfiles ) == "A"
        fileslist := aclone( cfiles )
      else
        aadd( fileslist, cfiles )
      endif
    endif

    For i := 1 To HMG_LEN ( FilesList)
      FilesList [i] := HB_UTF8STRTRAN ( FilesList [i] , "\\" , "\" )
    Next i

    Return ( FilesList )

  endif

Return Nil
This enhancement is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - line 864

Code: Select all

     cAuxFileName := GETFILE( aReadFilter, 'Open', cFileFolder, NIL, NIL, nReadFilter )
Enhanced function Putfile( [aFilter], [cTitle], [cIniFolder], [lNoChangeDir], [cDefaultFileName], [cExtFile], [nFilterIndex] ) --> cFileName

Added nFilterInddex argument to control the default file filter. If passed by reference, nFilterIndex returns the index of file filter selected by user. See viewtopic.php?f=8&t=4009

http://kevincarmody.com/hmg/SOURCE/c_dialogs.c - lines 268-269, 276-280, 311-312, 327-331

Code: Select all

HB_FUNC ( C_PUTFILE )
{
 
 OPENFILENAME ofn;
 TCHAR buffer[1024];
 
 // Following line added by Kevin Carmody, November 2014
 int iFilterIndex = 1;        
 int flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER ;
 
 if ( hb_parl(4) )
 {
  flags = flags | OFN_NOCHANGEDIR ;
 }
 // Following if statement added by Kevin Carmody, November 2014
 if ( hb_parni(7) )
 {
  iFilterIndex = hb_parni(7) ;
 }
 
//******************************************************************************************************//   
// ofn.lpstrFilter = A buffer containing pairs of null-terminated filter strings. 
//                   The last string in the buffer must be terminated by two NULL characters.
// The following code converts a ANSI (CHAR) filter string into a UNICODE (UNSIGNED INT) filter string
   
   #define _MAX_FILTER 5*1024
   INT i, j=0, cont=0;
   CHAR *p = (CHAR*) hb_parc(1);
   TCHAR Filter [_MAX_FILTER];
   memset((void*) &Filter, 0, sizeof(Filter));
   
   for (i=0; *p != '\0'; i++)
   {  cont = cont + strlen (p) + 1;
      if (cont < _MAX_FILTER)
      {   lstrcpy ( &Filter[j], HMG_CHAR_TO_WCHAR (p) );
          j = j + lstrlen ( HMG_CHAR_TO_WCHAR (p) ) + 1;
          p = p + strlen (p) + 1;
      }
      else
         break;
   }
//**********************************************************************//   

 lstrcpy (buffer, HMG_parc(5));
 
 memset( (void*) &ofn, 0, sizeof( OPENFILENAME ) );
 ofn.lStructSize = sizeof(OPENFILENAME);
 ofn.hwndOwner = GetActiveWindow() ;
 ofn.lpstrFilter = (LPCTSTR) &Filter;
 // Following statement modified by Kevin Carmody, November 2014
 ofn.nFilterIndex = (DWORD) iFilterIndex;
 ofn.lpstrFile = (LPTSTR) &buffer;
 ofn.nMaxFile = sizeof(buffer) / sizeof(TCHAR);
 ofn.lpstrInitialDir = (LPCTSTR) HMG_parc(3);
 ofn.lpstrTitle = (LPCTSTR) HMG_parc(2);
 ofn.Flags = flags;
 ofn.lpstrDefExt = (LPCTSTR) HMG_parc(6);
  
 if ( GetSaveFileName (&ofn) )
 {   if (HB_ISBYREF(6))
     {  if (ofn.nFileExtension > ofn.nFileOffset)  
            HMG_storc (&ofn.lpstrFile [ofn.nFileExtension], 6);
        else
            HMG_storc (_TEXT(""), 6);
     }
     // Following if statement added by Kevin Carmody, November 2014
     if (HB_ISBYREF(7))
     {
        hb_stornl ((LONG) ofn.nFilterIndex, 7);
     }
     HMG_retc(ofn.lpstrFile);
 }
 else
     HMG_retc( _TEXT("") );
}
http://kevincarmody.com/hmg/SOURCE/h_dialogs.prg - lines 154-155, 175-178, 185-186

Code: Select all

*--------------------------------------------------------------------------------------------*
// Following line modified by Kevin Carmody, November 2014
Function Putfile ( aFilter, cTitle, cIniFolder, lNoChangeDir, cDefaultFileName, cExtFile, nFilterIndex )
*--------------------------------------------------------------------------------------------*
LOCAL cFilter:='' , n, cFileName
 
   IF aFilter == Nil
      aFilter:={}
   EndIf

   IF cTitle == NIL
      cTitle := ""
   ENDIF   

   IF cIniFolder == NIL
      cIniFolder := ""
   ENDIF

   IF cDefaultFileName == NIL
      cDefaultFileName := ""
   ENDIF
   
   // Following if block added by Kevin Carmody, November 2014
   IF nFilterIndex == NIL
      nFilterIndex := 1
   ENDIF    

   FOR n := 1 TO HMG_LEN ( aFilter )
      cFilter = cFilter + aFilter [n] [1] + CHR(0) + aFilter [n] [2] + CHR(0)
   NEXT
   cFilter :=  cFilter + CHR(0)

   // Following line modified by Kevin Carmody, November 2014
   cFileName := C_PutFile ( cFilter, cTitle, cIniFolder, lNoChangeDir, cDefaultFileName, @cExtFile, @nFilterIndex )

Return cFileName
This enhancement is used in http://kevincarmody.com/hmg/SAMPLES/Con ... x/demo.prg - lines 957-958

Code: Select all

   cAuxFileName := PUTFILE( aWriteFilter, 'Save As', cFileFolder, ;
      NIL, cFileName, @cFileExt, @nWriteFilter )
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 - lines 7943-7944

Code: Select all

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

Code: Select all

// Following 2 lines modified by Kevin Carmody, November 2014
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 - lines 9444-9445

Code: Select all

// Following line modified by Kevin Carmody, November 2014
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 905

Code: Select all

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

Code: Select all

      IF ! wMain.ebDoc.RTFTXTSAVEFILE( cFileName, .N., nFormat )
Kevin
User avatar
srvet_claudio
Posts: 2193
Joined: Thu Feb 25, 2010 8:43 pm
Location: Uruguay
Contact:

Re: RichEditBox handling of Unicode text files now fixed

Post by srvet_claudio »

Thanks Kevin.
Many of these changes will be for the next version because HMG.3.3.2 now is ready.
Best regards.
Dr. Claudio Soto
(from Uruguay)
http://srvet.blogspot.com
User avatar
bpd2000
Posts: 1207
Joined: Sat Sep 10, 2011 4:07 am
Location: India

Re: RichEditBox handling of Unicode text files now fixed

Post by bpd2000 »

srvet_claudio wrote:Thanks Kevin.
Many of these changes will be for the next version because HMG.3.3.2 now is ready.
Eagerly waiting for new version

Great work done by Mr. Kevin in a very short period, Thank you for your contribution
Last edited by bpd2000 on Wed Nov 26, 2014 10:00 am, edited 1 time in total.
BPD
Convert Dream into Reality through HMG
User avatar
serge_girard
Posts: 3158
Joined: Sun Nov 25, 2012 2:44 pm
DBs Used: 1 MySQL - MariaDB
2 DBF
Location: Belgium
Contact:

Re: RichEditBox handling of Unicode text files now fixed

Post by serge_girard »

Thanks Kevin and Claudio,

Question: any idea about a function for converting to HTML...?
That would be so great!

Serge
There's nothing you can do that can't be done...
User avatar
kcarmody
Posts: 152
Joined: Tue Oct 07, 2014 11:13 am
Contact:

Re: RichEditBox handling of Unicode text files now fixed

Post by kcarmody »

serge_girard wrote:Question: any idea about a function for converting to HTML...?
That would be so great!
Some years ago I wrote a Harbour library called UnicodeLib that has functions for converting between ANSI, Unicode, RTF, and HTML. Some of these functions now have equivalents in Harbour, but I don't think the HTML conversions do. You can download UnicodeLib from http://kevincarmody.com/software/akshar ... unicodelib.

Kevin
User avatar
serge_girard
Posts: 3158
Joined: Sun Nov 25, 2012 2:44 pm
DBs Used: 1 MySQL - MariaDB
2 DBF
Location: Belgium
Contact:

Re: RichEditBox handling of Unicode text files now fixed

Post by serge_girard »

Thanks Kevin !

Serge
There's nothing you can do that can't be done...
Post Reply