oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

BYOB: Build Your Own Browser, Part 3

by Andrew Anderson

Editor's note: In part two of this series, Andrew Anderson enhanced his basic WebKit browser with multi-window capabilities. In this third and final installment, he includes a preferences window and content eliminator.


The next feature we are going to add to our browser is a preferences window. This will allow users to set options that are applied across all instances of the browser window. We will implement the window so we can set options for both the WebView object and options that we add to the mix, such as the default homepage.

Building the preferences window will take three steps: first we will set up MyDocument.nib to work with the preferences window. Second, we will build and connect a preferences window, and finally, we will add a controller class to handle the "work" of the preferences window.

Altering MyDocument.nib

The first thing we need to do is to setup MyDocument to work with our preferences window. The preferences window will access a WebPreference object that we will make static between the instances of the WebView objects in our MyDocument windows. WebView contains the functionality to share WebPreference objects between its instances if we set the WebView's Preferences ID.

To set the Preferences ID, load MyDocument.nib into Interface Builder, choose the WebView object, and choose Show Info from the Tools pull-down menu to make the info window pop up. Choose the Attributes selection from the list box, and change the Preferences ID value to "BYOB."

Next, set the other default values for the WebView preferences. I prefer to deselect all of the preferences except the "Maintain back/forward list" (this needs to be checked, because it is set on an instance basis, not a shared basis), so that I know what the default state is. Once we get our preferences window fully developed, the values will be loaded whenever we start the program, so the default values can be set to whatever you want. Save the file, and we are done!

The Preferences Window

The next step is to build the actual preferences window. To do this, we need to edit MainMenu.nib in Interface Builder. When MainMenu.nib is loaded in Interface Builder, drag a window object from the Cocoa-Window panel of the controller window to any place on the screen, causing a new empty window to pop up. Once the window is displayed, go to the Instances pane of the control panel and rename the window from "window" to "prefWindow."

The next step is to add the user interface elements to the window. Our goal is to have the user interface window look like this:

As you can see, we are setting four values from this window:

  • Autoload Images
  • Javascript Enabled
  • Allow Animated Image Looping
  • Default Homepage

The first three values are set via check boxes. To add these to the window, drag three check boxes (they are the square boxes with the word Switch next to them) from the Cocoa-Controls panel of the controller window to the window and stack them on top of each other, and then change the text on each.

Next, switch to the Cocoa-Text panel and drag the large System Font Text control to the window. Change the text in this to read "Default Homepage," and then drag a square, boxed text input field onto the window.

Finally switch back to the Cocoa-Controls panel and drag two rounded buttons to the window. Place these buttons on the bottom of the page, and change the text to be Apply and Choose as appropriate.

The last step is to connect the new preferences window to the application pull down menus. First double-click "MainMenu" from the "Instances" pane of Interface Builders control panel, which will make the main menu bar for the application pop up. From the "New Application" menu of the main menu bar, Ctrl-drag a line from the "Preferences" menu choice to the "prefWindow" instance in the control panel. Finally in the "Show Info" window that pops up, go to the "Target/Actions" tab of the connections panel, click "makeKeyAndOrderFront" and choose the "connect" button. Now when a user chooses the "Preferences" menu, the new "prefWindow" will be displayed.

Connecting the Pieces

Related Reading

Cocoa in a Nutshell
A Desktop Quick Reference
By Michael Beam, James Duncan Davidson

Once the preferences window has been built, we need to connect the UI pieces to code. To do this, we need to create a new class to act as the controller between the UI and code. From the Classes tab in the control panel, choose NSObject, go to the Classes pull-down menu, and choose Subclass NSObject. A new object named MyObject will appear in the class navigator. Rename this object Controller.

Once Controller has been created, we need to add the appropriate actions and outlets to it. Make sure that the class is still selected in the class navigator, and choose Add Outlet to Controller from the Classes menu.

Create four outlets, named:

  • allowAnimatedImages
  • autoLoadImages
  • javaScriptEnabled
  • defaultHomepage

After the outlets are created, we need to add an action to handle the Apply button. Start by choosing the Actions tab, and then click the Add button and create an "apply:" action. Once that is done, close the Controller Class Info window. Next, we need to create an instance of Controller so that we can connect the outlets and actions to the instance. Make sure Controller is selected in the class navigator, and then choose Instantiate Controller from the Classes pull-down menu.

The next step is to connect the UI elements to the Controller instance. First, switch to the Instances tab of the control panel window. Then, Ctrl-drag a line from the Controller instance to each of the UI elements on the screen and choose the appropriate outlet for each UI element.

Choose the Apply button on the UI and Ctrl-drag a line to the Controller instance and choose the "apply:" action. Now we need to connect the Close button by choosing the Close button on the UI, Ctrl-dragging to the "prefWindow" instance, and connecting it to the "performClose:" action. Finally, Ctrl-drag a line from the preferences window instance to the Controller instance and connect it to the "delegate" outlet. This last connection makes the Controller the delegate for the preferences window, so we can control what appears on the window in our code.

The last step in the process is to create the files for the Controller class. To do this, switch back to the Classes tab of the MainMenu.nib window and select the Controller class from the class navigator. Select Create Files For Controller from the Classes pull-down menu. A dialog box will pop up, asking where to put the files; make sure that it has the project selected under "Insert into targets:" and click OK. Once the classes have been created, save the work and quit Interface Builder.

Writing the code

Now it's time to add the code to the Controller.h and Controller.m files that Interface Builder created. Interface Builder already added the outlets and actions for the UI when we created the files. We need to add two variables:

  WebPreferences *p;
  MyDocument *md;

and two import statements:

#import <WebKit/WebPreferences.h>
#import <WebKit/WebView.h>

After adding the statements, the file should look like this:

#import <Cocoa/Cocoa.h>
#import <WebKit/WebPreferences.h>
#import <WebKit/WebView.h>

#import "MyDocument.h"

@interface Controller : NSObject
  IBOutlet id allowAnimatedImages;
  IBOutlet id autoloadImages;
  IBOutlet id defaultHomepage;
  IBOutlet id javascriptEnabled;
  IBOutlet id ignoreContent;
  WebPreferences *p;
  MyDocument *md;
- (IBAction)apply:(id)sender;


Now onto Controller.m, the implementation file for the Controller class, where we will add three methods:

  • awakeFromNib
  • windowDidBecomeKey
  • apply

The awakeFromNib method is called by the Cocoa runtime environment when the NIB is first loaded. We will use it to set up preference handling and to load preference values from the user database. windowDidBecomeKey is a window delegate call that is called when ever the preferences window becomes the key window (this delegate was set up when we connected the Controller to the preferences window delegate outlet). This method will make sure that the correct values are displayed on the preferences window whenever it is opened or made key. The last method we will deal with is apply, which will set the values that we input in the preferences window into the structures that we save.

The code for awakeFromNib is pretty straightforward:

- (void)awakeFromNib
  md = [MyDocument alloc];
  [md init];

  WebView *wv = [WebView alloc];
  [wv init];
  [wv setPreferencesIdentifier:@"BYOB"];

  p = [wv preferences];
  [p setAutosaves:YES];

  NSUserDefaults *defaults;
  defaults = [NSUserDefaults standardUserDefaults];
  NSString * dHomepage = [defaults stringForKey:@"defaultHomepage"];
  if (dHomepage != nil) {
  [md setDefaultHomepage:dHomepage];

First, create a new MyDocument and set it to the instance variable md. We need this value so we can access the static defaultHomepage variable of MyDocument. The next step is to create a WebView object. Once we create the WebView object, we set its preference identifier to BYOB -- the same value that we set in MyDocument's WebView control in Interface Builder. Since they have the same value, they will share the same WebPreferences object, so we pull the WebPreferences object from the WebView and save it into the p variable that we created before.

Calling setAutosaves on p with the value YES ensures that the WebPreferences will automatically take care of loading and saving WebView's preference values in the user default database. Unfortunately, since the default homepage is not part of WebView, our code needs to take care of this. The next group of lines takes care of loading the default homepage value and setting it in the MyDocument object. Since the variable in MyDocument is static, we can set it in our instance and it will be shared across all of the instances.

Pages: 1, 2

Next Pagearrow