3 * Created by: haranp on Sat Feb 17 13:35:54 2007 UTC
16 formatted_string::formatted_string(int init_colour
)
20 textcolor(init_colour
);
23 formatted_string::formatted_string(const std::string
&s
, int init_colour
)
27 textcolor(init_colour
);
31 int formatted_string::get_colour(const std::string
&tag
)
39 const int colour
= str_to_colour(tag
);
40 return (colour
!= -1? colour
: LIGHTGREY
);
43 // Display a formatted string without printing literal \n.
44 // This is important if the text is not supposed
45 // to clobber existing text to the right of the lines being displayed
46 // (some of the tutorial messages need this).
47 void display_tagged_block(const std::string
&s
)
49 std::vector
<formatted_string
> lines
;
50 formatted_string::parse_string_to_multiple(s
, lines
);
54 for (int i
= 0, size
= lines
.size(); i
< size
; ++i
)
62 formatted_string
formatted_string::parse_string(
65 bool (*process
)(const std::string
&tag
),
68 // main_colour will usually be LIGHTGREY (default).
69 std::vector
<int> colour_stack
;
70 colour_stack
.push_back(main_colour
);
74 parse_string1(s
, fs
, colour_stack
, process
);
77 if (colour_stack
.back() != colour_stack
.front())
78 fs
.textcolor(colour_stack
.front());
83 // Parses a formatted string in much the same way as parse_string, but
84 // handles \n by creating a new formatted_string.
85 void formatted_string::parse_string_to_multiple(
87 std::vector
<formatted_string
> &out
)
89 std::vector
<int> colour_stack
;
90 colour_stack
.push_back(LIGHTGREY
);
92 std::vector
<std::string
> lines
= split_string("\n", s
, false, true);
94 for (int i
= 0, size
= lines
.size(); i
< size
; ++i
)
96 out
.push_back(formatted_string());
97 formatted_string
& fs
= out
.back();
98 fs
.textcolor(colour_stack
.back());
99 parse_string1(lines
[i
], fs
, colour_stack
, NULL
);
100 if (colour_stack
.back() != colour_stack
.front())
101 fs
.textcolor(colour_stack
.front());
105 // Helper for the other parse_ methods.
106 void formatted_string::parse_string1(
107 const std::string
&s
,
108 formatted_string
&fs
,
109 std::vector
<int> &colour_stack
,
110 bool (*process
)(const std::string
&tag
))
112 // FIXME: This is a lame mess, just good enough for the task on hand
114 std::string::size_type tag
= std::string::npos
;
115 std::string::size_type length
= s
.length();
120 for (tag
= 0; tag
< length
; ++tag
)
122 bool revert_colour
= false;
123 std::string::size_type endpos
= std::string::npos
;
125 // Break string up if it gets too big.
126 if (currs
.size() >= 999)
128 // Break the string at the end of a line, if possible, so
129 // that none of the broken string ends up overwritten.
130 std::string::size_type bound
= currs
.rfind("\n", 999);
136 fs
.cprintf(currs
.substr(0, bound
));
137 if (currs
.size() > bound
)
138 currs
= currs
.substr(bound
);
145 if (s
[tag
] != '<' || tag
>= length
- 2)
152 // Is this a << escape?
153 if (s
[tag
+ 1] == '<')
161 endpos
= s
.find('>', tag
+ 1);
163 if (endpos
== std::string::npos
)
170 std::string tagtext
= s
.substr(tag
+ 1, endpos
- tag
- 1);
171 if (tagtext
.empty() || tagtext
== "/")
178 if (tagtext
[0] == '/')
180 revert_colour
= true;
181 tagtext
= tagtext
.substr(1);
185 if (tagtext
[0] == '?')
187 if (tagtext
.length() == 1)
189 else if (process
&& !process(tagtext
.substr(1)))
192 tag
+= tagtext
.length() + 1;
204 colour_stack
.pop_back();
205 if (colour_stack
.size() < 1)
206 die("Stack underflow in string \"%s\"", s
.c_str());
210 colour_stack
.push_back(get_colour(tagtext
));
213 // fs.cprintf("%d%d", colour_stack.size(), colour_stack.back());
214 fs
.textcolor(colour_stack
.back());
216 tag
+= tagtext
.length() + 1;
222 formatted_string::operator std::string() const
225 for (unsigned i
= 0, size
= ops
.size(); i
< size
; ++i
)
227 if (ops
[i
] == FSOP_TEXT
)
233 void replace_all_in_string(std::string
& s
, const std::string
& search
,
234 const std::string
& replace
)
236 std::string::size_type pos
= 0;
237 while ((pos
= s
.find(search
, pos
)) != std::string::npos
)
239 s
.replace(pos
, search
.size(), replace
);
240 pos
+= replace
.size();
244 std::string
formatted_string::html_dump() const
247 for (unsigned i
= 0; i
< ops
.size(); ++i
)
254 // (very) crude HTMLification
255 replace_all_in_string(tmp
, "&", "&");
256 replace_all_in_string(tmp
, " ", " ");
257 replace_all_in_string(tmp
, "<", "<");
258 replace_all_in_string(tmp
, ">", ">");
259 replace_all_in_string(tmp
, "\n", "<br>");
264 s
+= colour_to_str(ops
[i
].x
);
272 bool formatted_string::operator < (const formatted_string
&other
) const
274 return std::string(*this) < std::string(other
);
277 const formatted_string
&
278 formatted_string::operator += (const formatted_string
&other
)
280 ops
.insert(ops
.end(), other
.ops
.begin(), other
.ops
.end());
284 std::string::size_type
formatted_string::length() const
286 // Just add up the individual string lengths.
287 std::string::size_type len
= 0;
288 for (unsigned i
= 0, size
= ops
.size(); i
< size
; ++i
)
289 if (ops
[i
] == FSOP_TEXT
)
290 len
+= ops
[i
].text
.length();
294 inline void cap(int &i
, int max
)
296 if (i
< 0 && -i
<= max
)
304 char &formatted_string::operator [] (size_t idx
)
306 size_t rel_idx
= idx
;
307 int size
= ops
.size();
308 for (int i
= 0; i
< size
; ++i
)
310 if (ops
[i
] != FSOP_TEXT
)
313 size_t len
= ops
[i
].text
.length();
317 return ops
[i
].text
[rel_idx
];
319 die("Invalid index");
323 std::string
formatted_string::tostring(int s
, int e
) const
327 int size
= ops
.size();
331 for (int i
= s
; i
<= e
&& i
< size
; ++i
)
333 if (ops
[i
] == FSOP_TEXT
)
339 std::string
formatted_string::to_colour_string() const
342 const int size
= ops
.size();
343 for (int i
= 0; i
< size
; ++i
)
345 if (ops
[i
] == FSOP_TEXT
)
347 // gotta double up those '<' chars ...
348 size_t start
= st
.size();
353 const size_t left_angle
= st
.find('<', start
);
354 if (left_angle
== std::string::npos
)
357 st
.insert(left_angle
, "<");
358 start
= left_angle
+ 2;
361 else if (ops
[i
] == FSOP_COLOUR
)
364 st
+= colour_to_str(ops
[i
].x
);
372 void formatted_string::display(int s
, int e
) const
374 int size
= ops
.size();
381 for (int i
= s
; i
<= e
&& i
< size
; ++i
)
385 int formatted_string::find_last_colour() const
389 for (int i
= ops
.size() - 1; i
>= 0; --i
)
390 if (ops
[i
].type
== FSOP_COLOUR
)
396 formatted_string
formatted_string::substr(size_t start
, size_t substr_length
) const
398 const unsigned int NONE
= UINT_MAX
; // from limits.h
399 unsigned int last_FSOP_COLOUR
= NONE
;
401 // Find the first string to copy
403 for (i
= 0; i
< ops
.size(); ++i
)
405 const fs_op
& op
= ops
[i
];
406 if (op
.type
== FSOP_COLOUR
)
407 last_FSOP_COLOUR
= i
;
408 else if (op
.type
== FSOP_TEXT
)
410 if (op
.text
.length() > start
)
413 start
-= op
.text
.length();
418 return formatted_string();
420 formatted_string result
;
422 if (last_FSOP_COLOUR
!= NONE
)
423 result
.ops
.push_back(ops
[last_FSOP_COLOUR
]);
426 for (; i
<ops
.size(); i
++)
428 const fs_op
& op
= ops
[i
];
429 if (op
.type
== FSOP_TEXT
)
431 result
.ops
.push_back(op
);
432 std::string
& new_string
= result
.ops
[result
.ops
.size()-1].text
;
433 if (start
> 0 || op
.text
.length() > substr_length
)
434 new_string
= new_string
.substr(start
, substr_length
);
436 substr_length
-= new_string
.length();
437 if (substr_length
== 0)
442 result
.ops
.push_back(op
);
449 void formatted_string::add_glyph(glyph g
)
451 const int last_col
= find_last_colour();
452 this->textcolor(g
.col
);
453 this->cprintf("%s", stringize_glyph(g
.ch
).c_str());
454 this->textcolor(last_col
);
457 void formatted_string::textcolor(int color
)
459 if (!ops
.empty() && ops
[ ops
.size() - 1 ].type
== FSOP_COLOUR
)
460 ops
.erase(ops
.end() - 1);
462 ops
.push_back(color
);
465 void formatted_string::clear()
470 bool formatted_string::empty()
472 return (ops
.empty());
475 void formatted_string::cprintf(const char *s
, ...)
479 cprintf(vmake_stringf(s
, args
));
483 void formatted_string::cprintf(const std::string
&s
)
488 void formatted_string::fs_op::display() const
496 ::cprintf("%s", text
.c_str());
501 void formatted_string::swap(formatted_string
& other
)
506 void formatted_string::all_caps()
508 for (unsigned int i
= 0; i
< ops
.size(); i
++)
509 if (ops
[i
].type
== FSOP_TEXT
)
510 uppercase(ops
[i
].text
);
513 int count_linebreaks(const formatted_string
& fs
)
515 std::string::size_type where
= 0;
516 const std::string s
= fs
;
520 where
= s
.find("\n", where
);
521 if (where
== std::string::npos
)
532 // Return the actual (string) offset of character #loc to be printed,
533 // i.e. ignoring tags. So for instance, if s == "<tag>ab</tag>", then
534 // _find_string_location(s, 2) == 6.
535 static int _find_string_location(const std::string
& s
, int loc
)
541 for (std::string::const_iterator ci
= s
.begin();
542 ci
!= s
.end() && real_offset
< loc
;
547 if (*ci
== '<' && last_taglen
== 1)
574 // Return the substring of s from character start to character end,
575 // where tags count as length 0.
576 std::string
tagged_string_substr(const std::string
& s
, int start
, int end
)
578 return (s
.substr(_find_string_location(s
, start
),
579 _find_string_location(s
, end
)));
582 int tagged_string_printable_length(const std::string
& s
)
587 for (std::string::const_iterator ci
= s
.begin(); ci
!= s
.end(); ++ci
)
591 if (*ci
== '<' && last_taglen
== 1) // "<<" sequence
593 in_tag
= false; // this is an escape for '<'
594 ++len
; // len wasn't incremented before
596 else if (*ci
== '>') // tag close, still nothing printed
600 else // tag continues
605 else if (*ci
== '<') // tag starts
610 else // normal, printable character
619 // Count the length of the tags in the string.
620 int tagged_string_tag_length(const std::string
& s
)
622 return s
.size() - tagged_string_printable_length(s
);