]> git.saurik.com Git - wxWidgets.git/blob - utils/ogl/src/misc.cpp
360740897553068c3b3c2c6f97498976ba13f15c
[wxWidgets.git] / utils / ogl / src / misc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: misc.cpp
3 // Purpose: Miscellaneous OGL support functions
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 12/07/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "misc.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include <wx/wxprec.h>
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include <wx/wx.h>
25 #endif
26
27 #ifdef PROLOGIO
28 #include <wx/wxexpr.h>
29 #endif
30
31 #include <wx/types.h>
32
33 #if USE_IOSTREAMH
34 #include <iostream.h>
35 #else
36 #include <iostream>
37 #endif
38 #include <ctype.h>
39 #include <math.h>
40 #include <stdlib.h>
41
42 #include "basic.h"
43 #include "basicp.h"
44 #include "misc.h"
45 #include "constrnt.h"
46 #include "composit.h"
47
48 wxFont *g_oglNormalFont;
49
50 wxPen *black_pen;
51 wxPen *white_background_pen;
52 wxPen *transparent_pen;
53 wxBrush *white_background_brush;
54 wxPen *black_foreground_pen;
55
56 char *GraphicsBuffer = NULL;
57 wxCursor *GraphicsBullseyeCursor = NULL;
58
59 wxList wxObjectCopyMapping(wxKEY_INTEGER);
60
61 void wxOGLInitialize()
62 {
63 GraphicsBullseyeCursor = new wxCursor(wxCURSOR_BULLSEYE);
64
65 g_oglNormalFont = new wxFont(12, wxMODERN, wxNORMAL, wxNORMAL);
66
67 black_pen = new wxPen("BLACK", 1, wxSOLID);
68
69 white_background_pen = new wxPen("WHITE", 1, wxSOLID);
70 transparent_pen = new wxPen("WHITE", 1, wxTRANSPARENT);
71 white_background_brush = new wxBrush("WHITE", wxSOLID);
72 black_foreground_pen = new wxPen("BLACK", 1, wxSOLID);
73
74 OGLInitializeConstraintTypes();
75
76 // Initialize big buffer used when writing images
77 GraphicsBuffer = new char[3000];
78
79 if (!oglPopupDivisionMenu)
80 {
81 oglPopupDivisionMenu = new wxMenu("", (wxFunction)oglGraphicsDivisionMenuProc);
82 oglPopupDivisionMenu->Append(DIVISION_MENU_SPLIT_HORIZONTALLY, "Split horizontally");
83 oglPopupDivisionMenu->Append(DIVISION_MENU_SPLIT_VERTICALLY, "Split vertically");
84 oglPopupDivisionMenu->AppendSeparator();
85 oglPopupDivisionMenu->Append(DIVISION_MENU_EDIT_LEFT_EDGE, "Edit left edge");
86 oglPopupDivisionMenu->Append(DIVISION_MENU_EDIT_TOP_EDGE, "Edit top edge");
87 }
88 }
89
90 void wxOGLCleanUp()
91 {
92 if (GraphicsBuffer)
93 {
94 delete[] GraphicsBuffer;
95 GraphicsBuffer = NULL;
96 }
97 GraphicsBuffer = NULL;
98 if (oglPopupDivisionMenu)
99 {
100 delete oglPopupDivisionMenu;
101 oglPopupDivisionMenu = NULL;
102 }
103 }
104
105 wxFont *MatchFont(int point_size)
106 {
107 wxFont *font = wxTheFontList->FindOrCreateFont(point_size, wxSWISS, wxNORMAL, wxNORMAL);
108 #if 0
109 switch (point_size)
110 {
111 case 4:
112 font = swiss_font_4;
113 break;
114 case 6:
115 font = swiss_font_6;
116 break;
117 case 8:
118 font = swiss_font_8;
119 break;
120 case 12:
121 font = swiss_font_12;
122 break;
123 case 14:
124 font = swiss_font_14;
125 break;
126 case 18:
127 font = swiss_font_18;
128 break;
129 case 24:
130 font = swiss_font_24;
131 break;
132 default:
133 case 10:
134 font = swiss_font_10;
135 break;
136 }
137 #endif
138 return font;
139 }
140
141 int FontSizeDialog(wxFrame *parent, int old_size)
142 {
143 if (old_size <= 0)
144 old_size = 10;
145 char buf[40];
146 sprintf(buf, "%d", old_size);
147 wxString ans = wxGetTextFromUser("Enter point size", "Font size", buf, parent);
148 if (ans == "")
149 return 0;
150
151 int new_size = atoi(ans);
152 if ((new_size <= 0) || (new_size > 40))
153 {
154 wxMessageBox("Invalid point size!", "Error", wxOK);
155 return 0;
156 }
157 return new_size;
158 /*
159 char *strings[8];
160 strings[0] = "4";
161 strings[1] = "6";
162 strings[2] = "8";
163 strings[3] = "10";
164 strings[4] = "12";
165 strings[5] = "14";
166 strings[6] = "18";
167 strings[7] = "24";
168 char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
169 if (ans)
170 {
171 int size;
172 sscanf(ans, "%d", &size);
173 return MatchFont(size);
174 }
175 else return NULL;
176 */
177 }
178
179 // Centre a list of strings in the given box. xOffset and yOffset are the
180 // the positions that these lines should be relative to, and this might be
181 // the same as m_xpos, m_ypos, but might be zero if formatting from left-justifying.
182 void CentreText(wxDC& dc, wxList *text_list,
183 float m_xpos, float m_ypos, float width, float height,
184 int formatMode)
185 {
186 int n = text_list->Number();
187
188 if (!text_list || (n == 0))
189 return;
190
191 // First, get maximum dimensions of box enclosing text
192
193 float char_height = 0;
194 float max_width = 0;
195 float current_width = 0;
196
197 // Store text extents for speed
198 float *widths = new float[n];
199
200 wxNode *current = text_list->First();
201 int i = 0;
202 while (current)
203 {
204 wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
205 dc.GetTextExtent(line->GetText(), &current_width, &char_height);
206 widths[i] = current_width;
207
208 if (current_width > max_width)
209 max_width = current_width;
210 current = current->Next();
211 i ++;
212 }
213
214 float max_height = n*char_height;
215
216 float xoffset, yoffset, xOffset, yOffset;
217
218 if (formatMode & FORMAT_CENTRE_VERT)
219 {
220 if (max_height < height)
221 yoffset = (float)(m_ypos - (height/2.0) + (height - max_height)/2.0);
222 else
223 yoffset = (float)(m_ypos - (height/2.0));
224 yOffset = m_ypos;
225 }
226 else
227 {
228 yoffset = 0.0;
229 yOffset = 0.0;
230 }
231
232 if (formatMode & FORMAT_CENTRE_HORIZ)
233 {
234 xoffset = (float)(m_xpos - width/2.0);
235 xOffset = m_xpos;
236 }
237 else
238 {
239 xoffset = 0.0;
240 xOffset = 0.0;
241 }
242
243 current = text_list->First();
244 i = 0;
245
246 while (current)
247 {
248 wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
249
250 float x;
251 if ((formatMode & FORMAT_CENTRE_HORIZ) && (widths[i] < width))
252 x = (float)((width - widths[i])/2.0 + xoffset);
253 else
254 x = xoffset;
255 float y = (float)(i*char_height + yoffset);
256
257 line->SetX( x - xOffset ); line->SetY( y - yOffset );
258 current = current->Next();
259 i ++;
260 }
261
262 delete widths;
263 }
264
265 // Centre a list of strings in the given box
266 void CentreTextNoClipping(wxDC& dc, wxList *text_list,
267 float m_xpos, float m_ypos, float width, float height)
268 {
269 int n = text_list->Number();
270
271 if (!text_list || (n == 0))
272 return;
273
274 // First, get maximum dimensions of box enclosing text
275
276 float char_height = 0;
277 float max_width = 0;
278 float current_width = 0;
279
280 // Store text extents for speed
281 float *widths = new float[n];
282
283 wxNode *current = text_list->First();
284 int i = 0;
285 while (current)
286 {
287 wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
288 dc.GetTextExtent(line->GetText(), &current_width, &char_height);
289 widths[i] = current_width;
290
291 if (current_width > max_width)
292 max_width = current_width;
293 current = current->Next();
294 i ++;
295 }
296
297 float max_height = n*char_height;
298
299 float yoffset = (float)(m_ypos - (height/2.0) + (height - max_height)/2.0);
300
301 float xoffset = (float)(m_xpos - width/2.0);
302
303 current = text_list->First();
304 i = 0;
305
306 while (current)
307 {
308 wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
309
310 float x = (float)((width - widths[i])/2.0 + xoffset);
311 float y = (float)(i*char_height + yoffset);
312
313 line->SetX( x - m_xpos ); line->SetY( y - m_ypos );
314 current = current->Next();
315 i ++;
316 }
317 delete widths;
318 }
319
320 void GetCentredTextExtent(wxDC& dc, wxList *text_list,
321 float m_xpos, float m_ypos, float width, float height,
322 float *actual_width, float *actual_height)
323 {
324 int n = text_list->Number();
325
326 if (!text_list || (n == 0))
327 {
328 *actual_width = 0;
329 *actual_height = 0;
330 return;
331 }
332
333 // First, get maximum dimensions of box enclosing text
334
335 float char_height = 0;
336 float max_width = 0;
337 float current_width = 0;
338
339 wxNode *current = text_list->First();
340 int i = 0;
341 while (current)
342 {
343 wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
344 dc.GetTextExtent(line->GetText(), &current_width, &char_height);
345
346 if (current_width > max_width)
347 max_width = current_width;
348 current = current->Next();
349 i ++;
350 }
351
352 *actual_height = n*char_height;
353 *actual_width = max_width;
354 }
355
356 // Format a string to a list of strings that fit in the given box.
357 // Interpret %n and 10 or 13 as a new line.
358 wxList *FormatText(wxDC& dc, const wxString& text, float width, float height, int formatMode)
359 {
360 // First, parse the string into a list of words
361 wxList word_list;
362
363 // Make new lines into NULL strings at this point
364 int i = 0; int j = 0; int len = strlen(text);
365 char word[200]; word[0] = 0;
366 bool end_word = FALSE; bool new_line = FALSE;
367 while (i < len)
368 {
369 switch (text[i])
370 {
371 case '%':
372 {
373 i ++;
374 if (i == len)
375 { word[j] = '%'; j ++; }
376 else
377 {
378 if (text[i] == 'n')
379 { new_line = TRUE; end_word = TRUE; i++; }
380 else
381 { word[j] = '%'; j ++; word[j] = text[i]; j ++; i ++; }
382 }
383 break;
384 }
385 case 10:
386 {
387 new_line = TRUE; end_word = TRUE; i++;
388 break;
389 }
390 case 13:
391 {
392 new_line = TRUE; end_word = TRUE; i++;
393 }
394 case ' ':
395 {
396 end_word = TRUE;
397 i ++;
398 break;
399 }
400 default:
401 {
402 word[j] = text[i];
403 j ++; i ++;
404 break;
405 }
406 }
407 if (i == len) end_word = TRUE;
408 if (end_word)
409 {
410 word[j] = 0;
411 j = 0;
412 word_list.Append((wxObject *)copystring(word));
413 end_word = FALSE;
414 }
415 if (new_line)
416 {
417 word_list.Append((wxObject *)NULL);
418 new_line = FALSE;
419 }
420 }
421 // Now, make a list of strings which can fit in the box
422 wxList *string_list = new wxList;
423
424 char buffer[400];
425 buffer[0] = 0;
426 wxNode *node = word_list.First();
427 float x, y;
428
429 while (node)
430 {
431 char *keep_string = copystring(buffer);
432
433 char *s = (char *)node->Data();
434 if (!s)
435 {
436 // FORCE NEW LINE
437 if (strlen(keep_string) > 0)
438 string_list->Append((wxObject *)keep_string);
439 else
440 delete[] keep_string;
441
442 buffer[0] = 0;
443 }
444 else
445 {
446 if (buffer[0] != 0)
447 strcat(buffer, " ");
448
449 strcat(buffer, s);
450 dc.GetTextExtent(buffer, &x, &y);
451
452 // Don't fit within the bounding box if we're fitting shape to contents
453 if ((x > width) && !(formatMode & FORMAT_SIZE_TO_CONTENTS))
454 {
455 // Deal with first word being wider than box
456 if (strlen(keep_string) > 0)
457 string_list->Append((wxObject *)keep_string);
458 else
459 delete[] keep_string;
460
461 buffer[0] = 0;
462 strcat(buffer, s);
463 delete[] s;
464 }
465 else
466 delete[] keep_string;
467 }
468
469 node = node->Next();
470 }
471 if (buffer[0] != 0)
472 string_list->Append((wxObject *)copystring(buffer));
473
474 return string_list;
475 }
476
477 void DrawFormattedText(wxDC& dc, wxList *text_list,
478 float m_xpos, float m_ypos, float width, float height,
479 int formatMode)
480 {
481 float xoffset, yoffset;
482 if (formatMode & FORMAT_CENTRE_HORIZ)
483 xoffset = m_xpos;
484 else
485 xoffset = (float)(m_xpos - (width / 2.0));
486
487 if (formatMode & FORMAT_CENTRE_VERT)
488 yoffset = m_ypos;
489 else
490 yoffset = (float)(m_ypos - (height / 2.0));
491
492 dc.SetClippingRegion(
493 (float)(m_xpos - width/2.0), (float)(m_ypos - height/2.0),
494 (float)width, (float)height);
495
496 wxNode *current = text_list->First();
497 while (current)
498 {
499 wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
500
501 dc.DrawText(line->GetText(), xoffset + line->GetX(), yoffset + line->GetY());
502 current = current->Next();
503 }
504
505 dc.DestroyClippingRegion();
506 }
507
508 /*
509 * Find centroid given list of points comprising polyline
510 *
511 */
512
513 void find_polyline_centroid(wxList *points, float *x, float *y)
514 {
515 float xcount = 0;
516 float ycount = 0;
517
518 wxNode *node = points->First();
519 while (node)
520 {
521 wxRealPoint *point = (wxRealPoint *)node->Data();
522 xcount += point->x;
523 ycount += point->y;
524 node = node->Next();
525 }
526
527 *x = (xcount/points->Number());
528 *y = (ycount/points->Number());
529 }
530
531 /*
532 * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
533 * If so, ratio1 gives the proportion along the first line
534 * that the intersection occurs (or something like that).
535 * Used by functions below.
536 *
537 */
538 void check_line_intersection(float x1, float y1, float x2, float y2,
539 float x3, float y3, float x4, float y4,
540 float *ratio1, float *ratio2)
541 {
542 float denominator_term = (y4 - y3)*(x2 - x1) - (y2 - y1)*(x4 - x3);
543 float numerator_term = (x3 - x1)*(y4 - y3) + (x4 - x3)*(y1 - y3);
544
545 float line_constant;
546 float length_ratio = 1.0;
547 float k_line = 1.0;
548
549 // Check for parallel lines
550 if ((denominator_term < 0.005) && (denominator_term > -0.005))
551 line_constant = -1.0;
552 else
553 line_constant = numerator_term/denominator_term;
554
555 // Check for intersection
556 if ((line_constant < 1.0) && (line_constant > 0.0))
557 {
558 // Now must check that other line hits
559 if (((y4 - y3) < 0.005) && ((y4 - y3) > -0.005))
560 k_line = ((x1 - x3) + line_constant*(x2 - x1))/(x4 - x3);
561 else
562 k_line = ((y1 - y3) + line_constant*(y2 - y1))/(y4 - y3);
563
564 if ((k_line >= 0.0) && (k_line < 1.0))
565 length_ratio = line_constant;
566 else
567 k_line = 1.0;
568 }
569 *ratio1 = length_ratio;
570 *ratio2 = k_line;
571 }
572
573 /*
574 * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
575 * (*x3, *y3) is the point where it hits.
576 *
577 */
578 void find_end_for_polyline(float n, float xvec[], float yvec[],
579 float x1, float y1, float x2, float y2, float *x3, float *y3)
580 {
581 int i;
582 float lastx = xvec[0];
583 float lasty = yvec[0];
584
585 float min_ratio = 1.0;
586 float line_ratio;
587 float other_ratio;
588
589 for (i = 1; i < n; i++)
590 {
591 check_line_intersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i],
592 &line_ratio, &other_ratio);
593 lastx = xvec[i];
594 lasty = yvec[i];
595
596 if (line_ratio < min_ratio)
597 min_ratio = line_ratio;
598 }
599
600 // Do last (implicit) line if last and first floats are not identical
601 if (!(xvec[0] == lastx && yvec[0] == lasty))
602 {
603 check_line_intersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0],
604 &line_ratio, &other_ratio);
605
606 if (line_ratio < min_ratio)
607 min_ratio = line_ratio;
608 }
609
610 *x3 = (x1 + (x2 - x1)*min_ratio);
611 *y3 = (y1 + (y2 - y1)*min_ratio);
612
613 }
614
615 /*
616 * Find where the line hits the box.
617 *
618 */
619
620 void find_end_for_box(float width, float height,
621 float x1, float y1, // Centre of box (possibly)
622 float x2, float y2, // other end of line
623 float *x3, float *y3) // End on box edge
624 {
625 float xvec[5];
626 float yvec[5];
627
628 xvec[0] = (float)(x1 - width/2.0);
629 yvec[0] = (float)(y1 - height/2.0);
630 xvec[1] = (float)(x1 - width/2.0);
631 yvec[1] = (float)(y1 + height/2.0);
632 xvec[2] = (float)(x1 + width/2.0);
633 yvec[2] = (float)(y1 + height/2.0);
634 xvec[3] = (float)(x1 + width/2.0);
635 yvec[3] = (float)(y1 - height/2.0);
636 xvec[4] = (float)(x1 - width/2.0);
637 yvec[4] = (float)(y1 - height/2.0);
638
639 find_end_for_polyline(5, xvec, yvec, x2, y2, x1, y1, x3, y3);
640 }
641
642 /*
643 * Find where the line hits the circle.
644 *
645 */
646
647 void find_end_for_circle(float radius,
648 float x1, float y1, // Centre of circle
649 float x2, float y2, // Other end of line
650 float *x3, float *y3)
651 {
652 float H = (float)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
653
654 if (H == 0.0)
655 {
656 *x3 = x1;
657 *y3 = y1;
658 }
659 else
660 {
661 *y3 = radius * (y2 - y1)/H + y1;
662 *x3 = radius * (x2 - x1)/H + x1;
663 }
664 }
665
666 /*
667 * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
668 * return the position of the tip of the arrow and the left and right vertices of the arrow.
669 *
670 */
671
672 void get_arrow_points(float x1, float y1, float x2, float y2,
673 float length, float width,
674 float *tip_x, float *tip_y,
675 float *side1_x, float *side1_y,
676 float *side2_x, float *side2_y)
677 {
678 float l = (float)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
679
680 if (l < 0.01)
681 l = (float) 0.01;
682
683 float i_bar = (x2 - x1)/l;
684 float j_bar = (y2 - y1)/l;
685
686 float x3 = (- length*i_bar) + x2;
687 float y3 = (- length*j_bar) + y2;
688
689 *side1_x = width*(-j_bar) + x3;
690 *side1_y = width*i_bar + y3;
691
692 *side2_x = -width*(-j_bar) + x3;
693 *side2_y = -width*i_bar + y3;
694
695 *tip_x = x2; *tip_y = y2;
696 }
697
698 /*
699 * Given an ellipse and endpoints of a line, returns the point at which
700 * the line touches the ellipse in values x4, y4.
701 * This function assumes that the centre of the ellipse is at x1, y1, and the
702 * ellipse has a width of width1 and a height of height1. It also assumes you are
703 * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
704 * This function calculates the x,y coordinates of the intersection point of
705 * the arc with the ellipse.
706 * Author: Ian Harrison
707 */
708
709 void draw_arc_to_ellipse(float x1, float y1, float width1, float height1, float x2, float y2, float x3, float y3,
710 float *x4, float *y4)
711 {
712 float a1 = (float)(width1/2.0);
713 float b1 = (float)(height1/2.0);
714
715 // These are required to give top left x and y coordinates for DrawEllipse
716 // float top_left_x1 = (float)(x1 - a1);
717 // float top_left_y1 = (float)(y1 - b1);
718 /*
719 // Check for vertical line
720 if (fabs(x2 - x3) < 0.05)
721 {
722 *x4 = x3;
723 if (y2 < y3)
724 *y4 = (float)(y1 - b1);
725 else
726 *y4 = (float)(y1 + b1);
727 return;
728 }
729 */
730 // Check that x2 != x3
731 if (fabs(x2 - x3) < 0.05)
732 {
733 *x4 = x2;
734 if (y3 > y2)
735 *y4 = (float)(y1 - sqrt((b1*b1 - (((x2-x1)*(x2-x1))*(b1*b1)/(a1*a1)))));
736 else
737 *y4 = (float)(y1 + sqrt((b1*b1 - (((x2-x1)*(x2-x1))*(b1*b1)/(a1*a1)))));
738 return;
739 }
740
741 // Calculate the x and y coordinates of the point where arc intersects ellipse
742
743 float A, B, C, D, E, F, G, H, K;
744 float ellipse1_x, ellipse1_y;
745
746 A = (float)(1/(a1 * a1));
747 B = (float)((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1);
748 C = (float)(2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1);
749 D = (float)((y2 - y1) * (y2 - y1)) / (b1 * b1);
750 E = (float)(A + B);
751 F = (float)(C - (2 * A * x1) - (2 * B * x2));
752 G = (float)((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1);
753 H = (float)((y3 - y2) / (x3 - x2));
754 K = (float)((F * F) - (4 * E * G));
755
756 if (K >= 0)
757 // In this case the line intersects the ellipse, so calculate intersection
758 {
759 if(x2 >= x1)
760 {
761 ellipse1_x = (float)(((F * -1) + sqrt(K)) / (2 * E));
762 ellipse1_y = (float)((H * (ellipse1_x - x2)) + y2);
763 }
764 else
765 {
766 ellipse1_x = (float)(((F * -1) - sqrt(K)) / (2 * E));
767 ellipse1_y = (float)((H * (ellipse1_x - x2)) + y2);
768 }
769 }
770 else
771 // in this case, arc does not intersect ellipse, so just draw arc
772 {
773 ellipse1_x = x3;
774 ellipse1_y = y3;
775 }
776 *x4 = ellipse1_x;
777 *y4 = ellipse1_y;
778
779 /*
780 // Draw a little circle (radius = 2) at the end of the arc where it hits
781 // the ellipse .
782
783 float circle_x = ellipse1_x - 2.0;
784 float circle_y = ellipse1_y - 2.0;
785 m_canvas->DrawEllipse(circle_x, circle_y, 4.0, 4.0);
786 */
787 }
788
789 // Update a list item from a list of strings
790 void UpdateListBox(wxListBox *item, wxList *list)
791 {
792 item->Clear();
793 if (!list)
794 return;
795
796 wxNode *node = list->First();
797 while (node)
798 {
799 char *s = (char *)node->Data();
800 item->Append(s);
801 node = node->Next();
802 }
803 }
804
805