2 * Copyright 2001-2006 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Marc Flerackers, mflerackers@androme.be
7 * Stefano Ceccherini, burton666@libero.it
10 /** Style storage used by BTextView */
13 #include "InlineInput.h"
14 #include "StyleBuffer.h"
21 // #pragma mark - _BStyleRunDescBuffer_
24 _BStyleRunDescBuffer_::_BStyleRunDescBuffer_()
26 _BTextViewSupportBuffer_
<STEStyleRunDesc
>(20)
32 _BStyleRunDescBuffer_::InsertDesc(STEStyleRunDesc
* inDesc
, int32 index
)
34 InsertItemsAt(1, index
, inDesc
);
39 _BStyleRunDescBuffer_::RemoveDescs(int32 index
, int32 count
)
41 RemoveItemsAt(count
, index
);
46 _BStyleRunDescBuffer_::OffsetToRun(int32 offset
) const
52 int32 maxIndex
= fItemCount
;
55 while (minIndex
< maxIndex
) {
56 index
= (minIndex
+ maxIndex
) >> 1;
57 if (offset
>= fBuffer
[index
].offset
) {
58 if (index
>= fItemCount
- 1
59 || offset
< fBuffer
[index
+ 1].offset
) {
72 _BStyleRunDescBuffer_::BumpOffset(int32 delta
, int32 index
)
74 for (int32 i
= index
; i
< fItemCount
; i
++)
75 fBuffer
[i
].offset
+= delta
;
79 // #pragma mark - _BStyleRecordBuffer_
82 _BStyleRecordBuffer_::_BStyleRecordBuffer_()
84 _BTextViewSupportBuffer_
<STEStyleRecord
>()
90 _BStyleRecordBuffer_::InsertRecord(const BFont
* inFont
,
91 const rgb_color
* inColor
)
95 // look for style in buffer
96 if (MatchRecord(inFont
, inColor
, &index
))
99 // style not found, add it
101 inFont
->GetHeight(&fh
);
103 // check if there's any unused space
104 for (index
= 0; index
< fItemCount
; index
++) {
105 if (fBuffer
[index
].refs
< 1) {
106 fBuffer
[index
].refs
= 0;
107 fBuffer
[index
].ascent
= fh
.ascent
;
108 fBuffer
[index
].descent
= fh
.descent
+ fh
.leading
;
109 fBuffer
[index
].style
.font
= *inFont
;
110 fBuffer
[index
].style
.color
= *inColor
;
115 // no unused space, expand the buffer
116 const STEStyle style
= { *inFont
, *inColor
};
117 const STEStyleRecord newRecord
= {
120 fh
.descent
+ fh
.leading
,
123 InsertItemsAt(1, fItemCount
, &newRecord
);
130 _BStyleRecordBuffer_::CommitRecord(int32 index
)
132 fBuffer
[index
].refs
++;
137 _BStyleRecordBuffer_::RemoveRecord(int32 index
)
139 fBuffer
[index
].refs
--;
144 _BStyleRecordBuffer_::MatchRecord(const BFont
* inFont
, const rgb_color
* inColor
,
147 for (int32 i
= 0; i
< fItemCount
; i
++) {
148 if (*inFont
== fBuffer
[i
].style
.font
149 && *inColor
== fBuffer
[i
].style
.color
) {
159 // #pragma mark - SetStyleFromMode
163 SetStyleFromMode(uint32 mode
, const BFont
* fromFont
, BFont
* toFont
,
164 const rgb_color
* fromColor
, rgb_color
* toColor
)
166 if (fromFont
!= NULL
&& toFont
!= NULL
) {
167 if ((mode
& B_FONT_FAMILY_AND_STYLE
) != 0)
168 toFont
->SetFamilyAndStyle(fromFont
->FamilyAndStyle());
170 if ((mode
& B_FONT_FACE
) != 0)
171 toFont
->SetFace(fromFont
->Face());
173 if ((mode
& B_FONT_SIZE
) != 0)
174 toFont
->SetSize(fromFont
->Size());
176 if ((mode
& B_FONT_SHEAR
) != 0)
177 toFont
->SetShear(fromFont
->Shear());
179 if ((mode
& B_FONT_FALSE_BOLD_WIDTH
) != 0)
180 toFont
->SetFalseBoldWidth(fromFont
->FalseBoldWidth());
183 if (fromColor
!= NULL
&& toColor
!= NULL
184 && (mode
== 0 || mode
== B_FONT_ALL
)) {
185 *toColor
= *fromColor
;
190 // #pragma mark - BTextView::StyleBuffer
193 BTextView::StyleBuffer::StyleBuffer(const BFont
* inFont
,
194 const rgb_color
* inColor
)
196 fValidNullStyle(true)
198 fNullStyle
.font
= *inFont
;
199 fNullStyle
.color
= *inColor
;
204 BTextView::StyleBuffer::InvalidateNullStyle()
206 fValidNullStyle
= false;
211 BTextView::StyleBuffer::IsValidNullStyle() const
213 return fValidNullStyle
;
218 BTextView::StyleBuffer::SyncNullStyle(int32 offset
)
220 if (fValidNullStyle
|| fStyleRunDesc
.ItemCount() < 1)
223 int32 index
= OffsetToRun(offset
);
224 fNullStyle
= fStyleRecord
[fStyleRunDesc
[index
]->index
]->style
;
226 fValidNullStyle
= true;
231 BTextView::StyleBuffer::SetNullStyle(uint32 inMode
, const BFont
* inFont
,
232 const rgb_color
* inColor
, int32 offset
)
234 if (fValidNullStyle
|| fStyleRunDesc
.ItemCount() < 1) {
235 SetStyleFromMode(inMode
, inFont
, &fNullStyle
.font
, inColor
,
238 int32 index
= OffsetToRun(offset
- 1);
239 fNullStyle
= fStyleRecord
[fStyleRunDesc
[index
]->index
]->style
;
240 SetStyleFromMode(inMode
, inFont
, &fNullStyle
.font
, inColor
,
244 fValidNullStyle
= true;
249 BTextView::StyleBuffer::GetNullStyle(const BFont
** font
,
250 const rgb_color
** color
) const
253 *font
= &fNullStyle
.font
;
256 *color
= &fNullStyle
.color
;
261 BTextView::StyleBuffer::AllocateStyleRange(const int32 numStyles
) const
263 STEStyleRange
* range
= (STEStyleRange
*)malloc(sizeof(int32
)
264 + sizeof(STEStyleRun
) * numStyles
);
266 range
->count
= numStyles
;
273 BTextView::StyleBuffer::SetStyleRange(int32 fromOffset
, int32 toOffset
,
274 int32 textLen
, uint32 inMode
, const BFont
* inFont
,
275 const rgb_color
* inColor
)
278 inFont
= &fNullStyle
.font
;
281 inColor
= &fNullStyle
.color
;
283 if (fromOffset
== toOffset
) {
284 SetNullStyle(inMode
, inFont
, inColor
, fromOffset
);
288 if (fStyleRunDesc
.ItemCount() < 1) {
289 STEStyleRunDesc newDesc
;
290 newDesc
.offset
= fromOffset
;
291 newDesc
.index
= fStyleRecord
.InsertRecord(inFont
, inColor
);
292 fStyleRunDesc
.InsertDesc(&newDesc
, 0);
293 fStyleRecord
.CommitRecord(newDesc
.index
);
297 int32 offset
= fromOffset
;
298 int32 runIndex
= OffsetToRun(offset
);
299 int32 styleIndex
= 0;
301 const STEStyleRunDesc runDesc
= *fStyleRunDesc
[runIndex
];
302 int32 runEnd
= textLen
;
303 if (runIndex
< fStyleRunDesc
.ItemCount() - 1)
304 runEnd
= fStyleRunDesc
[runIndex
+ 1]->offset
;
306 STEStyle style
= fStyleRecord
[runDesc
.index
]->style
;
307 SetStyleFromMode(inMode
, inFont
, &style
.font
, inColor
, &style
.color
);
309 styleIndex
= fStyleRecord
.InsertRecord(&style
.font
, &style
.color
);
311 if (runDesc
.offset
== offset
&& runIndex
> 0
312 && fStyleRunDesc
[runIndex
- 1]->index
== styleIndex
) {
313 RemoveStyles(runIndex
);
317 if (styleIndex
!= runDesc
.index
) {
318 if (offset
> runDesc
.offset
) {
319 STEStyleRunDesc newDesc
;
320 newDesc
.offset
= offset
;
321 newDesc
.index
= styleIndex
;
322 fStyleRunDesc
.InsertDesc(&newDesc
, runIndex
+ 1);
323 fStyleRecord
.CommitRecord(newDesc
.index
);
326 fStyleRunDesc
[runIndex
]->index
= styleIndex
;
327 fStyleRecord
.CommitRecord(styleIndex
);
330 if (toOffset
< runEnd
) {
331 STEStyleRunDesc newDesc
;
332 newDesc
.offset
= toOffset
;
333 newDesc
.index
= runDesc
.index
;
334 fStyleRunDesc
.InsertDesc(&newDesc
, runIndex
+ 1);
335 fStyleRecord
.CommitRecord(newDesc
.index
);
341 } while (offset
< toOffset
);
343 if (offset
== toOffset
&& runIndex
< fStyleRunDesc
.ItemCount()
344 && fStyleRunDesc
[runIndex
]->index
== styleIndex
) {
345 RemoveStyles(runIndex
);
351 BTextView::StyleBuffer::GetStyle(int32 inOffset
, BFont
* outFont
,
352 rgb_color
* outColor
) const
354 if (fStyleRunDesc
.ItemCount() < 1) {
356 *outFont
= fNullStyle
.font
;
358 if (outColor
!= NULL
)
359 *outColor
= fNullStyle
.color
;
364 int32 runIndex
= OffsetToRun(inOffset
);
365 int32 styleIndex
= fStyleRunDesc
[runIndex
]->index
;
368 *outFont
= fStyleRecord
[styleIndex
]->style
.font
;
370 if (outColor
!= NULL
)
371 *outColor
= fStyleRecord
[styleIndex
]->style
.color
;
376 BTextView::StyleBuffer::GetStyleRange(int32 startOffset
, int32 endOffset
) const
378 int32 startIndex
= OffsetToRun(startOffset
);
379 int32 endIndex
= OffsetToRun(endOffset
);
381 int32 numStyles
= endIndex
- startIndex
+ 1;
385 STEStyleRange
* result
= AllocateStyleRange(numStyles
);
389 STEStyleRun
* run
= &result
->runs
[0];
390 for (int32 index
= 0; index
< numStyles
; index
++) {
391 *run
= (*this)[startIndex
+ index
];
392 run
->offset
-= startOffset
;
404 BTextView::StyleBuffer::RemoveStyleRange(int32 fromOffset
, int32 toOffset
)
406 int32 fromIndex
= fStyleRunDesc
.OffsetToRun(fromOffset
);
407 int32 toIndex
= fStyleRunDesc
.OffsetToRun(toOffset
) - 1;
409 int32 count
= toIndex
- fromIndex
;
411 RemoveStyles(fromIndex
+ 1, count
);
415 fStyleRunDesc
.BumpOffset(fromOffset
- toOffset
, fromIndex
+ 1);
417 if (toIndex
== fromIndex
&& toIndex
< fStyleRunDesc
.ItemCount() - 1) {
418 STEStyleRunDesc
* runDesc
= fStyleRunDesc
[toIndex
+ 1];
419 runDesc
->offset
= fromOffset
;
422 if (fromIndex
< fStyleRunDesc
.ItemCount() - 1) {
423 STEStyleRunDesc
* runDesc
= fStyleRunDesc
[fromIndex
];
424 if (runDesc
->offset
== (runDesc
+ 1)->offset
) {
425 RemoveStyles(fromIndex
);
430 if (fromIndex
>= 0 && fromIndex
< fStyleRunDesc
.ItemCount() - 1) {
431 STEStyleRunDesc
* runDesc
= fStyleRunDesc
[fromIndex
];
432 if (runDesc
->index
== (runDesc
+ 1)->index
)
433 RemoveStyles(fromIndex
+ 1);
439 BTextView::StyleBuffer::RemoveStyles(int32 index
, int32 count
)
441 for (int32 i
= index
; i
< index
+ count
; i
++)
442 fStyleRecord
.RemoveRecord(fStyleRunDesc
[i
]->index
);
444 fStyleRunDesc
.RemoveDescs(index
, count
);
449 BTextView::StyleBuffer::Iterate(int32 fromOffset
, int32 length
,
451 const BFont
** outFont
, const rgb_color
** outColor
,
452 float* outAscent
, float* outDescent
, uint32
*) const
454 // TODO: Handle the InlineInput style here in some way
455 int32 numRuns
= fStyleRunDesc
.ItemCount();
456 if (length
< 1 || numRuns
< 1)
459 int32 result
= length
;
460 int32 runIndex
= fStyleRunDesc
.OffsetToRun(fromOffset
);
461 STEStyleRunDesc
* run
= fStyleRunDesc
[runIndex
];
464 *outFont
= &fStyleRecord
[run
->index
]->style
.font
;
466 if (outColor
!= NULL
)
467 *outColor
= &fStyleRecord
[run
->index
]->style
.color
;
469 if (outAscent
!= NULL
)
470 *outAscent
= fStyleRecord
[run
->index
]->ascent
;
472 if (outDescent
!= NULL
)
473 *outDescent
= fStyleRecord
[run
->index
]->descent
;
475 if (runIndex
< numRuns
- 1) {
476 int32 nextOffset
= (run
+ 1)->offset
- fromOffset
;
477 result
= min_c(result
, nextOffset
);
485 BTextView::StyleBuffer::OffsetToRun(int32 offset
) const
487 return fStyleRunDesc
.OffsetToRun(offset
);
492 BTextView::StyleBuffer::BumpOffset(int32 delta
, int32 index
)
494 fStyleRunDesc
.BumpOffset(delta
, index
);
499 BTextView::StyleBuffer::operator[](int32 index
) const
503 if (fStyleRunDesc
.ItemCount() < 1) {
505 run
.style
= fNullStyle
;
507 STEStyleRunDesc
* runDesc
= fStyleRunDesc
[index
];
508 run
.offset
= runDesc
->offset
;
509 run
.style
= fStyleRecord
[runDesc
->index
]->style
;
516 // TODO: Horrible name, but can't think of a better one
520 FixupMode(const STEStyle
&firstStyle
, const STEStyle
&otherStyle
, uint32
&mode
,
523 if ((mode
& B_FONT_FAMILY_AND_STYLE
) != 0) {
524 if (firstStyle
.font
!= otherStyle
.font
)
525 mode
&= ~B_FONT_FAMILY_AND_STYLE
;
527 if ((mode
& B_FONT_SIZE
) != 0) {
528 if (firstStyle
.font
.Size() != otherStyle
.font
.Size())
529 mode
&= ~B_FONT_SIZE
;
531 if ((mode
& B_FONT_SHEAR
) != 0) {
532 if (firstStyle
.font
.Shear() != otherStyle
.font
.Shear())
533 mode
&= ~B_FONT_SHEAR
;
535 if ((mode
& B_FONT_FALSE_BOLD_WIDTH
) != 0) {
536 if (firstStyle
.font
.FalseBoldWidth()
537 != otherStyle
.font
.FalseBoldWidth()) {
538 mode
&= ~B_FONT_FALSE_BOLD_WIDTH
;
541 if (firstStyle
.color
!= otherStyle
.color
)
544 // TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc.
550 BTextView::StyleBuffer::ContinuousGetStyle(BFont
*outFont
, uint32
* ioMode
,
551 rgb_color
* outColor
, bool* sameColor
, int32 fromOffset
,
552 int32 toOffset
) const
554 uint32 mode
= B_FONT_ALL
;
556 if (fStyleRunDesc
.ItemCount() < 1) {
561 *outFont
= fNullStyle
.font
;
563 if (outColor
!= NULL
)
564 *outColor
= fNullStyle
.color
;
566 if (sameColor
!= NULL
)
572 int32 fromIndex
= OffsetToRun(fromOffset
);
573 int32 toIndex
= OffsetToRun(toOffset
- 1);
575 if (fromIndex
== toIndex
) {
576 int32 styleIndex
= fStyleRunDesc
[fromIndex
]->index
;
577 const STEStyle
* style
= &fStyleRecord
[styleIndex
]->style
;
583 *outFont
= style
->font
;
585 if (outColor
!= NULL
)
586 *outColor
= style
->color
;
588 if (sameColor
!= NULL
)
591 bool oneColor
= true;
592 int32 styleIndex
= fStyleRunDesc
[toIndex
]->index
;
593 STEStyle theStyle
= fStyleRecord
[styleIndex
]->style
;
595 for (int32 i
= fromIndex
; i
< toIndex
; i
++) {
596 styleIndex
= fStyleRunDesc
[i
]->index
;
597 FixupMode(fStyleRecord
[styleIndex
]->style
, theStyle
, mode
,
605 *outFont
= theStyle
.font
;
607 if (outColor
!= NULL
)
608 *outColor
= theStyle
.color
;
610 if (sameColor
!= NULL
)
611 *sameColor
= oneColor
;