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.
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
' ========================================================================================
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
' ========================================================================================
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
' ========================================================================================
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
' ========================================================================================
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.
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.
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.
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.
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
' ========================================================================================