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