×

VCOF0116

OmniFocus: Note Paragraph Settings

A voice command for setting and restoring the paragraph attributes of the note of the selected project or task.

Scope and Syntax

Language: English (United States 🇺🇸 · Great Britain 🇬🇧 · Australia 🇦🇺 · Canada 🇨🇦) • Scope: OmniFocus

Command phrase variations (words in [brackets] are optional):

Requirements

Video: Note Settings
A set of voice commands for controlling the formatting of the note text of the selected project or task.

Downloads

Using the Command

The following is an explanation of how to use the paragraph attribute editing interface.

Paragraph attributes plug-in dialog

(above) Each of the specified paragraph attributes has its own menu of settings and options. By default, the initial setting for each property is: “No Change”

All but the alignment property menus offer these two options:

  • No Change • When this option is chosen, no changes will be made to the paragraph attribute.
  • Default • When this option is chosen, the default value for the paragraph attribute will be applied to the note text.

NOTE: Immediately following each attribute title, placed within parens, is the current value of that attribute in the note.

IMPORTANT: Attribute changes are applied to all of the note text, not just a selection.

Here is a description of each of the paragraph attributes:

  • Alignment (TextAlignment) • The text alignment of the paragraph. Natural text alignment is realized as left or right alignment depending on the line sweep direction of the first script contained in the paragraph.
  • First Line Head Indent (Number) • The indentation of the first line of the paragraph. A paragraph’s “head” is the side from which you read. This property contains the distance (in points) from the leading margin of a text container to the beginning of the paragraph’s first line. This value is always nonnegative.
  • Head Indent (Number) • The indentation of the paragraph’s lines other than the first. A paragraph’s “head” is the side from which you read. This property contains the distance (in points) from the leading margin of a text container to the beginning of lines other than the first. This value is always nonnegative.
  • Tail Indent (Number) • If positive, this value is the distance from the leading margin (for example, the left margin in left-to-right text). If 0 or negative, it’s the distance from the trailing margin.
  • Line Height Multiple (Number) • The natural line height of the paragraph is multiplied by this factor (if positive) before being constrained by minimum and maximum line height. The default value of this property is 0.0.
  • Line Spacing (Number) • The distance in points between the bottom of one line fragment and the top of the next. This value is always nonnegative.
  • Spacing (Number) • The space after the end of the paragraph. This property contains the space (measured in points) added at the end of the paragraph to separate it from the following paragraph. This value is always nonnegative. The space between paragraphs is determined by adding the previous paragraph’s Paragraph Spacing and the current paragraph’s Paragraph Spacing Before.
  • Spacing Before (Number) • The distance between the paragraph’s top and the beginning of its text content. This property contains the space (measured in points) between the paragraph’s top and the beginning of its text content. The default value of this property is 0.0.
Paragraph Settings


(async () => { try { function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } var synthesizer = new Speech.Synthesizer() var sel = document.windows[0].selection if(sel.tasks.length === 1 || sel.projects.length === 1 ){ var item = sel.databaseObjects[0] } else { throw { name: "Selection Issue", message: "Please select a single project or task." } } if(item.note.length === 0){ throw { name: "Selection Issue", message: "The note of the selected item, contains no text." } } // CREATE VIRTUAL TEXT OBJECT CONTAING CURRENT NOTE var noteObj = new Text("", baseStyle) noteObj.replace(noteObj.range, item.noteText) var nStyle = noteObj.style // GET ATTRIBUTE RUN RANGES var runRanges = noteObj.ranges(TextComponent.AttributeRuns) // TEXT OBJECTS AND STYLE var runRanges = noteObj.ranges(TextComponent.AttributeRuns) // CREATE FORM AND FORM ELEMENTS var txtAlgnCurVal = nStyle.get(Style.Attribute.ParagraphAlignment) var txtAlgnDsplyVal switch(String(txtAlgnCurVal)) { case "[object TextAlignment: Left]": txtAlgnDsplyVal = 'Left' break case "[object TextAlignment: Right]": txtAlgnDsplyVal = 'Right' break case "[object TextAlignment: Center]": txtAlgnDsplyVal = 'Center' break case "[object TextAlignment: Natural]": txtAlgnDsplyVal = 'Natural' break case "[object TextAlignment: Justified]": txtAlgnDsplyVal = 'Justified' break } paragraphAlignmentMenu = new Form.Field.Option( "paragraphAlignment", `Alignment (${txtAlgnDsplyVal})`, TextAlignment.all, null, null ) paragraphAlignmentMenu.allowsNull = true paragraphAlignmentMenu.nullOptionTitle = "No Change" spcBfrCurVal = nStyle.get(Style.Attribute.ParagraphSpacingBefore) spcBfrValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] spcBfrValueStrings = ["Default", "0", "4", "6", "8", "10", "12", "18", "24", "36"] spaceBeforeMenu = new Form.Field.Option( "spaceBefore", `Space Before (${spcBfrCurVal})`, spcBfrValues, spcBfrValueStrings, null ) spaceBeforeMenu.allowsNull = true spaceBeforeMenu.nullOptionTitle = "No Change" spcAftCurVal = nStyle.get(Style.Attribute.ParagraphSpacing) spcAftValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] spcAftValueStrings = ["Default", "0", "4", "6", "8", "10", "12", "18", "24", "36"] spaceAfterMenu = new Form.Field.Option( "spaceAfter", `Space After (${spcAftCurVal})`, spcAftValues, spcAftValueStrings, null ) spaceAfterMenu.allowsNull = true spaceAfterMenu.nullOptionTitle = "No Change" frstLnCurVal = nStyle.get(Style.Attribute.ParagraphFirstLineHeadIndent) frstLnValues = [0, 1, 2, 3, 4, 5, 6, 7] frstLnValueStrings = ["Default", "12", "18", "24", "36", "48", "64", "72"] firstLineIndentMenu = new Form.Field.Option( "firstLineIndent", `1st Line Indent (${frstLnCurVal})`, frstLnValues, frstLnValueStrings, null ) firstLineIndentMenu.allowsNull = true firstLineIndentMenu.nullOptionTitle = "No Change" headCurVal = nStyle.get(Style.Attribute.ParagraphHeadIndent) headValues = [0, 1, 2, 3, 4, 5, 6, 7] headValueStrings = ["Default", "0", "12", "24", "36", "48", "64", "72"] headIndentMenu = new Form.Field.Option( "headIndent", `Head Indent (${headCurVal})`, headValues, headValueStrings, null ) headIndentMenu.allowsNull = true headIndentMenu.nullOptionTitle = "No Change" tailCurVal = nStyle.get(Style.Attribute.ParagraphTailIndent) tailValues = [0, 1, 2, 3, 4, 5, 6, 7] tailValueStrings = ["Default", "0", "12", "24", "36", "48", "64", "72"] tailIndentMenu = new Form.Field.Option( "tailIndent", `Tail Indent (${tailCurVal})`, tailValues, tailValueStrings, null ) tailIndentMenu.allowsNull = true tailIndentMenu.nullOptionTitle = "No Change" lneHgtFctrCurVal = nStyle.get(Style.Attribute.ParagraphLineHeightMultiple) //console.log(lneHgtFctrCurVal) lneHgtFctrValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] lneHgtFctrValueStrings = ["Default", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "2.0"] lineHeightFactorMenu = new Form.Field.Option( "lineHeightFactor", `Line Height Multiple (${lneHgtFctrCurVal})`, lneHgtFctrValues, lneHgtFctrValueStrings, null ) lineHeightFactorMenu.allowsNull = true lineHeightFactorMenu.nullOptionTitle = "No Change" lneSpcCurVal = nStyle.get(Style.Attribute.ParagraphLineSpacing) lneSpcValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] lneSpcValueStrings = ["Default", "0", "1", "2", "3", "4", "6", "8", "10", "12"] lineSpacingMenu = new Form.Field.Option( "lineSpacing", `Line Spacing (${lneSpcCurVal})`, lneSpcValues, lneSpcValueStrings, null ) lineSpacingMenu.allowsNull = true lineSpacingMenu.nullOptionTitle = "No Change" inputForm = new Form() inputForm.addField(paragraphAlignmentMenu) inputForm.addField(spaceBeforeMenu) inputForm.addField(spaceAfterMenu) inputForm.addField(firstLineIndentMenu) inputForm.addField(headIndentMenu) inputForm.addField(tailIndentMenu) inputForm.addField(lineHeightFactorMenu) inputForm.addField(lineSpacingMenu) utterance = createUtterance("Paragraph Settings") synthesizer.speakUtterance(utterance) formPrompt = "Paragraph Settings:" buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) // retrieve attribute settings paragraphAlignment = formObject.values["paragraphAlignment"] spcBfrIndicator = formObject.values["spaceBefore"] spcBfrValue = spcBfrValueStrings[spcBfrIndicator] spcAftIndicator = formObject.values["spaceAfter"] spcAftValue = spcAftValueStrings[spcAftIndicator] firstLineIndentIndicator = formObject.values["firstLineIndent"] firstLineIndentValue = frstLnValueStrings[firstLineIndentIndicator] headIndentIndicator = formObject.values["headIndent"] headIndentValue = headValueStrings[headIndentIndicator] tailIndentIndicator = formObject.values["tailIndent"] tailIndentValue = tailValueStrings[tailIndentIndicator] lineHeightMultipleIndicator = formObject.values["lineHeightFactor"] lineHeightMultiple = lneHgtFctrValueStrings[lineHeightMultipleIndicator] lineSpacingIndicator = formObject.values["lineSpacing"] lineSpacingValue = lneSpcValueStrings[lineSpacingIndicator] // SET ATTRIBUTE VALUES var attsToModify = [] var valuesToApply = [] var changeCount = 0 if(paragraphAlignment){ if(paragraphAlignment === "Default"){ var valueToSet = Style.Attribute.ParagraphAlignment.defaultValue } else { var valueToSet = paragraphAlignment } attsToModify.push(Style.Attribute.ParagraphAlignment) valuesToApply.push(valueToSet) changeCount += 1 } if(spcBfrValue){ if(spcBfrValue === "Default"){ var valueToSet = Style.Attribute.ParagraphSpacingBefore.defaultValue } else { var valueToSet = parseInt(spcBfrValue) } attsToModify.push(Style.Attribute.ParagraphSpacingBefore) valuesToApply.push(valueToSet) changeCount += 1 } if(spcAftValue){ if(spcAftValue === "Default"){ var valueToSet = Style.Attribute.ParagraphSpacing.defaultValue } else { var valueToSet = parseInt(spcAftValue) } attsToModify.push(Style.Attribute.ParagraphSpacing) valuesToApply.push(valueToSet) changeCount += 1 } if(firstLineIndentValue){ if(firstLineIndentValue === "Default"){ var valueToSet = Style.Attribute.ParagraphFirstLineHeadIndent.defaultValue } else { var valueToSet = parseInt(firstLineIndentValue) } attsToModify.push(Style.Attribute.ParagraphFirstLineHeadIndent) valuesToApply.push(valueToSet) changeCount += 1 } if(headIndentValue){ if(headIndentValue === "Default"){ var valueToSet = Style.Attribute.ParagraphHeadIndent.defaultValue } else { var valueToSet = parseInt(headIndentValue) } attsToModify.push(Style.Attribute.ParagraphHeadIndent) valuesToApply.push(valueToSet) changeCount += 1 } if(tailIndentValue){ if(tailIndentValue === "Default"){ var valueToSet = Style.Attribute.ParagraphTailIndent.defaultValue } else { var valueToSet = parseInt(tailIndentValue) } attsToModify.push(Style.Attribute.ParagraphTailIndent) valuesToApply.push(valueToSet) changeCount += 1 } if(lineHeightMultiple){ if(lineHeightMultiple === "Default"){ var valueToSet = Style.Attribute.ParagraphLineHeightMultiple.defaultValue } else { var valueToSet = Number(lineHeightMultiple) } attsToModify.push(Style.Attribute.ParagraphLineHeightMultiple) valuesToApply.push(valueToSet) changeCount += 1 } if(lineSpacingValue){ if(lineSpacingValue === "Default"){ var valueToSet = Style.Attribute.ParagraphLineSpacing.defaultValue } else { var valueToSet = Number(lineSpacingValue) } attsToModify.push(Style.Attribute.ParagraphLineSpacing) valuesToApply.push(valueToSet) changeCount += 1 } // APPLY THE SPECIFIED ATTRIBUTE SETTINGS runRanges.forEach(range => { runStyle = noteObj.styleForRange(range) attsToModify.forEach((att, index) => { valueToSet = valuesToApply[index] runStyle.set(att, valueToSet) }) }) // REPLACE NOTE WITH VIRTUAL OBJECT if(changeCount !== 0){ item.noteText.replace(item.noteText.range, noteObj) } // REVEAL NOTE tree = document.windows[0].content tree.nodeForObject(item).expandNote(false) changeCount = (changeCount === 0) ? "No":String(changeCount) editOrEdits = (changeCount === 1) ? "edit":"edits" utterance = createUtterance(`${changeCount} ${editOrEdits} applied!`) synthesizer.speakUtterance(utterance) } catch(err){ if(!err.causedByUserCancelling){ utterance = createUtterance(err.message) synthesizer.speakUtterance(utterance) //new Alert(err.name, err.message).show() } } })();

 

 

 

LEGAL

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Mention of third-party websites and products is for informational purposes only and constitutes neither an endorsement nor a recommendation. OMNI-AUTOMATION.COM assumes no responsibility with regard to the selection, performance or use of information or products found at third-party websites. OMNI-AUTOMATION.COM provides this only as a convenience to our users. OMNI-AUTOMATION.COM has not tested the information found on these sites and makes no representations regarding its accuracy or reliability. There are risks inherent in the use of any information or products found on the Internet, and OMNI-AUTOMATION.COM assumes no responsibility in this regard. Please understand that a third-party site is independent from OMNI-AUTOMATION.COM and that OMNI-AUTOMATION.COM has no control over the content on that website. Please contact the vendor for additional information.