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