1 /*
2 * Dr Geo an interactive geometry software
3 * (C) Copyright Hilaire Fernandes 1997-2003
4 * hilaire@ofset.org
5 *
6 *
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public Licences as by published
11 * by the Free Software Foundation; either version 2; or (at your option)
12 * any later version
13 *
14 * This program is distributed in the hope that it will entertaining,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Publis License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.
21 * 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "drgeo_figure.h"
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "drgeo_gtkdrawable.h"
29 #include "drgeo_command.h"
30 #include "drgeo_flydrawable.h"
31 #include "drgeo_latexdrawable.h"
32 #include "drgeo_postscriptdrawable.h"
33 #include "xml.h"
34 #include "drgeo_point.h"
35 #include "drgeo_vector.h"
36 #include "drgeo_line.h"
37 #include "drgeo_halfLine.h"
38 #include "drgeo_segment.h"
39 #include "drgeo_circle.h"
40 #include "drgeo_arcCircle.h"
41 #include "drgeo_angle.h"
42 #include "drgeo_numeric.h"
43 #include "drgeo_equation.h"
44 #include "drgeo_locus.h"
45 #include "drgeo_polygon.h"
46 #include "drgeo_script.h"
47 #include "drgeo_buildObject.h"
48 #include "drgeo_dialog.h"
49 #include "traite.h"
50
51 struct
52 {
53 drgeoPoint mouse;
54 drgeoFigure *figure;
55 }
56 drgeoDialogData;
57
58 typedef struct drgeoSearchValue
59 {
60 gpointer value;
61 gpointer key;
62 }
63 drgeoSearchValue;
64
65 static void _searchValue (gpointer key, gpointer value, gpointer data);
66 static void _freeKeyFromHash (gpointer key, gpointer value, gpointer data);
67
drgeoFigure(xmlNodePtr figureTree)68 drgeoFigure::drgeoFigure (xmlNodePtr figureTree)
69 {
70 xmlNodePtr item;
71 gchar *id;
72 gint myId;
73 GHashTable *itemIdToAddress;
74 geometricObject *fig;
75
76 itemFilter = NULL;
77 mode = SOURIS_SELECT_MODE;
78 saved = TRUE;
79 password = NULL;
80 showGrid = false;
81
82 for (myId = 0; myId < DRGEO_TOOLS_NUMBER; myId++)
83 showTool[myId] = TRUE;
84
85 // set a scm interface
86 scmInterface = new ScmInterface (this);
87
88 // undo/redo initialization stuff
89 undoHash = g_hash_table_new (g_str_hash, g_str_equal);
90 undoLevel = NULL;
91 undoLevel = g_list_append (undoLevel, NULL);
92
93 if (figureTree == NULL)
94 {
95 /* create a new empty figure */
96 blinkOn = TRUE;
97 return;
98 }
99
100 /* remove blinking as we going to re-build the items from the XML tree */
101 blinkOn = FALSE;
102
103 if (strcmp ((char *) figureTree->name, "drgeo"))
104 {
105 fprintf (stderr,
106 "drgeoFigure::drgeoFigure : invalid element type %s, 'drgeo' expected\n",
107 figureTree->name);
108 }
109
110 /* Let's see if the grid is attached to the figure */
111 id = (gchar *) xmlGetProp (figureTree, BAD_CAST "grid");
112 if (id != NULL)
113 {
114 if (!strcmp (id, "True"))
115 showGrid = true;
116 else
117 showGrid = false;
118 g_free (id);
119 }
120
121 if ((item = figureTree->xmlChildrenNode) == NULL)
122 {
123 // empty figure
124 blinkOn = TRUE;
125 return;
126 }
127
128
129 // hash table to get item Address from their Id
130 itemIdToAddress = g_hash_table_new (g_str_hash, g_str_equal);
131
132 while (item != NULL)
133 {
134 // create the item
135 fig = xmlBuildItem (item, itemIdToAddress);
136 addToFigureListNoRedraw (fig, TRUE);
137 if (fig != NULL)
138 {
139 // push the object on the hash table
140 if ((id = xml_value_get (item, "id")) == NULL)
141 fprintf (stderr,
142 "drgeoFigure::drgeoFigure : element without id\n");
143 else
144 g_hash_table_insert (itemIdToAddress, id, (void *) fig);
145 }
146 // get the next item
147 item = item->next;
148 }
149 g_hash_table_foreach (itemIdToAddress, _freeKeyFromHash, NULL);
150 g_hash_table_destroy (itemIdToAddress);
151 blinkOn = TRUE;
152 }
153
~drgeoFigure()154 drgeoFigure::~drgeoFigure ()
155 {
156 liste_elem *fig;
157 geometricObject *item;
158 // Clear the undo pipe
159 undoLevel = g_list_first (undoLevel);
160 // The first node is empty, skip it
161 undoLevel = g_list_next (undoLevel);
162 while (undoLevel != NULL)
163 {
164 delete ((drgeoCommand *) (undoLevel->data));
165 undoLevel = g_list_next (undoLevel);
166 printf ("Free one undo item\n");
167 }
168 g_list_free (undoLevel);
169 g_hash_table_foreach (undoHash, _freeKeyFromHash, NULL);
170 g_hash_table_destroy (undoHash);
171 /* free the figure */
172 fig = getFigureList ();
173 fig->init_lire ();
174 while ((item = (geometricObject *) fig->lire (0)) != NULL)
175 delete item;
176 }
177
178
saveAs(xmlNodePtr drgeoXml,const gchar * name)179 gboolean drgeoFigure::saveAs (xmlNodePtr drgeoXml, const gchar * name)
180 {
181 gint nb, a, c;
182 xmlNodePtr tree, tree1, tree2;
183 drgeoPoint center, p1, p2;
184
185 tree = xmlNewChild (drgeoXml, NULL, BAD_CAST "drgeo", NULL);
186 xmlSetProp (tree, BAD_CAST "name", BAD_CAST name);
187
188 // Save the scale and coordinate of the origin
189 xml_set_double_prop (tree, "scale", area->getScale ());
190 center = area->getAreaCenter ();
191 xml_set_double_prop (tree, "origin_x", center.getX ());
192 xml_set_double_prop (tree, "origin_y", center.getY ());
193 if (showGrid)
194 xmlSetProp (tree, BAD_CAST "grid", BAD_CAST "True");
195 else
196 xmlSetProp (tree, BAD_CAST "grid", BAD_CAST "False");
197
198 /* eventually save the printing area if defined */
199 if (((drgeoGtkDrawable *) area)->printingArea ())
200 {
201 p1 = ((drgeoGtkDrawable *) area)->getPrintingAreaBR ();
202 p2 = ((drgeoGtkDrawable *) area)->getPrintingAreaTL ();
203
204 tree1 = xmlNewChild (tree, NULL, BAD_CAST "boundingBox", NULL);
205 xml_set_double_prop (tree1, "tl_x", p2.getX ());
206 xml_set_double_prop (tree1, "tl_y", p2.getY ());
207 xml_set_double_prop (tree1, "br_x", p1.getX ());
208 xml_set_double_prop (tree1, "br_y", p1.getY ());
209 }
210
211 /* eventually save the customized ui */
212 if (getPassword ())
213 {
214 tree1 = xmlNewChild (tree, NULL, BAD_CAST "customUI", NULL);
215 xmlSetProp (tree1, BAD_CAST "password", BAD_CAST getPassword ());
216 for (a = 0; a < DRGEO_TOOLS_NUMBER; a++)
217 {
218 if (getToolState ((drgeoToolId) a))
219 tree2 =
220 xmlNewChild (tree1, NULL, BAD_CAST "tool", BAD_CAST "TRUE");
221 else
222 tree2 =
223 xmlNewChild (tree1, NULL, BAD_CAST "tool", BAD_CAST "FALSE");
224 xml_set_int_prop (tree2, "id", a);
225 }
226 }
227 // save the data of each figure item object
228 nb = figureList.nb_elem;
229 figureList.init_lire ();
230 for (a = 1; a <= nb; a++)
231 {
232 ((geometricObject *) figureList.lire (0))->save (tree, figureList);
233 }
234 saved = TRUE;
235 return saved;
236 }
237
isSaved()238 gboolean drgeoFigure::isSaved ()
239 {
240 return saved;
241 }
242
exportToFlydraw(const gchar * fileName)243 gboolean drgeoFigure::exportToFlydraw (const gchar * fileName)
244 {
245 FILE * fileHandle;
246 drgeoDrawable * fly;
247 drgeoGtkDrawable *gtkArea;
248 int a, nb;
249 geometricObject * fig;
250 drgeoPoint center, size, br, tl;
251
252 gtkArea = (drgeoGtkDrawable *) area;
253 if (gtkArea->printingArea ())
254 {
255 br = gtkArea->getPrintingAreaBR ();
256 tl = gtkArea->getPrintingAreaTL ();
257 center = (br + tl) / 2;
258 size.setX (br.getX () - tl.getX ());
259 size.setY (tl.getY () - br.getY ());
260 }
261 else
262 {
263 center = area->getAreaCenter ();
264 size = area->getAreaSize ();
265 }
266
267
268 if (!(fileHandle = fopen (fileName, "w")))
269 return FALSE;
270
271 fly = new drgeoFlyDrawable (this, fileHandle, center, size, 25.0);
272
273 /* eventually print the grid */
274 if (showGrid)
275 fly->showGrid ();
276
277 /* scan the figure and get the output */
278 nb = figureList.nb_elem;
279 figureList.init_lire ();
280 for (a = 1; a <= nb; a++)
281 {
282 fig = (geometricObject *) figureList.lire (0);
283 if (doNotDraw.position ((void *) fig) == 0)
284 fig->draw (*fly, FALSE);
285 }
286 fprintf (fileHandle, "output image.png\n");
287 fclose (fileHandle);
288 delete (fly);
289
290 return TRUE;
291 }
292
exportToLatex(const gchar * fileName)293 gboolean drgeoFigure::exportToLatex (const gchar * fileName)
294 {
295 FILE * fileHandle;
296 drgeoDrawable * latex;
297 drgeoGtkDrawable *gtkArea;
298 int a, nb;
299 geometricObject * fig;
300 drgeoPoint center, size, br, tl;
301
302 gtkArea = (drgeoGtkDrawable *) area;
303 if (gtkArea->printingArea ())
304 {
305 br = gtkArea->getPrintingAreaBR ();
306 tl = gtkArea->getPrintingAreaTL ();
307 center = (br + tl) / 2;
308 size.setX (br.getX () - tl.getX ());
309 size.setY (tl.getY () - br.getY ());
310 }
311 else
312 {
313 center = area->getAreaCenter ();
314 size = area->getAreaSize ();
315 }
316
317
318 if (!(fileHandle = fopen (fileName, "w")))
319 return FALSE;
320
321 fprintf (fileHandle,
322 "\\documentclass[a4paper]{minimal}\n\\usepackage{pstricks}\n\\begin{document}\n");
323 latex =
324 new drgeoLatexDrawable (this, fileHandle, center,
325 size, 1.0);
326
327 /* eventually print the grid */
328 if (showGrid)
329 latex->showGrid ();
330
331 /* scan the figure and get the output */
332 nb = figureList.nb_elem;
333 figureList.init_lire ();
334 for (a = 1; a <= nb; a++)
335 {
336 fig = (geometricObject *) figureList.lire (0);
337 if (doNotDraw.position ((void *) fig) == 0)
338 fig->draw (*latex, FALSE);
339 }
340 fprintf (fileHandle, "\\end{pspicture*}\n\\end{document}\n");
341 fclose (fileHandle);
342 delete (latex);
343
344 return TRUE;
345 }
346
exportToPostScript(const gchar * fileName)347 gboolean drgeoFigure::exportToPostScript (const gchar * fileName)
348 {
349 FILE * fileHandle;
350 gboolean ret;
351
352 if (!(fileHandle = fopen (fileName, "w")))
353 return FALSE;
354
355 ret = exportToPostScriptHandle (fileHandle, g_basename (fileName));
356
357 fclose (fileHandle);
358 return ret;
359 }
360
361 gboolean
exportToPostScriptHandle(FILE * fileHandle,const gchar * name)362 drgeoFigure::exportToPostScriptHandle (FILE *fileHandle, const gchar * name)
363 {
364 drgeoDrawable * postscript;
365 drgeoGtkDrawable *gtkArea;
366 int a, nb;
367 geometricObject * fig;
368 drgeoPoint center, size, br, tl;
369
370 gtkArea = (drgeoGtkDrawable *) area;
371
372 if (gtkArea->printingArea ())
373 {
374 br = gtkArea->getPrintingAreaBR ();
375 tl = gtkArea->getPrintingAreaTL ();
376 center = (br + tl) / 2;
377 size.setX (br.getX () - tl.getX ());
378 size.setY (tl.getY () - br.getY ());
379 }
380 else
381 {
382 center = area->getAreaCenter ();
383 size = area->getAreaSize ();
384 }
385
386 postscript =
387 new drgeoPostScriptDrawable (name, this, fileHandle,
388 center, size, 1.0);
389
390 /* eventually print the grid */
391 if (showGrid)
392 postscript->showGrid ();
393
394 /* scan the figure and get the output */
395
396 //first draw the polygon
397 nb = figureList.nb_elem;
398 figureList.init_lire ();
399 for (a = 1; a <= nb; a++)
400 {
401 fig = (geometricObject *) figureList.lire (0);
402 if ((doNotDraw.position ((void *) fig) == 0) &&
403 fig->getCategory () == POLYGON)
404 fig->draw (*postscript, FALSE);
405 }
406
407
408 // then the other object
409 nb = figureList.nb_elem;
410 figureList.init_lire ();
411 for (a = 1; a <= nb; a++)
412 {
413 fig = (geometricObject *) figureList.lire (0);
414 if ((doNotDraw.position ((void *) fig) == 0) &&
415 fig->getCategory () != POLYGON)
416 fig->draw (*postscript, FALSE);
417 }
418 delete (postscript);
419
420 return TRUE;
421 }
422
423 void
clear()424 drgeoFigure::clear ()
425 {
426 figureList.vide ();
427 freeFigureList.vide ();
428 }
429
430 void
updateItems()431 drgeoFigure::updateItems ()
432 {
433 int a, nb;
434 geometricObject *fig;
435
436 nb = figureList.nb_elem;
437 figureList.init_lire ();
438
439 for (a = 1; a <= nb; a++)
440 {
441 fig = (geometricObject *) figureList.lire (0);
442 fig->update (*area);
443 }
444 }
445
446
447 void
redraw(gchar all)448 drgeoFigure::redraw (gchar all)
449 {
450 // The all flag indicate if the hidden objects should be redrawn:
451 //
452 // * the hidden attribute of an object can be toggle from the style
453 // box for normal redraw this flag should be FALSE
454 //
455 // * it will be TRUE when the user select the style box, so he can
456 // see all objects, and therefore make invisible items visible
457 // again.
458 //
459 // There are other objects, used for macro-constructions, which are
460 // never displayed, only computed.
461 //
462 // Additionally objects in the doNotDraw list are temporarily not
463 // displayed.
464
465 int a, nb;
466 geometricObject *fig;
467
468 // Update the item, we need to do that because of the locus
469 // object
470 updateItems ();
471
472
473 // We just draw all the items in the figure, following the rules
474 // listed above.
475
476 nb = figureList.nb_elem;
477 figureList.init_lire ();
478
479 for (a = 1; a <= nb; a++)
480 {
481 fig = (geometricObject *) figureList.lire (0);
482 if (doNotDraw.position ((void *) fig) == 0)
483 fig->draw (*area, all);
484 }
485 }
486
487 void
undo(gint level)488 drgeoFigure::undo (gint level)
489 {
490 GList *prev;
491 prev = g_list_previous (undoLevel);
492 if (prev)
493 {
494 /* clear all the list containing item object,
495 if not this bring hazardous effect */
496 selection.vide ();
497 underMouse.vide ();
498 highlightItem.vide ();
499 if (itemFilter)
500 itemFilter->reset ();
501 doNotDraw.vide ();
502 ((drgeoCommand *) undoLevel->data)->unexecute ();
503 undoLevel = prev;
504 }
505 area->refresh ();
506 area->updateUndoState ();
507 }
508
509 void
redo(gint level)510 drgeoFigure::redo (gint level)
511 {
512 GList *next;
513 next = g_list_next (undoLevel);
514 if (next)
515 {
516 /* clear all the list containing item object,
517 if not this bring hazardous effect */
518 selection.vide ();
519 underMouse.vide ();
520 highlightItem.vide ();
521 doNotDraw.vide ();
522 if (itemFilter)
523 itemFilter->reset ();
524 ((drgeoCommand *) next->data)->execute ();
525 undoLevel = next;
526 }
527 area->refresh ();
528 area->updateUndoState ();
529 }
530
531 void
emptyUndoQueueEnd()532 drgeoFigure::emptyUndoQueueEnd ()
533 {
534 GList *rmList, *tmpList;
535 drgeoCommand *rmCommand;
536
537 rmList = NULL;
538 tmpList = undoLevel;
539 while (tmpList = g_list_next (tmpList))
540 rmList = g_list_append (rmList, tmpList->data);
541
542 rmList = g_list_first (rmList);
543 undoLevel = g_list_first (undoLevel);
544 while (rmList)
545 {
546 rmCommand = (drgeoCommand *) rmList->data;
547 delete (rmCommand);
548 undoLevel = g_list_remove (undoLevel, (gpointer) rmCommand);
549 rmList = g_list_next (rmList);
550 }
551 g_list_free (rmList);
552 }
553
554 void
emptyUndoQueueAll()555 drgeoFigure::emptyUndoQueueAll ()
556 {
557 GList *rmList;
558 drgeoCommand *rmCommand;
559
560 rmList = g_list_first (undoLevel);
561 while (rmList)
562 {
563 rmCommand = (drgeoCommand *) rmList->data;
564 delete (rmCommand);
565 rmList = g_list_next (rmList);
566 }
567
568 g_list_free (undoLevel);
569 undoLevel = NULL;
570 undoLevel = g_list_append (undoLevel, NULL);
571 }
572
573 void
addToUndoQueue(drgeoCommand * command)574 drgeoFigure::addToUndoQueue (drgeoCommand * command)
575 {
576 drgeoCommand *rmCommand;
577
578 if (g_list_length (undoLevel) > preferedUndoLevel ())
579 {
580 /* Watch out, for internal managment the undoLevel list
581 contains a first empty element, we skip it */
582 rmCommand = (drgeoCommand *) ((g_list_nth (undoLevel, 1))->data);
583 /* we shoud permanently update this object reference */
584 delete (rmCommand);
585 undoLevel = g_list_remove (undoLevel, (gpointer) rmCommand);
586 }
587
588 undoLevel = g_list_append (undoLevel, (gpointer) command);
589 undoLevel = g_list_last (undoLevel);
590 }
591
592 gboolean
undoActive()593 drgeoFigure::undoActive ()
594 {
595 return (gboolean) (undoLevel->data != 0);
596 }
597
598 gboolean
redoActive()599 drgeoFigure::redoActive ()
600 {
601 return (gboolean) (undoLevel->next != 0);
602 }
603
604 void
setMode(gint toMode,buildParametersListForGeometricObject * itemBuilder)605 drgeoFigure::setMode (gint toMode,
606 buildParametersListForGeometricObject * itemBuilder)
607 {
608 mode = toMode;
609 clearSelection ();
610 if (mode >= POINT_MODE && mode <= SCRIPT_MODE)
611 {
612 itemFilter = itemBuilder;
613 itemFilter->reset ();
614 }
615 }
616
617 liste_elem *
getSelection()618 drgeoFigure::getSelection ()
619 {
620 return &selection;
621 }
622
623 void
clearSelection()624 drgeoFigure::clearSelection ()
625 {
626 selection.vide ();
627 }
628
629 void
setSelection(liste_elem * toSelection)630 drgeoFigure::setSelection (liste_elem * toSelection)
631 {
632 selection.vide ();
633 concatene (selection, *toSelection);
634 }
635
636 void
removeItemFromSelection(void * item)637 drgeoFigure::removeItemFromSelection (void *item)
638 {
639 selection.supprime (item);
640 }
641
642 void
addToFigureList(geometricObject * item,char free)643 drgeoFigure::addToFigureList (geometricObject * item, char free)
644 {
645 figureList.ajoute ((void *) item);
646 if (free)
647 freeFigureList.ajoute ((void *) item);
648 item->draw (*area, FALSE);
649 }
650
651 void
addToFigureListNoRedraw(geometricObject * item,char free)652 drgeoFigure::addToFigureListNoRedraw (geometricObject * item, char free)
653 {
654 if (item == NULL)
655 return;
656 figureList.ajoute ((void *) item);
657 if (free)
658 freeFigureList.ajoute ((void *) item);
659 }
660
661
addToSelection(drgeoPoint & mouse,geometricObject * item)662 gint drgeoFigure::addToSelection (drgeoPoint & mouse, geometricObject * item)
663 {
664 // convention: if item == NULL this mean the user has clicked on the
665 // background I need this to create new point on the background or
666 // value the user can edit. See below
667 gint
668 cas;
669 GList *
670 builded;
671
672 if (mode >= POINT_MODE && mode <= SCRIPT_MODE && item != NULL)
673 {
674 if (itemFilter
675 && (cas = itemFilter->inserer_figure (selection, item)) != -1)
676 {
677 // The builder fonctions.
678 GList *(*builderFonction[]) (int, drgeoPoint &, drgeoFigure *) =
679 {
680 buildFreePoint, buildMiddlePoint,
681 buildIntersectionPoint, buildCoordinatePoint,
682 buildLine, buildHalfLine, buildSegment,
683 buildVector, buildCircle, buildArcCircle,
684 buildLocus, buildParallelLine,
685 buildOrthogonalLine, buildReflexion,
686 buildSymmetry, buildTranslation, buildRotation,
687 buildScale, buildNumeric, buildAngle,
688 buildCoordinateAndEquation, buildPolygon, buildScript};
689
690 // Enough item are selected, we can build the
691 // item corresponding to the mode we need the
692 // mouse coordinate there...
693
694 builded = builderFonction[mode] (cas, mouse, this);
695 /* process the list of builded in the undo
696 pipe */
697 builded = g_list_first (builded);
698 while (builded)
699 {
700 createItem ((geometricObject *) builded->data);
701 builded = g_list_next (builded);
702 }
703 clearSelection ();
704 itemFilter->reset ();
705 redraw (FALSE);
706 }
707 }
708 else if (item == NULL && mode == POINT_MODE)
709 {
710 // build a free point on the plane
711 geometricObject *
712 new_fig;
713 new_fig = new point (mouse, selection, FREE, FALSE, &figureList);
714 createItem (new_fig);
715 clearSelection ();
716 itemFilter->reset ();
717 redraw (FALSE);
718 }
719 else if (item == NULL && mode == NUMERIQUE_MODE)
720 {
721 // buid a free value, the user need to enter a value
722 drgeoDialogData.mouse = mouse;
723 drgeoDialogData.figure = this;
724 get_edited_value ();
725 }
726 else if (item == NULL && mode == SCRIPT_MODE)
727 {
728 // build a script without input value
729 geometricObject *
730 new_fig;
731 gchar *
732 scriptCode;
733
734 scriptCode = g_strdup ("'\"Dr. Genius\"");
735 new_fig = new script (mouse, selection, SCRIPT_NITEMS, scriptCode,
736 FALSE, &figureList);
737 g_free (scriptCode);
738 createItem (new_fig);
739 clearSelection ();
740 itemFilter->reset ();
741 redraw (FALSE);
742 }
743 else if (item != NULL)
744 {
745 switch (mode)
746 {
747 case MACRO_ENREGISTRE_MODE:
748 // FIXME Everything is handled by the drgeoMacroBuildTool.
749 break;
750 case MACRO_PLAY_MODE:
751 // FIXME Everything is handled by the drgeoMacroPlayTool.
752 break;
753 case SOURIS_SELECT_MODE:
754 selection.ajoute ((void *) item);
755 break;
756 case MISE_EN_FORME_MODE:
757 // we want the selected item to blink
758 selection.ajoute ((void *) item);
759 break;
760 case PROPERTY_MODE:
761 // we want the item to blink, we add it to the selection list
762 {
763 objectCategory
764 category;
765 int
766 type;
767
768 category = item->getCategory ();
769 type = item->getType ();
770 if (category == FREE_PT ||
771 (category == NUMERIC && type == FREE_VALUE) ||
772 (category == SCRIPT))
773 selection.ajoute ((void *) item);
774 break;
775 }
776 case EFFACE_OBJET_MODE:
777 // FIXME: delete me, this now handle from
778 // drgeoDeleteTool::HandleChoice with the
779 // method removeItem to take care of undo
780 // system
781
782 break;
783 }
784 }
785 return selection.nb_elem;
786 }
787
788 void
showRemovedItems()789 drgeoFigure::showRemovedItems ()
790 {
791 doNotDraw.vide ();
792 }
793
794 void
hideRemovedItems(geometricObject * item)795 drgeoFigure::hideRemovedItems (geometricObject * item)
796 {
797 geometricObject *fig;
798 liste_elem *children;
799
800 doNotDraw.vide ();
801 doNotDraw.ajoute ((void *) item);
802 doNotDraw.init_lire ();
803 // we research all the object who depend on the object
804 // 'item' we must delete these one also
805 children = searchChildren (item, figureList);
806 concatene (doNotDraw, *children);
807 delete (children);
808
809 // let display the figure without those items
810 (this->getDrawable ())->refresh ();
811 }
812
813 char
doesItemExist(geometricObject * fig)814 drgeoFigure::doesItemExist (geometricObject * fig)
815 {
816 liste_elem parent_orig, parent;
817 int nb_figure;
818 geometricObject *obj;
819
820 nb_figure = figureList.nb_elem;
821 figureList.init_lire ();
822 fig->parents (parent_orig);
823
824 while (nb_figure--)
825 {
826 obj = (geometricObject *) figureList.lire (0);
827 if (obj->getType () == fig->getType ()
828 && obj->getMask () != alway
829 && obj->getCategory () == fig->getCategory ())
830 {
831 obj->parents (parent);
832 if (liste_elem_egale (parent_orig, parent))
833 return TRUE;
834 }
835 }
836 return FALSE;
837 }
838
839
840 liste_elem *
mouseSelection(drgeoPoint & mouse)841 drgeoFigure::mouseSelection (drgeoPoint & mouse)
842 {
843 gchar trouve;
844 gint nb, a;
845 geometricObject *fig;
846
847 // Return the list of items currently located near the mouse (near
848 // means here that their distance to the mouse is < range). Only
849 // items relevant to the current mode are listed.
850
851 if (mode >= POINT_MODE && mode <= SCRIPT_MODE)
852 {
853 nb = figureList.nb_elem;
854 figureList.init_lire ();
855
856 underMouse.vide ();
857
858 for (a = 1; a <= nb; a++)
859 {
860 fig = (geometricObject *) (figureList.lire (0));
861 if (fig->overObject (mouse, area->getRange ())
862 && fig->objectExist ()
863 && (fig->getMask () == no
864 || (fig->getMask () == yes && mode == MISE_EN_FORME_MODE)))
865 if (itemFilter && itemFilter->utilisable1 (fig, selection))
866 underMouse.ajoute ((void *) fig);
867 }
868 }
869 else
870 {
871 switch (mode)
872 {
873 case MACRO_ENREGISTRE_MODE:
874 // All the items currently under the mouse are interesting for
875 // the macro creation.
876 trouve = itemsUnderMouse (mouse, FALSE);
877 break;
878 case MACRO_PLAY_MODE:
879 // play a macro
880 // We should check for relevant item according to the selected macro
881 // Go down asking to the macro player, referenced in the drawable
882 trouve = ((drgeoDrawableUI *) area)->player->itemsUnderMouseForMacro
883 (mouse, underMouse);
884 break;
885 case SOURIS_SELECT_MODE:
886 // Drag mode
887 trouve = itemsUnderMouse (mouse, FALSE);
888
889 break;
890 case MISE_EN_FORME_MODE:
891 // Style mode
892 trouve = itemsUnderMouse (mouse, TRUE);
893 break;
894 case PROPERTY_MODE:
895 // Property mode
896 objectCategory category;
897 int type;
898
899 trouve = itemsUnderMouse (mouse, FALSE);
900 if (trouve)
901 {
902 underMouse.init_lire ();
903 nb = underMouse.nb_elem;
904 do
905 {
906 fig = (geometricObject *) underMouse.lire (0);
907 category = fig->getCategory ();
908 type = fig->getType ();
909 if (category != FREE_PT &&
910 (category != NUMERIC || type != FREE_VALUE) &&
911 (category != SCRIPT))
912 underMouse.supprime ((void *) fig);
913 }
914 while (--nb != 0);
915 }
916 break;
917 case ANIMATION_MODE:
918 trouve = itemsUnderMouse (mouse, FALSE);
919 if (trouve)
920 filterItems (underMouse, FREE_PT_ON_CURVE);
921 break;
922 case EFFACE_OBJET_MODE:
923 // Remove mode
924 trouve = itemsUnderMouse (mouse, FALSE);
925 break;
926 }
927 }
928 return &underMouse;
929 }
930
931 void
filterItems(liste_elem & items,objectCategory categories)932 drgeoFigure::filterItems (liste_elem &items, objectCategory categories)
933 {
934 gint nb;
935 geometricObject *fig;
936 objectCategory category;
937
938 items.init_lire ();
939 nb = items.nb_elem;
940 do
941 {
942 fig = (geometricObject *) items.lire (0);
943 if ( (fig->getCategory() & categories) == 0)
944 underMouse.supprime ((void *) fig);
945 }
946 while (--nb != 0);
947
948 }
949
950 void
dragSelection(drgeoPoint & start,drgeoPoint & mouse)951 drgeoFigure::dragSelection (drgeoPoint & start, drgeoPoint & mouse)
952 {
953 geometricObject *fig;
954
955 // Only move the first item in the selection list otherwise result
956 // may be unpredictable.
957 selection.init_lire ();
958 fig = (geometricObject *) selection.lire (0);
959 if (fig)
960 {
961 start = mouse - start;
962 fig->move (start);
963 this->updateItems ();
964 }
965 }
966
967 void
dropSelection(drgeoPoint & start,drgeoPoint & mouse)968 drgeoFigure::dropSelection (drgeoPoint & start, drgeoPoint & mouse)
969 {
970 geometricObject *fig;
971
972 // Only move the first item in the selection list otherwise result
973 // may be unpredictable. Same thing as dragSelection currently.
974 selection.init_lire ();
975 fig = (geometricObject *) selection.lire (0);
976 if (fig)
977 {
978 start = mouse - start;
979 fig->move (start);
980 this->updateItems ();
981 }
982 }
983
984
985 void
addUndoHash(gpointer key,gpointer value)986 drgeoFigure::addUndoHash (gpointer key, gpointer value)
987 {
988 /* add key/value in the table, check if the pair already
989 exist before */
990 gchar *skey;
991
992 skey = g_strdup_printf ("%X", GPOINTER_TO_UINT (key));
993 /* is the key already used? */
994 if (g_hash_table_lookup (undoHash, (gconstpointer) skey) == NULL)
995 {
996 g_hash_table_insert (undoHash, (gpointer) skey, value);
997 }
998 else
999 g_free (skey);
1000 }
1001
1002 void
updateUndoHash(gpointer key,gpointer value)1003 drgeoFigure::updateUndoHash (gpointer key, gpointer value)
1004 {
1005 /* The key need to be converted as a string. This is expected
1006 by the item buidler. We have to take care in creating and
1007 freeing memory for the key string eventually */
1008 gchar *skey;
1009 gpointer origKey, origValue;
1010
1011 skey = g_strdup_printf ("%X", GPOINTER_TO_UINT (key));
1012 /* is the key already used? */
1013 if (g_hash_table_lookup_extended
1014 (undoHash, (gconstpointer) skey, &origKey, &origValue))
1015 {
1016 /* skey exist, update the value if needed */
1017 if (origValue != value)
1018 g_hash_table_insert (undoHash, (gpointer) origKey, value);
1019 }
1020 g_free (skey);
1021 }
1022
1023 gpointer
getValueFromKeyOnHash(gpointer key)1024 drgeoFigure::getValueFromKeyOnHash (gpointer key)
1025 {
1026 gchar *skey;
1027 gpointer val;
1028
1029 skey = g_strdup_printf ("%X", GPOINTER_TO_UINT (key));
1030 /* is the key already used? */
1031 val = g_hash_table_lookup (undoHash, (gconstpointer) skey);
1032 g_free (skey);
1033 return val;
1034 }
1035
1036 gpointer
getKeyFromValueOnHash(gpointer value)1037 drgeoFigure::getKeyFromValueOnHash (gpointer value)
1038 {
1039 drgeoSearchValue data;
1040 data.value = value;
1041 data.key = NULL;
1042 g_hash_table_foreach (undoHash, _searchValue, (gpointer) & data);
1043 if (data.key == NULL)
1044 return NULL;
1045 else
1046 // data.key is a * gchar get it content as a gpointer
1047 return GINT_TO_POINTER (strtol ((gchar *) data.key, NULL, 16));
1048 }
1049
1050 void
removeKeyFromHash(gpointer key)1051 drgeoFigure::removeKeyFromHash (gpointer key)
1052 {
1053 gchar *skey;
1054 gpointer origKey, origValue;
1055
1056 skey = g_strdup_printf ("%X", GPOINTER_TO_UINT (key));
1057 /* is the key already used? */
1058 if (g_hash_table_lookup_extended
1059 (undoHash, (gconstpointer) skey, &origKey, &origValue))
1060 {
1061 g_hash_table_remove (undoHash, origKey);
1062 g_free (origKey);
1063 }
1064 else
1065 g_printerr ("Warning: key %X can't be found in hash table\n", key);
1066 g_free (skey);
1067 }
1068
1069
1070 GHashTable *
getUndoHash()1071 drgeoFigure::getUndoHash ()
1072 {
1073 return undoHash;
1074 }
1075
1076 liste_elem *
dependantItems(geometricObject * item)1077 drgeoFigure::dependantItems (geometricObject * item)
1078 {
1079 /* search for both upward and backward dependences of item
1080 Backward dependence only concerns macro-node only used by
1081 the item */
1082 liste_elem *dependence, *lostNode, *children;
1083
1084 dependence = new liste_elem ();
1085 dependence->ajoute ((void *) item);
1086
1087 /* We research all the objects who depend on the object item. We
1088 have to provide them in chronology order */
1089
1090 children = searchChildren (item, figureList);
1091 concatene (*dependence , *children);
1092 delete (children);
1093
1094 children = new liste_elem (figureList);
1095 substract (*children, doNotDraw);
1096
1097 lostNode = searchLostNode (*children);
1098 delete (children);
1099
1100 concatene (*lostNode, *dependence);
1101 delete (dependence);
1102
1103 return lostNode;
1104 }
1105
1106 liste_elem *
searchChildren(geometricObject * item,liste_elem & myList)1107 drgeoFigure::searchChildren (geometricObject * item, liste_elem & myList)
1108 {
1109 liste_elem *children;
1110 geometricObject *fig;
1111 guint pos, nbFigure;
1112
1113 children = new liste_elem ();
1114
1115 nbFigure = myList.nb_elem;
1116 for (pos = myList.position ((void *) item) + 1 ; pos <= nbFigure; pos++)
1117 {
1118 fig = (geometricObject *) myList.lire (pos);
1119 if (fig->dependOn (item))
1120 children->ajoute ((void *) fig);
1121 }
1122 return children;
1123 }
1124
1125 liste_elem *
searchLostNode(liste_elem & myList)1126 drgeoFigure::searchLostNode (liste_elem &myList)
1127 {
1128 guint pos, nbFigure;
1129 geometricObject *fig, *child;
1130 liste_elem *children, *lostNode;
1131 gboolean lost;
1132
1133 myList.init_lire ();
1134 lostNode = new liste_elem ();
1135
1136 while (fig = (geometricObject *) myList.lire (0))
1137 {
1138 if (fig->getMask () != alway)
1139 continue;
1140 children = searchChildren (fig, myList);
1141 children->init_lire ();
1142 lost = true;
1143 // if there is only macro nodes, we got a lost one.
1144 while ((child = (geometricObject *) children->lire (0)) && lost)
1145 {
1146 if (child->getMask () != alway)
1147 lost = false;
1148 }
1149 delete (children);
1150 if (lost)
1151 lostNode->ajoute (fig);
1152 }
1153 return lostNode;
1154 }
1155
1156 void
removeItem(geometricObject * item)1157 drgeoFigure::removeItem (geometricObject * item)
1158 {
1159 drgeoCommand *command;
1160
1161 emptyUndoQueueEnd ();
1162 command = new drgeoRemoveItemCommand (this, item);
1163 command->execute ();
1164 addToUndoQueue (command);
1165 area->updateUndoState ();
1166 area->refresh ();
1167 }
1168
1169 void
createItem(geometricObject * item)1170 drgeoFigure::createItem (geometricObject * item)
1171 {
1172 drgeoCommand *command;
1173
1174 emptyUndoQueueEnd ();
1175
1176 command = new drgeoCreateItemCommand (this, item);
1177 command->execute ();
1178 addToUndoQueue (command);
1179 area->updateUndoState ();
1180 area->refresh ();
1181 }
1182
1183 void
moveItem(geometricObject * item,drgeoVector & t)1184 drgeoFigure::moveItem (geometricObject * item, drgeoVector & t)
1185 {
1186 // Build the command corresponding to a move
1187 // Do not move the item actually
1188 drgeoCommand *command;
1189
1190 emptyUndoQueueEnd ();
1191 if (item == NULL)
1192 item = (geometricObject *) selection.lire (1);
1193 /* use the selection list as items to move */
1194 command = new drgeoMoveItemCommand (this, item, t);
1195 addToUndoQueue (command);
1196 area->updateUndoState ();
1197 area->refresh ();
1198 }
1199
1200 void
1201 drgeoFigure::
setItemAttribute(geometricObject * item,drgeoAttributeType attr,gpointer value)1202 setItemAttribute (geometricObject * item, drgeoAttributeType attr,
1203 gpointer value)
1204 {
1205 drgeoCommand *command;
1206
1207 emptyUndoQueueEnd ();
1208 command = new drgeoSetItemAttributeCommand (this, item, attr, value);
1209 command->execute ();
1210 addToUndoQueue (command);
1211 area->updateUndoState ();
1212 area->refresh ();
1213 }
1214
1215 void
setPassword(gchar * newPassword)1216 drgeoFigure::setPassword (gchar * newPassword)
1217 {
1218 if (password)
1219 g_free (password);
1220 if (newPassword)
1221 password = g_strdup (newPassword);
1222 else
1223 password = NULL;
1224 }
1225
1226
1227 gchar *
getPassword()1228 drgeoFigure::getPassword ()
1229 {
1230 return password;
1231 }
1232
1233 void
setToolState(drgeoToolId id,gboolean visible)1234 drgeoFigure::setToolState (drgeoToolId id, gboolean visible)
1235 {
1236 showTool[id] = visible;
1237 }
1238
1239 gboolean
getToolState(drgeoToolId id)1240 drgeoFigure::getToolState (drgeoToolId id)
1241 {
1242 return showTool[id];
1243 }
1244
1245 gboolean
getShowGridState()1246 drgeoFigure::getShowGridState ()
1247 {
1248 return showGrid;
1249 }
1250
1251 void
setShowGridState(gboolean state)1252 drgeoFigure::setShowGridState (gboolean state)
1253 {
1254 showGrid = state;
1255 }
1256
1257 ScmInterface *
getScmInterface()1258 drgeoFigure::getScmInterface ()
1259 {
1260 return scmInterface;
1261 }
1262
1263 char
itemsUnderMouse(drgeoPoint & mouse,int toute)1264 drgeoFigure::itemsUnderMouse (drgeoPoint & mouse, int toute)
1265 {
1266 char trouve = FALSE, point = FALSE;
1267 int a, nb;
1268 geometricObject *fig;
1269
1270 // This method does compute the list of all figure items under the
1271 // mouse. Its trying to be smart when there is a point under the
1272 // mouse and lines. In this case it's only returning the point as
1273 // being under the mouse. This makes it easier to select points
1274 // with the mouse.
1275
1276 nb = figureList.nb_elem;
1277 figureList.init_lire ();
1278 underMouse.vide ();
1279
1280 for (a = 1; a <= nb; a++)
1281 {
1282 fig = (geometricObject *) figureList.lire (0);
1283 if (fig->overObject (mouse, area->getRange ())
1284 && (fig->getMask () == no
1285 || (fig->getMask () == yes && toute == TRUE)))
1286 {
1287 underMouse.ajoute ((void *) fig);
1288 trouve = TRUE;
1289 if (fig->getCategory () & POINT)
1290 point = TRUE;
1291 }
1292 }
1293 // If we have one point, we remove all other items from the list.
1294 if (point)
1295 {
1296 underMouse.init_lire ();
1297 nb = underMouse.nb_elem;
1298 do
1299 {
1300 fig = (geometricObject *) underMouse.lire (0);
1301 if ((fig->getCategory () & POINT) == 0)
1302 underMouse.supprime ((void *) fig);
1303 }
1304 while (--nb != 0);
1305 }
1306 return trouve;
1307 }
1308
1309 gint
preferedUndoLevel()1310 drgeoFigure::preferedUndoLevel ()
1311 {
1312 atoi (drgeoConfigGet (":undoLevel"));
1313 }
1314
1315 void
flashSelection(bool drawGrey)1316 drgeoFigure::flashSelection (bool drawGrey)
1317 {
1318 int nb;
1319 drgeoColorType color;
1320 geometricObject *fig;
1321
1322 if (mode == ANIMATION_MODE)
1323 return;
1324
1325 if (!blinkOn)
1326 return;
1327
1328 if (drawGrey)
1329 {
1330 // Display selected items in grey color, they are currently in
1331 // normal color.
1332 nb = selection.nb_elem;
1333 highlightItem.vide ();
1334 if (nb == 0)
1335 return;
1336 // Fill the highlightItem list with the selected items.
1337 concatene (highlightItem, selection);
1338 highlightItem.init_lire ();
1339 while (nb-- != 0)
1340 {
1341 fig = (geometricObject *) (highlightItem.lire (0));
1342 color = fig->getStyle ().color;
1343 fig->getStyle ().color = drgeoColorGrey;
1344 fig->draw (*area, FALSE);
1345 fig->getStyle ().color = color;
1346 }
1347 }
1348 else
1349 {
1350 // Display highlight items in normal color, they are actually
1351 // in grey color. There is just to redraw them.
1352 nb = highlightItem.nb_elem;
1353 highlightItem.init_lire ();
1354 while (nb-- != 0)
1355 {
1356 fig = (geometricObject *) (highlightItem.lire (0));
1357 fig->draw (*area, FALSE);
1358 }
1359 highlightItem.vide ();
1360 concatene (highlightItem, selection);
1361 }
1362 }
1363
1364 geometricObject *
xmlBuildItem(xmlNodePtr item,GHashTable * itemIdToAddress)1365 drgeoFigure::xmlBuildItem (xmlNodePtr item, GHashTable * itemIdToAddress)
1366 {
1367 gchar *itemType, *attr;
1368 geometricObject *fig = NULL;
1369 gint i;
1370
1371 itemType = (gchar *) item->name;
1372 if (!strcmp (itemType, "point"))
1373 {
1374 fig = new point (item, itemIdToAddress, &figureList);
1375 }
1376 else if (!strcmp (itemType, "line"))
1377 {
1378 fig = new line (item, itemIdToAddress, &figureList);
1379 }
1380 else if (!strcmp (itemType, "halfLine"))
1381 {
1382 fig = new halfLine (item, itemIdToAddress, &figureList);
1383 }
1384 else if (!strcmp (itemType, "segment"))
1385 {
1386 fig = new segment (item, itemIdToAddress, &figureList);
1387 }
1388 else if (!strcmp (itemType, "vector"))
1389 {
1390 fig = new vector (item, itemIdToAddress, &figureList);
1391 }
1392 else if (!strcmp (itemType, "circle"))
1393 {
1394 fig = new circle (item, itemIdToAddress, &figureList);
1395 }
1396 else if (!strcmp (itemType, "arcCircle"))
1397 {
1398 fig = new arcCircle (item, itemIdToAddress, &figureList);
1399 }
1400 else if (!strcmp (itemType, "locus"))
1401 {
1402 fig = new locus (item, itemIdToAddress, &figureList);
1403 }
1404 else if (!strcmp (itemType, "repere"))
1405 {
1406 /* FIXME: need some hack
1407 fig = new repere (item, itemIdToAddress, &figureList);
1408 */
1409 }
1410 else if (!strcmp (itemType, "angle"))
1411 {
1412 fig = new angle (item, itemIdToAddress, &figureList);
1413 }
1414 else if (!strcmp (itemType, "numeric"))
1415 {
1416 fig = new numeric (item, itemIdToAddress, &figureList);
1417 }
1418 else if (!strcmp (itemType, "equation"))
1419 {
1420 fig = new equation (item, itemIdToAddress, &figureList);
1421 }
1422 else if (!strcmp (itemType, "polygon"))
1423 {
1424 fig = new polygon (item, itemIdToAddress, &figureList);
1425 }
1426 else if (!strcmp (itemType, "script"))
1427 {
1428 fig = new script (item, itemIdToAddress, &figureList);
1429 }
1430 else if (!strcmp (itemType, "customUI"))
1431 {
1432 attr = (gchar *) xmlGetProp (item, BAD_CAST "password");
1433 setPassword (attr);
1434 g_free (attr);
1435 item = xml_search_next (item->xmlChildrenNode, "tool");
1436 while (item)
1437 {
1438 if (xml_get_value_int (item, "id", &i))
1439 {
1440 attr = (gchar *) xmlNodeGetContent (item);
1441 if (!strcmp (attr, "TRUE"))
1442 setToolState ((drgeoToolId) i, TRUE);
1443 else
1444 setToolState ((drgeoToolId) i, FALSE);
1445 g_free (attr);
1446 }
1447 item = xml_search_next (item->next, "tool");
1448 }
1449 }
1450 else
1451 {
1452 fprintf (stderr, "xmlBuildItem : unknown element %s\n", itemType);
1453 return NULL;
1454 }
1455 return fig;
1456 }
1457
1458 static void
_searchValue(gpointer key,gpointer value,gpointer data)1459 _searchValue (gpointer key, gpointer value, gpointer data)
1460 {
1461 if (((drgeoSearchValue *) data)->value == value)
1462 ((drgeoSearchValue *) data)->key = key;
1463 }
1464
1465 static void
_freeKeyFromHash(gpointer key,gpointer value,gpointer data)1466 _freeKeyFromHash (gpointer key, gpointer value, gpointer data)
1467 {
1468 g_free (key);
1469 }
1470