Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

An Icon Editor


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);
   }
     }
}






<a name="013e_0017"><a name="013e_0017">
<a name="013e_0018">
[LISTING TWO]
<a name="013e_0018">

/*  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);


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.