- Step 1: Add Controls to the Diary Window
- Step 2: Implement the Add Entry Push Button
- Step 3: Implement the Add Tag Push Button
- Step 4: Validate the Add Tag Push Button
- Step 5: Implement and Validate the Navigation Buttons
- Step 6: Implement and Validate the Date Picker
- Step 7: Implement and Validate the Search Field
- Step 8: Build and Run the Application
- Step 9: Save and Archive the Project
- Conclusion
Step 5: Implement and Validate the Navigation Buttons
Approach the four navigation buttons in the same way you did the Add Tag button. First write methods that return the range of the target entry's title. Then write a separate action method for each to scroll the title into view and select it. Finally, subclass the buttons to take advantage of the validation protocols you have just written.
You already implemented two of the range methods underlying the navigation buttons in Step 3, -currentEntryTitleRangeForIndex: and -nextEntryTitleRangeForIndex:. Now implement the other three, -firstEntryTitleRange, -lastEntryTitleRange, and -previousEntryTitleRangeForIndex:, as well as all four of the action methods.
Declare and implement the three new range methods in the DiaryDocument class. You will find them in the downloadable project file for Recipe 4.
These three methods follow the same strategy as -currentEntrytTitleRangeForIndex:. The first two define the entire text storage as the search range, so they don't require an index parameter. The -firstEntryTitleRange method searches forward from the beginning of the diary, while -lastEntryTitleRange searches backward from the end. The first entry marker character they find marks the location of the targeted entry title. The last method, -previousEntryTitleRangeForIndex:, finds the current entry by calling -currentEntrytTitleRange and then searching backward for the previous entry marker.
Next, write the four navigation action methods. Name each of them with goTo as the first part of the method name, on the model of several methods in AppKit's PDFView class. All of them select an entry's title using the corresponding range method to get the target range, and then scroll it into view. They also give keyboard focus to the appropriate text view, in case the search field had focus when the user clicked the navigation button.
As action methods, the navigation methods belong in DiaryWindowController. You will find them in the downloadable project file for Recipe 4.
Connect the action methods.
In the DiaryWindow nib file, Control-drag from the top-left navigation button in the diary window to the First Responder proxy in the nib file's window; then choose the goToFirstEntry: action in the Received Actions section of the HUD. Alternatively, Control-click (right-click) the top-left navigation button, and then drag from the marker beside the goToFirstEntry: action in the Sent Actions section of the HUD to the First Responder proxy; or Control-click the First Responder proxy and choose the goToFirstEntry: action in the Received Actions section of the HUD.
Do the same thing with the other three navigation buttons, connecting the bottom-left button to the goToLastEntry: action, the top-right button to the goToPreviousEntry: action, and the bottom-right button to the goToNextEntry: action.
The four navigation buttons work now, but they ought to be disabled when there is nothing for them to do. All of the buttons should be disabled when there are no entries in the diary, and the top-right and bottom-right buttons should also be disabled when there is no entry before or after the current entry. Otherwise, it is always useful to scroll the targeted entry's title into view and, even if it is already scrolled into view, to select it so that it catches the user's eye.
You already set up the validation machinery for the diary window in Step 4. You have to do only two additional things to bring the navigation buttons under its control.
First, set the class of the four navigation buttons to ValidatedDiaryButton. To do this, select each of them in turn, and in the Button Identity inspector, choose ValidatedDiaryButton in the Class pop-up menu.
Second, add several clauses to the -validateUserInterfaceItem: protocol method you wrote in Step 4. This is the revised method in full:
- (BOOL)validateUserInterfaceItem: (id <VRValidatedControl>)item { SEL action = [item action]; if (action == @selector(addTag:)) { return ([[self document] currentEntryTitleRangeForIndex: [self insertionPointIndex]].location != NSNotFound); } else if (action == @selector(goToFirstEntry:)) { return [[self document] firstEntryTitleRange].location != NSNotFound; } else if (action == @selector(goToLastEntry:)) { return [[self document]lastEntryTitleRange].location != NSNotFound; } else if (action == @selector(goToPreviousEntry:)) { return [[self document]previousEntryTitleRangeForIndex: [self insertionPointIndex]].location != NSNotFound; } else if (action == @selector(goToNextEntry:)) { return [[self document]nextEntryTitleRangeForIndex: [self insertionPointIndex]].location != NSNotFound; } return YES; }
If the button being checked is not the button connected to the addTag: action, the method checks whether it is the button connected to the goToFirstEntry: action. If it isn't that button, it continues down the chained else if clauses looking for each of the other navigation buttons. When it determines that one of the navigation buttons is currently being validated, it searches for that button's target entry title range. Finally, it returns the Boolean value YES if the target range is found and otherwise NO. As in Step 4, it returns YES for all other buttons to ensure that they are enabled.
- Run the application and test the navigation buttons. Add several entries to the diary using the Add Entry button. Then click the navigation buttons in a variety of ways and watch what happens. Verify through observation that they are enabled and disabled as designed.