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