1 // -*- C++ -*-
2
3 /*
4 * Gnome Chemistry Utils
5 * gccv/text-tag.cc
6 *
7 * Copyright (C) 2008-2010 Jean Bréfort <jean.brefort@normalesup.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
23 */
24
25 #include "config.h"
26 #include "text-tag.h"
27 #include <list>
28 #include <map>
29
30 namespace gccv {
31
32 ////////////////////////////////////////////////////////////////////////////////
33 // Base tag class
34
TextTag(Tag tag,TagPriority priority)35 TextTag::TextTag (Tag tag, TagPriority priority):
36 m_Tag (tag),
37 m_Priority (priority),
38 m_StartIndex (0),
39 m_EndIndex (0),
40 m_Stacked (false),
41 m_NewLine (false)
42 {
43 }
44
~TextTag()45 TextTag::~TextTag ()
46 {
47 }
48
49 Tag TextTag::MaxTag = gccv::MaxTag;
50
RegisterTagType()51 Tag TextTag::RegisterTagType ()
52 {
53 Tag result = MaxTag;
54 MaxTag = static_cast <Tag> (MaxTag + 1);
55 return result;
56 }
57
Order(TextTag * first,TextTag * last)58 bool TextTag::Order (TextTag *first, TextTag *last)
59 {
60 if (first->GetStartIndex () < last->GetStartIndex ())
61 return true;
62 else if (first->GetStartIndex () > last->GetStartIndex ())
63 return false;
64 if (first->GetEndIndex () > last->GetEndIndex ())
65 return true;
66 else if (first->GetEndIndex () < last->GetEndIndex ())
67 return false;
68 return first->GetTag () < last->GetTag ();
69 }
70
Restrict(TextTag * tag)71 TextTag *TextTag::Restrict (TextTag *tag)
72 {
73 if (tag->GetTag () == GetTag () && tag->GetEndIndex () > GetStartIndex () && tag->GetStartIndex () < GetEndIndex ()) {
74 if (*tag == *this) {
75 if (m_StartIndex > tag->GetStartIndex ())
76 m_StartIndex = tag->GetStartIndex ();
77 if (m_EndIndex < tag->GetEndIndex ())
78 m_EndIndex = tag->GetEndIndex ();
79 tag->SetEndIndex (m_StartIndex); // makes tag invalid
80 return NULL;
81 }
82 if (tag->GetEndIndex () > GetEndIndex ()) {
83 if (tag->GetStartIndex () < GetStartIndex ()) {
84 // split tag
85 TextTag *new_tag = tag->Duplicate ();
86 new_tag->SetStartIndex (m_EndIndex);
87 new_tag->SetEndIndex (tag->GetEndIndex ());
88 tag->SetEndIndex (m_StartIndex);
89 return new_tag;
90 }
91 tag->SetStartIndex (m_EndIndex);
92 return NULL;
93 } else {
94 tag->SetEndIndex (m_StartIndex);
95 return NULL;
96 }
97 }
98 return NULL;
99 }
100
101 ////////////////////////////////////////////////////////////////////////////////
102 // Font family tag class
103
FamilyTextTag(std::string const & family)104 FamilyTextTag::FamilyTextTag (std::string const &family):
105 TextTag (Family),
106 m_Family (family)
107 {
108 }
109
FamilyTextTag(char const * family)110 FamilyTextTag::FamilyTextTag (char const *family):
111 TextTag (Family),
112 m_Family (family)
113 {
114 }
115
~FamilyTextTag()116 FamilyTextTag::~FamilyTextTag ()
117 {
118 }
119
Filter(PangoAttrList * l,unsigned start,unsigned end)120 void FamilyTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
121 {
122 PangoAttribute *attr = pango_attr_family_new (m_Family.c_str ());
123 attr->start_index = start;
124 attr->end_index = end;
125 pango_attr_list_insert (l, attr);
126 }
127
operator ==(TextTag const & tag) const128 bool FamilyTextTag::operator== (TextTag const& tag) const
129 {
130 if (tag.GetTag () != Family)
131 return false;
132 return static_cast <FamilyTextTag const&> (tag).m_Family == m_Family;
133 }
134
Duplicate() const135 TextTag *FamilyTextTag::Duplicate () const
136 {
137 return new FamilyTextTag (m_Family);
138 }
139
140 ////////////////////////////////////////////////////////////////////////////////
141 // Font size tag class
142
SizeTextTag(double size)143 SizeTextTag::SizeTextTag (double size):
144 TextTag (Size),
145 m_Size (size)
146 {
147 }
148
~SizeTextTag()149 SizeTextTag::~SizeTextTag ()
150 {
151 }
152
Filter(PangoAttrList * l,unsigned start,unsigned end)153 void SizeTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
154 {
155 PangoAttribute *attr = pango_attr_size_new (m_Size);
156 attr->start_index = start;
157 attr->end_index = end;
158 pango_attr_list_insert (l, attr);
159 }
160
operator ==(TextTag const & tag) const161 bool SizeTextTag::operator== (TextTag const& tag) const
162 {
163 if (tag.GetTag () != Size)
164 return false;
165 return static_cast <SizeTextTag const&> (tag).m_Size == m_Size;
166 }
167
Duplicate() const168 TextTag *SizeTextTag::Duplicate () const
169 {
170 return new SizeTextTag (m_Size);
171 }
172
173 ////////////////////////////////////////////////////////////////////////////////
174 // Font style tag class
175
StyleTextTag(PangoStyle style)176 StyleTextTag::StyleTextTag (PangoStyle style):
177 TextTag (Style),
178 m_Style (style)
179 {
180 }
181
~StyleTextTag()182 StyleTextTag::~StyleTextTag ()
183 {
184 }
185
Filter(PangoAttrList * l,unsigned start,unsigned end)186 void StyleTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
187 {
188 PangoAttribute *attr = pango_attr_style_new (m_Style);
189 attr->start_index = start;
190 attr->end_index = end;
191 pango_attr_list_insert (l, attr);
192 }
193
operator ==(TextTag const & tag) const194 bool StyleTextTag::operator== (TextTag const& tag) const
195 {
196 if (tag.GetTag () != Style)
197 return false;
198 return static_cast <StyleTextTag const&> (tag).m_Style == m_Style;
199 }
200
Duplicate() const201 TextTag *StyleTextTag::Duplicate () const
202 {
203 return new StyleTextTag (m_Style);
204 }
205
206 ////////////////////////////////////////////////////////////////////////////////
207 // Font weight tag class
208
WeightTextTag(PangoWeight weight)209 WeightTextTag::WeightTextTag (PangoWeight weight):
210 TextTag (Weight),
211 m_Weight (weight)
212 {
213 }
214
~WeightTextTag()215 WeightTextTag::~WeightTextTag ()
216 {
217 }
218
Filter(PangoAttrList * l,unsigned start,unsigned end)219 void WeightTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
220 {
221 PangoAttribute *attr = pango_attr_weight_new (m_Weight);
222 attr->start_index = start;
223 attr->end_index = end;
224 pango_attr_list_insert (l, attr);
225 }
226
operator ==(TextTag const & tag) const227 bool WeightTextTag::operator== (TextTag const& tag) const
228 {
229 if (tag.GetTag () != Weight)
230 return false;
231 return static_cast <WeightTextTag const&> (tag).m_Weight == m_Weight;
232 }
233
Duplicate() const234 TextTag *WeightTextTag::Duplicate () const
235 {
236 return new WeightTextTag (m_Weight);
237 }
238
239 ////////////////////////////////////////////////////////////////////////////////
240 // Font variant tag class
241
VariantTextTag(PangoVariant variant)242 VariantTextTag::VariantTextTag (PangoVariant variant):
243 TextTag (Variant),
244 m_Variant (variant)
245 {
246 }
247
~VariantTextTag()248 VariantTextTag::~VariantTextTag ()
249 {
250 }
251
Filter(PangoAttrList * l,unsigned start,unsigned end)252 void VariantTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
253 {
254 PangoAttribute *attr = pango_attr_variant_new (m_Variant);
255 attr->start_index = start;
256 attr->end_index = end;
257 pango_attr_list_insert (l, attr);
258 }
259
operator ==(TextTag const & tag) const260 bool VariantTextTag::operator== (TextTag const& tag) const
261 {
262 if (tag.GetTag () != Variant)
263 return false;
264 return static_cast <VariantTextTag const&> (tag).m_Variant == m_Variant;
265 }
266
Duplicate() const267 TextTag *VariantTextTag::Duplicate () const
268 {
269 return new VariantTextTag (m_Variant);
270 }
271
272 ////////////////////////////////////////////////////////////////////////////////
273 // Font strecth tag class
274
StretchTextTag(PangoStretch stretch)275 StretchTextTag::StretchTextTag (PangoStretch stretch):
276 TextTag (Stretch),
277 m_Stretch (stretch)
278 {
279 }
280
~StretchTextTag()281 StretchTextTag::~StretchTextTag ()
282 {
283 }
284
Filter(PangoAttrList * l,unsigned start,unsigned end)285 void StretchTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
286 {
287 PangoAttribute *attr = pango_attr_stretch_new (m_Stretch);
288 attr->start_index = start;
289 attr->end_index = end;
290 pango_attr_list_insert (l, attr);
291 }
292
operator ==(TextTag const & tag) const293 bool StretchTextTag::operator== (TextTag const& tag) const
294 {
295 if (tag.GetTag () != Stretch)
296 return false;
297 return static_cast <StretchTextTag const&> (tag).m_Stretch == m_Stretch;
298 }
299
Duplicate() const300 TextTag *StretchTextTag::Duplicate () const
301 {
302 return new StretchTextTag (m_Stretch);
303 }
304
305 ////////////////////////////////////////////////////////////////////////////////
306 // Font underline tag class
307
UnderlineTextTag(TextDecoration underline,GOColor color)308 UnderlineTextTag::UnderlineTextTag (TextDecoration underline, GOColor color):
309 TextTag (Underline),
310 m_Underline (underline),
311 m_Color (color)
312 {
313 }
314
~UnderlineTextTag()315 UnderlineTextTag::~UnderlineTextTag ()
316 {
317 }
318
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)319 void UnderlineTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
320 {
321 }
322
operator ==(TextTag const & tag) const323 bool UnderlineTextTag::operator== (TextTag const& tag) const
324 {
325 if (tag.GetTag () != Underline)
326 return false;
327 return static_cast <UnderlineTextTag const&> (tag).m_Underline == m_Underline;
328 }
329
Duplicate() const330 TextTag *UnderlineTextTag::Duplicate () const
331 {
332 return new UnderlineTextTag (m_Underline);
333 }
334
335 ////////////////////////////////////////////////////////////////////////////////
336 // Font overline tag class
337
OverlineTextTag(TextDecoration overline,GOColor color)338 OverlineTextTag::OverlineTextTag (TextDecoration overline, GOColor color):
339 TextTag (Overline),
340 m_Overline (overline),
341 m_Color (color)
342 {
343 }
344
~OverlineTextTag()345 OverlineTextTag::~OverlineTextTag ()
346 {
347 }
348
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)349 void OverlineTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
350 {
351 }
352
operator ==(TextTag const & tag) const353 bool OverlineTextTag::operator== (TextTag const& tag) const
354 {
355 if (tag.GetTag () != Overline)
356 return false;
357 return static_cast <OverlineTextTag const&> (tag).m_Overline == m_Overline;
358 }
359
Duplicate() const360 TextTag *OverlineTextTag::Duplicate () const
361 {
362 return new OverlineTextTag (m_Overline);
363 }
364
365 ////////////////////////////////////////////////////////////////////////////////
366 // Font strikethrough tag class
367
StrikethroughTextTag(TextDecoration strikethrough,GOColor color)368 StrikethroughTextTag::StrikethroughTextTag (TextDecoration strikethrough, GOColor color):
369 TextTag (Strikethrough),
370 m_Strikethrough (strikethrough),
371 m_Color (color)
372 {
373 }
374
~StrikethroughTextTag()375 StrikethroughTextTag::~StrikethroughTextTag ()
376 {
377 }
378
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)379 void StrikethroughTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
380 {
381 }
382
operator ==(TextTag const & tag) const383 bool StrikethroughTextTag::operator== (TextTag const& tag) const
384 {
385 if (tag.GetTag () != Strikethrough)
386 return false;
387 return static_cast <StrikethroughTextTag const&> (tag).m_Strikethrough == m_Strikethrough;
388 }
389
Duplicate() const390 TextTag *StrikethroughTextTag::Duplicate () const
391 {
392 return new StrikethroughTextTag (m_Strikethrough);
393 }
394
395 ////////////////////////////////////////////////////////////////////////////////
396 // Font foreground tag class
397
ForegroundTextTag(GOColor color)398 ForegroundTextTag::ForegroundTextTag (GOColor color):
399 TextTag (Foreground),
400 m_Color (color)
401 {
402 }
403
~ForegroundTextTag()404 ForegroundTextTag::~ForegroundTextTag ()
405 {
406 }
407
Filter(PangoAttrList * l,unsigned start,unsigned end)408 void ForegroundTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
409 {
410 // this might be not enough since we use a global attribute with SetColor().
411 PangoAttribute *attr = pango_attr_foreground_new (GO_COLOR_UINT_R (m_Color) * 0x101, GO_COLOR_UINT_G (m_Color) * 0x101, GO_COLOR_UINT_B (m_Color) * 0x101);
412 attr->start_index = start;
413 attr->end_index = end;
414 pango_attr_list_insert (l, attr);
415 }
416
operator ==(TextTag const & tag) const417 bool ForegroundTextTag::operator== (TextTag const& tag) const
418 {
419 if (tag.GetTag () != Foreground)
420 return false;
421 return static_cast <ForegroundTextTag const&> (tag).m_Color == m_Color;
422 }
423
Duplicate() const424 TextTag *ForegroundTextTag::Duplicate () const
425 {
426 return new ForegroundTextTag (m_Color);
427 }
428
429 ////////////////////////////////////////////////////////////////////////////////
430 // Font background tag class
431
BackgroundTextTag(GOColor color)432 BackgroundTextTag::BackgroundTextTag (GOColor color):
433 TextTag (Background),
434 m_Color (color)
435 {
436 }
437
~BackgroundTextTag()438 BackgroundTextTag::~BackgroundTextTag ()
439 {
440 }
441
Filter(PangoAttrList * l,unsigned start,unsigned end)442 void BackgroundTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
443 {
444 PangoAttribute *attr = pango_attr_background_new (GO_COLOR_UINT_R (m_Color) * 0x101, GO_COLOR_UINT_G (m_Color) * 0x101, GO_COLOR_UINT_B (m_Color) * 0x101);
445 attr->start_index = start;
446 attr->end_index = end;
447 pango_attr_list_insert (l, attr);
448 }
449
operator ==(TextTag const & tag) const450 bool BackgroundTextTag::operator== (TextTag const& tag) const
451 {
452 if (tag.GetTag () != Background)
453 return false;
454 return static_cast <BackgroundTextTag const&> (tag).m_Color == m_Color;
455 }
456
Duplicate() const457 TextTag *BackgroundTextTag::Duplicate () const
458 {
459 return new BackgroundTextTag (m_Color);
460 }
461
462 ////////////////////////////////////////////////////////////////////////////////
463 // Font rise tag class
464
RiseTextTag(double rise)465 RiseTextTag::RiseTextTag (double rise):
466 TextTag (Rise),
467 m_Rise (rise)
468 {
469 }
470
~RiseTextTag()471 RiseTextTag::~RiseTextTag ()
472 {
473 }
474
Filter(PangoAttrList * l,unsigned start,unsigned end)475 void RiseTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
476 {
477 PangoAttribute *attr = pango_attr_rise_new (m_Rise);
478 attr->start_index = start;
479 attr->end_index = end;
480 pango_attr_list_insert (l, attr);
481 }
482
operator ==(TextTag const & tag) const483 bool RiseTextTag::operator== (TextTag const& tag) const
484 {
485 if (tag.GetTag () != Rise)
486 return false;
487 return static_cast <RiseTextTag const&> (tag).m_Rise == m_Rise;
488 }
489
Duplicate() const490 TextTag *RiseTextTag::Duplicate () const
491 {
492 return new RiseTextTag (m_Rise);
493 }
494
495 ////////////////////////////////////////////////////////////////////////////////
496 // Position tag class (normal, subscript, superscript,...)
497
PositionTextTag(TextPosition position,double size,bool stacked,Tag tag)498 PositionTextTag::PositionTextTag (TextPosition position, double size, bool stacked, Tag tag):
499 TextTag (tag, TagPriorityLast),
500 m_Position (position),
501 m_Size (size)
502 {
503 m_Stacked = stacked;
504 }
505
~PositionTextTag()506 PositionTextTag::~PositionTextTag ()
507 {
508 }
509
510 struct position_data {
511 guint start, end;
512 std::list <PangoAttribute *>extra;
513 std::map <unsigned, int> sizes, rises;
514 };
515
516 static gboolean
position_filter(PangoAttribute * attr,gpointer _data)517 position_filter (PangoAttribute *attr, gpointer _data)
518 {
519 struct position_data *data = static_cast <struct position_data *> (_data);
520 int start = MAX (data->start, attr->start_index);
521
522 if (attr->end_index <= data->start || attr->start_index >= data->end)
523 return false;
524 switch (attr->klass->type) {
525 case PANGO_ATTR_SIZE:
526 data->sizes[start] = reinterpret_cast <PangoAttrSize *> (attr)->size;
527 break;
528 case PANGO_ATTR_RISE:
529 data->rises[start] = reinterpret_cast <PangoAttrInt *> (attr)->value;
530 break;
531 default:
532 return false;
533 }
534 if (attr->end_index > data->end || attr->start_index < data->start) {
535 // add a new attribute
536 PangoAttribute *new_attr = pango_attribute_copy (attr);
537 new_attr->start_index = data->end;
538 new_attr->end_index = attr->end_index;
539 attr->end_index = data->start;
540 data->extra.push_back (new_attr);
541 } else if (attr->start_index < data->start)
542 attr->end_index = data->start;
543 else
544 attr->start_index = data->end;
545 return false;
546 }
547
Filter(PangoAttrList * l,unsigned start,unsigned end)548 void PositionTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
549 {
550 if (m_Position == Normalscript)
551 return;
552 struct position_data data;
553 data.start = start;
554 data.end = end;
555 data.sizes[start] = m_Size * PANGO_SCALE;
556 data.rises[start] = 0.;
557 pango_attr_list_filter (l, position_filter, &data);
558 // Build and apply the new attributes
559 std::map <unsigned, int>::iterator i, j, iend = data.sizes.end (), jend = data.rises.end (), nexti, nextj;
560 unsigned cur_start = start, cur_end;
561 j = data.rises.begin ();
562 double rise_f = 1.;
563 switch (m_Position) {
564 case Subscript:
565 rise_f = -3.;
566 break;
567 case Superscript:
568 rise_f = +3/2.;
569 break;
570 case Normalscript:
571 break;
572 }
573 for (i = data.sizes.begin (); i != iend; i++) {
574 nexti = i;
575 nexti++;
576 for (; j!= jend && (*j).first < ((nexti == iend)? end: (*nexti).first); j++) {
577 nextj = j;
578 nextj++;
579 cur_end = MIN (((nexti == iend)? end: (*nexti).first), ((nextj == jend)? end: (*nextj).first));
580 PangoAttribute *attr = pango_attr_size_new ((*i).second * 2 / 3);
581 attr->start_index = cur_start;
582 attr->end_index = cur_end;
583 pango_attr_list_insert (l, attr);
584 attr = pango_attr_rise_new ((*j).second + (*i).second / rise_f);
585 attr->start_index = cur_start;
586 attr->end_index = cur_end;
587 pango_attr_list_insert (l, attr);
588 cur_start = cur_end;
589 }
590 //WARNING: might be buggy
591 }
592 // Apply the extra attributes.
593 std::list <PangoAttribute *>::iterator k, kend = data.extra.end ();
594 for (k = data.extra.begin (); k != kend; k++)
595 pango_attr_list_insert (l, *k);
596 }
597
operator ==(TextTag const & tag) const598 bool PositionTextTag::operator== (TextTag const& tag) const
599 {
600 if (tag.GetTag () != GetTag ())
601 return false;
602 return static_cast <PositionTextTag const&> (tag).m_Position == m_Position;
603 }
604
Duplicate() const605 TextTag *PositionTextTag::Duplicate () const
606 {
607 return new PositionTextTag (m_Position, m_Size, m_Stacked, GetTag ());
608 }
609
610 ////////////////////////////////////////////////////////////////////////////////
611 // Position tag class (normal, subscript, superscript,...)
612
NewLineTextTag()613 NewLineTextTag::NewLineTextTag ():
614 TextTag (NewLine)
615 {
616 m_NewLine = true;
617 }
618
~NewLineTextTag()619 NewLineTextTag::~NewLineTextTag ()
620 {
621 }
622
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)623 void NewLineTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
624 {
625 // Nothing to do
626 }
627
operator ==(G_GNUC_UNUSED TextTag const & tag) const628 bool NewLineTextTag::operator== (G_GNUC_UNUSED TextTag const& tag) const
629 {
630 return false;
631 }
632
Duplicate() const633 TextTag *NewLineTextTag::Duplicate () const
634 {
635 return new NewLineTextTag ();
636 }
637
638 ////////////////////////////////////////////////////////////////////////////////
639 // Tags list class
TextTagList()640 TextTagList::TextTagList (): std::list <TextTag *> ()
641 {
642 }
643
~TextTagList()644 TextTagList::~TextTagList ()
645 {
646 std::list <TextTag *>::iterator i, iend = end ();
647 for (i = begin (); i != iend; i++)
648 delete (*i);
649 }
650
651 }
652