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