^15.9 Schnittstelle zum GEOS DateisystemEin Programm, das mit Dokumenten arbeitet, muss mit folgenden Situationen umgehen können:
|
Application MyAppObject OnStartup = DocStartupHandler OnExit = DocExitHandler OnConnection = DocConnectionHandler ... End Object |
|
Außerdem benutzen wir die Tatsache, dass alle Instancevariablen von GenericClass Objekten eine Systemrestart automatisch überleben. Das DocumentGuardian-Objekt merkt sich also z.B. den Namen und den Pfad der aktuell offenen Datei automatisch, ohne unser Zutun. Wenn wir weiterhin darauf achten, dass alle geänderten Daten des Dokument in irgendwelchen Instancevariablen gespeichert sind (das ist z.B. automatisch der Fall beim Text eines Memo-Objekts, dem ausgewählten Eintrag einer Liste oder der aktuellen Farbe bei einem ColorSelector), stehen uns diese Daten nach einem Systemneustart automatisch wieder zur Verfügung. Kümmern müssen Sie sich nur, wenn Sie Daten in globalen Variablen haben. Diese könnten Sie z.B. in der Instancevariablen "documentUserData" des DocumentGuardian-Objekts "retten".
Um eine DOS-Datei mit einem Programm zu verknüpfen muss in der GEOS.INI in der Kategorie [filemanager] unter "fileNameTokens" ein Eintrag der Form *.EXT="DTOK", 0, "AppT", 16600existieren, wobei *.EXT die DOS-Datei beschreibt, "DTOK", 0 das Token ist, das der Geomanager zur Anzeige der Datei verwenden soll und "AppT", 16600 das Token unseres Programms ist. R-BASIC unterstützt das Setzen eines solchen Eintrags nicht. Der Nutzer muss das selbst tun, z.B. mit Hilfe des Voreinstellungs-Moduls.
Für GEOS- und VM-Dateien müssen wir nicht in die GEOS.INI eingreifen. Wir müssen der Datei nur ein CreatorToken zuweisen. Damit weiß das System zu welchem Programm die Datei gehört. Sinnvoller Weise geben wir der Datei auch noch ein eigenes Token. Das alles erledigt das DocumentGuardian-Objekt für uns. Wir müssen nur Token und CreatorToken bei der Konfiguration des Objekts angeben (siehe Kapitel 15.3, Routine DoInitDocumentGuardian).
Der OnStartup-HandlerWie oben beschrieben, muss der OnStartup-Handler (er heißt DocStartupHandler) unterscheiden, ob das Programm neu startet oder ob GEOS gerade wieder hochfährt. Außerdem muss er wissen, ob eine Datendatei (Dokument) übergeben wurde oder nicht. Die entsprechende Information ist im Parameter 'flags' zu finden. Ist das Bit AF_RESTORE gesetzt, fährt GEOS nach einem Shutdown wieder hoch. Das Bit AF_DATA_FILE ist gesetzt, wenn eine Datei an den Handler übergeben wurde. Das ermöglicht uns folgendes Vorgehen:
|
SYSTEMACTION DocStartupHandler
IF flags AND AF_RESTORE THEN
DocumentObj.HandleRestart
RETURN
End IF
DoInitDocumentGuardian(DocumentObj)
IF flags AND AF_DATA_FILE THEN
OpenExternalFile(dataFile$)
ELSE
DTShowNewOpenDialog(ConvertObjForSDK(DocumentObj),
NOF_STARTUP + NOF_NEW_OPEN_TEMPLATE + NOF_CONFIG + NOF_IMPORT, "")
End IF
END ACTION
|
Der OnExit-HandlerDer OnExit-Handler namens DocExitHandler hat nichts zu tun, wenn gar kein Dokument offen ist. Das wird deswegen zuerst abgefragt.Ist ein Dokument offen, muss er zwischen zwei Fällen unterscheiden:
|
SYSTEMACTION DocExitHandler
DIM cmd
IF DocumentObj.documentHandle = NullFile() THEN RETURN
IF flags AND AF_SHUTDOWN THEN
DocSaveCachedData (DocumentObj)
DocumentObj.HandleShutdown
RETURN
End IF
cmd = DTConfirmClose(ConvertObjForSDK(DocumentObj), FALSE)
ON cmd SWITCH
CASE CLOSE_SAVE: ' Änderungen speichern
BasicSaveDoc' Handelt alle denkbaren Fälle
END CASE
CASE CLOSE_SAVE_AS:
BasicSaveAsDoc()
END SWITCH
DocumentObj.CloseDocument
END ACTION
|
Der OnConnection-HandlerDer OnConnection-Handler (er heißt DocConnectionHandler) wird gerufen, wenn der Nutzer ein Dokument im Geomanager doppelklickt, das zugehörige Programm aber schon läuft. Der vollständige Pfad zu diesem Dokument wird dem Handler im Parameter dataFile$ übergeben. Da es nicht auszuschließen ist, dass GEOS den Handler auch in anderen Zusammenhängen ruft, fragen wir das Bit AF_DATA_FILE ab, bevor wir OpenExternalFile zum Öffnen der Datei rufen. |
SYSTEMACTION DocConnectionHandler
IF flags AND AF_DATA_FILE THEN
OpenExternalFile(dataFile$)
End IF
END ACTION
|
OpenExternalFileDie Routine OpenExternalFile erledigt alles was nötig ist um eine Datei zu öffnen, die einem der Handler DocStartupHandler oder DocConnectionHandler übergeben wurde. |
SUB OpenExternalFile (file$ as string(235))
DIM err, n, state
DIM fileName$ as String(32)
DIM path$ as String(235)
state = DocumentObj.documentState
IF state THEN
err = BasicCloseDoc(True)
IF err THEN RETURN
END IF
path$ = file$
n = InStr("\\", path$)
WHILE n <> 0
path$ = Right$(path$, len(path$) - n)
n = InStr("\\", path$)
WEND
fileName$ = path$
path$ = left$(file$, len(file$) - len(fileName$) - 1)
SetCurrentPath path$
DocumentObj.OpenDocument fileName$
DoReadDataFromDoc
DoUpdateDocButtons
END SUB
|
|
Zunächst prüfen wir ob noch eine Datei offen ist (die Variable state ist dann ungleich Null). In diesem Fall erledigt BasicCloseDoc das Schließen der Datei mit vorheriger Nachfrage beim Nutzer. Entscheidet sich der Nutzer die Datei doch nicht zu schließen liefert BasicCloseDoc TRUE und wir verlassen die Routine OpenExternalFile.
Der nächste Schritt ist das Separieren von Pfad und Dateinamen. Dafür verwenden wir die lokale Variable path$ denn der Parameter file$ wird später noch gebraucht. Die WHILE Schleife sucht jeweils den nächsten Backslash. Aus "C:\GEOS\DOCUMENT\NAME.EXT" wird so schrittweise "GEOS\DOCUMENT\ NAME.EXT", "DOCUMENT\NAME.EXT" und schließlich "NAME.EXT". Das ist der Dateiname, also speichern wir ihn in fileName$. Der Pfad ist dann alles links davon, mit Ausnahme des letzten Backslash-Zeichens.
Jetzt können wir mit SetCurrentPath in den richtigen Ordner wechseln und mit der Methode OpenDocument das Dokument öffnen. Abschließend rufen wir DoReadDataFromDoc und DoUpdateDocButtons um die UI zu updaten.
15.10 Ein einfaches BeispielAm Beispiel der Programms "Yellow Notes", das im Ordner "Beispiele\Objekte \Dateiarbeit" gefunden werden kann, soll gezeigt werden, wie man die zum Dokumentinterface gehörende Routinen an das eigene Programm anpassen kann.Kernobjekte des Programms sind ein Textobjekt (YNotesText) und ein Menu (YNotesColorMenu) mit zwei ColorSelektoren (YNotesTextColor und YNotesBackColor) für die Vordergrund- und die Hintergrundfarbe. Sehr häufig ist es sinnvoll, die Dokumentdaten in einer Struktur zu speichern. Das vereinfacht den Zugriff auf die Daten und deren Verwaltung, insbesondere das Speichern in einer Dokumentdatei, enorm. Für das "Yellow-Notes"-Beispiel benötigen wir die Farben von Text und Hintergrund sowie den Notiztext selbst. Außerdem haben wir 8 Word Reserve vorgesehen, die wir später zur kompatiblen Erweiterung des Programms verwenden können. |
STRUCT NotesData backcolor, textColor as word reserve[8] as word text as String(1024) End STRUCT |
|
Als Dokumentdatei wählen wir eine GEOS Datendatei. Eine DOS-Datei sollte man nur verwenden, wenn es erforderlich ist. Der Zugriff auf eine GEOS Datendatei ist genau so einfach wie der auf eine DOS-Datei, aber man kann ein Token und ein CreatorToken vergeben, so dass die Verknüpfung mit dem zugehörigen Programm ohne Eingriff in die GEOS.INI erfolgt.
Wie am Anfang des Kapitels beschrieben beschränkt sich die Anpassung des Dokument-Interfaces auf die folgenden Routinen:
|
SUB DoInitDocumentGuardian(guardian as object) DIM dc as DocumentConfigStruct guardian.buttonhandler = DocumentAndToolButtonHandler dc.noDocumentString$ = "leer" dc.nameForNew$ = "Notiz " dc.fileType = GFT_DATA dc.creatorToken.tokenChars = "YNot" dc.creatorToken.manufid = 16600 dc.token.tokenChars = "YNOD" dc.token.manufid = 16600 dc.matchFlags = DOC_MATCH_TOKEN guardian.ConfigData = dc END SUB |
|
|
SUB YNotesInitializeDocument () DIM notes as NotesData notes.textColor = BLACK notes.backColor = YELLOW notes.text = "" FileSetPos DocumentObj.documentHandle , 0 FileWrite DocumentObj.documentHandle , notes, sizeof(NotesData) END SUB |
|
|
SUB YNotesUpdateUI ()
IF DocumentObj.documentState THEN
YNotesText.enabled = TRUE
YNotesColorMenu.enabled = TRUE
ELSE
YNotesText.enabled = FALSE
YNotesColorMenu.enabled = FALSE
End IF
END SUB
|
|
|
SUB DoReadDataFromDoc ()
DIM notes as NotesData
IF DocumentObj.documentHandle == NullFile() THEN
YNotesText.text$ = ""
ELSE
' Text und Farben updaten
FileSetPos DocumentObj.documentHandle, 0
notes = FileRead DocumentObj.documentHandle,sizeof(NotesData)
YNotesText.textColor = notes.textColor
YNotesText.backColor = notes.backColor
YNotesText.text$ = notes.text
YNotesTextColor.csColor = notes.textColor
YNotesBackColor.csColor = notes.backColor
End IF
END SUB
|
|
|
SUB DoSaveDataToDoc (fh as FILE) DIM notes AS NotesData notes.textColor = YNotesText.textColor notes.backColor = YNotesText.backColor notes.text = YNotesText.text$ FileSetPos fh, 0 FileWrite fh, notes, sizeof(NotesData) END SUB |
|
|
SUB DoEnterDocumentPath (forNew as Integer)
IF forNew THEN
SetStandardPath SP_TOP
ELSE
SetStandardPath SP_DOCUMENT
END IF
END SUB
|
|
|
SUB DoSetDocModified (modi as INTEGER)
IF modi THEN
' ist schon "modified"? => Return
IF DocumentObj.documentState AND DOCS_MODIFIED THEN RETURN
DocumentObj.SetDocumentState DOCS_MODIFIED, 0
ELSE
' ist schon "not modified"? => Return
IF (DocumentObj.documentState AND DOCS_MODIFIED) = 0 THEN RETURN
DocumentObj.SetDocumentState 0, DOCS_MODIFIED
YNotesText.modified = FALSE
End IF
DoUpdateDocButtons
END SUB
|
|
|
SUB DoRevertDoc () END SUB |
|
Der OnModified Handler des Textobjekts ist sehr einfach. Er ruft nur DoSetDocModified (TRUE). Diese Routine informiert das DocumentGuardian-Objekt und ruft DoUpdateDocButtons. Mehr ist nicht zu tun. |
TEXTACTION TextModifedHandler DoSetDocModified (TRUE) END ACTION |
|
ColorSelector Objekte haben die Eigenart, dass der Handler öfter gerufen wird, als es für unsere Zwecke sinnvoll ist, z.B. wenn sie erstmalig auf dem Schirm erscheinen. Das könnte dazu führen, dass das Dokument als "geändert" markiert wird, obwohl es eigentlich nicht geändert wurde. Deswegen fragen wir die aktuellen Farben des Textobjekts ab und rufen DoSetDocModified (TRUE) nur dann, wenn sie sich wirklich geändert haben. |
COLORACTION NewColorHandler
dim tc, bc
tc = YNotesText.textColor
bc = YNotesText.backColor
YNotesText.textColor = YNotesTextColor.csIndexColor
YNotesText.backColor = YNotesBackColor.csIndexColor
IF (tc <> YNotesText.textColor) OR (bc <> YNotesText.backColor) THEN
DoSetDocModified (TRUE)
End IF
END ACTION
|
Den kompletten Quellcode für dieses Beispiel sowie die Iconeditor-Datei mit den Iconbildern findet man im Ordner "Beispiele\Objekte\Dateiarbeit".
^ |