xref: /haiku/src/kits/interface/textview_support/StyleBuffer.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
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 _BStyleBuffer_::_BStyleBuffer_(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 _BStyleBuffer_::InvalidateNullStyle()
187 {
188 	fValidNullStyle = false;
189 }
190 
191 
192 bool
193 _BStyleBuffer_::IsValidNullStyle() const
194 {
195 	return fValidNullStyle;
196 }
197 
198 
199 void
200 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::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 _BStyleBuffer_::Iterate(int32 fromOffset, int32 length, _BInlineInput_ *input,
420 	const BFont **outFont, const rgb_color **outColor,
421 	float *outAscent, float *outDescent, uint32 *) const
422 {
423 	// TODO: Handle the _BInlineInput_ style here in some way
424 	int32 numRuns = fStyleRunDesc.ItemCount();
425 	if (length < 1 || numRuns < 1)
426 		return 0;
427 
428 	int32 result = length;
429 	int32 runIndex = fStyleRunDesc.OffsetToRun(fromOffset);
430 	STEStyleRunDesc* run = fStyleRunDesc[runIndex];
431 
432 	if (outFont != NULL)
433 		*outFont = &fStyleRecord[run->index]->style.font;
434 	if (outColor != NULL)
435 		*outColor = &fStyleRecord[run->index]->style.color;
436 	if (outAscent != NULL)
437 		*outAscent = fStyleRecord[run->index]->ascent;
438 	if (outDescent != NULL)
439 		*outDescent = fStyleRecord[run->index]->descent;
440 
441 	if (runIndex < numRuns - 1) {
442 		int32 nextOffset = (run + 1)->offset - fromOffset;
443 		result = min_c(result, nextOffset);
444 	}
445 
446 	return result;
447 }
448 
449 
450 int32
451 _BStyleBuffer_::OffsetToRun(int32 offset) const
452 {
453 	return fStyleRunDesc.OffsetToRun(offset);
454 }
455 
456 
457 void
458 _BStyleBuffer_::BumpOffset(int32 delta, int32 index)
459 {
460 	fStyleRunDesc.BumpOffset(delta, index);
461 }
462 
463 
464 STEStyleRun
465 _BStyleBuffer_::operator[](int32 index) const
466 {
467 	STEStyleRun run;
468 
469 	if (fStyleRunDesc.ItemCount() < 1) {
470 		run.offset = 0;
471 		run.style = fNullStyle;
472 	} else {
473 		STEStyleRunDesc* runDesc = fStyleRunDesc[index];
474 		run.offset = runDesc->offset;
475 		run.style = fStyleRecord[runDesc->index]->style;
476 	}
477 
478 	return run;
479 }
480 
481 
482 // TODO: Horrible name, but can't think of a better one
483 // ? CompareStyles ?
484 // ? FilterStyles ?
485 static void
486 FixupMode(const STEStyle &firstStyle, const STEStyle &otherStyle, uint32 &mode, bool &sameColor)
487 {
488 	if (mode & B_FONT_FAMILY_AND_STYLE) {
489 		if (firstStyle.font != otherStyle.font)
490 			mode &= ~B_FONT_FAMILY_AND_STYLE;
491 	}
492 	if (mode & B_FONT_SIZE) {
493 		if (firstStyle.font.Size() != otherStyle.font.Size())
494 			mode &= ~B_FONT_SIZE;
495 	}
496 	if (mode & B_FONT_SHEAR) {
497 		if (firstStyle.font.Shear() != otherStyle.font.Shear())
498 			mode &= ~B_FONT_SHEAR;
499 	}
500 	if (firstStyle.color != otherStyle.color)
501 		sameColor = false;
502 
503 	// TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc.
504 	// if needed
505 }
506 
507 
508 void
509 _BStyleBuffer_::ContinuousGetStyle(BFont *outFont, uint32 *ioMode,
510 	rgb_color *outColor, bool *sameColor, int32 fromOffset,
511 	int32 toOffset) const
512 {
513 	uint32 mode = B_FONT_ALL;
514 
515 	if (fStyleRunDesc.ItemCount() < 1) {
516 		if (ioMode)
517 			*ioMode = mode;
518 		if (outFont)
519 			*outFont = fNullStyle.font;
520 		if (outColor)
521 			*outColor = fNullStyle.color;
522 		if (sameColor)
523 			*sameColor = true;
524 		return;
525 	}
526 
527 	int32 fromIndex = OffsetToRun(fromOffset);
528 	int32 toIndex = OffsetToRun(toOffset - 1);
529 
530 	if (fromIndex == toIndex) {
531 		int32 styleIndex = fStyleRunDesc[fromIndex]->index;
532 		const STEStyle* style = &fStyleRecord[styleIndex]->style;
533 
534 		if (ioMode)
535 			*ioMode = mode;
536 		if (outFont)
537 			*outFont = style->font;
538 		if (outColor)
539 			*outColor = style->color;
540 		if (sameColor)
541 			*sameColor = true;
542 	} else {
543 		bool oneColor = true;
544 		int32 styleIndex = fStyleRunDesc[toIndex]->index;
545 		STEStyle theStyle = fStyleRecord[styleIndex]->style;
546 
547 		for (int32 i = fromIndex; i < toIndex; i++) {
548 			styleIndex = fStyleRunDesc[i]->index;
549 			FixupMode(fStyleRecord[styleIndex]->style, theStyle, mode, oneColor);
550 		}
551 
552 		if (ioMode)
553 			*ioMode = mode;
554 		if (outFont)
555 			*outFont = theStyle.font;
556 		if (outColor)
557 			*outColor = theStyle.color;
558 		if (sameColor)
559 			*sameColor = oneColor;
560 	}
561 }
562