章 13. 記帳簿

內容目錄

文件程式
表格
存取檔案
Drag and drop in table

文件程式

文件程式是一種常用的程式類型. 在文件程式中, 使用者可以同時開啟多個文件, 分別修改並儲存. 像一般的文字處理器就是文件程式. 在 GNUstep 中有個 Ink.app 即為一個良好的範例, 亦可參考 Cocoa 文件. 文件程式的架構很簡單, 首先是程式本體, NSApp. NSApp 呼叫代理者來處理選單. NSApp 不繪製視窗, 因為文件程式只有在開啟文件或是產生新文件時才會開啟視窗, 也就是有文件內容時才會有相對應的視窗. 選單產生之後, NSApp 將代理者改成 NSDocumentController. NSDocumentController 是處理文件者. 在 GNUstep 中使用 MVC 架構來設計程式. MVC 架構為資料 (Model), 控制 (Control) 及顯示 (View) 三部份. 一個文件應該只有一份資料, 但可以有很多的 View 來顯示一份資料, 如編輯模式, 輸入模式等. 而控制則是負責做為資料與顯示之間的溝通, 當資料有改變時, 會反應在顯示上. 當使用者在顯示上做改變時, 會反應在資料上. 根據這個架構, NSDocumentController 就做為控制的部份, 負責資料與顯示的溝通. 資料的儲存由 NSDocument 負責, 而顯示的部份則在 NSWindow 中, 再由 NSWindowController 呼叫. GNUstep 提供內建的 NSDocumentController, 因此只要設計 NSDocument 及視窗即可.

在此, 使用 Gorm 產生 NSWindow. NSDocumentController and NSWindowController 使用內建的即可. 所以只需要設計好 NSDocument 即可. 這使得設計文件程式簡單許多. 在這個範例中, 先將文件程式的架構建立好, 以後的章節再製做實際的程式.

因為文件程式的主程式沒有視窗, 只有選單, 因此相當容易. 開啟 Gorm, 選 "Document->New Application". 選 Gorm 主視窗中的 window. 使用 "Edit->Delete" 刪除視窗, 只留下選單. 從 palettes 中拖出 "Info" 及 "Document" 並加入主選單. 程式介面看起來如下:

圖形 13.1. Menu of document-base application

Menu of document-base application

只剩下選單, 沒有視窗.

接著需要一個 NSDocumentController. 選 "NSDocumentController" 類別. 使用 "Classes->Instantiate" 產生物件.

圖形 13.2. 產生 NSDocumentController 物件

產生 NSDocumentController 物件

將這個主程式介面存檔成 "Money.gorm".

NSDocumentController 會檢查程式的屬性檔 (Property List), 在此稱為 MoneyInfo.plist.

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

最重要的是 NSTypes. 其決定了那些文件可以被這個程式處理, 及使用那一個類別來開啟這個文件. 在這個例子中, 使用 Document" 這個類別來處理文件. Document 是接下來要實作的類別. 這個屬性檔是簡化過的, 可參考 Ink.app 中的 InkInfo.plist.

接下來要製做 Document 類別, 來處理 NSTypes 指定的文件種類. 也需要一個視窗來顯示文件內容. 開啟 Gorm, 使用 "Document->New Empty". 產生新的介面. 從 palettes 中拖出一個視窗. 在這裡只是個其本架構, 因此只是個空白視窗.

在 Gorm 主視窗中繼承自 NSDocument, 產生一個 Document 類別.

圖形 13.3. 產生 NSDocument 子類別

產生 NSDocument 子類別

在此不產生 Document 物件. 選 Gorm 視窗中的 NSOwner, 在 Inspector 中的 Attributes 中找到這個新的 Document 類別. 選這個 Document 類別. 這時 NSOwner 即成為 Docuemtn 類別.

圖形 13.4. Set document as NSOwner of window

Set document as NSOwner of window
Set document as NSOwner of window

現在 NSOwner 的類別是 Document. 連結 NSOwner 中的 _window 至視窗.

圖形 13.5. Connect NSOwner to window

Connect NSOwner to window
Connect NSOwner to window

並設定視窗的代理者為 NSOwner, 即為 Document 物件.

圖形 13.6. Connect delegate to NSOwner

Connect delegate to NSOwner
Connect delegate to NSOwner

最後, 使用 "Classes->Create Class Files" 產生 Document 類別的檔案. 存當成 Document.m 及 Document.h. 並將這個使用者介面存成 Document.gorm.

現在, 一共有 Money.gorm, MoneyInfo.plist, Document.h, Document.m 及 Document.gorm 五個檔案. 其中 Gorm 檔其實是個目錄.

NSDocumentController 可以從程式的屬性檔中找到正確的類別來處理文件, 那 NSDocument 或其子類別 (在本範例中為 Docuemtn) 如何找到正確的視窗來顯文件呢 ? 最簡單的方法是在 Document 中實作 -windowNibName:.

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

在 Document 中傳回要使用的視窗介面檔名即可.

以下是其他所需的檔案.

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

程式碼在此: Money-src.tar.gz

執行這個程式時, 可以看到一個選單. 使用 "Document->New" 會開啟一個空白視窗. 在此可以開啟許多視窗, 選單會隨著程式目前開啟視窗與否而有所改變. 因為這範例只是個基本架構, 大部份的選單都沒有作用.

GNUstep 提供了一個完整的架構來設計文件程式. 接下來只要專注在 Document 類別及視窗即可, 亦即處理文件的本身, 而不需要耽心視窗及文件的管理. GNUstep 會自動處理相關的細節.