Interactive PowerBasic Forum

IT-Consultant: José Roca (PBWIN 10+/PBCC 6+) (Archive only) => Freebasic => Topic started by: José Roca on January 17, 2025, 06:37:59 AM

Title: Rich Edit Text Object Model project
Post by: José Roca on January 17, 2025, 06:37:59 AM
Hello to all,

After several years without programming, I realised that I was forgetting a lot of my knowledge about the use of Freebasic, which, little by little, has been improving and becoming more solid.

To refresh my knowledge, I have started a new project involving the Rich Edit control, with several classes that wrap the Text Object Model (TOM2) interfaces.

This will take me a while, because besides writing and testing the code, I also document it, and this takes more work than the other.

Here you can find what I have done so far. The files with the extension .md are the documentation in markdown language. The CTextDocument2 class file is finished and I'm working on the CTextRange2 class file.

https://github.com/JoseRoca/WinFBX/tree/master/docs/RichEdit

Title: Re: Rich Edit Text Object Model project
Post by: Charles Pegge on January 17, 2025, 06:47:37 PM
Wow! This is so huge  :o
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on January 17, 2025, 07:30:35 PM
There are still three more interfaces: IRichEditOle, ITExtStrings and ITexStoryRanges.

Then to convert examples like this one:
https://forum.it-berater.org/index.php/topic,4377.msg15298.html#msg15298

I also plan to write "high level" functions for the most common used methods, like this one:

' ========================================================================================
' Gets the plain text in this range.
' ========================================================================================
PRIVATE FUNCTION AfxRichEditTOM_GetText (BYVAL hRichEdit AS HWND, BYVAL cpActive AS LONG = 0, BYVAL cpAnchor AS LONG = 0, BYVAL Flags AS LONG = 0) AS CBSTR
   CTOM_DP("AfxRichEditTOM_GetText")
   DIM cbsText AS CBSTR
   ' // Create an instance of the CTextDocument2 class
   DIM pCTextDoc AS CTextDocument2 = hRichEdit
   IF pCTextDoc.TextDocumentPtr = NULL THEN RETURN cbsText   ' // Return an empty string
   ' // Create a range
   IF cpActive < 0 THEN cpActive = 0
   DIM numChars AS LONG = RichEdit_GetTextLength(hRichEdit)   ' // Length of the text
   IF cpAnchor < 0 THEN cpAnchor = numChars
   IF cpAnchor > numChars THEN cpAnchor = numChars
   ' // Get the text
   DIM pCRange2 AS CTextRange2 = pCTextDoc.Range2(cpActive, cpAnchor)
   IF pCRange2.TextRangePtr = NULL THEN RETURN cbsText   ' // Return an empty string
   cbsText = pCRange2.GetText2(Flags)
   RETURN cbsText
END FUNCTION
' ========================================================================================

The first Text Object Model (TOM) was much smaller, but could not be used with languages like Power BASIC because the classes used the thiscall calling convention. TOM2 uses stdcall.

BTW FreeBasic has added support for thiscall and fastcall.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on January 17, 2025, 07:39:15 PM
Compound syntax can also be used, e.g.

DIM cbsText AS CBSTR = CTextRange2(CTextDocument2(hRichEdit).Range2(0, numChars)).GetText2(0)

Can substitute the above function, although I never have been a fan of this syntax because you can't check intermediate results, and if one of the returned intermediate pointers is null, it will GPF.

However, I have designed the FreeBasic wrapper classes in a way that allows to use it.
Title: Re: Rich Edit Text Object Model project
Post by: Frank Brübach on January 17, 2025, 08:47:05 PM
Hello Jose thats a great Work and Job.. many thanks :-)

I have Made a First Test and its running all fine Here

'
' cTom_01.bas 'file

' ########################################################################################
' Microsoft Windows
' File: CTOM_01.bas
' Contents: CWindow Rich Edit example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxRichEdit.inc"
#define _CTOM_DEBUG_ 1
#Include ONCE "Afx/CTOM.inc"
#INCLUDE ONCE "Afx/AfxRichEditTOM.inc"
USING Afx

CONST IDC_RICHEDIT = 1001
CONST IDC_TEST = 1002

DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
                          BYVAL hPrevInstance AS HINSTANCE, _
                          BYVAL szCmdLine AS ZSTRING PTR, _
                          BYVAL nCmdShow AS LONG) AS LONG

   END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
                  BYVAL hPrevInstance AS HINSTANCE, _
                  BYVAL szCmdLine AS ZSTRING PTR, _
                  BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   AfxSetProcessDPIAware

   DIM pWindow AS CWindow
   pWindow.Create(NULL, "CWindow with a Rich Edit control", @WndProc)
   pWindow.SetClientSize(500, 320)
   pWindow.Center

   ' // Add a rich edit control without coordinates (it will be resized in WM_SIZE)
   DIM hRichEdit AS HWND = pWindow.AddControl("RichEdit", , IDC_RICHEDIT, "RichEdit box test")
   SetFocus hRichEdit

   ' // Add a button without coordinates (it will be resized in WM_SIZE)
   pWindow.AddControl("Button", , IDCANCEL, "&Close", 0, 0, 75, 23)
   pWindow.AddControl("Button", , IDC_TEST, "&Test", 0, 0, 75, 23)

   ' // Dispatch Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main window callback procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

   SELECT CASE uMsg

      CASE WM_COMMAND
         SELECT CASE GET_WM_COMMAND_ID(wParam, lParam)
            ' // If ESC key pressed, close the application sending an WM_CLOSE message
            CASE IDCANCEL
               IF GET_WM_COMMAND_CMD(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
            CASE IDC_TEST
               DIM hRichEdit AS HWND = GetDlgItem(hwnd, IDC_RICHEDIT)
               AfxMsg AfxRichEditTOM_GetText(hRichEdit, 3, -1)
               AfxMsg AfxRichEditTOM_ChangeCase(hRichEdit, tomToggleCase)
               DIM chrRange AS CHARRANGE = TYPE<CHARRANGE>(3, 12)
               RichEdit_ExSetSel(hRichEdit, @chrRange)
               AfxMsg STR(AfxRichEditTOM_GetCch(hRichEdit, 3, 10))
               AfxMsg RichEdit_GetRtfText(hRichEdit)

               SCOPE
                  DIM pCTextDocument2 AS CTextDocument2 = hRichEdit
                  DIM pCRange2 AS CTextRange2 = pCTextDocument2.Range2(3, 8)
                  IF pCRange2 THEN pcRange2.SetChar(ASC("X"))
               END SCOPE

         END SELECT

      CASE WM_SIZE
         ' // If the window isn't minimized, resize it
         IF wParam <> SIZE_MINIMIZED THEN
            ' // Resize the controls
            DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
            IF pWindow THEN
               pWindow->MoveWindow GetDlgItem(hwnd, IDC_RICHEDIT), 100, 50, pWindow->ClientWidth - 200, pWindow->ClientHeight - 150, CTRUE
               pWindow->MoveWindow GetDlgItem(hwnd, IDCANCEL), pWindow->ClientWidth - 95, pWindow->ClientHeight - 35, 75, 23, CTRUE
               pWindow->MoveWindow GetDlgItem(hwnd, IDC_TEST), pWindow->ClientWidth - 195, pWindow->ClientHeight - 35, 75, 23, CTRUE
            END IF
         END IF

        CASE WM_DESTROY

         ' // End the application by sending an WM_QUIT message
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   ' // Default processing of Windows messages
   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================

What was your Motivation to start programming with freebasic and AfxRichEdit  again? 
Title: Re: Rich Edit Text Object Model project
Post by: Charles Pegge on January 18, 2025, 10:01:18 AM
José, Are there any COM interfaces that use thiscall or fastcall?. These calling conventions are redundant in 64bit anyway.

PS
I came accross this post:
https://github.com/microsoft/win32metadata/issues/1053
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on January 18, 2025, 05:48:40 PM
The only ones that I know are these belonging to the first version of the Text Object Model (TOM). This includes the RichEdit classes (except IRichEditOle) and ITextHost and ITextServices. I suspect that I was a mistake (thiscall is the default calling convention for C++ classes) that they have corrected in TOM2, that uses stdcall. You have to create the RichEdit control using the RichEdit50W class instead of older classes (for example, RichEdit20), that use thiscall.

The addition of thiscall and fastcall in FreeBasic is one of the needed steps in its attempt to add support to use non COM C++ classes that use these calling conventions. It also can be used with FreeBasic classes, but it is not the default (you have to add the Extern C++ directive or specify the calling convention in the declarations of the procedures).

declare Sub name __Thiscall [Overload] [Alias "alias"] ( parameters )
declare Function name __Thiscall [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Title: Re: Rich Edit Text Object Model project
Post by: José Roca on January 18, 2025, 05:54:26 PM
Regarding the problems of GPFs with the compound syntax if one of the intermediate result is a null pointer, I will add checking for null pointers in all the methods of my classes.

I will also add optional parameters with default values to some of the methods.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on January 18, 2025, 06:01:00 PM
> What was your Motivation to start programming with freebasic and AfxRichEdit  again?

It's good for my mind. Solving complex problems keeps your brain sharp, which is essential at my advanced age.
Title: Re: Rich Edit Text Object Model project
Post by: Charles Pegge on January 22, 2025, 02:02:20 PM
I will implement thiscall and fastcall in OxygenBasic for 32bit code. The ms64 calling convention makes them redundant in 64bit compiling, since the first 4 parameters are always passed in registers.

To maintain 32/64 bit compatibility, calling convention directives will be ignored, and ms64 applies to all extern procedures.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 05, 2025, 06:56:26 AM
I have finished the initial version of the CRichEditCtrl class:

Source: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CRichEditCtrl.inc

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CRichEditCtrl%20Class.md#insertimage

And a template for the callback class needed to load RTF text from file or from a resource.

Source: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CRichEditOleCallback.inc

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CRichEditOleCallback%20Class.md

Next I want to incorporate the methods and properties of the ITextDocument2 interface and methods to print the RTF text and to save it to a file.
Title: Re: Rich Edit Text Object Model project
Post by: Johan Klassen on February 05, 2025, 12:41:38 PM
 8)
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 05, 2025, 07:58:36 PM
Quote from: José Roca on January 17, 2025, 06:37:59 AMHello to all,

After several years without programming, I realised that I was forgetting a lot of my knowledge about the use of Freebasic, which, little by little, has been improving and becoming more solid.

To refresh my knowledge, I have started a new project involving the Rich Edit control, with several classes that wrap the Text Object Model (TOM2) interfaces.

This will take me a while, because besides writing and testing the code, I also document it, and this takes more work than the other.

Here you can find what I have done so far. The files with the extension .md are the documentation in markdown language. The CTextDocument2 class file is finished and I'm working on the CTextRange2 class file.

https://github.com/JoseRoca/WinFBX/tree/master/docs/RichEdit

i hope you also use the new available K.I.-Resourcees, as they wil help you with the boring tasks.
You find all important KI-Links here in the Forum.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 06, 2025, 12:07:24 AM
> i hope you also use the new available K.I.-Resourcees, as they wil help you with the boring tasks.You find all important KI-Links here in the Forum.

Sorry. I don't find anything in the forum.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 06, 2025, 12:44:14 AM
I've got InsertObject to work, but it is better to use InsertImage to insert images, because InsertObject only displays bitmaps; for other image types, like .jpg, it displays an icon. However, InsertObject can insert Ole objects, like .xls files., althoug it will display a clickable icon that will launch the application associated with the file extension.

I have modified InsertImage to get the size of the image using GDI+ if you don't pass the width and height. By default, the images will be scaled according the High DPI settings. You can use the ScalingRratio property to make the image bigger or smaller, or not to scale at all.

There is an curious problem: In my computer, the scaling ratio is 1.75, and InsertImage uses that ratio by default. InsertObject scales the image, but using a ratio of 1.68, and drag and drop scales the image, but using a ratio of 1.24. So you get different image sizes depending of the method that you use, but what can I do?



Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 06, 2025, 10:16:45 PM
Quote from: José Roca on February 06, 2025, 12:07:24 AM> i hope you also use the new available K.I.-Resourcees, as they wil help you with the boring tasks.You find all important KI-Links here in the Forum.

Sorry. I don't find anything in the forum.



Goto:
IT-Berater: Theo Gottwald (IT-Consultant)
then roll down until you find "Claude 3" which is still the most popular
alternative, or use DeepSeek or one of the other new KI's, just click on the link you will be redirected there.
It can save you from many stupid tasks, so you can concentrate on the oversight.

Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 06, 2025, 10:23:33 PM
Quote from: José Roca on February 06, 2025, 12:44:14 AMThere is an curious problem: In my computer, the scaling ratio is 1.75, and InsertImage uses that ratio by default. InsertObject scales the image, but using a ratio of 1.68, and drag and drop scales the image, but using a ratio of
1.24. So you get different image sizes depending of the method that you use, but what can I do?

[quote]Below is an answer that summarizes what I found from online sources regarding inconsistent image scaling ratios in Windows high‐DPI applications:

Why the Different Ratios?
Many developers have noted that when you insert images by different methods (such as through a programmatic InsertImage, via an InsertObject call, or by drag and drop) you may see different scaling ratios. This happens because each method can end up querying the system's DPI settings from different sources or in different contexts. For example:

InsertImage using GDI+ might use the DPI embedded in the image file or the DPI settings obtained via GDI+ APIs.
InsertObject might use the DPI value provided by the Windows common controls or the application's DPI awareness settings.
Drag and Drop often works through a different message loop or DPI virtualization layer and may use a fallback DPI value.
As a result, one method might return 1.75 while another returns 1.68 or 1.24. Essentially, the "scaling ratio" you see depends on the DPI context and API used to obtain it.

How to Resolve It?
Several experts recommend the following unified approach:

Declare Your Application DPI-Aware:
Use an application manifest or call APIs like SetProcessDPIAware (or on Windows 10, SetProcessDpiAwarenessContext) so that your app is fully DPI-aware. This prevents Windows from "virtualizing" DPI values and ensures that all parts of your application use the same DPI context.

Microsoft: High DPI Desktop Application Development on Windows
Centralize DPI Retrieval:
Write a single routine (for example, GetScalingRatio()) that uses a consistent API (such as GetDeviceCaps or GetDpiForWindow) to retrieve the DPI value from your current window or display. Then use that scaling ratio in all your image insertion methods.

Override if Necessary:
If you'd like more control, expose a global property (or a property on your image container) that allows you to override the scaling ratio. For example, you might let the user set a custom ratio (1.0 for no scaling, etc.) so that all insertion methods use exactly that value.

By ensuring your application is fully DPI-aware and by having all methods call the same DPI retrieval function, you guarantee that InsertImage, InsertObject, and drag‐and‐drop will use the same scaling ratio. Several forum posts (for example on MSDN and StackOverflow) support this approach as the most reliable solution to DPI scaling inconsistencies.

In summary:
The differences in scaling (1.75 vs. 1.68 vs. 1.24) come from different DPI contexts. To fix this, make your application DPI‐aware and use one centralized function to get the scaling ratio so that all image‐insertion code paths use the same value.

This solution is in line with best practices from Microsoft's High DPI documentation and many developer forum discussions.[/quote]


[quote]The differences you're seeing are likely due to the fact that each method (InsertImage, InsertObject, drag‐and‐drop) is obtaining the DPI scaling factor from a different source or using a different DPI‐awareness context. In other words, one function might be using the system DPI from GDI+, another might be using values derived from your process or application manifest settings, and drag and drop might be using yet another DPI context.

To resolve this inconsistency, consider the following steps:

Unify DPI Awareness:
Ensure your application is declared as DPI‐aware (using an application manifest or by calling an API like SetProcessDPIAware or SetProcessDpiAwarenessContext). This guarantees that all parts of your application are using the same DPI context when retrieving DPI values.

Use a Common DPI Retrieval Method:
Instead of letting each method calculate its own scaling ratio, call a single routine (for example, a GetScalingRatio function) that uses the same API (for instance, GDI+ or GetDeviceCaps) to retrieve the current DPI and compute the scaling factor. Then pass that ratio explicitly to InsertImage, InsertObject, and any drag and drop handling routines.

Override with a Global Property:
If you wish to allow flexibility, you can introduce a global scaling property (or a property on your image container) that overrides the default computed ratio. That way, you can ensure that all methods use the same ratio (for example, set it to 1.0 for no scaling or to a value you decide on) regardless of the underlying DPI retrieval differences.

Review the DPI-Specific Code:
Check the modifications you made to InsertImage. For example, if you're calling GDI+ functions to get the image's original size and then multiplying by the scaling ratio, verify that the same DPI value is being used by InsertObject and drag and drop routines. You might need to refactor those methods so that they all call your common DPI scaling routine.

For more detailed information on these issues, you might review Microsoft's guidelines on developing high DPI–aware applications (for instance, "High DPI Desktop Application Development on Windows" on Microsoft Docs) and related forum posts on consistent DPI scaling in PowerBASIC applications.

By ensuring that all parts of your application share the same DPI context and by explicitly setting or retrieving the scaling ratio from a single source, you can avoid the discrepancies (1.75 vs. 1.68 vs. 1.24) you're seeing between the various methods.[/quote]
'------------------------------------------------------------------
' 1. DECLARE DPI AWARENESS AND REQUIRED API FUNCTIONS
'------------------------------------------------------------------
#COMPILE EXE
#DIM ALL
#INCLUDE "Win32Api.inc"

' DPI Awareness Context (Windows 10+)
DECLARE FUNCTION SetProcessDpiAwarenessContext LIB "User32.dll" _
    ALIAS "SetProcessDpiAwarenessContext" (BYVAL dpiFlag AS DWORD) AS LONG

DECLARE FUNCTION GetDpiForWindow LIB "User32.dll" _
    ALIAS "GetDpiForWindow" (BYVAL hWnd AS DWORD) AS DWORD

'------------------------------------------------------------------
' 2. INITIALIZE DPI AWARENESS AT PROGRAM START
'------------------------------------------------------------------
FUNCTION InitializeDPIAwareness() AS LONG
    ' Try Windows 10 Per-Monitor v2 awareness
    LOCAL hr AS LONG
    hr = SetProcessDpiAwarenessContext(&HFFFFFFFFFFFFFFF4) ' DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
    IF hr = 0 THEN FUNCTION = 1 ELSE FUNCTION = 0
END FUNCTION

'------------------------------------------------------------------
' 3. GET CONSISTENT SCALING RATIO
'------------------------------------------------------------------
FUNCTION GetSystemScalingRatio(BYVAL hWnd AS DWORD) AS SINGLE
    LOCAL dpi AS DWORD
   
    ' First try GetDpiForWindow (Windows 10+)
    dpi = GetDpiForWindow(hWnd)
   
    ' Fallback to system DPI if failed
    IF dpi = 0 THEN
        LOCAL hDC AS DWORD
        hDC = GetDC(hWnd)
        dpi = GetDeviceCaps(hDC, %LOGPIXELSX)
        ReleaseDC hWnd, hDC
    END IF
   
    FUNCTION = CSng(dpi) / 96.0!
END FUNCTION

'------------------------------------------------------------------
' 4. SAMPLE IMAGE INSERTION WITH CONSISTENT SCALING
'------------------------------------------------------------------
SUB InsertImage(BYVAL hWnd AS DWORD, imagePath AS STRING, OPTIONAL userScaling AS SINGLE)
    ' Default user scaling to 1.0 if not provided
    IF ISMISSING(userScaling) THEN userScaling = 1.0
   
    ' Get combined scaling ratio
    LOCAL sysScaling AS SINGLE
    sysScaling = GetSystemScalingRatio(hWnd)
    LOCAL finalScaling AS SINGLE
    finalScaling = sysScaling * userScaling
   
    ' Get native image dimensions (example using GDI+)
    LOCAL pImage AS IDispatch
    LOCAL nativeWidth AS LONG, nativeHeight AS LONG
    GDIP_LoadImageFromFile(imagePath, pImage)
    GDIP_GetImageDimensions(pImage, nativeWidth, nativeHeight)
   
    ' Apply scaling
    LOCAL scaledWidth AS LONG, scaledHeight AS LONG
    scaledWidth = nativeWidth * finalScaling
    scaledHeight = nativeHeight * finalScaling
   
    ' Insert image with scaled dimensions
    ' (Your actual image insertion code here)
    MSGBOX "Inserting image at:" + STR$(scaledWidth) + "x" + STR$(scaledHeight)
   
    GDIP_DisposeImage(pImage)
END SUB

'------------------------------------------------------------------
' 5. HANDLE DRAG AND DROP CONSISTENTLY
'------------------------------------------------------------------
CALLBACK FUNCTION WindowProc
    SELECT CASE CBMSG
        CASE %WM_DROPFILES
            LOCAL hDrop AS DWORD
            hDrop = CBWPARAM
           
            ' Get scaling ratio for current window
            LOCAL scaling AS SINGLE
            scaling = GetSystemScalingRatio(CBHNDL)
           
            ' Process dropped files
            ' (Your actual drag/drop handling code here)
            MSGBOX "Drag/drop scaling ratio:" + STR$(scaling)
           
            DragFinish hDrop
            FUNCTION = 0
            EXIT FUNCTION
    END SELECT
END FUNCTION

'------------------------------------------------------------------
' MAIN PROGRAM
'------------------------------------------------------------------
FUNCTION PBMAIN()
    InitializeDPIAwareness
   
    ' Create test window
    LOCAL hWnd AS DWORD
    hWnd = CBHNDL
   
    ' Test image insertion
    InsertImage hWnd, "C:\test.png", 1.0  ' No additional user scaling
   
    ' Test with user scaling
    InsertImage hWnd, "C:\test.png", 0.8  ' 80% of system scaling
END FUNCTION





Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 06, 2025, 11:58:22 PM
My InsertImage function does scaling perfectly (scaling ratio: 1.75). It is the InsertObject function, that uses the IRichEditOle interface, the one that scales using a ratio of 1.64, but it does not provide any method to set the DPI that it has to use, and drag and drop uses an scaling ratio of 1.24. All these numbers happen in my computer and with the images that I'm using; they may change in another system. Both the IRichEditOle interface and Windows drag and drop need a callback that implements several methods, the most important of them being GetNewStorage, but none of them indicates which scaling ratio is going to use and allow to change it. I can control my own code, and use the scaling that I wish, but I have no control to do the same with the other two methods.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 07, 2025, 01:06:19 AM
I have been able to solve the High DPI problem with the InsertObject method, that uses the IRichEditOle interface, by modifying the Microsoft C++ example. The REOBJECT structure has a member that is a SIZEL structure, where you can specify the wdith and height.

Therefore, I have changed this code

   ' // Set up object info
   DIM reobject AS REOBJECT
   reobject.cbStruct = SIZEOF(REOBJECT)
   reobject.clsid = clsid
   reobject.cp = REO_CP_SELECTION
   reobject.dvaspect = DVASPECT_CONTENT
   reobject.dwFlags = REO_RESIZABLE OR REO_BELOWBASELINE
   reobject.dwUser = 0
   reobject.poleobj = pObject
   reobject.polesite = pClientSite
   reobject.pstg = pStorage
   DIM sizel AS SIZEL
   reobject.sizel = sizel

to


   ' // Set up object info
   DIM reobject AS REOBJECT
   reobject.cbStruct = SIZEOF(REOBJECT)
   reobject.clsid = clsid
   reobject.cp = REO_CP_SELECTION
   reobject.dvaspect = DVASPECT_CONTENT
   reobject.dwFlags = 0 'REO_RESIZABLE OR REO_BELOWBASELINE
   reobject.dwUser = 0
   reobject.poleobj = pObject
   reobject.polesite = pClientSite
   reobject.pstg = pStorage
   DIM sizel AS SIZEL
   DIM dwWidth AS DWORD, dwHeight AS DWORD
   DIM nStatus AS LONG = AfxGdipGetImageSizeFromFile(wszFileName, @dwWidth, @dwHeight)
   IF nStatus = 0 THEN
      sizel.cx = AfxPixelsToHiMetricX(dwWidth * m_ScalingRatio)
      sizel.cy = AfxPixelsToHiMetricY(dwHeight * m_ScalingRatio)
   END IF
   reobject.sizel = sizel

I'm removing the REO_RESIZABLE OR REO_BELOWBASELINE flags, getting the width and height of the image using GDI+ and filling the SIZEL structure with the these values multiplied by the scaling ratio, and convert them from pixels to HIMETRIC units.

What remains is the problem with drag and drop.
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 07, 2025, 09:29:20 AM
You can ask:
Tülu 3 (https://playground.allenai.org/thread/msg_V6S2B2V2B6)

QuoteTo address the drag and drop issue with high DPI scaling, you'll need to make adjustments to ensure that the drag and drop operations account for the scaling factor in the same way as you've adjusted the image size. This typically involves translating the mouse coordinates and the dimensions of the dragged object to ensure they are correctly positioned and sized relative to the target window or control.

Here's a general approach to handle drag and drop operations with high DPI scaling:

Capture the Drop Position: When a drop operation occurs, you'll need to capture the position of the drop in screen coordinates.

Translate Coordinates: Convert the screen coordinates to client coordinates relative to the target window or control. This can be done using the ScreenToClient function or a similar method.

Apply DPI Scaling: Adjust the coordinates (and possibly the size) based on the DPI scaling factor. This involves multiplying the coordinates by the scaling ratio to ensure they are correctly positioned within the target window or control.

Insert the Object: With the adjusted coordinates and size, insert the object at the correct position and size within the target window or control.

Here's a simplified example of how you might adjust the drop coordinates:

POINT ptDropScreen; // Assume this is the drop position in screen coordinates
HWND hWndTarget; // Handle to the target window

// Convert screen coordinates to client coordinates
POINT ptDropClient = ptDropScreen;
ScreenToClient(hWndTarget, &ptDropClient);

// Apply DPI scaling
float dpiScalingX = /* Get X scaling factor */;
float dpiScalingY = /* Get Y scaling factor */;
ptDropClient.x = static_cast<LONG>(ptDropClient.x * dpiScalingX);
ptDropClient.y = static_cast<LONG>(ptDropClient.y * dpiScalingY);

// Now use ptDropClient to position the object
To get the DPI scaling factors, you can use the GetDpiForWindow function (available since Windows 10 version 1607) or the GetDeviceCaps function with the LOGPIXELSX and LOGPIXELSY indices.

Remember that the exact implementation will depend on the specifics of your application and the frameworks or libraries you're using. The key is to ensure that all coordinates and sizes are correctly scaled based on the DPI settings of the display.

For more detailed guidance or assistance with specific issues, consider consulting the documentation for the frameworks or libraries you're using, or seek out community resources and forums related to Windows development and high DPI support.

or lets see what

Claude 3.5 (https://claude.ai/chat/)
says:

QuoteI understand you're dealing with scaling inconsistencies between different image insertion methods in a RichEdit control, specifically:

Your InsertImage function - scales correctly at 1.75x
IRichEditOle InsertObject - scales at 1.64x with no apparent way to control it
Drag and drop - scales at 1.24x, also without control

You've successfully fixed the direct insertion method by:

Removing the REO_RESIZABLE and REO_BELOWBASELINE flags
Getting image dimensions via GDI+
Applying your desired scaling ratio
Converting to HIMETRIC units

For the drag and drop issue, since it's using the OLE subsystem, you might be able to intercept and modify the object before it's inserted. Have you considered implementing the IRichEditOleCallback interface and using its GetNewStorage method to modify the object properties?
Here's a potential approach:
cppCopyclass CRichEditOleCallback : public IRichEditOleCallback
{
    // ... other interface methods ...

    HRESULT GetNewStorage(LPSTORAGE* ppStg)
    {
        // Create storage
        HRESULT hr = CreateStorage(ppStg);
        if (SUCCEEDED(hr))
        {
            // Here you could potentially intercept and modify the
            // object properties before it's inserted
            // You might be able to access the REOBJECT structure
            // and modify its sizel member
        }
        return hr;
    }
};
Would you be able to share more details about how you're currently handling drag and drop? That might help identify where we could intervene to apply the correct scaling.

or you could have asked the famouse

DeepSeek AI (https://chat.deepseek.com/)

but currently the servers seem to be busy. Too many people use it because its free.

Alternatively you could have asked the good old CHatGPT andd you would get this:
QuoteThe core of the problem is that—even though you can adjust scaling in your own code (as you did with InsertImage and even in your custom InsertObject handler by modifying REOBJECT.sizel)—the drag and drop path is handled entirely by Windows' OLE mechanism. The drag and drop callback (via IRichEditOleCallback, especially methods like GetNewStorage) doesn't provide any parameter or hook to specify a DPI or scaling factor. As a result, Windows calculates and applies its own scaling (in your case, about 1.24) that you can't directly change.

Two potential workarounds are:

• **Intercept and Process Drag Data Manually:** Instead of letting the default drag and drop insertion occur, you could implement a custom drop handler (for example, by subclassing the control's IDropTarget) that intercepts the dropped image data. In that handler, you can manually scale the image (using your desired ratio, such as 1.75 or 1.64) and then insert it into the RichEdit control using your own code (e.g. via a custom InsertObject routine). This gives you full control over the scaling process.

• **Adjust Application DPI Settings:** Ensuring your application is fully DPI-aware (for example, by setting the appropriate `<dpiAwareness>` tag in your manifest) may help align some of the scaling behaviors. However, even with full DPI awareness, Windows' internal drag and drop handling still uses its own logic without exposing a way to override the scaling ratio.

In summary, because neither the IRichEditOle interface nor the drag and drop callback methods expose any DPI or scaling controls, you cannot "tell" Windows to use a different scaling ratio for drag and drop. The only solutions are to either live with the system's default behavior or to implement your own drag and drop mechanism that scales images before insertion.

For reference, the official [IRichEditOleCallback::GetNewStorage documentation](�cite�turn0search0�) shows that while it's critical for operations like cut/copy/paste and drag and drop, it does not offer any DPI or scaling parameters.


The scaling differences you're seeing—in your own code versus when Windows internally handles object insertion (via IRichEditOle) or drag and drop—are not unique to your system. Many developers working with rich edit controls on high‐DPI systems have encountered inconsistencies in how image sizes are converted and rendered. In your case, you observe ratios of 1.75 for your manual InsertImage routine, 1.64 for IRichEditOle–based InsertObject, and 1.24 for drag and drop. Although these numbers may vary by system and image content, they stem from several underlying factors:

---

### 1. DPI Assumptions in Windows and the Rich Edit Control

When you write your own insertion code, you have full control: you retrieve the image dimensions (for example, via GDI+), multiply them by a chosen scaling factor (your "m_ScalingRatio"), and then convert pixels to HIMETRIC units using functions such as AfxPixelsToHiMetricX/Y. This routine works "perfectly" because you are explicitly controlling the conversion.

In contrast, the IRichEditOle interface and the internal drag and drop handlers do not provide any parameter to specify DPI or a custom scaling factor. They use internal logic (often assuming a base DPI of 96) to convert pixel dimensions into HIMETRIC. Because the conversion is performed inside Windows' code paths, you have no hook to adjust the ratio. This explains why the InsertObject and drag and drop code paths yield different scaling ratios—they rely on different internal assumptions and rounding behaviors.

> For example, the [IRichEditOleCallback::GetNewStorage documentation](�cite�turn0search0�) makes it clear that no DPI parameter is provided when storage is allocated for a new object.

---

### 2. Rounding and Internal Conversion Differences

The rich edit control, as well as other parts of Windows' OLE and drag/drop subsystems, often perform conversions between logical units (pixels) and HIMETRIC using integer math. As described in discussions such as the Stack Overflow thread on [rounding errors when scaling the rendered output of the rich edit control via EM_FORMATRANGE](�cite�turn1search3�), these conversions can introduce cumulative rounding errors. In your situation, slight differences in how the width and height are rounded when converting to HIMETRIC may result in scaling ratios like 1.64 versus 1.24.

Each insertion method may trigger a different code path:
- **Your InsertImage routine:** You control the conversion entirely.
- **IRichEditOle InsertObject:** Uses a standard REOBJECT structure and Windows' default conversion (and may also be influenced by flags like REO_RESIZABLE or REO_BELOWBASELINE, which you found it necessary to remove).
- **Drag and drop:** Relies on the drag source's data object and Windows' drag-and-drop helper routines, which again use their own logic for determining size—often optimized for interactive behavior (like cursor display) rather than exact DPI scaling.

Because these internal routines were designed long before high-DPI displays became common, they have hard-coded assumptions (and sometimes "magic numbers") that can result in inconsistent scaling across different operations.

---

### 3. Community Experience and Workarounds

Developers in various forums (including CodeProject and Stack Overflow) have reported similar issues when inserting OLE objects or handling drag and drop on high-DPI systems. The consensus is that:
- **For programmatic insertion (InsertImage):** It is best to compute and set the size manually, as you have done.
- **For OLE-based insertion and drag and drop:** There is no exposed mechanism to override the DPI conversion because the underlying IRichEditOle interface and the drag/drop system do not provide DPI or scaling callbacks.
- **Workarounds:** Some developers choose to post-process the inserted object by manually adjusting its size after insertion. Others implement a custom drag-and-drop mechanism to apply a uniform scaling factor, though this requires reimplementing parts of the drag-and-drop behavior that Windows handles automatically.

This problem is intrinsic to how Windows' rich edit control and OLE embedding were designed. The different ratios you observe (1.75 vs. 1.64 vs. 1.24) are simply manifestations of different default conversion paths and rounding artifacts. Other developers have reported that even with modifications (like yours for InsertObject) the drag-and-drop scenario remains problematic.

---

### In Summary

- **Custom Insertion vs. OLE/Drag-and-Drop:** When you control the conversion (as in your InsertImage function), you can apply your preferred scaling factor. In contrast, IRichEditOle and Windows drag/drop rely on internal conversions with fixed assumptions (often based on 96 DPI), leading to different and less adjustable scaling ratios.
- **Rounding and Code Paths:** Internal rounding and the use of integer math in converting pixels to HIMETRIC can cause small discrepancies that accumulate into the different ratios you've observed.
- **Community Observations:** Similar issues have been noted by others; while some workarounds exist (e.g., manually adjusting the REOBJECT.sizel values or implementing a custom drag-and-drop handler), a uniform solution is hindered by the fact that neither the IRichEditOle interface nor the drag-and-drop callbacks expose DPI settings.

Unfortunately, without a supported API to set the DPI or scaling factor for these operations, you must either accept these differences or implement additional code to adjust the sizes after insertion. This is a well-documented "quirk" among developers dealing with rich edit OLE embedding in high-DPI environments.




Please also test:

Qwen AI (https://chat.qwenlm.ai/)
It makes bug-free Powerbasic Code (FB? Don't know ...)
you can even give it the buggy code from other AI's and say "Make it bugfree in Powerbasic".

And he will do it!


Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 07, 2025, 09:05:39 PM
Now that I have solved the DPI problem with insert object, I think that I have a solution for not displaying the image, but an icon, with other kind of images that are not bitmaps, like .jpg, .png, .gif.

Instead of using OleCreateFromFile to load and render the image, I can use GDI+ to load the image from file with the function GdipCreateBitmapFromFile and get a bitmap handle with GdipCreateHBITMAPFromBitmap. Then I will fill The cfFormat member of the FormatEtc structure with the CF_BITMAP constant and the tymed member with the TYMED_GDI constant. I also will need an instance of the STGMEDIUM structure with its tymed member = TYMED_GDI and its hBitmap member = the bitmap handle returned by GdipCreateHBITMAPFromBitmap. After that, I can call the Windows function OleCreateStaticFromData to create an static object. Looks easy, isn't it? But there is a problem... There is always a problem, isn't it? You can't transfer the data directly to OleCreateStaticFromData. That would be too easy and Microsoft likes to do things in a convoluted way. Why make things easy if you can make them complicated? Instead of providing an API function that accepts the FormatEtc and STGMEDIUM structures, you have to store the data in a DataObject COM object that implements the IDataObject interface and provide a pointer to it to OleCreateStaticFromData, that will use this pointer to access the data.

Regarding drag and drop and the cut and paste operations, they use the clipboard for data transfer.
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 07, 2025, 11:15:49 PM
Once you know what you want, Qwen can code an outline ...

Below is the PowerBASIC code that implements your described solution for loading and rendering images using GDI+ and creating a static object with `OleCreateStaticFromData`. The code includes all necessary declarations, structures, and function calls. It assumes you have already installed the GDI+ library and linked it properly in your project.

### Code Implementation

```basic
#Compile Exe
#Dim All

%Unicode = 1

' Include necessary libraries
#Include "windows.bi"
#Include "ole2.bi"
#Include "gdiplus.bas" ' Ensure this file contains GDI+ declarations

' Declare constants
Global Const CF_BITMAP = 2
Global Const TYMED_GDI = &H3

' Define required structures
Type FORMATETC
    cfFormat As Long
    ptd As Long
    dwAspect As Long
    lindex As Long
    tymed As Long
End Type

Type STGMEDIUM
    tymed As Long
    hBitmap As Long
    pData As Long
End Type

' Function prototypes
Declare Function OleCreateStaticFromData Lib "ole32.dll" (ByVal pDataObject As Long, ByVal riid As Long, ByVal renderopt As Long, ByVal lpFormatEtc As FORMATETC, ByVal pClientSite As Long, ByVal pStg As Long, ByVal ppvObj As Long) As Long
Declare Function CreateStreamOnHGlobal Lib "ole32.dll" (ByVal hGlobal As Long, ByVal fDeleteOnRelease As Long, ppstm As Long) As Long
Declare Function CoTaskMemAlloc Lib "ole32.dll" (ByVal cb As Long) As Long
Declare Function CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long) As Void

' GDI+ initialization
Global gdiplusToken As Dword
GdiplusStartup(gdiplusToken, ByVal VarPtr(GdiplusStartupInput), ByVal 0)

Function CreateDataObject(ByVal hBitmap As Long) As Long
    ' Implement IDataObject interface
    Local pDataObject As Long
    Local iidIDataObject As GUID
    Local formatetc As FORMATETC
    Local stgmedium As STGMEDIUM
    Local hr As Long

    ' Initialize IID_IDataObject
    With iidIDataObject
        .Data1 = &H7FD52380
        .Data2 = &HA1A1
        .Data3 = &H11CE
        .Data4(0) = &H8C
        .Data4(1) = &H7A
        .Data4(2) = &H00
        .Data4(3) = &HAA
        .Data4(4) = &H00
        .Data4(5) = &H4B
        .Data4(6) = &HA7
        .Data4(7) = &HBA
    End With

    ' Allocate memory for IDataObject
    pDataObject = CoTaskMemAlloc(SizeOf(IDataObject))
    If pDataObject = 0 Then Exit Function

    ' Initialize FORMATETC structure
    With formatetc
        .cfFormat = CF_BITMAP
        .ptd = 0
        .dwAspect = DVASPECT_CONTENT
        .lindex = -1
        .tymed = TYMED_GDI
    End With

    ' Initialize STGMEDIUM structure
    With stgmedium
        .tymed = TYMED_GDI
        .hBitmap = hBitmap
        .pData = 0
    End With

    ' Implement SetData method of IDataObject
    ' This is a simplified implementation; you may need to write a full COM object if required.
    ' For simplicity, we assume the data is stored directly in the structure.
    hr = pDataObject.SetData(formatetc, stgmedium, True)
    If hr <> S_OK Then
        CoTaskMemFree(pDataObject)
        pDataObject = 0
    End If

    Function = pDataObject
End Function

Sub Main()
    Local hBitmap As Long
    Local pDataObject As Long
    Local formatetc As FORMATETC
    Local stgmedium As STGMEDIUM
    Local hwndStatic As Long
    Local hr As Long

    ' Load image using GDI+
    Local bitmap As GpBitmap Ptr
    Local status As GpStatus
    status = GdipCreateBitmapFromFile("image.jpg", bitmap)
    If status <> Ok Then
        Print "Failed to load image."
        Exit Sub
    End If

    ' Convert GDI+ bitmap to HBITMAP
    status = GdipCreateHBITMAPFromBitmap(bitmap, hBitmap, 0)
    If status <> Ok Then
        Print "Failed to create HBITMAP."
        GdipDisposeImage(bitmap)
        Exit Sub
    End If

    ' Create IDataObject
    pDataObject = CreateDataObject(hBitmap)
    If pDataObject = 0 Then
        Print "Failed to create IDataObject."
        DeleteObject(hBitmap)
        GdipDisposeImage(bitmap)
        Exit Sub
    End If

    ' Initialize FORMATETC structure
    With formatetc
        .cfFormat = CF_BITMAP
        .ptd = 0
        .dwAspect = DVASPECT_CONTENT
        .lindex = -1
        .tymed = TYMED_GDI
    End With

    ' Create static control
    hwndStatic = CreateWindowEx(0, "STATIC", "", WS_CHILD Or WS_VISIBLE Or SS_BITMAP, 10, 10, 200, 200, GetConsoleWindow(), 0, App.hInstance, 0)
    If hwndStatic = 0 Then
        Print "Failed to create static control."
        CoTaskMemFree(pDataObject)
        DeleteObject(hBitmap)
        GdipDisposeImage(bitmap)
        Exit Sub
    End If

    ' Call OleCreateStaticFromData
    hr = OleCreateStaticFromData(pDataObject, IID_IUnknown, OleRender::OleRenderNormal, formatetc, 0, 0, ByVal 0)
    If hr <> S_OK Then
        Print "Failed to create static object."
    Else
        Print "Static object created successfully."
    End If

    ' Cleanup
    CoTaskMemFree(pDataObject)
    DeleteObject(hBitmap)
    GdipDisposeImage(bitmap)
End Sub

Main()

' Shutdown GDI+
GdiplusShutdown(gdiplusToken)
```

### Explanation of the Code

1. **GDI+ Initialization**: The `GdiplusStartup` function initializes the GDI+ library, which is required to load non-BMP image formats like JPEG, PNG, and GIF.

2. **Loading the Image**: The `GdipCreateBitmapFromFile` function loads the image from a file into a GDI+ bitmap object.

3. **Converting to HBITMAP**: The `GdipCreateHBITMAPFromBitmap` function converts the GDI+ bitmap into an HBITMAP, which is compatible with Windows API functions.

4. **Creating IDataObject**: A custom implementation of the `IDataObject` interface is created to store the HBITMAP. This is required because `OleCreateStaticFromData` expects a pointer to an `IDataObject`.

5. **Creating Static Control**: A static control is created using `CreateWindowEx`, and the `OleCreateStaticFromData` function is called to associate the `IDataObject` with the static control.

6. **Cleanup**: Resources such as the HBITMAP, GDI+ bitmap, and `IDataObject` are released to prevent memory leaks.

### Notes

- Ensure that the `gdiplus.bas` file is correctly included and contains all necessary GDI+ declarations.
- Replace `"image.jpg"` with the path to your desired image file.
- This code assumes basic knowledge of COM programming and GDI+. If you encounter issues, consult Microsoft's documentation for more details on these APIs.

But looks like a bit Pseudocode not 100% Powerbasic, lets try TÜLU 3 (https://playground.allenai.org/thread/msg_B6T4I1K3R5).

#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"

' GDI+ Flat API Declarations
DECLARE FUNCTION GdiplusStartup LIB "GDIPLUS.DLL" ALIAS "GdiplusStartup" (token AS DWORD, input AS GdiplusStartupInput, output AS DWORD) AS LONG
DECLARE SUB GdiplusShutdown LIB "GDIPLUS.DLL" ALIAS "GdiplusShutdown" (BYVAL token AS DWORD)

' GDI+ Startup Input Structure
TYPE GdiplusStartupInput
    GdiplusVersion AS LONG
    DebugEventCallback AS DWORD
    SuppressBackgroundThread AS BOOL
    SuppressExternalCodecs AS BOOL
END TYPE

' GDI+ Bitmap Loading and Conversion
DECLARE FUNCTION GdipCreateBitmapFromFile LIB "GDIPLUS.DLL" ALIAS "GdipCreateBitmapFromFile" (BYVAL filename AS ASCIIZ PTR, bitmap AS DWORD) AS LONG
DECLARE FUNCTION GdipCreateHBITMAPFromBitmap LIB "GDIPLUS.DLL" ALIAS "GdipCreateHBITMAPFromBitmap" (BYVAL bitmap AS DWORD, BYVAL background AS DWORD, hbmReturn AS DWORD, color AS DWORD) AS LONG

' OLE and DataObject
DECLARE FUNCTION OleCreateStaticFromData LIB "OLE32.DLL" ALIAS "OleCreateStaticFromData" (BYVAL pSrcDataObj AS DWORD, BYREF pFormatEtc AS FormatEtc, pClientSite AS DWORD, pStg AS DWORD, ppvObj AS DWORD) AS LONG

' FormatEtc and STGMEDIUM Structures
TYPE FormatEtc
    cfFormat AS WORD
    ptd      AS DWORD
    dwAspect AS DWORD
    lindex   AS LONG
    tymed    AS DWORD
END TYPE

TYPE STGMEDIUM
    tymed         AS DWORD
    union
        hBitmap       AS DWORD
        ' ... other members
    END UNION
    pUnkForRelease AS DWORD
END TYPE

' Main Function
FUNCTION PBMAIN () AS LONG
    LOCAL token AS DWORD
    LOCAL startupInput AS GdiplusStartupInput
    LOCAL pBitmap AS DWORD
    LOCAL hBitmap AS DWORD
    LOCAL formatEtc AS FormatEtc
    LOCAL stgMedium AS STGMEDIUM
    LOCAL pDataObject AS DWORD
    LOCAL pStaticObj AS DWORD

    ' Initialize GDI+
    startupInput.GdiplusVersion = 1
    IF GdiplusStartup(token, startupInput, BYVAL 0&) <> 0 THEN
        MSGBOX "Failed to initialize GDI+"
        EXIT FUNCTION
    END IF

    ' Load the image file
    IF GdipCreateBitmapFromFile("path\to\your\image.jpg", pBitmap) <> 0 THEN
        MSGBOX "Failed to load image"
        GOTO Cleanup
    END IF

    ' Create a GDI bitmap from the GDI+ bitmap
    IF GdipCreateHBITMAPFromBitmap(pBitmap, 0, hBitmap, 0) <> 0 THEN
        MSGBOX "Failed to create bitmap"
        GOTO Cleanup
    END IF

    ' Prepare the FormatEtc structure
    formatEtc.cfFormat = %CF_BITMAP
    formatEtc.dwAspect = %DVASPECT_CONTENT
    formatEtc.lindex = -1
    formatEtc.ptd = 0
    formatEtc.tymed = %TYMED_GDI

    ' Prepare the STGMEDIUM structure
    stgMedium.tymed = %TYMED_GDI
    stgMedium.hBitmap = hBitmap

    ' Create the DataObject COM object (simplified, actual implementation may vary)
    ' pDataObject = ... (create and initialize IDataObject)

    ' Create a static object from the data
    IF OleCreateStaticFromData(pDataObject, formatEtc, 0, 0, pStaticObj) <> %S_OK THEN
        MSGBOX "Failed to create static object"
        GOTO Cleanup
    END IF

    ' Use the static object as needed
    ' ...

Cleanup:
    ' Release GDI+ resources
    IF hBitmap THEN DeleteObject hBitmap
    IF pBitmap THEN GdipDisposeImage pBitmap

    ' Shutdown GDI+
    GdiplusShutdown token

END FUNCTION


Does not look perfect. et's see what Deepseek provides:


#COMPILE EXE
#DIM ALL

' Include necessary Windows and GDI+ headers
#INCLUDE "win32api.inc"
#INCLUDE "gdiplus.inc"

' Constants
%CF_BITMAP = 2
%TYMED_GDI = 1

' GDI+ function declarations
DECLARE FUNCTION GdipCreateBitmapFromFile LIB "gdiplus.dll" ALIAS "GdipCreateBitmapFromFile" _
    (BYVAL filename AS WSTRINGZ, BYREF bitmap AS DWORD) AS LONG
DECLARE FUNCTION GdipCreateHBITMAPFromBitmap LIB "gdiplus.dll" ALIAS "GdipCreateHBITMAPFromBitmap" _
    (BYVAL bitmap AS DWORD, BYREF hbmReturn AS DWORD, BYVAL background AS DWORD) AS LONG
DECLARE FUNCTION GdipDisposeImage LIB "gdiplus.dll" ALIAS "GdipDisposeImage" _
    (BYVAL image AS DWORD) AS LONG

' IDataObject interface GUID
$IID_IDataObject = GUID$("{0000010E-0000-0000-C000-000000000046}")

' Main function
FUNCTION PBMAIN() AS LONG
    LOCAL hBitmap AS DWORD
    LOCAL gdiplusToken AS DWORD
    LOCAL pBitmap AS DWORD
    LOCAL hr AS LONG
    LOCAL pDataObject AS IDataObject PTR
    LOCAL formatEtc AS FORMATETC
    LOCAL stgMedium AS STGMEDIUM

    ' Initialize GDI+
    LOCAL gdiplusStartupInput AS GdiplusStartupInput
    gdiplusStartupInput.GdiplusVersion = 1
    hr = GdiplusStartup(gdiplusToken, gdiplusStartupInput, BYVAL %NULL)
    IF hr <> %S_OK THEN
        MSGBOX "Failed to initialize GDI+"
        EXIT FUNCTION
    END IF

    ' Load the image file
    hr = GdipCreateBitmapFromFile("C:\path\to\your\image.jpg", pBitmap)
    IF hr <> %S_OK THEN
        MSGBOX "Failed to load image"
        GdiplusShutdown(gdiplusToken)
        EXIT FUNCTION
    END IF

    ' Convert the image to a bitmap handle
    hr = GdipCreateHBITMAPFromBitmap(pBitmap, hBitmap, 0)
    IF hr <> %S_OK THEN
        MSGBOX "Failed to create HBITMAP"
        GdipDisposeImage(pBitmap)
        GdiplusShutdown(gdiplusToken)
        EXIT FUNCTION
    END IF

    ' Prepare FORMATETC structure
    formatEtc.cfFormat = %CF_BITMAP
    formatEtc.ptd = %NULL
    formatEtc.dwAspect = %DVASPECT_CONTENT
    formatEtc.lindex = -1
    formatEtc.tymed = %TYMED_GDI

    ' Prepare STGMEDIUM structure
    stgMedium.tymed = %TYMED_GDI
    stgMedium.hBitmap = hBitmap
    stgMedium.pUnkForRelease = %NULL

    ' Create a DataObject COM object (simplified for demonstration)
    ' In a real implementation, you would need to implement the IDataObject interface.
    pDataObject = COM CREATE $IID_IDataObject
    IF ISFALSE(pDataObject) THEN
        MSGBOX "Failed to create DataObject"
        DeleteObject(hBitmap)
        GdipDisposeImage(pBitmap)
        GdiplusShutdown(gdiplusToken)
        EXIT FUNCTION
    END IF

    ' Use OleCreateStaticFromData to create a static object (not shown here)
    ' You would pass pDataObject to OleCreateStaticFromData.

    ' Clean up
    COM RELEASE pDataObject
    DeleteObject(hBitmap)
    GdipDisposeImage(pBitmap)
    GdiplusShutdown(gdiplusToken)

    MSGBOX "Success!"
END FUNCTION


This really looks like Powerbasic, and it was not even the model R1.

Title: Re: Rich Edit Text Object Model project
Post by: Charles Pegge on February 08, 2025, 03:57:02 AM
Perhaps the developers at Microsoft are incentivized to add these layers of complexity. It keeps them employed. GDIplus itself is very old and very powerful, as is RichEdit, and their APIs are quite easy to handle. José has demonstrated that it requires great determination to crack these higher layers.

I use RichEdit in my Peroxide IDE instead of Scintilla, and colorizing keywords is still possible. And GDIplus is essential for loading and saving images, including Opengl textures.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 08, 2025, 07:14:37 AM
I will implement IDataObject if I can find enough information about how to make it. For the moment is not necessary because the InsertImage method works like a charm.

After implementing IRichEditOleCallback, two important methods to load RTF files and save the contents to a file, also work fine.

Saving the contents of the rich edit control to a file is as easy as:

DIM strRTFData AS STRING = pRichEdit->GetRTFText
DIM hFile AS HANDLE = CreateFileW("Test1.rtf", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)
IF hFile THEN
   DIM dwBytesWritten AS DWORD
   DIM bSuccess AS BOOLEAN = WriteFile(hFile, STRPTR(strRTFData), LEN(strRTFData), @dwBytesWritten, NULL)
   CloseHandle(hFile)
END IF

To make it even easier, I will add a save method with parameters for the path and file name and a boolean value to overwrite or not the file if it already exists.

Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 08, 2025, 08:07:41 AM
I have found that the SHCreateDataObject API function can be used to create a data object,

https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shcreatedataobject

And a Microsoft example that uses it:

https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appplatform/DragDropVisuals/DataObject.cpp
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 08, 2025, 02:20:15 PM
@José Roca
Just ask Qwen KI he knows that.

Certainly! Implementing `IDataObject` in .NET (typically used in the context of drag-and-drop, clipboard operations, or OLE data transfers) can be a bit tricky because it involves working with COM (Component Object Model) interfaces. However, I'll provide you with an overview of what `IDataObject` is and how to implement it.

### What is IDataObject?

`IDataObject` is an interface that provides a standard mechanism for transferring data between applications. It is commonly used in scenarios such as:

- **Clipboard operations**: When copying or pasting data between applications.
- **Drag-and-Drop**: When dragging data from one control or application to another.
- **OLE Data Transfer**: When embedding or linking objects between applications.

The `IDataObject` interface is defined in the `System.Windows.Forms` namespace and is part of the .NET Framework. It allows you to store and retrieve data in various formats.

### Key Methods of IDataObject

The `IDataObject` interface has several key methods that you need to implement:

1. **GetData**: Retrieves the data associated with a specified format.
2. **GetDataPresent**: Checks whether the data object contains data in a specific format.
3. **GetFormats**: Returns a list of all the formats that the data object supports.
4. **SetData**: Stores data in the data object in a specified format.

### How to Implement IDataObject

To implement `IDataObject`, you need to create a class that implements these methods. Here's a basic example of how you might implement `IDataObject`:

```csharp
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class MyDataObject : IDataObject
{
    private readonly Dictionary<string, object> _data = new Dictionary<string, object>();

    // IDataObject implementation

    public object GetData(string format)
    {
        if (_data.ContainsKey(format))
        {
            return _data[format];
        }
        return null;
    }

    public object GetData(Type format)
    {
        return GetData(format.FullName);
    }

    public object GetData(string format, bool autoConvert)
    {
        // In this simple implementation, we ignore autoConvert
        return GetData(format);
    }

    public bool GetDataPresent(string format)
    {
        return _data.ContainsKey(format);
    }

    public bool GetDataPresent(Type format)
    {
        return GetDataPresent(format.FullName);
    }

    public bool GetDataPresent(string format, bool autoConvert)
    {
        // In this simple implementation, we ignore autoConvert
        return GetDataPresent(format);
    }

    public string[] GetFormats()
    {
        return GetFormats(false);
    }

    public string[] GetFormats(bool autoConvert)
    {
        // In this simple implementation, we ignore autoConvert
        return _data.Keys.ToArray();
    }

    public void SetData(object data)
    {
        SetData(data.GetType(), data);
    }

    public void SetData(Type format, object data)
    {
        SetData(format.FullName, data);
    }

    public void SetData(string format, object data)
    {
        _data[format] = data;
    }

    public void SetData(string format, bool autoConvert, object data)
    {
        // In this simple implementation, we ignore autoConvert
        SetData(format, data);
    }
}
```

### Explanation of the Implementation

- **_data**: A dictionary that holds the data in different formats. The key is the format (e.g., "Text", "Bitmap"), and the value is the actual data.
 
- **GetData**: This method retrieves the data for a given format. If the format exists in the `_data` dictionary, it returns the corresponding data; otherwise, it returns `null`.

- **GetDataPresent**: This method checks whether the data object contains data in a specific format. It simply checks if the format exists in the `_data` dictionary.

- **GetFormats**: This method returns an array of all the formats that the data object supports. In this case, it just returns the keys from the `_data` dictionary.

- **SetData**: This method stores data in the data object. You can specify the format explicitly or let the method infer the format from the type of the data.

### Using the Custom IDataObject

You can use your custom `IDataObject` implementation in scenarios like drag-and-drop or clipboard operations. Here's an example of using it with the clipboard:

```csharp
public class Program
{
    public static void Main()
    {
        // Create an instance of your custom IDataObject
        MyDataObject myDataObject = new MyDataObject();

        // Set some data
        myDataObject.SetData(DataFormats.Text, "Hello, World!");
        myDataObject.SetData(DataFormats.Bitmap, new System.Drawing.Bitmap(100, 100));

        // Copy the data to the clipboard
        Clipboard.SetDataObject(myDataObject);

        // Retrieve data from the clipboard
        IDataObject retrievedData = Clipboard.GetDataObject();

        if (retrievedData.GetDataPresent(DataFormats.Text))
        {
            string text = (string)retrievedData.GetData(DataFormats.Text);
            Console.WriteLine("Clipboard Text: " + text);
        }

        if (retrievedData.GetDataPresent(DataFormats.Bitmap))
        {
            var bitmap = (System.Drawing.Bitmap)retrievedData.GetData(DataFormats.Bitmap);
            Console.WriteLine("Clipboard contains a bitmap.");
        }
    }
}
```

### Notes

- **AutoConvert**: In this simple implementation, we ignored the `autoConvert` parameter. In more advanced implementations, you might want to handle automatic conversion between formats (e.g., converting text to Unicode).
 
- **COM Interop**: If you're working with OLE data transfer or other COM-based scenarios, you may need to deal with COM interop and implement additional interfaces like `IComDataObject`. This is more complex and typically requires marshaling data between managed and unmanaged code.

- **Data Formats**: Common data formats include `DataFormats.Text`, `DataFormats.Bitmap`, `DataFormats.FileDrop`, etc. You can also define custom formats if needed.

### Conclusion

This is a basic implementation of `IDataObject`. Depending on your use case, you might need to extend it further, especially if you're dealing with more complex scenarios like OLE data transfer or custom clipboard formats.

If you have any specific questions or need further clarification, feel free to ask!
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 08, 2025, 09:22:24 PM
> @José Roca
> Just ask Qwen KI he knows that.

Thanks, but as I'm not using C#, that code is completely useless to me.

I already have a couple of ideas of how to do it.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 09, 2025, 02:10:40 AM
I have added methods to save the entire contents or a selection of the contents of a rich edit control in RTF format (with or without the inclusion of inserted images or COM objects) or in text format.
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 09, 2025, 09:18:53 AM
I am still using Bob's PowerBasic and i do not plan to switch until Jürgen has something better.
He is still working on it. While there are some strange symptoms when compiling (sometimes no need to declare local variables, sometimes Labels and variables, or Labels and Function names get into conflict) i did not run against a limit.
Must say that the whole Object things that was implemented lastly seems to produce memory leaks so i do not use it for stuff that is often called.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 09, 2025, 09:47:29 AM
A new compiler made by a man alone is a project of at least 10 years (ask Charles). I'm too old to wait. FreeBasic offers what I need and all the code that I write can be compiled to 32 or 64 bit without changes.

BTW I'm going to add methods to work easily with fonts: Bold, Italic, StrikeOut, Underline, Height, FaceName, Offset and color. Having to fill a CHARFORMAT structure and read the documentation each time to see which values and flags you have to set is very tedious and time consuming.

So, instead of
`
pRichEdit->ExSetSel(98, 113)          ' // Select word at position 98, 113
DIM cf AS CHARFORMAT
cf.dwMask = CFM_BOLD                  ' // Character effects
cf.dwEffects = CFE_BOLD               ' // The CFE_BOLD value of the dwEffects member is valid
pRichEdit->SelectionCharFormat = cf   ' // Set the color
pRichEdit->HideSelection(TRUE)        ' // Hide selection

you will use

pRichEdit->SetSelFontBold(98,113)
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 09, 2025, 10:19:17 AM
If you bother to read the documentation of my WinFBX framework, you will seee that I offer all that PowerBasic offers and much more, and my CWindow class is better than DDT and as easy to use, and it has the advantage that is 100% SDK compatible.

See: https://github.com/JoseRoca/WinFBX/blob/master/docs/Windows%20GUI/CWindow%20Class.md

I also write extensive documentation and provide many examples. I consider that good documentation is essential, even if it takes me more time that to write the code.

An editor as complex as WinFBE (by Paul Squires) has been written entirely using my framework. This is a proof of how powerful it is.

And if I die, I'm providing the source code.

And if nobody uses it, I don't care. I'm working on it to keep my mind in good shape.


Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 09, 2025, 11:33:24 AM
I was very disappointed when Bob incorporated a graphic control that could only work with DDT. Also, because it was a subclassed image static control, it could not deal with alpha blended images. Therefore, I made my own graphic control that not only supported alpha blended images, but also has support for GDI, GDI+ and OpenGL, whereas the PB Graphic control only has native support for GDI.

Classes work so well in FreeBasic, that I'm thinking in writing several more that will incorporate related procedures of my framework.

For example, a CString class, with these methods (other that the intrinsic ones already provided by the language):

ClipLeft
ClipMid
ClipRight
Cset
Delete
Extract
Insert
Join
LCase
LSet
Parse
ParseCount
Remain
Remove
Repeat
Replace
Retain
Reverse
RSet
Shrink
Split
Spn
Tally
UCase
UnWrap
Verify
Wrap

and some more specialized such

Base64Decore
Base64Encode
CryotBinarytoString
CryptStringToBinary
etc.
Title: Re: Rich Edit Text Object Model project
Post by: Johan Klassen on February 09, 2025, 01:00:35 PM
very interesting José
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 10, 2025, 10:37:47 AM
I have added methods and properties to get/set the text color, height, offset and font face name of the selected text or the worder under the cursor if there is no slection. I have also added properties to get/set the bold, italic, strikeout and underline attibutes. These are toggle properties, i.e. if the text or word is not already bolded, it bolds it, and viceversa. This is useful if you write an editor with buttons to set/unset them (you only need a button, instead of two, to do it). If you want to do it programatically, there are properties (IsTextBold, etc.) to check its state, e.g. If pRichEdit->IsTextBolded returns false, call the SetTextBold method to bold it, and if it returns true, do nothing.

To do it programatically, first select the text with the SetSel method.


Printing must not be too difficult because I already have working methods to get the text and also a print class (CPrint).
See: https://github.com/JoseRoca/WinFBX/blob/master/docs/Printing/CPrint%20Class.md
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 10, 2025, 11:03:24 AM
With the FreeBasic classes I'm able not only of write wrappers and objects, but even implement new data types that can be used as if were native.

For example, FreeBasic has not yet a Unicode dynamic string, so I wrote CWSTR ( https://github.com/JoseRoca/WinFBX/blob/master/docs/String%20Management/CWSTR%20Class.md ). There were some quirks when I wrote it, but coderJeff was kind enough to modify the compiler to eliminate them and now it behaves as if it was a native data type, working directly with the intrinsic Free Basic string functions and operators.

So I like FreeBasic because it allows me to extend the language.
Title: Re: Rich Edit Text Object Model project
Post by: Zlatko Vid on February 10, 2025, 02:17:48 PM
Nice

QuoteSo I like FreeBasic because it allows me to extend the language.

my observation to this will be ...

I like to use OxygenBasic because it allows me to use it in a way i want.  ;)
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 10, 2025, 04:16:50 PM
I'm glad you finally found a compiler you like. You've been wandering around every forum out there.
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 10, 2025, 05:57:15 PM
𝐼'𝑚 𝑛𝑎𝑡𝑢𝑟𝑎𝑙𝑙𝑦 𝑑𝑖𝑠𝑎𝑝𝑝𝑜𝑖𝑛𝑡𝑒𝑑 𝑡𝘩𝑎𝑡 𝑦𝑜𝑢'𝑣𝑒 𝑠𝑤𝑖𝑡𝑐𝘩𝑒𝑑 𝑡𝑜 𝑡𝘩𝑒 𝑜𝑡𝘩𝑒𝑟 𝐵𝐴𝑆𝐼𝐶.
𝐹𝑜𝑟 𝑚𝑒, 𝑎𝑙𝑙 𝑚𝑦 𝑠𝑜𝑢𝑟𝑐𝑒 𝑐𝑜𝑑𝑒𝑠 𝑎𝑛𝑑 𝑖𝑚𝑝𝑜𝑟𝑡𝑎𝑛𝑡 𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑠 𝑎𝑟𝑒 𝑖𝑛 𝑃𝑜𝑤𝑒𝑟𝐵𝐴𝑆𝐼𝐶, 𝑠𝑜 𝐼 𝘩𝑎𝑣𝑒 𝑛𝑜 𝑐𝘩𝑜𝑖𝑐𝑒.
𝐼 𝑐𝑎𝑛'𝑡 𝑢𝑠𝑒 𝑎𝑛𝑦𝑡𝘩𝑖𝑛𝑔 𝑒𝑙𝑠𝑒 𝑟𝑖𝑔𝘩𝑡 𝑛𝑜𝑤, 𝑏𝑢𝑡 𝑖𝑓 𝐼 𝑐𝑜𝑢𝑙𝑑, 𝐼'𝑑 𝑑𝑒𝑓𝑖𝑛𝑖𝑡𝑒𝑙𝑦 𝑡𝑎𝑘𝑒 𝑎 𝑐𝑙𝑜𝑠𝑒𝑟 𝑙𝑜𝑜𝑘 𝑎𝑡 𝐶𝘩𝑎𝑟𝑙𝑒𝑠'𝑠 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟.
𝑊𝘩𝑦? 𝐵𝑒𝑐𝑎𝑢𝑠𝑒 𝑤𝑒 𝘩𝑎𝑣𝑒 𝑡𝘩𝑒 𝑠𝑜𝑢𝑟𝑐𝑒 𝑐𝑜𝑑𝑒 𝑡𝘩𝑒𝑟𝑒, 𝑎𝑙𝑙𝑜𝑤𝑖𝑛𝑔 𝑢𝑠 𝑡𝑜 𝑚𝑎𝑘𝑒 𝑎𝑑𝑗𝑢𝑠𝑡𝑚𝑒𝑛𝑡𝑠 𝑜𝑢𝑟𝑠𝑒𝑙𝑣𝑒𝑠.

𝐽ü𝑟𝑔𝑒𝑛 𝑖𝑠 𝑎𝑙𝑠𝑜 𝑚𝑎𝑘𝑖𝑛𝑔 𝑠𝑖𝑔𝑛𝑖𝑓𝑖𝑐𝑎𝑛𝑡 𝑝𝑟𝑜𝑔𝑟𝑒𝑠𝑠; 𝘩𝑒 𝑚𝑖𝑔𝘩𝑡 𝑐𝑟𝑒𝑎𝑡𝑒 𝑠𝑜𝑚𝑒𝑡𝘩𝑖𝑛𝑔 𝑡𝘩𝑎𝑡 𝑐𝑜𝑢𝑙𝑑 𝑏𝑒 𝑐𝑜𝑚𝑝𝑎𝑡𝑖𝑏𝑙𝑒 𝑤𝑖𝑡𝘩 𝑃𝑜𝑤𝑒𝑟𝐵𝐴𝑆𝐼𝐶.

𝑅𝑒𝑔𝑎𝑟𝑑𝑖𝑛𝑔 𝑡𝘩𝑒 10-𝑦𝑒𝑎𝑟 𝑠𝑝𝑎𝑛, 𝐼 𝑡𝘩𝑖𝑛𝑘 𝑡𝘩𝑎𝑡'𝑠 𝑎 𝑡𝘩𝑖𝑛𝑔 𝑜𝑓 𝑡𝘩𝑒 𝑝𝑎𝑠𝑡. 𝐼𝑛 𝑡𝑜𝑑𝑎𝑦'𝑠 𝐴𝐼 𝑒𝑟𝑎, 𝑠𝑜𝑚𝑒𝑜𝑛𝑒 𝑙𝑖𝑘𝑒 𝑦𝑜𝑢, 𝐽𝑜𝑠𝑒, 𝑐𝑜𝑢𝑙𝑑 𝑝𝑟𝑜𝑏𝑎𝑏𝑙𝑦 𝑑𝑒𝑣𝑒𝑙𝑜𝑝 𝑎 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑝𝑟𝑜𝑡𝑜𝑡𝑦𝑝𝑒 𝑤𝑖𝑡𝘩 𝑏𝑎𝑠𝑖𝑐 𝑑𝑎𝑡𝑎 𝑡𝑦𝑝𝑒𝑠 𝑖𝑛 𝑗𝑢𝑠𝑡 𝑡𝑤𝑜 𝑤𝑒𝑒𝑘𝑠.

𝑇𝘩𝑒 𝑏𝑖𝑔𝑔𝑒𝑟 𝑐𝘩𝑎𝑙𝑙𝑒𝑛𝑔𝑒 𝑖𝑠 𝑎𝑐𝑡𝑢𝑎𝑙𝑙𝑦 𝑡𝘩𝑒 𝑙𝑖𝑛𝑘𝑒𝑟.
𝑇𝘩𝑒 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑡𝘩𝑎𝑡 𝑔𝑒𝑛𝑒𝑟𝑎𝑡𝑒𝑠 𝑡𝘩𝑒 𝑎𝑠𝑠𝑒𝑚𝑏𝑙𝑦 𝑐𝑜𝑑𝑒 𝑖𝑠𝑛'𝑡 𝑡𝘩𝑒 𝑚𝑎𝑖𝑛 𝑖𝑠𝑠𝑢𝑒; 𝑚𝑜𝑠𝑡 𝑜𝑓 𝑡𝘩𝑎𝑡 𝑐𝑎𝑛 𝑛𝑜𝑤 𝑏𝑒 𝑑𝑜𝑛𝑒 𝑏𝑦 𝐴𝐼 𝑎𝑡 𝑡𝘩𝑒 𝑝𝑢𝑠𝘩 𝑜𝑓 𝑎 𝑏𝑢𝑡𝑡𝑜𝑛.
𝑌𝑜𝑢 𝑗𝑢𝑠𝑡 𝑠𝑎𝑦, "𝐻𝑒𝑦 𝐴𝐼, 𝑑𝑜 𝑡𝘩𝑖𝑠," 𝑎𝑛𝑑 𝑦𝑜𝑢 𝑔𝑒𝑡 𝑎𝑙𝑚𝑜𝑠𝑡 𝑓𝑖𝑛𝑖𝑠𝘩𝑒𝑑 𝑎𝑠𝑠𝑒𝑚𝑏𝑙𝑦 𝑐𝑜𝑑𝑒, 𝑤𝘩𝑖𝑐𝘩 𝑦𝑜𝑢 𝑐𝑎𝑛 𝑟𝑒𝑣𝑖𝑒𝑤 𝑎𝑛𝑑 𝑐𝑜𝑟𝑟𝑒𝑐𝑡 𝑏𝑒𝑓𝑜𝑟𝑒 𝑢𝑠𝑖𝑛𝑔. 𝐼𝑡'𝑠 𝑛𝑜 𝑙𝑜𝑛𝑔𝑒𝑟 𝑎 𝑚𝑎𝑡𝑡𝑒𝑟 𝑜𝑓 𝑤𝑟𝑖𝑡𝑖𝑛𝑔 𝑒𝑣𝑒𝑟𝑦𝑡𝘩𝑖𝑛𝑔 𝑏𝑦 𝘩𝑎𝑛𝑑—𝑡𝘩𝑒 𝐴𝐼 𝑡𝑎𝑘𝑒𝑠 𝑐𝑎𝑟𝑒 𝑜𝑓 𝑡𝘩𝑎𝑡.

𝑇𝘩𝑒 𝑟𝑒𝑎𝑙 𝑝𝑟𝑜𝑏𝑙𝑒𝑚 𝑙𝑖𝑒𝑠 𝑤𝑖𝑡𝘩 𝑡𝘩𝑒 𝑙𝑖𝑛𝑘𝑒𝑟, 𝑎𝑠 𝑡𝘩𝑒𝑟𝑒 𝑎𝑟𝑒 𝑣𝑎𝑟𝑖𝑜𝑢𝑠 𝑠𝑡𝑎𝑛𝑑𝑎𝑟𝑑𝑠 𝑖𝑛𝑣𝑜𝑙𝑣𝑒𝑑, 𝑚𝑎𝑘𝑖𝑛𝑔 𝑖𝑡 𝑎 𝑑𝑒𝑒𝑝𝑒𝑟 𝑡𝑜𝑝𝑖𝑐 𝑡𝑜 𝑒𝑥𝑝𝑙𝑜𝑟𝑒.

𝐻𝑜𝑤𝑒𝑣𝑒𝑟, 𝑖𝑓 𝑦𝑜𝑢 𝑘𝑛𝑜𝑤 𝑤𝘩𝑎𝑡 𝑦𝑜𝑢 𝑤𝑎𝑛𝑡, 𝐴𝐼 𝑐𝑎𝑛 𝑔𝑟𝑒𝑎𝑡𝑙𝑦 𝑎𝑠𝑠𝑖𝑠𝑡 𝑖𝑛 𝑡𝘩𝑒 𝑝𝑟𝑜𝑐𝑒𝑠𝑠.
𝑆𝑜, 𝑐𝑟𝑒𝑎𝑡𝑖𝑛𝑔 𝑎 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑡𝑜𝑑𝑎𝑦 𝑖𝑠 𝑛𝑜𝑡 𝑎 10-𝑦𝑒𝑎𝑟 𝑒𝑛𝑑𝑒𝑎𝑣𝑜𝑟, 𝑒𝑠𝑝𝑒𝑐𝑖𝑎𝑙𝑙𝑦 𝑖𝑓 𝑖𝑡'𝑠 𝑗𝑢𝑠𝑡 𝑜𝑛𝑒 𝑝𝑒𝑟𝑠𝑜𝑛 𝑤𝑜𝑟𝑘𝑖𝑛𝑔 𝑜𝑛 𝑖𝑡.
𝑇𝘩𝑒 𝑝𝑟𝑖𝑛𝑐𝑖𝑝𝑙𝑒 𝑖𝑠 𝑐𝑙𝑒𝑎𝑟: 𝑝𝑢𝑡 𝑎𝑙𝑙 𝑒𝑠𝑠𝑒𝑛𝑡𝑖𝑎𝑙 𝑓𝑒𝑎𝑡𝑢𝑟𝑒𝑠 𝑖𝑛 𝑡𝘩𝑒 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑎𝑛𝑑 𝑒𝑣𝑒𝑟𝑦𝑡𝘩𝑖𝑛𝑔 𝑒𝑙𝑠𝑒 𝑖𝑛 𝑙𝑖𝑏𝑟𝑎𝑟𝑖𝑒𝑠.

𝐼'𝑑 𝑙𝑖𝑘𝑒 𝑡𝑜 𝑎𝑑𝑑 𝑡𝘩𝑎𝑡 𝑓𝑜𝑟 𝑐𝑒𝑟𝑡𝑎𝑖𝑛 𝑡𝑎𝑠𝑘𝑠 𝑟𝑒𝑞𝑢𝑖𝑟𝑖𝑛𝑔 𝑟𝑒𝑎𝑑𝑦-𝑚𝑎𝑑𝑒 𝑙𝑖𝑏𝑟𝑎𝑟𝑖𝑒𝑠, 𝐼 𝑎𝑙𝑠𝑜 𝑒𝑛𝑗𝑜𝑦 𝑢𝑠𝑖𝑛𝑔 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐.
𝐴𝑠 𝑦𝑜𝑢'𝑣𝑒 𝑚𝑒𝑛𝑡𝑖𝑜𝑛𝑒𝑑 𝑏𝑒𝑓𝑜𝑟𝑒, 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐 𝑖𝑠 𝑒𝑠𝑠𝑒𝑛𝑡𝑖𝑎𝑙𝑙𝑦 𝑎 𝑚𝑎𝑐𝑟𝑜 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑎𝑛𝑑 𝑛𝑜𝑡 𝑎 𝑡𝑟𝑢𝑒 𝐵𝑎𝑠𝑖𝑐 𝑙𝑎𝑛𝑔𝑢𝑎𝑔𝑒, 𝑎 𝑓𝑎𝑐𝑡 𝑡𝘩𝑎𝑡 𝑟𝑒𝑚𝑎𝑖𝑛𝑠 𝑢𝑛𝑐𝘩𝑎𝑛𝑔𝑒𝑑 𝑡𝑜𝑑𝑎𝑦.
𝐻𝑜𝑤𝑒𝑣𝑒𝑟, 𝑖𝑡 𝑜𝑓𝑓𝑒𝑟𝑠 𝑙𝑖𝑏𝑟𝑎𝑟𝑖𝑒𝑠 𝑡𝘩𝑎𝑡 𝑔𝑟𝑒𝑎𝑡𝑙𝑦 𝑠𝑖𝑚𝑝𝑙𝑖𝑓𝑦 𝑤𝑜𝑟𝑘 𝑓𝑜𝑟 𝑠𝑝𝑒𝑐𝑖𝑓𝑖𝑐 𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑠.

𝑇𝘩𝑖𝑠 𝑖𝑠 𝑝𝑎𝑟𝑡𝑖𝑐𝑢𝑙𝑎𝑟𝑙𝑦 𝑟𝑒𝑙𝑒𝑣𝑎𝑛𝑡 𝑐𝑜𝑚𝑝𝑎𝑟𝑒𝑑 𝑡𝑜 𝑤𝘩𝑎𝑡 𝑃𝑜𝑤𝑒𝑟𝐵𝑎𝑠𝑖𝑐 𝑜𝑓𝑡𝑒𝑛 𝑙𝑎𝑐𝑘𝑠—𝑒𝑥𝑐𝑒𝑝𝑡, 𝑜𝑓 𝑐𝑜𝑢𝑟𝑠𝑒, 𝑓𝑜𝑟 𝑦𝑜𝑢𝑟 𝑙𝑖𝑏𝑟𝑎𝑟𝑖𝑒𝑠, 𝑤𝘩𝑖𝑐𝘩 𝑎𝑟𝑒 𝑡𝘩𝑒 𝑒𝑥𝑐𝑒𝑝𝑡𝑖𝑜𝑛—𝑤𝘩𝑒𝑟𝑒 𝘩𝑎𝑣𝑖𝑛𝑔 𝑎𝑛 𝐻𝑇𝑇𝑃 𝑜𝑟 𝐽𝑆𝑂𝑁 𝑙𝑖𝑏𝑟𝑎𝑟𝑦 𝑖𝑠 𝑐𝑟𝑢𝑐𝑖𝑎𝑙.

𝐴𝑑𝑑𝑖𝑡𝑖𝑜𝑛𝑎𝑙𝑙𝑦, 𝐴𝐼 𝘩𝑎𝑛𝑑𝑙𝑒𝑠 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐 𝑣𝑒𝑟𝑦 𝑤𝑒𝑙𝑙, 𝑚𝑒𝑎𝑛𝑖𝑛𝑔 𝑚𝑎𝑛𝑦 𝐴𝐼 𝑡𝑜𝑜𝑙𝑠 𝑐𝑎𝑛 𝑝𝑟𝑜𝑑𝑢𝑐𝑒 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐 𝑐𝑜𝑑𝑒 𝑡𝘩𝑎𝑡 𝑖𝑠 𝑎𝑙𝑚𝑜𝑠𝑡 𝑝𝑟𝑜𝑑𝑢𝑐𝑡𝑖𝑜𝑛-𝑟𝑒𝑎𝑑𝑦.
𝑌𝑜𝑢 𝑐𝑎𝑛 𝑎𝑠𝑠𝑖𝑔𝑛 𝑟𝑒𝑎𝑙𝑙𝑦 𝑐𝑜𝑚𝑝𝑙𝑒𝑥 𝑡𝑎𝑠𝑘𝑠, 𝑙𝑖𝑘𝑒 𝘩𝑎𝑣𝑖𝑛𝑔 𝑎𝑛 𝐴𝐼 𝑎𝑛𝑎𝑙𝑦𝑧𝑒 𝑜𝑢𝑡𝑝𝑢𝑡, 𝑎𝑛𝑑 𝑖𝑡 𝑐𝑎𝑛 𝑓𝑜𝑟𝑚𝑢𝑙𝑎𝑡𝑒 𝑡𝘩𝑎𝑡 𝑖𝑛 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐.
𝐼'𝑚 𝑛𝑜𝑡 𝑠𝑢𝑟𝑒 𝑤𝘩𝑦 𝑠𝑜𝑚𝑒 𝐴𝐼𝑠 𝑠𝑒𝑒𝑚 𝑡𝑜 𝑝𝑒𝑟𝑓𝑜𝑟𝑚 𝑏𝑒𝑡𝑡𝑒𝑟 𝑖𝑛 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐 𝑡𝘩𝑎𝑛 𝑖𝑛 𝑃𝑜𝑤𝑒𝑟𝐵𝑎𝑠𝑖𝑐, 𝑏𝑢𝑡 𝑖𝑡 𝑚𝑖𝑔𝘩𝑡 𝑏𝑒 𝑑𝑢𝑒 𝑡𝑜 𝑃𝑢𝑟𝑒𝐵𝑎𝑠𝑖𝑐'𝑠 𝑚𝑜𝑟𝑒 𝐶-𝑙𝑖𝑘𝑒 𝑠𝑦𝑛𝑡𝑎𝑥, 𝑤𝘩𝑖𝑐𝘩 𝑐𝑎𝑛 𝑎𝑙𝑠𝑜 𝑏𝑒 𝑎 𝑑𝑟𝑎𝑤𝑏𝑎𝑐𝑘.

𝑂𝑛 𝑎 𝑠𝑖𝑑𝑒 𝑛𝑜𝑡𝑒, 𝐼 𝑟𝑒𝑎𝑙𝑙𝑦 𝑒𝑛𝑗𝑜𝑦 𝑖𝑡 𝑤𝘩𝑒𝑛 𝑦𝑜𝑢 𝑜𝑐𝑐𝑎𝑠𝑖𝑜𝑛𝑎𝑙𝑙𝑦 𝑐𝑜𝑚𝑒 𝑏𝑦, 𝑝𝑟𝑜𝑔𝑟𝑎𝑚, 𝑎𝑛𝑑 𝑤𝑟𝑖𝑡𝑒 𝑎 𝑏𝑖𝑡.
𝐼𝑡'𝑠 𝑎 𝑔𝑟𝑒𝑎𝑡 𝑒𝑛𝑟𝑖𝑐𝘩𝑚𝑒𝑛𝑡 𝑓𝑜𝑟 𝑢𝑠 𝑎𝑛𝑑 𝑎 𝑙𝑜𝑣𝑒𝑙𝑦 𝑟𝑒𝑚𝑖𝑛𝑑𝑒𝑟 𝑜𝑓 𝑡𝘩𝑒 𝑜𝑙𝑑 𝑡𝑖𝑚𝑒𝑠.


#𝑃𝑜𝑤𝑒𝑟𝐵𝐴𝑆𝐼𝐶 #𝐶𝑜𝑚𝑝𝑖𝑙𝑒𝑟𝐷𝑒𝑣𝑒𝑙𝑜𝑝𝑚𝑒𝑛𝑡 #𝐴𝑟𝑡𝑖𝑓𝑖𝑐𝑖𝑎𝑙𝐼𝑛𝑡𝑒𝑙𝑙𝑖𝑔𝑒𝑛𝑐𝑒 #𝑃𝑟𝑜𝑔𝑟𝑎𝑚𝑚𝑖𝑛𝑔𝐶𝑜𝑚𝑚𝑢𝑛𝑖𝑡𝑦 #𝑁𝑜𝑠𝑡𝑎𝑙𝑔𝑖𝑎
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 10, 2025, 07:08:48 PM
> 𝐼'𝑚 𝑛𝑎𝑡𝑢𝑟𝑎𝑙𝑙𝑦 𝑑𝑖𝑠𝑎𝑝𝑝𝑜𝑖𝑛𝑡𝑒𝑑 𝑡𝘩𝑎𝑡 𝑦𝑜𝑢'𝑣𝑒 𝑠𝑤𝑖𝑡𝑐𝘩𝑒𝑑 𝑡𝑜 𝑡𝘩𝑒 𝑜𝑡𝘩𝑒𝑟 𝐵𝐴𝑆𝐼𝐶.

What choice did I have? PowerBasic is dead.

I don't like PureBasic. It has an horrible syntax. Don't know why it is called "Pure".

> 𝑅𝑒𝑔𝑎𝑟𝑑𝑖𝑛𝑔 𝑡𝘩𝑒 10-𝑦𝑒𝑎𝑟 𝑠𝑝𝑎𝑛, 𝐼 𝑡𝘩𝑖𝑛𝑘 𝑡𝘩𝑎𝑡'𝑠 𝑎 𝑡𝘩𝑖𝑛𝑔 𝑜𝑓 𝑡𝘩𝑒 𝑝𝑎𝑠𝑡. 𝐼𝑛 𝑡𝑜𝑑𝑎𝑦'𝑠 𝐴𝐼 𝑒𝑟𝑎, 𝑠𝑜𝑚𝑒𝑜𝑛𝑒 𝑙𝑖𝑘𝑒 𝑦𝑜𝑢, 𝐽𝑜𝑠𝑒, 𝑐𝑜𝑢𝑙𝑑 𝑝𝑟𝑜𝑏𝑎𝑏𝑙𝑦 𝑑𝑒𝑣𝑒𝑙𝑜𝑝 𝑎 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑝𝑟𝑜𝑡𝑜𝑡𝑦𝑝𝑒 𝑤𝑖𝑡𝘩 𝑏𝑎𝑠𝑖𝑐 𝑑𝑎𝑡𝑎 𝑡𝑦𝑝𝑒𝑠 𝑖𝑛 𝑗𝑢𝑠𝑡 𝑡𝑤𝑜 𝑤𝑒𝑒𝑘𝑠.

Why bother?

> 𝑇𝘩𝑒 𝑝𝑟𝑖𝑛𝑐𝑖𝑝𝑙𝑒 𝑖𝑠 𝑐𝑙𝑒𝑎𝑟: 𝑝𝑢𝑡 𝑎𝑙𝑙 𝑒𝑠𝑠𝑒𝑛𝑡𝑖𝑎𝑙 𝑓𝑒𝑎𝑡𝑢𝑟𝑒𝑠 𝑖𝑛 𝑡𝘩𝑒 𝑐𝑜𝑚𝑝𝑖𝑙𝑒𝑟 𝑎𝑛𝑑 𝑒𝑣𝑒𝑟𝑦𝑡𝘩𝑖𝑛𝑔 𝑒𝑙𝑠𝑒 𝑖𝑛 𝑙𝑖𝑏𝑟𝑎𝑟𝑖𝑒𝑠.

I already have a compiler that I like, providing the essential and much more. For the lacking features, I write classes, like I'm doing now.

Title: Re: Rich Edit Text Object Model project
Post by: Frank Brübach on February 10, 2025, 07:57:07 PM
Year 2025: That will took some time to get the  final result for this simple example below by an interpreter and compiler I think
 minimum half of a year until one year for a first release.. my Personal opinion..

'
dim a,b,c as long
dim s as string

a=10:b=20:c=a*b
s="Hello NewBasic " & c

print s
'
 you need a lexer(tokenizer input), parser, semantic and syntax analyzer, code optimizer (performance)
 and code generator (translation to machine code).
 
 last not least you need a good IDE.
 
 there are some tools That Can Help (parser, lexer, compiler tools)

 handcoding with manpower: if you are planing to do all from Scratch: This could take several months to a year or more.

 support by an AI you need some good programmer to adept complexed written code for corrections and error handling

 you need support for a good documentation, a nice engaged forum, beta tester much more...

 this can take three to six month in my eyes to build a first version

 Using existing tools (other compiler for example) can significantly speed up the process at all

 Implement basic optimizations to improve the performance of the generated code and

 testing and debugging the compiler to ensure it works correctly across different platforms is a final step

 realistical timeline (for advanced user and programmers!)

 Week 1-4: Define the language grammar and set up the lexer and parser.

 Week 5-7: Implement semantic analysis and build the intermediate representation

 Week 8-10: Work on basic optimizations and implementations

 Week 11-14: Implement code generation

 Week 15-18: Testing and debugging

 all in all about three to six month minimum continous work at the compiler

 you cant do this Work Alone I guess you need 3 - 4 persons for this job

'
 kind regards, frank
Title: Re: Rich Edit Text Object Model project
Post by: Charles Pegge on February 10, 2025, 08:13:48 PM
QuoteA new compiler made by a man alone is a project of at least 10 years (ask Charles). I'm too old to wait. FreeBasic offers what I need and all the code that I write can be compiled to 32 or 64 bit without changes.

I think the two weeks spent creating a Basic compiler would soon turn into 10 years. In my case it is 17 years :). But the satisfaction is immense, and I am constantly discovering new ways of doing things. I've also been able to incorporate some of the awkward fringe features like passing and returning UDTs byval.

I'm not sure what to make of PowerBasic. Is it worth reviving? Is it worth building and maintaining a compatibility library?
Title: Re: Rich Edit Text Object Model project
Post by: Theo Gottwald on February 10, 2025, 10:31:07 PM
To understand my journey, Charles, you need to know the whole story. I started with the Atari 400 and Atari BASIC, a language packed in just 8 KB of ROM for the 8-bit 6502 processor. Back then, we already had concepts like "Goes Up" and "Return," along with variables, addition and subtraction, and commands like Peek and Poke.

For faster operations, we had the USR command to call machine code. This is where my programming journey began, and I've built upon it ever since.

What I loved about Power BASIC and procedural programming was the ability to take functional components, name them, and assemble something greater, much like building blocks.

This concept still fascinates me today!
Yet, there's often the challenge of naming those components, as many names have already been taken over time.
So it is missing "Namespaces", i call them Modules.

The fantastic thing is that the same concept still works today, though I wish certain features were expanded.

For instance, I'd love functions that can return multiple results, similar to using two Ref parameters but more straightforward. I once suggested genuine parameters for threads —not just one parameter, but the ability to pass any number of parameters like a function or procedure. Bob did something but it never really worked.
Anyway, I could achieve this with objects later, but frequent calls often led to memory leaks, especially with Power BASIC objects.

I come from the old school of simple BASIC, and one has to understand that. I've dabbled in C and other modular languages, but my thought processes have developed in specific ways. By the way, Jose is right: Power BASIC is more of a macro compiler, yet it has incredible libraries. For many modern needs, a simple subroutine from a Pure BASIC library can do half the work, which results in significant efficiency.

When considering a programming language, it's crucial to remember that while a language might be good, it needs libraries to solve real problems easily.

Understanding my journey means seeing the path from the 8-bit Atari 400, through various stages, to Power BASIC—a dream come true for me back then. I still take joy in how far I've come and in grasping how it all works together.

I also want to mention that I appreciate having direct contact with the people who can tweak a compiler. In case of errors or when specific features are needed, it's invaluable to reach out directly to them. This is very interesting to me and is one of the main reasons I really like Charles's Oxygen Basic, aside from its excellent macro capabilities.

Creating a basic compiler isn't as daunting as it once was. It's worth noting how many tiny BASIC and Small BASIC compilers are floating around out there. Sure, some may be interpreters, but many are indeed compilers, typically developed in a relatively short period. Moreover, if you have a clear understanding of which subprograms and parameters you need, you don't have to create them from scratch. Once you can describe it clearly and outline a precise plan, which is often the main challenge, you can hand it over to AI. Today's AI systems can essentially automate this process; they can write code efficiently. While a complete solution in Python might be feasible, AI systems have become quite proficient at working with ASM as well.
In response to your comment, Charles, I want to emphasize that a machine-generated compiler program is certainly not as refined and perfect as one you have been improving over the last 10 years based on practical observations. There is a significant difference; it's simply a tool where you input code at the top and get the combined assembly code output at the bottom. And then, of course, the optimization phase begins, which always requires time.

#CompilerDevelopment #MachineLearning #CodeOptimization #SoftwareEngineering #AIProgramming
#Compiler #Programming #AI #Coding #Automation
#OxygenBasic #CompilerDevelopment #ProgrammingCommunity #MacroCapabilities #CharlesOxygen

🌟💻👾✨

#ProgrammingJourney #Atari400 #PowerBASIC #Coding #RetroGaming #TechHistory 🎮🚀🧩💡
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 11, 2025, 03:43:26 AM
Enough of this daydreaming. Too many people expounding theories and asking others to do the work. If AI is so wonderful, ask it to make you a compiler to your liking. I'm still waiting for the damn AI to give me a worthwhile answer.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 11, 2025, 05:26:16 AM
I have asked Copilot: "Give me code to implement a rich edit ole callback with FreeBasic".

And it has answered:

#include "windows.bi"
#include "richedit.bi"
#include "commctrl.bi"

' OLE callback function
function RichEditOleCallback(byval lpParam as LPDATAOBJECT, byval iob as long, byval ppt as LPPOINTL, byval pFlags as long) as HRESULT
    ' Example: handle the callback
    print "OLE callback function called."
    return S_OK
end function

' Main function
sub Main()
    dim hInstance as HINSTANCE
    dim hWnd as HWND
    dim hRichEdit as HWND

    hInstance = GetModuleHandle(NULL)

    ' Initialize Rich Edit control
    LoadLibrary("riched20.dll")

    ' Create a window class
    dim wc as WNDCLASS
    wc.style = CS_HREDRAW or CS_VREDRAW
    wc.lpfnWndProc = @DefWindowProc
    wc.cbClsExtra = 0
    wc.cbWndExtra = 0
    wc.hInstance = hInstance
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION)
    wc.hCursor = LoadCursor(NULL, IDC_ARROW)
    wc.hbrBackground = cast(HBRUSH, COLOR_WINDOW + 1)
    wc.lpszMenuName = NULL
    wc.lpszClassName = strptr("RichEditOLECallback")

    RegisterClass(@wc)

    ' Create main window
    hWnd = CreateWindowEx(0, strptr("RichEditOLECallback"), strptr("Rich Edit OLE Callback Example"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL)

    ' Create Rich Edit control
    hRichEdit = CreateWindowEx(0, RICHEDIT_CLASS, "", WS_CHILD or WS_VISIBLE or WS_BORDER or ES_MULTILINE or ES_AUTOVSCROLL, 10, 10, 780, 580, hWnd, NULL, hInstance, NULL)

    ' Set OLE callback
    dim pOleCallback as IRichEditOleCallback ptr
    pOleCallback = @RichEditOleCallback
    SendMessage(hRichEdit, EM_SETOLECALLBACK, 0, cast(LPARAM, pOleCallback))

    ' Show window
    ShowWindow(hWnd, SW_SHOW)
    UpdateWindow(hWnd)

    ' Message loop
    dim msg as MSG
    while GetMessage(@msg, NULL, 0, 0)
        TranslateMessage(@msg)
        DispatchMessage(@msg)
    wend
end sub

Main()

Let's see...

' OLE callback function
function RichEditOleCallback(byval lpParam as LPDATAOBJECT, byval iob as long, byval ppt as LPPOINTL, byval pFlags as long) as HRESULT
    ' Example: handle the callback
    print "OLE callback function called."
    return S_OK
end function

Invented parameters [(byval lpParam as LPDATAOBJECT, byval iob as long, byval ppt as LPPOINTL, byval pFlags as long)] and no code in the callback. Very helpful!
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 11, 2025, 06:19:24 AM
Back to things that matter. I'm going to implement the ITExtDocument2 interface inside of the CRichEditCtl class.

I can retrieve a pointer to this interface of the document object that creates the rich edit control with this method:

' ========================================================================================
' Retrieves a pointer to the ITextDocument2 interface
' ========================================================================================
PRIVATE FUNCTION CRichEditCtrl.GetDocInterface () AS ITextDocument2 PTR
   ' // Retrieve a pointer to the IRichEditOle interface of the rich edit control
   DIM pUnk AS IUnknown PTR, pTextDocument2 AS ITextDocument2 PTR
   this.SetResult(SendMessageW(hRichEdit, EM_GETOLEINTERFACE, 0, cast(LPARAM, @pUnk)))
   ' // Retrieve a pointer to its ITextDocument2 interface
   IF pUnk THEN
      DIM IID_ITextDocument2_ AS IID = AfxGuid(AFX_IID_ITextDocument2)
      this.SetResult(IUnknown_QueryInterface(pUnk, @IID_ITextDocument2_, @pTextDocument2))
      IUnknown_Release(pUnk)
   END IF
   RETURN pTextDocument2
END FUNCTION
' ========================================================================================

The question is: "To be or not to be?", sorry  :), I mean, to store this pointer in an instance variable or to retrieve it each time that I call a ITextDocument2 method just in case it has changed. Don't know if some action could make the rich edit control to destroy the document object and create a new one or if the document object remains the same for the life of the rich edit control. The MSDN documentation does not provide this information.

I have made test with the two ITextDocument2 interface that potentially could change it: Open and New.

Open: Opens a new document. If another document is open, this method saves any current changes and closes the current document before opening a new one.

New: Opens a new document.

The difference is that, besides closing the current document, Open loads the specified file.

Neither Open, nor New, change the interface pointer. They reuse the existing document object.

Getting a document interface pointer each time I call a ITextDocument2 method will be a bit slower (although not much) but safer. If i cache the value of the pointer the application would crash if it changes for some reason.

Does AI something to say?
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 12, 2025, 08:35:25 AM
New methods:

OpenDoc and SaveDoc.

Can open and save files in these formats: ansi, utf-8, unicode and rtf.

You can also load a file in a format and save it in another format.


New method:

ChangeCase

Changes the case of the characters in the specified range to lower case, upper case, title case, sentence case or it can toggle the case.

New methods:

Freeze and Unfreeze

If the freeze count is nonzero, screen updating is disabled. This allows a sequence of editing operations to be performed without the performance loss and flicker of screen updating.
Title: Re: Rich Edit Text Object Model project
Post by: Johan Klassen on February 12, 2025, 10:48:20 AM
hello José Roca  :)
I think that OpenDoc and SaveDoc will be immensely useful, thank you
Title: Re: Rich Edit Text Object Model project
Post by: Frank Brübach on February 12, 2025, 03:43:34 PM
hello jose, you have mentioned at first page to built in new features in your richEdit TOM library
and new interfaces

does that mean you can also load word files (*.doc) and Excel files (*.xls) ?

lRichEditOLE, lTExtrStrings, lTextStoryRanges.

Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 12, 2025, 04:19:23 PM
No. As I have said,

Can open and save files in these formats: ansi, utf-8, unicode and rtf.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 15, 2025, 08:03:26 PM
I have made CRichEditCtrl zoomaable by adding the ES_EX_ZOOMABLE (&H0010) extended style.

' // EM_SETEXTENDEDSTYLE = ECM_FIRST + 10
' // EM_GETEXTENDEDSTYLE = ECM_FIRST + 11
DIM dwExStyle AS DWORD = SendMessageW(m_hRichEdit, ECM_FIRST + 11, 0, 0)
SendMessageW(m_hRichEdit, ECM_FIRST + 10, dwExStyle, &H0010)

I have used a magic number because the ES_EX_ZOOMABLE constant does not exist in the FreeBasic headers. It works like a charm.

I have tried to write a method to print RTF text. It prints, but the width and height of the page are not right.

As I don't have a printer, I'm testing it with "Microsoft Print to PDF".


Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 18, 2025, 04:21:31 PM
I've managed to get printing working.

I also have implemented an optional WYSIWYG option. If you pass the name of the printer, formating in the screen will be the same as it will be printed.

Also methods to get and set the position of the caret.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 19, 2025, 02:58:36 AM
Added two new methods:

AppendRtfFile

Appends the contents of an RTF file.

InsertRtfFile

Inserts the contents of an RTF file at the specified location.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 21, 2025, 02:51:59 AM
Solved two problems with subclassed multiline edit rich controls.

1) If you press the ESC key, the control sends a WM_CLOSE message to its parent window, closing the application. The solution is to eat this keystroke. This can be done when processing the WM_KEYDOWN message.

2) TAB key. Its normal behavior in a rich edit control is to insert tabs and also provide naviagation between the cells of a table and to add a new row to the table y you press it in the last row and column. However, you may want to use it to provide navigation between controls. You can also eat it when processing VM_KEYDOWN, but then the control insists in inserting a tab. The solution is to eat it when procesing the WM_GETDLGCODE message

The subclassed CRichEditCtrl control uses the default behavior by default, but allows to eat the tab key and provide navigation between controls if the AcceptTab property is set to FALSE.

You can add tables by writing

+-----------------------+---------------------+---------------------+

and pressing the RETURN key.

Title: Re: Rich Edit Text Object Model project
Post by: Johan Klassen on February 21, 2025, 11:35:28 AM
José Roca
I am following your progress with great interest  :)
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 21, 2025, 03:56:03 PM
Quote from: Johan Klassen on February 21, 2025, 11:35:28 AMJosé Roca
I am following your progress with great interest  :)

Thanks. I'm rediscovering this powerful control. It has changed very much since my first tests. For example, the addition of messages like EM_INSERTIMAGE and EM_SETTEXTEX allows the insertion of images and RTF text without having to use the more complicated and slower method of streaming. The methods that use EM_SETTEXTEX are faster because they read and set the whole file at once, whereas the methods that use EM_STREAMIN have to make repeated calls to load and set the rtf contents. One is faster but needs more memory and the other is slower but needs less memory. I will keep the two methods and the user will choose the one that it better suits him.

Also now we can use the TOM2 interfaces (previously, the older TOM interfaces could only be used with C++ because they used the thiscall calling convention). CRichEditCtrl already has support for all the Rich Edit messages and I will add support for all the methods of the ITextDocument2 interface. I have already added the most important ones: NewDoc, OpenDoc and SaveDoc.

For the other interfaces, I will write optional individual classes that you can easily use when you need them. CRichEditCtrl will have methods to get pointers to these interfaces (ITextFont, ITextRange, ITextPara, etc) and then you will only need to create an instance of the class, e.g. CTextFont, passing the retrieved pointer and use the methods that the class contain. This method has the advantage of not bloating CRichEditCtrl and also allows me to add functionalitry gradually (there are many interfaces, each one containing many methods).

Also, from my previous experience with other controls that I have wrote, like CXpButton, ex VB users and also writers of visual designers like Paul Squires, prefer properties than Get/Set methods. Get properties and methods are the same, but the Set methods allow to return a result code. To please everybody, I'm implementing both properties and methods.

After implementing a new method, I document it in GitHub. This has two advantages: it forces me to revise the code, sometimes discovering an error or making me to think of a way to improve it, and having the documentation always updated. I know by experience that if you delay this tedious task, then the work piles up and you never find time to do it.

This is the current documentation for CRichEditCtrl.

https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CRichEditCtrl%20Class.md

Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 22, 2025, 01:24:00 AM
I have removed LoadRtfFromFile and will use OpenDoc. The reason is that LoadRtfFile, that uses streaming, only works fine with RTF and ANSI files, while OpenDoc can also work with UTF8 and Unicode, and converts ANSI to Unicode. Also the OpenDoc method does not need callbacks.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 27, 2025, 06:23:56 AM
CRichEditCtrl now incorporates all the Rich Edit Windows messages and all the methods of the ITextDocument2 interface.

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CRichEditCtrl%20Class.md

Tonight I will implement the CTextFont2, wrapping all the methods of the ITextFont2 interface.

Next, I will work in the ITextRange2 and ITextPara2 interfaces.
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on February 28, 2025, 01:35:40 PM
I have finished the implementation of the CTextFont2 class.

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CTextFont2%20Class.md
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on March 01, 2025, 07:01:41 PM
I have finished the implementation of the CTextRange2 class.

Documentation: https://github.com/JoseRoca/WinFBX/blob/master/docs/RichEdit/CTextRange2%20Class.md
Title: Re: Rich Edit Text Object Model project
Post by: José Roca on March 02, 2025, 01:52:09 AM
Rich Edit Control class versión 01.

CRichEditCtrl.bas demonstrates how to create an instance of the control.

The GUI has a button called "Test". Processing the IDC_TEST message you can add your code for testing.

The same source compiles to 32 or 64 bit without changes in the code.

Made with the aid of NI (Natural Intelligence). Not AI involved.

Documentation: https://github.com/JoseRoca/WinFBX/tree/master/docs/RichEdit