// Text of project TXWord written on 2/4/97 at 11:57 AM // Beginning of text file Constants /* ** Newton Developer Technical Support Sample Code ** ** TXWord, a Newton Text Editor (protoTXView) example ** ** by J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1996-7 by Apple Computer, Inc. All rights reserved. ** ** You may incorporate this sample code into your applications without ** restriction. This sample code has been provided "AS IS" and the ** responsibility for its operation is 100% yours. You are not ** permitted to modify and redistribute the source as "DTS Sample Code." ** If you are going to re-distribute the source, we require that you ** make it clear in the source that the code was descended from ** Apple-provided sample code, but that you've made changes. */ constant kSoupName := "TXWord:DTS"; // Get the picture from resources OpenResFile(HOME & "TXWord.rsrc"); DefConst('kJustSayNoPict, GetPICTAsBits("JustSayNo", nil)); CloseResFile(); constant kAboutString := "TXWord is Newton DTS Sample Code by J. Christopher Bell."&& "This sample shows how to use the protoTXView object and the MakeFontMenu function."; constant kHelpString := "To use the Censor or UpperCase buttons, first select"&& "text. 'UpperCase' upper-cases the text. 'Censor' replaces text with a graphic."; constant kScrollOverlap := 20; // End of text file Constants // Beginning of file TXWord.t // Before Script for TXWord Base View /* ** Newton Developer Technical Support Sample Code ** ** TXWord, a Newton Text Editor (protoTXView) example ** ** by J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1996-7 by Apple Computer, Inc. All rights reserved. ** ** You may incorporate this sample code into your applications without ** restriction. This sample code has been provided "AS IS" and the ** responsibility for its operation is 100% yours. You are not ** permitted to modify and redistribute the source as "DTS Sample Code." ** If you are going to re-distribute the source, we require that you ** make it clear in the source that the code was descended from ** Apple-provided sample code, but that you've made changes. */ nil TXWord Base View := {viewBounds: {left: 0, top: 0, right: 0, bottom: 0}, viewFormat: 83951873, viewFlags: 1, declareSelf: 'base, viewJustify: 240, viewClass: 74 /* clView */ }; TXWord Base View_v229_0 := {title: kAppName, _proto: @229 /* protoTitle */}; AddStepForm(TXWord Base View, TXWord Base View_v229_0); frame := {viewBounds: {left: 15, top: 30, right: -15, bottom: -30}, viewFlags: 513, viewFormat: 336, viewJustify: 240, viewClass: 74 /* clView */ }; AddStepForm(TXWord Base View, frame); editor := {viewJustify: 8432, viewBounds: {left: 0, top: 0, right: 0, bottom: 0}, viewFlags: 33553953, viewSetupFormScript: func() begin inherited:?viewSetupFormScript(); // store the data in a Virtual Binary Object (VBO) on a store, not in NewtonScript memory :SetStore(GetDefaultStore()); // Used in our GetTextHeight method. It is cached in a slot for speed reasons self.ourGlobalBoxTopCoordinate := :GlobalBox().top; // Set the "height" to a very big number. That will allow us to see all of the text. Note that // if you pass a non-nil value to the isPaged argument (the first arg), you must set the height // to the size of an individual page. constant kMaxScrollHeight := 32767; :SetGeometry(nil, :localbox().right, kMaxScrollHeight, Relbounds(0,0,0,0)); end, viewSetupDoneScript: func() begin :SetupScrollers(); // our method which sets up scrollers inherited:?viewSetupDoneScript(); :GetSoupData(); // Our method to get information from soup data SetKeyView(self, 0); // set the keyboard focus since we ARE full screen & keyboard-savvy end, GetSoupData: // We store our data in a soup. If asked to get it, look in the soup and grab // the first entry we see (there shouldn't be more than one...) func() begin local soup := GetUnionSoup(kSoupName); if soup then begin local query := soup:Query(nil); // Set our slot with the entry, if there theSoupEntry := query:entry(); if theSoupEntry then self:Internalize(theSoupEntry.EditorData); else if kDebugOn then print("TXWord Soup found, but no entries."); // Since there is only supposed to be one entry, when debugging we check for other ones if kDebugOn and query:clone():next() then Throw('|evt.ex.msg|, kAppName && "debug failure: duplicate entry found!"); end; else if kDebugOn then print("No TXWord soup found."); end, SetSoupData: func() begin // For performance reasons, ONLY Externalize the protoTXView // if the user has modified something in the text!! if not :IsModified() then return; // The external version of the data is NOT readable. Do NOT poke around in it or modify it! // The only things you can do with it are the Internalize protoTXView method // and searching it using the TXViewFinder proto! local externalData := self:Externalize(); // if we already have a soup entry, change that one if theSoupEntry then begin theSoupEntry.EditorData := externalData; EntryChange(theSoupEntry); end; else begin local soup := GetUnionSoupAlways(kSoupName); if soup then begin //¥¥ workaround for Newton 2.1 prerelease bug related to soupdefs! if not GetDefaultStore():GetSoup(kSoupName) then GetDefaultStore():CreateSoup(kSoupName, []); theSoupEntry := soup:AddToDefaultStoreXmit({EditorData: externalData}, nil); end; else begin soup := GetDefaultStore():CreateSoup(kSoupName, []); theSoupEntry := soup:AddXmit({EditorData: externalData}, nil); end; end; end, theSoupEntry: nil, viewQuitScript: func() begin :SetSoupData(); // Our method to get information from soup data inherited:?viewQuitScript(); end, Censor: // This replaces certain phrases with a shape indicating "censored" // This message is sent via a button in the status bar. func() begin local graphicSpecForTXView := { class: 'graphics, shape: MakeShape(kJustSayNoPict) }; // Get the hilited range and replace it with the censor shape. local range := editor:GetHiliteRange(); editor:Replace(range, graphicSpecForTXView, true); end, viewWordScript: // protoTXView does NOT handle handwriting recognition. // Instead, we can use viewWordScript to find the best word and add it. // We do NOT provide the same hooks as clParagraphView, for instance you will not // get the "correction" picker that you sometimes get when double tapping on words. // nor will you be able to write letters over incorrect letters to change them, // nor will you be able to implement most of the editing gestures function. func(unit) begin // Get the array of likely words for recognition. The first item is the most likely word. local words := GetWordArray(unit); // Get the range of "selected" data. Note: range.first may equal range.last local range := editor:GetHiliteRange(); // we want to see if we need to add a "space" char before and/or after the word // So we check to see if one is already there local first := range.first - 1; local last := range.last + 1; local totalChars := :GetCountCharacters(); local textToAdd := ""; if first >= 0 and totalChars > 0 then if :GetRangeData({first: first, last: first + 1}, 'text)[0] <> $ then textToAdd := textToAdd & " "; // add a space char 'before' the word textToAdd := textToAdd & words[0]; // Add the "most likely" word returned by recognition if last < totalChars then if :GetRangeData({first: last, last: last + 1}, 'text)[0] <> $ then textToAdd := textToAdd & " "; // add a space char 'after' the word editor:Replace(range, {text: textToAdd}, true); true; // Return true if input has been completely handled, nil otherwise end, GetTextHeight: // We provide this method because when isPaged (an argument to :SetGeometry(...)) is nil, // we must set the height in SetGeometry to a large number. Unforunately, this means // that the :GetTotalHeight() method returns our arbitrary and large number, rather than // the actual text height. Refer to the protoTXView documentation for more information // about the differences between paged and non-paged views. // // This method returns the height of the text that exists in the view. // // Note that if your text view is paged, :GetTotalHeight() will return the correct value! // (that is, the number of pages actually used times the page height.) func() begin local lastChar := :GetCountCharacters(); local maxInfo := :CharToPoint(lastChar); local globalY := maxInfo.y + maxInfo.lineHeight; // take the scroll position into consideration... globalY := globalY + :GetScrollValues().y; globalY - ourGlobalBoxTopCoordinate; // ourGlobalBoxTopCoordinate is set in viewSetupFormScript end, ViewUpdateScrollersScript: // we need to change the "scroll arrows" func(updateMaxValue, scrolled) begin // Normally, when we add text to a protoTXview, the updateMaxVal parameter would be true. However, // since we are using a non-paged view, the max height of the view never changes because it // was set to a very large number in the viewSetupFormScript. Because of this, we need to check // and see if the text height has changed each time viewUpdateScrollersScript is called. scroller.scrollRect.bottom := :GetTextHeight(); // Use our special method to find the text height scroller.dataRect := scroller.scrollRect; // If the view has been scrolled then reset the scroller y value if scrolled then scroller.yPos := :GetScrollValues().y; // Finally, tell the scrollers to adjust themselves if call kViewIsOpenFunc with (scroller) then scroller:AdjustArrows(); end, ViewScroll2DScript: // This method is called by the scroller func(direction, extras) begin if direction = 'up then :Scroll({x:0, y: -(:LocalBox().bottom - kScrollOverlap)}); else // direction = 'down :Scroll({x:0, y: :LocalBox().bottom - kScrollOverlap}); // Set the new yPosition of the scroller scroller.yPos := :GetScrollValues().y; // Update the scrollers. This is called so that the // scroller can set the arrow to the proper graphic // to show whether there is more data in a given // direction. if call kViewIsOpenFunc with (scroller) then scroller:AdjustArrows(); // RefreshViews is necessary to update the scroller. protoTXView // automatically updates itself when you scroll. RefreshViews(); end, SetupScrollers: // our own method to set up scroller stuff after the editor view and the scroller arrows have been created func() begin scroller.scrollview := editor; scroller.viewRect := editor:GetScrollableRect(); // Note: GetScrollableRect returns the scroll area in global coordinates local visualArea := :GetScrollableRect(); // ommitting the ruler area (if it is visible) // Setup the viewRect and the scrollRect for the scrollers. This information is used // by the scroller so that it knows when you have scrolled to either the beginning or // the end of the text. // ViewRect represents the portion of the view that contains text. // The ScrollRect represents the entire view. scroller.viewRect := Clone(:LocalBox()); scroller.viewRect.bottom := visualArea.bottom - visualArea.top; scroller.scrollRect := RelBounds(0,0, scroller.viewRect.right, :GetTotalHeight()); end, _proto: @826 /* protoTXView */ }; AddStepForm(frame, editor); StepDeclare(TXWord Base View, editor, 'editor); scroller := {viewFlags: 33, dataRect: {left: 0, top: 0, right: 0, bottom: 0}, scrollRect: {left: 0, top: 0, right: 0, bottom: 0}, viewRect: {left: 0, top: 0, right: 0, bottom: 0}, _proto: @656 /* protoUpDownScroller */ }; AddStepForm(frame, scroller); StepDeclare(TXWord Base View, scroller, 'scroller); TheButtonBar := { menuLeftButtons: [ { _proto: protoInfoButton, DoInfoAbout: func() :Notify(kNotifyQAlert, kAppName, kAboutString), DoInfoHelp: func() :Notify(kNotifyQAlert, kAppName, kHelpString) }, { _proto: protoKeyboardButton }, { _proto: protoPopupButton, text: " Font", ButtonClickScript: func() begin // Extract the font information for the selected text and create the font menu self.font := editor:GetContinuousRun(); popup := MakeFontMenu(font, nil, 'none, 'none); // display only fonts inherited:?ButtonClickScript(); end, pickActionScript: func(index) begin // Change the text to the newly selected font local range := editor:GetHiliteRange(); editor:ChangeRangeRuns(range, {family: popup[index].family}, nil, true); inherited:?pickActionScript(index); end; }, { _proto: protoPopupButton, text: " Size", ButtonClickScript: func() begin // Extract the font information for the selected text and create the font menu self.font := editor:GetContinuousRun(); popup := MakeFontMenu(font, 'none, nil, 'none); // display only sizes inherited:?ButtonClickScript(); end, pickActionScript: func(index) begin // Change the text to the newly selected size local range := editor:GetHiliteRange(); editor:ChangeRangeRuns(range, {size: popup[index].size}, nil, true ); inherited:?pickActionScript(index); end; } ] , menuRightButtons: [ { _proto: protoTextButton, text: "UpperCase", ButtonClickScript: func() begin // Extract the text and styles of the hilited range local range := editor:GetHiliteRange(); local theText := editor:GetRangeData(range, 'text); local theStyles := editor:GetRangeData(range, 'styles); Upcase(theText); // change the string "in place" to upper case characters // if we left the styles slot nil, the styles would be reset // to match the style at the beginning of the run. editor:Replace(range, {text: theText, styles: theStyles}, true); // Ensure the new text is visible and selected editor:SetHiliteRange(range, true, true); // Call the inherited buttonClickScript (unhilite the button) inherited:?ButtonClickScript(); end, }, { _proto: protoTextButton, text: "Censor", buttonClickScript: func() editor:Censor(); } ], _proto: @401 /* newtStatusBar */ }; AddStepForm(TXWord Base View, TheButtonBar); constant |layout_TXWord.t| := TXWord Base View; // End of file TXWord.t