From 7d067f807d30f15d53ba1c9ecfb3c841096f5c86 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 9 Jun 2021 22:08:31 +0200 Subject: [PATCH 01/12] Add text alignment tags --- nw/core/tokenizer.py | 27 ++++++++++++++++++++++++++- nw/gui/dochighlight.py | 7 +++++++ sample/content/636b6aa9b697b.nwd | 10 ++++++++++ sample/nwProject.nwx | 22 +++++++++++----------- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 583eb948d..d0bc33eb0 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -436,6 +436,31 @@ def tokenizeText(self): # Skip all body text continue + # Check Alignment + tagLeft = False + tagRight = False + if aLine.startswith(">>"): + tagRight = True + aLine = aLine[2:].lstrip() + elif aLine.startswith(">>"): + tagRight = True + aLine = aLine[8:].lstrip() + + if aLine.endswith("<<"): + tagLeft = True + aLine = aLine[:-2].rstrip() + elif aLine.endswith("<<"): + tagLeft = True + aLine = aLine[:-8].rstrip() + + textAlign = self.A_NONE + if tagLeft and tagRight: + textAlign = self.A_CENTRE + elif tagLeft: + textAlign = self.A_LEFT + elif tagRight: + textAlign = self.A_RIGHT + # Otherwise we use RegEx to find formatting tags within a line of text fmtPos = [] for theRX, theKeys in rxFormats: @@ -452,7 +477,7 @@ def tokenizeText(self): # sorted by position fmtPos = sorted(fmtPos, key=itemgetter(0)) self.theTokens.append(( - self.T_TEXT, nLine, aLine, fmtPos, self.A_NONE + self.T_TEXT, nLine, aLine, fmtPos, textAlign )) if self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index ceeacf451..f33b94b9d 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -198,6 +198,13 @@ def initHighlighter(self): } )) + # Alignment Tags + self.hRules.append(( + r"(^>{1,2}|<{1,2}$)", { + 1 : self.hStyles["hidden"], + } + )) + # Auto-Replace Tags self.hRules.append(( r"<(\S+?)>", { diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd index be611dced..e05d40412 100644 --- a/sample/content/636b6aa9b697b.nwd +++ b/sample/content/636b6aa9b697b.nwd @@ -24,3 +24,13 @@ Thin spaces and thin non-breaking spaces are also supported from the Insert menu If you need to split a scene file up into further pieces, you can do so with the level four heading, like above. This is referred to as a section. Both scene and section titles can be left out of the final exported document. The formatting of titles can be selected from the Build Novel Project dialog. You can also have them replaced with scene separators like “* * *”. + +#### Text Alignment + +The text by default will have the left or justified alignment. You can also specify alignment for a specific paragraph by “pushing” it away from an edge with a set of ‘>>’ or ‘<<’ symbols, like so: + +This text is left-aligned. << + +>> This text is right-aligned. + +>> This text is centred. << \ No newline at end of file diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index fe3bcc24b..a5e489bcb 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,13 +1,13 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1062 - 166 - 49870 + 1083 + 172 + 50790 False @@ -17,8 +17,8 @@ True 636b6aa9b697b 636b6aa9b697b - 1077 - 701 + 1132 + 756 376 B @@ -118,10 +118,10 @@ 1st Draft True SCENE - 1810 - 318 - 8 - 1505 + 2107 + 373 + 12 + 2049 Another Scene @@ -145,7 +145,7 @@ 633 101 3 - 0 + 308 A Note on Structure From 6f9e3168b0b4b0ab5dcbea1d15ad5f0d80f1dcb9 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 9 Jun 2021 23:05:16 +0200 Subject: [PATCH 02/12] Fix default alignment --- nw/core/tokenizer.py | 50 ++++++++++++++++++-------------- sample/content/53b69b83cdafc.nwd | 4 ++- sample/nwProject.nwx | 20 ++++++------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index d0bc33eb0..708a23de3 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -366,6 +366,12 @@ def tokenizeText(self): (QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]), ] + # Determine default text alignment + if self.isTitle or self.isPart: + defAlign = self.A_CENTRE + else: + defAlign = self.A_NONE + self.theTokens = [] tmpMarkdown = [] nLine = 0 @@ -375,7 +381,7 @@ def tokenizeText(self): # Tag lines starting with specific characters if len(aLine.strip()) == 0: self.theTokens.append(( - self.T_EMPTY, nLine, "", None, self.A_NONE + self.T_EMPTY, nLine, "", None, defAlign )) if self.keepMarkdown: tmpMarkdown.append("\n") @@ -385,48 +391,48 @@ def tokenizeText(self): synTag = cLine[:9].lower() if synTag == "synopsis:": self.theTokens.append(( - self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, self.A_NONE + self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, defAlign )) if self.doSynopsis and self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) else: self.theTokens.append(( - self.T_COMMENT, nLine, aLine[1:].strip(), None, self.A_NONE + self.T_COMMENT, nLine, aLine[1:].strip(), None, defAlign )) if self.doComments and self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[0] == "@": self.theTokens.append(( - self.T_KEYWORD, nLine, aLine[1:].strip(), None, self.A_NONE + self.T_KEYWORD, nLine, aLine[1:].strip(), None, defAlign )) if self.doKeywords and self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:2] == "# ": self.theTokens.append(( - self.T_HEAD1, nLine, aLine[2:].strip(), None, self.A_NONE + self.T_HEAD1, nLine, aLine[2:].strip(), None, defAlign )) if self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:3] == "## ": self.theTokens.append(( - self.T_HEAD2, nLine, aLine[3:].strip(), None, self.A_NONE + self.T_HEAD2, nLine, aLine[3:].strip(), None, defAlign )) if self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:4] == "### ": self.theTokens.append(( - self.T_HEAD3, nLine, aLine[4:].strip(), None, self.A_NONE + self.T_HEAD3, nLine, aLine[4:].strip(), None, defAlign )) if self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:5] == "#### ": self.theTokens.append(( - self.T_HEAD4, nLine, aLine[5:].strip(), None, self.A_NONE + self.T_HEAD4, nLine, aLine[5:].strip(), None, defAlign )) if self.keepMarkdown: tmpMarkdown.append("%s\n" % aLine) @@ -453,7 +459,7 @@ def tokenizeText(self): tagLeft = True aLine = aLine[:-8].rstrip() - textAlign = self.A_NONE + textAlign = defAlign if tagLeft and tagRight: textAlign = self.A_CENTRE elif tagLeft: @@ -484,7 +490,7 @@ def tokenizeText(self): # Always add an empty line at the end self.theTokens.append(( - self.T_EMPTY, nLine, "", None, self.A_NONE + self.T_EMPTY, nLine, "", None, defAlign )) if self.keepMarkdown: tmpMarkdown.append("\n") @@ -496,8 +502,8 @@ def tokenizeText(self): # =========== # Some items need a second pass - pToken = (self.T_EMPTY, 0, "", None, self.A_NONE) - nToken = (self.T_EMPTY, 0, "", None, self.A_NONE) + pToken = (self.T_EMPTY, 0, "", None, defAlign) + nToken = (self.T_EMPTY, 0, "", None, defAlign) tCount = len(self.theTokens) for n, tToken in enumerate(self.theTokens): @@ -626,27 +632,27 @@ def doHeaders(self): tToken[0], tToken[1], tTemp, None, self.A_NONE ) - # For title page and partitions, we need to centre all text. - # For partition, we also add a page break before, and for - # both types we always add a page break after the content. - # We also swap header level 1 with a title type instead. + # For title page we use a different title class, and we will set + # an automatic page break before (i.e. added if needed). For + # partitions we always need a page break before. if self.isTitle or self.isPart: for n, tToken in enumerate(self.theTokens): + aStyle = tToken[4] + if n == 0: + if self.isTitle: + aStyle |= self.A_PBB_AUT + else: + aStyle |= self.A_PBB + if tToken[0] == self.T_HEAD1: if self.isTitle: - aStyle = self.A_PBB_AUT | self.A_CENTRE self.theTokens[n] = ( self.T_TITLE, tToken[1], tToken[2], tToken[3], aStyle ) else: - aStyle = self.A_PBB | self.A_CENTRE self.theTokens[n] = ( tToken[0], tToken[1], tToken[2], tToken[3], aStyle ) - else: - self.theTokens[n] = ( - tToken[0], tToken[1], tToken[2], tToken[3], self.A_CENTRE - ) # Add a page break after the last entry n = len(self.theTokens) - 1 diff --git a/sample/content/53b69b83cdafc.nwd b/sample/content/53b69b83cdafc.nwd index f33a2a985..cca928b9d 100644 --- a/sample/content/53b69b83cdafc.nwd +++ b/sample/content/53b69b83cdafc.nwd @@ -5,4 +5,6 @@ **By Jane Doh** -It’s also possible to add some text on this page. \ No newline at end of file +It’s also possible to add some text on this page. All text on Title and Partition pages are by default centred when the document is exported, but this can be overridden on individual paragraphs. + +The text can be left-aligned like this. << diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index a5e489bcb..8512ed42b 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,13 +1,13 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1083 - 172 - 50790 + 1088 + 176 + 51413 False @@ -17,8 +17,8 @@ True 636b6aa9b697b 636b6aa9b697b - 1132 - 756 + 1164 + 788 376 B @@ -63,10 +63,10 @@ Started True TITLE - 72 - 15 - 2 - 78 + 259 + 47 + 3 + 268 Page From 239cff3dee5e240e908ac98f93b2986417e56e60 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 9 Jun 2021 23:32:22 +0200 Subject: [PATCH 03/12] Fix tests --- nw/core/tokenizer.py | 25 +++----- .../guiBuild_Tool_Step1_Lorem_Ipsum.htm | 4 +- .../guiBuild_Tool_Step2_Lorem_Ipsum.fodt | 63 +++++++++---------- .../guiBuild_Tool_Step2_Lorem_Ipsum.htm | 4 +- .../guiBuild_Tool_Step3_Lorem_Ipsum.fodt | 63 +++++++++---------- .../guiBuild_Tool_Step3_Lorem_Ipsum.htm | 4 +- tests/test_core/test_core_tokenizer.py | 13 ++-- 7 files changed, 82 insertions(+), 94 deletions(-) diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 708a23de3..582fc44f6 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -655,25 +655,18 @@ def doHeaders(self): ) # Add a page break after the last entry - n = len(self.theTokens) - 1 - if n >= 0: - tToken = self.theTokens[n] - self.theTokens[n] = ( + if len(self.theTokens) > 0: + tToken = self.theTokens[-1] + self.theTokens[-1] = ( tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] | self.A_PBA ) - # A single page is always left-aligned and starts on a fresh - # page, unless it's empty. - if self.isPage: - for n, tToken in enumerate(self.theTokens): - if n == 0: - self.theTokens[n] = ( - tToken[0], tToken[1], tToken[2], tToken[3], self.A_LEFT | self.A_PBB - ) - else: - self.theTokens[n] = ( - tToken[0], tToken[1], tToken[2], tToken[3], self.A_LEFT - ) + # A single page always starts on a fresh page, unless it's empty. + if self.isPage and len(self.theTokens) > 0: + tToken = self.theTokens[0] + self.theTokens[0] = ( + tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] | self.A_PBB + ) return True diff --git a/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm index 5fa77a9b1..5ab8b4264 100644 --- a/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step1_Lorem_Ipsum.htm @@ -26,8 +26,8 @@

Lorem Ips

By lipsum.com

“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”

“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”

-

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

-

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

+

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

+

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

Prologue

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Act One

diff --git a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.fodt b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.fodt index 5dbff2197..db7121da7 100644 --- a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.fodt +++ b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.fodt @@ -66,24 +66,21 @@ - - - - + - + - + - + - + - + @@ -112,22 +109,22 @@ “Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…” “There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…” Comment: Exctracted from the lipsum.com website. - Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32. - The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. - Prologue + Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32. + The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. + Prologue Synopsis: Explanation from the lipsum.com website. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. - Act One + Act One “Fusce maximus felis libero” - Chapter One: Chapter One - Point of View: Bod - Plot: Main + Chapter One: Chapter One + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar. Scene 1.1: Scene One - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada. @@ -136,8 +133,8 @@ Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst. Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend. Scene 1.2: Scene Two - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien. @@ -147,20 +144,20 @@ Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu. Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor. Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus. - Chapter Two: Why do we use it? + Chapter Two: Why do we use it? Comment: Exctracted from the lipsum.com website. It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). - Chapter Three: Chapter Two - Point of View: Bod - Plot: Main + Chapter Three: Chapter Two + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at. Scene 3.1: Scene Three - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis. @@ -168,8 +165,8 @@ Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus. Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac. Scene 3.2: Scene Four - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien. @@ -179,8 +176,8 @@ Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui. Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero. Scene 3.3: Scene Five - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet. @@ -188,18 +185,18 @@ Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis. Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue. Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo. - Notes: Characters + Notes: Characters Nobody Owens Tag: Bod Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus. Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat. Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque. - Notes: Plot + Notes: Plot Main Plot Tag: Main Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet. Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur. - Notes: World + Notes: World Ancient Europe Tag: Europe Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula. diff --git a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm index 017e661b8..daf40716a 100644 --- a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.htm @@ -27,8 +27,8 @@

Lorem Ips

“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”

“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”

Comment: Exctracted from the lipsum.com website.

-

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

-

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

+

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

+

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

Prologue

Synopsis: Explanation from the lipsum.com website.

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

diff --git a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt index 3411a1e16..5b2251d7e 100644 --- a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt +++ b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt @@ -66,24 +66,21 @@
- - - - + - + - + - + - + - + @@ -112,22 +109,22 @@ “Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…” “There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…” Comment: Exctracted from the lipsum.com website. - Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32. - The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. - Prologue + Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32. + The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. + Prologue Synopsis: Explanation from the lipsum.com website. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. - Act One + Act One “Fusce maximus felis libero” - Chapter One: Chapter One - Point of View: Bod - Plot: Main + Chapter One: Chapter One + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar. Scene 1.1: Scene One - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada. @@ -136,8 +133,8 @@ Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst. Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend. Scene 1.2: Scene Two - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien. @@ -147,20 +144,20 @@ Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu. Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor. Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus. - Chapter Two: Why do we use it? + Chapter Two: Why do we use it? Comment: Exctracted from the lipsum.com website. It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). - Chapter Three: Chapter Two - Point of View: Bod - Plot: Main + Chapter Three: Chapter Two + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at. Scene 3.1: Scene Three - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis. @@ -168,8 +165,8 @@ Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus. Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac. Scene 3.2: Scene Four - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien. @@ -179,8 +176,8 @@ Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui. Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero. Scene 3.3: Scene Five - Point of View: Bod - Plot: Main + Point of View: Bod + Plot: Main Locations: Europe Synopsis: Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet. @@ -188,18 +185,18 @@ Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis. Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue. Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo. - Notes: Characters + Notes: Characters Nobody Owens Tag: Bod Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus. Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat. Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque. - Notes: Plot + Notes: Plot Main Plot Tag: Main Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet. Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur. - Notes: World + Notes: World Ancient Europe Tag: Europe Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula. diff --git a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm index 4c97fd0fc..75ccdfe9e 100644 --- a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm +++ b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.htm @@ -27,8 +27,8 @@

Lorem Ips

“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”

“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”

Comment: Exctracted from the lipsum.com website.

-

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

-

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

+

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

+

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

Prologue

Synopsis: Explanation from the lipsum.com website.

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index 0f4d9f856..de81cedea 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -664,9 +664,10 @@ def testCoreToken_Headers(dummyGUI): # H1: Title theToken.theText = "# Novel Title\n" - theToken.tokenizeText() + theToken.setTitleFormat(r"T: %title%") theToken.isTitle = True theToken.isPart = False + theToken.tokenizeText() theToken.doHeaders() assert theToken.theTokens == [ (Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_PBB_AUT | Tokenizer.A_CENTRE), @@ -676,9 +677,9 @@ def testCoreToken_Headers(dummyGUI): # H1: Partition theToken.theText = "# Partition Title\n" theToken.setTitleFormat(r"T: %title%") - theToken.tokenizeText() theToken.isTitle = False theToken.isPart = True + theToken.tokenizeText() theToken.doHeaders() assert theToken.theTokens == [ (Tokenizer.T_HEAD1, 1, "Partition Title", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), @@ -699,10 +700,10 @@ def testCoreToken_Headers(dummyGUI): theToken.tokenizeText() theToken.doHeaders() assert theToken.theTokens == [ - (Tokenizer.T_TEXT, 1, "Page text", [], Tokenizer.A_PBB | Tokenizer.A_LEFT), - (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_LEFT), - (Tokenizer.T_TEXT, 3, "More text", [], Tokenizer.A_LEFT), - (Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_LEFT), + (Tokenizer.T_TEXT, 1, "Page text", [], Tokenizer.A_PBB), + (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 3, "More text", [], Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE), ] # END Test testCoreToken_Headers From e0de3ff191a4f500b5ed0d757bc7cc1f3c1452db Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 9 Jun 2021 23:40:37 +0200 Subject: [PATCH 04/12] Improve test coverage --- tests/test_core/test_core_tokenizer.py | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index de81cedea..5de89e90b 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -406,6 +406,58 @@ def testCoreToken_Tokenize(dummyGUI): "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n" ) + # Alignment + theToken.theText = ( + "Some regular text\n\n" + "Some left-aligned text <<\n\n" + ">> Some right-aligned text\n\n" + ">> Some centered text <<\n\n" + ) + theToken.tokenizeText() + assert theToken.theTokens == [ + (Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT), + (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 5, "Some right-aligned text", [], Tokenizer.A_RIGHT), + (Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE), + (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), + ] + assert theToken.theMarkdown[-1] == ( + "Some regular text\n\n" + "Some left-aligned text\n\n" + "Some right-aligned text\n\n" + "Some centered text\n\n\n" + ) + + # Alignment w/HTML Codes + theToken.theText = ( + "Some regular text\n\n" + "Some left-aligned text <<\n\n" + ">> Some right-aligned text\n\n" + ">> Some centered text <<\n\n" + ) + theToken.tokenizeText() + assert theToken.theTokens == [ + (Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT), + (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 5, "Some right-aligned text", [], Tokenizer.A_RIGHT), + (Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE), + (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), + ] + assert theToken.theMarkdown[-1] == ( + "Some regular text\n\n" + "Some left-aligned text\n\n" + "Some right-aligned text\n\n" + "Some centered text\n\n\n" + ) + # END Test testCoreToken_Tokenize @pytest.mark.core From 80c4826253ca62b7f0809fc15f74812899881936 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 10 Jun 2021 00:04:54 +0200 Subject: [PATCH 05/12] Added left/right indentation to Tokenizer class --- nw/core/tokenizer.py | 23 +++++++++++++- nw/gui/dochighlight.py | 42 ++++++++++++------------ tests/test_core/test_core_tokenizer.py | 44 +++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 27 deletions(-) diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 582fc44f6..97e527170 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -75,6 +75,8 @@ class Tokenizer(): A_PBA_AUT = 0x0080 # Page break after auto A_Z_TOPMRG = 0x0100 # Zero top margin A_Z_BTMMRG = 0x0200 # Zero bottom margin + A_IND_L = 0x0400 # Left indentation + A_IND_R = 0x0800 # Right indentation def __init__(self, theProject): @@ -442,15 +444,23 @@ def tokenizeText(self): # Skip all body text continue - # Check Alignment + # Check Alignment and Indentation tagLeft = False tagRight = False + indLeft = False + indRight = False if aLine.startswith(">>"): tagRight = True aLine = aLine[2:].lstrip() elif aLine.startswith(">>"): tagRight = True aLine = aLine[8:].lstrip() + elif aLine.startswith(">"): + indLeft = True + aLine = aLine[1:].lstrip() + elif aLine.startswith(">"): + indLeft = True + aLine = aLine[4:].lstrip() if aLine.endswith("<<"): tagLeft = True @@ -458,6 +468,12 @@ def tokenizeText(self): elif aLine.endswith("<<"): tagLeft = True aLine = aLine[:-8].rstrip() + elif aLine.endswith("<"): + indRight = True + aLine = aLine[:-1].rstrip() + elif aLine.endswith("<"): + indRight = True + aLine = aLine[:-4].rstrip() textAlign = defAlign if tagLeft and tagRight: @@ -467,6 +483,11 @@ def tokenizeText(self): elif tagRight: textAlign = self.A_RIGHT + if indLeft: + textAlign |= self.A_IND_L + if indRight: + textAlign |= self.A_IND_R + # Otherwise we use RegEx to find formatting tags within a line of text fmtPos = [] for theRX, theKeys in rxFormats: diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index f33b94b9d..95ed4759a 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -106,27 +106,27 @@ def initHighlighter(self): self.colEmph = QColor(*self.theTheme.colEmph) self.hStyles = { - "header1" : self._makeFormat(self.colHead, "bold", 1.8), - "header2" : self._makeFormat(self.colHead, "bold", 1.6), - "header3" : self._makeFormat(self.colHead, "bold", 1.4), - "header4" : self._makeFormat(self.colHead, "bold", 1.2), - "header1h" : self._makeFormat(self.colHeadH, "bold", 1.8), - "header2h" : self._makeFormat(self.colHeadH, "bold", 1.6), - "header3h" : self._makeFormat(self.colHeadH, "bold", 1.4), - "header4h" : self._makeFormat(self.colHeadH, "bold", 1.2), - "bold" : self._makeFormat(self.colEmph, "bold"), - "italic" : self._makeFormat(self.colEmph, "italic"), - "strike" : self._makeFormat(self.colHidden, "strike"), - "mspaces" : self._makeFormat(self.colError, "errline"), - "nobreak" : self._makeFormat(self.colBreak, "background"), - "dialogue1" : self._makeFormat(self.colDialN), - "dialogue2" : self._makeFormat(self.colDialD), - "dialogue3" : self._makeFormat(self.colDialS), - "replace" : self._makeFormat(self.colRepTag), - "hidden" : self._makeFormat(self.colHidden), - "keyword" : self._makeFormat(self.colKey), - "modifier" : self._makeFormat(self.colMod), - "value" : self._makeFormat(self.colVal, "underline"), + "header1" : self._makeFormat(self.colHead, "bold", 1.8), + "header2" : self._makeFormat(self.colHead, "bold", 1.6), + "header3" : self._makeFormat(self.colHead, "bold", 1.4), + "header4" : self._makeFormat(self.colHead, "bold", 1.2), + "header1h" : self._makeFormat(self.colHeadH, "bold", 1.8), + "header2h" : self._makeFormat(self.colHeadH, "bold", 1.6), + "header3h" : self._makeFormat(self.colHeadH, "bold", 1.4), + "header4h" : self._makeFormat(self.colHeadH, "bold", 1.2), + "bold" : self._makeFormat(self.colEmph, "bold"), + "italic" : self._makeFormat(self.colEmph, "italic"), + "strike" : self._makeFormat(self.colHidden, "strike"), + "mspaces" : self._makeFormat(self.colError, "errline"), + "nobreak" : self._makeFormat(self.colBreak, "background"), + "dialogue1" : self._makeFormat(self.colDialN), + "dialogue2" : self._makeFormat(self.colDialD), + "dialogue3" : self._makeFormat(self.colDialS), + "replace" : self._makeFormat(self.colRepTag), + "hidden" : self._makeFormat(self.colHidden), + "keyword" : self._makeFormat(self.colKey), + "modifier" : self._makeFormat(self.colMod), + "value" : self._makeFormat(self.colVal, "underline"), } self.hRules = [] diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index 5de89e90b..79a18b794 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -406,12 +406,18 @@ def testCoreToken_Tokenize(dummyGUI): "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n" ) - # Alignment + # Alignment and Indentation + dblIndent = Tokenizer.A_IND_L | Tokenizer.A_IND_R + rIndAlign = Tokenizer.A_RIGHT | Tokenizer.A_IND_R theToken.theText = ( "Some regular text\n\n" "Some left-aligned text <<\n\n" ">> Some right-aligned text\n\n" ">> Some centered text <<\n\n" + "> Left-indented block\n\n" + "Right-indented block <\n\n" + "> Double-indented block <\n\n" + ">> Right-indent, right-aligned <\n\n" ) theToken.tokenizeText() assert theToken.theTokens == [ @@ -423,13 +429,25 @@ def testCoreToken_Tokenize(dummyGUI): (Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE), (Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), - (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 9, "Left-indented block", [], Tokenizer.A_IND_L), + (Tokenizer.T_EMPTY, 10, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 11, "Right-indented block", [], Tokenizer.A_IND_R), + (Tokenizer.T_EMPTY, 12, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 13, "Double-indented block", [], dblIndent), + (Tokenizer.T_EMPTY, 14, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 15, "Right-indent, right-aligned", [], rIndAlign), + (Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == ( "Some regular text\n\n" "Some left-aligned text\n\n" "Some right-aligned text\n\n" - "Some centered text\n\n\n" + "Some centered text\n\n" + "Left-indented block\n\n" + "Right-indented block\n\n" + "Double-indented block\n\n" + "Right-indent, right-aligned\n\n\n" ) # Alignment w/HTML Codes @@ -438,6 +456,10 @@ def testCoreToken_Tokenize(dummyGUI): "Some left-aligned text <<\n\n" ">> Some right-aligned text\n\n" ">> Some centered text <<\n\n" + "> Left-indented block\n\n" + "Right-indented block <\n\n" + "> Double-indented block <\n\n" + ">> Right-indent, right-aligned <\n\n" ) theToken.tokenizeText() assert theToken.theTokens == [ @@ -449,13 +471,25 @@ def testCoreToken_Tokenize(dummyGUI): (Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE), (Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), - (Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 9, "Left-indented block", [], Tokenizer.A_IND_L), + (Tokenizer.T_EMPTY, 10, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 11, "Right-indented block", [], Tokenizer.A_IND_R), + (Tokenizer.T_EMPTY, 12, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 13, "Double-indented block", [], dblIndent), + (Tokenizer.T_EMPTY, 14, "", None, Tokenizer.A_NONE), + (Tokenizer.T_TEXT, 15, "Right-indent, right-aligned", [], rIndAlign), + (Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE), + (Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == ( "Some regular text\n\n" "Some left-aligned text\n\n" "Some right-aligned text\n\n" - "Some centered text\n\n\n" + "Some centered text\n\n" + "Left-indented block\n\n" + "Right-indented block\n\n" + "Double-indented block\n\n" + "Right-indent, right-aligned\n\n\n" ) # END Test testCoreToken_Tokenize From e5477f314eed750aca7f855c054cea35f1bb930a Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 10 Jun 2021 21:38:58 +0200 Subject: [PATCH 06/12] Add support for indented paragraphs in exports --- nw/core/tohtml.py | 8 +++++++ nw/core/tokenizer.py | 29 ++++++++++++++--------- nw/core/toodt.py | 55 +++++++++++++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 5586575de..a205f6b14 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -78,6 +78,9 @@ def setReplaceUnicode(self, doReplace): html entities replacement. """ # Control characters must always be replaced + # This affects alignment and indenting code, so the Tokenizer + # must take this into account when parsing for markup using + # angle brackets. self._trMap = str.maketrans({ "<" : "<", ">" : ">", @@ -181,6 +184,11 @@ def doConvert(self): if tStyle & self.A_Z_TOPMRG: aStyle.append("margin-top: 0;") + if tStyle & self.A_IND_L: + aStyle.append("margin-left: %dpx;" % self.mainConf.tabWidth) + if tStyle & self.A_IND_R: + aStyle.append("margin-right: %dpx;" % self.mainConf.tabWidth) + if len(aStyle) > 0: hStyle = " style='%s'" % (" ".join(aStyle)) else: diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index 97e527170..19d09bc3d 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -24,6 +24,7 @@ along with this program. If not, see . """ +import nw import logging import re @@ -82,6 +83,7 @@ def __init__(self, theProject): self.theProject = theProject self.theParent = theProject.theParent + self.mainConf = nw.CONFIG # Data Variables self.theText = "" # The raw text to be tokenized @@ -97,14 +99,15 @@ def __init__(self, theProject): self.textFont = "Serif" # Output text font self.textSize = 11 # Output text size self.textFixed = False # Fixed width text - self.lineHeight = 1.15 # Line height + self.lineHeight = 1.15 # Line height in units of em + self.blockIndent = 4.00 # Block indent in units of em self.doJustify = False # Justify text self.doBodyText = True # Include body text self.doSynopsis = False # Also process synopsis comments self.doComments = False # Also process comments self.doKeywords = False # Also process keywords like tags and references - ## Title Margins + ## Margins self.marginTitle = (1.000, 0.500) self.marginHead1 = (1.000, 0.500) self.marginHead2 = (0.834, 0.500) @@ -188,7 +191,11 @@ def setFont(self, textFont, textSize, textFixed=False): return def setLineHeight(self, lineHeight): - self.lineHeight = float(lineHeight) + self.lineHeight = min(max(float(lineHeight), 0.5), 5.0) + return + + def setBlockIndent(self, blockIndent): + self.blockIndent = min(max(float(blockIndent), 0.0), 10.0) return def setJustify(self, doJustify): @@ -451,29 +458,29 @@ def tokenizeText(self): indRight = False if aLine.startswith(">>"): tagRight = True - aLine = aLine[2:].lstrip() + aLine = aLine[2:].lstrip(" ") elif aLine.startswith(">>"): tagRight = True - aLine = aLine[8:].lstrip() + aLine = aLine[8:].lstrip(" ") elif aLine.startswith(">"): indLeft = True - aLine = aLine[1:].lstrip() + aLine = aLine[1:].lstrip(" ") elif aLine.startswith(">"): indLeft = True - aLine = aLine[4:].lstrip() + aLine = aLine[4:].lstrip(" ") if aLine.endswith("<<"): tagLeft = True - aLine = aLine[:-2].rstrip() + aLine = aLine[:-2].rstrip(" ") elif aLine.endswith("<<"): tagLeft = True - aLine = aLine[:-8].rstrip() + aLine = aLine[:-8].rstrip(" ") elif aLine.endswith("<"): indRight = True - aLine = aLine[:-1].rstrip() + aLine = aLine[:-1].rstrip(" ") elif aLine.endswith("<"): indRight = True - aLine = aLine[:-4].rstrip() + aLine = aLine[:-4].rstrip(" ") textAlign = defAlign if tagLeft and tagRight: diff --git a/nw/core/toodt.py b/nw/core/toodt.py index f5770c9f8..b6dd08fc6 100644 --- a/nw/core/toodt.py +++ b/nw/core/toodt.py @@ -99,19 +99,20 @@ def __init__(self, theProject, isFlat): self.headerText = "" # Internal - self._fontFamily = "'Liberation Sans'" - self._fontPitch = "variable" - self._fSizeTitle = "30pt" - self._fSizeHead1 = "24pt" - self._fSizeHead2 = "20pt" - self._fSizeHead3 = "16pt" - self._fSizeHead4 = "14pt" - self._fSizeHead = "14pt" - self._fSizeText = "12pt" - self._lineHeight = "115%" - self._textAlign = "left" - self._dLanguage = "en" - self._dCountry = "GB" + self._fontFamily = "'Liberation Sans'" + self._fontPitch = "variable" + self._fSizeTitle = "30pt" + self._fSizeHead1 = "24pt" + self._fSizeHead2 = "20pt" + self._fSizeHead3 = "16pt" + self._fSizeHead4 = "14pt" + self._fSizeHead = "14pt" + self._fSizeText = "12pt" + self._lineHeight = "115%" + self._blockIndent = "1.693cm" + self._textAlign = "left" + self._dLanguage = "en" + self._dCountry = "GB" ## Text Margings in Units of em self._mTopTitle = "0.423cm" @@ -222,8 +223,9 @@ def initDocument(self): self._colMetaTx = "#813709" self._opaMetaTx = "100%" - self._lineHeight = f"{round(100 * self.lineHeight):d}%" - self._textAlign = "justify" if self.doJustify else "left" + self._lineHeight = f"{round(100 * self.lineHeight):d}%" + self._blockIndent = self._emToCm(self.blockIndent) + self._textAlign = "justify" if self.doJustify else "left" # Document Header # =============== @@ -356,6 +358,11 @@ def doConvert(self): if tStyle & self.A_Z_TOPMRG: oStyle.setMarginTop("0.000cm") + if tStyle & self.A_IND_L: + oStyle.setMarginLeft(self._blockIndent) + if tStyle & self.A_IND_R: + oStyle.setMarginRight(self._blockIndent) + # Process Text Types if tType == self.T_EMPTY: if len(thisPar) > 1 and parStyle is not None: @@ -515,7 +522,7 @@ def _formatComments(self, tText): def _formatKeywords(self, tText): """Apply formatting to keywords. """ - isValid, theBits, thePos = self.theParent.theIndex.scanThis("@"+tText) + isValid, theBits, _ = self.theParent.theIndex.scanThis("@"+tText) if not isValid or not theBits: return "" @@ -1008,6 +1015,8 @@ def __init__(self): self._pAttr = { "margin-top": ["fo", None], "margin-bottom": ["fo", None], + "margin-left": ["fo", None], + "margin-right": ["fo", None], "line-height": ["fo", None], "text-align": ["fo", None], "break-before": ["fo", None], @@ -1064,6 +1073,14 @@ def setMarginBottom(self, theValue): self._pAttr["margin-bottom"][1] = str(theValue) return + def setMarginLeft(self, theValue): + self._pAttr["margin-left"][1] = str(theValue) + return + + def setMarginRight(self, theValue): + self._pAttr["margin-right"][1] = str(theValue) + return + def setLineHeight(self, theValue): self._pAttr["line-height"][1] = str(theValue) return @@ -1142,13 +1159,13 @@ def checkNew(self, refStyle): """Check if there are new settings in refStyle that differ from those in the current object. """ - for aName, (aNm, aVal) in refStyle._mAttr.items(): + for aName, (_, aVal) in refStyle._mAttr.items(): if aVal is not None and aVal != self._mAttr[aName][1]: return True - for aName, (aNm, aVal) in refStyle._pAttr.items(): + for aName, (_, aVal) in refStyle._pAttr.items(): if aVal is not None and aVal != self._pAttr[aName][1]: return True - for aName, (aNm, aVal) in refStyle._tAttr.items(): + for aName, (_, aVal) in refStyle._tAttr.items(): if aVal is not None and aVal != self._tAttr[aName][1]: return True return False From 70d3ee07bf3b7c6936a6938e5c967cd47d042d1d Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 10 Jun 2021 21:39:58 +0200 Subject: [PATCH 07/12] Update sample project --- sample/content/53b69b83cdafc.nwd | 4 +-- sample/content/636b6aa9b697b.nwd | 16 ++++++---- sample/content/6a2d6d5f4f401.nwd | 1 + sample/content/974e400180a99.nwd | 2 +- sample/content/ba8a28a246524.nwd | 18 +++++------ sample/content/edca4be2fcaf8.nwd | 2 +- sample/nwProject.nwx | 54 ++++++++++++++++---------------- 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/sample/content/53b69b83cdafc.nwd b/sample/content/53b69b83cdafc.nwd index cca928b9d..82e4361c6 100644 --- a/sample/content/53b69b83cdafc.nwd +++ b/sample/content/53b69b83cdafc.nwd @@ -5,6 +5,6 @@ **By Jane Doh** -It’s also possible to add some text on this page. All text on Title and Partition pages are by default centred when the document is exported, but this can be overridden on individual paragraphs. +It’s also possible to add some text on this page. Everything on Title and Partition pages is by default centred, but this can be overridden on individual paragraphs. -The text can be left-aligned like this. << +For instance, the text can be left-aligned like this. << diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd index e05d40412..514b4bf1e 100644 --- a/sample/content/636b6aa9b697b.nwd +++ b/sample/content/636b6aa9b697b.nwd @@ -9,11 +9,11 @@ A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree. -Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, _italic_ and **_bold italic_**. You can also ~~strike through~~ text. There is **some support for _nested_ emphasis**, but it isn’t fully Markdown compliant. If the syntax highlighter doesn’t show it correctly, the export tool will not either. +Each paragraph in the scene is separated by a blank line. The text supports minimal formatting, like **bold**, _italic_ and **_bold italic_**. You can also ~~strike through~~ text. There is **some support for _nested_ emphasis**, but there are some known limitations. If the syntax highlighter doesn’t show it correctly, the export tool will not either. In addition, the editor supports automatic formatting of “quotes”, both double and ‘single’. Depending on the syntax highlighter, these can be in different colours. “You can of course use **bold** and _italic_ text inside of quotes too.” -If you have the need for it, you can also add text that can be automatically replaced by other text when you generate a preview or export the project. Now, let’s auto-replace this A with , and this C with . While is just . Press Ctrl+R to see what this looks like in the view pane. +If you have the need for it, you can also add text that can be automatically replaced by other text when you generate a preview or export the project. Now, let’s auto-replace this A with , and this C with . While is just . Press Ctrl+R to see what this looks like in the view pane. The editor also supports non breaking spaces, and the spell checker accepts long dashes—like this—as valid word separators. Regular dashes are also supported – and can be automatically inserted when typing two hyphens. @@ -21,16 +21,20 @@ Thin spaces and thin non-breaking spaces are also supported from the Insert menu #### Some Section Here -If you need to split a scene file up into further pieces, you can do so with the level four heading, like above. This is referred to as a section. +If you need to split a scene file up into further pieces, you can do so with the level four heading, like the one above this paragraph. This is referred to as a “section” in the documentation. Both scene and section titles can be left out of the final exported document. The formatting of titles can be selected from the Build Novel Project dialog. You can also have them replaced with scene separators like “* * *”. -#### Text Alignment +#### Text Alignment and Indent -The text by default will have the left or justified alignment. You can also specify alignment for a specific paragraph by “pushing” it away from an edge with a set of ‘>>’ or ‘<<’ symbols, like so: +The text by default will have the left or justified alignment in the main text files in your project. You can also specify alignment for a specific paragraph by “pushing” it away from an edge with a set of ‘>>’ or ‘<<’ symbols, like so: This text is left-aligned. << >> This text is right-aligned. ->> This text is centred. << \ No newline at end of file +>> This text is centred. << + +You can also indent a paragraph from both the left and right margin with ‘>’ and ‘<’ symbols. + +> This paragraph is indented from both the left margin and the right margin. This is useful for when you want to quote a large chunk of text for instance. < diff --git a/sample/content/6a2d6d5f4f401.nwd b/sample/content/6a2d6d5f4f401.nwd index ad4554d85..172774d55 100644 --- a/sample/content/6a2d6d5f4f401.nwd +++ b/sample/content/6a2d6d5f4f401.nwd @@ -8,3 +8,4 @@ % Synopsis: We can add a chapter file, but keep the scene files separate. In the chapter file we can set the meta data that applies to the whole chapter if we wish to. +A chapter can also contain leading text before the first scene. diff --git a/sample/content/974e400180a99.nwd b/sample/content/974e400180a99.nwd index 0a4f5df86..885e2bbbe 100644 --- a/sample/content/974e400180a99.nwd +++ b/sample/content/974e400180a99.nwd @@ -3,4 +3,4 @@ %%~kind: NOVEL/PAGE This is a plain page with some text on it. -This file should receive no special formatting, but the text will always be left aligned and the content will always start on a fresh page when the project is exported. +Text on plain pages will always start on a fresh page when the project is exported. diff --git a/sample/content/ba8a28a246524.nwd b/sample/content/ba8a28a246524.nwd index 83d441fc7..55e1b5079 100644 --- a/sample/content/ba8a28a246524.nwd +++ b/sample/content/ba8a28a246524.nwd @@ -5,18 +5,18 @@ % Notice that this is a file with the flag ‘N.Un’. The ‘N’ means it’s a novel file, and the ‘Un’ means it’s an unnumbered chapter. Unnumbered chapters can be treated separately from numbered chapters during export. Perfect for when you want to add an interlude, or for a prologue or epilogue. -I am the very model of a modern Major-General -I've information vegetable, animal, and mineral -I know the kings of England, and I quote the fights historical +I am the very model of a modern Major-General +I've information vegetable, animal, and mineral +I know the kings of England, and I quote the fights historical From Marathon to Waterloo, in order categorical -I'm very well acquainted, too, with matters mathematical -I understand equations, both the simple and quadratical -About binomial theorem I'm teeming with a lot o’ news +I'm very well acquainted, too, with matters mathematical +I understand equations, both the simple and quadratical +About binomial theorem I'm teeming with a lot o’ news With many cheerful facts about the square of the hypotenuse - With many cheerful facts about the square of the hypotenuse - With many cheerful facts about the square of the hypotenuse + With many cheerful facts about the square of the hypotenuse + With many cheerful facts about the square of the hypotenuse With many cheerful facts about the square of the hypotepotenuse -% Notice that the lines in the verse have highlighted spaces at the end of each line. (If the syntax theme allows it.) These are two spaces, which indicates that you want a hard line break at that point. The blank lines still indicate a paragraph. Press Ctrl+R to see what this renders like. +% Notice that the lines in the verse end in a single line break. Single line breaks do not create a new paragraph but instead insert a break within the paragraph. Press Ctrl+R to see what this renders like. diff --git a/sample/content/edca4be2fcaf8.nwd b/sample/content/edca4be2fcaf8.nwd index a033257d2..73271f7ef 100644 --- a/sample/content/edca4be2fcaf8.nwd +++ b/sample/content/edca4be2fcaf8.nwd @@ -3,4 +3,4 @@ %%~kind: NOVEL/PARTITION # Part One -The first part. \ No newline at end of file +In the beginning … \ No newline at end of file diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 8512ed42b..be00422c8 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,13 +1,13 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1088 - 176 - 51413 + 1095 + 188 + 52304 False @@ -17,8 +17,8 @@ True 636b6aa9b697b 636b6aa9b697b - 1164 - 788 + 1224 + 848 376 B @@ -63,10 +63,10 @@ Started True TITLE - 259 - 47 + 244 + 43 3 - 268 + 252
Page @@ -75,10 +75,10 @@ New True PAGE - 210 - 40 + 125 + 26 2 - 213 + 127 Part One @@ -87,10 +87,10 @@ New True PARTITION - 23 - 5 + 26 + 6 1 - 0 + 30 A Folder @@ -106,10 +106,10 @@ Notes True CHAPTER - 12 - 3 - 0 - 215 + 75 + 14 + 1 + 279 Making a Scene @@ -118,10 +118,10 @@ 1st Draft True SCENE - 2107 - 373 - 12 - 2049 + 2450 + 439 + 14 + 61 Another Scene @@ -133,7 +133,7 @@ 476 93 3 - 430 + 577 Interlude @@ -142,10 +142,10 @@ New True UNNUMBERED - 633 + 617 101 3 - 308 + 1137 A Note on Structure @@ -157,7 +157,7 @@ 1692 313 6 - 1721 + 1110 Chapter Two From 32e9c10702b07c7b00b23baae38677f680cfcabc Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 10 Jun 2021 23:38:11 +0200 Subject: [PATCH 08/12] Fix word counter for align and indent tags --- nw/core/index.py | 46 ++++++++++++--------- sample/nwProject.nwx | 18 ++++----- tests/test_core/test_core_index.py | 64 ++++++++++++++++++++++-------- 3 files changed, 83 insertions(+), 45 deletions(-) diff --git a/nw/core/index.py b/nw/core/index.py index c48c53298..da11e401d 100644 --- a/nw/core/index.py +++ b/nw/core/index.py @@ -908,6 +908,9 @@ def countWords(theText): paraCount = 0 prevEmpty = True + if not isinstance(theText, str): + return charCount, wordCount, paraCount + # We need to treat dashes as word separators for counting words. # The check+replace apprach is much faster that direct replace for # large texts, and a bit slower for small texts, but in the latter @@ -920,33 +923,38 @@ def countWords(theText): for aLine in theText.splitlines(): countPara = True - theLen = len(aLine) - if theLen == 0: + if not aLine: prevEmpty = True continue if aLine[0] == "@" or aLine[0] == "%": continue - if aLine[0:5] == "#### ": - wordCount -= 1 - charCount -= 5 - countPara = False - elif aLine[0:4] == "### ": - wordCount -= 1 - charCount -= 4 - countPara = False - elif aLine[0:3] == "## ": - wordCount -= 1 - charCount -= 3 - countPara = False - elif aLine[0:2] == "# ": - wordCount -= 1 - charCount -= 2 - countPara = False + if aLine[0] == "#": + if aLine[:5] == "#### ": + aLine = aLine[5:] + countPara = False + elif aLine[:4] == "### ": + aLine = aLine[4:] + countPara = False + elif aLine[:3] == "## ": + aLine = aLine[3:] + countPara = False + elif aLine[:2] == "# ": + aLine = aLine[2:] + countPara = False + elif aLine[0] == ">" or aLine[-1] == "<": + if aLine[:2] == ">>": + aLine = aLine[2:].lstrip(" ") + elif aLine[:1] == ">": + aLine = aLine[1:].lstrip(" ") + if aLine[-2:] == "<<": + aLine = aLine[:-2].rstrip(" ") + elif aLine[-1:] == "<": + aLine = aLine[:-1].rstrip(" ") wordCount += len(aLine.split()) - charCount += theLen + charCount += len(aLine) if countPara and prevEmpty: paraCount += 1 diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index be00422c8..c1960d236 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,13 +1,13 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1095 + 1096 188 - 52304 + 52328 False @@ -17,8 +17,8 @@ True 636b6aa9b697b 636b6aa9b697b - 1224 - 848 + 1217 + 841 376 B @@ -63,8 +63,8 @@ Started True TITLE - 244 - 43 + 241 + 42 3 252 @@ -118,8 +118,8 @@ 1st Draft True SCENE - 2450 - 439 + 2434 + 433 14 61 diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 87ce2b02b..0fdde6713 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -1177,29 +1177,59 @@ def testCoreIndex_CheckTextCounts(dummyGUI): def testCoreIndex_CountWords(): """Test the word counter and the exclusion filers. """ - testText = ( + # Non-Text + assert countWords(None) == (0, 0, 0) + assert countWords(1234) == (0, 0, 0) + + # General Text + cC, wC, pC = countWords(( "# Heading One\n" "## Heading Two\n" "### Heading Three\n" - "#### Heading Four\n" - "\n" - "@tag: value\n" - "\n" - "% A comment that should n ot be counted.\n" - "\n" - "The first paragraph.\n" - "\n" - "The second paragraph.\n" - "\n" - "\n" - "The third paragraph.\n" - "\n" + "#### Heading Four\n\n" + "@tag: value\n\n" + "% A comment that should not be counted.\n\n" + "The first paragraph.\n\n" + "The second paragraph.\n\n\n" + "The third paragraph.\n\n" "Dashes\u2013and even longer\u2014dashes." - ) - cC, wC, pC = countWords(testText) - + )) assert cC == 138 assert wC == 22 assert pC == 4 + # Text Alignment + cC, wC, pC = countWords(( + "# Title\n\n" + "Left aligned<<\n\n" + "Left aligned <<\n\n" + "Right indent<\n\n" + "Right indent <\n\n" + )) + assert cC == 53 + assert wC == 9 + assert pC == 4 + + cC, wC, pC = countWords(( + "# Title\n\n" + ">>Right aligned\n\n" + ">> Right aligned\n\n" + ">Left indent\n\n" + "> Left indent\n\n" + )) + assert cC == 53 + assert wC == 9 + assert pC == 4 + + cC, wC, pC = countWords(( + "# Title\n\n" + ">>Centre aligned<<\n\n" + ">> Centre aligned <<\n\n" + ">Double indent<\n\n" + "> Double indent <\n\n" + )) + assert cC == 59 + assert wC == 9 + assert pC == 4 + # END Test testCoreIndex_CountWords From cb797fe903a56c40ebba93ed020ed8d10d82a60d Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:28:23 +0200 Subject: [PATCH 09/12] Add menu entries for align and indent --- nw/enum.py | 5 ++++ nw/gui/doceditor.py | 46 +++++++++++++++++++++++++++++++- nw/gui/mainmenu.py | 44 ++++++++++++++++++++++++++++++ nw/guimain.py | 5 ++++ sample/content/636b6aa9b697b.nwd | 6 +++-- sample/nwProject.nwx | 18 ++++++------- 6 files changed, 112 insertions(+), 12 deletions(-) diff --git a/nw/enum.py b/nw/enum.py index 830e8953f..f619d7ddf 100644 --- a/nw/enum.py +++ b/nw/enum.py @@ -90,6 +90,11 @@ class nwDocAction(Enum): REPL_SNG = 19 REPL_DBL = 20 RM_BREAKS = 21 + ALIGN_L = 22 + ALIGN_C = 23 + ALIGN_R = 24 + INDENT_L = 25 + INDENT_R = 26 # END Enum nwDocAction diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 9209ab4ca..a03c5927d 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -787,6 +787,16 @@ def docAction(self, theAction): self._replaceQuotes("\"", self._typDQOpen, self._typDQClose) elif theAction == nwDocAction.RM_BREAKS: self._removeInParLineBreaks() + elif theAction == nwDocAction.ALIGN_L: + self._formatBlock(nwDocAction.ALIGN_L) + elif theAction == nwDocAction.ALIGN_C: + self._formatBlock(nwDocAction.ALIGN_C) + elif theAction == nwDocAction.ALIGN_R: + self._formatBlock(nwDocAction.ALIGN_R) + elif theAction == nwDocAction.INDENT_L: + self._formatBlock(nwDocAction.INDENT_L) + elif theAction == nwDocAction.INDENT_R: + self._formatBlock(nwDocAction.INDENT_R) else: logger.debug("Unknown or unsupported document action %s" % str(theAction)) self._allowAutoReplace(True) @@ -1733,10 +1743,32 @@ def _formatBlock(self, docAction): elif theText.startswith("#### "): newText = theText[5:] cOffset = 5 + elif theText.startswith(">> "): + newText = theText[3:] + cOffset = 3 + elif theText.startswith("> ") and docAction != nwDocAction.INDENT_R: + newText = theText[2:] + cOffset = 2 + elif theText.startswith(">>"): + newText = theText[2:] + cOffset = 2 + elif theText.startswith(">") and docAction != nwDocAction.INDENT_R: + newText = theText[1:] + cOffset = 1 else: newText = theText cOffset = 0 + # Also remove formatting tags at the end + if theText.endswith(" <<"): + newText = newText[:-3] + elif theText.endswith(" <") and docAction != nwDocAction.INDENT_L: + newText = newText[:-2] + elif theText.endswith("<<"): + newText = newText[:-2] + elif theText.endswith("<") and docAction != nwDocAction.INDENT_L: + newText = newText[:-1] + # Apply new format if docAction == nwDocAction.BLOCK_COM: theText = "% "+newText @@ -1753,9 +1785,21 @@ def _formatBlock(self, docAction): elif docAction == nwDocAction.BLOCK_H4: theText = "#### "+newText cOffset -= 5 + elif docAction == nwDocAction.ALIGN_L: + theText = newText+" <<" + elif docAction == nwDocAction.ALIGN_C: + theText = ">> "+newText+" <<" + cOffset -= 3 + elif docAction == nwDocAction.ALIGN_R: + theText = ">> "+newText + cOffset -= 3 + elif docAction == nwDocAction.INDENT_L: + theText = "> "+newText + cOffset -= 2 + elif docAction == nwDocAction.INDENT_R: + theText = newText+" <" elif docAction == nwDocAction.BLOCK_TXT: theText = newText - cOffset -= 0 else: logger.error("Unknown or unsupported block format requested: %s" % str(docAction)) return False diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index d0c4c491c..b9cf5e6e0 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -830,6 +830,50 @@ def _buildFormatMenu(self): self.aFmtHead4.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H4)) self.fmtMenu.addAction(self.aFmtHead4) + # Format > Separator + self.fmtMenu.addSeparator() + + # Format > Align Left + self.aFmtAlignLeft = QAction(self.tr("Align Left"), self) + self.aFmtAlignLeft.setStatusTip(self.tr("Change the block alignment to left")) + self.aFmtAlignLeft.setShortcut("Ctrl+5") + self.aFmtAlignLeft.triggered.connect(lambda: self._docAction(nwDocAction.ALIGN_L)) + self.fmtMenu.addAction(self.aFmtAlignLeft) + + # Format > Align Centre + self.aFmtAlignCentre = QAction(self.tr("Align Centre"), self) + self.aFmtAlignCentre.setStatusTip(self.tr("Change the block alignment to centre")) + self.aFmtAlignCentre.setShortcut("Ctrl+6") + self.aFmtAlignCentre.triggered.connect(lambda: self._docAction(nwDocAction.ALIGN_C)) + self.fmtMenu.addAction(self.aFmtAlignCentre) + + # Format > Align Right + self.aFmtAlignRight = QAction(self.tr("Align Right"), self) + self.aFmtAlignRight.setStatusTip(self.tr("Change the block alignment to right")) + self.aFmtAlignRight.setShortcut("Ctrl+7") + self.aFmtAlignRight.triggered.connect(lambda: self._docAction(nwDocAction.ALIGN_R)) + self.fmtMenu.addAction(self.aFmtAlignRight) + + # Format > Separator + self.fmtMenu.addSeparator() + + # Format > Indent Left + self.aFmtIndentLeft = QAction(self.tr("Indent Left"), self) + self.aFmtIndentLeft.setStatusTip(self.tr("Increase the block's left margin")) + self.aFmtIndentLeft.setShortcut("Ctrl+8") + self.aFmtIndentLeft.triggered.connect(lambda: self._docAction(nwDocAction.INDENT_L)) + self.fmtMenu.addAction(self.aFmtIndentLeft) + + # Format > Indent Right + self.aFmtIndentRight = QAction(self.tr("Indent Right"), self) + self.aFmtIndentRight.setStatusTip(self.tr("Increase the block's right margin")) + self.aFmtIndentRight.setShortcut("Ctrl+9") + self.aFmtIndentRight.triggered.connect(lambda: self._docAction(nwDocAction.INDENT_R)) + self.fmtMenu.addAction(self.aFmtIndentRight) + + # Format > Separator + self.fmtMenu.addSeparator() + # Format > Comment self.aFmtComment = QAction(self.tr("Comment"), self) self.aFmtComment.setStatusTip(self.tr("Change the block format to comment")) diff --git a/nw/guimain.py b/nw/guimain.py index 700f670e6..039f11009 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -1345,6 +1345,11 @@ def _connectMenuActions(self): self.addAction(self.mainMenu.aFmtHead2) self.addAction(self.mainMenu.aFmtHead3) self.addAction(self.mainMenu.aFmtHead4) + self.addAction(self.mainMenu.aFmtAlignLeft) + self.addAction(self.mainMenu.aFmtAlignCentre) + self.addAction(self.mainMenu.aFmtAlignRight) + self.addAction(self.mainMenu.aFmtIndentLeft) + self.addAction(self.mainMenu.aFmtIndentRight) self.addAction(self.mainMenu.aFmtComment) self.addAction(self.mainMenu.aFmtNoFormat) diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd index 514b4bf1e..9db44eec6 100644 --- a/sample/content/636b6aa9b697b.nwd +++ b/sample/content/636b6aa9b697b.nwd @@ -25,7 +25,7 @@ If you need to split a scene file up into further pieces, you can do so with the Both scene and section titles can be left out of the final exported document. The formatting of titles can be selected from the Build Novel Project dialog. You can also have them replaced with scene separators like “* * *”. -#### Text Alignment and Indent +#### Text Alignment The text by default will have the left or justified alignment in the main text files in your project. You can also specify alignment for a specific paragraph by “pushing” it away from an edge with a set of ‘>>’ or ‘<<’ symbols, like so: @@ -35,6 +35,8 @@ This text is left-aligned. << >> This text is centred. << -You can also indent a paragraph from both the left and right margin with ‘>’ and ‘<’ symbols. +#### Text Indent + +You can indent a paragraph from both the left and right margin with ‘>’ and ‘<’ symbols. > This paragraph is indented from both the left margin and the right margin. This is useful for when you want to quote a large chunk of text for instance. < diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index c1960d236..067fa0a36 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,13 +1,13 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1096 - 188 - 52328 + 1107 + 189 + 52789 False @@ -17,8 +17,8 @@ True 636b6aa9b697b 636b6aa9b697b - 1217 - 841 + 1216 + 840 376 B @@ -118,10 +118,10 @@ 1st Draft True SCENE - 2434 - 433 + 2429 + 432 14 - 61 + 1329 Another Scene From fa4f4a8db47c85f813406022a3658a8f96b88026 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:32:11 +0200 Subject: [PATCH 10/12] Make the comment format action a toggle feature --- nw/gui/doceditor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index a03c5927d..32b50ebc6 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -1728,9 +1728,13 @@ def _formatBlock(self, docAction): elif theText.startswith("% "): newText = theText[2:] cOffset = 2 + if docAction == nwDocAction.BLOCK_COM: + docAction = nwDocAction.BLOCK_TXT elif theText.startswith("%"): newText = theText[1:] cOffset = 1 + if docAction == nwDocAction.BLOCK_COM: + docAction = nwDocAction.BLOCK_TXT elif theText.startswith("# "): newText = theText[2:] cOffset = 2 From 3246f4bfcc90136ab0bd8c4264ba6201fca8c1ea Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:46:35 +0200 Subject: [PATCH 11/12] Add tests for the menu functions --- tests/test_gui/test_gui_mainmenu.py | 70 ++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index 3da0d48c7..752e596e8 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -40,19 +40,19 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): """Test the main menu Edit and Format entries. """ # Block message box - monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) - monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True) + monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) + monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) # Test Document Action with No Project - assert not nwGUI.docEditor.docAction(nwDocAction.COPY) + assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.openProject(nwLipsum) + assert nwGUI.openProject(nwLipsum) is True qtbot.wait(stepDelay) # Split By Chapter - assert nwGUI.openDocument("4c4f28287af27") - assert nwGUI.docEditor.setCursorPosition(30) + assert nwGUI.openDocument("4c4f28287af27") is True + assert nwGUI.docEditor.setCursorPosition(30) is True cleanText = nwGUI.docEditor.getText()[27:74] @@ -114,30 +114,44 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): # Block Formats assert nwGUI.docEditor.setCursorPosition(30) + + ## Header 1 nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger) fmtStr = "# Pellentesque nec erat ut nulla posuere commodo." assert nwGUI.docEditor.getText()[27:76] == fmtStr qtbot.wait(stepDelay) + + ## Header 2 nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger) fmtStr = "## Pellentesque nec erat ut nulla posuere commodo." assert nwGUI.docEditor.getText()[27:77] == fmtStr qtbot.wait(stepDelay) + + ## Header 3 nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger) fmtStr = "### Pellentesque nec erat ut nulla posuere commodo." assert nwGUI.docEditor.getText()[27:78] == fmtStr qtbot.wait(stepDelay) + + ## Header 4 nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger) fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo." assert nwGUI.docEditor.getText()[27:79] == fmtStr qtbot.wait(stepDelay) + + ## Clear Format nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) assert nwGUI.docEditor.getText()[27:74] == cleanText qtbot.wait(stepDelay) + + ## Comment On nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) fmtStr = "% Pellentesque nec erat ut nulla posuere commodo." assert nwGUI.docEditor.getText()[27:76] == fmtStr qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) + + ## Comment Off + nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) assert nwGUI.docEditor.getText()[27:74] == cleanText qtbot.wait(stepDelay) @@ -213,6 +227,48 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): nwGUI.docEditor.clear() assert nwGUI.docEditor.isEmpty() + # Alignment & Indent + cleanText = "A single, short paragraph.\n\n" + nwGUI.docEditor.setText(cleanText) + assert nwGUI.docEditor.setCursorPosition(0) + + ## Left Align + nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger) + fmtStr = "A single, short paragraph. <<" + assert nwGUI.docEditor.getText()[:29] == fmtStr + qtbot.wait(stepDelay) + + ## Right Align + nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger) + fmtStr = ">> A single, short paragraph." + assert nwGUI.docEditor.getText()[:29] == fmtStr + qtbot.wait(stepDelay) + + ## Centre Align + nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger) + fmtStr = ">> A single, short paragraph. <<" + assert nwGUI.docEditor.getText()[:32] == fmtStr + qtbot.wait(stepDelay) + + ## Left Indent + nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger) + fmtStr = "> A single, short paragraph." + assert nwGUI.docEditor.getText()[:28] == fmtStr + qtbot.wait(stepDelay) + + ## Right Indent + nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger) + fmtStr = "> A single, short paragraph. <" + assert nwGUI.docEditor.getText()[:30] == fmtStr + qtbot.wait(stepDelay) + + ## No Format + nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[:30] == cleanText + qtbot.wait(stepDelay) + + # Other Checks + # Replace Quotes nwGUI.docEditor.setText(( "### New Text\n\n" From 307a52e7f4bf58ad2500566dd15b3f5657b86d07 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 13 Jun 2021 19:07:36 +0200 Subject: [PATCH 12/12] Update documentation --- docs/source/index.rst | 1 + docs/source/int_introduction.rst | 2 +- docs/source/usage_interface.rst | 171 +++++++------------------------ docs/source/usage_shortcuts.rst | 147 ++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 136 deletions(-) create mode 100644 docs/source/usage_shortcuts.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index d110f7466..e3e256148 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -71,6 +71,7 @@ operating system for further details. :caption: Usage usage_interface + usage_shortcuts usage_projects usage_structure usage_notes diff --git a/docs/source/int_introduction.rst b/docs/source/int_introduction.rst index 693b8dbe5..706480cf0 100644 --- a/docs/source/int_introduction.rst +++ b/docs/source/int_introduction.rst @@ -44,7 +44,7 @@ The main window does not have a toolbar like many other applications do. This re since the documents are formatted with markdown tags, is more or less redundant. However, all formatting features supported are available through convenient keyboard shortcuts. They are also available in the main menu so you don't have to look up the syntax every time until you've -memorised it. A full list of shortcuts can be found in the :ref:`a_ui_shortcuts` section. +memorised it. A full list of shortcuts can be found in the :ref:`a_kb` section. In addition, novelWriter has a :guilabel:`Focus Mode` where all the user interface elements other than the document editor itself are hidden away. diff --git a/docs/source/usage_interface.rst b/docs/source/usage_interface.rst index a93784721..25f16470f 100644 --- a/docs/source/usage_interface.rst +++ b/docs/source/usage_interface.rst @@ -281,6 +281,42 @@ background, depending on the selected theme. it prevents the line wrapping algorithms from adding line breaks where it shouldn't. +.. _a_ui_md_align: + +Paragraphs Alignment and Indentation +------------------------------------ + +Aside from Title Page and Partition which by default have its text centred, all document layouts +have the text by default aligned to the left ot justified, depending on your Preferences. + +You can override the default text alignment on individual paragraphs by specifying alignment tags. +These tags are double angle brackets. Either ``>>`` or ``<<``. You put them either before or after +the paragraph, and they will "push" the text towards the edge the brackets point towards. This +should be fairly intuitive. + +Indentation uses a similar syntax. But here you use a single ``>`` or ``<`` to push the text away +from the edge. + +Examples: + +.. csv-table:: Text Alignment and Indentation + :header: "Syntax", "Description" + :widths: 40, 60 + :class: "tight-table" + + "``>> Right aligned text``", "The text paragraph is right-aligned." + "``Left aligned text <<``", "The text paragraph is left-aligned." + "``>> Centred text <<``", "The text paragraph is centred." + "``> Indented text``", "The text has an increased left margin." + "``Indented text <``", "The text has an increased right margin." + "``> Indented text <``", "The text has an both margins increased." + +.. note:: + The text editor will not show the alignment and indentation live. But the viewer will show them + when you open the document in the viewer. It will of course also be reflected in the document + generated from the build tool. + + .. _a_ui_md_emph: Text Emphasis @@ -399,138 +435,3 @@ very large, The :guilabel:`Synopsis` column of the outline view takes its information from a specially formatted comment. See :ref:`a_ui_md_comm`. - - -.. _a_ui_shortcuts: - -Keyboard Shortcuts -================== - -Most features are available as keyboard shortcuts. These are as follows: - -.. csv-table:: Keyboard Shortcuts - :header: "Shortcut", "Description" - :widths: 30, 70 - :class: "tight-table" - - ":kbd:`Alt`:kbd:`1`", "Switch focus to the project tree. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`1`." - ":kbd:`Alt`:kbd:`2`", "Switch focus to document editor. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`2`." - ":kbd:`Alt`:kbd:`3`", "Switch focus to document viewer. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`3`." - ":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`." - ":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer." - ":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer." - ":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor." - ":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog." - ":kbd:`Ctrl`:kbd:`/`", "Change block format to comment." - ":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor." - ":kbd:`Ctrl`:kbd:`1`", "Change block format to header level 1." - ":kbd:`Ctrl`:kbd:`2`", "Change block format to header level 2." - ":kbd:`Ctrl`:kbd:`3`", "Change block format to header level 3." - ":kbd:`Ctrl`:kbd:`4`", "Change block format to header level 4." - ":kbd:`Ctrl`:kbd:`A`", "Select all text in the document." - ":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)." - ":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard." - ":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor." - ":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`.)" - ":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected." - ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`.)" - ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)" - ":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)." - ":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_ui_shortcuts_ins`." - ":kbd:`Ctrl`:kbd:`N`", "Create new document." - ":kbd:`Ctrl`:kbd:`O`", "Open selected document." - ":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter." - ":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing." - ":kbd:`Ctrl`:kbd:`S`", "Save the current document in the document editor." - ":kbd:`Ctrl`:kbd:`V`", "Paste text from clipboard to cursor position." - ":kbd:`Ctrl`:kbd:`W`", "Close the current document in the document editor." - ":kbd:`Ctrl`:kbd:`X`", "Cut selected text to clipboard." - ":kbd:`Ctrl`:kbd:`Y`", "Redo latest undo." - ":kbd:`Ctrl`:kbd:`Z`", "Undo latest changes." - ":kbd:`Ctrl`:kbd:`F7`", "Toggle spell checking." - ":kbd:`Ctrl`:kbd:`F10`", "Toggle automatic updating of project outline." - ":kbd:`Ctrl`:kbd:`Up`", "Move item one step up in the project tree." - ":kbd:`Ctrl`:kbd:`Down`", "Move item one step down in the project tree." - ":kbd:`Ctrl`:kbd:`Del`", "Delete next word in editor." - ":kbd:`Ctrl`:kbd:`Backspace`", "Delete previous word in editor." - ":kbd:`Ctrl`:kbd:`'`", "Wrap selected text, or word under cursor, in single quotes." - ":kbd:`Ctrl`:kbd:`""`", "Wrap selected text, or word under cursor, in double quotes." - ":kbd:`Ctrl`:kbd:`Retrun`", "Open the tag or reference under the cursor in the Viewer." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`,`", "Open the :guilabel:`Project Settings` dialog." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`/`", "Remove block formatting for block under cursor." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`.)" - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Undo move of project tree item." - ":kbd:`Ctrl`:kbd:`Shift`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder." - ":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website." - ":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)" - ":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)" - ":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog." - ":kbd:`F6`", "Open the :guilabel:`Writing Statistics` dialog." - ":kbd:`F7`", "Re-run spell checker." - ":kbd:`F8`", "Activate :guilabel:`Focus Mode`, hiding the project tree and document viewer." - ":kbd:`F9`", "Re-build the project index." - ":kbd:`F10`", "Re-build the project outline." - ":kbd:`F11`", "Activate full screen mode." - ":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser." - ":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)" - ":kbd:`Shift`:kbd:`F6`", "Open the :guilabel:`Project Details` dialog." - ":kbd:`Return`", "If in the project tree, open a document for editing." - -.. note:: - On macOS, replace :kbd:`Ctrl` with :kbd:`Cmd`. - - -.. _a_ui_shortcuts_ins: - -Insert Shortcuts ----------------- - -A set of insert features are also available through shortcuts, but they require a double -combination of key sequences. The insert feature is activated with :kbd:`Ctrl`:kbd:`K`, followed by -a key or key combination for the inserted content. - -.. csv-table:: Keyboard Shortcuts - :header: "Shortcut", "Description" - :widths: 40, 60 - :class: "tight-table" - - ":kbd:`Ctrl`:kbd:`K`, :kbd:`−`", "Insert a short dash (en dash)." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`_`", "Insert a long dash (em dash)." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`_`", "Insert a horizontal bar (quotation dash)." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`~`", "Insert a figure dash (same width as a number)." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert a left single quote." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert a right single quote." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert a left double quote." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert a right double quote." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier apostrophe." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert an ellipsis." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`'`", "Insert a prime." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`""`", "Insert a double prime." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`", "Insert a thin non-breaking space." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`*`", "Insert a list bullet." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`−`", "Insert a hyphen bullet (alternative bullet)." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`*`", "Insert a flower mark (alternative bullet)." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`%`", "Insert a per mille symbol." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`O`", "Insert a degree symbol." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`X`", "Insert a times sign." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`D`", "Insert a division sign." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`G`", "Insert a ``@tag`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`V`", "Insert a ``@pov`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`F`", "Insert a ``@focus`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`C`", "Insert a ``@char`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`P`", "Insert a ``@plot`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`T`", "Insert a ``@time`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`L`", "Insert a ``@location`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`O`", "Insert an ``@object`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`E`", "Insert an ``@entity`` keyword." - ":kbd:`Ctrl`:kbd:`K`, :kbd:`X`", "Insert a ``@custom`` keyword." diff --git a/docs/source/usage_shortcuts.rst b/docs/source/usage_shortcuts.rst new file mode 100644 index 000000000..6c92c3506 --- /dev/null +++ b/docs/source/usage_shortcuts.rst @@ -0,0 +1,147 @@ +.. _a_kb: + +****************** +Keyboard Shortcuts +****************** + +Most features in novelWriter are available as keyboard shortcuts. + + +.. _a_kb_main: + +Main Shortcuts +============== + +The main shorcuts are as follows: + +.. csv-table:: Keyboard Shortcuts + :header: "Shortcut", "Description" + :widths: 30, 70 + :class: "tight-table" + + ":kbd:`Alt`:kbd:`1`", "Switch focus to the project tree. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`1`." + ":kbd:`Alt`:kbd:`2`", "Switch focus to document editor. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`2`." + ":kbd:`Alt`:kbd:`3`", "Switch focus to document viewer. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`3`." + ":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`." + ":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer." + ":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer." + ":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor." + ":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog." + ":kbd:`Ctrl`:kbd:`/`", "Toggle block format as comment." + ":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor." + ":kbd:`Ctrl`:kbd:`1`", "Change block format to header level 1." + ":kbd:`Ctrl`:kbd:`2`", "Change block format to header level 2." + ":kbd:`Ctrl`:kbd:`3`", "Change block format to header level 3." + ":kbd:`Ctrl`:kbd:`4`", "Change block format to header level 4." + ":kbd:`Ctrl`:kbd:`5`", "Change block alignment to left-aligned." + ":kbd:`Ctrl`:kbd:`6`", "Change block alignment to centred." + ":kbd:`Ctrl`:kbd:`7`", "Change block alignment to right-aligned." + ":kbd:`Ctrl`:kbd:`8`", "Add a left margin to the block." + ":kbd:`Ctrl`:kbd:`9`", "Add a right margin to the block." + ":kbd:`Ctrl`:kbd:`A`", "Select all text in the document." + ":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)." + ":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard." + ":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor." + ":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`.)" + ":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected." + ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`.)" + ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)" + ":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)." + ":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`." + ":kbd:`Ctrl`:kbd:`N`", "Create new document." + ":kbd:`Ctrl`:kbd:`O`", "Open selected document." + ":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter." + ":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing." + ":kbd:`Ctrl`:kbd:`S`", "Save the current document in the document editor." + ":kbd:`Ctrl`:kbd:`V`", "Paste text from clipboard to cursor position." + ":kbd:`Ctrl`:kbd:`W`", "Close the current document in the document editor." + ":kbd:`Ctrl`:kbd:`X`", "Cut selected text to clipboard." + ":kbd:`Ctrl`:kbd:`Y`", "Redo latest undo." + ":kbd:`Ctrl`:kbd:`Z`", "Undo latest changes." + ":kbd:`Ctrl`:kbd:`F7`", "Toggle spell checking." + ":kbd:`Ctrl`:kbd:`F10`", "Toggle automatic updating of project outline." + ":kbd:`Ctrl`:kbd:`Up`", "Move item one step up in the project tree." + ":kbd:`Ctrl`:kbd:`Down`", "Move item one step down in the project tree." + ":kbd:`Ctrl`:kbd:`Del`", "Delete next word in editor." + ":kbd:`Ctrl`:kbd:`Backspace`", "Delete previous word in editor." + ":kbd:`Ctrl`:kbd:`'`", "Wrap selected text, or word under cursor, in single quotes." + ":kbd:`Ctrl`:kbd:`""`", "Wrap selected text, or word under cursor, in double quotes." + ":kbd:`Ctrl`:kbd:`Retrun`", "Open the tag or reference under the cursor in the Viewer." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`,`", "Open the :guilabel:`Project Settings` dialog." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`/`", "Remove block formatting for block under cursor." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`.)" + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`W`", "Close the current project." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`Z`", "Undo move of project tree item." + ":kbd:`Ctrl`:kbd:`Shift`:kbd:`Del`", "If in the project tree, move a document to trash, or delete a folder." + ":kbd:`F1`", "Open the documentation. This will either open the Qt Assistant, if available, or send you to the documentation website." + ":kbd:`F2`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`Ctrl`:kbd:`E`)" + ":kbd:`F3`", "Find next occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`G`)" + ":kbd:`F5`", "Open the :guilabel:`Build Novel Project` dialog." + ":kbd:`F6`", "Open the :guilabel:`Writing Statistics` dialog." + ":kbd:`F7`", "Re-run spell checker." + ":kbd:`F8`", "Activate :guilabel:`Focus Mode`, hiding the project tree and document viewer." + ":kbd:`F9`", "Re-build the project index." + ":kbd:`F10`", "Re-build the project outline." + ":kbd:`F11`", "Activate full screen mode." + ":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser." + ":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)" + ":kbd:`Shift`:kbd:`F6`", "Open the :guilabel:`Project Details` dialog." + ":kbd:`Return`", "If in the project tree, open a document for editing." + +.. note:: + On macOS, replace :kbd:`Ctrl` with :kbd:`Cmd`. + + +.. _a_kb_ins: + +Insert Shortcuts +================ + +A set of insert features are also available through shortcuts, but they require a double +combination of key sequences. The insert feature is activated with :kbd:`Ctrl`:kbd:`K`, followed by +a key or key combination for the inserted content. + +.. csv-table:: Keyboard Shortcuts + :header: "Shortcut", "Description" + :widths: 40, 60 + :class: "tight-table" + + ":kbd:`Ctrl`:kbd:`K`, :kbd:`−`", "Insert a short dash (en dash)." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`_`", "Insert a long dash (em dash)." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`_`", "Insert a horizontal bar (quotation dash)." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`~`", "Insert a figure dash (same width as a number)." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert a left single quote." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert a right single quote." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert a left double quote." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert a right double quote." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier apostrophe." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert an ellipsis." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`'`", "Insert a prime." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`""`", "Insert a double prime." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`Space`", "Insert a thin non-breaking space." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`*`", "Insert a list bullet." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`−`", "Insert a hyphen bullet (alternative bullet)." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`*`", "Insert a flower mark (alternative bullet)." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`%`", "Insert a per mille symbol." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`O`", "Insert a degree symbol." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`X`", "Insert a times sign." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`Ctrl`:kbd:`D`", "Insert a division sign." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`G`", "Insert a ``@tag`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`V`", "Insert a ``@pov`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`F`", "Insert a ``@focus`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`C`", "Insert a ``@char`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`P`", "Insert a ``@plot`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`T`", "Insert a ``@time`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`L`", "Insert a ``@location`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`O`", "Insert an ``@object`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`E`", "Insert an ``@entity`` keyword." + ":kbd:`Ctrl`:kbd:`K`, :kbd:`X`", "Insert a ``@custom`` keyword."