Disabling Buttons for BetterMultiradix Input

You may have noticed that there is still a big problem with our Calculator — the keypad doesn’t work correctly in any base except for decimal. The reason for this failure lies with the following statement in the enterDigit: method:

X = (X*10.0) + [ [sender selectedCell] tag];

This statement multiplies whatever is in the X register by 10 and adds the tag of a digit button each time one is clicked. Unfortunately, we don’t want to multiply the X register by 10 if a radix other than base 10 is in effect; instead, we want to multiply by the current radix. So, for a first pass, the 10.0 in this statement should be replaced with radix.

  1. Replace the 10.0 in the enterDigit: method in Controller.m with radix to get:

    X = (X*radix) + [ [sender selectedCell] tag];

But that’s not the only change we need to make; we also have to change the keypad of buttons so that particular buttons are deactivated when certain bases are selected. For example, a user shouldn’t be able to press the 8 button when the Octal base is chosen. Also, it would be nice to make buttons for the numbers A, B, C, D, E, and F appear when the user selects Hex. We’ll address all of these problems and add the new features in the remainder of this chapter.

Accessing NSMatrix Cells with an NSArray Object

Every Cocoa button is either enabled or disabled. If a button is disabled, the black labeling on it turns gray, and the button won’t respond to the mouse. In the following steps, we’ll modify thesetRadix: method so that each time the radix is changed, the method will scan all of the buttons in the digit-button matrix and disable the ones whose tags are equal to or greater than the newly selected radix.

To scan all of the digit buttons in the NSMatrix, we’ll need its id. We’ll also need the id of each individual button that the matrix contains. As we will see, the id of each cell inside an NSMatrix object is stored in yet another Foundation object, called an NSArray. As its name implies, an NSArray contains an array (or list) of other objects.

  1. Insert the keyPad outlet declaration shown here in bold into Controller.h:

    ...
    @interface Controller:NSObject
    {
    ...
       IBOutlet id radixPopUp;   IBOutlet id keyPad;
    }
  2. Save Controller.h.

Our program will use the keyPad outlet to learn the id of the NSMatrix object when the application starts up. As usual, we’ll arrange for this initialization in IB.

  1. Now drag the Controller.h icon from PB’s Groups & Files pane and drop it in the MainMenu.nib window in IB.

    This operation has the same effect as choosing IB’s Classes Read Controller.h menu command. You can also drag the Controller.h icon from the Finder and drop it in the same place. Now MainMenu.nib “knows about” the new keyPad outlet in the Controller class.

  2. Connect the newly created keyPad outlet in the Controller instance to the NSMatrix object that contains the digit buttons for your Calculator. Make sure you connect to the matrix and not to a single digit button within the matrix (move the mouse near the edge of the matrix until you see that it’s surrounded by a connection wire box, as shown in Figure 7-11).

Connecting Controller instance to matrix with keyPad outlet

Figure 7-11. Connecting Controller instance to matrix with keyPad outlet

NSArray is an important class for Cocoa programmers; it’s a generic class (actually called a Foundation class) for maintaining a collection, or list, of other objects. The NSArray class has methods for:

  • Creating an array from a single object, or from a collection of objects

  • Sending a message to every object in the list

  • Counting the number of elements in the list

  • Accessing a specific element in the list by number

  • Creating a new NSArray from the existing array

After an NSArray is created and initialized, the collection of objects that make up the array never changes. If you need to create an array to which objects can be added or removed, use an NSMutableArray instead. This mutable (changeable) class has additional methods for:

  • Adding an object to the list

  • Adding an object to the list if it isn’t already there

  • Removing an object from the list

Refer to the Cocoa Foundation documentation for a detailed explanation of the NSArray and NSMutableArray classes.

  1. Back in PB, insert the code shown here in bold into the setRadix: action method in Controller.m:

    - (IBAction)setRadix:(id)sender
    {    NSArray *cells;
        int i;
    
        radix = [[sender selectedCell] tag];    // Disable the buttons that are higher than selected radix
                                cells = [keyPad cells];
        for (i=0; i<[cells count]; i++) {
                                    id cell = [cells objectAtIndex: i];
                                    [cell setEnabled: ([cell tag] < radix) ];
                                }
        [self displayX];
    }

We’ll explain the new code in setRadix: line by line. The following line sends the cells message to the keyPad (NSMatrix) object, which causes the object to return the id of the NSArray object that holds all of the NSMatrix’s (button) cells:

cells = [keyPad cells];

Once we have the id (stored in the cells instance variable) of this NSArray object, we can easily access the objects stored inside it. This line sets up a loop that will execute for each of the objects stored in the NSArray object:

for (i=0; i<[cells count]; i++)

This line sets the cell local variable to be the id of the ith element in the NSArray object:

id cell = [cellList objectAtIndex: i];

The expression [cell tag] < radix in the following line returns YES if cell should be enabled and NO if it shouldn’t (YES and NO are specified by #define operators in the Foundation class NSObjCRuntime.h file):

[cell setEnabled: ([cell tag] < radix)];

The outermost message then sets the cell to be enabled or disabled as appropriate for the current radix. For example, if the radix is 8 (octal), all cells with tags less than 8 should be enabled (YES), while cells with tags 8 or greater should be disabled (NO).

There are a variety of ways to loop over the objects stored with an NSArray. You can create an integer variable and step through all of the variables, as we did earlier. Alternately, you can ask the array for an objectEnumerator and step that enumerator through the contents of the array. For example, the setRadix: method could be rewritten to look like the following (not necessary to implement):

- (IBAction)setRadix:(id)sender
{    NSEnumerator *enumerator;
                      NSCell *cell;

    radix = [[sender selectedCell] tag];
    // Disable the buttons that are higher than selected radix
                      enumerator = [[keyPad cells] objectEnumerator];
                      while (cell = [enumerator nextObject]) {
                          [cell setEnabled: ([cell tag] < radix) ];
                      }
    [self displayX];
}

This revised version of the setRadix: method is smaller and more object-oriented, but some people may find it harder to understand. It may also take a few thousandths of a second longer to run; on the other hand, it may not. Ultimately, both versions of the method work equally well, but the object-oriented version is easier to debug and easier to maintain. In general, you should use the NSEnumerator class for iterating through NSArrays, rather than a for loop with the objectAtIndex: method.

  1. Save all pertinent files and build and run the Calculator application.

  2. With Calculator running, click the digit buttons to display the number 258.

  3. Now click the Dec pop-up menu button and drag to Binary. Note that the number 258 changes to its binary representation and all the digit buttons except 0 and 1 are disabled, as shown in the window on the left in Figure 7-12. The buttons turn gray because Cocoa buttons automatically display their titles in gray when they are disabled.

Disabling digit buttons for different bases in Calculator

Figure 7-12. Disabling digit buttons for different bases in Calculator

  1. Next, click the Binary pop-up menu button and drag to Octal. Note that the number changes to its octal representation and that the digit buttons 8 and 9 are disabled, as shown in the window on the right in Figure 7-12.

  2. Quit Calculator.

Coherence in Object-Oriented Programming

The changes to the setRadix: method bear mentioning, because they contain the essence of another important object-oriented concept: coherence . Being coherent means being logically or aesthetically ordered or integrated. In object-oriented programming, coherence means writing as little code as necessary by writing code that figures out what it needs to know when it runs, rather than having things preprogrammed. This way, if something changes, the code automatically reconfigures itself at runtime.

In this example, the setRadix: method disables those buttons in the matrix that have a tag that is equal to or greater than the current radix — so, for example, the buttons labeled 2-9 don’t work when the Calculator is in binary mode. But rather than hardcoding the keys, the setRadix: method needs to disable the keys for each radix; we have setRadix: find these keys by scanning through the associated NSArray object that contains the matrix cells. Likewise, rather than hardcoding into setRadix: the number of buttons in the matrix, we have setRadix: determine the number by asking the NSArray how many objects it contains. This way, we can change the number of cells in the matrix while in IB and not have to make any changes to the setRadix: method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.110.155