home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / os / mswindo / announce / 116 < prev    next >
Encoding:
Text File  |  1993-01-28  |  39.5 KB  |  1,026 lines

  1. Path: sparky!uunet!ukma!cs.widener.edu!dsinc!pitt.edu!infidel
  2. From: tomh@wes.on.ca (Tom Haapanen)
  3. Newsgroups: comp.os.ms-windows.announce
  4. Subject: Windows Programmer FAQ: ASCII format, 02/04
  5. Message-ID: <1993Jan28.030802.117.comp.os.ms-windows.FAQ-prg.2@pitt.edu>
  6. Date: 27 Jan 93 16:59:07 GMT
  7. Sender: infidel+@pitt.edu
  8. Followup-To: comp.os.ms-windows.misc
  9. Organization: Waterloo Engineering Software
  10. Lines: 1013
  11. Approved: infidel+@pitt.edu
  12.  
  13. The following is part 02 of 04 of the ASCII text version of the
  14. Microsoft Windows Programmer FAQ.  While some of you may find this the best
  15. format, the more popular format is the WinHelp file.  These are posted
  16. simultaneously with the ASCII version to comp.binaries.ms-windows.
  17.  
  18. You can obtain a copy of the WinHelp version as "faqprg01.zip" by ftp
  19. from sonygate.sony.com [192.63.138.2], in /pub/comp.binaries.ms-windows.
  20. It will also be placed on ftp.cica.indiana.edu once disk space permits.
  21.  
  22.  
  23.  
  24. BEGIN--------------cut here--------cut here------------cut here----------
  25.   from the system menu, if there is one.
  26.   
  27.   Also, you can  alternately disable resizing by creating the windows with
  28.   ~WS_THICKFRAME, and disabling the Size... item on the system menu.
  29.   
  30. ...........................................................................
  31.  
  32. 5.1.10.  Getting the handle of the active window
  33. ------------------------------------------------
  34.   To get the active window's handle, just call
  35.        hActiveWindow = GetFocus();
  36.   
  37. ...........................................................................
  38.  
  39. 5.1.11.  Keeeping a window on top
  40. ---------------------------------
  41.   To keep a window on top of all other windows without requiring it to be the
  42.   active window (akin to the behaviour exhibited by the Windows 3.1 clock),
  43.   you can make the following function call:
  44.        SetWindowPos(hWnd, (HWND)-1, 0, 0, 0, 0,
  45.                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  46.   Note that it is not considered polite behavior by an application to always
  47.   keep its window topmost, unless you at least give the user the option of
  48.   disabling this "feature".
  49.   
  50. ...........................................................................
  51.  
  52. 5.1.12.  Limiting window size
  53. -----------------------------
  54.   To limit window size, you'll need to process the WM_GETMINMAXINFO message,
  55.   and fill in the structure describing the minimum and maximum window sizes.
  56.   
  57. ...........................................................................
  58.  
  59. 5.1.13.  Right-justifying menu items
  60. ------------------------------------
  61.   To right-justify an entire menu item or just a part of it, place a \a in
  62.   the string just before the right-justified part.
  63.   
  64.   Incidentally, the Windows 3.0 CUA guidelines no longer call for right-
  65.   justifying the Help menu on the menu bar.
  66.   
  67. ...........................................................................
  68.  
  69. 5.1.14.  Right-justifying menu items at runtime
  70. -----------------------------------------------
  71.   It's undocumented, but what you need is a 0x08 in the menu string.  The
  72.   easiest way to do this is to place a \b in the string before the right-
  73.   justified part (either the text of the accelerator key).
  74.   
  75.   Incidentally, the Windows 3.0 CUA guidelines no longer call for right
  76.   justifying the Help menu on the menu bar.
  77.   
  78. ...........................................................................
  79.  
  80. 5.1.15.  Trapping mouse clicks on desktop
  81. -----------------------------------------
  82.   To trap mouseclicks on the desktop, you will need to subclass the desktop
  83.   window.  The following code fragment illustrates the technique (sample code
  84.   courtesy of Blake Coverett, blakeco@microsoft.com ):
  85.   
  86.   To subclass the desktop:
  87.       hWndDesktop=GetDesktopWindow();
  88.       hSaveTask=GetCurrentTask();
  89.       lpfnDesktop=(FARPROC)GetWindowLong(hWndDesktop, GWL_WNDPROC);
  90.       lpfnSubClassProc=MakeProcInstance((FARPROC)WndProc, hInst);
  91.       SetWindowLong(hWndDesktop, GWL_WNDPROC,
  92.                     (LPARAM)(LONG)lpfnSubClassProc);
  93.   
  94.   and then to undo it when ready to unload:
  95.       SetWindowLong(hWnd,GWL_WNDPROC, (LPARAM)(LONG)lpfnDesktop);
  96.       PostAppMessage(hSaveTask,WM_QUIT,0,0);
  97.   
  98. ...........................................................................
  99.  
  100. 5.1.16.  Using status bars with MDI
  101. -----------------------------------
  102.   Add the following code fragments to the indicated places in the WinProc()
  103.   of an application, or the FrameWinProc() of a MDI application.
  104.   
  105.   case WM_CREATE:
  106.       hWndClient = CreateWindow( "MDIClient",...,
  107.                       WS_CHILD|WS_VISIBLE|WS_CLIBSIBLINGS|
  108.                       WS_HSCROLL|WS_VSCROLL,... );
  109.       hWndStatus = CreateWindow( "Static",...,
  110.                       WS_CHILD|WS_VISIBLE|SS_LEFT|SS_NOPREFIX,... );
  111.       ...
  112.   case WM_SIZE:
  113.       GetClientRect( hWnd,&rect );
  114.       // Calculate DIVIDING_LINE such that.
  115.       // rect.top < DIVIDING_LINE < rect.bottom
  116.       // One choice:
  117.       // DIVIDING_LINE = rect.bottom - GetSystemMetrics(SM_CYMENU);
  118.       MoveWindow( hWndClient,rect.left,rect.top,
  119.                   rect.right,DIVIDING_LINE,TRUE );
  120.       MoveWindow( hWndStatus,rect.left,DIVIDING_LINE,
  121.                   rect.right,rect.bottom,TRUE );
  122.       break;  // Do *not* pass to DefFrameProc() of MDI app!!!
  123.       ...
  124.       // To change the status text:
  125.       SendMessage( hWndStatus,WM_SETTEXT,0,(LONG)(LPSTR)szStatusText );
  126.   
  127.   Notes:
  128.   * For non-MDI applications, all references to hWndClient above will simply
  129.      be removed.
  130.   * Menu selection can be tracked by setting the status text at a response to
  131.      the WM_MENUSELECT message.
  132.   * To draw a line between status bar and the rest of the client area, the
  133.      DIVIDING_LINE should be adjusted in either MoveWindow() call to leave a
  134.      gap between, which is called InvalidateRect() for, and actually being
  135.      painted in response to the WM_PAINT.
  136.   * The parent window should have WS_CLIPCHILDREN style bit set.
  137.   
  138. ---------------------------------------------------------------------------
  139.  
  140. 5.2.  Dialogs
  141. =============
  142.   
  143. ...........................................................................
  144.  
  145. 5.2.1.  A dialog as an MDI child window [Borland OWL]
  146. -----------------------------------------------------
  147.   If you want to use a dialog as an MDI child window, you can use the
  148.   following code as the basis of your own implementation.  Thanks to Martin
  149.   Calsyn (martin@iastate.edu) for the sample code.
  150.   
  151.   What follows are three files:
  152.        TMDIDLOG.H          The interface for modeless MDI-child dialogs.
  153.        TMDIDLOG.CPP   The implementation of same
  154.        EXAMPLE.CPP         An (partial) example of using the TMDIDLOG
  155.   classes.
  156.   
  157.   You need to sub-class TMDIDialogWindow in order to give your MDI child a
  158.   unique icon, etc (not shown in the example) and to implement the
  159.   CreateChildDialog abstract method.  And, of course, you need to sub-class
  160.   TMDIDialog in order to establish a transfer structure, interface objects
  161.   and all the things you usually do with sub-classes of TDialog.
  162.   
  163.   The classes included below work by presenting the dialog as a child of a
  164.   TBWindow which is itself a child of the MDI.  These classes are based on
  165.   tips and suggestions offered by the Borland advisor line.
  166.   
  167.   Martin claims that the transfer of this technique to MS MFC is trivial.
  168.   
  169.   ---------------------------- TMDIDLOG.H -------------------------
  170.   
  171.   #ifndef __tmdidlog_h__
  172.   #define __tmdidlog_h__
  173.   
  174.   #include <owl.h>
  175.   #include <bwindow.h>
  176.   
  177.   _CLASSDEF(TMDIDialog)
  178.   _CLASSDEF(TMDIDialogWindow)
  179.   
  180.   class TMDIDialog : public TDialog
  181.   {
  182.   public:
  183.       TMDIDialog(PTWindowsObject AParent,LPSTR AName,PTModule AModule=NULL) :
  184.       TDialog(AParent, AName, AModule) {};
  185.       TMDIDialog(PTWindowsObject AParent,int ResourceId,PTModule
  186.   AModule=NULL):
  187.       TDialog( AParent, ResourceId, AModule ) {};
  188.       virtual void WMSetFocus( RTMessage Msg ) = [WM_FIRST + WM_SETFOCUS];
  189.       virtual void CloseWindow( int ret );
  190.       virtual void CloseWindow();
  191.       virtual void Cancel( RTMessage msg ) = [ID_FIRST + IDCANCEL];
  192.   
  193.       friend class TMDIDialogWindow;
  194.   
  195.   protected:
  196.       TMDIDialogWindow     *mdiw;
  197.   };
  198.   
  199.   class TMDIDialogWindow : public TBWindow
  200.   {
  201.   public:
  202.       TMDIDialogWindow(PTWindowsObject aParent,LPSTR aName,
  203.                        PTModule aModule = NULL);
  204.       virtual void WMMDIActivate( RTMessage Msg ) = [WM_FIRST +
  205.   WM_MDIACTIVATE];
  206.       virtual void WMSetFocus( RTMessage Msg ) = [WM_FIRST + WM_SETFOCUS];
  207.       virtual void WMSize( RTMessage Msg ) = [WM_FIRST + WM_SIZE];
  208.       virtual void SetupWindow();
  209.       virtual LPSTR GetClassName() { return "_TMDIDialogWindow"; };
  210.       virtual void GetWindowClass( WNDCLASS& aWndClass );
  211.       virtual void WMGetMinMaxInfo( RTMessage msg ) = [WM_FIRST +
  212.   WM_GETMINMAXINFO];
  213.       virtual TMDIDialog *CreateChildDialog() = 0;
  214.   
  215.   private:
  216.       PTMDIDialog dlog;
  217.   };
  218.   
  219.   #endif
  220.   
  221.   ------------------------- TMDIDLOG.CPP --------------------------
  222.   
  223.   #include "tmdidlog.h"
  224.   
  225.   TMDIDialogWindow::TMDIDialogWindow(PTWindowsObject aParent,
  226.                   LPSTR aName,
  227.                   PTModule aModule) :
  228.             TBWindow(aParent,aName,aModule)
  229.   {
  230.       dlog = NULL;
  231.       GetApplication()->SetKBHandler( this );
  232.       SetFlags( WB_KBHANDLER, FALSE );
  233.   }
  234.   
  235.   void TMDIDialogWindow::WMMDIActivate( RTMessage Msg )
  236.   {
  237.       TWindow::WMMDIActivate( Msg );
  238.   
  239.       if ( Msg.WParam == TRUE ) {
  240.           GetApplication()->SetKBHandler(this);
  241.           if (dlog!=NULL)
  242.               SetFocus(((PTDialog)dlog)->HWindow );
  243.       }
  244.   }
  245.   
  246.   void TMDIDialogWindow::GetWindowClass( WNDCLASS& aWndClass )
  247.   {
  248.       TWindow::GetWindowClass(aWndClass);
  249.   }
  250.   
  251.   void TMDIDialogWindow::WMSetFocus(RTMessage Msg)
  252.   {
  253.       PostMessage(HWindow, WM_MDIACTIVATE, TRUE, 0L );
  254.       DefWndProc(Msg);
  255.   }
  256.   
  257.   void TMDIDialogWindow::SetupWindow()
  258.   {
  259.       TWindow::SetupWindow();
  260.   
  261.       dlog = CreateChildDialog();
  262.       if (dlog) {
  263.           dlog->mdiw = this;
  264.           RECT r;
  265.           GetApplication()->MakeWindow(dlog);
  266.           SetFocus(dlog->HWindow);
  267.           GetWindowRect(HWindow,&r);
  268.           Attr.X = r.left;
  269.           Attr.Y = r.top;
  270.           GetWindowRect(dlog->HWindow,&r);
  271.           AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
  272.           Attr.W = r.right - r.left;
  273.           Attr.H = r.bottom - r.top;
  274.           MoveWindow(HWindow,Attr.X, Attr.Y, Attr.W, Attr.H, 1);
  275.       }
  276.   }
  277.   
  278.   void TMDIDialogWindow::WMGetMinMaxInfo( RTMessage msg )
  279.   {
  280.       if (dlog) {
  281.           MINMAXINFO *lpmmi = (MINMAXINFO *)msg.LParam;
  282.           RECT r;
  283.           POINT pt1,pt2;
  284.           GetWindowRect(HWindow,&r);
  285.           pt1.x = r.left;
  286.           pt1.y = r.top;
  287.           GetWindowRect(dlog->HWindow,&r);
  288.           AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
  289.           pt2.x = r.right - r.left;
  290.           pt2.y = r.bottom - r.top;
  291.           lpmmi->ptMaxSize = pt2;
  292.           lpmmi->ptMaxPosition = pt1;
  293.           lpmmi->ptMinTrackSize = pt2;
  294.           lpmmi->ptMaxTrackSize = pt2;
  295.       }
  296.   }
  297.   
  298.   void TMDIDialogWindow::WMSize( RTMessage Msg )
  299.   {
  300.       RECT r;
  301.       TWindow::WMSize( Msg );
  302.       if (!IsIconic(HWindow)) {
  303.           GetWindowRect(dlog->HWindow,&r);
  304.           AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
  305.           Attr.W = r.right - r.left;
  306.           Attr.H = r.bottom - r.top;
  307.           MoveWindow(HWindow, Attr.X, Attr.Y, Attr.W, Attr.H, 1);
  308.       }
  309.   }
  310.   
  311.   //
  312.   //  TMDIDialog methods...
  313.   //
  314.   
  315.   void TMDIDialog::WMSetFocus( RTMessage Msg )
  316.   {
  317.       GetApplication()->SetKBHandler( this );
  318.       DefWndProc( Msg );
  319.   }
  320.   
  321.   void TMDIDialog::CloseWindow(int ret)
  322.   {
  323.       TDialog::CloseWindow(ret);
  324.       mdiw->CloseWindow();
  325.   }
  326.   
  327.   void TMDIDialog::CloseWindow()
  328.   {
  329.       TDialog::CloseWindow();
  330.       mdiw->CloseWindow();
  331.   }
  332.   
  333.   void TMDIDialog::Cancel(RTMessage msg)
  334.   {
  335.       CloseWindow(IDCANCEL);
  336.   }
  337.   
  338.   -------------------------- EXAMPLE.CPP --------------------------
  339.   
  340.   //
  341.   //  #include owl, tmdidlog.h and others as needed
  342.   //
  343.   
  344.   //
  345.   //  ChildDialog is a dialog box defined in the RC file under
  346.   //    the name 'MY_DLOG'.  The definition of MY_DLOG is pretty plain
  347.   //    except that it should have the following attributes:
  348.   //              STYLE WS_CHILD | WS_VISIBLE
  349.   //    and optionally CLASS "BORDLG"
  350.   //  That is, it should have no caption, sys menu, or border of any
  351.   //  type.  It should also have an origin of 0,0 otherwise it will
  352.   //  be offset within the MDI child window.
  353.   //
  354.   //  An instance of ClientDialog can be created as a modeless MDI
  355.   //  client by doing : `new ChildDialogWindow(this, "Title", NULL)'
  356.   //  from your MDI frame object or other appropriate place.
  357.   //
  358.   
  359.   class ChildDialog : public TMDIDialog
  360.   {
  361.   public:
  362.       ChildDialog(PTWindowsObject aParent,
  363.                    LPSTR aName, PTModule aModule = NULL);
  364.   
  365.   };
  366.   
  367.   ChildDialogWindow::ChildDialogWindow(PTWindowsObject aParent,LPSTR aName,
  368.                                          PTModule aModule)
  369.             : TMDIDialogWindow(aParent,aName,aModule)
  370.   {
  371.       obj_id = object_id;
  372.   }
  373.   
  374.   TMDIDialog* ChildDialogWindow::CreateChildDialog()
  375.   {
  376.       return (TMDIDialog*)new ChildDialog(this,(LPSTR)"MY_DLOG",NULL);
  377.   }
  378.   
  379.   ChildDialog::ChildDialog(PTWindowsObject aParent,
  380.                              LPSTR aName, PTModule aModule)
  381.             : TMDIDialog(aParent,aName,aModule)
  382.   {
  383.       // The usual stuff:  Initialize the transfer structure, create
  384.       //  interface objects, etc.
  385.   
  386.   }
  387.   
  388.   
  389. ...........................................................................
  390.  
  391. 5.2.2.  Adding controls to a non-dialog window
  392. ----------------------------------------------
  393.   You can do this by simply calling CreateWindow() with one of the predefined
  394.   child window control class names (see the control class definition table in
  395.   the SDK reference manual).
  396.   
  397. ...........................................................................
  398.  
  399. 5.2.3.  Doing a timeout in a dialog
  400. -----------------------------------
  401.   Start a timer in your WM_INITDIALOG processing. If your dialog box receives
  402.   the WM_TIMER message, kill the timer and post yourself a WM_COMMAND messgae
  403.   with wParam == IDOK. If the user presses any button, restart the timer.
  404.   
  405. ...........................................................................
  406.  
  407. 5.2.4.  Minimize button on modal dialog moves when clicked
  408. ----------------------------------------------------------
  409.   It's a bug in Windows 3.1.  To duplicate this, create a modal dialog with
  410.   the styles CAPTION, MODAL FRAME, MINIMIZE-BOX, activate the dialog, press
  411.   the Minimize button -- and watch it move to the top right corner, on top of
  412.   the modal frame!
  413.   
  414.   The workaround: don't use a Minimize box on a modal dialog.
  415.   
  416. ...........................................................................
  417.  
  418. 5.2.5.  Modifying common dialogs
  419. --------------------------------
  420.   If you want to modify a common dialog, do not delete controls from the
  421.   templates, as the common dialog procedures expect these controls to exist.
  422.   Instead, make them invisible or move them outside the boundaries of the
  423.   dialog gox to prevent the user from seeing or accessing them.
  424.   
  425. ...........................................................................
  426.  
  427. 5.2.6.  Null dialog handles from Borland custom dialogs
  428. -------------------------------------------------------
  429.   If you keep getting null dialog handles with Borland C++ unless I have
  430.   Turbo C++ running, your dialog is probably of the BorDlg class, which
  431.   requires code in BWCC.DLL.  However, you have probably not done anything to
  432.   force BWCC.DLL to be loaded with your application, so the dialog manager
  433.   cannot find the necessary routines to draw the dialog.  The easiest way to
  434.   force BWCC.DLL to be loaded is to call BWCCGetVersion() at the very
  435.   beginning of your application, and to link in BWCC.LIB.
  436.   
  437.   Alternatly, you can do a
  438.        WinExec("loadbwcc.exe");
  439.   when you start up your application, as long as loadbwcc.exe is available.
  440.   
  441. ...........................................................................
  442.  
  443. 5.2.7.  Preventing switching away from a modal dialog
  444. -----------------------------------------------------
  445.   The design of the Windows API means that if there are two dialogs active
  446.   simultaneously, the user can switch between the two, even one of them is
  447.   modal.  To prevent this, you should use EnableWindow() to explicitly diable
  448.   any modeless dialogs when your modal dialog starts up.
  449.   
  450. ...........................................................................
  451.  
  452. 5.2.8.  Using a dialog as your main window
  453. ------------------------------------------
  454.   First, you have to create a dialog box first.  Include a class name(you
  455.   name
  456.   it).  Then, in your WinMain function, register a class using that class
  457.   name
  458.   and add the constant DLGWINDOWEXTRA to the window extra byte component.
  459.   This
  460.   constant is defined in Windows.h.  Then, use CreateDialog() to display the
  461.   dialog, make sure the last 2 parameters are set to NULL.  In your WndProc
  462.   function, instead of calling DefWindowProc, call DefDlgProc.  Then, you are
  463.   basically all set.  Make sure in your dialog definition you create your
  464.   dialog
  465.   box initially visible.  For further information, see your docs.
  466.   
  467.   
  468. ...........................................................................
  469.  
  470. 5.2.9.  Using Borland custom dialogs with other compilers
  471. ---------------------------------------------------------
  472.   You can't integrate bwcc.dll with the Dialog Editor but you can manually
  473.   modify the dialog file and use appropriate BWCC control classes and styles.
  474.   
  475.   Include bwcc.h in your header file and then add bwcc.lib to your link
  476.   options (before libw.lib). Also make sure bwcc.dll can be found in either
  477.   the Windows directory or the current directory when the app starts or in
  478.   the path.
  479.   
  480.   Examples (thanks to Sam Espartero, sqe@hpcc01.corp.hp.com):
  481.   
  482.   Link Options:
  483.       /align:16 /NOD PLAYCD USERCODE MCICDA SUPERCLS,PLAYCD.EXE,,
  484.       LIBW MLIBCEW bwcc mmsystem, PLAYCD.DEF
  485.   
  486.   Dialog File:
  487.   
  488.        ABOUT DIALOG  4, 5, 199, 137
  489.        STYLE WS_POPUPWINDOW | DS_MODALFRAME | WS_VISIBLE |
  490.              WS_CLIPSIBLINGS | WS_DLGFRAME
  491.        CAPTION "Sample BWCC Dialog"
  492.        CLASS "BorDlg"
  493.        BEGIN
  494.            CONTROL "", 100, "Static", SS_ICON | WS_CHILD | WS_VISIBLE,
  495.                    5, 16, 16, 16
  496.            CONTROL "", -1, "BorShade", WS_CHILD | WS_BORDER,
  497.                    33,6,161,126
  498.            ...
  499.        
  500.            CONTROL "", IDOK, "BorBtn", BS_DEFPUSHBUTTON | WS_TABSTOP |
  501.                    WS_CHILD, 154, 9, 32, 20
  502.        END
  503.   
  504.   Message box:
  505.   
  506.        #ifdef BWCC
  507.            BWCCMessageBox(GetActiveWindow(), "Unknown MCI Error!",
  508.                           "MCI Error", MB_OK | MB_ICONHAND);
  509.        #else
  510.            MessageBox(GetActiveWindow(), "Unknown MCI Error!",
  511.                       "MCI Error", MB_OK | MB_ICONHAND);
  512.        #endif
  513.   
  514. ---------------------------------------------------------------------------
  515.  
  516. 5.3.  Controls
  517. ==============
  518.   
  519. ...........................................................................
  520.  
  521. 5.3.1.  Allowing ENTER in a multiline edit control
  522. --------------------------------------------------
  523.   To allow the use of the Enter key, there is no need to subclass the edit
  524.   control.  An easier way in Windows 3.1 and later (which also works better!)
  525.   is to specify ES_WANTRETURN as part of the style for the edit control (see
  526.   the Windows 3.1 SDK documentation for details).
  527.   
  528. ...........................................................................
  529.  
  530. 5.3.2.  Aligning multi-column listboxes
  531. ---------------------------------------
  532.   In the resource file make sure the list box has the LBS_USETABSTOPS style.
  533.   When you add the items to the listbox, separate the fields with tabs.  You
  534.   can either use the default tab stops, or set your own by sending the
  535.   LBS_SETTABSTOPS message to the listbox. For more information, see the SDK
  536.   Reference, volumes 1 and 2.
  537.   
  538.    It is also possible to use a fixed font, but the tabstop solution usually
  539.   ends up looking much better.
  540.   
  541. ...........................................................................
  542.  
  543. 5.3.3.  Changing button colors
  544. ------------------------------
  545.   In Windows 3.0, the button face is defined by two colors. The grey (white
  546.   with EGA) face and a dark grey (grey if ega) shadow.  The colors also
  547.   change when the button goes from a normal to pushed in state.  The
  548.   WM_CTLCOLOR message only allows you to change one color at a time so to
  549.   which of the button face colors should this apply?  (Windows 2.x button
  550.   faces had only one color so it made sense.)
  551.   
  552.   Maybe something tricky could have been done by using the background color
  553.   for the shadow and foreground color for the face and perhaps doing
  554.   something strange to get the text color in another way... And how do you
  555.   return 2 brushes (you now need a foreground and a background brush)?  Or
  556.   maybe even better, make colors a property of the window and some windows
  557.   could have multiple color properties...
  558.   
  559.   Anyway, Windows doesn't look at the WM_CTLCOLOR message for buttons and
  560.   thus doesn't allow you to change the button colors.  Try it with a listbox
  561.   instead...  The only way to change button colors is to specify
  562.   ButtonColor=, ButtonShadow= and ButtonText= in the [Colors] section of your
  563.   win.ini file.
  564.   
  565.   In Windows 3.1, the button text, shadow and face colors can also be defined
  566.   using the Control Panel.
  567.   
  568. ...........................................................................
  569.  
  570. 5.3.4.  Creating controls at runtime in Visual Basic
  571. ----------------------------------------------------
  572.   To create controls dynamically, you need to create a control array at
  573.   design time.  You can then extend it at runtime, and specify new properties
  574.   for the new controls you have created.
  575.   
  576. ...........................................................................
  577.  
  578. 5.3.5.  Combo boxes with tab stops
  579. ----------------------------------
  580.   Windows does not support combo boxes with tab stops.  If you need such
  581.   items, you have three choices:
  582.      * Use an owner-draw combo box.  This will allow you to draw the items at
  583.        the precise locations you need.  Text-only owner-draw controls are not
  584.        difficult to implement.
  585.      * Use GetTextExtent() to determine the length of each part, padding with
  586.        blanks until it approximately lines up.  Fairly easy, but not fast and
  587.        not precise.
  588.      * Change the font of the control to either Courier or the fixed System
  589.        font.  While this is simple to do, your combo box font will not be
  590.        consistent with the rest of your dialog box controls.
  591.   
  592. ...........................................................................
  593.  
  594. 5.3.6.  Custom button bitmaps in Borland dialogs
  595. ------------------------------------------------
  596.   To create owner-drawn buttons for new additional buttons beyond those
  597.   supplied in bwcc.dll, you first need to create the button bitmaps.  Create
  598.   them 63 pixels wide by 39 deep, and number them "logically".
  599.   
  600.   The bitmap numbers are related to the resource id of the button that you
  601.   want to use them.  Use the following numbers:
  602.        button id + 1000    Unpressed bitmap
  603.        button id + 3000    Pressed bitmap
  604.        button id + 2000    Unpressed bitmap, EGA
  605.        button id + 4000    Pressed bitmap, EGA
  606.   If you use these bitmap ids, bwcc.dll will use the bitmaps on the buttons
  607.   automatically.
  608.   
  609. ...........................................................................
  610.  
  611. 5.3.7.  Drawing on a dialog background
  612. --------------------------------------
  613.   To draw on a dialog background, you can either subclass the dialog, or,
  614.   more simply, respond to the WM_PAINT message in your dialog procedure,
  615.   calling ValidateRect() after you have redrawn the background.
  616.   
  617. ...........................................................................
  618.  
  619. 5.3.8.  Hiding dialog controls
  620. ------------------------------
  621.   EnableWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE), FALSE);
  622.   ShowWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE), SW_HIDE);
  623.   UpdateWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE));
  624.   
  625. ...........................................................................
  626.  
  627. 5.3.9.  Listboxes with large amounts of data
  628. --------------------------------------------
  629.   A standard listbox is limited to 32K of data.  If you need a listbox with a
  630.   larger number of items, you can use one of the following options:
  631.      * Use an owner-draw listbox with LBS_HASSTRINGS.  This will allow 4096
  632.        items.
  633.      * Use an owner-draw listbox LBS_OWNERDRAWFIXED but without
  634.        LBS_HASSTRINGS.  This is somewhat more work, but will allow 8192
  635.        items.
  636.   Some third-party libraries also implement listbox classes which can handle
  637.   huge amounts of data.
  638.   Finally, Microsoft has a virtual listbox implementation available by ftp at
  639.   ftp.uu.net, in the directory /vendor/microsoft/developer-network.
  640.   
  641. ...........................................................................
  642.  
  643. 5.3.10.  Subclassing standard controls
  644. --------------------------------------
  645.   You can subclass standard controls by having your own window procedure
  646.   handle the messages for the windows you create (using SetWindowLong()).
  647.   The only caveat here is for useability: make sure that your subclassed
  648.   controls don't behave in an unexpected manner.
  649.   
  650.   What is definitely a bad idea is modify the class procedure of a standard
  651.   control (using SetClassLong()) and changing the window procedure for all
  652.   such windows, as this will affect all edit controls in all applications
  653.   currently running in the Windows session.
  654.   
  655. ...........................................................................
  656.  
  657. 5.3.11.  Using a window as a modal dialog
  658. -----------------------------------------
  659.   To use a window as a modal dialog, you will need to implement your own
  660.   message loop for that window, complete with PeekMessage(),
  661.   TranslateMessage() and DispatchMessage() calls.  This will allow you to
  662.   accept events only for the current window, discarding all other events.
  663.   
  664. ---------------------------------------------------------------------------
  665.  
  666. 5.4.  Memory
  667. ============
  668.   
  669. ...........................................................................
  670.  
  671. 5.4.1.  Using new() in C++
  672. --------------------------
  673.   In Borland C++ 2.0, and in 3.x's medium model, new() ends up calling
  674.   LocalAlloc(), allocating memory from your near 64K segment.  In BCC 3.x's
  675.   large and compact models (and in Microsoft C/C++ 7.0), however, it will
  676.   make one GlobalAlloc() and do subsegment allocations to allow you access to
  677.   the full memory without making excessive demands on the system limit of
  678.   4096 (8192 in 386 enhanced mode) global memory handles.
  679.   
  680. ...........................................................................
  681.  
  682. 5.4.2.  Global memory owned by DLL
  683. ----------------------------------
  684.   If you use GlobalAlloc in a DLL, the application that called the DLL will
  685.   own the object.  There is a way around this, though: allocate the memory
  686.   using the GMEM_DDESHARE flag; this will make the allocating code segment
  687.   (rather than the current task) own the memory.
  688.   
  689. ...........................................................................
  690.  
  691. 5.4.3.  Determining size of physical memory
  692. -------------------------------------------
  693.   You need to make a DPMI call to obtain that piece of information.  DPMI
  694.   call 0500h with ES:DI pointing to a 30h byte buffer returns the "Free
  695.   Memory Information":
  696.   
  697.       Offset  Description
  698.       00h     Largest available free block in bytes
  699.       04h     Maximum unlocked page allocation
  700.       08h     Maximum locked page allocation
  701.       0Ch     Linear address space size in pages
  702.       10h     Total number of unlocked pages
  703.       14h     Number of free pages
  704.       18h     Total number of physical pages
  705.       1Ch     Free linear address space in pages
  706.       20h     Size of paging file/partition in pages
  707.       24h-2Fh Reserved
  708.   
  709.   The size of one page in bytes can be determined by function 0604h, which
  710.   returns the page size in bytes in BX:CX.  To call a DPMI function, invoke
  711.   the interrupt 31h. Carry bit will be clear if call was successful.
  712.   
  713.   The complete DPMI 0.9 specification is available free from Intel Literature
  714.   JP26, Santa Clara.  It's also available on ftp.cica.indiana.edu.
  715.   
  716.   
  717. ---------------------------------------------------------------------------
  718.  
  719. 5.5.  GDI
  720. =========
  721.   
  722. ...........................................................................
  723.  
  724. 5.5.1.  Animation
  725. -----------------
  726.   If you want to do good-quality animation under Windows 3.1 without
  727.   requiring that each user have a 486/50 with an accelerated video card, you
  728.   should consider using a differential animation technique.  There is a good
  729.   example available on ftp.uunet.uu.net (and also on CompuServe) under
  730.   /vendor/microsoft/multimedia/sample  called rleapp, which uses this
  731.   technique.  Another sample program in the same directory, transblt,
  732.   demonstrates a technique for doing fast BitBlits.
  733.   
  734.   Both techniques are also documented in the technotes in /vendor/microsoft/
  735.   multimedia/technote.
  736.   
  737. ...........................................................................
  738.  
  739. 5.5.2.  Background color
  740. ------------------------
  741.   If you insist on a white background, use
  742.        WinClass.hbrBackground = GetStockObject(GCW_WHITEBRUSH);
  743.   for your window background.  If it doesn't matter to you, however, you
  744.   should use the Control Panel-defined window background color instead:
  745.        WinClass.hbrBackground = CreateSolidBrush(COLOR_WINDOW + 1);
  746.   
  747.   
  748. ...........................................................................
  749.  
  750. 5.5.3.  Changing palette entries in 16-color mode
  751. -------------------------------------------------
  752.   If you are using a standard driver, you will need to bypass Windows to do
  753.   it (if you happen to have a 16-color driver which support palettes, you can
  754.   use standard Windows palette management functions).
  755.   
  756.   Microsoft will tell you to buy the DDK, but there is another way.  Now, the
  757.   Windows system palette maps onto the VGA 16-color palette as follows:
  758.   
  759.   VGAPAL SYSPAL   VGAPAL  SYSPAL
  760.     00     00       08      07
  761.     01     01       09      13
  762.     02     02       10      14
  763.     03     03       11      15
  764.     04     04       12      16
  765.     05     05       13      17
  766.     06     06       14      18
  767.     07     12       15      19
  768.   
  769.   So you can define some macros to take care of the mapping:
  770.   
  771.   #define syspal(n) (n<7 ? n : (n>8 ? n+4 : (n=7 ? 12 : 7)))
  772.   #define vgapal(n) (n<7 ? n : (n>12 ? n-4 : (n=7 ? 8 : 7)))
  773.   
  774.   When you get a WM_SETFOCUS event, save the current state of the hardware
  775.   colormap and installs the one you want.  When you get a WM_KILLFOCUS event,
  776.   restore the original palette. Don't use the palette registers directly,
  777.   though, just modify the color registersthat they point to.  (For details on
  778.   redefining a VGA palette, see a book such as A Programmer's Guide to PC and
  779.   PS/2 Video Systems by Richard Wilton.)
  780.   
  781. ...........................................................................
  782.  
  783. 5.5.4.  DIB bitmaps
  784. -------------------
  785.   Microsoft has higher-level DIB library and DLL available for downloading
  786.   from CompuServe.  Unfortunately, it is not currently available by ftp.
  787.   
  788. ...........................................................................
  789.  
  790. 5.5.5.  Speeding up WM_PAINT redraws
  791. ------------------------------------
  792.   To speed up your WM_PAINT processing, you may want to use a technique
  793.   similar to the following one, as presented by John Grant
  794.   (jagrant@emr1.emr.ca):
  795.   
  796.   There are two reasons for redrawing your window:
  797.      *   because Windows tells you to do it
  798.      *   because something application specific requires it
  799.   
  800.   You shouldn't have to redraw everything from scratch every time you get a
  801.   WM_PAINT message - just save it as a bitmap and repaint from the bitmap.
  802.   However, there are cases when the bitmap becomes invalid and you have to re-
  803.   paint the hard way.
  804.   
  805.   In the main window WndProc, I respond to Windows messages as follows (note
  806.   there may be other code too, I just put in the stuff relevant to the
  807.   redraw):
  808.   
  809.   case WM_CREATE:     KillBitmap();
  810.                       break;
  811.   case WM_SIZE:       KillBitmap();
  812.                       break;
  813.   case WM_PAINT:      DrawMyPicture(hwnd);
  814.                       break;
  815.   case WM_DESTROY:    KillBitmap();
  816.                       PostQuitMessage(0);
  817.                       break;
  818.   
  819.   Notice that you should not do KillBitmap() when I handle WM_PAINT.  If, in
  820.   response to an application-specific condition, you want to force a redraw,
  821.   do the following:
  822.   
  823.        KillBitmap();
  824.        InvalidateRect(hwnd,NULL,TRUE);    // generates WM_PAINT
  825.   
  826.   Now, for an application specific sample code fragment:
  827.   
  828.   // global variable
  829.        static HBITMAP hbitmap_main = NULL;
  830.   
  831.   void DrawMyPicture(HWND hwnd)
  832.   {
  833.       PAINTSTRUCT ps;
  834.       HDC hdc;
  835.   
  836.       hdc = BeginPaint(hwnd, &ps);
  837.       if (hbitmap_main == NULL){
  838.             DrawMyPictureTheHardWay(hwnd, hdc);
  839.           hbitmap_main = SaveClientAreaAsBitmap(hwnd);
  840.       } else {
  841.            DrawBitMap(hdc, hwnd, 0, 0, hbitmap_main);
  842.       }
  843.       EndPaint(hwnd,&ps);
  844.   }
  845.   
  846.   void KillBitmap(void)
  847.   {
  848.       if (hbitmap_main != NULL){
  849.          DeleteObject(hbitmap_main);
  850.          hbitmap_main = NULL;
  851.       }
  852.   }
  853.   
  854.   Don't use PAINTSTRUCT.rcPaint which describes the area that Windows says
  855.   needs repainting; just BitBlt the whole thing and Windows will clip it.
  856.   No, it's not overkill - it's fast!
  857.   
  858.   Finally, two routines that you can put in your library for use with all
  859.   your apps.
  860.   
  861.   
  862.   /*-----------------------------------------------------------------*
  863.    | save entire client area of window into a bitmap                 |
  864.    *-----------------------------------------------------------------*/
  865.   
  866.   HBITMAP SaveClientAreaAsBitmap(HWND hwnd)
  867.   {
  868.   RECT rect;
  869.   HDC hdc,hmemdc;
  870.   HBITMAP hbitmap,old_hbitmap;
  871.   
  872.        hbitmap=NULL;
  873.        hmemdc=NULL;
  874.        hdc=NULL;
  875.   
  876.        //get source device context for the client area
  877.        hdc=GetDC(hwnd);
  878.        if(hdc==NULL) goto done;
  879.   
  880.        //get destination memory hdc compatible with client area
  881.        hmemdc=CreateCompatibleDC(hdc);
  882.        if(hmemdc==NULL) goto done;
  883.   
  884.        //create compatible bitmap for client area
  885.        GetClientRect(hwnd,&rect);      //.top & .left are both 0
  886.        hbitmap=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
  887.        if(hbitmap==NULL) goto done;
  888.   
  889.        //select client area bitmap into device context so we can write on it
  890.        old_hbitmap=SelectObject(hmemdc,hbitmap);
  891.   
  892.        //and copy it to the new hmemdc
  893.        BitBlt( hmemdc,0,0,             //destination (x,y)
  894.             rect.right,rect.bottom, //width, height
  895.             hdc,0,0,                //source (x,y)
  896.             SRCCOPY);
  897.   
  898.        //all done
  899.        SelectObject(hmemdc,old_hbitmap);
  900.   
  901.   done:   if(hdc   !=NULL) ReleaseDC(hwnd,hdc);
  902.        if(hmemdc!=NULL) DeleteDC(hmemdc);
  903.   
  904.        return(hbitmap);
  905.   }
  906.   
  907.   
  908.   /*----------------------------------------------------------------*
  909.    | Draw a bitmap into the current device context.                 |
  910.    | This is essentially the same as Petzold's code.                |
  911.    *----------------------------------------------------------------*/
  912.   void DrawBitMap(HDC hdc,int xleft,int ytop,HBITMAP hbitmap)
  913.   {
  914.   RECT    rect;
  915.   HDC     hmemdc;
  916.   BITMAP  bm;
  917.   POINT   point;
  918.   HBITMAP old_hbitmap;
  919.   
  920.        hmemdc=NULL;
  921.        old_hbitmap=NULL;
  922.   
  923.        //create memory device context & select bitmap
  924.        hmemdc=CreateCompatibleDC(hdc);
  925.        if(hmemdc==NULL) goto done;
  926.        old_hbitmap=SelectObject(hmemdc,hbitmap);
  927.   
  928.        SetMapMode(hmemdc,GetMapMode(hdc));     //same as for hdc
  929.   
  930.        //get bitmap dimensions & convert to logical
  931.        GetObject(hbitmap,sizeof(bm),&bm);
  932.        point.x=bm.bmWidth;
  933.        point.y=bm.bmHeight;
  934.        DPtoLP(hdc,&point,1);
  935.   
  936.        BitBlt(hdc,xleft,ytop,                  //destination
  937.             point.x,point.y,                //width, height
  938.             hmemdc,0,0,                     //source
  939.             SRCCOPY);
  940.   
  941.   done:   if(old_hbitmap!=NULL) SelectObject(hmemdc,old_hbitmap);
  942.        if(hmemdc!=NULL) DeleteDC(hmemdc);
  943.        return;
  944.   }
  945.   
  946.   
  947. ...........................................................................
  948.  
  949. 5.5.6.  Using CMY colors instead of RGB
  950. ---------------------------------------
  951.   To use CMY colors with the Windows GDI instead of RGB, you can define the
  952.   following macro to supplement the standard RGB one:
  953.      #define  CMY(c, m, y)     RGB(255 - c, 255 - m, 255 - y)
  954.   
  955. ...........................................................................
  956.  
  957. 5.5.7.  Using only solid colors
  958. -------------------------------
  959.   If you want to use only solid colors (that is, get the nearest solid color
  960.   to the one you specified), use the GetNearestColor()  function call to map
  961.   an RGB value to a solid cor available in the current color palette.  Also,
  962.   if you are using a palette, you can call the PALETTERGB(r,g,b) macro
  963.   instead of the usual RGB(r,g,b) to map to the nearest color in the palette.
  964.   
  965. ---------------------------------------------------------------------------
  966.  
  967. 5.6.  Text and fonts
  968. ====================
  969.   
  970. ...........................................................................
  971.  
  972. 5.6.1.  Creating new fonts
  973. --------------------------
  974.   To create new TrueType fonts, you will need a commercial package such as
  975.   Fontographer or FontMonger.  While FontMonger is a more limited tool, it is
  976.   quite inexpensive and often sufficient for smaller projects.  Fontographer
  977.   is a semi-professional tool with a higher price tag.  Neither tool allows
  978.   you to construct your own hints for small point sizes.
  979.   
  980. ...........................................................................
  981.  
  982. 5.6.2.  Rotating fonts
  983. ----------------------
  984.   First, you cannot rotate screen fonts.  Effectively this means that you can
  985.   only rotate TrueType and Type 1 (ATM) fonts.  To do the rotation, you will
  986.   have to create a new logical font with the correct escapement value.
  987.   Select the font into the display context, do your displaying, select the
  988.   original font back in, and delete the font object.
  989.   
  990.   Note that Type 1 fonts, which are considered device fonts, use a reversed
  991.   direction for the rotation from TrueType fonts; you'll have to check the
  992.   font type before doing the rotation, or otherwise you will have some fonts
  993.   angled up and others down.
  994.   
  995. ...........................................................................
  996.  
  997. 5.6.3.  TrueType width calculation
  998. ----------------------------------
  999.   The most accurate widths for TrueType fonts can be obtained using the
  1000.   following method (courtesy of Glenn Adams (glenn@wheat-chex.ai.mit.edu)):
  1001.   1.  Using EnumFonts() (or EnumFontFamilies()), obtain lpntm->ntmSizeEM.
  1002.       This value "specifies the size of the em square for the font, in the
  1003.       units for which the font was designed (notional units)."  Most fonts
  1004.       will have the value of 2048.
  1005.   2.  Create a font using the above size as the lfHeight.  This will create a
  1006.       font with metrics that coincide with the coordinate space used in the
  1007.       font's design, i.e., no scaling will occur in the logical coordinate
  1008.       space.
  1009.   3.  Use GetCharABCWidths() to get the ABC width structures for all the
  1010.       font's glyphs.  You can now compute the widths quite accurately, along
  1011.       with overhang and overhang.  Keep in mind that the horizontal escapement
  1012.       (i.e., the distance current point is advanced after rendering a glyph)
  1013.       is equal to A + B + C of the returned widths.  If A is negative, the
  1014.       glyph extends outside the EM square to the left; if C is negative, then
  1015.       the glyph extends outside the EM square to the right.
  1016.   One last point: the above widths will not take into account grid fitting,
  1017.   which will occur in actual display.  But, if you set the mapping mode to
  1018.   MM_ISOTROPIC and use a logical coordinate space which coresponds to the
  1019.   font design size, you can do all your layout computations using design
  1020.   sizes without regard to final viewport mapping.
  1021.   
  1022. ---------------------------------------------------------------------------
  1023.  
  1024. 5.7.  Miscellaneous
  1025. END--cut here--cut here
  1026.