Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
RichTextBox.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: RichTextBox.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Window class
13 */
14 
15 #include "MemDebug.h"
16 #include "RichTextBox.h"
17 #include "EventDispatch.h"
18 #include "Color.h"
19 #include "Bitmap.h"
20 #include "Font.h"
21 #include "FontMgr.h"
22 #include "Mouse.h"
23 #include "Screen.h"
24 #include "View.h"
25 
26 // +--------------------------------------------------------------------+
27 
28 RichTextBox::RichTextBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD astyle)
29 : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, astyle, p)
30 {
31  leading = 2;
32 
33  char buf[32];
34  sprintf_s(buf, "RichTextBox %d", id);
35  desc = buf;
36 }
37 
38 RichTextBox::RichTextBox(Screen* screen, int ax, int ay, int aw, int ah, DWORD aid, DWORD astyle)
39 : ScrollWindow(screen, ax, ay, aw, ah, aid, astyle)
40 {
41  leading = 2;
42 
43  char buf[32];
44  sprintf_s(buf, "RichTextBox %d", id);
45  desc = buf;
46 }
47 
49 {
50 }
51 
52 // +--------------------------------------------------------------------+
53 
54 void
55 RichTextBox::SetText(const char* t)
56 {
58  ScrollTo(0);
59 }
60 
61 // +--------------------------------------------------------------------+
62 
63 void
64 RichTextBox::DrawContent(const Rect& ctrl_rect)
65 {
67 }
68 
69 // +--------------------------------------------------------------------+
70 
71 void
73 {
74  if (font && text.length()) {
75  int border_size = 4;
76 
78  border_size = 8;
79 
80  Rect label_rect = rect;
81 
82  label_rect.x = border_size;
83  label_rect.y = border_size;
84  label_rect.w -= border_size * 2;
85  label_rect.h -= border_size * 2;
86 
87  if (scroll_bar)
88  label_rect.w -= SCROLL_TRACK;
89 
90  if (line_height < font->Height())
91  line_height = font->Height();
92 
94  DrawRichText(label_rect);
95  }
96 }
97 
98 // +--------------------------------------------------------------------+
99 
100 int RichTextBox::find_next_word_start(const char* text, int index)
101 {
102  // step through intra-word space:
103  while (text[index] && isspace(text[index]) &&
104  (text[index] != '\t') &&
105  (text[index] != '\n') &&
106  (text[index] != '<'))
107  index++;
108 
109  return index;
110 }
111 
112 int RichTextBox::find_next_word_end(const char* text, int index)
113 {
114  // check for leading newline or tag:
115  if (text[index] == '\n' || text[index] == '\t' || text[index] == '<')
116  return index;
117 
118  // step through intra-word space:
119  while (text[index] && isspace(text[index]))
120  index++;
121 
122  // step through word:
123  while (text[index] && !isspace(text[index]) &&
124  (text[index] != '-') &&
125  (text[index] != '<'))
126  index++;
127 
128  if (index) {
129  if (text[index] != '-')
130  return index-1;
131  else
132  return index;
133  }
134 
135  return 0;
136 }
137 
139 {
140  if (isalpha(c))
141  return 10 + tolower(c) - 'a';
142 
143  else if (isdigit(c))
144  return c - '0';
145 
146  return 0;
147 }
148 
149 int RichTextBox::process_tag(const char* text, int index, Font*& font)
150 {
151  if (text[index] == '<') {
152  char tag[64];
153  int i = 0;
154 
155  while (text[index] && (text[index] != '>'))
156  tag[i++] = text[index++];
157 
158  if (text[index] == '>')
159  tag[i++] = text[index++];
160 
161  tag[i] = 0;
162 
163  switch (tag[1]) {
164  case 'c':
165  case 'C': if (_strnicmp(tag+1, "color", 5) == 0) {
166  int r = 0;
167  int g = 0;
168  int b = 0;
169 
170  if (i > 12) {
171  r = 16 * parse_hex_digit(tag[ 7]) + parse_hex_digit(tag[ 8]);
172  g = 16 * parse_hex_digit(tag[ 9]) + parse_hex_digit(tag[10]);
173  b = 16 * parse_hex_digit(tag[11]) + parse_hex_digit(tag[12]);
174  }
175 
176  font->SetColor(Color(r,g,b));
177  }
178  break;
179 
180  case 'f':
181  case 'F': if (_strnicmp(tag+1, "font", 4) == 0) {
182  Color current_color = Color::White;
183 
184  if (font)
185  current_color = font->GetColor();
186 
187  tag[i-1] = 0;
188  font = FontMgr::Find(tag+6);
189  font->SetColor(current_color);
190  }
191  break;
192  }
193  }
194 
195  return index;
196 }
197 
198 int
200 {
201  for (int i = 0; i < 10; i++) {
202  if (tab[i] > xpos)
203  return tab[i];
204  }
205 
206  return (xpos / 20) * 20 + 20;
207 }
208 
209 void
211 {
212  // clip the rect:
213  Rect clip_rect = ClipRect(text_rect);
214  clip_rect.h -= 8;
215 
216  if (clip_rect.w < 1 || clip_rect.h < 1)
217  return;
218 
219  const char* t = text.data();
220  int count = text.length();
221  int nlines = 0;
222 
223  int xpos = 0;
224  int block_start = 0;
225  int block_count = 0;
226  int curr_word_end = -1;
227  int next_word_end = 0;
228  int eol_index = 0;
229 
230  int new_line = 0;
231  int x_offset = 0;
232  int y_offset = 0;
233  int length = 0;
234 
235  Font* rich_font = font;
236  rich_font->SetColor(fore_color);
237 
238  if (smooth_scroll) {
239  double fraction = smooth_offset - (int) smooth_offset;
240 
241  y_offset = (int) ((1-fraction) * (rich_font->Height() + leading));
242  }
243 
244  // while there is still text:
245  while (block_start < count) {
246  bool found_tag = false;
247 
248  do {
249  found_tag = false;
250 
251  if (t[block_start] == '<') {
252  block_start = process_tag(t, block_start, rich_font);
253  found_tag = true;
254  }
255 
256  else if (t[block_start] == '\t') {
257  block_start++;
258  x_offset = GetNextTab(x_offset);
259 
260  if (x_offset > text_rect.w) {
261  nlines++;
262  if (nlines > top_index)
263  y_offset += rich_font->Height() + leading;
264  x_offset = 0;
265  new_line = false;
266  }
267 
268  found_tag = true;
269  }
270 
271  else if (t[block_start] == '\r') {
272  block_start++;
273 
274  if (t[block_start] == '\n')
275  block_start++;
276 
277  nlines++;
278  if (nlines > top_index)
279  y_offset += rich_font->Height() + leading;
280  x_offset = 0;
281  new_line = false;
282 
283  found_tag = true;
284  }
285 
286  else if (t[block_start] == '\n') {
287  block_start++;
288 
289  if (t[block_start] == '\r')
290  block_start++;
291 
292  nlines++;
293  if (nlines > top_index)
294  y_offset += rich_font->Height() + leading;
295  x_offset = 0;
296  new_line = false;
297 
298  found_tag = true;
299  }
300  }
301  while (found_tag);
302 
303  next_word_end = find_next_word_end(t, block_start);
304 
305  if (!next_word_end || next_word_end == curr_word_end) {
306  new_line = true;
307  }
308 
309  else if (t[next_word_end] == '\n') {
310  eol_index = curr_word_end = next_word_end;
311  new_line = true;
312  }
313 
314  else {
315  int word_len = next_word_end - block_start + 1;
316 
317  length = rich_font->StringWidth(t+block_start, word_len);
318 
319  // if this word was too long, wrap to next line:
320  if (x_offset + length > text_rect.w) {
321  nlines++;
322  if (nlines > top_index)
323  y_offset += rich_font->Height() + leading;
324  x_offset = 0;
325  new_line = false;
326  }
327 
328  // is there a trailing newline?
329  curr_word_end = next_word_end;
330 
331  // check for a newline in the next block of white space:
332  eol_index = 0;
333  const char* eol = &t[curr_word_end+1];
334  while (*eol && isspace(*eol) && *eol != '\n')
335  eol++;
336 
337  if (*eol == '\n') {
338  eol_index = eol - t;
339  new_line = true;
340  }
341  }
342 
343  block_count = curr_word_end - block_start + 1;
344 
345  if (block_count > 0) {
346  length = rich_font->StringWidth(t+block_start, block_count);
347  }
348 
349  // there was a single word longer than the entire line:
350  else {
351  block_count = next_word_end - block_start + 1;
352  length = rich_font->StringWidth(t+block_start, block_count);
353  curr_word_end = next_word_end;
354  }
355 
356  if (length > 0 && nlines >= top_index && nlines < top_index+page_size) {
357  int x1 = text_rect.x + x_offset + rect.x;
358  int y1 = text_rect.y + y_offset + rect.y;
359 
360  rich_font->DrawString(t+block_start, block_count, x1, y1, clip_rect);
361  }
362 
363  if (new_line) {
364  nlines++;
365  if (nlines > top_index)
366  y_offset += rich_font->Height() + leading;
367  x_offset = 0;
368  new_line = false;
369  }
370 
371  else if (length < 1 || text[next_word_end] == '-') {
372  x_offset += length;
373  }
374 
375  else {
376  x_offset += length + rich_font->SpaceWidth();
377  }
378 
379  if (eol_index > 0)
380  curr_word_end = eol_index;
381 
382  block_start = find_next_word_start(t, curr_word_end+1);
383  }
384 
385  line_count = nlines;
386 }
387 
388 // +--------------------------------------------------------------------+
389 
390 int RichTextBox::OnMouseMove(int x, int y)
391 {
392  if (captured) {
393  ActiveWindow* test = GetCapture();
394 
395  if (test != this) {
396  captured = false;
397  }
398 
399  else {
400  if (scrolling == SCROLL_THUMB) {
401  mouse_y = y - rect.y - TRACK_START;
402 
403  int dest = (int) ((double) mouse_y/track_length * (line_count-1));
404  ScrollTo(dest);
405  }
406  }
407  }
408 
409  return ActiveWindow::OnMouseMove(x,y);
410 }
411 
412 // +--------------------------------------------------------------------+
413 
414 int RichTextBox::OnLButtonDown(int x, int y)
415 {
416  return ScrollWindow::OnLButtonDown(x,y);
417 }
418 
419 // +--------------------------------------------------------------------+
420 
421 int RichTextBox::OnLButtonUp(int x, int y)
422 {
423  return ScrollWindow::OnLButtonUp(x,y);
424 }
425 
426 // +--------------------------------------------------------------------+
427 
429 {
430  return ScrollWindow::OnMouseWheel(wheel);
431 }
432 
433 // +--------------------------------------------------------------------+
434 
436 {
437  int fire_click = !scrolling;
438 
439  if (scrolling == SCROLL_THUMB)
441 
442  if (fire_click)
443  return ActiveWindow::OnClick();
444 
445  return 0;
446 }
447 
448 // +--------------------------------------------------------------------+
449 
450 int RichTextBox::OnKeyDown(int vk, int flags)
451 {
452  return ScrollWindow::OnKeyDown(vk, flags);
453 }
454