4.1 - The Basics#
Have you ever used a document-based application? I’d bet you have – document-based applications are one of the most common types of applications. Computers would be useless without them. Document-based applications are applications that interact with documents, such as text files, pictures, videos, and code. Web browsers are also document-based applications. In fact, document-based applications were invented long before computers – typewriters are essentially document-based applications, and you could argue that pen and paper are also document-based.
OpenStep, and therefore Cocoa and GNUstep, comes with built-in support for document-based applications in its Application Kit. This tutorial won’t cover everything you need to know about them – please read the Cocoa document about document-based applications. Otherwise, you probably will not know what I am doing.
Document-based apps look complicated at first – you have to deal with
NSDocumentController
, NSWindowController
, NSDocument
and NSWindow
.
However, since we’ll use Gorm to build the NSWindow
, and
NSDocumentController
and NSWindowController
are not required to
subclass, NSDocument
is the only class we have to deal with. That makes
everything very easy.
In this section, we only make the skeleton of the document-based application. We’ll add more features in the next part of this tutorial.
Skeleton user interface#
Now, we have to create the main user interface. Since it’ i’s a document-based application, there is no main window. We only need a menu so that users can open, save and close each document.
Open Gorm, choose “Document→New Application”. Click the window in the Gorm main window, use “Edit→Delete” to delete the window. Drag the menus “Info” and “Document” into the main menu. The main user interface will be like this:
Figure 4-32. Menu of document-based application
Note
Not having a main window can cause problems if you’re using a global or local menu bar, rather than a floating menu. Additionally, it’s not what users expect except on macOS. We’ll address these issues later.
Next, I need a NSDocumentController
. Choose the class
NSDocumentController
, and use menu item “Classes→Instantiate” to make an
instance.
Figure 4-33. Create instance of NSDocumentController
The main user interface is done. Save it as “Money.gorm”.
NSDocumentController
will look at the property list of this application
in order to know what kind of document it should handle. Here is the property list we’ll be using:
MoneyInfo.plist
{
ApplicationDescription = "Money";
ApplicationIcon = "";
ApplicationName = Money;
ApplicationRelease = 0.1;
Authors = "";
Copyright = "Copyright (C) 200x by ...";
CopyrightDescription = "Released under...";
FullVersionID = 0.1;
URL = "";
NSTypes = (
{
NSName = "mon";
NSHumanReadableName = "Money Document";
NSUnixExtensions = ("mon");
NSRole = Editor;
NSDocumentClass = Document;
}
);
}
The important part is that NSTypes
defines what kinds of documents our application, “Money”,
can handle, and what class (which should be a subclass of NSDocument
) handles the document. In this case, Money.app edits “Money Documents” with file extension .mon
, using the class Document
, which we haven’t implemented yet. This is a simplified property
list, but it works. Look at InkInfo.plist
from the Ink example application for a better example.
Now, we need to create the class Document
, and
the window for each document. Open Gorm, choose “Document→New
Empty”. Drag a window out of the palettes. We won’t do anything in the window yet – we’ll flesh out the UI in the next section.
Look at the classes in the Gorm main window, and use “Classes→Create
Subclass” to create a subclass of NSDocument
, called Document
.
Figure 4-34. Create NSDocument
subclass
Don’t instantiate it. Instead, we’ll make it the owner of the document
window (in Objects). Click the NSOwner in the Gorm main window, and select the Attributes pane in the inspector. Choose the Document
class.
Figure 4-35. Set document as NSOwner of window
Now, the NSOwner is the class of Document. I need to connect the
_window
outlet of NSOwner (an instance of Document
) to the window,
Figure 4-36. Connect NSOwner to window
and the set the delegate of window to the NSOwner (an instance of Document
).
Figure 4-37. Connect delegate to NSOwner
Finally, use “Classes→Create Class Files” to create the files of
class Document
. Save them to Document.m and Document.h. Save the Gorm
file into “Document.gorm”.
Loading the user interface#
Now, we have five files: Money.gorm, MoneyInfo.plist, Document.h, Document.m and Document.gorm (which is actually a special kind of directory, called a “bundle”).
NSDocumentController
knows what class to use because it is written in the
property list file. How does NSDocument
(or its subclass, Document
) know what the document viewer is? In this case, Money.app’s document viewer is Document.gorm
. The simplest way to tell Document
about Document.gorm
is to implement the method
-windowNibName:
in Document
.
The Document files that Gorm created for us won’t work perfectly. Enter the following code instead:
Document.h:
#import <AppKit/AppKit.h>
#import <AppKit/NSDocument.h>
@interface Document : NSDocument
{
}
@end
Document.m:
#import "Document.h"
@implementation Document
- (NSString*) windowNibName {
return @"Document.gorm";
}
@end
I deleted the extra code inherited from NSDocument
. Include
<AppKit/NSDocument.h>
explicitly in the header file because <AppKit/AppKit.h
doesn’t
include it (at least on some platforms). The most important part is that I return the name of the
document interface, Document.gorm
, so that NSDocument
can find where
the interface are.
Here are the rest of the files:
main.m:
#import <AppKit/AppKit.h>
int main(int argc, const char *argv[]) {
return NSApplicationMain(argc, argv);
}
GNUmakefile:
include $(GNUSTEP_MAKEFILES)/common.make
APP_NAME = Money
Money_HEADERS = Document.h
Money_OBJC_FILES = main.m \
Document.m
Money_RESOURCE_FILES = MoneyInfo.plist \
Money.gorm \
Document.gorm
Money_MAIN_MODEL_FILE = Money.gorm
include $(GNUSTEP_MAKEFILES)/application.make
Here is the source code: Money-src.tar.gz
Compile the application with make
, and run it with openapp ./Money.app
.
Once the application starts up, you will see only the menu. Use “Document→New” to open a new document. A empty window will show up. You can keep as many documents as you want. The menu items will be automatically enabled depending on the existance of the document. Most menu doesn’t work yet.
GNUstep offers a great framework for document-based applications. With GNUstep, we only need to focus on the document itself, and don’t need to worry about how to manage the multiple documents/windows. GNUstep will handle it perfectly.