xref: /haiku/src/kits/interface/textview_support/StyleBuffer.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
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 
175 	if (fromColor != NULL && toColor != NULL
176 		&& (mode == 0 || mode == B_FONT_ALL))
177 		*toColor = *fromColor;
178 }
179 
180 
181 BTextView::StyleBuffer::StyleBuffer(const BFont *inFont,
182 	const rgb_color *inColor)
183 	:
184 	fValidNullStyle(true)
185 {
186 	fNullStyle.font = *inFont;
187 	fNullStyle.color = *inColor;
188 }
189 
190 
191 void
192 BTextView::StyleBuffer::InvalidateNullStyle()
193 {
194 	fValidNullStyle = false;
195 }
196 
197 
198 bool
199 BTextView::StyleBuffer::IsValidNullStyle() const
200 {
201 	return fValidNullStyle;
202 }
203 
204 
205 void
206 BTextView::StyleBuffer::SyncNullStyle(int32 offset)
207 {
208 	if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1)
209 		return;
210 
211 	int32 index = OffsetToRun(offset);
212 	fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style;
213 
214 	fValidNullStyle = true;
215 }
216 
217 
218 void
219 BTextView::StyleBuffer::SetNullStyle(uint32 inMode, const BFont *inFont,
220 	const rgb_color *inColor, int32 offset)
221 {
222 	if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) {
223 		SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor,
224 			&fNullStyle.color);
225 	} else {
226 		int32 index = OffsetToRun(offset - 1);
227 		fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style;
228 		SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor,
229 			&fNullStyle.color);
230 	}
231 
232 	fValidNullStyle = true;
233 }
234 
235 
236 void
237 BTextView::StyleBuffer::GetNullStyle(const BFont **font,
238 	const rgb_color **color) const
239 {
240 	if (font != NULL)
241 		*font = &fNullStyle.font;
242 	if (color != NULL)
243 		*color = &fNullStyle.color;
244 }
245 
246 
247 STEStyleRange *
248 BTextView::StyleBuffer::AllocateStyleRange(const int32 numStyles) const
249 {
250 	STEStyleRange* range = (STEStyleRange *)malloc(sizeof(int32)
251 		+ sizeof(STEStyleRun) * numStyles);
252 	if (range != NULL)
253 		range->count = numStyles;
254 	return range;
255 }
256 
257 
258 void
259 BTextView::StyleBuffer::SetStyleRange(int32 fromOffset, int32 toOffset,
260 	int32 textLen, uint32 inMode, const BFont *inFont,
261 	const rgb_color *inColor)
262 {
263 	if (inFont == NULL)
264 		inFont = &fNullStyle.font;
265 
266 	if (inColor == NULL)
267 		inColor = &fNullStyle.color;
268 
269 	if (fromOffset == toOffset) {
270 		SetNullStyle(inMode, inFont, inColor, fromOffset);
271 		return;
272 	}
273 
274 	if (fStyleRunDesc.ItemCount() < 1) {
275 		STEStyleRunDesc newDesc;
276 		newDesc.offset = fromOffset;
277 		newDesc.index = fStyleRecord.InsertRecord(inFont, inColor);
278 		fStyleRunDesc.InsertDesc(&newDesc, 0);
279 		fStyleRecord.CommitRecord(newDesc.index);
280 		return;
281 	}
282 
283 	int32 offset = fromOffset;
284 	int32 runIndex = OffsetToRun(offset);
285 	int32 styleIndex = 0;
286 	do {
287 		const STEStyleRunDesc runDesc = *fStyleRunDesc[runIndex];
288 		int32 runEnd = textLen;
289 		if (runIndex < fStyleRunDesc.ItemCount() - 1)
290 			runEnd = fStyleRunDesc[runIndex + 1]->offset;
291 
292 		STEStyle style = fStyleRecord[runDesc.index]->style;
293 		SetStyleFromMode(inMode, inFont, &style.font, inColor, &style.color);
294 
295 		styleIndex = fStyleRecord.InsertRecord(&style.font, &style.color);
296 
297 		if (runDesc.offset == offset && runIndex > 0
298 			&& fStyleRunDesc[runIndex - 1]->index == styleIndex) {
299 			RemoveStyles(runIndex);
300 			runIndex--;
301 		}
302 
303 		if (styleIndex != runDesc.index) {
304 			if (offset > runDesc.offset) {
305 				STEStyleRunDesc newDesc;
306 				newDesc.offset = offset;
307 				newDesc.index = styleIndex;
308 				fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1);
309 				fStyleRecord.CommitRecord(newDesc.index);
310 				runIndex++;
311 			} else {
312 				fStyleRunDesc[runIndex]->index = styleIndex;
313 				fStyleRecord.CommitRecord(styleIndex);
314 			}
315 
316 			if (toOffset < runEnd) {
317 				STEStyleRunDesc newDesc;
318 				newDesc.offset = toOffset;
319 				newDesc.index = runDesc.index;
320 				fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1);
321 				fStyleRecord.CommitRecord(newDesc.index);
322 			}
323 		}
324 
325 		runIndex++;
326 		offset = runEnd;
327 	} while (offset < toOffset);
328 
329 	if (offset == toOffset && runIndex < fStyleRunDesc.ItemCount()
330 		&& fStyleRunDesc[runIndex]->index == styleIndex)
331 		RemoveStyles(runIndex);
332 }
333 
334 
335 void
336 BTextView::StyleBuffer::GetStyle(int32 inOffset, BFont *outFont,
337 	rgb_color *outColor) const
338 {
339 	if (fStyleRunDesc.ItemCount() < 1) {
340 		if (outFont)
341 			*outFont = fNullStyle.font;
342 		if (outColor)
343 			*outColor = fNullStyle.color;
344 		return;
345 	}
346 
347 	int32 runIndex = OffsetToRun(inOffset);
348 	int32 styleIndex = fStyleRunDesc[runIndex]->index;
349 
350 	if (outFont)
351 		*outFont = fStyleRecord[styleIndex]->style.font;
352 	if (outColor)
353 		*outColor = fStyleRecord[styleIndex]->style.color;
354 }
355 
356 
357 STEStyleRange*
358 BTextView::StyleBuffer::GetStyleRange(int32 startOffset, int32 endOffset) const
359 {
360 	int32 startIndex = OffsetToRun(startOffset);
361 	int32 endIndex = OffsetToRun(endOffset);
362 
363 	int32 numStyles = endIndex - startIndex + 1;
364 	if (numStyles < 1)
365 		numStyles = 1;
366 
367 	STEStyleRange* result = AllocateStyleRange(numStyles);
368 	if (!result)
369 		return NULL;
370 
371 	STEStyleRun* run = &result->runs[0];
372 	for (int32 index = 0; index < numStyles; index++) {
373 		*run = (*this)[startIndex + index];
374 		run->offset -= startOffset;
375 		if (run->offset < 0)
376 			run->offset = 0;
377 		run++;
378 	}
379 
380 	return result;
381 }
382 
383 
384 void
385 BTextView::StyleBuffer::RemoveStyleRange(int32 fromOffset, int32 toOffset)
386 {
387 	int32 fromIndex = fStyleRunDesc.OffsetToRun(fromOffset);
388 	int32 toIndex = fStyleRunDesc.OffsetToRun(toOffset) - 1;
389 
390 	int32 count = toIndex - fromIndex;
391 	if (count > 0) {
392 		RemoveStyles(fromIndex + 1, count);
393 		toIndex = fromIndex;
394 	}
395 
396 	fStyleRunDesc.BumpOffset(fromOffset - toOffset, fromIndex + 1);
397 
398 	if (toIndex == fromIndex && toIndex < fStyleRunDesc.ItemCount() - 1) {
399 		STEStyleRunDesc* runDesc = fStyleRunDesc[toIndex + 1];
400 		runDesc->offset = fromOffset;
401 	}
402 
403 	if (fromIndex < fStyleRunDesc.ItemCount() - 1) {
404 		STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex];
405 		if (runDesc->offset == (runDesc + 1)->offset) {
406 			RemoveStyles(fromIndex);
407 			fromIndex--;
408 		}
409 	}
410 
411 	if (fromIndex >= 0 && fromIndex < fStyleRunDesc.ItemCount() - 1) {
412 		STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex];
413 		if (runDesc->index == (runDesc + 1)->index)
414 			RemoveStyles(fromIndex + 1);
415 	}
416 }
417 
418 
419 void
420 BTextView::StyleBuffer::RemoveStyles(int32 index, int32 count)
421 {
422 	for (int32 i = index; i < (index + count); i++)
423 		fStyleRecord.RemoveRecord(fStyleRunDesc[i]->index);
424 
425 	fStyleRunDesc.RemoveDescs(index, count);
426 }
427 
428 
429 int32
430 BTextView::StyleBuffer::Iterate(int32 fromOffset, int32 length,
431 	InlineInput *input,
432 	const BFont **outFont, const rgb_color **outColor,
433 	float *outAscent, float *outDescent, uint32 *) const
434 {
435 	// TODO: Handle the InlineInput style here in some way
436 	int32 numRuns = fStyleRunDesc.ItemCount();
437 	if (length < 1 || numRuns < 1)
438 		return 0;
439 
440 	int32 result = length;
441 	int32 runIndex = fStyleRunDesc.OffsetToRun(fromOffset);
442 	STEStyleRunDesc* run = fStyleRunDesc[runIndex];
443 
444 	if (outFont != NULL)
445 		*outFont = &fStyleRecord[run->index]->style.font;
446 	if (outColor != NULL)
447 		*outColor = &fStyleRecord[run->index]->style.color;
448 	if (outAscent != NULL)
449 		*outAscent = fStyleRecord[run->index]->ascent;
450 	if (outDescent != NULL)
451 		*outDescent = fStyleRecord[run->index]->descent;
452 
453 	if (runIndex < numRuns - 1) {
454 		int32 nextOffset = (run + 1)->offset - fromOffset;
455 		result = min_c(result, nextOffset);
456 	}
457 
458 	return result;
459 }
460 
461 
462 int32
463 BTextView::StyleBuffer::OffsetToRun(int32 offset) const
464 {
465 	return fStyleRunDesc.OffsetToRun(offset);
466 }
467 
468 
469 void
470 BTextView::StyleBuffer::BumpOffset(int32 delta, int32 index)
471 {
472 	fStyleRunDesc.BumpOffset(delta, index);
473 }
474 
475 
476 STEStyleRun
477 BTextView::StyleBuffer::operator[](int32 index) const
478 {
479 	STEStyleRun run;
480 
481 	if (fStyleRunDesc.ItemCount() < 1) {
482 		run.offset = 0;
483 		run.style = fNullStyle;
484 	} else {
485 		STEStyleRunDesc* runDesc = fStyleRunDesc[index];
486 		run.offset = runDesc->offset;
487 		run.style = fStyleRecord[runDesc->index]->style;
488 	}
489 
490 	return run;
491 }
492 
493 
494 // TODO: Horrible name, but can't think of a better one
495 // ? CompareStyles ?
496 // ? FilterStyles ?
497 static void
498 FixupMode(const STEStyle &firstStyle, const STEStyle &otherStyle, uint32 &mode,
499 	bool &sameColor)
500 {
501 	if (mode & B_FONT_FAMILY_AND_STYLE) {
502 		if (firstStyle.font != otherStyle.font)
503 			mode &= ~B_FONT_FAMILY_AND_STYLE;
504 	}
505 	if (mode & B_FONT_SIZE) {
506 		if (firstStyle.font.Size() != otherStyle.font.Size())
507 			mode &= ~B_FONT_SIZE;
508 	}
509 	if (mode & B_FONT_SHEAR) {
510 		if (firstStyle.font.Shear() != otherStyle.font.Shear())
511 			mode &= ~B_FONT_SHEAR;
512 	}
513 	if (firstStyle.color != otherStyle.color)
514 		sameColor = false;
515 
516 	// TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc.
517 	// if needed
518 }
519 
520 
521 void
522 BTextView::StyleBuffer::ContinuousGetStyle(BFont *outFont, uint32 *ioMode,
523 	rgb_color *outColor, bool *sameColor, int32 fromOffset,
524 	int32 toOffset) const
525 {
526 	uint32 mode = B_FONT_ALL;
527 
528 	if (fStyleRunDesc.ItemCount() < 1) {
529 		if (ioMode)
530 			*ioMode = mode;
531 		if (outFont)
532 			*outFont = fNullStyle.font;
533 		if (outColor)
534 			*outColor = fNullStyle.color;
535 		if (sameColor)
536 			*sameColor = true;
537 		return;
538 	}
539 
540 	int32 fromIndex = OffsetToRun(fromOffset);
541 	int32 toIndex = OffsetToRun(toOffset - 1);
542 
543 	if (fromIndex == toIndex) {
544 		int32 styleIndex = fStyleRunDesc[fromIndex]->index;
545 		const STEStyle* style = &fStyleRecord[styleIndex]->style;
546 
547 		if (ioMode)
548 			*ioMode = mode;
549 		if (outFont)
550 			*outFont = style->font;
551 		if (outColor)
552 			*outColor = style->color;
553 		if (sameColor)
554 			*sameColor = true;
555 	} else {
556 		bool oneColor = true;
557 		int32 styleIndex = fStyleRunDesc[toIndex]->index;
558 		STEStyle theStyle = fStyleRecord[styleIndex]->style;
559 
560 		for (int32 i = fromIndex; i < toIndex; i++) {
561 			styleIndex = fStyleRunDesc[i]->index;
562 			FixupMode(fStyleRecord[styleIndex]->style, theStyle, mode,
563 				oneColor);
564 		}
565 
566 		if (ioMode)
567 			*ioMode = mode;
568 		if (outFont)
569 			*outFont = theStyle.font;
570 		if (outColor)
571 			*outColor = theStyle.color;
572 		if (sameColor)
573 			*sameColor = oneColor;
574 	}
575 }
576