An Icon Editor

Creating, saving, and editing icons for graphics applications is a snap with Keith and Loren's icon editor and its support tools.


July 01, 1989
URL:http://www.drdobbs.com/architecture-and-design/an-icon-editor/184408167

JUL89: LINE-OF-BEST-FIT

William H. Murray and Chris H. Pappas are professors of Computer Studies at Broome Community College. Together they have written several books, including Presentation Manager Graphics: An Introduction (Osborne/McGraw-Hill, 1989) from which portions of this article were adapted. Bill and Chris can be reached at Broome Community College, Binghamton, NY 13902.


With consistent device-independent programming environments such as Microsoft Windows and OS/2 Presentation Manager, applications can be executed and results ported to any installed hardware driver accordant with Microsoft's specifications. This consistent environment frees you from the necessity of also developing software drivers for zillions (or at least hundreds) of printers, plotters, and graphics display) devices. As a matter of fact, the latest version of Windows (Version 3.0) brings the Windows' environment in much closer alignment with the OS/2 Presentation Manager (PM), making the task of porting programs between real and protected modes easier.

The price you pay for entering into this consistent user interface is a rather steep learning curve. In this article, we will concentrate on a PM program that plots points on the screen and draws a line between them with a linear least-squares-fit. A least-squares-fit works on the principle that data points, collected on a chart, are often related mathematically. Because of this relationship, the group of points takes on the appearance of a line or curve. In the case of a linear mathematical relationship between point values, a single line (called the line-of-best-fit) can be drawn between all points. The data points for this program will be plotted by moving the mouse to a specific region on the screen and clicking the left button. When all points are entered, a click of the right mouse button will end data entry and draw a line-of-best-fit. The equation for that line will be plotted at the bottom of the screen in slope-intercept form. Among other things, this example illustrates how the mouse and other graphics primitives can be utilized under the protected-mode environment.

If you are unfamiliar with Windows or the PM philosophy, we strongly recommend you read Herb Schildt's article, "A Presentation Manager Application Template," which appeared in the March 1989 issued of DDJ. Herb describes one of three Presentation Manager "foundations" -- the Cached-Presentation Space. (The other two are the Micro-Presentation Space and the Normal-Presentation Space.) Each type offers an increasingly greater array of available functions and unfortunately larger code sizes. For this reason, it will be easiest for you to learn simple PM programming with the Cached-Presentation Space, which is what we implemented in this article. From there you can move to the other two presentation spaces as projects demand.

To enter and execute the code you will need the PM (Version 1.1 or later) operating on a 80386/80286 computer. Additionally, you'll need the Microsoft C Compiler (Version 5.1 or later), the Microsoft OS/2 Software Developer's Kit, and a Microsoft mouse. The program for this article was developed on an IBM Model 80 with VGA display and 6 Mbytes of RAM.

The Sum of the Parts Equals the Whole

The program itself is called MFIT (for mouse fit). MFIT does not consist of a single program file, but a group of files that are eventually linked together. As a matter of fact, there are ten files that will be found on your disk after a successful compilation: MFIT, MFIT.DEF, MFIT.EXE, MFIT.H, MFIT.ICO, MFIT.OBJ, MFIT.PTR, MFIT.RC, MFIT.RES, and MFIT.C.

MFIT, shown in Listing One is used by the MAKE utility and is responsible for the compilation of the C code by the C compiler, the assembling of the resource code by the Resource Compiler, and the eventual linking of this code with the icon and pointer information.

MFIT.DEF (Listing Two) is the definition file for the Presentation Manager. Definition files are used by both Windows and the Presentation Manager. The DEF file is responsible for establishing the heap and stack sizes, identifying the operating mode (real or protected), and naming exports. For our example, exports are the individual procedures used in the C program.

MFIT.H is a header file for unique identification numbers that will be used by the resource file and the C program. Listing Three shows a list of #define statements. Usually the #define instruction is followed by an ID name, followed by a unique integer constant. The names and the constants are your choice, but careful planning will sometimes eliminate later problems, such as the insertion of additional ID numbers.

MFIT.ICO is a file containing the shape of the minimum icon. The minimum icon is displayed at the bottom of the screen if that sizing option is chosen from the main menu of the PM. The icon is drawn with the use of the Icon Editor, which is part of the Microsoft Toolkit. (Because MFIT.ICO is not a text file that can be listed, it is only available on an optional diskette or on line.)

MFIT.PTR is a file containing the shape of the screen pointer. The screen pointer, by default, is an arrow. The user has the option of using the Icon Editor to draw a new pointer. This pointer will be present in the application window and can be used to make selections. In this case, a miniature line chart is used as the pointer. When the left button is pushed (in the example program) a small cross symbol will be plotted on the screen at the spot pointed to by the pointer. Again, the file returned by the Icon Editor is not a text file.

MFIT.RC is the resource file needed for describing the Menu, About Box, and Dialog Box and is shown in Listing Four. The resource file is a can of worms in itself. First, the About and Dialog Boxes are created with the Dialog Box Editor provided with the Microsoft Toolkit. Once created, the information is saved in a file with the extension .RES, which is not a text file. A text file version can be selected. We used the .DLG extension for this option. Our experience is that the text file is important for touching up the Dialog Box information. The catch is that this .DLG file cannot be read by the Dialog Box Editor. You will not have that problem with this example, because the placement of the Dialog Box and all controls have already been worked out. You will simply have to type the file MFIT.RC. The MAKE utility will compile that file into a .RES file and link it to the final executable program. The About Box is shown in Figure 1 and the Dialog Box in Figure 2.

MFIT.C is the C program proper. Listing Five contains all the necessary PM overhead required to establish and communicate with an application program. Specifically, two procedures or functions are required to process the Dialog Box messages for the About and LineDiaProc Dialog Boxes. A final procedure, GraphicProc, handles all other messages for the mouse position, buttons, and graphics routines.

Power in the Loop

PM and Windows use the concept of a message loop or queue for receiving and sending information throughout an application program. (Establishing this queue is the job of the main( ) function as described in Schildt's article.) Once the message queue is established, it is your responsibility to tap into the queue with your requests. Our program will use several procedures or functions. One procedure will help establish an About Box. In PM and Windows, the About Box is used to transmit information regarding the program. This usually consists of one or more of the following: the program name, a short description of the program, the developer's name(s), and the copyright date. The About Box is usually selected (as an item) from a program menu. Another procedure will establish a Dialog Box. Dialog Boxes, also selected as items from a user defined menu, establish a sophisticated means of data input. They go beyond the simple buttons and check boxes of menus and allow the user to enter string or numeric data from the keyboard. The Dialog Box in our program will allow the entry of a title, x axis and y axis labels, and maximum integer values for both the x and y axes. Finally, a third procedure will establish the "core" of our current application. It is within this procedure that mouse information is collected on coordinate positions and button status. It should be pointed out that only integers can be passed through the message queue. This does not mean that your program cannot use real numbers, it just means that you cannot pass them through the queue.

There is a similar structure for intercepting messages in the LineDiaProc. This Dialog Box is created if case IDM_INPUT is true during the processing of the WM_COMMAND function in the procedure. This is the Dialog Box that queries the user for labels and plotting ranges. In this case, information will only be returned from the Dialog Box when the "Okay" button is selected. If the "Cancel" button is selected, the Dialog Box will be destroyed, but no new information will be intercepted. The functions WinQueryDlgItemText and WinQueryDlgItemShort return the string information for the labels and the integer information for the plotting ranges, respectively. In both cases, the final outcome of selecting either push button is to process the message, destroy the Dialog Box, and return to the application program.

Processing the GraphicProc Information

There are seven switch-case statements in this procedure for processing message information: WM_CREATE, WM_BUTTON1DOWN, WM_MOUSEMOVE, WM_BUTTON2DOWN, WM_SIZE, WM_COMMAND, and WM_PAINT. It is within these cases that data will be collected and processed.

You have already seen that WM_COMMAND is responsible for establishing one of the two Dialog Boxes when requested. WM_CREATE will replace the default arrow pointer with the pointer created in the Icon Editor by calling Win LoadPointer. This pointer tracks the current mouse position with WM_MOUSEMOVE. In WM_MOUSEMOVE, the WinSetPointer function places the pointer in the window. WM_SIZE processes the request for the mouse menu. The mouse menu allows the selection of the two Dialog Box options mentioned earlier.

WM_BUTTON1DOWN and WM_BUTTON2DOWN report on the status of the two mouse buttons. If the left button is pushed, a marker is placed on the screen at the current mouse location. Additionally, if the left button is pushed, that coordinate information is also placed in two global arrays: ptxorg[] and ptyorg[]. This coordinate information will be used when plotting the line-of-best-fit between the data points. Note that during WM_BUTTON1DOWN, GpiSetMarker is used to draw a system marker symbol. GpiSetColor sets the symbol drawing color to blue. Mouse message data is returned through the parm1 parameter, which is of type MPARAM. MPARAM is a pointer to this additional message information. Up to 100 points can be collected in this manner. When all data points have been entered, the user can push the right mouse button to generate a line of best fit and the corresponding slope-intercept equation.

WM_BUTTON2DOWN uses the information returned to the global arrays, ptxorg[] and ptyorg[], to draw the line. First, however, this information is processed and scaled. When WM_BUTTON2DOWN is called, the slope and intercept of the line are used to establish a beginning and ending point on the graph. When the starting point is found, the GpiMove function places the cursor at that position. The GpiLine function then draws a red line between that point and the final point on the screen. Finally, the equation is drawn to the screen with a series of GpiBox, GpiColor, GpiMove, and GpiCharString function calls.

Numerous lines-of-best-fit can be plotted on the same graph. Simply plot a group of points and request a line, then plot another group and request another line. The screen can be cleared by selecting the mouse data Dialog Box and selecting either push button option.

WM_PAINT is responsible for setting the background color, drawing the coordinate axes, plotting tic marks, setting the character mode for drawing text to the screen, and plotting axis labels. Lines are drawn by moving the cursor to the starting point of the line with the GpiMove function. The coordinates (ptl.x and ptl.y) are of PM type POINTL. GpiLine specifies the ending point. The axes and tic marks are drawn with just these two functions. The GpiCbarStringAt function is used to draw the various labels. GpiCharStringAt requires that a handle, coordinate position, length of string parameter, and the actual label be provided for the function. If you examine the listing, you will see how this is done. It should be noted that for the default drawing mode, used by PM, the screen coordinates start at the lower-left portion of the screen (0,0) and extend to the upper-right portion of the screen. For the VGA monitor the upper-right coordinates are (639,479).

Calculating the Line-of-Best-Fit

When WM_BUTTON1DOWN has completed the process of filling the ptxorg[] and ptyorg[] arrays, it will be possible to calculate the line-of-best-fit. Most of the calculations take place in the GraphicProc procedure, immediately after the data declarations. Two groups of arrays are used: ptxorg[] and ptyorg[] along with ptxscaled[] and ptyscaled[]. The arrays ptxorg[] and ptyorg[] are used to determine the slope-intercept data for the line that will be drawn on the screen. These contain the screen coordinate values returned by the mouse. However, because this information is biased by the starting, ending, and scaling of the screen, it must be adjusted in order to provide "real world" values for the line-of-best-fit equation that will also be printed to the screen. Thus, ptxscaled[] and ptyscaled[] are arrays that hold the unbiased values. The screen window for gathering points varies from (100,100) to (500,400).

Two standard equations (shown in Figure 3) are used for the linear curve fitting program. They are derived from the calculus and give the slope and intercept of the final line -- the line-of-best-fit. More information on general curve fitting can be found in Numerical Recipes in C (Press, Flannery, Teukolsky, and Vetterling, Cambridge University Press, 1988.)

In these equations, n represents the number of data points with each x[i] and y[i] representing the individual x and y values for each data point. If you study the code under the comment "/* Calculate values for screen location */" you will notice that two numerators, num1 and num2, are calculated along with one denominator, deno2. If num1 is divided by deno2 the intercept will be calculated. If num2 is divided by deno2, the slope of the line is calculated. This is done under the call to WM_BUTTON2DOWN. A similar technique is used for the array of scaled data points.

Running the Program

If you have entered the code and compiled the program give it a try. Make sure you have a Microsoft mouse or equivalent connected and installed. Experiment with several lines. Figure 4 shows one line-fitting example.

The program suffers from two weaknesses. First, it only allows you to plot points in the first quadrant. Second, it will fail if a vertical line (an infinite slope) is requested. Both problems are relatively easy to solve. Plotting in all four quadrants can be achieved by remapping the axes and adjusting the scaling factors. The vertical line problem is even easier -- simply use a case statement to capture the request and then use the GpiLine function to draw a vertical line at the current x position.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063; or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

_LINE-OF-BEST-FIT_ by William Murray and Chris Pappas

[LISTING ONE]




mfit17.obj : mfit17.c mfit17.h
CL /c /Lp /Zp /Od /G2sw /W2 mfit17.c

mfit17.res : mfit17.rc mfit17.ico mfit17.h mfit17.ptr
RC -r mfit17

mfit17.exe : mfit17.obj mfit17.def mfit17.res
link mfit17, /align:16, NUL, OS2, mfit17
rc mfit17.res



[LISTING TWO]


;MFIT17.DEF for C Compiling

NAME          mfit17
EXPORTS        About
EXPORTS        LineDiaProc
EXPORTS        GraphicProc
PROTMODE
HEAPSIZE       2048
STACKSIZE      9216




[LISTING THREE]


#define ID_RESOURCE 10
#define ID_ABOUT    15
#define ID_OK       20
#define ID_CANCEL   25
#define ID_INPUT    30

#define IDM_ABOUT    40
#define IDM_INPUT    45
#define IDM_LINEINPUT 55

#define DM_XAXIS 470
#define DM_YAXIS 475
#define DM_TITLE 480
#define DM_X1  485
#define DM_Y1  490




[LISTING FOUR]


#include <os2.h>
#include "mfit17.h"

POINTER ID_RESOURCE mfit17.ico
POINTER IDP_POINTER mfit17.ptr

MENU            ID_RESOURCE
BEGIN
  SUBMENU "Mouse_Data",IDM_LINEINPUT
  BEGIN
    MENUITEM "About...",   IDM_ABOUT
    MENUITEM "Program Data...", IDM_INPUT
  END
END

DLGTEMPLATE         ID_ABOUT
BEGIN
  DIALOG "",ID_ABOUT,50,300,180,80,FS_DLGBORDER
  BEGIN
    CTEXT "Mouse Data Input Program",-1,2,60,176,10
    CTEXT "by William H. Murray & Chris H. Pappas",-1,2,45,
     176,10
    CONTROL "OK",ID_OK,75,10,32,14,WC_BUTTON,BS_PUSHBUTTON
       |BS_DEFAULT|WS_GROUP|WS_TABSTOP|WS_VISIBLE
  END
END

DLGTEMPLATE ID_INPUT LOADONCALL MOVEABLE DISCARDABLE
BEGIN
    DIALOG "Mouse Program Information",ID_INPUT,76,247,148,193,
      FS_NOBYTEALIGN|FS_DLGBORDER|WS_VISIBLE|
      WS_CLIPSIBLINGS|WS_SAVEBITS,FCF_TITLEBAR
    BEGIN
   CONTROL "Mouse Program Labels",257,2,92,145,72,WC_STATIC,
      SS_GROUPBOX|WS_GROUP|WS_VISIBLE
   CONTROL "X & Y Maximums",259,2,42,145,42,WC_STATIC,
      SS_GROUPBOX|WS_GROUP|WS_VISIBLE
   CONTROL "Enter Title:",260,5,133,51,8,WC_STATIC,SS_TEXT
      |DT_LEFT|DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "     x-axis:",261,5,118,54,8,WC_STATIC,SS_TEXT
      |DT_LEFT|DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "     y-axis:",262,5,102,50,8,WC_STATIC,SS_TEXT
      |DT_LEFT|DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "Mouse Data Points",DM_TITLE,54,132,89,13,
      WC_ENTRYFIELD,ES_LEFT|ES_AUTOSCROLL|ES_MARGIN
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "x - axis label",DM_XAXIS,54,117,89,13,
      WC_ENTRYFIELD,ES_LEFT|ES_AUTOSCROLL|ES_MARGIN
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "y - axis label",DM_YAXIS,54,101,89,13,
      WC_ENTRYFIELD,ES_LEFT|ES_AUTOSCROLL|ES_MARGIN
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "   x:",273,25,57,20,8,WC_STATIC,SS_TEXT|DT_LEFT|
      DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "   y:",283,75,57,20,8,WC_STATIC,SS_TEXT|DT_LEFT|
      DT_TOP|WS_GROUP|WS_VISIBLE
   CONTROL "500",DM_X1,50,57,20,15,WC_ENTRYFIELD,ES_LEFT
      |ES_AUTOSCROLL|ES_MARGIN|WS_TABSTOP|WS_VISIBLE
   CONTROL "400",DM_Y1,100,57,20,15,WC_ENTRYFIELD,ES_LEFT
      |ES_AUTOSCROLL|ES_MARGIN|WS_TABSTOP|WS_VISIBLE
   CONTROL "OK",ID_OK,17,13,24,14,WC_BUTTON,BS_PUSHBUTTON
      |WS_TABSTOP|WS_VISIBLE
   CONTROL "Cancel",ID_CANCEL,101,13,34,14,WC_BUTTON,
      BS_PUSHBUTTON|WS_TABSTOP|WS_VISIBLE
    END
END




[LISTING FIVE]



/* Cached-PS Window Platform for PM Graphics      */
/* (c) William H. Murray and Chris H. Pappas, 1989 */

#define INCL_PM

#include <os2.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mfit17.h"

MRESULT EXPENTRY About(HWND,USHORT,MPARAM,MPARAM);
MRESULT EXPENTRY LineDiaProc(HWND,USHORT,MPARAM,MPARAM);
MRESULT EXPENTRY GraphicProc(HWND,USHORT,MPARAM,MPARAM);

#define maxpts 100

HPS   hps;
HWND  hfrm,hcnt;
QMSG  qmsg;
long  ptxorg[maxpts];
long  ptyorg[maxpts];
long  xaxismax=500;
long  yaxismax=400;
char  title[80]="Mouse Data Points";
char  xstring[80]="x - axis label";
char  ystring[80]="y - axis label";
POINTL mousepos;

main ()
  {
  static CHAR pszClassName[]="FirstClass";
  HAB   hab;
  HMQ   hmq;
  HDC   hdc;
  SIZEL page;
  ULONG flFrameFlags=FCF_SYSMENU|FCF_TITLEBAR|
           FCF_SIZEBORDER|FCF_MINMAX|
           FCF_ICON|FCF_MENU;
  ULONG flFrameStyle=WS_VISIBLE;

  hab=WinInitialize(0);
  hmq=WinCreateMsgQueue(hab,0);
  WinRegisterClass(hab,pszClassName,GraphicProc,
         CS_SIZEREDRAW,0);
  hfrm=WinCreateStdWindow(HWND_DESKTOP,flFrameStyle,
          &flFrameFlags,pszClassName,
          "Cached-PS PM Graphics",
          0L,NULL,ID_RESOURCE,&hcnt);
  hdc=WinOpenWindowDC(hcnt);
  hps=GpiCreatePS(hab,hdc,&page,PU_PELS|GPIA_ASSOC);
  WinSetWindowPos(hfrm,NULL,10,10,610,470,SWP_SIZE|
          SWP_MOVE|SWP_SHOW);
  while (WinGetMsg(hab,&qmsg,NULL,0,0))
    WinDispatchMsg(hab,&qmsg);
  WinDestroyWindow(hfrm);
  WinDestroyMsgQueue(hmq);
  WinTerminate(hab);
  return 0;
  }

MRESULT EXPENTRY About(hwnd,messg,parm1,parm2)
  HWND    hwnd;
  USHORT messg;
  MPARAM parm1,parm2;
  {
  switch (messg)
    {
    case WM_COMMAND:
      switch (LOUSHORT(parm1))
   {
   case ID_OK:
     WinDismissDlg(hwnd,TRUE);
     return 0;
   }
   break;
    }
    return (WinDefDlgProc(hwnd,messg,parm1,parm2));
  }

MRESULT EXPENTRY LineDiaProc(hwnd,messg,parm1,parm2)
  HWND hwnd;
  USHORT messg;
  MPARAM parm1;
  MPARAM parm2;
  {
  short X1;
  short Y1;
    switch (messg)
      {
      case WM_COMMAND:
   switch (LOUSHORT (parm1))
     {
     case ID_OK:
       WinQueryDlgItemText(hwnd,
            DM_TITLE,
            80,
            title);
       WinQueryDlgItemText(hwnd,
            DM_XAXIS,
            80,
            xstring);
       WinQueryDlgItemText(hwnd,
            DM_YAXIS,
            80,
            ystring);
       WinQueryDlgItemShort(hwnd,
            DM_X1,
            &X1,
            1);
       WinQueryDlgItemShort(hwnd,
            DM_Y1,
            &Y1,
            1);
       xaxismax=(long) X1;
       yaxismax=(long) Y1;
       WinDismissDlg(hwnd,TRUE);
       return 0;

     case ID_CANCEL:
       WinDismissDlg(hwnd,TRUE);
       return 0;
     }
     break;
      }
  return WinDefDlgProc (hwnd,messg,parm1,parm2);
  }

MRESULT EXPENTRY GraphicProc(hwnd,messg,parm1,parm2)
  HWND       hwnd;
  USHORT    messg;
  MPARAM    parm1,parm2;
  {
  static    LONG    ColorDataInfo[]={CLR_NEUTRAL,
                 RGB_BLACK,
                 CLR_BACKGROUND,
                 RGB_WHITE};
  static    HPOINTER linePtr;
  static    HWND     hmenu;
  HPS       hgpi;
  HDC       hdc;
  POINTL    ptl;
  GRADIENTL gradl;
  RECTL     rcl;
  int       i,alength,blength;
  int       lenxstring,lenystring,lentitle,
       lenmaxxlabel,lenmaxylabel;
  long       ptxmax,ptymax,sumx1,sumy1,sumxy1,
       sumxsqr1,deno1,numa1,numb1,
       sumx2,sumy2,sumxy2,sumxsqr2,
       deno2,numa2,numb2;
  long       ptxscaled[maxpts],ptyscaled[maxpts],maxx;
  char       maxxlabel[4],maxylabel[4],
       astring[10],bstring[10];
  double    a1,b1,a2,b2,y;
  static    int j=0;
  static    int npts=0;

  /* Length of various strings */
  lentitle=strlen(title);
  lenxstring=strlen(xstring);
  lenystring=strlen(ystring);

  /* Convert maximum x value to a string */
  itoa((int)xaxismax,maxxlabel,10);
  lenmaxxlabel=strlen(maxxlabel);

  /* Convert maximum y value to a string */
  itoa((int)yaxismax,maxylabel,10);
  lenmaxylabel=strlen(maxylabel);

  /* Scale all x values in original array. */
    for (i=0;i<npts;i++)
    ptxscaled[i]=((ptxorg[i]-100)*xaxismax/400);

  /* Scale all y values in original array. */
    for (i=0;i<npts;i++)
    ptyscaled[i]=((ptyorg[i]-100)*yaxismax/300);

  /* Calculate values for screen equation */
  sumxsqr1=0;
  sumx1=0;
  sumy1=0;
  sumxy1=0;
  for (i=0;i<npts;i++)
  {
    sumxsqr1+=ptxscaled[i]*ptxscaled[i];
    sumx1+=ptxscaled[i];
    sumy1+=ptyscaled[i];
    sumxy1+=ptxscaled[i]*ptyscaled[i];
  }
  deno1=(npts*sumxsqr1)-(sumx1*sumx1);
  numa1=(sumxsqr1*sumy1)-(sumx1*sumxy1);
  numb1=(npts*sumxy1)-(sumx1*sumy1);

  /* Calculate values for drawing line on screen */
  sumxsqr2=0;
  sumx2=0;
  sumy2=0;
  sumxy2=0;
  for (i=0;i<npts;i++)
  {
    sumxsqr2+=ptxorg[i]*ptxorg[i];
    sumx2+=ptxorg[i];
    sumy2+=ptyorg[i];
    sumxy2+=ptxorg[i]*ptyorg[i];
  }
  deno2=(npts*sumxsqr2)-(sumx2*sumx2);
  numa2=(sumxsqr2*sumy2)-(sumx2*sumxy2);
  numb2=(npts*sumxy2)-(sumx2*sumy2);

  switch (messg)
    {

    case WM_CREATE:
      linePtr=WinLoadPointer(HWND_DESKTOP,
              NULL,IDP_POINTER);
      return 0;

    case WM_BUTTON1DOWN:
      GpiSetMarker(hps,MARKSYM_PLUS);
      GpiSetColor(hps,CLR_BLUE);
      mousepos.x=(LONG) LOUSHORT(parm1);
      mousepos.y=(LONG) HIUSHORT(parm1);
      if(mousepos.x>99 & mousepos.x<500 &
    mousepos.y>99 & mousepos.y<400)
    {
    ptxorg[j]=mousepos.x;
    ptyorg[j]=mousepos.y;
    j++;
    npts=j;
    GpiMarker(hps,&mousepos);
    }
      return TRUE;

    case WM_MOUSEMOVE:
      WinSetPointer(HWND_DESKTOP,linePtr);
      return 0;

    case WM_BUTTON2DOWN:
      GpiSetColor(hps,CLR_RED);
      j=0;         /*reset pointer */
      maxx=0;
      /* Slope and intercept for org. and scaled */
      a1=(double) numa1/deno1;
      b1=(double) numb1/deno1;
      a2=(double) numa2/deno2;
      b2=(double) numb2/deno2;

      /* Starting point for line of best fit */
      for (i=99;i<510;i++)
   {
     y=a2+(b2*i);
     if(y>99.0 && y<410.0)
     {
       ptl.x=i;
       ptl.y=(long) y;
       break;
     }
   }
      GpiMove(hps,&ptl);

      /* Ending point for line of best fit */
      for (i=510;i>99;i--)
   {
     y=a2+(b2*i);
     if(y>99.0 && y<410.0)
     {
       ptl.x=i;
       ptl.y=(long) y;
       break;
     }
   }
      GpiLine(hps,&ptl);

    /* Draw the equation to the screen. */
      GpiSetColor(hps,CLR_WHITE);
      ptl.x=100;
      ptl.y=80;
      GpiMove(hps,&ptl);
      ptl.x=500;
      ptl.y=55;
      GpiBox(hps,DRO_FILL,&ptl,0L,0L);
      GpiSetColor(hps,CLR_RED);

      gcvt(a1,7,astring);
      alength=strlen(astring);
      gcvt(b1,7,bstring);
      blength=strlen(bstring);
      ptl.x=300-(LONG)(alength+blength+8)*4;
      ptl.y=62;
      GpiMove(hps,&ptl);
      GpiCharString(hps,4L,"y = ");
      GpiCharString(hps,(long) alength,astring);
      GpiCharString(hps,3L," + ");
      GpiCharString(hps,(long) blength,bstring);
      GpiCharString(hps,3L,"(x)");
      return TRUE;

    case WM_SIZE:
      if (hmenu==NULL)
   hmenu=WinWindowFromID(WinQueryWindow(hwnd,
               QW_PARENT,FALSE),FID_MENU);
      return 0;

    case WM_COMMAND:
      {
      switch (COMMANDMSG(&messg)->cmd)
   {
   case IDM_ABOUT:
     if (WinDlgBox(HWND_DESKTOP,hwnd,About,
         NULL,ID_ABOUT,NULL))
        WinInvalidateRect(hwnd,NULL,FALSE);
   return 0;

   case IDM_INPUT:
     if (WinDlgBox(HWND_DESKTOP,hwnd,LineDiaProc,
         NULL,ID_INPUT,NULL))
         WinInvalidateRect(hwnd,NULL,FALSE);
     return 0;

   return 0;
   }
      }
    break;

    case WM_PAINT:
    hgpi=WinBeginPaint(hwnd,NULL,NULL);
    GpiCreateLogColorTable(hgpi,LCOL_RESET,
    LCOLF_INDRGB,0L,4L,ColorDataInfo);
    GpiErase(hgpi);

/*--------- your routines below ----------*/

    GpiSetColor(hgpi,CLR_BLACK);

    /* Draw X and Y coordinate axis */
    ptl.x=99;
    ptl.y=410;
    GpiMove(hgpi,&ptl);
    ptl.y=99;
    GpiLine(hgpi,&ptl);
    ptl.x=510;
    GpiLine(hgpi,&ptl);

    /* Draw Y axis tic marks */
    ptl.y=130;
    for (i=0;i<10;i++)
      {
      ptl.x=95;
      GpiMove(hgpi,&ptl);
      ptl.x=99;
      GpiLine(hgpi,&ptl);
      ptl.y+=30;
      }

    /* Draw X axis tic marks */
    ptl.x=140;
    for (i=0;i<10;i++)
      {
      ptl.y=99;
      GpiMove(hgpi,&ptl);
      ptl.y=95;
      GpiLine(hgpi,&ptl);
      ptl.x+=40;
      }

    GpiSetCharMode(hgpi,CM_MODE3);

    /* Center and print line chart title */
    ptl.y=410;
    ptl.x=300-((LONG) (lentitle/2)*6);
    GpiCharStringAt(hgpi,&ptl,(LONG) lentitle,title);

    /* Center and print horizontal axis label */
    ptl.y=40;
    ptl.x=300-((LONG) (lenxstring/2)*6);
    GpiCharStringAt(hgpi,&ptl,(LONG) lenxstring,
          xstring);

    /* Print horizontal axis maximum value */
    ptl.y=82;
    ptl.x=490;
    GpiCharStringAt(hgpi,&ptl,(long) lenmaxxlabel,
          maxxlabel);

    /* Print vertical axis maximum value */
    ptl.y=400;
    ptl.x=70;
    GpiCharStringAt(hgpi,&ptl,(long) lenmaxylabel,
          maxylabel);

    /* Center and print vertical axis label */
    ptl.y=240-((LONG) (lenystring/2)*6);
    ptl.x=70;
    gradl.x=0;
    gradl.y=90;
    GpiSetCharAngle(hgpi,&gradl);
    GpiCharStringAt(hgpi,&ptl,(LONG) lenystring,
          ystring);

    /*--------- your routines above ----------*/

    WinEndPaint(hgpi);
    break;

    default:
      return WinDefWindowProc(hwnd,messg,parm1,parm2);
    }
  return 0;
  }








Copyright © 1989, Dr. Dobb's Journal

Figure 1


Copyright © 1989, Dr. Dobb's Journal

Figure 2


Copyright © 1989, Dr. Dobb's Journal

Figure 3


Copyright © 1989, Dr. Dobb's Journal

Figure 4


Copyright © 1989, Dr. Dobb's Journal

Figure 1


Copyright © 1989, Dr. Dobb's Journal

Figure 2


Copyright © 1989, Dr. Dobb's Journal

Figure 3


Copyright © 1989, Dr. Dobb's Journal

Figure 3


Copyright © 1989, Dr. Dobb's Journal

Keith and Loren are the authors of Power Graphics Using Turbo C published by John Wiley & Sons. They can be reached at 3120 E. Paradise Ln., Suite 12, Phoenix, AZ 85032 or through CompuServe at 72561, 1536.


George Orwell, the great author and futurist, was born forty years too early to be a hacker, but he had a lot to say about languages. In an essay he published in 1946 entitled "Politics and the English Language," he introduced his famous principle of optimization: "Never use a long word where a short one will do." Unfortunately, George's crystal ball wasn't fine-tuned enough to predict the Macintosh, Windows, and Presentation Manager explosion. If it were, he could have taken credit for the rule that all serious user interface designers have posted by their machines: "Never use a word where a picture will do."

Indeed, these little pictures, commonly called icons, have invaded our lives. They're on the streets, in the courts, and now they're on our screens. And if you've ever used a visually oriented application, such as a painting program, you probably have already discovered that clicking on a picture of a line in order to draw a line is much easier than having to type a command that you can't remember.

In this article, we'll show how to develop an icon editor in Turbo C called "ICONED." With this editor you can use a mouse to create, save, and edit icons. The icons that you design can be read from files and then displayed in your applications to enhance your user interfaces. Our application has two parts: The main program, which contains the code for the icon editor (see Listing One), and a set of tools that are required to support the mouse (see Listings Two and Three).

Supporting Graphics Devices

In the past, the process of writing a graphics-based program for the PC was about as frustrating as cleaning a swimming pool in a dust storm. As soon as you got your eye-catching program up and running, another graphics standard would be introduced. Fortunately, compiler vendors such as Microsoft and Borland now provide device-independent graphics tools with their language products. This means that you can write a program that works with all of the major graphics adapters including the CGA, EGA, and VGA.

For this article, we decided to use Turbo C because of its flexible BGI (Borland Graphics Interface). (After making some minor modifications to the code, you could also compile it with Microsoft's QuickC.) The BGI tools provided with Turbo C (and with all of the other Turbo languages) is a useful graphics package because it lets you use a variety of PC graphics hardware devices, from CGA to VGA, to perform high-and low-level graphics. More than 70 graphics routines are provided for performing tasks that range from detecting graphics adapters to drawing and filling polygons.

Getting to Work

The BGI provides all of the high-level graphics support tools that are needed in order to create the icon editor. To support the icon editor, a graphics adapter supported by the BGI plus a Microsoft-compatible mouse are required. Mouse support is handled by some of the functions that we present in this article. These functions initialize the mouse, read the mouse position, and hide and display the mouse cursor. (We won't spend a lot of time discussing how the mouse works. For more information about the mouse, see the references at the end of this article.)

A sample of the type of icons generated by the icon program is shown in Figure 1. The functions used in the main program are listed and briefly described in Table 1. Essentially, these functions fall into two categories: "graphics screen processing" and "file processing." The graphics screen processing functions, such as init_bigbit( ) and init_graphics( ), control how icon images are displayed. The file processing functions, such as read_icon( ) and save_icon( ), are used to read and save icon images. The mouse support functions are listed in Table 2.

Table 1: Functions used in the icon editor

  Function Name            Description
  ------------------------------------------------------------------------

  draw_enlarged_icon()     Draws an enlarged view of an icon
  init_bigbit()            Initializes a single big icon bit
  init_graphics()          Initializes the graphics hardware
  read_icon()              Reads an icon file
  save_icon()              Saves an icon image to a file
  show_icon()              Displays the icon pattern stored in the icon array
  toggle_bigbit()          Turns a big bit on or off
  toggle_icons_bit()       Turns a standard size icon bit on or off

Table 2: Mouse support functions

  Function Name          Description
  -----------------------------------------------------------------

  mouse()                 The mouse interface function
  initmouse()             Initializes the mouse
  showmouse()             Displays the mouse cursor
  hidemouse()             Hides the mouse cursor
  waitforinput()          Reads a mouse or keyboard input
  testbutton()            Tests the state of one of the mouse buttons
  getmousecoords()        Obtains the coordinates of the mouse cursor

The Icon Editor in Action

The icon editor is shown in Figure 2. Notice that two primary regions are displayed. The left side of the screen shows an enlarged representation of the icon that is being edited. The editing process is performed within this window. The right side of the screen displays the icon pattern in the pattern's actual size while the pattern is being edited. The current state of the icon is updated after every mouse action.

To edit an icon, simply click the mouse on any of the big icon bits in the enlarged icon window. Each time you click the mouse on a big pixel, the pixel's state is toggled either ON or OFF. To exit the editing process and save or discard your work, select the Esc key. If the edited icon is saved, then it can be read in later and edited again.

Representing Icons

Icons can be represented in many different ways. Our goal is to develop icons that look good when displayed with the different graphics modes supported by the BGI. An icon drawn in one mode may appear quite different in another mode because of that mode's different screen resolution and aspect ratio.

We've standardized the size of our icons at 16-by-16 pixels. This size works well in most cases (at least in the standard graphics modes supported by the CGA, EGA, and VGA adapters). Internally, an icon pattern is represented in the editor as a two-dimensional array.

Each location in the icon array corresponds to a pixel setting in the icon pattern. Because our icons are displayed in a single color, they can be easily represented by storing the values 1 or 0 in each location of the icon array. If the stored value is 1, then the corresponding icon pixel is displayed. If the stored value is 0, then the pixel is set to the current background color. (If you wanted to store color attributes, you could modify the icon array data structure.)

Saving Icons

As each icon is created, it is saved in its own file. This file consists of a header and a body. The header is a single line that specifies the icon's width and height. Because the size of each newly created icon is 16-by-16 pixels, each icon's header will always start with those two numbers. (This feature lets you easily modify the program to work with icons of different sizes.) The remainder of the file contains the icon pattern, which is organized in a row and column format. A 16-by-16 icon has 16 numbers per line where each pixel of the icon image is represented by a 0 or a 1. Figure 3 shows a sample icon and Figure 4 shows the file that stores the icon shown in Figure 3. Compare the two figures and note the one-to-one correspondence between each pixel set in the icon and each value of 1 in the file.

Figure 4: The contents of the file that make up the icon shown in Figure 3.

   16 16
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
  0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0
  0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
  0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0
  0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
  0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0
  0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0
  0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0
  0 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0
  0 0 1 1 0 0 1 0 0 0 1 1 0 0 0 0
  0 0 1 1 1 1 0 0 0 1 0 0 1 0 0 0
  0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0
  0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0
  0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

The function used to save an icon image is save_icon(). To simplify user input, save_icon() is designed to be invoked while the program is in text mode. The process of saving an icon involves several steps. First, save_icon() prompts you to enter the name of the file where the icon is to be stored. Next, fopen() is called to open and initialize the file. Finally, a nested for loop writes the icon pattern stored in the array icon to the file and calls fclose() to close the file.

Reading an Icon File

The process of reading an icon file into the icon editor is similar to the process of writing a file. An icon file is read into the icon editor by the function read_icon(), which is designed to run in text mode in order to simplify text input.

read_icon() first initializes the icon pattern so that the pattern contains all zeros. (This step ensures that the icon pattern starts as a "blank" pattern.) read_icon() then asks if you wish to edit an existing icon file. If your response is affirmative (you type any character except the letter N), read_icon() requests the name of the icon file. Remember that during this process, the data entry operations are performed in text mode.

Once the filename is specified, read_icon() attempts to open the corresponding file. If that operation succeeds, then the first line of the file, which contains the header, is read. If the first line consists of two values that are equivalent to ICONWIDTH and ICONHEIGHT, read_icon() continues. If any other numbers are found, then the file is invalid and read_icon() terminates by returning 0.

The icon pattern is read from the file into the array icon by two for loops (like those used in save_icon(), which write the icon data to a file. In this case, the forloops use fscanf() to read the icon pattern from the file. Once the icon pattern has been read into the array icon, the pattern can be displayed.

The Core of the Icon Editor

Now that we've discussed the process of representing icons and saving them in external files, let's jump in and explore the main program. The first ten statements in ICONED (Listing One) initialize the various components of the icon editor. The process of initialization consists of reading an icon file, initializing the graphics mode, initializing the mouse, generating the enlarged icon pattern, and displaying the initial state of the icon pattern. The functions called are:

 read_icon();
 init_graphics();
 initmouse();
 draw_enlarged_icon();
 show_icon();

These statements must be called in the order shown above. In particular, read_icon() should be kept in text mode (before graphics initialization occurs) in order to simplify user interaction. In addition, the mouse initialization process must always occur after the graphics initialization process is completed.

The next five statements print a series of banners to the screen by calling the outtextxy() function. Notice that a pair of hidemouse() and showmouse() function calls surround the screen output statements. These calls ensure that the mouse cursor is turned off while the screen is being updated with the displayed messages.

After the screen is initialized, the icon editor is ready for business. Two types of input are accepted while an icon is being edited:

    1. A left mouse button, which toggles the icon pixel that is being pointed to by the mouse cursor;

    2. The Esc key, which terminates the program. The while loop in the function main() reads and processes the user inputs as shown below:

  while ((c=waitforinput(LEFT_BUTTON))!= ESC) {
      if (c < 0) {
          getmousecoords(&x, &y);
              toggle_bigbit(x, y);
      }
  } while (!done);

The loop uses the general purpose mouse input function waitforinput(), (see Listing Two). This function returns a negative value if the specified button (in this case, the left mouse button) is pressed. If a key is pressed, then waitforprint() returns the value of that key. The program will continue until the Esc key is pressed. If the left mouse button is pressed, the body of the while loop is entered. At this point, getmousecoords() retrieves the current location of the mouse cursor and passes it to the function toggle-bigbit(), which changes the setting of the icon pixel that the mouse is pointing to.

Creating an Enlarged Icon

The function draw_enlarged_icon(), which is called from main(), displays an editing grid on the left side of the screen. The grid consists of 17 horizontal and vertical dotted lines that designate a 16-by-16 grid for the icon pattern. Before anything is written to the screen, the mouse cursor is turned off by a call to hidemouse(). Later, the mouse cursor is restored by a call to showmouse(). This technique eliminates the problem of overwriting the mouse cursor.

Examine the code in draw_enlarged_icon() and note that this function relies upon numerous macro constants. A list of these macro constants, along with a description of their meanings, is shown in Table 3. You may want to refer to this table as you work your way through draw_enlarged_icon().

Table 3: Macro constants used in drawing the enlarged icon pattern

  Macro Constant      Description
  ---------------------------------------------------------------------

  BIGICONLEFT         The column where the big icon pattern begins
  BIGICONTOP          The top row of the big icon pattern
  BIGBITSIZE          Size of the big pixels in the enlarged icon pattern
  ICONWIDTH           Width of an icon
  ICONHEIGHT          Height of an icon
  DOTTED_LINE         From graphics.h; specifies the line type
  NORM_WIDTH          From graphics.h; pixel width of lines

The last line in draw_enlarged_icon() is a call to init_bigbit(), which creates an image of an enlarged icon pixel. To toggle the icon pixels in the enlarged icon pattern, we'll exclusive OR the image created by init_bigbit() to the rectangular regions within the enlarged icon pattern.

A nested for loop is used to create the image of one of the enlarged bits at the beginning of init_bigbit(), as shown below:

     for (j=bby+1; j<=bby+BIGBITSIZE; j++)
          for (i=bbx+1; i<=bbx+2*BIG BITSIZE; i++))
              putpixel(i,j,getmaxcolor();
              
              

These two loops paint a block of pixels in the top-left corner of the enlarged icon pattern. This block is the size of BIGBITSIZE. Notice that the width of the enlarged icon is drawn to be twice the width of BIGBITSIZE. This step adjusts the icon image to the aspect ratio of the graphics screen.

After the enlarged icon pixel is created, it is copied into the array bigbit. (Once the pixel pattern has been stored in bigbit, an image of the pattern can be exclusive-ORed later during the editing process in order to toggle one of the enlarged icon pixels.)

Before the enlarged icon pixels' image can be copied into bigbit, space for the image must be allocated by a call to malloc(). After the space is allocated, getimage() is called to copy the image of the big pixel. Next, putimage() is invoked with the XOR_PUT replacement in order to remove the big pixel from the top-left corner of the enlarged icon pixel:

  putimage(bbx+1, bby+1, bigbit, XOR_PUT);

Finally, the mouse cursor is restored by a call to showmouse(), and then init_bigbit() terminates.

Displaying the Original Icon

The next step in the screen initialization process is to display the icon pattern that was read in at the beginning of the program. (Remember, if an icon file is not read in, then read_icon() initializes the icon array to zeros.) The function show_icon() displays the current state of the icon pattern. This function consists of two nested for loops that sequence through the array icon. For each byte location that stores the value 1, a corresponding big bit is toggled in the enlarged icon, and the small icon pattern is updated. The process of setting one of the big icon pixels is a matter of exclusive-ORing the image of the big icon pixel (as discussed earlier) at the appropriate locations. The putimage() function, which is located within the inner for loop, performs this step.

The call to toggle_icons_bit() is required in order to turn on the appropriate pixel in the small icon pattern. This function accepts the index of a pixel in the icon array as an argument, and checks the corresponding pixel by testing whether that pixel is equal to the background color. Depending upon the pixel's current value, the small icon's pixels are toggled. The small icon is displayed at the column indicated by ICONLEFT. The top row of the small icon coincides with BIGICONTOP. Notice that although the icon is represented as a 16-by-16 pattern, the small icon is displayed in a 32-by-16 format. In other words, each column of pixels is displayed twice --and that's why the multiplication factor of 2 in the x coordinate calculation is required.

Toggling an Icon Pixel

The process of toggling a pixel in an icon that is being edited involves three steps:

    1. The pixel's value in the icon array must be changed;

    2. The big pixel image in the enlarged icon pattern must be toggled; and

    3. The icon's pixel in the small icon pattern must be updated.

Each of these actions is set in motion by invoking toggle_bigbit(). This function takes two arguments that correspond to the screen coordinates of the enlarged icon bit that is to be changed. These screen coordinates are determined from the location of the mouse cursor at the time of the button press. The mouse coordinates are determined by our mouse routine, getmousecoords().

The bulk of toggle_bigbit() is involved in deciding which icon bit (if any) should be toggled. This decision is made by the two for loops that sequence through the locations of the big icon pattern and test whether the coordinates passed to toggle_bigbit() fall within any of the rows or columns in the big icon pattern. If the passed coordinates fall within the big icon pattern, putimage() is used to exclusive-OR an image of the bigbit image that was made earlier over the current location in the icon pattern. This step toggles the icon pixel in the large icon pattern. Because the corresponding bit in the small icon pattern must be changed, the routine is designed so that the line number and the column number determined by the for loops will correspond to the indices that can access the same bit in the icon array. These values, which are stored in the variables i and j, are passed to another function, toggle_icons_bit(). This step changes the icon array value and updates the small icon on the screen.

Exiting the Icon Editor

The while loop (described earlier) that controls user interaction continues to loop until the Esc key is pressed. Once the Esc key is pressed, the mouse cursor is disabled, the screen is returned to text mode, and the user is prompted to save the icon currently in the icon editor. If the user responds with anything other than the letter N, the program enters the save_icon() function and prompts the user for the name of the file in which the icon will be stored.

Putting it Together

To create an executable version of the icon editor, compile the files iconed.c and mouse.c (provided in Listings One and Two) and then link them with Turbo C's graphics library file, graphics.lib. To use the icons that you create with the icon editor in your application programs, include the read_icon() and show_icon() functions. You'll also need to write a routine that allows the user to select icons by clicking on them with the mouse.

Enhancements

There are a number of enhancements that you can make to the icon editor. For instance, we have designed the icon editor to work with fixed-size icons. Because of the flexibility of the program, you can change the default icon size or include an option that lets you design and store icons of different sizes.

We've already pointed out some of the mouse functions used in the icon editor, such as hidemouse(), showmouse(), getmousecoords(), and waitforinput(). All of these functions directly or indirectly invoke the main mouse interface function, mouse(). This function communicates with the mouse software drivers by invoking interrupt 33h with Turbo C's int86() function. If you don't have a mouse installed, the main program will terminate after calling initmouse(). Alternatively, you could modify the mouse support routines so that they support the keyboard if a mouse is not available.

These suggestions represent the tip of the iceberg. The icon editor can serve as the first of a series of tools that will help you design more flexible user interfaces. Because icons are created and stored independent of the application program, you can redesign an interface without the need to make major changes to the program.

References

Kent Porter, "Mouse Mysteries, Part I: Text," TURBO TECHNIX 1:4, (May/June 1988).


Kent Porter, "Mouse Mysteries, Part II: Graphics," TURBO TECHNIX 1:5, (July/August 1988).

_AN ICON EDITOR_ by Keith Weiskamp and Loren Heiny

[LISTING ONE]


/*   iconed.c -- a special purpose icon editor.
 *
 *   This program allows you to interactively edit icons that
 *   can be used in a graphics program. You can create an icon,
 *   edit an existing one, or save an icon pattern to a file. The
 *   program requires a mouse. The icon files produced are of the
 *   form:
 *         ICONWIDTH ICONHEIGHT
 *         one row of icon pattern
 *         next row of icon pattern
 *               .  .  .
 *         last row of icon pattern
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <graphics.h>
#include <stdarg.h>
#include <alloc.h>
#include "mouse.h"        /* mouse and keyboard routines */

#define BIGICONLEFT 20    /* left side of the big icon pattern */
#define BIGICONTOP  50    /* top side of the big icon pattern */
#define BIGBITSIZE  8     /* big bits are 8 pixels in size */
#define ICONWIDTH   16    /* an icon is a 16x16 pattern */
#define ICONHEIGHT  16
#define ICONLEFT    400   /* small icon pattern located here */
#define ESC         27    /* the value of the ESC key */

/*   Here are the functions used in iconed.c:  */

void draw_enlarged_icon(void);
void toggle_bigbit(int x, int y);
void toggle_icons_bit(int x, int y);
void init_bigbit(void);
void toggle_cursor(int x, int y);
void save_icon(void);
int read_icon(void);
void init_graphics(void);
void show_icon(void);

/*  The global variables */

void *bigbit;      /* points to image of a big bit */
                   /* holds icon pattern */
unsigned char icon[ICONHEIGHT][ICONWIDTH];

void main()
{
    int x, y;
    int c;

    read_icon();           /* read in an icon file */
    init_graphics();
                           /* initialize mouse */
    if (!initmouse()) {    /* must have mouse */
      restorecrtmode();
      printf("\nMouse not installed");
      printf("\nQuitting the Icon Editor");
      exit(1);
    }
    draw_enlarged_icon();  /* an empty big icon pattern */
    show_icon();           /* Draw the icon */

    hidemouse();      /* mouse cursor must be turned off */
            /* before writing to screen */
    outtextxy(BIGICONLEFT, 10, "Press ESC when finished ...");
    outtextxy(BIGICONLEFT, BIGICONTOP-20, "Enlarged Icon");
    outtextxy(ICONLEFT, BIGICONTOP-20, "Actual Icon");
    showmouse();       /* redisplay mouse cursor */

      /* get input from mouse/keyboard */
    while ((c=waitforinput(LEFT_BUTTON)) != ESC) {
   if (c < 0) {     /* if true mouse button pressed */
            getmousecoords(&x, &y);  /* get current position */
       toggle_bigbit(x, y);     /* toggle big bit */
   }
    }

    hidemouse();       /* turn mouse off and then get out */
    closegraph();      /* of graphics mode */
    printf("Do you want to save this icon to a file? (y) ");
    if (getch() != 'n')
   save_icon();
}


void draw_enlarged_icon(void)
{
/*
 *  This function draws an enlarged view of the icon pattern.
 *  You can click a big bit in this pattern to toggle the
 *  corresponding icons on and off in the actual icon. The
 *  icon is drawn at BIGICONLEFT, BIGICONTOP, to right, bottom.
 */
    int i;
    int right,bottom;

    setlinestyle(DOTTED_LINE, 0, NORM_WIDTH);
    right = 2 * (BIGICONLEFT + (ICONWIDTH-1) *
      (BIGBITSIZE + NORM_WIDTH));
    bottom = BIGICONTOP + ICONHEIGHT * (BIGBITSIZE + NORM_WIDTH);

    hidemouse();    /* draw vertical and horizonatal dashed */
    for (i=0; i<=ICONHEIGHT; i++)
        line(BIGICONLEFT, BIGICONTOP+i*(BIGBITSIZE+NORM_WIDTH),
        right, BIGICONTOP+i*(BIGBITSIZE+NORM_WIDTH));
    for (i=0; i<=ICONWIDTH; i++)
      line(BIGICONLEFT+2*(i*(BIGBITSIZE+NORM_WIDTH)), BIGICONTOP,
      BIGICONLEFT+2*(i*(BIGBITSIZE+NORM_WIDTH)), bottom);
    showmouse();
    init_bigbit();   /* create the big bit image */
}


void init_bigbit(void)
{
/*  This function creates the image of a single big bit. This
 *  image is used to toggle the big bits whenever the user
 *  clicks on the big icon pattern.
 */
    int bbx, bby;
    int i,j;

    bbx = BIGICONLEFT;
    bby = BIGICONTOP;     /* corner of the big icon */

    hidemouse();       /* hide the mouse before drawing */
    for (j=bby+1; j<=bby+BIGBITSIZE; j++) {
        for (i=bbx+1; i<=bbx+2*BIGBITSIZE; i++) {
       putpixel(i,j,getmaxcolor());
   }
    }

  /* Set aside memory for the big bit image and then use
     getimage() to capture its image. */

    bigbit = malloc(imagesize(bbx,bby,bbx+2*BIGBITSIZE,
          bby+BIGBITSIZE));
    getimage(bbx+1,bby+1,bbx+2*BIGBITSIZE,bby+BIGBITSIZE,

        bigbit);

  /* Erase the big bit by exclusive ORing it with itself */

    putimage(bbx+1, bby+1, bigbit, XOR_PUT);
    showmouse();      /* turn the mouse back on */
}


void toggle_bigbit(int x, int y)
{
/*
 *  This function toggles a big bit and the corresponding pixel
 *  in the icon pattern. The x and y coordinates specify the
 *  mouse position.
 */
    int i, j;
    int line1, line2, col1, col2;

    for (j=0; j<ICONHEIGHT; j++) {
        line1 = BIGICONTOP+j*(BIGBITSIZE+NORM_WIDTH);
        line2 = BIGICONTOP+(j+1)*(BIGBITSIZE+NORM_WIDTH);
   if (line1 <= y && y < line2) {
            for (i=0; i<ICONWIDTH; i++) {
          col1 = BIGICONLEFT+2*(i*(BIGBITSIZE+NORM_WIDTH));
          col2 = BIGICONLEFT+2*((i+1)*
          (BIGBITSIZE+NORM_WIDTH));
               if (col1 <= x && x < col2) {
                   hidemouse();
                   putimage(col1+1,line1+1,bigbit,XOR_PUT);
                   showmouse();
                   toggle_icons_bit(i, j);
         return;
              }
       }
   }
    }
}


void toggle_icons_bit(int x, int y)
{
/*
 *  This function toggles a single pixel in the icon pattern.
 *  The pixel's color and value is changed in the icon array.
 *  Arguments x and y are between 0 and ICONWIDTH or ICONHEIGHT.
 *  The array icon saves the icon pattern. If a location is set
 *  to 1, the corresponding pixel in the icon is displayed.
 */
    hidemouse();
       /* if pixel is not black, make it black */
    if (getpixel(2*x+ICONLEFT,BIGICONTOP+y) != BLACK) {
   putpixel(2*x+ICONLEFT,BIGICONTOP+y,BLACK);
   putpixel(2*x+1+ICONLEFT,BIGICONTOP+y,BLACK);
        icon[y][x] = 0;
    }
    else {        /* draw all pixels on with the max color */
   putpixel(2*x+ICONLEFT,BIGICONTOP+y,getmaxcolor());
   putpixel(2*x+1+ICONLEFT,BIGICONTOP+y,getmaxcolor());
   icon[y][x] = 1;
    }
    showmouse();
}


void save_icon(void)
{
/*  This function writes the icon pattern to a file. The user
 *  is prompted for the filename. The format of the file is
 *  presented at the top of the iconed.c file.
 */
    char filename[80];
    FILE *iconfile;
    int i, j;

    printf("\nEnter the file name to store the icon in: ");
    scanf("%s",filename);
    if ((iconfile = fopen(filename,"w")) == NULL) {
   printf("Could not open file.\n");
   return;
    }
      /*  Write the header to the file */
    fprintf(iconfile, "%d %d\n", ICONWIDTH, ICONHEIGHT);

    for (j=0; j<ICONHEIGHT; j++) {      /* Write the icon */
        for (i=0; i<ICONWIDTH; i++)     /* pattern to a file */
       fprintf(iconfile, "%x ", icon[j][i]);
   fprintf(iconfile, "\n");
    }
    fclose(iconfile);
}


int read_icon(void)
{
/* This function reads an icon file into the icon array and
 * calls show_icon() to turn the appropriate pixels on. If the
 * file header doesn't match ICONWIDTH and ICONHEIGHT, the
 * the file is invalid and the icon is not read. The function
 * returns a 0 if a file is not read; otherwise 1 is returned.
 */
    char filename[80];
    FILE *iconfile;
    int i, j;
    int width, height;

    for (j=0; j<ICONHEIGHT; j++) {   /* Initialize icon array */
        for (i=0; i<ICONWIDTH; i++)  /* to all blanks */
       icon[j][i] = 0;
    }

    printf("\n\n-----------  ICON EDITOR -------------\n\n");
    printf("Do you want to edit an existing icon? (y) ");

    if (getch() == 'n')
   return(0);

    printf("\nEnter name of the file to read the icon from: ");
    scanf("%s",filename);
    if ((iconfile = fopen(filename,"r")) == NULL) {
   printf("Cannot open file.\n");
   return(0);       /* return a failure flag */
    }
/*  Read first line of the icon file. It should contain two
    numbers that are equal to ICONWIDTH and ICONHEIGHT.
 */
    fscanf(iconfile,"%d %d", &width, &height);
    if (width != ICONWIDTH || height != ICONHEIGHT) {
   printf("Incompatible icon file.\n");
   return(0);          /* return a failure flag */
    }

    for (j=0; j<ICONHEIGHT; j++) {
        for (i=0; i<ICONWIDTH; i++)
       fscanf(iconfile, "%x", &icon[j][i]);
    }
    fclose(iconfile);
    return(1);             /* return a success flag */
}


void init_graphics(void)
{
/* This function initializes the graphics hardware.
*/
    int gdriver = CGA;
    int gmode, gerror;

    gmode =4;
    initgraph(&gdriver,&gmode,"");
    if ((gerror = graphresult()) < 0) {
   printf("Failed graphics initialization: gerror=%d\n",
      gerror);
   exit(1);
    }
}

void show_icon(void)
{
/*  This function displays the icon pattern stored in the
 *  icon array.
 */

    int x, y;

    for (y=0; y<ICONHEIGHT; y++)
      for (x=0; x<ICONWIDTH; x++) {
        if (icon[y][x] == 1) {
     putimage(BIGICONLEFT+2*(x*(BIGBITSIZE+NORM_WIDTH))+1,
     BIGICONTOP+y*(BIGBITSIZE+NORM_WIDTH)+1, bigbit,
     XOR_PUT);
     toggle_icons_bit(x, y);
   }
     }
}







[LISTING TWO]


/*  mouse.c  -- routines to support a Microsoft compatible mouse.
 *              This package assumes that you are running under
 *              graphics mode.
 */

#include <dos.h>
#include <conio.h>
#include "mouse.h"
#include "graphics.h"

#define TRUE     1
#define FALSE    0
int mouseexists;      /* internal variable set true if a */
            /* mouse driver is detected  */


void mouse(int *m1, int *m2, int *m3, int *m4)
{
/*
 *   This function provides the interface between the mouse
 *   driver and an application program. Several predefined mouse
 *   functions supported by the Microsoft mouse are available.
 *   Parameters are passed and returned with the ax, bx, cx and
 *   dx registers.
 */
    union REGS inregs, outregs;

    inregs.x.ax = *m1;
    inregs.x.bx = *m2;
    inregs.x.cx = *m3;
    inregs.x.dx = *m4;
    int86(0x33, &inregs, &outregs);
    *m1 = outregs.x.ax;      /* return parameters */
    *m2 = outregs.x.bx;
    *m3 = outregs.x.cx;
    *m4 = outregs.x.dx;
}


int initmouse(void)
{
/*
 *  This function initializes the mouse and displays
 *  the mouse cursor at the top, left of the screen, if one
 *  is present.
 */
    int m1, m2, m3, m4, gmode;
    char far *memory = (char far *)0x004000049L;

    mouseexists = TRUE;
    m1 = RESET_MOUSE;
    mouse(&m1, &m2, &m3, &m4);
    if (m1) {               /* if mouse reset okay, assume */
   gmode = getgraphmode();            /* mouse exists */
   if (gmode == HERCMONOHI) {   /* Test for Hercules */
       *memory = 0x06;
        }
   m1 = SET_MOUSE_COORD;
   mouse(&m1, &m2, &m3, &m4);  /* mouse exists and draw the */
   showmouse();            /* cursor on the screen at 0,0 */
   return(1);             /* return a success flag */
    }
    else {                       /* no mouse installed */
   mouseexists = FALSE;
   return(0);            /* return a no-mouse found flag */
    }
}


void hidemouse(void)
{
/*  This function removes the mouse cursor from the screen. It
 *  should be called before displaying data on the screen.
 *  Use showmouse() to redisplay the mouse cursor. The mouse
 *  cursor still moves even though it is not visible. Don't
 *  call hidemouse() if the mouse is not already visible.
 */
    int m1, m2, m3, m4;

    if (mouseexists) {            /* check for mouse */
       m1 = HIDE_MOUSE;            /* hide the mouse cursor */
       mouse(&m1, &m2, &m3, &m4);
    }
}


void showmouse(void)
{
/*  This function displays the mouse cursor. You should not call
 *  this function if the mouse is already visible.
*/
    int m1, m2, m3, m4;

    if (mouseexists) {      /* make sure mouse exists */
       m1 = SHOW_MOUSE;
       mouse(&m1, &m2, &m3, &m4);  /* display mouse cursor */
    }
}


void getmousecoords(int *x, int *y)
{
/*
 *  This function returns the position of the mouse cursor.
 */
    int m1, m2;

    if (mouseexists) {
       m1 = GET_MOUSE_STATUS;
       mouse(&m1, &m2, x, y);
       /* adjust for virtual coordinates */
       if (getmaxx() == 319) (*x) /= 2;
    }
}


int testbutton(int testtype, int whichbutton)
{
/*
 *  This function tests a mouse button state. It returns TRUE
 *  if the specified mouse button (whichbutton) meets the
 *  specified action (as indicated by testtype); otherwise the
 *  function returns FALSE.
 */
    int m1, m2, m3, m4;

    m1 = testtype;
    if (whichbutton == LEFT_BUTTON || whichbutton ==
        EITHER_BUTTON) {
        m2 = LEFT_BUTTON;
        mouse(&m1, &m2, &m3, &m4);
   if (m2) return(TRUE);     /* return TRUE if action ok*/
    }
    if (whichbutton == RIGHT_BUTTON || whichbutton ==
       EITHER_BUTTON) {
        m1 = testtype;
   m2 = RIGHT_BUTTON;
        mouse(&m1, &m2, &m3, &m4);
   if (m2) return(TRUE);     /* return TRUE if action ok */
    }
    return(FALSE);           /* return FALSE as a catch all */
}


int waitforinput(int whichbutton)
{
/*  This function returns a character if a key has been pressed,
 *  -1 if a mouse button has been pressed; otherwise a zero. If
    a mouse exists, this routine favors any keyboard action.
 */
    int c = 0;

      while (!c)   {
   if (kbhit())    /* check if a key has been pressed */
       c =getch();  /* return the character     */
        else {
         if (testbutton(CHECK_BUTTON_PRESS, whichbutton)) {
       while (!testbutton(CHECK_BUTTON_RELEASE,
            whichbutton));
       c = -1;
    }
   else if (testbutton(CHECK_BUTTON_RELEASE,
            whichbutton)) {
       c = -1;
       }
       }
      }
      return(c);
}




[LISTING THREE]


/*   mouse.h  -- this file includes function prototypes and macro
 *               constants for the functions in mouse.c.
 */

/*  The following is a list of constants that correspond to the
 *  mouse functions supported in mouse.c.
 */

#define RESET_MOUSE             0
#define SHOW_MOUSE              1
#define HIDE_MOUSE              2
#define GET_MOUSE_STATUS        3
#define SET_MOUSE_COORD         4
#define CHECK_BUTTON_PRESS      5
#define CHECK_BUTTON_RELEASE    6
#define SET_MOUSE_HORIZ_RANGE   7    /* not used */
#define SET_MOUSE_VERT_RANGE    8   /* not used */
#define SET_GRAPHICS_CURSOR    9   /* not used */
#define GET_MOUSE_MOVEMENT      11
#define SET_MICKEY_RATIO        15    /* not used */

#define LEFT_BUTTON             0   /* use left button */
#define RIGHT_BUTTON            1       /* use right button */
#define EITHER_BUTTON           2      /* use either button */


/*  The function prototypes for the functions in mouse.c
 */

void mouse(int *m1, int *m2, int *m3, int *m4);
int  initmouse(void);
void getmousecoords(int *x, int *y);
void hidemouse(void);
void showmouse(void);
int  testbutton(int testtype, int whichbutton);
int  waitforinput(int whichbutton);

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.