diff options
Diffstat (limited to 'src/gmdocument.c')
-rw-r--r-- | src/gmdocument.c | 160 |
1 files changed, 96 insertions, 64 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index a6fc39c5..fc49dac4 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -1,3 +1,25 @@ | |||
1 | /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
1 | #include "gmdocument.h" | 23 | #include "gmdocument.h" |
2 | #include "gmutil.h" | 24 | #include "gmutil.h" |
3 | #include "ui/color.h" | 25 | #include "ui/color.h" |
@@ -106,11 +128,11 @@ enum iGmLineType { | |||
106 | max_GmLineType, | 128 | max_GmLineType, |
107 | }; | 129 | }; |
108 | 130 | ||
109 | static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc *line) { | 131 | static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) { |
110 | if (d->format == plainText_GmDocumentFormat) { | 132 | if (d->format == plainText_GmDocumentFormat) { |
111 | return text_GmLineType; | 133 | return text_GmLineType; |
112 | } | 134 | } |
113 | if (isEmpty_Range(line)) { | 135 | if (isEmpty_Range(&line)) { |
114 | return text_GmLineType; | 136 | return text_GmLineType; |
115 | } | 137 | } |
116 | if (startsWith_Rangecc(line, "=>")) { | 138 | if (startsWith_Rangecc(line, "=>")) { |
@@ -128,10 +150,10 @@ static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangec | |||
128 | if (startsWith_Rangecc(line, "```")) { | 150 | if (startsWith_Rangecc(line, "```")) { |
129 | return preformatted_GmLineType; | 151 | return preformatted_GmLineType; |
130 | } | 152 | } |
131 | if (*line->start == '>') { | 153 | if (*line.start == '>') { |
132 | return quote_GmLineType; | 154 | return quote_GmLineType; |
133 | } | 155 | } |
134 | if (size_Range(line) >= 2 && line->start[0] == '*' && isspace(line->start[1])) { | 156 | if (size_Range(&line) >= 2 && line.start[0] == '*' && isspace(line.start[1])) { |
135 | return bullet_GmLineType; | 157 | return bullet_GmLineType; |
136 | } | 158 | } |
137 | return text_GmLineType; | 159 | return text_GmLineType; |
@@ -157,11 +179,11 @@ static int lastVisibleRunBottom_GmDocument_(const iGmDocument *d) { | |||
157 | iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *start, int font) { | 179 | iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *start, int font) { |
158 | const iRangecc content = { start, constEnd_String(&d->source) }; | 180 | const iRangecc content = { start, constEnd_String(&d->source) }; |
159 | iRangecc line = iNullRange; | 181 | iRangecc line = iNullRange; |
160 | nextSplit_Rangecc(&content, "\n", &line); | 182 | nextSplit_Rangecc(content, "\n", &line); |
161 | iAssert(startsWith_Rangecc(&line, "```")); | 183 | iAssert(startsWith_Rangecc(line, "```")); |
162 | iRangecc preBlock = { line.end + 1, line.end + 1 }; | 184 | iRangecc preBlock = { line.end + 1, line.end + 1 }; |
163 | while (nextSplit_Rangecc(&content, "\n", &line)) { | 185 | while (nextSplit_Rangecc(content, "\n", &line)) { |
164 | if (startsWith_Rangecc(&line, "```")) { | 186 | if (startsWith_Rangecc(line, "```")) { |
165 | break; | 187 | break; |
166 | } | 188 | } |
167 | preBlock.end = line.end; | 189 | preBlock.end = line.end; |
@@ -170,31 +192,35 @@ iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *sta | |||
170 | } | 192 | } |
171 | 193 | ||
172 | static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *linkId) { | 194 | static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *linkId) { |
173 | iRegExp *pattern = new_RegExp("=>\\s*([^\\s]+)(\\s.*)?", caseInsensitive_RegExpOption); | 195 | static iRegExp *pattern_; |
196 | if (!pattern_) { | ||
197 | pattern_ = new_RegExp("=>\\s*([^\\s]+)(\\s.*)?", caseInsensitive_RegExpOption); | ||
198 | } | ||
174 | iRegExpMatch m; | 199 | iRegExpMatch m; |
175 | if (matchRange_RegExp(pattern, line, &m)) { | 200 | init_RegExpMatch(&m); |
201 | if (matchRange_RegExp(pattern_, line, &m)) { | ||
176 | iGmLink *link = new_GmLink(); | 202 | iGmLink *link = new_GmLink(); |
177 | setRange_String(&link->url, capturedRange_RegExpMatch(&m, 1)); | 203 | setRange_String(&link->url, capturedRange_RegExpMatch(&m, 1)); |
178 | set_String(&link->url, absoluteUrl_String(&d->url, &link->url)); | 204 | set_String(&link->url, absoluteUrl_String(&d->url, &link->url)); |
179 | /* Check the URL. */ { | 205 | /* Check the URL. */ { |
180 | iUrl parts; | 206 | iUrl parts; |
181 | init_Url(&parts, &link->url); | 207 | init_Url(&parts, &link->url); |
182 | if (!equalCase_Rangecc(&parts.host, cstr_String(&d->localHost))) { | 208 | if (!equalCase_Rangecc(parts.host, cstr_String(&d->localHost))) { |
183 | link->flags |= remote_GmLinkFlag; | 209 | link->flags |= remote_GmLinkFlag; |
184 | } | 210 | } |
185 | if (startsWithCase_Rangecc(&parts.protocol, "gemini")) { | 211 | if (startsWithCase_Rangecc(parts.scheme, "gemini")) { |
186 | link->flags |= gemini_GmLinkFlag; | 212 | link->flags |= gemini_GmLinkFlag; |
187 | } | 213 | } |
188 | else if (startsWithCase_Rangecc(&parts.protocol, "http")) { | 214 | else if (startsWithCase_Rangecc(parts.scheme, "http")) { |
189 | link->flags |= http_GmLinkFlag; | 215 | link->flags |= http_GmLinkFlag; |
190 | } | 216 | } |
191 | else if (equalCase_Rangecc(&parts.protocol, "gopher")) { | 217 | else if (equalCase_Rangecc(parts.scheme, "gopher")) { |
192 | link->flags |= gopher_GmLinkFlag; | 218 | link->flags |= gopher_GmLinkFlag; |
193 | } | 219 | } |
194 | else if (equalCase_Rangecc(&parts.protocol, "file")) { | 220 | else if (equalCase_Rangecc(parts.scheme, "file")) { |
195 | link->flags |= file_GmLinkFlag; | 221 | link->flags |= file_GmLinkFlag; |
196 | } | 222 | } |
197 | else if (equalCase_Rangecc(&parts.protocol, "data")) { | 223 | else if (equalCase_Rangecc(parts.scheme, "data")) { |
198 | link->flags |= data_GmLinkFlag; | 224 | link->flags |= data_GmLinkFlag; |
199 | } | 225 | } |
200 | /* Check the file name extension, if present. */ | 226 | /* Check the file name extension, if present. */ |
@@ -232,7 +258,6 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
232 | line = capturedRange_RegExpMatch(&m, 1); /* Show the URL. */ | 258 | line = capturedRange_RegExpMatch(&m, 1); /* Show the URL. */ |
233 | } | 259 | } |
234 | } | 260 | } |
235 | iRelease(pattern); | ||
236 | return line; | 261 | return line; |
237 | } | 262 | } |
238 | 263 | ||
@@ -254,16 +279,21 @@ static size_t findLinkImage_GmDocument_(const iGmDocument *d, iGmLinkId linkId) | |||
254 | return iInvalidPos; | 279 | return iInvalidPos; |
255 | } | 280 | } |
256 | 281 | ||
282 | static iBool isGopher_GmDocument_(const iGmDocument *d) { | ||
283 | return equalCase_Rangecc(urlScheme_String(&d->url), "gopher"); | ||
284 | } | ||
285 | |||
257 | static void doLayout_GmDocument_(iGmDocument *d) { | 286 | static void doLayout_GmDocument_(iGmDocument *d) { |
287 | const iBool isGemini = !isGopher_GmDocument_(d); | ||
258 | /* TODO: Collect these parameters into a GmTheme. */ | 288 | /* TODO: Collect these parameters into a GmTheme. */ |
259 | static const int fonts[max_GmLineType] = { | 289 | const int fonts[max_GmLineType] = { |
260 | paragraph_FontId, | 290 | isGemini ? paragraph_FontId : preformatted_FontId, |
261 | paragraph_FontId, /* bullet */ | 291 | isGemini ? paragraph_FontId : preformatted_FontId, /* bullet */ |
262 | preformatted_FontId, | 292 | preformatted_FontId, |
263 | quote_FontId, | 293 | quote_FontId, |
264 | heading1_FontId, | 294 | isGemini ? heading1_FontId : preformatted_FontId, |
265 | heading2_FontId, | 295 | isGemini ? heading2_FontId : preformatted_FontId, |
266 | heading3_FontId, | 296 | isGemini ? heading3_FontId : preformatted_FontId, |
267 | regular_FontId, | 297 | regular_FontId, |
268 | }; | 298 | }; |
269 | static const int colors[max_GmLineType] = { | 299 | static const int colors[max_GmLineType] = { |
@@ -277,7 +307,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
277 | tmLinkText_ColorId, | 307 | tmLinkText_ColorId, |
278 | }; | 308 | }; |
279 | static const int indents[max_GmLineType] = { | 309 | static const int indents[max_GmLineType] = { |
280 | 5, 10, 5, 10, 0, 0, 0, 5 | 310 | 6, 12, 6, 12, 0, 0, 0, 6 |
281 | }; | 311 | }; |
282 | static const float topMargin[max_GmLineType] = { | 312 | static const float topMargin[max_GmLineType] = { |
283 | 0.0f, 0.5f, 1.0f, 0.5f, 2.0f, 2.0f, 1.5f, 1.0f | 313 | 0.0f, 0.5f, 1.0f, 0.5f, 2.0f, 2.0f, 1.5f, 1.0f |
@@ -298,20 +328,21 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
298 | return; | 328 | return; |
299 | } | 329 | } |
300 | const iRangecc content = range_String(&d->source); | 330 | const iRangecc content = range_String(&d->source); |
331 | iRangecc contentLine = iNullRange; | ||
301 | iInt2 pos = zero_I2(); | 332 | iInt2 pos = zero_I2(); |
302 | iRangecc line = iNullRange; | 333 | iBool isFirstText = isGemini; |
303 | iRangecc preAltText = iNullRange; | ||
304 | enum iGmLineType prevType; // = text_GmLineType; | ||
305 | iBool isPreformat = iFalse; | 334 | iBool isPreformat = iFalse; |
306 | iBool isFirstText = iTrue; | 335 | iRangecc preAltText = iNullRange; |
336 | int preFont = preformatted_FontId; | ||
307 | iBool enableIndents = iFalse; | 337 | iBool enableIndents = iFalse; |
308 | iBool addSiteBanner = iTrue; | 338 | iBool addSiteBanner = iTrue; |
309 | int preFont = preformatted_FontId; | 339 | enum iGmLineType prevType; |
310 | if (d->format == plainText_GmDocumentFormat) { | 340 | if (d->format == plainText_GmDocumentFormat) { |
311 | isPreformat = iTrue; | 341 | isPreformat = iTrue; |
312 | isFirstText = iFalse; | 342 | isFirstText = iFalse; |
313 | } | 343 | } |
314 | while (nextSplit_Rangecc(&content, "\n", &line)) { | 344 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { |
345 | iRangecc line = contentLine; /* `line` will be trimmed later; would confuse nextSplit */ | ||
315 | iGmRun run; | 346 | iGmRun run; |
316 | run.flags = 0; | 347 | run.flags = 0; |
317 | run.color = white_ColorId; | 348 | run.color = white_ColorId; |
@@ -320,7 +351,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
320 | enum iGmLineType type; | 351 | enum iGmLineType type; |
321 | int indent = 0; | 352 | int indent = 0; |
322 | if (!isPreformat) { | 353 | if (!isPreformat) { |
323 | type = lineType_GmDocument_(d, &line); | 354 | type = lineType_GmDocument_(d, line); |
324 | if (line.start == content.start) { | 355 | if (line.start == content.start) { |
325 | prevType = type; | 356 | prevType = type; |
326 | } | 357 | } |
@@ -358,7 +389,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
358 | /* Preformatted line. */ | 389 | /* Preformatted line. */ |
359 | type = preformatted_GmLineType; | 390 | type = preformatted_GmLineType; |
360 | if (d->format == gemini_GmDocumentFormat && | 391 | if (d->format == gemini_GmDocumentFormat && |
361 | startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) { | 392 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { |
362 | isPreformat = iFalse; | 393 | isPreformat = iFalse; |
363 | preAltText = iNullRange; | 394 | preAltText = iNullRange; |
364 | addSiteBanner = iFalse; /* overrides the banner */ | 395 | addSiteBanner = iFalse; /* overrides the banner */ |
@@ -664,6 +695,20 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
664 | } | 695 | } |
665 | /* Set up colors. */ | 696 | /* Set up colors. */ |
666 | if (d->themeSeed) { | 697 | if (d->themeSeed) { |
698 | enum iHue { | ||
699 | red_Hue, | ||
700 | reddishOrange_Hue, | ||
701 | yellowishOrange_Hue, | ||
702 | yellow_Hue, | ||
703 | greenishYellow_Hue, | ||
704 | green_Hue, | ||
705 | bluishGreen_Hue, | ||
706 | cyan_Hue, | ||
707 | skyBlue_Hue, | ||
708 | blue_Hue, | ||
709 | violet_Hue, | ||
710 | pink_Hue | ||
711 | }; | ||
667 | static const float hues[] = { 5, 25, 40, 56, 80, 120, 160, 180, 208, 231, 270, 324 }; | 712 | static const float hues[] = { 5, 25, 40, 56, 80, 120, 160, 180, 208, 231, 270, 324 }; |
668 | static const struct { | 713 | static const struct { |
669 | int index[2]; | 714 | int index[2]; |
@@ -681,9 +726,9 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
681 | { 8, 9 }, /* violet */ | 726 | { 8, 9 }, /* violet */ |
682 | { 7, 8 }, /* pink */ | 727 | { 7, 8 }, /* pink */ |
683 | }; | 728 | }; |
684 | const float saturationLevel = 1.0f; /* TODO: user setting */ | ||
685 | const iBool isBannerLighter = (d->themeSeed & 0x4000) != 0; | 729 | const iBool isBannerLighter = (d->themeSeed & 0x4000) != 0; |
686 | const size_t primIndex = d->themeSeed ? (d->themeSeed & 0xff) % iElemCount(hues) : 2; | 730 | const size_t primIndex = d->themeSeed ? (d->themeSeed & 0xff) % iElemCount(hues) : 2; |
731 | const float saturationLevel = 1.0f; /* TODO: user setting */ | ||
687 | const iBool isDarkBgSat = | 732 | const iBool isDarkBgSat = |
688 | (d->themeSeed & 0x200000) != 0 && (primIndex < 1 || primIndex > 4); | 733 | (d->themeSeed & 0x200000) != 0 && (primIndex < 1 || primIndex > 4); |
689 | iHSLColor base = { hues[primIndex], | 734 | iHSLColor base = { hues[primIndex], |
@@ -692,9 +737,10 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
692 | 1.0f }; | 737 | 1.0f }; |
693 | // printf("background: %d %f %f\n", (int) base.hue, base.sat, base.lum); | 738 | // printf("background: %d %f %f\n", (int) base.hue, base.sat, base.lum); |
694 | // printf("isDarkBgSat: %d\n", isDarkBgSat); | 739 | // printf("isDarkBgSat: %d\n", isDarkBgSat); |
695 | setHsl_Color(tmBackground_ColorId, base); | 740 | iHSLColor bgBase = base; |
741 | setHsl_Color(tmBackground_ColorId, bgBase); | ||
696 | 742 | ||
697 | setHsl_Color(tmBannerBackground_ColorId, addSatLum_HSLColor(base, 0.1f, 0.04f * (isBannerLighter ? 1 : -1))); | 743 | setHsl_Color(tmBannerBackground_ColorId, addSatLum_HSLColor(bgBase, 0.1f, 0.04f * (isBannerLighter ? 1 : -1))); |
698 | setHsl_Color(tmBannerTitle_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.1f, 0), 0.55f)); | 744 | setHsl_Color(tmBannerTitle_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.1f, 0), 0.55f)); |
699 | setHsl_Color(tmBannerIcon_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.35f, 0), 0.65f)); | 745 | setHsl_Color(tmBannerIcon_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.35f, 0), 0.65f)); |
700 | 746 | ||
@@ -714,7 +760,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
714 | setHsl_Color(tmHeading3_ColorId, setLum_HSLColor(altBase, titleLum + 0.60f)); | 760 | setHsl_Color(tmHeading3_ColorId, setLum_HSLColor(altBase, titleLum + 0.60f)); |
715 | 761 | ||
716 | setHsl_Color(tmParagraph_ColorId, addSatLum_HSLColor(base, 0.1f, 0.6f)); | 762 | setHsl_Color(tmParagraph_ColorId, addSatLum_HSLColor(base, 0.1f, 0.6f)); |
717 | setHsl_Color(tmFirstParagraph_ColorId, addSatLum_HSLColor(base, 0.2f, 0.8f)); | 763 | setHsl_Color(tmFirstParagraph_ColorId, addSatLum_HSLColor(base, 0.2f, 0.72f)); |
718 | setHsl_Color(tmPreformatted_ColorId, (iHSLColor){ altHue2, 1.0f, 0.75f, 1.0f }); | 764 | setHsl_Color(tmPreformatted_ColorId, (iHSLColor){ altHue2, 1.0f, 0.75f, 1.0f }); |
719 | set_Color(tmQuote_ColorId, get_Color(tmPreformatted_ColorId)); | 765 | set_Color(tmQuote_ColorId, get_Color(tmPreformatted_ColorId)); |
720 | set_Color(tmInlineContentMetadata_ColorId, get_Color(tmHeading3_ColorId)); | 766 | set_Color(tmInlineContentMetadata_ColorId, get_Color(tmHeading3_ColorId)); |
@@ -736,31 +782,12 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
736 | else if (i == tmHeading3_ColorId) { | 782 | else if (i == tmHeading3_ColorId) { |
737 | color.lum *= 0.75f; | 783 | color.lum *= 0.75f; |
738 | } | 784 | } |
739 | #if 0 | ||
740 | else if (isLink_ColorId(i)) { | ||
741 | /* Darken links generally to improve visibility against a | ||
742 | light background. */ | ||
743 | color.lum *= 0.5f; | ||
744 | color.sat = 1.0f; | ||
745 | } | ||
746 | #endif | ||
747 | else if (i == tmBannerIcon_ColorId || i == tmBannerTitle_ColorId) { | 785 | else if (i == tmBannerIcon_ColorId || i == tmBannerTitle_ColorId) { |
748 | if (isBannerLighter) { | 786 | color.sat = 1.0f; |
749 | color.lum *= 0.75f; | 787 | color.lum = 0.35f; |
750 | } | ||
751 | else { | ||
752 | color.lum = 0.98f; | ||
753 | } | ||
754 | } | 788 | } |
755 | else if (i == tmBannerBackground_ColorId) { | 789 | else if (i == tmBannerBackground_ColorId) { |
756 | if (isBannerLighter) { | 790 | color = hsl_Color(get_Color(tmBackground_ColorId)); |
757 | //color.lum = iMin(0.9, color.lum); | ||
758 | color = hsl_Color(get_Color(tmBackground_ColorId)); | ||
759 | } | ||
760 | else { | ||
761 | color.sat *= 0.8f; | ||
762 | color.lum = 0.6f; | ||
763 | } | ||
764 | } | 791 | } |
765 | else if (isText_ColorId(i)) { | 792 | else if (isText_ColorId(i)) { |
766 | color.sat = 0.9f; | 793 | color.sat = 0.9f; |
@@ -772,7 +799,12 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
772 | if (isDarkBgSat) { | 799 | if (isDarkBgSat) { |
773 | /* Saturate background, desaturate text. */ | 800 | /* Saturate background, desaturate text. */ |
774 | if (isBackground_ColorId(i)) { | 801 | if (isBackground_ColorId(i)) { |
775 | color.sat = (color.sat + 1) / 2; | 802 | if (primIndex != green_Hue) { |
803 | color.sat = (color.sat + 1) / 2; | ||
804 | } | ||
805 | else { | ||
806 | color.sat *= 0.5f; | ||
807 | } | ||
776 | color.lum *= 0.75f; | 808 | color.lum *= 0.75f; |
777 | } | 809 | } |
778 | else if (isText_ColorId(i)) { | 810 | else if (isText_ColorId(i)) { |
@@ -822,11 +854,11 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
822 | iRangecc src = range_String(&d->source); | 854 | iRangecc src = range_String(&d->source); |
823 | iRangecc line = iNullRange; | 855 | iRangecc line = iNullRange; |
824 | iBool isPreformat = iFalse; | 856 | iBool isPreformat = iFalse; |
825 | if (d->format == plainText_GmDocumentFormat) { | 857 | if (d->format == plainText_GmDocumentFormat || isGopher_GmDocument_(d)) { |
826 | isPreformat = iTrue; /* Cannot be turned off. */ | 858 | isPreformat = iTrue; /* Cannot be turned off. */ |
827 | } | 859 | } |
828 | const int preTabWidth = 8; /* TODO: user-configurable parameter */ | 860 | const int preTabWidth = 4; /* TODO: user-configurable parameter */ |
829 | while (nextSplit_Rangecc(&src, "\n", &line)) { | 861 | while (nextSplit_Rangecc(src, "\n", &line)) { |
830 | if (isPreformat) { | 862 | if (isPreformat) { |
831 | /* Replace any tab characters with spaces for visualization. */ | 863 | /* Replace any tab characters with spaces for visualization. */ |
832 | for (const char *ch = line.start; ch != line.end; ch++) { | 864 | for (const char *ch = line.start; ch != line.end; ch++) { |
@@ -842,12 +874,12 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
842 | } | 874 | } |
843 | } | 875 | } |
844 | appendCStr_String(normalized, "\n"); | 876 | appendCStr_String(normalized, "\n"); |
845 | if (lineType_GmDocument_(d, &line) == preformatted_GmLineType) { | 877 | if (lineType_GmDocument_(d, line) == preformatted_GmLineType) { |
846 | isPreformat = iFalse; | 878 | isPreformat = iFalse; |
847 | } | 879 | } |
848 | continue; | 880 | continue; |
849 | } | 881 | } |
850 | if (lineType_GmDocument_(d, &line) == preformatted_GmLineType) { | 882 | if (lineType_GmDocument_(d, line) == preformatted_GmLineType) { |
851 | isPreformat = iTrue; | 883 | isPreformat = iTrue; |
852 | appendRange_String(normalized, line); | 884 | appendRange_String(normalized, line); |
853 | appendCStr_String(normalized, "\n"); | 885 | appendCStr_String(normalized, "\n"); |