1 
2 /******************************************************************************
3 * MODULE     : edit_graphics.cpp
4 * DESCRIPTION: graphics between the editor and the window manager
5 * COPYRIGHT  : (C) 2003  Joris van der Hoeven and Henri Lesourd
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "Interface/edit_graphics.hpp"
13 #include "server.hpp"
14 #include "scheme.hpp"
15 #include "curve.hpp"
16 #include "Boxes/graphics.hpp"
17 #include "Bridge/impl_typesetter.hpp"
18 #include "drd_std.hpp"
19 
20 extern tree the_et;
21 
22 /******************************************************************************
23 * Constructors and destructors
24 ******************************************************************************/
25 
edit_graphics_rep()26 edit_graphics_rep::edit_graphics_rep () {
27   gr_x= gr_y= 0.0;
28   graphical_object= tree();
29 }
30 
~edit_graphics_rep()31 edit_graphics_rep::~edit_graphics_rep () {}
32 
33 /******************************************************************************
34 * Extra subroutines for graphical selections
35 ******************************************************************************/
36 
37 gr_selection
snap_to_guide(point p,gr_selections sels,double eps)38 snap_to_guide (point p, gr_selections sels, double eps) {
39   if (N(sels) == 0) {
40     gr_selection snap;
41     snap->type= "free";
42     snap->p= p;
43     snap->dist= 0;
44     return snap;
45   }
46 
47   sort (sels);
48   gr_selection best;
49   best->type= "none";
50   for (int i=0; i<N(sels); i++)
51     if (sels[i]->type == "grid-point")
52       best= sels[i];
53     else if (is_nil (sels[i]->c))
54       return sels[i];
55 
56   for (int i=0; i<N(sels); i++)
57     for (int j=i+1; j<N(sels); j++) {
58       if (!is_nil (sels[i]->c) &&
59 	  !is_nil (sels[j]->c) &&
60 	  (sels[i]->type != "grid-curve-point" ||
61 	   sels[j]->type != "grid-curve-point"))
62 	{
63 	  array<point> ins= intersection (sels[i]->c, sels[j]->c, p, eps);
64 	  for (int k=0; k<N(ins); k++)
65 	    if (best->type == "none" || norm (ins[k] - p) < best->dist) {
66 	      gr_selection sel;
67 	      sel->type= sels[i]->type * "&" * sels[j]->type;
68 	      sel->p   = ins[k];
69 	      sel->dist= (SI) norm (ins[k] - p);
70 	      sel->cp  = append (sels[i]->cp, sels[j]->cp);
71 	      sel->pts = append (sels[i]->pts, sels[j]->pts);
72 	      best= sel;
73 	    }
74 	}
75     }
76 
77   if (best->type != "none") return best;
78   return sels[0];
79 }
80 
81 /******************************************************************************
82 * Main edit_graphics routines
83 ******************************************************************************/
84 
85 path
graphics_path()86 edit_graphics_rep::graphics_path () {
87   path gp= search_upwards (GRAPHICS);
88   if (is_nil (gp)) return tp;
89   return gp * 0;
90 }
91 
92 bool
inside_graphics(bool b)93 edit_graphics_rep::inside_graphics (bool b) {
94   path p   = path_up (tp);
95   bool flag= false;
96   tree st  = et;
97   while (!is_nil (p)) {
98     if (is_func (st, GRAPHICS)) flag= true;
99     if (b && is_graphical_text (st)) flag= false;
100     if (is_atomic (st) || p->item < 0 || p->item >= N(st)) break;
101     st= st[p->item];
102     p = p->next;
103   }
104   return flag || (L(st) == GRAPHICS);
105 }
106 
107 bool
inside_active_graphics(bool b)108 edit_graphics_rep::inside_active_graphics (bool b) {
109   return inside_graphics (b) && get_env_string (PREAMBLE) == "false";
110 }
111 
112 bool
over_graphics(SI x,SI y)113 edit_graphics_rep::over_graphics (SI x, SI y) {
114   frame f= find_frame ();
115   if (!is_nil (f)) {
116     point lim1, lim2;
117     find_limits (lim1, lim2);
118     point p = adjust (f [point (x, y)]);
119     // cout << type << " at " << p << " [" << lim1 << ", " << lim2 << "]\n";
120     if (N(lim1) == 2)
121       if ((p[0]<lim1[0]) || (p[0]>lim2[0]) || (p[1]<lim1[1]) || (p[1]>lim2[1]))
122         return as_bool (call ("graphics-busy?"));
123     return true;
124   }
125   return false;
126 }
127 
128 tree
get_graphics()129 edit_graphics_rep::get_graphics () {
130   path p   = path_up (tp);
131   tree st  = et;
132   tree res = tree ();
133   while (!is_nil (p)) {
134     if (is_func (st, GRAPHICS)) res= st;
135     st= st[p->item];
136     p = p->next;
137   }
138   return res;
139 }
140 
141 double
get_x()142 edit_graphics_rep::get_x () {
143   return gr_x;
144 }
145 
146 double
get_y()147 edit_graphics_rep::get_y () {
148   return gr_y;
149 }
150 
151 frame
find_frame(bool last)152 edit_graphics_rep::find_frame (bool last) {
153   path gp= graphics_path ();
154   bool bp_found;
155   path bp= eb->find_box_path (gp, bp_found);
156   if (bp_found) return eb->find_frame (path_up (bp), last);
157   else return frame ();
158 }
159 
160 grid
find_grid()161 edit_graphics_rep::find_grid () {
162   path gp= graphics_path ();
163   bool bp_found;
164   path bp= eb->find_box_path (gp, bp_found);
165   if (bp_found) return eb->find_grid (path_up (bp));
166   else return grid ();
167 }
168 
169 void
find_limits(point & lim1,point & lim2)170 edit_graphics_rep::find_limits (point& lim1, point& lim2) {
171   path gp= graphics_path ();
172   lim1= point (); lim2= point ();
173   bool bp_found;
174   path bp= eb->find_box_path (gp, bp_found);
175   if (bp_found) eb->find_limits (path_up (bp), lim1, lim2);
176 }
177 
178 bool
find_graphical_region(SI & x1,SI & y1,SI & x2,SI & y2)179 edit_graphics_rep::find_graphical_region (SI& x1, SI& y1, SI& x2, SI& y2) {
180   point lim1, lim2;
181   find_limits (lim1, lim2);
182   if (lim1 == point ()) return false;
183   frame f= find_frame ();
184   if (is_nil (f)) return false;
185   point p1= f (point (lim1[0], lim1[1]));
186   point p2= f (point (lim2[0], lim2[1]));
187   x1= (SI) p1[0]; y1= (SI) p1[1];
188   x2= (SI) p2[0]; y2= (SI) p2[1];
189   return true;
190 }
191 
192 point
adjust(point p)193 edit_graphics_rep::adjust (point p) {
194   frame f= find_frame ();
195   grid g= find_grid ();
196   if (!is_nil (g) && !is_nil (gr0) && g != gr0) {
197     graphical_select (p[0], p[1]);
198     g= gr0;
199   }
200   if (is_nil (g)) return p;
201   point res;
202   gr_selections sels= copy (gs);
203   frame f2= find_frame (true);
204   if (is_nil (f2)) return p;
205   point fp= f2 (p);
206   if ((tree) g != "empty_grid") {
207     point q= g->find_point_around (p, 10*get_pixel_size (), f);
208     point fq= f2 (q);
209     if (norm (fq - fp) < 10*get_pixel_size ()) {
210       gr_selection sel;
211       sel->type= "grid-point";
212       sel->p   = fq;
213       sel->dist= (SI) norm (fq - fp);
214       sels << sel;
215     }
216     array<grid_curve> gc=
217       g->get_curves_around (p, 10*get_pixel_size (), f);
218     for (int i=0; i<N(gc); i++) {
219       point fc= closest (f2 (gc[i]->c), fp);
220       if (norm (fc - fp) < 10*get_pixel_size ()) {
221         gr_selection sel;
222         sel->type= "grid-curve-point";
223         sel->p   = fc;
224         sel->dist= (SI) norm (fc - fp);
225         sel->c   = f2 (gc[i]->c);
226         sels << sel;
227       }
228     }
229   }
230   double eps= get_pixel_size () / 10.0;
231   gr_selection snap= snap_to_guide (fp, sels, eps);
232   //cout << "Snap " << fp << " to " << snap << ", " << snap->p << "\n";
233   point snapped= f2[snap->p];
234   if (N(snapped) == 2) return snapped;
235   return p;
236   // FIXME: why can snapped be an invalid point?
237 }
238 
239 tree
find_point(point p)240 edit_graphics_rep::find_point (point p) {
241   return tree (_POINT, as_string (p[0]), as_string (p[1]));
242 }
243 
244 tree
graphical_select(double x,double y)245 edit_graphics_rep::graphical_select (double x, double y) {
246   frame f= find_frame ();
247   if (is_nil (f)) return tuple ();
248   gr_selections sels;
249   point p0 = point (x, y);
250   point p = f (p0);
251   sels= eb->graphical_select ((SI)p[0], (SI)p[1], 10*get_pixel_size ());
252   gs= sels;
253   gr0= empty_grid ();
254   grid g= find_grid ();
255   frame f2= find_frame (true);
256   if (!is_nil (g) && !is_nil (f2)) gr0= g;
257   return as_tree (sels);
258 }
259 
260 tree
graphical_select(double x1,double y1,double x2,double y2)261 edit_graphics_rep::graphical_select (
262   double x1, double y1, double x2, double y2)
263 {
264   frame f= find_frame ();
265   if (is_nil (f)) return tuple ();
266   gr_selections sels;
267   point p1 = f (point (x1, y1)), p2= f (point (x2, y2));
268   sels= eb->graphical_select ((SI)p1[0], (SI)p1[1], (SI)p2[0], (SI)p2[1]);
269   return as_tree (sels);
270 }
271 
272 tree
get_graphical_object()273 edit_graphics_rep::get_graphical_object () {
274   return graphical_object;
275 }
276 
277 void
set_graphical_object(tree t)278 edit_graphics_rep::set_graphical_object (tree t) {
279   go_box= box ();
280   graphical_object= t;
281   if (N (graphical_object) == 0) return;
282   edit_env env= get_typesetter ()->env;
283   //tree old_fr= env->local_begin (GR_FRAME, (tree) find_frame ());
284   frame f_env= env->fr;
285   env->fr= find_frame ();
286   if (!is_nil (env->fr)) {
287     int i,n=0;
288     go_box= typeset_as_concat (env, t, path (0));
289     for (i=0; i<N(go_box); i++)
290       if (go_box[i]!="") n++;
291     if (n) {
292       array<box> bx(n);
293       n=0;
294       for (i=0; i<N(go_box); i++) if (go_box[i]!="") {
295 	array<box> bx2(1);
296 	array<SI> spc2(1);
297 	bx2[0]= go_box[i];
298 	spc2[0]=0;
299 	bx[n]= concat_box (path (0), bx2, spc2);
300 	n++;
301       }
302       go_box= composite_box (path (0), bx);
303     }
304   }
305   env->fr= f_env;
306   //env->local_end (GR_FRAME, old_fr);
307 }
308 
309 void
invalidate_graphical_object()310 edit_graphics_rep::invalidate_graphical_object () {
311   SI gx1, gy1, gx2, gy2;
312   if (find_graphical_region (gx1, gy1, gx2, gy2) && !is_nil (go_box)) {
313     int i;
314     rectangles rs;
315     rectangle gr (gx1, gy1, gx2, gy2);
316     for (i=0; i<go_box->subnr(); i++) {
317       box b= go_box->subbox (i);
318       rs= rectangles (rectangle (b->x3, b->y3, b->x4, b->y4), rs);
319     }
320     rs= rs & rectangles (gr);
321     invalidate (rs);
322   }
323 }
324 
325 void
draw_graphical_object(renderer ren)326 edit_graphics_rep::draw_graphical_object (renderer ren) {
327   if (is_nil (go_box)) set_graphical_object (graphical_object);
328   if (is_nil (go_box)) return;
329   SI ox1, oy1, ox2, oy2;
330   ren->get_clipping (ox1, oy1, ox2, oy2);
331   SI gx1, gy1, gx2, gy2;
332   if (find_graphical_region (gx1, gy1, gx2, gy2))
333     ren->extra_clipping (gx1, gy1, gx2, gy2);
334   int i;
335   for (i=0; i<go_box->subnr(); i++) {
336     box b= go_box->subbox (i);
337     if ((tree)b=="point" || (tree)b=="curve")
338       b->display (ren);
339     else {
340       rectangles rs;
341       b->redraw (ren, path (), rs);
342     }
343   }
344   ren->set_clipping (ox1, oy1, ox2, oy2);
345 }
346 
347 void
back_in_text_at(tree t,path p,bool forward)348 edit_graphics_rep::back_in_text_at (tree t, path p, bool forward) {
349   int i= last_item (p);
350   if ((i == 0) && is_empty (t[0])) {
351     p= path_up (p);
352     if (is_func (subtree (et, path_up (p)), WITH)) p= path_up (p);
353     tree st= subtree (et, path_up (p));
354     if (is_func (st, GRAPHICS)) {
355       if (N(st) == 1) assign (p, "");
356       else {
357         remove (p, 1);
358         go_to_border (path_up (p) * 0, true);
359       }
360     }
361   }
362 }
363 
364 bool
mouse_graphics(string type,SI x,SI y,int m,time_t t)365 edit_graphics_rep::mouse_graphics (string type, SI x, SI y, int m, time_t t) {
366   //cout << type << ", " << x << ", " << y << ", " << m << ", " << t << "\n";
367   //cout << "et= " << et << "\n";
368   //cout << "tp= " << tp << "\n";
369   //cout << "gp= " << graphics_path () << "\n";
370   (void) t;
371   // apply_changes (); // FIXME: remove after review of synchronization
372   frame f= find_frame ();
373   if (!is_nil (f)) {
374     if (!over_graphics (x, y))
375       return false;
376     if (type == "move" || type == "dragging-left")
377       if (check_event (MOTION_EVENT))
378 	return true;
379     point p = f [point (x, y)];
380     graphical_select (p[0], p[1]); // init the caching for adjust().
381     p= adjust (p);
382     gr_x= p[0];
383     gr_y= p[1];
384     string sx= as_string (p[0]);
385     string sy= as_string (p[1]);
386     invalidate_graphical_object ();
387     call ("set-keyboard-modifiers", object (m));
388     if (type == "move")
389       call ("graphics-move", sx, sy);
390     else if (type == "release-left" || type == "double-left")
391       call ("graphics-release-left", sx, sy);
392     else if (type == "release-middle")
393       call ("graphics-release-middle", sx, sy);
394     else if (type == "release-right" || type == "double-right")
395       call ("graphics-release-right", sx, sy);
396     else if (type == "start-drag-left")
397       call ("graphics-start-drag-left", sx, sy);
398     else if (type == "dragging-left")
399       call ("graphics-dragging-left", sx, sy);
400     else if (type == "end-drag-left")
401       call ("graphics-end-drag-left", sx, sy);
402     else if (type == "start-drag-right")
403       call ("graphics-start-drag-right", sx, sy);
404     else if (type == "dragging-right")
405       call ("graphics-dragging-right", sx, sy);
406     else if (type == "end-drag-right")
407       call ("graphics-end-drag-right", sx, sy);
408     invalidate_graphical_object ();
409     notify_change (THE_CURSOR);
410     return true;
411   }
412   //cout << "No frame " << tp << ", " << subtree (et, path_up (tp)) << "\n";
413   return false;
414 }
415