]> git.saurik.com Git - wxWidgets.git/blame - utils/ogl/src/constrnt.cpp
no message
[wxWidgets.git] / utils / ogl / src / constrnt.cpp
CommitLineData
0fc1a713
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: constrnt.cpp
3// Purpose: OGL Constraint classes
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 "constrnt.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 "basic.h"
32#include "constrnt.h"
33#include "canvas.h"
34
6f5f3ca0 35wxList *wxOGLConstraintTypes = NULL;
0fc1a713
JS
36
37/*
38 * Constraint type
39 *
40 */
41
6f5f3ca0 42IMPLEMENT_DYNAMIC_CLASS(wxOGLConstraintType, wxObject)
0fc1a713 43
6f5f3ca0 44wxOGLConstraintType::wxOGLConstraintType(int theType, const wxString& theName, const wxString& thePhrase)
0fc1a713
JS
45{
46 m_type = theType;
47 m_name = theName;
48 m_phrase = thePhrase;
49}
50
6f5f3ca0 51wxOGLConstraintType::~wxOGLConstraintType()
0fc1a713
JS
52{
53}
54
55void OGLInitializeConstraintTypes()
56{
6f5f3ca0 57 if (!wxOGLConstraintTypes)
5de76427 58 return;
0fc1a713 59
6f5f3ca0 60 wxOGLConstraintTypes = new wxList(wxKEY_INTEGER);
0fc1a713 61
6f5f3ca0
JS
62 wxOGLConstraintTypes->Append(gyCONSTRAINT_CENTRED_VERTICALLY,
63 new wxOGLConstraintType(gyCONSTRAINT_CENTRED_VERTICALLY, "Centre vertically", "centred vertically w.r.t."));
0fc1a713 64
6f5f3ca0
JS
65 wxOGLConstraintTypes->Append(gyCONSTRAINT_CENTRED_HORIZONTALLY,
66 new wxOGLConstraintType(gyCONSTRAINT_CENTRED_HORIZONTALLY, "Centre horizontally", "centred horizontally w.r.t."));
0fc1a713 67
6f5f3ca0
JS
68 wxOGLConstraintTypes->Append(gyCONSTRAINT_CENTRED_BOTH,
69 new wxOGLConstraintType(gyCONSTRAINT_CENTRED_BOTH, "Centre", "centred w.r.t."));
0fc1a713 70
6f5f3ca0
JS
71 wxOGLConstraintTypes->Append(gyCONSTRAINT_LEFT_OF,
72 new wxOGLConstraintType(gyCONSTRAINT_LEFT_OF, "Left of", "left of"));
0fc1a713 73
6f5f3ca0
JS
74 wxOGLConstraintTypes->Append(gyCONSTRAINT_RIGHT_OF,
75 new wxOGLConstraintType(gyCONSTRAINT_RIGHT_OF, "Right of", "right of"));
0fc1a713 76
6f5f3ca0
JS
77 wxOGLConstraintTypes->Append(gyCONSTRAINT_ABOVE,
78 new wxOGLConstraintType(gyCONSTRAINT_ABOVE, "Above", "above"));
0fc1a713 79
6f5f3ca0
JS
80 wxOGLConstraintTypes->Append(gyCONSTRAINT_BELOW,
81 new wxOGLConstraintType(gyCONSTRAINT_BELOW, "Below", "below"));
0fc1a713 82
5de76427 83 // Alignment
6f5f3ca0
JS
84 wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_TOP,
85 new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_TOP, "Top-aligned", "aligned to the top of"));
0fc1a713 86
6f5f3ca0
JS
87 wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_BOTTOM,
88 new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_BOTTOM, "Bottom-aligned", "aligned to the bottom of"));
0fc1a713 89
6f5f3ca0
JS
90 wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_LEFT,
91 new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_LEFT, "Left-aligned", "aligned to the left of"));
0fc1a713 92
6f5f3ca0
JS
93 wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_RIGHT,
94 new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_RIGHT, "Right-aligned", "aligned to the right of"));
0fc1a713 95
5de76427 96 // Mid-alignment
6f5f3ca0
JS
97 wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_TOP,
98 new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_TOP, "Top-midaligned", "centred on the top of"));
0fc1a713 99
6f5f3ca0
JS
100 wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_BOTTOM,
101 new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_BOTTOM, "Bottom-midaligned", "centred on the bottom of"));
5de76427 102
6f5f3ca0
JS
103 wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_LEFT,
104 new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_LEFT, "Left-midaligned", "centred on the left of"));
5de76427 105
6f5f3ca0
JS
106 wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_RIGHT,
107 new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_RIGHT, "Right-midaligned", "centred on the right of"));
5de76427
JS
108}
109
110void OGLCleanUpConstraintTypes()
111{
6f5f3ca0 112 if (!wxOGLConstraintTypes)
5de76427
JS
113 return;
114
6f5f3ca0 115 wxNode* node = wxOGLConstraintTypes->First();
5de76427
JS
116 while (node)
117 {
6f5f3ca0 118 wxOGLConstraintType* ct = (wxOGLConstraintType*) node->Data();
5de76427
JS
119 delete ct;
120 node = node->Next();
121 }
6f5f3ca0
JS
122 delete wxOGLConstraintTypes;
123 wxOGLConstraintTypes = NULL;
0fc1a713
JS
124}
125
126/*
127 * Constraint Stuff
128 *
129 */
130
6f5f3ca0 131IMPLEMENT_DYNAMIC_CLASS(wxOGLConstraint, wxObject)
0fc1a713 132
6f5f3ca0 133wxOGLConstraint::wxOGLConstraint(int type, wxShape *constraining, wxList& constrained)
0fc1a713
JS
134{
135 m_xSpacing = 0.0;
136 m_ySpacing = 0.0;
137
138 m_constraintType = type;
139 m_constrainingObject = constraining;
140
141 m_constraintId = 0;
142 m_constraintName = "noname";
143
144 wxNode *node = constrained.First();
145 while (node)
146 {
147 m_constrainedObjects.Append(node->Data());
148 node = node->Next();
149 }
150}
151
6f5f3ca0 152wxOGLConstraint::~wxOGLConstraint()
0fc1a713
JS
153{
154}
155
6f5f3ca0 156bool wxOGLConstraint::Equals(double a, double b)
0fc1a713 157{
42cfaf8c 158 double marg = 0.5;
0fc1a713
JS
159
160 bool eq = ((b <= a + marg) && (b >= a - marg));
161 return eq;
162}
163
164// Return TRUE if anything changed
6f5f3ca0 165bool wxOGLConstraint::Evaluate()
0fc1a713 166{
42cfaf8c 167 double maxWidth, maxHeight, minWidth, minHeight, x, y;
0fc1a713
JS
168 m_constrainingObject->GetBoundingBoxMax(&maxWidth, &maxHeight);
169 m_constrainingObject->GetBoundingBoxMin(&minWidth, &minHeight);
170 x = m_constrainingObject->GetX();
171 y = m_constrainingObject->GetY();
172
173 wxClientDC dc(m_constrainingObject->GetCanvas());
174 m_constrainingObject->GetCanvas()->PrepareDC(dc);
175
176 switch (m_constraintType)
177 {
178 case gyCONSTRAINT_CENTRED_VERTICALLY:
179 {
180 int n = m_constrainedObjects.Number();
42cfaf8c 181 double totalObjectHeight = 0.0;
0fc1a713
JS
182 wxNode *node = m_constrainedObjects.First();
183 while (node)
184 {
185 wxShape *constrainedObject = (wxShape *)node->Data();
186
42cfaf8c 187 double width2, height2;
0fc1a713
JS
188 constrainedObject->GetBoundingBoxMax(&width2, &height2);
189 totalObjectHeight += height2;
190 node = node->Next();
191 }
42cfaf8c
JS
192 double startY;
193 double spacingY;
0fc1a713
JS
194 // Check if within the constraining object...
195 if ((totalObjectHeight + (n + 1)*m_ySpacing) <= minHeight)
196 {
42cfaf8c
JS
197 spacingY = (double)((minHeight - totalObjectHeight)/(n + 1));
198 startY = (double)(y - (minHeight/2.0));
0fc1a713
JS
199 }
200 // Otherwise, use default spacing
201 else
202 {
203 spacingY = m_ySpacing;
42cfaf8c 204 startY = (double)(y - ((totalObjectHeight + (n+1)*spacingY)/2.0));
0fc1a713
JS
205 }
206
207 // Now position the objects
208 bool changed = FALSE;
209 node = m_constrainedObjects.First();
210 while (node)
211 {
212 wxShape *constrainedObject = (wxShape *)node->Data();
42cfaf8c 213 double width2, height2;
0fc1a713 214 constrainedObject->GetBoundingBoxMax(&width2, &height2);
42cfaf8c 215 startY += (double)(spacingY + (height2/2.0));
0fc1a713
JS
216 if (!Equals(startY, constrainedObject->GetY()))
217 {
218 constrainedObject->Move(dc, constrainedObject->GetX(), startY, FALSE);
219 changed = TRUE;
220 }
42cfaf8c 221 startY += (double)(height2/2.0);
0fc1a713
JS
222 node = node->Next();
223 }
224 return changed;
225 }
226 case gyCONSTRAINT_CENTRED_HORIZONTALLY:
227 {
228 int n = m_constrainedObjects.Number();
42cfaf8c 229 double totalObjectWidth = 0.0;
0fc1a713
JS
230 wxNode *node = m_constrainedObjects.First();
231 while (node)
232 {
233 wxShape *constrainedObject = (wxShape *)node->Data();
234
42cfaf8c 235 double width2, height2;
0fc1a713
JS
236 constrainedObject->GetBoundingBoxMax(&width2, &height2);
237 totalObjectWidth += width2;
238 node = node->Next();
239 }
42cfaf8c
JS
240 double startX;
241 double spacingX;
0fc1a713
JS
242 // Check if within the constraining object...
243 if ((totalObjectWidth + (n + 1)*m_xSpacing) <= minWidth)
244 {
42cfaf8c
JS
245 spacingX = (double)((minWidth - totalObjectWidth)/(n + 1));
246 startX = (double)(x - (minWidth/2.0));
0fc1a713
JS
247 }
248 // Otherwise, use default spacing
249 else
250 {
251 spacingX = m_xSpacing;
42cfaf8c 252 startX = (double)(x - ((totalObjectWidth + (n+1)*spacingX)/2.0));
0fc1a713
JS
253 }
254
255 // Now position the objects
256 bool changed = FALSE;
257 node = m_constrainedObjects.First();
258 while (node)
259 {
260 wxShape *constrainedObject = (wxShape *)node->Data();
42cfaf8c 261 double width2, height2;
0fc1a713 262 constrainedObject->GetBoundingBoxMax(&width2, &height2);
42cfaf8c 263 startX += (double)(spacingX + (width2/2.0));
0fc1a713
JS
264 if (!Equals(startX, constrainedObject->GetX()))
265 {
266 constrainedObject->Move(dc, startX, constrainedObject->GetY(), FALSE);
267 changed = TRUE;
268 }
42cfaf8c 269 startX += (double)(width2/2.0);
0fc1a713
JS
270 node = node->Next();
271 }
272 return changed;
273 }
274 case gyCONSTRAINT_CENTRED_BOTH:
275 {
276 int n = m_constrainedObjects.Number();
42cfaf8c
JS
277 double totalObjectWidth = 0.0;
278 double totalObjectHeight = 0.0;
0fc1a713
JS
279 wxNode *node = m_constrainedObjects.First();
280 while (node)
281 {
282 wxShape *constrainedObject = (wxShape *)node->Data();
283
42cfaf8c 284 double width2, height2;
0fc1a713
JS
285 constrainedObject->GetBoundingBoxMax(&width2, &height2);
286 totalObjectWidth += width2;
287 totalObjectHeight += height2;
288 node = node->Next();
289 }
42cfaf8c
JS
290 double startX;
291 double spacingX;
292 double startY;
293 double spacingY;
0fc1a713
JS
294
295 // Check if within the constraining object...
296 if ((totalObjectWidth + (n + 1)*m_xSpacing) <= minWidth)
297 {
42cfaf8c
JS
298 spacingX = (double)((minWidth - totalObjectWidth)/(n + 1));
299 startX = (double)(x - (minWidth/2.0));
0fc1a713
JS
300 }
301 // Otherwise, use default spacing
302 else
303 {
304 spacingX = m_xSpacing;
42cfaf8c 305 startX = (double)(x - ((totalObjectWidth + (n+1)*spacingX)/2.0));
0fc1a713
JS
306 }
307
308 // Check if within the constraining object...
309 if ((totalObjectHeight + (n + 1)*m_ySpacing) <= minHeight)
310 {
42cfaf8c
JS
311 spacingY = (double)((minHeight - totalObjectHeight)/(n + 1));
312 startY = (double)(y - (minHeight/2.0));
0fc1a713
JS
313 }
314 // Otherwise, use default spacing
315 else
316 {
317 spacingY = m_ySpacing;
42cfaf8c 318 startY = (double)(y - ((totalObjectHeight + (n+1)*spacingY)/2.0));
0fc1a713
JS
319 }
320
321 // Now position the objects
322 bool changed = FALSE;
323 node = m_constrainedObjects.First();
324 while (node)
325 {
326 wxShape *constrainedObject = (wxShape *)node->Data();
42cfaf8c 327 double width2, height2;
0fc1a713 328 constrainedObject->GetBoundingBoxMax(&width2, &height2);
42cfaf8c
JS
329 startX += (double)(spacingX + (width2/2.0));
330 startY += (double)(spacingY + (height2/2.0));
0fc1a713
JS
331
332 if ((!Equals(startX, constrainedObject->GetX())) || (!Equals(startY, constrainedObject->GetY())))
333 {
334 constrainedObject->Move(dc, startX, startY, FALSE);
335 changed = TRUE;
336 }
337
42cfaf8c
JS
338 startX += (double)(width2/2.0);
339 startY += (double)(height2/2.0);
0fc1a713
JS
340
341 node = node->Next();
342 }
343 return changed;
344 }
345 case gyCONSTRAINT_LEFT_OF:
346 {
347 bool changed = FALSE;
348
349 wxNode *node = m_constrainedObjects.First();
350 while (node)
351 {
352 wxShape *constrainedObject = (wxShape *)node->Data();
353
42cfaf8c 354 double width2, height2;
0fc1a713
JS
355 constrainedObject->GetBoundingBoxMax(&width2, &height2);
356
42cfaf8c 357 double x3 = (double)(x - (minWidth/2.0) - (width2/2.0) - m_xSpacing);
0fc1a713
JS
358 if (!Equals(x3, constrainedObject->GetX()))
359 {
360 changed = TRUE;
361 constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
362 }
363
364 node = node->Next();
365 }
366 return changed;
367 }
368 case gyCONSTRAINT_RIGHT_OF:
369 {
370 bool changed = FALSE;
371
372 wxNode *node = m_constrainedObjects.First();
373 while (node)
374 {
375 wxShape *constrainedObject = (wxShape *)node->Data();
376
42cfaf8c 377 double width2, height2;
0fc1a713
JS
378 constrainedObject->GetBoundingBoxMax(&width2, &height2);
379
42cfaf8c 380 double x3 = (double)(x + (minWidth/2.0) + (width2/2.0) + m_xSpacing);
0fc1a713
JS
381 if (!Equals(x3, constrainedObject->GetX()))
382 {
383 changed = TRUE;
384 constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
385 }
386
387 node = node->Next();
388 }
389 return changed;
390
391 return FALSE;
392 }
393 case gyCONSTRAINT_ABOVE:
394 {
395 bool changed = FALSE;
396
397 wxNode *node = m_constrainedObjects.First();
398 while (node)
399 {
400 wxShape *constrainedObject = (wxShape *)node->Data();
401
42cfaf8c 402 double width2, height2;
0fc1a713
JS
403 constrainedObject->GetBoundingBoxMax(&width2, &height2);
404
42cfaf8c 405 double y3 = (double)(y - (minHeight/2.0) - (height2/2.0) - m_ySpacing);
0fc1a713
JS
406 if (!Equals(y3, constrainedObject->GetY()))
407 {
408 changed = TRUE;
409 constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
410 }
411
412 node = node->Next();
413 }
414 return changed;
415 }
416 case gyCONSTRAINT_BELOW:
417 {
418 bool changed = FALSE;
419
420 wxNode *node = m_constrainedObjects.First();
421 while (node)
422 {
423 wxShape *constrainedObject = (wxShape *)node->Data();
424
42cfaf8c 425 double width2, height2;
0fc1a713
JS
426 constrainedObject->GetBoundingBoxMax(&width2, &height2);
427
42cfaf8c 428 double y3 = (double)(y + (minHeight/2.0) + (height2/2.0) + m_ySpacing);
0fc1a713
JS
429 if (!Equals(y3, constrainedObject->GetY()))
430 {
431 changed = TRUE;
432 constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
433 }
434
435 node = node->Next();
436 }
437 return changed;
438 }
439 case gyCONSTRAINT_ALIGNED_LEFT:
440 {
441 bool changed = FALSE;
442
443 wxNode *node = m_constrainedObjects.First();
444 while (node)
445 {
446 wxShape *constrainedObject = (wxShape *)node->Data();
447
42cfaf8c 448 double width2, height2;
0fc1a713
JS
449 constrainedObject->GetBoundingBoxMax(&width2, &height2);
450
42cfaf8c 451 double x3 = (double)(x - (minWidth/2.0) + (width2/2.0) + m_xSpacing);
0fc1a713
JS
452 if (!Equals(x3, constrainedObject->GetX()))
453 {
454 changed = TRUE;
455 constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
456 }
457
458 node = node->Next();
459 }
460 return changed;
461 }
462 case gyCONSTRAINT_ALIGNED_RIGHT:
463 {
464 bool changed = FALSE;
465
466 wxNode *node = m_constrainedObjects.First();
467 while (node)
468 {
469 wxShape *constrainedObject = (wxShape *)node->Data();
470
42cfaf8c 471 double width2, height2;
0fc1a713
JS
472 constrainedObject->GetBoundingBoxMax(&width2, &height2);
473
42cfaf8c 474 double x3 = (double)(x + (minWidth/2.0) - (width2/2.0) - m_xSpacing);
0fc1a713
JS
475 if (!Equals(x3, constrainedObject->GetX()))
476 {
477 changed = TRUE;
478 constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
479 }
480
481 node = node->Next();
482 }
483 return changed;
484
485 return FALSE;
486 }
487 case gyCONSTRAINT_ALIGNED_TOP:
488 {
489 bool changed = FALSE;
490
491 wxNode *node = m_constrainedObjects.First();
492 while (node)
493 {
494 wxShape *constrainedObject = (wxShape *)node->Data();
495
42cfaf8c 496 double width2, height2;
0fc1a713
JS
497 constrainedObject->GetBoundingBoxMax(&width2, &height2);
498
42cfaf8c 499 double y3 = (double)(y - (minHeight/2.0) + (height2/2.0) + m_ySpacing);
0fc1a713
JS
500 if (!Equals(y3, constrainedObject->GetY()))
501 {
502 changed = TRUE;
503 constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
504 }
505
506 node = node->Next();
507 }
508 return changed;
509 }
510 case gyCONSTRAINT_ALIGNED_BOTTOM:
511 {
512 bool changed = FALSE;
513
514 wxNode *node = m_constrainedObjects.First();
515 while (node)
516 {
517 wxShape *constrainedObject = (wxShape *)node->Data();
518
42cfaf8c 519 double width2, height2;
0fc1a713
JS
520 constrainedObject->GetBoundingBoxMax(&width2, &height2);
521
42cfaf8c 522 double y3 = (double)(y + (minHeight/2.0) - (height2/2.0) - m_ySpacing);
0fc1a713
JS
523 if (!Equals(y3, constrainedObject->GetY()))
524 {
525 changed = TRUE;
526 constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
527 }
528
529 node = node->Next();
530 }
531 return changed;
532 }
533 case gyCONSTRAINT_MIDALIGNED_LEFT:
534 {
535 bool changed = FALSE;
536
537 wxNode *node = m_constrainedObjects.First();
538 while (node)
539 {
540 wxShape *constrainedObject = (wxShape *)node->Data();
541
42cfaf8c 542 double x3 = (double)(x - (minWidth/2.0));
0fc1a713
JS
543 if (!Equals(x3, constrainedObject->GetX()))
544 {
545 changed = TRUE;
546 constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
547 }
548
549 node = node->Next();
550 }
551 return changed;
552 }
553 case gyCONSTRAINT_MIDALIGNED_RIGHT:
554 {
555 bool changed = FALSE;
556
557 wxNode *node = m_constrainedObjects.First();
558 while (node)
559 {
560 wxShape *constrainedObject = (wxShape *)node->Data();
561
42cfaf8c 562 double x3 = (double)(x + (minWidth/2.0));
0fc1a713
JS
563 if (!Equals(x3, constrainedObject->GetX()))
564 {
565 changed = TRUE;
566 constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
567 }
568
569 node = node->Next();
570 }
571 return changed;
572
573 return FALSE;
574 }
575 case gyCONSTRAINT_MIDALIGNED_TOP:
576 {
577 bool changed = FALSE;
578
579 wxNode *node = m_constrainedObjects.First();
580 while (node)
581 {
582 wxShape *constrainedObject = (wxShape *)node->Data();
583
42cfaf8c 584 double y3 = (double)(y - (minHeight/2.0));
0fc1a713
JS
585 if (!Equals(y3, constrainedObject->GetY()))
586 {
587 changed = TRUE;
588 constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
589 }
590
591 node = node->Next();
592 }
593 return changed;
594 }
595 case gyCONSTRAINT_MIDALIGNED_BOTTOM:
596 {
597 bool changed = FALSE;
598
599 wxNode *node = m_constrainedObjects.First();
600 while (node)
601 {
602 wxShape *constrainedObject = (wxShape *)node->Data();
603
42cfaf8c 604 double y3 = (double)(y + (minHeight/2.0));
0fc1a713
JS
605 if (!Equals(y3, constrainedObject->GetY()))
606 {
607 changed = TRUE;
608 constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
609 }
610
611 node = node->Next();
612 }
613 return changed;
614 }
615
616 default:
617 return FALSE;
618 }
619 return FALSE;
620}
621