1/*
2 * Copyright 2001-2006 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Marc Flerackers, mflerackers@androme.be
7 *		Stefano Ceccherini, burton666@libero.it
8 */
9
10/**	Style storage used by BTextView */
11
12
13#include "InlineInput.h"
14#include "StyleBuffer.h"
15
16#include <View.h>
17
18#include <stdio.h>
19
20
21//	#pragma mark - _BStyleRunDescBuffer_
22
23
24_BStyleRunDescBuffer_::_BStyleRunDescBuffer_()
25	:
26	_BTextViewSupportBuffer_<STEStyleRunDesc>(20)
27{
28}
29
30
31void
32_BStyleRunDescBuffer_::InsertDesc(STEStyleRunDesc* inDesc, int32 index)
33{
34	InsertItemsAt(1, index, inDesc);
35}
36
37
38void
39_BStyleRunDescBuffer_::RemoveDescs(int32 index, int32 count)
40{
41	RemoveItemsAt(count, index);
42}
43
44
45int32
46_BStyleRunDescBuffer_::OffsetToRun(int32 offset) const
47{
48	if (fItemCount <= 1)
49		return 0;
50
51	int32 minIndex = 0;
52	int32 maxIndex = fItemCount;
53	int32 index = 0;
54
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) {
60				break;
61			} else
62				minIndex = index + 1;
63		} else
64			maxIndex = index;
65	}
66
67	return index;
68}
69
70
71void
72_BStyleRunDescBuffer_::BumpOffset(int32 delta, int32 index)
73{
74	for (int32 i = index; i < fItemCount; i++)
75		fBuffer[i].offset += delta;
76}
77
78
79//	#pragma mark - _BStyleRecordBuffer_
80
81
82_BStyleRecordBuffer_::_BStyleRecordBuffer_()
83	:
84	_BTextViewSupportBuffer_<STEStyleRecord>()
85{
86}
87
88
89int32
90_BStyleRecordBuffer_::InsertRecord(const BFont* inFont,
91	const rgb_color* inColor)
92{
93	int32 index = 0;
94
95	// look for style in buffer
96	if (MatchRecord(inFont, inColor, &index))
97		return index;
98
99	// style not found, add it
100	font_height fh;
101	inFont->GetHeight(&fh);
102
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;
111			return index;
112		}
113	}
114
115	// no unused space, expand the buffer
116	const STEStyle style = { *inFont, *inColor };
117	const STEStyleRecord newRecord = {
118		0,
119		fh.ascent,
120		fh.descent + fh.leading,
121		style
122	};
123	InsertItemsAt(1, fItemCount, &newRecord);
124
125	return index;
126}
127
128
129void
130_BStyleRecordBuffer_::CommitRecord(int32 index)
131{
132	fBuffer[index].refs++;
133}
134
135
136void
137_BStyleRecordBuffer_::RemoveRecord(int32 index)
138{
139	fBuffer[index].refs--;
140}
141
142
143bool
144_BStyleRecordBuffer_::MatchRecord(const BFont* inFont, const rgb_color* inColor,
145	int32* outIndex)
146{
147	for (int32 i = 0; i < fItemCount; i++) {
148		if (*inFont == fBuffer[i].style.font
149			&& *inColor == fBuffer[i].style.color) {
150			*outIndex = i;
151			return true;
152		}
153	}
154
155	return false;
156}
157
158
159//	#pragma mark - SetStyleFromMode
160
161
162static void
163SetStyleFromMode(uint32 mode, const BFont* fromFont, BFont* toFont,
164	const rgb_color* fromColor, rgb_color* toColor)
165{
166	if (fromFont != NULL && toFont != NULL) {
167		if ((mode & B_FONT_FAMILY_AND_STYLE) != 0)
168			toFont->SetFamilyAndStyle(fromFont->FamilyAndStyle());
169
170		if ((mode & B_FONT_FACE) != 0)
171			toFont->SetFace(fromFont->Face());
172
173		if ((mode & B_FONT_SIZE) != 0)
174			toFont->SetSize(fromFont->Size());
175
176		if ((mode & B_FONT_SHEAR) != 0)
177			toFont->SetShear(fromFont->Shear());
178
179		if ((mode & B_FONT_FALSE_BOLD_WIDTH) != 0)
180			toFont->SetFalseBoldWidth(fromFont->FalseBoldWidth());
181	}
182
183	if (fromColor != NULL && toColor != NULL
184		&& (mode == 0 || mode == B_FONT_ALL)) {
185		*toColor = *fromColor;
186	}
187}
188
189
190//	#pragma mark - BTextView::StyleBuffer
191
192
193BTextView::StyleBuffer::StyleBuffer(const BFont* inFont,
194	const rgb_color* inColor)
195	:
196	fValidNullStyle(true)
197{
198	fNullStyle.font = *inFont;
199	fNullStyle.color = *inColor;
200}
201
202
203void
204BTextView::StyleBuffer::InvalidateNullStyle()
205{
206	fValidNullStyle = false;
207}
208
209
210bool
211BTextView::StyleBuffer::IsValidNullStyle() const
212{
213	return fValidNullStyle;
214}
215
216
217void
218BTextView::StyleBuffer::SyncNullStyle(int32 offset)
219{
220	if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1)
221		return;
222
223	int32 index = OffsetToRun(offset);
224	fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style;
225
226	fValidNullStyle = true;
227}
228
229
230void
231BTextView::StyleBuffer::SetNullStyle(uint32 inMode, const BFont* inFont,
232	const rgb_color* inColor, int32 offset)
233{
234	if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) {
235		SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor,
236			&fNullStyle.color);
237	} else {
238		int32 index = OffsetToRun(offset - 1);
239		fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style;
240		SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor,
241			&fNullStyle.color);
242	}
243
244	fValidNullStyle = true;
245}
246
247
248void
249BTextView::StyleBuffer::GetNullStyle(const BFont** font,
250	const rgb_color** color) const
251{
252	if (font != NULL)
253		*font = &fNullStyle.font;
254
255	if (color != NULL)
256		*color = &fNullStyle.color;
257}
258
259
260STEStyleRange*
261BTextView::StyleBuffer::AllocateStyleRange(const int32 numStyles) const
262{
263	STEStyleRange* range = (STEStyleRange*)malloc(sizeof(int32)
264		+ sizeof(STEStyleRun) * numStyles);
265	if (range != NULL)
266		range->count = numStyles;
267
268	return range;
269}
270
271
272void
273BTextView::StyleBuffer::SetStyleRange(int32 fromOffset, int32 toOffset,
274	int32 textLen, uint32 inMode, const BFont* inFont,
275	const rgb_color* inColor)
276{
277	if (inFont == NULL)
278		inFont = &fNullStyle.font;
279
280	if (inColor == NULL)
281		inColor = &fNullStyle.color;
282
283	if (fromOffset == toOffset) {
284		SetNullStyle(inMode, inFont, inColor, fromOffset);
285		return;
286	}
287
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);
294		return;
295	}
296
297	int32 offset = fromOffset;
298	int32 runIndex = OffsetToRun(offset);
299	int32 styleIndex = 0;
300	do {
301		const STEStyleRunDesc runDesc = *fStyleRunDesc[runIndex];
302		int32 runEnd = textLen;
303		if (runIndex < fStyleRunDesc.ItemCount() - 1)
304			runEnd = fStyleRunDesc[runIndex + 1]->offset;
305
306		STEStyle style = fStyleRecord[runDesc.index]->style;
307		SetStyleFromMode(inMode, inFont, &style.font, inColor, &style.color);
308
309		styleIndex = fStyleRecord.InsertRecord(&style.font, &style.color);
310
311		if (runDesc.offset == offset && runIndex > 0
312			&& fStyleRunDesc[runIndex - 1]->index == styleIndex) {
313			RemoveStyles(runIndex);
314			runIndex--;
315		}
316
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);
324				runIndex++;
325			} else {
326				fStyleRunDesc[runIndex]->index = styleIndex;
327				fStyleRecord.CommitRecord(styleIndex);
328			}
329
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);
336			}
337		}
338
339		runIndex++;
340		offset = runEnd;
341	} while (offset < toOffset);
342
343	if (offset == toOffset && runIndex < fStyleRunDesc.ItemCount()
344		&& fStyleRunDesc[runIndex]->index == styleIndex) {
345		RemoveStyles(runIndex);
346	}
347}
348
349
350void
351BTextView::StyleBuffer::GetStyle(int32 inOffset, BFont* outFont,
352	rgb_color* outColor) const
353{
354	if (fStyleRunDesc.ItemCount() < 1) {
355		if (outFont != NULL)
356			*outFont = fNullStyle.font;
357
358		if (outColor != NULL)
359			*outColor = fNullStyle.color;
360
361		return;
362	}
363
364	int32 runIndex = OffsetToRun(inOffset);
365	int32 styleIndex = fStyleRunDesc[runIndex]->index;
366
367	if (outFont != NULL)
368		*outFont = fStyleRecord[styleIndex]->style.font;
369
370	if (outColor != NULL)
371		*outColor = fStyleRecord[styleIndex]->style.color;
372}
373
374
375STEStyleRange*
376BTextView::StyleBuffer::GetStyleRange(int32 startOffset, int32 endOffset) const
377{
378	int32 startIndex = OffsetToRun(startOffset);
379	int32 endIndex = OffsetToRun(endOffset);
380
381	int32 numStyles = endIndex - startIndex + 1;
382	if (numStyles < 1)
383		numStyles = 1;
384
385	STEStyleRange* result = AllocateStyleRange(numStyles);
386	if (result == NULL)
387		return NULL;
388
389	STEStyleRun* run = &result->runs[0];
390	for (int32 index = 0; index < numStyles; index++) {
391		*run = (*this)[startIndex + index];
392		run->offset -= startOffset;
393		if (run->offset < 0)
394			run->offset = 0;
395
396		run++;
397	}
398
399	return result;
400}
401
402
403void
404BTextView::StyleBuffer::RemoveStyleRange(int32 fromOffset, int32 toOffset)
405{
406	int32 fromIndex = fStyleRunDesc.OffsetToRun(fromOffset);
407	int32 toIndex = fStyleRunDesc.OffsetToRun(toOffset) - 1;
408
409	int32 count = toIndex - fromIndex;
410	if (count > 0) {
411		RemoveStyles(fromIndex + 1, count);
412		toIndex = fromIndex;
413	}
414
415	fStyleRunDesc.BumpOffset(fromOffset - toOffset, fromIndex + 1);
416
417	if (toIndex == fromIndex && toIndex < fStyleRunDesc.ItemCount() - 1) {
418		STEStyleRunDesc* runDesc = fStyleRunDesc[toIndex + 1];
419		runDesc->offset = fromOffset;
420	}
421
422	if (fromIndex < fStyleRunDesc.ItemCount() - 1) {
423		STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex];
424		if (runDesc->offset == (runDesc + 1)->offset) {
425			RemoveStyles(fromIndex);
426			fromIndex--;
427		}
428	}
429
430	if (fromIndex >= 0 && fromIndex < fStyleRunDesc.ItemCount() - 1) {
431		STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex];
432		if (runDesc->index == (runDesc + 1)->index)
433			RemoveStyles(fromIndex + 1);
434	}
435}
436
437
438void
439BTextView::StyleBuffer::RemoveStyles(int32 index, int32 count)
440{
441	for (int32 i = index; i < index + count; i++)
442		fStyleRecord.RemoveRecord(fStyleRunDesc[i]->index);
443
444	fStyleRunDesc.RemoveDescs(index, count);
445}
446
447
448int32
449BTextView::StyleBuffer::Iterate(int32 fromOffset, int32 length,
450	InlineInput* input,
451	const BFont** outFont, const rgb_color** outColor,
452	float* outAscent, float* outDescent, uint32*) const
453{
454	// TODO: Handle the InlineInput style here in some way
455	int32 numRuns = fStyleRunDesc.ItemCount();
456	if (length < 1 || numRuns < 1)
457		return 0;
458
459	int32 result = length;
460	int32 runIndex = fStyleRunDesc.OffsetToRun(fromOffset);
461	STEStyleRunDesc* run = fStyleRunDesc[runIndex];
462
463	if (outFont != NULL)
464		*outFont = &fStyleRecord[run->index]->style.font;
465
466	if (outColor != NULL)
467		*outColor = &fStyleRecord[run->index]->style.color;
468
469	if (outAscent != NULL)
470		*outAscent = fStyleRecord[run->index]->ascent;
471
472	if (outDescent != NULL)
473		*outDescent = fStyleRecord[run->index]->descent;
474
475	if (runIndex < numRuns - 1) {
476		int32 nextOffset = (run + 1)->offset - fromOffset;
477		result = min_c(result, nextOffset);
478	}
479
480	return result;
481}
482
483
484int32
485BTextView::StyleBuffer::OffsetToRun(int32 offset) const
486{
487	return fStyleRunDesc.OffsetToRun(offset);
488}
489
490
491void
492BTextView::StyleBuffer::BumpOffset(int32 delta, int32 index)
493{
494	fStyleRunDesc.BumpOffset(delta, index);
495}
496
497
498STEStyleRun
499BTextView::StyleBuffer::operator[](int32 index) const
500{
501	STEStyleRun run;
502
503	if (fStyleRunDesc.ItemCount() < 1) {
504		run.offset = 0;
505		run.style = fNullStyle;
506	} else {
507		STEStyleRunDesc* runDesc = fStyleRunDesc[index];
508		run.offset = runDesc->offset;
509		run.style = fStyleRecord[runDesc->index]->style;
510	}
511
512	return run;
513}
514
515
516// TODO: Horrible name, but can't think of a better one
517// ? CompareStyles ?
518// ? FilterStyles ?
519static void
520FixupMode(const STEStyle &firstStyle, const STEStyle &otherStyle, uint32 &mode,
521	bool &sameColor)
522{
523	if ((mode & B_FONT_FAMILY_AND_STYLE) != 0) {
524		if (firstStyle.font != otherStyle.font)
525			mode &= ~B_FONT_FAMILY_AND_STYLE;
526	}
527	if ((mode & B_FONT_SIZE) != 0) {
528		if (firstStyle.font.Size() != otherStyle.font.Size())
529			mode &= ~B_FONT_SIZE;
530	}
531	if ((mode & B_FONT_SHEAR) != 0) {
532		if (firstStyle.font.Shear() != otherStyle.font.Shear())
533			mode &= ~B_FONT_SHEAR;
534	}
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;
539		}
540	}
541	if (firstStyle.color != otherStyle.color)
542		sameColor = false;
543
544	// TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc.
545	// if needed
546}
547
548
549void
550BTextView::StyleBuffer::ContinuousGetStyle(BFont *outFont, uint32* ioMode,
551	rgb_color* outColor, bool* sameColor, int32 fromOffset,
552	int32 toOffset) const
553{
554	uint32 mode = B_FONT_ALL;
555
556	if (fStyleRunDesc.ItemCount() < 1) {
557		if (ioMode)
558			*ioMode = mode;
559
560		if (outFont != NULL)
561			*outFont = fNullStyle.font;
562
563		if (outColor != NULL)
564			*outColor = fNullStyle.color;
565
566		if (sameColor != NULL)
567			*sameColor = true;
568
569		return;
570	}
571
572	int32 fromIndex = OffsetToRun(fromOffset);
573	int32 toIndex = OffsetToRun(toOffset - 1);
574
575	if (fromIndex == toIndex) {
576		int32 styleIndex = fStyleRunDesc[fromIndex]->index;
577		const STEStyle* style = &fStyleRecord[styleIndex]->style;
578
579		if (ioMode != NULL)
580			*ioMode = mode;
581
582		if (outFont != NULL)
583			*outFont = style->font;
584
585		if (outColor != NULL)
586			*outColor = style->color;
587
588		if (sameColor != NULL)
589			*sameColor = true;
590	} else {
591		bool oneColor = true;
592		int32 styleIndex = fStyleRunDesc[toIndex]->index;
593		STEStyle theStyle = fStyleRecord[styleIndex]->style;
594
595		for (int32 i = fromIndex; i < toIndex; i++) {
596			styleIndex = fStyleRunDesc[i]->index;
597			FixupMode(fStyleRecord[styleIndex]->style, theStyle, mode,
598				oneColor);
599		}
600
601		if (ioMode != NULL)
602			*ioMode = mode;
603
604		if (outFont != NULL)
605			*outFont = theStyle.font;
606
607		if (outColor != NULL)
608			*outColor = theStyle.color;
609
610		if (sameColor != NULL)
611			*sameColor = oneColor;
612	}
613}
614