• Welcome to PlanetSquires Forums.
 

CWindow RC07

Started by José Roca, May 23, 2016, 12:58:08 PM

Previous topic - Next topic

José Roca

CWindow release candidate 7.

New wrappers.

New graphic control.

The AfxImagexxx functions to convert .png icons have been renamed as AfxIconxxx. I may need AfxImage for other purposes.

CGRAPHCTX is a graphic control for pictures, text and graphics.

This control features persistence and uses a virtual buffer (set initially equal to the size of the control when it is created) to allow to display images bigger than the size of the control. Scrollbars are automatically added when the size of the virtual buffer is bigger than the size of the control and removed when unneeded. It also features keyboard navigation and sends command messages to the parent window or dialog when the Return or Escape keys are pressed, and notification messages for mouse clicks.

To save the graphic bitmap to a file you just need to call one of the SaveImage methods: SaveImageToBmp, SaveImageToJpeg, SaveImageToPng, SaveImageToGif, SaveImageToTiff.

The PrintImage method prints the graphic bitmap in the default printer.

José Roca

Graphic control skeleton.


' ########################################################################################
' Microsoft Windows
' File: CW_GraphCtxSkeleton.fbtpl
' Contents: CWindow Graphic Control Skeleton
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose 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.
' ########################################################################################

#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/CGraphCtx.inc"

USING Afx.CWindowClass
USING Afx.CGraphCtxClass

CONST IDC_GRCTX = 1001

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)

' ========================================================================================
' Window 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_CREATE
         EXIT FUNCTION

      CASE WM_COMMAND
         SELECT CASE LOWORD(wParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application sending an WM_CLOSE message
               IF HIWORD(wParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

      CASE WM_SIZE
         ' // If the window isn't minimized, resize it
         IF wParam <> SIZE_MINIMIZED THEN
            DIM pWindow AS CWindow PTR = CAST(CWindow PTR, GetWindowLongPtr(hwnd, 0))
            pWindow->MoveWindow GetDlgItem(hwnd, IDC_GRCTX), 0, 0, pWindow->ClientWidth, pWindow->ClientHeight, CTRUE
         END IF

    CASE WM_DESTROY
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

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

' ========================================================================================
' 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 Graphic Control Skeleton", @WndProc)
   pWindow.SetClientSize(600, 350)
   pWindow.Center

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   pGraphCtx.Clear BGR(255, 255, 255)

   ' // Capture the desktop window and display it in the control
   DIM hBitmap AS HBITMAP = AfxCaptureDisplay
   pGraphCtx.SetVirtualBufferSize(AfxGetBitmapWidth(hBitmap), AfxGetBitmapHeight(hBitmap))
   AfxDrawBitmap(pGraphCtx.GetMemDC, 0, 0, hBitmap)
   DeleteObject hBitmap

   

   ' // Process Windows events
   FUNCTION = pWindow.DoEvents(nCmdShow)

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


José Roca

Stretchable graphic control skeleton.


' ########################################################################################
' Microsoft Windows
' File: CW_GraphCtxSkeletonStretchable.fbtpl
' Contents: CWindow Graphic Control Skeleton
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose 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.
' ########################################################################################

#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/CGraphCtx.inc"

USING Afx.CWindowClass
USING Afx.CGraphCtxClass

CONST IDC_GRCTX = 1001

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)

' ========================================================================================
' Window 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_CREATE
         EXIT FUNCTION

      CASE WM_COMMAND
         SELECT CASE LOWORD(wParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application sending an WM_CLOSE message
               IF HIWORD(wParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

      CASE WM_SIZE
         ' // If the window isn't minimized, resize it
         IF wParam <> SIZE_MINIMIZED THEN
            DIM pWindow AS CWindow PTR = CAST(CWindow PTR, GetWindowLongPtr(hwnd, 0))
            pWindow->MoveWindow GetDlgItem(hwnd, IDC_GRCTX), 0, 0, pWindow->ClientWidth, pWindow->ClientHeight, CTRUE
         END IF

    CASE WM_DESTROY
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

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

' ========================================================================================
' 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 Graphic Control Skeleton", @WndProc)
   pWindow.SetClientSize(600, 350)
   pWindow.Center

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   pGraphCtx.Clear BGR(255, 255, 255)

   ' // Set it stretchable
   pGraphCtx.Stretchable = TRUE

   ' // Capture the desktop window and display it in the control
   DIM hBitmap AS HBITMAP = AfxCaptureDisplay
   pGraphCtx.SetVirtualBufferSize(AfxGetBitmapWidth(hBitmap), AfxGetBitmapHeight(hBitmap))
   AfxDrawBitmap(pGraphCtx.GetMemDC, 0, 0, hBitmap)
   DeleteObject hBitmap

   

   ' // Process Windows events
   FUNCTION = pWindow.DoEvents(nCmdShow)

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


José Roca

Graphic control using GDI+ to draw a line in it.


' ########################################################################################
' Microsoft Windows
' File: CW_GraphCtxGdipSkeleton.fbtpl
' Contents: CWindow GDI+ Graphic Control Skeleton
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose 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.
' ########################################################################################

#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxGdiPlus.inc"
#INCLUDE ONCE "Afx/CGraphCtx.inc"

USING Afx.CWindowClass
USING Afx.CGraphCtxClass

CONST IDC_GRCTX = 1001

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)

DECLARE FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' The following sample code draws a line.
' Change it with your own code.
' ========================================================================================
SUB GDIP_Render (BYVAL hDC AS HDC)

   DIM hStatus AS GpStatus
   DIM pGraphics AS GpGraphics PTR
   DIM pPen AS GpPen PTR

   ' // Create a graphics object
   hStatus = GdipCreateFromHDC(hDC, @pGraphics)
   ' // Create a Pen
   hStatus = GdipCreatePen1(GDIP_ARGB(255, 255, 0, 0), AfxScaleX(1), UnitPixel, @pPen)
   ' // Draw the line
   GdipDrawLineI pGraphics, pPen, 0, 0, AfxScaleX(200), AfxScaleY(100)

   ' // Cleanup
   IF pPen THEN GdipDeletePen(pPen)
   IF pGraphics THEN GdipDeleteGraphics(pGraphics)

END SUB
' ========================================================================================

' ========================================================================================
' 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 GDI+ Graphic Control Skeleton", @WndProc)
   pWindow.Brush = CAST(HBRUSH, COLOR_WINDOW + 1)
   pWindow.SetClientSize(400, 250)
   pWindow.Center

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   pGraphCtx.Clear BGR(255, 255, 255)
   ' // Initialize GDI+
   DIM token AS UINT_PTR = AfxGdipInit
   ' // Draw the graphics
   GDIP_Render pGraphCtx.GetMemDC

   

   ' // Process Windows events
   FUNCTION = pWindow.DoEvents(nCmdShow)

   ' // Shutdown GDI+
   IF token THEN GdiPlusShutdown token

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

' ========================================================================================
' Window 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_CREATE
         EXIT FUNCTION

      CASE WM_COMMAND
         SELECT CASE LOWORD(wParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application sending an WM_CLOSE message
               IF HIWORD(wParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

      CASE WM_SIZE
         ' // If the window isn't minimized, resize it
         IF wParam <> SIZE_MINIMIZED THEN
            DIM pWindow AS CWindow PTR = CAST(CWindow PTR, GetWindowLongPtr(hwnd, 0))
            pWindow->MoveWindow GetDlgItem(hwnd, IDC_GRCTX), 0, 0, pWindow->ClientWidth, pWindow->ClientHeight, CTRUE
         END IF

    CASE WM_DESTROY
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

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


José Roca

#4
Graphic control displaying an image loaded from file.


' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_Button.fbtpl
' Contents: CWindow button example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose 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.
' ########################################################################################

#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxGdiplus.inc"
#INCLUDE ONCE "Afx/CGraphCtx.inc"

USING Afx.CWindowClass
USING Afx.CGraphCtxClass

CONST IDC_GRCTX = 1001

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)

' ========================================================================================
' Window 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_CREATE
         EXIT FUNCTION

      CASE WM_COMMAND
         SELECT CASE LOWORD(wParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application sending an WM_CLOSE message
               IF HIWORD(wParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

      CASE WM_SIZE
         ' // If the window isn't minimized, resize it
         IF wParam <> SIZE_MINIMIZED THEN
            DIM pWindow AS CWindow PTR = CAST(CWindow PTR, GetWindowLongPtr(hwnd, 0))
            pWindow->MoveWindow GetDlgItem(hwnd, IDC_GRCTX), 0, 0, pWindow->ClientWidth, pWindow->ClientHeight, CTRUE
         END IF

    CASE WM_DESTROY
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

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

' ========================================================================================
' 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 Graphic Control Skeleton", @WndProc)
   pWindow.SetClientSize(300, 350)
   pWindow.Center

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX)
   pGraphCtx.Clear BGR(255, 255, 255)

   ' // Load the image
   pGraphCtx.LoadImageFromFile ExePath & "\GARDNER_Ava_02.jpg"

   ' // Set the focus in the control
   SetFocus pGraphCtx.hWindow

   ' // Process Windows events
   FUNCTION = pWindow.DoEvents(nCmdShow)

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



José Roca

Quote
The AfxImagexxx functions to convert .png icons have been renamed as AfxIconxxx. I may need AfxImage for other purposes.

I think that this time I have got it right. The names of the functions that can be used for both icons and bitmaps are named AfxGdipImageFromBuffer, AfxGdipImageFromFile and AfxGdipImageFromRes, and I have added simplified functions that call the generic ones with the appropriate parameters: AfxGdipIconFromBuffer / AfxGdipBitmapFromBuffer, AfxGdipIconFromFile / AfxGdipBitmapFromFile, AfxGdipIconFromRes / AfxGdipBitmapFromRes.

In the Graphic Control, I have added the function CreateBitmapFromFile, that uses an alternative method to GdipCreateBitmapFromFile. The reason is that the GDI+ GdipCreateBitmapFromFile, as it also happens with GdipLoadImageFromFile, has a quirk: black and white images are loaded with increased contrast.

José Roca

#6
An added benefit of using my alternate methods instead of the GDI+ GdipLoadFromFile and GdipCreateBitmapFromFile functions, is that they don't lock the file. Therefore, the image can be saved overwriting the loaded image, or delete it. We can also use an image that has been saved as an array of bytes into a database, something not possible with the GDI+ loading functions.

Quote
SYMPTOMS

When either a Bitmap object or an Image object is constructed from a file, the file remains locked for the lifetime of the object. As a result, you cannot change an image and save it back to the same file where it originated.

Additionally, if the stream was destroyed during the life of the Bitmap object, you cannot successfully access an image that was based on a stream. For example, the Graphics.DrawImage() function may not succeed after the stream has been destroyed.

CAUSE

GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object.

To retain access to the source bits, GDI+ locks any source file, and forces the application to maintain the life of any source stream, for the life of the Bitmap or the Image object.

STATUS
This behavior is by design.
MORE INFORMATION
By using the Bitmap class constructor and the Image class constructor, you can create a Bitmap or an Image object that is based on the contents of a file or a stream. Also, a Bitmap object may be created from a block of memory or from a Windows GDI HBITMAP handle.

GDI+ requires access to the data that is contained in the file or stream or memory block during the life of the new Bitmap or the new Image object. This requirement is because GDI+ does not immediately decode the image bits. Instead, GDI+ delays the decoding of the image bits until the first time that the image is accessed. Additionally, GDI+ may sometimes choose to discard its buffer for the image and re-decode later.

To make these types of delayed decoding easier, GDI+ must have access to the raw image data during the life of the object. This can result in some unexpected consequences:

    You cannot overwrite the file.
    When a file name is provided to the Bitmap or to the Image class constructor, GDI+ opens the file and maintains a lock on the file during the life of the Bitmap or the Image object. As a result, you cannot write to the file before that Bitmap or the Image object is destroyed. This prevents the Image and the Bitmap classes from being able to save to the same file that the object was created from.
    You must not destroy the stream.
    When a stream is provided to the Bitmap or the Image class constructor, GDI+ may defer reading from the stream until later. This means that you must not destroy the stream until after you destroy the Bitmap or the Image object. If you try to use the Bitmap or the Image object after you destroy the stream, you may receive an error message. It is the responsibility of the application to make sure that the stream can be used during the life of the new image object.
    You must retain the memory.
    When a Bitmap object is created with a pointer to image bits, the new object may or may not use the memory at that address as backing for the image. Therefore, the application must make sure that the memory can be used during the life of the Bitmap object.
    You may destroy the HBITMAP.
    A Bitmap object that is based on an HBITMAP (DIBSection or device dependent bitmap) just makes a copy of the image from the HBITMAP. Therefore, you may destroy the HBITMAP immediately after creating the new Bitmap object.

For additional information about using GDI and GDI+ together, click the following article number to view the article in the Microsoft Knowledge Base:
311221 INFO: Interoperability between GDI and GDI+

https://support.microsoft.com/en-us/kb/814675

As the purpose of my wrapper functions is to create icons and bitmaps from .png, etc., files, and then use these icons or bitmaps with GDI or other non GDI+ technologies, there is not need too keep the file locked. A different matter is an image control that uses GDI+ to load and draw the images, such Paul's FireImage control or my own image control.

Not being able to delete or overwrite an image loaded with GDI+ has always been a nuisance.

José Roca

A planned improvement to the graphic control will be to add support for OpenGL. All I need is to set the pixel format and create a context.

PowerBASIC code to translate:


' ========================================================================================
' Set the pixel format
' ========================================================================================
FUNCTION GlCtx_SetPixelFormat (BYVAL hdc AS DWORD, BYVAL nBitsPerPel AS LONG) AS LONG

   LOCAL pf AS LONG
   LOCAL pfd AS PIXELFORMATDESCRIPTOR
   LOCAL cDepthBits AS LONG

   cDepthBits = nBitsPerPel - 8
   IF cDepthBits < 16 THEN cDepthBits = 16

   ' // Fill the PIXELFORMATDESCRIPTOR structure
   pfd.nSize           = SIZEOF(PIXELFORMATDESCRIPTOR)   ' Size of the structure
   pfd.nVersion        = 1                               ' Version number
   pfd.dwFlags         = %PFD_SUPPORT_OPENGL _           ' The buffer supports OpenGL drawing
                         OR %PFD_SUPPORT_GDI             ' The buffer supports GDI drawing.
   pfd.iPixelType      = %PFD_TYPE_RGBA                  ' Request an RGBA format
   pfd.cColorBits      = nBitsPerPel                     ' Number of color bitplanes in each color buffer
   pfd.cRedBits        = 0                               ' Number of red bitplanes in each RGBA color buffer.
   pfd.cRedShift       = 0                               ' Shift count for red bitplanes in each RGBA color buffer.
   pfd.cGreenBits      = 0                               ' Number of green bitplanes in each RGBA color buffer.
   pfd.cGreenShift     = 0                               ' Shift count for green bitplanes in each RGBA color buffer.
   pfd.cBlueBits       = 0                               ' Number of blue bitplanes in each RGBA color buffer.
   pfd.cBlueShift      = 0                               ' Shift count for blue bitplanes in each RGBA color buffer.
   pfd.cAlphaBits      = 0                               ' Number of alpha bitplanes in each RGBA color buffer
   pfd.cAlphaShift     = 0                               ' Shift count for alpha bitplanes in each RGBA color buffer.
   pfd.cAccumBits      = 64                              ' Total number of bitplanes in the accumulation buffer.
   pfd.cAccumRedBits   = 0                               ' Number of red bitplanes in the accumulation buffer.
   pfd.cAccumGreenBits = 0                               ' Number of gree bitplanes in the accumulation buffer.
   pfd.cAccumBlueBits  = 0                               ' Number of blue bitplanes in the accumulation buffer.
   pfd.cAccumAlphaBits = 0                               ' Number of alpha bitplanes in the accumulation buffer.
   pfd.cDepthBits      = cDepthBits                      ' Depth of the depth (z-axis) buffer.
   pfd.cStencilBits    = 0                               ' Depth of the stencil buffer.
   pfd.cAuxBuffers     = 0                               ' Number of auxiliary buffers.
   pfd.iLayerType      = 0                               ' Ignored. Earlier implementations of OpenGL used this member, but it is no longer used.
   pfd.bReserved       = 0                               ' Number of overlay and underlay planes.
   pfd.dwLayerMask     = 0                               ' Ignored. Earlier implementations of OpenGL used this member, but it is no longer used.
   pfd.dwVisibleMask   = 0                               ' Transparent color or index of an underlay plane.
   pfd.dwDamageMask    = 0                               ' Ignored. Earlier implementations of OpenGL used this member, but it is no longer used.

   ' // Find a matching pixel format
   pf = ChoosePixelFormat(hDC, pfd)
   IF ISFALSE pf THEN
'      OutputDebugString "Can't find a suitable pixel format"
      MessageBox(0, "Can't find a suitable pixel format", "GlCtx_SetPixelFormat", _
                 %MB_OK OR %MB_ICONINFORMATION OR %MB_APPLMODAL)
      EXIT FUNCTION
   END IF

   ' // Set the pixel format
   IF ISFALSE SetPixelFormat(hDC, pf, pfd) THEN
'      OutputDebugString "Can't set the pixel format"
      MessageBox(0, "Can't set the pixel format", "GlCtx_SetPixelFormat", _
                 %MB_OK OR %MB_ICONINFORMATION OR %MB_APPLMODAL)
      EXIT FUNCTION
   END IF
   FUNCTION = %TRUE

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

' ========================================================================================
' Create context
' ========================================================================================
FUNCTION GlCtx_CreateContext (BYVAL hdc AS DWORD) AS DWORD

   LOCAL hRC AS DWORD

   ' // Create a new OpenGL rendering context
   hRC = wglCreateContext(hdc)
   IF ISFALSE hRC THEN
'      OutputDebugString "Can't create an OpenGL rendering context"
      MessageBox(0, "Can't create an OpenGL rendering context", "GlCtx_CreateContext", _
                 %MB_OK OR %MB_ICONINFORMATION OR %MB_APPLMODAL)
      EXIT FUNCTION
   END IF

   ' // Make it current
   IF ISFALSE wglMakeCurrent(hdc, hRC) THEN
'      OutputDebugString "Can't activate the OpenGL rendering context"
      MessageBox(0, "Can't activate the OpenGL rendering context", "GlCtx_CreateContext", _
                 %MB_OK OR %MB_ICONINFORMATION OR %MB_APPLMODAL)
   END IF

   FUNCTION = hRC

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


and a method to make sure that OpenGL calls are directed to the correct render context if we are using more than one instance of the graphic control.


wglMakeCurrent(hMemDc, m_hRC)


In the graphic control I will add code like this:


SetPixelFormat(m_hMemDc, m_BitsPerPel)
m_hRC = CreateContext(m_hMemDc)


after creating the memory compatible bitmap, both when I create it and when I set the size of the virtual buffer.

José Roca

#8
The Graphic Control uses DIB sections and double buffering (to avoid flicker). It provides functions to get the bitmap handle, the memory device context and the DIB bits, and also supports he WM_PRINTCLIENT message. With these handles, you can write functions for printing, manipulate bits for effects, copying, saving, etc. I will eventually write some, but you aren't limited to what I will write.

Contrarily to the PowerBASIC graphic control, that uses a 24-bit color depth, it checks at runtime the color depth of the monitor (32 bit with the monitor that I'm using) and uses it when creating the DIB section.

The virtual buffer can be bigger than the control, which provides automatic scrollbars, mousewheel and keyboard navigation. It also forwards WM_COMMAND and WM_NOTIFY messages to the parent and sends click and focus messages to the parent in the form of notification messages.


For drawing operations you can use GDI, GDI+ and probably OpenGL after I made some changes.

José Roca

Removed the tooltip class from CWindow.inc and added the following functions to AfxWin.inc.


' ########################################################################################
'                                   *** TOOLTIPS ***
' ########################################################################################

' ========================================================================================
' Creates a tooltip for a control.
' Parameters:
' - hwndCtrl  = Handle of the control
' - wszText   = Tooltip text
' - bBalloon  = Ballon tip (TRUE or FALSE)
' - bCentered = Centered (TRUE or FALSE)
' Return Value:
'   The handle of the tooltip control
' ========================================================================================
PRIVATE FUNCTION AfxAddTooltip (BYVAL hwndCtrl AS HWND, BYREF wszText AS CONST WSTRING = "", BYVAL bBalloon AS BOOLEAN = FALSE, BYVAL bCentered AS BOOLEAN = FALSE) AS HWND
   IF IsWindow(hwndCtrl) = 0 THEN EXIT FUNCTION
   ' // Creates the tooltip control
   DIM dwStyle AS DWORD = WS_POPUP OR TTS_ALWAYSTIP
   IF bBalloon THEN dwStyle = dwStyle OR TTS_BALLOON
   DIM hTooltip AS HWND
   hTooltip = CreateWindowExW(0, "tooltips_class32", "", dwStyle, 0, 0, 0, 0, NULL, NULL, NULL, NULL)
   IF hTooltip = NULL THEN EXIT FUNCTION
   ' // Registers the window with the tooltip control
   ' // 32-bit: The size of the TOOLINFO structure is 48 bytes in
   '    version 6 of comctl32.dll, and 44 bytes in lower versions.
   ' // 64-bit: The size of the TOOLINFO structure is 72 bytes in
   '    version 6 of comctl32.dll, and 64 bytes in lower versions.
   DIM tti AS TOOLINFOW
#ifdef __FB_64BIT__
   IF AfxComCtlVersion < 600 THEN tti.cbSize = 64 ELSE tti.cbSize = 72
#else
   IF AfxComCtlVersion < 600 THEN tti.cbSize = 44 ELSE tti.cbSize = 48
#endif
   tti.uFlags = TTF_IDISHWND OR TTF_SUBCLASS
   IF bCentered THEN tti.uFlags = tti.uFlags OR TTF_CENTERTIP
   tti.hwnd = GetParent(hwndCtrl)
   tti.uId = CAST(UINT_PTR, hwndCtrl)
   tti.hinst = GetModuleHandleW(NULL)
   ' // The length of the string must not exceed of 80 characters, including the terminating null
   DIM wszTooltipText AS WSTRING * 80
   wszTooltipText = wszText
   tti.lpszText = @wszTooltipText
   SendMessageW hTooltip, TTM_ADDTOOLW, 0, CAST(LPARAM, @tti)
   FUNCTION = hTooltip
END FUNCTION
' ========================================================================================

' ========================================================================================
' Sets/replaces the text of a tooltip control
' Parameters:
' - hTooltip = Handle of the tooltip control
' - hwndCtrl = Handle of the control
' - wszText  = Tooltip text
' ========================================================================================
PRIVATE SUB AfxSetTooltipText (BYVAL hTooltip AS HWND, BYVAL hwndCtrl AS HWND, BYREF wszText AS CONST WSTRING)
   IF hTooltip = NULL OR hwndCtrl = NULL THEN EXIT SUB
   ' // The length of the text must not exceed of 80 characters, including the terminating null.
   DIM wszTooltipText AS WSTRING * 80
   wszTooltipText = wszText
   ' // 32-bit: The size of the TOOLINFO structure is 48 bytes in
   '    version 6 of comctl32.dll, and 44 bytes in lower versions.
   ' // 64-bit: The size of the TOOLINFO structure is 72 bytes in
   '    version 6 of comctl32.dll, and 64 bytes in lower versions.
   DIM tti AS TOOLINFOW
#ifdef __FB_64BIT__
   IF AfxComCtlVersion < 600 THEN tti.cbSize = 64 ELSE tti.cbSize = 72
#else
   IF AfxComCtlVersion < 600 THEN tti.cbSize = 44 ELSE tti.cbSize = 48
#endif
   tti.hwnd = GetParent(hwndCtrl)
   tti.uId = CAST(UINT_PTR, hwndCtrl)
   ' // Retrieve the tooltip information
   SendMessageW(hTooltip, TTM_GETTOOLINFOW, 0, CAST(LPARAM, @tti))
   IF SendMessageW(hTooltip, TTM_GETTOOLINFOW, 0, CAST(LPARAM, @tti)) THEN
      tti.lpszText = @wszTooltipText
      SendMessageW(hTooltip, TTM_SETTOOLINFOW, 0, CAST(LPARAM, @tti))
   END IF
   ' // Note: Windows provides the TTM_UPDATETIPTEXT message, but needs Vista or superior.
END SUB
' ========================================================================================

' ========================================================================================
' Removes a tool from a tooltip control.
' Parameters:
' - hTooltip = Handle of the tooltip control
' - hwndCtrl = Handle of the control
' ========================================================================================
PRIVATE SUB AfxDeleteTooltip (BYVAL hTooltip AS HWND, BYVAL hwndCtrl AS HWND)
   IF hTooltip = NULL OR hwndCtrl = NULL THEN EXIT SUB
   ' // 32-bit: The size of the TOOLINFO structure is 48 bytes in
   '    version 6 of comctl32.dll, and 44 bytes in lower versions.
   ' // 64-bit: The size of the TOOLINFO structure is 72 bytes in
   '    version 6 of comctl32.dll, and 64 bytes in lower versions.
   DIM tti AS TOOLINFOW
#ifdef __FB_64BIT__
   IF AfxComCtlVersion < 600 THEN tti.cbSize = 64 ELSE tti.cbSize = 72
#else
   IF AfxComCtlVersion < 600 THEN tti.cbSize = 44 ELSE tti.cbSize = 48
#endif
   tti.hwnd = GetParent(hwndCtrl)
   tti.uId = CAST(UINT_PTR, hwndCtrl)
   ' // Remove the tooltip
   SendMessageW(hTooltip, TTM_DELTOOL, 0, CAST(LPARAM, @tti))
END SUB
' ========================================================================================