* 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
This commit is contained in:
Axel Dörfler
2008-05-08 10:27:55 +00:00
parent 5a5ff85178
commit e06c7fe5d8
3 changed files with 187 additions and 87 deletions
+144 -56
View File
@@ -89,14 +89,19 @@ const rgb_color kSpellTextColor = {255, 0, 0, 255};
const rgb_color kHyperLinkColor = {0, 0, 255, 255}; const rgb_color kHyperLinkColor = {0, 0, 255, 255};
const rgb_color kHeaderColor = {72, 72, 72, 255}; const rgb_color kHeaderColor = {72, 72, 72, 255};
const rgb_color kQuoteColors[] = const rgb_color kQuoteColors[] = {
{ {0, 0, 0x80, 0}, // 3rd, 6th, ... quote level color (blue)
{0, 0, 0x80, 0}, // 3rd, 6th, ... quote level color {0, 0x80, 0, 0}, // 1st, 4th, ... quote level color (green)
{0, 0x80, 0, 0}, // 1st, 4th, ... quote level color {0x80, 0, 0, 0} // 2nd, ... (red)
{0x80, 0, 0, 0} // 2nd, ...
}; };
const int32 kNumQuoteColors = 3; 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); 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 bool
is_quote_char(char c) 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 /*! Fills the specified text_run_array with the correct values for the
* specified text. specified text.
* If "view" is NULL, it will assume that "line" lies on a line break, 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 if not, it will correctly retrieve the number of quotes the current
* line already has. line already has.
*/ */
void void
FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont &font, FillInQuoteTextRuns(BTextView* view, quote_context* context, const char* line,
text_run_array *style, int32 maxStyles) 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; int32 index = style->count;
bool begin; bool begin;
int32 pos = 0; int32 pos = 0;
int32 diffMode = 0;
bool inDiff = false;
bool wasDiff = false;
int32 level = 0; int32 level = 0;
// get index to the beginning of the current line // 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; int32 start, end;
view->GetSelection(&end, &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; // the following line works only reliable when text wrapping is set to
// so the complicated version actually used here is necessary: // off; so the complicated version actually used here is necessary:
// start = view->OffsetAt(view->CurrentLine()); // start = view->OffsetAt(view->CurrentLine());
const char *text = view->Text(); 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 // get number of nested qoutes for current line
if (!begin && start < end) { 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++) { diffMode = diff_mode(text[start]);
if (is_quote_char(text[i])) if (diffMode == 0) {
level++; for (int32 i = start; i < end; i++) {
else if (text[i] != ' ' && text[i] != '\t') { if (is_quote_char(text[i]))
begin = false; level++;
break; else if (text[i] != ' ' && text[i] != '\t') {
begin = false;
break;
}
} }
} } else
if (begin) // skip leading spaces (tabs & newlines aren't allowed here) inDiff = true;
if (begin) {
// skip leading spaces (tabs & newlines aren't allowed here)
while (line[pos] == ' ') while (line[pos] == ' ')
pos++; pos++;
}
} }
} else } else
begin = true; begin = true;
@@ -494,7 +534,10 @@ FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont
for (int32 pos = 0; pos < length;) { for (int32 pos = 0; pos < length;) {
int32 next; int32 next;
if (begin && is_quote_char(line[pos])) { if (begin && is_quote_char(line[pos])) {
begin = false;
while (pos < length && line[pos] != '\n') { while (pos < length && line[pos] != '\n') {
// insert style for each quote level
level++; level++;
bool search = true; bool search = true;
@@ -502,46 +545,84 @@ FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, const BFont
if (search && is_quote_char(line[next]) if (search && is_quote_char(line[next])
|| line[next] == '\n') || line[next] == '\n')
break; break;
else if (line[next] != ' ' && line[next] != '\t') else if (search && line[next] != ' ' && line[next] != '\t')
search = false; search = false;
} }
runs[index].offset = pos; runs[index].offset = pos;
runs[index].font = font; runs[index].font = font;
runs[index].color = level > 0 ? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor; runs[index].color = level > 0
? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor;
pos = next; pos = next;
if (++index >= maxStyles) if (++index >= maxStyles)
break; break;
} }
} else { } 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].offset = pos;
runs[index].font = font; runs[index].font = font;
runs[index].color = level > 0 ? kQuoteColors[level % kNumQuoteColors] : kNormalTextColor; if (wasDiff)
index++; 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++) { for (next = pos; next < length; next++) {
if (line[next] == '\n') if (line[next] == '\n') {
begin = true;
wasDiff = false;
break; break;
}
} }
pos = next; 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) if (index >= maxStyles)
break; 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; 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 - // #pragma mark -
TTextView::Reader::Reader(bool header, bool raw, bool quote, bool incoming, bool stripHeader, TTextView::Reader::Reader(bool header, bool raw, bool quote, bool incoming,
bool mime, TTextView *view, BEmailMessage *mail, BList *list, sem_id sem) bool stripHeader, bool mime, TTextView *view, BEmailMessage *mail,
BList *list, sem_id sem)
: :
fHeader(header), fHeader(header),
fRaw(raw), fRaw(raw),
@@ -2193,7 +2274,8 @@ TTextView::Reader::Reader(bool header, bool raw, bool quote, bool incoming, bool
bool bool
TTextView::Reader::ParseMail(BMailContainer *container, BTextMailComponent *ignore) TTextView::Reader::ParseMail(BMailContainer *container,
BTextMailComponent *ignore)
{ {
int32 count = 0; int32 count = 0;
for (int32 i = 0; i < container->CountComponents(); i++) { for (int32 i = 0; i < container->CountComponents(); i++) {
@@ -2337,7 +2419,8 @@ TTextView::Reader::Process(const char *data, int32 data_len, bool isHeader)
bool 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) if (!count)
return true; return true;
@@ -2345,17 +2428,20 @@ TTextView::Reader::Insert(const char *line, int32 count, bool isHyperLink, bool
BFont font(fView->Font()); BFont font(fView->Font());
TextRunArray style(count / 8 + 8); TextRunArray style(count / 8 + 8);
if (fView->fColoredQuotes && !isHeader && !isHyperLink) if (fView->fColoredQuotes && !isHeader && !isHyperLink) {
FillInQuoteTextRuns(fView, line, count, font, &style.Array(), style.MaxEntries()); FillInQuoteTextRuns(fView, &fQuoteContext, line, count, font,
else { &style.Array(), style.MaxEntries());
} else {
text_run_array &array = style.Array(); text_run_array &array = style.Array();
array.count = 1; array.count = 1;
array.runs[0].offset = 0; array.runs[0].offset = 0;
if (isHeader) { if (isHeader) {
array.runs[0].color = isHyperLink ? kHyperLinkColor : kHeaderColor; array.runs[0].color = isHyperLink ? kHyperLinkColor : kHeaderColor;
font.SetSize(font.Size() * 0.9); font.SetSize(font.Size() * 0.9);
} else } else {
array.runs[0].color = isHyperLink ? kHyperLinkColor : kNormalTextColor; array.runs[0].color = isHyperLink
? kHyperLinkColor : kNormalTextColor;
}
array.runs[0].font = font; array.runs[0].font = font;
} }
@@ -3061,7 +3147,8 @@ TTextView::AddQuote(int32 start, int32 finish)
const BFont *font = Font(); const BFont *font = Font();
TextRunArray style(targetLength / 8 + 8); 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()); Insert(target, targetLength, &style.Array());
} else } else
Insert(target, targetLength); Insert(target, targetLength);
@@ -3138,7 +3225,8 @@ TTextView::RemoveQuote(int32 start, int32 finish)
const BFont *font = Font(); const BFont *font = Font();
TextRunArray style(length / 8 + 8); 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()); Insert(target, length, &style.Array());
} else } else
Insert(target, length); Insert(target, length);
+40 -29
View File
@@ -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. names are registered trademarks or trademarks of their respective holders.
All rights reserved. All rights reserved.
*/ */
//--------------------------------------------------------------------
//
// Content.h
//
//--------------------------------------------------------------------
#ifndef _CONTENT_H #ifndef _CONTENT_H
#define _CONTENT_H #define _CONTENT_H
@@ -72,8 +65,7 @@ class BPopupMenu;
struct text_run_array; struct text_run_array;
typedef struct typedef struct {
{
bool header; bool header;
bool raw; bool raw;
bool quote; bool quote;
@@ -86,8 +78,7 @@ typedef struct
sem_id *stop_sem; sem_id *stop_sem;
} reader_info; } reader_info;
enum ENCLOSURE_TYPE enum ENCLOSURE_TYPE {
{
TYPE_ENCLOSURE = 100, TYPE_ENCLOSURE = 100,
TYPE_BE_ENCLOSURE, TYPE_BE_ENCLOSURE,
TYPE_URL, TYPE_URL,
@@ -113,8 +104,7 @@ class TSavePanel;
//==================================================================== //====================================================================
class TContentView : public BView class TContentView : public BView {
{
public: public:
TContentView(BRect, bool incoming, BEmailMessage *mail, BFont*, TContentView(BRect, bool incoming, BEmailMessage *mail, BFont*,
bool showHeader, bool coloredQuotes); bool showHeader, bool coloredQuotes);
@@ -138,8 +128,25 @@ enum {
S_SHOW_ERRORS = 2 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: public:
TTextView(BRect, BRect, bool incoming, BEmailMessage *mail, TTextView(BRect, BRect, bool incoming, BEmailMessage *mail,
TContentView *, BFont *, bool showHeader, bool coloredQuotes); TContentView *, BFont *, bool showHeader, bool coloredQuotes);
@@ -220,8 +227,7 @@ class TTextView : public BTextView
bool fRaw; bool fRaw;
bool fCursor; bool fCursor;
struct spell_mark struct spell_mark {
{
spell_mark *next; spell_mark *next;
int32 start; int32 start;
int32 end; int32 end;
@@ -230,18 +236,21 @@ class TTextView : public BTextView
spell_mark *fFirstSpellMark; spell_mark *fFirstSpellMark;
class Reader class Reader {
{
public: public:
Reader(bool header,bool raw,bool quote,bool incoming,bool stripHeaders,bool mime, Reader(bool header, bool raw, bool quote, bool incoming,
TTextView *view,BEmailMessage *mail,BList *list,sem_id sem); 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: private:
bool ParseMail(BMailContainer *container,BTextMailComponent *ignore); bool ParseMail(BMailContainer* container,
bool Process(const char *data, int32 len, bool isHeader = false); BTextMailComponent* ignore);
bool Insert(const char *line, int32 count, bool isHyperLink, bool isHeader = false); bool Process(const char* data, int32 length,
bool isHeader = false);
bool Insert(const char* line, int32 count, bool isHyperLink,
bool isHeader = false);
bool Lock(); bool Lock();
status_t Unlock(); status_t Unlock();
@@ -249,11 +258,12 @@ class TTextView : public BTextView
bool fHeader; bool fHeader;
bool fRaw; bool fRaw;
bool fQuote; bool fQuote;
quote_context fQuoteContext;
bool fIncoming; bool fIncoming;
bool fStripHeader; bool fStripHeader;
bool fMime; bool fMime;
TTextView *fView; TTextView* fView;
BEmailMessage *fMail; BEmailMessage* fMail;
BList *fEnclosures; BList *fEnclosures;
sem_id fStopSem; sem_id fStopSem;
}; };
@@ -288,7 +298,8 @@ class TextRunArray {
size_t fNumEntries; size_t fNumEntries;
}; };
extern void FillInQuoteTextRuns(BTextView *view, const char *line, int32 length, extern void FillInQuoteTextRuns(BTextView* view, quote_context* context,
const BFont &font, text_run_array *style, int32 maxStyles = 5); const char* line, int32 length, const BFont& font, text_run_array* style,
int32 maxStyles = 5);
#endif /* #ifndef _CONTENT_H */ #endif /* #ifndef _CONTENT_H */
+3 -2
View File
@@ -2169,8 +2169,9 @@ TMailWindow::Reply(entry_ref *ref, TMailWindow *window, uint32 type)
TextRunArray style(length / 8 + 8); TextRunArray style(length / 8 + 8);
FillInQuoteTextRuns(fContentView->fTextView, fContentView->fTextView->Text(), FillInQuoteTextRuns(fContentView->fTextView, NULL,
length, font, &style.Array(), style.MaxEntries()); fContentView->fTextView->Text(), length, font, &style.Array(),
style.MaxEntries());
fContentView->fTextView->SetRunArray(0, length, &style.Array()); fContentView->fTextView->SetRunArray(0, length, &style.Array());
} }