From e06c7fe5d89709394d0cfc4697fff78eb6e1f693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 8 May 2008 10:27:55 +0000 Subject: [PATCH] * Added a quote_context structure that FillInQuoteTextRuns() can now optionally use to keep track of the current context. This should not only make it faster, but may also fix the occasionally seen bug of the previous solution. * Added a simple diff mode coloring as well - only enabled when quote coloring is enabled. * Minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25364 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/mail/Content.cpp | 200 +++++++++++++++++++++++++---------- src/apps/mail/Content.h | 69 +++++++----- src/apps/mail/MailWindow.cpp | 5 +- 3 files changed, 187 insertions(+), 87 deletions(-) diff --git a/src/apps/mail/Content.cpp b/src/apps/mail/Content.cpp index feb574459d..8361c8a51f 100644 --- a/src/apps/mail/Content.cpp +++ b/src/apps/mail/Content.cpp @@ -89,14 +89,19 @@ const rgb_color kSpellTextColor = {255, 0, 0, 255}; const rgb_color kHyperLinkColor = {0, 0, 255, 255}; const rgb_color kHeaderColor = {72, 72, 72, 255}; -const rgb_color kQuoteColors[] = -{ - {0, 0, 0x80, 0}, // 3rd, 6th, ... quote level color - {0, 0x80, 0, 0}, // 1st, 4th, ... quote level color - {0x80, 0, 0, 0} // 2nd, ... +const rgb_color kQuoteColors[] = { + {0, 0, 0x80, 0}, // 3rd, 6th, ... quote level color (blue) + {0, 0x80, 0, 0}, // 1st, 4th, ... quote level color (green) + {0x80, 0, 0, 0} // 2nd, ... (red) }; const int32 kNumQuoteColors = 3; +const rgb_color kDiffColors[] = { + {0xb0, 0, 0, 0}, // '-', red + {0, 0x90, 0, 0}, // '+', green + {0x6a, 0x6a, 0x6a, 0} // '@@', dark grey +}; + void Unicode2UTF8(int32 c, char **out); @@ -422,6 +427,23 @@ CopyQuotes(const char *text, size_t length, char *outText, size_t &outLength) } +int32 +diff_mode(char c) +{ + if (c == '+') + return 2; + if (c == '-') + return 1; + if (c == '@') + return 3; + if (c == ' ') + return 0; + + // everything else ends the diff mode + return -1; +} + + bool is_quote_char(char c) { @@ -429,33 +451,42 @@ is_quote_char(char c) } -/** Fills the specified text_run_array with the correct values for the - * specified text. - * If "view" is NULL, it will assume that "line" lies on a line break, - * if not, it will correctly retrieve the number of quotes the current - * line already has. - */ - +/*! Fills the specified text_run_array with the correct values for the + specified text. + If "view" is NULL, it will assume that "line" lies on a line break, + if not, it will correctly retrieve the number of quotes the current + line already has. +*/ void -FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont &font, - text_run_array *style, int32 maxStyles) +FillInQuoteTextRuns(BTextView* view, quote_context* context, const char* line, + int32 length, const BFont& font, text_run_array* style, int32 maxStyles) { - text_run *runs = style->runs; + text_run* runs = style->runs; int32 index = style->count; bool begin; int32 pos = 0; + int32 diffMode = 0; + bool inDiff = false; + bool wasDiff = false; int32 level = 0; // get index to the beginning of the current line - if (view != NULL) { + if (context != NULL) { + level = context->level; + diffMode = context->diff_mode; + begin = context->begin; + inDiff = context->in_diff; + wasDiff = context->was_diff; + } else if (view != NULL) { int32 start, end; view->GetSelection(&end, &end); - begin = view->TextLength() == 0 || view->ByteAt(view->TextLength() - 1) == '\n'; + begin = view->TextLength() == 0 + || view->ByteAt(view->TextLength() - 1) == '\n'; - // the following line works only reliable when text wrapping is set to off; - // so the complicated version actually used here is necessary: + // the following line works only reliable when text wrapping is set to + // off; so the complicated version actually used here is necessary: // start = view->OffsetAt(view->CurrentLine()); const char *text = view->Text(); @@ -472,19 +503,28 @@ FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont // get number of nested qoutes for current line if (!begin && start < end) { - begin = true; // if there was no text in this line, there may come more nested quotes + begin = true; + // if there was no text in this line, there may come + // more nested quotes - for (int32 i = start; i < end; i++) { - if (is_quote_char(text[i])) - level++; - else if (text[i] != ' ' && text[i] != '\t') { - begin = false; - break; + diffMode = diff_mode(text[start]); + if (diffMode == 0) { + for (int32 i = start; i < end; i++) { + if (is_quote_char(text[i])) + level++; + else if (text[i] != ' ' && text[i] != '\t') { + begin = false; + break; + } } - } - if (begin) // skip leading spaces (tabs & newlines aren't allowed here) + } else + inDiff = true; + + if (begin) { + // skip leading spaces (tabs & newlines aren't allowed here) while (line[pos] == ' ') pos++; + } } } else begin = true; @@ -494,7 +534,10 @@ FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont for (int32 pos = 0; pos < length;) { int32 next; if (begin && is_quote_char(line[pos])) { + begin = false; + while (pos < length && line[pos] != '\n') { + // insert style for each quote level level++; bool search = true; @@ -502,46 +545,84 @@ FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont if (search && is_quote_char(line[next]) || line[next] == '\n') break; - else if (line[next] != ' ' && line[next] != '\t') + else if (search && line[next] != ' ' && line[next] != '\t') search = false; } runs[index].offset = pos; runs[index].font = font; - runs[index].color = level > 0 ? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor; + runs[index].color = level > 0 + ? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor; pos = next; if (++index >= maxStyles) break; } } else { + if (begin) { + if (!inDiff) { + inDiff = !strncmp(&line[pos], "--- ", 4); + wasDiff = false; + } + if (inDiff) { + diffMode = diff_mode(line[pos]); + if (diffMode < 0) { + inDiff = false; + wasDiff = true; + } + } + } + runs[index].offset = pos; runs[index].font = font; - runs[index].color = level > 0 ? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor; - index++; + if (wasDiff) + runs[index].color = kDiffColors[diff_mode('@') - 1]; + else if (diffMode <= 0) { + runs[index].color = level > 0 + ? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor; + } else + runs[index].color = kDiffColors[diffMode - 1]; + + begin = false; for (next = pos; next < length; next++) { - if (line[next] == '\n') + if (line[next] == '\n') { + begin = true; + wasDiff = false; break; + } } + pos = next; + index++; + } + + if (pos < length) + begin = line[pos] == '\n'; + + if (begin) { + pos++; + level = 0; + wasDiff = false; + + // skip one leading space (tabs & newlines aren't allowed here) + if (!inDiff && pos < length && line[pos] == ' ') + pos++; } if (index >= maxStyles) break; - - level = 0; - - if (pos < length && line[pos] == '\n') { - pos++; - begin = true; - - // skip leading spaces (tabs & newlines aren't allowed here) - while (pos < length && line[pos] == ' ') - pos++; - } } style->count = index; + + if (context) { + // update context for next run + context->level = level; + context->diff_mode = diffMode; + context->begin = begin; + context->in_diff = inDiff; + context->was_diff = wasDiff; + } } @@ -2171,12 +2252,12 @@ TTextView::AddAsContent(BEmailMessage *mail, bool wrap, uint32 charset, mail_enc } -//-------------------------------------------------------------------- // #pragma mark - -TTextView::Reader::Reader(bool header, bool raw, bool quote, bool incoming, bool stripHeader, - bool mime, TTextView *view, BEmailMessage *mail, BList *list, sem_id sem) +TTextView::Reader::Reader(bool header, bool raw, bool quote, bool incoming, + bool stripHeader, bool mime, TTextView *view, BEmailMessage *mail, + BList *list, sem_id sem) : fHeader(header), fRaw(raw), @@ -2193,7 +2274,8 @@ TTextView::Reader::Reader(bool header, bool raw, bool quote, bool incoming, bool bool -TTextView::Reader::ParseMail(BMailContainer *container, BTextMailComponent *ignore) +TTextView::Reader::ParseMail(BMailContainer *container, + BTextMailComponent *ignore) { int32 count = 0; for (int32 i = 0; i < container->CountComponents(); i++) { @@ -2337,7 +2419,8 @@ TTextView::Reader::Process(const char *data, int32 data_len, bool isHeader) bool -TTextView::Reader::Insert(const char *line, int32 count, bool isHyperLink, bool isHeader) +TTextView::Reader::Insert(const char *line, int32 count, bool isHyperLink, + bool isHeader) { if (!count) return true; @@ -2345,17 +2428,20 @@ TTextView::Reader::Insert(const char *line, int32 count, bool isHyperLink, bool BFont font(fView->Font()); TextRunArray style(count / 8 + 8); - if (fView->fColoredQuotes && !isHeader && !isHyperLink) - FillInQuoteTextRuns(fView, line, count, font, &style.Array(), style.MaxEntries()); - else { + if (fView->fColoredQuotes && !isHeader && !isHyperLink) { + FillInQuoteTextRuns(fView, &fQuoteContext, line, count, font, + &style.Array(), style.MaxEntries()); + } else { text_run_array &array = style.Array(); array.count = 1; array.runs[0].offset = 0; if (isHeader) { array.runs[0].color = isHyperLink ? kHyperLinkColor : kHeaderColor; font.SetSize(font.Size() * 0.9); - } else - array.runs[0].color = isHyperLink ? kHyperLinkColor : kNormalTextColor; + } else { + array.runs[0].color = isHyperLink + ? kHyperLinkColor : kNormalTextColor; + } array.runs[0].font = font; } @@ -3061,7 +3147,8 @@ TTextView::AddQuote(int32 start, int32 finish) const BFont *font = Font(); TextRunArray style(targetLength / 8 + 8); - FillInQuoteTextRuns(NULL, target, targetLength, font, &style.Array(), style.MaxEntries()); + FillInQuoteTextRuns(NULL, NULL, target, targetLength, font, + &style.Array(), style.MaxEntries()); Insert(target, targetLength, &style.Array()); } else Insert(target, targetLength); @@ -3138,7 +3225,8 @@ TTextView::RemoveQuote(int32 start, int32 finish) const BFont *font = Font(); TextRunArray style(length / 8 + 8); - FillInQuoteTextRuns(NULL, target, length, font, &style.Array(), style.MaxEntries()); + FillInQuoteTextRuns(NULL, NULL, target, length, font, + &style.Array(), style.MaxEntries()); Insert(target, length, &style.Array()); } else Insert(target, length); diff --git a/src/apps/mail/Content.h b/src/apps/mail/Content.h index bcd5d51c1b..ab7af445e7 100644 --- a/src/apps/mail/Content.h +++ b/src/apps/mail/Content.h @@ -31,13 +31,6 @@ of Be Incorporated in the United States and other countries. Other brand product names are registered trademarks or trademarks of their respective holders. All rights reserved. */ - -//-------------------------------------------------------------------- -// -// Content.h -// -//-------------------------------------------------------------------- - #ifndef _CONTENT_H #define _CONTENT_H @@ -72,8 +65,7 @@ class BPopupMenu; struct text_run_array; -typedef struct -{ +typedef struct { bool header; bool raw; bool quote; @@ -86,8 +78,7 @@ typedef struct sem_id *stop_sem; } reader_info; -enum ENCLOSURE_TYPE -{ +enum ENCLOSURE_TYPE { TYPE_ENCLOSURE = 100, TYPE_BE_ENCLOSURE, TYPE_URL, @@ -113,8 +104,7 @@ class TSavePanel; //==================================================================== -class TContentView : public BView -{ +class TContentView : public BView { public: TContentView(BRect, bool incoming, BEmailMessage *mail, BFont*, bool showHeader, bool coloredQuotes); @@ -138,8 +128,25 @@ enum { S_SHOW_ERRORS = 2 }; -class TTextView : public BTextView -{ +struct quote_context { + quote_context() + : + level(0), + diff_mode(0), + begin(true), + in_diff(false), + was_diff(false) + { + } + + int32 level; + int32 diff_mode; + bool begin; + bool in_diff; + bool was_diff; +}; + +class TTextView : public BTextView { public: TTextView(BRect, BRect, bool incoming, BEmailMessage *mail, TContentView *, BFont *, bool showHeader, bool coloredQuotes); @@ -220,8 +227,7 @@ class TTextView : public BTextView bool fRaw; bool fCursor; - struct spell_mark - { + struct spell_mark { spell_mark *next; int32 start; int32 end; @@ -230,18 +236,21 @@ class TTextView : public BTextView spell_mark *fFirstSpellMark; - class Reader - { + class Reader { public: - Reader(bool header,bool raw,bool quote,bool incoming,bool stripHeaders,bool mime, - TTextView *view,BEmailMessage *mail,BList *list,sem_id sem); + Reader(bool header, bool raw, bool quote, bool incoming, + bool stripHeaders, bool mime, TTextView* view, + BEmailMessage* mail, BList* list, sem_id sem); - static status_t Run(void *); + static status_t Run(void* _dummy); private: - bool ParseMail(BMailContainer *container,BTextMailComponent *ignore); - bool Process(const char *data, int32 len, bool isHeader = false); - bool Insert(const char *line, int32 count, bool isHyperLink, bool isHeader = false); + bool ParseMail(BMailContainer* container, + BTextMailComponent* ignore); + bool Process(const char* data, int32 length, + bool isHeader = false); + bool Insert(const char* line, int32 count, bool isHyperLink, + bool isHeader = false); bool Lock(); status_t Unlock(); @@ -249,11 +258,12 @@ class TTextView : public BTextView bool fHeader; bool fRaw; bool fQuote; + quote_context fQuoteContext; bool fIncoming; bool fStripHeader; bool fMime; - TTextView *fView; - BEmailMessage *fMail; + TTextView* fView; + BEmailMessage* fMail; BList *fEnclosures; sem_id fStopSem; }; @@ -288,7 +298,8 @@ class TextRunArray { size_t fNumEntries; }; -extern void FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, - const BFont &font, text_run_array *style, int32 maxStyles = 5); +extern void FillInQuoteTextRuns(BTextView* view, quote_context* context, + const char* line, int32 length, const BFont& font, text_run_array* style, + int32 maxStyles = 5); #endif /* #ifndef _CONTENT_H */ diff --git a/src/apps/mail/MailWindow.cpp b/src/apps/mail/MailWindow.cpp index 34104429b9..1656b641b6 100644 --- a/src/apps/mail/MailWindow.cpp +++ b/src/apps/mail/MailWindow.cpp @@ -2169,8 +2169,9 @@ TMailWindow::Reply(entry_ref *ref, TMailWindow *window, uint32 type) TextRunArray style(length / 8 + 8); - FillInQuoteTextRuns(fContentView->fTextView, fContentView->fTextView->Text(), - length, font, &style.Array(), style.MaxEntries()); + FillInQuoteTextRuns(fContentView->fTextView, NULL, + fContentView->fTextView->Text(), length, font, &style.Array(), + style.MaxEntries()); fContentView->fTextView->SetRunArray(0, length, &style.Array()); }