CtlWord9

CtlWord9 contains the automation interface for Microsoft Word 9.0. The objects contained in this module are explained in the corresponding help file (VBAWRD9.CHM) which is located on the MS Office CD but is not installed by default.
For more information about automation controllers in BlackBox see the CtlDocu.

Since all calls to the CtlWord9 interface go across the border of the process, they are not as efficient as ordenary calls in BlackBox. They can be as much as 50.000 times slower.

Getting Started

The interface of CtlWord9 is quite big, but generally not difficult to understand. To start with, there are only a few Objects Types that need to be known. Look at the Interface of CtlWord9 for more.

CtlWord9.Application
Represents the Word application. A new application can be obtained by calling NewApplication() which starts a new Word application and returns a application object. Windows might reuse this application when the user starts Word himself. Therefore closing an application object might also close documents of the user, while not closing it leaves a unused Word application in the memory. It is recommended never to open or close applications by yourself. Use the following procedures instead to get an Application object:

	VAR
		app: CtlWord9.Application;
		connect_doc: CtlWord9.Document;

	PROCEDURE Connect;
	BEGIN
		connect_doc := CtlWord9.NewDocument();
		connect_doc.Windows().Item(CtlT.Int(1)).PUTVisible(FALSE);
		app := connect_doc.Application()
	END Connect;

	PROCEDURE Disconnect;
	BEGIN
		connect_doc.Close(NIL, NIL, NIL); app := NIL
	END Disconnect;

CtlWord9.Document
Represents a Word document. Such a document can be obtained by calling the methods
app.ActiveDocument(), app.Documents().Item(CtlT.Int(1)), app.Documents().Add(NIL, NIL, NIL, NIL) and app.Documents().Open(CtlT.Str(filename), NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL) where app is an application object, or simply by calling CtlWord9.NewDocument().

CtlWord9.Range
Represents a part of the content of a document. It can be obtained in various ways from a document. The easiest way is to use the method CtlWord9.Document.Range(CtlT.Int(beg),CtlT.Int(end)) to access a part of the document, or CtlWord9.Document.Content() to get its whole content.
To edit the text of a Range, the methods CtlWord9.Range.Text() and CtlWord9.Range.PUTText() can be used. The location of the Range can be moved by various CtlWord9.Range.MoveXXX methods, the CtlWord9.Range.Next method or by setting the start and the end point to a different value with CtlWord9.Range.PUTStart and CtlWord9.Range.PUTEnd.

CtlWord9.Font
Represents a font of a text. It can be obtained from a CtlWord9.Range object by calling the CtlWord9.Range.Font() method. Attention: Since the font of a Range might not be homogeneous, not all properties might be defined.

A good starting point to use CtlWord9 is also the ObxWordEditExample.



MODULE ObxWordEdit;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	purpose	= "Some examples of how the Word Automation Interface 9.0 can be used."
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT CtlWord9, CtlT, StdLog, Fonts, Services;

	VAR
		app: CtlWord9.Application;
		connectDoc: CtlWord9.Document;

	PROCEDURE GetApplication (): CtlWord9.Application;
	BEGIN
		RETURN app
	END GetApplication;

	(* Connecting / Disconnecting *)

	PROCEDURE Connect*;
	(* Connects to a Word application. If Word is not running, it is started. *)
	BEGIN
		connectDoc := CtlWord9.NewDocument();
		connectDoc.Windows().Item(CtlT.Int(1)).PUTVisible(FALSE);
		app := connectDoc.Application();
		StdLog.String(' Connected to Word.'); StdLog.Ln()
	END Connect;

	PROCEDURE Disconnect*;
	(* Disconnects from Word. If nobody else is using the application, it is terminated. *)
	BEGIN
		connectDoc.Close(NIL, NIL, NIL);
		app := NIL;
		StdLog.String(' Disconnected to Word.'); StdLog.Ln()
	END Disconnect;

	(* Starting / Quitting *)

	PROCEDURE Start*;
	(* Starts a new Word application. *)
	BEGIN
		app := CtlWord9.NewApplication();
		app.Options().PUTSmartCutPaste(FALSE);
		app.PUTVisible(TRUE);
		StdLog.String(' Word started.'); StdLog.Ln()
	END Start;

	PROCEDURE Quit*;
	(* Quits Word. *)
	BEGIN
		app.Quit(CtlT.Bool(FALSE),NIL,NIL); app := NIL;
		StdLog.String(' Word quited.'); StdLog.Ln()
	END Quit;

	PROCEDURE Restart*;
	(* Restarts Word. *)
	BEGIN
		Quit; Start
	END Restart;

	(* File *)

	PROCEDURE NewDoc*;
	(* Creates a new document using CtlWord9.NewDocument() and makes it visible. *)
	(* The document is created in the oldest running Word. If there is no Word running, it is started. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := CtlWord9.NewDocument();
		doc.Windows().Item(CtlT.Int(1)).PUTVisible(TRUE);
		StdLog.String("New Document created. "); StdLog.Ln
	END NewDoc;

	PROCEDURE CreateDoc*;
	(* Creates a new, visible document using CtlWord9.Application.Documents().Add. *)
	(* It is only visible, if the application is visible. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().Documents().Add(NIL, NIL, NIL, CtlT.Bool(TRUE));
		StdLog.String(' Document '); StdLog.String(doc.FullName());
		StdLog.String(' created.'); StdLog.Ln
	END CreateDoc;

	PROCEDURE CreateInvisibleDoc*;
	(* Creates a new, invisible document. It is also invisible, if the application is visible. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().Documents().Add(NIL, NIL, NIL, CtlT.Bool(FALSE));
		StdLog.String(' Document '); StdLog.String(doc.FullName());
		StdLog.String(' created invisible.'); StdLog.Ln
	END CreateInvisibleDoc;

	PROCEDURE OpenDoc (filename: ARRAY OF CHAR);
	(* Opens the file filename. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().Documents().Open(CtlT.Str(filename),NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL);
		StdLog.String(' Document '); StdLog.String(doc.FullName());
		StdLog.String(' opened.'); StdLog.Ln
	END OpenDoc;

	PROCEDURE OpenDocTest*;
	(* Opens a test file. *)
	BEGIN
		OpenDoc('D:\Users\Juerg\test.doc')
	END OpenDocTest;

	PROCEDURE CloseDoc*;
	(* Closes the active document without saving. *)
		VAR doc: CtlWord9.Document; name: CtlT.Strg;
	BEGIN
		doc := GetApplication().ActiveDocument();
		name := doc.FullName();
		doc.Close(CtlT.Int(CtlWord9.wdDoNotSaveChanges),NIL,NIL);
		StdLog.String(' Document '); StdLog.String(name);
		StdLog.String(' closed.'); StdLog.Ln
	END CloseDoc;

	PROCEDURE SaveAndCloseDoc*;
	(* Saves the active document and closes it. *)
		VAR doc: CtlWord9.Document; name: CtlT.Strg;
	BEGIN
		doc := GetApplication().ActiveDocument();
		name := doc.FullName();
		doc.Close(CtlT.Int(CtlWord9.wdSaveChanges),NIL,NIL);
		StdLog.String(' Document '); StdLog.String(name);
		StdLog.String(' saved and closed.'); StdLog.Ln
	END SaveAndCloseDoc;

	PROCEDURE SaveDoc*;
	(* Saves the active document. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		doc.Save;
		StdLog.String(' Document '); StdLog.String(doc.FullName());
		StdLog.String(' saved.'); StdLog.Ln
	END SaveDoc;

	PROCEDURE Print*;
	(* Prints the active document. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		doc.PrintOut(NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL);
		StdLog.String(' Document '); StdLog.String(doc.FullName());
		StdLog.String(' printed.'); StdLog.Ln
	END Print;

	(* Application *)

	PROCEDURE DispDocs*;
	(* Displays the full names of all the open documents. *)
		VAR count, i: INTEGER; docs: CtlWord9.Documents;
	BEGIN
		docs := GetApplication().Documents();
		count := docs.Count();
		StdLog.Int(count); StdLog.String(' Documents available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(docs.Item(CtlT.Int(i)).FullName()); StdLog.Ln
		END
	END DispDocs;

	PROCEDURE DispFontNames*;
	(* Displays the names of the available fonts in Word. *)
		VAR fontNames : CtlWord9.FontNames; count, i: INTEGER;
	BEGIN
		fontNames := GetApplication().FontNames();
		count := fontNames.Count();
		StdLog.Int(count);
		StdLog.String(' FontNames available.');
		StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(fontNames.Item(i)); StdLog.Ln
		END
	END DispFontNames;

	PROCEDURE DispBBFontNames*;
	(* Displays the names of the available fonts in BlackBox. There are more than in Word. *)
		VAR t: Fonts.TypefaceInfo;
	BEGIN
		StdLog.String(' BB FontNames:');
		t := Fonts.dir.TypefaceList();
		WHILE t # NIL DO
			StdLog.String(t.typeface); StdLog.Ln;
			t := t.next
		END
	END DispBBFontNames;

	PROCEDURE DispLanguages*;
	(* Displays the languages available in Word. *)
		VAR count: INTEGER; languages: CtlWord9.Languages; lang: CtlWord9.Language; enum: CtlT.Enumerator;
	BEGIN
		languages := GetApplication().Languages();
		count := languages.Count();
		StdLog.Int(count); StdLog.String(' Languages available.'); StdLog.Ln;
		enum:= languages._NewEnum();
		lang := CtlWord9.ThisLanguage(enum.First());
		WHILE lang # NIL DO
			StdLog.String(lang.NameLocal());
			StdLog.Ln;
			lang := CtlWord9.ThisLanguage(enum.Next())
		END
	END DispLanguages;

	PROCEDURE DispLanguagesAndDictionaries*;
	(* Displays the languages available in Word and whether they have a dictionary. *)
	(* Attention: ActiveSpellingDictionary traps if there is no dictionary available... *)
		VAR count: INTEGER; languages : CtlWord9.Languages; lang: CtlWord9.Language; enum:CtlT.Enumerator;
	BEGIN
		languages := GetApplication().Languages();
		count := languages.Count();
		StdLog.Int(count); StdLog.String(' Languages available.'); StdLog.Ln;
		enum:= languages._NewEnum();
		lang := CtlWord9.ThisLanguage(enum.First());
		WHILE lang # NIL DO
			StdLog.String(lang.NameLocal());
			IF lang.ActiveSpellingDictionary() # NIL THEN
				StdLog.String(' (Dict)')
			END;
			StdLog.Ln;
			lang := CtlWord9.ThisLanguage(enum.Next())
		END
	END DispLanguagesAndDictionaries;

	PROCEDURE UseSmartCutPaste*;
	(* Sets the option SmartCutPaste. *)
	BEGIN
		GetApplication().Options().PUTSmartCutPaste(TRUE);
		StdLog.String(' SmartCutPaste turned on.'); StdLog.Ln
	END UseSmartCutPaste;

	(* Visibility *)

	PROCEDURE MakeWordVisible*;
	(* Makes all documents visible, also the ones that were created invisible. *)
	BEGIN
		GetApplication().PUTVisible(TRUE);
		StdLog.String(' Word made visible.'); StdLog.Ln
	END MakeWordVisible;

	PROCEDURE MakeWordInvisible*;
	(* Makes all documents invisible. *)
	BEGIN
		GetApplication().PUTVisible(FALSE);
		StdLog.String(' Word made invisible.'); StdLog.Ln
	END MakeWordInvisible;

	PROCEDURE MakeWinVisible*;
	(* Makes the first window of the active document visible. *)
	BEGIN
		GetApplication().ActiveDocument().Windows().Item(CtlT.Int(1)).PUTVisible(TRUE);
		StdLog.String(' Window made visible.'); StdLog.Ln
	END MakeWinVisible;

	PROCEDURE MakeWinInvisible*;
	(* Makes the first window of the active document invisible. *)
	BEGIN
		GetApplication().ActiveDocument().Windows().Item(CtlT.Int(1)).PUTVisible(FALSE);
		StdLog.String(' Window made invisible.'); StdLog.Ln
	END MakeWinInvisible;

	PROCEDURE IsWinVisible*;
	(* Displays whether the first window of the active document is visible. *)
	BEGIN
		StdLog.Bool(GetApplication().ActiveDocument().Windows().Item(CtlT.Int(1)).Visible()); StdLog.Ln
	END IsWinVisible;

	(* Document *)

	PROCEDURE Undo*;
	(* Undoes the last action. Actions, such a typing characters, can be merged to one action by Word. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		IF doc.Undo(CtlT.Int(1)) THEN
			StdLog.String(' Undone.')
		ELSE
			StdLog.String(' Undo not possible.')
		END;
		StdLog.Ln
	END Undo;

	PROCEDURE Redo*;
	(* Redoes the last undone action. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		IF doc.Redo(CtlT.Int(1)) THEN
			StdLog.String(' Redone.')
		ELSE
			StdLog.String(' Redo not possible.')
		END;
		StdLog.Ln
	END Redo;

	PROCEDURE Protect*;
	(* Protects the active document. *)
	BEGIN
		GetApplication().ActiveDocument().Protect(CtlWord9.wdAllowOnlyFormFields, NIL, NIL);
		StdLog.String('Document locked.'); StdLog.Ln
	END Protect;

	PROCEDURE Unprotect*;
	(* Unprotects the active document. *)
	BEGIN
		GetApplication().ActiveDocument().Unprotect(NIL);
		StdLog.String('Document unlocked.'); StdLog.Ln
	END Unprotect;

	(* Accessing the Content of a Document *)

	PROCEDURE DispContent*;
	(* Displays the content of the active document. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		StdLog.String('Document content: ');
		StdLog.String(doc.Content().Text()); StdLog.Ln
	END DispContent;

	PROCEDURE DispParagraphs*;
	(* Displays the paragraphs of the active document. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; paras: CtlWord9.Paragraphs;
	BEGIN
		doc := GetApplication().ActiveDocument();
		paras := doc.Paragraphs();
		count := paras.Count();
		StdLog.Int(count); StdLog.String(' Paragraphs available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(paras.Item(i).Range().Text());
			StdLog.Ln; StdLog.Ln
		END
	END DispParagraphs;

	PROCEDURE DispListParagraphs*;
	(* Displays the ListParagraphs of the active document. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; paras: CtlWord9.ListParagraphs;
	BEGIN
		doc := GetApplication().ActiveDocument();
		paras := doc.ListParagraphs();
		count := paras.Count();
		StdLog.Int(count); StdLog.String(' ListParagraphs available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(paras.Item(i).Range().Text());
			StdLog.Ln; StdLog.Ln
		END
	END DispListParagraphs;

	PROCEDURE DispLists*;
	(* Displays the Lists of the active document. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; lists: CtlWord9.Lists;
	BEGIN
		doc := GetApplication().ActiveDocument();
		lists := doc.Lists();
		count := lists.Count();
		StdLog.Int(count); StdLog.String(' Lists available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(lists.Item(i).Range().Text());
			StdLog.Ln; StdLog.Ln
		END
	END DispLists;

	PROCEDURE DispWords*;
	(* Displays the Words of the active document, using the CtlWord9.Document.Words method. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; words: CtlWord9.Words;
	BEGIN
		doc := GetApplication().ActiveDocument();
		words := doc.Words();
		count := words.Count();
		StdLog.Int(count); StdLog.String(' Words available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(words.Item(i).Text()); StdLog.Ln
		END
	END DispWords;

	PROCEDURE DispWords2*;
	(* Displays the Words of the active document, using the CtlWord9.Range.Next method. *)
		VAR len: INTEGER; doc: CtlWord9.Document; r: CtlWord9.Range;
	BEGIN
		doc := GetApplication().ActiveDocument();
		len := doc.Characters().Count();
		r := doc.Range(CtlT.Int(0),CtlT.Int(0));
		StdLog.String(' Words: '); StdLog.Ln;
		REPEAT
			r := r.Next(CtlT.Int(CtlWord9.wdWord), NIL);
			StdLog.String(r.Text()); StdLog.Ln
		UNTIL r.End() >= len
	END DispWords2;

	PROCEDURE DispWords3*;
	(* Displays the Words of the active document, using the CtlWord9.Range.MoveEnd method. *)
		VAR moved, lastEnd: INTEGER; doc: CtlWord9.Document; r: CtlWord9.Range;
	BEGIN
		doc := GetApplication().ActiveDocument();
		r := doc.Range(CtlT.Int(0),CtlT.Int(0));
		lastEnd := -1;
		StdLog.String(' Words: '); StdLog.Ln;
		REPEAT
			lastEnd := r.End();
			moved := r.MoveEnd(CtlT.Int(CtlWord9.wdWord), CtlT.Int(1));
			StdLog.String(r.Text()); StdLog.Ln;
			r.Collapse(CtlT.Int(CtlWord9.wdCollapseEnd))
		UNTIL lastEnd = r.End()
	END DispWords3;

	PROCEDURE DispCharacters*;
	(* Displays the Characters of the active document. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; chars: CtlWord9.Characters;
	BEGIN
		doc := GetApplication().ActiveDocument();
		chars := doc.Characters();
		count := chars.Count();
		StdLog.Int(count); StdLog.String(' Characters available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(chars.Item(i).Text()); StdLog.String(' ')
		END
	END DispCharacters;

	PROCEDURE DispSentences*;
	(* Displays the Sentences of the active document. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; sentences: CtlWord9.Sentences;
	BEGIN
		doc := GetApplication().ActiveDocument();
		sentences := doc.Sentences();
		count := sentences.Count();
		StdLog.Int(count); StdLog.String(' Sentences available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(sentences.Item(i).Text());
			StdLog.Ln; StdLog.Ln
		END
	END DispSentences;

	PROCEDURE DispStoryRanges*;
	(* Displays the StoryRanges of the active document. *)
		VAR count, i: INTEGER; doc: CtlWord9.Document; storyRanges: CtlWord9.StoryRanges;
	BEGIN
		doc := GetApplication().ActiveDocument();
		storyRanges := doc.StoryRanges();
		count := storyRanges.Count();
		StdLog.Int(count); StdLog.String(' StoryRanges available.'); StdLog.Ln;
		FOR i := 1 TO count DO
			StdLog.String(storyRanges.Item(i).Text());
			StdLog.Ln; StdLog.Ln
		END
	END DispStoryRanges;

	PROCEDURE DispRuns*;
	(* Should write the runs of the active document, but it does not work! How can we get the runs? *)
		VAR len: INTEGER; doc: CtlWord9.Document; r: CtlWord9.Range;
	BEGIN
		doc := GetApplication().ActiveDocument();
		len := doc.Characters().Count();
		r := doc.Range(CtlT.Int(0),CtlT.Int(0));
		StdLog.String('Runs: '); StdLog.Ln;
		REPEAT
			r := r.Next(CtlT.Int(CtlWord9.wdCharacterFormatting), NIL);
			StdLog.String(r.Text()); StdLog.Ln
		UNTIL r.End() >= len
	END DispRuns;

	(* Editing Text *)

	PROCEDURE AppendThisText*;
	(* Appends "This Text" to the end of the active document. This means that it is insert in front of the last 0DX. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		doc.Content().InsertAfter('This Text');
		StdLog.String('This Text appended.'); StdLog.Ln
	END AppendThisText;

	PROCEDURE OverwriteLastCharWithA*;
	(* Overwrites the last character, which is always a 0DX, with "A". Word then inserts a new 0DX at the end. *)
		VAR count: INTEGER; doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		count := doc.Characters().Count();
		doc.Range(CtlT.Int(count-1), CtlT.Int(count)).PUTText('A');
		StdLog.String('Last Character overwritten.'); StdLog.Ln
	END OverwriteLastCharWithA;

	PROCEDURE CopyText*;
	(* Copies and inserts the first 10 chars at the beginning of the active document. *)
		VAR doc: CtlWord9.Document; text: CtlT.Strg;
	BEGIN
		doc := GetApplication().ActiveDocument();
		text := doc.Range(CtlT.Int(0),CtlT.Int(10)).Text();
		doc.Range(CtlT.Int(0),CtlT.Int(0)).PUTText(text);
		StdLog.String('Text copied.'); StdLog.Ln
	END CopyText;

	PROCEDURE CopyFormattedText*;
	(* Copies and inserts the first 10 chars at the beginning of the active document, formatted. *)
		VAR doc: CtlWord9.Document; formattedText: CtlWord9.Range;
	BEGIN
		doc := GetApplication().ActiveDocument();
		formattedText := doc.Range(CtlT.Int(0),CtlT.Int(10)).FormattedText();
		doc.Range(CtlT.Int(0),CtlT.Int(0)).PUTFormattedText(formattedText);
		StdLog.String('Formatted text copied.'); StdLog.Ln
	END CopyFormattedText;

	PROCEDURE DeleteText*;
	(* Deletes the first 10 character of the active document. *)
		VAR doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		doc.Range(CtlT.Int(0),CtlT.Int(10)).PUTText('');
		StdLog.String('Text deleted.'); StdLog.Ln
	END DeleteText;

	(* Font *)

	PROCEDURE Disp2ndParagraphFontName*;
	(* Displays the name of the font of the 2nd paragraph of the active document, if it is defined. *)
		VAR doc: CtlWord9.Document; fontName: ARRAY 100 OF CHAR;
	BEGIN
		doc := GetApplication().ActiveDocument();
		fontName := doc.Paragraphs().Item(2).Range().Font().Name()$;
		IF fontName[0] = 0X THEN
			StdLog.String('2nd Paragraph Font Name is not defined (result = empty string)')
		ELSE
			StdLog.String('2nd Paragraph Font Name = '); StdLog.String(fontName)
		END;
		StdLog.Ln
	END Disp2ndParagraphFontName;

	PROCEDURE Is1stParagraphBold*;
	(* Displays whether the 1st paragraph of the active document is bold or not, if it is defined. *)
		VAR bold: INTEGER; doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		bold := doc.Paragraphs().Item(1).Range().Bold();
		StdLog.String('The first paragraph ');
		IF bold = 0 THEN
			StdLog.String('is Not Bold')
		ELSIF bold = -1 THEN
			StdLog.String('is Bold')
		ELSE
			StdLog.String('is neither Bold nor Not Bold')
		END;
		StdLog.String(' (result = '); StdLog.Int(bold); StdLog.String(')'); StdLog.Ln
	END Is1stParagraphBold;

	PROCEDURE Disp1stParagraphFontSize*;
	(* Displays the size of the font of the 1st paragraph of the active document, if it is defined. *)
		VAR size: SHORTREAL; doc: CtlWord9.Document;
	BEGIN
		doc := GetApplication().ActiveDocument();
		StdLog.String('The first paragraph: size =');
		size := doc.Paragraphs().Item(1).Range().Font().Size();
		IF size # CtlWord9.wdUndefined THEN
			StdLog.Real(size)
		ELSE
			StdLog.String('undefined (result = '); StdLog.Real(size); StdLog.String(' )')
		END;
		StdLog.Ln
	END Disp1stParagraphFontSize;

	(* Performance *)

	PROCEDURE Performance*;
		CONST
			wordTries = 1000;
			bbTries = 100000000;
		VAR start, counterTime, wordTime, bbTime : LONGINT; i : INTEGER; opt: CtlWord9.Options;

		PROCEDURE Put (newValue: BOOLEAN);
			VAR value: BOOLEAN;
		BEGIN
			value := newValue
		END Put;

	BEGIN
		IF app = NIL THEN Start END;
		opt := GetApplication().Options();

		start:= Services.Ticks();
		FOR i := 1 TO wordTries DO END;
		counterTime := Services.Ticks() - start;

		start:= Services.Ticks();
		FOR i := 1 TO wordTries DO opt.PUTSmartCutPaste(TRUE) END;
		wordTime := Services.Ticks() - start - counterTime;
		StdLog.Int(wordTries);
		StdLog.String(" PUTSmartCutPaste took ");
		StdLog.Real(wordTime / Services.resolution);
		StdLog.String(" sec."); StdLog.Ln;

		start:= Services.Ticks();
		FOR i := 1 TO bbTries DO END;
		counterTime := Services.Ticks() - start;

		start := Services.Ticks();
		FOR i := 1 TO bbTries DO Put(TRUE) END;
		bbTime := Services.Ticks() - start - counterTime;
		StdLog.Int(bbTries);
		StdLog.String(" BlackBox procedure calls took ");
		StdLog.Real(bbTime / Services.resolution);
		StdLog.String(" sec."); StdLog.Ln;

		StdLog.String("Properties are ");
		StdLog.Int( (wordTime * bbTries) DIV (bbTime * wordTries));
		StdLog.String(" times slower."); StdLog.Ln
	END Performance;

END ObxWordEdit.


 ObxWordEditExample 


Stumbling Blocks

Here are some things we found out using Word with this interface and which one might expect to be differently.
- The CtlWord9.NewApplication() method always creates a new application. If the user also opens a new Word application and there is no Word running, the user gets the SAME application as BlackBox. Never expect that you are the only one using the application. If you documents are invisible, the user sees that there are other documents open in the Window menu, but he is not able to access them.
- The CtlWord9.NewDocument() method creates a new document in the oldest running Word. It has the same effect as if the user chooses Word from the Start menu. If no Word is not running, one is started. If this Document is closed and there is no other open document in that application, the application might also terminated. However, if the application has been created with NewApplication, the application also remains running after the last document has been closed.
- A Word document always ends with a new-line (0DX). If a character is inserted after this mark, Word will insert a new 0DX after that character. If the method Collapse(CtlT.Int(CtlWord9.wdCollapseEnd)) is called for a Range which includes this ending character, the Range will be collapsed BEFORE this character. To avoid problems, this character should not be regarded part of the content.
- All the Collection objects are indexed starting at 1. A collection object is a object that has a name ending in 's' and which has the procedures Item and _NewEnum. However the CtlWord9.Range objects start indexing at 0. Range(CtlT.Int(2), CtlT.Int(4)) for example returns a range of length 2, starting at the 3rd character.
- Languages objects are also collections, but they cannot be indexed using numbers. The only way to access languages is by the language's name, if this is known, or by creating an enumeration object.
- BlackBox has more font available in the Fonts.dir.TypefaceList() than Word in CtlWord9.Application.FontNames().
- The properties of a CtlWord9.Font are possibly undefined. Then, the constant CtlWord9.wdUndefined is used. When the property is set, it has the value -1, and when it is not set, 0. This is not documented in the MSDN. The underline property has different values, according to the CtlWord9.wdUnderlineXXX constants. The CtlWord9.Font.Name() returns an empty string, if there is no typeface defined.
- A CtlWord9.Language can have a dictionary installed. Then the method ActiveSpellingDictionary() returns a dictionary. Otherwise Word might ask if it should install the dictionary for some languages.
- The method CtlWord9.Range.Delete(NIL, NIL) deletes the content of the Range, if it is not empty. however if the content is empty (collapsed), it deletes the character to the right of the Range. Therefore Delete has a similar effect as pressing the delete key. If this is not desirable, use the method CtlWord9.Range.PUTText(''), which does nothing if the content of the Range is empty.
- The unit CtlWord9.wdCharacterFormatting can be used to access the runs of a text. A run is a homogeneous part of the text. However, we have not found a method which would accept this unit. We have not found a way to get runs from Word.
- In Word, every window and the application have a visible property. A window is only visible if both, the property of the document and the application are set to TRUE. Changing the property of the application also changes the properties of all the windows.
