diff --git a/src/apps/haiku-depot/Jamfile b/src/apps/haiku-depot/Jamfile index ecd1902bc5..961337d9ac 100644 --- a/src/apps/haiku-depot/Jamfile +++ b/src/apps/haiku-depot/Jamfile @@ -13,6 +13,8 @@ for sourceDir in $(sourceDirs) { } local textDocumentSources = + Bullet.cpp + BulletData.cpp CharacterStyle.cpp CharacterStyleData.cpp Paragraph.cpp diff --git a/src/apps/haiku-depot/textview/Bullet.cpp b/src/apps/haiku-depot/textview/Bullet.cpp new file mode 100644 index 0000000000..ce6596d570 --- /dev/null +++ b/src/apps/haiku-depot/textview/Bullet.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2013, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "Bullet.h" + + +static BulletData sEmptyBullet; + + +Bullet::Bullet() + : + fBulletData(&sEmptyBullet) +{ +} + + +Bullet::Bullet(const BString& string, float spacing) + : + fBulletData(new BulletData(string, spacing), true) +{ +} + + +Bullet::Bullet(const Bullet& other) + : + fBulletData(other.fBulletData) +{ +} + + +Bullet& +Bullet::operator=(const Bullet& other) +{ + if (this == &other) + return *this; + + fBulletData = other.fBulletData; + return *this; +} + + +bool +Bullet::operator==(const Bullet& other) const +{ + if (this == &other) + return true; + + if (fBulletData == other.fBulletData) + return true; + + if (fBulletData.Get() != NULL && other.fBulletData.Get() != NULL) + return *fBulletData.Get() == *other.fBulletData.Get(); + + return false; +} + + +bool +Bullet::operator!=(const Bullet& other) const +{ + return !(*this == other); +} + + +bool +Bullet::SetString(const BString& string) +{ + BulletDataRef data = fBulletData->SetString(string); + if (data == fBulletData) + return data->String() == string; + + fBulletData = data; + return true; +} + + +const BString& +Bullet::String() const +{ + return fBulletData->String(); +} + + +bool +Bullet::SetSpacing(float spacing) +{ + BulletDataRef data = fBulletData->SetSpacing(spacing); + if (data == fBulletData) + return data->Spacing() == spacing; + + fBulletData = data; + return true; +} + + +float +Bullet::Spacing() const +{ + return fBulletData->Spacing(); +} diff --git a/src/apps/haiku-depot/textview/Bullet.h b/src/apps/haiku-depot/textview/Bullet.h new file mode 100644 index 0000000000..2fc8568b91 --- /dev/null +++ b/src/apps/haiku-depot/textview/Bullet.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef BULLET_H +#define BULLET_H + +#include "BulletData.h" + + +class Bullet { +public: + Bullet(); + Bullet(const BString& string, float spacing); + Bullet(const Bullet& other); + + Bullet& operator=(const Bullet& other); + bool operator==(const Bullet& other) const; + bool operator!=(const Bullet& other) const; + + bool SetString(const BString& string); + const BString& String() const; + + bool SetSpacing(float spacing); + float Spacing() const; + +private: + BulletDataRef fBulletData; +}; + + +#endif // BULLET_H diff --git a/src/apps/haiku-depot/textview/BulletData.cpp b/src/apps/haiku-depot/textview/BulletData.cpp new file mode 100644 index 0000000000..92e2f1c5cb --- /dev/null +++ b/src/apps/haiku-depot/textview/BulletData.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2013, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "BulletData.h" + +#include + + +BulletData::BulletData() + : + fString(""), + fSpacing(0.0f) +{ +} + + +BulletData::BulletData(const BString& string, float spacing) + : + fString(string), + fSpacing(spacing) +{ +} + + +BulletData::BulletData(const BulletData& other) + : + fString(other.fString), + fSpacing(other.fSpacing) +{ +} + + +bool +BulletData::operator==(const BulletData& other) const +{ + if (this == &other) + return true; + + return fString == other.fString + && fSpacing == other.fSpacing; +} + + +bool +BulletData::operator!=(const BulletData& other) const +{ + return !(*this == other); +} + + +BulletDataRef +BulletData::SetString(const BString& string) +{ + if (fString == string) + return BulletDataRef(this); + + BulletData* ret = new(std::nothrow) BulletData(*this); + if (ret == NULL) + return BulletDataRef(this); + + ret->fString = string; + return BulletDataRef(ret, true); +} + + +BulletDataRef +BulletData::SetSpacing(float spacing) +{ + if (fSpacing == spacing) + return BulletDataRef(this); + + BulletData* ret = new(std::nothrow) BulletData(*this); + if (ret == NULL) + return BulletDataRef(this); + + ret->fSpacing = spacing; + return BulletDataRef(ret, true); +} + + +// #pragma mark - private + + +BulletData& +BulletData::operator=(const BulletData& other) +{ + return *this; +} diff --git a/src/apps/haiku-depot/textview/BulletData.h b/src/apps/haiku-depot/textview/BulletData.h new file mode 100644 index 0000000000..53f58c888d --- /dev/null +++ b/src/apps/haiku-depot/textview/BulletData.h @@ -0,0 +1,46 @@ +/* + * Copyright 2013, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef BULLET_DATA_H +#define BULLET_DATA_H + +#include +#include + + +class BulletData; +typedef BReference BulletDataRef; + + +// You cannot modify a BulletData object once it has been created. +class BulletData : public BReferenceable { +public: + BulletData(); + BulletData(const BString& string, + float spacing); + BulletData(const BulletData& other); + + bool operator==( + const BulletData& other) const; + bool operator!=( + const BulletData& other) const; + + BulletDataRef SetString(const BString& string); + inline const BString& String() const + { return fString; } + + BulletDataRef SetSpacing(float spacing); + inline float Spacing() const + { return fSpacing; } + +private: + BulletData& operator=(const BulletData& other); + +private: + BString fString; + float fSpacing; +}; + + +#endif // BULLET_DATA_H diff --git a/src/apps/haiku-depot/textview/ParagraphLayout.cpp b/src/apps/haiku-depot/textview/ParagraphLayout.cpp index 187d11c6fc..c56f136cb3 100644 --- a/src/apps/haiku-depot/textview/ParagraphLayout.cpp +++ b/src/apps/haiku-depot/textview/ParagraphLayout.cpp @@ -257,6 +257,17 @@ ParagraphLayout::Draw(BView* view, const BPoint& offset) const LineInfo& line = fLineInfos.ItemAtFast(i); _DrawLine(view, offset, line); } + + const Bullet& bullet = fParagraphStyle.Bullet(); + if (bullet.Spacing() > 0.0f && bullet.String().Length() > 0) { + // Draw bullet at offset + view->SetHighColor(0, 0, 0, 255); + BPoint bulletPos(offset); + bulletPos.x += fParagraphStyle.FirstLineInset() + + fParagraphStyle.LineInset(); + bulletPos.y += fLineInfos.ItemAt(0).maxAscent; + view->DrawString(bullet.String(), bulletPos); + } } @@ -295,7 +306,10 @@ ParagraphLayout::_Layout() { fLineInfos.Clear(); - float x = fParagraphStyle.FirstLineInset(); + const Bullet& bullet = fParagraphStyle.Bullet(); + + float x = fParagraphStyle.LineInset() + fParagraphStyle.FirstLineInset() + + bullet.Spacing(); float y = 0.0f; int lineIndex = 0; int lineStart = 0; @@ -388,7 +402,7 @@ ParagraphLayout::_Layout() _FinalizeLine(lineStart, lineEnd, lineIndex, y, lineHeight); // Start position of the next line - x = fParagraphStyle.LineInset(); + x = fParagraphStyle.LineInset() + bullet.Spacing(); y += lineHeight + fParagraphStyle.LineSpacing(); if (lineBreak) @@ -642,7 +656,7 @@ ParagraphLayout::_FinalizeLine(int lineStart, int lineEnd, int lineIndex, if (addSpan) { const TextSpan& span = fTextSpans.ItemAt(spanIndex); TextSpan subSpan = span.SubSpan(i - spanStart, - (lineEnd - spanStart) - (i - spanStart)); + (lineEnd - spanStart + 1) - (i - spanStart)); line.layoutedSpans.Add(subSpan); _IncludeStyleInLine(line, span.Style()); } diff --git a/src/apps/haiku-depot/textview/ParagraphStyle.cpp b/src/apps/haiku-depot/textview/ParagraphStyle.cpp index 2fd6b3e337..5f4890edda 100644 --- a/src/apps/haiku-depot/textview/ParagraphStyle.cpp +++ b/src/apps/haiku-depot/textview/ParagraphStyle.cpp @@ -187,3 +187,22 @@ ParagraphStyle::SpacingBottom() const } +bool +ParagraphStyle::SetBullet(const ::Bullet& bullet) +{ + ParagraphStyleDataRef data = fStyleData->SetBullet(bullet); + if (data == fStyleData) + return data->Bullet() == bullet; + + fStyleData = data; + return true; +} + + +const ::Bullet& +ParagraphStyle::Bullet() const +{ + return fStyleData->Bullet(); +} + + diff --git a/src/apps/haiku-depot/textview/ParagraphStyle.h b/src/apps/haiku-depot/textview/ParagraphStyle.h index ec525731d5..56456ef469 100644 --- a/src/apps/haiku-depot/textview/ParagraphStyle.h +++ b/src/apps/haiku-depot/textview/ParagraphStyle.h @@ -38,6 +38,8 @@ public: bool SetSpacingBottom(float spacing); float SpacingBottom() const; + bool SetBullet(const ::Bullet& bullet); + const ::Bullet& Bullet() const; private: ParagraphStyleDataRef fStyleData; diff --git a/src/apps/haiku-depot/textview/ParagraphStyleData.cpp b/src/apps/haiku-depot/textview/ParagraphStyleData.cpp index d36a0acb5a..a838de02c9 100644 --- a/src/apps/haiku-depot/textview/ParagraphStyleData.cpp +++ b/src/apps/haiku-depot/textview/ParagraphStyleData.cpp @@ -17,7 +17,9 @@ ParagraphStyleData::ParagraphStyleData() fLineInset(0.0f), fSpacingTop(0.0f), - fSpacingBottom(0.0f) + fSpacingBottom(0.0f), + + fBullet() { } @@ -31,7 +33,9 @@ ParagraphStyleData::ParagraphStyleData(const ParagraphStyleData& other) fLineInset(other.fLineInset), fSpacingTop(other.fSpacingTop), - fSpacingBottom(other.fSpacingBottom) + fSpacingBottom(other.fSpacingBottom), + + fBullet(other.fBullet) { } @@ -47,7 +51,8 @@ ParagraphStyleData::operator==(const ParagraphStyleData& other) const && fFirstLineInset == other.fFirstLineInset && fLineInset == other.fLineInset && fSpacingTop == other.fSpacingTop - && fSpacingBottom == other.fSpacingBottom; + && fSpacingBottom == other.fSpacingBottom + && fBullet == other.fBullet; } @@ -163,6 +168,21 @@ ParagraphStyleData::SetSpacingBottom(float spacing) } +ParagraphStyleDataRef +ParagraphStyleData::SetBullet(const ::Bullet& bullet) +{ + if (fBullet == bullet) + return ParagraphStyleDataRef(this); + + ParagraphStyleData* ret = new(std::nothrow) ParagraphStyleData(*this); + if (ret == NULL) + return ParagraphStyleDataRef(this); + + ret->fBullet = bullet; + return ParagraphStyleDataRef(ret, true); +} + + // #pragma mark - private diff --git a/src/apps/haiku-depot/textview/ParagraphStyleData.h b/src/apps/haiku-depot/textview/ParagraphStyleData.h index cce60a11fd..e3847520ff 100644 --- a/src/apps/haiku-depot/textview/ParagraphStyleData.h +++ b/src/apps/haiku-depot/textview/ParagraphStyleData.h @@ -5,7 +5,7 @@ #ifndef PARAGRAPH_STYLE_DATA_H #define PARAGRAPH_STYLE_DATA_H -#include +#include "Bullet.h" enum Alignment { @@ -60,6 +60,9 @@ public: inline float SpacingBottom() const { return fSpacingBottom; } + ParagraphStyleDataRef SetBullet(const ::Bullet& bullet); + inline const ::Bullet& Bullet() const + { return fBullet; } private: ParagraphStyleData& operator=(const ParagraphStyleData& other); @@ -73,6 +76,8 @@ private: float fSpacingTop; float fSpacingBottom; + + ::Bullet fBullet; }; diff --git a/src/apps/haiku-depot/textview/TextDocumentTest.cpp b/src/apps/haiku-depot/textview/TextDocumentTest.cpp index 41d7658463..433f0fadfb 100644 --- a/src/apps/haiku-depot/textview/TextDocumentTest.cpp +++ b/src/apps/haiku-depot/textview/TextDocumentTest.cpp @@ -53,9 +53,9 @@ TextDocumentTest::ReadyToRun() TextDocumentRef document(new TextDocument(), true); Paragraph paragraph(paragraphStyle); - paragraph.Append(TextSpan("This is a ", regularStyle)); - paragraph.Append(TextSpan("test", bigStyle)); - paragraph.Append(TextSpan(" to see if ", regularStyle)); + paragraph.Append(TextSpan("This is a", regularStyle)); + paragraph.Append(TextSpan(" test ", bigStyle)); + paragraph.Append(TextSpan("to see if ", regularStyle)); paragraph.Append(TextSpan("different", boldStyle)); paragraph.Append(TextSpan(" character styles already work.", regularStyle)); document->Append(paragraph); @@ -70,6 +70,22 @@ TextDocumentTest::ReadyToRun() paragraph.Append(TextSpan(" supported as of now!", regularStyle)); document->Append(paragraph); + // Test a bullet list + paragraphStyle.SetSpacingTop(8.0f); + paragraphStyle.SetAlignment(ALIGN_LEFT); + paragraphStyle.SetJustify(true); + paragraphStyle.SetBullet(Bullet("•", 12.0f)); + paragraphStyle.SetLineInset(10.0f); + + paragraph = Paragraph(paragraphStyle); + paragraph.Append(TextSpan("Even bullet lists are supported.", regularStyle)); + document->Append(paragraph); + + paragraph = Paragraph(paragraphStyle); + paragraph.Append(TextSpan("The wrapping in this bullet item should " + "look visually pleasing. And why should it not?", regularStyle)); + document->Append(paragraph); + documentView->SetTextDocument(document); window->Show();