xref: /haiku/src/kits/interface/textview_support/StyleBuffer.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
31 void
32 _BStyleRunDescBuffer_::InsertDesc(STEStyleRunDesc* inDesc, int32 index)
33 {
34 	InsertItemsAt(1, index, inDesc);
35 }
36 
37 
38 void
39 _BStyleRunDescBuffer_::RemoveDescs(int32 index, int32 count)
40 {
41 	RemoveItemsAt(count, index);
42 }
43 
44 
45 int32
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 
71 void
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 
89 int32
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 
129 void
130 _BStyleRecordBuffer_::CommitRecord(int32 index)
131 {
132 	fBuffer[index].refs++;
133 }
134 
135 
136 void
137 _BStyleRecordBuffer_::RemoveRecord(int32 index)
138 {
139 	fBuffer[index].refs--;
140 }
141 
142 
143 bool
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 
162 static void
163 SetStyleFromMode(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 
193 BTextView::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 
203 void
204 BTextView::StyleBuffer::InvalidateNullStyle()
205 {
206 	fValidNullStyle = false;
207 }
208 
209 
210 bool
211 BTextView::StyleBuffer::IsValidNullStyle() const
212 {
213 	return fValidNullStyle;
214 }
215 
216 
217 void
218 BTextView::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 
230 void
231 BTextView::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 
248 void
249 BTextView::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 
260 STEStyleRange*
261 BTextView::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 
272 void
273 BTextView::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 
350 void
351 BTextView::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 
375 STEStyleRange*
376 BTextView::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 
403 void
404 BTextView::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 
438 void
439 BTextView::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 
448 int32
449 BTextView::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 
484 int32
485 BTextView::StyleBuffer::OffsetToRun(int32 offset) const
486 {
487 	return fStyleRunDesc.OffsetToRun(offset);
488 }
489 
490 
491 void
492 BTextView::StyleBuffer::BumpOffset(int32 delta, int32 index)
493 {
494 	fStyleRunDesc.BumpOffset(delta, index);
495 }
496 
497 
498 STEStyleRun
499 BTextView::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 ?
519 static void
520 FixupMode(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 
549 void
550 BTextView::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