CS-360
Fall, 2000
Class 3
R. Eckert

WINDOWS RESOURCES, TEXT AND GRAPHICS (DRAWING ON A WINDOW)

THE RESOURCE SCRIPT (.RC FILE)--

  The WINAPP2 example program produces a menu bar with "Rectangle", "Circle", 
  "Clear Screen", and "Quit" items and has a program icon that appears on the task 
  bar when the application is minimized.

  Recall that resources are static data that the linker combines with our
  compiled application. Resources are defined in a script (.rc) file that is
  compiled by a resource compiler into a .res file.

  The WINAPP2.RC resource script file must have a header file included (which 
  gives values to named constants that identify the menu items) and then the
  description of the menu and the icon.

  The most straightforward way of setting up the resource script file is to use 
  a text editor:

  /* WINAPP2.RC  resource file */

  #include "resource.h"

  MYICON ICON myicon.ico

  MYMENU MENU
  BEGIN
    MENUITEM "&Circle",             IDM_CIRCLE
    MENUITEM "&Rectangle",          IDM_RECTANGLE
    MENUITEM "Clear &Screen",       IDM_CLEAR
    MENUITEM "&Quit",               IDM_QUIT
  END

  In this resource script there is an icon identified by "MYICON". The keyword ICON 
  specifies that the following file name (here myicon.ico) is that of the file that 
  describes the actual icon. In addition there is a menu identified by "MYMENU"
  which has the menu items: "Circle", "Rectangle", "Clear Screen", and "Quit".
  Notice that each menu item is identified by constant name: IDM_CIRCLE,
  IDM_RECTANGLE, IDM_CLEAR, and IDM_QUIT. These identifiers have been defined in 
  the resource.h header file whose contents are shown here:

  #define IDM_CIRCLE       40006
  #define IDM_RECTANGLE    40007
  #define IDM_CLEAR        40008
  #define IDM_QUIT         40009

  In the .CPP source program file, the menu items are referred to by these 
  constant names. But they need to have ID numbers so the program can determine 
  which item was selected. The use of constant names makes the  program more 
  readable and less prone to using the wrong ID value. The values and names 
  can be anything. The resource.h header file must also be included in the .CPP 
  source program file so that the same ID values are known both to the C++
  compiler and to the resource compiler.

  In the resource script's "MYMENU", the keyword MENUITEM specifies that the 
  following text in quotes will appear on the window's main menu bar.
    -The '&' means that the following character, when used in conjunction
     with ALT from keyboard and 'cooked', will generate a message indicating
     that the item has been selected. The character following the '&' will be
     underlined on the window's menu bar.

  The resource script file can also be prepared visually using Microsoft's

  Developer Studio resource editors. If done in this way the various id numbers
  will be assigned automatically. This is especially useful when many
  resources (in addition to a menu and an icon) are to be incorporated into
  the application. We'll see how to do this later.

  In the WINAPP2.CPP program, the lpszMenuName field of the wndclass
  structure is set to "MYMENU"--the menu described in the resource file--
  when the window class is registered. This menu will appear when any
  window based on this window class is created.

  As we'll see, the WndProc() must made to respond to user interaction with
  menu items. The key idea is the following:

    -Whenever a menu item is selected with the mouse or its keyboard
     alternative, Windows sends the application a WM_COMMAND message;

        -The low-order word of the wParam contains the ID number of the menu 
         item selected. This can be extracted by using the macro LOWORD().

        -We can do a switch/case on LOWORD(wParam) to perform the correct action.

  The hIcon member of the wndclass is to be set to the icon identified by the
  name "MYICON"--the icon described in the resource file--when the window
  class is registered. To do that, we make use of the Win32 function
  LoadIcon(hInstance,"MYICON"). The first parameter, the instance handle,
  identifies the instance of the application containing the resource
  information (the present one), and the second parameter identifies which
  resource it is.

Before examining the details of the WINAPP2.CPP program's WndProc(), we
need to see how graphics and text output work in Windows.

TEXT AND GRAPHICS OUTPUT (Displaying something in a window)

Under DOS--character modes have fixed sizes and fonts.

   -This is fast, but has limited display possibilities.

DOS systems have video boards that support several graphics modes, but:

   -Drawing commands may depend on graphics mode.

   -Each program that does graphics has to have different logic for each
    graphics mode which means lots of duplicate code.

   -There can be program portability problems.

Windows--always runs in a graphics mode.

   -It is slower, but more flexible.

   -Everything (including text) is drawn one pixel at a time, so:

   -Any size or shape is possible.


THE DEVICE CONTEXT AND DEVICE INDEPENDENCE--

An important design goal of Windows was "Device Independence." This means that:

   -The same program should work using different hardware without modification.

   -Windows takes care of hardware interface.

   -The programmer can concentrate on the program.

How is device independence achieved--

   -Windows programs don't send data directly to hardware devices (screen, printer).

   -They use the "Graphics Device Interface" (GDI) as an intermediary.

   -They draw on an abstract surface called a "device context" (DC).

The following figure illustrates the relationship between the hardware
device, the Graphics Device Interface, and the Device Context.



The Device Context--

   -Is associated with a physical device:
      (display screen, printer, bitmap, metafile).

   -Abstracts the physical device it represents.

   -Commands to draw on a DC are the same regardless of Hardware (video card, printer).

   -The GDI translates these to hardware commands to output to the physical device.

   -The DC is accessed with a "handle to a DC".

   -It must be "gotten" from Windows using GetDC() or BeginPaint().

   -It is specific to a given window so the application can only draw in its client area.

   -The DC specifies attribute settings for drawing (e.g., colors).

   -Attribute settings specify HOW drawing primitives will look
      e.g., background color, text color, etc.

      -An advantage: when drawing, we don't have to send a whole bunch of parameters.

   -A DC contains drawing objects (pen, brush, bitmap, font, etc.)
      These also determine how primitives look (pen--> line-drawing color).

   -Common DCs come from a Windows cache of limited size!
      A program should release a DC when it's done using it, or disastrous
      things could happen!

   -DCs are released with ReleaseDC() or EndPaint().


The GDI: Graphics Device Interface--

   -The part of Windows that converts drawing function calls to hardware commands.

   -It is located in the file GDI.EXE (a DLL).

   -It has many graphics functions:

      -Functions that draw Graphics/Text primitives.

      -Functions to change the values of attributes (settings) in a DC.

   -It has functions that create drawing "objects" (pens, brushes, fonts, etc.).

   -It is shared by all running applications, so a GDI error by one program can 
    crash Windows.

   -The GDI may make use of a device driver program (.DRV) for certain devices.

   -.DRV programs assist the GDI in converting graphics commands to hardware
    commands.

   -These must be provided by the manufacturer of new devices (graphics cards, etc.).


SOME GDI ATTRIBUTE SETTINGS AND OBJECTS--

ATTRIBUTE            DEFAULT         FUNCTION
-------------------------------------------------------------------------------
Background color     white           SetBkColor()     color of empty spaces

Background mode      OPAQUE          SetBkMode()      or TRANSPARENT

Brush Origin         (0,0)           SetBrushOrigin() pattern origin

Clipping Region      whole surf.     SelectClipRgn()  where output displayed

Current Position     (0,0)           MoveToEx()       Start coordinate of line

Drawing Mode         R2COPYPEN       SetROP2()        How to combine with background

Mapping Mode         MM_TEXT         SetMapMode()     units/scaling in DC

Polygon Fill Mode    ALTERNATE       SetPolyFillMode() how polygons filled

Text Char Spacing    0               SetTextCharacterExtra()

Text Color           Black           SetTextColor()   color of text



OBJECT               DEFAULT         FUNCTION
------------------------------------------------------------------------

Bitmap               none            SelectObject()   image object

Brush                WHITE_BRUSH     SelectObject()   area fill object

Font                 SYSTEM_FONT     SelectObject()   text font object

Pen                  BLACK_PEN       SelectObject()   line-drawing object

Color Palette        DEFAULT_PALETTE SelectPalette()  color palette


Graphics objects may be thought of as being similar to an artist's tools.
They must be created and brought into ("selected into") a device context
before they can be used. In this analogy the device context should be thought
of as the artist's portfolio in which he carries his canvasas and other 
tools.

The following figure illustrates the process:


Drawing basics (graphics and text): Steps your program must perform--

  1. Obtain a DC.

  2. Obtain (Create) and select GDI objects, if required (e.g., a pen).

  3. Set drawing attributes, if necessary.

  4. Call drawing functions.

  5. Deselect and Delete any GDI objects that were created.

  6. Release the DC -- in same state in which it was obtained.


hDC = GetDC(hWnd); --

   -Retrieves a handle to ad device context for the client area of the specified 
    window.

   -The device context is identified by the value returned in hDC.

   -This will be used for all subsequent drawing on that DC.

   -GetDC() allows the application to draw on the DC at any time.


Another function that gets a DC: BeginPaint(hWnd, &ps); --

   -This is only used in response to a WM_PAINT message--
      which is issued when some area of the window has been exposed and thus 
      needs to be repainted.

   -It returns a handle to a device context for the client area of the specified
    window.

   -And a pointer to a paint structure (PAINTSTRUCT).

   -This contains information about the area of the window that needs to be
    repainted:

      typedef struct tagPAINTSTRUCT
      {
       HDC     hdc;      /* device context handle */
       BOOL    fErase;   /* should background be redrawn? TRUE/FALSE */
       RECT    rcPaint;  /* rectangular area to update */
       BOOL    fRestore; /* reserved for internal use by Windows */
       BOOL    fIncUpdate;       /* reserved */
       BYTE    rgbReserved[16];  /* reserved */
      } PAINTSTRUCT;


ReleaseDC (hWnd, hDC); --

  -Each DC takes about 1000 bytes of memory; so we can only have a limited
   number of common DCs in memory at a time.

  -You must release the DC as soon as you've done your drawing.

  -If not, bad things can happen AFTER the application has been running a while.

  -It is used in conjunction with GetDC().

  -EndPaint() does the equivalent for a DC obtained with BeginPaint().


WINDOWS RGB COLOR MODEL--

Windows uses four-byte numbers to represent colors.

The simplest scheme is direct color in which a color is specified by giving a
COLORREF value:

typedef DWORD  COLORREF;

  ------------------------------------------------------------
 |       0      | Blue (0-255) | Green (0-255) | Red (0-255)  |
  ------------------------------------------------------------

If the most Significant Byte is zero:
   It means that direct RGB color is being used.
   The other bytes specify Red, Green, and Blue intensities.
   Dithering is done for colors that don't match the system colors.

If the most Significant Byte is not zero:
   The other bytes specify an index into a color lookup table (palette).
   The RGB values are found at that entry in the lookup table.
   This is indirect color.

The macro RGB() allows us to specify Red, Green Blue intensities.

It generates a COLORREF value (which can be used in color-setting functions) e.g.--

  COLORREF  cr;
  cr = RGB (0,0,255);   /* blue */

The definition of the macro RGB():

#define RBG(r,g,b)
   ((DWORD)(((BYTE)(r) | ((WORD)(g) << 8)) | (((DWORD)(BYTE(b)) << 16)))


Example use in a program--

  -SetTextColor(hDC,RGB(255,0,0))  // changes the text-drawing color to red.

  -SetBkColor(hDC,RGB(0,0,255)) changes the color of the background around
     each character to blue.


GDI Objects--pens, brushes, bitmaps, fonts

    -Used to "draw" or "paint" on a device context.

    -Created with functions like:

           CreatePen (PS_STYLE, nWidth, cColRef);

           CreateSolidBrush (cColRef);

    -There are lots more (see on-line help).

    -To be used they must be "selected into" the DC with SelectObject():

HGDIOBJ SelectObject(HDC, HGDIOBJ);
  HGDIOBJ--handle to a GDI object.
    Parameter--a handle to the new object being selected into the DC.
    Value returned--a handle to the object being displaced from the DC.

When selected, the data associated with the object is made available to the
DC for immediate use with graphics drawing functions:


Some GDI drawing functions--

[In each of the following (x1,y1), (x2,y2) are the coordinates of
 diagonally opposite corners of the bounding rectangle]

  Arc (hDC,x1,y1,x2,y2,xArcStart,yArcStart,xArcEnd,yArcEnd);
  [draws a portion of an ellipse with the current pen]

  Chord (hDC,x1,y1,x2,y2,xArcStart,yArcStart,xArcEnd,yArcEnd);
  [paints a filled portion of an ellipse bounded by ellipse border & line
   using the current brush to fill the area, current pen for border]

  Ellipse (hDc,x1,y1,x2,y2);
  [paints an ellipse with the current brush]

  MovetoEx (hDC,x1,y1,lpPoint);
  [establish position of start of line]

  LineTo (hDC,x1,y1);
  [draws a line from start point to the specified point with current pen]

  Pie (hDC,x1,y1,x2,y2,xArcStart,yArcStart,xArcEnd,yArcEnd);
  [paints a filled area of ellipse bounded by ellipse arc and a wedge
   using the current brush for area, current pen for border]

  Polygon (hDC,points_array,nCount);
  [paints a closed polygon using the current brush and pen]

  Polyline (hDC,points_array,nCount);
  [draws a series of one or more connected lines with the current pen]

  Rectangle (hDC,x1,y1,x2,y2);
  [paints a rectangle with the current brush and pen]

  SetPixel (hDC,x1,y1,colref);
  [Turns the pixel (point) at x1,y1 to the color specified by colref]

There are many more drawing primitives (see the on-line help).

When an object is selected, Windows allocates space for the object in the
program's data segment; this spaced is limited, so a program should delete the
object when it's done drawing with it--use DeleteObject().

If you don't, they will use up memory after the program that created them
has terminated! If there are too many undeleted objects, Windows could crash!

But to delete an object--

  -first, the object has to be selected out of the DC.

  -(trying to delete an object still selected into a DC could crash the
    program).

To do this, select the original object back into the DC--

  -This displaces former object.

  -Then the former object can be deleted.

A typical sequence with objects:

HPEN    hOldPen, hNewPen;
HDC     hDC;

hDC = GetDC(hWnd);                   /* Get a device context to draw on */

hNewPen = CreatePen(PS_SOLID, 3, RGB(0,0,99)); /* Create pen to draw with */

hOldPen = (HPEN)SelectObject(hDC, hNewPen);    /* Select new pen into DC ...*/
                                            /* & save handle to old one */
/* DO SOME DRAWING STUFF WITH THE PEN HERE */

SelectObject(hDC,hOldPen);                  /* Displace New Pen from DC */
                                            /* Now we can delete it */
DeleteObject(hNewPen);                      /* Delete the new pen */

ReleaseDC(hWnd,hDC);                        /* Get rid of device context */


Stock objects--predefined in Windows; obtain one with GetStockObject();

GetStockObject() retrieves a handle to a predefined stock pen/brush/font--

  -Stock objects are maintained by Windows.

  -They should not be deleted!

Example--

SelectObject (hDc, GetStockObject(BLACK_PEN));

Stock
Object Type    Choices
-----------------------------------------------------
Pen            BLACK_PEN, WHITE_PEN, NULL_PEN

Brush          DKGRAY_BRUSH, GRAY_BRUSH, BLACK_BRUSH, LTGRAY_BRUSH,
               NULL_BRUSH, WHITE_BRUSH

Font           ANSI_FIXED_FONT, ANSI_VAR_FONT, DEVICE_DEFAULT_FONT,
               OEM_FIXED_FONT, SYSTEM_FIXED_FONT, SYSTEM_FONT


ANSI_FIXED_FONT--useful for tables with characters that need to line up.

ANSI_VAR_FONT--smallest of the stock fonts (good for limited space).

An object can be displaced out of the DC for deletion by selecting a stock
object into the DC; i.e. replace the last 3 lines of code above with:

SelectObect(hDC, GetStockObject(BLACK_PEN));
DeleteObject(hNewPen);
ReleaseDC(hWnd, hDC);


THE WINAPP2.CPP APPLICATION MESSAGE PROCESSING: Details of WndProc()--

Our WINAPP2.CPP program code overrides the default window procedure action for
WM_COMMAND, WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_CHAR, and WM_DESTROY messages.

WM_COMMAND (menu item clicked)--

    LOWORD(wParam)==IDM_CIRCLE (User clicked on "Circle" menu item) ==>
    Get a device context, create a blue pen for the outline and a
    crosshatched magenta brush for the interior of the circle, select
    these objects into the device context, and call the Ellipse()
    function to draw the circle. After that, displace the pen and brush
    from the device context, delete them, and release the device context.

    LOWORD(wParam)==IDM_RECTANGLE (user clicked on "Rectangle" menu item) ==>
    Get a CD, create a red pen and a solid cyan brush, select them into
    the DC, call the Rectangle() function, select the objects out of the
    DC, delete them, and release the DC.

    LOWORD(wParam)==IDM_CLEAR (User clicked on "Clear Screen" menu item) ==>
    call InvalidateRect() which causes Windows to send a WM_PAINT message.
    This message means that the client area needs to be repainted. (It is
    also generated any time the window is exposed.) The Default Window
    Procedure responds to this message by repainting the client area with
    the class background brush, effectively erasing the window's client
    area.

    LOWORD(wParam)==IDM_QUIT (User clicked on "Quit") ==> program calls
    DestroyWindow(), which causes Windows to destroy the window.

WM_LBUTTONDOWN (Left mouse button pressed) ==>
   -Get x,y coordinates of cursor from lParam with LOWORD() & HIWORD() macros;
   -Get a device context to draw on;
   -Output the letter "L" at (x,y) on the device context with TextOut();
   -Release the device context.

WM_RBUTTONDOWN (Right mouse button pressed) ==>
   -Same as above, but outputs the letter "R".

WM_CHAR (A key or key combination was pressed that corresponds to an ANSI
         character) ==>
   -Get a device context to write on;
   -copy the character from the wParam into the string cBuf;
   -Output it to the upper left hand corner of the window's client area with
    TextOut();
   -Release the device context.

WM_DESTROY (User double clicked on System menu, close button, or hit Alt-F4) ==>
   -Post a WM_QUIT message to the application's queue
   -This will cause the program to exit the event loop and return to Windows
    as described in earlier notes.


USING MICROSOFT DEVELOPER STUDIO TO CREATE A VISUAL C++ APPLICATION--

The following is a recipe for preparing the WINAPP2 application visually--

1. Get into Developer Studio, open a New Workspace, and create a Win32 application.
   ("File | New | Projects tab | Win32 Application")
2. Open a new C++ file and type in the program or copy and paste it from clipboard:
   ("File | New | Files tab | C++ Source"); make sure "Add to Project" is checked
   and enter a name (e.g., winapp2). Type or paste in the resulting Edit window.
   Be sure to have the statement: #include "resource.h" which will include
   the resource constants that are to be defined by Developer Studio.
   (This replaces the statement #include "winapp2.h" in the manual version.)
3. Create the .rc file using: "File | New | Files tab | Resource Script"
   Give it the name winapp2.rc; make sure "Add to Project" is checked.
4. From the main menu select "Insert | Resource | Icon | New"
   This will bring up the icon editor. Draw the icon you want.
   When done, hit enter and, in the Icon Properties Dialog Box, type the ID:
   "MYICON" (MUST BE IN QUOTES) and the .ico file name you want to give it.
5. Select "Insert | Resource | Menu | New"
   This will bring up the menu editor.
   Double click in the dotted rectangle on the gray menu bar.
   In the resulting "Menu Item Properties" box, remove the Pop-up check mark
   and enter the ID: IDM_CIRCLE and the Caption &Circle.
   Do the same for the &Rectangle, Clear &Screen, and &Quit menu items.
   The IDs should be: IDM_RECTANGLE, IDM_CLEAR, and IDM_QUIT. 
6. After minimizing the Menu Editor box, you will see that the menu name is
   IDR_MENU1. Right click on that and click on "Properties" in the resulting
   dialog box. This will bring up a "Menu Properties" box". Change the ID: to
   "MYMENU" (Again it must be in quotes.)
7. Build the project.

If you haven't made any mistakes, your application should be ready to go. You
might want to take a look at what Developer Studio has done for you. Examine
the resource.h file and the winapp2.rc file. (Be sure to open the latter as a
text file ("File | Open" and in the resulting dialog box, choose winapp2.rc, 
change the Open As: "Auto" to "Text", and click "Open"). Don't be alarmed at 
all the extra junk that Developer Studio has put in this file.

As you read this, the process of creating an application visually may seem to
be very complex. But once you get used to the visual way of doing things, you
will find that it becomes very easy and intuitive!

MANUALLY SETTING UP ICON AND MENU RESOURCES FOR YOUR APP: 
A SOMETIMES QUICK AND DIRTY ALTERNATIVE--

-Use Developer Studio's Icon Editor to create and save the icon (.ico) file.
-Use any text editor to create and save the .h file with the constant definitions;
   e.g., enter lines like #define IDM_CIRCLE 40006.
-Use a text editor to create and save the .rc file with ICON and MENU definitions.
-Use a text editor to create the .cpp (or .c) source file.
   Both these files should #include the .h file.
-Open a New Workspace and create a Win32 Application.
-Copy the first four files into the resulting Workspace directory.
-Add the .rc and .cpp (or .c) files to the project.
   ("Project | Add to Project | Files").
-Build the project.

This is a sometimes fast way of doing things, but only useful for applications that
have very few resources. For most projects you'll want to develop your
resources visually using the various resource editors. Then Developer Studio
will automatically incorporate them into your project.