Thread View: comp.os.ms-windows.announce
1 messages
1 total messages
Started by Tom Haapanen
Wed, 27 Jan 1993 16:59
Windows Programmer FAQ: ASCII format, 02/04
Author: Tom Haapanen
Date: Wed, 27 Jan 1993 16:59
Date: Wed, 27 Jan 1993 16:59
1013 lines
41460 bytes
41460 bytes
The following is part 02 of 04 of the ASCII text version of the
Microsoft Windows Programmer FAQ. While some of you may find this the best
format, the more popular format is the WinHelp file. These are posted
simultaneously with the ASCII version to comp.binaries.ms-windows.
You can obtain a copy of the WinHelp version as "faqprg01.zip" by ftp
from sonygate.sony.com [192.63.138.2], in /pub/comp.binaries.ms-windows.
It will also be placed on ftp.cica.indiana.edu once disk space permits.
BEGIN--------------cut here--------cut here------------cut here----------
from the system menu, if there is one.
Also, you can alternately disable resizing by creating the windows with
~WS_THICKFRAME, and disabling the Size... item on the system menu.
...........................................................................
5.1.10. Getting the handle of the active window
------------------------------------------------
To get the active window's handle, just call
hActiveWindow = GetFocus();
...........................................................................
5.1.11. Keeeping a window on top
---------------------------------
To keep a window on top of all other windows without requiring it to be the
active window (akin to the behaviour exhibited by the Windows 3.1 clock),
you can make the following function call:
SetWindowPos(hWnd, (HWND)-1, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
Note that it is not considered polite behavior by an application to always
keep its window topmost, unless you at least give the user the option of
disabling this "feature".
...........................................................................
5.1.12. Limiting window size
-----------------------------
To limit window size, you'll need to process the WM_GETMINMAXINFO message,
and fill in the structure describing the minimum and maximum window sizes.
...........................................................................
5.1.13. Right-justifying menu items
------------------------------------
To right-justify an entire menu item or just a part of it, place a \a in
the string just before the right-justified part.
Incidentally, the Windows 3.0 CUA guidelines no longer call for right-
justifying the Help menu on the menu bar.
...........................................................................
5.1.14. Right-justifying menu items at runtime
-----------------------------------------------
It's undocumented, but what you need is a 0x08 in the menu string. The
easiest way to do this is to place a \b in the string before the right-
justified part (either the text of the accelerator key).
Incidentally, the Windows 3.0 CUA guidelines no longer call for right
justifying the Help menu on the menu bar.
...........................................................................
5.1.15. Trapping mouse clicks on desktop
-----------------------------------------
To trap mouseclicks on the desktop, you will need to subclass the desktop
window. The following code fragment illustrates the technique (sample code
courtesy of Blake Coverett, blakeco@microsoft.com ):
To subclass the desktop:
hWndDesktop=GetDesktopWindow();
hSaveTask=GetCurrentTask();
lpfnDesktop=(FARPROC)GetWindowLong(hWndDesktop, GWL_WNDPROC);
lpfnSubClassProc=MakeProcInstance((FARPROC)WndProc, hInst);
SetWindowLong(hWndDesktop, GWL_WNDPROC,
(LPARAM)(LONG)lpfnSubClassProc);
and then to undo it when ready to unload:
SetWindowLong(hWnd,GWL_WNDPROC, (LPARAM)(LONG)lpfnDesktop);
PostAppMessage(hSaveTask,WM_QUIT,0,0);
...........................................................................
5.1.16. Using status bars with MDI
-----------------------------------
Add the following code fragments to the indicated places in the WinProc()
of an application, or the FrameWinProc() of a MDI application.
case WM_CREATE:
hWndClient = CreateWindow( "MDIClient",...,
WS_CHILD|WS_VISIBLE|WS_CLIBSIBLINGS|
WS_HSCROLL|WS_VSCROLL,... );
hWndStatus = CreateWindow( "Static",...,
WS_CHILD|WS_VISIBLE|SS_LEFT|SS_NOPREFIX,... );
...
case WM_SIZE:
GetClientRect( hWnd,&rect );
// Calculate DIVIDING_LINE such that.
// rect.top < DIVIDING_LINE < rect.bottom
// One choice:
// DIVIDING_LINE = rect.bottom - GetSystemMetrics(SM_CYMENU);
MoveWindow( hWndClient,rect.left,rect.top,
rect.right,DIVIDING_LINE,TRUE );
MoveWindow( hWndStatus,rect.left,DIVIDING_LINE,
rect.right,rect.bottom,TRUE );
break; // Do *not* pass to DefFrameProc() of MDI app!!!
...
// To change the status text:
SendMessage( hWndStatus,WM_SETTEXT,0,(LONG)(LPSTR)szStatusText );
Notes:
* For non-MDI applications, all references to hWndClient above will simply
be removed.
* Menu selection can be tracked by setting the status text at a response to
the WM_MENUSELECT message.
* To draw a line between status bar and the rest of the client area, the
DIVIDING_LINE should be adjusted in either MoveWindow() call to leave a
gap between, which is called InvalidateRect() for, and actually being
painted in response to the WM_PAINT.
* The parent window should have WS_CLIPCHILDREN style bit set.
---------------------------------------------------------------------------
5.2. Dialogs
=============
...........................................................................
5.2.1. A dialog as an MDI child window [Borland OWL]
-----------------------------------------------------
If you want to use a dialog as an MDI child window, you can use the
following code as the basis of your own implementation. Thanks to Martin
Calsyn (martin@iastate.edu) for the sample code.
What follows are three files:
TMDIDLOG.H The interface for modeless MDI-child dialogs.
TMDIDLOG.CPP The implementation of same
EXAMPLE.CPP An (partial) example of using the TMDIDLOG
classes.
You need to sub-class TMDIDialogWindow in order to give your MDI child a
unique icon, etc (not shown in the example) and to implement the
CreateChildDialog abstract method. And, of course, you need to sub-class
TMDIDialog in order to establish a transfer structure, interface objects
and all the things you usually do with sub-classes of TDialog.
The classes included below work by presenting the dialog as a child of a
TBWindow which is itself a child of the MDI. These classes are based on
tips and suggestions offered by the Borland advisor line.
Martin claims that the transfer of this technique to MS MFC is trivial.
---------------------------- TMDIDLOG.H -------------------------
#ifndef __tmdidlog_h__
#define __tmdidlog_h__
#include <owl.h>
#include <bwindow.h>
_CLASSDEF(TMDIDialog)
_CLASSDEF(TMDIDialogWindow)
class TMDIDialog : public TDialog
{
public:
TMDIDialog(PTWindowsObject AParent,LPSTR AName,PTModule AModule=NULL) :
TDialog(AParent, AName, AModule) {};
TMDIDialog(PTWindowsObject AParent,int ResourceId,PTModule
AModule=NULL):
TDialog( AParent, ResourceId, AModule ) {};
virtual void WMSetFocus( RTMessage Msg ) = [WM_FIRST + WM_SETFOCUS];
virtual void CloseWindow( int ret );
virtual void CloseWindow();
virtual void Cancel( RTMessage msg ) = [ID_FIRST + IDCANCEL];
friend class TMDIDialogWindow;
protected:
TMDIDialogWindow *mdiw;
};
class TMDIDialogWindow : public TBWindow
{
public:
TMDIDialogWindow(PTWindowsObject aParent,LPSTR aName,
PTModule aModule = NULL);
virtual void WMMDIActivate( RTMessage Msg ) = [WM_FIRST +
WM_MDIACTIVATE];
virtual void WMSetFocus( RTMessage Msg ) = [WM_FIRST + WM_SETFOCUS];
virtual void WMSize( RTMessage Msg ) = [WM_FIRST + WM_SIZE];
virtual void SetupWindow();
virtual LPSTR GetClassName() { return "_TMDIDialogWindow"; };
virtual void GetWindowClass( WNDCLASS& aWndClass );
virtual void WMGetMinMaxInfo( RTMessage msg ) = [WM_FIRST +
WM_GETMINMAXINFO];
virtual TMDIDialog *CreateChildDialog() = 0;
private:
PTMDIDialog dlog;
};
#endif
------------------------- TMDIDLOG.CPP --------------------------
#include "tmdidlog.h"
TMDIDialogWindow::TMDIDialogWindow(PTWindowsObject aParent,
LPSTR aName,
PTModule aModule) :
TBWindow(aParent,aName,aModule)
{
dlog = NULL;
GetApplication()->SetKBHandler( this );
SetFlags( WB_KBHANDLER, FALSE );
}
void TMDIDialogWindow::WMMDIActivate( RTMessage Msg )
{
TWindow::WMMDIActivate( Msg );
if ( Msg.WParam == TRUE ) {
GetApplication()->SetKBHandler(this);
if (dlog!=NULL)
SetFocus(((PTDialog)dlog)->HWindow );
}
}
void TMDIDialogWindow::GetWindowClass( WNDCLASS& aWndClass )
{
TWindow::GetWindowClass(aWndClass);
}
void TMDIDialogWindow::WMSetFocus(RTMessage Msg)
{
PostMessage(HWindow, WM_MDIACTIVATE, TRUE, 0L );
DefWndProc(Msg);
}
void TMDIDialogWindow::SetupWindow()
{
TWindow::SetupWindow();
dlog = CreateChildDialog();
if (dlog) {
dlog->mdiw = this;
RECT r;
GetApplication()->MakeWindow(dlog);
SetFocus(dlog->HWindow);
GetWindowRect(HWindow,&r);
Attr.X = r.left;
Attr.Y = r.top;
GetWindowRect(dlog->HWindow,&r);
AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
Attr.W = r.right - r.left;
Attr.H = r.bottom - r.top;
MoveWindow(HWindow,Attr.X, Attr.Y, Attr.W, Attr.H, 1);
}
}
void TMDIDialogWindow::WMGetMinMaxInfo( RTMessage msg )
{
if (dlog) {
MINMAXINFO *lpmmi = (MINMAXINFO *)msg.LParam;
RECT r;
POINT pt1,pt2;
GetWindowRect(HWindow,&r);
pt1.x = r.left;
pt1.y = r.top;
GetWindowRect(dlog->HWindow,&r);
AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
pt2.x = r.right - r.left;
pt2.y = r.bottom - r.top;
lpmmi->ptMaxSize = pt2;
lpmmi->ptMaxPosition = pt1;
lpmmi->ptMinTrackSize = pt2;
lpmmi->ptMaxTrackSize = pt2;
}
}
void TMDIDialogWindow::WMSize( RTMessage Msg )
{
RECT r;
TWindow::WMSize( Msg );
if (!IsIconic(HWindow)) {
GetWindowRect(dlog->HWindow,&r);
AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
Attr.W = r.right - r.left;
Attr.H = r.bottom - r.top;
MoveWindow(HWindow, Attr.X, Attr.Y, Attr.W, Attr.H, 1);
}
}
//
// TMDIDialog methods...
//
void TMDIDialog::WMSetFocus( RTMessage Msg )
{
GetApplication()->SetKBHandler( this );
DefWndProc( Msg );
}
void TMDIDialog::CloseWindow(int ret)
{
TDialog::CloseWindow(ret);
mdiw->CloseWindow();
}
void TMDIDialog::CloseWindow()
{
TDialog::CloseWindow();
mdiw->CloseWindow();
}
void TMDIDialog::Cancel(RTMessage msg)
{
CloseWindow(IDCANCEL);
}
-------------------------- EXAMPLE.CPP --------------------------
//
// #include owl, tmdidlog.h and others as needed
//
//
// ChildDialog is a dialog box defined in the RC file under
// the name 'MY_DLOG'. The definition of MY_DLOG is pretty plain
// except that it should have the following attributes:
// STYLE WS_CHILD | WS_VISIBLE
// and optionally CLASS "BORDLG"
// That is, it should have no caption, sys menu, or border of any
// type. It should also have an origin of 0,0 otherwise it will
// be offset within the MDI child window.
//
// An instance of ClientDialog can be created as a modeless MDI
// client by doing : `new ChildDialogWindow(this, "Title", NULL)'
// from your MDI frame object or other appropriate place.
//
class ChildDialog : public TMDIDialog
{
public:
ChildDialog(PTWindowsObject aParent,
LPSTR aName, PTModule aModule = NULL);
};
ChildDialogWindow::ChildDialogWindow(PTWindowsObject aParent,LPSTR aName,
PTModule aModule)
: TMDIDialogWindow(aParent,aName,aModule)
{
obj_id = object_id;
}
TMDIDialog* ChildDialogWindow::CreateChildDialog()
{
return (TMDIDialog*)new ChildDialog(this,(LPSTR)"MY_DLOG",NULL);
}
ChildDialog::ChildDialog(PTWindowsObject aParent,
LPSTR aName, PTModule aModule)
: TMDIDialog(aParent,aName,aModule)
{
// The usual stuff: Initialize the transfer structure, create
// interface objects, etc.
}
...........................................................................
5.2.2. Adding controls to a non-dialog window
----------------------------------------------
You can do this by simply calling CreateWindow() with one of the predefined
child window control class names (see the control class definition table in
the SDK reference manual).
...........................................................................
5.2.3. Doing a timeout in a dialog
-----------------------------------
Start a timer in your WM_INITDIALOG processing. If your dialog box receives
the WM_TIMER message, kill the timer and post yourself a WM_COMMAND messgae
with wParam == IDOK. If the user presses any button, restart the timer.
...........................................................................
5.2.4. Minimize button on modal dialog moves when clicked
----------------------------------------------------------
It's a bug in Windows 3.1. To duplicate this, create a modal dialog with
the styles CAPTION, MODAL FRAME, MINIMIZE-BOX, activate the dialog, press
the Minimize button -- and watch it move to the top right corner, on top of
the modal frame!
The workaround: don't use a Minimize box on a modal dialog.
...........................................................................
5.2.5. Modifying common dialogs
--------------------------------
If you want to modify a common dialog, do not delete controls from the
templates, as the common dialog procedures expect these controls to exist.
Instead, make them invisible or move them outside the boundaries of the
dialog gox to prevent the user from seeing or accessing them.
...........................................................................
5.2.6. Null dialog handles from Borland custom dialogs
-------------------------------------------------------
If you keep getting null dialog handles with Borland C++ unless I have
Turbo C++ running, your dialog is probably of the BorDlg class, which
requires code in BWCC.DLL. However, you have probably not done anything to
force BWCC.DLL to be loaded with your application, so the dialog manager
cannot find the necessary routines to draw the dialog. The easiest way to
force BWCC.DLL to be loaded is to call BWCCGetVersion() at the very
beginning of your application, and to link in BWCC.LIB.
Alternatly, you can do a
WinExec("loadbwcc.exe");
when you start up your application, as long as loadbwcc.exe is available.
...........................................................................
5.2.7. Preventing switching away from a modal dialog
-----------------------------------------------------
The design of the Windows API means that if there are two dialogs active
simultaneously, the user can switch between the two, even one of them is
modal. To prevent this, you should use EnableWindow() to explicitly diable
any modeless dialogs when your modal dialog starts up.
...........................................................................
5.2.8. Using a dialog as your main window
------------------------------------------
First, you have to create a dialog box first. Include a class name(you
name
it). Then, in your WinMain function, register a class using that class
name
and add the constant DLGWINDOWEXTRA to the window extra byte component.
This
constant is defined in Windows.h. Then, use CreateDialog() to display the
dialog, make sure the last 2 parameters are set to NULL. In your WndProc
function, instead of calling DefWindowProc, call DefDlgProc. Then, you are
basically all set. Make sure in your dialog definition you create your
dialog
box initially visible. For further information, see your docs.
...........................................................................
5.2.9. Using Borland custom dialogs with other compilers
---------------------------------------------------------
You can't integrate bwcc.dll with the Dialog Editor but you can manually
modify the dialog file and use appropriate BWCC control classes and styles.
Include bwcc.h in your header file and then add bwcc.lib to your link
options (before libw.lib). Also make sure bwcc.dll can be found in either
the Windows directory or the current directory when the app starts or in
the path.
Examples (thanks to Sam Espartero, sqe@hpcc01.corp.hp.com):
Link Options:
/align:16 /NOD PLAYCD USERCODE MCICDA SUPERCLS,PLAYCD.EXE,,
LIBW MLIBCEW bwcc mmsystem, PLAYCD.DEF
Dialog File:
ABOUT DIALOG 4, 5, 199, 137
STYLE WS_POPUPWINDOW | DS_MODALFRAME | WS_VISIBLE |
WS_CLIPSIBLINGS | WS_DLGFRAME
CAPTION "Sample BWCC Dialog"
CLASS "BorDlg"
BEGIN
CONTROL "", 100, "Static", SS_ICON | WS_CHILD | WS_VISIBLE,
5, 16, 16, 16
CONTROL "", -1, "BorShade", WS_CHILD | WS_BORDER,
33,6,161,126
...
CONTROL "", IDOK, "BorBtn", BS_DEFPUSHBUTTON | WS_TABSTOP |
WS_CHILD, 154, 9, 32, 20
END
Message box:
#ifdef BWCC
BWCCMessageBox(GetActiveWindow(), "Unknown MCI Error!",
"MCI Error", MB_OK | MB_ICONHAND);
#else
MessageBox(GetActiveWindow(), "Unknown MCI Error!",
"MCI Error", MB_OK | MB_ICONHAND);
#endif
---------------------------------------------------------------------------
5.3. Controls
==============
...........................................................................
5.3.1. Allowing ENTER in a multiline edit control
--------------------------------------------------
To allow the use of the Enter key, there is no need to subclass the edit
control. An easier way in Windows 3.1 and later (which also works better!)
is to specify ES_WANTRETURN as part of the style for the edit control (see
the Windows 3.1 SDK documentation for details).
...........................................................................
5.3.2. Aligning multi-column listboxes
---------------------------------------
In the resource file make sure the list box has the LBS_USETABSTOPS style.
When you add the items to the listbox, separate the fields with tabs. You
can either use the default tab stops, or set your own by sending the
LBS_SETTABSTOPS message to the listbox. For more information, see the SDK
Reference, volumes 1 and 2.
It is also possible to use a fixed font, but the tabstop solution usually
ends up looking much better.
...........................................................................
5.3.3. Changing button colors
------------------------------
In Windows 3.0, the button face is defined by two colors. The grey (white
with EGA) face and a dark grey (grey if ega) shadow. The colors also
change when the button goes from a normal to pushed in state. The
WM_CTLCOLOR message only allows you to change one color at a time so to
which of the button face colors should this apply? (Windows 2.x button
faces had only one color so it made sense.)
Maybe something tricky could have been done by using the background color
for the shadow and foreground color for the face and perhaps doing
something strange to get the text color in another way... And how do you
return 2 brushes (you now need a foreground and a background brush)? Or
maybe even better, make colors a property of the window and some windows
could have multiple color properties...
Anyway, Windows doesn't look at the WM_CTLCOLOR message for buttons and
thus doesn't allow you to change the button colors. Try it with a listbox
instead... The only way to change button colors is to specify
ButtonColor=, ButtonShadow= and ButtonText= in the [Colors] section of your
win.ini file.
In Windows 3.1, the button text, shadow and face colors can also be defined
using the Control Panel.
...........................................................................
5.3.4. Creating controls at runtime in Visual Basic
----------------------------------------------------
To create controls dynamically, you need to create a control array at
design time. You can then extend it at runtime, and specify new properties
for the new controls you have created.
...........................................................................
5.3.5. Combo boxes with tab stops
----------------------------------
Windows does not support combo boxes with tab stops. If you need such
items, you have three choices:
* Use an owner-draw combo box. This will allow you to draw the items at
the precise locations you need. Text-only owner-draw controls are not
difficult to implement.
* Use GetTextExtent() to determine the length of each part, padding with
blanks until it approximately lines up. Fairly easy, but not fast and
not precise.
* Change the font of the control to either Courier or the fixed System
font. While this is simple to do, your combo box font will not be
consistent with the rest of your dialog box controls.
...........................................................................
5.3.6. Custom button bitmaps in Borland dialogs
------------------------------------------------
To create owner-drawn buttons for new additional buttons beyond those
supplied in bwcc.dll, you first need to create the button bitmaps. Create
them 63 pixels wide by 39 deep, and number them "logically".
The bitmap numbers are related to the resource id of the button that you
want to use them. Use the following numbers:
button id + 1000 Unpressed bitmap
button id + 3000 Pressed bitmap
button id + 2000 Unpressed bitmap, EGA
button id + 4000 Pressed bitmap, EGA
If you use these bitmap ids, bwcc.dll will use the bitmaps on the buttons
automatically.
...........................................................................
5.3.7. Drawing on a dialog background
--------------------------------------
To draw on a dialog background, you can either subclass the dialog, or,
more simply, respond to the WM_PAINT message in your dialog procedure,
calling ValidateRect() after you have redrawn the background.
...........................................................................
5.3.8. Hiding dialog controls
------------------------------
EnableWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE), FALSE);
ShowWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE), SW_HIDE);
UpdateWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE));
...........................................................................
5.3.9. Listboxes with large amounts of data
--------------------------------------------
A standard listbox is limited to 32K of data. If you need a listbox with a
larger number of items, you can use one of the following options:
* Use an owner-draw listbox with LBS_HASSTRINGS. This will allow 4096
items.
* Use an owner-draw listbox LBS_OWNERDRAWFIXED but without
LBS_HASSTRINGS. This is somewhat more work, but will allow 8192
items.
Some third-party libraries also implement listbox classes which can handle
huge amounts of data.
Finally, Microsoft has a virtual listbox implementation available by ftp at
ftp.uu.net, in the directory /vendor/microsoft/developer-network.
...........................................................................
5.3.10. Subclassing standard controls
--------------------------------------
You can subclass standard controls by having your own window procedure
handle the messages for the windows you create (using SetWindowLong()).
The only caveat here is for useability: make sure that your subclassed
controls don't behave in an unexpected manner.
What is definitely a bad idea is modify the class procedure of a standard
control (using SetClassLong()) and changing the window procedure for all
such windows, as this will affect all edit controls in all applications
currently running in the Windows session.
...........................................................................
5.3.11. Using a window as a modal dialog
-----------------------------------------
To use a window as a modal dialog, you will need to implement your own
message loop for that window, complete with PeekMessage(),
TranslateMessage() and DispatchMessage() calls. This will allow you to
accept events only for the current window, discarding all other events.
---------------------------------------------------------------------------
5.4. Memory
============
...........................................................................
5.4.1. Using new() in C++
--------------------------
In Borland C++ 2.0, and in 3.x's medium model, new() ends up calling
LocalAlloc(), allocating memory from your near 64K segment. In BCC 3.x's
large and compact models (and in Microsoft C/C++ 7.0), however, it will
make one GlobalAlloc() and do subsegment allocations to allow you access to
the full memory without making excessive demands on the system limit of
4096 (8192 in 386 enhanced mode) global memory handles.
...........................................................................
5.4.2. Global memory owned by DLL
----------------------------------
If you use GlobalAlloc in a DLL, the application that called the DLL will
own the object. There is a way around this, though: allocate the memory
using the GMEM_DDESHARE flag; this will make the allocating code segment
(rather than the current task) own the memory.
...........................................................................
5.4.3. Determining size of physical memory
-------------------------------------------
You need to make a DPMI call to obtain that piece of information. DPMI
call 0500h with ES:DI pointing to a 30h byte buffer returns the "Free
Memory Information":
Offset Description
00h Largest available free block in bytes
04h Maximum unlocked page allocation
08h Maximum locked page allocation
0Ch Linear address space size in pages
10h Total number of unlocked pages
14h Number of free pages
18h Total number of physical pages
1Ch Free linear address space in pages
20h Size of paging file/partition in pages
24h-2Fh Reserved
The size of one page in bytes can be determined by function 0604h, which
returns the page size in bytes in BX:CX. To call a DPMI function, invoke
the interrupt 31h. Carry bit will be clear if call was successful.
The complete DPMI 0.9 specification is available free from Intel Literature
JP26, Santa Clara. It's also available on ftp.cica.indiana.edu.
---------------------------------------------------------------------------
5.5. GDI
=========
...........................................................................
5.5.1. Animation
-----------------
If you want to do good-quality animation under Windows 3.1 without
requiring that each user have a 486/50 with an accelerated video card, you
should consider using a differential animation technique. There is a good
example available on ftp.uunet.uu.net (and also on CompuServe) under
/vendor/microsoft/multimedia/sample called rleapp, which uses this
technique. Another sample program in the same directory, transblt,
demonstrates a technique for doing fast BitBlits.
Both techniques are also documented in the technotes in /vendor/microsoft/
multimedia/technote.
...........................................................................
5.5.2. Background color
------------------------
If you insist on a white background, use
WinClass.hbrBackground = GetStockObject(GCW_WHITEBRUSH);
for your window background. If it doesn't matter to you, however, you
should use the Control Panel-defined window background color instead:
WinClass.hbrBackground = CreateSolidBrush(COLOR_WINDOW + 1);
...........................................................................
5.5.3. Changing palette entries in 16-color mode
-------------------------------------------------
If you are using a standard driver, you will need to bypass Windows to do
it (if you happen to have a 16-color driver which support palettes, you can
use standard Windows palette management functions).
Microsoft will tell you to buy the DDK, but there is another way. Now, the
Windows system palette maps onto the VGA 16-color palette as follows:
VGAPAL SYSPAL VGAPAL SYSPAL
00 00 08 07
01 01 09 13
02 02 10 14
03 03 11 15
04 04 12 16
05 05 13 17
06 06 14 18
07 12 15 19
So you can define some macros to take care of the mapping:
#define syspal(n) (n<7 ? n : (n>8 ? n+4 : (n=7 ? 12 : 7)))
#define vgapal(n) (n<7 ? n : (n>12 ? n-4 : (n=7 ? 8 : 7)))
When you get a WM_SETFOCUS event, save the current state of the hardware
colormap and installs the one you want. When you get a WM_KILLFOCUS event,
restore the original palette. Don't use the palette registers directly,
though, just modify the color registersthat they point to. (For details on
redefining a VGA palette, see a book such as A Programmer's Guide to PC and
PS/2 Video Systems by Richard Wilton.)
...........................................................................
5.5.4. DIB bitmaps
-------------------
Microsoft has higher-level DIB library and DLL available for downloading
from CompuServe. Unfortunately, it is not currently available by ftp.
...........................................................................
5.5.5. Speeding up WM_PAINT redraws
------------------------------------
To speed up your WM_PAINT processing, you may want to use a technique
similar to the following one, as presented by John Grant
(jagrant@emr1.emr.ca):
There are two reasons for redrawing your window:
* because Windows tells you to do it
* because something application specific requires it
You shouldn't have to redraw everything from scratch every time you get a
WM_PAINT message - just save it as a bitmap and repaint from the bitmap.
However, there are cases when the bitmap becomes invalid and you have to re-
paint the hard way.
In the main window WndProc, I respond to Windows messages as follows (note
there may be other code too, I just put in the stuff relevant to the
redraw):
case WM_CREATE: KillBitmap();
break;
case WM_SIZE: KillBitmap();
break;
case WM_PAINT: DrawMyPicture(hwnd);
break;
case WM_DESTROY: KillBitmap();
PostQuitMessage(0);
break;
Notice that you should not do KillBitmap() when I handle WM_PAINT. If, in
response to an application-specific condition, you want to force a redraw,
do the following:
KillBitmap();
InvalidateRect(hwnd,NULL,TRUE); // generates WM_PAINT
Now, for an application specific sample code fragment:
// global variable
static HBITMAP hbitmap_main = NULL;
void DrawMyPicture(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(hwnd, &ps);
if (hbitmap_main == NULL){
DrawMyPictureTheHardWay(hwnd, hdc);
hbitmap_main = SaveClientAreaAsBitmap(hwnd);
} else {
DrawBitMap(hdc, hwnd, 0, 0, hbitmap_main);
}
EndPaint(hwnd,&ps);
}
void KillBitmap(void)
{
if (hbitmap_main != NULL){
DeleteObject(hbitmap_main);
hbitmap_main = NULL;
}
}
Don't use PAINTSTRUCT.rcPaint which describes the area that Windows says
needs repainting; just BitBlt the whole thing and Windows will clip it.
No, it's not overkill - it's fast!
Finally, two routines that you can put in your library for use with all
your apps.
/*-----------------------------------------------------------------*
| save entire client area of window into a bitmap |
*-----------------------------------------------------------------*/
HBITMAP SaveClientAreaAsBitmap(HWND hwnd)
{
RECT rect;
HDC hdc,hmemdc;
HBITMAP hbitmap,old_hbitmap;
hbitmap=NULL;
hmemdc=NULL;
hdc=NULL;
//get source device context for the client area
hdc=GetDC(hwnd);
if(hdc==NULL) goto done;
//get destination memory hdc compatible with client area
hmemdc=CreateCompatibleDC(hdc);
if(hmemdc==NULL) goto done;
//create compatible bitmap for client area
GetClientRect(hwnd,&rect); //.top & .left are both 0
hbitmap=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
if(hbitmap==NULL) goto done;
//select client area bitmap into device context so we can write on it
old_hbitmap=SelectObject(hmemdc,hbitmap);
//and copy it to the new hmemdc
BitBlt( hmemdc,0,0, //destination (x,y)
rect.right,rect.bottom, //width, height
hdc,0,0, //source (x,y)
SRCCOPY);
//all done
SelectObject(hmemdc,old_hbitmap);
done: if(hdc !=NULL) ReleaseDC(hwnd,hdc);
if(hmemdc!=NULL) DeleteDC(hmemdc);
return(hbitmap);
}
/*----------------------------------------------------------------*
| Draw a bitmap into the current device context. |
| This is essentially the same as Petzold's code. |
*----------------------------------------------------------------*/
void DrawBitMap(HDC hdc,int xleft,int ytop,HBITMAP hbitmap)
{
RECT rect;
HDC hmemdc;
BITMAP bm;
POINT point;
HBITMAP old_hbitmap;
hmemdc=NULL;
old_hbitmap=NULL;
//create memory device context & select bitmap
hmemdc=CreateCompatibleDC(hdc);
if(hmemdc==NULL) goto done;
old_hbitmap=SelectObject(hmemdc,hbitmap);
SetMapMode(hmemdc,GetMapMode(hdc)); //same as for hdc
//get bitmap dimensions & convert to logical
GetObject(hbitmap,sizeof(bm),&bm);
point.x=bm.bmWidth;
point.y=bm.bmHeight;
DPtoLP(hdc,&point,1);
BitBlt(hdc,xleft,ytop, //destination
point.x,point.y, //width, height
hmemdc,0,0, //source
SRCCOPY);
done: if(old_hbitmap!=NULL) SelectObject(hmemdc,old_hbitmap);
if(hmemdc!=NULL) DeleteDC(hmemdc);
return;
}
...........................................................................
5.5.6. Using CMY colors instead of RGB
---------------------------------------
To use CMY colors with the Windows GDI instead of RGB, you can define the
following macro to supplement the standard RGB one:
#define CMY(c, m, y) RGB(255 - c, 255 - m, 255 - y)
...........................................................................
5.5.7. Using only solid colors
-------------------------------
If you want to use only solid colors (that is, get the nearest solid color
to the one you specified), use the GetNearestColor() function call to map
an RGB value to a solid cor available in the current color palette. Also,
if you are using a palette, you can call the PALETTERGB(r,g,b) macro
instead of the usual RGB(r,g,b) to map to the nearest color in the palette.
---------------------------------------------------------------------------
5.6. Text and fonts
====================
...........................................................................
5.6.1. Creating new fonts
--------------------------
To create new TrueType fonts, you will need a commercial package such as
Fontographer or FontMonger. While FontMonger is a more limited tool, it is
quite inexpensive and often sufficient for smaller projects. Fontographer
is a semi-professional tool with a higher price tag. Neither tool allows
you to construct your own hints for small point sizes.
...........................................................................
5.6.2. Rotating fonts
----------------------
First, you cannot rotate screen fonts. Effectively this means that you can
only rotate TrueType and Type 1 (ATM) fonts. To do the rotation, you will
have to create a new logical font with the correct escapement value.
Select the font into the display context, do your displaying, select the
original font back in, and delete the font object.
Note that Type 1 fonts, which are considered device fonts, use a reversed
direction for the rotation from TrueType fonts; you'll have to check the
font type before doing the rotation, or otherwise you will have some fonts
angled up and others down.
...........................................................................
5.6.3. TrueType width calculation
----------------------------------
The most accurate widths for TrueType fonts can be obtained using the
following method (courtesy of Glenn Adams (glenn@wheat-chex.ai.mit.edu)):
1. Using EnumFonts() (or EnumFontFamilies()), obtain lpntm->ntmSizeEM.
This value "specifies the size of the em square for the font, in the
units for which the font was designed (notional units)." Most fonts
will have the value of 2048.
2. Create a font using the above size as the lfHeight. This will create a
font with metrics that coincide with the coordinate space used in the
font's design, i.e., no scaling will occur in the logical coordinate
space.
3. Use GetCharABCWidths() to get the ABC width structures for all the
font's glyphs. You can now compute the widths quite accurately, along
with overhang and overhang. Keep in mind that the horizontal escapement
(i.e., the distance current point is advanced after rendering a glyph)
is equal to A + B + C of the returned widths. If A is negative, the
glyph extends outside the EM square to the left; if C is negative, then
the glyph extends outside the EM square to the right.
One last point: the above widths will not take into account grid fitting,
which will occur in actual display. But, if you set the mapping mode to
MM_ISOTROPIC and use a logical coordinate space which coresponds to the
font design size, you can do all your layout computations using design
sizes without regard to final viewport mapping.
---------------------------------------------------------------------------
5.7. Miscellaneous
END--cut here--cut here
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads