1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "autohint.h"
31 #include "autosave.h"
32 #include "autotrace.h"
33 #include "autowidth.h"
34 #include "charview_private.h"
35 #include "cvruler.h"
36 #include "cvundoes.h"
37 #include "dlist.h"
38 #include "dumppfa.h"
39 #include "encoding.h"
40 #include "ffglib.h"
41 #include "fontforgeui.h"
42 #include "fvcomposite.h"
43 #include "fvfonts.h"
44 #include "gkeysym.h"
45 #include "gresedit.h"
46 #include "gresource.h"
47 #include "hotkeys.h"
48 #include "lookups.h"
49 #include "mm.h"
50 #include "namelist.h"
51 #include "prefs.h"
52 #include "sfd.h"
53 #include "spiro.h"
54 #include "splinefill.h"
55 #include "splineorder2.h"
56 #include "splineoverlap.h"
57 #include "splinesaveafm.h"
58 #include "splineutil.h"
59 #include "splineutil2.h"
60 #include "unicodelibinfo.h"
61 #include "ustring.h"
62 #include "utype.h"
63 #include "wordlistparser.h"
64 
65 #include <locale.h>
66 #include <math.h>
67 
68 #ifdef HAVE_IEEEFP_H
69 # include <ieeefp.h>		/* Solaris defines isnan in ieeefp rather than math.h */
70 #endif
71 
72 /* Barry wants to be able to redefine menu bindings only in the charview (I think) */
73 /*  the menu parser will first check for something like "CV*Open|Ctl+O", and */
74 /*  if that fails will strip off "CV*" and check for "Open|Ctl+O" */
75 #undef H_
76 #define H_(str) ("CV*" str)
77 
78 extern void UndoesFreeButRetainFirstN( Undoes** undopp, int retainAmount );
79 static void CVMoveInWordListByOffset( CharView* cv, int offset );
80 extern void CVDebugFree( DebugView *dv );
81 
82 extern int _GScrollBar_Width;
83 
84 int additionalCharsToShowLimit = 50;
85 
86 int ItalicConstrained=true;
87 float arrowAmount=1;
88 float arrowAccelFactor=10.;
89 float snapdistance=3.5;
90 float snapdistancemeasuretool=3.5;
91 int updateflex = false;
92 extern int clear_tt_instructions_when_needed;
93 int use_freetype_with_aa_fill_cv = 1;
94 int interpCPsOnMotion=false;
95 int DrawOpenPathsWithHighlight = 1;
96 #define default_cv_width 540
97 #define default_cv_height 540
98 int cv_width = default_cv_width;
99 int cv_height = default_cv_height;
100 int cv_show_fill_with_space = 1;
101 
102 #define prefs_cvEditHandleSize_default 5.0
103 float prefs_cvEditHandleSize = prefs_cvEditHandleSize_default;
104 
105 int   prefs_cvInactiveHandleAlpha = 255;
106 
107 int prefs_cv_show_control_points_always_initially = 0;
108 int prefs_create_dragging_comparison_outline = 0;
109 
110 extern struct lconv localeinfo;
111 extern char *coord_sep;
112 struct cvshows CVShows = {
113 	1,		/* show foreground */
114 	1,		/* show background */
115 	1,		/* show grid plane */
116 	1,		/* show horizontal hints */
117 	1,		/* show vertical hints */
118 	1,		/* show diagonal hints */
119 	1,		/* show points */
120 	0,		/* show filled */
121 	1,		/* show rulers */
122 	1,		/* show points which are to be rounded to the ttf grid and aren't on hints */
123 	1,		/* show x minimum distances */
124 	1,		/* show y minimum distances */
125 	1,		/* show horizontal metrics */
126 	1,		/* show vertical metrics */
127 	0,		/* mark extrema */
128 	0,		/* show points of inflection */
129 	1,		/* show blue values */
130 	1,		/* show family blues too */
131 	1,		/* show anchor points */
132 	0,		/* show control point info when moving them */
133 	1,		/* show tabs containing names of former glyphs */
134 	1,		/* show side bearings */
135 	1,		/* show the names of references */
136  	1,		/* snap outlines to pixel grid */
137 	0,		/* show lines which are almost, but not exactly horizontal or vertical */
138 	0,		/* show curves which are almost, but not exactly horizontal or vertical at the end-points */
139 	3,		/* number of em-units a coord difference must be less than to qualify for almost hv */
140 	1,		/* Check for self-intersections in the element view */
141 	1		/* In tt debugging, mark changed rasters differently */
142 };
143 struct cvshows CVShowsPrevewToggleSavedState;
144 
145 #define CID_Base	      1001
146 #define CID_getValueFromUser  CID_Base + 1
147 
148 
149 // Note that the default values supplied in CVColInit over-ride these values.
150 static Color pointcol = 0xff0000;
151 static Color subcol = 0xffffff;
152 static Color firstpointcol = 0x707000;
153 static Color selectedpointcol = 0xc8c800;
154 static int selectedpointwidth = 2;
155 static Color extremepointcol = 0xCAA80A;
156 static Color pointofinflectioncol = 0x008080;
157 static Color almosthvcol = 0x00ff80;
158 Color nextcpcol = 0x007090;
159 Color prevcpcol = 0xcc00cc;
160 static Color selectedcpcol = 0xffffff;
161 static Color coordcol = 0x808080;
162 Color widthcol = 0x000000;
163 static Color widthselcol = 0x00ff00;
164 static Color lbearingselcol = 0x00ff00;
165 static Color widthgridfitcol = 0x009800;
166 static Color lcaretcol = 0x909040;
167 static Color rastercol = 0xffa0a0a0;		/* Translucent */
168 static Color rasternewcol = 0xff909090;
169 static Color rasteroldcol = 0xffc0c0c0;
170 static Color rastergridcol = 0xffb0b0ff;
171 static Color rasterdarkcol = 0xff606060;
172 static Color deltagridcol = 0xcc0000;
173 static Color italiccoordcol = 0x909090;
174 static Color metricslabelcol = 0x00000;
175 static Color hintlabelcol = 0x00cccc;
176 static Color bluevalstipplecol = 0x808080ff;	/* Translucent */
177 static Color fambluestipplecol = 0x80ff7070;	/* Translucent */
178 static Color mdhintcol = 0x80e04040;		/* Translucent */
179 static Color dhintcol = 0x80d0a0a0;		/* Translucent */
180 static Color hhintcol = 0x80a0d0a0;		/* Translucent */
181 static Color vhintcol = 0x80c0c0ff;		/* Translucent */
182 static Color hflexhintcol = 0x00ff00;
183 static Color vflexhintcol = 0x00ff00;
184 static Color conflicthintcol = 0x00ffff;
185 static Color hhintactivecol = 0x00a000;
186 static Color vhintactivecol = 0x0000ff;
187 static Color anchorcol = 0x0040ff;
188 static Color anchoredoutlinecol = 0x0040ff;
189 static Color templateoutlinecol = 0x009800;
190 static Color oldoutlinecol = 0x008000;
191 static Color transformorigincol = 0x000000;
192 static Color guideoutlinecol = 0x808080;
193 static Color gridfitoutlinecol = 0x009800;
194 static Color backoutlinecol = 0x009800;
195 static Color foreoutlinecol = 0x000000;
196 static Color clippathcol = 0x0000ff;
197 static Color openpathcol = 0x40660000;
198 static Color backimagecol = 0x707070;
199 static Color fillcol = 0x80707070;		/* Translucent */
200 static Color tracecol = 0x008000;
201 static Color rulerbigtickcol = 0x008000;
202 static Color previewfillcol = 0x0f0f0f;
203 static Color DraggingComparisonOutlineColor = 0x8800BB00;
204 static Color DraggingComparisonAlphaChannelOverride = 0x88000000;
205 static Color foreoutthicklinecol = 0x20707070;
206 static Color backoutthicklinecol = 0x20707070;
207 int prefs_cv_outline_thickness = 1;
208 int cvbutton3d = 1;
209 Color cvbutton3dedgelightcol = 0xe0e0e0;
210 Color cvbutton3dedgedarkcol = 0x707070;
211 
212 // Format is 0x AA RR GG BB.
213 
214 static int CV_OnCharSelectorTextChanged( GGadget *g, GEvent *e );
215 static void CVHScrollSetPos( CharView *cv, int newpos );
216 
217 static void CVClear(GWindow,GMenuItem *mi, GEvent *);
218 static void CVMouseMove(CharView *cv, GEvent *event );
219 static void CVMouseUp(CharView *cv, GEvent *event );
220 static void CVHScroll(CharView *cv,struct sbevent *sb);
221 static void CVVScroll(CharView *cv,struct sbevent *sb);
222 /*static void CVElide(GWindow gw,struct gmenuitem *mi,GEvent *e);*/
223 static void CVMenuSimplify(GWindow gw,struct gmenuitem *mi,GEvent *e);
224 static void CVMenuSimplifyMore(GWindow gw,struct gmenuitem *mi,GEvent *e);
225 static void CVPreviewModeSet(GWindow gw, int checked);
226 static void CVExposeRulers(CharView *cv, GWindow pixmap);
227 
228 static int cvcolsinited = false;
229 
230 // Note that the GResource names for these preferences are defined separately in CVColInit.
231 // It would be wise to match any changes to these data structures with changes to the values in CVColInit.
232 
233 static struct resed charview_re[] = {
234     { N_("Point Color"), "PointColor", rt_color, &pointcol, N_("The color of an on-curve point"), NULL, { 0 }, 0, 0 },
235     { N_("First Point Color"), "FirstPointColor", rt_color, &firstpointcol, N_("The color of the point which is the start of a contour"), NULL, { 0 }, 0, 0 },
236     { N_("Selected Point Color"), "SelectedPointColor", rt_color, &selectedpointcol, N_("The color of a selected point"), NULL, { 0 }, 0, 0 },
237     { N_("Selected Point Width"), "SelectedPointWidth", rt_int, &selectedpointwidth, N_("The width of the line used to draw selected points"), NULL, { 0 }, 0, 0 },
238     { N_("Extrema Point Color"), "ExtremePointColor", rt_color, &extremepointcol, N_("The color used to draw points at extrema (if that mode is active)"), NULL, { 0 }, 0, 0 },
239     { N_("Point of Inflection Color"), "PointOfInflectionColor", rt_color, &pointofinflectioncol, N_("The color used to draw points of inflection (if that mode is active)"), NULL, { 0 }, 0, 0 },
240     { N_("Almost H/V Color"), "AlmostHVColor", rt_color, &almosthvcol, N_("The color used to draw markers for splines which are almost, but not quite horizontal or vertical at their end-points"), NULL, { 0 }, 0, 0 },
241     { N_("Next CP Color"), "NextCPColor", rt_color, &nextcpcol, N_("The color used to draw the \"next\" control point of an on-curve point"), NULL, { 0 }, 0, 0 },
242     { N_("Prev CP Color"), "PrevCPColor", rt_color, &prevcpcol, N_("The color used to draw the \"previous\" control point of an on-curve point"), NULL, { 0 }, 0, 0 },
243     { N_("Selected CP Color"), "SelectedCPColor", rt_color, &selectedcpcol, N_("The color used to draw a selected control point of an on-curve point"), NULL, { 0 }, 0, 0 },
244     { N_("Coordinate Line Color"), "CoordinateLineColor", rt_color, &coordcol, NULL, NULL, { 0 }, 0, 0 },
245     { N_("Italic Coord. Color"), "ItalicCoordColor", rt_color, &italiccoordcol, NULL, NULL, { 0 }, 0, 0 },
246     { N_("Metrics Label Color"), "MetricsLabelColor", rt_color, &metricslabelcol, NULL, NULL, { 0 }, 0, 0 },
247     { N_("Hint Label Color"), "HintLabelColor", rt_color, &hintlabelcol,NULL, NULL, { 0 }, 0, 0 },
248     { N_("Blue Values Color"), "BlueValuesStippledColor", rt_coloralpha, &bluevalstipplecol, N_("The color used to mark blue zones in the blue values entry of the private dictionary"), NULL, { 0 }, 0, 0 },
249     { N_("Family Blue Color"), "FamilyBlueStippledColor", rt_coloralpha, &fambluestipplecol, N_("The color used to mark blue zones in the family blues entry of the private dictionary"), NULL, { 0 }, 0, 0 },
250     { N_("Diagonal Hint Color"), "DHintColor", rt_coloralpha, &dhintcol, N_("The color used to draw diagonal hints"), NULL, { 0 }, 0, 0 },
251     { N_("Horiz. Hint Color"), "HHintColor", rt_coloralpha, &hhintcol, N_("The color used to draw horizontal hints"), NULL, { 0 }, 0, 0 },
252     { N_("Vert. Hint Color"), "VHintColor", rt_coloralpha, &vhintcol, N_("The color used to draw vertical hints"), NULL, { 0 }, 0, 0 },
253     { N_("HFlex Hint Color"), "HFlexHintColor", rt_color, &hflexhintcol, NULL, NULL, { 0 }, 0, 0 },
254     { N_("VFlex Hint Color"), "VFlexHintColor", rt_color, &vflexhintcol, NULL, NULL, { 0 }, 0, 0 },
255     { N_("Conflict Hint Color"), "ConflictHintColor", rt_color, &conflicthintcol, N_("The color used to draw a hint which conflicts with another"), NULL, { 0 }, 0, 0 },
256     { N_("HHint Active Color"), "HHintActiveColor", rt_color, &hhintactivecol, N_("The color used to draw the active horizontal hint which the Review Hints dialog is examining"), NULL, { 0 }, 0, 0 },
257     { N_("VHint Active Color"), "VHintActiveColor", rt_color, &vhintactivecol, N_("The color used to draw the active vertical hint which the Review Hints dialog is examining"), NULL, { 0 }, 0, 0 },
258     { N_("Dragging Comparison Outline Color"), "DraggingComparisonOutlineColor", rt_coloralpha, &DraggingComparisonOutlineColor, N_("The color used to draw the outline of the old spline when you are interactively modifying a glyph"), NULL, { 0 }, 0, 0 },
259     { N_("Dragging Comparison Outline Color"), "DraggingComparisonAlphaChannelOverride", rt_coloralpha, &DraggingComparisonAlphaChannelOverride, N_("Only the alpha value is used and if non zero it will set the alpha channel for the control points, bezier information and other non spline indicators for the Dragging Comparison Outline spline"), NULL, { 0 }, 0, 0 },
260     RESED_EMPTY
261 };
262 
263 static struct resed charview2_re[] = {
264     { N_("Width Color"), "WidthColor", rt_color, &widthcol, N_("The color of the line marking the advance width"), NULL, { 0 }, 0, 0 },
265     { N_("Selected Width Color"), "WidthSelColor", rt_color, &widthselcol, N_("The color of the line marking the advance width when it is selected"), NULL, { 0 }, 0, 0 },
266     { N_("Selected LBearing Color"), "LBearingSelColor", rt_color, &lbearingselcol, N_("The color of the line marking the left bearing when it is selected"), NULL, { 0 }, 0, 0 },
267     { N_("Grid Fit Width Color"), "GridFitWidthColor", rt_color, &widthgridfitcol, N_("The color of the line marking the grid-fit advance width"), NULL, { 0 }, 0, 0 },
268     { N_("Ligature Caret Color"), "LigatureCaretColor", rt_color, &lcaretcol, N_("The color of the line(s) marking ligature carets"), NULL, { 0 }, 0, 0 },
269     { N_("Anchor Color"), "AnchorColor", rt_color, &anchorcol, N_("The color of anchor stars"), NULL, { 0 }, 0, 0 },
270     { N_("Anchored Line Color"), "AnchoredOutlineColor", rt_color, &anchoredoutlinecol, N_("The color of another glyph drawn in the current view to show where it would be placed by an anchor lookup"), NULL, { 0 }, 0, 0 },
271     { N_("Template Color"), "TemplateOutlineColor", rt_color, &templateoutlinecol, NULL, NULL, { 0 }, 0, 0 },
272     { N_("Old Outline Color"), "OldOutlineColor", rt_color, &oldoutlinecol, NULL, NULL, { 0 }, 0, 0 },
273     { N_("Original Color"), "TransformOriginColor", rt_color, &transformorigincol, NULL, NULL, { 0 }, 0, 0 },
274     { N_("Guide Layer Color"), "GuideOutlineColor", rt_color, &guideoutlinecol, NULL, NULL, { 0 }, 0, 0 },
275     { N_("Grid Fit Color"), "GridFitOutlineColor", rt_color, &gridfitoutlinecol, N_("The color of grid-fit outlines"), NULL, { 0 }, 0, 0 },
276     { N_("Inactive Layer Color"), "BackgroundOutlineColor", rt_color, &backoutlinecol, N_("The color of outlines in inactive layers"), NULL, { 0 }, 0, 0 },
277     { N_("Active Layer Color"), "ForegroundOutlineColor", rt_color, &foreoutlinecol, N_("The color of outlines in the active layer"), NULL, { 0 }, 0, 0 },
278     { N_("Inactive Thick Layer Color"), "BackgroundThickOutlineColor", rt_coloralpha, &backoutthicklinecol, N_("The color of thick outlines in inactive layers"), NULL, { 0 }, 0, 0 },
279     { N_("Active Thick Layer Color"), "ForegroundThickOutlineColor", rt_coloralpha, &foreoutthicklinecol, N_("The color of thick outlines in the active layer"), NULL, { 0 }, 0, 0 },
280     { N_("Clip Path Color"), "ClipPathColor", rt_color, &clippathcol, N_("The color of the clip path"), NULL, { 0 }, 0, 0 },
281     { N_("Open Path Color"), "OpenPathColor", rt_coloralpha, &openpathcol, N_("The color of the open path"), NULL, { 0 }, 0, 0 },
282     { N_("Background Image Color"), "BackgroundImageColor", rt_coloralpha, &backimagecol, N_("The color used to draw bitmap (single bit) images which do not specify a clut"), NULL, { 0 }, 0, 0 },
283     { N_("Fill Color"), "FillColor", rt_coloralpha, &fillcol, N_("The color used to fill the outline if that mode is active"), NULL, { 0 }, 0, 0 },
284     { N_("Preview Fill Color"), "PreviewFillColor", rt_coloralpha, &previewfillcol, N_("The color used to fill the outline when in preview mode"), NULL, { 0 }, 0, 0 },
285     { N_("Trace Color"), "TraceColor", rt_color, &tracecol, NULL, NULL, { 0 }, 0, 0 },
286     { N_("Raster Color"), "RasterColor", rt_coloralpha, &rastercol, N_("The color of grid-fit (and other) raster blocks"), NULL, { 0 }, 0, 0 },
287     { N_("Raster New Color"), "RasterNewColor", rt_coloralpha, &rasternewcol, N_("The color of raster blocks which have just been turned on (in the debugger when an instruction moves a point)"), NULL, { 0 }, 0, 0 },
288     { N_("Raster Old Color"), "RasterOldColor", rt_coloralpha, &rasteroldcol, N_("The color of raster blocks which have just been turned off (in the debugger when an instruction moves a point)"), NULL, { 0 }, 0, 0 },
289     { N_("Raster Grid Color"), "RasterGridColor", rt_coloralpha, &rastergridcol, NULL, NULL, { 0 }, 0, 0 },
290     { N_("Raster Dark Color"), "RasterDarkColor", rt_coloralpha, &rasterdarkcol, N_("When debugging in grey-scale this is the color of a raster block which is fully covered."), NULL, { 0 }, 0, 0 },
291     { N_("Delta Grid Color"), "DeltaGridColor", rt_color, &deltagridcol, N_("Indicates a notable grid pixel when suggesting deltas."), NULL, { 0 }, 0, 0 },
292     { N_("Ruler Big Tick Color"), "RulerBigTickColor", rt_color, &rulerbigtickcol, N_("The color used to draw the large tick marks in rulers."), NULL, { 0 }, 0, 0 },
293     { N_("Measure Tool Line Color"), "MeasureToolLineColor", rt_color, &measuretoollinecol, N_("The color used to draw the measure tool line."), NULL, { 0 }, 0, 0 },
294     { N_("Measure Tool Point Color"), "MeasureToolPointColor", rt_color, &measuretoolpointcol, N_("The color used to draw the measure tool points."), NULL, { 0 }, 0, 0 },
295     { N_("Measure Tool Point Snapped Color"), "MeasureToolPointSnappedColor", rt_color, &measuretoolpointsnappedcol, N_("The color used to draw the measure tool points when snapped."), NULL, { 0 }, 0, 0 },
296     { N_("Measure Tool Canvas Number Color"), "MeasureToolCanvasNumbersColor", rt_color, &measuretoolcanvasnumberscol, N_("The color used to draw the measure tool numbers on the canvas."), NULL, { 0 }, 0, 0 },
297     { N_("Measure Tool Canvas Number Snapped Color"), "MeasureToolCanvasNumbersSnappedColor", rt_color, &measuretoolcanvasnumberssnappedcol, N_("The color used to draw the measure tool numbers on the canvas when snapped."), NULL, { 0 }, 0, 0 },
298     { N_("Measure Tool Windows Foreground Color"), "MeasureToolWindowForeground", rt_color, &measuretoolwindowforegroundcol, N_("The measure tool window foreground color."), NULL, { 0 }, 0, 0 },
299     { N_("Measure Tool Windows Background Color"), "MeasureToolWindowBackground", rt_color, &measuretoolwindowbackgroundcol, N_("The measure tool window background color."), NULL, { 0 }, 0, 0 },
300     RESED_EMPTY
301 };
302 
303 /* return 1 if anything changed */
update_spacebar_hand_tool(CharView * cv)304 static void update_spacebar_hand_tool(CharView *cv) {
305     if ( GDrawKeyState(cv->v, ' ') ) {
306 	if ( !cv->spacebar_hold  && !cv_auto_goto ) {
307 	    cv->spacebar_hold = 1;
308 	    cv->b1_tool_old = cv->b1_tool;
309 	    cv->b1_tool = cvt_hand;
310 	    cv->active_tool = cvt_hand;
311 	    CVMouseDownHand(cv);
312 	    CVPreviewModeSet(cv->gw, cv_show_fill_with_space);
313 	}
314     } else {
315 	if ( cv->spacebar_hold ) {
316 	    cv->spacebar_hold = 0;
317 	    cv->b1_tool = cv->b1_tool_old;
318 	    cv->active_tool = cvt_none;
319 	    cv->b1_tool_old = cvt_none;
320 	    CVPreviewModeSet(cv->gw, false);
321 	}
322     }
323 }
324 
325 
CVInSpiro(CharView * cv)326 int CVInSpiro( CharView *cv )
327 {
328     int inspiro = 0;
329     int canspiro = hasspiro();
330     if( cv )
331 	inspiro = canspiro && cv->b.sc->inspiro;
332     return inspiro;
333 }
334 
335 /**
336  * Returns the number of points which are currently selected in this
337  * charview. Handy for menus and the like which might like to grey out
338  * if there are <2, or <3 points actively selected.
339  */
CVCountSelectedPoints(CharView * cv)340 int CVCountSelectedPoints(CharView *cv) {
341     SplinePointList *spl;
342     Spline *spline, *first;
343     int ret = 0;
344 
345     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
346 	first = NULL;
347 	for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
348 	    if( spline == spl->first->next ) {
349 		if ( spline->from->selected ) {
350 		    ret++;
351 		}
352 	    }
353 	    if ( spline->to->selected ) {
354 		if( spline->to != spl->first->next->from )
355 		    ret++;
356 	    }
357 	    if ( first==NULL ) {
358 		first = spline;
359 	    }
360 	}
361     }
362     return ret;
363 }
364 
365 
366 
367 /* floor(pt) would _not_ be more correct, as we want
368  * shapes not to cross axes multiple times while scaling.
369  */
rpt(CharView * cv,double pt)370 static double rpt(CharView *cv, double pt) {
371     return cv->snapoutlines ? rint(pt) : pt;
372 }
373 
shouldShowFilledUsingCairo(CharView * cv)374 static int shouldShowFilledUsingCairo(CharView *cv) {
375     if ( cv->showfilled && GDrawHasCairo(cv->v) & gc_buildpath ) {
376 	return 1;
377     }
378     return 0;
379 }
380 
CVColInit(void)381 void CVColInit( void ) {
382     if ( cvcolsinited )
383 return;
384     GResEditFind( charview_re, "CharView.");
385     GResEditFind( charview2_re, "CharView.");
386     cvcolsinited = true;
387 
388   // These value over-ride the static initializers.
389   // Note that the base resource names are copied from charview_re and charview2_re.
390   pointcol = GResourceFindColor("CharView.PointColor",0xff0000);
391   firstpointcol = GResourceFindColor("CharView.FirstPointColor",0x707000);
392   selectedpointcol = GResourceFindColor("CharView.SelectedPointColor",0xc8c800);
393   selectedpointwidth = GResourceFindInt("CharView.SelectedPointWidth",2);
394   extremepointcol = GResourceFindColor("CharView.ExtremePointColor",0xc00080);
395   pointofinflectioncol = GResourceFindColor("CharView.PointOfInflectionColor",0x008080);
396   almosthvcol = GResourceFindColor("CharView.AlmostHVColor",0x00ff80);
397   nextcpcol = GResourceFindColor("CharView.NextCPColor",0x007090);
398   prevcpcol = GResourceFindColor("CharView.PointColor",0xcc00cc);
399   selectedcpcol = GResourceFindColor("CharView.SelectedCPColor",0xffffff);
400   coordcol = GResourceFindColor("CharView.CoordinateColor",0x808080);
401   widthcol = GResourceFindColor("CharView.WidthColor",0x000000);
402   widthselcol = GResourceFindColor("CharView.WidthSelColor",0x00ff00);
403   lbearingselcol = GResourceFindColor("CharView.LBearingSelColor",0x00ff00);
404   widthgridfitcol = GResourceFindColor("CharView.GridFitWidthColor",0x009800);
405   lcaretcol = GResourceFindColor("CharView.LigatureCaretColor",0x909040);
406   rastercol = GResourceFindColor("CharView.RasterColor",0xffa0a0a0);		/* Translucent */
407   rasternewcol = GResourceFindColor("CharView.RasterNewColor",0xff909090);
408   rasteroldcol = GResourceFindColor("CharView.RasterOldColor",0xffc0c0c0);
409   rastergridcol = GResourceFindColor("CharView.RasterGridColor",0xffb0b0ff);
410   rasterdarkcol = GResourceFindColor("CharView.RasterDarkColor",0xff606060);
411   deltagridcol = GResourceFindColor("CharView.DeltaGridColor",0xcc0000);
412   italiccoordcol = GResourceFindColor("CharView.ItalicCoordColor",0x909090);
413   metricslabelcol = GResourceFindColor("CharView.MetricsLabelColor",0x00000);
414   hintlabelcol = GResourceFindColor("CharView.HintLabelColor",0x00cccc);
415   bluevalstipplecol = GResourceFindColor("CharView.BlueValuesStippledColor",0x808080ff);	/* Translucent */
416   fambluestipplecol = GResourceFindColor("CharView.FamilyBlueStippledColor",0x80ff7070);	/* Translucent */
417   mdhintcol = GResourceFindColor("CharView.xxxxxx",0x80e04040);		/* Translucent */
418   dhintcol = GResourceFindColor("CharView.DHintColor",0x80d0a0a0);		/* Translucent */
419   hhintcol = GResourceFindColor("CharView.HHintColor",0x80a0d0a0);		/* Translucent */
420   vhintcol = GResourceFindColor("CharView.VHintColor",0x80c0c0ff);		/* Translucent */
421   hflexhintcol = GResourceFindColor("CharView.HFlexHintColor",0x00ff00);
422   vflexhintcol = GResourceFindColor("CharView.VFlexHintColor",0x00ff00);
423   conflicthintcol = GResourceFindColor("CharView.ConflictHintColor",0x00ffff);
424   hhintactivecol = GResourceFindColor("CharView.HHintActiveColor",0x00a000);
425   vhintactivecol = GResourceFindColor("CharView.VHintActiveColor",0x0000ff);
426   anchorcol = GResourceFindColor("CharView.AnchorColor",0x0040ff);
427   anchoredoutlinecol = GResourceFindColor("CharView.AnchoredOutlineColor",0x0040ff);
428   templateoutlinecol = GResourceFindColor("CharView.TemplateOutlineColor",0x009800);
429   oldoutlinecol = GResourceFindColor("CharView.OldOutlineColor",0x008000);
430   transformorigincol = GResourceFindColor("CharView.TransformOriginColor",0x000000);
431   guideoutlinecol = GResourceFindColor("CharView.GuideOutlineColor",0x808080);
432   gridfitoutlinecol = GResourceFindColor("CharView.GridFitOutlineColor",0x009800);
433   backoutlinecol = GResourceFindColor("CharView.BackgroundOutlineColor",0x009800);
434   foreoutlinecol = GResourceFindColor("CharView.ForegroundOutlineColor",0x000000);
435   clippathcol = GResourceFindColor("CharView.ClipPathColor",0x0000ff);
436   openpathcol = GResourceFindColor("CharView.OpenPathColor",0x40660000);
437   backimagecol = GResourceFindColor("CharView.BackgroundImageColor",0x707070);
438   fillcol = GResourceFindColor("CharView.FillColor",0x80707070);		/* Translucent */
439   tracecol = GResourceFindColor("CharView.TraceColor",0x008000);
440   rulerbigtickcol = GResourceFindColor("CharView.RulerBigTickColor",0x008000);
441   // previewfillcol = GResourceFindColor(,0x0f0f0f);
442   // The code below defaults differently from the static initializer (from which we copied this value).
443     if( GResourceFindColor("CharView.PreviewFillColor", COLOR_UNKNOWN) == COLOR_UNKNOWN ) {
444 	// no explicit previewfillcolor
445 	previewfillcol = fillcol;
446 	if( GResourceFindColor("CharView.FillColor", COLOR_UNKNOWN) == COLOR_UNKNOWN ) {
447 	    // no explicit fill color either
448 	    previewfillcol = 0x000000;
449 	}
450     }
451   DraggingComparisonOutlineColor = GResourceFindColor("CharView.DraggingComparisonOutlineColor",0x8800BB00);
452   DraggingComparisonAlphaChannelOverride = GResourceFindColor("CharView.DraggingComparisonAlphaChannelOverride",0x88000000);
453   foreoutthicklinecol = GResourceFindColor("CharView.ForegroundThickOutlineColor",0x20707070);
454   backoutthicklinecol = GResourceFindColor("CharView.BackgroundThickOutlineColor",0x20707070);
455   cvbutton3d = GResourceFindInt("CharView.Button3D", 1);
456   cvbutton3dedgelightcol = GResourceFindColor("CharView.Button3DEdgeLightColor", 0xe0e0e0);
457   cvbutton3dedgedarkcol = GResourceFindColor("CharView.Button3DEdgeDarkColor", 0x707070);
458 }
459 
460 
461 GDevEventMask input_em[] = {
462 	/* Event masks for wacom devices */
463     /* negative utility in opening Mouse1 */
464     /* No point in distinguishing cursor from core mouse */
465     { (1<<et_mousemove)|(1<<et_mousedown)|(1<<et_mouseup)|(1<<et_char), "stylus" },
466     { (1<<et_mousemove)|(1<<et_mousedown)|(1<<et_mouseup), "eraser" },
467     { 0, NULL }
468 };
469 const int input_em_cnt = sizeof(input_em)/sizeof(input_em[0])-1;
470 
471 /* Positions on the info line */
472 #define RPT_BASE	5		/* Place to draw the pointer icon */
473 #define RPT_DATA	13		/* x,y text after above */
474 #define SPT_BASE	83		/* Place to draw selected pt icon */
475 #define SPT_DATA	97		/* Any text for it */
476 #define SOF_BASE	157		/* Place to draw selection to pointer icon */
477 #define SOF_DATA	179		/* Any text for it */
478 #define SDS_BASE	259		/* Place to draw distance icon */
479 #define SDS_DATA	281		/* Any text for it */
480 #define SAN_BASE	331		/* Place to draw angle icon */
481 #define SAN_DATA	353		/* Any text for it */
482 #define MAG_BASE	383		/* Place to draw magnification icon */
483 #define MAG_DATA	394		/* Any text for it */
484 #define LAYER_DATA	454		/* Text to show the current layer */
485 #define CODERANGE_DATA	574		/* Text to show the current code range (if the debugger be active) */
486 #define FLAGS_DATA	724		/* Text to show the current drawmode flags */
487 
CVGetActiveTab(CharView * cv)488 CharViewTab* CVGetActiveTab(CharView *cv) {
489     if (cv->tabs == NULL) {
490         // This happens when used by the bitmap view, which doesn't support tabs
491         return &cv->cvtabs[0];
492     }
493     int tab = GTabSetGetSel(cv->tabs);
494     return &cv->cvtabs[tab];
495 }
496 
CVDrawRubberRect(GWindow pixmap,CharView * cv)497 void CVDrawRubberRect(GWindow pixmap, CharView *cv) {
498     GRect r;
499     CharViewTab *tab = CVGetActiveTab(cv);
500     if ( !cv->p.rubberbanding )
501 return;
502     r.x =  tab->xoff + rint(cv->p.cx*tab->scale);
503     r.y = -tab->yoff + cv->height - rint(cv->p.cy*tab->scale);
504     r.width = rint( (cv->p.ex-cv->p.cx)*tab->scale);
505     r.height = -rint( (cv->p.ey-cv->p.cy)*tab->scale);
506     if ( r.width<0 ) {
507 	r.x += r.width;
508 	r.width = -r.width;
509     }
510     if ( r.height<0 ) {
511 	r.y += r.height;
512 	r.height = -r.height;
513     }
514     GDrawSetDashedLine(pixmap,2,2,0);
515     GDrawSetLineWidth(pixmap,0);
516     GDrawDrawRect(pixmap,&r,oldoutlinecol);
517     GDrawSetDashedLine(pixmap,0,0,0);
518 }
519 
CVDrawRubberLine(GWindow pixmap,CharView * cv)520 static void CVDrawRubberLine(GWindow pixmap, CharView *cv) {
521     int x,y, xend,yend;
522     CharViewTab* tab = CVGetActiveTab(cv);
523     Color col = cv->active_tool==cvt_ruler ? measuretoollinecol : oldoutlinecol;
524     if ( !cv->p.rubberlining )
525 return;
526     x =  tab->xoff + rint(cv->p.cx*tab->scale);
527     y = -tab->yoff + cv->height - rint(cv->p.cy*tab->scale);
528     xend =  tab->xoff + rint(cv->info.x*tab->scale);
529     yend = -tab->yoff + cv->height - rint(cv->info.y*tab->scale);
530 	GDrawSetLineWidth(pixmap,0);
531     GDrawDrawLine(pixmap,x,y,xend,yend,col);
532 }
533 
CVDrawBB(CharView * cv,GWindow pixmap,DBounds * bb)534 static void CVDrawBB(CharView *cv, GWindow pixmap, DBounds *bb) {
535     GRect r;
536     CharViewTab* tab = CVGetActiveTab(cv);
537     int off = tab->xoff+cv->height-tab->yoff;
538 
539     r.x =  tab->xoff + rint(bb->minx*tab->scale);
540     r.y = -tab->yoff + cv->height - rint(bb->maxy*tab->scale);
541     r.width = rint((bb->maxx-bb->minx)*tab->scale);
542     r.height = rint((bb->maxy-bb->miny)*tab->scale);
543     GDrawSetDashedLine(pixmap,1,1,off);
544     GDrawDrawRect(pixmap,&r,GDrawGetDefaultForeground(NULL));
545     GDrawSetDashedLine(pixmap,0,0,0);
546 }
547 
548 /* Sigh. I have to do my own clipping because at large magnifications */
549 /*  things can easily exceed 16 bits */
CVSplineOutside(CharView * cv,Spline * spline)550 static int CVSplineOutside(CharView *cv, Spline *spline) {
551     CharViewTab* tab = CVGetActiveTab(cv);
552     int x[4], y[4];
553 
554     x[0] =  tab->xoff + rint(spline->from->me.x*tab->scale);
555     y[0] = -tab->yoff + cv->height - rint(spline->from->me.y*tab->scale);
556 
557     x[1] =  tab->xoff + rint(spline->to->me.x*tab->scale);
558     y[1] = -tab->yoff + cv->height - rint(spline->to->me.y*tab->scale);
559 
560     if ( spline->from->nonextcp && spline->to->noprevcp ) {
561 	if ( (x[0]<0 && x[1]<0) || (x[0]>=cv->width && x[1]>=cv->width) ||
562 		(y[0]<0 && y[1]<0) || (y[0]>=cv->height && y[1]>=cv->height) )
563 return( true );
564     } else {
565 	x[2] =  tab->xoff + rint(spline->from->nextcp.x*tab->scale);
566 	y[2] = -tab->yoff + cv->height - rint(spline->from->nextcp.y*tab->scale);
567 	x[3] =  tab->xoff + rint(spline->to->prevcp.x*tab->scale);
568 	y[3] = -tab->yoff + cv->height - rint(spline->to->prevcp.y*tab->scale);
569 	if ( (x[0]<0 && x[1]<0 && x[2]<0 && x[3]<0) ||
570 		(x[0]>=cv->width && x[1]>=cv->width && x[2]>=cv->width && x[3]>=cv->width ) ||
571 		(y[0]<0 && y[1]<0 && y[2]<0 && y[3]<0 ) ||
572 		(y[0]>=cv->height && y[1]>=cv->height && y[2]>=cv->height && y[3]>=cv->height) )
573 return( true );
574     }
575 
576 return( false );
577 }
578 
CVLinesIntersectScreen(CharView * cv,LinearApprox * lap)579 static int CVLinesIntersectScreen(CharView *cv, LinearApprox *lap) {
580     CharViewTab* tab = CVGetActiveTab(cv);
581     LineList *l;
582     int any = false;
583     int x,y;
584     int bothout;
585 
586     for ( l=lap->lines; l!=NULL; l=l->next ) {
587 	l->asend.x = l->asstart.x = tab->xoff + l->here.x;
588 	l->asend.y = l->asstart.y = -tab->yoff + cv->height-l->here.y;
589 	l->flags = 0;
590 	if ( l->asend.x<0 || l->asend.x>=cv->width || l->asend.y<0 || l->asend.y>=cv->height ) {
591 	    l->flags = cvli_clipped;
592 	    any = true;
593 	}
594     }
595     if ( !any ) {
596 	for ( l=lap->lines; l!=NULL; l=l->next )
597 	    l->flags = cvli_onscreen;
598 	lap->any = true;
599 return( true );
600     }
601 
602     any = false;
603     for ( l=lap->lines; l->next!=NULL; l=l->next ) {
604 	if ( !(l->flags&cvli_clipped) && !(l->next->flags&cvli_clipped) )
605 	    l->flags = cvli_onscreen;
606 	else {
607 	    bothout = (l->flags&cvli_clipped) && (l->next->flags&cvli_clipped);
608 	    if (( l->asstart.x<0 && l->next->asend.x>0 ) ||
609 		    ( l->asstart.x>0 && l->next->asend.x<0 )) {
610 		y = -(l->next->asend.y-l->asstart.y)*(double)l->asstart.x/(l->next->asend.x-l->asstart.x) +
611 			l->asstart.y;
612 		if ( l->asstart.x<0 ) {
613 		    l->asstart.x = 0;
614 		    l->asstart.y = y;
615 		} else {
616 		    l->next->asend.x = 0;
617 		    l->next->asend.y = y;
618 		}
619 	    } else if ( l->asstart.x<0 && l->next->asend.x<0 )
620     continue;
621 	    if (( l->asstart.x<cv->width && l->next->asend.x>cv->width ) ||
622 		    ( l->asstart.x>cv->width && l->next->asend.x<cv->width )) {
623 		y = (l->next->asend.y-l->asstart.y)*(double)(cv->width-l->asstart.x)/(l->next->asend.x-l->asstart.x) +
624 			l->asstart.y;
625 		if ( l->asstart.x>cv->width ) {
626 		    l->asstart.x = cv->width;
627 		    l->asstart.y = y;
628 		} else {
629 		    l->next->asend.x = cv->width;
630 		    l->next->asend.y = y;
631 		}
632 	    } else if ( l->asstart.x>cv->width && l->next->asend.x>cv->width )
633     continue;
634 	    if (( l->asstart.y<0 && l->next->asend.y>0 ) ||
635 		    ( l->asstart.y>0 && l->next->asend.y<0 )) {
636 		x = -(l->next->asend.x-l->asstart.x)*(double)l->asstart.y/(l->next->asend.y-l->asstart.y) +
637 			l->asstart.x;
638 		if (( x<0 || x>=cv->width ) && bothout )
639     continue;			/* Not on screen */;
640 		if ( l->asstart.y<0 ) {
641 		    l->asstart.y = 0;
642 		    l->asstart.x = x;
643 		} else {
644 		    l->next->asend.y = 0;
645 		    l->next->asend.x = x;
646 		}
647 	    } else if ( l->asstart.y<0 && l->next->asend.y< 0 )
648     continue;
649 	    if (( l->asstart.y<cv->height && l->next->asend.y>cv->height ) ||
650 		    ( l->asstart.y>cv->height && l->next->asend.y<cv->height )) {
651 		x = (l->next->asend.x-l->asstart.x)*(double)(cv->height-l->asstart.y)/(l->next->asend.y-l->asstart.y) +
652 			l->asstart.x;
653 		if (( x<0 || x>=cv->width ) && bothout )
654     continue;			/* Not on screen */;
655 		if ( l->asstart.y>cv->height ) {
656 		    l->asstart.y = cv->height;
657 		    l->asstart.x = x;
658 		} else {
659 		    l->next->asend.y = cv->height;
660 		    l->next->asend.x = x;
661 		}
662 	    } else if ( l->asstart.y>cv->height && l->next->asend.y>cv->height )
663     continue;
664 	    l->flags |= cvli_onscreen;
665 	    any = true;
666 	}
667     }
668     lap->any = any;
669 return( any );
670 }
671 
672 typedef struct gpl { struct gpl *next; GPoint *gp; int cnt; } GPointList;
673 
GPLFree(GPointList * gpl)674 static void GPLFree(GPointList *gpl) {
675     GPointList *next;
676 
677     while ( gpl!=NULL ) {
678 	next = gpl->next;
679 	free( gpl->gp );
680 	free( gpl );
681 	gpl = next;
682     }
683 }
684 
685 /* Before we did clipping this was a single polygon. Now it is a set of */
686 /*  sets of line segments. If no clipping is done, then we end up with */
687 /*  one set which is the original polygon, otherwise we get the segments */
688 /*  which are inside the screen. Each set of segments is contiguous */
MakePoly(CharView * cv,SplinePointList * spl)689 static GPointList *MakePoly(CharView *cv, SplinePointList *spl) {
690     int i, len;
691     LinearApprox *lap;
692     LineList *line, *prev;
693     Spline *spline, *first;
694     GPointList *head=NULL, *last=NULL, *cur;
695     int closed;
696 
697     for ( i=0; i<2; ++i ) {
698 	len = 0; first = NULL;
699 	closed = true;
700 	cur = NULL;
701 	for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
702 	    if ( !CVSplineOutside(cv,spline) && !isnan(spline->splines[0].a) && !isnan(spline->splines[1].a)) {
703         CharViewTab* tab = CVGetActiveTab(cv);
704 		lap = SplineApproximate(spline,tab->scale);
705 		if ( i==0 )
706 		    CVLinesIntersectScreen(cv,lap);
707 		if ( lap->any ) {
708 		    for ( prev = lap->lines, line=prev->next; line!=NULL; prev=line, line=line->next ) {
709 			if ( !(prev->flags&cvli_onscreen) ) {
710 			    closed = true;
711 		    continue;
712 			}
713 			if ( closed || (prev->flags&cvli_clipped) ) {
714 			    if ( i==0 ) {
715 				cur = calloc(1,sizeof(GPointList));
716 				if ( head==NULL )
717 				    head = cur;
718 				else {
719 				    last->cnt = len;
720 				    last->next = cur;
721 				}
722 				last = cur;
723 			    } else {
724 				if ( cur==NULL )
725 				    cur = head;
726 				else
727 				    cur = cur->next;
728 				cur->gp = malloc(cur->cnt*sizeof(GPoint));
729 				cur->gp[0].x = prev->asstart.x;
730 				cur->gp[0].y = prev->asstart.y;
731 			    }
732 			    len=1;
733 			    closed = false;
734 			}
735 			if ( i!=0 ) {
736 			    if ( len>=cur->cnt )
737 				fprintf( stderr, "Clipping is screwed up, about to die %d (should be less than %d)\n", len, cur->cnt );
738 			    cur->gp[len].x = line->asend.x;
739 			    cur->gp[len].y = line->asend.y;
740 			}
741 			++len;
742 			if ( line->flags&cvli_clipped )
743 			    closed = true;
744 		    }
745 		} else
746 		    closed = true;
747 	    } else
748 		closed = true;
749 	    if ( first==NULL ) first = spline;
750 	}
751 	if ( i==0 && cur!=NULL )
752 	    cur->cnt = len;
753     }
754 return( head );
755 }
756 
DrawTangentPoint(GWindow pixmap,int x,int y,BasePoint * unit,int outline,Color col)757 static void DrawTangentPoint( GWindow pixmap, int x, int y,
758 			      BasePoint *unit, int outline, Color col )
759 {
760     int dir;
761     const int gp_sz = 4;
762     GPoint gp[5];
763 
764     dir = 0;
765     if ( unit->x!=0 || unit->y!=0 ) {
766 	float dx = unit->x, dy = unit->y;
767 	if ( dx<0 ) dx= -dx;
768 	if ( dy<0 ) dy= -dy;
769 	if ( dx>2*dy ) {
770 	    if ( unit->x>0 ) dir = 0 /* right */;
771 	    else dir = 1 /* left */;
772 	} else if ( dy>2*dx ) {
773 	    if ( unit->y>0 ) dir = 2 /* up */;
774 	    else dir = 3 /* down */;
775 	} else {
776 	    if ( unit->y>0 && unit->x>0 ) dir=4;
777 	    else if ( unit->x>0 ) dir=5;
778 	    else if ( unit->y>0 ) dir=7;
779 	    else dir = 6;
780 	}
781     }
782 
783     float sizedelta = 4;
784     if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
785 	sizedelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
786 
787     if ( dir==1 /* left */ || dir==0 /* right */) {
788 	gp[0].y = y; gp[0].x = (dir==0)?x+sizedelta:x-sizedelta;
789 	gp[1].y = y-sizedelta; gp[1].x = x;
790 	gp[2].y = y+sizedelta; gp[2].x = x;
791     } else if ( dir==2 /* up */ || dir==3 /* down */ ) {
792 	gp[0].x = x; gp[0].y = dir==2?y-sizedelta:y+sizedelta;	/* remember screen coordinates are backwards in y from character coords */
793 	gp[1].x = x-sizedelta; gp[1].y = y;
794 	gp[2].x = x+sizedelta; gp[2].y = y;
795     } else {
796 	/* at a 45 angle, a value of 4 looks too small. I probably want 4*1.414 */
797 	sizedelta = 5;
798 	if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
799 	    sizedelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
800 	int xdiff = unit->x > 0 ?   sizedelta  : -1*sizedelta;
801 	int ydiff = unit->y > 0 ? -1*sizedelta :    sizedelta;
802 
803 	gp[0].x = x+xdiff/2; gp[0].y = y+ydiff/2;
804 	gp[1].x = gp[0].x-xdiff; gp[1].y = gp[0].y;
805 	gp[2].x = gp[0].x; gp[2].y = gp[0].y-ydiff;
806     }
807     gp[3] = gp[0];
808     if ( outline )
809 	GDrawDrawPoly(pixmap,gp,gp_sz,col);
810     else
811 	GDrawFillPoly(pixmap,gp,4,col);
812 }
813 
DrawPoint_SetupRectForSize(GRect * r,int cx,int cy,float sz)814 static GRect* DrawPoint_SetupRectForSize( GRect* r, int cx, int cy, float sz )
815 {
816     float sizedelta = sz;
817     if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
818 	sizedelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
819 
820     r->x = cx - sizedelta;
821     r->y = cy - sizedelta;
822     r->width  = 1 + sizedelta * 2;
823     r->height = 1 + sizedelta * 2;
824     return r;
825 }
826 
827 
MaybeMaskColorToAlphaChannelOverride(Color c,Color AlphaChannelOverride)828 static Color MaybeMaskColorToAlphaChannelOverride( Color c, Color AlphaChannelOverride )
829 {
830     if( AlphaChannelOverride )
831     {
832 	c &= 0x00FFFFFF;
833 	c |= (AlphaChannelOverride & 0xFF000000);
834     }
835     return c;
836 }
837 
838 /*
839  * Format the given real value to .001 precision, discarding trailing
840  * zeroes, and the decimal point if not needed.
841  */
FmtReal(char * buf,int buflen,real r)842 static int FmtReal(char *buf, int buflen, real r)
843 {
844     if (buf && buflen > 0) {
845 	int ct;
846 
847 	if ((ct = snprintf(buf, buflen, "%.3f", r)) >= buflen)
848 	    ct = buflen-1;
849 
850 	if (strchr(buf, '.')) {
851 	    while (buf[ct-1] == '0') {
852 		--ct;
853 	    }
854 	    if (buf[ct-1] == '.') {
855 		--ct;
856 	    }
857 	    buf[ct] = '\0';
858 	}
859 	return ct;
860     }
861     return -1;
862 }
863 
864 /*
865  * Write the given spline point's coordinates in the form (xxx,yyy)
866  * into the buffer provided, rounding fractions to the nearest .001
867  * and discarding trailing zeroes.
868  */
SplinePointCoords(char * buf,int buflen,SplinePoint * sp)869 static int SplinePointCoords(char *buf, int buflen, SplinePoint *sp)
870 {
871     if (buf && buflen > 0) {
872 	int ct = 0, clen;
873 
874 	/* 4 chars for parens, comma and trailing NUL, then divide what
875 	   remains between the two coords */
876 	clen = (buflen - 4) / 2;
877 	if (clen > 0) {
878 	    buf[ct++] = '(';
879 	    ct += FmtReal(buf+ct, clen, sp->me.x);
880 	    buf[ct++] = ',';
881 	    ct += FmtReal(buf+ct, clen, sp->me.y);
882 	    buf[ct++] = ')';
883 	}
884 	buf[ct] = '\0';
885 
886 	return ct;
887     }
888     return -1;
889 }
890 
DrawPoint(CharView * cv,GWindow pixmap,SplinePoint * sp,SplineSet * spl,int onlynumber,int truetype_markup,Color AlphaChannelOverride)891 static void DrawPoint( CharView *cv, GWindow pixmap, SplinePoint *sp,
892 		       SplineSet *spl, int onlynumber, int truetype_markup,
893                        Color AlphaChannelOverride )
894 {
895     GRect r;
896     int x, y, cx, cy;
897     CharViewTab* tab = CVGetActiveTab(cv);
898     Color col = sp==spl->first ? firstpointcol : pointcol;
899     int pnum;
900     char buf[16];
901     int isfake;
902 
903     if ( cv->markextrema && SpIsExtremum(sp) && sp!=spl->first )
904 	col = extremepointcol;
905     if ( sp->selected )
906 	col = selectedpointcol;
907     else {
908 	col = col&0x00ffffff;
909 	col |= prefs_cvInactiveHandleAlpha << 24;
910     }
911 
912     col = MaybeMaskColorToAlphaChannelOverride( col, AlphaChannelOverride );
913     Color subcolmasked    = MaybeMaskColorToAlphaChannelOverride( subcol, AlphaChannelOverride );
914     Color nextcpcolmasked = MaybeMaskColorToAlphaChannelOverride( nextcpcol, AlphaChannelOverride );
915     Color prevcpcolmasked = MaybeMaskColorToAlphaChannelOverride( prevcpcol, AlphaChannelOverride );
916     Color selectedpointcolmasked = MaybeMaskColorToAlphaChannelOverride( selectedpointcol, AlphaChannelOverride );
917     Color selectedcpcolmasked = MaybeMaskColorToAlphaChannelOverride( selectedcpcol, AlphaChannelOverride );
918 
919     x =  tab->xoff + rint(sp->me.x*tab->scale);
920     y = -tab->yoff + cv->height - rint(sp->me.y*tab->scale);
921     if ( x<-4000 || y<-4000 || x>cv->width+4000 || y>=cv->height+4000 )
922 return;
923 
924     /* draw the control points if it's selected */
925     if ( sp->selected
926 	 || cv->showpointnumbers
927 	 || cv->alwaysshowcontrolpoints
928 	 || cv->show_ft_results
929 	 || cv->dv )
930     {
931 	if ( !sp->nonextcp ) {
932 	    cx =  tab->xoff + rint(sp->nextcp.x*tab->scale);
933 	    cy = -tab->yoff + cv->height - rint(sp->nextcp.y*tab->scale);
934 	    if ( cx<-100 ) {		/* Clip */
935 		cy = cx==x ? x : (cy-y) * (double)(-100-x)/(cx-x) + y;
936 		cx = -100;
937 	    } else if ( cx>cv->width+100 ) {
938 		cy = cx==x ? x : (cy-y) * (double)(cv->width+100-x)/(cx-x) + y;
939 		cx = cv->width+100;
940 	    }
941 	    if ( cy<-100 ) {
942 		cx = cy==y ? y : (cx-x) * (double)(-100-y)/(cy-y) + x;
943 		cy = -100;
944 	    } else if ( cy>cv->height+100 ) {
945 		cx = cy==y ? y : (cx-x) * (double)(cv->height+100-y)/(cy-y) + x;
946 		cy = cv->height+100;
947 	    }
948 	    subcolmasked = nextcpcolmasked;
949 
950 	    //
951 	    // If the next BCP is selected we should decorate the
952 	    // drawing to let the user know that. The primary (last)
953 	    // selected BCP is drawn with a backing rectangle of size
954 	    // 3, the secondary BCP (2nd, 3rd, 4th last selected BCP)
955 	    // are drawn with slightly smaller highlights.
956 	    //
957 	    if( !onlynumber && SPIsNextCPSelected( sp, cv ))
958 	    {
959 		float sz = 2;
960 		if( SPIsNextCPSelectedSingle( sp, cv ))
961 		    sz *= 1.5;
962 
963 		DrawPoint_SetupRectForSize( &r, cx, cy, sz );
964 		GDrawFillRect(pixmap,&r, nextcpcol);
965 		subcolmasked = selectedcpcolmasked;
966 	    }
967 	    else if ( truetype_markup )
968 	    {
969 		if ( sp->flexy ) {
970 		    /* cp is about to be moved (or changed in some other way) */
971 		    DrawPoint_SetupRectForSize( &r, cx, cy, 3 );
972 		    GDrawFillRect(pixmap,&r, selectedpointcol);
973 		}
974 		if ( sp->flexx ) {
975 		    /* cp is a reference point */
976 		    DrawPoint_SetupRectForSize( &r, cx, cy, 5 );
977 		    GDrawDrawElipse(pixmap,&r,selectedpointcol );
978 		}
979 	    }
980 	    if ( !onlynumber )
981 	    {
982 		float sizedelta = 3;
983 		if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
984 		    sizedelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
985 		GDrawDrawLine(pixmap,x,y,cx,cy, nextcpcolmasked );
986 		GDrawDrawLine(pixmap,cx-sizedelta,cy-sizedelta,cx+sizedelta,cy+sizedelta,subcolmasked);
987 		GDrawDrawLine(pixmap,cx+sizedelta,cy-sizedelta,cx-sizedelta,cy+sizedelta,subcolmasked);
988 	    }
989 	    if ( cv->showpointnumbers || cv->show_ft_results || cv->dv ) {
990 		pnum = sp->nextcpindex;
991 		if ( pnum!=0xffff && pnum!=0xfffe ) {
992                     if (cv->showpointnumbers == 2)
993 			SplinePointCoords( buf, sizeof(buf), sp);
994                     else
995 		        sprintf( buf,"%d", pnum );
996 		    GDrawDrawText8(pixmap,cx,cy-6,buf,-1,nextcpcol);
997 		}
998 	    }
999 	}
1000 	if ( !sp->noprevcp ) {
1001 	    cx =  tab->xoff + rint(sp->prevcp.x*tab->scale);
1002 	    cy = -tab->yoff + cv->height - rint(sp->prevcp.y*tab->scale);
1003 	    if ( cx<-100 ) {		/* Clip */
1004 		cy = cx==x ? x : (cy-y) * (double)(-100-x)/(cx-x) + y;
1005 		cx = -100;
1006 	    } else if ( cx>cv->width+100 ) {
1007 		cy = cx==x ? x : (cy-y) * (double)(cv->width+100-x)/(cx-x) + y;
1008 		cx = cv->width+100;
1009 	    }
1010 	    if ( cy<-100 ) {
1011 		cx = cy==y ? y : (cx-x) * (double)(-100-y)/(cy-y) + x;
1012 		cy = -100;
1013 	    } else if ( cy>cv->height+100 ) {
1014 		cx = cy==y ? y : (cx-x) * (double)(cv->height+100-y)/(cy-y) + x;
1015 		cy = cv->height+100;
1016 	    }
1017 	    subcolmasked = prevcpcolmasked;
1018 	    if( !onlynumber && SPIsPrevCPSelected( sp, cv ))
1019 	    {
1020 		float sz = 2;
1021 		if( SPIsPrevCPSelectedSingle( sp, cv ))
1022 		    sz *= 1.5;
1023 		DrawPoint_SetupRectForSize( &r, cx, cy, sz );
1024 		GDrawFillRect(pixmap,&r, prevcpcol);
1025 		subcolmasked = selectedcpcolmasked;
1026 	    }
1027 	    if ( !onlynumber ) {
1028 		float sizedelta = 3;
1029 		if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
1030 		    sizedelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
1031 		GDrawDrawLine(pixmap,x,y,cx,cy, prevcpcolmasked);
1032 		GDrawDrawLine(pixmap,cx-sizedelta,cy-sizedelta,cx+sizedelta,cy+sizedelta,subcolmasked);
1033 		GDrawDrawLine(pixmap,cx+sizedelta,cy-sizedelta,cx-sizedelta,cy+sizedelta,subcolmasked);
1034 	    }
1035 	}
1036     }
1037 
1038     if ( x<-4 || y<-4 || x>cv->width+4 || y>=cv->height+4 )
1039 return;
1040     r.x = x-2;
1041     r.y = y-2;
1042     r.width = r.height = 0;
1043     r.width  += prefs_cvEditHandleSize;
1044     r.height += prefs_cvEditHandleSize;
1045     if ( sp->selected )
1046 	GDrawSetLineWidth(pixmap,selectedpointwidth);
1047     isfake = false;
1048     if ( cv->b.layerheads[cv->b.drawmode]->order2 &&
1049 	    cv->b.layerheads[cv->b.drawmode]->refs==NULL )
1050     {
1051 	int mightbe_fake = SPInterpolate(sp);
1052         if ( !mightbe_fake && sp->ttfindex==0xffff )
1053 	    sp->ttfindex = 0xfffe;	/* if we have no instructions we won't call instrcheck and won't notice when a point stops being fake */
1054 	else if ( mightbe_fake )
1055 	    sp->ttfindex = 0xffff;
1056 	isfake = sp->ttfindex==0xffff;
1057     }
1058     if ( onlynumber )
1059     {
1060 	/* Draw Nothing */;
1061     }
1062     else if ( sp->pointtype==pt_curve )
1063     {
1064 	r.width +=2; r.height += 2;
1065 	r.x = x - r.width  / 2;
1066 	r.y = y - r.height / 2;
1067 	if ( sp->selected || isfake )
1068 	    GDrawDrawElipse(pixmap,&r,col);
1069 	else
1070 	    GDrawFillElipse(pixmap,&r,col);
1071     }
1072     else if ( sp->pointtype==pt_corner )
1073     {
1074 	r.x = x - r.width  / 2;
1075 	r.y = y - r.height / 2;
1076 	if ( sp->selected || isfake )
1077 	    GDrawDrawRect(pixmap,&r,col);
1078 	else
1079 	    GDrawFillRect(pixmap,&r,col);
1080     }
1081     else if ( sp->pointtype==pt_hvcurve )
1082     {
1083 	const int gp_sz = 5;
1084 	GPoint gp[5];
1085 
1086 	float sizedelta = 3;
1087 	float offsetdelta = 0; // 4 * tab->scale;
1088 	if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
1089 	{
1090 	    sizedelta   *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
1091 	    offsetdelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
1092 	}
1093 
1094 	float basex = r.x + 3 + offsetdelta;
1095 	float basey = r.y + 3 + offsetdelta;
1096 	gp[0].x = basex - sizedelta; gp[0].y = basey + 0;
1097 	gp[1].x = basex + 0;         gp[1].y = basey + sizedelta;
1098 	gp[2].x = basex + sizedelta; gp[2].y = basey + 0;
1099 	gp[3].x = basex + 0;         gp[3].y = basey - sizedelta;
1100 	gp[4] = gp[0];
1101 
1102 	if ( sp->selected || isfake )
1103 	    GDrawDrawPoly(pixmap,gp,gp_sz,col);
1104 	else
1105 	    GDrawFillPoly(pixmap,gp,gp_sz,col);
1106     }
1107     else
1108     {
1109 	BasePoint *cp=NULL;
1110 	BasePoint unit;
1111 
1112 	if ( !sp->nonextcp )
1113 	    cp = &sp->nextcp;
1114 	else if ( !sp->noprevcp )
1115 	    cp = &sp->prevcp;
1116 	memset(&unit,0,sizeof(unit));
1117 	if ( cp!=NULL ) {
1118 	    unit.x = cp->x-sp->me.x; unit.y = cp->y-sp->me.y;
1119 	}
1120 	DrawTangentPoint(pixmap, x, y, &unit, sp->selected || isfake, col);
1121     }
1122     GDrawSetLineWidth(pixmap,0);
1123     if ( (cv->showpointnumbers || cv->show_ft_results || cv->dv )
1124 	 && sp->ttfindex!=0xffff )
1125     {
1126 	if ( sp->ttfindex==0xfffe )
1127 	    strcpy(buf,"??");
1128         else if (cv->showpointnumbers == 2)
1129 	    SplinePointCoords(buf, sizeof(buf), sp);
1130 	else
1131 	    sprintf( buf,"%d", sp->ttfindex );
1132 	GDrawDrawText8(pixmap,x,y-6,buf,-1,col);
1133     }
1134     if ( truetype_markup && sp->roundx ) {
1135 	r.x = x-5; r.y = y-5;
1136 	r.width = r.height = 11;
1137 	GDrawDrawElipse(pixmap,&r,selectedpointcolmasked);
1138     } else if ( !onlynumber && !truetype_markup ) {
1139 	if ((( sp->roundx || sp->roundy ) &&
1140 		 (((cv->showrounds&1) && tab->scale>=.3) || (cv->showrounds&2))) ||
1141 		(sp->watched && cv->dv!=NULL) ||
1142 		sp->hintmask!=NULL ) {
1143 	    r.x = x-5; r.y = y-5;
1144 	    r.width = r.height = 11;
1145 	    GDrawDrawElipse(pixmap,&r,col);
1146 	}
1147 	if (( sp->flexx && cv->showhhints ) || (sp->flexy && cv->showvhints)) {
1148 	    r.x = x-5; r.y = y-5;
1149 	    r.width = r.height = 11;
1150 	    GDrawDrawElipse(pixmap,&r,
1151 			    MaybeMaskColorToAlphaChannelOverride( sp->flexx ? hflexhintcol : vflexhintcol,
1152 								  AlphaChannelOverride ));
1153 	}
1154     }
1155 }
1156 
DrawSpiroPoint(CharView * cv,GWindow pixmap,spiro_cp * cp,SplineSet * spl,int cp_i,Color AlphaChannelOverride)1157 static void DrawSpiroPoint( CharView *cv, GWindow pixmap, spiro_cp *cp,
1158 			    SplineSet *spl, int cp_i, Color AlphaChannelOverride )
1159 {
1160     CharViewTab* tab = CVGetActiveTab(cv);
1161     GRect r;
1162     int x, y;
1163     Color col = cp==&spl->spiros[0] ? firstpointcol : pointcol;
1164     char ty = cp->ty&0x7f;
1165     int selected = SPIRO_SELECTED(cp);
1166     GPoint gp[5];
1167 
1168     if ( selected )
1169 	 col = selectedpointcol;
1170 
1171     if( !selected )
1172     {
1173 	col = col & 0x00ffffff;
1174 	col |= prefs_cvInactiveHandleAlpha << 24;
1175     }
1176 
1177     col = MaybeMaskColorToAlphaChannelOverride( col, AlphaChannelOverride );
1178 
1179 
1180     x =  tab->xoff + rint(cp->x*tab->scale);
1181     y = -tab->yoff + cv->height - rint(cp->y*tab->scale);
1182     if ( x<-4 || y<-4 || x>cv->width+4 || y>=cv->height+4 )
1183 return;
1184 
1185     DrawPoint_SetupRectForSize( &r, x, y, 2 );
1186     /* r.x = x-2; */
1187     /* r.y = y-2; */
1188     /* r.width = r.height = 5; */
1189     if ( selected )
1190 	GDrawSetLineWidth(pixmap,selectedpointwidth);
1191 
1192     float sizedelta = 3;
1193     if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
1194 	sizedelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
1195 
1196     if ( ty == SPIRO_RIGHT ) {
1197 	GDrawSetLineWidth(pixmap,2);
1198 	gp[0].x = x-sizedelta; gp[0].y = y-sizedelta;
1199 	gp[1].x = x;           gp[1].y = y-sizedelta;
1200 	gp[2].x = x;           gp[2].y = y+sizedelta;
1201 	gp[3].x = x-sizedelta; gp[3].y = y+sizedelta;
1202 	GDrawDrawPoly(pixmap,gp,4,col);
1203     } else if ( ty == SPIRO_LEFT ) {
1204 	GDrawSetLineWidth(pixmap,2);
1205 	gp[0].x = x+sizedelta; gp[0].y = y-sizedelta;
1206 	gp[1].x = x;           gp[1].y = y-sizedelta;
1207 	gp[2].x = x;           gp[2].y = y+sizedelta;
1208 	gp[3].x = x+sizedelta; gp[3].y = y+sizedelta;
1209 	GDrawDrawPoly(pixmap,gp,4,col);
1210     } else if ( ty == SPIRO_G2 ) {
1211 	GPoint gp[5];
1212 
1213 	float sizedelta = 3;
1214 	float offsetdelta = 1;
1215 	if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
1216 	{
1217 	    sizedelta   *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
1218 	    offsetdelta *= prefs_cvEditHandleSize / prefs_cvEditHandleSize_default;
1219 	}
1220 
1221 	float basex = r.x + 1 + offsetdelta;
1222 	float basey = r.y + 1 + offsetdelta;
1223 	gp[0].x = basex - sizedelta; gp[0].y = basey + 0;
1224 	gp[1].x = basex + 0;         gp[1].y = basey + sizedelta;
1225 	gp[2].x = basex + sizedelta; gp[2].y = basey + 0;
1226 	gp[3].x = basex + 0;         gp[3].y = basey - sizedelta;
1227 	gp[4] = gp[0];
1228 	if ( selected )
1229 	    GDrawDrawPoly(pixmap,gp,5,col);
1230 	else
1231 	    GDrawFillPoly(pixmap,gp,5,col);
1232     } else if ( ty==SPIRO_CORNER ) {
1233 	if ( selected )
1234 	    GDrawDrawRect(pixmap,&r,col);
1235 	else
1236 	    GDrawFillRect(pixmap,&r,col);
1237     } else {
1238 	--r.x; --r.y; r.width +=2; r.height += 2;
1239 	if ( selected )
1240 	    GDrawDrawElipse(pixmap,&r,col);
1241 	else
1242 	    GDrawFillElipse(pixmap,&r,col);
1243     }
1244     GDrawSetLineWidth(pixmap,0);
1245 }
1246 
DrawLine(CharView * cv,GWindow pixmap,real x1,real y1,real x2,real y2,Color fg)1247 static void DrawLine(CharView *cv, GWindow pixmap,
1248 	real x1, real y1, real x2, real y2, Color fg) {
1249     CharViewTab* tab = CVGetActiveTab(cv);
1250     int ix1 = tab->xoff + rint(x1*tab->scale);
1251     int iy1 = -tab->yoff + cv->height - rint(y1*tab->scale);
1252     int ix2 = tab->xoff + rint(x2*tab->scale);
1253     int iy2 = -tab->yoff + cv->height - rint(y2*tab->scale);
1254     if ( iy1==iy2 ) {
1255 	if ( iy1<0 || iy1>cv->height )
1256 return;
1257 	if ( ix1<0 ) ix1 = 0;
1258 	if ( ix2>cv->width ) ix2 = cv->width;
1259     } else if ( ix1==ix2 ) {
1260 	if ( ix1<0 || ix1>cv->width )
1261 return;
1262 	if ( iy1<0 ) iy1 = 0;
1263 	if ( iy2<0 ) iy2 = 0;
1264 	if ( iy1>cv->height ) iy1 = cv->height;
1265 	if ( iy2>cv->height ) iy2 = cv->height;
1266     }
1267     GDrawDrawLine(pixmap, ix1,iy1, ix2,iy2, fg );
1268 }
1269 
DrawDirection(CharView * cv,GWindow pixmap,SplinePoint * sp)1270 static void DrawDirection(CharView *cv,GWindow pixmap, SplinePoint *sp) {
1271     BasePoint dir, *other;
1272     double len;
1273     CharViewTab* tab = CVGetActiveTab(cv);
1274     int x,y,xe,ye;
1275     SplinePoint *test;
1276 
1277     if ( sp->next==NULL )
1278 return;
1279 
1280     x = tab->xoff + rint(sp->me.x*tab->scale);
1281     y = -tab->yoff + cv->height - rint(sp->me.y*tab->scale);
1282     if ( x<0 || y<0 || x>cv->width || y>cv->width )
1283 return;
1284 
1285     /* Werner complained when the first point and the second point were at */
1286     /*  the same location... */ /* Damn. They weren't at the same location */
1287     /*  the were off by a rounding error. I'm not going to fix for that */
1288     for ( test=sp; ; ) {
1289 	if ( test->me.x!=sp->me.x || test->me.y!=sp->me.y ) {
1290 	    other = &test->me;
1291     break;
1292 	} else if ( !test->nonextcp ) {
1293 	    other = &test->nextcp;
1294     break;
1295 	}
1296 	if ( test->next==NULL )
1297 return;
1298 	test = test->next->to;
1299 	if ( test==sp )
1300 return;
1301     }
1302 
1303     dir.x = other->x-sp->me.x;
1304     dir.y = sp->me.y-other->y;		/* screen coordinates are the mirror of user coords */
1305     len = sqrt(dir.x*dir.x + dir.y*dir.y);
1306     dir.x /= len; dir.y /= len;
1307 
1308     x += rint(5*dir.y);
1309     y -= rint(5*dir.x);
1310     xe = x + rint(7*dir.x);
1311     ye = y + rint(7*dir.y);
1312     GDrawDrawLine(pixmap,x,y,xe,ye,firstpointcol);
1313     GDrawDrawLine(pixmap,xe,ye,xe+rint(2*(dir.y-dir.x)),ye+rint(2*(-dir.y-dir.x)), firstpointcol);
1314     GDrawDrawLine(pixmap,xe,ye,xe+rint(2*(-dir.y-dir.x)),ye+rint(2*(dir.x-dir.y)), firstpointcol);
1315 }
1316 
CVMarkInterestingLocations(CharView * cv,GWindow pixmap,SplinePointList * spl)1317 static void CVMarkInterestingLocations(CharView *cv, GWindow pixmap,
1318 	SplinePointList *spl) {
1319     Spline *s, *first;
1320     extended interesting[6];
1321     CharViewTab* tab = CVGetActiveTab(cv);
1322     int i, ecnt, cnt;
1323     GRect r;
1324 
1325     for ( s=spl->first->next, first=NULL; s!=NULL && s!=first; s=s->to->next ) {
1326 	if ( first==NULL ) first = s;
1327 	cnt = ecnt = 0;
1328 	if ( cv->markextrema )
1329 	    ecnt = cnt = Spline2DFindExtrema(s,interesting);
1330 
1331 	if ( cv->markpoi ) {
1332 	    cnt += Spline2DFindPointsOfInflection(s,interesting+cnt);
1333 	}
1334 	r.width = r.height = 9;
1335 	for ( i=0; i<cnt; ++i ) if ( interesting[i]>0 && interesting[i]<1.0 ) {
1336 	    Color col = i<ecnt ? extremepointcol : pointofinflectioncol;
1337 	    double x = ((s->splines[0].a*interesting[i]+s->splines[0].b)*interesting[i]+s->splines[0].c)*interesting[i]+s->splines[0].d;
1338 	    double y = ((s->splines[1].a*interesting[i]+s->splines[1].b)*interesting[i]+s->splines[1].c)*interesting[i]+s->splines[1].d;
1339 	    double sx =  tab->xoff + rint(x*tab->scale);
1340 	    double sy = -tab->yoff + cv->height - rint(y*tab->scale);
1341 	    if ( sx<-5 || sy<-5 || sx>10000 || sy>10000 )
1342 	continue;
1343 	    GDrawDrawLine(pixmap,sx-4,sy,sx+4,sy, col);
1344 	    GDrawDrawLine(pixmap,sx,sy-4,sx,sy+4, col);
1345 	    r.x = sx-4; r.y = sy-4;
1346 	    GDrawDrawElipse(pixmap,&r,col);
1347 	}
1348     }
1349 }
1350 
CVMarkAlmostHV(CharView * cv,GWindow pixmap,SplinePointList * spl)1351 static void CVMarkAlmostHV(CharView *cv, GWindow pixmap,
1352 	SplinePointList *spl) {
1353     Spline *s, *first;
1354     double dx, dy;
1355     int x1,x2,y1,y2;
1356     CharViewTab* tab = CVGetActiveTab(cv);
1357 
1358     for ( s=spl->first->next, first=NULL; s!=NULL && s!=first; s=s->to->next ) {
1359 	if ( first==NULL ) first = s;
1360 
1361 	if ( s->islinear ) {
1362 	    if ( !cv->showalmosthvlines )
1363     continue;
1364 	    if ( (dx = s->from->me.x - s->to->me.x)<0 ) dx = -dx;
1365 	    if ( (dy = s->from->me.y - s->to->me.y)<0 ) dy = -dy;
1366 	    if ( dx<=cv->hvoffset && dy<=cv->hvoffset )
1367     continue;
1368 	    if ( dx==0 || dy==0 )
1369     continue;
1370 	    if ( dx<cv->hvoffset || dy<cv->hvoffset ) {
1371 		x1 =  tab->xoff + rint(s->from->me.x*tab->scale);
1372 		y1 = -tab->yoff + cv->height - rint(s->from->me.y*tab->scale);
1373 		x2 =  tab->xoff + rint(s->to->me.x*tab->scale);
1374 		y2 = -tab->yoff + cv->height - rint(s->to->me.y*tab->scale);
1375 		GDrawDrawLine(pixmap,x1,y1,x2,y2,almosthvcol);
1376 	    }
1377 	} else {
1378 	    if ( !cv->showalmosthvcurves )
1379     continue;
1380 	    if ( (dx = s->from->me.x - s->from->nextcp.x)<0 ) dx = -dx;
1381 	    if ( (dy = s->from->me.y - s->from->nextcp.y)<0 ) dy = -dy;
1382 	    if ( dx<=cv->hvoffset && dy<=cv->hvoffset )
1383 		/* Ignore */;
1384 	    else if ( dx==0 || dy==0 )
1385 		/* It's right */;
1386 	    else if ( dx<cv->hvoffset || dy<cv->hvoffset ) {
1387 		x2 = x1 =  tab->xoff + rint(s->from->me.x*tab->scale);
1388 		y2 = y1 = -tab->yoff + cv->height - rint(s->from->me.y*tab->scale);
1389 		if ( dx<cv->hvoffset ) {
1390 		    if ( s->from->me.y<s->from->nextcp.y )
1391 			y2 += 15;
1392 		    else
1393 			y2 -= 15;
1394 		} else {
1395 		    if ( s->from->me.x<s->from->nextcp.x )
1396 			x2 += 15;
1397 		    else
1398 			x2 -= 15;
1399 		}
1400 		GDrawDrawLine(pixmap,x1,y1,x2,y2,almosthvcol);
1401 	    }
1402 
1403 	    if ( (dx = s->to->me.x - s->to->prevcp.x)<0 ) dx = -dx;
1404 	    if ( (dy = s->to->me.y - s->to->prevcp.y)<0 ) dy = -dy;
1405 	    if ( dx<=cv->hvoffset && dy<=cv->hvoffset )
1406 		/* Ignore */;
1407 	    else if ( dx==0 || dy==0 )
1408 		/* It's right */;
1409 	    else if ( dx<cv->hvoffset || dy<cv->hvoffset ) {
1410 		x2 = x1 =  tab->xoff + rint(s->to->me.x*tab->scale);
1411 		y2 = y1 = -tab->yoff + cv->height - rint(s->to->me.y*tab->scale);
1412 		if ( dx<cv->hvoffset ) {
1413 		    if ( s->to->me.y<s->to->prevcp.y )
1414 			y2 += 15;
1415 		    else
1416 			y2 -= 15;
1417 		} else {
1418 		    if ( s->to->me.x<s->to->prevcp.x )
1419 			x2 += 15;
1420 		    else
1421 			x2 -= 15;
1422 		}
1423 		GDrawDrawLine(pixmap,x1,y1,x2,y2,almosthvcol);
1424 	    }
1425 	}
1426     }
1427 }
1428 
CVDrawPointName(CharView * cv,GWindow pixmap,SplinePoint * sp,Color fg)1429 static void CVDrawPointName(CharView *cv, GWindow pixmap, SplinePoint *sp, Color fg)
1430 {
1431     CharViewTab* tab = CVGetActiveTab(cv);
1432     if (sp->name && *sp->name) {
1433 	int32 theight;
1434 
1435 	GDrawSetFont(pixmap, cv->normal);
1436 	theight = GDrawGetText8Height(pixmap, sp->name, -1);
1437 	GDrawDrawText8(pixmap,
1438 		       tab->xoff + rint(sp->me.x*tab->scale),
1439 		       cv->height-tab->yoff - rint(sp->me.y*tab->scale) + theight + 3,
1440 		       sp->name,-1,fg);
1441 	GDrawSetFont(pixmap,cv->small);	/* For point numbers */
1442     }
1443 }
1444 
CVDrawContourName(CharView * cv,GWindow pixmap,SplinePointList * ss,Color fg)1445 static void CVDrawContourName(CharView *cv, GWindow pixmap, SplinePointList *ss,
1446 	Color fg ) {
1447     SplinePoint *sp, *topright;
1448     GPoint tr;
1449     CharViewTab* tab = CVGetActiveTab(cv);
1450 
1451     /* Find the top right point of the contour. This is where we will put the */
1452     /*  label */
1453     for ( sp = topright = ss->first; ; ) {
1454 	if ( sp->me.y>topright->me.y || (sp->me.y==topright->me.y && sp->me.x>topright->me.x) )
1455 	    topright = sp;
1456 	if ( sp->next==NULL )
1457     break;
1458 	sp = sp->next->to;
1459 	if ( sp==ss->first )
1460     break;
1461     }
1462     tr.x = tab->xoff + rint(topright->me.x*tab->scale);
1463     tr.y = cv->height-tab->yoff - rint(topright->me.y*tab->scale);
1464 
1465     /* If the top edge of the contour is off the bottom of the screen */
1466     /*  then the contour won't show */
1467     if ( tr.y>cv->height )
1468 return;
1469 
1470     GDrawSetFont(pixmap,cv->normal);
1471 
1472     if ( ss->first->prev==NULL && ss->first->next!=NULL &&
1473 	    ss->first->next->to->next==NULL && ss->first->next->knownlinear &&
1474 	    (tr.y < cv->nfh || tr.x>cv->width-cv->nfh) ) {
1475 	/* It's a simple line */
1476 	/* A special case because: It's common, and it's important to get it */
1477 	/*  right and label lines even if the point to which we'd normally */
1478 	/*  attach a label is offscreen */
1479 	SplinePoint *sp1 = ss->first, *sp2 = ss->first->next->to;
1480 	double dx, dy, slope, off, yinter, xinter;
1481 
1482 	if ( (dx = sp1->me.x-sp2->me.x)<0 ) dx = -dx;
1483 	if ( (dy = sp1->me.y-sp2->me.y)<0 ) dy = -dy;
1484 	if ( dx==0 ) {
1485 	    /* Vertical line */
1486 	    tr.y = cv->nfh;
1487 	    tr.x += 2;
1488 	} else if ( dy==0 ) {
1489 	    /* Horizontal line */
1490 	    tr.x = cv->width - cv->nfh - GDrawGetText8Width(pixmap,ss->contour_name,-1);
1491 	} else {
1492 	    /* y = slope*x + off; */
1493 	    slope = (sp1->me.y-sp2->me.y)/(sp1->me.x-sp2->me.x);
1494 	    off = sp1->me.y - slope*sp1->me.x;
1495 	    /* Now translate to screen coords */
1496 	    off = (cv->height-tab->yoff)+slope*tab->xoff - tab->scale*off;
1497 	    slope = -slope;
1498 	    xinter = (0-off)/slope;
1499 	    yinter = slope*cv->width + off;
1500 	    if ( xinter>0 && xinter<cv->width ) {
1501 		tr.x = xinter+2;
1502 		tr.y = cv->nfh;
1503 	    } else if ( yinter>0 && yinter<cv->height ) {
1504 		tr.x = cv->width - cv->nfh - GDrawGetText8Width(pixmap,ss->contour_name,-1);
1505 		tr.y = yinter;
1506 	    }
1507 	}
1508     } else {
1509 	tr.y -= cv->nfh/2;
1510 	tr.x -= GDrawGetText8Width(pixmap,ss->contour_name,-1)/2;
1511     }
1512 
1513     GDrawDrawText8(pixmap,tr.x,tr.y,ss->contour_name,-1,fg);
1514     GDrawSetFont(pixmap,cv->small);	/* For point numbers */
1515 }
1516 
CVDrawSplineSet(CharView * cv,GWindow pixmap,SplinePointList * set,Color fg,int dopoints,DRect * clip)1517 void CVDrawSplineSet(CharView *cv, GWindow pixmap, SplinePointList *set,
1518 	Color fg, int dopoints, DRect *clip ) {
1519     CVDrawSplineSetSpecialized( cv, pixmap, set, fg, dopoints, clip,
1520 				sfm_stroke, 0 );
1521 }
1522 
1523 
CVDrawSplineSetOutlineOnly(CharView * cv,GWindow pixmap,SplinePointList * set,Color fg,int dopoints,DRect * clip,enum outlinesfm_flags strokeFillMode)1524 void CVDrawSplineSetOutlineOnly(CharView *cv, GWindow pixmap, SplinePointList *set,
1525 				Color fg, int dopoints, DRect *clip, enum outlinesfm_flags strokeFillMode ) {
1526     SplinePointList *spl;
1527     int currentSplineCounter = 0;
1528     int activelayer = CVLayer(&cv->b);
1529     CharViewTab* tab = CVGetActiveTab(cv);
1530 
1531     if( strokeFillMode == sfm_fill ) {
1532     	GDrawFillRuleSetWinding(pixmap);
1533     }
1534 
1535     for ( spl = set; spl!=NULL; spl = spl->next ) {
1536 
1537 	Color fc  = spl->is_clip_path ? clippathcol : fg;
1538 	/**
1539 	 * Only make the outline red if this is not a grid layer
1540 	 * and we want to highlight open paths
1541 	 * and the activelayer is sane
1542 	 * and the activelayer contains the given splinepointlist
1543 	 * and the path is open
1544 	 */
1545 	if ( cv->b.drawmode!=dm_grid
1546 	     && DrawOpenPathsWithHighlight
1547 	     && activelayer < cv->b.sc->layer_cnt
1548 	     && activelayer >= 0
1549 	     && SplinePointListContains( cv->b.sc->layers[activelayer].splines, spl )
1550 	     && spl->first
1551 	     && spl->first->prev==NULL )
1552 	{
1553             if ( GDrawGetLineWidth( pixmap ) <= 1 )
1554 		fc = openpathcol | 0xff000000;
1555 	    else
1556 		fc = openpathcol;
1557 	}
1558 
1559 	if ( GDrawHasCairo(pixmap)&gc_buildpath ) {
1560 	    Spline *first, *spline;
1561 	    double x,y, cx1, cy1, cx2, cy2, dx,dy;
1562 	    GDrawPathStartSubNew(pixmap);
1563 	    x = rpt(cv,  tab->xoff + spl->first->me.x*tab->scale);
1564 	    y = rpt(cv, -tab->yoff + cv->height - spl->first->me.y*tab->scale);
1565 	    GDrawPathMoveTo(pixmap,x+.5,y+.5);
1566 	    currentSplineCounter++;
1567 	    for ( spline=spl->first->next, first=NULL; spline!=first && spline!=NULL; spline=spline->to->next ) {
1568 		x = rpt(cv,  tab->xoff + spline->to->me.x*tab->scale);
1569 		y = rpt(cv, -tab->yoff + cv->height - spline->to->me.y*tab->scale);
1570 		if ( spline->knownlinear )
1571 		    GDrawPathLineTo(pixmap,x+.5,y+.5);
1572 		else if ( spline->order2 ) {
1573 		    dx = rint(spline->from->me.x*tab->scale) - spline->from->me.x*tab->scale;
1574 		    dy = rint(spline->from->me.y*tab->scale) - spline->from->me.y*tab->scale;
1575 		    cx1 = spline->from->me.x + spline->splines[0].c/3;
1576 		    cy1 = spline->from->me.y + spline->splines[1].c/3;
1577 		    cx2 = cx1 + (spline->splines[0].b+spline->splines[0].c)/3;
1578 		    cy2 = cy1 + (spline->splines[1].b+spline->splines[1].c)/3;
1579 		    cx1 = tab->xoff + cx1*tab->scale + dx;
1580 		    cy1 = -tab->yoff + cv->height - cy1*tab->scale - dy;
1581 		    dx = rint(spline->to->me.x*tab->scale) - spline->to->me.x*tab->scale;
1582 		    dy = rint(spline->to->me.y*tab->scale) - spline->to->me.y*tab->scale;
1583 		    cx2 = tab->xoff + cx2*tab->scale + dx;
1584 		    cy2 = -tab->yoff + cv->height - cy2*tab->scale - dy;
1585 		    GDrawPathCurveTo(pixmap,cx1+.5,cy1+.5,cx2+.5,cy2+.5,x+.5,y+.5);
1586 		} else {
1587 		    dx = rint(spline->from->me.x*tab->scale) - spline->from->me.x*tab->scale;
1588 		    dy = rint(spline->from->me.y*tab->scale) - spline->from->me.y*tab->scale;
1589 		    cx1 = tab->xoff + spline->from->nextcp.x*tab->scale + dx;
1590 		    cy1 = -tab->yoff + cv->height - spline->from->nextcp.y*tab->scale - dy;
1591 		    dx = rint(spline->to->me.x*tab->scale) - spline->to->me.x*tab->scale;
1592 		    dy = rint(spline->to->me.y*tab->scale) - spline->to->me.y*tab->scale;
1593 		    cx2 = tab->xoff + spline->to->prevcp.x*tab->scale + dx;
1594 		    cy2 = -tab->yoff + cv->height - spline->to->prevcp.y*tab->scale - dy;
1595 		    GDrawPathCurveTo(pixmap,cx1+.5,cy1+.5,cx2+.5,cy2+.5,x+.5,y+.5);
1596 		}
1597 		if ( first==NULL )
1598 		    first = spline;
1599 	    }
1600 	    if ( spline!=NULL )
1601 		GDrawPathClose(pixmap);
1602 
1603 	    switch( strokeFillMode ) {
1604             case sfm_stroke_trans:
1605                 GDrawPathStroke( pixmap, fc );
1606                 break;
1607             case sfm_stroke:
1608                 GDrawPathStroke( pixmap, fc | 0xff000000 );
1609                 break;
1610             case sfm_clip:
1611             case sfm_fill:
1612             case sfm_nothing:
1613                 break;
1614 	    }
1615 	} else if (strokeFillMode != sfm_clip) {
1616 	    GPointList *gpl = MakePoly(cv,spl), *cur;
1617 	    for ( cur=gpl; cur!=NULL; cur=cur->next )
1618 		GDrawDrawPoly(pixmap,cur->gp,cur->cnt,fc);
1619 	    GPLFree(gpl);
1620 	}
1621     }
1622 
1623     if (strokeFillMode == sfm_clip && (GDrawHasCairo(pixmap) & gc_buildpath)) {
1624         // Really only cairo_clip needs to be called
1625         // But then I'd have to change the GDraw interface, ew...
1626         GDrawClipPreserve( pixmap );
1627         GDrawPathStartNew( pixmap );
1628     } else if (strokeFillMode == sfm_fill) {
1629 	if ( cv->inPreviewMode )
1630 	    GDrawPathFill(pixmap, previewfillcol|0xff000000);
1631 	else
1632 	    GDrawPathFill(pixmap, fillcol);
1633     }
1634 }
1635 
1636 
1637 
CVDrawSplineSetSpecialized(CharView * cv,GWindow pixmap,SplinePointList * set,Color fg,int dopoints,DRect * clip,enum outlinesfm_flags strokeFillMode,Color AlphaChannelOverride)1638 void CVDrawSplineSetSpecialized( CharView *cv, GWindow pixmap, SplinePointList *set,
1639 				 Color fg, int dopoints, DRect *clip,
1640 				 enum outlinesfm_flags strokeFillMode,
1641 				 Color AlphaChannelOverride )
1642 {
1643     Spline *spline, *first;
1644     SplinePointList *spl;
1645     int truetype_markup = set==cv->b.gridfit && cv->dv!=NULL;
1646     CharViewTab* tab = CVGetActiveTab(cv);
1647 
1648     if ( cv->inactive )
1649 	dopoints = false;
1650 
1651     if( strokeFillMode == sfm_fill ) {
1652     	CVDrawSplineSetOutlineOnly( cv, pixmap, set,
1653     				    fg, dopoints, clip, strokeFillMode );
1654     }
1655 
1656     GDrawSetFont(pixmap,cv->small);		/* For point numbers */
1657     for ( spl = set; spl!=NULL; spl = spl->next ) {
1658 	if ( spl->contour_name!=NULL )
1659 	    CVDrawContourName(cv,pixmap,spl,fg);
1660 	if ( dopoints>0 || (dopoints==-1 && cv->showpointnumbers) ) {
1661 	    first = NULL;
1662 	    if ( dopoints>0 )
1663 		DrawDirection(cv,pixmap,spl->first);
1664 	    if ( cv->b.sc->inspiro && hasspiro()) {
1665 		if ( dopoints>=0 ) {
1666 		    int i;
1667 		    if ( spl->spiros==NULL ) {
1668 			spl->spiros = SplineSet2SpiroCP(spl,&spl->spiro_cnt);
1669 			spl->spiro_max = spl->spiro_cnt;
1670 		    }
1671 		    for ( i=0; i<spl->spiro_cnt-1; ++i )
1672 			DrawSpiroPoint(cv,pixmap,&spl->spiros[i],spl,i, AlphaChannelOverride );
1673 		}
1674 	    } else {
1675 		for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
1676 		    DrawPoint(cv,pixmap,spline->from,spl,dopoints<0,truetype_markup, AlphaChannelOverride );
1677 		    CVDrawPointName(cv,pixmap,spline->from,fg);
1678 		    if ( first==NULL ) first = spline;
1679 		}
1680 		if ( spline==NULL ) {
1681 		    DrawPoint(cv,pixmap,spl->last,spl,dopoints<0,truetype_markup, AlphaChannelOverride );
1682 		    CVDrawPointName(cv,pixmap,spl->last,fg);
1683 		}
1684 	    }
1685 	}
1686     }
1687 
1688     if( strokeFillMode != sfm_nothing ) {
1689         /*
1690          * If we were filling, we have to stroke the outline again to properly show
1691          * clip path splines which will possibly have a different stroke color
1692          */
1693         Color thinfgcolor = fg;
1694         enum outlinesfm_flags fgstrokeFillMode = sfm_stroke;
1695         if( strokeFillMode==sfm_stroke_trans )
1696             fgstrokeFillMode = sfm_stroke_trans;
1697         if( shouldShowFilledUsingCairo(cv) ) {
1698             if (cv->inPreviewMode)
1699                 thinfgcolor = (thinfgcolor | 0x01000000) & 0x01ffffff;
1700             fgstrokeFillMode = sfm_stroke_trans;
1701         }
1702         CVDrawSplineSetOutlineOnly( cv, pixmap, set,
1703                         thinfgcolor, dopoints, clip,
1704                         fgstrokeFillMode );
1705 
1706         if( prefs_cv_outline_thickness > 1 )
1707         {
1708             // we only draw the inner half, so we double the user's expected
1709             // thickness here.
1710             int strokeWidth = prefs_cv_outline_thickness * 2 * tab->scale;
1711             Color strokefg = foreoutthicklinecol;
1712 
1713             if( shouldShowFilledUsingCairo(cv) && cv->inPreviewMode ) {
1714                 strokefg = (strokefg | 0x01000000) & 0x01ffffff;
1715             }
1716 
1717             int16 oldwidth = GDrawGetLineWidth( pixmap );
1718             GDrawSetLineWidth( pixmap, strokeWidth );
1719             GDrawPushClipOnly( pixmap );
1720 
1721             CVDrawSplineSetOutlineOnly( cv, pixmap, set,
1722                                         strokefg, dopoints, clip,
1723                                         sfm_clip );
1724             CVDrawSplineSetOutlineOnly( cv, pixmap, set,
1725                                         strokefg, dopoints, clip,
1726                                         sfm_stroke_trans );
1727 
1728             GDrawPopClip( pixmap, NULL );
1729             GDrawSetLineWidth( pixmap, oldwidth );
1730         }
1731     }
1732 
1733     for ( spl = set; spl!=NULL; spl = spl->next ) {
1734 	if (( cv->markextrema || cv->markpoi ) && dopoints && !cv->b.sc->inspiro )
1735 	    CVMarkInterestingLocations(cv,pixmap,spl);
1736 	if ( (cv->showalmosthvlines || cv->showalmosthvcurves ) && dopoints )
1737 	    CVMarkAlmostHV(cv,pixmap,spl);
1738     }
1739 }
1740 
CVDrawLayerSplineSet(CharView * cv,GWindow pixmap,Layer * layer,Color fg,int dopoints,DRect * clip,enum outlinesfm_flags strokeFillMode)1741 static void CVDrawLayerSplineSet(CharView *cv, GWindow pixmap, Layer *layer,
1742 	Color fg, int dopoints, DRect *clip, enum outlinesfm_flags strokeFillMode ) {
1743     CharViewTab* tab = CVGetActiveTab(cv);
1744     int active = cv->b.layerheads[cv->b.drawmode]==layer;
1745     int ml = cv->b.sc->parent->multilayer;
1746 
1747     if ( ml && layer->dostroke ) {
1748 	if ( layer->stroke_pen.brush.col!=COLOR_INHERITED &&
1749 		layer->stroke_pen.brush.col!=view_bgcol )
1750 	    fg = layer->stroke_pen.brush.col;
1751     }
1752     if ( ml && layer->dofill ) {
1753 	if ( layer->fill_brush.col!=COLOR_INHERITED &&
1754 		layer->fill_brush.col!=view_bgcol )
1755 	    fg = layer->fill_brush.col;
1756     }
1757 
1758     if ( ml && !active && layer!=&cv->b.sc->layers[ly_back] )
1759 	GDrawSetDashedLine(pixmap,5,5,tab->xoff+cv->height-tab->yoff);
1760 
1761     CVDrawSplineSetSpecialized( cv, pixmap, layer->splines,
1762 				fg, dopoints && active, clip,
1763 				strokeFillMode, 0 );
1764 
1765     if ( ml && !active && layer!=&cv->b.sc->layers[ly_back] )
1766 	GDrawSetDashedLine(pixmap,0,0,0);
1767 }
1768 
CVDrawTemplates(CharView * cv,GWindow pixmap,SplineChar * template,DRect * clip)1769 static void CVDrawTemplates(CharView *cv,GWindow pixmap,SplineChar *template,DRect *clip) {
1770     RefChar *r;
1771 
1772     CVDrawSplineSet(cv,pixmap,template->layers[ly_fore].splines,templateoutlinecol,false,clip);
1773     for ( r=template->layers[ly_fore].refs; r!=NULL; r=r->next )
1774 	CVDrawSplineSet(cv,pixmap,r->layers[0].splines,templateoutlinecol,false,clip);
1775 }
1776 
CVShowDHintInstance(CharView * cv,GWindow pixmap,BasePoint * bp)1777 static void CVShowDHintInstance(CharView *cv, GWindow pixmap, BasePoint *bp) {
1778     IPoint ip[40], ip2[40];
1779     GPoint clipped[13];
1780     int i,j, tot,last;
1781     CharViewTab* tab = CVGetActiveTab(cv);
1782 
1783     ip[0].x = tab->xoff + rint( bp[0].x*tab->scale );
1784     ip[0].y = -tab->yoff + cv->height - rint( bp[0].y*tab->scale );
1785     ip[1].x = tab->xoff + rint(bp[1].x*tab->scale);
1786     ip[1].y = -tab->yoff + cv->height - rint( bp[1].y*tab->scale );
1787     ip[2].x = tab->xoff + rint( bp[2].x*tab->scale );
1788     ip[2].y = -tab->yoff + cv->height - rint( bp[2].y*tab->scale );
1789     ip[3].x = tab->xoff + rint( bp[3].x*tab->scale );
1790     ip[3].y = -tab->yoff + cv->height - rint( bp[3].y*tab->scale );
1791 
1792     if (( ip[0].x<0 && ip[1].x<0 && ip[2].x<0 && ip[3].x<0 ) ||
1793 	    ( ip[0].x>=cv->width && ip[1].x>=cv->width && ip[2].x>=cv->width && ip[3].x>=cv->width ) ||
1794 	    ( ip[0].y<0 && ip[1].y<0 && ip[2].y<0 && ip[3].y<0 ) ||
1795 	    ( ip[0].y>=cv->height && ip[1].y>=cv->height && ip[2].y>=cv->height && ip[3].y>=cv->height ))
1796 return;		/* Offscreen */
1797 
1798     /* clip to left edge */
1799     tot = 4;
1800     for ( i=j=0; i<tot; ++i ) {
1801 	last = i==0?tot-1:i-1;
1802 	if ( ip[i].x>=0 && ip[last].x>=0) {
1803 	    ip2[j++] = ip[i];
1804 	} else if ( ip[i].x<0 && ip[last].x<0 ) {
1805 	    if ( j==0 || ip2[j-1].x!=0 || ip2[j-1].y!=ip[i].y ) {
1806 		ip2[j].x = 0;
1807 		ip2[j++].y = ip[i].y;
1808 	    }
1809 	} else {
1810 	    ip2[j].x = 0;
1811 	    ip2[j++].y = ip[last].y - ip[last].x * ((real) (ip[i].y-ip[last].y))/(ip[i].x-ip[last].x);
1812 	    if ( ip[i].x>0 )
1813 		ip2[j++] = ip[i];
1814 	    else {
1815 		ip2[j].x = 0;
1816 		ip2[j++].y = ip[i].y;
1817 	    }
1818 	}
1819     }
1820     /* clip to right edge */
1821     tot = j;
1822     for ( i=j=0; i<tot; ++i ) {
1823 	last = i==0?tot-1:i-1;
1824 	if ( ip2[i].x<cv->width && ip2[last].x<cv->width ) {
1825 	    ip[j++] = ip2[i];
1826 	} else if ( ip2[i].x>=cv->width && ip2[last].x>=cv->width ) {
1827 	    if ( j==0 || ip[j-1].x!=cv->width-1 || ip[j-1].y!=ip2[i].y ) {
1828 		ip[j].x = cv->width-1;
1829 		ip[j++].y = ip2[i].y;
1830 	    }
1831 	} else {
1832 	    ip[j].x = cv->width-1;
1833 	    ip[j++].y = ip2[last].y + (cv->width-1- ip2[last].x) * ((real) (ip2[i].y-ip2[last].y))/(ip2[i].x-ip2[last].x);
1834 	    if ( ip2[i].x<cv->width )
1835 		ip[j++] = ip2[i];
1836 	    else {
1837 		ip[j].x = cv->width-1;
1838 		ip[j++].y = ip2[i].y;
1839 	    }
1840 	}
1841     }
1842     /* clip to bottom edge */
1843     tot = j;
1844     for ( i=j=0; i<tot; ++i ) {
1845 	last = i==0?tot-1:i-1;
1846 	if ( ip[i].y>=0 && ip[last].y>=0) {
1847 	    ip2[j++] = ip[i];
1848 	} else if ( ip[i].y<0 && ip[last].y<0 ) {
1849 	    ip2[j].y = 0;
1850 	    ip2[j++].x = ip[i].x;
1851 	} else {
1852 	    ip2[j].y = 0;
1853 	    ip2[j++].x = ip[last].x - ip[last].y * ((real) (ip[i].x-ip[last].x))/(ip[i].y-ip[last].y);
1854 	    if ( ip[i].y>0 )
1855 		ip2[j++] = ip[i];
1856 	    else {
1857 		ip2[j].y = 0;
1858 		ip2[j++].x = ip[i].x;
1859 	    }
1860 	}
1861     }
1862     /* clip to top edge */
1863     tot = j;
1864     for ( i=j=0; i<tot; ++i ) {
1865 	last = i==0?tot-1:i-1;
1866 	if ( ip2[i].y<cv->height && ip2[last].y<cv->height ) {
1867 	    ip[j++] = ip2[i];
1868 	} else if ( ip2[i].y>=cv->height && ip2[last].y>=cv->height ) {
1869 	    ip[j].y = cv->height-1;
1870 	    ip[j++].x = ip2[i].x;
1871 	} else {
1872 	    ip[j].y = cv->height-1;
1873 	    ip[j++].x = ip2[last].x + (cv->height-1- ip2[last].y) * ((real) (ip2[i].x-ip2[last].x))/(ip2[i].y-ip2[last].y);
1874 	    if ( ip2[i].y<cv->height )
1875 		ip[j++] = ip2[i];
1876 	    else {
1877 		ip[j].y = cv->height-1;
1878 		ip[j++].x = ip2[i].x;
1879 	    }
1880 	}
1881     }
1882 
1883     tot=j;
1884     clipped[0].x = ip[0].x; clipped[0].y = ip[0].y;
1885     for ( i=j=1; i<tot; ++i ) {
1886 	if ( ip[i].x!=ip[i-1].x || ip[i].y!=ip[i-1].y ) {
1887 	    clipped[j].x = ip[i].x; clipped[j++].y = ip[i].y;
1888 	}
1889     }
1890     clipped[j++] = clipped[0];
1891     GDrawFillPoly(pixmap,clipped,j,dhintcol);
1892 }
1893 
CVShowDHint(CharView * cv,GWindow pixmap,DStemInfo * dstem)1894 static void CVShowDHint ( CharView *cv, GWindow pixmap, DStemInfo *dstem ) {
1895     BasePoint bp[4];
1896     HintInstance *hi;
1897     double roff;
1898 
1899     roff =  ( dstem->right.x - dstem->left.x ) * dstem->unit.x +
1900             ( dstem->right.y - dstem->left.y ) * dstem->unit.y;
1901 
1902     for ( hi=dstem->where; hi!=NULL; hi=hi->next ) {
1903         bp[0].x = dstem->left.x + dstem->unit.x * hi->begin;
1904         bp[0].y = dstem->left.y + dstem->unit.y * hi->begin;
1905         bp[1].x = dstem->right.x + dstem->unit.x * ( hi->begin - roff );
1906         bp[1].y = dstem->right.y + dstem->unit.y * ( hi->begin - roff );
1907         bp[2].x = dstem->right.x + dstem->unit.x * ( hi->end - roff );
1908         bp[2].y = dstem->right.y + dstem->unit.y * ( hi->end - roff );
1909         bp[3].x = dstem->left.x + dstem->unit.x * hi->end;
1910         bp[3].y = dstem->left.y + dstem->unit.y * hi->end;
1911         CVShowDHintInstance( cv, pixmap, bp );
1912     }
1913 }
1914 
CVShowMinimumDistance(CharView * cv,GWindow pixmap,MinimumDistance * md)1915 static void CVShowMinimumDistance(CharView *cv, GWindow pixmap,MinimumDistance *md) {
1916     CharViewTab* tab = CVGetActiveTab(cv);
1917     int x1,y1, x2,y2;
1918     int xa, ya;
1919     int off = tab->xoff+cv->height-tab->yoff;
1920 
1921     if (( md->x && !cv->showmdx ) || (!md->x && !cv->showmdy))
1922 return;
1923     if ( md->sp1==NULL && md->sp2==NULL )
1924 return;
1925     if ( md->sp1!=NULL ) {
1926 	x1 = tab->xoff + rint( md->sp1->me.x*tab->scale );
1927 	y1 = -tab->yoff + cv->height - rint(md->sp1->me.y*tab->scale);
1928     } else {
1929 	x1 = tab->xoff + rint( cv->b.sc->width*tab->scale );
1930 	y1 = 0x80000000;
1931     }
1932     if ( md->sp2!=NULL ) {
1933 	x2 = tab->xoff + rint( md->sp2->me.x*tab->scale );
1934 	y2 = -tab->yoff + cv->height - rint(md->sp2->me.y*tab->scale);
1935     } else {
1936 	x2 = tab->xoff + rint( cv->b.sc->width*tab->scale );
1937 	y2 = y1-8;
1938     }
1939     if ( y1==0x80000000 )
1940 	y1 = y2-8;
1941     if ( md->x ) {
1942 	ya = (y1+y2)/2;
1943 	GDrawDrawArrow(pixmap, x1,ya, x2,ya, 2, mdhintcol);
1944 	GDrawSetDashedLine(pixmap,5,5,off);
1945 	GDrawDrawLine(pixmap, x1,ya, x1,y1, mdhintcol);
1946 	GDrawDrawLine(pixmap, x2,ya, x2,y2, mdhintcol);
1947     } else {
1948 	xa = (x1+x2)/2;
1949 	GDrawDrawArrow(pixmap, xa,y1, xa,y2, 2, mdhintcol);
1950 	GDrawSetDashedLine(pixmap,5,5,off);
1951 	GDrawDrawLine(pixmap, xa,y1, x1,y1, mdhintcol);
1952 	GDrawDrawLine(pixmap, xa,y2, x2,y2, mdhintcol);
1953     }
1954     GDrawSetDashedLine(pixmap,0,0,0);
1955 }
1956 
dtos(char * buf,real val)1957 static void dtos(char *buf,real val) {
1958     char *pt;
1959 
1960     sprintf( buf,"%.1f", (double) val);
1961     pt = buf+strlen(buf);
1962     if ( pt[-1]=='0' && pt[-2]=='.' ) pt[-2] = '\0';
1963 }
1964 
CVDrawBlues(CharView * cv,GWindow pixmap,char * bluevals,char * others,Color col)1965 static void CVDrawBlues(CharView *cv,GWindow pixmap,char *bluevals,char *others,
1966 	Color col) {
1967     double blues[24];
1968     char *pt, *end;
1969     int i=0, bcnt=0;
1970     GRect r;
1971     char buf[20];
1972     int len,len2;
1973     CharViewTab* tab = CVGetActiveTab(cv);
1974 
1975     if ( bluevals!=NULL ) {
1976 	for ( pt = bluevals; isspace( *pt ) || *pt=='['; ++pt);
1977 	while ( i<14 && *pt!='\0' && *pt!=']' ) {
1978 	    blues[i] = g_ascii_strtod(pt,&end);
1979 	    if ( pt==end )
1980 	break;
1981 	    ++i;
1982 	    pt = end;
1983 	    while ( isspace( *pt )) ++pt;
1984 	}
1985 	if ( i&1 ) --i;
1986     }
1987     if ( others!=NULL ) {
1988 	for ( pt = others; isspace( *pt ) || *pt=='['; ++pt);
1989 	while ( i<24 && *pt!='\0' && *pt!=']' ) {
1990 	    blues[i] = g_ascii_strtod(pt,&end);
1991 	    if ( pt==end )
1992 	break;
1993 	    ++i;
1994 	    pt = end;
1995 	    while ( isspace( *pt )) ++pt;
1996 	}
1997 	if ( i&1 ) --i;
1998     }
1999     bcnt = i;
2000     if ( i==0 )
2001 return;
2002 
2003     r.x = 0; r.width = cv->width;
2004     for ( i=0; i<bcnt; i += 2 ) {
2005 	int first, other;
2006 	first = -tab->yoff + cv->height - rint(blues[i]*tab->scale);
2007 	other = -tab->yoff + cv->height - rint(blues[i+1]*tab->scale);
2008 	r.y = first;
2009 	if ( ( r.y<0 && other<0 ) || (r.y>cv->height && other>cv->height))
2010     continue;
2011 	if ( r.y<0 ) r.y = 0;
2012 	else if ( r.y>cv->height ) r.y = cv->height;
2013 	if ( other<0 ) other = 0;
2014 	else if ( other>cv->height ) other = cv->height;
2015 	if ( other<r.y ) {
2016 	    r.height = r.y-other;
2017 	    r.y = other;
2018 	} else
2019 	    r.height = other-r.y;
2020 	if ( r.height==0 ) r.height = 1;	/* show something */
2021 	GDrawSetStippled(pixmap,2, 0,0);
2022 	GDrawFillRect(pixmap,&r,col);
2023 	GDrawSetStippled(pixmap,0, 0,0);
2024 
2025 	if ( first>-20 && first<cv->height+20 ) {
2026 	    dtos( buf, blues[i]);
2027 	    len = GDrawGetText8Width(pixmap,buf,-1);
2028 	    GDrawDrawText8(pixmap,cv->width-len-5,first-3,buf,-1,hintlabelcol);
2029 	} else
2030 	    len = 0;
2031 	if ( other>-20 && other<cv->height+20 ) {
2032 	    dtos( buf, blues[i+1]-blues[i]);
2033 	    len2 = GDrawGetText8Width(pixmap,buf,-1);
2034 	    GDrawDrawText8(pixmap,cv->width-len-5-len2-5,other+cv->sas-3,buf,-1,hintlabelcol);
2035 	}
2036     }
2037 }
2038 
CVShowHints(CharView * cv,GWindow pixmap)2039 static void CVShowHints(CharView *cv, GWindow pixmap) {
2040     StemInfo *hint;
2041     GRect r;
2042     HintInstance *hi;
2043     int end;
2044     Color col;
2045     DStemInfo *dstem;
2046     MinimumDistance *md;
2047     char *blues, *others;
2048     struct psdict *private = cv->b.sc->parent->private;
2049     char buf[20];
2050     int len, len2;
2051     SplinePoint *sp;
2052     SplineSet *spl;
2053     CharViewTab* tab = CVGetActiveTab(cv);
2054 
2055     GDrawSetFont(pixmap,cv->small);
2056     blues = PSDictHasEntry(private,"BlueValues"); others = PSDictHasEntry(private,"OtherBlues");
2057     if ( cv->showblues && (blues!=NULL || others!=NULL))
2058 	CVDrawBlues(cv,pixmap,blues,others,bluevalstipplecol);
2059     blues = PSDictHasEntry(private,"FamilyBlues"); others = PSDictHasEntry(private,"FamilyOtherBlues");
2060     if ( cv->showfamilyblues && (blues!=NULL || others!=NULL))
2061 	CVDrawBlues(cv,pixmap,blues,others,fambluestipplecol);
2062 
2063     if ( cv->showdhints ) for ( dstem = cv->b.sc->dstem; dstem!=NULL; dstem = dstem->next ) {
2064 	CVShowDHint(cv,pixmap,dstem);
2065     }
2066 
2067     if ( cv->showhhints && cv->b.sc->hstem!=NULL ) {
2068 	GDrawSetDashedLine(pixmap,5,5,tab->xoff);
2069 	for ( hint = cv->b.sc->hstem; hint!=NULL; hint = hint->next ) {
2070 	    if ( hint->width<0 ) {
2071 		r.y = -tab->yoff + cv->height - rint(hint->start*tab->scale);
2072 		r.height = rint(-hint->width*tab->scale)+1;
2073 	    } else {
2074 		r.y = -tab->yoff + cv->height - rint((hint->start+hint->width)*tab->scale);
2075 		r.height = rint(hint->width*tab->scale)+1;
2076 	    }
2077 	    col = hint->active ? hhintactivecol : hhintcol;
2078 	    /* XRectangles are shorts! */
2079 	    if ( r.y<32767 && r.y+r.height>-32768 ) {
2080 		if ( r.y<-32768 ) {
2081 		    r.height -= (-32768-r.y);
2082 		    r.y = -32768;
2083 		}
2084 		if ( r.y+r.height>32767 )
2085 		    r.height = 32767-r.y;
2086 		for ( hi=hint->where; hi!=NULL; hi=hi->next ) {
2087 		    r.x = tab->xoff + rint(hi->begin*tab->scale);
2088 		    end = tab->xoff + rint(hi->end*tab->scale);
2089 		    if ( end>=0 && r.x<=cv->width ) {
2090 			r.width = end-r.x+1;
2091 			GDrawFillRect(pixmap,&r,col);
2092 		    }
2093 		}
2094 	    }
2095 	    col = (!hint->active && hint->hasconflicts) ? conflicthintcol : col;
2096 	    if ( r.y>=0 && r.y<=cv->height )
2097 		GDrawDrawLine(pixmap,0,r.y,cv->width,r.y,col);
2098 	    if ( r.y+r.height>=0 && r.y+r.height<=cv->width )
2099 		GDrawDrawLine(pixmap,0,r.y+r.height-1,cv->width,r.y+r.height-1,col);
2100 
2101 	    r.y = -tab->yoff + cv->height - rint(hint->start*tab->scale);
2102 	    r.y += ( hint->width>0 ) ? -3 : cv->sas+3;
2103 	    if ( r.y>-20 && r.y<cv->height+20 ) {
2104 		dtos( buf, hint->start);
2105 		len = GDrawGetText8Width(pixmap,buf,-1);
2106 		GDrawDrawText8(pixmap,cv->width-len-5,r.y,buf,-1,hintlabelcol);
2107 	    } else
2108 		len = 0;
2109 	    r.y = -tab->yoff + cv->height - rint((hint->start+hint->width)*tab->scale);
2110 	    r.y += ( hint->width>0 ) ? cv->sas+3 : -3;
2111 	    if ( r.y>-20 && r.y<cv->height+20 ) {
2112 		if ( hint->ghost ) {
2113 		    buf[0] = 'G';
2114 		    buf[1] = ' ';
2115 		    dtos(buf+2, hint->width);
2116 		} else
2117 		    dtos( buf, hint->width);
2118 		len2 = GDrawGetText8Width(pixmap,buf,-1);
2119 		GDrawDrawText8(pixmap,cv->width-len-5-len2-5,r.y,buf,-1,hintlabelcol);
2120 	    }
2121 	}
2122     }
2123     if ( cv->showvhints && cv->b.sc->vstem!=NULL ) {
2124 	GDrawSetDashedLine(pixmap,5,5,cv->height-tab->yoff);
2125 	for ( hint = cv->b.sc->vstem; hint!=NULL; hint = hint->next ) {
2126 	    if ( hint->width<0 ) {
2127 		r.x = tab->xoff + rint( (hint->start+hint->width)*tab->scale );
2128 		r.width = rint(-hint->width*tab->scale)+1;
2129 	    } else {
2130 		r.x = tab->xoff + rint(hint->start*tab->scale);
2131 		r.width = rint(hint->width*tab->scale)+1;
2132 	    }
2133 	    col = hint->active ? vhintactivecol : vhintcol;
2134 	    if ( r.x<32767 && r.x+r.width>-32768 ) {
2135 		if ( r.x<-32768 ) {
2136 		    r.width -= (-32768-r.x);
2137 		    r.x = -32768;
2138 		}
2139 		if ( r.x+r.width>32767 )
2140 		    r.width = 32767-r.x;
2141 		for ( hi=hint->where; hi!=NULL; hi=hi->next ) {
2142 		    r.y = -tab->yoff + cv->height - rint(hi->end*tab->scale);
2143 		    end = -tab->yoff + cv->height - rint(hi->begin*tab->scale);
2144 		    if ( end>=0 && r.y<=cv->height ) {
2145 			r.height = end-r.y+1;
2146 			GDrawFillRect(pixmap,&r,col);
2147 		    }
2148 		}
2149 	    }
2150 	    col = (!hint->active && hint->hasconflicts) ? conflicthintcol : col;
2151 	    if ( r.x>=0 && r.x<=cv->width )
2152 		GDrawDrawLine(pixmap,r.x,0,r.x,cv->height,col);
2153 	    if ( r.x+r.width>=0 && r.x+r.width<=cv->width )
2154 		GDrawDrawLine(pixmap,r.x+r.width-1,0,r.x+r.width-1,cv->height,col);
2155 
2156 	    r.x = tab->xoff + rint(hint->start*tab->scale);
2157 	    if ( r.x>-60 && r.x<cv->width+20 ) {
2158 		dtos( buf, hint->start);
2159 		len = GDrawGetText8Width(pixmap,buf,-1);
2160 		r.x += ( hint->width>0 ) ? 3 : -len-3;
2161 		GDrawDrawText8(pixmap,r.x,cv->sas+3,buf,-1,hintlabelcol);
2162 	    }
2163 	    r.x = tab->xoff + rint((hint->start+hint->width)*tab->scale);
2164 	    if ( r.x>-60 && r.x<cv->width+20 ) {
2165 		if ( hint->ghost ) {
2166 		    buf[0] = 'G';
2167 		    buf[1] = ' ';
2168 		    dtos(buf+2, hint->width);
2169 		} else
2170 		    dtos( buf, hint->width);
2171 		len = GDrawGetText8Width(pixmap,buf,-1);
2172 		r.x += ( hint->width>0 ) ? -len-3 : 3;
2173 		GDrawDrawText8(pixmap,r.x,cv->sas+cv->sfh+3,buf,-1,hintlabelcol);
2174 	    }
2175 	}
2176     }
2177     GDrawSetDashedLine(pixmap,0,0,0);
2178 
2179     for ( md=cv->b.sc->md; md!=NULL; md=md->next )
2180 	CVShowMinimumDistance(cv, pixmap,md);
2181 
2182     if ( cv->showvhints || cv->showhhints ) {
2183 	for ( spl=cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
2184 	    if ( spl->first->prev!=NULL ) for ( sp=spl->first ; ; ) {
2185 		if ( cv->showhhints && sp->flexx ) {
2186 		    double x,y,end;
2187 		    x = tab->xoff + rint(sp->me.x*tab->scale);
2188 		    y = -tab->yoff + cv->height - rint(sp->me.y*tab->scale);
2189 		    end = tab->xoff + rint(sp->next->to->me.x*tab->scale);
2190 		    if ( x>-4096 && x<32767 && y>-4096 && y<32767 ) {
2191 			GDrawDrawLine(pixmap,x,y,end,y,hflexhintcol);
2192 		    }
2193 		}
2194 		if ( cv->showvhints && sp->flexy ) {
2195 		    double x,y,end;
2196 		    x = tab->xoff + rint(sp->me.x*tab->scale);
2197 		    y = -tab->yoff + cv->height - rint(sp->me.y*tab->scale);
2198 		    end = -tab->yoff + cv->height - rint(sp->next->to->me.y*tab->scale);
2199 		    if ( x>-4096 && x<32767 && y>-4096 && y<32767 ) {
2200 			GDrawDrawLine(pixmap,x,y,x,end,vflexhintcol);
2201 		    }
2202 		}
2203 		if ( sp->next==NULL )		/* This can happen if we get an internal error inside of RemoveOverlap when the pointlist is not in good shape */
2204 	    break;
2205 		sp = sp->next->to;
2206 		if ( sp==spl->first )
2207 	    break;
2208 	    }
2209 	}
2210     }
2211 }
2212 
CVDrawRefName(CharView * cv,GWindow pixmap,RefChar * ref,int fg)2213 static void CVDrawRefName(CharView *cv,GWindow pixmap,RefChar *ref,int fg) {
2214     CharViewTab* tab = CVGetActiveTab(cv);
2215     int x,y, len;
2216     GRect size;
2217 
2218     x = tab->xoff + rint(ref->top.x*tab->scale);
2219     y = -tab->yoff + cv->height - rint(ref->top.y*tab->scale);
2220     y -= 5;
2221     if ( x<-400 || y<-40 || x>cv->width+400 || y>cv->height )
2222 return;
2223 
2224     GDrawLayoutInit(pixmap,ref->sc->name,-1,cv->small);
2225     GDrawLayoutExtents(pixmap,&size);
2226     GDrawLayoutDraw(pixmap,x-size.width/2,y,fg);
2227     len = size.width;
2228     if ( ref->use_my_metrics )
2229 	GDrawDrawImage(pixmap,&GIcon_lock,NULL,x+len+3,y-cv->sas);
2230 }
2231 
DrawAnchorPoint(GWindow pixmap,int x,int y,int selected)2232 void DrawAnchorPoint(GWindow pixmap,int x, int y,int selected) {
2233     GPoint gp[9];
2234     Color col = anchorcol;
2235 
2236     gp[0].x = x-1; gp[0].y = y-1;
2237     gp[1].x = x;   gp[1].y = y-6;
2238     gp[2].x = x+1; gp[2].y = y-1;
2239     gp[3].x = x+6; gp[3].y = y;
2240     gp[4].x = x+1; gp[4].y = y+1;
2241     gp[5].x = x;   gp[5].y = y+6;
2242     gp[6].x = x-1; gp[6].y = y+1;
2243     gp[7].x = x-6; gp[7].y = y;
2244     gp[8] = gp[0];
2245     if ( selected )
2246 	GDrawDrawPoly(pixmap,gp,9,col);
2247     else
2248 	GDrawFillPoly(pixmap,gp,9,col);
2249 }
2250 
CVDrawAnchorPoints(CharView * cv,GWindow pixmap)2251 static void CVDrawAnchorPoints(CharView *cv,GWindow pixmap) {
2252     CharViewTab* tab = CVGetActiveTab(cv);
2253     int x,y, len, sel;
2254     Color col = anchorcol;
2255     AnchorPoint *ap;
2256     char *name, ubuf[50];
2257     GRect r;
2258 
2259     if ( cv->b.drawmode!=dm_fore || cv->b.sc->anchor==NULL || !cv->showanchor )
2260 return;
2261     GDrawSetFont(pixmap,cv->normal);
2262 
2263     for ( sel=0; sel<2; ++sel ) {
2264 	for ( ap = cv->b.sc->anchor; ap!=NULL; ap=ap->next ) if ( ap->selected==sel ) {
2265 	    x = tab->xoff + rint(ap->me.x*tab->scale);
2266 	    y = -tab->yoff + cv->height - rint(ap->me.y*tab->scale);
2267 	    if ( x<-400 || y<-40 || x>cv->width+400 || y>cv->height )
2268 	continue;
2269 
2270 	    DrawAnchorPoint(pixmap,x,y,ap->selected);
2271 	    ubuf[30]=0;
2272 	    if ( ap->anchor->type==act_mkmk ) {
2273 		cc_strncpy(ubuf,ap->anchor->name,30);
2274 		strcat(ubuf," ");
2275 		strcat(ubuf,ap->type==at_basemark ? _("Base") : _("Mark") );
2276 		name = ubuf;
2277 	    } else if ( ap->type==at_basechar || ap->type==at_mark || ap->type==at_basemark ) {
2278 		name = ap->anchor->name;
2279 	    } else if ( ap->type==at_centry || ap->type==at_cexit ) {
2280 		cc_strncpy(ubuf,ap->anchor->name,30);
2281 		strcat(ubuf,ap->type==at_centry ? _("Entry") : _("Exit") );
2282 		name = ubuf;
2283 	    } else if ( ap->type==at_baselig ) {
2284 		cc_strncpy(ubuf,ap->anchor->name,30);
2285 		sprintf(ubuf+strlen(ubuf),"#%d", ap->lig_index);
2286 		name = ubuf;
2287 	    } else
2288 		name = NULL;		/* Should never happen */
2289 
2290 	    GRect size;
2291 	    GDrawLayoutInit(pixmap,name,-1,NULL);
2292 	    GDrawLayoutExtents(pixmap,&size);
2293 	    len = size.width;
2294 
2295 	    r.x = x-len/2; r.width = len;
2296 	    r.y = y+7; r.height = cv->nfh;
2297 	    GDrawFillRect(pixmap,&r,view_bgcol );
2298 	    GDrawLayoutDraw(pixmap,x-len/2,y+7+cv->nas,col);
2299 	}
2300     }
2301 }
2302 
DrawImageList(CharView * cv,GWindow pixmap,ImageList * backimages)2303 static void DrawImageList(CharView *cv,GWindow pixmap,ImageList *backimages) {
2304     CharViewTab* tab = CVGetActiveTab(cv);
2305 
2306     while ( backimages!=NULL ) {
2307 	struct _GImage *base = backimages->image->list_len==0?
2308 		backimages->image->u.image:backimages->image->u.images[0];
2309 
2310 	GDrawDrawImageMagnified(pixmap, backimages->image, NULL,
2311 		(int) (tab->xoff + rint(backimages->xoff * tab->scale)),
2312 		(int) (-tab->yoff + cv->height - rint(backimages->yoff*tab->scale)),
2313 		(int) rint((base->width*backimages->xscale*tab->scale)),
2314 		(int) rint((base->height*backimages->yscale*tab->scale)));
2315 	backimages = backimages->next;
2316     }
2317 }
2318 
DrawSelImageList(CharView * cv,GWindow pixmap,ImageList * images)2319 static void DrawSelImageList(CharView *cv,GWindow pixmap,ImageList *images) {
2320     while ( images!=NULL ) {
2321 	if ( images->selected )
2322 	    CVDrawBB(cv,pixmap,&images->bb);
2323 	images = images->next;
2324     }
2325 }
2326 
DrawOldState(CharView * cv,GWindow pixmap,Undoes * undo,DRect * clip)2327 static void DrawOldState(CharView *cv, GWindow pixmap, Undoes *undo, DRect *clip) {
2328     RefChar *refs;
2329 
2330     if ( undo==NULL )
2331 return;
2332 
2333     CVDrawSplineSet(cv,pixmap,undo->u.state.splines,oldoutlinecol,false,clip);
2334     for ( refs=undo->u.state.refs; refs!=NULL; refs=refs->next )
2335 	if ( refs->layers[0].splines!=NULL )
2336 	    CVDrawSplineSet(cv,pixmap,refs->layers[0].splines,oldoutlinecol,false,clip);
2337     /* Don't do images... */
2338 }
2339 
DrawTransOrigin(CharView * cv,GWindow pixmap)2340 static void DrawTransOrigin(CharView *cv, GWindow pixmap) {
2341     CharViewTab* tab = CVGetActiveTab(cv);
2342     int x = rint(cv->p.cx*tab->scale) + tab->xoff, y = cv->height-tab->yoff-rint(cv->p.cy*tab->scale);
2343 
2344     GDrawDrawLine(pixmap,x-4,y,x+4,y,transformorigincol);
2345     GDrawDrawLine(pixmap,x,y-4,x,y+4,transformorigincol);
2346 }
2347 
DrawVLine(CharView * cv,GWindow pixmap,real pos,Color fg,int flags,GImage * lock,char * name)2348 static void DrawVLine(CharView *cv,GWindow pixmap,real pos,Color fg, int flags,
2349 	GImage *lock, char *name) {
2350     CharViewTab* tab = CVGetActiveTab(cv);
2351     char buf[20];
2352     int x = tab->xoff + rint(pos*tab->scale);
2353     DrawLine(cv,pixmap,pos,-32768,pos,32767,fg);
2354     if ( x>-400 && x<cv->width+400 ) {
2355 	if ( flags&1 ) {
2356 	    dtos( buf, pos);
2357 	    GDrawSetFont(pixmap,cv->small);
2358 	    GDrawDrawText8(pixmap,x+5,cv->sas+3,buf,-1,metricslabelcol);
2359 	    if ( lock!=NULL )
2360 		GDrawDrawImage(pixmap,lock,NULL,x+5,3+cv->sfh);
2361 	}
2362 	if ( name!=NULL )
2363 	    GDrawDrawText8(pixmap,x+5,cv->sas+cv->sfh*(1+lock!=NULL)+3,name,-1,metricslabelcol);
2364     }
2365     if ( ItalicConstrained && cv->b.sc->parent->italicangle!=0 ) {
2366 	double t = tan(-cv->b.sc->parent->italicangle*FF_PI/180.);
2367 	int xoff = rint(8096*t);
2368 	DrawLine(cv,pixmap,pos-xoff,-8096,pos+xoff,8096,italiccoordcol);
2369     }
2370 }
2371 
DrawMMGhosts(CharView * cv,GWindow pixmap,DRect * clip)2372 static void DrawMMGhosts(CharView *cv,GWindow pixmap,DRect *clip) {
2373     /* In an MM font, draw any selected alternate versions of the current char */
2374     MMSet *mm = cv->b.sc->parent->mm;
2375     int j;
2376     SplineFont *sub;
2377     SplineChar *sc;
2378     RefChar *rf;
2379 
2380     if ( mm==NULL )
2381 return;
2382     for ( j = 0; j<mm->instance_count+1; ++j ) {
2383 	if ( j==0 )
2384 	    sub = mm->normal;
2385 	else
2386 	    sub = mm->instances[j-1];
2387 	sc = NULL;
2388 	if ( cv->b.sc->parent!=sub && (cv->mmvisible & (1<<j)) &&
2389 		cv->b.sc->orig_pos<sub->glyphcnt )
2390 	    sc = sub->glyphs[cv->b.sc->orig_pos];
2391 	if ( sc!=NULL ) {
2392 	    for ( rf=sc->layers[ly_fore].refs; rf!=NULL; rf = rf->next )
2393 		CVDrawSplineSet(cv,pixmap,rf->layers[0].splines,backoutlinecol,false,clip);
2394 	    CVDrawSplineSet(cv,pixmap,sc->layers[ly_fore].splines,backoutlinecol,false,clip);
2395 	}
2396     }
2397 }
2398 
CVDrawGridRaster(CharView * cv,GWindow pixmap,DRect * clip)2399 static void CVDrawGridRaster(CharView *cv, GWindow pixmap, DRect *clip ) {
2400     CharViewTab* tab = CVGetActiveTab(cv);
2401     if ( cv->showgrids ) {
2402 	/* Draw ppem grid, and the raster for truetype debugging, grid fit */
2403 	GRect pixel;
2404 	real ygrid_spacing = (cv->b.sc->parent->ascent+cv->b.sc->parent->descent) / (real) cv->ft_ppemy;
2405 	real xgrid_spacing = (cv->b.sc->parent->ascent+cv->b.sc->parent->descent) / (real) cv->ft_ppemx;
2406 	int max,jmax,ii,i,jj,j;
2407 	int minx, maxx, miny, maxy, r,or=0;
2408 	Color clut[256];
2409 
2410 	pixel.width = xgrid_spacing*tab->scale+1;
2411 	pixel.height = ygrid_spacing*tab->scale+1;
2412 	if ( cv->raster!=NULL ) {
2413 	    if ( cv->raster->num_greys>2 ) {
2414 		int rb, gb, bb, rd, gd, bd;
2415 		clut[0] = view_bgcol ;
2416 		rb = COLOR_RED(clut[0]); gb = COLOR_GREEN(clut[0]); bb = COLOR_BLUE(clut[0]);
2417 		rd = COLOR_RED(rasterdarkcol)-rb;
2418 		gd = COLOR_GREEN(rasterdarkcol)-gb;
2419 		bd = COLOR_BLUE(rasterdarkcol)-bb;
2420 		for ( i=1; i<256; ++i ) {
2421 		    clut[i] = ( (rb +rd*(i)/0xff)<<16 ) |
2422 			    ( (gb+gd*(i)/0xff)<<8 ) |
2423 			    ( (bb+bd*(i)/0xff) );
2424 		}
2425 	    }
2426 	    minx = cv->raster->lb; maxx = minx+cv->raster->cols;
2427 	    maxy = cv->raster->as; miny = maxy-cv->raster->rows;
2428 	    if ( cv->oldraster!=NULL ) {
2429 		if ( cv->oldraster->lb<minx ) minx = cv->oldraster->lb;
2430 		if ( cv->oldraster->lb+cv->oldraster->cols>maxx ) maxx = cv->oldraster->lb+cv->oldraster->cols;
2431 		if ( cv->oldraster->as>maxy ) maxy = cv->oldraster->as;
2432 		if ( cv->oldraster->as-cv->oldraster->rows<miny ) miny = cv->oldraster->as-cv->oldraster->rows;
2433 	    }
2434 	    for ( ii=maxy; ii>miny; --ii ) {
2435 		for ( jj=minx; jj<maxx; ++jj ) {
2436 		    i = cv->raster->as-ii; j = jj-cv->raster->lb;
2437 		    if ( i<0 || i>=cv->raster->rows || j<0 || j>=cv->raster->cols )
2438 			r = 0;
2439 		    else if ( cv->raster->num_greys<=2 )
2440 			r = cv->raster->bitmap[i*cv->raster->bytes_per_row+(j>>3)] & (1<<(7-(j&7)));
2441 		    else
2442 			r = cv->raster->bitmap[i*cv->raster->bytes_per_row+j];
2443 		    if ( cv->oldraster==NULL || cv->oldraster->num_greys!=cv->raster->num_greys)
2444 			or = r;
2445 		    else {
2446 			i = cv->oldraster->as-ii; j = jj-cv->oldraster->lb;
2447 			if ( i<0 || i>=cv->oldraster->rows || j<0 || j>=cv->oldraster->cols )
2448 			    or = 0;
2449 			else if ( cv->oldraster->num_greys<=2 )
2450 			    or = cv->oldraster->bitmap[i*cv->oldraster->bytes_per_row+(j>>3)] & (1<<(7-(j&7)));
2451 			else
2452 			    or = cv->oldraster->bitmap[i*cv->oldraster->bytes_per_row+j];
2453 		    }
2454 		    if ( r || ( or && cv->showdebugchanges)) {
2455 			pixel.x = jj*xgrid_spacing*tab->scale + tab->xoff+1;
2456 			pixel.y = cv->height-tab->yoff - rint(ii*ygrid_spacing*tab->scale);
2457 			if ( cv->showdebugchanges ) {
2458 			    if ( cv->raster->num_greys<=2 )
2459 				GDrawFillRect(pixmap,&pixel,(r && or) ? rastercol : r ? rasternewcol : rasteroldcol );
2460 			    else
2461 				GDrawFillRect(pixmap,&pixel,(r-or>-16 && r-or<16) ? clut[r] : (clut[r]&0x00ff00) );
2462 			} else {
2463 			    if ( cv->raster->num_greys<=2 )
2464 				GDrawFillRect(pixmap,&pixel, rastercol );
2465 			    else
2466 				GDrawFillRect(pixmap,&pixel, clut[r] );
2467 			}
2468 		    }
2469 		}
2470 	    }
2471 	}
2472 
2473 	for ( i = floor( clip->x/xgrid_spacing ), max = ceil((clip->x+clip->width)/xgrid_spacing);
2474 		i<=max; ++i )
2475 	    DrawLine(cv,pixmap,i*xgrid_spacing,-32768,i*xgrid_spacing,32767,i==0?coordcol:rastergridcol);
2476 	for ( i = floor( clip->y/ygrid_spacing ), max = ceil((clip->y+clip->height)/ygrid_spacing);
2477 		i<=max; ++i )
2478 	    DrawLine(cv,pixmap,-32768,i*ygrid_spacing,32767,i*ygrid_spacing,i==0?coordcol:rastergridcol);
2479 	if ( xgrid_spacing*tab->scale>=7 && ygrid_spacing*tab->scale>=7) {
2480 	    for ( i = floor( clip->x/xgrid_spacing ), max = ceil((clip->x+clip->width)/xgrid_spacing);
2481 		    i<=max; ++i )
2482 		for ( j = floor( clip->y/ygrid_spacing ), jmax = ceil((clip->y+clip->height)/ygrid_spacing);
2483 			j<=jmax; ++j ) {
2484 		    int x = (i+.5)*xgrid_spacing*tab->scale + tab->xoff;
2485 		    int y = cv->height-tab->yoff - rint((j+.5)*ygrid_spacing*tab->scale);
2486 		    GDrawDrawLine(pixmap,x-2,y,x+2,y,rastergridcol);
2487 		    GDrawDrawLine(pixmap,x,y-2,x,y+2,rastergridcol);
2488 		}
2489 	}
2490 	if ( cv->qg!=NULL ) {
2491 	    pixel.x = cv->note_x*xgrid_spacing*tab->scale + tab->xoff;
2492 	    pixel.y = cv->height-tab->yoff - rint(cv->note_y*ygrid_spacing*tab->scale)
2493 		- pixel.height;
2494 	    if ( pixel.height>=20 )
2495 		GDrawSetLineWidth(pixmap,3);
2496 	    else if ( pixel.height>10 )
2497 		GDrawSetLineWidth(pixmap,2);
2498 	    GDrawDrawRect(pixmap,&pixel,deltagridcol);
2499 	    GDrawSetLineWidth(pixmap,0);
2500 	    {
2501 		int x = (cv->note_x+.5)*xgrid_spacing*tab->scale + tab->xoff;
2502 		int y = cv->height-tab->yoff - rint((cv->note_y+.5)*ygrid_spacing*tab->scale);
2503 		GDrawDrawLine(pixmap,x-2,y,x+2,y,deltagridcol);
2504 		GDrawDrawLine(pixmap,x,y-2,x,y+2,deltagridcol);
2505 	    }
2506 	}
2507     }
2508     if ( cv->showback[0]&1 ) {
2509 	CVDrawSplineSet(cv,pixmap,cv->b.gridfit,gridfitoutlinecol,
2510 		cv->showpoints,clip);
2511     }
2512 }
2513 
APinSC(AnchorPoint * ap,SplineChar * sc)2514 static int APinSC(AnchorPoint *ap,SplineChar *sc) {
2515     /* Anchor points can be deleted ... */
2516     AnchorPoint *test;
2517 
2518     for ( test=sc->anchor; test!=NULL && test!=ap; test = test->next );
2519 return( test==ap );
2520 }
2521 
DrawAPMatch(CharView * cv,GWindow pixmap,DRect * clip)2522 static void DrawAPMatch(CharView *cv,GWindow pixmap,DRect *clip) {
2523     SplineChar *sc = cv->b.sc, *apsc = cv->apsc;
2524     SplineFont *sf = sc->parent;
2525     real trans[6];
2526     SplineSet *head, *tail, *temp;
2527     RefChar *ref;
2528     int layer = CVLayer((CharViewBase *) cv);
2529 
2530     if ( cv->b.drawmode==dm_grid )
2531 return;
2532 
2533     /* The other glyph might have been removed from the font */
2534     /* Either anchor might have been deleted. Be prepared for that to happen */
2535     if ( (apsc->orig_pos>=sf->glyphcnt || apsc->orig_pos<0) ||
2536 	    sf->glyphs[apsc->orig_pos]!=apsc ||
2537 	    !APinSC(cv->apmine,sc) || !APinSC(cv->apmatch,apsc)) {
2538 	cv->apmine = cv->apmatch = NULL;
2539 	cv->apsc =NULL;
2540 return;
2541     }
2542 
2543     /* Ok this isn't very accurate, but we are going to use the current glyph's*/
2544     /*  coordinate system (because we're showing the current glyph), we should */
2545     /*  always use the base character's coordinates, but that would screw up   */
2546     /*  editing of the current glyph if it happened to be the mark */
2547     trans[0] = trans[3] = 1;
2548     trans[1] = trans[2] = 0;
2549     trans[4] = cv->apmine->me.x - cv->apmatch->me.x;
2550     trans[5] = cv->apmine->me.y - cv->apmatch->me.y;
2551 
2552     head = tail = SplinePointListCopy(apsc->layers[layer].splines);
2553     for ( ref = apsc->layers[layer].refs; ref!=NULL; ref = ref->next ) {
2554 	temp = SplinePointListCopy(ref->layers[0].splines);
2555 	if ( head!=NULL ) {
2556 	    for ( ; tail->next!=NULL; tail = tail->next );
2557 	    tail->next = temp;
2558 	} else
2559 	    head = tail = temp;
2560     }
2561     head = SplinePointListTransform(head,trans,tpt_AllPoints);
2562     CVDrawSplineSet(cv,pixmap,head,anchoredoutlinecol,
2563 	    false,clip);
2564     SplinePointListsFree(head);
2565     if ( cv->apmine->type==at_mark || cv->apmine->type==at_centry ) {
2566 	DrawVLine(cv,pixmap,trans[4],anchoredoutlinecol,false,NULL,NULL);
2567 	DrawLine(cv,pixmap,-8096,trans[5],8096,trans[5],anchoredoutlinecol);
2568     }
2569 }
2570 
DrawPLine(CharView * cv,GWindow pixmap,int x1,int y1,int x2,int y2,Color col)2571 static void DrawPLine(CharView *cv,GWindow pixmap,int x1, int y1, int x2, int y2,Color col) {
2572 
2573     if ( x1==x2 || y1==y2 ) {
2574 	if ( x1<0 ) x1=0;
2575 	else if ( x1>cv->width ) x1 = cv->width;
2576 	if ( x2<0 ) x2=0;
2577 	else if ( x2>cv->width ) x2 = cv->width;
2578 	if ( y1<0 ) y1=0;
2579 	else if ( y1>cv->height ) y1 = cv->height;
2580 	if ( y2<0 ) y2=0;
2581 	else if ( y2>cv->height ) y2 = cv->height;
2582     } else if ( y1<-1000 || y2<-1000 || x1<-1000 || x2<-1000 ||
2583 	    y1>cv->height+1000 || y2>cv->height+1000 ||
2584 	    x1>cv->width+1000 || x2>cv->width+1000 )
2585 return;
2586     GDrawDrawLine(pixmap,x1,y1,x2,y2,col);
2587 }
2588 
FindQuickBounds(SplineSet * ss,BasePoint ** bounds)2589 static void FindQuickBounds(SplineSet *ss,BasePoint **bounds) {
2590     SplinePoint *sp;
2591 
2592     for ( ; ss!=NULL; ss=ss->next ) {
2593 	sp = ss->first;
2594 	if ( sp->next==NULL || sp->next->to==sp )	/* Ignore contours with one point. Often tt points for moving references or anchors */
2595     continue;
2596 	for (;;) {
2597 	    if ( bounds[0]==NULL )
2598 		bounds[0] = bounds[1] = bounds[2] = bounds[3] = &sp->me;
2599 	    else {
2600 		if ( sp->me.x<bounds[0]->x ) bounds[0] = &sp->me;
2601 		if ( sp->me.x>bounds[1]->x ) bounds[1] = &sp->me;
2602 		if ( sp->me.y<bounds[2]->y ) bounds[2] = &sp->me;
2603 		if ( sp->me.y>bounds[3]->y ) bounds[3] = &sp->me;
2604 	    }
2605 	    if ( sp->next==NULL )
2606 	break;
2607 	    sp = sp->next->to;
2608 	    if ( sp==ss->first )
2609 	break;
2610 	}
2611     }
2612 }
2613 
SSFindItalicBounds(SplineSet * ss,double t,SplinePoint ** left,SplinePoint ** right)2614 static void SSFindItalicBounds(SplineSet *ss,double t,SplinePoint **left, SplinePoint **right) {
2615     SplinePoint *sp;
2616 
2617     if ( t==0 )
2618 return;
2619 
2620     for ( ; ss!=NULL; ss=ss->next ) {
2621 	sp = ss->first;
2622 	if ( sp->next==NULL || sp->next->to==sp )	/* Ignore contours with one point. Often tt points for moving references or anchors */
2623     continue;
2624 	for (;;) {
2625 	    if ( *left==NULL )
2626 		*left = *right = sp;
2627 	    else {
2628 		double xoff = sp->me.y*t;
2629 		if ( sp->me.x-xoff < (*left)->me.x - (*left)->me.y*t ) *left = sp;
2630 		if ( sp->me.x-xoff > (*right)->me.x - (*right)->me.y*t ) *right = sp;
2631 	    }
2632 	    if ( sp->next==NULL )
2633 	break;
2634 	    sp = sp->next->to;
2635 	    if ( sp==ss->first )
2636 	break;
2637 	}
2638     }
2639 }
2640 
CVSideBearings(GWindow pixmap,CharView * cv)2641 static void CVSideBearings(GWindow pixmap, CharView *cv) {
2642     CharViewTab* tab = CVGetActiveTab(cv);
2643     SplineChar *sc = cv->b.sc;
2644     RefChar *ref;
2645     BasePoint *bounds[4];
2646     int layer,last, first,l;
2647     int x,y, x2, y2;
2648     char buf[20];
2649 
2650     memset(bounds,0,sizeof(bounds));
2651     if ( sc->parent->multilayer ) {
2652 	last = sc->layer_cnt-1;
2653 	first = ly_fore;
2654     } else {
2655 	first = last = CVLayer( (CharViewBase *) cv);
2656 	if ( first==ly_grid )
2657 	    first = last = ly_fore;
2658     }
2659     for ( layer = first ; layer<=last; ++layer ) {
2660 	FindQuickBounds(sc->layers[layer].splines,bounds);
2661 	for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next )
2662 	    for ( l=0; l<ref->layer_cnt; ++l )
2663 		FindQuickBounds(ref->layers[l].splines,bounds);
2664     }
2665 
2666     if ( bounds[0]==NULL )
2667 return;				/* no points. no side bearings */
2668 
2669     GDrawSetFont(pixmap,cv->small);
2670     if ( cv->showhmetrics ) {
2671 	if ( bounds[0]->x!=0 ) {
2672 	    x = rint(bounds[0]->x*tab->scale) + tab->xoff;
2673 	    y = cv->height-tab->yoff-rint(bounds[0]->y*tab->scale);
2674 	    DrawPLine(cv,pixmap,tab->xoff,y,x,y,metricslabelcol);
2675 	     /* arrow heads */
2676 	     DrawPLine(cv,pixmap,tab->xoff,y,tab->xoff+4,y+4,metricslabelcol);
2677 	     DrawPLine(cv,pixmap,tab->xoff,y,tab->xoff+4,y-4,metricslabelcol);
2678 	     DrawPLine(cv,pixmap,x,y,x-4,y-4,metricslabelcol);
2679 	     DrawPLine(cv,pixmap,x,y,x-4,y+4,metricslabelcol);
2680 	    dtos( buf, bounds[0]->x);
2681 	    x = tab->xoff + (x-tab->xoff-GDrawGetText8Width(pixmap,buf,-1))/2;
2682 	    GDrawDrawText8(pixmap,x,y-4,buf,-1,metricslabelcol);
2683 	}
2684 
2685 	if ( sc->width != bounds[1]->x ) {
2686 	    x = rint(bounds[1]->x*tab->scale) + tab->xoff;
2687 	    y = cv->height-tab->yoff-rint(bounds[1]->y*tab->scale);
2688 	    x2 = rint(sc->width*tab->scale) + tab->xoff;
2689 	    DrawPLine(cv,pixmap,x,y,x2,y,metricslabelcol);
2690 	     /* arrow heads */
2691 	     DrawPLine(cv,pixmap,x,y,x+4,y+4,metricslabelcol);
2692 	     DrawPLine(cv,pixmap,x,y,x+4,y-4,metricslabelcol);
2693 	     DrawPLine(cv,pixmap,x2,y,x2-4,y-4,metricslabelcol);
2694 	     DrawPLine(cv,pixmap,x2,y,x2-4,y+4,metricslabelcol);
2695 	    dtos( buf, sc->width-bounds[1]->x);
2696 	    x = x + (x2-x-GDrawGetText8Width(pixmap,buf,-1))/2;
2697 	    GDrawDrawText8(pixmap,x,y-4,buf,-1,metricslabelcol);
2698 	}
2699 	if ( ItalicConstrained && cv->b.sc->parent->italicangle!=0 ) {
2700 	    double t = tan(-cv->b.sc->parent->italicangle*FF_PI/180.);
2701 	    if ( t!=0 ) {
2702 		SplinePoint *leftmost=NULL, *rightmost=NULL;
2703 		for ( layer=first; layer<=last; ++layer ) {
2704 		    SSFindItalicBounds(sc->layers[layer].splines,t,&leftmost,&rightmost);
2705 		    for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next )
2706 			for ( l=0; l<ref->layer_cnt; ++l )
2707 			    SSFindItalicBounds(ref->layers[l].splines,t,&leftmost,&rightmost);
2708 		}
2709 		if ( leftmost!=NULL ) {
2710 		    x = rint(leftmost->me.y*t*tab->scale) + tab->xoff;
2711 		    x2 = rint(leftmost->me.x*tab->scale) + tab->xoff;
2712 		    y = cv->height-tab->yoff-rint(leftmost->me.y*tab->scale);
2713 		    DrawPLine(cv,pixmap,x,y,x2,y,italiccoordcol);
2714 		     /* arrow heads */
2715 		     DrawPLine(cv,pixmap,x,y,x+4,y+4,italiccoordcol);
2716 		     DrawPLine(cv,pixmap,x,y,x+4,y-4,italiccoordcol);
2717 		     DrawPLine(cv,pixmap,x2,y,x2-4,y-4,italiccoordcol);
2718 		     DrawPLine(cv,pixmap,x2,y,x2-4,y+4,italiccoordcol);
2719 		    dtos( buf, leftmost->me.x-leftmost->me.y*t);
2720 		    x = x + (x2-x-GDrawGetText8Width(pixmap,buf,-1))/2;
2721 		    GDrawDrawText8(pixmap,x,y+12,buf,-1,italiccoordcol);
2722 		}
2723 		if ( rightmost!=NULL ) {
2724 		    x = rint(rightmost->me.x*tab->scale) + tab->xoff;
2725 		    y = cv->height-tab->yoff-rint(rightmost->me.y*tab->scale);
2726 		    x2 = rint((sc->width + rightmost->me.y*t)*tab->scale) + tab->xoff;
2727 		    DrawPLine(cv,pixmap,x,y,x2,y,italiccoordcol);
2728 		     /* arrow heads */
2729 		     DrawPLine(cv,pixmap,x,y,x+4,y+4,italiccoordcol);
2730 		     DrawPLine(cv,pixmap,x,y,x+4,y-4,italiccoordcol);
2731 		     DrawPLine(cv,pixmap,x2,y,x2-4,y-4,italiccoordcol);
2732 		     DrawPLine(cv,pixmap,x2,y,x2-4,y+4,italiccoordcol);
2733 		    dtos( buf, sc->width+rightmost->me.y*t-rightmost->me.x);
2734 		    x = x + (x2-x-GDrawGetText8Width(pixmap,buf,-1))/2;
2735 		    GDrawDrawText8(pixmap,x,y+12,buf,-1,italiccoordcol);
2736 		}
2737 	    }
2738 	}
2739     }
2740 
2741     if ( cv->showvmetrics ) {
2742 	x = rint(bounds[2]->x*tab->scale) + tab->xoff;
2743 	y = cv->height-tab->yoff-rint(bounds[2]->y*tab->scale);
2744 	y2 = cv->height-tab->yoff-rint(-sc->parent->descent*tab->scale);
2745 	DrawPLine(cv,pixmap,x,y,x,y2,metricslabelcol);
2746 	 /* arrow heads */
2747 	 DrawPLine(cv,pixmap,x,y,x-4,y+4,metricslabelcol);
2748 	 DrawPLine(cv,pixmap,x,y,x+4,y+4,metricslabelcol);
2749 	 DrawPLine(cv,pixmap,x,y2,x+4,y2-4,metricslabelcol);
2750 	 DrawPLine(cv,pixmap,x,y2,x-4,y2-4,metricslabelcol);
2751 	dtos( buf, bounds[2]->y+sc->parent->descent);
2752 	y = y - (y-y2-cv->sfh)/2;
2753 	GDrawDrawText8(pixmap,x+4,y,buf,-1,metricslabelcol);
2754 
2755 	x = rint(bounds[3]->x*tab->scale) + tab->xoff;
2756 	y = cv->height-tab->yoff-rint(bounds[3]->y*tab->scale);
2757 	y2 = cv->height-tab->yoff-rint(sc->parent->ascent*tab->scale);
2758 	DrawPLine(cv,pixmap,x,y,x,y2,metricslabelcol);
2759 	 /* arrow heads */
2760 	 DrawPLine(cv,pixmap,x,y,x-4,y-4,metricslabelcol);
2761 	 DrawPLine(cv,pixmap,x,y,x+4,y-4,metricslabelcol);
2762 	 DrawPLine(cv,pixmap,x,y2,x+4,y2+4,metricslabelcol);
2763 	 DrawPLine(cv,pixmap,x,y2,x-4,y2+4,metricslabelcol);
2764 	dtos( buf, sc->parent->ascent-bounds[3]->y);
2765 	x = x + (x2-x-GDrawGetText8Width(pixmap,buf,-1))/2;
2766 	GDrawDrawText8(pixmap,x,y-4,buf,-1,metricslabelcol);
2767     }
2768 }
2769 
CVExposeGlyphFill(CharView * cv,GWindow pixmap,GEvent * event,DRect * clip)2770 static int CVExposeGlyphFill(CharView *cv, GWindow pixmap, GEvent *event, DRect* clip ) {
2771     CharViewTab* tab = CVGetActiveTab(cv);
2772     int layer, cvlayer = CVLayer((CharViewBase *) cv);
2773     int filled = 0;
2774 
2775     if( shouldShowFilledUsingCairo(cv) ) {
2776 	layer = cvlayer;
2777 	if ( layer>=0 ) {
2778 	    CVDrawLayerSplineSet(cv,pixmap,&cv->b.sc->layers[layer],foreoutlinecol,
2779 	    			 cv->showpoints, clip, sfm_fill );
2780 	    filled = 1;
2781 	}
2782     } else {
2783 	if (( cv->showfore || cv->b.drawmode==dm_fore ) && cv->showfilled &&
2784 	    cv->filled!=NULL ) {
2785 	    GDrawDrawImage(pixmap, &cv->gi, NULL,
2786 			   tab->xoff + cv->filled->xmin,
2787 			   -tab->yoff + cv->height-cv->filled->ymax);
2788 	    filled = 1;
2789 	}
2790     }
2791     return(filled);
2792 }
2793 
CVExposeReferences(CharView * cv,GWindow pixmap,SplineChar * sc,int layer,DRect * clip)2794 static void CVExposeReferences( CharView *cv, GWindow pixmap, SplineChar* sc, int layer, DRect* clip )
2795 {
2796     RefChar *rf = 0;
2797     int rlayer = 0;
2798 
2799     for ( rf = sc->layers[layer].refs; rf!=NULL; rf = rf->next )
2800     {
2801 	if ( cv->showrefnames )
2802 	    CVDrawRefName(cv,pixmap,rf,0);
2803 	enum outlinesfm_flags refsfm = sfm_stroke;
2804 	if( shouldShowFilledUsingCairo(cv) ) {
2805 	    refsfm = sfm_fill;
2806 	}
2807 
2808 	for ( rlayer=0; rlayer<rf->layer_cnt; ++rlayer )
2809 	    CVDrawSplineSetSpecialized(cv,pixmap,rf->layers[rlayer].splines,foreoutlinecol,-1,clip, refsfm, 0);
2810 	if ( rf->selected && cv->b.layerheads[cv->b.drawmode]==&sc->layers[layer])
2811 	    CVDrawBB(cv,pixmap,&rf->bb);
2812     }
2813 }
2814 
2815 
CVExpose(CharView * cv,GWindow pixmap,GEvent * event)2816 static void CVExpose(CharView *cv, GWindow pixmap, GEvent *event ) {
2817     CharViewTab* tab = CVGetActiveTab(cv);
2818     SplineFont *sf = cv->b.sc->parent;
2819     RefChar *rf;
2820     GRect old;
2821     DRect clip;
2822     char buf[20];
2823     PST *pst;
2824     int i, layer, rlayer, cvlayer = CVLayer((CharViewBase *) cv);
2825     enum outlinesfm_flags strokeFillMode = sfm_stroke;
2826     int GlyphHasBeenFilled = 0;
2827 
2828     GDrawPushClip(pixmap,&event->u.expose.rect,&old);
2829 
2830     if( shouldShowFilledUsingCairo(cv) ) {
2831     	strokeFillMode = sfm_fill;
2832     }
2833 
2834     clip.width = event->u.expose.rect.width/tab->scale;
2835     clip.height = event->u.expose.rect.height/tab->scale;
2836     clip.x = (event->u.expose.rect.x-tab->xoff)/tab->scale;
2837     clip.y = (cv->height-event->u.expose.rect.y-event->u.expose.rect.height-tab->yoff)/tab->scale;
2838 
2839     GDrawSetFont(pixmap,cv->small);
2840     GDrawSetLineWidth(pixmap,0);
2841 
2842     if ( !cv->show_ft_results && cv->dv==NULL ) {
2843 
2844 	if ( cv->backimgs==NULL && !(GDrawHasCairo(cv->v)&gc_buildpath))
2845 	    cv->backimgs = GDrawCreatePixmap(GDrawGetDisplayOfWindow(cv->v),cv->v,cv->width,cv->height);
2846 	if ( GDrawHasCairo(cv->v)&gc_buildpath ) {
2847 	    for ( layer = ly_back; layer<cv->b.sc->layer_cnt; ++layer ) if ( cv->b.sc->layers[layer].images!=NULL ) {
2848 		if (( sf->multilayer && ((( cv->showback[0]&1 || cvlayer==layer) && layer==ly_back ) ||
2849 			    ((cv->showfore || cvlayer==layer) && layer>ly_back)) ) ||
2850 		    ( !sf->multilayer && (((cv->showfore && cvlayer==layer) && layer==ly_fore) ||
2851 			    (((cv->showback[layer>>5]&(1<<(layer&31))) || cvlayer==layer) && layer!=ly_fore))) ) {
2852 		    /* This really should be after the grids, but then it would completely*/
2853 		    /*  hide them. */
2854 		    DrawImageList(cv,pixmap,cv->b.sc->layers[layer].images);
2855 		}
2856 	    }
2857 	    cv->back_img_out_of_date = false;
2858 	    if ( cv->showhhints || cv->showvhints || cv->showdhints || cv->showblues || cv->showfamilyblues)
2859 		CVShowHints(cv,pixmap);
2860 	} else if ( cv->back_img_out_of_date ) {
2861 	    GDrawFillRect(cv->backimgs,NULL,view_bgcol);
2862 	    if ( cv->showhhints || cv->showvhints || cv->showdhints || cv->showblues || cv->showfamilyblues)
2863 		CVShowHints(cv,cv->backimgs);
2864 	    for ( layer = ly_back; layer<cv->b.sc->layer_cnt; ++layer ) if ( cv->b.sc->layers[layer].images!=NULL ) {
2865 		if (( sf->multilayer && ((( cv->showback[0]&1 || cvlayer==layer) && layer==ly_back ) ||
2866 			    ((cv->showfore || cvlayer==layer) && layer>ly_back)) ) ||
2867 		    ( !sf->multilayer && (((cv->showfore && cvlayer==layer) && layer==ly_fore) ||
2868 			    (((cv->showback[layer>>5]&(1<<(layer&31))) || cvlayer==layer) && layer!=ly_fore))) ) {
2869 		    /* This really should be after the grids, but then it would completely*/
2870 		    /*  hide them. */
2871 		    if ( cv->back_img_out_of_date )
2872 			DrawImageList(cv,cv->backimgs,cv->b.sc->layers[layer].images);
2873 		}
2874 	    }
2875 	    cv->back_img_out_of_date = false;
2876 	}
2877 	if ( cv->backimgs!=NULL ) {
2878 	    GRect r;
2879 	    r.x = r.y = 0; r.width = cv->width; r.height = cv->height;
2880 	    GDrawDrawPixmap(pixmap,cv->backimgs,&r,0,0);
2881 	} else if ( !(GDrawHasCairo(cv->v)&gc_buildpath) &&
2882 		( cv->showhhints || cv->showvhints || cv->showdhints || cv->showblues || cv->showfamilyblues)) {
2883 	    /* if we've got bg images (and we're showing them) then the hints live in */
2884 	    /*  the bg image pixmap (else they get overwritten by the pixmap) */
2885 	    CVShowHints(cv,pixmap);
2886 	}
2887 	if ( cv->showgrids || cv->b.drawmode==dm_grid ) {
2888 	    CVDrawSplineSet(cv,pixmap,cv->b.fv->sf->grid.splines,guideoutlinecol,
2889 		    cv->showpoints && cv->b.drawmode==dm_grid,&clip);
2890 	}
2891 	if ( cv->showhmetrics ) {
2892 	    Color lbcolor = (!cv->inactive && cv->lbearingsel) ? lbearingselcol : coordcol;
2893 	    DrawVLine(cv,pixmap,0,lbcolor,false,NULL,NULL);
2894 	    DrawLine(cv,pixmap,-8096,0,8096,0,coordcol);
2895 	    DrawLine(cv,pixmap,-8096,sf->ascent,8096,sf->ascent,coordcol);
2896 	    DrawLine(cv,pixmap,-8096,-sf->descent,8096,-sf->descent,coordcol);
2897 	}
2898 	if ( cv->showvmetrics ) {
2899 	    /*DrawLine(cv,pixmap,(sf->ascent+sf->descent)/2,-8096,(sf->ascent+sf->descent)/2,8096,coordcol);
2900 	    DrawLine(cv,pixmap,-8096,sf->vertical_origin,8096,sf->vertical_origin,coordcol);*/
2901 	}
2902 
2903 	DrawSelImageList(cv,pixmap,cv->b.layerheads[cv->b.drawmode]->images);
2904 
2905 	/* Wrong order, I know. But it is useful to have the background */
2906 	/*  visible on top of the fill... */
2907 	GlyphHasBeenFilled = CVExposeGlyphFill(cv, pixmap, event, &clip );
2908     } else {
2909 	/* Draw FreeType Results */
2910 	CVDrawGridRaster(cv,pixmap,&clip);
2911     }
2912 
2913     if ( cv->b.layerheads[cv->b.drawmode]->undoes!=NULL &&
2914 	    cv->b.layerheads[cv->b.drawmode]->undoes->undotype==ut_tstate )
2915 	DrawOldState(cv,pixmap,cv->b.layerheads[cv->b.drawmode]->undoes, &clip);
2916 
2917     if ( cv->showfore )
2918 	cv->showback[0] |= (1<<ly_fore);
2919     else
2920 	cv->showback[0] &= ~(1<<ly_fore);
2921 
2922     for ( layer=ly_back; layer<cv->b.sc->layer_cnt; ++layer ) if ( layer!=cvlayer ) {
2923 	if ( cv->showback[layer>>5]&(1<<(layer&31)) ) {
2924 	    /* Used to draw the image list here, but that's too slow. Optimization*/
2925 	    /*  is to draw to pixmap, dump pixmap a bit earlier */
2926 	    /* Then when we moved the fill image around, we had to deal with the */
2927 	    /*  images before the fill... */
2928 	    int activelayer = CVLayer(&cv->b);
2929 
2930 	    enum outlinesfm_flags strokeFillMode = sfm_stroke;
2931 	    if( cv->inPreviewMode )
2932 		strokeFillMode = sfm_nothing;
2933 	    if( layer == activelayer && layer >= ly_back )
2934 		strokeFillMode = sfm_fill;
2935 
2936 	    CVDrawLayerSplineSet(cv,pixmap,&cv->b.sc->layers[layer],
2937 	    			 !sf->multilayer || layer==ly_back ? backoutlinecol : foreoutlinecol,
2938 	    			 false,&clip,strokeFillMode);
2939 	    for ( rf=cv->b.sc->layers[layer].refs; rf!=NULL; rf = rf->next ) {
2940 		if ( /* cv->b.drawmode==dm_back &&*/ cv->showrefnames )
2941 		    CVDrawRefName(cv,pixmap,rf,0);
2942 		for ( rlayer=0; rlayer<rf->layer_cnt; ++rlayer )
2943 		    CVDrawSplineSet(cv,pixmap,rf->layers[rlayer].splines, backoutlinecol,false,&clip);
2944 		if ( rf->selected && cv->b.layerheads[cv->b.drawmode]==&cv->b.sc->layers[layer])
2945 		    CVDrawBB(cv,pixmap,&rf->bb);
2946 	    }
2947 	}
2948     }
2949     if ( cv->mmvisible!=0 )
2950 	DrawMMGhosts(cv,pixmap,&clip);
2951     if ( cv->template1!=NULL )
2952 	CVDrawTemplates(cv,pixmap,cv->template1,&clip);
2953     if ( cv->template2!=NULL )
2954 	CVDrawTemplates(cv,pixmap,cv->template2,&clip);
2955 
2956     /* Draw the active layer last so its splines are on top. */
2957     /* Note that we don't check whether the layer is visible or not, we always*/
2958     /*  draw the current layer -- unless they've turned on grid fit. Then they*/
2959     /*  might want to hide the active layer. */
2960     layer = cvlayer;
2961 
2962 
2963     /*
2964      * If we have a pretransform_spl and the user wants to see it then show it to them
2965      */
2966      if(cv->p.pretransform_spl) {
2967          CVDrawSplineSetSpecialized(cv, pixmap, cv->p.pretransform_spl,
2968             DraggingComparisonOutlineColor, 1, &clip, sfm_stroke_trans,
2969             DraggingComparisonAlphaChannelOverride);
2970      }
2971 
2972     /* The call to CVExposeGlyphFill() above will have rendered a filled glyph already. */
2973     /* We draw the outline only at this stage so as to have it layered */
2974     /* over the control points if they are currently visible. */
2975     /* CVDrawLayerSplineSet() will draw both the control points, and the font outline over those */
2976     /* NB:
2977      *     Drawing the stroked outline may also use the color
2978      *     clippathcol for some splines, so we can't really avoid a
2979      *     restroke unless we are sure
2980      *     FOR-ALL(splines):spl->is_clip_path==0 */
2981     if( shouldShowFilledUsingCairo(cv) ) {
2982 	strokeFillMode = sfm_stroke;
2983     }
2984     if( GlyphHasBeenFilled ) {
2985 	strokeFillMode = sfm_stroke_trans;
2986     }
2987 
2988     if ( layer<0 ) /* Guide lines are special */
2989 	CVDrawLayerSplineSet( cv,pixmap,cv->b.layerheads[cv->b.drawmode],foreoutlinecol,
2990 			      cv->showpoints ,&clip, strokeFillMode );
2991     else if ( (cv->showback[layer>>5]&(1<<(layer&31))) ||
2992 	    (!cv->show_ft_results && cv->dv==NULL ))
2993     {
2994 	CVExposeReferences( cv, pixmap, cv->b.sc, layer, &clip );
2995     }
2996     if ( layer>=0 )
2997     {
2998 	if( cv->dv && !(cv->showback[layer>>5]&(1<<(layer&31))))
2999 	{
3000 	    // MIQ 2013 feb: issue/168
3001 	    // turn off the glyph outline if we are in debug mode and
3002 	    // the layer is not visible
3003 	}
3004 	else
3005 	{
3006 	    CVDrawLayerSplineSet( cv,pixmap,&cv->b.sc->layers[layer],foreoutlinecol,
3007 	    			  cv->showpoints ,&clip, strokeFillMode );
3008 
3009 
3010 	    int showpoints = 0;
3011 	    enum outlinesfm_flags sm = sfm_stroke;
3012 	    if( cv->inPreviewMode ) {
3013 		sm = sfm_fill;
3014 	    }
3015 
3016 	    int ridx = cv->additionalCharsToShowActiveIndex+1;
3017 //	    TRACE("expose(b) additionalCharsToShowActiveIndex:%d\n", cv->additionalCharsToShowActiveIndex );
3018 	    if( cv->additionalCharsToShow[ ridx ] )
3019 	    {
3020 		int i = 1;
3021 		int originalxoff = tab->xoff;
3022 		int offset = tab->scale * cv->b.sc->width;
3023 		for( i=ridx; i < additionalCharsToShowLimit; i++ )
3024 		{
3025 //		    TRACE("expose(right) loop:%d\n", i );
3026 		    SplineChar* xc = cv->additionalCharsToShow[i];
3027 		    if( !xc )
3028 			break;
3029 
3030 		    tab->xoff += offset;
3031 		    CVExposeReferences(   cv, pixmap, xc, layer, &clip );
3032 		    CVDrawLayerSplineSet( cv, pixmap, &xc->layers[layer], foreoutlinecol,
3033 					  showpoints ,&clip, sm );
3034 		    offset = tab->scale * xc->width;
3035 		}
3036 		tab->xoff = originalxoff;
3037 	    }
3038 
3039 
3040 
3041 	    if( cv->additionalCharsToShowActiveIndex > 0 )
3042 	    {
3043 		int i = 1;
3044 		int originalxoff = tab->xoff;
3045 		int offset = 0;
3046 
3047 		for( i=cv->additionalCharsToShowActiveIndex-1; i >= 0; i-- )
3048 		{
3049 //		    TRACE("expose(left) loop:%d\n", i );
3050 		    SplineChar* xc = cv->additionalCharsToShow[i];
3051 		    if( !xc )
3052 			break;
3053 
3054 		    offset = tab->scale * xc->width;
3055 		    tab->xoff -= offset;
3056 		    CVExposeReferences(   cv, pixmap, xc, layer, &clip );
3057 		    CVDrawLayerSplineSet( cv, pixmap, &xc->layers[layer], foreoutlinecol,
3058 					  showpoints ,&clip, sm );
3059 		}
3060 		tab->xoff = originalxoff;
3061 	    }
3062 
3063 //	    TRACE("expose(e) ridx:%d\n", ridx );
3064 
3065 	}
3066     }
3067 
3068 
3069     if ( cv->freehand.current_trace )
3070     	CVDrawSplineSet( cv,pixmap,cv->freehand.current_trace,tracecol,
3071     			 false,&clip);
3072 
3073     if ( cv->showhmetrics && (cv->b.container==NULL || cv->b.container->funcs->type==cvc_mathkern) ) {
3074 	RefChar *lock = HasUseMyMetrics(cv->b.sc,cvlayer);
3075 	if ( lock!=NULL ) cv->b.sc->width = lock->sc->width;
3076 	DrawVLine(cv,pixmap,cv->b.sc->width,(!cv->inactive && cv->widthsel)?widthselcol:widthcol,true,
3077 		lock!=NULL ? &GIcon_lock : NULL, NULL);
3078 	if ( cv->b.sc->italic_correction!=TEX_UNDEF && cv->b.sc->italic_correction!=0 ) {
3079 	    GDrawSetDashedLine(pixmap,2,2,0);
3080 	    DrawVLine(cv,pixmap,cv->b.sc->width+cv->b.sc->italic_correction,(!cv->inactive && cv->icsel)?widthselcol:widthcol,
3081 /* GT: Italic Correction */
3082 		    false, NULL,_("ItalicCor."));
3083 	    GDrawSetDashedLine(pixmap,0,0,0);
3084 	}
3085     }
3086     if ( cv->showhmetrics && cv->b.container==NULL ) {
3087 	for ( pst=cv->b.sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
3088 	if ( pst!=NULL ) {
3089 	    for ( i=0; i<pst->u.lcaret.cnt; ++i )
3090 		DrawVLine(cv,pixmap,pst->u.lcaret.carets[i],lcaretcol,true,NULL,_("Lig.Caret"));
3091 	}
3092 	if ( cv->show_ft_results || cv->dv!=NULL )
3093 	    DrawVLine(cv,pixmap,cv->b.ft_gridfitwidth,widthgridfitcol,true,NULL,NULL);
3094 	if ( cv->b.sc->top_accent_horiz!=TEX_UNDEF )
3095 	    DrawVLine(cv,pixmap,cv->b.sc->top_accent_horiz,(!cv->inactive && cv->tah_sel)?widthselcol:anchorcol,true,
3096 		    NULL,_("TopAccent"));
3097     }
3098     if ( cv->showvmetrics ) {
3099 	int vertical_height = -cv->b.sc->vwidth + cv->b.sc->parent->ascent;
3100 	int len, y = -tab->yoff + cv->height - rint(vertical_height*tab->scale);
3101 	DrawLine(cv,pixmap,-32768,vertical_height,32767,vertical_height,
3102 		(!cv->inactive && cv->vwidthsel)?widthselcol:widthcol);
3103 	if ( y>-40 && y<cv->height+40 ) {
3104 	    dtos( buf, cv->b.sc->vwidth);
3105 	    GDrawSetFont(pixmap,cv->small);
3106 	    len = GDrawGetText8Width(pixmap,buf,-1);
3107 	    GDrawDrawText8(pixmap,cv->width-len-5,y,buf,-1,metricslabelcol);
3108 	}
3109     }
3110     if ( cv->showsidebearings && cv->showfore &&
3111 	    (cv->showvmetrics || cv->showhmetrics))
3112 	CVSideBearings(pixmap,cv);
3113 
3114     if ((( cv->active_tool >= cvt_scale && cv->active_tool <= cvt_perspective ) ||
3115 		cv->active_shape!=NULL ) &&
3116 	    cv->p.pressed )
3117 	DrawTransOrigin(cv,pixmap);
3118     if ( cv->dv==NULL || (cv->showback[ly_fore>>5]&(1<<(ly_fore&31))) )
3119 	CVDrawAnchorPoints(cv,pixmap);
3120     if ( cv->apmine!=NULL )
3121 	DrawAPMatch(cv,pixmap,&clip);
3122 
3123     if ( cv->p.rubberbanding || cv->p.rubberlining ) {
3124 	if ( cv->p.rubberbanding )
3125 	    CVDrawRubberRect(pixmap,cv);
3126 	if ( cv->p.rubberlining )
3127 	    CVDrawRubberLine(pixmap,cv);
3128     }
3129     CVRulerExpose(pixmap,cv);
3130 
3131     GDrawPopClip(pixmap,&old);
3132 }
3133 
SC_UpdateAll(SplineChar * sc)3134 static void SC_UpdateAll(SplineChar *sc) {
3135     CharView *cv;
3136     struct splinecharlist *dlist;
3137     MetricsView *mv;
3138     FontView *fv;
3139 
3140     for ( cv=(CharView *) (sc->views); cv!=NULL; cv=(CharView *) (cv->b.next) )
3141 	GDrawRequestExpose(cv->v,NULL,false);
3142     for ( dlist=sc->dependents; dlist!=NULL; dlist=dlist->next )
3143 	SCUpdateAll(dlist->sc);
3144     if ( sc->parent!=NULL ) {
3145 	for ( fv = (FontView *) (sc->parent->fv); fv!=NULL; fv=(FontView *) (fv->b.nextsame) )
3146 	    FVRegenChar(fv,sc);
3147 	for ( mv = sc->parent->metrics; mv!=NULL; mv=mv->next )
3148 	    MVRegenChar(mv,sc);
3149     }
3150 }
3151 
SC_OutOfDateBackground(SplineChar * sc)3152 static void SC_OutOfDateBackground(SplineChar *sc) {
3153     CharView *cv;
3154 
3155     for ( cv=(CharView *) (sc->views); cv!=NULL; cv=(CharView *) (cv->b.next) )
3156 	cv->back_img_out_of_date = true;
3157 }
3158 
3159 /* CVRegenFill() regenerates data used to show or not show paths as filled */
3160 /* This is not static so that it can be called from the layers palette */
CVRegenFill(CharView * cv)3161 void CVRegenFill(CharView *cv) {
3162     CharViewTab* tab = CVGetActiveTab(cv);
3163     BDFCharFree(cv->filled);
3164     cv->filled = NULL;
3165     if ( cv->showfilled && !shouldShowFilledUsingCairo(cv) ) {
3166 	extern int use_freetype_to_rasterize_fv;
3167 	int layer = CVLayer((CharViewBase *) cv);
3168 	int size = tab->scale*(cv->b.fv->sf->ascent+cv->b.fv->sf->descent);
3169 	int clut_len= 2;
3170 
3171         if ( layer==ly_grid ) layer=ly_fore; /* otherwise crashes when using guides layer! */
3172 
3173 	/* Generally I don't think there's much point in doing an anti-aliased*/
3174 	/*  fill. But on the "M" (and "W") glyph of extravigant caps, ft won't*/
3175 	/*  do a mono fill */
3176 	if ( use_freetype_to_rasterize_fv && hasFreeType()) {
3177 	    int depth = 1;
3178 	    if( use_freetype_with_aa_fill_cv ) {
3179 		depth = 4;
3180 		clut_len = 16;
3181 	    }
3182 	    cv->filled = SplineCharFreeTypeRasterizeNoHints(cv->b.sc,layer,
3183 		size,72, depth);
3184 	    if ( cv->filled==NULL && size<2000 ) {
3185 		/* There are some glyphs which freetype won't rasterize in */
3186 		/* mono mode, but will in grey scale. Don't ask me why */
3187 		cv->filled = SplineCharFreeTypeRasterizeNoHints(cv->b.sc,
3188 		    layer, size, 72, 4);
3189 		clut_len = 16;
3190 	    }
3191 	}
3192 	if ( cv->filled==NULL )
3193 	    cv->filled = SplineCharRasterize(cv->b.sc,layer,size+.1);
3194 	if ( cv->filled==NULL )
3195 return;
3196 	cv->gi.u.image->image_type = clut_len==2 ? it_mono : it_index;
3197 	cv->gi.u.image->data = cv->filled->bitmap;
3198 	cv->gi.u.image->bytes_per_line = cv->filled->bytes_per_line;
3199 	cv->gi.u.image->width = cv->filled->xmax-cv->filled->xmin+1;
3200 	cv->gi.u.image->height = cv->filled->ymax-cv->filled->ymin+1;
3201 	if ( clut_len!=cv->gi.u.image->clut->clut_len ) {
3202 	    GClut *clut = cv->gi.u.image->clut;
3203 	    int i;
3204 	    Color bg = view_bgcol;
3205 	    for ( i=0; i<clut_len; ++i ) {
3206 		int r,g,b;
3207 		r = ((bg>>16)&0xff)*(clut_len-1-i) + ((fillcol>>16)&0xff)*i;
3208 		g = ((bg>>8 )&0xff)*(clut_len-1-i) + ((fillcol>>8 )&0xff)*i;
3209 		b = ((bg    )&0xff)*(clut_len-1-i) + ((fillcol    )&0xff)*i;
3210 		clut->clut[i] = COLOR_CREATE(r/(clut_len-1),g/(clut_len-1),b/(clut_len-1));
3211 	    }
3212 	    clut->clut_len = clut_len;
3213 	}
3214 	GDrawRequestExpose(cv->v,NULL,false);
3215     }
3216 }
3217 
3218 
FVRedrawAllCharViewsSF(SplineFont * sf)3219 static void FVRedrawAllCharViewsSF(SplineFont *sf)
3220 {
3221     int i;
3222     CharView *cv;
3223 
3224     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
3225 	for ( cv = (CharView *) (sf->glyphs[i]->views); cv!=NULL; cv=(CharView *) (cv->b.next) )
3226 	    GDrawRequestExpose(cv->v,NULL,false);
3227 }
3228 
FVRedrawAllCharViews(FontView * fv)3229 void FVRedrawAllCharViews(FontView *fv)
3230 {
3231     FVRedrawAllCharViewsSF( fv->b.sf );
3232 }
3233 
SCRegenFills(SplineChar * sc)3234 static void SCRegenFills(SplineChar *sc) {
3235     struct splinecharlist *dlist;
3236     CharView *cv;
3237 
3238     for ( cv = (CharView *) (sc->views); cv!=NULL; cv=(CharView *) (cv->b.next) )
3239 	CVRegenFill(cv);
3240     for ( dlist=sc->dependents; dlist!=NULL; dlist=dlist->next )
3241 	SCRegenFills(dlist->sc);
3242 }
3243 
SCRegenDependents(SplineChar * sc,int layer)3244 static void SCRegenDependents(SplineChar *sc, int layer) {
3245     struct splinecharlist *dlist;
3246     FontView *fv;
3247     int first, last;
3248 
3249     first = last = layer;
3250     if ( layer==ly_all ) {
3251 	first = 0; last = sc->layer_cnt-1;
3252     }
3253     for ( layer = first; layer<=last; ++layer ) {
3254 	for ( fv = (FontView *) (sc->parent->fv); fv!=NULL; fv=(FontView *) (fv->b.nextsame) ) {
3255 	    if ( fv->sv!=NULL && layer<=ly_fore ) {
3256 		SCReinstanciateRef(&fv->sv->sd.sc_srch,sc,layer);
3257 		SCReinstanciateRef(&fv->sv->sd.sc_rpl,sc,layer);
3258 	    }
3259 	}
3260 
3261 	for ( dlist=sc->dependents; dlist!=NULL; dlist=dlist->next ) {
3262 	    SCReinstanciateRef(dlist->sc,sc,layer);
3263 	    SCRegenDependents(dlist->sc,layer);
3264 	}
3265     }
3266 }
3267 
CVUpdateInfo(CharView * cv,GEvent * event)3268 static void CVUpdateInfo(CharView *cv, GEvent *event) {
3269     CharViewTab* tab = CVGetActiveTab(cv);
3270 
3271     cv->info_within = true;
3272     cv->e.x = event->u.mouse.x; cv->e.y = event->u.mouse.y;
3273     cv->info.x = (event->u.mouse.x-tab->xoff)/tab->scale;
3274     cv->info.y = (cv->height-event->u.mouse.y-tab->yoff)/tab->scale;
3275     CVInfoDraw(cv,cv->gw);
3276 }
3277 
CVNewScale(CharView * cv)3278 static void CVNewScale(CharView *cv) {
3279     CharViewTab* tab = CVGetActiveTab(cv);
3280     GEvent e;
3281 
3282     CVRegenFill(cv);
3283     cv->back_img_out_of_date = true;
3284 
3285     GScrollBarSetBounds(cv->vsb,-20000*tab->scale,8000*tab->scale,cv->height);
3286     GScrollBarSetBounds(cv->hsb,-8000*tab->scale,32000*tab->scale,cv->width);
3287     GScrollBarSetPos(cv->vsb,tab->yoff-cv->height);
3288     GScrollBarSetPos(cv->hsb,-tab->xoff);
3289 
3290     GDrawRequestExpose(cv->v,NULL,false);
3291     if ( cv->showrulers )
3292 	GDrawRequestExpose(cv->gw,NULL,false);
3293     GDrawGetPointerPosition(cv->v,&e);
3294     CVUpdateInfo(cv,&e);
3295 }
3296 
_CVFit(CharView * cv,DBounds * b,int integral)3297 static void _CVFit(CharView *cv,DBounds *b, int integral) {
3298     CharViewTab* tab = CVGetActiveTab(cv);
3299     real left, right, top, bottom, hsc, wsc;
3300     extern int palettes_docked;
3301     int offset = palettes_docked ? 90 : 0;
3302     int em = cv->b.sc->parent->ascent + cv->b.sc->parent->descent;
3303     int hsmall = true;
3304 
3305     if ( offset>cv->width ) offset = 0;
3306 
3307     bottom = b->miny;
3308     top = b->maxy;
3309     left = b->minx;
3310     right = b->maxx;
3311 
3312     if ( top<bottom ) IError("Bottom bigger than top!");
3313     if ( right<left ) IError("Left bigger than right!");
3314     top -= bottom;
3315     right -= left;
3316     if ( top==0 ) top = em;
3317     if ( right==0 ) right = em;
3318     wsc = (cv->width-offset) / right;
3319     hsc = cv->height / top;
3320     if ( wsc<hsc ) { hsc = wsc; hsmall = false ; }
3321 
3322     tab->scale = hsc;
3323     if ( integral ) {
3324 	if ( tab->scale > 1.0 ) {
3325 	    tab->scale = floor(tab->scale);
3326 	} else {
3327 	    tab->scale = 1/ceil(1/tab->scale);
3328 	}
3329     } else {
3330 	if ( tab->scale > 1.0 ) {
3331 	    tab->scale = floor(2*tab->scale)/2;
3332 	} else {
3333 	    tab->scale = 2/ceil(2/tab->scale);
3334 	}
3335     }
3336 
3337     /* Center glyph horizontally */
3338     tab->xoff = ( (cv->width-offset) - (right*tab->scale) )/2 + offset  - b->minx*tab->scale;
3339     if ( hsmall )
3340 	tab->yoff = -bottom*tab->scale;
3341     else
3342 	tab->yoff = -(bottom+top/2)*tab->scale + cv->height/2;
3343 
3344     CVNewScale(cv);
3345 }
3346 
CVFit(CharView * cv)3347 static void CVFit(CharView *cv) {
3348     DBounds b;
3349     double center;
3350 
3351     SplineCharFindBounds(cv->b.sc,&b);
3352     if ( b.miny==0 && b.maxy==0 ) {
3353 	b.maxy =cv->b.sc->parent->ascent;
3354 	b.miny = -cv->b.sc->parent->descent;
3355     }
3356     /* FF used to normalize bounding boxes making maxx positive. But this */
3357     /* may result into an incorrect positioning for combining marks, which */
3358     /* usually have both bearings negative. So keep negative values as they are. */
3359     /* As for the Y axis, we only ensure the minimum value doesn't exceed zero, */
3360     /* so that the baseline is always visible. */
3361 
3362     if ( b.miny>0 ) b.miny = 0;
3363 
3364     /* Now give some extra space around the interesting stuff */
3365     center = (b.maxx+b.minx)/2;
3366     b.minx = center - (center - b.minx)*1.2;
3367     b.maxx = center + (b.maxx - center)*1.2;
3368     center = (b.maxy+b.miny)/2;
3369     b.miny = center - (center - b.miny)*1.2;
3370     b.maxy = center + (b.maxy - center)*1.2;
3371 
3372     _CVFit(cv,&b,true);
3373 }
3374 
CVUnlinkView(CharView * cv)3375 void CVUnlinkView(CharView *cv ) {
3376     CharView *test;
3377 
3378     if ( cv->b.sc->views == (CharViewBase *) cv ) {
3379 	cv->b.sc->views = cv->b.next;
3380     } else {
3381 	for ( test=(CharView *) (cv->b.sc->views);
3382 		test->b.next!=(CharViewBase *) cv && test->b.next!=NULL;
3383 		test=(CharView *) (test->b.next) );
3384 	if ( test->b.next==(CharViewBase *) cv )
3385 	    test->b.next = cv->b.next;
3386     }
3387 }
3388 
CharIcon(CharView * cv,FontView * fv)3389 static GWindow CharIcon(CharView *cv, FontView *fv) {
3390     SplineChar *sc = cv->b.sc;
3391     BDFFont *bdf, *bdf2;
3392     BDFChar *bdfc;
3393     GWindow icon = cv->icon;
3394     GRect r;
3395 
3396     r.x = r.y = 0; r.width = r.height = fv->cbw-1;
3397     if ( icon == NULL )
3398 	cv->icon = icon = GDrawCreatePixmap(NULL,NULL,r.width,r.width);
3399     GDrawFillRect(icon,&r,0x0);		/* for some reason icons seem to be color reversed by my defn */
3400 
3401     bdf = NULL; bdfc = NULL;
3402     if ( sc->layers[ly_fore].refs!=NULL || sc->layers[ly_fore].splines!=NULL ) {
3403 	bdf = fv->show;
3404 	if ( sc->orig_pos>=bdf->glyphcnt || bdf->glyphs[sc->orig_pos]==NULL )
3405 	    bdf = fv->filled;
3406 	if ( sc->orig_pos>=bdf->glyphcnt || bdf->glyphs[sc->orig_pos]==NULL ) {
3407 	    bdf2 = NULL; bdfc = NULL;
3408 	    for ( bdf=fv->b.sf->bitmaps; bdf!=NULL && bdf->pixelsize<24 ; bdf=bdf->next )
3409 		bdf2 = bdf;
3410 	    if ( bdf2!=NULL && bdf!=NULL ) {
3411 		if ( 24-bdf2->pixelsize < bdf->pixelsize-24 )
3412 		    bdf = bdf2;
3413 	    } else if ( bdf==NULL )
3414 		bdf = bdf2;
3415 	}
3416 	if ( bdf!=NULL && sc->orig_pos<bdf->glyphcnt )
3417 	    bdfc = bdf->glyphs[sc->orig_pos];
3418     }
3419 
3420     if ( bdfc!=NULL ) {
3421 	GClut clut;
3422 	struct _GImage base;
3423 	GImage gi;
3424 	/* if not empty, use the font's own shape, otherwise use a standard */
3425 	/*  font */
3426 	memset(&gi,'\0',sizeof(gi));
3427 	memset(&base,'\0',sizeof(base));
3428 	memset(&clut,'\0',sizeof(clut));
3429 	gi.u.image = &base;
3430 	base.trans = -1;
3431 	base.clut = &clut;
3432 	if ( bdfc->byte_data ) { int i;
3433 	    base.image_type = it_index;
3434 	    clut.clut_len = bdf->clut->clut_len;
3435 	    for ( i=0; i<clut.clut_len; ++i ) {
3436 		int v = 255-i*255/(clut.clut_len-1);
3437 		clut.clut[i] = COLOR_CREATE(v,v,v);
3438 	    }
3439 	    clut.trans_index = -1;
3440 	} else {
3441 	    base.image_type = it_mono;
3442 	    clut.clut_len = 2;
3443 	    clut.clut[1] = 0xffffff;
3444 	}
3445 	base.data = bdfc->bitmap;
3446 	base.bytes_per_line = bdfc->bytes_per_line;
3447 	base.width = bdfc->xmax-bdfc->xmin+1;
3448 	base.height = bdfc->ymax-bdfc->ymin+1;
3449 	GDrawDrawImage(icon,&gi,NULL,(r.width-base.width)/2,(r.height-base.height)/2);
3450     } else if ( sc->unicodeenc!=-1 ) {
3451 	FontRequest rq;
3452 	GFont *font;
3453 	unichar_t text[2];
3454 	int as, ds, ld, width;
3455 
3456 	memset(&rq,0,sizeof(rq));
3457 	rq.utf8_family_name = SERIF_UI_FAMILIES;
3458 	rq.point_size = 24;
3459 	rq.weight = 400;
3460 	font = GDrawInstanciateFont(NULL,&rq);
3461 	GDrawSetFont(icon,font);
3462 	text[0] = sc->unicodeenc; text[1] = 0;
3463 	GDrawWindowFontMetrics(icon,font,&as,&ds,&ld);
3464 	width = GDrawGetTextWidth(icon,text,1);
3465 	GDrawDrawText(icon,(r.width-width)/2,(r.height-as-ds)/2+as,text,1,0xffffff);
3466     }
3467 return( icon );
3468 }
3469 
CVCurEnc(CharView * cv)3470 static int CVCurEnc(CharView *cv)
3471 {
3472     if ( cv->map_of_enc == ((FontView *) (cv->b.fv))->b.map && cv->enc!=-1 )
3473         return( cv->enc );
3474 
3475     return( ((FontView *) (cv->b.fv))->b.map->backmap[cv->b.sc->orig_pos] );
3476 }
3477 
CVMakeTitles(CharView * cv,char * buf,size_t len)3478 static char *CVMakeTitles(CharView *cv,char *buf,size_t len) {
3479     char *title;
3480     SplineChar *sc = cv->b.sc;
3481     SplineFont *sf = sc->parent;
3482     char *uniname;
3483     size_t used;
3484 
3485 /* GT: This is the title for a window showing an outline character */
3486 /* GT: It will look something like: */
3487 /* GT:  exclam at 33 from Arial */
3488 /* GT: $1 is the name of the glyph */
3489 /* GT: $2 is the glyph's encoding */
3490 /* GT: $3 is the font name */
3491 /* GT: $4 is the changed flag ('*' for the changed items)*/
3492     used = snprintf(buf,len,_("%1$.80s at %2$d from %3$.90s%4$s"),
3493 		    sc->name, CVCurEnc(cv), sf->fontname,
3494 		    sc->changed ? "*" : "");
3495     title = copy(buf);
3496 
3497     if (used < len) {
3498 	/* Enhance 'buf' description with Nameslist.txt unicode name definition */
3499 	if ( (uniname=unicode_name(sc->unicodeenc))!=NULL ) {
3500 	    used += snprintf(buf+used, len-used, " %s", uniname);
3501 	    free(uniname);
3502 	}
3503     }
3504 
3505     if (used < len && ( cv->show_ft_results || cv->dv )) {
3506 	snprintf(buf+used, len-used, " (%gpt, %ddpi)", (double) cv->ft_pointsizey, cv->ft_dpi );
3507     }
3508 
3509     return( title );
3510 }
3511 
SC_RefreshTitles(SplineChar * sc)3512 static void SC_RefreshTitles(SplineChar *sc) {
3513     /* Called if the user changes the unicode encoding or the character name */
3514     CharView *cv;
3515     char buf[300], *title;
3516 
3517     if ( (CharView *) (sc->views)==NULL )
3518 return;
3519     for ( cv = (CharView *) (sc->views); cv!=NULL; cv=(CharView *) (cv->b.next) ) {
3520 	title = CVMakeTitles(cv,buf,sizeof(buf));
3521 	/* Could be different if one window is debugging and one is not */
3522 	GDrawSetWindowTitles8(cv->gw,buf,title);
3523 	free(title);
3524     }
3525 }
3526 
CVChangeTabsVisibility(CharView * cv,int makevisible)3527 static void CVChangeTabsVisibility(CharView *cv,int makevisible) {
3528     GRect gsize, pos, sbsize;
3529 
3530     if ( cv->tabs==NULL || GGadgetIsVisible(cv->tabs)==makevisible )
3531 return;
3532     GGadgetGetSize(cv->tabs,&gsize);
3533     GGadgetGetSize(cv->vsb,&sbsize);
3534     if ( makevisible ) {
3535 	cv->mbh += gsize.height;
3536 	cv->height -= gsize.height;
3537 	GGadgetMove(cv->vsb,sbsize.x,sbsize.y+gsize.height);
3538 	GGadgetResize(cv->vsb,sbsize.width,sbsize.height-gsize.height);
3539 	GGadgetMoveAddToY( cv->charselector, gsize.height );
3540 	GGadgetMoveAddToY( cv->charselectorNext, gsize.height );
3541 	GGadgetMoveAddToY( cv->charselectorPrev, gsize.height );
3542     } else {
3543 	cv->mbh -= gsize.height;
3544 	cv->height += gsize.height;
3545 	GGadgetMove(cv->vsb,sbsize.x,sbsize.y-gsize.height);
3546 	GGadgetResize(cv->vsb,sbsize.width,sbsize.height+gsize.height);
3547 	GGadgetMoveAddToY( cv->charselector, -1*gsize.height );
3548 	GGadgetMoveAddToY( cv->charselectorNext, -1*gsize.height );
3549 	GGadgetMoveAddToY( cv->charselectorPrev, -1*gsize.height );
3550     }
3551     GGadgetSetVisible(cv->tabs,makevisible);
3552     cv->back_img_out_of_date = true;
3553     pos.x = 0; pos.y = cv->mbh+cv->charselectorh+cv->infoh;
3554     pos.width = cv->width; pos.height = cv->height;
3555     if ( cv->showrulers ) {
3556 	pos.x += cv->rulerh;
3557 	pos.y += cv->rulerh;
3558     }
3559     GDrawMoveResize(cv->v,pos.x,pos.y,pos.width,pos.height);
3560     GDrawSync(NULL);
3561     GDrawRequestExpose(cv->v,NULL,false);
3562     GDrawRequestExpose(cv->gw,NULL,false);
3563 }
3564 
CVCheckPoints(CharView * cv)3565 static void CVCheckPoints(CharView *cv) {
3566     if ( !SCPointsNumberedProperly(cv->b.sc,CVLayer((CharViewBase *) cv)) && cv->b.sc->ttf_instrs_len!=0 ) {
3567 	char *buts[3];
3568 	int answer;
3569 	buts[0] = _("_Yes");
3570 	buts[1] = _("_No");
3571 	buts[2] = NULL;
3572 	answer = ff_ask(_("Bad Point Numbering"),(const char **) buts,0,1,_("The points in %s are not numbered properly. This means that any instructions will probably move the wrong points and do the wrong thing.\nWould you like me to remove the instructions?"),cv->b.sc->name);
3573 	if ( answer==0 ) {
3574 	    free(cv->b.sc->ttf_instrs); cv->b.sc->ttf_instrs = NULL;
3575 	    cv->b.sc->ttf_instrs_len = 0;
3576 	}
3577     }
3578 }
3579 
CVChangeSC_storeTab(CharView * cv,int tabnumber)3580 static void CVChangeSC_storeTab( CharView *cv, int tabnumber )
3581 {
3582     TRACE("CVChangeSC_storeTab() %d\n", tabnumber );
3583     if( tabnumber < charview_cvtabssz )
3584     {
3585 	CharViewTab* t = &cv->cvtabs[tabnumber];
3586 	strncpy( t->charselected,
3587 		 GGadgetGetTitle8(cv->charselector),
3588 		 charviewtab_charselectedsz );
3589     }
3590 }
3591 
CVChangeSC_fetchTab(CharView * cv,int tabnumber)3592 static void CVChangeSC_fetchTab( CharView *cv, int tabnumber )
3593 {
3594     if( tabnumber < charview_cvtabssz )
3595     {
3596 	CharViewTab* t = &cv->cvtabs[tabnumber];
3597 	GGadgetSetTitle8(cv->charselector, t->charselected );
3598     }
3599 }
3600 
CVSetCharSelectorValueFromSC(CharView * cv,SplineChar * sc)3601 static void CVSetCharSelectorValueFromSC( CharView *cv, SplineChar *sc )
3602 {
3603     const char* title = Wordlist_getSCName( sc );
3604     GGadgetSetTitle8(cv->charselector, title);
3605 }
3606 
3607 // See comment in gtabset.c (fn GTabSetRemoveTabByPos)
3608 static void CVMenuCloseTab(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e));
CVTabSetRemoveSync(GWindow gw,int pos)3609 static void CVTabSetRemoveSync(GWindow gw, int pos) {
3610     CharView* cv = (CharView*) GDrawGetUserData(gw);
3611     // There's no other good way to pass an "argument" to this function as its definition
3612     // is required to be a certain way. The user_data of the GWindow is already our CharView
3613     // so we can't use that.
3614     cv->ctpos = pos;
3615     CVMenuCloseTab(gw, NULL, NULL);
3616 }
3617 
CVTabSetSwapSync(GWindow gw,int pos_a,int pos_b)3618 static void CVTabSetSwapSync(GWindow gw, int pos_a, int pos_b) {
3619     CharView* cv = (CharView*) GDrawGetUserData(gw);
3620     CharViewTab tempt = cv->cvtabs[pos_a];
3621     char* fnt = cv->former_names[pos_a];
3622 
3623     cv->cvtabs[pos_a] = cv->cvtabs[pos_b];
3624     cv->former_names[pos_a] = cv->former_names[pos_b];
3625     cv->cvtabs[pos_b] = tempt;
3626     cv->former_names[pos_b] = fnt;
3627 
3628     int sel = GTabSetGetSel(cv->tabs);
3629     if (pos_a == sel)
3630     cv->oldtabnum = pos_b;
3631     if (pos_b == sel)
3632     cv->oldtabnum = pos_a;
3633 }
3634 
CVChangeSC(CharView * cv,SplineChar * sc)3635 void CVChangeSC( CharView *cv, SplineChar *sc )
3636 {
3637     char *title;
3638     char buf[300];
3639     extern int updateflex;
3640     int i;
3641     int old_layer = CVLayer((CharViewBase *) cv), blayer;
3642     int was_fitted = cv->dv==NULL && cv->b.gridfit!=NULL;
3643 
3644     if ( old_layer>=sc->layer_cnt )
3645 	old_layer = ly_fore;		/* Can happen in type3 fonts where each glyph has a different layer cnt */
3646 
3647     memset( cv->additionalCharsToShow, 0, sizeof(SplineChar*) * additionalCharsToShowLimit );
3648     cv->additionalCharsToShowActiveIndex = 0;
3649     cv->additionalCharsToShow[0] = sc;
3650 
3651     CVDebugFree(cv->dv);
3652 
3653     if ( cv->expandedge != ee_none ) {
3654 	GDrawSetCursor(cv->v,ct_mypointer);
3655 	cv->expandedge = ee_none;
3656     }
3657 
3658     SplinePointListsFree(cv->b.gridfit); cv->b.gridfit = NULL;
3659     FreeType_FreeRaster(cv->oldraster); cv->oldraster = NULL;
3660     FreeType_FreeRaster(cv->raster); cv->raster = NULL;
3661 
3662     SCLigCaretCheck(sc,false);
3663 
3664     CVUnlinkView(cv);
3665     cv->p.nextcp = cv->p.prevcp = cv->widthsel = cv->vwidthsel = false;
3666     if ( (CharView *) (sc->views)==NULL && updateflex )
3667 	SplineCharIsFlexible(sc,old_layer!=ly_grid ? old_layer : ly_fore );
3668     cv->b.sc = sc;
3669     cv->b.next = sc->views;
3670     sc->views = &cv->b;
3671     cv->enc = ( ((FontView *) (cv->b.fv))->b.map->backmap[cv->b.sc->orig_pos] );
3672     cv->b.layerheads[dm_fore] = &sc->layers[ly_fore];
3673     blayer = old_layer;
3674     if ( old_layer==ly_grid || old_layer==ly_fore ||
3675 	    sc->parent->multilayer || old_layer>=sc->layer_cnt )
3676 	blayer = ly_back;
3677     cv->b.layerheads[dm_back] = &sc->layers[blayer];
3678     cv->b.layerheads[dm_grid] = &sc->parent->grid;
3679     cv->p.sp = cv->lastselpt = NULL;
3680     cv->p.spiro = cv->lastselcp = NULL;
3681     cv->apmine = cv->apmatch = NULL; cv->apsc = NULL;
3682     cv->template1 = cv->template2 = NULL;
3683 #if HANYANG
3684     if ( cv->b.sc->parent->rules!=NULL && cv->b.sc->compositionunit )
3685 	Disp_DefaultTemplate(cv);
3686 #endif
3687     if ( cv->b.layerheads[cv->b.drawmode]->order2 )
3688 	CVCheckPoints(cv);
3689     if ( cv->showpointnumbers || cv->show_ft_results )
3690 	SCNumberPoints(sc,old_layer);
3691     if ( cv->show_ft_results )
3692 	CVGridFitChar(cv);
3693 
3694     CVNewScale(cv);
3695 
3696     CharIcon(cv,(FontView *) (cv->b.fv));
3697     title = CVMakeTitles(cv,buf,sizeof(buf));
3698     GDrawSetWindowTitles8(cv->gw,buf,title);
3699     CVInfoDraw(cv,cv->gw);
3700     free(title);
3701     _CVPaletteActivate(cv,true,false);
3702 
3703     if ( cv->tabs!=NULL ) {
3704 	for ( i=0; i<cv->former_cnt; ++i )
3705 	    if ( strcmp(cv->former_names[i],sc->name)==0 )
3706 	break;
3707 	if ( i!=cv->former_cnt && cv->showtabs )
3708 	{
3709 	    CVChangeSC_storeTab( cv, cv->oldtabnum );
3710 	    CVChangeSC_fetchTab( cv, i );
3711 	    cv->oldtabnum = i;
3712 	    GTabSetSetSel(cv->tabs,i);
3713 	}
3714 	else
3715 	{
3716 	    // Only need to store here, as we are about to make a new tab.
3717 	    CVChangeSC_storeTab( cv, cv->oldtabnum );
3718 	    cv->oldtabnum = 0;
3719 	    // have to shuffle the cvtabs along to be in sync with cv->tabs
3720 	    {
3721 		int i = 0;
3722 		for( i=charview_cvtabssz-1; i > 0; i-- )
3723 		{
3724 		    cv->cvtabs[i] = cv->cvtabs[i-1];
3725 		}
3726 	    }
3727 	    CVSetCharSelectorValueFromSC( cv, sc );
3728 
3729 	    if ( cv->former_cnt==CV_TABMAX )
3730 		free(cv->former_names[CV_TABMAX-1]);
3731 	    for ( i=cv->former_cnt<CV_TABMAX?cv->former_cnt-1:CV_TABMAX-2; i>=0; --i )
3732 		cv->former_names[i+1] = cv->former_names[i];
3733 	    cv->former_names[0] = copy(sc->name);
3734 	    if ( cv->former_cnt<CV_TABMAX )
3735 		++cv->former_cnt;
3736 	    for ( i=0; i<cv->former_cnt; ++i )
3737             {
3738                 if( i < charview_cvtabssz )
3739                 {
3740                     CharViewTab* t = &cv->cvtabs[i];
3741                     GTabSetChangeTabName(cv->tabs, t->charselected, i);
3742                 }
3743             }
3744 
3745 	    GTabSetRemetric(cv->tabs);
3746 	    GTabSetSetSel(cv->tabs,0);	/* This does a redraw */
3747 	    if ( !GGadgetIsVisible(cv->tabs) && cv->showtabs )
3748 		CVChangeTabsVisibility(cv,true);
3749 	}
3750     }
3751     if( !strcmp(GGadgetGetTitle8(cv->charselector),""))
3752 	CVSetCharSelectorValueFromSC( cv, sc );
3753 
3754     if ( sc->inspiro && !hasspiro() && !sc->parent->complained_about_spiros ) {
3755 	sc->parent->complained_about_spiros = true;
3756 #ifdef _NO_LIBSPIRO
3757 	ff_post_error(_("You may not use spiros"),_("This glyph should display spiro points, but unfortunately this version of fontforge was not linked with the spiro library, so only normal bezier points will be displayed."));
3758 #else
3759 	ff_post_error(_("You may not use spiros"),_("This glyph should display spiro points, but unfortunately FontForge was unable to load libspiro, spiros are not available for use, and normal bezier points will be displayed instead."));
3760 #endif
3761     }
3762 
3763     if ( was_fitted )
3764 	CVGridFitChar(cv);
3765 
3766     // Force any extra chars to be setup and drawn
3767     GEvent e;
3768     e.type=et_controlevent;
3769     e.u.control.subtype = et_textchanged;
3770     e.u.control.u.tf_changed.from_pulldown = 0;
3771     CV_OnCharSelectorTextChanged( cv->charselector, &e );
3772 }
3773 
CVChangeChar(CharView * cv,int i)3774 static void CVChangeChar(CharView *cv, int i )
3775 {
3776     SplineChar *sc;
3777     SplineFont *sf = cv->b.sc->parent;
3778     EncMap *map = ((FontView *) (cv->b.fv))->b.map;
3779     int gid = i<0 || i>= map->enccount ? -2 : map->map[i];
3780 
3781     if ( sf->cidmaster!=NULL && !map->enc->is_compact ) {
3782 	SplineFont *cidmaster = sf->cidmaster;
3783 	int k;
3784 	for ( k=0; k<cidmaster->subfontcnt; ++k ) {
3785 	    SplineFont *sf = cidmaster->subfonts[k];
3786 	    if ( i<sf->glyphcnt && sf->glyphs[i]!=NULL )
3787 	break;
3788 	}
3789 	if ( k!=cidmaster->subfontcnt ) {
3790 	    if ( cidmaster->subfonts[k] != sf ) {
3791 		sf = cidmaster->subfonts[k];
3792 		gid = ( i>=sf->glyphcnt ) ? -2 : i;
3793 		/* can't create a new glyph this way */
3794 	    }
3795 	}
3796     }
3797     if ( gid == -2 )
3798 return;
3799     if ( gid==-1 || (sc = sf->glyphs[gid])==NULL ) {
3800 	sc = SFMakeChar(sf,map,i);
3801 	sc->inspiro = cv->b.sc->inspiro && hasspiro();
3802     }
3803 
3804     if ( sc==NULL || (cv->b.sc == sc && cv->enc==i ))
3805 return;
3806     cv->map_of_enc = map;
3807     cv->enc = i;
3808     CVChangeSC(cv,sc);
3809 }
3810 
3811 /*
3812  * Unused
3813 static void CVSwitchToTab(CharView *cv,int tnum ) {
3814     if( tnum >= cv->former_cnt )
3815 	return;
3816 
3817     SplineFont *sf = cv->b.fv->sf;
3818     char* n = cv->former_names[tnum];
3819     int unienc = UniFromName(n,sf->uni_interp,cv->b.fv->map->enc);
3820     CVChangeChar(cv,unienc);
3821 }
3822 
3823 static void CVMenuShowTab(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3824     CharView *cv = (CharView *) GDrawGetUserData(gw);
3825     CVSwitchToTab(cv,mi->mid);
3826 }
3827 */
3828 
CVChangeToFormer(GGadget * g,GEvent * e)3829 static int CVChangeToFormer( GGadget *g, GEvent *e) {
3830     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
3831 	CharView *cv = GDrawGetUserData(GGadgetGetWindow(g));
3832 	int new_aspect = GTabSetGetSel(g);
3833 	SplineFont *sf = cv->b.sc->parent;
3834 	int gid;
3835 
3836 	for ( gid=sf->glyphcnt-1; gid>=0; --gid )
3837 	    if ( sf->glyphs[gid]!=NULL &&
3838 		    strcmp(sf->glyphs[gid]->name,cv->former_names[new_aspect])==0 )
3839 	break;
3840 	if ( gid<0 ) {
3841 	    /* They changed the name? See if we can get a unicode value from it */
3842 	    int unienc = UniFromName(cv->former_names[new_aspect],sf->uni_interp,cv->b.fv->map->enc);
3843 	    if ( unienc>=0 ) {
3844 		gid = SFFindGID(sf,unienc,cv->former_names[new_aspect]);
3845 		if ( gid>=0 ) {
3846 		    free(cv->former_names[new_aspect]);
3847 		    cv->former_names[new_aspect] = copy(sf->glyphs[gid]->name);
3848 		}
3849 	    }
3850 	}
3851 	if ( gid<0 )
3852 return( true );
3853 	CVChangeSC(cv,sf->glyphs[gid]);
3854 	TRACE("CVChangeToFormer: Changed SC to %s (GID %d)\n", sf->glyphs[gid]->name, gid);
3855 	cv->enc = ((FontView *) (cv->b.fv))->b.map->backmap[cv->b.sc->orig_pos];
3856     }
3857 return( true );
3858 }
3859 
CVFakeMove(CharView * cv,GEvent * event)3860 static void CVFakeMove(CharView *cv, GEvent *event) {
3861     GEvent e;
3862 
3863     memset(&e,0,sizeof(e));
3864     e.type = et_mousemove;
3865     e.w = cv->v;
3866     if ( event->w!=cv->v ) {
3867 	GPoint p;
3868 	p.x = event->u.chr.x; p.y = event->u.chr.y;
3869 	GDrawTranslateCoordinates(event->w,cv->v,&p);
3870 	event->u.chr.x = p.x; event->u.chr.y = p.y;
3871     }
3872     e.u.mouse.state = TrueCharState(event);
3873     e.u.mouse.x = event->u.chr.x;
3874     e.u.mouse.y = event->u.chr.y;
3875     e.u.mouse.device = NULL;
3876     CVMouseMove(cv,&e);
3877 }
3878 
CVDoFindInFontView(CharView * cv)3879 static void CVDoFindInFontView(CharView *cv) {
3880     FVChangeChar((FontView *) (cv->b.fv),CVCurEnc(cv));
3881     GDrawSetVisible(((FontView *) (cv->b.fv))->gw,true);
3882     GDrawRaise(((FontView *) (cv->b.fv))->gw);
3883 }
3884 
3885 static uint16 HaveModifiers = 0;
3886 static uint16 PressingTilde = 0;
3887 static uint16 PrevCharEventWasCharUpOnControl = 0;
3888 
3889 
CVCharUp(CharView * cv,GEvent * event)3890 static void CVCharUp(CharView *cv, GEvent *event ) {
3891 
3892     if ( !event->u.chr.autorepeat && !HaveModifiers && event->u.chr.keysym==' ' ) {
3893 	update_spacebar_hand_tool(cv);
3894     }
3895 
3896     int oldactiveModifierControl = cv->activeModifierControl;
3897     int oldactiveModifierAlt = cv->activeModifierAlt;
3898     cv->activeModifierControl &= ~( event->u.chr.keysym == GK_Control_L || event->u.chr.keysym == GK_Control_R
3899 				    || event->u.chr.keysym == GK_Meta_L || event->u.chr.keysym == GK_Meta_R );
3900     cv->activeModifierAlt     &= ~( event->u.chr.keysym == GK_Alt_L || event->u.chr.keysym == GK_Alt_R
3901 				    || event->u.chr.keysym == GK_Mode_switch );
3902     // helps with keys on the mac
3903     if( (event->u.chr.state&ksm_meta) )
3904         cv->activeModifierAlt = 0;
3905     if( oldactiveModifierControl != cv->activeModifierControl
3906 	|| oldactiveModifierAlt != cv->activeModifierAlt )
3907     {
3908 	CVInfoDraw(cv,cv->gw);
3909     }
3910 
3911 
3912 
3913 //    TRACE("CVCharUp() ag:%d key:%d\n", cv_auto_goto, event->u.chr.keysym );
3914     if( !cv_auto_goto )
3915     {
3916 	bool isImmediateKeyTogglePreview = isImmediateKey( cv->gw, "TogglePreview", event ) != NULL;
3917 	if( isImmediateKeyTogglePreview ) {
3918 	    PressingTilde = 1;
3919 	}
3920 
3921 	if( PrevCharEventWasCharUpOnControl && isImmediateKeyTogglePreview )
3922 	{
3923 	    HaveModifiers = 0;
3924 	    PrevCharEventWasCharUpOnControl = 0;
3925 	    return;
3926 	}
3927 	PrevCharEventWasCharUpOnControl = 0;
3928 
3929 	if( !event->u.chr.autorepeat
3930 	    && (event->u.chr.keysym == GK_Control_L
3931 		|| event->u.chr.keysym == GK_Control_R ))
3932 	{
3933 	    PrevCharEventWasCharUpOnControl = 1;
3934 	    if( !PressingTilde ) {
3935 		HaveModifiers = 0;
3936 	    }
3937 	}
3938 
3939 	if ( !event->u.chr.autorepeat && !HaveModifiers && isImmediateKeyTogglePreview ) {
3940 	    PressingTilde = 0;
3941 	    CVPreviewModeSet( cv->gw, false );
3942 	    return;
3943 	}
3944 
3945 	if ( !event->u.chr.autorepeat && isImmediateKeyTogglePreview ) {
3946 	    PressingTilde = 0;
3947 	}
3948 	if ( event->u.chr.autorepeat && HaveModifiers && isImmediateKeyTogglePreview ) {
3949 	    return;
3950 	}
3951     }
3952 
3953 
3954     if( event->u.chr.keysym == GK_Escape )
3955     {
3956 	TRACE("escape char.......!\n");
3957 	GGadget *active = GWindowGetFocusGadgetOfWindow(cv->gw);
3958 	if( active == cv->charselector )
3959 	{
3960 	    TRACE("was on charselector\n");
3961 	    GWidgetIndicateFocusGadget( cv->hsb );
3962 	}
3963 	else if ( cv->charselector != NULL )
3964 	{
3965 	    TRACE("was on NOT charselector\n");
3966 	    GWidgetIndicateFocusGadget( cv->charselector );
3967 	}
3968     }
3969 
3970 
3971 #if _ModKeysAutoRepeat
3972     /* Under cygwin these keys auto repeat, they don't under normal X */
3973     if ( event->u.chr.keysym == GK_Shift_L || event->u.chr.keysym == GK_Shift_R ||
3974 	    event->u.chr.keysym == GK_Control_L || event->u.chr.keysym == GK_Control_R ||
3975 	    event->u.chr.keysym == GK_Meta_L || event->u.chr.keysym == GK_Meta_R ||
3976 	    event->u.chr.keysym == GK_Alt_L || event->u.chr.keysym == GK_Alt_R ||
3977 	    event->u.chr.keysym == GK_Super_L || event->u.chr.keysym == GK_Super_R ||
3978 	    event->u.chr.keysym == GK_Hyper_L || event->u.chr.keysym == GK_Hyper_R ) {
3979 	if ( cv->autorpt!=NULL ) {
3980 	    GDrawCancelTimer(cv->autorpt);
3981 	    CVToolsSetCursor(cv,cv->oldstate,NULL);
3982 	}
3983 	cv->keysym = event->u.chr.keysym;
3984 	cv->oldkeyx = event->u.chr.x;
3985 	cv->oldkeyy = event->u.chr.y;
3986 	cv->oldkeyw = event->w;
3987 	cv->oldstate = TrueCharState(event);
3988 	cv->autorpt = GDrawRequestTimer(cv->v,100,0,NULL);
3989     } else {
3990 	if ( cv->autorpt!=NULL ) {
3991 	    GDrawCancelTimer(cv->autorpt); cv->autorpt=NULL;
3992 	    CVToolsSetCursor(cv,cv->oldstate,NULL);
3993 	}
3994 	CVToolsSetCursor(cv,TrueCharState(event),NULL);
3995     }
3996 #else
3997     CVToolsSetCursor(cv,TrueCharState(event),NULL);
3998     if ( event->u.chr.keysym == GK_Shift_L || event->u.chr.keysym == GK_Shift_R ||
3999 	     event->u.chr.keysym == GK_Alt_L || event->u.chr.keysym == GK_Alt_R ||
4000 	     event->u.chr.keysym == GK_Meta_L || event->u.chr.keysym == GK_Meta_R )
4001 	CVFakeMove(cv, event);
4002 #endif
4003 }
4004 
CVInfoDrawText(CharView * cv,GWindow pixmap)4005 void CVInfoDrawText(CharView *cv, GWindow pixmap ) {
4006     CharViewTab* tab = CVGetActiveTab(cv);
4007     GRect r;
4008     Color bg = GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(pixmap));
4009     Color fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
4010     const int buffersz = 150;
4011     char buffer[buffersz+1];
4012     int ybase = cv->mbh+cv->charselectorh+(cv->infoh-cv->sfh)/2+cv->sas;
4013     real xdiff, ydiff;
4014     SplinePoint *sp, dummy;
4015     spiro_cp *cp;
4016 
4017     GDrawSetFont(pixmap,cv->small);
4018     r.x = RPT_DATA; r.width = SPT_BASE-RPT_DATA;
4019     r.y = cv->mbh+cv->charselectorh; r.height = cv->infoh-1;
4020     GDrawFillRect(pixmap,&r,bg);
4021     r.x = SPT_DATA; r.width = SOF_BASE-SPT_DATA;
4022     GDrawFillRect(pixmap,&r,bg);
4023     r.x = SOF_DATA; r.width = SDS_BASE-SOF_DATA;
4024     GDrawFillRect(pixmap,&r,bg);
4025     r.x = SDS_DATA; r.width = SAN_BASE-SDS_DATA;
4026     GDrawFillRect(pixmap,&r,bg);
4027     r.x = SAN_DATA; r.width = MAG_BASE-SAN_DATA;
4028     GDrawFillRect(pixmap,&r,bg);
4029     r.x = MAG_DATA; r.width = LAYER_DATA-MAG_DATA;
4030     GDrawFillRect(pixmap,&r,bg);
4031     r.x = LAYER_DATA; r.width = CODERANGE_DATA-LAYER_DATA;
4032     GDrawFillRect(pixmap,&r,bg);
4033     r.x = CODERANGE_DATA; r.width = FLAGS_DATA-CODERANGE_DATA;
4034     GDrawFillRect(pixmap,&r,bg);
4035     r.x = FLAGS_DATA; r.width = 200;
4036     GDrawFillRect(pixmap,&r,bg);
4037 
4038     if ( cv->info_within ) {
4039 	if ( cv->info.x>=1000 || cv->info.x<=-1000 || cv->info.y>=1000 || cv->info.y<=-1000 )
4040 	    sprintf(buffer,"%d%s%d", (int) cv->info.x, coord_sep, (int) cv->info.y );
4041 	else
4042 	    sprintf(buffer,"%.4g%s%.4g", (double) cv->info.x, coord_sep, (double) cv->info.y );
4043 	buffer[11] = '\0';
4044 	GDrawDrawText8(pixmap,RPT_DATA,ybase,buffer,-1,fg);
4045     }
4046     if ( tab->scale>=.25 )
4047 	sprintf( buffer, "%d%%", (int) (100*tab->scale));
4048     else
4049 	sprintf( buffer, "%.3g%%", (double) (100*tab->scale));
4050     GDrawDrawText8(pixmap,MAG_DATA,ybase,buffer,-1,fg);
4051 
4052     const int layernamesz = 100;
4053     char layername[layernamesz+1];
4054     strncpy(layername,_("Guide"),layernamesz);
4055     if(cv->b.drawmode!=dm_grid) {
4056 	int idx = CVLayer((CharViewBase *) cv);
4057 	if(idx >= 0 && idx < cv->b.sc->parent->layer_cnt) {
4058 	    strncpy(layername,cv->b.sc->parent->layers[idx].name,layernamesz);
4059 	}
4060     }
4061     snprintf( buffer, buffersz, _("Active Layer: %s (%s)"),
4062 	      layername,
4063 /* GT: Guide layer, make it short */
4064 	      ( cv->b.drawmode==dm_grid ? _("Guide") :
4065 /* GT: Background, make it short */
4066 		cv->b.layerheads[cv->b.drawmode]->background ? _("Back") :
4067 /* GT: Foreground, make it short */
4068 		_("Fore") )
4069 	      );
4070     GDrawDrawText8(pixmap,LAYER_DATA,ybase,buffer,-1,fg);
4071     GDrawDrawText8(pixmap,LAYER_DATA,ybase,buffer,-1,fg);
4072 
4073     if ( cv->coderange==cr_none )
4074     {
4075 	snprintf( buffer, buffersz, _("Modes: "));
4076 	if( CVShouldInterpolateCPsOnMotion(cv) )
4077 	    strcat( buffer, "Interpolate" );
4078 	GDrawDrawText8(pixmap,FLAGS_DATA,ybase,buffer,-1,fg);
4079     }
4080 
4081 
4082     if ( cv->coderange!=cr_none ) {
4083 	GDrawDrawText8(pixmap,CODERANGE_DATA,ybase,
4084 		cv->coderange==cr_fpgm ? _("'fpgm'") :
4085 		cv->coderange==cr_prep ? _("'prep'") : _("Glyph"),
4086 	    -1,fg);
4087 	GDrawDrawText8(pixmap,CODERANGE_DATA+40,ybase,
4088 		FreeTypeStringVersion(), -1,fg);
4089     }
4090     sp = NULL; cp = NULL;
4091     if ( cv->b.sc->inspiro && hasspiro())
4092 	cp = cv->p.spiro!=NULL ? cv->p.spiro : cv->lastselcp;
4093     else
4094 	sp = cv->p.sp!=NULL ? cv->p.sp : cv->lastselpt;
4095     if ( sp==NULL && cp==NULL )
4096 	    if ( cv->active_tool==cvt_rect || cv->active_tool==cvt_elipse ||
4097 		    cv->active_tool==cvt_poly || cv->active_tool==cvt_star ||
4098 		    cv->active_tool==cvt_scale || cv->active_tool==cvt_skew ||
4099 		    cv->active_tool==cvt_rotate || cv->active_tool==cvt_flip ) {
4100 	dummy.me.x = cv->p.cx; dummy.me.y = cv->p.cy;
4101 	sp = &dummy;
4102     }
4103     if ( sp || cp ) {
4104 	real selx, sely;
4105 	if ( sp ) {
4106 	    if ( cv->pressed && sp==cv->p.sp ) {
4107 		selx = cv->p.constrain.x;
4108 		sely = cv->p.constrain.y;
4109 	    } else {
4110 		selx = sp->me.x;
4111 		sely = sp->me.y;
4112 	    }
4113 	} else {
4114 	    selx = cp->x;
4115 	    sely = cp->y;
4116 	}
4117 	xdiff=cv->info.x-selx;
4118 	ydiff = cv->info.y-sely;
4119 
4120 	if ( selx>=1000 || selx<=-1000 || sely>=1000 || sely<=-1000 )
4121 	    sprintf(buffer,"%d%s%d", (int) selx, coord_sep, (int) sely );
4122 	else
4123 	    sprintf(buffer,"%.4g%s%.4g", (double) selx, coord_sep, (double) sely );
4124 	buffer[11] = '\0';
4125 	GDrawDrawText8(pixmap,SPT_DATA,ybase,buffer,-1,fg);
4126     } else if ( cv->widthsel && cv->info_within ) {
4127 	xdiff = cv->info.x-cv->p.cx;
4128 	ydiff = 0;
4129     } else if ( cv->p.rubberbanding && cv->info_within ) {
4130 	xdiff=cv->info.x-cv->p.cx;
4131 	ydiff = cv->info.y-cv->p.cy;
4132     } else
4133 return;
4134     if ( !cv->info_within )
4135 return;
4136 
4137     if ( cv->active_tool==cvt_scale ) {
4138 	xdiff = 100.0 + (cv->info.x-cv->p.cx)/(4*tab->scale);
4139 	ydiff = 100.0 + (cv->info.y-cv->p.cy)/(4*tab->scale);
4140 	if ( xdiff>=100 || xdiff<=-100 || ydiff>=100 || ydiff<=-100 )
4141 	    sprintf(buffer,"%d%%%s%d%%", (int) xdiff, coord_sep, (int) ydiff );
4142 	else
4143 	    sprintf(buffer,"%.3g%%%s%.3g%%", (double) xdiff, coord_sep, (double) ydiff );
4144     } else if ( xdiff>=1000 || xdiff<=-1000 || ydiff>=1000 || ydiff<=-1000 )
4145 	sprintf(buffer,"%d%s%d", (int) xdiff, coord_sep, (int) ydiff );
4146     else
4147 	sprintf(buffer,"%.4g%s%.4g", (double) xdiff, coord_sep, (double) ydiff );
4148     buffer[11] = '\0';
4149     GDrawDrawText8(pixmap,SOF_DATA,ybase,buffer,-1,fg);
4150 
4151     sprintf( buffer, "%.1f", sqrt(xdiff*xdiff+ydiff*ydiff));
4152     GDrawDrawText8(pixmap,SDS_DATA,ybase,buffer,-1,fg);
4153 
4154 	/* Utf-8 for degree sign */
4155     sprintf( buffer, "%d\302\260", (int) rint(180*atan2(ydiff,xdiff)/FF_PI));
4156     GDrawDrawText8(pixmap,SAN_DATA,ybase,buffer,-1,fg);
4157 }
4158 
CVInfoDrawRulers(CharView * cv,GWindow pixmap)4159 static void CVInfoDrawRulers(CharView *cv, GWindow pixmap ) {
4160     // Check if we have any rulers to draw over
4161     if (cv->hruler == NULL || cv->vruler == NULL) {
4162         return;
4163     }
4164 
4165     int rstart = cv->mbh+cv->charselectorh+cv->infoh;
4166     GRect rh, rv, oldrh, oldrv;
4167     rh.y = rstart; rh.height = cv->rulerh; rh.x = cv->rulerh; rh.width = cv->width;
4168     rv.x = 0; rv.width = cv->rulerh; rv.y = rstart + cv->rulerh; rv.height = cv->height;
4169 
4170     GDrawSetLineWidth(pixmap,0);
4171     // Draw the new rulers
4172     GDrawPushClip(pixmap, &rh, &oldrh);
4173     rh.x = cv->olde.x; rh.y = 0; rh.width = 1;
4174     GDrawDrawPixmap(pixmap, cv->hruler, &rh, cv->rulerh + cv->olde.x, rstart);
4175     GDrawDrawLine(pixmap,cv->e.x+cv->rulerh,rstart,cv->e.x+cv->rulerh,rstart+cv->rulerh,0xff0000);
4176     GDrawPopClip(pixmap, &oldrh);
4177 
4178     GDrawPushClip(pixmap, &rv, &oldrv);
4179     rv.x = 0; rv.y = cv->olde.y; rv.height = 1;
4180     GDrawDrawPixmap(pixmap, cv->vruler, &rv, 0, cv->rulerh + rstart + cv->olde.y);
4181     GDrawDrawLine(pixmap,0,cv->e.y+rstart+cv->rulerh,cv->rulerh,cv->e.y+rstart+cv->rulerh,0xff0000);
4182     GDrawPopClip(pixmap, &oldrv);
4183 
4184     cv->olde = cv->e;
4185 }
4186 
CVInfoDraw(CharView * cv,GWindow pixmap)4187 void CVInfoDraw(CharView *cv, GWindow pixmap ) {
4188     CVInfoDrawText(cv,pixmap);
4189     if ( cv->showrulers )
4190 	CVInfoDrawRulers(cv,pixmap);
4191 }
4192 
CVCrossing(CharView * cv,GEvent * event)4193 static void CVCrossing(CharView *cv, GEvent *event ) {
4194     CharViewTab* tab = CVGetActiveTab(cv);
4195     CVToolsSetCursor(cv,event->u.mouse.state,event->u.mouse.device);
4196     cv->info_within = event->u.crossing.entered;
4197     cv->info.x = (event->u.crossing.x-tab->xoff)/tab->scale;
4198     cv->info.y = (cv->height-event->u.crossing.y-tab->yoff)/tab->scale;
4199     CVInfoDraw(cv,cv->gw);
4200     CPEndInfo(cv);
4201 }
4202 
CheckSpiroPoint(FindSel * fs,spiro_cp * cp,SplineSet * spl,int index)4203 static int CheckSpiroPoint(FindSel *fs, spiro_cp *cp, SplineSet *spl,int index) {
4204 
4205     if ( fs->xl<=cp->x && fs->xh>=cp->x &&
4206 	    fs->yl<=cp->y && fs->yh >= cp->y ) {
4207 	fs->p->spiro = cp;
4208 	fs->p->spline = NULL;
4209 	fs->p->anysel = true;
4210 	fs->p->spl = spl;
4211 	fs->p->spiro_index = index;
4212 return( true );
4213     }
4214 return( false );
4215 }
4216 
CheckPoint(FindSel * fs,SplinePoint * sp,SplineSet * spl)4217 static int CheckPoint(FindSel *fs, SplinePoint *sp, SplineSet *spl) {
4218 
4219     if ( fs->xl<=sp->me.x && fs->xh>=sp->me.x &&
4220 	    fs->yl<=sp->me.y && fs->yh >= sp->me.y ) {
4221 	fs->p->sp = sp;
4222 	fs->p->spline = NULL;
4223 	fs->p->anysel = true;
4224 	fs->p->spl = spl;
4225 	if ( !fs->seek_controls )
4226 return( true );
4227     }
4228     if ( (sp->selected && fs->select_controls)
4229 	 || fs->all_controls
4230 	 || fs->alwaysshowcontrolpoints )
4231     {
4232 	int seln=false, selp=false;
4233 	if ( fs->c_xl<=sp->nextcp.x && fs->c_xh>=sp->nextcp.x &&
4234 		fs->c_yl<=sp->nextcp.y && fs->c_yh >= sp->nextcp.y )
4235 	    seln = true;
4236 	if ( fs->c_xl<=sp->prevcp.x && fs->c_xh>=sp->prevcp.x &&
4237 		fs->c_yl<=sp->prevcp.y && fs->c_yh >= sp->prevcp.y )
4238 	    selp = true;
4239 	if ( seln && selp ) {
4240 	    /* Select the one with a spline attached. */
4241 	    if ( sp->prev!=NULL && sp->next==NULL )
4242 		seln = false;
4243 	}
4244 	if ( seln ) {
4245 	    fs->p->sp = sp;
4246 	    fs->p->spline = NULL;
4247 	    fs->p->spl = spl;
4248 	    fs->p->nextcp = true;
4249 	    fs->p->anysel = true;
4250 	    fs->p->cp = sp->nextcp;
4251 	    if ( sp->nonextcp && (sp->pointtype==pt_curve || sp->pointtype==pt_hvcurve)) {
4252 		fs->p->cp.x = sp->me.x + (sp->me.x-sp->prevcp.x);
4253 		fs->p->cp.y = sp->me.y + (sp->me.y-sp->prevcp.y);
4254 	    }
4255 	    sp->selected = true;
4256 	    sp->nextcpselected = true;
4257 return( true );
4258 	} else if ( selp ) {
4259 	    fs->p->sp = sp;
4260 	    fs->p->spline = NULL;
4261 	    fs->p->spl = spl;
4262 	    fs->p->prevcp = true;
4263 	    fs->p->anysel = true;
4264 	    fs->p->cp = sp->prevcp;
4265 	    if ( sp->noprevcp && (sp->pointtype==pt_curve || sp->pointtype==pt_hvcurve)) {
4266 		fs->p->cp.x = sp->me.x + (sp->me.x-sp->nextcp.x);
4267 		fs->p->cp.y = sp->me.y + (sp->me.y-sp->nextcp.y);
4268 	    }
4269 	    sp->selected = true;
4270 	    sp->prevcpselected = true;
4271 return( true );
4272 	}
4273     }
4274 return( false );
4275 }
4276 
CheckSpline(FindSel * fs,Spline * spline,SplineSet * spl)4277 static int CheckSpline(FindSel *fs, Spline *spline, SplineSet *spl) {
4278 
4279     /* Anything else is better than a spline */
4280     if ( fs->p->anysel )
4281 return( false );
4282 
4283     if ( NearSpline(fs,spline)) {
4284 	fs->p->spline = spline;
4285 	fs->p->spl = spl;
4286 	fs->p->anysel = true;
4287 	fs->p->spiro_index = SplineT2SpiroIndex(spline,fs->p->t,spl);
4288 return( false /*true*/ );	/* Check if there's a point where we are first */
4289 	/* if there is use it, if not (because anysel is true) we'll fall back */
4290 	/* here */
4291     }
4292 
4293 return( false );
4294 }
4295 
InImage(FindSel * fs,ImageList * img)4296 static int InImage( FindSel *fs, ImageList *img) {
4297     int x,y;
4298 
4299     x = floor((fs->p->cx-img->xoff)/img->xscale);
4300     y = floor((img->yoff-fs->p->cy)/img->yscale);
4301     if ( x<0 || y<0 || x>=GImageGetWidth(img->image) || y>=GImageGetHeight(img->image))
4302 return ( false );
4303     if ( GImageGetPixelRGBA(img->image,x,y)<0x80000000 )	/* Transparent(ish) */
4304 return( false );
4305 
4306 return( true );
4307 }
4308 
InSplineSet(FindSel * fs,SplinePointList * set,int inspiro)4309 static int InSplineSet( FindSel *fs, SplinePointList *set,int inspiro) {
4310     SplinePointList *spl;
4311     Spline *spline, *first;
4312     int i;
4313 
4314     for ( spl = set; spl!=NULL; spl = spl->next ) {
4315 	if ( inspiro ) {
4316 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
4317 		if ( CheckSpiroPoint(fs,&spl->spiros[i],spl,i))
4318 return( true );
4319 	    first = NULL;
4320 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
4321 		if ( CheckSpline(fs,spline,spl), fs->p->anysel )
4322 return( true );
4323 		if ( first==NULL ) first = spline;
4324 	    }
4325 	} else {
4326 	    if ( CheckPoint(fs,spl->first,spl) && ( !fs->seek_controls || fs->p->nextcp || fs->p->prevcp )) {
4327 return( true );
4328 	    }
4329 	    first = NULL;
4330 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
4331 		if ( (CheckPoint(fs,spline->to,spl) && ( !fs->seek_controls || fs->p->nextcp || fs->p->prevcp )) ||
4332 			( CheckSpline(fs,spline,spl) && !fs->seek_controls )) {
4333 return( true );
4334 		}
4335 		if ( first==NULL ) first = spline;
4336 	    }
4337 	}
4338     }
4339 return( fs->p->anysel );
4340 }
4341 
NearSplineSetPoints(FindSel * fs,SplinePointList * set,int inspiro)4342 static int NearSplineSetPoints( FindSel *fs, SplinePointList *set,int inspiro) {
4343     SplinePointList *spl;
4344     Spline *spline, *first;
4345     int i;
4346 
4347     for ( spl = set; spl!=NULL; spl = spl->next ) {
4348 	if ( inspiro ) {
4349 	    for ( i=0; i<spl->spiro_cnt; ++i )
4350 		if ( CheckSpiroPoint(fs,&spl->spiros[i],spl,i))
4351 return( true );
4352 	} else {
4353 	    if ( CheckPoint(fs,spl->first,spl)) {
4354 return( true );
4355 	    }
4356 	    first = NULL;
4357 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
4358 		if ( CheckPoint(fs,spline->to,spl) ) {
4359 return( true );
4360 		}
4361 		if ( first==NULL ) first = spline;
4362 	    }
4363 	}
4364     }
4365 return( fs->p->anysel );
4366 }
4367 
MouseToCX(CharView * cv,int16 mx)4368 static int16 MouseToCX( CharView *cv, int16 mx )
4369 {
4370     CharViewTab* tab = CVGetActiveTab(cv);
4371     return( mx - tab->xoff ) / tab->scale;
4372 }
4373 
4374 
SetFS(FindSel * fs,PressedOn * p,CharView * cv,GEvent * event)4375 static void SetFS( FindSel *fs, PressedOn *p, CharView *cv, GEvent *event) {
4376     CharViewTab* tab = CVGetActiveTab(cv);
4377     extern int snaptoint;
4378 
4379     memset(p,'\0',sizeof(PressedOn));
4380     p->pressed = true;
4381 
4382     memset(fs,'\0',sizeof(*fs));
4383     fs->p = p;
4384     fs->e = event;
4385     p->x = event->u.mouse.x;
4386     p->y = event->u.mouse.y;
4387     p->cx = (event->u.mouse.x-tab->xoff)/tab->scale;
4388     p->cy = (cv->height-event->u.mouse.y-tab->yoff)/tab->scale;
4389 
4390     fs->fudge = (cv->active_tool==cvt_ruler ? snapdistancemeasuretool : snapdistance)/tab->scale;
4391 
4392     /* If they have really large control points then expand
4393      * the selection range to allow them to still click on the
4394      * very edge of the control point to select it.
4395      */
4396     if( prefs_cvEditHandleSize > prefs_cvEditHandleSize_default )
4397     {
4398 	float delta = (prefs_cvEditHandleSize - prefs_cvEditHandleSize_default) / tab->scale;
4399 	delta *= 1.5;
4400 	fs->fudge += delta;
4401     }
4402 
4403     fs->c_xl = fs->xl = p->cx - fs->fudge;
4404     fs->c_xh = fs->xh = p->cx + fs->fudge;
4405     fs->c_yl = fs->yl = p->cy - fs->fudge;
4406     fs->c_yh = fs->yh = p->cy + fs->fudge;
4407     if ( snaptoint ) {
4408 	p->cx = rint(p->cx);
4409 	p->cy = rint(p->cy);
4410 	if ( fs->xl>p->cx - fs->fudge )
4411 	    fs->xl = p->cx - fs->fudge;
4412 	if ( fs->xh < p->cx + fs->fudge )
4413 	    fs->xh = p->cx + fs->fudge;
4414 	if ( fs->yl>p->cy - fs->fudge )
4415 	    fs->yl = p->cy - fs->fudge;
4416 	if ( fs->yh < p->cy + fs->fudge )
4417 	    fs->yh = p->cy + fs->fudge;
4418     }
4419 }
4420 
CVMouseAtSpline(CharView * cv,GEvent * event)4421 int CVMouseAtSpline(CharView *cv,GEvent *event) {
4422     FindSel fs;
4423     int pressed = cv->p.pressed;
4424 
4425     SetFS(&fs,&cv->p,cv,event);
4426     cv->p.pressed = pressed;
4427 return( InSplineSet(&fs,cv->b.layerheads[cv->b.drawmode]->splines,cv->b.sc->inspiro && hasspiro()));
4428 }
4429 
CVConstrainedMouseDown(CharView * cv,GEvent * event,GEvent * fake)4430 static GEvent *CVConstrainedMouseDown(CharView *cv,GEvent *event, GEvent *fake) {
4431     CharViewTab* tab = CVGetActiveTab(cv);
4432     SplinePoint *base;
4433     spiro_cp *basecp;
4434     int basex, basey, dx, dy;
4435     double basetruex, basetruey;
4436     int sign;
4437 
4438     if ( !CVAnySelPoint(cv,&base,&basecp))
4439 return( event );
4440 
4441     if ( base!=NULL ) {
4442 	basetruex = base->me.x;
4443 	basetruey = base->me.y;
4444     } else {
4445 	basetruex = basecp->x;
4446 	basetruey = basecp->y;
4447     }
4448     basex =  tab->xoff + rint(basetruex*tab->scale);
4449     basey = -tab->yoff + cv->height - rint(basetruey*tab->scale);
4450 
4451     dx= event->u.mouse.x-basex, dy = event->u.mouse.y-basey;
4452     sign = dx*dy<0?-1:1;
4453 
4454     fake->u.mouse = event->u.mouse;
4455     if ( dx<0 ) dx = -dx; if ( dy<0 ) dy = -dy;
4456     if ( dy >= 2*dx ) {
4457 	cv->p.x = fake->u.mouse.x = basex;
4458 	cv->p.cx = basetruex ;
4459 	if ( !(event->u.mouse.state&ksm_meta) &&
4460 		ItalicConstrained && cv->b.sc->parent->italicangle!=0 ) {
4461 	    double off = tan(cv->b.sc->parent->italicangle*FF_PI/180)*
4462 		    (cv->p.cy-basetruey);
4463 	    double aoff = off<0 ? -off : off;
4464 	    if ( dx>=aoff*tab->scale/2 && (event->u.mouse.x-basex<0)!=(off<0) ) {
4465 		cv->p.cx -= off;
4466 		cv->p.x = fake->u.mouse.x = tab->xoff + rint(cv->p.cx*tab->scale);
4467 	    }
4468 	}
4469     } else if ( dx >= 2*dy ) {
4470 	fake->u.mouse.y = basey;
4471 	cv->p.cy = basetruey;
4472     } else if ( dx > dy ) {
4473 	fake->u.mouse.x = basex + sign * (event->u.mouse.y-cv->p.y);
4474 	cv->p.cx = basetruex - sign * (cv->p.cy-basetruey);
4475     } else {
4476 	fake->u.mouse.y = basey + sign * (event->u.mouse.x-cv->p.x);
4477 	cv->p.cy = basetruey - sign * (cv->p.cx-basetruex);
4478     }
4479 
4480 return( fake );
4481 }
4482 
CVSetConstrainPoint(CharView * cv,GEvent * event)4483 static void CVSetConstrainPoint(CharView *cv, GEvent *event) {
4484     SplineSet *sel;
4485 
4486     if ( (sel = CVAnySelPointList(cv))!=NULL ) {
4487 	if ( sel->first->selected ) cv->p.constrain = sel->first->me;
4488 	else cv->p.constrain = sel->last->me;
4489     } else if ( cv->p.sp!=NULL ) {
4490 	cv->p.constrain = cv->p.sp->me;
4491     } else {
4492 	cv->p.constrain.x = cv->info.x;
4493 	cv->p.constrain.y = cv->info.y;
4494     }
4495 }
4496 
CVDoSnaps(CharView * cv,FindSel * fs)4497 static void CVDoSnaps(CharView *cv, FindSel *fs) {
4498     PressedOn *p = fs->p;
4499 
4500     if ( cv->b.drawmode!=dm_grid && cv->b.layerheads[dm_grid]->splines!=NULL ) {
4501 	PressedOn temp;
4502 	int oldseek = fs->seek_controls;
4503 	temp = *p;
4504 	fs->p = &temp;
4505 	fs->seek_controls = false;
4506 	if ( InSplineSet( fs, cv->b.layerheads[dm_grid]->splines,cv->b.sc->inspiro && hasspiro())) {
4507 	    if ( temp.spline!=NULL ) {
4508 		p->cx = ((temp.spline->splines[0].a*temp.t+
4509 			    temp.spline->splines[0].b)*temp.t+
4510 			    temp.spline->splines[0].c)*temp.t+
4511 			    temp.spline->splines[0].d;
4512 		p->cy = ((temp.spline->splines[1].a*temp.t+
4513 			    temp.spline->splines[1].b)*temp.t+
4514 			    temp.spline->splines[1].c)*temp.t+
4515 			    temp.spline->splines[1].d;
4516 	    } else if ( temp.sp!=NULL ) {
4517 		p->cx = temp.sp->me.x;
4518 		p->cy = temp.sp->me.y;
4519 	    }
4520 	}
4521 	fs->p = p;
4522 	fs->seek_controls = oldseek;
4523     }
4524     if ( p->cx>-fs->fudge && p->cx<fs->fudge )
4525 	p->cx = 0;
4526     else if ( p->cx>cv->b.sc->width-fs->fudge && p->cx<cv->b.sc->width+fs->fudge &&
4527 	    !cv->widthsel)
4528 	p->cx = cv->b.sc->width;
4529     else if ( cv->widthsel && p!=&cv->p &&
4530 	    p->cx>cv->oldwidth-fs->fudge && p->cx<cv->oldwidth+fs->fudge )
4531 	p->cx = cv->oldwidth;
4532     if ( p->cy>-fs->fudge && p->cy<fs->fudge )
4533 	p->cy = 0;
4534 }
4535 
_CVTestSelectFromEvent(CharView * cv,FindSel * fs)4536 static int _CVTestSelectFromEvent(CharView *cv,FindSel *fs) {
4537     PressedOn temp;
4538     ImageList *img;
4539     int found;
4540 
4541     found = InSplineSet(fs,cv->b.layerheads[cv->b.drawmode]->splines,cv->b.sc->inspiro && hasspiro());
4542 
4543     if ( !found ) {
4544 	RefChar *rf;
4545 	temp = cv->p;
4546 	fs->p = &temp;
4547 	fs->seek_controls = false;
4548 	for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf = rf->next ) {
4549 	    if ( InSplineSet(fs,rf->layers[0].splines,cv->b.sc->inspiro && hasspiro())) {
4550 		cv->p.ref = rf;
4551 		cv->p.anysel = true;
4552 	break;
4553 	    }
4554 	}
4555 	if ( cv->b.drawmode==dm_fore && ( cv->showanchor && !cv->p.anysel )) {
4556 	    AnchorPoint *ap, *found=NULL;
4557 	    /* I do this pecular search because: */
4558 	    /*	1) I expect there to be lots of times we get multiple */
4559 	    /*     anchors at the same location */
4560 	    /*  2) The anchor points are drawn so that the bottommost */
4561 	    /*	   is displayed */
4562 	    for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next )
4563 		if ( fs->xl<=ap->me.x && fs->xh>=ap->me.x &&
4564 			fs->yl<=ap->me.y && fs->yh >= ap->me.y )
4565 		    found = ap;
4566 	    if ( found!=NULL ) {
4567 		cv->p.ap = found;
4568 		cv->p.anysel = true;
4569 	    }
4570 	}
4571 	for ( img = cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img=img->next ) {
4572 	    if ( InImage(fs,img)) {
4573 		cv->p.img = img;
4574 		cv->p.anysel = true;
4575 	break;
4576 	    }
4577 	}
4578     }
4579 return( cv->p.anysel );
4580 }
4581 
CVTestSelectFromEvent(CharView * cv,GEvent * event)4582 int CVTestSelectFromEvent(CharView *cv,GEvent *event) {
4583     FindSel fs;
4584 
4585     SetFS(&fs,&cv->p,cv,event);
4586 return( _CVTestSelectFromEvent(cv,&fs));
4587 }
4588 
4589 /**
4590  * A cache for the selected spline point or spiro control point
4591  */
4592 typedef struct lastselectedpoint
4593 {
4594     SplinePoint *lastselpt;
4595     spiro_cp *lastselcp;
4596 } lastSelectedPoint;
4597 
CVMaybeCreateDraggingComparisonOutline(CharView * cv)4598 static void CVMaybeCreateDraggingComparisonOutline( CharView* cv )
4599 {
4600     if (!prefs_create_dragging_comparison_outline)
4601         return;
4602     if (!cv)
4603         return;
4604     if (cv->p.pretransform_spl) {
4605         SplinePointListFree(cv->p.pretransform_spl);
4606         cv->p.pretransform_spl = NULL;
4607     }
4608 
4609     Layer* l = cv->b.layerheads[cv->b.drawmode];
4610     if (!l || !l->splines)
4611         return;
4612 
4613     if (cv->p.anysel) {
4614         SplinePointList *cur = NULL;
4615         for (SplinePointList *spl = l->splines; spl; spl = spl->next) {
4616             if (SplinePointListCheckSelected1(spl, cv->b.sc->inspiro && hasspiro(), NULL, false)) {
4617                 if (cur == NULL) {
4618                     cv->p.pretransform_spl = cur = SplinePointListCopy1(spl);
4619                 } else {
4620                     cur->next = SplinePointListCopy1(spl);
4621                     cur = cur->next;
4622                 }
4623             }
4624         }
4625     }
4626 }
4627 
CVSwitchActiveSC(CharView * cv,SplineChar * sc,int idx)4628 static void CVSwitchActiveSC( CharView *cv, SplineChar* sc, int idx )
4629 {
4630     CharViewTab* tab = CVGetActiveTab(cv);
4631     int i=0;
4632     FontViewBase *fv = cv->b.fv;
4633     char buf[300];
4634 
4635     TRACE("CVSwitchActiveSC() idx:%d active:%d\n", idx, cv->additionalCharsToShowActiveIndex );
4636     if( !sc )
4637     {
4638 	sc = cv->additionalCharsToShow[i];
4639 	if( !sc )
4640 	    return;
4641     }
4642     else
4643     {
4644 	// test for setting something twice
4645 	if( !idx && cv->additionalCharsToShowActiveIndex == idx )
4646 	{
4647 	    if( cv->additionalCharsToShow[i] == sc )
4648 		return;
4649 	}
4650     }
4651 
4652     cv->changedActiveGlyph = 1;
4653     TRACE("CVSwitchActiveSC(b) activeidx:%d newidx:%d\n", cv->additionalCharsToShowActiveIndex, idx );
4654     for( i=0; i < additionalCharsToShowLimit; i++ )
4655 	if( cv->additionalCharsToShow[i] )
4656 	    TRACE("CVSwitchActiveSC(b) toshow.. i:%d char:%s\n", i, cv->additionalCharsToShow[i]
4657 		   ? cv->additionalCharsToShow[i]->name : "N/A" );
4658 
4659 
4660     //
4661     // Work out how far we should scroll the panel to keep
4662     // the display from jumping around. First look right then
4663     // check left.
4664     int scroll_offset = 0;
4665     if( idx > cv->additionalCharsToShowActiveIndex )
4666     {
4667 	SplineChar* xc = 0;
4668 	int i = 0;
4669 	scroll_offset = cv->b.sc->width;
4670 	int ridx = cv->additionalCharsToShowActiveIndex+1;
4671 	for( i=ridx; i < idx; i++ )
4672 	{
4673 	    if((xc = cv->additionalCharsToShow[i]))
4674 		scroll_offset += xc->width;
4675 	}
4676     }
4677     if( idx < cv->additionalCharsToShowActiveIndex )
4678     {
4679 	SplineChar* xc = 0;
4680 	int i = 0;
4681 	scroll_offset = 0;
4682 	int ridx = cv->additionalCharsToShowActiveIndex-1;
4683 	for( i=ridx; i >= idx; i-- )
4684 	{
4685 	    if((xc = cv->additionalCharsToShow[i]))
4686 		scroll_offset -= xc->width;
4687 	}
4688     }
4689 
4690 
4691 
4692 
4693 
4694     CVUnlinkView( cv );
4695     cv->b.sc = sc;
4696     cv->b.next = sc->views;
4697     cv->b.layerheads[dm_fore] = &sc->layers[ly_fore];
4698     cv->b.layerheads[dm_back] = &sc->layers[ly_back];
4699     cv->b.layerheads[dm_grid] = &fv->sf->grid;
4700     if ( !sc->parent->multilayer && fv->active_layer!=ly_fore ) {
4701 	cv->b.layerheads[dm_back] = &sc->layers[fv->active_layer];
4702 	cv->b.drawmode = dm_back;
4703     }
4704     CharIcon(cv,(FontView *) (cv->b.fv));
4705     char* title = CVMakeTitles(cv,buf,sizeof(buf));
4706     GDrawSetWindowTitles8(cv->gw,buf,title);
4707     CVInfoDraw(cv,cv->gw);
4708     free(title);
4709     _CVPaletteActivate(cv,true,false);
4710 
4711     TRACE("CVSwitchActiveSC() idx:%d\n", idx );
4712 
4713     cv->additionalCharsToShowActiveIndex = idx;
4714 
4715     // update the select[i]on in the input text to reflect
4716     // the users currently selected char.
4717     {
4718 	SplineFont* sf = cv->b.sc->parent;
4719 	EncMap *map = ((FontView *) (cv->b.fv))->b.map;
4720 	unichar_t *srctxt = GGadgetGetTitle( cv->charselector );
4721 	int endsWithSlash = u_endswith( srctxt, c_to_u("/"));
4722 
4723 	unichar_t* p = 0;
4724 	p = Wordlist_selectionClear( sf, map, srctxt );
4725 	p = Wordlist_selectionAdd(   sf, map, p, idx );
4726 	if( endsWithSlash )
4727 	    uc_strcat( p, "/" );
4728 
4729 	// only update when the selection has changed.
4730 	// updating this string is a non reversable operation if the
4731 	// user is part way through typing some text.
4732 	if( !Wordlist_selectionsEqual( srctxt, p ))
4733 	{
4734 	    GGadgetSetTitle( cv->charselector, p );
4735 	}
4736     }
4737 
4738 
4739     cv->b.next = sc->views;
4740     sc->views = &cv->b;
4741 
4742     // Move the scrollbar so that it appears the selection
4743     // box has moved rather than all the characters.
4744     if( scroll_offset )
4745 	CVHScrollSetPos( cv, tab->xoff + scroll_offset * tab->scale );
4746 
4747 //    if ( CVClearSel(cv))
4748 //	SCUpdateAll(cv->b.sc);
4749 
4750 }
4751 
CVMouseDown(CharView * cv,GEvent * event)4752 static void CVMouseDown(CharView *cv, GEvent *event ) {
4753     FindSel fs;
4754     GEvent fake;
4755     lastSelectedPoint lastSel;
4756     memset( &lastSel, 0, sizeof(lastSelectedPoint));
4757 
4758     if ( event->u.mouse.button==2 && event->u.mouse.device!=NULL &&
4759 	    strcmp(event->u.mouse.device,"stylus")==0 )
4760 return;		/* I treat this more like a modifier key change than a button press */
4761 
4762     if ( cv->expandedge != ee_none )
4763 	GDrawSetCursor(cv->v,ct_mypointer);
4764     if ( event->u.mouse.button==3 )
4765     {
4766 	/* context menu */
4767 	CVToolsPopup(cv,event);
4768 	return;
4769     }
4770 
4771     TRACE("tool:%d pointer:%d ctl:%d alt:%d\n",
4772 	   cv->showing_tool,
4773 	   (cv->showing_tool == cvt_pointer),
4774 	   cv->activeModifierControl, cv->activeModifierAlt );
4775 
4776     int8 override_showing_tool = cvt_none;
4777     int8 old_showing_tool = cv->showing_tool;
4778     if( cv->showing_tool == cvt_pointer
4779 	&& cv->activeModifierControl
4780 	&& cv->activeModifierAlt )
4781     {
4782 	FindSel fs;
4783 	SetFS(&fs,&cv->p,cv,event);
4784 	int found = InSplineSet( &fs, cv->b.layerheads[cv->b.drawmode]->splines,
4785 				 cv->b.sc->inspiro && hasspiro());
4786 	TRACE("in spline set:%d cv->p.sp:%p\n", found, cv->p.sp );
4787 
4788 	//
4789 	// Only overwrite to create a point if the user has clicked a spline.
4790 	//
4791 	if( found && !cv->p.sp )
4792 	    override_showing_tool = cvt_curve;
4793     }
4794 
4795     if( cv->charselector && cv->charselector == GWindowGetFocusGadgetOfWindow(cv->gw))
4796 	GWindowClearFocusGadgetOfWindow(cv->gw);
4797 
4798     update_spacebar_hand_tool(cv);
4799 
4800     CVToolsSetCursor(cv,event->u.mouse.state|(1<<(7+event->u.mouse.button)), event->u.mouse.device );
4801     if( override_showing_tool != cvt_none )
4802 	cv->showing_tool = override_showing_tool;
4803     cv->active_tool = cv->showing_tool;
4804     cv->needsrasterize = false;
4805     cv->recentchange = false;
4806 
4807 
4808     SetFS(&fs,&cv->p,cv,event);
4809     if ( event->u.mouse.state&ksm_shift )
4810 	event = CVConstrainedMouseDown(cv,event,&fake);
4811 
4812     if ( cv->active_tool == cvt_pointer ) {
4813 	fs.select_controls = true;
4814 	if ( event->u.mouse.state&ksm_meta ) {
4815 	    fs.seek_controls = true;
4816 	    /* Allow more slop looking for control points if they asked for them */
4817 	    fs.c_xl -= fs.fudge; fs.c_xh += fs.fudge;
4818 	    fs.c_yl -= fs.fudge; fs.c_yh += fs.fudge;
4819 	}
4820 	if ( cv->showpointnumbers && cv->b.layerheads[cv->b.drawmode]->order2 )
4821 	    fs.all_controls = true;
4822 	fs.alwaysshowcontrolpoints = cv->alwaysshowcontrolpoints;
4823 	lastSel.lastselpt = cv->lastselpt;
4824 	lastSel.lastselcp = cv->lastselcp;
4825 	cv->lastselpt = NULL;
4826 	cv->lastselcp = NULL;
4827 	_CVTestSelectFromEvent(cv,&fs);
4828 	fs.p = &cv->p;
4829 
4830 //	TRACE("cvmousedown tab->xoff:%d\n", tab->xoff );
4831 //	TRACE("cvmousedown x:%d y:%d\n",   event->u.mouse.x, event->u.mouse.y );
4832 
4833 	if( !cv->p.anysel && cv->b.drawmode != dm_grid )
4834 	{
4835 	    // If we are in left-right arrow cursor mode to move
4836 	    // those bearings then don't even think about changing
4837 	    // the char right now.
4838 	    if( !CVNearRBearingLine( cv, cv->p.cx, fs.fudge )
4839 		&& !CVNearLBearingLine( cv, cv->p.cx, fs.fudge ))
4840 	    {
4841 		int i=0;
4842 		FindSel fsadjusted = fs;
4843 		fsadjusted.c_xl -= 2*fsadjusted.fudge;
4844 		fsadjusted.c_xh += 2*fsadjusted.fudge;
4845 		fsadjusted.xl -= 2*fsadjusted.fudge;
4846 		fsadjusted.xh += 2*fsadjusted.fudge;
4847 		SplineChar* xc = 0;
4848 		int xcidx = -1;
4849 		int borderFudge = 20;
4850 
4851 		{
4852 		    int offset = cv->b.sc->width;
4853 		    int cumulativeLeftSideBearing = 0;
4854 //	        TRACE("first offset:%d original cx:%f \n", offset, fsadjusted.p->cx );
4855 		    int ridx = cv->additionalCharsToShowActiveIndex+1;
4856 		    for( i=ridx; i < additionalCharsToShowLimit; i++ )
4857 		    {
4858 			if( i == cv->additionalCharsToShowActiveIndex )
4859 			    continue;
4860 			xc = cv->additionalCharsToShow[i];
4861 			if( !xc )
4862 			    break;
4863 			int OffsetForDoingCharNextToActive = 0;
4864 			if( i == ridx )
4865 			{
4866 			    OffsetForDoingCharNextToActive = borderFudge;
4867 			}
4868 
4869 
4870 			cumulativeLeftSideBearing += offset;
4871 			/* TRACE("1 adj. x:%f %f\n",fsadjusted.xl,fsadjusted.xh); */
4872 			/* TRACE("1 adj.cx:%f %f\n",fsadjusted.c_xl,fsadjusted.c_xh); */
4873 			/* TRACE("1 p.  cx:%f %f\n",fsadjusted.p->cx,fsadjusted.p->cy ); */
4874 			/* fsadjusted.c_xl -= offset; */
4875 			/* fsadjusted.c_xh -= offset; */
4876 			/* fsadjusted.xl   -= offset; */
4877 			/* fsadjusted.xh   -= offset; */
4878 			/* TRACE("2 adj. x:%f %f\n",fsadjusted.xl,fsadjusted.xh); */
4879 			/* TRACE("2 adj.cx:%f %f\n",fsadjusted.c_xl,fsadjusted.c_xh); */
4880 			/* TRACE("2 p.  cx:%f %f\n",fsadjusted.p->cx,fsadjusted.p->cy ); */
4881 			/* int found = InSplineSet( &fsadjusted, */
4882 			/* 			     xc->layers[cv->b.drawmode-1].splines, */
4883 			/* 			     xc->inspiro && hasspiro()); */
4884 			TRACE("A:%d\n", cumulativeLeftSideBearing );
4885 			TRACE("B:%f\n", fsadjusted.p->cx );
4886 			TRACE("C:%d\n", cumulativeLeftSideBearing+xc->width );
4887 			int found = IS_IN_ORDER3(
4888 			    cumulativeLeftSideBearing + OffsetForDoingCharNextToActive,
4889 			    fsadjusted.p->cx,
4890 			    cumulativeLeftSideBearing+xc->width );
4891 			TRACE("CVMOUSEDOWN i:%d found:%d\n", i, found );
4892 			if( found )
4893 			{
4894 			    TRACE("FOUND FOUND FOUND FOUND FOUND FOUND FOUND \n");
4895 
4896 			    xcidx = i;
4897 //		    CVChangeSC(cv,xc);
4898 			    break;
4899 			}
4900 
4901 			offset = xc->width;
4902 		    }
4903 		}
4904 
4905 		fsadjusted = fs;
4906 		fsadjusted.c_xl -= 2*fsadjusted.fudge;
4907 		fsadjusted.c_xh += 2*fsadjusted.fudge;
4908 		fsadjusted.xl -= 2*fsadjusted.fudge;
4909 		fsadjusted.xh += 2*fsadjusted.fudge;
4910 
4911 		if( !xc && cv->additionalCharsToShowActiveIndex > 0 )
4912 		{
4913 		    xc = cv->additionalCharsToShow[cv->additionalCharsToShowActiveIndex-1];
4914 		    int offset = xc->width;
4915 		    int cumulativeLeftSideBearing = 0;
4916 //	        TRACE("first offset:%d original cx:%f \n", offset, fsadjusted.p->cx );
4917 		    int lidx = cv->additionalCharsToShowActiveIndex-1;
4918 		    for( i=lidx; i>=0; i-- )
4919 		    {
4920 			if( i == cv->additionalCharsToShowActiveIndex )
4921 			    continue;
4922 			xc = cv->additionalCharsToShow[i];
4923 			if( !xc )
4924 			    break;
4925 			cumulativeLeftSideBearing -= xc->width;
4926 			int OffsetForDoingCharNextToActive = 0;
4927 			if( i == lidx )
4928 			{
4929 			    OffsetForDoingCharNextToActive = borderFudge;
4930 			}
4931 
4932 			/* TRACE("1 adj. x:%f %f\n",fsadjusted.xl,fsadjusted.xh); */
4933 			/* TRACE("1 adj.cx:%f %f\n",fsadjusted.c_xl,fsadjusted.c_xh); */
4934 			/* TRACE("1 p.  cx:%f %f\n",fsadjusted.p->cx,fsadjusted.p->cy ); */
4935 			/* fsadjusted.c_xl += offset; */
4936 			/* fsadjusted.c_xh += offset; */
4937 			/* fsadjusted.xl   += offset; */
4938 			/* fsadjusted.xh   += offset; */
4939 			/* TRACE("2 adj. x:%f %f\n",fsadjusted.xl,fsadjusted.xh); */
4940 			/* TRACE("2 adj.cx:%f %f\n",fsadjusted.c_xl,fsadjusted.c_xh); */
4941 			/* TRACE("2 p.  cx:%f %f\n",fsadjusted.p->cx,fsadjusted.p->cy ); */
4942 			/* int found = InSplineSet( &fsadjusted, */
4943 			/* 			     xc->layers[cv->b.drawmode-1].splines, */
4944 			/* 			     xc->inspiro && hasspiro()); */
4945 			TRACE("A:%d\n", cumulativeLeftSideBearing );
4946 			TRACE("B:%f\n", fsadjusted.p->cx );
4947 			TRACE("C:%d\n", cumulativeLeftSideBearing+xc->width );
4948 			int found = IS_IN_ORDER3(
4949 			    cumulativeLeftSideBearing,
4950 			    fsadjusted.p->cx,
4951 			    cumulativeLeftSideBearing + xc->width - OffsetForDoingCharNextToActive );
4952 
4953 			TRACE("cvmousedown i:%d found:%d\n", i, found );
4954 			if( found )
4955 			{
4956 			    TRACE("FOUND FOUND FOUND FOUND FOUND FOUND FOUND i:%d\n", i);
4957 			    xcidx = i;
4958 			    break;
4959 			}
4960 
4961 			offset = xc->width;
4962 		    }
4963 		}
4964 
4965 		TRACE("have xc:%p xcidx:%d\n", xc, xcidx );
4966 		TRACE("    idx:%d active:%d\n", xcidx, cv->additionalCharsToShowActiveIndex );
4967 		if( xc && xcidx >= 0 )
4968 		{
4969 		    CVSwitchActiveSC( cv, xc, xcidx );
4970 		    GDrawRequestExpose(cv->v,NULL,false);
4971 		    return;
4972 		}
4973 	    }
4974 	}
4975 
4976 
4977 
4978     } else if ( cv->active_tool == cvt_curve || cv->active_tool == cvt_corner ||
4979 	    cv->active_tool == cvt_tangent || cv->active_tool == cvt_hvcurve ||
4980 	    cv->active_tool == cvt_pen || cv->active_tool == cvt_ruler )
4981     {
4982 	/* Snap to points and splines */
4983 	InSplineSet(&fs,cv->b.layerheads[cv->b.drawmode]->splines,cv->b.sc->inspiro && hasspiro());
4984 	if ( fs.p->sp==NULL && fs.p->spline==NULL )
4985 	    CVDoSnaps(cv,&fs);
4986     } else {
4987 	/* Just snap to points */
4988 	NearSplineSetPoints(&fs,cv->b.layerheads[cv->b.drawmode]->splines,cv->b.sc->inspiro && hasspiro());
4989 	if ( fs.p->sp==NULL && fs.p->spline==NULL )
4990 	    CVDoSnaps(cv,&fs);
4991     }
4992 
4993     cv->e.x = event->u.mouse.x; cv->e.y = event->u.mouse.y;
4994     if ( cv->p.sp!=NULL ) {
4995 	BasePoint *p;
4996 	if ( cv->p.nextcp )
4997 	    p = &cv->p.sp->nextcp;
4998 	else if ( cv->p.prevcp )
4999 	    p = &cv->p.sp->prevcp;
5000 	else
5001 	    p = &cv->p.sp->me;
5002 	cv->info.x = p->x;
5003 	cv->info.y = p->y;
5004 	cv->p.cx = p->x; cv->p.cy = p->y;
5005     } else if ( cv->p.spiro!=NULL ) {
5006 	cv->info.x = cv->p.spiro->x;
5007 	cv->info.y = cv->p.spiro->y;
5008 	cv->p.cx = cv->p.spiro->x; cv->p.cy = cv->p.spiro->y;
5009     } else {
5010 	cv->info.x = cv->p.cx;
5011 	cv->info.y = cv->p.cy;
5012     }
5013     cv->info_within = true;
5014     CVInfoDraw(cv,cv->gw);
5015     CVSetConstrainPoint(cv,event);
5016 
5017     switch ( cv->active_tool ) {
5018       case cvt_pointer:
5019 	CVMouseDownPointer(cv, &fs, event);
5020 	CVMaybeCreateDraggingComparisonOutline( cv );
5021 	cv->lastselpt = fs.p->sp;
5022 	cv->lastselcp = fs.p->spiro;
5023       break;
5024       case cvt_magnify: case cvt_minify:
5025           //When scroll zooming, the old showing tool is the normal pointer.
5026           old_showing_tool = cv->active_tool;
5027       break;
5028       case cvt_hand:
5029 	CVMouseDownHand(cv);
5030       break;
5031       case cvt_freehand:
5032 	CVMouseDownFreeHand(cv,event);
5033       break;
5034       case cvt_curve: case cvt_corner: case cvt_tangent: case cvt_pen:
5035       case cvt_hvcurve:
5036 	CVMouseDownPoint(cv,event);
5037       break;
5038       case cvt_ruler:
5039 	CVMouseDownRuler(cv,event);
5040       break;
5041       case cvt_rotate: case cvt_flip: case cvt_scale: case cvt_skew:
5042       case cvt_3d_rotate: case cvt_perspective:
5043 	CVMouseDownTransform(cv);
5044       break;
5045       case cvt_knife:
5046 	CVMouseDownKnife(cv);
5047       break;
5048       case cvt_rect: case cvt_elipse: case cvt_poly: case cvt_star:
5049 	CVMouseDownShape(cv,event);
5050       break;
5051     }
5052     cv->showing_tool = old_showing_tool;
5053 }
5054 
_SCHintsChanged(SplineChar * sc)5055 static void _SCHintsChanged(SplineChar *sc) {
5056     struct splinecharlist *dlist;
5057 
5058     if ( !sc->changedsincelasthinted ) {
5059 	sc->changedsincelasthinted = true;
5060 	FVMarkHintsOutOfDate(sc);
5061     }
5062 
5063     for ( dlist=sc->dependents; dlist!=NULL; dlist=dlist->next )
5064 	_SCHintsChanged(dlist->sc);
5065 }
5066 
SC_HintsChanged(SplineChar * sc)5067 static void SC_HintsChanged(SplineChar *sc) {
5068     struct splinecharlist *dlist;
5069     int was = sc->changedsincelasthinted;
5070 
5071     if ( sc->parent->onlybitmaps || sc->parent->multilayer || sc->parent->strokedfont )
5072 return;
5073     sc->changedsincelasthinted = false;		/* We just applied a hinting change */
5074     if ( !sc->changed ) {
5075 	sc->changed = true;
5076 	FVToggleCharChanged(sc);
5077 	SCRefreshTitles(sc);
5078 	if ( !sc->parent->changed ) {
5079 	    sc->parent->changed = true;
5080 	    FVSetTitles(sc->parent);
5081 	}
5082     }
5083     for ( dlist=sc->dependents; dlist!=NULL; dlist=dlist->next )
5084 	_SCHintsChanged(dlist->sc);
5085     if ( was ) {
5086 	FontView *fvs;
5087 	for ( fvs = (FontView *) (sc->parent->fv); fvs!=NULL; fvs=(FontView *) (fvs->b.nextsame) )
5088 	    GDrawRequestExpose(fvs->v,NULL,false);
5089     }
5090 }
5091 
CVSetCharChanged(CharView * cv,int changed)5092 void CVSetCharChanged(CharView *cv,int changed) {
5093     SplineFont *sf = cv->b.fv->sf;
5094     SplineChar *sc = cv->b.sc;
5095     int oldchanged = sf->changed;
5096     /* A changed argument of 2 means the outline didn't change, but something */
5097     /*  else (width, anchorpoint) did */
5098     int cvlayer = CVLayer((CharViewBase *) cv);
5099 
5100     if ( changed )
5101 	SFSetModTime(sf);
5102     if ( cv->b.drawmode==dm_grid ) {
5103 	if ( changed ) {
5104 	    sf->changed = true;
5105 	    if ( sf->cidmaster!=NULL )
5106 		sf->cidmaster->changed = true;
5107 	}
5108     } else {
5109 	if ( cv->b.drawmode==dm_fore && changed==1 ) {
5110 	    sf->onlybitmaps = false;
5111 	}
5112 	SCTickValidationState(cv->b.sc,cvlayer);
5113 	if ( (sc->changed==0) != (changed==0) ) {
5114 	    sc->changed = (changed!=0);
5115 	    FVToggleCharChanged(sc);
5116 	    SCRefreshTitles(sc);
5117 	    if ( changed ) {
5118 		sf->changed = true;
5119 		if ( sf->cidmaster!=NULL )
5120 		    sf->cidmaster->changed = true;
5121 	    }
5122 	}
5123 	if ( changed==1 ) {
5124 	    instrcheck(sc,cvlayer);
5125 	    /*SCDeGridFit(sc);*/
5126 	    if ( sc->parent->onlybitmaps )
5127 		/* Do nothing */;
5128 	    else if ( sc->parent->multilayer || sc->parent->strokedfont || sc->layers[cvlayer].order2 )
5129 		sc->changed_since_search = true;
5130 	    else if ( cv->b.drawmode==dm_fore ) {
5131 		sc->changed_since_search = true;
5132 		_SCHintsChanged(cv->b.sc);
5133 	    }
5134 	    sc->changed_since_autosave = true;
5135 	    sf->changed_since_autosave = true;
5136 	    sf->changed_since_xuidchanged = true;
5137 	    if ( sf->cidmaster!=NULL ) {
5138 		sf->cidmaster->changed_since_autosave = true;
5139 		sf->cidmaster->changed_since_xuidchanged = true;
5140 	    }
5141 	}
5142 	if ( cv->b.drawmode!=dm_grid ) {
5143 	    cv->needsrasterize = true;
5144 	}
5145     }
5146     cv->recentchange = true;
5147     if ( !oldchanged )
5148 	FVSetTitles(sf);
5149 }
5150 
SCClearSelPt(SplineChar * sc)5151 void SCClearSelPt(SplineChar *sc) {
5152     CharView *cv;
5153 
5154     for ( cv=(CharView *) (sc->views); cv!=NULL; cv=(CharView *) (cv->b.next) ) {
5155 	cv->lastselpt = cv->p.sp = NULL;
5156 	cv->p.spiro = cv->lastselcp = NULL;
5157     }
5158 }
5159 
_SC_CharChangedUpdate(SplineChar * sc,int layer,int changed)5160 static void _SC_CharChangedUpdate(SplineChar *sc,int layer,int changed) {
5161     SplineFont *sf = sc->parent;
5162     extern int updateflex;
5163     /* layer might be ly_none or ly_all */
5164 
5165     if ( layer>=sc->layer_cnt ) {
5166 	IError( "Bad layer in _SC_CharChangedUpdate");
5167 	layer = ly_fore;
5168     }
5169     if ( layer>=0 && !sc->layers[layer].background )
5170 	TTFPointMatches(sc,layer,true);
5171     if ( changed != -1 ) {
5172 	sc->changed_since_autosave = true;
5173 	SFSetModTime(sf);
5174 	if ( (sc->changed==0) != (changed==0) ) {
5175 	    sc->changed = (changed!=0);
5176 	    if ( changed && layer>=ly_fore && (sc->layers[layer].splines!=NULL || sc->layers[layer].refs!=NULL))
5177 		sc->parent->onlybitmaps = false;
5178 	    FVToggleCharChanged(sc);
5179 	    SCRefreshTitles(sc);
5180 	}
5181 	if ( !sf->changed ) {
5182 	    sf->changed = true;
5183 	    if ( sf->cidmaster )
5184 		sf->cidmaster->changed = true;
5185 	    FVSetTitles(sf);
5186 	}
5187 	if ( changed && layer>=0 && !sc->layers[layer].background && sc->layers[layer].order2 ) {
5188 	    instrcheck(sc,layer);
5189 	    SCReGridFit(sc,layer);
5190 	}
5191 	if ( !sc->parent->onlybitmaps && !sc->parent->multilayer &&
5192 		changed==1 && !sc->parent->strokedfont &&
5193 		layer>=0 &&
5194 		!sc->layers[layer].background && !sc->layers[layer].order2 )
5195 	    _SCHintsChanged(sc);
5196 	sc->changed_since_search = true;
5197 	sf->changed = true;
5198 	sf->changed_since_autosave = true;
5199 	sf->changed_since_xuidchanged = true;
5200 	if ( layer>=0 )
5201 	    SCTickValidationState(sc,layer);
5202     }
5203     if ( sf->cidmaster!=NULL )
5204 	sf->cidmaster->changed = sf->cidmaster->changed_since_autosave =
5205 		sf->cidmaster->changed_since_xuidchanged = true;
5206     SCRegenDependents(sc,ly_all);	/* All chars linked to this one need to get the new splines */
5207     if ( updateflex && (CharView *) (sc->views)!=NULL && layer>=ly_fore )
5208 	SplineCharIsFlexible(sc,layer);
5209     SCUpdateAll(sc);
5210     SCLayersChange(sc);
5211     SCRegenFills(sc);
5212 }
5213 
SC_CharChangedUpdate(SplineChar * sc,int layer)5214 static void SC_CharChangedUpdate(SplineChar *sc,int layer) {
5215     _SC_CharChangedUpdate(sc,layer,true);
5216 }
5217 
_CV_CharChangedUpdate(CharView * cv,int changed)5218 static void _CV_CharChangedUpdate(CharView *cv,int changed) {
5219     extern int updateflex;
5220     FontView *fv;
5221     int cvlayer = CVLayer((CharViewBase *) cv);
5222 
5223     CVSetCharChanged(cv,changed);
5224     CVLayerChange(cv);
5225     if ( cv->needsrasterize ) {
5226 	TTFPointMatches(cv->b.sc,cvlayer,true);		/* Must precede regen dependents, as this can change references */
5227 	SCRegenDependents(cv->b.sc,cvlayer);		/* All chars linked to this one need to get the new splines */
5228 	if ( cv->b.layerheads[cv->b.drawmode]->order2 )
5229 	    SCReGridFit(cv->b.sc,cvlayer);
5230 	if ( updateflex && cvlayer!=ly_grid && !cv->b.layerheads[cv->b.drawmode]->background )
5231 	    SplineCharIsFlexible(cv->b.sc,cvlayer);
5232 	SCUpdateAll(cv->b.sc);
5233 	SCRegenFills(cv->b.sc);
5234 	for ( fv = (FontView *) (cv->b.sc->parent->fv); fv!=NULL; fv=(FontView *) (fv->b.nextsame) )
5235 	    FVRegenChar(fv,cv->b.sc);
5236 	cv->needsrasterize = false;
5237     } else if ( cv->b.drawmode!=dm_grid ) {
5238 	/* If we changed the background then only views of this character */
5239 	/*  need to know about it. No dependents needed, but why write */
5240 	/*  another routine for a rare case... */
5241 	SCUpdateAll(cv->b.sc);
5242     } else /* if ( cv->b.drawmode==dm_grid )*/ {
5243 	/* If we changed the grid then any character needs to know it */
5244 	FVRedrawAllCharViewsSF(cv->b.sc->parent);
5245     }
5246     if ( cv->showpointnumbers || cv->show_ft_results )
5247 	SCNumberPoints(cv->b.sc, cvlayer);
5248     cv->recentchange = false;
5249     cv->p.sp = NULL;		/* Might have been deleted */
5250 }
5251 
CV_CharChangedUpdate(CharView * cv)5252 static void CV_CharChangedUpdate(CharView *cv) {
5253     _CV_CharChangedUpdate(cv,true);
5254 }
5255 
CVMouseMove(CharView * cv,GEvent * event)5256 static void CVMouseMove(CharView *cv, GEvent *event ) {
5257     CharViewTab* tab = CVGetActiveTab(cv);
5258     real cx, cy;
5259     PressedOn p;
5260     FindSel fs;
5261     GEvent fake;
5262     int stop_motion = false;
5263     int has_spiro = hasspiro();
5264 
5265 		/* Debug wacom !!!! */
5266  /* TRACE( "dev=%s (%d,%d) 0x%x\n", event->u.mouse.device!=NULL?event->u.mouse.device:"<None>", */
5267  /*     event->u.mouse.x, event->u.mouse.y, event->u.mouse.state); */
5268 
5269     if ( event->u.mouse.device!=NULL )
5270 	CVToolsSetCursor(cv,event->u.mouse.state,event->u.mouse.device);
5271 
5272     if ( !cv->p.pressed ) {
5273 	CVUpdateInfo(cv, event);
5274 	if ( cv->showing_tool==cvt_pointer ) {
5275 	    CVCheckResizeCursors(cv);
5276 	    if ( cv->dv!=NULL )
5277 		CVDebugPointPopup(cv);
5278 	} else if ( cv->showing_tool == cvt_ruler )
5279 	    CVMouseMoveRuler(cv,event);
5280 return;
5281     }
5282 
5283     //GDrawRequestExpose(cv->v,NULL,false);	/* TBD, hack to clear ruler */
5284 
5285     SetFS(&fs,&p,cv,event);
5286     if ( cv->active_tool == cvt_freehand )
5287 	/* freehand does it's own kind of constraining */;
5288     else if ( (event->u.mouse.state&ksm_shift) && !cv->p.rubberbanding ) {
5289 	/* Constrained */
5290 
5291 	fake.u.mouse = event->u.mouse;
5292 	if ( ((event->u.mouse.state&ksm_meta) ||
5293 		    (!cv->cntrldown && (event->u.mouse.state&ksm_control))) &&
5294 		(cv->p.nextcp || cv->p.prevcp)) {
5295 	    real dot = (cv->p.cp.x-cv->p.constrain.x)*(p.cx-cv->p.constrain.x) +
5296 		    (cv->p.cp.y-cv->p.constrain.y)*(p.cy-cv->p.constrain.y);
5297 	    real len = (cv->p.cp.x-cv->p.constrain.x)*(cv->p.cp.x-cv->p.constrain.x)+
5298 		    (cv->p.cp.y-cv->p.constrain.y)*(cv->p.cp.y-cv->p.constrain.y);
5299 	    if ( len!=0 ) {
5300 		dot /= len;
5301 		/* constrain control point to same angle with respect to base point*/
5302 		if ( dot<0 ) dot = 0;
5303 		p.cx = cv->p.constrain.x + dot*(cv->p.cp.x-cv->p.constrain.x);
5304 		p.cy = cv->p.constrain.y + dot*(cv->p.cp.y-cv->p.constrain.y);
5305 		p.x = fake.u.mouse.x = tab->xoff + rint(p.cx*tab->scale);
5306 		p.y = fake.u.mouse.y = -tab->yoff + cv->height - rint(p.cy*tab->scale);
5307 	    }
5308 	} else {
5309 	    /* Constrain mouse to hor/vert/45 from base point */
5310 	    int basex = cv->active_tool!=cvt_hand ? tab->xoff + rint(cv->p.constrain.x*tab->scale) : cv->p.x;
5311 	    int basey = cv->active_tool!=cvt_hand ?-tab->yoff + cv->height - rint(cv->p.constrain.y*tab->scale) : cv->p.y;
5312 	    int dx= event->u.mouse.x-basex, dy = event->u.mouse.y-basey;
5313 	    int sign = dx*dy<0?-1:1;
5314 	    double aspect = 1.0;
5315 
5316 	    if ( dx<0 ) dx = -dx; if ( dy<0 ) dy = -dy;
5317 	    if ( cv->p.img!=NULL && cv->p.img->bb.minx!=cv->p.img->bb.maxx )
5318 		aspect = (cv->p.img->bb.maxy - cv->p.img->bb.miny) / (cv->p.img->bb.maxx - cv->p.img->bb.minx);
5319 	    else if ( cv->p.ref!=NULL && cv->p.ref->bb.minx!=cv->p.ref->bb.maxx )
5320 		aspect = (cv->p.ref->bb.maxy - cv->p.ref->bb.miny) / (cv->p.ref->bb.maxx - cv->p.ref->bb.minx);
5321 	    if ( dy >= 2*dx ) {
5322 		p.x = fake.u.mouse.x = basex;
5323 		p.cx = cv->p.constrain.x;
5324 		if ( ItalicConstrained && cv->b.sc->parent->italicangle!=0 ) {
5325 		    double off = tan(cv->b.sc->parent->italicangle*FF_PI/180)*
5326 			    (p.cy-cv->p.constrain.y);
5327 		    double aoff = off<0 ? -off : off;
5328 		    if ( dx>=aoff*tab->scale/2 && (event->u.mouse.x-basex<0)!=(off<0) ) {
5329 			p.cx -= off;
5330 			p.x = fake.u.mouse.x = tab->xoff + rint(p.cx*tab->scale);
5331 		    }
5332 		}
5333 	    } else if ( dx >= 2*dy ) {
5334 		p.y = fake.u.mouse.y = basey;
5335 		p.cy = cv->p.constrain.y;
5336 	    } else if ( dx > dy ) {
5337 		p.x = fake.u.mouse.x = basex + sign * (event->u.mouse.y-basey)/aspect;
5338 		p.cx = cv->p.constrain.x - sign * (p.cy-cv->p.constrain.y)/aspect;
5339 	    } else {
5340 		p.y = fake.u.mouse.y = basey + sign * (event->u.mouse.x-basex)*aspect;
5341 		p.cy = cv->p.constrain.y - sign * (p.cx-cv->p.constrain.x)*aspect;
5342 	    }
5343 	}
5344 	event = &fake;
5345     }
5346 
5347     /* If we've changed the character (recentchange is true) we want to */
5348     /*  snap to the original location, otherwise we'll keep snapping to the */
5349     /*  current point as it moves across the screen (jerkily) */
5350     if ( cv->active_tool == cvt_hand || cv->active_tool == cvt_freehand )
5351 	/* Don't snap to points */;
5352     else if ( !cv->joinvalid ||
5353 	    ((!cv->b.sc->inspiro || has_spiro) && !CheckPoint(&fs,&cv->joinpos,NULL)) ||
5354 	    (  cv->b.sc->inspiro && has_spiro  && !CheckSpiroPoint(&fs,&cv->joincp,NULL,0))) {
5355 	SplinePointList *spl;
5356 	spl = cv->b.layerheads[cv->b.drawmode]->splines;
5357 	if ( cv->recentchange && cv->active_tool==cvt_pointer &&
5358 		cv->b.layerheads[cv->b.drawmode]->undoes!=NULL &&
5359 		(cv->b.layerheads[cv->b.drawmode]->undoes->undotype==ut_state ||
5360 		 cv->b.layerheads[cv->b.drawmode]->undoes->undotype==ut_tstate ))
5361 	    spl = cv->b.layerheads[cv->b.drawmode]->undoes->u.state.splines;
5362 	if ( cv->active_tool != cvt_knife && cv->active_tool != cvt_ruler ) {
5363 	    if ( cv->active_tool == cvt_pointer && ( cv->p.nextcp || cv->p.prevcp ))
5364 		fs.select_controls = true;
5365 	    NearSplineSetPoints(&fs,spl,cv->b.sc->inspiro && has_spiro);
5366 	} else
5367 	    InSplineSet(&fs,spl,cv->b.sc->inspiro && has_spiro);
5368     }
5369     /* p.sp and cv->p.sp may correspond to different undo states, thus being */
5370     /* different objects even while describing essentially the same point. */
5371     /* So compare point coordinates rather than the points themselves */
5372     if ( (cv->p.nextcp || cv->p.prevcp) && p.nextcp &&
5373 	    p.sp!=NULL && cv->p.sp != NULL &&
5374 	    p.sp->me.x == cv->p.sp->me.x && p.sp->me.y == cv->p.sp->me.y ) {
5375 	/* If either control point selected, then snap to it or its brother */
5376 	/*  when close */
5377 	p.cx = p.sp->nextcp.x;
5378 	p.cy = p.sp->nextcp.y;
5379     } else if (( cv->p.nextcp || cv->p.prevcp) && p.prevcp &&
5380 	    p.sp!=NULL && cv->p.sp != NULL &&
5381 	    p.sp->me.x == cv->p.sp->me.x && p.sp->me.y == cv->p.sp->me.y ) {
5382 	p.cx = p.sp->prevcp.x;
5383 	p.cy = p.sp->prevcp.y;
5384     } else if ( p.sp!=NULL && p.sp!=cv->active_sp ) {		/* Snap to points */
5385 	p.cx = p.sp->me.x;
5386 	p.cy = p.sp->me.y;
5387     } else if ( p.spiro!=NULL && p.spiro!=cv->active_cp ) {
5388 	p.cx = p.spiro->x;
5389 	p.cy = p.spiro->y;
5390     } else {
5391 	CVDoSnaps(cv,&fs);
5392     }
5393     cx = (p.cx -cv->p.cx) / tab->scale;
5394     cy = (p.cy - cv->p.cy) / tab->scale;
5395     if ( cx<0 ) cx = -cx;
5396     if ( cy<0 ) cy = -cy;
5397 
5398 	/* If they haven't moved far from the start point, then snap to it */
5399     if ( cx+cy < 4 ) {
5400 	p.x = cv->p.x; p.y = cv->p.y;
5401     }
5402 
5403     cv->info.x = p.cx; cv->info.y = p.cy;
5404     cv->info_sp = p.sp;
5405     cv->info_spline = p.spline;
5406     cv->info_t = p.t;
5407     cv->e.x = event->u.mouse.x; cv->e.y = event->u.mouse.y;
5408     CVInfoDraw(cv,cv->gw);
5409 
5410     switch ( cv->active_tool ) {
5411       case cvt_pointer:
5412 	stop_motion = CVMouseMovePointer(cv,event);
5413       break;
5414       case cvt_magnify: case cvt_minify:
5415 	if ( !cv->p.rubberbanding ) {
5416 	    cv->p.ex = cv->p.cx;
5417 	    cv->p.ey = cv->p.cy;
5418 	}
5419 	cv->p.ex = cv->info.x;
5420 	cv->p.ey = cv->info.y;
5421 	cv->p.rubberbanding = true;
5422 	GDrawRequestExpose(cv->v, NULL, false);
5423       break;
5424       case cvt_hand:
5425 	CVMouseMoveHand(cv,event);
5426       break;
5427       case cvt_freehand:
5428 	CVMouseMoveFreeHand(cv,event);
5429       break;
5430       case cvt_curve: case cvt_corner: case cvt_tangent: case cvt_hvcurve:
5431 	CVMouseMovePoint(cv,&p);
5432       break;
5433       case cvt_pen:
5434 	CVMouseMovePen(cv,&p,event);
5435       break;
5436       case cvt_ruler:
5437 	CVMouseMoveRuler(cv,event);
5438       break;
5439       case cvt_rotate: case cvt_flip: case cvt_scale: case cvt_skew:
5440       case cvt_3d_rotate: case cvt_perspective:
5441 	CVMouseMoveTransform(cv);
5442       break;
5443       case cvt_knife:
5444 	CVMouseMoveKnife(cv,&p);
5445       break;
5446       case cvt_rect: case cvt_elipse: case cvt_poly: case cvt_star:
5447 	CVMouseMoveShape(cv);
5448       break;
5449     }
5450     if ( stop_motion ) {
5451 	event->type = et_mouseup;
5452 	CVMouseUp(cv,event);
5453     }
5454 }
5455 
CVMagnify(CharView * cv,real midx,real midy,int bigger,int LockPosition)5456 static void CVMagnify(CharView *cv, real midx, real midy, int bigger, int LockPosition) {
5457     CharViewTab* tab = CVGetActiveTab(cv);
5458     static float scales[] = { 1, 2, 3, 4, 6, 8, 11, 16, 23, 32, 45, 64, 90, 128, 181, 256, 512, 1024, 0 };
5459     float oldscale;
5460     int i, j;
5461 
5462     oldscale = tab->scale;
5463 
5464     if ( bigger!=0 ) {
5465 	if ( tab->scale>=1 ) {
5466 	    for ( i=0; scales[i]!=0 && tab->scale>scales[i]; ++i );
5467 	    if ( scales[i]==0 ) i=j= i-1;
5468 	    else if ( RealNear(scales[i],tab->scale) ) j=i;
5469 	    else if ( i!=0 && RealNear(scales[i-1],tab->scale) ) j= --i; /* Round errors! */
5470 	    else j = i-1;
5471 	} else { real sinv = 1/tab->scale; int t;
5472 	    for ( i=0; scales[i]!=0 && sinv>scales[i]; ++i );
5473 	    if ( scales[i]==0 ) i=j= i-1;
5474 	    else if ( RealNear(scales[i],sinv) ) j=i;
5475 	    else if ( i!=0 && RealNear(scales[i-1],sinv) ) j= --i; /* Round errors! */
5476 	    else j = i-1;
5477 	    t = j;
5478 	    j = -i; i = -t;
5479 	}
5480 	if ( bigger==1 ) {
5481 	    if ( i==j ) ++i;
5482 	    if ( i>0 && scales[i]==0 ) --i;
5483 	    if ( i>=0 )
5484 		tab->scale = scales[i];
5485 	    else
5486 		tab->scale = 1/scales[-i];
5487 	} else {
5488 	    if ( i==j ) --j;
5489 	    if ( j<0 && scales[-j]==0 ) ++j;
5490 	    if ( j>=0 )
5491 		tab->scale = scales[j];
5492 	    else
5493 		tab->scale = 1/scales[-j];
5494 	}
5495     }
5496 
5497     if (LockPosition) {
5498 	float mousex = rint(midx * oldscale + tab->xoff);
5499 	float mousey = rint(midy * oldscale + tab->yoff - cv->height);
5500 	tab->xoff = mousex - midx*tab->scale;
5501 	tab->yoff = mousey - midy*tab->scale + cv->height;
5502     }
5503     else {
5504         tab->xoff = -(rint(midx*tab->scale) - cv->width/2);
5505         tab->yoff = -(rint(midy*tab->scale) - cv->height/2);
5506     }
5507 
5508     CVNewScale(cv);
5509 }
5510 
CVMouseUp(CharView * cv,GEvent * event)5511 static void CVMouseUp(CharView *cv, GEvent *event ) {
5512     CharViewTab* tab = CVGetActiveTab(cv);
5513 
5514     CVMouseMove(cv,event);
5515     if ( cv->pressed!=NULL ) {
5516 	GDrawCancelTimer(cv->pressed);
5517 	cv->pressed = NULL;
5518     }
5519     cv->p.pressed = false;
5520     SplinePointListFree(cv->p.pretransform_spl);
5521     cv->p.pretransform_spl = NULL;
5522     update_spacebar_hand_tool(cv);
5523 
5524     if ( cv->p.rubberbanding ) {
5525 	cv->p.rubberbanding = false;
5526     } else if ( cv->p.rubberlining ) {
5527 	cv->p.rubberlining = false;
5528     }
5529 
5530     // This is needed to allow characters to the left of the
5531     // active one to be picked with the mouse,
5532     // but outright it does mess with keyboard input changing BCP
5533     // so we only do it for mouse up to the left of the left side
5534     // bearing, because that click can not currently activate any BCP
5535     if ( cv->active_tool == cvt_pointer &&
5536 	 MouseToCX( cv, event->u.mouse.x ) < -2 )
5537     {
5538 	// Since we allow clicking anywhere now, instead of having
5539 	// to check if you clicked on a spline of a prev char,
5540 	// then we don't need this. It also causes an issue with the arrow
5541 	// keys moving a BCP on a spline left of the lbearing line.
5542 	// (comment included just in case this click on spline feature is desired in the future)
5543 	/* FindSel fs; */
5544 	/* SetFS(&fs,&cv->p,cv,event); */
5545 	/* _CVTestSelectFromEvent(cv,&fs); */
5546 	/* fs.p = &cv->p; */
5547     }
5548 
5549 
5550     switch ( cv->active_tool ) {
5551       case cvt_pointer:
5552 	CVMouseUpPointer(cv);
5553       break;
5554       case cvt_ruler:
5555 	CVMouseUpRuler(cv,event);
5556       break;
5557       case cvt_hand:
5558 	CVMouseUpHand(cv);
5559       break;
5560       case cvt_freehand:
5561 	CVMouseUpFreeHand(cv,event);
5562       break;
5563       case cvt_curve: case cvt_corner: case cvt_tangent: case cvt_hvcurve:
5564       case cvt_pen:
5565 	CVMouseUpPoint(cv,event);
5566 	CVGridHandlePossibleFitChar( cv );
5567       break;
5568       case cvt_magnify: case cvt_minify:
5569 	if ( cv->p.x>=event->u.mouse.x-6 && cv->p.x<=event->u.mouse.x+6 &&
5570 		 cv->p.y>=event->u.mouse.y-6 && cv->p.y<=event->u.mouse.y+6 ) {
5571 	    real cx, cy;
5572 	    cx = (event->u.mouse.x-tab->xoff)/tab->scale;
5573 	    cy = (cv->height-event->u.mouse.y-tab->yoff)/tab->scale ;
5574 	    CVMagnify(cv,cx,cy,cv->active_tool==cvt_minify?-1:1,event->u.mouse.button>3);
5575         } else {
5576 	    DBounds b;
5577 	    double oldscale = tab->scale;
5578 	    if ( cv->p.cx>cv->info.x ) {
5579 		b.minx = cv->info.x;
5580 		b.maxx = cv->p.cx;
5581 	    } else {
5582 		b.minx = cv->p.cx;
5583 		b.maxx = cv->info.x;
5584 	    }
5585 	    if ( cv->p.cy>cv->info.y ) {
5586 		b.miny = cv->info.y;
5587 		b.maxy = cv->p.cy;
5588 	    } else {
5589 		b.miny = cv->p.cy;
5590 		b.maxy = cv->info.y;
5591 	    }
5592 	    _CVFit(cv,&b,false);
5593 	    if ( oldscale==tab->scale ) {
5594 		tab->scale += .5;
5595 		CVNewScale(cv);
5596 	    }
5597 	}
5598       break;
5599       case cvt_rotate: case cvt_flip: case cvt_scale: case cvt_skew:
5600       case cvt_3d_rotate: case cvt_perspective:
5601 	CVMouseUpTransform(cv);
5602       break;
5603       case cvt_knife:
5604 	CVMouseUpKnife(cv,event);
5605       break;
5606       case cvt_rect: case cvt_elipse: case cvt_poly: case cvt_star:
5607 	CVMouseUpShape(cv);
5608 	CVGridHandlePossibleFitChar( cv );
5609       break;
5610     }
5611     cv->active_tool = cvt_none;
5612     CVToolsSetCursor(cv,event->u.mouse.state&~(1<<(7+event->u.mouse.button)),event->u.mouse.device);		/* X still has the buttons set in the state, even though we just released them. I don't want em */
5613     /* CharChangedUpdate is a rather blunt tool. When moving anchor points with */
5614     /*  the mouse we need finer control than it provides */
5615     /* If recentchange is set then change should also be set, don't think we */
5616     /*  need the full form of this call */
5617     if ( cv->needsrasterize || cv->recentchange )
5618 	_CV_CharChangedUpdate(cv,2);
5619 
5620     dlist_foreach( &cv->pointInfoDialogs, (dlist_foreach_func_type)PIChangePoint );
5621 }
5622 
CVTimer(CharView * cv,GEvent * event)5623 static void CVTimer(CharView *cv,GEvent *event) {
5624     CharViewTab* tab = CVGetActiveTab(cv);
5625 
5626     if ( event->u.timer.timer==cv->pressed ) {
5627 	GEvent e;
5628 	GDrawGetPointerPosition(cv->v,&e);
5629 	if ( e.u.mouse.x<0 || e.u.mouse.y<0 ||
5630 		e.u.mouse.x>=cv->width || e.u.mouse.y >= cv->height ) {
5631 	    real dx = 0, dy = 0;
5632 	    if ( e.u.mouse.x<0 )
5633 		dx = cv->width/8;
5634 	    else if ( e.u.mouse.x>=cv->width )
5635 		dx = -cv->width/8;
5636 	    if ( e.u.mouse.y<0 )
5637 		dy = -cv->height/8;
5638 	    else if ( e.u.mouse.y>=cv->height )
5639 		dy = cv->height/8;
5640 	    tab->xoff += dx; tab->yoff += dy;
5641 	    cv->back_img_out_of_date = true;
5642 	    if ( dy!=0 )
5643 		GScrollBarSetPos(cv->vsb,tab->yoff-cv->height);
5644 	    if ( dx!=0 )
5645 		GScrollBarSetPos(cv->hsb,-tab->xoff);
5646 	    GDrawRequestExpose(cv->v,NULL,false);
5647 	}
5648 #if _ModKeysAutoRepeat
5649 	/* Under cygwin the modifier keys auto repeat, they don't under normal X */
5650     } else if ( cv->autorpt==event->u.timer.timer ) {
5651 	cv->autorpt = NULL;
5652 	CVToolsSetCursor(cv,cv->oldstate,NULL);
5653 	if ( cv->keysym == GK_Shift_L || cv->keysym == GK_Shift_R ||
5654 		 cv->keysym == GK_Alt_L || cv->keysym == GK_Alt_R ||
5655 		 cv->keysym == GK_Meta_L || cv->keysym == GK_Meta_R ) {
5656 	    GEvent e;
5657 	    e.w = cv->oldkeyw;
5658 	    e.u.chr.keysym = cv->keysym;
5659 	    e.u.chr.x = cv->oldkeyx;
5660 	    e.u.chr.y = cv->oldkeyy;
5661 	    CVFakeMove(cv,&e);
5662 	}
5663 #endif
5664     }
5665 }
5666 
CVDrop(CharView * cv,GEvent * event)5667 static void CVDrop(CharView *cv,GEvent *event) {
5668     /* We should get a list of character names. Add each as a RefChar */
5669     int32 len;
5670     int ch, first = true;
5671     char *start, *pt, *cnames;
5672     SplineChar *rsc;
5673     RefChar *new;
5674     int layer = CVLayer((CharViewBase *) cv);
5675 
5676     if ( cv->b.drawmode==dm_grid ) {
5677 	ff_post_error(_("Not Guides"),_("References may not be dragged into the guidelines layer"));
5678 return;
5679     }
5680     if ( !GDrawSelectionHasType(cv->gw,sn_drag_and_drop,"STRING"))
5681 return;
5682     cnames = GDrawRequestSelection(cv->gw,sn_drag_and_drop,"STRING",&len);
5683     if ( cnames==NULL )
5684 return;
5685 
5686     start = cnames;
5687     while ( *start ) {
5688 	while ( *start==' ' ) ++start;
5689 	if ( *start=='\0' )
5690     break;
5691 	for ( pt=start; *pt && *pt!=' '; ++pt );
5692 	ch = *pt; *pt = '\0';
5693 	if ( (rsc=SFGetChar(cv->b.sc->parent,-1,start))!=NULL && rsc!=cv->b.sc ) {
5694 	    if ( first ) {
5695 		CVPreserveState(&cv->b);
5696 		first =false;
5697 	    }
5698 	    new = RefCharCreate();
5699 	    new->transform[0] = new->transform[3] = 1.0;
5700 	    new->layers[0].splines = NULL;
5701 	    new->sc = rsc;
5702 	    new->next = cv->b.sc->layers[layer].refs;
5703 	    cv->b.sc->layers[layer].refs = new;
5704 	    SCReinstanciateRefChar(cv->b.sc,new,layer);
5705 	    SCMakeDependent(cv->b.sc,rsc);
5706 	}
5707 	*pt = ch;
5708 	start = pt;
5709     }
5710 
5711     free(cnames);
5712     CVCharChangedUpdate(&cv->b);
5713 }
5714 
v_e_h(GWindow gw,GEvent * event)5715 static int v_e_h(GWindow gw, GEvent *event) {
5716     CharView *cv = (CharView *) GDrawGetUserData(gw);
5717 
5718     GGadgetPopupExternalEvent(event);
5719     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
5720 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7 ) ) {
5721 	if ( !(event->u.mouse.state&(ksm_control)) )	/* bind control to magnify/minify */
5722 	{
5723 	    int ish = event->u.mouse.button>5;
5724 	    if ( event->u.mouse.state&ksm_shift ) ish = !ish;
5725 	    if ( ish ) /* bind shift to vertical scrolling */
5726 return( GGadgetDispatchEvent(cv->hsb,event));
5727 	    else
5728 return( GGadgetDispatchEvent(cv->vsb,event));
5729 	}
5730     }
5731 
5732     switch ( event->type ) {
5733       case et_expose:
5734 	GDrawSetLineWidth(gw,0);
5735 	CVExpose(cv,gw,event);
5736       break;
5737       case et_crossing:
5738 	CVCrossing(cv,event);
5739       break;
5740       case et_mousedown:
5741 	CVPaletteActivate(cv);
5742 	if ( cv->gic!=NULL )
5743 	    GDrawSetGIC(gw,cv->gic,0,20);
5744 	if ( cv->inactive )
5745 	    (cv->b.container->funcs->activateMe)(cv->b.container,&cv->b);
5746 	CVMouseDown(cv,event);
5747       break;
5748       case et_mousemove:
5749 	if ( cv->p.pressed || cv->spacebar_hold)
5750 	    GDrawSkipMouseMoveEvents(cv->v,event);
5751 	CVMouseMove(cv,event);
5752       break;
5753       case et_mouseup:
5754 	CVMouseUp(cv,event);
5755       break;
5756       case et_char:
5757 	if ( cv->b.container!=NULL )
5758 	    (cv->b.container->funcs->charEvent)(cv->b.container,event);
5759 	else
5760 	    CVChar(cv,event);
5761       break;
5762       case et_charup:
5763 	CVCharUp(cv,event);
5764       break;
5765       case et_timer:
5766 	CVTimer(cv,event);
5767       break;
5768       case et_drop:
5769 	CVDrop(cv,event);
5770       break;
5771       case et_focus:
5772 	if ( event->u.focus.gained_focus ) {
5773 	    if ( cv->gic!=NULL )
5774 		GDrawSetGIC(gw,cv->gic,0,20);
5775 	}
5776       break;
5777     }
5778 return( true );
5779 }
5780 
CVDrawNum(CharView * UNUSED (cv),GWindow pixmap,int x,int y,char * format,real val,int align)5781 static void CVDrawNum(CharView *UNUSED(cv),GWindow pixmap,int x, int y, char *format,real val, int align) {
5782     char buffer[40];
5783     int len;
5784 
5785     if ( val==0 ) val=0;		/* avoid -0 */
5786     sprintf(buffer,format,(double)val); /* formats are given as for doubles */
5787     if ( align!=0 ) {
5788 	len = GDrawGetText8Width(pixmap,buffer,-1);
5789 	if ( align==1 )
5790 	    x-=len/2;
5791 	else
5792 	    x-=len;
5793     }
5794     GDrawDrawText8(pixmap,x,y,buffer,-1,GDrawGetDefaultForeground(NULL));
5795 }
5796 
CVDrawVNum(CharView * cv,GWindow pixmap,int x,int y,char * format,real val,int align)5797 static void CVDrawVNum(CharView *cv,GWindow pixmap,int x, int y, char *format,real val, int align) {
5798     char buffer[40], *pt;
5799     int len;
5800 
5801     if ( val==0 ) val=0;		/* avoid -0 */
5802     sprintf(buffer,format,(double)val); /* formats are given as for doubles */
5803     if ( align!=0 ) {
5804 	len = strlen(buffer)*cv->sfh;
5805 	if ( align==1 )
5806 	    y-=len/2;
5807 	else
5808 	    y-=len;
5809     }
5810     for ( pt=buffer; *pt; ++pt ) {
5811 	GDrawDrawText8(pixmap,x,y,pt,1,GDrawGetDefaultForeground(NULL));
5812 	y += cv->sdh;
5813     }
5814 }
5815 
5816 
CVExposeRulers(CharView * cv,GWindow pixmap)5817 static void CVExposeRulers(CharView *cv, GWindow pixmap ) {
5818     CharViewTab* tab = CVGetActiveTab(cv);
5819     real xmin, xmax, ymin, ymax;
5820     real onehundred, pos;
5821     real units, littleunits;
5822     int ybase = cv->mbh+cv->charselectorh+cv->infoh;
5823     int x,y;
5824     GRect rect;
5825     Color def_fg = GDrawGetDefaultForeground(NULL);
5826 
5827     xmin = -tab->xoff/tab->scale;
5828     xmax = (cv->width-tab->xoff)/tab->scale;
5829     ymin = -tab->yoff/tab->scale;
5830     ymax = (cv->height-tab->yoff)/tab->scale;
5831     onehundred = 100/tab->scale;
5832 
5833     if ( onehundred<5 ) {
5834 	units = 1; littleunits=0;
5835     } else if ( onehundred<10 ) {
5836 	units = 5; littleunits=1;
5837     } else if ( onehundred<50 ) {
5838 	units = 10; littleunits=2;
5839     } else if ( onehundred<100 ) {
5840 	units = 25; littleunits=5;
5841     } else if ( onehundred<500/2 ) {
5842 	units = 100; littleunits=20;
5843     } else if ( onehundred<1000/2 ) {
5844 	// The next numbers (1000 and up) take more space to display, so Frank has adjusted the thresholds.
5845 	units = 250; littleunits=50;
5846     } else if ( onehundred<5000/2 ) {
5847 	units = 1000; littleunits=200;
5848     } else if ( onehundred<10000/2 ) {
5849 	units = 2500; littleunits=500;
5850     } else if ( onehundred<50000/2 ) {
5851 	units = 10000; littleunits=2000;
5852     } else {
5853 	for ( units=1 ; units<onehundred*2; units *= 10 );
5854 	units/=10; littleunits = units/5;
5855     }
5856 
5857     // Create the pixmaps
5858     if (cv->hruler != NULL) {
5859         GDrawGetSize(cv->hruler, &rect);
5860         if (rect.width != cv->width || rect.height != cv->rulerh) {
5861             GDrawDestroyWindow(cv->hruler);
5862             cv->hruler = NULL;
5863         }
5864     }
5865     if (cv->vruler != NULL) {
5866         GDrawGetSize(cv->vruler, &rect);
5867         if (rect.height != cv->height || rect.width != cv->rulerh) {
5868             GDrawDestroyWindow(cv->vruler);
5869             cv->vruler = NULL;
5870         }
5871     }
5872     if (cv->hruler == NULL) {
5873         cv->hruler = GDrawCreatePixmap(GDrawGetDisplayOfWindow(cv->v), cv->v, cv->width, cv->rulerh);
5874     }
5875     if (cv->vruler == NULL) {
5876         cv->vruler = GDrawCreatePixmap(GDrawGetDisplayOfWindow(cv->v), cv->v, cv->rulerh, cv->height);
5877     }
5878 
5879     // Set background
5880     rect.x = 0; rect.width = cv->width; rect.y = 0; rect.height = cv->rulerh;
5881     GDrawFillRect(cv->hruler, &rect, GDrawGetDefaultBackground(NULL));
5882     rect.width = cv->rulerh; rect.height = cv->height;
5883     GDrawFillRect(cv->vruler, &rect, GDrawGetDefaultBackground(NULL));
5884 
5885     // Draw bottom line of rulers
5886     GDrawSetLineWidth(cv->hruler, 0);
5887     GDrawSetLineWidth(cv->vruler, 0);
5888     GDrawDrawLine(cv->hruler, 0, cv->rulerh - 1, cv->width, cv->rulerh - 1, def_fg);
5889     GDrawDrawLine(cv->vruler, cv->rulerh - 1, 0, cv->rulerh - 1, cv->height, def_fg);
5890 
5891     // Draw the ticks
5892     GDrawSetFont(cv->hruler, cv->small);
5893     GDrawSetFont(cv->vruler, cv->small);
5894     if ( xmax-xmin<1 && cv->width>100 ) {
5895 	CVDrawNum(cv,cv->hruler,0,cv->sas,"%.3f",xmin,0);
5896 	CVDrawNum(cv,cv->hruler,cv->width,cv->sas,"%.3f",xmax,2);
5897     }
5898 
5899     if ( ymax-ymin<1 && cv->height>100 ) {
5900 	CVDrawVNum(cv,cv->vruler,1,cv->height+cv->sas,"%.3f",ymin,0);
5901 	CVDrawVNum(cv,cv->vruler,1,cv->sas,"%.3f",ymax,2);
5902     }
5903     if ( fabs(xmin/units) < 1e5 && fabs(ymin/units)<1e5 && fabs(xmax/units)<1e5 && fabs(ymax/units)<1e5 ) {
5904 	if ( littleunits!=0 ) {
5905 	    for ( pos=littleunits*ceil(xmin/littleunits); pos<xmax; pos += littleunits ) {
5906 		x = tab->xoff + rint(pos*tab->scale);
5907 		GDrawDrawLine(cv->hruler,x,cv->rulerh-4,x,cv->rulerh, def_fg);
5908 	    }
5909 	    for ( pos=littleunits*ceil(ymin/littleunits); pos<ymax; pos += littleunits ) {
5910 		y = -tab->yoff + cv->height - rint(pos*tab->scale);
5911 		GDrawDrawLine(cv->vruler,cv->rulerh-4,y,cv->rulerh,y, def_fg);
5912 	    }
5913 	}
5914 	for ( pos=units*ceil(xmin/units); pos<xmax; pos += units ) {
5915 	    x = tab->xoff + rint(pos*tab->scale);
5916 	    GDrawDrawLine(cv->hruler,x,0,x,cv->rulerh, rulerbigtickcol);
5917 	    CVDrawNum(cv,cv->hruler,x+15,cv->sas,"%g",pos,1);
5918 	}
5919 	for ( pos=units*ceil(ymin/units); pos<ymax; pos += units ) {
5920 	    y = -tab->yoff + cv->height - rint(pos*tab->scale);
5921 	    GDrawDrawLine(cv->vruler,0,y,cv->rulerh,y, rulerbigtickcol);
5922 	    CVDrawVNum(cv,cv->vruler,1,y+cv->sas+20,"%g",pos,1);
5923 	}
5924     }
5925 
5926     // Draw the pixmaps to screen
5927     GDrawDrawPixmap(pixmap, cv->vruler, &rect, 0, cv->rulerh + ybase);
5928     rect.width = cv->width; rect.height = cv->rulerh;
5929     GDrawDrawPixmap(pixmap, cv->hruler, &rect, cv->rulerh, ybase);
5930 }
5931 
InfoExpose(CharView * cv,GWindow pixmap,GEvent * expose)5932 static void InfoExpose(CharView *cv, GWindow pixmap, GEvent *expose) {
5933     GRect old1, old2;
5934     Color def_fg = GDrawGetDefaultForeground(NULL);
5935 
5936     if ( expose->u.expose.rect.y + expose->u.expose.rect.height < cv->mbh ||
5937 	    (!cv->showrulers && expose->u.expose.rect.y >= cv->mbh+cv->charselectorh+cv->infoh ))
5938 return;
5939 
5940     GDrawPushClip(pixmap,&expose->u.expose.rect,&old1);
5941     GDrawSetLineWidth(pixmap,0);
5942     if ( expose->u.expose.rect.y< cv->mbh+cv->charselectorh+cv->infoh ) {
5943 	GDrawPushClip(pixmap,&expose->u.expose.rect,&old2);
5944 
5945 	GDrawDrawLine(pixmap,0,cv->mbh+cv->charselectorh+cv->infoh-1,8096,cv->mbh+cv->charselectorh+cv->infoh-1,def_fg);
5946 	GDrawDrawImage(pixmap,&GIcon_rightpointer,NULL,RPT_BASE,cv->mbh+cv->charselectorh+2);
5947 	GDrawDrawImage(pixmap,&GIcon_selectedpoint,NULL,SPT_BASE,cv->mbh+cv->charselectorh+2);
5948 	GDrawDrawImage(pixmap,&GIcon_sel2ptr,NULL,SOF_BASE,cv->mbh+cv->charselectorh+2);
5949 	GDrawDrawImage(pixmap,&GIcon_distance,NULL,SDS_BASE,cv->mbh+cv->charselectorh+2);
5950 	GDrawDrawImage(pixmap,&GIcon_angle,NULL,SAN_BASE,cv->mbh+cv->charselectorh+2);
5951 	GDrawDrawImage(pixmap,&GIcon_mag,NULL,MAG_BASE,cv->mbh+cv->charselectorh+2);
5952 	CVInfoDrawText(cv,pixmap);
5953 	GDrawPopClip(pixmap,&old2);
5954     }
5955     if ( cv->showrulers ) {
5956 	CVExposeRulers(cv,pixmap);
5957 	cv->olde.x = -1;
5958 	CVInfoDrawRulers(cv,pixmap);
5959     }
5960     GDrawPopClip(pixmap,&old1);
5961 }
5962 
CVResize(CharView * cv)5963 void CVResize(CharView *cv ) {
5964     int sbsize = GDrawPointsToPixels(cv->gw,_GScrollBar_Width);
5965     GRect size;
5966     GDrawGetSize(cv->gw,&size);
5967     {
5968 	int newwidth = size.width-sbsize,
5969 	    newheight = size.height-sbsize - cv->mbh-cv->charselectorh-cv->infoh;
5970 	int sbwidth = newwidth, sbheight = newheight;
5971 
5972 	if ( cv->dv!=NULL ) {
5973 	    newwidth -= cv->dv->dwidth;
5974 	    sbwidth -= cv->dv->dwidth;
5975 	}
5976 	if ( newwidth<30 || newheight<50 ) {
5977 	    if ( newwidth<30 )
5978 		newwidth = 30+sbsize+(cv->dv!=NULL ? cv->dv->dwidth : 0);
5979 	    if ( newheight<50 )
5980 		newheight = 50+sbsize+cv->mbh+cv->charselectorh+cv->infoh;
5981 	    GDrawResize(cv->gw,newwidth,newheight);
5982 return;
5983 	}
5984 
5985 	if ( cv->dv!=NULL ) {
5986 	    int dvheight = size.height-(cv->mbh+cv->charselectorh+cv->infoh);
5987 	    GDrawMove(cv->dv->dv,size.width-cv->dv->dwidth,cv->mbh+cv->charselectorh+cv->infoh);
5988 	    GDrawResize(cv->dv->dv,cv->dv->dwidth,dvheight);
5989 	    GDrawResize(cv->dv->ii.v,cv->dv->dwidth-sbsize,dvheight-cv->dv->toph);
5990 	    GGadgetResize(cv->dv->ii.vsb,sbsize,dvheight-cv->dv->toph);
5991 	    cv->dv->ii.vheight = dvheight-cv->dv->toph;
5992 	    GDrawRequestExpose(cv->dv->dv,NULL,false);
5993 	    GDrawRequestExpose(cv->dv->ii.v,NULL,false);
5994 	    GScrollBarSetBounds(cv->dv->ii.vsb,0,cv->dv->ii.lheight+1,
5995 		    cv->dv->ii.vheight/cv->dv->ii.fh);
5996 	}
5997 
5998 	if (cv->charselector != NULL && cv->charselectorPrev != NULL && cv->charselectorNext != NULL) {
5999 	  GRect charselector_size;
6000 	  GRect charselectorNext_size;
6001 	  GRect charselectorPrev_size;
6002 	  GGadgetGetSize(cv->charselector, &charselector_size);
6003 	  GGadgetGetSize(cv->charselectorPrev, &charselectorPrev_size);
6004 	  GGadgetGetSize(cv->charselectorNext, &charselectorNext_size);
6005 	  int new_charselector_width = newwidth + sbsize - ( 4 * charselector_size.x ) - charselectorNext_size.width - charselectorPrev_size.width;
6006 	  int new_charselectorPrev_x = newwidth + sbsize - ( 2 * charselector_size.x ) - charselectorNext_size.width - charselectorPrev_size.width;
6007 	  int new_charselectorNext_x = newwidth + sbsize - ( 1 * charselector_size.x ) - charselectorNext_size.width;
6008 	  GGadgetResize(cv->charselector, new_charselector_width, charselector_size.height);
6009 	  GGadgetMove(cv->charselectorPrev, new_charselectorPrev_x, charselectorPrev_size.y);
6010 	  GGadgetMove(cv->charselectorNext, new_charselectorNext_x, charselectorNext_size.y);
6011 	  charselector_size.x = 0; charselector_size.y = cv->mbh; charselector_size.width = newwidth + sbsize; charselector_size.height = cv->charselectorh;
6012 	  GDrawRequestExpose(cv->gw, &charselector_size, false);
6013 	}
6014 
6015 	if ( cv->showrulers ) {
6016 	    newheight -= cv->rulerh;
6017 	    newwidth -= cv->rulerh;
6018 	}
6019 
6020 	if ( newwidth == cv->width && newheight == cv->height )
6021 return;
6022 	if ( cv->backimgs!=NULL )
6023 	    GDrawDestroyWindow(cv->backimgs);
6024 	cv->backimgs = NULL;
6025 
6026 	/* MenuBar takes care of itself */
6027 	GDrawResize(cv->v,newwidth,newheight);
6028 	GGadgetMove(cv->vsb,sbwidth, cv->mbh+cv->charselectorh+cv->infoh);
6029 	GGadgetResize(cv->vsb,sbsize,sbheight);
6030 	GGadgetMove(cv->hsb,0,size.height-sbsize);
6031 	GGadgetResize(cv->hsb,sbwidth,sbsize);
6032 	cv->width = newwidth; cv->height = newheight;
6033 	/*CVFit(cv);*/ CVNewScale(cv);
6034 	CVPalettesRaise(cv);
6035 	if ( cv->b.container == NULL && ( cv_width!=size.width || cv_height!=size.height )) {
6036 	    cv_width = size.width;
6037 	    cv_height = size.height;
6038 	    SavePrefs(true);
6039 	}
6040     }
6041 }
6042 
CVHScrollSetPos(CharView * cv,int newpos)6043 static void CVHScrollSetPos( CharView *cv, int newpos )
6044 {
6045     CharViewTab* tab = CVGetActiveTab(cv);
6046     TRACE("CVHScrollSetPos(1) cvxoff:%f newpos:%d\n", tab->xoff, newpos );
6047     if ( newpos<-(32000*tab->scale-cv->width) )
6048         newpos = -(32000*tab->scale-cv->width);
6049     if ( newpos>8000*tab->scale ) newpos = 8000*tab->scale;
6050     TRACE("CVHScrollSetPos(2) cvxoff:%f newpos:%d\n", tab->xoff, newpos );
6051     if ( newpos!=tab->xoff ) {
6052 	int diff = newpos-tab->xoff;
6053 	tab->xoff = newpos;
6054 	cv->back_img_out_of_date = true;
6055 	GScrollBarSetPos(cv->hsb,-newpos);
6056 	GDrawScroll(cv->v,NULL,diff,0);
6057 	if (( cv->showhhints && cv->b.sc->hstem!=NULL ) || cv->showblues || cv->showvmetrics ) {
6058 	    GRect r;
6059 	    r.y = 0; r.height = cv->height;
6060 	    r.width = 6*cv->sfh+10;
6061 	    if ( diff>0 )
6062 		r.x = cv->width-r.width;
6063 	    else
6064 		r.x = cv->width+diff-r.width;
6065 	    GDrawRequestExpose(cv->v,&r,false);
6066 	}
6067 	if ( cv->showrulers ) {
6068 	    GRect r;
6069 	    r.y = cv->infoh+cv->mbh+cv->charselectorh; r.height = cv->rulerh; r.x = 0; r.width = cv->rulerh+cv->width;
6070 	    GDrawRequestExpose(cv->gw,&r,false);
6071 	}
6072     }
6073 }
6074 
CVHScroll(CharView * cv,struct sbevent * sb)6075 static void CVHScroll(CharView *cv, struct sbevent *sb) {
6076     CharViewTab* tab = CVGetActiveTab(cv);
6077     int newpos = tab->xoff;
6078 
6079     switch( sb->type ) {
6080       case et_sb_top:
6081         newpos = 0;
6082       break;
6083       case et_sb_uppage:
6084         newpos += 9*cv->width/10;
6085       break;
6086       case et_sb_up:
6087         newpos += cv->width/15;
6088       break;
6089       case et_sb_down:
6090         newpos -= cv->width/15;
6091       break;
6092       case et_sb_downpage:
6093         newpos -= 9*cv->width/10;
6094       break;
6095       case et_sb_bottom:
6096         newpos = 0;
6097       break;
6098       case et_sb_thumb:
6099       case et_sb_thumbrelease:
6100         newpos = -sb->pos;
6101       break;
6102       case et_sb_halfup:
6103         newpos += cv->width/30;
6104       break;
6105       case et_sb_halfdown:
6106         newpos -= cv->width/30;
6107       break;
6108     }
6109 
6110     CVHScrollSetPos( cv, newpos );
6111 }
6112 
CVVScroll(CharView * cv,struct sbevent * sb)6113 static void CVVScroll(CharView *cv, struct sbevent *sb) {
6114     CharViewTab* tab = CVGetActiveTab(cv);
6115     int newpos = tab->yoff;
6116 
6117     switch( sb->type ) {
6118       case et_sb_top:
6119         newpos = 0;
6120       break;
6121       case et_sb_uppage:
6122         newpos -= 9*cv->height/10;
6123       break;
6124       case et_sb_up:
6125         newpos -= cv->height/15;
6126       break;
6127       case et_sb_down:
6128         newpos += cv->height/15;
6129       break;
6130       case et_sb_downpage:
6131         newpos += 9*cv->height/10;
6132       break;
6133       case et_sb_bottom:
6134         newpos = 0;
6135       break;
6136       case et_sb_thumb:
6137       case et_sb_thumbrelease:
6138         newpos = sb->pos+cv->height;
6139       break;
6140       case et_sb_halfup:
6141         newpos -= cv->height/30;
6142       break;
6143       case et_sb_halfdown:
6144         newpos += cv->height/30;
6145       break;
6146     }
6147     if ( newpos<-(20000*tab->scale-cv->height) )
6148         newpos = -(20000*tab->scale-cv->height);
6149     if ( newpos>8000*tab->scale ) newpos = 8000*tab->scale;
6150     if ( newpos!=tab->yoff ) {
6151 	int diff = newpos-tab->yoff;
6152 	tab->yoff = newpos;
6153 	cv->back_img_out_of_date = true;
6154 	GScrollBarSetPos(cv->vsb,newpos-cv->height);
6155 	GDrawScroll(cv->v,NULL,0,diff);
6156 	if (( cv->showvhints && cv->b.sc->vstem!=NULL) || cv->showhmetrics ) {
6157 	    GRect r;
6158 	    RefChar *lock = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
6159 	    r.x = 0; r.width = cv->width;
6160 	    r.height = 2*cv->sfh+6;
6161 	    if ( lock!=NULL )
6162 		r.height = cv->sfh+3+GImageGetHeight(&GIcon_lock);
6163 	    if ( diff>0 )
6164 		r.y = 0;
6165 	    else
6166 		r.y = -diff;
6167 	    GDrawRequestExpose(cv->v,&r,false);
6168 	}
6169 	if ( cv->showrulers ) {
6170 	    GRect r;
6171 	    r.x = 0; r.width = cv->rulerh; r.y = cv->infoh+cv->charselectorh+cv->mbh; r.height = cv->rulerh+cv->height;
6172 	    GDrawRequestExpose(cv->gw,&r,false);
6173 	}
6174     }
6175 }
6176 
LogoExpose(GWindow pixmap,GEvent * event,GRect * r,enum drawmode dm)6177 void LogoExpose(GWindow pixmap,GEvent *event, GRect *r,enum drawmode dm) {
6178     int sbsize = GDrawPointsToPixels(pixmap,_GScrollBar_Width);
6179     GRect old;
6180 
6181     r->width = r->height = sbsize;
6182     if ( event->u.expose.rect.x+event->u.expose.rect.width >= r->x &&
6183 	    event->u.expose.rect.y+event->u.expose.rect.height >= r->y ) {
6184 	/* Put something into the little box where the two scroll bars meet */
6185 	int xoff, yoff;
6186 	GImage *which = (dm==dm_fore) ? &GIcon_FontForgeLogo :
6187 			(dm==dm_back) ? &GIcon_FontForgeBack :
6188 			    &GIcon_FontForgeGuide;
6189 	struct _GImage *base = which->u.image;
6190 	xoff = (sbsize-base->width);
6191 	yoff = (sbsize-base->height);
6192 	GDrawPushClip(pixmap,r,&old);
6193 	GDrawDrawImage(pixmap,which,NULL,
6194 		r->x+(xoff-xoff/2),r->y+(yoff-yoff/2));
6195 	GDrawPopClip(pixmap,&old);
6196 	/*GDrawDrawLine(pixmap,r->x+sbsize-1,r->y,r->x+sbsize-1,r->y+sbsize,0x000000);*/
6197     }
6198 }
6199 
CVLogoExpose(CharView * cv,GWindow pixmap,GEvent * event)6200 static void CVLogoExpose(CharView *cv,GWindow pixmap,GEvent *event) {
6201     int rh = cv->showrulers ? cv->rulerh : 0;
6202     GRect r;
6203 
6204     r.x = cv->width+rh;
6205     r.y = cv->height+cv->mbh+cv->charselectorh+cv->infoh+rh;
6206     LogoExpose(pixmap,event,&r,cv->b.drawmode==dm_grid? dm_grid :
6207 	    cv->b.layerheads[cv->b.drawmode]->background ? dm_back : dm_fore );
6208 }
6209 
CVDrawGuideLine(CharView * cv,int old_guide_pos,int guide_pos)6210 static void CVDrawGuideLine(CharView *cv, int old_guide_pos, int guide_pos) {
6211     GWindow pixmap = cv->v;
6212 
6213     if ( guide_pos<0 )
6214 return;
6215     GDrawSetDashedLine(pixmap,2,2,0);
6216     GDrawSetLineWidth(pixmap,0);
6217 
6218     if ( cv->ruler_pressedv ) {
6219         if (old_guide_pos >= 0) {
6220             GRect r = {.x = old_guide_pos, .y = 0, .width = 1, .height = cv->height};
6221             GDrawRequestExpose(pixmap,&r,false);
6222         }
6223 	GDrawDrawLine(pixmap,guide_pos,0,guide_pos,cv->height,0x000000);
6224     } else {
6225         if (old_guide_pos >= 0) {
6226             GRect r = {.x = 0, .y = old_guide_pos, .width = cv->width, .height = 1};
6227             GDrawRequestExpose(pixmap,&r,false);
6228         }
6229 	GDrawDrawLine(pixmap,0,guide_pos,cv->width,guide_pos,0x000000);
6230     }
6231     GDrawSetDashedLine(pixmap,0,0,0);
6232 }
6233 
CVAddGuide(CharView * cv,int is_v,int guide_pos)6234 static void CVAddGuide(CharView *cv,int is_v,int guide_pos) {
6235     CharViewTab* tab = CVGetActiveTab(cv);
6236     SplinePoint *sp1, *sp2;
6237     SplineSet *ss;
6238     SplineFont *sf = cv->b.sc->parent;
6239     int emsize = sf->ascent+sf->descent;
6240 
6241     if ( is_v ) {
6242 	/* Create a vertical guide line */
6243 	double x = (guide_pos-tab->xoff)/tab->scale;
6244 	sp1 = SplinePointCreate(x,sf->ascent+emsize/2);
6245 	sp2 = SplinePointCreate(x,-sf->descent-emsize/2);
6246     } else {
6247 	double y = (cv->height-guide_pos-tab->yoff)/tab->scale;
6248 	sp1 = SplinePointCreate(-emsize,y);
6249 	sp2 = SplinePointCreate(2*emsize,y);
6250     }
6251     SplineMake(sp1,sp2,sf->grid.order2);
6252     ss = chunkalloc(sizeof(SplineSet));
6253     ss->first = sp1; ss->last = sp2;
6254     ss->next = sf->grid.splines;
6255     sf->grid.splines = ss;
6256     ss->contour_name = gwwv_ask_string(_("Name this contour"),NULL,
6257 		_("Name this guideline or cancel to create it without a name"));
6258     if ( ss->contour_name!=NULL && *ss->contour_name=='\0' ) {
6259 	free(ss->contour_name);
6260 	ss->contour_name = NULL;
6261     }
6262 
6263     FVRedrawAllCharViewsSF(sf);
6264     if ( !sf->changed ) {
6265 	sf->changed = true;
6266 	FVSetTitles(sf);
6267     }
6268 }
6269 
cv_e_h(GWindow gw,GEvent * event)6270 static int cv_e_h(GWindow gw, GEvent *event) {
6271     CharView *cv = (CharView *) GDrawGetUserData(gw);
6272 
6273     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
6274 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
6275 	int ish = event->u.mouse.button>5;
6276 	if ( event->u.mouse.state&ksm_shift ) ish = !ish;
6277 	if ( ish ) /* bind shift to vertical scrolling */
6278 return( GGadgetDispatchEvent(cv->hsb,event));
6279 	else
6280 return( GGadgetDispatchEvent(cv->vsb,event));
6281     }
6282 
6283     switch ( event->type ) {
6284       case et_selclear:
6285 	ClipboardClear();
6286       break;
6287       case et_expose:
6288 	GDrawSetLineWidth(gw,0);
6289 	InfoExpose(cv,gw,event);
6290 	CVLogoExpose(cv,gw,event);
6291       break;
6292       case et_char:
6293 	if ( cv->b.container!=NULL )
6294 	    (cv->b.container->funcs->charEvent)(cv->b.container,event);
6295 	else
6296 	    CVChar(cv,event);
6297       break;
6298       case et_charup:
6299 	CVCharUp(cv,event);
6300       break;
6301       case et_resize:
6302 	if ( event->u.resize.sized )
6303 	    CVResize(cv);
6304       break;
6305       case et_controlevent:
6306 	switch ( event->u.control.subtype ) {
6307 	  case et_scrollbarchange:
6308 	    if ( event->u.control.g == cv->hsb )
6309 		CVHScroll(cv,&event->u.control.u.sb);
6310 	    else
6311 		CVVScroll(cv,&event->u.control.u.sb);
6312 	  break;
6313 	}
6314       break;
6315       case et_map:
6316 	if ( event->u.map.is_visible )
6317 	    CVPaletteActivate(cv);
6318 	else
6319 	    CVPalettesHideIfMine(cv);
6320       break;
6321       case et_destroy:
6322 	CVUnlinkView(cv);
6323 	CVPalettesHideIfMine(cv);
6324 	if ( cv->backimgs!=NULL ) {
6325 	    GDrawDestroyWindow(cv->backimgs);
6326 	    cv->backimgs = NULL;
6327 	}
6328 	if ( cv->icon!=NULL ) {
6329 	    GDrawDestroyWindow(cv->icon);
6330 	    cv->icon = NULL;
6331 	}
6332     if (cv->hruler != NULL) {
6333         GDrawDestroyWindow(cv->hruler);
6334         cv->hruler = NULL;
6335     }
6336     if (cv->vruler != NULL) {
6337         GDrawDestroyWindow(cv->vruler);
6338         cv->vruler = NULL;
6339     }
6340 	CharViewFree(cv);
6341       break;
6342       case et_close:
6343 	  dlist_foreach( &cv->pointInfoDialogs, PI_Destroy );
6344 	  GDrawDestroyWindow(gw);
6345       break;
6346       case et_mouseup: case et_mousedown:
6347 	GGadgetEndPopup();
6348 	CVPaletteActivate(cv);
6349 	if ( cv->inactive )
6350 	    (cv->b.container->funcs->activateMe)(cv->b.container,&cv->b);
6351 	if ( cv->showrulers ) {
6352 	    int is_h = event->u.mouse.y>cv->mbh+cv->charselectorh+cv->infoh && event->u.mouse.y<cv->mbh+cv->charselectorh+cv->infoh+cv->rulerh;
6353 	    int is_v = event->u.mouse.x<cv->rulerh;
6354 	    if ( cv->gwgic!=NULL && event->type==et_mousedown)
6355 		GDrawSetGIC(gw,cv->gwgic,0,20);
6356 	    if ( event->type == et_mousedown ) {
6357 		if ( is_h && is_v )
6358 		    /* Ambiguous, ignore */;
6359 		else if ( is_h ) {
6360 		    cv->ruler_pressed = true;
6361 		    cv->ruler_pressedv = false;
6362 		    GDrawSetCursor(cv->v,ct_updown);
6363 		    GDrawSetCursor(cv->gw,ct_updown);
6364 		} else if ( is_v ) {
6365 		    cv->ruler_pressed = true;
6366 		    cv->ruler_pressedv = true;
6367 		    GDrawSetCursor(cv->v,ct_leftright);
6368 		    GDrawSetCursor(cv->gw,ct_leftright);
6369 		}
6370 		cv->guide_pos = -1;
6371 	    } else if ( event->type==et_mouseup && cv->ruler_pressed ) {
6372 		CVDrawGuideLine(cv,-1,cv->guide_pos);
6373 		cv->guide_pos = -1;
6374 		cv->showing_tool = cvt_none;
6375 		CVToolsSetCursor(cv,event->u.mouse.state&~(1<<(7+event->u.mouse.button)),event->u.mouse.device);		/* X still has the buttons set in the state, even though we just released them. I don't want em */
6376 		GDrawSetCursor(cv->gw,ct_mypointer);
6377 		cv->ruler_pressed = false;
6378 		if ( is_h || is_v )
6379 		    /* Do Nothing */;
6380 		else if ( cv->ruler_pressedv )
6381 		    CVAddGuide(cv,true,event->u.mouse.x-cv->rulerh);
6382 		else
6383 		    CVAddGuide(cv,false,event->u.mouse.y-(cv->mbh+cv->charselectorh+cv->infoh+cv->rulerh));
6384 	    }
6385 	}
6386       break;
6387       case et_mousemove:
6388 	if ( cv->ruler_pressed ) {
6389         int old_pos = cv->guide_pos;
6390         CharViewTab* tab = CVGetActiveTab(cv);
6391 	    cv->e.x = event->u.mouse.x - cv->rulerh;
6392 	    cv->e.y = event->u.mouse.y-(cv->mbh+cv->charselectorh+cv->infoh+cv->rulerh);
6393 	    cv->info.x = (cv->e.x-tab->xoff)/tab->scale;
6394 	    cv->info.y = (cv->height-cv->e.y-tab->yoff)/tab->scale;
6395 	    if ( cv->ruler_pressedv )
6396 		cv->guide_pos = cv->e.x;
6397 	    else
6398 		cv->guide_pos = cv->e.y;
6399 	    CVDrawGuideLine(cv,old_pos,cv->guide_pos);
6400 	    CVInfoDraw(cv,cv->gw);
6401 	}
6402     else if ( event->u.mouse.y > cv->mbh )
6403     {
6404         if( GGadgetContainsEventLocation( cv->charselectorPrev, event ))
6405         {
6406             GGadgetPreparePopup(cv->gw,c_to_u("Show the previous word in the current word list\n"
6407                                               "Select the menu File / Load Word List... to load a wordlist."));
6408         }
6409         else if( GGadgetContainsEventLocation( cv->charselectorNext, event ))
6410         {
6411             GGadgetPreparePopup(cv->gw,c_to_u("Show the next word in the current word list\n"
6412                                               "Select the menu File / Load Word List... to load a wordlist."));
6413         }
6414         else if( GGadgetContainsEventLocation( cv->charselector, event ))
6415         {
6416             GGadgetPreparePopup(cv->gw,c_to_u("This is a word list that you can step through to quickly see your glyphs in context\n"
6417                                               "Select the menu File / Load Word List... to load a wordlist."));
6418         }
6419         else
6420         {
6421             int enc = CVCurEnc(cv);
6422             SCPreparePopup(cv->gw,cv->b.sc,cv->b.fv->map->remap,enc,
6423                            UniFromEnc(enc,cv->b.fv->map->enc));
6424         }
6425     }
6426       break;
6427       case et_drop:
6428 	CVDrop(cv,event);
6429       break;
6430       case et_focus:
6431 	if ( event->u.focus.gained_focus ) {
6432 	    if ( cv->gic!=NULL )
6433 		GDrawSetGIC(gw,cv->gic,0,20);
6434 
6435 	    // X11 on Windows is broken, non-active windows
6436 	    // receive this event on mouseover
6437 #if !defined(_WIN32) || defined(FONTFORGE_CAN_USE_GDK)
6438 	    CVPaletteActivate(cv);
6439 #endif
6440 	}
6441       break;
6442     }
6443 return( true );
6444 }
6445 
6446 #define MID_Fit		2001
6447 #define MID_ZoomIn	2002
6448 #define MID_ZoomOut	2003
6449 #define MID_HidePoints	2004
6450 #define MID_HideControlPoints	2005
6451 #define MID_Fill	2006
6452 #define MID_Next	2007
6453 #define MID_Prev	2008
6454 #define MID_HideRulers	2009
6455 #define MID_Preview     2010
6456 #define MID_DraggingComparisonOutline 2011
6457 #define MID_NextDef	2012
6458 #define MID_PrevDef	2013
6459 #define MID_DisplayCompositions	2014
6460 #define MID_MarkExtrema	2015
6461 #define MID_Goto	2016
6462 #define MID_FindInFontView	2017
6463 #define MID_KernPairs	2018
6464 #define MID_AnchorPairs	2019
6465 #define MID_ShowGridFit 2020
6466 #define MID_PtsNone	2021
6467 #define MID_PtsTrue	2022
6468 #define MID_PtsPost	2023
6469 #define MID_PtsSVG	2024
6470 #define MID_Ligatures	2025
6471 #define MID_Former	2026
6472 #define MID_MarkPointsOfInflection	2027
6473 #define MID_ShowCPInfo	2028
6474 #define MID_ShowTabs	2029
6475 #define MID_AnchorGlyph	2030
6476 #define MID_AnchorControl 2031
6477 #define MID_ShowSideBearings	2032
6478 #define MID_Bigger	2033
6479 #define MID_Smaller	2034
6480 #define MID_GridFitAA	2035
6481 #define MID_GridFitOff	2036
6482 #define MID_ShowHHints	2037
6483 #define MID_ShowVHints	2038
6484 #define MID_ShowDHints	2039
6485 #define MID_ShowBlueValues	2040
6486 #define MID_ShowFamilyBlues	2041
6487 #define MID_ShowAnchors		2042
6488 #define MID_ShowHMetrics	2043
6489 #define MID_ShowVMetrics	2044
6490 #define MID_ShowRefNames	2045
6491 #define MID_ShowAlmostHV	2046
6492 #define MID_ShowAlmostHVCurves	2047
6493 #define MID_DefineAlmost	2048
6494 #define MID_SnapOutlines	2049
6495 #define MID_ShowDebugChanges	2050
6496 #define MID_Cut		2101
6497 #define MID_Copy	2102
6498 #define MID_Paste	2103
6499 #define MID_Clear	2104
6500 #define MID_Merge	2105
6501 #define MID_SelAll	2106
6502 #define MID_CopyRef	2107
6503 #define MID_UnlinkRef	2108
6504 #define MID_Undo	2109
6505 #define MID_Redo	2110
6506 #define MID_CopyWidth	2111
6507 #define MID_RemoveUndoes	2112
6508 #define MID_CopyFgToBg	2115
6509 #define MID_NextPt	2116
6510 #define MID_PrevPt	2117
6511 #define MID_FirstPt	2118
6512 #define MID_NextCP	2119
6513 #define MID_PrevCP	2120
6514 #define MID_SelNone	2121
6515 #define MID_SelectWidth	2122
6516 #define MID_SelectVWidth	2123
6517 #define MID_CopyLBearing	2124
6518 #define MID_CopyRBearing	2125
6519 #define MID_CopyVWidth	2126
6520 #define MID_Join	2127
6521 #define MID_CopyGridFit	2128
6522 /*#define MID_Elide	2129*/
6523 #define MID_SelectAllPoints	2130
6524 #define MID_SelectAnchors	2131
6525 #define MID_FirstPtNextCont	2132
6526 #define MID_Contours	2133
6527 #define MID_SelectHM	2134
6528 #define MID_SelInvert	2136
6529 #define MID_CopyBgToFg	2137
6530 #define MID_SelPointAt	2138
6531 #define MID_CopyLookupData	2139
6532 #define MID_SelectOpenContours	2140
6533 #define MID_MergeToLine	2141
6534 #define MID_Clockwise	2201
6535 #define MID_Counter	2202
6536 #define MID_GetInfo	2203
6537 #define MID_Correct	2204
6538 #define MID_AvailBitmaps	2210
6539 #define MID_RegenBitmaps	2211
6540 #define MID_Stroke	2205
6541 #define MID_RmOverlap	2206
6542 #define MID_Simplify	2207
6543 #define MID_BuildAccent	2208
6544 #define MID_Autotrace	2212
6545 #define MID_Round	2213
6546 #define MID_Embolden	2217
6547 #define MID_Condense	2218
6548 #define MID_Average	2219
6549 #define MID_SpacePts	2220
6550 #define MID_SpaceRegion	2221
6551 #define MID_MakeParallel	2222
6552 #define MID_ShowDependentRefs	2223
6553 #define MID_AddExtrema	2224
6554 #define MID_CleanupGlyph	2225
6555 #define MID_TilePath	2226
6556 #define MID_BuildComposite	2227
6557 #define MID_Exclude	2228
6558 #define MID_Intersection	2229
6559 #define MID_FindInter	2230
6560 #define MID_Styles	2231
6561 #define MID_SimplifyMore	2232
6562 #define MID_First	2233
6563 #define MID_Earlier	2234
6564 #define MID_Later	2235
6565 #define MID_Last	2236
6566 #define MID_CharInfo	2240
6567 #define MID_ShowDependentSubs	2241
6568 #define MID_CanonicalStart	2242
6569 #define MID_CanonicalContours	2243
6570 #define MID_RemoveBitmaps	2244
6571 #define MID_RoundToCluster	2245
6572 #define MID_Align		2246
6573 #define MID_FontInfo		2247
6574 #define MID_FindProblems	2248
6575 #define MID_InsertText		2249
6576 #define MID_Italic		2250
6577 #define MID_ChangeXHeight	2251
6578 #define MID_ChangeGlyph		2252
6579 #define MID_CheckSelf		2253
6580 #define MID_GlyphSelfIntersects	2254
6581 #define MID_ReverseDir		2255
6582 #define MID_Corner	2301
6583 #define MID_Tangent	2302
6584 #define MID_Curve	2303
6585 #define MID_MakeFirst	2304
6586 #define MID_MakeLine	2305
6587 #define MID_CenterCP	2306
6588 #define MID_ImplicitPt	2307
6589 #define MID_NoImplicitPt	2308
6590 #define MID_InsertPtOnSplineAt	2309
6591 #define MID_AddAnchor	2310
6592 #define MID_HVCurve	2311
6593 #define MID_SpiroG4	2312
6594 #define MID_SpiroG2	2313
6595 #define MID_SpiroCorner	2314
6596 #define MID_SpiroLeft	2315
6597 #define MID_SpiroRight	2316
6598 #define MID_SpiroMakeFirst 2317
6599 #define MID_NamePoint	2318
6600 #define MID_NameContour	2319
6601 #define MID_AcceptableExtrema 2320
6602 #define MID_MakeArc	2321
6603 #define MID_ClipPath	2322
6604 
6605 #define MID_AutoHint	2400
6606 #define MID_ClearHStem	2401
6607 #define MID_ClearVStem	2402
6608 #define MID_ClearDStem	2403
6609 #define MID_AddHHint	2404
6610 #define MID_AddVHint	2405
6611 #define MID_AddDHint	2406
6612 #define MID_ReviewHints	2407
6613 #define MID_CreateHHint	2408
6614 #define MID_CreateVHint	2409
6615 #define MID_MinimumDistance	2410
6616 #define MID_AutoInstr	2411
6617 #define MID_ClearInstr	2412
6618 #define MID_EditInstructions 2413
6619 #define MID_Debug	2414
6620 #define MID_HintSubsPt	2415
6621 #define MID_AutoCounter	2416
6622 #define MID_DontAutoHint	2417
6623 #define MID_Deltas	2418
6624 #define MID_Tools	2501
6625 #define MID_Layers	2502
6626 #define MID_DockPalettes	2503
6627 #define MID_Center	2600
6628 #define MID_SetWidth	2601
6629 #define MID_SetLBearing	2602
6630 #define MID_SetRBearing	2603
6631 #define MID_Thirds	2604
6632 #define MID_RemoveKerns	2605
6633 #define MID_SetVWidth	2606
6634 #define MID_RemoveVKerns	2607
6635 #define MID_KPCloseup	2608
6636 #define MID_AnchorsAway	2609
6637 #define MID_SetBearings	2610
6638 #define MID_OpenBitmap	2700
6639 #define MID_Revert	2702
6640 #define MID_Recent	2703
6641 #define MID_RevertGlyph	2707
6642 #define MID_Open	2708
6643 #define MID_New		2709
6644 #define MID_Close	2710
6645 #define MID_Quit	2711
6646 #define MID_CloseTab	2712
6647 #define MID_GenerateTTC	2713
6648 #define MID_VKernClass  2715
6649 #define MID_VKernFromHKern 2716
6650 
6651 #define MID_ShowGridFitLiveUpdate 2720
6652 
6653 #define MID_MMReblend	2800
6654 #define MID_MMAll	2821
6655 #define MID_MMNone	2822
6656 
6657 #define MID_PtsPos      2823
6658 
6659 #define MID_Warnings	3000
6660 
CVMenuClose(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6661 static void CVMenuClose(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6662     CharView *cv = (CharView *) GDrawGetUserData(gw);
6663     if ( cv->b.container )
6664 	(cv->b.container->funcs->doClose)(cv->b.container);
6665     else
6666 	GDrawDestroyWindow(gw);
6667 }
6668 
6669 // This can be triggered two ways: by the "X" buttons on tabs, and regularly in the menu.
CVMenuCloseTab(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6670 static void CVMenuCloseTab(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6671     CharView *cv = (CharView *) GDrawGetUserData(gw);
6672     int pos, i;
6673 
6674     if ( cv->b.container || cv->tabs==NULL || cv->former_cnt<=1 )
6675 return;
6676     pos = cv->ctpos == -1 ? GTabSetGetSel(cv->tabs) : cv->ctpos;
6677     free(cv->former_names[pos]);
6678     for ( i=pos+1; i<cv->former_cnt; ++i ) {
6679 	cv->former_names[i-1] = cv->former_names[i];
6680     cv->cvtabs[i-1] = cv->cvtabs[i];
6681     }
6682     --cv->former_cnt;
6683     if (cv->ctpos==-1){
6684         GTabSetRemoveTabByPos(cv->tabs,pos);	/* This should send an event that the selection has changed */
6685     }
6686     GTabSetRemetric(cv->tabs);
6687     if (GTabSetGetSel(cv->tabs) >= pos) {
6688     CVChangeSC_fetchTab(cv, pos);
6689     // Otherwise subsequent calls to CVChangeSC_storeTab which use this value will be off by one
6690     cv->oldtabnum = pos;
6691     }
6692     if (GTabSetGetTabCount(cv->tabs)<=1) CVChangeTabsVisibility(cv,false);
6693     // Reset this argument so in case it's called by the menu the right tab will be removed.
6694     cv->ctpos = -1;
6695 }
6696 
CVMenuOpen(GWindow gw,struct gmenuitem * mi,GEvent * g)6697 static void CVMenuOpen(GWindow gw, struct gmenuitem *mi, GEvent *g) {
6698     CharView *d = (CharView*)GDrawGetUserData(gw);
6699     FontView *fv = NULL;
6700     if (d) {
6701         fv = (FontView*)d->b.fv;
6702     }
6703     _FVMenuOpen(fv);
6704 }
6705 
CVMenuOpenBitmap(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6706 static void CVMenuOpenBitmap(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6707     CharView *cv = (CharView *) GDrawGetUserData(gw);
6708     if ( cv->b.fv->sf->bitmaps==NULL )
6709 return;
6710     BitmapViewCreatePick(CVCurEnc(cv),(FontView *) (cv->b.fv));
6711 }
6712 
CVMenuOpenMetrics(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6713 static void CVMenuOpenMetrics(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6714     CharView *cv = (CharView *) GDrawGetUserData(gw);
6715     MetricsViewCreate((FontView *) (cv->b.fv),cv->b.sc,NULL);
6716 }
6717 
CVMenuSave(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6718 static void CVMenuSave(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6719     CharView *cv = (CharView *) GDrawGetUserData(gw);
6720     _FVMenuSave((FontView *) (cv->b.fv));
6721 }
6722 
CVMenuSaveAs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6723 static void CVMenuSaveAs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6724     CharView *cv = (CharView *) GDrawGetUserData(gw);
6725     _FVMenuSaveAs((FontView *) (cv->b.fv));
6726 }
6727 
CVMenuGenerate(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6728 static void CVMenuGenerate(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6729     CharView *cv = (CharView *) GDrawGetUserData(gw);
6730     FontViewBase *fv = cv->b.fv;
6731     SFGenerateFont(cv->b.sc->parent,CVLayer((CharViewBase *) cv),false,fv->normal==NULL?fv->map:fv->normal);
6732 }
6733 
CVMenuGenerateFamily(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6734 static void CVMenuGenerateFamily(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6735     CharView *cv = (CharView *) GDrawGetUserData(gw);
6736     _FVMenuGenerate((FontView *) (cv->b.fv),gf_macfamily);
6737 }
6738 
CVMenuGenerateTTC(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6739 static void CVMenuGenerateTTC(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6740     CharView *cv = (CharView *) GDrawGetUserData(gw);
6741     _FVMenuGenerate((FontView *) (cv->b.fv),gf_ttc);
6742 }
6743 
CVMenuExport(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6744 static void CVMenuExport(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6745     CharView *cv = (CharView *) GDrawGetUserData(gw);
6746     CVExport(cv);
6747 }
6748 
CVInkscapeAdjust(CharView * cv)6749 static void CVInkscapeAdjust(CharView *cv) {
6750     CharViewTab* tab = CVGetActiveTab(cv);
6751     /* Inkscape considers different coordinates useful. That is to say, */
6752     /*  Inkscape views the world as a blank sheet of paper and often */
6753     /*  put things outside the [0,1000] range (especially in Y) that */
6754     /*  FF uses. So after doing a Paste, or Import or something similar */
6755     /*  check and see if the image is completely out of view, and if so */
6756     /*  then adjust the view field */
6757     DBounds b;
6758     int layer = CVLayer((CharViewBase *) cv);
6759 
6760     if (layer != ly_grid) SplineCharLayerQuickBounds(cv->b.sc,layer,&b);
6761     else {
6762         b.minx = b.miny = 1e10;
6763         b.maxx = b.maxy = -1e10;
6764 	SplineSetQuickBounds(cv->b.sc->parent->grid.splines,&b);
6765     }
6766 
6767     b.minx *= tab->scale; b.maxx *= tab->scale;
6768     b.miny *= tab->scale; b.maxy *= tab->scale;
6769 
6770     if ( b.minx + tab->xoff < 0 || b.miny + tab->yoff < 0 ||
6771 	    b.maxx + tab->xoff > cv->width || b.maxy + tab->yoff > cv->height )
6772 	CVFit(cv);
6773 }
6774 
CVMenuImport(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6775 static void CVMenuImport(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6776     CharView *cv = (CharView *) GDrawGetUserData(gw);
6777     CVImport(cv);
6778     CVInkscapeAdjust(cv);
6779 }
6780 
CVMenuRevert(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6781 static void CVMenuRevert(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6782     CharView *cv = (CharView *) GDrawGetUserData(gw);
6783     FVDelay((FontView *) (cv->b.fv),(void (*)(FontView *)) FVRevert);
6784 			    /* The revert command can potentially */
6785 			    /* destroy our window (if the char weren't in the */
6786 			    /* old font). If that happens before the menu finishes */
6787 			    /* we get a crash. So delay till after the menu completes */
6788 }
6789 
CVMenuRevertGlyph(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6790 static void CVMenuRevertGlyph(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6791     CharView *cv = (CharView *) GDrawGetUserData(gw);
6792     SplineChar *sc, temp;
6793     Undoes **undoes;
6794     int layer, lc;
6795     CharView *cvs;
6796     int mylayer = CVLayer((CharViewBase *) cv);
6797 
6798     if ( cv->b.sc->parent->filename==NULL || cv->b.sc->namechanged || cv->b.sc->parent->mm!=NULL )
6799 return;
6800     if ( cv->b.sc->parent->sfd_version<2 )
6801 	ff_post_error(_("Old sfd file"),_("This font comes from an old format sfd file. Not all aspects of it can be reverted successfully."));
6802 
6803     sc = SFDReadOneChar(cv->b.sc->parent,cv->b.sc->name);
6804     if ( sc==NULL ) {
6805 	ff_post_error(_("Can't Find Glyph"),_("The glyph, %.80s, can't be found in the sfd file"),cv->b.sc->name);
6806 	cv->b.sc->namechanged = true;
6807     } else {
6808 	SCPreserveState(cv->b.sc,true);
6809 	SCPreserveBackground(cv->b.sc);
6810 	temp = *cv->b.sc;
6811 	cv->b.sc->dependents = NULL;
6812 	lc = cv->b.sc->layer_cnt;
6813 	undoes = malloc(lc*sizeof(Undoes *));
6814 	for ( layer=0; layer<lc; ++layer ) {
6815 	    undoes[layer] = cv->b.sc->layers[layer].undoes;
6816 	    cv->b.sc->layers[layer].undoes = NULL;
6817 	}
6818 	SplineCharFreeContents(cv->b.sc);
6819 	*cv->b.sc = *sc;
6820 	chunkfree(sc,sizeof(SplineChar));
6821 	cv->b.sc->parent = temp.parent;
6822 	cv->b.sc->dependents = temp.dependents;
6823 	for ( layer = 0; layer<lc && layer<cv->b.sc->layer_cnt; ++layer )
6824 	    cv->b.sc->layers[layer].undoes = undoes[layer];
6825 	for ( ; layer<lc; ++layer )
6826 	    UndoesFree(undoes[layer]);
6827 	free(undoes);
6828 	cv->b.sc->views = temp.views;
6829 	/* cv->b.sc->changed = temp.changed; */
6830 	for ( cvs=(CharView *) (cv->b.sc->views); cvs!=NULL; cvs=(CharView *) (cvs->b.next) ) {
6831 	    cvs->b.layerheads[dm_back] = &cv->b.sc->layers[ly_back];
6832 	    cvs->b.layerheads[dm_fore] = &cv->b.sc->layers[ly_fore];
6833 	    if ( cv->b.sc->parent->multilayer ) {
6834 		if ( mylayer!=ly_back )
6835 		    cvs->b.layerheads[dm_fore] = &cv->b.sc->layers[mylayer];
6836 	    } else {
6837 		if ( mylayer!=ly_fore )
6838 		    cvs->b.layerheads[dm_back] = &cv->b.sc->layers[mylayer];
6839 	    }
6840 	}
6841 	RevertedGlyphReferenceFixup(cv->b.sc, temp.parent);
6842 	_CV_CharChangedUpdate(cv,false);
6843     }
6844 }
6845 
CVAddWordList(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6846 static void CVAddWordList(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e))
6847 {
6848     CharView *cv = (CharView *) GDrawGetUserData(gw);
6849     WordlistLoadToGTextInfo( cv->charselector, &cv->charselectoridx );
6850 }
6851 
CVMenuPrint(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6852 static void CVMenuPrint(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6853     CharView *cv = (CharView *) GDrawGetUserData(gw);
6854 
6855     PrintFFDlg(NULL,cv->b.sc,NULL);
6856 }
6857 
6858 #if !defined(_NO_PYTHON)
CVMenuExecute(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6859 static void CVMenuExecute(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6860     CharView *cv = (CharView *) GDrawGetUserData(gw);
6861     ScriptDlg((FontView *) (cv->b.fv),cv);
6862 }
6863 #endif		/* !_NO_PYTHON */
6864 
fllistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))6865 static void fllistcheck(GWindow gw, struct gmenuitem *mi,GEvent *UNUSED(e)) {
6866     CharView *cv = (CharView *) GDrawGetUserData(gw);
6867     FontView *fvs;
6868 
6869     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
6870 	switch ( mi->mid ) {
6871 	  case MID_Open: case MID_New:
6872 	    mi->ti.disabled = cv->b.container!=NULL && !(cv->b.container->funcs->canOpen)(cv->b.container);
6873 	  break;
6874 	  case MID_GenerateTTC:
6875 	    for ( fvs=fv_list; fvs!=NULL; fvs=(FontView *) (fvs->b.next) ) {
6876 		if ( fvs!=(FontView *) cv->b.fv )
6877 	    break;
6878 	    }
6879 	    mi->ti.disabled = fvs==NULL || cv->b.container!=NULL;
6880 	  break;
6881 	  case MID_Revert:
6882 	    mi->ti.disabled = cv->b.fv->sf->origname==NULL || cv->b.fv->sf->new || cv->b.container;
6883 	  break;
6884 	  case MID_RevertGlyph:
6885 	    mi->ti.disabled = cv->b.fv->sf->filename==NULL ||
6886 		    cv->b.fv->sf->sfd_version<2 ||
6887 		    cv->b.sc->namechanged ||
6888 		    cv->b.fv->sf->mm!=NULL ||
6889 		    cv->b.container;
6890 	  break;
6891 	  case MID_Recent:
6892 	    mi->ti.disabled = !RecentFilesAny() ||
6893 		    (cv->b.container!=NULL && !(cv->b.container->funcs->canOpen)(cv->b.container));
6894 	  break;
6895 	  case MID_Close: case MID_Quit:
6896 	    mi->ti.disabled = false;
6897 	  break;
6898 	  case MID_CloseTab:
6899 	    mi->ti.disabled = cv->tabs==NULL || cv->former_cnt<=1;
6900 	  break;
6901 	  default:
6902 	    mi->ti.disabled = cv->b.container!=NULL;
6903 	  break;
6904 	}
6905     }
6906 }
6907 
CVMenuFontInfo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6908 static void CVMenuFontInfo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6909     CharView *cv = (CharView *) GDrawGetUserData(gw);
6910     FontInfo(cv->b.sc->parent,CVLayer((CharViewBase *) cv),-1,false);
6911 }
6912 
CVMenuFindProblems(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6913 static void CVMenuFindProblems(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6914     CharView *cv = (CharView *) GDrawGetUserData(gw);
6915     FindProblems(NULL,cv,NULL);
6916 }
6917 
CVMenuEmbolden(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6918 static void CVMenuEmbolden(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6919     CharView *cv = (CharView *) GDrawGetUserData(gw);
6920     EmboldenDlg(NULL,cv);
6921 }
6922 
CVMenuItalic(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6923 static void CVMenuItalic(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6924     CharView *cv = (CharView *) GDrawGetUserData(gw);
6925     ItalicDlg(NULL,cv);
6926 }
6927 
CVMenuOblique(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6928 static void CVMenuOblique(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6929     CharView *cv = (CharView *) GDrawGetUserData(gw);
6930     ObliqueDlg(NULL,cv);
6931 }
6932 
CVMenuCondense(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6933 static void CVMenuCondense(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6934     CharView *cv = (CharView *) GDrawGetUserData(gw);
6935     CondenseExtendDlg(NULL,cv);
6936 }
6937 
CVMenuChangeXHeight(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6938 static void CVMenuChangeXHeight(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6939     CharView *cv = (CharView *) GDrawGetUserData(gw);
6940     ChangeXHeightDlg(NULL,cv);
6941 }
6942 
CVMenuChangeGlyph(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6943 static void CVMenuChangeGlyph(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6944     CharView *cv = (CharView *) GDrawGetUserData(gw);
6945     GlyphChangeDlg(NULL,cv,gc_generic);
6946 }
6947 
CVMenuInline(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6948 static void CVMenuInline(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6949     CharView *cv = (CharView *) GDrawGetUserData(gw);
6950     OutlineDlg(NULL,cv,NULL,true);
6951 }
6952 
CVMenuOutline(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6953 static void CVMenuOutline(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6954     CharView *cv = (CharView *) GDrawGetUserData(gw);
6955     OutlineDlg(NULL,cv,NULL,false);
6956 }
6957 
CVMenuShadow(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6958 static void CVMenuShadow(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6959     CharView *cv = (CharView *) GDrawGetUserData(gw);
6960     ShadowDlg(NULL,cv,NULL,false);
6961 }
6962 
CVMenuWireframe(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6963 static void CVMenuWireframe(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6964     CharView *cv = (CharView *) GDrawGetUserData(gw);
6965     ShadowDlg(NULL,cv,NULL,true);
6966 }
6967 
_CVMenuScale(CharView * cv,int mid)6968 static void _CVMenuScale(CharView *cv, int mid) {
6969     CharViewTab* tab = CVGetActiveTab(cv);
6970     if ( mid == MID_Fit ) {
6971 	CVFit(cv);
6972     } else {
6973 	BasePoint c;
6974 	c.x = (cv->width/2-tab->xoff)/tab->scale;
6975 	c.y = (cv->height/2-tab->yoff)/tab->scale;
6976 	if ( CVAnySel(cv,NULL,NULL,NULL,NULL))
6977 	    CVFindCenter(cv,&c,false);
6978 	CVMagnify(cv,c.x,c.y, mid==MID_ZoomOut?-1:1,0);
6979     }
6980 }
6981 
CVMenuScale(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))6982 static void CVMenuScale(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
6983     CharView *cv = (CharView *) GDrawGetUserData(gw);
6984     _CVMenuScale(cv,mi->mid);
6985 }
6986 
CVMenuShowHide(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6987 static void CVMenuShowHide(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6988     CharView *cv = (CharView *) GDrawGetUserData(gw);
6989     CVShows.showpoints = cv->showpoints = !cv->showpoints;
6990     GDrawRequestExpose(cv->v,NULL,false);
6991 }
6992 
CVMenuShowHideControlPoints(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))6993 static void CVMenuShowHideControlPoints(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
6994     CharView *cv = (CharView *) GDrawGetUserData(gw);
6995     CVShows.alwaysshowcontrolpoints = cv->alwaysshowcontrolpoints = !cv->alwaysshowcontrolpoints;
6996     GDrawRequestExpose(cv->v,NULL,false);
6997 }
6998 
CVMenuNumberPoints(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))6999 static void CVMenuNumberPoints(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
7000     CharView *cv = (CharView *) GDrawGetUserData(gw);
7001 
7002     switch ( mi->mid ) {
7003       case MID_PtsNone:
7004 	cv->showpointnumbers = 0;
7005       break;
7006       case MID_PtsTrue:
7007 	cv->showpointnumbers = 1;
7008 	CVCheckPoints(cv);
7009       break;
7010       case MID_PtsPost:
7011 	cv->showpointnumbers = 1;
7012 	cv->b.sc->numberpointsbackards = true;
7013       break;
7014       case MID_PtsSVG:
7015 	cv->showpointnumbers = 1;
7016 	cv->b.sc->numberpointsbackards = false;
7017       break;
7018       case MID_PtsPos:
7019         cv->showpointnumbers = 2;
7020       break;
7021     }
7022     SCNumberPoints(cv->b.sc,CVLayer((CharViewBase *) cv));
7023     SCUpdateAll(cv->b.sc);
7024 }
7025 
CVMenuMarkExtrema(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7026 static void CVMenuMarkExtrema(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7027     CharView *cv = (CharView *) GDrawGetUserData(gw);
7028 
7029     CVShows.markextrema = cv->markextrema = !cv->markextrema;
7030     SavePrefs(true);
7031     GDrawRequestExpose(cv->v,NULL,false);
7032 }
7033 
CVMenuMarkPointsOfInflection(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7034 static void CVMenuMarkPointsOfInflection(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7035     CharView *cv = (CharView *) GDrawGetUserData(gw);
7036 
7037     CVShows.markpoi = cv->markpoi = !cv->markpoi;
7038     SavePrefs(true);
7039     GDrawRequestExpose(cv->v,NULL,false);
7040 }
7041 
CVMenuShowAlmostHV(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7042 static void CVMenuShowAlmostHV(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7043     CharView *cv = (CharView *) GDrawGetUserData(gw);
7044 
7045     CVShows.showalmosthvlines = cv->showalmosthvlines = !cv->showalmosthvlines;
7046     SavePrefs(true);
7047     GDrawRequestExpose(cv->v,NULL,false);
7048 }
7049 
CVMenuShowAlmostHVCurves(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7050 static void CVMenuShowAlmostHVCurves(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7051     CharView *cv = (CharView *) GDrawGetUserData(gw);
7052 
7053     CVShows.showalmosthvcurves = cv->showalmosthvcurves = !cv->showalmosthvcurves;
7054     SavePrefs(true);
7055     GDrawRequestExpose(cv->v,NULL,false);
7056 }
7057 
CVMenuDefineAlmost(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7058 static void CVMenuDefineAlmost(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7059     CharView *cv = (CharView *) GDrawGetUserData(gw);
7060     char buf[20], *end;
7061     int val;
7062     char *ret;
7063 
7064     sprintf(buf,"%d",cv->hvoffset );
7065 
7066     ret = gwwv_ask_string(_("Define \"Almost Horizontal\""),buf,
7067 	    _("A line is \"almost\" horizontal (or vertical)\nif the coordinates are within this many em-units"));
7068     if ( ret==NULL )
7069 return;
7070     val = strtol(ret,&end,10);
7071     if ( val>100 || val<=0 || *end!='\0' ) {
7072 	free(ret);
7073 	ff_post_error(_("Bad number"),_("Bad number"));
7074     } else {
7075 	free(ret);
7076 	CVShows.hvoffset = cv->hvoffset = val;
7077 	SavePrefs(true);
7078 	GDrawRequestExpose(cv->v,NULL,false);
7079     }
7080 }
7081 
CVMenuShowCPInfo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7082 static void CVMenuShowCPInfo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7083     CharView *cv = (CharView *) GDrawGetUserData(gw);
7084 
7085     CVShows.showcpinfo = cv->showcpinfo = !cv->showcpinfo;
7086     SavePrefs(true);
7087     /* Nothing to update, only show this stuff in the user is moving a cp */
7088     /*  which s/he is currently not, s/he is manipulating the menu */
7089 }
7090 
CVMenuShowTabs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7091 static void CVMenuShowTabs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7092     CharView *cv = (CharView *) GDrawGetUserData(gw);
7093 
7094     CVShows.showtabs = cv->showtabs = !cv->showtabs;
7095     CVChangeTabsVisibility(cv,cv->showtabs);
7096     SavePrefs(true);
7097 }
7098 
CVMenuShowSideBearings(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7099 static void CVMenuShowSideBearings(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7100     CharView *cv = (CharView *) GDrawGetUserData(gw);
7101 
7102     CVShows.showsidebearings = cv->showsidebearings = !cv->showsidebearings;
7103     SavePrefs(true);
7104     GDrawRequestExpose(cv->v,NULL,false);
7105 }
7106 
CVMenuShowRefNames(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7107 static void CVMenuShowRefNames(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7108     CharView *cv = (CharView *) GDrawGetUserData(gw);
7109 
7110     CVShows.showrefnames = cv->showrefnames = !cv->showrefnames;
7111     SavePrefs(true);
7112     GDrawRequestExpose(cv->v,NULL,false);
7113 }
7114 
CVMenuSnapOutlines(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7115 static void CVMenuSnapOutlines(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7116     CharView *cv = (CharView *) GDrawGetUserData(gw);
7117 
7118     CVShows.snapoutlines = cv->snapoutlines = !cv->snapoutlines;
7119     SavePrefs(true);
7120     GDrawRequestExpose(cv->v,NULL,false);
7121 }
7122 
CVMenuShowHints(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))7123 static void CVMenuShowHints(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
7124     CharView *cv = (CharView *) GDrawGetUserData(gw);
7125 
7126     switch ( mi->mid ) {
7127       case MID_ShowHHints:
7128 	CVShows.showhhints = cv->showhhints = !cv->showhhints;
7129 	cv->back_img_out_of_date = true;	/* only this cv */
7130       break;
7131       case MID_ShowVHints:
7132 	CVShows.showvhints = cv->showvhints = !cv->showvhints;
7133 	cv->back_img_out_of_date = true;	/* only this cv */
7134       break;
7135       case MID_ShowDHints:
7136 	CVShows.showdhints = cv->showdhints = !cv->showdhints;
7137 	cv->back_img_out_of_date = true;	/* only this cv */
7138       break;
7139       case MID_ShowBlueValues:
7140 	CVShows.showblues = cv->showblues = !cv->showblues;
7141 	cv->back_img_out_of_date = true;	/* only this cv */
7142       break;
7143       case MID_ShowFamilyBlues:
7144 	CVShows.showfamilyblues = cv->showfamilyblues = !cv->showfamilyblues;
7145 	cv->back_img_out_of_date = true;	/* only this cv */
7146       break;
7147       case MID_ShowAnchors:
7148 	CVShows.showanchor = cv->showanchor = !cv->showanchor;
7149       break;
7150       case MID_ShowHMetrics:
7151 	CVShows.showhmetrics = cv->showhmetrics = !cv->showhmetrics;
7152       break;
7153       case MID_ShowVMetrics:
7154 	CVShows.showvmetrics = cv->showvmetrics = !cv->showvmetrics;
7155       break;
7156       case MID_ShowDebugChanges:
7157 	CVShows.showdebugchanges = cv->showdebugchanges = !cv->showdebugchanges;
7158       break;
7159       default:
7160         IError("Unexpected call to CVMenuShowHints");
7161       break;
7162     }
7163     SavePrefs(true);
7164     GDrawRequestExpose(cv->v,NULL,false);
7165     /* !!!! In this interim version we should request an expose on cvlayers */
7166     /*  but that's private to cvpalettes, and later we won't need to */
7167 }
7168 
_CVMenuShowHideRulers(CharView * cv)7169 static void _CVMenuShowHideRulers(CharView *cv) {
7170     GRect pos;
7171 
7172     CVShows.showrulers = cv->showrulers = !cv->showrulers;
7173     pos.y = cv->mbh+cv->charselectorh+cv->infoh;
7174     pos.x = 0;
7175     if ( cv->showrulers ) {
7176 	cv->height -= cv->rulerh;
7177 	cv->width -= cv->rulerh;
7178 	pos.y += cv->rulerh;
7179 	pos.x += cv->rulerh;
7180     } else {
7181 	cv->height += cv->rulerh;
7182 	cv->width += cv->rulerh;
7183     }
7184     cv->back_img_out_of_date = true;
7185     pos.width = cv->width; pos.height = cv->height;
7186     GDrawMoveResize(cv->v,pos.x,pos.y,pos.width,pos.height);
7187     GDrawSync(NULL);
7188     GDrawRequestExpose(cv->v,NULL,false);
7189     SavePrefs(true);
7190 }
7191 
CVMenuShowHideRulers(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7192 static void CVMenuShowHideRulers(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7193     CharView *cv = (CharView *) GDrawGetUserData(gw);
7194     _CVMenuShowHideRulers(cv);
7195 }
7196 
CVMenuFill(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7197 static void CVMenuFill(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7198     CharView *cv = (CharView *) GDrawGetUserData(gw);
7199 
7200     CVShows.showfilled = cv->showfilled = !cv->showfilled;
7201     CVRegenFill(cv);
7202     GDrawRequestExpose(cv->v,NULL,false);
7203 }
7204 
cvshowsCopyTo(struct cvshows * dst,CharView * src)7205 static struct cvshows* cvshowsCopyTo( struct cvshows* dst, CharView* src )
7206 {
7207     dst->showfore = src->showfore;
7208     dst->showgrids = src->showgrids;
7209     dst->showhhints = src->showhhints;
7210     dst->showvhints = src->showvhints;
7211     dst->showdhints = src->showdhints;
7212     dst->showpoints = src->showpoints;
7213     dst->alwaysshowcontrolpoints = src->alwaysshowcontrolpoints;
7214     dst->showfilled = src->showfilled;
7215     dst->showrulers = src->showrulers;
7216     dst->showrounds = src->showrounds;
7217     dst->showmdx = src->showmdx;
7218     dst->showmdy = src->showmdy;
7219     dst->showhmetrics = src->showhmetrics;
7220     dst->showvmetrics = src->showvmetrics;
7221     dst->markextrema = src->markextrema;
7222     dst->markpoi = src->markpoi;
7223     dst->showblues = src->showblues;
7224     dst->showfamilyblues = src->showfamilyblues;
7225     dst->showanchor = src->showanchor;
7226     dst->showcpinfo = src->showcpinfo;
7227     dst->showtabs = src->showtabs;
7228     dst->showsidebearings = src->showsidebearings;
7229     dst->showrefnames = src->showrefnames;
7230     dst->snapoutlines = src->snapoutlines;
7231     dst->showalmosthvlines = src->showalmosthvlines;
7232     dst->showalmosthvcurves = src->showalmosthvcurves;
7233     dst->hvoffset = src->hvoffset;
7234     dst->checkselfintersects = src->checkselfintersects;
7235     dst->showdebugchanges = src->showdebugchanges;
7236     return dst;
7237 }
7238 
cvshowsCopyFrom(CharView * dst,struct cvshows * src)7239 static CharView* cvshowsCopyFrom( CharView* dst, struct cvshows* src )
7240 {
7241     dst->showfore = src->showfore;
7242     dst->showgrids = src->showgrids;
7243     dst->showhhints = src->showhhints;
7244     dst->showvhints = src->showvhints;
7245     dst->showdhints = src->showdhints;
7246     dst->showpoints = src->showpoints;
7247     dst->alwaysshowcontrolpoints = src->alwaysshowcontrolpoints;
7248     dst->showfilled = src->showfilled;
7249     dst->showrulers = src->showrulers;
7250     dst->showrounds = src->showrounds;
7251     dst->showmdx = src->showmdx;
7252     dst->showmdy = src->showmdy;
7253     dst->showhmetrics = src->showhmetrics;
7254     dst->showvmetrics = src->showvmetrics;
7255     dst->markextrema = src->markextrema;
7256     dst->markpoi = src->markpoi;
7257     dst->showblues = src->showblues;
7258     dst->showfamilyblues = src->showfamilyblues;
7259     dst->showanchor = src->showanchor;
7260     dst->showcpinfo = src->showcpinfo;
7261     dst->showtabs = src->showtabs;
7262     dst->showsidebearings = src->showsidebearings;
7263     dst->showrefnames = src->showrefnames;
7264     dst->snapoutlines = src->snapoutlines;
7265     dst->showalmosthvlines = src->showalmosthvlines;
7266     dst->showalmosthvcurves = src->showalmosthvcurves;
7267     dst->hvoffset = src->hvoffset;
7268     dst->checkselfintersects = src->checkselfintersects;
7269     dst->showdebugchanges = src->showdebugchanges;
7270     return dst;
7271 }
7272 
CVPreviewModeSet(GWindow gw,int checked)7273 static void CVPreviewModeSet(GWindow gw, int checked ) {
7274     CharView *cv = (CharView *) GDrawGetUserData(gw);
7275     if( checked == cv->inPreviewMode )
7276         return;
7277 
7278     cv->inPreviewMode = checked;
7279     if( checked ) {
7280         cvshowsCopyTo( &CVShowsPrevewToggleSavedState, cv );
7281         cv->showfore   = 1;
7282         cv->showgrids  = 0;
7283         cv->showhhints = 0;
7284         cv->showvhints = 0;
7285         cv->showdhints = 0;
7286         cv->showpoints = 0;
7287 	cv->alwaysshowcontrolpoints = 0;
7288         cv->showfilled = 1;
7289         cv->showrounds = 0;
7290         cv->showanchor = 0;
7291         cv->showrefnames = 0;
7292         cv->showhmetrics = 0;
7293         cv->showvmetrics = 0;
7294     } else {
7295         cvshowsCopyFrom( cv, &CVShowsPrevewToggleSavedState );
7296     }
7297     CVRegenFill(cv);
7298     GDrawRequestExpose(cv->v,NULL,false);
7299 }
7300 
CVMenuPreview(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))7301 static void CVMenuPreview(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
7302     int checked = mi->ti.checked;
7303 
7304 #if 0
7305     // This has the effect of breaking the button since cv_auto_goto and HaveModifiers tend to be false.
7306     // The purpose of these checks is unclear.
7307     if( !cv_auto_goto ) {
7308 	if( !HaveModifiers )
7309 	    return;
7310     }
7311 #endif // 0
7312 
7313     CVPreviewModeSet(gw, checked);
7314 }
7315 
CVMenuDraggingComparisonOutline(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))7316 static void CVMenuDraggingComparisonOutline(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e))
7317 {
7318     CharView *cv = (CharView *) GDrawGetUserData(gw);
7319 
7320     int checked = mi->ti.checked;
7321     SplinePointListFree(cv->p.pretransform_spl);
7322     cv->p.pretransform_spl = NULL;
7323     prefs_create_dragging_comparison_outline = checked;
7324     SavePrefs(true);
7325 }
7326 
7327 
CVMenuShowGridFit(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7328 static void CVMenuShowGridFit(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7329     CharView *cv = (CharView *) GDrawGetUserData(gw);
7330 
7331     if ( !hasFreeType() || cv->dv!=NULL )
7332 	return;
7333     cv->show_ft_results_live_update = 0;
7334     CVFtPpemDlg(cv,false);
7335 }
7336 
CVMenuShowGridFitLiveUpdate(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7337 static void CVMenuShowGridFitLiveUpdate(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7338     CharView *cv = (CharView *) GDrawGetUserData(gw);
7339 
7340     if ( !hasFreeType() || cv->dv!=NULL )
7341 	return;
7342     cv->show_ft_results_live_update = 1;
7343     CVFtPpemDlg(cv,false);
7344 }
7345 
CVMenuChangePointSize(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))7346 static void CVMenuChangePointSize(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
7347     CharView *cv = (CharView *) GDrawGetUserData(gw);
7348 
7349     if ( !hasFreeType() || cv->dv!=NULL || !cv->show_ft_results )
7350 return;
7351 
7352     if ( mi->mid==MID_GridFitOff ) {
7353 	cv->show_ft_results = false;
7354 	cv->show_ft_results_live_update = false;
7355 
7356 	SplinePointListsFree(cv->b.gridfit); cv->b.gridfit = NULL;
7357 	FreeType_FreeRaster(cv->raster); cv->raster = NULL;
7358 	GDrawRequestExpose(cv->v,NULL,false);
7359     } else {
7360 	if ( mi->mid==MID_Bigger ) {
7361 	    ++cv->ft_pointsizex;
7362 	    ++cv->ft_pointsizey;
7363 	} else if ( mi->mid==MID_Smaller ) {
7364 	    if ( cv->ft_pointsizex>1 )
7365 		--cv->ft_pointsizex;
7366 	    if ( cv->ft_pointsizey>1 )
7367 		--cv->ft_pointsizey;
7368 	}
7369 
7370 	if ( mi->mid==MID_GridFitAA )
7371 	    cv->ft_depth = cv->ft_depth==8 ? 1 : 8;
7372 	cv->ft_ppemx = rint(cv->ft_pointsizex*cv->ft_dpi/72.0);
7373 	cv->ft_ppemy = rint(cv->ft_pointsizey*cv->ft_dpi/72.0);
7374 	CVGridFitChar(cv);
7375     }
7376     SCRefreshTitles(cv->b.sc);
7377 }
7378 
CVMenuEditInstrs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7379 static void CVMenuEditInstrs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7380     CharView *cv = (CharView *) GDrawGetUserData(gw);
7381     SCEditInstructions(cv->b.sc);
7382 }
7383 
CVMenuDebug(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7384 static void CVMenuDebug(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7385     CharView *cv = (CharView *) GDrawGetUserData(gw);
7386 
7387     if ( !hasFreeTypeDebugger())
7388 return;
7389     CVFtPpemDlg(cv,true);
7390 }
7391 
CVMenuDeltas(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7392 static void CVMenuDeltas(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7393     CharView *cv = (CharView *) GDrawGetUserData(gw);
7394 
7395     if ( !hasFreeTypeDebugger())
7396 return;
7397     DeltaSuggestionDlg(NULL,cv);
7398 }
7399 
CVMenuClearInstrs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7400 static void CVMenuClearInstrs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7401     CharView *cv = (CharView *) GDrawGetUserData(gw);
7402 
7403     if ( cv->b.sc->ttf_instrs_len!=0 ) {
7404 	free(cv->b.sc->ttf_instrs);
7405 	cv->b.sc->ttf_instrs = NULL;
7406 	cv->b.sc->ttf_instrs_len = 0;
7407 	cv->b.sc->instructions_out_of_date = false;
7408 	SCCharChangedUpdate(cv->b.sc,ly_none);
7409 	SCMarkInstrDlgAsChanged(cv->b.sc);
7410 	cv->b.sc->complained_about_ptnums = false;	/* Should be after CharChanged */
7411     }
7412 }
7413 
CVMenuKernPairs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7414 static void CVMenuKernPairs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7415     CharView *cv = (CharView *) GDrawGetUserData(gw);
7416     SFShowKernPairs(cv->b.sc->parent,cv->b.sc,NULL,CVLayer((CharViewBase *) cv));
7417 }
7418 
CVMenuLigatures(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7419 static void CVMenuLigatures(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7420     CharView *cv = (CharView *) GDrawGetUserData(gw);
7421     SFShowLigatures(cv->b.fv->sf,cv->b.sc);
7422 }
7423 
CVMenuAnchorPairs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7424 static void CVMenuAnchorPairs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7425     CharView *cv = (CharView *) GDrawGetUserData(gw);
7426     SFShowKernPairs(cv->b.sc->parent,cv->b.sc,(AnchorClass *) (-1),CVLayer((CharViewBase *) cv));
7427 }
7428 
CVMenuAPDetach(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))7429 static void CVMenuAPDetach(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
7430     CharView *cv = (CharView *) GDrawGetUserData(gw);
7431     cv->apmine = cv->apmatch = NULL;
7432     cv->apsc = NULL;
7433     GDrawRequestExpose(cv->v,NULL,false);
7434 }
7435 
CVMenuAPAttachSC(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))7436 static void CVMenuAPAttachSC(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
7437     CharView *cv = (CharView *) GDrawGetUserData(gw);
7438     enum anchor_type type;
7439     AnchorPoint *ap;
7440     AnchorClass *ac;
7441 
7442     for ( ap = cv->b.sc->anchor; ap!=NULL && !ap->selected; ap=ap->next );
7443     if ( ap==NULL )
7444 	ap = cv->b.sc->anchor;
7445     if ( ap==NULL )
7446 return;
7447     type = ap->type;
7448     cv->apmine = ap;
7449     ac = ap->anchor;
7450     cv->apsc = mi->ti.userdata;
7451     for ( ap = cv->apsc->anchor; ap!=NULL ; ap=ap->next ) {
7452 	if ( ap->anchor==ac &&
7453 		((type==at_centry && ap->type==at_cexit) ||
7454 		 (type==at_cexit && ap->type==at_centry) ||
7455 		 (type==at_mark && ap->type!=at_mark) ||
7456 		 ((type==at_basechar || type==at_baselig || type==at_basemark) && ap->type==at_mark)) )
7457     break;
7458     }
7459     cv->apmatch = ap;
7460     GDrawRequestExpose(cv->v,NULL,false);
7461 }
7462 
GlyphsMatchingAP(SplineFont * sf,AnchorPoint * ap)7463 static SplineChar **GlyphsMatchingAP(SplineFont *sf, AnchorPoint *ap) {
7464     SplineChar *sc;
7465     AnchorClass *ac = ap->anchor;
7466     enum anchor_type type = ap->type;
7467     int i, k, gcnt;
7468     SplineChar **glyphs;
7469 
7470     glyphs = NULL;
7471     for ( k=0; k<2; ++k ) {
7472 	gcnt = 0;
7473 	for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc=sf->glyphs[i])!=NULL ) {
7474 	    for ( ap = sc->anchor; ap!=NULL; ap=ap->next ) {
7475 		if ( ap->anchor == ac &&
7476 			((type==at_centry && ap->type==at_cexit) ||
7477 			 (type==at_cexit && ap->type==at_centry) ||
7478 			 (type==at_mark && ap->type!=at_mark) ||
7479 			 ((type==at_basechar || type==at_baselig || type==at_basemark) && ap->type==at_mark)) )
7480 	     break;
7481 	     }
7482 	     if ( ap!=NULL ) {
7483 		 if ( k )
7484 		     glyphs[gcnt] = sc;
7485 		 ++gcnt;
7486 	     }
7487 	 }
7488 	 if ( !k ) {
7489 	     if ( gcnt==0 )
7490 return( NULL );
7491 	     glyphs = malloc((gcnt+1)*sizeof(SplineChar *));
7492 	 } else
7493 	     glyphs[gcnt] = NULL;
7494      }
7495 return( glyphs );
7496 }
7497 
_CVMenuChangeChar(CharView * cv,int mid)7498 static void _CVMenuChangeChar(CharView *cv, int mid) {
7499     SplineFont *sf = cv->b.sc->parent;
7500     int pos = -1;
7501     int gid;
7502     EncMap *map = cv->b.fv->map;
7503     Encoding *enc = map->enc;
7504 
7505     if ( cv->b.container!=NULL ) {
7506 	if ( cv->b.container->funcs->doNavigate!=NULL && mid != MID_Former)
7507 	    (cv->b.container->funcs->doNavigate)(cv->b.container,
7508 		    mid==MID_Next ? nt_next :
7509 		    mid==MID_Prev ? nt_prev :
7510 		    mid==MID_NextDef ? nt_next :
7511 		    /* mid==MID_PrevDef ?*/ nt_prev);
7512 return;
7513     }
7514 
7515     int curenc = CVCurEnc(cv);
7516 
7517     if( cv->charselector )
7518     {
7519         char* txt = GGadgetGetTitle8( cv->charselector );
7520         if( txt && strlen(txt) > 1 )
7521         {
7522             int offset = 1;
7523             if ( mid == MID_Prev )
7524                 offset = -1;
7525 
7526 	    unichar_t *txtu = GGadgetGetTitle( cv->charselector );
7527             unichar_t* r = Wordlist_advanceSelectedCharsBy( cv->b.sc->parent,
7528                                                             ((FontView *) (cv->b.fv))->b.map,
7529                                                             txtu, offset );
7530             free( txtu );
7531 
7532 	    GGadgetSetTitle( cv->charselector, r );
7533             // Force any extra chars to be setup and drawn
7534             GEvent e;
7535             e.type=et_controlevent;
7536             e.u.control.subtype = et_textchanged;
7537             e.u.control.u.tf_changed.from_pulldown = 0;
7538             CV_OnCharSelectorTextChanged( cv->charselector, &e );
7539             return;
7540         }
7541     }
7542 
7543     if ( mid == MID_Next ) {
7544 	pos = curenc+1;
7545     } else if ( mid == MID_Prev ) {
7546 	pos = curenc-1;
7547     } else if ( mid == MID_NextDef ) {
7548 	for ( pos = CVCurEnc(cv)+1; pos<map->enccount &&
7549 		((gid=map->map[pos])==-1 || !SCWorthOutputting(sf->glyphs[gid])); ++pos );
7550 	if ( pos>=map->enccount ) {
7551 	    if ( enc->is_tradchinese ) {
7552 		if ( strstrmatch(enc->enc_name,"hkscs")!=NULL ) {
7553 		    if ( CVCurEnc(cv)<0x8140 )
7554 			pos = 0x8140;
7555 		} else {
7556 		    if ( CVCurEnc(cv)<0xa140 )
7557 			pos = 0xa140;
7558 		}
7559 	    } else if ( CVCurEnc(cv)<0x8431 && strstrmatch(enc->enc_name,"johab")!=NULL )
7560 		pos = 0x8431;
7561 	    else if ( CVCurEnc(cv)<0xa1a1 && strstrmatch(enc->iconv_name?enc->iconv_name:enc->enc_name,"EUC")!=NULL )
7562 		pos = 0xa1a1;
7563 	    else if ( CVCurEnc(cv)<0x8140 && strstrmatch(enc->enc_name,"sjis")!=NULL )
7564 		pos = 0x8140;
7565 	    else if ( CVCurEnc(cv)<0xe040 && strstrmatch(enc->enc_name,"sjis")!=NULL )
7566 		pos = 0xe040;
7567 	    if ( pos>=map->enccount )
7568 return;
7569 	}
7570     } else if ( mid == MID_PrevDef ) {
7571 	for ( pos = CVCurEnc(cv)-1; pos>=0 &&
7572 		((gid=map->map[pos])==-1 || !SCWorthOutputting(sf->glyphs[gid])); --pos );
7573 	if ( pos<0 )
7574 return;
7575     } else if ( mid == MID_Former ) {
7576 	if ( cv->former_cnt<=1 )
7577 return;
7578 	for ( gid=sf->glyphcnt-1; gid>=0; --gid )
7579 	    if ( sf->glyphs[gid]!=NULL &&
7580 		    strcmp(sf->glyphs[gid]->name,cv->former_names[1])==0 )
7581 	break;
7582 	if ( gid<0 )
7583 return;
7584 	pos = map->backmap[gid];
7585     }
7586     /* Werner doesn't think it should wrap */
7587     if ( pos<0 ) /* pos = map->enccount-1; */
7588 return;
7589     else if ( pos>= map->enccount ) /* pos = 0; */
7590 return;
7591 
7592     if ( pos>=0 && pos<map->enccount )
7593 	CVChangeChar(cv,pos);
7594 }
7595 
7596 
CVMenuChangeChar(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))7597 static void CVMenuChangeChar(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
7598     CharView *cv = (CharView *) GDrawGetUserData(gw);
7599     _CVMenuChangeChar(cv,mi->mid);
7600 }
7601 
CVMoveToNextInWordList(GGadget * g,GEvent * e)7602 static int CVMoveToNextInWordList(GGadget *g, GEvent *e)
7603 {
7604     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
7605         CharView *cv = GDrawGetUserData(GGadgetGetWindow(g));
7606         CVMoveInWordListByOffset( cv, 1 );
7607     }
7608     return 1;
7609 }
CVMoveToPrevInWordList(GGadget * g,GEvent * e)7610 static int CVMoveToPrevInWordList(GGadget *g, GEvent *e)
7611 {
7612     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
7613         CharView *cv = GDrawGetUserData(GGadgetGetWindow(g));
7614         CVMoveInWordListByOffset( cv, -1 );
7615     }
7616     return 1;
7617 }
7618 
7619 /**
7620  * If the prev/next BCP is selected than add those to the hash table
7621  * at "ret".
7622  */
getSelectedControlPointsVisitor(SplinePoint * splfirst,Spline * s,SplinePoint * sp,void * udata)7623 static void getSelectedControlPointsVisitor(SplinePoint* splfirst, Spline* s, SplinePoint* sp, void* udata )
7624 {
7625     GHashTable* ret = (GHashTable*)udata;
7626     if( sp->nextcpselected )
7627 	g_hash_table_insert( ret, sp, 0 );
7628     if( sp->prevcpselected )
7629 	g_hash_table_insert( ret, sp, 0 );
7630 }
getAllControlPointsVisitor(SplinePoint * splfirst,Spline * s,SplinePoint * sp,void * udata)7631 static void getAllControlPointsVisitor(SplinePoint* splfirst, Spline* s, SplinePoint* sp, void* udata )
7632 {
7633     GHashTable* ret = (GHashTable*)udata;
7634     g_hash_table_insert( ret, sp, 0 );
7635     g_hash_table_insert( ret, sp, 0 );
7636 }
7637 
7638 /**
7639  * Get a hash table with all the selected BCP in it.
7640  *
7641  * The caller must call g_hash_table_destroy() on the return value.
7642  */
getSelectedControlPoints(CharView * cv,PressedOn * p)7643 static GHashTable* getSelectedControlPoints( CharView *cv, PressedOn *p )
7644 {
7645     Layer* l = cv->b.layerheads[cv->b.drawmode];
7646     if( !l || !l->splines )
7647 	return 0;
7648 
7649     GHashTable* ret = g_hash_table_new( g_direct_hash, g_direct_equal );
7650     SplinePointList* spl = l->splines;
7651     for( ; spl; spl = spl->next )
7652     {
7653 	SPLFirstVisitPoints( spl->first, getSelectedControlPointsVisitor, ret );
7654     }
7655 
7656     return ret;
7657 }
getAllControlPoints(CharView * cv,PressedOn * p)7658 static GHashTable* getAllControlPoints( CharView *cv, PressedOn *p )
7659 {
7660     Layer* l = cv->b.layerheads[cv->b.drawmode];
7661     if( !l || !l->splines )
7662 	return 0;
7663 
7664     GHashTable* ret = g_hash_table_new( g_direct_hash, g_direct_equal );
7665     SplinePointList* spl = l->splines;
7666     for( ; spl; spl = spl->next )
7667     {
7668 	SPLFirstVisitPoints( spl->first, getAllControlPointsVisitor, ret );
7669     }
7670     return ret;
7671 }
7672 
FE_touchControlPoint(void * key,void * value,SplinePoint * sp,BasePoint * which,bool isnext,void * udata)7673 void FE_touchControlPoint( void* key,
7674 			   void* value,
7675 			   SplinePoint* sp,
7676 			   BasePoint *which,
7677 			   bool isnext,
7678 			   void* udata )
7679 {
7680     TRACE("FE_touchControlPoint() which:%p\n", which );
7681     SPTouchControl( sp, which, (int)(intptr_t)udata );
7682 }
7683 
FE_unselectBCP(void * key,void * value,SplinePoint * sp,BasePoint * which,bool isnext,void * udata)7684 void FE_unselectBCP( void* key,
7685 		     void* value,
7686 		     SplinePoint* sp,
7687 		     BasePoint *which,
7688 		     bool isnext,
7689 		     void* udata )
7690 {
7691     sp->nextcpselected = 0;
7692     sp->prevcpselected = 0;
7693 }
7694 
FE_adjustBCPByDelta(void * key,void * value,SplinePoint * sp,BasePoint * which,bool isnext,void * udata)7695 void FE_adjustBCPByDelta( void* key,
7696 			  void* value,
7697 			  SplinePoint* sp,
7698 			  BasePoint *which,
7699 			  bool isnext,
7700 			  void* udata )
7701 {
7702     FE_adjustBCPByDeltaData* data = (FE_adjustBCPByDeltaData*)udata;
7703     CharView *cv = data->cv;
7704 
7705     TRACE("FE_adjustBCPByDelta %p %d\n", which, isnext );
7706     BasePoint to;
7707     to.x = which->x + data->dx;
7708     to.y = which->y + data->dy;
7709     SPAdjustControl(sp,which,&to,cv->b.layerheads[cv->b.drawmode]->order2);
7710     CVSetCharChanged(cv,true);
7711 }
7712 
FE_adjustBCPByDeltaWhilePreservingBCPAngle(void * key,void * value,SplinePoint * sp,BasePoint * which,bool isnext,void * udata)7713 void FE_adjustBCPByDeltaWhilePreservingBCPAngle( void* key,
7714 						 void* value,
7715 						 SplinePoint* sp,
7716 						 BasePoint *which,
7717 						 bool isnext,
7718 						 void* udata )
7719 {
7720     FE_adjustBCPByDeltaData* data = (FE_adjustBCPByDeltaData*)udata;
7721     CharView *cv = data->cv;
7722 
7723 //    TRACE("FE_adjustBCPByDeltaWhilePreservingBCPAngle which:%p data:%p isnext:%d\n", which, data, isnext );
7724 //    TRACE("FE_adjustBCPByDeltaWhilePreservingBCPAngle    delta %f %f\n", data->dx, data->dy );
7725     BasePoint to;
7726     to.x = which->x + data->dx;
7727     to.y = which->y + data->dy;
7728 
7729     // work out near and far BCP for this movement
7730     BasePoint* near = &sp->nextcp;
7731     BasePoint* far  = &sp->prevcp;
7732     if( !isnext )
7733     {
7734 	near = &sp->prevcp;
7735 	far  = &sp->nextcp;
7736     }
7737 
7738     real dx = near->x - far->x;
7739     if( !fabs(dx) )
7740     {
7741 	// flatline protection
7742 	to.x = which->x + data->dx;
7743 	to.y = which->y + data->dy;
7744     }
7745     else
7746     {
7747 	// we have a workable gradient
7748 	real m = (near->y - far->y) / dx;
7749 
7750 	// should we lean to x or y for the change?
7751 	// all this is based on m = y2-y1/x2-x1
7752 	// we use m from near and far and extrapolate to the new location
7753 	// based on either x or y and calculate the other ordinate from the
7754 	// gradiant function above.
7755 	if( fabs(m) < 1.0 )
7756 	{
7757 	    real datadx = data->dx;
7758 	    if( data->keyboarddx && !datadx )
7759 	    {
7760 		datadx = data->dy;
7761 		if( m < 0 )
7762 		    datadx *= -1;
7763 	    }
7764 
7765 	    real dx = near->x - far->x;
7766 	    real m = (near->y - far->y) / dx;
7767 	    to.x = which->x + datadx;
7768 	    to.y = m * (to.x - near->x) + near->y;
7769 	}
7770 	else
7771 	{
7772 	    real datady = data->dy;
7773 	    if( data->keyboarddx && !datady )
7774 	    {
7775 		datady = data->dx;
7776 		if( m < 0 )
7777 		    datady *= -1;
7778 	    }
7779 	    real dx = near->x - far->x;
7780 	    real m = (near->y - far->y) / dx;
7781 	    to.y = which->y + datady;
7782 	    to.x = (to.y - near->y + m*near->x) / m;
7783 	}
7784     }
7785 
7786     // move the point and update
7787     SPAdjustControl(sp,which,&to,cv->b.layerheads[cv->b.drawmode]->order2);
7788     CVSetCharChanged(cv,true);
7789 }
7790 
7791 /**
7792  * Container for arguments to FE_visitSelectedControlPoints.
7793  */
7794 typedef struct visitSelectedControlPoints_CallbackDataS
7795 {
7796     int count;                              // number of times visitor is called.
7797     int sel;
7798     visitSelectedControlPointsVisitor func; // Visitor function to delegate to
7799     gpointer udata;                         // user data to use when calling above func()
7800 
7801 } visitSelectedControlPoints_CallbackData;
7802 
7803 /**
7804  * Visitor function: calls a delegate visitor function for any prev
7805  * and next BCP which are selected for each spline point. This is a
7806  * handy visitor when your BCP handling code for the most part doesn't
7807  * care if it will operate on the next or prev BCP, ie your visitor
7808  * simply wants to visit all the selected BCP.
7809  */
FE_visitSelectedControlPoints(gpointer key,gpointer value,gpointer udata)7810 static void FE_visitSelectedControlPoints( gpointer key,
7811 					   gpointer value,
7812 					   gpointer udata )
7813 {
7814     visitSelectedControlPoints_CallbackData* d = (visitSelectedControlPoints_CallbackData*)udata;
7815     SplinePoint* sp = (SplinePoint*)key;
7816 
7817     d->count++;
7818     if( sp->nextcpselected )
7819     {
7820 	BasePoint *which = &sp->nextcp;
7821 	d->func( key, value, sp, which, true, d->udata );
7822     }
7823     if( sp->prevcpselected )
7824     {
7825 	BasePoint *which = &sp->prevcp;
7826 	d->func( key, value, sp, which, false, d->udata );
7827     }
7828 }
FE_visitAllControlPoints(gpointer key,gpointer value,gpointer udata)7829 static void FE_visitAllControlPoints( gpointer key,
7830 				      gpointer value,
7831 				      gpointer udata )
7832 {
7833     visitSelectedControlPoints_CallbackData* d = (visitSelectedControlPoints_CallbackData*)udata;
7834     SplinePoint* sp = (SplinePoint*)key;
7835 
7836     d->count++;
7837     {
7838 	BasePoint *which = &sp->nextcp;
7839 	d->func( key, value, sp, which, true, d->udata );
7840     }
7841     {
7842 	BasePoint *which = &sp->prevcp;
7843 	d->func( key, value, sp, which, false, d->udata );
7844     }
7845 }
FE_visitAdjacentToSelectedControlPoints(gpointer key,gpointer value,gpointer udata)7846 static void FE_visitAdjacentToSelectedControlPoints( gpointer key,
7847 						     gpointer value,
7848 						     gpointer udata )
7849 {
7850     visitSelectedControlPoints_CallbackData* d = (visitSelectedControlPoints_CallbackData*)udata;
7851     SplinePoint* sp = (SplinePoint*)key;
7852 
7853     if( sp->selected )
7854 	return;
7855 
7856     d->count++;
7857     if( sp->prev && sp->prev->from && sp->prev->from->selected )
7858     {
7859 	d->func( key, value, sp, &sp->nextcp, true, d->udata );
7860 	d->func( key, value, sp, &sp->prevcp, true, d->udata );
7861     }
7862     if( sp->next && sp->next->to && sp->next->to->selected )
7863     {
7864 	d->func( key, value, sp, &sp->nextcp, true, d->udata );
7865 	d->func( key, value, sp, &sp->prevcp, true, d->udata );
7866     }
7867 }
7868 
visitSelectedControlPoints(GHashTable * col,visitSelectedControlPointsVisitor f,gpointer udata)7869 void visitSelectedControlPoints( GHashTable *col, visitSelectedControlPointsVisitor f, gpointer udata )
7870 {
7871     visitSelectedControlPoints_CallbackData d;
7872     d.func = f;
7873     d.udata = udata;
7874     d.count = 0;
7875     g_hash_table_foreach( col, FE_visitSelectedControlPoints, &d );
7876 }
7877 
visitAllControlPoints(GHashTable * col,visitSelectedControlPointsVisitor f,gpointer udata)7878 void visitAllControlPoints( GHashTable *col, visitSelectedControlPointsVisitor f, gpointer udata )
7879 {
7880     visitSelectedControlPoints_CallbackData d;
7881     d.func = f;
7882     d.udata = udata;
7883     d.count = 0;
7884     g_hash_table_foreach( col, FE_visitAllControlPoints, &d );
7885 }
visitAdjacentToSelectedControlPoints(GHashTable * col,visitSelectedControlPointsVisitor f,gpointer udata)7886 static void visitAdjacentToSelectedControlPoints( GHashTable *col, visitSelectedControlPointsVisitor f, gpointer udata )
7887 {
7888     visitSelectedControlPoints_CallbackData d;
7889     d.func = f;
7890     d.udata = udata;
7891     d.count = 0;
7892     g_hash_table_foreach( col, FE_visitAdjacentToSelectedControlPoints, &d );
7893 }
7894 
CVFindAndVisitSelectedControlPoints(CharView * cv,bool preserveState,visitSelectedControlPointsVisitor f,void * udata)7895 void CVFindAndVisitSelectedControlPoints( CharView *cv, bool preserveState,
7896 					  visitSelectedControlPointsVisitor f, void* udata )
7897 {
7898 //    TRACE("CVFindAndVisitSelectedControlPoints(top) cv->p.sp:%p\n", cv->p.sp );
7899     GHashTable* col = getSelectedControlPoints( cv, &cv->p );
7900     if(!col)
7901 	return;
7902 
7903     if( g_hash_table_size( col ) )
7904     {
7905 	if( preserveState )
7906 	    CVPreserveState(&cv->b);
7907 	visitSelectedControlPoints( col, f, udata );
7908     }
7909     g_hash_table_destroy(col);
7910 }
7911 
CVVisitAllControlPoints(CharView * cv,bool preserveState,visitSelectedControlPointsVisitor f,void * udata)7912 void CVVisitAllControlPoints( CharView *cv, bool preserveState,
7913 			      visitSelectedControlPointsVisitor f, void* udata )
7914 {
7915     TRACE("CVVisitAllControlPoints(top) cv->p.spl:%p cv->p.sp:%p\n", cv->p.spl, cv->p.sp );
7916     if( !cv->p.spl || !cv->p.sp )
7917 	return;
7918 
7919     GHashTable* col = getAllControlPoints( cv, &cv->p );
7920     if( g_hash_table_size( col ) )
7921     {
7922 	if( preserveState )
7923 	    CVPreserveState(&cv->b);
7924 	visitAllControlPoints( col, f, udata );
7925     }
7926     g_hash_table_destroy(col);
7927 }
7928 
CVVisitAdjacentToSelectedControlPoints(CharView * cv,bool preserveState,visitSelectedControlPointsVisitor f,void * udata)7929 void CVVisitAdjacentToSelectedControlPoints( CharView *cv, bool preserveState,
7930 					     visitSelectedControlPointsVisitor f, void* udata )
7931 {
7932 //    TRACE("CVVisitAdjacentToSelectedControlPoints(top) cv->p.sp:%p\n", cv->p.sp );
7933     if( !cv->p.spl || !cv->p.sp )
7934 	return;
7935 
7936     GHashTable* col = getAllControlPoints( cv, &cv->p );
7937     if( !col )
7938 	return;
7939 
7940     if( g_hash_table_size( col ) )
7941     {
7942 	if( preserveState )
7943 	    CVPreserveState(&cv->b);
7944 	visitAdjacentToSelectedControlPoints( col, f, udata );
7945     }
7946     g_hash_table_destroy(col);
7947 }
7948 
7949 
7950 
CVChar(CharView * cv,GEvent * event)7951 void CVChar(CharView *cv, GEvent *event ) {
7952     extern float arrowAmount, arrowAccelFactor;
7953     extern int navigation_mask;
7954 
7955     if( !cv_auto_goto )
7956     {
7957 	if( event->u.chr.keysym == GK_Control_L
7958 	    || event->u.chr.keysym == GK_Control_R )
7959 	{
7960 	    HaveModifiers = 1;
7961 	}
7962 	bool isImmediateKeyTogglePreview = isImmediateKey( cv->gw, "TogglePreview", event ) != NULL;
7963 
7964 	if( !HaveModifiers && isImmediateKeyTogglePreview ) {
7965 	    PressingTilde = 1;
7966 	    CVPreviewModeSet( cv->gw, true );
7967 	    return;
7968 	}
7969     }
7970 
7971     /* TRACE("GK_Control_L:%d\n", ( event->u.chr.keysym == GK_Control_L )); */
7972     /* TRACE("GK_Meta_L:%d\n", ( event->u.chr.keysym == GK_Meta_L )); */
7973 
7974     int oldactiveModifierControl = cv->activeModifierControl;
7975     int oldactiveModifierAlt = cv->activeModifierAlt;
7976     cv->activeModifierControl |= ( event->u.chr.keysym == GK_Control_L || event->u.chr.keysym == GK_Control_R
7977 				   || event->u.chr.keysym == GK_Meta_L || event->u.chr.keysym == GK_Meta_R );
7978     cv->activeModifierAlt     |= ( event->u.chr.keysym == GK_Alt_L || event->u.chr.keysym == GK_Alt_R
7979 				   || event->u.chr.keysym == GK_Mode_switch );
7980 
7981     if( oldactiveModifierControl != cv->activeModifierControl
7982 	|| oldactiveModifierAlt != cv->activeModifierAlt )
7983     {
7984 	CVInfoDraw(cv,cv->gw);
7985     }
7986 
7987 
7988 #if _ModKeysAutoRepeat
7989 	/* Under cygwin these keys auto repeat, they don't under normal X */
7990 	if ( cv->autorpt!=NULL ) {
7991 	    GDrawCancelTimer(cv->autorpt); cv->autorpt = NULL;
7992 	    if ( cv->keysym == event->u.chr.keysym )	/* It's an autorepeat, ignore it */
7993 return;
7994 	    CVToolsSetCursor(cv,cv->oldstate,NULL);
7995 	}
7996 #endif
7997 
7998 #if MyMemory
7999     if ( event->u.chr.keysym == GK_F2 ) {
8000 	fprintf( stderr, "Malloc debug on\n" );
8001 	__malloc_debug(5);
8002     } else if ( event->u.chr.keysym == GK_F3 ) {
8003 	fprintf( stderr, "Malloc debug off\n" );
8004 	__malloc_debug(0);
8005     }
8006 #endif
8007 
8008     if ( !HaveModifiers && event->u.chr.keysym==' ' && cv->spacebar_hold==0 ) {
8009 	cv->p.x = event->u.mouse.x;
8010 	cv->p.y = event->u.mouse.y;
8011 	update_spacebar_hand_tool(cv);
8012     }
8013 
8014     CVPaletteActivate(cv);
8015     CVToolsSetCursor(cv,TrueCharState(event),NULL);
8016 
8017 
8018 	/* The window check is to prevent infinite loops since DVChar can */
8019 	/*  call CVChar too */
8020     if ( cv->dv!=NULL && (event->w==cv->gw || event->w==cv->v) && DVChar(cv->dv,event))
8021     {
8022 	/* All Done */;
8023     }
8024     else if ( event->u.chr.keysym=='s' &&
8025 	    (event->u.chr.state&ksm_control) &&
8026 	    (event->u.chr.state&ksm_meta) )
8027 	MenuSaveAll(NULL,NULL,NULL);
8028     else if ( event->u.chr.keysym=='q' &&
8029 	    (event->u.chr.state&ksm_control) &&
8030 	    (event->u.chr.state&ksm_meta) )
8031 	MenuExit(NULL,NULL,NULL);
8032     else if ( event->u.chr.keysym == GK_Shift_L || event->u.chr.keysym == GK_Shift_R ||
8033 	     event->u.chr.keysym == GK_Alt_L || event->u.chr.keysym == GK_Alt_R ||
8034 	     event->u.chr.keysym == GK_Meta_L || event->u.chr.keysym == GK_Meta_R ) {
8035 	CVFakeMove(cv, event);
8036     } else if (( event->u.chr.keysym == GK_Tab || event->u.chr.keysym == GK_BackTab) &&
8037 	    (event->u.chr.state&ksm_control) && cv->showtabs && cv->former_cnt>1 ) {
8038 	GGadgetDispatchEvent(cv->tabs,event);
8039     } else if ( (event->u.chr.state&ksm_meta) &&
8040 	    !(event->u.chr.state&(ksm_control|ksm_shift)) &&
8041 	    event->u.chr.chars[0]!='\0' ) {
8042 	CVPaletteMnemonicCheck(event);
8043     } else if ( !(event->u.chr.state&(ksm_control|ksm_meta)) &&
8044 	    event->u.chr.keysym == GK_BackSpace ) {
8045 	/* Menu does delete */
8046 	CVClear(cv->gw,NULL,NULL);
8047     } else if ( event->u.chr.keysym == GK_Help ) {
8048 	MenuHelp(NULL,NULL,NULL);	/* Menu does F1 */
8049     } else if ( event->u.chr.keysym=='<' && (event->u.chr.state&ksm_control) ) {
8050 	/* European keyboards do not need shift to get < */
8051 	CVDoFindInFontView(cv);
8052     } else if ( (event->u.chr.keysym=='[' || event->u.chr.keysym==']') &&
8053 	    (event->u.chr.state&ksm_control) ) {
8054 	/* European keyboards need a funky modifier to get [] */
8055 	_CVMenuChangeChar(cv,event->u.chr.keysym=='[' ? MID_Prev : MID_Next );
8056     } else if ( (event->u.chr.keysym=='{' || event->u.chr.keysym=='}') &&
8057 	    (event->u.chr.state&ksm_control) ) {
8058 	/* European keyboards need a funky modifier to get {} */
8059 	_CVMenuChangeChar(cv,event->u.chr.keysym=='{' ? MID_PrevDef : MID_NextDef );
8060     } else if ( event->u.chr.keysym=='\\' && (event->u.chr.state&ksm_control) ) {
8061 	/* European keyboards need a funky modifier to get \ */
8062 	CVDoTransform(cv,cvt_none);
8063     } else if ( (event->u.chr.keysym=='F' || event->u.chr.keysym=='B') &&
8064             !(event->u.chr.state&(ksm_control|ksm_meta)) ) {
8065         CVLSelectLayer(cv, event->u.chr.keysym=='F' ? 1 : 0);
8066     } else if ( (event->u.chr.state&ksm_control) && (event->u.chr.keysym=='-' || event->u.chr.keysym==0xffad/*XK_KP_Subtract*/) ){
8067 	    _CVMenuScale(cv, MID_ZoomOut);
8068     } else if ( (event->u.chr.state&ksm_control) && (event->u.chr.keysym=='=' || event->u.chr.keysym==0xffab/*XK_KP_Add*/) ){
8069 	    _CVMenuScale(cv, MID_ZoomIn);
8070     }
8071     else if ( event->u.chr.keysym == GK_Left ||
8072 	    event->u.chr.keysym == GK_Up ||
8073 	    event->u.chr.keysym == GK_Right ||
8074 	    event->u.chr.keysym == GK_Down ||
8075 	    event->u.chr.keysym == GK_KP_Left ||
8076 	    event->u.chr.keysym == GK_KP_Up ||
8077 	    event->u.chr.keysym == GK_KP_Right ||
8078 	    event->u.chr.keysym == GK_KP_Down )
8079     {
8080 	TRACE("key left/right/up/down...\n");
8081 
8082 	GGadget *active = GWindowGetFocusGadgetOfWindow(cv->gw);
8083 	if( active == cv->charselector )
8084 	{
8085 	    if ( event->u.chr.keysym == GK_Left ||event->u.chr.keysym == GK_Right )
8086 	    {
8087 		TRACE("left/right on the charselector!\n");
8088 	    }
8089 	    int dir = ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up ) ? -1 : 1;
8090 	    Wordlist_MoveByOffset( cv->charselector, &cv->charselectoridx, dir );
8091 
8092 	    return;
8093 	}
8094 
8095 
8096 	real dx=0, dy=0; int anya;
8097 	switch ( event->u.chr.keysym ) {
8098 	  case GK_Left: case GK_KP_Left:
8099 	    dx = -1;
8100 	  break;
8101 	  case GK_Right: case GK_KP_Right:
8102 	    dx = 1;
8103 	  break;
8104 	  case GK_Up: case GK_KP_Up:
8105 	    dy = 1;
8106 	  break;
8107 	  case GK_Down: case GK_KP_Down:
8108 	    dy = -1;
8109 	  break;
8110 	}
8111 	if ( event->u.chr.state & (ksm_control|ksm_capslock) ) {
8112 	    struct sbevent sb;
8113 	    sb.type = dy>0 || dx<0 ? et_sb_halfup : et_sb_halfdown;
8114 	    if ( dx==0 )
8115 		CVVScroll(cv,&sb);
8116 	    else
8117 		CVHScroll(cv,&sb);
8118 	}
8119 	else
8120 	{
8121 //	    TRACE("cvchar( moving points? ) shift:%d\n", ( event->u.chr.state & (ksm_shift) ));
8122 	    FE_adjustBCPByDeltaData d;
8123 	    memset( &d, 0, sizeof(FE_adjustBCPByDeltaData));
8124 	    visitSelectedControlPointsVisitor func = FE_adjustBCPByDelta;
8125 	    if ( event->u.chr.state & ksm_meta )
8126 	    {
8127 		// move the bcp 1 unit in the direction it already has
8128 		func = FE_adjustBCPByDeltaWhilePreservingBCPAngle;
8129 		// allow that func to work it's magic on any gradient
8130 		d.keyboarddx = 1;
8131 	    }
8132 	    /* if ( event->u.chr.state & (ksm_shift) ) */
8133 	    /* 	dx -= dy*tan((cv->b.sc->parent->italicangle)*(FF_PI/180) ); */
8134 	    if ( event->u.chr.state & (ksm_shift) )
8135 	    {
8136 		dx *= arrowAccelFactor; dy *= arrowAccelFactor;
8137 	    }
8138 
8139 	    if ((  cv->p.sp!=NULL || cv->lastselpt!=NULL ) &&
8140 		    (cv->p.nextcp || cv->p.prevcp) )
8141 	    {
8142 		// This code moves 1 or more BCP
8143 
8144 		SplinePoint *old = cv->p.sp;
8145 		d.cv = cv;
8146 		d.dx = dx * arrowAmount;
8147 		d.dy = dy * arrowAmount;
8148 		CVFindAndVisitSelectedControlPoints( cv, true,
8149 						     func, &d );
8150 		cv->p.sp = old;
8151 		SCUpdateAll(cv->b.sc);
8152 
8153 	    }
8154 	    else if ( CVAnySel(cv,NULL,NULL,NULL,&anya) || cv->widthsel || cv->vwidthsel )
8155 	    {
8156 		CVPreserveState(&cv->b);
8157 		CVMoveSelection(cv,dx*arrowAmount,dy*arrowAmount, event->u.chr.state);
8158 		if ( cv->widthsel )
8159 		    SCSynchronizeWidth(cv->b.sc,cv->b.sc->width,cv->b.sc->width-dx,NULL);
8160 		_CV_CharChangedUpdate(cv,2);
8161 		CVInfoDraw(cv,cv->gw);
8162 	    }
8163 	    CVGridHandlePossibleFitChar( cv );
8164 	}
8165     } else if ( event->u.chr.keysym == GK_Page_Up ||
8166 	    event->u.chr.keysym == GK_KP_Page_Up ||
8167 	    event->u.chr.keysym == GK_Prior ||
8168 	    event->u.chr.keysym == GK_Page_Down ||
8169 	    event->u.chr.keysym == GK_KP_Page_Down ||
8170 	    event->u.chr.keysym == GK_Next ) {
8171 	/* Um... how do we scroll horizontally??? */
8172 	struct sbevent sb;
8173 	sb.type = et_sb_uppage;
8174 	if ( event->u.chr.keysym == GK_Page_Down ||
8175 		event->u.chr.keysym == GK_KP_Page_Down ||
8176 		event->u.chr.keysym == GK_Next )
8177 	    sb.type = et_sb_downpage;
8178 	CVVScroll(cv,&sb);
8179     } else if ( event->u.chr.keysym == GK_Home ) {
8180 	CVFit(cv);
8181     } else if ( event->u.chr.keysym==' ' && cv->spacebar_hold ){
8182     } else if ( (event->u.chr.state&((GMenuMask()|navigation_mask)&~(ksm_shift|ksm_capslock)))==navigation_mask &&
8183 	    event->type == et_char &&
8184 	    event->u.chr.keysym!=0 &&
8185 	    (event->u.chr.keysym<GK_Special /*|| event->u.chr.keysym>=0x10000*/)) {
8186 	SplineFont *sf = cv->b.sc->parent;
8187 	int i;
8188 	EncMap *map = cv->b.fv->map;
8189 	extern int cv_auto_goto;
8190 	if ( cv_auto_goto ) {
8191 	    i = SFFindSlot(sf,map,event->u.chr.keysym,NULL);
8192 	    if ( i!=-1 )
8193 		CVChangeChar(cv,i);
8194 	}
8195     }
8196 }
8197 
CVShowPoint(CharView * cv,BasePoint * me)8198 void CVShowPoint(CharView *cv, BasePoint *me) {
8199     CharViewTab* tab = CVGetActiveTab(cv);
8200     int x, y;
8201     int fudge = 30;
8202 
8203     if ( cv->width<60 )
8204 	fudge = cv->width/3;
8205     if ( cv->height<60 && fudge>cv->height/3 )
8206 	fudge = cv->height/3;
8207 
8208     /* Make sure the point is visible and has some context around it */
8209     x =  tab->xoff + rint(me->x*tab->scale);
8210     y = -tab->yoff + cv->height - rint(me->y*tab->scale);
8211     if ( x<fudge || y<fudge || x>cv->width-fudge || y>cv->height-fudge )
8212 	CVMagnify(cv,me->x,me->y,0,0);
8213 }
8214 
CVSelectContours(CharView * cv)8215 static void CVSelectContours(CharView *cv) {
8216     SplineSet *spl;
8217     SplinePoint *sp;
8218     int sel;
8219     int i;
8220 
8221     for ( spl=cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
8222 	sel = false;
8223 	if ( cv->b.sc->inspiro && hasspiro()) {
8224 	    for ( i=0; i<spl->spiro_cnt-1; ++i ) {
8225 		if ( SPIRO_SELECTED(&spl->spiros[i]) ) {
8226 		    sel = true;
8227 	    break;
8228 		}
8229 	    }
8230 	    if ( sel ) {
8231 		for ( i=0; i<spl->spiro_cnt-1; ++i )
8232 		    SPIRO_SELECT(&spl->spiros[i]);
8233 	    }
8234 	} else {
8235 	    for ( sp=spl->first ; ; ) {
8236 		if ( sp->selected ) {
8237 		    sel = true;
8238 	    break;
8239 		}
8240 		if ( sp->next==NULL )
8241 	    break;
8242 		sp = sp->next->to;
8243 		if ( sp==spl->first )
8244 	    break;
8245 	    }
8246 	    if ( sel ) {
8247 		for ( sp=spl->first ; ; ) {
8248 		    sp->selected = true;
8249 		    if ( sp->next==NULL )
8250 		break;
8251 		    sp = sp->next->to;
8252 		    if ( sp==spl->first )
8253 		break;
8254 		}
8255 	    }
8256 	}
8257     }
8258     SCUpdateAll(cv->b.sc);
8259 }
8260 
CVMenuSelectContours(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8261 static void CVMenuSelectContours(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8262     CharView *cv = (CharView *) GDrawGetUserData(gw);
8263     CVSelectContours(cv);
8264 }
8265 
CVMenuSelectPointAt(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8266 static void CVMenuSelectPointAt(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8267     CharView *cv = (CharView *) GDrawGetUserData(gw);
8268     CVSelectPointAt(cv);
8269 }
8270 
CVNextPrevSpiroPt(CharView * cv,struct gmenuitem * mi)8271 static void CVNextPrevSpiroPt(CharView *cv, struct gmenuitem *mi) {
8272     CharViewTab* tab = CVGetActiveTab(cv);
8273     RefChar *r; ImageList *il;
8274     SplineSet *spl, *ss;
8275     SplinePoint *junk;
8276     int x, y;
8277     spiro_cp *selcp = NULL, *other;
8278     int index;
8279 
8280     if ( mi->mid == MID_FirstPt ) {
8281 	if ( cv->b.layerheads[cv->b.drawmode]->splines==NULL )
8282 return;
8283 	CVClearSel(cv);
8284 	other = &cv->b.layerheads[cv->b.drawmode]->splines->spiros[0];
8285     } else {
8286 	if ( !CVOneThingSel(cv,&junk,&spl,&r,&il,NULL,&selcp) || spl==NULL )
8287 return;
8288 	other = selcp;
8289 	if ( spl==NULL )
8290 return;
8291 	index = selcp - spl->spiros;
8292 	if ( mi->mid == MID_NextPt ) {
8293 	    if ( index!=spl->spiro_cnt-2 )
8294 		other = &spl->spiros[index+1];
8295 	    else {
8296 		if ( spl->next == NULL )
8297 		    spl = cv->b.layerheads[cv->b.drawmode]->splines;
8298 		else
8299 		    spl = spl->next;
8300 		other = &spl->spiros[0];
8301 	    }
8302 	} else if ( mi->mid == MID_PrevPt ) {
8303 	    if ( index!=0 ) {
8304 		other = &spl->spiros[index-1];
8305 	    } else {
8306 		if ( spl==cv->b.layerheads[cv->b.drawmode]->splines ) {
8307 		    for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss->next!=NULL; ss=ss->next );
8308 		} else {
8309 		    for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss->next!=spl; ss=ss->next );
8310 		}
8311 		spl = ss;
8312 		other = &ss->spiros[ss->spiro_cnt-2];
8313 	    }
8314 	} else if ( mi->mid == MID_FirstPtNextCont ) {
8315 	    if ( spl->next!=NULL )
8316 		other = &spl->next->spiros[0];
8317 	    else
8318 		other = NULL;
8319 	}
8320     }
8321     if ( selcp!=NULL )
8322 	SPIRO_DESELECT(selcp);
8323     if ( other!=NULL )
8324 	SPIRO_SELECT(other);
8325     cv->p.sp = NULL;
8326     cv->lastselpt = NULL;
8327     cv->lastselcp = other;
8328 
8329     /* Make sure the point is visible and has some context around it */
8330     if ( other!=NULL ) {
8331 	x =  tab->xoff + rint(other->x*tab->scale);
8332 	y = -tab->yoff + cv->height - rint(other->y*tab->scale);
8333 	if ( x<40 || y<40 || x>cv->width-40 || y>cv->height-40 )
8334 	    CVMagnify(cv,other->x,other->y,0,0);
8335     }
8336 
8337     CVInfoDraw(cv,cv->gw);
8338     SCUpdateAll(cv->b.sc);
8339 }
8340 
CVNextPrevPt(CharView * cv,struct gmenuitem * mi)8341 static void CVNextPrevPt(CharView *cv, struct gmenuitem *mi) {
8342     CharViewTab* tab = CVGetActiveTab(cv);
8343     SplinePoint *selpt=NULL, *other;
8344     RefChar *r; ImageList *il;
8345     SplineSet *spl, *ss;
8346     int x, y;
8347     spiro_cp *junk;
8348 
8349     if ( cv->b.sc->inspiro && hasspiro()) {
8350 	CVNextPrevSpiroPt(cv,mi);
8351 return;
8352     }
8353 
8354     if ( mi->mid == MID_FirstPt ) {
8355 	if ( cv->b.layerheads[cv->b.drawmode]->splines==NULL )
8356 return;
8357 	other = (cv->b.layerheads[cv->b.drawmode]->splines)->first;
8358 	CVClearSel(cv);
8359     } else {
8360 	if ( !CVOneThingSel(cv,&selpt,&spl,&r,&il,NULL,&junk) || spl==NULL )
8361 return;
8362 	other = selpt;
8363 	if ( spl==NULL )
8364 return;
8365 	else if ( mi->mid == MID_NextPt ) {
8366 	    if ( other->next!=NULL && other->next->to!=spl->first )
8367 		other = other->next->to;
8368 	    else {
8369 		if ( spl->next == NULL )
8370 		    spl = cv->b.layerheads[cv->b.drawmode]->splines;
8371 		else
8372 		    spl = spl->next;
8373 		other = spl->first;
8374 	    }
8375 	} else if ( mi->mid == MID_PrevPt ) {
8376 	    if ( other!=spl->first ) {
8377 		other = other->prev->from;
8378 	    } else {
8379 		if ( spl==cv->b.layerheads[cv->b.drawmode]->splines ) {
8380 		    for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss->next!=NULL; ss=ss->next );
8381 		} else {
8382 		    for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss->next!=spl; ss=ss->next );
8383 		}
8384 		spl = ss;
8385 		other = ss->last;
8386 		if ( spl->last==spl->first && spl->last->prev!=NULL )
8387 		    other = other->prev->from;
8388 	    }
8389 	} else if ( mi->mid == MID_FirstPtNextCont ) {
8390 	    if ( spl->next!=NULL )
8391 		other = spl->next->first;
8392 	    else
8393 		other = NULL;
8394 	}
8395     }
8396     if ( selpt!=NULL )
8397 	selpt->selected = false;
8398     if ( other!=NULL )
8399 	other->selected = true;
8400     cv->p.sp = NULL;
8401     cv->lastselpt = other;
8402     cv->p.spiro = cv->lastselcp = NULL;
8403 
8404     /* Make sure the point is visible and has some context around it */
8405     if ( other!=NULL ) {
8406 	x =  tab->xoff + rint(other->me.x*tab->scale);
8407 	y = -tab->yoff + cv->height - rint(other->me.y*tab->scale);
8408 	if ( x<40 || y<40 || x>cv->width-40 || y>cv->height-40 )
8409 	    CVMagnify(cv,other->me.x,other->me.y,0,0);
8410     }
8411 
8412     CVInfoDraw(cv,cv->gw);
8413     SCUpdateAll(cv->b.sc);
8414 }
8415 
CVMenuNextPrevPt(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))8416 static void CVMenuNextPrevPt(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8417     CharView *cv = (CharView *) GDrawGetUserData(gw);
8418     CVNextPrevPt(cv, mi);
8419 }
8420 
CVNextPrevCPt(CharView * cv,struct gmenuitem * mi)8421 static void CVNextPrevCPt(CharView *cv, struct gmenuitem *mi) {
8422     SplinePoint *selpt=NULL;
8423     RefChar *r; ImageList *il;
8424     SplineSet *spl;
8425     spiro_cp *junk;
8426 
8427     if ( !CVOneThingSel(cv,&selpt,&spl,&r,&il,NULL,&junk))
8428 return;
8429     if ( selpt==NULL )
8430 return;
8431     cv->p.nextcp = mi->mid==MID_NextCP;
8432     cv->p.prevcp = mi->mid==MID_PrevCP;
8433     SCUpdateAll(cv->b.sc);
8434 }
8435 
CVMenuNextPrevCPt(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))8436 static void CVMenuNextPrevCPt(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8437     CharView *cv = (CharView *) GDrawGetUserData(gw);
8438     CVNextPrevCPt(cv, mi);
8439 }
8440 
CVMenuGotoChar(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8441 static void CVMenuGotoChar(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8442     CharView *cv = (CharView *) GDrawGetUserData(gw);
8443     int pos;
8444 
8445     if ( cv->b.container ) {
8446 	(cv->b.container->funcs->doNavigate)(cv->b.container,nt_goto);
8447 return;
8448     }
8449 
8450     pos = GotoChar(cv->b.fv->sf,cv->b.fv->map,NULL);
8451     if ( pos!=-1 )
8452 	CVChangeChar(cv,pos);
8453 }
8454 
CVMenuFindInFontView(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8455 static void CVMenuFindInFontView(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8456     CharView *cv = (CharView *) GDrawGetUserData(gw);
8457     CVDoFindInFontView(cv);
8458 }
8459 
CVMenuPalettesDock(GWindow UNUSED (gw),struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8460 static void CVMenuPalettesDock(GWindow UNUSED(gw), struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8461     PalettesChangeDocking();
8462 }
8463 
CVMenuPaletteShow(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))8464 static void CVMenuPaletteShow(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8465     CharView *cv = (CharView *) GDrawGetUserData(gw);
8466     CVPaletteSetVisible(cv, mi->mid==MID_Tools, !CVPaletteIsVisible(cv, mi->mid==MID_Tools));
8467 }
8468 
cv_pllistcheck(CharView * cv,struct gmenuitem * mi)8469 static void cv_pllistcheck(CharView *cv, struct gmenuitem *mi) {
8470     extern int palettes_docked;
8471 
8472     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
8473 	switch ( mi->mid ) {
8474 	  case MID_Tools:
8475 	    mi->ti.checked = CVPaletteIsVisible(cv,1);
8476 	  break;
8477 	  case MID_Layers:
8478 	    mi->ti.checked = CVPaletteIsVisible(cv,0);
8479 	  break;
8480 	  case MID_DockPalettes:
8481 	    mi->ti.checked = palettes_docked;
8482 	  break;
8483 	}
8484     }
8485 }
8486 
pllistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))8487 static void pllistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8488     CharView *cv = (CharView *) GDrawGetUserData(gw);
8489     cv_pllistcheck(cv, mi);
8490 }
8491 
8492 /*
8493  * Unused
8494 static void tablistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8495     GDrawGetUserData(gw);
8496 }
8497 */
8498 
CVUndo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8499 static void CVUndo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8500     CharView *cv = (CharView *) GDrawGetUserData(gw);
8501 
8502     CVDoUndo(&cv->b);
8503     cv->lastselpt = NULL;
8504 }
8505 
CVRedo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8506 static void CVRedo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8507     CharView *cv = (CharView *) GDrawGetUserData(gw);
8508 
8509     CVDoRedo(&cv->b);
8510     cv->lastselpt = NULL;
8511 }
8512 
_CVCopy(CharView * cv)8513 static void _CVCopy(CharView *cv) {
8514     int desel = false, anya;
8515 
8516     /* If nothing is selected, copy everything. Do that by temporarily selecting everything */
8517     if ( !CVAnySel(cv,NULL,NULL,NULL,&anya))
8518 	if ( !(desel = CVSetSel(cv,-1)))
8519 return;
8520     CopySelected(&cv->b,cv->showanchor);
8521     if ( desel )
8522 	CVClearSel(cv);
8523 }
8524 
CVCopy(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8525 static void CVCopy(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8526     CharView *cv = (CharView *) GDrawGetUserData(gw);
8527     _CVCopy(cv);
8528 }
8529 
CVCopyLookupData(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8530 static void CVCopyLookupData(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8531     CharView *cv = (CharView *) GDrawGetUserData(gw);
8532     SCCopyLookupData(cv->b.sc);
8533 }
8534 
CVCopyRef(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8535 static void CVCopyRef(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8536     CharView *cv = (CharView *) GDrawGetUserData(gw);
8537     CopyReference(cv->b.sc);
8538 }
8539 
CVMenuCopyGridFit(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8540 static void CVMenuCopyGridFit(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8541     CharView *cv = (CharView *) GDrawGetUserData(gw);
8542     CVCopyGridFit(&cv->b);
8543 }
8544 
CVCopyWidth(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))8545 static void CVCopyWidth(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8546     CharView *cv = (CharView *) GDrawGetUserData(gw);
8547     if ( mi->mid==MID_CopyVWidth && !cv->b.sc->parent->hasvmetrics )
8548 return;
8549     CopyWidth(&cv->b,mi->mid==MID_CopyWidth?ut_width:
8550 		     mi->mid==MID_CopyVWidth?ut_vwidth:
8551 		     mi->mid==MID_CopyLBearing?ut_lbearing:
8552 					     ut_rbearing);
8553 }
8554 
CVDoClear(CharView * cv)8555 static void CVDoClear(CharView *cv) {
8556     ImageList *prev, *imgs, *next;
8557     RefChar *refs, *rnext;
8558     int layer = CVLayer((CharViewBase *) cv);
8559     int anyimages;
8560 
8561     CVPreserveState(&cv->b);
8562     if ( cv->b.drawmode==dm_fore )
8563 	SCRemoveSelectedMinimumDistances(cv->b.sc,2);
8564     cv->b.layerheads[cv->b.drawmode]->splines = SplinePointListRemoveSelected(cv->b.sc,
8565 	    cv->b.layerheads[cv->b.drawmode]->splines);
8566     for ( refs=cv->b.layerheads[cv->b.drawmode]->refs; refs!=NULL; refs = rnext ) {
8567 	rnext = refs->next;
8568 	if ( refs->selected )
8569 	    SCRemoveDependent(cv->b.sc,refs,layer);
8570     }
8571     if ( cv->b.drawmode==dm_fore ) {
8572 	AnchorPoint *ap, *aprev=NULL, *anext;
8573 	if ( cv->showanchor ) for ( ap=cv->b.sc->anchor; ap!=NULL; ap=anext ) {
8574 	    anext = ap->next;
8575 	    if ( ap->selected ) {
8576 		if ( aprev!=NULL )
8577 		    aprev->next = anext;
8578 		else
8579 		    cv->b.sc->anchor = anext;
8580 		ap->next = NULL;
8581 		AnchorPointsFree(ap);
8582 	    } else
8583 		aprev = ap;
8584 	}
8585     }
8586     anyimages = false;
8587     for ( prev = NULL, imgs=cv->b.layerheads[cv->b.drawmode]->images; imgs!=NULL; imgs = next ) {
8588 	next = imgs->next;
8589 	if ( !imgs->selected )
8590 	    prev = imgs;
8591 	else {
8592 	    if ( prev==NULL )
8593 		cv->b.layerheads[cv->b.drawmode]->images = next;
8594 	    else
8595 		prev->next = next;
8596 	    chunkfree(imgs,sizeof(ImageList));
8597 	    /* garbage collection of images????!!!! */
8598 	    anyimages = true;
8599 	}
8600     }
8601     if ( anyimages )
8602 	SCOutOfDateBackground(cv->b.sc);
8603     if ( cv->lastselpt!=NULL || cv->p.sp!=NULL || cv->p.spiro!=NULL || cv->lastselcp!=NULL ) {
8604 	cv->lastselpt = NULL; cv->p.sp = NULL;
8605 	cv->p.spiro = cv->lastselcp = NULL;
8606 	CVInfoDraw(cv,cv->gw);
8607     }
8608 }
8609 
CVClear(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8610 static void CVClear(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8611     CharView *cv = (CharView *) GDrawGetUserData(gw);
8612     int anyanchor;
8613 
8614     if ( !CVAnySel(cv,NULL,NULL,NULL,&anyanchor))
8615 return;
8616     CVDoClear(cv);
8617     CVGridHandlePossibleFitChar( cv );
8618     CVCharChangedUpdate(&cv->b);
8619 }
8620 
CVClearBackground(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8621 static void CVClearBackground(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8622     CharView *cv = (CharView *) GDrawGetUserData(gw);
8623     SCClearBackground(cv->b.sc);
8624 }
8625 
_CVPaste(CharView * cv)8626 static void _CVPaste(CharView *cv) {
8627     enum undotype ut = CopyUndoType();
8628     int was_empty = cv->b.drawmode==dm_fore && cv->b.sc->hstem==NULL && cv->b.sc->vstem==NULL && cv->b.layerheads[cv->b.drawmode]->splines==NULL && cv->b.layerheads[cv->b.drawmode]->refs==NULL;
8629     if ( ut!=ut_lbearing )	/* The lbearing code does this itself */
8630 	CVPreserveStateHints(&cv->b);
8631     if ( ut!=ut_width && ut!=ut_vwidth && ut!=ut_lbearing && ut!=ut_rbearing && ut!=ut_possub )
8632 	CVClearSel(cv);
8633     PasteToCV(&cv->b);
8634     cv->lastselpt = NULL;
8635     CVCharChangedUpdate(&cv->b);
8636     if ( was_empty && (cv->b.sc->hstem != NULL || cv->b.sc->vstem!=NULL ))
8637 	cv->b.sc->changedsincelasthinted = false;
8638 }
8639 
CVPaste(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8640 static void CVPaste(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8641     CharView *cv = (CharView *) GDrawGetUserData(gw);
8642     _CVPaste(cv);
8643     // Before July 2019, all pastes would reset the view such that all splines
8644     // were visible. This is wrong in most cases, e.g. tracing many letters
8645     // from one manuscript page, or copying and pasting parts of a glyph.
8646     //
8647     // A more complete fix would be to make it so only selected points were put
8648     // into view, but it was deemed not worth it at the time, and this comment
8649     // was left instead. See Github issue №3783 and PR №3813 for more details.
8650     // CVInkscapeAdjust(cv);
8651 }
8652 
_CVMerge(CharView * cv,int elide)8653 static void _CVMerge(CharView *cv, int elide) {
8654     int anyp = 0;
8655 
8656     if ( !CVAnySel(cv,&anyp,NULL,NULL,NULL) || !anyp)
8657 return;
8658     CVPreserveState(&cv->b);
8659     SplineCharMerge(cv->b.sc,&cv->b.layerheads[cv->b.drawmode]->splines,!elide);
8660     SCClearSelPt(cv->b.sc);
8661     CVCharChangedUpdate(&cv->b);
8662 }
8663 
CVMerge(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8664 void CVMerge(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8665     CharView *cv = (CharView *) GDrawGetUserData(gw);
8666     _CVMerge(cv,false);
8667 }
8668 
_CVMergeToLine(CharView * cv,int elide)8669 static void _CVMergeToLine(CharView *cv, int elide) {
8670     int anyp = 0;
8671 
8672     if ( !CVAnySel(cv,&anyp,NULL,NULL,NULL) || !anyp)
8673 	return;
8674     CVPreserveState(&cv->b);
8675     SplineCharMerge(cv->b.sc,&cv->b.layerheads[cv->b.drawmode]->splines,!elide);
8676 
8677     // Select the other side of the new curve
8678     if (!CVInSpiro(cv)) {
8679         GList_Glib* gl = CVGetSelectedPoints( cv );
8680         if( g_list_first(gl) )
8681         SPSelectPrevPoint( (SplinePoint*)g_list_first(gl)->data, 1 );
8682         g_list_free( gl );
8683     }
8684 
8685     // And make the curve between the two active points a line
8686     _CVMenuMakeLine( (CharViewBase*) cv, 0, 0 );
8687     SCClearSelPt(cv->b.sc);
8688     CVCharChangedUpdate(&cv->b);
8689 }
8690 
CVMergeToLine(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8691 void CVMergeToLine(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8692     CharView *cv = (CharView *) GDrawGetUserData(gw);
8693     _CVMergeToLine(cv,false);
8694 
8695 }
8696 
_CVJoin(CharView * cv)8697 static void _CVJoin(CharView *cv) {
8698     CharViewTab* tab = CVGetActiveTab(cv);
8699     int anyp = 0, changed;
8700     extern float joinsnap;
8701 
8702     CVAnySel(cv,&anyp,NULL,NULL,NULL);
8703     CVPreserveState(&cv->b);
8704     cv->b.layerheads[cv->b.drawmode]->splines = SplineSetJoin(cv->b.layerheads[cv->b.drawmode]->splines,!anyp,joinsnap/tab->scale,&changed);
8705     if ( changed )
8706 	CVCharChangedUpdate(&cv->b);
8707 }
8708 
CVJoin(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8709 static void CVJoin(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8710     CharView *cv = (CharView *) GDrawGetUserData(gw);
8711     _CVJoin(cv);
8712 }
8713 
_CVCut(CharView * cv)8714 static void _CVCut(CharView *cv) {
8715     int anya;
8716 
8717     if ( !CVAnySel(cv,NULL,NULL,NULL,&anya))
8718 return;
8719     _CVCopy(cv);
8720     CVDoClear(cv);
8721     CVCharChangedUpdate(&cv->b);
8722 }
8723 
CVCut(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8724 static void CVCut(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8725     CharView *cv = (CharView *) GDrawGetUserData(gw);
8726     _CVCut(cv);
8727 }
8728 
CVCopyFgBg(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8729 static void CVCopyFgBg(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8730     CharView *cv = (CharView *) GDrawGetUserData(gw);
8731 
8732     if ( cv->b.sc->layers[ly_fore].splines==NULL )
8733 return;
8734     SCCopyLayerToLayer(cv->b.sc,ly_fore,ly_back,false);
8735 }
8736 
CVMenuCopyL2L(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8737 static void CVMenuCopyL2L(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8738     CharView *cv = (CharView *) GDrawGetUserData(gw);
8739     CVCopyLayerToLayer(cv);
8740 }
8741 
CVMenuCompareL2L(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8742 static void CVMenuCompareL2L(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8743     CharView *cv = (CharView *) GDrawGetUserData(gw);
8744     CVCompareLayerToLayer(cv);
8745 }
8746 
CVSelectAll(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))8747 static void CVSelectAll(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
8748     CharView *cv = (CharView *) GDrawGetUserData(gw);
8749     int mask = -1;
8750 
8751     if ( mi->mid==MID_SelectAllPoints )
8752 	mask = 1;
8753     else if ( mi->mid==MID_SelectAnchors )
8754 	mask = 2;
8755     else if ( mi->mid==MID_SelAll ) {
8756 	mask = 1;
8757 	if (cv->b.drawmode==dm_fore) mask+=2;
8758 	/* TODO! Should we also check if this is the right foreground layer? */
8759     }
8760 
8761     if ( CVSetSel(cv,mask))
8762 	SCUpdateAll(cv->b.sc);
8763 }
8764 
CVSelectOpenContours(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8765 static void CVSelectOpenContours(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8766     CharView *cv = (CharView *) GDrawGetUserData(gw);
8767     SplineSet *ss;
8768     int i;
8769     SplinePoint *sp;
8770     int changed = CVClearSel(cv);
8771 
8772     for ( ss=cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL; ss=ss->next ) {
8773 	if ( ss->first->prev==NULL ) {
8774 	    changed = true;
8775 	    if ( cv->b.sc->inspiro && hasspiro()) {
8776 		for ( i=0; i<ss->spiro_cnt; ++i )
8777 		    SPIRO_SELECT(&ss->spiros[i]);
8778 	    } else {
8779 		for ( sp=ss->first ;; ) {
8780 		    sp->selected = true;
8781 		    if ( sp->next==NULL )
8782 		break;
8783 		    sp = sp->next->to;
8784 		}
8785 	    }
8786 	}
8787     }
8788     if ( changed )
8789 	SCUpdateAll(cv->b.sc);
8790 }
8791 
CVSelectNone(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8792 static void CVSelectNone(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8793     CharView *cv = (CharView *) GDrawGetUserData(gw);
8794     if ( CVClearSel(cv))
8795 	SCUpdateAll(cv->b.sc);
8796 }
8797 
CVSelectInvert(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8798 static void CVSelectInvert(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8799     CharView *cv = (CharView *) GDrawGetUserData(gw);
8800     CVInvertSel(cv);
8801     SCUpdateAll(cv->b.sc);
8802 }
8803 
CVSelectWidth(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8804 static void CVSelectWidth(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8805     CharView *cv = (CharView *) GDrawGetUserData(gw);
8806     if ( HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv))!=NULL )
8807 return;
8808     cv->widthsel = !cv->widthsel;
8809     cv->oldwidth = cv->b.sc->width;
8810     SCUpdateAll(cv->b.sc);
8811 }
8812 
CVSelectVWidth(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8813 static void CVSelectVWidth(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8814     CharView *cv = (CharView *) GDrawGetUserData(gw);
8815     if ( !cv->showvmetrics || !cv->b.sc->parent->hasvmetrics )
8816 return;
8817     if ( HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv))!=NULL )
8818 return;
8819     cv->vwidthsel = !cv->widthsel;
8820     cv->oldvwidth = cv->b.sc->vwidth;
8821     SCUpdateAll(cv->b.sc);
8822 }
8823 
CVSelectHM(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8824 static void CVSelectHM(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8825     CharView *cv = (CharView *) GDrawGetUserData(gw);
8826     SplinePoint *sp; SplineSet *spl; RefChar *r; ImageList *im;
8827     spiro_cp *junk;
8828     int exactlyone = CVOneThingSel(cv,&sp,&spl,&r,&im,NULL,&junk);
8829 
8830     if ( !exactlyone || sp==NULL || sp->hintmask == NULL || spl==NULL )
8831 return;
8832     while ( sp!=NULL ) {
8833 	if ( sp->prev==NULL )
8834     break;
8835 	sp = sp->prev->from;
8836 	if ( sp == spl->first )
8837     break;
8838 	if ( sp->hintmask!=NULL )
8839  goto done;
8840 	sp->selected = true;
8841     }
8842     for ( spl = spl->next; spl!=NULL; spl = spl->next ) {
8843 	for ( sp=spl->first; sp!=NULL;  ) {
8844 	    if ( sp->hintmask!=NULL )
8845  goto done;
8846 	    sp->selected = true;
8847 	    if ( sp->prev==NULL )
8848 	break;
8849 	    sp = sp->prev->from;
8850 	    if ( sp == spl->first )
8851 	break;
8852 	}
8853     }
8854  done:
8855     SCUpdateAll(cv->b.sc);
8856 }
8857 
_CVUnlinkRef(CharView * cv)8858 static void _CVUnlinkRef(CharView *cv) {
8859     int anyrefs=0;
8860     RefChar *rf, *next;
8861 
8862     if ( cv->b.layerheads[cv->b.drawmode]->refs!=NULL ) {
8863 	CVPreserveState(&cv->b);
8864 	for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL && !anyrefs; rf=rf->next )
8865 	    if ( rf->selected ) anyrefs = true;
8866 	for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL ; rf=next ) {
8867 	    next = rf->next;
8868 	    if ( rf->selected || !anyrefs) {
8869 		SCRefToSplines(cv->b.sc,rf,CVLayer((CharViewBase *) cv));
8870 	    }
8871 	}
8872 	CVSetCharChanged(cv,true);
8873 	SCUpdateAll(cv->b.sc);
8874 	/* Don't need to update dependancies, their splines won't have changed*/
8875     }
8876 }
8877 
CVUnlinkRef(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))8878 static void CVUnlinkRef(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
8879     CharView *cv = (CharView *) GDrawGetUserData(gw);
8880     _CVUnlinkRef(cv);
8881 }
8882 
8883 typedef struct getValueDialogData
8884 {
8885     int done;
8886     int cancelled;
8887     CharView *cv;
8888     GWindow gw;
8889     char* ret;
8890     GTextInfo label;
8891 } GetValueDialogData;
8892 
getValueDialogData_e_h(GWindow gw,GEvent * event)8893 static int getValueDialogData_e_h(GWindow gw, GEvent *event) {
8894     if ( event->type==et_close ) {
8895 	GetValueDialogData *hd = GDrawGetUserData(gw);
8896 	hd->done = true;
8897     } else if ( event->type == et_char ) {
8898 return( false );
8899     } else if ( event->type == et_map ) {
8900 	/* Above palettes */
8901 	GDrawRaise(gw);
8902     }
8903 return( true );
8904 }
8905 
getValueFromUser_OK(GGadget * g,GEvent * e)8906 static int getValueFromUser_OK(GGadget *g, GEvent *e)
8907 {
8908     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
8909         GetValueDialogData *hd = GDrawGetUserData(GGadgetGetWindow(g));
8910         strcpy( hd->ret, u_to_c(hd->label.text));
8911         strcpy( hd->ret, GGadgetGetTitle8(GWidgetGetControl(hd->gw,CID_getValueFromUser)));
8912         hd->done = true;
8913     }
8914     return( true );
8915 }
8916 
getValueFromUser_Cancel(GGadget * g,GEvent * e)8917 static int getValueFromUser_Cancel(GGadget *g, GEvent *e) {
8918     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
8919         GetValueDialogData *hd = GDrawGetUserData(GGadgetGetWindow(g));
8920         hd->cancelled = true;
8921         hd->done = true;
8922     }
8923     return( true );
8924 }
8925 
getValueFromUser(CharView * cv,const char * windowTitle,const char * msg,const char * defaultValue)8926 static char* getValueFromUser( CharView *cv, const char* windowTitle, const char* msg, const char* defaultValue )
8927 {
8928     const int retsz = 4096;
8929     static char ret[4097];
8930     static GetValueDialogData DATA;
8931     GRect pos;
8932     GWindow gw;
8933     GWindowAttrs wattrs;
8934     GGadgetCreateData gcd[9], *harray1[4], *harray2[9], *barray[7], *varray[5][2], boxes[5];
8935     GTextInfo label[9];
8936 
8937     DATA.cancelled = false;
8938     DATA.done = false;
8939     DATA.cv = cv;
8940     DATA.ret = ret;
8941     ret[0] = '\0';
8942 
8943     if ( DATA.gw==NULL ) {
8944 	memset(&wattrs,0,sizeof(wattrs));
8945 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
8946 	wattrs.event_masks = ~(1<<et_charup);
8947 	wattrs.restrict_input_to_me = 1;
8948 	wattrs.undercursor = 1;
8949 	wattrs.cursor = ct_pointer;
8950 	wattrs.utf8_window_title = windowTitle;
8951 	wattrs.is_dlg = true;
8952 	pos.x = pos.y = 0;
8953 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,170));
8954 	pos.height = GDrawPointsToPixels(NULL,90);
8955 	DATA.gw = gw = GDrawCreateTopWindow(NULL,&pos,getValueDialogData_e_h,&DATA,&wattrs);
8956 
8957 	memset(&label,0,sizeof(label));
8958 	memset(&gcd,  0,sizeof(gcd));
8959 	memset(&boxes,0,sizeof(boxes));
8960 
8961 	label[0].text = (unichar_t *) msg;
8962 	label[0].text_is_1byte = true;
8963 	label[0].text_in_resource = true;
8964 	gcd[0].gd.label = &label[0];
8965 	gcd[0].gd.pos.x = 5;
8966 	gcd[0].gd.pos.y = 5;
8967 	gcd[0].gd.flags = gg_enabled|gg_visible;
8968 	gcd[0].creator = GLabelCreate;
8969 	harray1[0] = GCD_Glue;
8970 	harray1[1] = &gcd[0];
8971 	harray1[2] = 0;
8972 
8973 	label[1].text = (unichar_t *) defaultValue;
8974 	label[1].text_is_1byte = true;
8975 	DATA.label = label[1];
8976 	gcd[1].gd.label = &label[1];
8977 	gcd[1].gd.pos.x = 5;
8978 	gcd[1].gd.pos.y = 17+5;
8979 	gcd[1].gd.pos.width = 40;
8980 	gcd[1].gd.flags = gg_enabled|gg_visible;
8981 	gcd[1].gd.cid = CID_getValueFromUser;
8982 	gcd[1].creator = GTextFieldCreate;
8983 	harray2[0] = &gcd[1];
8984 	harray2[1] = 0;
8985 
8986 	int idx = 2;
8987 	gcd[idx].gd.pos.x = 20-3;
8988 	gcd[idx].gd.pos.y = 17+37;
8989 	gcd[idx].gd.pos.width = -1;
8990 	gcd[idx].gd.pos.height = 0;
8991 	gcd[idx].gd.flags = gg_visible | gg_enabled | gg_but_default;
8992 	label[idx].text = (unichar_t *) _("_OK");
8993 	label[idx].text_is_1byte = true;
8994 	label[idx].text_in_resource = true;
8995 	gcd[idx].gd.mnemonic = 'O';
8996 	gcd[idx].gd.label = &label[idx];
8997 	gcd[idx].gd.handle_controlevent = getValueFromUser_OK;
8998 	gcd[idx].creator = GButtonCreate;
8999 	barray[0] = GCD_Glue;
9000 	barray[1] = &gcd[idx];
9001 	barray[2] = GCD_Glue;
9002 
9003 	++idx;
9004 	gcd[idx].gd.pos.x = -20;
9005 	gcd[idx].gd.pos.y = 17+37+3;
9006 	gcd[idx].gd.pos.width = -1;
9007 	gcd[idx].gd.pos.height = 0;
9008 	gcd[idx].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
9009 	label[idx].text = (unichar_t *) _("_Cancel");
9010 	label[idx].text_is_1byte = true;
9011 	label[idx].text_in_resource = true;
9012 	gcd[idx].gd.label = &label[idx];
9013 	gcd[idx].gd.mnemonic = 'C';
9014 	gcd[idx].gd.handle_controlevent = getValueFromUser_Cancel;
9015 	gcd[idx].creator = GButtonCreate;
9016 	barray[3] = GCD_Glue;
9017 	barray[4] = &gcd[idx];
9018 	barray[5] = GCD_Glue;
9019 	barray[6] = NULL;
9020 
9021 	gcd[7].gd.pos.x = 5;
9022 	gcd[7].gd.pos.y = 17+31;
9023 	gcd[7].gd.pos.width = 170-10;
9024 	gcd[7].gd.flags = gg_enabled|gg_visible;
9025 	gcd[7].creator = GLineCreate;
9026 
9027 	boxes[2].gd.flags = gg_enabled|gg_visible;
9028 	boxes[2].gd.u.boxelements = harray1;
9029 	boxes[2].creator = GHBoxCreate;
9030 
9031 	boxes[3].gd.flags = gg_enabled|gg_visible;
9032 	boxes[3].gd.u.boxelements = harray2;
9033 	boxes[3].creator = GHBoxCreate;
9034 
9035 	boxes[4].gd.flags = gg_enabled|gg_visible;
9036 	boxes[4].gd.u.boxelements = barray;
9037 	boxes[4].creator = GHBoxCreate;
9038 
9039 	varray[0][0] = &boxes[2]; varray[0][1] = NULL;
9040 	varray[1][0] = &boxes[3]; varray[1][1] = NULL;
9041 	varray[2][0] = &gcd[7];   varray[2][1] = NULL;
9042 	varray[3][0] = &boxes[4]; varray[3][1] = NULL;
9043 	varray[4][0] = NULL;
9044 
9045 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
9046 	boxes[0].gd.flags = gg_enabled|gg_visible;
9047 	boxes[0].gd.u.boxelements = varray[0];
9048 	boxes[0].creator = GHVGroupCreate;
9049 
9050 	GGadgetsCreate(gw,boxes);
9051 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandglue);
9052 	GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
9053 	GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame);
9054 	GHVBoxFitWindow(boxes[0].ret);
9055     } else {
9056 	gw = DATA.gw;
9057 	snprintf( ret, retsz, "%s", defaultValue );
9058 	GGadgetSetTitle8(GWidgetGetControl(gw,CID_getValueFromUser),ret);
9059 	GDrawSetTransientFor(gw,(GWindow) -1);
9060     }
9061 
9062     GWidgetIndicateFocusGadget(GWidgetGetControl(gw,CID_getValueFromUser));
9063     GTextFieldSelect(GWidgetGetControl(gw,CID_getValueFromUser),0,-1);
9064 
9065     GWidgetHidePalettes();
9066     GDrawSetVisible(gw,true);
9067     while ( !DATA.done )
9068 	GDrawProcessOneEvent(NULL);
9069     GDrawSetVisible(gw,false);
9070 
9071     if( DATA.cancelled )
9072         return 0;
9073     return ret;
9074 }
9075 
9076 
9077 
CVRemoveUndoes(GWindow gw,struct gmenuitem * mi,GEvent * e)9078 static void CVRemoveUndoes(GWindow gw,struct gmenuitem *mi,GEvent *e)
9079 {
9080     CharView *cv = (CharView *) GDrawGetUserData(gw);
9081     static int lastValue = 10;
9082     int v = toint(getValueFromUser( cv,
9083 				    _("Trimming Undo Information"),
9084 				    _("How many most-recent Undos should be kept?"),
9085 				    tostr(lastValue)));
9086     lastValue = v;
9087     UndoesFreeButRetainFirstN(&cv->b.layerheads[cv->b.drawmode]->undoes,v);
9088     UndoesFreeButRetainFirstN(&cv->b.layerheads[cv->b.drawmode]->redoes,v);
9089 }
9090 
9091 
9092 /* We can only paste if there's something in the copy buffer */
9093 /* we can only copy if there's something selected to copy */
9094 /* figure out what things are possible from the edit menu before the user */
9095 /*  pulls it down */
cv_edlistcheck(CharView * cv,struct gmenuitem * mi)9096 static void cv_edlistcheck(CharView *cv, struct gmenuitem *mi) {
9097     int anypoints, anyrefs, anyimages, anyanchor;
9098 
9099     CVAnySel(cv,&anypoints,&anyrefs,&anyimages,&anyanchor);
9100 
9101     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
9102 	switch ( mi->mid ) {
9103 	  case MID_Join:
9104 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL;
9105 	  break;
9106 	  case MID_Merge:
9107 	    mi->ti.disabled = !anypoints;
9108 	  break;
9109 	  case MID_MergeToLine:
9110 	    mi->ti.disabled = !anypoints;
9111 	  break;
9112 	  case MID_Clear: case MID_Cut: /*case MID_Copy:*/
9113 	    /* If nothing is selected, copy copies everything */
9114 	    /* In spiro mode copy will copy all contours with at least (spiro) one point selected */
9115 	    mi->ti.disabled = !anypoints && !anyrefs && !anyimages && !anyanchor;
9116 	  break;
9117 	  case MID_CopyLBearing: case MID_CopyRBearing:
9118 	    mi->ti.disabled = cv->b.drawmode!=dm_fore ||
9119 		    (cv->b.layerheads[cv->b.drawmode]->splines==NULL && cv->b.layerheads[cv->b.drawmode]->refs==NULL);
9120 	  break;
9121 	  case MID_CopyFgToBg:
9122 	    mi->ti.disabled = cv->b.sc->layers[ly_fore].splines==NULL;
9123 	  break;
9124 	  case MID_CopyGridFit:
9125 	    mi->ti.disabled = cv->b.gridfit==NULL;
9126 	  break;
9127 	  case MID_Paste:
9128 	    mi->ti.disabled = !CopyContainsSomething() && !SCClipboardHasPasteableContents();
9129 	  break;
9130 	  case MID_Undo:
9131 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->undoes==NULL;
9132 	  break;
9133 	  case MID_Redo:
9134 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->redoes==NULL;
9135 	  break;
9136 	  case MID_RemoveUndoes:
9137 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->undoes==NULL && cv->b.layerheads[cv->b.drawmode]->redoes==NULL;
9138 	  break;
9139 	  case MID_CopyRef:
9140 	    mi->ti.disabled = cv->b.drawmode!=dm_fore || cv->b.container!=NULL;
9141 	  break;
9142 	  case MID_CopyLookupData:
9143 	    mi->ti.disabled = (cv->b.sc->possub==NULL && cv->b.sc->kerns==NULL && cv->b.sc->vkerns==NULL) ||
9144 		    cv->b.container!=NULL;
9145 	  break;
9146 	  case MID_UnlinkRef:
9147 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->refs==NULL;
9148 	  break;
9149 	}
9150     }
9151 }
9152 
edlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9153 static void edlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9154     CharView *cv = (CharView *) GDrawGetUserData(gw);
9155     cv_edlistcheck(cv, mi);
9156 }
9157 
CVMenuAcceptableExtrema(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9158 static void CVMenuAcceptableExtrema(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9159     CharView *cv = (CharView *) GDrawGetUserData(gw);
9160     SplineSet *ss;
9161     Spline *s, *first;
9162 
9163     for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL ; ss = ss->next ) {
9164 	first = NULL;
9165 	for ( s=ss->first->next; s!=NULL && s!=first; s = s->to->next ) {
9166 	    if ( first == NULL )
9167 		first = s;
9168 	    if ( s->from->selected && s->to->selected )
9169 		s->acceptableextrema = !s->acceptableextrema;
9170 	}
9171     }
9172 }
9173 
_CVMenuPointType(CharView * cv,struct gmenuitem * mi)9174 static void _CVMenuPointType(CharView *cv, struct gmenuitem *mi) {
9175     int pointtype = mi->mid==MID_Corner?pt_corner:mi->mid==MID_Tangent?pt_tangent:
9176 	    mi->mid==MID_Curve?pt_curve:pt_hvcurve;
9177     SplinePointList *spl;
9178     Spline *spline, *first;
9179 
9180     CVPreserveState(&cv->b);	/* We should only get here if there's a selection */
9181     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL ; spl = spl->next ) {
9182 	first = NULL;
9183 	if ( spl->first->selected ) {
9184 	    if ( spl->first->pointtype!=pointtype )
9185 		SPChangePointType(spl->first,pointtype);
9186 	}
9187 	for ( spline=spl->first->next; spline!=NULL && spline!=first ; spline = spline->to->next ) {
9188 	    if ( spline->to->selected ) {
9189 	    if ( spline->to->pointtype!=pointtype )
9190 		SPChangePointType(spline->to,pointtype);
9191 	    }
9192 	    if ( first == NULL ) first = spline;
9193 	}
9194     }
9195     CVCharChangedUpdate(&cv->b);
9196 }
9197 
_CVMenuSpiroPointType(CharView * cv,struct gmenuitem * mi)9198 static void _CVMenuSpiroPointType(CharView *cv, struct gmenuitem *mi) {
9199     int pointtype = mi->mid==MID_SpiroCorner?SPIRO_CORNER:
9200 		    mi->mid==MID_SpiroG4?SPIRO_G4:
9201 		    mi->mid==MID_SpiroG2?SPIRO_G2:
9202 		    mi->mid==MID_SpiroLeft?SPIRO_LEFT:SPIRO_RIGHT;
9203     SplinePointList *spl;
9204     int i, changes;
9205 
9206     CVPreserveState(&cv->b);	/* We should only get here if there's a selection */
9207     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL ; spl = spl->next ) {
9208 	changes = false;
9209 	for ( i=0; i<spl->spiro_cnt-1; ++i ) {
9210 	    if ( SPIRO_SELECTED(&spl->spiros[i]) ) {
9211 		if ( (spl->spiros[i].ty&0x7f)!=SPIRO_OPEN_CONTOUR ) {
9212 		    spl->spiros[i].ty = pointtype|0x80;
9213 		    changes = true;
9214 		}
9215 	    }
9216 	}
9217 	if ( changes )
9218 	    SSRegenerateFromSpiros(spl);
9219     }
9220     CVCharChangedUpdate(&cv->b);
9221 }
9222 
9223 
CVMenuPointType(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9224 void CVMenuPointType(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9225     CharView *cv = (CharView *) GDrawGetUserData(gw);
9226     if ( cv->b.sc->inspiro && hasspiro())
9227 	_CVMenuSpiroPointType(cv, mi);
9228     else
9229 	_CVMenuPointType(cv, mi);
9230 }
9231 
_CVMenuImplicit(CharView * cv,struct gmenuitem * mi)9232 static void _CVMenuImplicit(CharView *cv, struct gmenuitem *mi) {
9233     SplinePointList *spl;
9234     Spline *spline, *first;
9235     int dontinterpolate = mi->mid==MID_NoImplicitPt;
9236 
9237     if ( !cv->b.layerheads[cv->b.drawmode]->order2 )
9238 return;
9239     CVPreserveState(&cv->b);	/* We should only get here if there's a selection */
9240     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL ; spl = spl->next ) {
9241 	first = NULL;
9242 	if ( spl->first->selected ) {
9243 	    spl->first->dontinterpolate = dontinterpolate;
9244 	}
9245 	for ( spline=spl->first->next; spline!=NULL && spline!=first ; spline = spline->to->next ) {
9246 	    if ( spline->to->selected ) {
9247 		spline->to->dontinterpolate = dontinterpolate;
9248 	    }
9249 	    if ( first == NULL ) first = spline;
9250 	}
9251     }
9252     CVCharChangedUpdate(&cv->b);
9253 }
9254 
CVMenuImplicit(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9255 static void CVMenuImplicit(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9256     CharView *cv = (CharView *) GDrawGetUserData(gw);
9257     _CVMenuImplicit(cv, mi);
9258 }
9259 
9260 static GMenuItem2 spiroptlist[], ptlist[];
cv_ptlistcheck(CharView * cv,struct gmenuitem * mi)9261 static void cv_ptlistcheck(CharView *cv, struct gmenuitem *mi) {
9262     int type = -2, cnt=0, ccp_cnt=0, spline_selected=0;
9263     int spirotype = -2, opencnt=0, spirocnt=0;
9264     SplinePointList *spl, *sel=NULL, *onlysel=NULL;
9265     Spline *spline, *first;
9266     SplinePoint *selpt=NULL;
9267     int notimplicit = -1;
9268     int acceptable = -1;
9269     uint16 junk;
9270     int i;
9271 
9272     if ( cv->showing_spiro_pt_menu != (cv->b.sc->inspiro && hasspiro())) {
9273 	GMenuItemArrayFree(mi->sub);
9274 	mi->sub = GMenuItem2ArrayCopy(cv->b.sc->inspiro && hasspiro()?spiroptlist:ptlist,&junk);
9275 	cv->showing_spiro_pt_menu = cv->b.sc->inspiro && hasspiro();
9276     }
9277     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
9278 	first = NULL;
9279 	if ( spl->first->selected ) {
9280 	    sel = spl;
9281 	    if ( onlysel==NULL || onlysel==spl ) onlysel = spl; else onlysel = (SplineSet *) (-1);
9282 	    selpt = spl->first; ++cnt;
9283 	    if ( type==-2 ) type = spl->first->pointtype;
9284 	    else if ( type!=spl->first->pointtype ) type = -1;
9285 	    if ( !spl->first->nonextcp && !spl->first->noprevcp && spl->first->prev!=NULL )
9286 		++ccp_cnt;
9287 	    if ( notimplicit==-1 ) notimplicit = spl->first->dontinterpolate;
9288 	    else if ( notimplicit!=spl->first->dontinterpolate ) notimplicit = -2;
9289 	}
9290 	for ( spline=spl->first->next; spline!=NULL && spline!=first; spline = spline->to->next ) {
9291 	    if ( spline->to->selected ) {
9292 		if ( type==-2 ) type = spline->to->pointtype;
9293 		else if ( type!=spline->to->pointtype ) type = -1;
9294 		selpt = spline->to;
9295 		if ( onlysel==NULL || onlysel==spl ) onlysel = spl; else onlysel = (SplineSet *) (-1);
9296 		sel = spl; ++cnt;
9297 		if ( !spline->to->nonextcp && !spline->to->noprevcp && spline->to->next!=NULL )
9298 		    ++ccp_cnt;
9299 		if ( notimplicit==-1 ) notimplicit = spline->to->dontinterpolate;
9300 		else if ( notimplicit!=spline->to->dontinterpolate ) notimplicit = -2;
9301 		if ( spline->from->selected )
9302 		    ++spline_selected;
9303 	    }
9304 	    if ( spline->to->selected && spline->from->selected ) {
9305 		if ( acceptable==-1 )
9306 		    acceptable = spline->acceptableextrema;
9307 		else if ( acceptable!=spline->acceptableextrema )
9308 		    acceptable = -2;
9309 	    }
9310 	    if ( first == NULL ) first = spline;
9311 	}
9312 	for ( i=0; i<spl->spiro_cnt-1; ++i ) {
9313 	    if ( SPIRO_SELECTED(&spl->spiros[i])) {
9314 		int ty = spl->spiros[i].ty&0x7f;
9315 		++spirocnt;
9316 		if ( ty==SPIRO_OPEN_CONTOUR )
9317 		    ++opencnt;
9318 		else if ( spirotype==-2 )
9319 		    spirotype = ty;
9320 		else if ( spirotype!=ty )
9321 		    spirotype = -1;
9322 		if ( onlysel==NULL || onlysel==spl ) onlysel = spl; else onlysel = (SplineSet *) (-1);
9323 	    }
9324 	}
9325     }
9326 
9327     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
9328 	switch ( mi->mid ) {
9329 	  case MID_Corner:
9330 	    mi->ti.disabled = type==-2;
9331 	    mi->ti.checked = type==pt_corner;
9332 	  break;
9333 	  case MID_Tangent:
9334 	    mi->ti.disabled = type==-2;
9335 	    mi->ti.checked = type==pt_tangent;
9336 	  break;
9337 	  case MID_Curve:
9338 	    mi->ti.disabled = type==-2;
9339 	    mi->ti.checked = type==pt_curve;
9340 	  break;
9341 	  case MID_HVCurve:
9342 	    mi->ti.disabled = type==-2;
9343 	    mi->ti.checked = type==pt_hvcurve;
9344 	  break;
9345 	  case MID_SpiroG4:
9346 	    mi->ti.disabled = spirotype==-2;
9347 	    mi->ti.checked = spirotype==SPIRO_G4;
9348 	  break;
9349 	  case MID_SpiroG2:
9350 	    mi->ti.disabled = spirotype==-2;
9351 	    mi->ti.checked = spirotype==SPIRO_G2;
9352 	  break;
9353 	  case MID_SpiroCorner:
9354 	    mi->ti.disabled = spirotype==-2;
9355 	    mi->ti.checked = spirotype==SPIRO_CORNER;
9356 	  break;
9357 	  case MID_SpiroLeft:
9358 	    mi->ti.disabled = spirotype==-2;
9359 	    mi->ti.checked = spirotype==SPIRO_LEFT;
9360 	  break;
9361 	  case MID_SpiroRight:
9362 	    mi->ti.disabled = spirotype==-2;
9363 	    mi->ti.checked = spirotype==SPIRO_RIGHT;
9364 	  break;
9365 	  case MID_MakeFirst:
9366 	    mi->ti.disabled = cnt!=1 || sel->first->prev==NULL || sel->first==selpt;
9367 	  break;
9368 	  case MID_SpiroMakeFirst:
9369 	    mi->ti.disabled = opencnt!=0 || spirocnt!=1;
9370 	  break;
9371 	  case MID_MakeLine: case MID_MakeArc:
9372 	    mi->ti.disabled = cnt<2;
9373 	  break;
9374 	  case MID_AcceptableExtrema:
9375 	    mi->ti.disabled = acceptable<0;
9376 	    mi->ti.checked = acceptable==1;
9377 	  break;
9378 	  case MID_NamePoint:
9379 	    mi->ti.disabled = onlysel==NULL || onlysel == (SplineSet *) -1;
9380 	  break;
9381 	  case MID_NameContour:
9382 	    mi->ti.disabled = onlysel==NULL || onlysel == (SplineSet *) -1;
9383 	  break;
9384 	  case MID_ClipPath:
9385 	    mi->ti.disabled = !cv->b.sc->parent->multilayer;
9386 	  break;
9387 	  case MID_InsertPtOnSplineAt:
9388 	    mi->ti.disabled = spline_selected!=1;
9389 	  break;
9390 	  case MID_CenterCP:
9391 	    mi->ti.disabled = ccp_cnt==0;
9392 	  break;
9393 	  case MID_ImplicitPt:
9394 	    mi->ti.disabled = !cv->b.layerheads[cv->b.drawmode]->order2;
9395 	    mi->ti.checked = notimplicit==0;
9396 	  break;
9397 	  case MID_NoImplicitPt:
9398 	    mi->ti.disabled = !cv->b.layerheads[cv->b.drawmode]->order2;
9399 	    mi->ti.checked = notimplicit==1;
9400 	  break;
9401 	  case MID_AddAnchor:
9402 	    mi->ti.disabled = cv->b.container!=NULL;
9403 	  break;
9404 	}
9405     }
9406 }
9407 
ptlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9408 static void ptlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9409     CharView *cv = (CharView *) GDrawGetUserData(gw);
9410     cv_ptlistcheck(cv, mi);
9411 }
9412 
_CVMenuDir(CharView * cv,struct gmenuitem * mi)9413 static void _CVMenuDir(CharView *cv, struct gmenuitem *mi) {
9414     int splinepoints, dir;
9415     SplinePointList *spl;
9416     Spline *spline, *first;
9417     int needsrefresh = false;
9418 
9419     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
9420 	first = NULL;
9421 	splinepoints = 0;
9422 	if ( cv->b.sc->inspiro  && hasspiro()) {
9423 	    int i;
9424 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
9425 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
9426 		    splinepoints = true;
9427 	    break;
9428 		}
9429 	} else {
9430 	    if ( spl->first->selected ) splinepoints = true;
9431 	    for ( spline=spl->first->next; spline!=NULL && spline!=first && !splinepoints; spline = spline->to->next ) {
9432 		if ( spline->to->selected ) splinepoints = true;
9433 		if ( first == NULL ) first = spline;
9434 	    }
9435 	}
9436 	if ( splinepoints && spl->first->prev!=NULL ) {
9437 	    dir = SplinePointListIsClockwise(spl);
9438 	    if ( (mi->mid==MID_Clockwise && dir==0) || (mi->mid==MID_Counter && dir==1)) {
9439 		if ( !needsrefresh )
9440 		    CVPreserveState(&cv->b);
9441 		SplineSetReverse(spl);
9442 		needsrefresh = true;
9443 	    }
9444 	}
9445     }
9446     if ( needsrefresh )
9447 	CVCharChangedUpdate(&cv->b);
9448 }
9449 
CVMenuDir(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9450 static void CVMenuDir(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9451     CharView *cv = (CharView *) GDrawGetUserData(gw);
9452     _CVMenuDir(cv, mi);
9453 }
9454 
CVMenuCheckSelf(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9455 static void CVMenuCheckSelf(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9456     CharView *cv = (CharView *) GDrawGetUserData(gw);
9457     CVShows.checkselfintersects = cv->checkselfintersects = !cv->checkselfintersects;
9458 }
9459 
CVMenuGlyphSelfIntersects(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9460 static void CVMenuGlyphSelfIntersects(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9461     CharView *cv = (CharView *) GDrawGetUserData(gw);
9462     Spline *s=NULL, *s2=NULL;
9463     SplineSet *ss;
9464     DBounds b;
9465     double off;
9466 
9467     ss = LayerAllSplines(cv->b.layerheads[cv->b.drawmode]);
9468     SplineSetIntersect(ss,&s,&s2);
9469     LayerUnAllSplines(cv->b.layerheads[cv->b.drawmode]);
9470 
9471     if ( s!=NULL || s2!=NULL ) {
9472 	memset(&b,0,sizeof(b));
9473 	CVClearSel(cv);
9474 	if ( s!=NULL ) {
9475 	    b.minx = b.maxx = s->from->me.x;
9476 	    b.miny = b.maxy = s->from->me.y;
9477 	} else if ( s2!=NULL ) {
9478 	    b.minx = b.maxx = s2->from->me.x;
9479 	    b.miny = b.maxy = s2->from->me.y;
9480 	}
9481 	if ( s!=NULL ) {
9482 	    s->from->selected = s->to->selected = true;
9483 	    if ( s->to->me.x>b.maxx ) b.maxx = s->to->me.x;
9484 	    if ( s->to->me.x<b.minx ) b.minx = s->to->me.x;
9485 	    if ( s->to->me.y>b.maxy ) b.maxy = s->to->me.y;
9486 	    if ( s->to->me.y<b.miny ) b.miny = s->to->me.y;
9487 	}
9488 	if ( s2!=NULL ) {
9489 	    s2->from->selected = s2->to->selected = true;
9490 	    if ( s2->from->me.x>b.maxx ) b.maxx = s2->from->me.x;
9491 	    if ( s2->from->me.x<b.minx ) b.minx = s2->from->me.x;
9492 	    if ( s2->from->me.y>b.maxy ) b.maxy = s2->from->me.y;
9493 	    if ( s2->from->me.y<b.miny ) b.miny = s2->from->me.y;
9494 	    if ( s2->to->me.x>b.maxx ) b.maxx = s2->to->me.x;
9495 	    if ( s2->to->me.x<b.minx ) b.minx = s2->to->me.x;
9496 	    if ( s2->to->me.y>b.maxy ) b.maxy = s2->to->me.y;
9497 	    if ( s2->to->me.y<b.miny ) b.miny = s2->to->me.y;
9498 	}
9499 	off = (b.maxx-b.minx)/10;
9500 	if ( off==0 ) off = 1;
9501 	b.minx -= off; b.maxx += off;
9502 	off = (b.maxy-b.miny)/10;
9503 	if ( off==0 ) off = 1;
9504 	b.miny -= off; b.maxy += off;
9505 	_CVFit(cv,&b,false);
9506     } else
9507 	ff_post_notice(_("No Intersections"),_("No Intersections"));
9508 }
9509 
getorigin(void * d,BasePoint * base,int index)9510 static int getorigin(void *d,BasePoint *base,int index) {
9511     CharView *cv = (CharView *) d;
9512 
9513     base->x = base->y = 0;
9514     switch ( index ) {
9515       case 0:		/* Character origin */
9516 	/* all done */
9517       break;
9518       case 1:		/* Center of selection */
9519 	CVFindCenter(cv,base,!CVAnySel(cv,NULL,NULL,NULL,NULL));
9520       break;
9521       case 2:		/* last press */
9522 	base->x = cv->p.cx;
9523 	base->y = cv->p.cy;
9524 	/* I don't have any way of telling if a press has happened. if one */
9525 	/*  hasn't they'll just get a 0,0 origin. oh well */
9526       break;
9527       default:
9528 return( false );
9529     }
9530 return( true );
9531 }
9532 
TransRef(RefChar * ref,real transform[6],enum fvtrans_flags flags)9533 static void TransRef(RefChar *ref,real transform[6], enum fvtrans_flags flags) {
9534     int j;
9535     real t[6];
9536 
9537     for ( j=0; j<ref->layer_cnt; ++j )
9538 	SplinePointListTransform(ref->layers[j].splines,transform,tpt_AllPoints);
9539     t[0] = ref->transform[0]*transform[0] +
9540 		ref->transform[1]*transform[2];
9541     t[1] = ref->transform[0]*transform[1] +
9542 		ref->transform[1]*transform[3];
9543     t[2] = ref->transform[2]*transform[0] +
9544 		ref->transform[3]*transform[2];
9545     t[3] = ref->transform[2]*transform[1] +
9546 		ref->transform[3]*transform[3];
9547     t[4] = ref->transform[4]*transform[0] +
9548 		ref->transform[5]*transform[2] +
9549 		transform[4];
9550     t[5] = ref->transform[4]*transform[1] +
9551 		ref->transform[5]*transform[3] +
9552 		transform[5];
9553     if ( flags&fvt_round_to_int ) {
9554 	t[4] = rint( t[4] );
9555 	t[5] = rint( t[5] );
9556     }
9557     memcpy(ref->transform,t,sizeof(t));
9558     RefCharFindBounds(ref);
9559 }
9560 
CVTransFuncLayer(CharView * cv,Layer * ly,real transform[6],enum fvtrans_flags flags)9561 void CVTransFuncLayer(CharView *cv,Layer *ly,real transform[6], enum fvtrans_flags flags)
9562 {
9563     int anysel = cv->p.transany;
9564     RefChar *refs;
9565     ImageList *img;
9566     AnchorPoint *ap;
9567     KernPair *kp;
9568     PST *pst;
9569     int l, cvlayer;
9570     enum transformPointMask tpmask = 0;
9571 
9572     if ( cv->b.sc->inspiro && hasspiro() )
9573 	SplinePointListSpiroTransform(ly->splines,transform,!anysel);
9574     else
9575     {
9576 	if( cv->active_tool==cvt_scale )
9577 	    tpmask |= tpmask_operateOnSelectedBCP;
9578 
9579 	SplinePointListTransformExtended(
9580 	    ly->splines, transform,
9581 	    !anysel?tpt_AllPoints: interpCPsOnMotion?tpt_OnlySelectedInterpCPs:tpt_OnlySelected,
9582 	    tpmask );
9583     }
9584 
9585     if ( flags&fvt_round_to_int )
9586 	SplineSetsRound2Int(ly->splines,1.0,cv->b.sc->inspiro && hasspiro(),!anysel);
9587     if ( ly->images!=NULL ) {
9588 	ImageListTransform(ly->images,transform,!anysel);
9589 	SCOutOfDateBackground(cv->b.sc);
9590     }
9591     for ( refs = ly->refs; refs!=NULL; refs=refs->next )
9592 	if ( refs->selected || !anysel )
9593 	    TransRef(refs,transform,flags);
9594     if ( cv->showanchor ) {
9595 	for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next ) if ( ap->selected || !anysel )
9596 	    ApTransform(ap,transform);
9597     }
9598     if ( !anysel ) {
9599 	if ( flags & fvt_scalepstpos ) {
9600 	    for ( kp=cv->b.sc->kerns; kp!=NULL; kp=kp->next )
9601 		kp->off = rint(kp->off*transform[0]);
9602 	    for ( kp=cv->b.sc->vkerns; kp!=NULL; kp=kp->next )
9603 		kp->off = rint(kp->off*transform[3]);
9604 	    for ( pst = cv->b.sc->possub; pst!=NULL; pst=pst->next ) {
9605 		if ( pst->type == pst_position )
9606 		    VrTrans(&pst->u.pos,transform);
9607 		else if ( pst->type==pst_pair ) {
9608 		    VrTrans(&pst->u.pair.vr[0],transform);
9609 		    VrTrans(&pst->u.pair.vr[1],transform);
9610 		}
9611 	    }
9612 	}
9613 	if ( transform[1]==0 && transform[2]==0 ) {
9614 	    TransHints(cv->b.sc->hstem,transform[3],transform[5],transform[0],transform[4],flags&fvt_round_to_int);
9615 	    TransHints(cv->b.sc->vstem,transform[0],transform[4],transform[3],transform[5],flags&fvt_round_to_int);
9616 	    TransDStemHints(cv->b.sc->dstem,transform[0],transform[4],transform[3],transform[5],flags&fvt_round_to_int);
9617 	}
9618 	if ( transform[0]==1 && transform[3]==1 && transform[1]==0 &&
9619 		transform[2]==0 && transform[5]==0 &&
9620 		transform[4]!=0 && CVAllSelected(cv) &&
9621 		cv->b.sc->unicodeenc!=-1 && isalpha(cv->b.sc->unicodeenc)) {
9622 	    SCUndoSetLBearingChange(cv->b.sc,(int) rint(transform[4]));
9623 	    SCSynchronizeLBearing(cv->b.sc,transform[4],CVLayer((CharViewBase *) cv));
9624 	}
9625     }
9626     if ( !(flags&fvt_dontmovewidth) && (cv->widthsel || !anysel))
9627 	if ( transform[0]>0 && transform[3]>0 && transform[1]==0 &&
9628 		transform[2]==0 && transform[4]!=0 )
9629 	    SCSynchronizeWidth(cv->b.sc,cv->b.sc->width*transform[0]+transform[4],cv->b.sc->width,NULL);
9630     if ( !(flags&fvt_dontmovewidth) && (cv->vwidthsel || !anysel))
9631 	if ( transform[0]==1 && transform[3]==1 && transform[1]==0 &&
9632 		transform[2]==0 && transform[5]!=0 )
9633 	    cv->b.sc->vwidth+=transform[5];
9634     if ( (flags&fvt_alllayers) && !anysel ) {
9635 	/* SCPreserveBackground(cv->b.sc); */ /* done by caller */
9636 	cvlayer = CVLayer( (CharViewBase *) cv );
9637 	for ( l=0; l<cv->b.sc->layer_cnt; ++l ) if ( l!=cvlayer ) {
9638 	    for ( img = cv->b.sc->layers[l].images; img!=NULL; img=img->next )
9639 		BackgroundImageTransform(cv->b.sc, img, transform);
9640 	    SplinePointListTransform(cv->b.sc->layers[l].splines,
9641 		    transform,tpt_AllPoints);
9642 	    for ( refs=cv->b.sc->layers[l].refs; refs!=NULL; refs=refs->next )
9643 		TransRef(refs,transform,flags);
9644 	}
9645     }
9646 }
9647 
CVTransFunc(CharView * cv,real transform[6],enum fvtrans_flags flags)9648 void CVTransFunc(CharView *cv,real transform[6], enum fvtrans_flags flags)
9649 {
9650     Layer *ly = cv->b.layerheads[cv->b.drawmode];
9651     CVTransFuncLayer( cv, ly, transform, flags );
9652 }
9653 
CVTransFuncAllLayers(CharView * cv,real transform[6],enum fvtrans_flags flags)9654 void CVTransFuncAllLayers(CharView *cv,real transform[6], enum fvtrans_flags flags)
9655 {
9656     int idx;
9657     for( idx = 0; idx < cv->b.sc->layer_cnt; ++idx )
9658     {
9659 	Layer *ly = &cv->b.sc->layers[ idx ];
9660 	CVTransFuncLayer( cv, ly, transform, flags );
9661     }
9662 }
9663 
transfunc(void * d,real transform[6],int otype,BVTFunc * bvts,enum fvtrans_flags flags)9664 static void transfunc(void *d,real transform[6],int otype,BVTFunc *bvts,
9665 	enum fvtrans_flags flags) {
9666     CharView *cv = (CharView *) d;
9667     int anya, l, cvlayer = CVLayer((CharViewBase *) cv);
9668 
9669     if ( cv->b.layerheads[cv->b.drawmode]->undoes!=NULL &&
9670 	    cv->b.layerheads[cv->b.drawmode]->undoes->undotype==ut_tstate )
9671     {
9672 	CVDoUndo(&cv->b);
9673     }
9674 
9675     if ( flags&fvt_revert )
9676 	return;
9677 
9678     cv->p.transany = CVAnySel(cv,NULL,NULL,NULL,&anya);
9679     if ( flags&fvt_justapply )
9680 	CVPreserveTState(cv);
9681     else {
9682 	CVPreserveStateHints(&cv->b);
9683 	if ( flags&fvt_alllayers )
9684 	    for ( l=0; l<cv->b.sc->layer_cnt; ++l ) if ( l!=cvlayer )
9685 		SCPreserveLayer(cv->b.sc,l,false);
9686     }
9687 
9688     CVPreserveMaybeState( cv, flags&fvt_justapply );
9689     CVTransFunc(cv,transform,flags);
9690     CVCharChangedUpdate(&cv->b);
9691 }
9692 
CVDoTransform(CharView * cv,enum cvtools cvt)9693 void CVDoTransform(CharView *cv, enum cvtools cvt ) {
9694     int anysel = CVAnySel(cv,NULL,NULL,NULL,NULL);
9695     TransformDlgCreate(cv,transfunc,getorigin,!anysel?(tdf_enableback|tdf_addapply):tdf_addapply,
9696 	    cvt);
9697 }
9698 
CVMenuTransform(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9699 static void CVMenuTransform(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9700     CharView *cv = (CharView *) GDrawGetUserData(gw);
9701     CVDoTransform(cv,cvt_none);
9702 }
9703 
CVMenuPOV(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9704 static void CVMenuPOV(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9705     CharView *cv = (CharView *) GDrawGetUserData(gw);
9706     struct pov_data pov_data;
9707     if ( PointOfViewDlg(&pov_data,cv->b.sc->parent,true)==-1 )
9708 return;
9709     CVPointOfView(cv,&pov_data);
9710 }
9711 
CVMenuNLTransform(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9712 static void CVMenuNLTransform(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9713     CharView *cv = (CharView *) GDrawGetUserData(gw);
9714     cv->lastselpt = NULL; cv->lastselcp = NULL;
9715     NonLinearDlg(NULL,cv);
9716 }
9717 
CVMenuConstrain(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9718 void CVMenuConstrain(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9719     CharView *cv = (CharView *) GDrawGetUserData(gw);
9720     CVConstrainSelection( cv,
9721                           mi->mid==MID_Average  ? constrainSelection_AveragePoints :
9722                           mi->mid==MID_SpacePts ? constrainSelection_SpacePoints   :
9723                           constrainSelection_SpaceSelectedRegions );
9724 }
9725 
CVMenuMakeParallel(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9726 static void CVMenuMakeParallel(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9727     CharView *cv = (CharView *) GDrawGetUserData(gw);
9728     CVMakeParallel(cv);
9729 }
9730 
_CVMenuRound2Int(CharView * cv,double factor)9731 static void _CVMenuRound2Int(CharView *cv, double factor) {
9732     int anysel = CVAnySel(cv,NULL,NULL,NULL,NULL);
9733     RefChar *r;
9734     AnchorPoint *ap;
9735 
9736     CVPreserveState(&cv->b);
9737     SplineSetsRound2Int(cv->b.layerheads[cv->b.drawmode]->splines,factor,
9738 	    cv->b.sc->inspiro && hasspiro(), anysel);
9739     for ( r=cv->b.layerheads[cv->b.drawmode]->refs; r!=NULL; r=r->next ) {
9740 	if ( r->selected || !anysel ) {
9741 	    r->transform[4] = rint(r->transform[4]*factor)/factor;
9742 	    r->transform[5] = rint(r->transform[5]*factor)/factor;
9743 	}
9744     }
9745     if ( cv->b.drawmode==dm_fore ) {
9746 	for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next ) {
9747 	    if ( ap->selected || !anysel ) {
9748 		ap->me.x = rint(ap->me.x*factor)/factor;
9749 		ap->me.y = rint(ap->me.y*factor)/factor;
9750 	    }
9751 	}
9752     }
9753     CVCharChangedUpdate(&cv->b);
9754 }
9755 
CVMenuRound2Int(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9756 static void CVMenuRound2Int(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9757     CharView *cv = (CharView *) GDrawGetUserData(gw);
9758     _CVMenuRound2Int(cv,1.0);
9759 }
9760 
CVMenuRound2Hundredths(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9761 static void CVMenuRound2Hundredths(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9762     CharView *cv = (CharView *) GDrawGetUserData(gw);
9763     _CVMenuRound2Int(cv,100.0);
9764 }
9765 
CVMenuCluster(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9766 static void CVMenuCluster(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9767     CharView *cv = (CharView *) GDrawGetUserData(gw);
9768     int layer = cv->b.drawmode == dm_grid ? ly_grid :
9769 		cv->b.drawmode == dm_back ? ly_back
9770 					: cv->b.layerheads[dm_fore] - cv->b.sc->layers;
9771     SCRoundToCluster(cv->b.sc,layer,true,.1,.5);
9772 }
9773 
CVMenuStroke(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9774 static void CVMenuStroke(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9775     CharView *cv = (CharView *) GDrawGetUserData(gw);
9776     CVStroke(cv);
9777 }
9778 
9779 #ifdef FONTFORGE_CONFIG_TILEPATH
CVMenuTilePath(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9780 static void CVMenuTilePath(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9781     CharView *cv = (CharView *) GDrawGetUserData(gw);
9782     CVTile(cv);
9783 }
9784 
CVMenuPatternTile(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))9785 static void CVMenuPatternTile(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
9786     CharView *cv = (CharView *) GDrawGetUserData(gw);
9787     CVPatternTile(cv);
9788 }
9789 #endif
9790 
_CVMenuOverlap(CharView * cv,enum overlap_type ot)9791 static void _CVMenuOverlap(CharView *cv,enum overlap_type ot) {
9792     /* We know it's more likely that we'll find a problem in the overlap code */
9793     /*  than anywhere else, so let's save the current state against a crash */
9794     int layer = cv->b.drawmode == dm_grid ? ly_grid :
9795 		cv->b.drawmode == dm_back ? ly_back
9796 					: cv->b.layerheads[dm_fore] - cv->b.sc->layers;
9797 
9798     DoAutoSaves();
9799 #if 0
9800     // We await testing on the necessity of this operation.
9801     if ( !SCRoundToCluster(cv->b.sc,layer,false,.03,.12))
9802 	CVPreserveState(&cv->b);	/* SCRound2Cluster does this when it makes a change, not otherwise */
9803 #else
9804     CVPreserveState(&cv->b);
9805 #endif // 0
9806     if ( cv->b.drawmode==dm_fore ) {
9807 	MinimumDistancesFree(cv->b.sc->md);
9808 	cv->b.sc->md = NULL;
9809     }
9810     cv->b.layerheads[cv->b.drawmode]->splines = SplineSetRemoveOverlap(cv->b.sc,cv->b.layerheads[cv->b.drawmode]->splines,ot);
9811     // Check for removal of last selected points.
9812     if ( cv->b.sc->inspiro && hasspiro()) {
9813 	// Detection is not implemented for Spiro, so just clear them.
9814 	// TODO: Detect point survival in Spiro mode.
9815 	cv->p.sp = cv->lastselpt = NULL;
9816 	cv->p.spiro = cv->lastselcp = NULL;
9817     } else {
9818 	// Check whether the last selected point is still in the spline set.
9819 	// If not, remove the reference to it.
9820 	if (cv->lastselpt != NULL &&
9821 	        !SplinePointListContainsPoint(cv->b.layerheads[cv->b.drawmode]->splines, cv->lastselpt))
9822 	     cv->p.sp = cv->lastselpt = NULL;
9823 	cv->p.spiro = cv->lastselcp = NULL;
9824     }
9825     CVCharChangedUpdate(&cv->b);
9826 }
9827 
CVMenuOverlap(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9828 static void CVMenuOverlap(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9829     CharView *cv = (CharView *) GDrawGetUserData(gw);
9830     int anysel;
9831 
9832     (void) CVAnySel(cv,&anysel,NULL,NULL,NULL);
9833     _CVMenuOverlap(cv,mi->mid==MID_RmOverlap ? (anysel ? over_rmselected: over_remove) :
9834 		      mi->mid==MID_Intersection ? (anysel ? over_intersel : over_intersect ) :
9835 		      mi->mid==MID_Exclude ? over_exclude :
9836 			  (anysel ? over_fisel : over_findinter));
9837 }
9838 
CVMenuOrder(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))9839 static void CVMenuOrder(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
9840     CharView *cv = (CharView *) GDrawGetUserData(gw);
9841     SplinePointList *spl;
9842     RefChar *r;
9843     ImageList *im;
9844     int exactlyone = CVOneContourSel(cv,&spl,&r,&im);
9845 
9846     if ( !exactlyone )
9847 return;
9848 
9849     CVPreserveState(&cv->b);
9850     if ( spl!=NULL ) {
9851 	SplinePointList *p, *pp, *t;
9852 	p = pp = NULL;
9853 	for ( t=cv->b.layerheads[cv->b.drawmode]->splines; t!=NULL && t!=spl; t=t->next ) {
9854 	    pp = p; p = t;
9855 	}
9856 	switch ( mi->mid ) {
9857 	  case MID_First:
9858 	    if ( p!=NULL ) {
9859 		p->next = spl->next;
9860 		spl->next = cv->b.layerheads[cv->b.drawmode]->splines;
9861 		cv->b.layerheads[cv->b.drawmode]->splines = spl;
9862 	    }
9863 	  break;
9864 	  case MID_Earlier:
9865 	    if ( p!=NULL ) {
9866 		p->next = spl->next;
9867 		spl->next = p;
9868 		if ( pp==NULL ) {
9869 		    cv->b.layerheads[cv->b.drawmode]->splines = spl;
9870 		} else {
9871 		    pp->next = spl;
9872 		}
9873 	    }
9874 	  break;
9875 	  case MID_Last:
9876 	    if ( spl->next!=NULL ) {
9877 		for ( t=cv->b.layerheads[cv->b.drawmode]->splines; t->next!=NULL; t=t->next );
9878 		t->next = spl;
9879 		if ( p==NULL )
9880 		    cv->b.layerheads[cv->b.drawmode]->splines = spl->next;
9881 		else
9882 		    p->next = spl->next;
9883 		spl->next = NULL;
9884 	    }
9885 	  break;
9886 	  case MID_Later:
9887 	    if ( spl->next!=NULL ) {
9888 		t = spl->next;
9889 		spl->next = t->next;
9890 		t->next = spl;
9891 		if ( p==NULL )
9892 		    cv->b.layerheads[cv->b.drawmode]->splines = t;
9893 		else
9894 		    p->next = t;
9895 	    }
9896 	  break;
9897 	}
9898     } else if ( r!=NULL ) {
9899 	RefChar *p, *pp, *t;
9900 	p = pp = NULL;
9901 	for ( t=cv->b.layerheads[cv->b.drawmode]->refs; t!=NULL && t!=r; t=t->next ) {
9902 	    pp = p; p = t;
9903 	}
9904 	switch ( mi->mid ) {
9905 	  case MID_First:
9906 	    if ( p!=NULL ) {
9907 		p->next = r->next;
9908 		r->next = cv->b.layerheads[cv->b.drawmode]->refs;
9909 		cv->b.layerheads[cv->b.drawmode]->refs = r;
9910 	    }
9911 	  break;
9912 	  case MID_Earlier:
9913 	    if ( p!=NULL ) {
9914 		p->next = r->next;
9915 		r->next = p;
9916 		if ( pp==NULL ) {
9917 		    cv->b.layerheads[cv->b.drawmode]->refs = r;
9918 		} else {
9919 		    pp->next = r;
9920 		}
9921 	    }
9922 	  break;
9923 	  case MID_Last:
9924 	    if ( r->next!=NULL ) {
9925 		for ( t=cv->b.layerheads[cv->b.drawmode]->refs; t->next!=NULL; t=t->next );
9926 		t->next = r;
9927 		if ( p==NULL )
9928 		    cv->b.layerheads[cv->b.drawmode]->refs = r->next;
9929 		else
9930 		    p->next = r->next;
9931 		r->next = NULL;
9932 	    }
9933 	  break;
9934 	  case MID_Later:
9935 	    if ( r->next!=NULL ) {
9936 		t = r->next;
9937 		r->next = t->next;
9938 		t->next = r;
9939 		if ( p==NULL )
9940 		    cv->b.layerheads[cv->b.drawmode]->refs = t;
9941 		else
9942 		    p->next = t;
9943 	    }
9944 	  break;
9945 	}
9946     } else if ( im!=NULL ) {
9947 	ImageList *p, *pp, *t;
9948 	p = pp = NULL;
9949 	for ( t=cv->b.layerheads[cv->b.drawmode]->images; t!=NULL && t!=im; t=t->next ) {
9950 	    pp = p; p = t;
9951 	}
9952 	switch ( mi->mid ) {
9953 	  case MID_First:
9954 	    if ( p!=NULL ) {
9955 		p->next = im->next;
9956 		im->next = cv->b.layerheads[cv->b.drawmode]->images;
9957 		cv->b.layerheads[cv->b.drawmode]->images = im;
9958 	    }
9959 	  break;
9960 	  case MID_Earlier:
9961 	    if ( p!=NULL ) {
9962 		p->next = im->next;
9963 		im->next = p;
9964 		if ( pp==NULL ) {
9965 		    cv->b.layerheads[cv->b.drawmode]->images = im;
9966 		} else {
9967 		    pp->next = im;
9968 		}
9969 	    }
9970 	  break;
9971 	  case MID_Last:
9972 	    if ( im->next!=NULL ) {
9973 		for ( t=cv->b.layerheads[cv->b.drawmode]->images; t->next!=NULL; t=t->next );
9974 		t->next = im;
9975 		if ( p==NULL )
9976 		    cv->b.layerheads[cv->b.drawmode]->images = im->next;
9977 		else
9978 		    p->next = im->next;
9979 		im->next = NULL;
9980 	    }
9981 	  break;
9982 	  case MID_Later:
9983 	    if ( im->next!=NULL ) {
9984 		t = im->next;
9985 		im->next = t->next;
9986 		t->next = im;
9987 		if ( p==NULL )
9988 		    cv->b.layerheads[cv->b.drawmode]->images = t;
9989 		else
9990 		    p->next = t;
9991 	    }
9992 	  break;
9993 	}
9994     }
9995     CVCharChangedUpdate(&cv->b);
9996 }
9997 
_CVMenuAddExtrema(CharView * cv)9998 static void _CVMenuAddExtrema(CharView *cv) {
9999     int anysel;
10000     SplineFont *sf = cv->b.sc->parent;
10001 
10002     (void) CVAnySel(cv,&anysel,NULL,NULL,NULL);
10003     CVPreserveState(&cv->b);
10004     SplineCharAddExtrema(cv->b.sc,cv->b.layerheads[cv->b.drawmode]->splines,
10005 	    anysel?ae_between_selected:ae_only_good,sf->ascent+sf->descent);
10006     CVCharChangedUpdate(&cv->b);
10007 }
10008 
CVMenuAddExtrema(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10009 static void CVMenuAddExtrema(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10010     CharView *cv = (CharView *) GDrawGetUserData(gw);
10011     _CVMenuAddExtrema(cv);
10012 }
10013 
CVSimplify(CharView * cv,int type)10014 static void CVSimplify(CharView *cv,int type) {
10015     static struct simplifyinfo smpls[] = {
10016 	    { sf_normal, 0, 0, 0, 0, 0, 0 },
10017 	    { sf_normal,.75,.05,0,-1, 0, 0 },
10018 	    { sf_normal,.75,.05,0,-1, 0, 0 }};
10019     struct simplifyinfo *smpl = &smpls[type+1];
10020 
10021     if ( smpl->linelenmax==-1 || (type==0 && !smpl->set_as_default)) {
10022 	smpl->err = (cv->b.sc->parent->ascent+cv->b.sc->parent->descent)/1000.;
10023 	smpl->linelenmax = (cv->b.sc->parent->ascent+cv->b.sc->parent->descent)/100.;
10024     }
10025 
10026     if ( type==1 ) {
10027 	if ( !SimplifyDlg(cv->b.sc->parent,smpl))
10028 return;
10029 	if ( smpl->set_as_default )
10030 	    smpls[1] = *smpl;
10031     }
10032 
10033     CVPreserveState(&cv->b);
10034     smpl->check_selected_contours = true;
10035     cv->b.layerheads[cv->b.drawmode]->splines = SplineCharSimplify(cv->b.sc,cv->b.layerheads[cv->b.drawmode]->splines,
10036 	    smpl);
10037     CVCharChangedUpdate(&cv->b);
10038 }
10039 
CVMenuSimplify(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10040 static void CVMenuSimplify(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10041     CharView *cv = (CharView *) GDrawGetUserData(gw);
10042     CVSimplify(cv,0);
10043 }
10044 
CVMenuSimplifyMore(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10045 static void CVMenuSimplifyMore(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10046     CharView *cv = (CharView *) GDrawGetUserData(gw);
10047     CVSimplify(cv,1);
10048 }
10049 
CVMenuCleanupGlyph(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10050 static void CVMenuCleanupGlyph(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10051     CharView *cv = (CharView *) GDrawGetUserData(gw);
10052     CVSimplify(cv,-1);
10053 }
10054 
SPLSelected(SplineSet * ss)10055 static int SPLSelected(SplineSet *ss) {
10056     SplinePoint *sp;
10057 
10058     for ( sp=ss->first ;; ) {
10059 	if ( sp->selected )
10060 return( true );
10061 	if ( sp->next==NULL )
10062 return( false );
10063 	sp = sp->next->to;
10064 	if ( sp==ss->first )
10065 return( false );
10066     }
10067 }
10068 
CVCanonicalStart(CharView * cv)10069 static void CVCanonicalStart(CharView *cv) {
10070     SplineSet *ss;
10071     int changed = 0;
10072 
10073     for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL; ss=ss->next )
10074 	if ( ss->first==ss->last && SPLSelected(ss)) {
10075 	    SPLStartToLeftmost(cv->b.sc,ss,&changed);
10076 	    /* The above clears the spiros if needed */
10077 	}
10078 }
10079 
CVMenuCanonicalStart(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10080 static void CVMenuCanonicalStart(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10081     CharView *cv = (CharView *) GDrawGetUserData(gw);
10082     CVCanonicalStart(cv);
10083 }
10084 
CVCanonicalContour(CharView * cv)10085 static void CVCanonicalContour(CharView *cv) {
10086     CanonicalContours(cv->b.sc,CVLayer((CharViewBase *) cv));
10087 }
10088 
CVMenuCanonicalContours(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10089 static void CVMenuCanonicalContours(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10090     CharView *cv = (CharView *) GDrawGetUserData(gw);
10091     CVCanonicalContour(cv);
10092 }
10093 
_CVMenuMakeFirst(CharView * cv)10094 static void _CVMenuMakeFirst(CharView *cv) {
10095     SplinePoint *selpt = NULL;
10096     int anypoints = 0, splinepoints;
10097     SplinePointList *spl, *sel;
10098     Spline *spline, *first;
10099 
10100     sel = NULL;
10101     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10102 	first = NULL;
10103 	splinepoints = 0;
10104 	if ( spl->first->selected ) { splinepoints = 1; sel = spl; selpt=spl->first; }
10105 	for ( spline=spl->first->next; spline!=NULL && spline!=first && !splinepoints; spline = spline->to->next ) {
10106 	    if ( spline->to->selected ) { ++splinepoints; sel = spl; selpt=spline->to; }
10107 	    if ( first == NULL ) first = spline;
10108 	}
10109 	anypoints += splinepoints;
10110     }
10111 
10112     if ( anypoints!=1 || sel->first->prev==NULL || sel->first==selpt )
10113 return;
10114 
10115     CVPreserveState(&cv->b);
10116     sel->first = sel->last = selpt;
10117     CVCharChangedUpdate(&cv->b);
10118 }
10119 
CVMenuMakeFirst(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10120 static void CVMenuMakeFirst(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10121     CharView *cv = (CharView *) GDrawGetUserData(gw);
10122     _CVMenuMakeFirst(cv);
10123 }
10124 
_CVMenuSpiroMakeFirst(CharView * cv)10125 static void _CVMenuSpiroMakeFirst(CharView *cv) {
10126     int anypoints = 0, which;
10127     SplinePointList *spl, *sel;
10128     int i;
10129     spiro_cp *newspiros;
10130 
10131     sel = NULL;
10132     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10133 	for ( i=0; i<spl->spiro_cnt-1; ++i ) {
10134 	    if ( SPIRO_SELECTED(&spl->spiros[i])) {
10135 		if ( SPIRO_SPL_OPEN(spl))
10136 return;
10137 		++anypoints;
10138 		sel = spl;
10139 		which = i;
10140 	    }
10141 	}
10142     }
10143 
10144     if ( anypoints!=1 || sel==NULL )
10145 return;
10146 
10147     CVPreserveState(&cv->b);
10148     newspiros = malloc((sel->spiro_max+1)*sizeof(spiro_cp));
10149     memcpy(newspiros,sel->spiros+which,(sel->spiro_cnt-1-which)*sizeof(spiro_cp));
10150     memcpy(newspiros+(sel->spiro_cnt-1-which),sel->spiros,which*sizeof(spiro_cp));
10151     memcpy(newspiros+sel->spiro_cnt-1,sel->spiros+sel->spiro_cnt-1,sizeof(spiro_cp));
10152     free(sel->spiros);
10153     sel->spiros = newspiros;
10154     SSRegenerateFromSpiros(sel);
10155     CVCharChangedUpdate(&cv->b);
10156 }
10157 
CVMenuSpiroMakeFirst(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10158 static void CVMenuSpiroMakeFirst(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10159     CharView *cv = (CharView *) GDrawGetUserData(gw);
10160     _CVMenuSpiroMakeFirst(cv);
10161 }
10162 
CVMenuMakeLine(GWindow gw,struct gmenuitem * mi,GEvent * e)10163 static void CVMenuMakeLine(GWindow gw, struct gmenuitem *mi, GEvent *e) {
10164     CharView *cv = (CharView *) GDrawGetUserData(gw);
10165     _CVMenuMakeLine((CharViewBase *) cv,mi->mid==MID_MakeArc, e!=NULL && (e->u.mouse.state&ksm_meta));
10166 }
10167 
_CVMenuNamePoint(CharView * cv,SplinePoint * sp)10168 void _CVMenuNamePoint(CharView *cv, SplinePoint *sp) {
10169     char *ret, *name, *oldname;
10170 
10171     oldname = (sp->name && *sp->name) ? sp->name : NULL;
10172     ret = gwwv_ask_string(_("Name this point"), oldname,
10173                   _("Please name this point"));
10174     if ( ret!=NULL ) {
10175         name = *ret ? ret : NULL;
10176         if (name != oldname || (name && oldname && strcmp(name,oldname))) {
10177             sp->name = name;
10178             CVCharChangedUpdate(&cv->b);
10179         }
10180         if (name != ret) { free(ret); ret = NULL; }
10181         if (name != oldname) { free(oldname); oldname = NULL; }
10182     }
10183 }
10184 
CVMenuNamePoint(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10185 static void CVMenuNamePoint(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10186     CharView *cv = (CharView *) GDrawGetUserData(gw);
10187     SplinePointList *spl;
10188     SplinePoint *sp;
10189     RefChar *r;
10190     ImageList *il;
10191     spiro_cp *junk;
10192 
10193     if ( CVOneThingSel( cv, &sp, &spl, &r, &il, NULL, &junk ) && sp) {
10194 	_CVMenuNamePoint(cv, sp);
10195     }
10196 }
10197 
_CVMenuNameContour(CharView * cv)10198 void _CVMenuNameContour(CharView *cv) {
10199     SplinePointList *spl, *onlysel = NULL;
10200     SplinePoint *sp;
10201     char *ret;
10202     int i;
10203 
10204     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10205 	if ( !cv->b.sc->inspiro || !hasspiro()) {
10206 	    for ( sp=spl->first; ; ) {
10207 		if ( sp->selected ) {
10208 		    if ( onlysel==NULL )
10209 			onlysel = spl;
10210 		    else if ( onlysel!=spl )
10211 return;
10212 		}
10213 		if ( sp->next==NULL )
10214 	    break;
10215 		sp = sp->next->to;
10216 		if ( sp==spl->first )
10217 	    break;
10218 	    }
10219 	} else {
10220 	    for ( i=0; i<spl->spiro_cnt; ++i ) {
10221 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
10222 		    if ( onlysel==NULL )
10223 			onlysel = spl;
10224 		    else if ( onlysel!=spl )
10225 return;
10226 		}
10227 	    }
10228 	}
10229     }
10230 
10231     if ( onlysel!=NULL ) {
10232 	ret = gwwv_ask_string(_("Name this contour"),onlysel->contour_name,
10233 		_("Please name this contour"));
10234 	if ( ret!=NULL ) {
10235 	    free(onlysel->contour_name);
10236 	    if ( *ret!='\0' )
10237 		onlysel->contour_name = ret;
10238 	    else {
10239 		onlysel->contour_name = NULL;
10240 		free(ret);
10241 	    }
10242 	    CVCharChangedUpdate(&cv->b);
10243 	}
10244     }
10245 }
10246 
CVMenuNameContour(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10247 static void CVMenuNameContour(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10248     CharView *cv = (CharView *) GDrawGetUserData(gw);
10249     _CVMenuNameContour(cv);
10250 }
10251 
10252 struct insertonsplineat {
10253     int done;
10254     GWindow gw;
10255     Spline *s;
10256     CharView *cv;
10257 };
10258 
10259 #define CID_X	1001
10260 #define CID_Y	1002
10261 #define CID_XR	1003
10262 #define CID_YR	1004
10263 
IOSA_OK(GGadget * g,GEvent * e)10264 static int IOSA_OK(GGadget *g, GEvent *e) {
10265 
10266     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
10267 	int err = false;
10268 	struct insertonsplineat *iosa = GDrawGetUserData(GGadgetGetWindow(g));
10269 	double val;
10270 	extended ts[3];
10271 	int which;
10272 	SplinePoint *sp;
10273 
10274 	if ( GGadgetIsChecked(GWidgetGetControl(iosa->gw,CID_XR)) ) {
10275 	    val = GetReal8(iosa->gw,CID_X,"X",&err);
10276 	    which = 0;
10277 	} else {
10278 	    val = GetReal8(iosa->gw,CID_Y,"Y",&err);
10279 	    which = 1;
10280 	}
10281 	if ( err )
10282 return(true);
10283 	if ( CubicSolve(&iosa->s->splines[which],val,ts)==0 ) {
10284 	    ff_post_error(_("Out of Range"),_("The spline does not reach %g"), (double) val );
10285 return( true );
10286 	}
10287 	iosa->done = true;
10288 	CVPreserveState(&iosa->cv->b);
10289 	for (;;) {
10290 	    sp = SplineBisect(iosa->s,ts[0]);
10291 	    SplinePointCategorize(sp);
10292 	    if ( which==0 ) {
10293 		double off = val-sp->me.x;
10294 		sp->me.x = val; sp->nextcp.x += off; sp->prevcp.x += off;
10295 	    } else {
10296 		double off = val-sp->me.y;
10297 		sp->me.y = val; sp->nextcp.y += off; sp->prevcp.y += off;
10298 	    }
10299 	    SplineRefigure(sp->prev); SplineRefigure(sp->next);
10300 	    if ( ts[1]==-1 ) {
10301 		CVCharChangedUpdate(&iosa->cv->b);
10302 return( true );
10303 	    }
10304 	    iosa->s = sp->next;
10305 	    if ( CubicSolve(&iosa->s->splines[which],val,ts)==0 ) {
10306 		/* Odd. We found one earlier */
10307 		CVCharChangedUpdate(&iosa->cv->b);
10308 return( true );
10309 	    }
10310 	}
10311     }
10312 return( true );
10313 }
10314 
IOSA_Cancel(GGadget * g,GEvent * e)10315 static int IOSA_Cancel(GGadget *g, GEvent *e) {
10316     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
10317 	struct insertonsplineat *iosa = GDrawGetUserData(GGadgetGetWindow(g));
10318 	iosa->done = true;
10319     }
10320 return( true );
10321 }
10322 
IOSA_FocusChange(GGadget * g,GEvent * e)10323 static int IOSA_FocusChange(GGadget *g, GEvent *e) {
10324     if ( e->type==et_controlevent && e->u.control.subtype == et_textfocuschanged ) {
10325 	struct insertonsplineat *iosa = GDrawGetUserData(GGadgetGetWindow(g));
10326 	int cid = (intpt) GGadgetGetUserData(g);
10327 	GGadgetSetChecked(GWidgetGetControl(iosa->gw,cid),true);
10328     }
10329 return( true );
10330 }
10331 
IOSA_RadioChange(GGadget * g,GEvent * e)10332 static int IOSA_RadioChange(GGadget *g, GEvent *e) {
10333     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
10334 	struct insertonsplineat *iosa = GDrawGetUserData(GGadgetGetWindow(g));
10335 	int cid = (intpt) GGadgetGetUserData(g);
10336 	GWidgetIndicateFocusGadget(GWidgetGetControl(iosa->gw,cid));
10337 	GTextFieldSelect(GWidgetGetControl(iosa->gw,cid),0,-1);
10338     }
10339 return( true );
10340 }
10341 
iosa_e_h(GWindow gw,GEvent * event)10342 static int iosa_e_h(GWindow gw, GEvent *event) {
10343     if ( event->type==et_close ) {
10344 	struct insertonsplineat *iosa = GDrawGetUserData(gw);
10345 	iosa->done = true;
10346     } else if ( event->type == et_char ) {
10347 return( false );
10348     } else if ( event->type == et_map ) {
10349 	/* Above palettes */
10350 	GDrawRaise(gw);
10351     }
10352 return( true );
10353 }
10354 
_CVMenuInsertPt(CharView * cv)10355 void _CVMenuInsertPt(CharView *cv) {
10356     SplineSet *spl;
10357     Spline *s, *found=NULL, *first;
10358     struct insertonsplineat iosa;
10359     GRect pos;
10360     GWindowAttrs wattrs;
10361     GGadgetCreateData gcd[11], boxes[2], topbox[2], *hvs[13], *varray[8], *buttons[6];
10362     GTextInfo label[11];
10363 
10364     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10365 	first = NULL;
10366 	for ( s=spl->first->next; s!=NULL && s!=first ; s = s->to->next ) {
10367 	    if ( first==NULL ) first=s;
10368 	    if ( s->from->selected && s->to->selected ) {
10369 		if ( found!=NULL )
10370 return;		/* Can only work with one spline */
10371 		found = s;
10372 	    }
10373 	}
10374     }
10375     if ( found==NULL )
10376 return;		/* Need a spline */
10377 
10378     memset(&iosa,0,sizeof(iosa));
10379     iosa.s = found;
10380     iosa.cv = cv;
10381     memset(&wattrs,0,sizeof(wattrs));
10382     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
10383     wattrs.event_masks = ~(1<<et_charup);
10384     wattrs.restrict_input_to_me = 1;
10385     wattrs.undercursor = 1;
10386     wattrs.cursor = ct_pointer;
10387     wattrs.utf8_window_title = _("Insert a point on the given spline at either...");
10388     wattrs.is_dlg = true;
10389     pos.x = pos.y = 0;
10390     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,210));
10391     pos.height = GDrawPointsToPixels(NULL,120);
10392     iosa.gw = GDrawCreateTopWindow(NULL,&pos,iosa_e_h,&iosa,&wattrs);
10393 
10394     memset(&label,0,sizeof(label));
10395     memset(&gcd,0,sizeof(gcd));
10396 
10397     label[0].text = (unichar_t *) _("_X:");
10398     label[0].text_is_1byte = true;
10399     label[0].text_in_resource = true;
10400     gcd[0].gd.label = &label[0];
10401     gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 5;
10402     gcd[0].gd.flags = gg_enabled|gg_visible|gg_cb_on;
10403     gcd[0].gd.cid = CID_XR;
10404     gcd[0].gd.handle_controlevent = IOSA_RadioChange;
10405     gcd[0].data = (void *) CID_X;
10406     gcd[0].creator = GRadioCreate;
10407 
10408     label[1].text = (unichar_t *) _("_Y:");
10409     label[1].text_is_1byte = true;
10410     label[1].text_in_resource = true;
10411     gcd[1].gd.label = &label[1];
10412     gcd[1].gd.pos.x = 5; gcd[1].gd.pos.y = 32;
10413     gcd[1].gd.flags = gg_enabled|gg_visible|gg_rad_continueold ;
10414     gcd[1].gd.cid = CID_YR;
10415     gcd[1].gd.handle_controlevent = IOSA_RadioChange;
10416     gcd[1].data = (void *) CID_Y;
10417     gcd[1].creator = GRadioCreate;
10418 
10419     gcd[2].gd.pos.x = 131; gcd[2].gd.pos.y = 5;  gcd[2].gd.pos.width = 60;
10420     gcd[2].gd.flags = gg_enabled|gg_visible;
10421     gcd[2].gd.cid = CID_X;
10422     gcd[2].gd.handle_controlevent = IOSA_FocusChange;
10423     gcd[2].data = (void *) CID_XR;
10424     gcd[2].creator = GTextFieldCreate;
10425 
10426     gcd[3].gd.pos.x = 131; gcd[3].gd.pos.y = 32;  gcd[3].gd.pos.width = 60;
10427     gcd[3].gd.flags = gg_enabled|gg_visible;
10428     gcd[3].gd.cid = CID_Y;
10429     gcd[3].gd.handle_controlevent = IOSA_FocusChange;
10430     gcd[3].data = (void *) CID_YR;
10431     gcd[3].creator = GTextFieldCreate;
10432 
10433     gcd[4].gd.pos.x = 20-3; gcd[4].gd.pos.y = 120-32-3;
10434     gcd[4].gd.pos.width = -1; gcd[4].gd.pos.height = 0;
10435     gcd[4].gd.flags = gg_visible | gg_enabled | gg_but_default;
10436     label[4].text = (unichar_t *) _("_OK");
10437     label[4].text_is_1byte = true;
10438     label[4].text_in_resource = true;
10439     gcd[4].gd.mnemonic = 'O';
10440     gcd[4].gd.label = &label[4];
10441     gcd[4].gd.handle_controlevent = IOSA_OK;
10442     gcd[4].creator = GButtonCreate;
10443 
10444     gcd[5].gd.pos.x = -20; gcd[5].gd.pos.y = 120-32;
10445     gcd[5].gd.pos.width = -1; gcd[5].gd.pos.height = 0;
10446     gcd[5].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
10447     label[5].text = (unichar_t *) _("_Cancel");
10448     label[5].text_is_1byte = true;
10449     label[5].text_in_resource = true;
10450     gcd[5].gd.label = &label[5];
10451     gcd[5].gd.mnemonic = 'C';
10452     gcd[5].gd.handle_controlevent = IOSA_Cancel;
10453     gcd[5].creator = GButtonCreate;
10454 
10455     hvs[0] = &gcd[0]; hvs[1] = &gcd[2]; hvs[2] = NULL;
10456     hvs[3] = &gcd[1]; hvs[4] = &gcd[3]; hvs[5] = NULL;
10457     hvs[6] = NULL;
10458 
10459     buttons[0] = buttons[2] = buttons[4] = GCD_Glue; buttons[5] = NULL;
10460     buttons[1] = &gcd[4]; buttons[3] = &gcd[5];
10461 
10462     varray[0] = &boxes[1]; varray[1] = NULL;
10463     varray[2] = GCD_Glue; varray[3] = NULL;
10464     varray[4] = &boxes[0]; varray[5] = NULL;
10465     varray[6] = NULL;
10466 
10467     memset(boxes,0,sizeof(boxes));
10468     boxes[0].gd.flags = gg_enabled|gg_visible;
10469     boxes[0].gd.u.boxelements = buttons;
10470     boxes[0].creator = GHBoxCreate;
10471 
10472     boxes[1].gd.flags = gg_enabled|gg_visible;
10473     boxes[1].gd.u.boxelements = hvs;
10474     boxes[1].creator = GHVBoxCreate;
10475 
10476     memset(topbox,0,sizeof(topbox));
10477     topbox[0].gd.pos.x = topbox[0].gd.pos.y = 2;
10478     topbox[0].gd.pos.width = pos.width-4; topbox[0].gd.pos.height = pos.height-4;
10479     topbox[0].gd.flags = gg_enabled|gg_visible;
10480     topbox[0].gd.u.boxelements = varray;
10481     topbox[0].creator = GHVGroupCreate;
10482 
10483 
10484     GGadgetsCreate(iosa.gw,topbox);
10485     GHVBoxSetExpandableRow(topbox[0].ret,1);
10486     GHVBoxSetExpandableCol(boxes[0].ret,gb_expandgluesame);
10487     GHVBoxSetExpandableCol(boxes[1].ret,1);
10488     GWidgetIndicateFocusGadget(GWidgetGetControl(iosa.gw,CID_X));
10489     GTextFieldSelect(GWidgetGetControl(iosa.gw,CID_X),0,-1);
10490     GHVBoxFitWindow(topbox[0].ret);
10491 
10492     GDrawSetVisible(iosa.gw,true);
10493     while ( !iosa.done )
10494 	GDrawProcessOneEvent(NULL);
10495     GDrawDestroyWindow(iosa.gw);
10496 }
10497 
CVMenuInsertPt(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10498 static void CVMenuInsertPt(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10499     CharView *cv = (CharView *) GDrawGetUserData(gw);
10500     _CVMenuInsertPt(cv);
10501 }
10502 
_CVCenterCP(CharView * cv)10503 static void _CVCenterCP(CharView *cv) {
10504     SplinePointList *spl;
10505     SplinePoint *sp;
10506     int changed = false;
10507     enum movething { mt_pt, mt_ncp, mt_pcp } movething = mt_pt;
10508 
10509     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10510 	for ( sp=spl->first; ; ) {
10511 	    if ( sp->selected && sp->prev!=NULL && sp->next!=NULL &&
10512 		    !sp->noprevcp && !sp->nonextcp ) {
10513 		if ( sp->me.x != (sp->nextcp.x+sp->prevcp.x)/2 ||
10514 			sp->me.y != (sp->nextcp.y+sp->prevcp.y)/2 ) {
10515 		    if ( !changed ) {
10516 			CVPreserveState(&cv->b);
10517 			changed = true;
10518 		    }
10519 		    switch ( movething ) {
10520 		      case mt_pt:
10521 			sp->me.x = (sp->nextcp.x+sp->prevcp.x)/2;
10522 			sp->me.y = (sp->nextcp.y+sp->prevcp.y)/2;
10523 			SplineRefigure(sp->prev);
10524 			SplineRefigure(sp->next);
10525 		      break;
10526 		      case mt_ncp:
10527 			sp->nextcp.x = sp->me.x - (sp->prevcp.x-sp->me.x);
10528 			sp->nextcp.y = sp->me.y - (sp->prevcp.y-sp->me.y);
10529 			if ( sp->next->order2 ) {
10530 			    sp->next->to->prevcp = sp->nextcp;
10531 			    sp->next->to->noprevcp = false;
10532 			}
10533 			SplineRefigure(sp->prev);
10534 			SplineRefigureFixup(sp->next);
10535 		      break;
10536 		      case mt_pcp:
10537 			sp->prevcp.x = sp->me.x - (sp->nextcp.x-sp->me.x);
10538 			sp->prevcp.y = sp->me.y - (sp->nextcp.y-sp->me.y);
10539 			if ( sp->prev->order2 ) {
10540 			    sp->prev->from->nextcp = sp->prevcp;
10541 			    sp->prev->from->nonextcp = false;
10542 			}
10543 			SplineRefigureFixup(sp->prev);
10544 			SplineRefigure(sp->next);
10545 		      break;
10546 		    }
10547 		}
10548 	    }
10549 	    if ( sp->next==NULL )
10550 	break;
10551 	    sp = sp->next->to;
10552 	    if ( sp==spl->first )
10553 	break;
10554 	}
10555     }
10556 
10557     if ( changed )
10558 	CVCharChangedUpdate(&cv->b);
10559 }
10560 
CVMenuCenterCP(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10561 static void CVMenuCenterCP(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10562     CharView *cv = (CharView *) GDrawGetUserData(gw);
10563     _CVCenterCP(cv);
10564 }
10565 
CVMakeClipPath(CharView * cv)10566 void CVMakeClipPath(CharView *cv) {
10567     SplineSet *ss;
10568     SplinePoint *sp;
10569     int sel;
10570     int changed=false;
10571 
10572     for ( ss=cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL; ss=ss->next ) {
10573 	sel = false;
10574 	for ( sp=ss->first; ; ) {
10575 	    if ( sp->selected ) {
10576 		sel = true;
10577 	break;
10578 	    }
10579 	    if ( sp->next==NULL )
10580 	break;
10581 	    sp = sp->next->to;
10582 	    if ( sp==ss->first )
10583 	break;
10584 	}
10585 	if ( sel!=ss->is_clip_path ) {
10586 	    if ( !changed )
10587 		CVPreserveState((CharViewBase *) cv);
10588 	    changed = true;
10589 	    ss->is_clip_path = sel;
10590 	}
10591     }
10592     if ( changed )
10593 	CVCharChangedUpdate((CharViewBase *) cv);
10594 }
10595 
CVMenuClipPath(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10596 static void CVMenuClipPath(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10597     CharView *cv = (CharView *) GDrawGetUserData(gw);
10598     CVMakeClipPath(cv);
10599 }
10600 
CVAddAnchor(CharView * cv)10601 void CVAddAnchor(CharView *cv) {
10602     int waslig;
10603 
10604     if ( AnchorClassUnused(cv->b.sc,&waslig)==NULL ) {
10605         SplineFont *sf = cv->b.sc->parent;
10606         AnchorClass *ac;
10607         GTextInfo **ti;
10608         int j;
10609         char *name = gwwv_ask_string(_("Anchor Class Name"),"",_("Please enter the name of a Anchor point class to create"));
10610         if ( name==NULL )
10611 return;
10612         ac = SFFindOrAddAnchorClass(sf,name,NULL);
10613         free(name);
10614 	if ( AnchorClassUnused(cv->b.sc,&waslig)==NULL )
10615 return;
10616     }
10617     ApGetInfo(cv,NULL);
10618 }
10619 
CVMenuAddAnchor(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10620 static void CVMenuAddAnchor(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10621     CharView *cv = (CharView *) GDrawGetUserData(gw);
10622     CVAddAnchor(cv);
10623 }
10624 
CVMenuAutotrace(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * e)10625 static void CVMenuAutotrace(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *e) {
10626     CharView *cv = (CharView *) GDrawGetUserData(gw);
10627     GCursor ct;
10628 
10629     ct = GDrawGetCursor(cv->v);
10630     GDrawSetCursor(cv->v,ct_watch);
10631     ff_progress_allow_events();
10632     SCAutoTrace(cv->b.sc,CVLayer((CharViewBase *) cv),e!=NULL && (e->u.mouse.state&ksm_shift));
10633     GDrawSetCursor(cv->v,ct);
10634 }
10635 
CVMenuBuildAccent(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10636 static void CVMenuBuildAccent(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10637     CharView *cv = (CharView *) GDrawGetUserData(gw);
10638     extern int onlycopydisplayed;
10639     int layer = CVLayer((CharViewBase *) cv);
10640 
10641     if ( SFIsRotatable(cv->b.fv->sf,cv->b.sc))
10642 	/* It's ok */;
10643     else if ( !SFIsSomethingBuildable(cv->b.fv->sf,cv->b.sc,layer,true) )
10644 return;
10645     SCBuildComposit(cv->b.fv->sf,cv->b.sc,layer,NULL,onlycopydisplayed,true);
10646 }
10647 
CVMenuBuildComposite(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10648 static void CVMenuBuildComposite(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10649     CharView *cv = (CharView *) GDrawGetUserData(gw);
10650     extern int onlycopydisplayed;
10651     int layer = CVLayer((CharViewBase *) cv);
10652 
10653     if ( SFIsRotatable(cv->b.fv->sf,cv->b.sc))
10654 	/* It's ok */;
10655     else if ( !SFIsCompositBuildable(cv->b.fv->sf,cv->b.sc->unicodeenc,cv->b.sc,layer) )
10656 return;
10657     SCBuildComposit(cv->b.fv->sf,cv->b.sc,layer,NULL,onlycopydisplayed,false);
10658 }
10659 
CVMenuReverseDir(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10660 static void CVMenuReverseDir(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10661     CharView *cv = (CharView *) GDrawGetUserData(gw);
10662     int changed=false;
10663     SplineSet *ss;
10664 
10665     for ( ss = cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL; ss=ss->next )
10666 	if ( PointListIsSelected(ss)) {
10667 	    if ( !changed ) {
10668 		CVPreserveState(&cv->b);
10669 	        cv->lastselpt = NULL; cv->lastselcp = NULL;
10670 		changed = true;
10671 	    }
10672 	    SplineSetReverse(ss);
10673 	}
10674 
10675     if ( changed )
10676 	CVCharChangedUpdate(&cv->b);
10677 }
10678 
CVMenuCorrectDir(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10679 static void CVMenuCorrectDir(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10680     CharView *cv = (CharView *) GDrawGetUserData(gw);
10681     int changed=false, refchanged=false;
10682     RefChar *ref;
10683     int asked=-1;
10684     int layer = CVLayer( (CharViewBase *) cv);
10685 
10686     for ( ref=cv->b.sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
10687 	if ( ref->transform[0]*ref->transform[3]<0 ||
10688 		(ref->transform[0]==0 && ref->transform[1]*ref->transform[2]>0)) {
10689 	    if ( asked==-1 ) {
10690 		char *buts[4];
10691 		buts[0] = _("_Unlink");
10692 		buts[1] = _("_No");
10693 		buts[2] = _("_Cancel");
10694 		buts[3] = NULL;
10695 		asked = gwwv_ask(_("Flipped Reference"),(const char **) buts,0,2,_("%.50s contains a flipped reference. This cannot be corrected as is. Would you like me to unlink it and then correct it?"), cv->b.sc->name );
10696 		if ( asked==2 )
10697 return;
10698 		else if ( asked==1 )
10699     break;
10700 	    }
10701 	    if ( asked==0 ) {
10702 		if ( !refchanged ) {
10703 		    refchanged = true;
10704 		    CVPreserveState(&cv->b);
10705 		}
10706 		SCRefToSplines(cv->b.sc,ref,layer);
10707 	    }
10708 	}
10709     }
10710 
10711     if ( !refchanged )
10712 	CVPreserveState(&cv->b);
10713 
10714     cv->b.layerheads[cv->b.drawmode]->splines = SplineSetsCorrect(cv->b.layerheads[cv->b.drawmode]->splines,&changed);
10715     if ( changed || refchanged )
10716 	CVCharChangedUpdate(&cv->b);
10717 }
10718 
CVMenuInsertText(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10719 static void CVMenuInsertText(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10720     CharView *cv = (CharView *) GDrawGetUserData(gw);
10721     InsertTextDlg(cv);
10722 }
10723 
CVMenuGetInfo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10724 static void CVMenuGetInfo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10725     CharView *cv = (CharView *) GDrawGetUserData(gw);
10726     CVGetInfo(cv);
10727 }
10728 
CVMenuCharInfo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10729 static void CVMenuCharInfo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10730     CharView *cv = (CharView *) GDrawGetUserData(gw);
10731     SCCharInfo(cv->b.sc,CVLayer((CharViewBase *) cv),cv->b.fv->map,CVCurEnc(cv));
10732 }
10733 
CVMenuShowDependentRefs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10734 static void CVMenuShowDependentRefs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10735     CharView *cv = (CharView *) GDrawGetUserData(gw);
10736     SCRefBy(cv->b.sc);
10737 }
10738 
CVMenuShowDependentSubs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10739 static void CVMenuShowDependentSubs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10740     CharView *cv = (CharView *) GDrawGetUserData(gw);
10741     SCSubBy(cv->b.sc);
10742 }
10743 
CVMenuBitmaps(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))10744 static void CVMenuBitmaps(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
10745     CharView *cv = (CharView *) GDrawGetUserData(gw);
10746     BitmapDlg((FontView *) (cv->b.fv),cv->b.sc,mi->mid==MID_RemoveBitmaps?-1: (mi->mid==MID_AvailBitmaps) );
10747 }
10748 
cv_allistcheck(CharView * cv,struct gmenuitem * mi)10749 static void cv_allistcheck(CharView *cv, struct gmenuitem *mi) {
10750     int selpoints = 0;
10751     SplinePointList *spl;
10752     SplinePoint *sp=NULL;
10753 
10754     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10755 	sp=spl->first;
10756 	while ( 1 ) {
10757 	    if ( sp->selected )
10758 		++selpoints;
10759 	    if ( sp->next==NULL )
10760 	break;
10761 	    sp = sp->next->to;
10762 	    if ( sp==spl->first )
10763 	break;
10764 	}
10765     }
10766 
10767     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
10768 	switch ( mi->mid ) {
10769 	  case MID_Average:
10770 	    mi->ti.disabled = selpoints<2;
10771 	  break;
10772 	  case MID_SpacePts:
10773 	    mi->ti.disabled = ((selpoints<3) && (selpoints!=1));
10774 	  break;
10775 	  case MID_SpaceRegion:
10776 	    mi->ti.disabled = selpoints<3;
10777 	  break;
10778 	  case MID_MakeParallel:
10779 	    mi->ti.disabled = selpoints!=4;
10780 	  break;
10781         }
10782     }
10783 }
10784 
allistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))10785 static void allistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
10786     CharView *cv = (CharView *) GDrawGetUserData(gw);
10787     cv_allistcheck(cv, mi);
10788 }
10789 
cv_balistcheck(CharView * cv,struct gmenuitem * mi)10790 static void cv_balistcheck(CharView *cv, struct gmenuitem *mi) {
10791     int layer = CVLayer((CharViewBase *) cv);
10792 
10793     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
10794 	switch ( mi->mid ) {
10795 	  case MID_BuildAccent:
10796 	    mi->ti.disabled = !SFIsSomethingBuildable(cv->b.fv->sf,cv->b.sc,layer,true);
10797 	  break;
10798 	  case MID_BuildComposite:
10799 	    mi->ti.disabled = !SFIsSomethingBuildable(cv->b.fv->sf,cv->b.sc,layer,false);
10800 	  break;
10801         }
10802     }
10803 }
10804 
balistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))10805 static void balistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
10806     CharView *cv = (CharView *) GDrawGetUserData(gw);
10807     cv_balistcheck(cv, mi);
10808 }
10809 
delistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))10810 static void delistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
10811     CharView *cv = (CharView *) GDrawGetUserData(gw);
10812 
10813     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
10814 	switch ( mi->mid ) {
10815 	  case MID_ShowDependentRefs:
10816 	    mi->ti.disabled = cv->b.sc->dependents==NULL;
10817 	  break;
10818 	  case MID_ShowDependentSubs:
10819 	    mi->ti.disabled = !SCUsedBySubs(cv->b.sc);
10820 	  break;
10821 	}
10822     }
10823 }
10824 
rndlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))10825 static void rndlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
10826     CharView *cv = (CharView *) GDrawGetUserData(gw);
10827 
10828     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
10829 	switch ( mi->mid ) {
10830 	  case MID_RoundToCluster:
10831 	    mi->ti.disabled = cv->b.sc->inspiro && hasspiro();
10832 	  break;
10833         }
10834     }
10835 }
10836 
cv_ellistcheck(CharView * cv,struct gmenuitem * mi)10837 static void cv_ellistcheck(CharView *cv, struct gmenuitem *mi) {
10838     int anypoints = 0, splinepoints, dir = -2;
10839     int self_intersects=-2;
10840     SplinePointList *spl;
10841     Spline *spline, *first;
10842     AnchorPoint *ap;
10843     spiro_cp *cp;
10844     int i;
10845 
10846 #ifdef FONTFORGE_CONFIG_TILEPATH
10847     int badsel = false;
10848     RefChar *ref;
10849     ImageList *il;
10850 
10851     for ( ref=cv->b.layerheads[cv->b.drawmode]->refs; ref!=NULL; ref=ref->next )
10852 	if ( ref->selected )
10853 	    badsel = true;
10854 
10855     for ( il=cv->b.layerheads[cv->b.drawmode]->images; il!=NULL; il=il->next )
10856 	if ( il->selected )
10857 	    badsel = true;
10858 #endif
10859 
10860     if ( cv->checkselfintersects ) {
10861 	Spline *s, *s2;
10862 	SplineSet *ss;
10863 	ss = LayerAllSplines(cv->b.layerheads[cv->b.drawmode]);
10864 	self_intersects = SplineSetIntersect(ss,&s,&s2);
10865 	LayerUnAllSplines(cv->b.layerheads[cv->b.drawmode]);
10866     }
10867 
10868     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
10869 	first = NULL;
10870 	splinepoints = 0;
10871 	if ( cv->b.sc->inspiro && hasspiro()) {
10872 	    for ( i=0; i<spl->spiro_cnt-1; ++i ) {
10873 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
10874 		    splinepoints = 1;
10875 	    break;
10876 		}
10877 	    }
10878 	} else {
10879 	    if ( spl->first->selected ) { splinepoints = 1; }
10880 	    for ( spline=spl->first->next; spline!=NULL && spline!=first && !splinepoints; spline = spline->to->next ) {
10881 		if ( spline->to->selected ) { ++splinepoints; }
10882 		if ( first == NULL ) first = spline;
10883 	    }
10884 	}
10885 	if ( splinepoints ) {
10886 	    anypoints += splinepoints;
10887 	    if ( dir==-1 )
10888 		/* Do nothing */;
10889 	    else if ( spl->first!=spl->last || spl->first->next==NULL ) {
10890 		if ( dir==-2 || dir==2 )
10891 		    dir = 2;	/* Not a closed path, no direction */
10892 		else
10893 		    dir = -1;
10894 	    } else if ( dir==-2 )
10895 		dir = SplinePointListIsClockwise(spl);
10896 		if ( dir==-1 )
10897 		    self_intersects = 1;	/* Sometimes the clockwise test finds intersections the main routine can't */
10898 	    else {
10899 		int subdir = SplinePointListIsClockwise(spl);
10900 		if ( subdir==-1 )
10901 		    self_intersects = 1;
10902 		if ( subdir!=dir )
10903 		    dir = -1;
10904 	    }
10905 	}
10906     }
10907 
10908     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
10909 	switch ( mi->mid ) {
10910 	  case MID_FontInfo: case MID_CharInfo: case MID_ShowDependentRefs:
10911 	  case MID_FindProblems:
10912 	  case MID_AvailBitmaps:
10913 	    mi->ti.disabled = cv->b.container!=NULL;
10914 	  break;
10915 	  case MID_GetInfo:
10916 	    {
10917 		SplinePoint *sp; SplineSet *spl; RefChar *ref; ImageList *img;
10918 		mi->ti.disabled = !CVOneThingSel(cv,&sp,&spl,&ref,&img,&ap,&cp);
10919 	    }
10920 	  break;
10921 	  case MID_CheckSelf:
10922 	    mi->ti.checked = cv->checkselfintersects;
10923 	  break;
10924 	  case MID_GlyphSelfIntersects:
10925 	    mi->ti.disabled = !cv->checkselfintersects;
10926 	    mi->ti.checked = self_intersects==1;
10927 	  break;
10928 	  case MID_Clockwise:
10929 	    mi->ti.disabled = !anypoints || dir==2 || dir<0;
10930 	    mi->ti.checked = dir==1;
10931 	  break;
10932 	  case MID_Counter:
10933 	    mi->ti.disabled = !anypoints || dir==2 || dir<0;
10934 	    mi->ti.checked = dir==0;
10935 	  break;
10936 	  case MID_Correct:
10937 	    mi->ti.disabled = (cv->b.layerheads[cv->b.drawmode]->splines==NULL && cv->b.layerheads[cv->b.drawmode]->refs==NULL) ||
10938 		    dir==2 || self_intersects==1;
10939 	  break;
10940 	  case MID_ReverseDir:
10941 	    mi->ti.disabled = !anypoints;
10942 	  break;
10943 	  case MID_Stroke:
10944 	  case MID_RmOverlap:
10945 	  case MID_Styles:
10946 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL ||
10947 				cv->b.container!=NULL;
10948 	  break;
10949 #ifdef FONTFORGE_CONFIG_TILEPATH
10950 	  case MID_TilePath:
10951 	    mi->ti.disabled = badsel;
10952 	  break;
10953 #endif
10954 	  case MID_RegenBitmaps: case MID_RemoveBitmaps:
10955 	    mi->ti.disabled = cv->b.fv->sf->bitmaps==NULL;
10956 	  break;
10957 	  case MID_AddExtrema:
10958 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL || (cv->b.sc->inspiro && hasspiro());
10959 	  /* Like Simplify, always available, but may not do anything if */
10960 	  /*  all extrema have points. I'm not going to check for that, too hard */
10961 	  break;
10962 	  case MID_Simplify:
10963 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL || (cv->b.sc->inspiro && hasspiro());
10964 	  /* Simplify is always available (it may not do anything though) */
10965 	  /*  well, ok. Disable it if there is absolutely nothing to work on */
10966 	  break;
10967 	  case MID_BuildAccent:
10968 	    mi->ti.disabled = !SFIsSomethingBuildable(cv->b.fv->sf,cv->b.sc,
10969 		    CVLayer((CharViewBase *) cv),false);
10970 	  break;
10971 	  case MID_Autotrace:
10972 	    mi->ti.disabled = FindAutoTraceName()==NULL || cv->b.sc->layers[ly_back].images==NULL;
10973 	  break;
10974 	  case MID_Align:
10975 	    mi->ti.disabled = cv->b.sc->inspiro && hasspiro();
10976 	  break;
10977 	}
10978     }
10979 }
10980 
ellistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))10981 static void ellistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
10982     CharView *cv = (CharView *) GDrawGetUserData(gw);
10983     cv_ellistcheck(cv, mi);
10984 }
10985 
CVMenuAutoHint(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))10986 static void CVMenuAutoHint(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
10987     CharView *cv = (CharView *) GDrawGetUserData(gw);
10988     /*int removeOverlap = e==NULL || !(e->u.mouse.state&ksm_shift);*/
10989     int was = cv->b.sc->changedsincelasthinted;
10990 
10991     /* Hint undoes are done in _SplineCharAutoHint */
10992     cv->b.sc->manualhints = false;
10993     SplineCharAutoHint(cv->b.sc,CVLayer((CharViewBase *) cv),NULL);
10994     SCUpdateAll(cv->b.sc);
10995     if ( was ) {
10996 	FontView *fvs;
10997 	for ( fvs=(FontView *) (cv->b.fv); fvs!=NULL; fvs=(FontView *) (fvs->b.nextsame) )
10998 	    GDrawRequestExpose(fvs->v,NULL,false);	/* Clear any changedsincelasthinted marks */
10999     }
11000 }
11001 
CVMenuAutoHintSubs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11002 static void CVMenuAutoHintSubs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11003     CharView *cv = (CharView *) GDrawGetUserData(gw);
11004     SCFigureHintMasks(cv->b.sc,CVLayer((CharViewBase *) cv));
11005     SCUpdateAll(cv->b.sc);
11006 }
11007 
CVMenuAutoCounter(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11008 static void CVMenuAutoCounter(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11009     CharView *cv = (CharView *) GDrawGetUserData(gw);
11010     SCFigureCounterMasks(cv->b.sc);
11011 }
11012 
CVMenuDontAutoHint(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11013 static void CVMenuDontAutoHint(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11014     CharView *cv = (CharView *) GDrawGetUserData(gw);
11015     cv->b.sc->manualhints = !cv->b.sc->manualhints;
11016 }
11017 
CVMenuNowakAutoInstr(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11018 static void CVMenuNowakAutoInstr(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11019     CharView *cv = (CharView *) GDrawGetUserData(gw);
11020     SplineChar *sc = cv->b.sc;
11021     GlobalInstrCt gic;
11022 
11023     if ( cv->b.layerheads[cv->b.drawmode]->splines!=NULL && sc->hstem==NULL && sc->vstem==NULL
11024 	    && sc->dstem==NULL && !no_windowing_ui )
11025 	ff_post_notice(_("Things could be better..."), _("Glyph, %s, has no hints. FontForge will not produce many instructions."),
11026 		sc->name );
11027 
11028     InitGlobalInstrCt(&gic, sc->parent, CVLayer((CharViewBase *) cv), NULL);
11029     NowakowskiSCAutoInstr(&gic, sc);
11030     FreeGlobalInstrCt(&gic);
11031     SCUpdateAll(sc);
11032 }
11033 
CVMenuClearHints(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11034 static void CVMenuClearHints(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11035     CharView *cv = (CharView *) GDrawGetUserData(gw);
11036 
11037     SCPreserveHints(cv->b.sc,CVLayer((CharViewBase *) cv));
11038     SCHintsChanged(cv->b.sc);
11039     if ( mi->mid==MID_ClearHStem ) {
11040 	StemInfosFree(cv->b.sc->hstem);
11041 	cv->b.sc->hstem = NULL;
11042 	cv->b.sc->hconflicts = false;
11043     } else if ( mi->mid==MID_ClearVStem ) {
11044 	StemInfosFree(cv->b.sc->vstem);
11045 	cv->b.sc->vstem = NULL;
11046 	cv->b.sc->vconflicts = false;
11047     } else if ( mi->mid==MID_ClearDStem ) {
11048 	DStemInfosFree(cv->b.sc->dstem);
11049 	cv->b.sc->dstem = NULL;
11050     }
11051     cv->b.sc->manualhints = true;
11052 
11053     if ( mi->mid != MID_ClearDStem ) {
11054         SCClearHintMasks(cv->b.sc,CVLayer((CharViewBase *) cv),true);
11055     }
11056     SCOutOfDateBackground(cv->b.sc);
11057     SCUpdateAll(cv->b.sc);
11058 }
11059 
11060 /* This is an improved version of the older CVTwoForePointsSelected function. */
11061 /* Unlike the former, it doesn't just check if there are exactly two points   */
11062 /* selected, but rather returns the number of selected points (whatever this  */
11063 /* number can be) and puts references to those points into an array. It is up */
11064 /* to the calling code to see if the returned result is satisfiable (there    */
11065 /* should be exactly two points selected for specifying a vertical or         */
11066 /* horizontal stem and four points for a diagonal stem). */
CVNumForePointsSelected(CharView * cv,BasePoint ** bp)11067 static int CVNumForePointsSelected(CharView *cv, BasePoint **bp) {
11068     SplineSet *spl;
11069     SplinePoint *test, *first;
11070     BasePoint *bps[5];
11071     int i, cnt;
11072 
11073     if ( cv->b.drawmode!=dm_fore )
11074 return( 0 ) ;
11075     cnt = 0;
11076     for ( spl = cv->b.sc->layers[ly_fore].splines; spl!=NULL; spl = spl->next ) {
11077 	first = NULL;
11078 	for ( test = spl->first; test!=first; test = test->next->to ) {
11079 	    if ( test->selected ) {
11080 		bps[cnt++] = &(test->me);
11081 		if ( cnt>4 )
11082 return( 0 );
11083 	    }
11084 	    if ( first == NULL ) first = test;
11085 	    if ( test->next==NULL )
11086 	break;
11087 	}
11088     }
11089     for (i=0; i<cnt; i++) {
11090         bp[i] = bps[i];
11091     }
11092 return( cnt );
11093 }
11094 
CVMenuAddHint(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11095 static void CVMenuAddHint(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11096     CharView *cv = (CharView *) GDrawGetUserData(gw);
11097     BasePoint *bp[4], unit;
11098     StemInfo *h=NULL;
11099     DStemInfo *d;
11100     int num;
11101     int layer = CVLayer((CharViewBase *) cv);
11102 
11103     num = CVNumForePointsSelected( cv,bp );
11104 
11105     /* We need exactly 2 points to specify a horizontal or vertical stem */
11106     /* and exactly 4 points to specify a diagonal stem */
11107     if ( !(num == 2 && mi->mid != MID_AddDHint) &&
11108          !(num == 4 && mi->mid == MID_AddDHint))
11109 return;
11110 
11111     SCPreserveHints(cv->b.sc,CVLayer((CharViewBase *) cv));
11112     SCHintsChanged(cv->b.sc);
11113     if ( mi->mid==MID_AddHHint ) {
11114 	if ( bp[0]->y==bp[1]->y )
11115 return;
11116 	h = chunkalloc(sizeof(StemInfo));
11117 	if ( bp[1]->y>bp[0]->y ) {
11118 	    h->start = bp[0]->y;
11119 	    h->width = bp[1]->y-bp[0]->y;
11120 	} else {
11121 	    h->start = bp[1]->y;
11122 	    h->width = bp[0]->y-bp[1]->y;
11123 	}
11124 	SCGuessHHintInstancesAndAdd(cv->b.sc,layer,h,bp[0]->x,bp[1]->x);
11125 	cv->b.sc->hconflicts = StemListAnyConflicts(cv->b.sc->hstem);
11126     } else if ( mi->mid==MID_AddVHint ) {
11127 	if ( bp[0]->x==bp[1]->x )
11128 return;
11129 	h = chunkalloc(sizeof(StemInfo));
11130 	if ( bp[1]->x>bp[0]->x ) {
11131 	    h->start = bp[0]->x;
11132 	    h->width = bp[1]->x-bp[0]->x;
11133 	} else {
11134 	    h->start = bp[1]->x;
11135 	    h->width = bp[0]->x-bp[1]->x;
11136 	}
11137 	SCGuessVHintInstancesAndAdd(cv->b.sc,layer,h,bp[0]->y,bp[1]->y);
11138 	cv->b.sc->vconflicts = StemListAnyConflicts(cv->b.sc->vstem);
11139     } else {
11140 	if ( !PointsDiagonalable( cv->b.sc->parent,bp,&unit ))
11141 return;
11142 	/* No additional tests, as the points should have already been */
11143         /* reordered by PointsDiagonalable */
11144         d = chunkalloc(sizeof(DStemInfo));
11145         d->where = NULL;
11146         d->left = *bp[0];
11147         d->right = *bp[1];
11148         d->unit = unit;
11149         SCGuessDHintInstances( cv->b.sc,layer,d );
11150         if ( d->where == NULL )
11151             DStemInfoFree( d );
11152         else
11153             MergeDStemInfo( cv->b.sc->parent,&cv->b.sc->dstem,d );
11154     }
11155     cv->b.sc->manualhints = true;
11156 
11157     /* Hint Masks are not relevant for diagonal stems, so modifying */
11158     /* diagonal stems should not affect them */
11159     if ( (mi->mid==MID_AddVHint) || (mi->mid==MID_AddHHint) ) {
11160         if ( h!=NULL && cv->b.sc->parent->mm==NULL )
11161 	    SCModifyHintMasksAdd(cv->b.sc,layer,h);
11162         else
11163 	    SCClearHintMasks(cv->b.sc,layer,true);
11164     }
11165     SCOutOfDateBackground(cv->b.sc);
11166     SCUpdateAll(cv->b.sc);
11167 }
11168 
CVMenuCreateHint(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11169 static void CVMenuCreateHint(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11170     CharView *cv = (CharView *) GDrawGetUserData(gw);
11171     CVCreateHint(cv,mi->mid==MID_CreateHHint,true);
11172 }
11173 
CVMenuReviewHints(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11174 static void CVMenuReviewHints(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11175     CharView *cv = (CharView *) GDrawGetUserData(gw);
11176 
11177     if ( cv->b.sc->hstem==NULL && cv->b.sc->vstem==NULL )
11178 return;
11179     CVReviewHints(cv);
11180 }
11181 
htlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11182 static void htlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11183     CharView *cv = (CharView *) GDrawGetUserData(gw);
11184     int cvlayer = CVLayer((CharViewBase *) cv);
11185     BasePoint *bp[4], unit;
11186     int multilayer = cv->b.sc->parent->multilayer;
11187     int i=0, num = 0;
11188 
11189     for (i=0; i<4; i++) {bp[i]=NULL;}
11190 
11191     num = CVNumForePointsSelected(cv,bp);
11192 
11193     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11194 	switch ( mi->mid ) {
11195 	  case MID_AutoHint:
11196 	    mi->ti.disabled = cvlayer == ly_grid || multilayer;
11197 	  break;
11198 	  case MID_HintSubsPt:
11199 	    mi->ti.disabled = multilayer ||
11200 		              cv->b.layerheads[cv->b.drawmode]->order2 ||
11201 		              cvlayer == ly_grid;
11202 	  break;
11203 	  case MID_AutoCounter:
11204 	    mi->ti.disabled = multilayer;
11205 	  break;
11206 	  case MID_DontAutoHint:
11207 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->order2 || multilayer;
11208 	    mi->ti.checked = cv->b.sc->manualhints;
11209 	  break;
11210 	  case MID_AutoInstr:
11211 	  case MID_EditInstructions:
11212 	    mi->ti.disabled = multilayer ||
11213 		!cv->b.layerheads[cv->b.drawmode]->order2 ||
11214 		cvlayer == ly_grid;
11215 	  break;
11216 	  case MID_Debug:
11217 	    mi->ti.disabled = multilayer ||
11218 		!cv->b.layerheads[cv->b.drawmode]->order2 ||
11219 		!hasFreeTypeDebugger();
11220 	  break;
11221 	  case MID_Deltas:
11222 	    mi->ti.disabled = multilayer ||
11223 		!cv->b.layerheads[cv->b.drawmode]->order2 ||
11224 		!hasFreeTypeDebugger();
11225 	  break;
11226           case  MID_ClearHStem:
11227           case  MID_ClearVStem:
11228           case  MID_ClearDStem:
11229 	    mi->ti.disabled = cvlayer == ly_grid;
11230 	  break;
11231 	  case MID_ClearInstr:
11232 	    mi->ti.disabled = cv->b.sc->ttf_instrs_len==0;
11233 	  break;
11234 	  case MID_AddHHint:
11235 	    mi->ti.disabled = num != 2 || bp[1]->y==bp[0]->y || multilayer;
11236 	  break;
11237 	  case MID_AddVHint:
11238 	    mi->ti.disabled = num != 2 || bp[1]->x==bp[0]->x || multilayer;
11239 	  break;
11240 	  case MID_AddDHint:
11241 	    mi->ti.disabled = num != 4 || !PointsDiagonalable( cv->b.sc->parent,bp,&unit ) || multilayer;
11242 	  break;
11243           case  MID_CreateHHint:
11244           case  MID_CreateVHint:
11245 	    mi->ti.disabled = cvlayer == ly_grid;
11246 	  break;
11247 	  case MID_ReviewHints:
11248 	    mi->ti.disabled = (cv->b.sc->hstem==NULL && cv->b.sc->vstem==NULL ) || multilayer;
11249 	  break;
11250 	}
11251     }
11252 }
11253 
mtlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11254 static void mtlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11255     CharView *cv = (CharView *) GDrawGetUserData(gw);
11256     RefChar *r = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
11257 
11258     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11259 	switch ( mi->mid ) {
11260 	  case MID_RemoveKerns:
11261 	    mi->ti.disabled = cv->b.sc->kerns==NULL;
11262 	  break;
11263 	  case MID_RemoveVKerns:
11264 	    mi->ti.disabled = cv->b.sc->vkerns==NULL;
11265 	  break;
11266 	  case MID_SetVWidth:
11267 	    mi->ti.disabled = !cv->b.sc->parent->hasvmetrics || r!=NULL;
11268 	  break;
11269 	  case MID_AnchorsAway:
11270 	    mi->ti.disabled = cv->b.sc->anchor==NULL;
11271 	  break;
11272 	  case MID_SetWidth: case MID_SetLBearing: case MID_SetRBearing: case MID_SetBearings:
11273 	    mi->ti.disabled = r!=NULL;
11274 	  break;
11275 	}
11276     }
11277 }
11278 
cv_sllistcheck(CharView * cv,struct gmenuitem * mi)11279 static void cv_sllistcheck(CharView *cv, struct gmenuitem *mi) {
11280     SplinePoint *sp; SplineSet *spl; RefChar *r; ImageList *im;
11281     spiro_cp *scp;
11282     SplineSet *test;
11283     int exactlyone = CVOneThingSel(cv,&sp,&spl,&r,&im,NULL,&scp);
11284 
11285     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11286 	switch ( mi->mid ) {
11287 	  case MID_NextCP: case MID_PrevCP:
11288 	    mi->ti.disabled = !exactlyone || sp==NULL || (cv->b.sc->inspiro && hasspiro());
11289 	  break;
11290 	  case MID_NextPt: case MID_PrevPt:
11291 	  case MID_FirstPtNextCont:
11292 	    mi->ti.disabled = !exactlyone || (sp==NULL && scp==NULL);
11293 	  break;
11294 	  case MID_FirstPt: case MID_SelPointAt:
11295 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL;
11296 	  break;
11297 	  case MID_Contours:
11298 	    mi->ti.disabled = !CVAnySelPoints(cv);
11299 	  break;
11300 	  case MID_SelectOpenContours:
11301 	    mi->ti.disabled = true;
11302 	    for ( test=cv->b.layerheads[cv->b.drawmode]->splines; test!=NULL; test=test->next ) {
11303 		if ( test->first->prev==NULL ) {
11304 		    mi->ti.disabled = false;
11305 	    break;
11306 		}
11307 	    }
11308 	  break;
11309 	  case MID_SelectWidth:
11310 	    mi->ti.disabled = !cv->showhmetrics;
11311 	    if ( HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv))!=NULL )
11312 		mi->ti.disabled = true;
11313 	    if ( !mi->ti.disabled ) {
11314 		free(mi->ti.text);
11315 		mi->ti.text = utf82u_copy(cv->widthsel?_("Deselect Width"):_("Width"));
11316 	    }
11317 	  break;
11318 	  case MID_SelectVWidth:
11319 	    mi->ti.disabled = !cv->showvmetrics || !cv->b.sc->parent->hasvmetrics;
11320 	    if ( HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv))!=NULL )
11321 		mi->ti.disabled = true;
11322 	    if ( !mi->ti.disabled ) {
11323 		free(mi->ti.text);
11324 		mi->ti.text = utf82u_copy(cv->vwidthsel?_("Deselect VWidth"):_("VWidth"));
11325 	    }
11326 	  break;
11327 	  case MID_SelectHM:
11328 	    mi->ti.disabled = !exactlyone || sp==NULL || sp->hintmask==NULL;
11329 	  break;
11330 	}
11331     }
11332 }
11333 
sllistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11334 static void sllistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11335     CharView *cv = (CharView *) GDrawGetUserData(gw);
11336     cv_sllistcheck(cv, mi);
11337 }
11338 
cv_cblistcheck(CharView * cv,struct gmenuitem * mi)11339 static void cv_cblistcheck(CharView *cv, struct gmenuitem *mi) {
11340     int i;
11341     KernPair *kp;
11342     SplineChar *sc = cv->b.sc;
11343     SplineFont *sf = sc->parent;
11344     PST *pst;
11345     char *name;
11346 
11347     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11348 	switch ( mi->mid ) {
11349 	  case MID_AnchorPairs:
11350 	    mi->ti.disabled = sc->anchor==NULL;
11351 	  break;
11352 	  case MID_AnchorControl:
11353 	    mi->ti.disabled = sc->anchor==NULL;
11354 	  break;
11355 	  case MID_AnchorGlyph:
11356 	    if ( cv->apmine!=NULL )
11357 		mi->ti.disabled = false;
11358 	    else
11359 		mi->ti.disabled = sc->anchor==NULL;
11360 	  break;
11361 	  case MID_KernPairs:
11362 	    mi->ti.disabled = sc->kerns==NULL;
11363 	    if ( sc->kerns==NULL ) {
11364 		for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
11365 		    for ( kp = sf->glyphs[i]->kerns; kp!=NULL; kp=kp->next ) {
11366 			if ( kp->sc == sc ) {
11367 			    mi->ti.disabled = false;
11368 		goto out;
11369 			}
11370 		    }
11371 		}
11372 	      out:;
11373 	    }
11374 	  break;
11375 	  case MID_Ligatures:
11376 	    name = sc->name;
11377 	    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
11378 		for ( pst=sf->glyphs[i]->possub; pst!=NULL; pst=pst->next ) {
11379 		    if ( pst->type==pst_ligature &&
11380 			    PSTContains(pst->u.lig.components,name)) {
11381 			mi->ti.disabled = false;
11382 	  goto break_out_2;
11383 		    }
11384 		}
11385 	    }
11386 	    mi->ti.disabled = true;
11387 	  break_out_2:;
11388 	  break;
11389 	}
11390     }
11391 }
11392 
cv_nplistcheck(CharView * cv,struct gmenuitem * mi)11393 static void cv_nplistcheck(CharView *cv, struct gmenuitem *mi) {
11394     SplineChar *sc = cv->b.sc;
11395     int order2 = cv->b.layerheads[cv->b.drawmode]->order2;
11396     int is_grid_layer = cv->b.drawmode == dm_grid;
11397 
11398     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11399 	switch ( mi->mid ) {
11400 	  case MID_PtsNone:
11401 	    mi->ti.disabled = !order2 || is_grid_layer;
11402 	    mi->ti.checked = (cv->showpointnumbers == 0);
11403 	  break;
11404 	  case MID_PtsTrue:
11405 	    mi->ti.disabled = !order2 || is_grid_layer;
11406 	    mi->ti.checked = cv->showpointnumbers && order2;
11407 	  break;
11408 	  case MID_PtsPost:
11409 	    mi->ti.disabled = order2 || is_grid_layer;
11410 	    mi->ti.checked = cv->showpointnumbers && !order2 && sc->numberpointsbackards;
11411 	  break;
11412 	  case MID_PtsSVG:
11413 	    mi->ti.disabled = order2 || is_grid_layer;
11414 	    mi->ti.checked = cv->showpointnumbers && !order2 && !sc->numberpointsbackards;
11415 	  break;
11416           case MID_PtsPos:
11417 	    mi->ti.disabled = is_grid_layer;
11418             mi->ti.checked = (cv->showpointnumbers == 2);
11419 	}
11420     }
11421 }
11422 
gflistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11423 static void gflistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11424     CharView *cv = (CharView *) GDrawGetUserData(gw);
11425 
11426     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11427 	switch ( mi->mid ) {
11428 	  case MID_ShowGridFit:
11429 	    mi->ti.disabled = !hasFreeType() || cv->dv!=NULL;
11430 	    mi->ti.checked = cv->show_ft_results;
11431 	  break;
11432 	  case MID_ShowGridFitLiveUpdate:
11433 	    mi->ti.disabled = !hasFreeType() || cv->dv!=NULL;
11434 	    mi->ti.checked = cv->show_ft_results_live_update;
11435 	  break;
11436 	  case MID_Bigger:
11437 	    mi->ti.disabled = !cv->show_ft_results;
11438 	  break;
11439 	  case MID_Smaller:
11440 	    mi->ti.disabled = !cv->show_ft_results || cv->ft_pointsizex<2 || cv->ft_pointsizey<2;
11441 	  break;
11442 	  case MID_GridFitAA:
11443 	    mi->ti.disabled = !cv->show_ft_results;
11444 	    mi->ti.checked = cv->ft_depth==8;
11445 	  break;
11446 	  case MID_GridFitOff:
11447 	    mi->ti.disabled = !cv->show_ft_results;
11448 	  break;
11449 	}
11450     }
11451 }
11452 
swlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11453 static void swlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11454     CharView *cv = (CharView *) GDrawGetUserData(gw);
11455     SplineFont *sf = cv->b.sc->parent;
11456 
11457     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11458 	switch ( mi->mid ) {
11459 	  case MID_MarkExtrema:
11460 	    mi->ti.checked = cv->markextrema;
11461 	    mi->ti.disabled = cv->b.sc->inspiro && hasspiro();
11462 	  break;
11463 	  case MID_MarkPointsOfInflection:
11464 	    mi->ti.checked = cv->markpoi;
11465 	    mi->ti.disabled = cv->b.sc->inspiro && hasspiro();
11466 	  break;
11467 	  case MID_ShowAlmostHV:
11468 	    mi->ti.checked = cv->showalmosthvlines;
11469 	  break;
11470 	  case MID_ShowAlmostHVCurves:
11471 	    mi->ti.checked = cv->showalmosthvcurves;
11472 	  break;
11473 	  case MID_DefineAlmost:
11474 	    mi->ti.disabled = !cv->showalmosthvlines && !cv->showalmosthvcurves;
11475 	  break;
11476 	  case MID_ShowCPInfo:
11477 	    mi->ti.checked = cv->showcpinfo;
11478 	  break;
11479           case MID_DraggingComparisonOutline:
11480 	    mi->ti.checked = prefs_create_dragging_comparison_outline;
11481 	    break;
11482 	  case MID_ShowSideBearings:
11483 	    mi->ti.checked = cv->showsidebearings;
11484 	  break;
11485 	  case MID_ShowRefNames:
11486 	    mi->ti.checked = cv->showrefnames;
11487 	  break;
11488 	  case MID_ShowTabs:
11489 	    mi->ti.checked = cv->showtabs;
11490 	    mi->ti.disabled = cv->former_cnt<=1;
11491 	  break;
11492 	  case MID_HidePoints:
11493 	    mi->ti.checked = cv->showpoints;
11494 	  break;
11495 	case MID_HideControlPoints:
11496 	    mi->ti.checked = cv->alwaysshowcontrolpoints;
11497 	    break;
11498 	  case MID_HideRulers:
11499 	    mi->ti.checked = cv->showrulers;
11500 	  break;
11501 	  case MID_Fill:
11502 	    mi->ti.checked = cv->showfilled;
11503 	  break;
11504 	  case MID_ShowHHints:
11505 	    mi->ti.checked = cv->showhhints;
11506 	    mi->ti.disabled = sf->multilayer;
11507 	  break;
11508 	  case MID_ShowVHints:
11509 	    mi->ti.checked = cv->showvhints;
11510 	    mi->ti.disabled = sf->multilayer;
11511 	  break;
11512 	  case MID_ShowDHints:
11513 	    mi->ti.checked = cv->showdhints;
11514 	    mi->ti.disabled = sf->multilayer;
11515 	  break;
11516 	  case MID_ShowBlueValues:
11517 	    mi->ti.checked = cv->showblues;
11518 	    mi->ti.disabled = sf->multilayer;
11519 	  break;
11520 	  case MID_ShowFamilyBlues:
11521 	    mi->ti.checked = cv->showfamilyblues;
11522 	    mi->ti.disabled = sf->multilayer;
11523 	  break;
11524 	  case MID_ShowAnchors:
11525 	    mi->ti.checked = cv->showanchor;
11526 	    mi->ti.disabled = sf->multilayer;
11527 	  break;
11528 	  case MID_ShowHMetrics:
11529 	    mi->ti.checked = cv->showhmetrics;
11530 	  break;
11531 	  case MID_ShowVMetrics:
11532 	    mi->ti.checked = cv->showvmetrics;
11533 	    mi->ti.disabled = !sf->hasvmetrics;
11534 	  break;
11535 	  case MID_ShowDebugChanges:
11536 	    mi->ti.checked = cv->showdebugchanges;
11537 	  break;
11538 	  case MID_SnapOutlines:
11539 #ifndef _NO_LIBCAIRO
11540 	    if ( GDrawHasCairo(cv->v)&gc_alpha ) {
11541 		mi->ti.checked = cv->snapoutlines;
11542 		mi->ti.disabled = false;
11543 	    } else
11544 #endif
11545 	    {
11546 		mi->ti.checked = true;
11547 		mi->ti.disabled = true;
11548 	    }
11549 	  break;
11550 	}
11551     }
11552 }
11553 
cv_vwlistcheck(CharView * cv,struct gmenuitem * mi)11554 static void cv_vwlistcheck(CharView *cv, struct gmenuitem *mi) {
11555     int pos, gid;
11556     SplineFont *sf = cv->b.sc->parent;
11557     EncMap *map = cv->b.fv->map;
11558 
11559     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11560 	switch ( mi->mid ) {
11561 	  case MID_NextDef:
11562 	    if ( cv->b.container==NULL ) {
11563 		for ( pos = CVCurEnc(cv)+1; pos<map->enccount && ((gid=map->map[pos])==-1 || !SCWorthOutputting(sf->glyphs[gid])); ++pos );
11564 		mi->ti.disabled = pos==map->enccount;
11565 	    } else
11566 		mi->ti.disabled = !(cv->b.container->funcs->canNavigate)(cv->b.container,nt_nextdef);
11567 	  break;
11568 	  case MID_PrevDef:
11569 	    if ( cv->b.container==NULL ) {
11570 		for ( pos = CVCurEnc(cv)-1; pos>=0 && ((gid=map->map[pos])==-1 || !SCWorthOutputting(sf->glyphs[gid])); --pos );
11571 		mi->ti.disabled = pos<0 || cv->b.container!=NULL;
11572 	    } else
11573 		mi->ti.disabled = !(cv->b.container->funcs->canNavigate)(cv->b.container,nt_nextdef);
11574 	  break;
11575 	  case MID_Next:
11576 	    mi->ti.disabled = cv->b.container==NULL ? CVCurEnc(cv)==map->enccount-1 : !(cv->b.container->funcs->canNavigate)(cv->b.container,nt_nextdef);
11577 	  break;
11578 	  case MID_Prev:
11579 	    mi->ti.disabled = cv->b.container==NULL ? CVCurEnc(cv)==0 : !(cv->b.container->funcs->canNavigate)(cv->b.container,nt_nextdef);
11580 	  break;
11581 	  case MID_Former:
11582 	    if ( cv->former_cnt<=1 )
11583 		pos = -1;
11584 	    else for ( pos = sf->glyphcnt-1; pos>=0 ; --pos )
11585 		if ( sf->glyphs[pos]!=NULL && strcmp(sf->glyphs[pos]->name,cv->former_names[1])==0 )
11586 	    break;
11587 	    mi->ti.disabled = pos==-1 || cv->b.container!=NULL;
11588 	  break;
11589 	  case MID_Goto:
11590 	    mi->ti.disabled = cv->b.container!=NULL && !(cv->b.container->funcs->canNavigate)(cv->b.container,nt_goto);
11591 	  break;
11592 	  case MID_FindInFontView:
11593 	    mi->ti.disabled = cv->b.container!=NULL;
11594 	  break;
11595 #if HANYANG
11596 	  case MID_DisplayCompositions:
11597 	    mi->ti.disabled = !cv->b.sc->compositionunit || cv->b.sc->parent->rules==NULL;
11598 	  break;
11599 #endif
11600 	}
11601     }
11602 }
11603 
cblistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11604 static void cblistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11605     CharView *cv = (CharView *) GDrawGetUserData(gw);
11606     cv_cblistcheck(cv, mi);
11607 }
11608 
nplistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11609 static void nplistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11610     CharView *cv = (CharView *) GDrawGetUserData(gw);
11611     cv_nplistcheck(cv, mi);
11612 }
11613 
vwlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11614 static void vwlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11615     CharView *cv = (CharView *) GDrawGetUserData(gw);
11616     cv_vwlistcheck(cv, mi);
11617 }
11618 
CVMenuCenter(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11619 static void CVMenuCenter(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11620     CharView *cv = (CharView *) GDrawGetUserData(gw);
11621     DBounds bb;
11622     real transform[6];
11623     int drawmode = cv->b.drawmode;
11624 
11625     cv->b.drawmode = dm_fore;
11626 
11627     memset(transform,0,sizeof(transform));
11628     transform[0] = transform[3] = 1.0;
11629     transform[1] = transform[2] = transform[5] = 0.0;
11630     if ( cv->b.sc->parent->italicangle==0 )
11631 	SplineCharFindBounds(cv->b.sc,&bb);
11632     else {
11633 	SplineSet *base, *temp;
11634 	base = LayerAllSplines(cv->b.layerheads[cv->b.drawmode]);
11635 	transform[2] = tan( cv->b.sc->parent->italicangle * FF_PI/180.0 );
11636 	temp = SplinePointListTransform(SplinePointListCopy(base),transform,tpt_AllPoints);
11637 	transform[2] = 0;
11638 	LayerUnAllSplines(cv->b.layerheads[cv->b.drawmode]);
11639 	SplineSetFindBounds(temp,&bb);
11640 	SplinePointListsFree(temp);
11641     }
11642 
11643     if ( mi->mid==MID_Center )
11644 	transform[4] = (cv->b.sc->width-(bb.maxx-bb.minx))/2 - bb.minx;
11645     else
11646 	transform[4] = (cv->b.sc->width-(bb.maxx-bb.minx))/3 - bb.minx;
11647     if ( transform[4]!=0 ) {
11648 	cv->p.transany = false;
11649 	CVPreserveState(&cv->b);
11650 	CVTransFuncAllLayers(cv, transform, fvt_dontmovewidth );
11651 	CVCharChangedUpdate(&cv->b);
11652     }
11653     cv->b.drawmode = drawmode;
11654 }
11655 
CVMenuSetWidth(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11656 static void CVMenuSetWidth(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11657     CharView *cv = (CharView *) GDrawGetUserData(gw);
11658 
11659     if ( mi->mid == MID_SetVWidth && !cv->b.sc->parent->hasvmetrics )
11660 return;
11661     CVSetWidth(cv,mi->mid==MID_SetWidth?wt_width:
11662 		  mi->mid==MID_SetLBearing?wt_lbearing:
11663 		  mi->mid==MID_SetRBearing?wt_rbearing:
11664 		  mi->mid==MID_SetBearings?wt_bearings:
11665 		  wt_vwidth);
11666 }
11667 
CVMenuRemoveKern(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11668 static void CVMenuRemoveKern(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11669     CharView *cv = (CharView *) GDrawGetUserData(gw);
11670     SCRemoveKern(cv->b.sc);
11671 }
11672 
CVMenuRemoveVKern(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11673 static void CVMenuRemoveVKern(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11674     CharView *cv = (CharView *) GDrawGetUserData(gw);
11675     SCRemoveVKern(cv->b.sc);
11676 }
11677 
CVMenuKPCloseup(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))11678 static void CVMenuKPCloseup(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
11679     CharView *cv = (CharView *) GDrawGetUserData(gw);
11680     KernPairD(cv->b.sc->parent,cv->b.sc,NULL,CVLayer((CharViewBase *) cv),false);
11681 }
11682 
CVMenuAnchorsAway(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11683 static void CVMenuAnchorsAway(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11684     CharView *cv = (CharView *) GDrawGetUserData(gw);
11685     AnchorPoint *ap;
11686 
11687     ap = mi->ti.userdata;
11688     if ( ap==NULL )
11689 	for ( ap = cv->b.sc->anchor; ap!=NULL && !ap->selected; ap = ap->next );
11690     if ( ap==NULL ) ap= cv->b.sc->anchor;
11691     if ( ap==NULL )
11692 return;
11693 
11694     GDrawSetCursor(cv->v,ct_watch);
11695     GDrawSync(NULL);
11696     GDrawProcessPendingEvents(NULL);
11697     AnchorControl(cv->b.sc,ap,CVLayer((CharViewBase *) cv));
11698     GDrawSetCursor(cv->v,ct_pointer);
11699 }
11700 
11701 static GMenuItem2 wnmenu[] = {
11702     { { (unichar_t *) N_("New O_utline Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 0, 0, 0, 0, 0, 1, 1, 0, 'u' }, H_("New Outline Window|No Shortcut"), NULL, NULL, /* No function, never avail */NULL, 0 },
11703     { { (unichar_t *) N_("New _Bitmap Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("New Bitmap Window|No Shortcut"), NULL, NULL, CVMenuOpenBitmap, MID_OpenBitmap },
11704     { { (unichar_t *) N_("New _Metrics Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("New Metrics Window|No Shortcut"), NULL, NULL, CVMenuOpenMetrics, 0 },
11705     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11706     { { (unichar_t *) N_("Warnings"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Warnings|No Shortcut"), NULL, NULL, _MenuWarnings, MID_Warnings },
11707     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11708     GMENUITEM2_EMPTY
11709 };
11710 
CVWindowMenuBuild(GWindow gw,struct gmenuitem * mi,GEvent * e)11711 static void CVWindowMenuBuild(GWindow gw, struct gmenuitem *mi, GEvent *e) {
11712     CharView *cv = (CharView *) GDrawGetUserData(gw);
11713     struct gmenuitem *wmi;
11714 
11715     WindowMenuBuild(gw,mi,e);
11716     for ( wmi = mi->sub; wmi->ti.text!=NULL || wmi->ti.line ; ++wmi ) {
11717 	switch ( wmi->mid ) {
11718 	  case MID_OpenBitmap:
11719 	    wmi->ti.disabled = cv->b.sc->parent->bitmaps==NULL;
11720 	  break;
11721 	  case MID_Warnings:
11722 	    wmi->ti.disabled = ErrorWindowExists();
11723 	  break;
11724 	}
11725     }
11726     if ( cv->b.container!=NULL ) {
11727 	int canopen = (cv->b.container->funcs->canOpen)(cv->b.container);
11728 	if ( !canopen ) {
11729 	    for ( wmi = mi->sub; wmi->ti.text!=NULL || wmi->ti.line ; ++wmi ) {
11730 		wmi->ti.disabled = true;
11731 	    }
11732 	}
11733     }
11734 }
11735 
11736 static GMenuItem2 dummyitem[] = {
11737     { { (unichar_t *) N_("Font|_New"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, NULL, NULL, NULL, NULL, 0 },
11738     GMENUITEM2_EMPTY
11739 };
11740 static GMenuItem2 fllist[] = {
11741     { { (unichar_t *) N_("Font|_New"), (GImage *) "filenew.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, H_("New|No Shortcut"), NULL, NULL, MenuNew, MID_New },
11742     { { (unichar_t *) N_("_Open"), (GImage *) "fileopen.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Open|No Shortcut"), NULL, NULL, CVMenuOpen, MID_Open },
11743     { { (unichar_t *) N_("Recen_t"), (GImage *) "filerecent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 't' }, NULL, dummyitem, MenuRecentBuild, NULL, MID_Recent },
11744     { { (unichar_t *) N_("_Close"), (GImage *) "fileclose.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Close|No Shortcut"), NULL, NULL, CVMenuClose, MID_Close },
11745     { { (unichar_t *) N_("C_lose Tab"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Close Tab|No Shortcut"), NULL, NULL, CVMenuCloseTab, MID_CloseTab },
11746     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11747     { { (unichar_t *) N_("_Save"), (GImage *) "filesave.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Save|No Shortcut"), NULL, NULL, CVMenuSave, 0 },
11748     { { (unichar_t *) N_("S_ave as..."), (GImage *) "filesaveas.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'a' }, H_("Save as...|No Shortcut"), NULL, NULL, CVMenuSaveAs, 0 },
11749     { { (unichar_t *) N_("_Generate Fonts..."), (GImage *) "filegenerate.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'G' }, H_("Generate Fonts...|No Shortcut"), NULL, NULL, CVMenuGenerate, 0 },
11750     { { (unichar_t *) N_("Generate Mac _Family..."), (GImage *) "filegeneratefamily.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Generate Mac Family...|No Shortcut"), NULL, NULL, CVMenuGenerateFamily, 0 },
11751     { { (unichar_t *) N_("Generate TTC..."), (GImage *) "filegeneratefamily.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Generate TTC...|No Shortcut"), NULL, NULL, CVMenuGenerateTTC, MID_GenerateTTC },
11752     { { (unichar_t *) N_("E_xport..."), (GImage *) "fileexport.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 't' }, H_("Export...|No Shortcut"), NULL, NULL, CVMenuExport, 0 },
11753     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11754     { { (unichar_t *) N_("_Import..."), (GImage *) "fileimport.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Import...|No Shortcut"), NULL, NULL, CVMenuImport, 0 },
11755     { { (unichar_t *) N_("_Revert File"), (GImage *) "filerevert.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Revert File|No Shortcut"), NULL, NULL, CVMenuRevert, MID_Revert },
11756     { { (unichar_t *) N_("Revert Gl_yph"), (GImage *) "filerevertglyph.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Revert Glyph|No Shortcut"), NULL, NULL, CVMenuRevertGlyph, MID_RevertGlyph },
11757     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11758     { { (unichar_t *) N_("Load Word List..."), (GImage *) 0, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Load Word List...|No Shortcut"), NULL, NULL, CVAddWordList, 0 },
11759     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11760     { { (unichar_t *) N_("_Print..."), (GImage *) "fileprint.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Print...|No Shortcut"), NULL, NULL, CVMenuPrint, 0 },
11761     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11762 #if !defined(_NO_PYTHON)
11763     { { (unichar_t *) N_("E_xecute Script..."), (GImage *) "python.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'x' }, H_("Execute Script...|No Shortcut"), NULL, NULL, CVMenuExecute, 0 },
11764     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11765 #endif
11766     { { (unichar_t *) N_("Pr_eferences..."), (GImage *) "fileprefs.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'e' }, H_("Preferences...|No Shortcut"), NULL, NULL, MenuPrefs, 0 },
11767     { { (unichar_t *) N_("_X Resource Editor..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'e' }, H_("X Resource Editor...|No Shortcut"), NULL, NULL, MenuXRes, 0 },
11768     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11769     { { (unichar_t *) N_("_Quit"), (GImage *) "filequit.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'Q' }, H_("Quit|No Shortcut"), NULL, NULL, MenuExit, MID_Quit },
11770     GMENUITEM2_EMPTY
11771 };
11772 
11773 static GMenuItem2 sllist[] = {
11774     { { (unichar_t *) N_("Select _All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Select All|No Shortcut"), NULL, NULL, CVSelectAll, MID_SelAll },
11775     { { (unichar_t *) N_("_Invert Selection"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Invert Selection|No Shortcut"), NULL, NULL, CVSelectInvert, MID_SelInvert },
11776     { { (unichar_t *) N_("_Deselect All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Deselect All|Escape"), NULL, NULL, CVSelectNone, MID_SelNone },
11777     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11778     { { (unichar_t *) N_("_First Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("First Point|No Shortcut"), NULL, NULL, CVMenuNextPrevPt, MID_FirstPt },
11779     { { (unichar_t *) N_("First P_oint, Next Contour"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("First Point, Next Contour|No Shortcut"), NULL, NULL, CVMenuNextPrevPt, MID_FirstPtNextCont },
11780     { { (unichar_t *) N_("_Next Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, H_("Next Point|No Shortcut"), NULL, NULL, CVMenuNextPrevPt, MID_NextPt },
11781     { { (unichar_t *) N_("_Prev Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Prev Point|No Shortcut"), NULL, NULL, CVMenuNextPrevPt, MID_PrevPt },
11782     { { (unichar_t *) N_("Ne_xt Control Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'x' }, H_("Next Control Point|No Shortcut"), NULL, NULL, CVMenuNextPrevCPt, MID_NextCP },
11783     { { (unichar_t *) N_("P_rev Control Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'r' }, H_("Prev Control Point|No Shortcut"), NULL, NULL, CVMenuNextPrevCPt, MID_PrevCP },
11784     { { (unichar_t *) N_("Points on Selected _Contours"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'r' }, H_("Points on Selected Contours|No Shortcut"), NULL, NULL, CVMenuSelectContours, MID_Contours },
11785     { { (unichar_t *) N_("Point A_t"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'r' }, H_("Point At|No Shortcut"), NULL, NULL, CVMenuSelectPointAt, MID_SelPointAt },
11786     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11787     { { (unichar_t *) N_("Select All _Points & Refs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Select All Points & Refs|No Shortcut"), NULL, NULL, CVSelectAll, MID_SelectAllPoints },
11788     { { (unichar_t *) N_("Select Open Contours"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Select Open Contours|No Shortcut"), NULL, NULL, CVSelectOpenContours, MID_SelectOpenContours },
11789     { { (unichar_t *) N_("Select Anc_hors"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'c' }, H_("Select Anchors|No Shortcut"), NULL, NULL, CVSelectAll, MID_SelectAnchors },
11790     { { (unichar_t *) N_("_Width"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Width|No Shortcut"), NULL, NULL, CVSelectWidth, MID_SelectWidth },
11791     { { (unichar_t *) N_("_VWidth"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("VWidth|No Shortcut"), NULL, NULL, CVSelectVWidth, MID_SelectVWidth },
11792     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11793     { { (unichar_t *) N_("Select Points Affected by HM"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Select Points Affected by HM|No Shortcut"), NULL, NULL, CVSelectHM, MID_SelectHM },
11794     GMENUITEM2_EMPTY
11795 };
11796 
11797 static GMenuItem2 edlist[] = {
11798     { { (unichar_t *) N_("_Undo"), (GImage *) "editundo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'U' }, H_("Undo|No Shortcut"), NULL, NULL, CVUndo, MID_Undo },
11799     { { (unichar_t *) N_("_Redo"), (GImage *) "editredo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Redo|No Shortcut"), NULL, NULL, CVRedo, MID_Redo },
11800     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11801     { { (unichar_t *) N_("Cu_t"), (GImage *) "editcut.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 't' }, H_("Cut|No Shortcut"), NULL, NULL, CVCut, MID_Cut },
11802     { { (unichar_t *) N_("_Copy"), (GImage *) "editcopy.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Copy|No Shortcut"), NULL, NULL, CVCopy, MID_Copy },
11803     { { (unichar_t *) N_("C_opy Reference"), (GImage *) "editcopyref.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Copy Reference|No Shortcut"), NULL, NULL, CVCopyRef, MID_CopyRef },
11804     { { (unichar_t *) N_("Copy Loo_kup Data"), (GImage *) "editcopylookupdata.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Copy Lookup Data|No Shortcut"), NULL, NULL, CVCopyLookupData, MID_CopyLookupData },
11805     { { (unichar_t *) N_("Copy _Width"), (GImage *) "editcopywidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Copy Width|No Shortcut"), NULL, NULL, CVCopyWidth, MID_CopyWidth },
11806     { { (unichar_t *) N_("Co_py LBearing"), (GImage *) "editcopylbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'p' }, H_("Copy LBearing|No Shortcut"), NULL, NULL, CVCopyWidth, MID_CopyLBearing },
11807     { { (unichar_t *) N_("Copy RBearin_g"), (GImage *) "editcopyrbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'g' }, H_("Copy RBearing|No Shortcut"), NULL, NULL, CVCopyWidth, MID_CopyRBearing },
11808     { { (unichar_t *) N_("_Paste"), (GImage *) "editpaste.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Paste|No Shortcut"), NULL, NULL, CVPaste, MID_Paste },
11809     { { (unichar_t *) N_("C_hop"), (GImage *) "editclear.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'h' }, H_("Chop|Delete"), NULL, NULL, CVClear, MID_Clear },
11810     { { (unichar_t *) N_("Clear _Background"), (GImage *) "editclearback.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Clear Background|No Shortcut"), NULL, NULL, CVClearBackground, 0 },
11811     { { (unichar_t *) N_("points|_Merge"), (GImage *) "editmerge.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Merge|No Shortcut"), NULL, NULL, CVMerge, MID_Merge },
11812     { { (unichar_t *) N_("points|Merge to Line"), (GImage *) "editmergetoline.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Merge to Line|No Shortcut"), NULL, NULL, CVMergeToLine, MID_MergeToLine },
11813     /*{ { (unichar_t *) N_("_Elide"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Elide|No Shortcut"), NULL, NULL, CVElide, MID_Elide },*/
11814     { { (unichar_t *) N_("_Join"), (GImage *) "editjoin.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'J' }, H_("Join|No Shortcut"), NULL, NULL, CVJoin, MID_Join },
11815     { { (unichar_t *) N_("Copy _Fg To Bg"), (GImage *) "editcopyfg2bg.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Copy Fg To Bg|No Shortcut"), NULL, NULL, CVCopyFgBg, MID_CopyFgToBg },
11816     { { (unichar_t *) N_("Cop_y Layer To Layer..."), (GImage *) "editcopylayer2layer.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Copy Layer To Layer...|No Shortcut"), NULL, NULL, CVMenuCopyL2L, MID_CopyBgToFg },
11817     { { (unichar_t *) N_("Copy Gri_d Fit"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Copy Grid Fit|No Shortcut"), NULL, NULL, CVMenuCopyGridFit, MID_CopyGridFit },
11818     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11819     { { (unichar_t *) N_("_Select"), (GImage *) "editselect.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Select|No Shortcut"), sllist, sllistcheck, NULL, 0 },
11820     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11821     { { (unichar_t *) N_("U_nlink Reference"), (GImage *) "editunlink.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'U' }, H_("Unlink Reference|No Shortcut"), NULL, NULL, CVUnlinkRef, MID_UnlinkRef },
11822     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11823     { { (unichar_t *) N_("Remo_ve Undoes..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'e' }, H_("Remove Undoes...|No Shortcut"), NULL, NULL, CVRemoveUndoes, MID_RemoveUndoes },
11824     GMENUITEM2_EMPTY
11825 };
11826 
11827 static GMenuItem2 ptlist[] = {
11828     { { (unichar_t *) N_("_Curve"), (GImage *) "pointscurve.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Curve|No Shortcut"), NULL, NULL, CVMenuPointType, MID_Curve },
11829     { { (unichar_t *) N_("_HVCurve"), (GImage *) "pointshvcurve.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("HVCurve|No Shortcut"), NULL, NULL, CVMenuPointType, MID_HVCurve },
11830     { { (unichar_t *) N_("C_orner"), (GImage *) "pointscorner.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("Corner|No Shortcut"), NULL, NULL, CVMenuPointType, MID_Corner },
11831     { { (unichar_t *) N_("_Tangent"), (GImage *) "pointstangent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Tangent|No Shortcut"), NULL, NULL, CVMenuPointType, MID_Tangent },
11832     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11833 /* GT: Make this (selected) point the first point in the glyph */
11834     { { (unichar_t *) N_("_Make First"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Make First|No Shortcut"), NULL, NULL, CVMenuMakeFirst, MID_MakeFirst },
11835     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11836     { { (unichar_t *) N_("Can Be _Interpolated"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Can Be Interpolated|No Shortcut"), NULL, NULL, CVMenuImplicit, MID_ImplicitPt },
11837     { { (unichar_t *) N_("Can't _Be Interpolated"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Can't Be Interpolated|No Shortcut"), NULL, NULL, CVMenuImplicit, MID_NoImplicitPt },
11838     { { (unichar_t *) N_("Center Bet_ween Control Points"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Center Between Control Points|No Shortcut"), NULL, NULL, CVMenuCenterCP, MID_CenterCP },
11839     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11840     { { (unichar_t *) N_("_Add Anchor"), (GImage *) "pointsaddanchor.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Add Anchor|No Shortcut"), NULL, NULL, CVMenuAddAnchor, MID_AddAnchor },
11841     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11842     { { (unichar_t *) N_("Acceptable _Extrema"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Acceptable Extrema|No Shortcut"), NULL, NULL, CVMenuAcceptableExtrema, MID_AcceptableExtrema },
11843     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11844     { { (unichar_t *) N_("Make _Line"), (GImage *) "pointsmakeline.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Make Line|No Shortcut"), NULL, NULL, CVMenuMakeLine, MID_MakeLine },
11845     { { (unichar_t *) N_("Ma_ke Arc"), (GImage *) "pointsmakearc.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Make Arc|No Shortcut"), NULL, NULL, CVMenuMakeLine, MID_MakeArc },
11846     { { (unichar_t *) N_("Inse_rt Point On Spline At..."),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Insert Point On Spline At...|No Shortcut"), NULL, NULL, CVMenuInsertPt, MID_InsertPtOnSplineAt },
11847     { { (unichar_t *) N_("_Name Point"),  (GImage *) "pointsnamepoint.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Name Point|No Shortcut"), NULL, NULL, CVMenuNamePoint, MID_NamePoint },
11848     { { (unichar_t *) N_("_Name Contour"),  (GImage *) "pointsnamecontour.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Name Contour|No Shortcut"), NULL, NULL, CVMenuNameContour, MID_NameContour },
11849     { { (unichar_t *) N_("Make Clip _Path"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Make Clip Path|No Shortcut"), NULL, NULL, CVMenuClipPath, MID_ClipPath },
11850     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11851     { { (unichar_t *) N_("Tool_s"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Tools|No Shortcut"), cvtoollist, cvtoollist_check, NULL, MID_Tools },
11852     GMENUITEM2_EMPTY
11853 };
11854 
11855 static GMenuItem2 spiroptlist[] = {
11856     { { (unichar_t *) N_("G4 _Curve"), (GImage *) "pointscurve.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("G4 Curve|No Shortcut"), NULL, NULL, CVMenuPointType, MID_SpiroG4 },
11857     { { (unichar_t *) N_("_G2 Curve"), (GImage *) "pointsG2curve.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("G2 Curve|No Shortcut"), NULL, NULL, CVMenuPointType, MID_SpiroG2 },
11858     { { (unichar_t *) N_("C_orner"), (GImage *) "pointscorner.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("Corner|No Shortcut"), NULL, NULL, CVMenuPointType, MID_SpiroCorner },
11859     { { (unichar_t *) N_("_Left Constraint"), (GImage *) "pointsspiroprev.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Left Constraint|No Shortcut"), NULL, NULL, CVMenuPointType, MID_SpiroLeft },
11860     { { (unichar_t *) N_("_Right Constraint"), (GImage *) "pointsspironext.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Right Constraint|No Shortcut"), NULL, NULL, CVMenuPointType, MID_SpiroRight },
11861     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11862 /* GT: Make this (selected) point the first point in the glyph */
11863     { { (unichar_t *) N_("_Make First"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Make First|No Shortcut"), NULL, NULL, CVMenuSpiroMakeFirst, MID_SpiroMakeFirst },
11864     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11865     { { (unichar_t *) N_("_Add Anchor"), (GImage *) "pointsaddanchor.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Add Anchor|No Shortcut"), NULL, NULL, CVMenuAddAnchor, MID_AddAnchor },
11866     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11867     { { (unichar_t *) N_("_Name Point"), (GImage *) "pointsnamepoint.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Name Point|No Shortcut"), NULL, NULL, CVMenuNamePoint, MID_NamePoint },
11868     { { (unichar_t *) N_("_Name Contour"), (GImage *) "pointsnamecontour.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Name Contour|No Shortcut"), NULL, NULL, CVMenuNameContour, MID_NameContour },
11869     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11870     { { (unichar_t *) N_("Tool_s"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, NULL, cvspirotoollist, cvtoollist_check, NULL, MID_Tools },
11871     GMENUITEM2_EMPTY
11872 };
11873 
11874 static GMenuItem2 allist[] = {
11875 /* GT: Align these points to their average position */
11876     { { (unichar_t *) N_("_Align Points"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Align Points|No Shortcut"), NULL, NULL, CVMenuConstrain, MID_Average },
11877     { { (unichar_t *) N_("_Space Points"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Space Points|No Shortcut"), NULL, NULL, CVMenuConstrain, MID_SpacePts },
11878     { { (unichar_t *) N_("Space _Regions..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Space Regions...|No Shortcut"), NULL, NULL, CVMenuConstrain, MID_SpaceRegion },
11879     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11880     { { (unichar_t *) N_("Make _Parallel..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Make Parallel...|No Shortcut"), NULL, NULL, CVMenuMakeParallel, MID_MakeParallel },
11881     GMENUITEM2_EMPTY
11882 };
11883 
11884 static GMenuItem2 smlist[] = {
11885     { { (unichar_t *) N_("_Simplify"), (GImage *) "elementsimplify.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Simplify|No Shortcut"), NULL, NULL, CVMenuSimplify, MID_Simplify },
11886     { { (unichar_t *) N_("Simplify More..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Simplify More...|No Shortcut"), NULL, NULL, CVMenuSimplifyMore, MID_SimplifyMore },
11887     { { (unichar_t *) N_("Clea_nup Glyph"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'n' }, H_("Cleanup Glyph|No Shortcut"), NULL, NULL, CVMenuCleanupGlyph, MID_CleanupGlyph },
11888     { { (unichar_t *) N_("Canonical Start _Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'n' }, H_("Canonical Start Point|No Shortcut"), NULL, NULL, CVMenuCanonicalStart, MID_CanonicalStart },
11889     { { (unichar_t *) N_("Canonical _Contours"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'n' }, H_("Canonical Contours|No Shortcut"), NULL, NULL, CVMenuCanonicalContours, MID_CanonicalContours },
11890     GMENUITEM2_EMPTY
11891 };
11892 
smlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11893 static void smlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11894     CharView *cv = (CharView *) GDrawGetUserData(gw);
11895 
11896     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11897 	switch ( mi->mid ) {
11898 	  case MID_Simplify:
11899 	  case MID_CleanupGlyph:
11900 	  case MID_SimplifyMore:
11901 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL;
11902 	  break;
11903 	  case MID_CanonicalStart:
11904 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL ||
11905 		    (cv->b.sc->inspiro && hasspiro());
11906 	  break;
11907 	  case MID_CanonicalContours:
11908 	    mi->ti.disabled = cv->b.layerheads[cv->b.drawmode]->splines==NULL ||
11909 		cv->b.layerheads[cv->b.drawmode]->splines->next==NULL ||
11910 		cv->b.drawmode!=dm_fore;
11911 	  break;
11912 	}
11913     }
11914 }
11915 
11916 static GMenuItem2 orlist[] = {
11917     { { (unichar_t *) N_("_First"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("First|No Shortcut"), NULL, NULL, CVMenuOrder, MID_First },
11918     { { (unichar_t *) N_("_Earlier"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Earlier|No Shortcut"), NULL, NULL, CVMenuOrder, MID_Earlier },
11919     { { (unichar_t *) N_("L_ater"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'n' }, H_("Later|No Shortcut"), NULL, NULL, CVMenuOrder, MID_Later },
11920     { { (unichar_t *) N_("_Last"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'n' }, H_("Last|No Shortcut"), NULL, NULL, CVMenuOrder, MID_Last },
11921     GMENUITEM2_EMPTY
11922 };
11923 
orlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))11924 static void orlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
11925     CharView *cv = (CharView *) GDrawGetUserData(gw);
11926     SplinePointList *spl;
11927     RefChar *r;
11928     ImageList *im;
11929     int exactlyone = CVOneContourSel(cv,&spl,&r,&im);
11930     int isfirst, islast;
11931 
11932     isfirst = islast = false;
11933     if ( spl!=NULL ) {
11934 	isfirst = cv->b.layerheads[cv->b.drawmode]->splines==spl;
11935 	islast = spl->next==NULL;
11936     } else if ( r!=NULL ) {
11937 	isfirst = cv->b.layerheads[cv->b.drawmode]->refs==r;
11938 	islast = r->next==NULL;
11939     } else if ( im!=NULL ) {
11940 	isfirst = cv->b.layerheads[cv->b.drawmode]->images==im;
11941 	islast = im->next==NULL;
11942     }
11943 
11944     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
11945 	switch ( mi->mid ) {
11946 	  case MID_First:
11947 	  case MID_Earlier:
11948 	    mi->ti.disabled = !exactlyone || isfirst;
11949 	  break;
11950 	  case MID_Last:
11951 	  case MID_Later:
11952 	    mi->ti.disabled = !exactlyone || islast;
11953 	  break;
11954 	}
11955     }
11956 }
11957 
11958 static GMenuItem2 rmlist[] = {
11959     { { (unichar_t *) N_("_Remove Overlap"), (GImage *) "overlaprm.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Remove Overlap|No Shortcut"), NULL, NULL, CVMenuOverlap, MID_RmOverlap },
11960     { { (unichar_t *) N_("_Intersect"), (GImage *) "overlapintersection.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Intersect|No Shortcut"), NULL, NULL, CVMenuOverlap, MID_Intersection },
11961     { { (unichar_t *) N_("_Exclude"), (GImage *) "overlapexclude.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'E' }, H_("Exclude|No Shortcut"), NULL, NULL, CVMenuOverlap, MID_Exclude },
11962     { { (unichar_t *) N_("_Find Intersections"), (GImage *) "overlapfindinter.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Find Intersections|No Shortcut"), NULL, NULL, CVMenuOverlap, MID_FindInter },
11963     GMENUITEM2_EMPTY
11964 };
11965 
11966 static GMenuItem2 eflist[] = {
11967     { { (unichar_t *) N_("Change _Weight..."), (GImage *) "styleschangeweight.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Change Weight...|No Shortcut"), NULL, NULL, CVMenuEmbolden, MID_Embolden },
11968     { { (unichar_t *) N_("_Italic..."), (GImage *) "stylesitalic.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Italic...|No Shortcut"), NULL, NULL, CVMenuItalic, MID_Italic },
11969     { { (unichar_t *) N_("Obli_que..."), (GImage *) "stylesoblique.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Oblique...|No Shortcut"), NULL, NULL, CVMenuOblique, 0 },
11970     { { (unichar_t *) N_("_Condense/Extend..."), (GImage *) "stylesextendcondense.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Condense/Extend...|No Shortcut"), NULL, NULL, CVMenuCondense, MID_Condense },
11971     { { (unichar_t *) N_("Change _X-Height..."), (GImage *) "styleschangexheight.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Change X-Height...|No Shortcut"), NULL, NULL, CVMenuChangeXHeight, MID_ChangeXHeight },
11972     { { (unichar_t *) N_("Change _Glyph..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Change Glyph...|No Shortcut"), NULL, NULL, CVMenuChangeGlyph, MID_ChangeGlyph },
11973     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
11974     { { (unichar_t *) N_("In_line..."), (GImage *) "stylesinline.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Inline...|No Shortcut"), NULL, NULL, CVMenuInline, 0 },
11975     { { (unichar_t *) N_("_Outline..."), (GImage *) "stylesoutline.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Outline...|No Shortcut"), NULL, NULL, CVMenuOutline, 0 },
11976     { { (unichar_t *) N_("S_hadow..."), (GImage *) "stylesshadow.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Shadow...|No Shortcut"), NULL, NULL, CVMenuShadow, 0 },
11977     { { (unichar_t *) N_("_Wireframe..."), (GImage *) "styleswireframe.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Wireframe...|No Shortcut"), NULL, NULL, CVMenuWireframe, 0 },
11978     GMENUITEM2_EMPTY
11979 };
11980 
11981 static GMenuItem2 balist[] = {
11982     { { (unichar_t *) N_("_Build Accented Glyph"), (GImage *) "elementbuildaccent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'u' }, H_("Build Accented Glyph|No Shortcut"), NULL, NULL, CVMenuBuildAccent, MID_BuildAccent },
11983     { { (unichar_t *) N_("Build _Composite Glyph"), (GImage *) "elementbuildcomposite.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Build Composite Glyph|No Shortcut"), NULL, NULL, CVMenuBuildComposite, MID_BuildComposite },
11984     GMENUITEM2_EMPTY
11985 };
11986 
11987 static GMenuItem2 delist[] = {
11988     { { (unichar_t *) N_("_References..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'u' }, H_("References...|No Shortcut"), NULL, NULL, CVMenuShowDependentRefs, MID_ShowDependentRefs },
11989     { { (unichar_t *) N_("_Substitutions..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Substitutions...|No Shortcut"), NULL, NULL, CVMenuShowDependentSubs, MID_ShowDependentSubs },
11990     GMENUITEM2_EMPTY
11991 };
11992 
11993 static GMenuItem2 trlist[] = {
11994     { { (unichar_t *) N_("_Transform..."), (GImage *) "elementtransform.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Transform...|No Shortcut"), NULL, NULL, CVMenuTransform, 0 },
11995     { { (unichar_t *) N_("_Point of View Projection..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Point of View Projection...|No Shortcut"), NULL, NULL, CVMenuPOV, 0 },
11996     { { (unichar_t *) N_("_Non Linear Transform..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Non Linear Transform...|No Shortcut"), NULL, NULL, CVMenuNLTransform, 0 },
11997     GMENUITEM2_EMPTY
11998 };
11999 
12000 static GMenuItem2 rndlist[] = {
12001     { { (unichar_t *) N_("To _Int"), (GImage *) "elementround.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("To Int|No Shortcut"), NULL, NULL, CVMenuRound2Int, MID_Round },
12002     { { (unichar_t *) N_("To _Hundredths"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("To Hundredths|No Shortcut"), NULL, NULL, CVMenuRound2Hundredths, 0 },
12003     { { (unichar_t *) N_("_Cluster"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Cluster|No Shortcut"), NULL, NULL, CVMenuCluster, MID_RoundToCluster },
12004     GMENUITEM2_EMPTY
12005 };
12006 
12007 static GMenuItem2 ellist[] = {
12008     { { (unichar_t *) N_("_Font Info..."), (GImage *) "elementfontinfo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Font Info...|No Shortcut"), NULL, NULL, CVMenuFontInfo, MID_FontInfo },
12009     { { (unichar_t *) N_("_Glyph Info..."), (GImage *) "elementglyphinfo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Glyph Info...|No Shortcut"), NULL, NULL, CVMenuCharInfo, MID_CharInfo },
12010     { { (unichar_t *) N_("Get _Info..."), (GImage *) "elementgetinfo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Get Info...|No Shortcut"), NULL, NULL, CVMenuGetInfo, MID_GetInfo },
12011     { { (unichar_t *) N_("S_how Dependent"), (GImage *) "elementshowdep.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Show Dependent|No Shortcut"), delist, delistcheck, NULL, MID_ShowDependentRefs },
12012     { { (unichar_t *) N_("Find Proble_ms..."), (GImage *) "elementfindprobs.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Find Problems...|No Shortcut"), NULL, NULL, CVMenuFindProblems, MID_FindProblems },
12013     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12014     { { (unichar_t *) N_("Bitm_ap strikes Available..."), (GImage *) "elementbitmapsavail.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Bitmap strikes Available...|No Shortcut"), NULL, NULL, CVMenuBitmaps, MID_AvailBitmaps },
12015     { { (unichar_t *) N_("Regenerate _Bitmap Glyphs..."), (GImage *) "elementregenbitmaps.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Regenerate Bitmap Glyphs...|No Shortcut"), NULL, NULL, CVMenuBitmaps, MID_RegenBitmaps },
12016     { { (unichar_t *) N_("Remove Bitmap Glyphs..."), (GImage *) "elementremovebitmaps.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Remove Bitmap Glyphs...|No Shortcut"), NULL, NULL, CVMenuBitmaps, MID_RemoveBitmaps },
12017     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12018     { { (unichar_t *) N_("St_yles"), (GImage *) "elementstyles.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Styles|No Shortcut"), eflist, NULL, NULL, MID_Styles },
12019     { { (unichar_t *) N_("_Transformations"), (GImage *) "elementtransform.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Transformations|No Shortcut"), trlist, NULL, NULL, 0 },
12020     { { (unichar_t *) N_("_Expand Stroke..."), (GImage *) "elementexpandstroke.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'E' }, H_("Expand Stroke...|No Shortcut"), NULL, NULL, CVMenuStroke, MID_Stroke },
12021 #ifdef FONTFORGE_CONFIG_TILEPATH
12022     { { (unichar_t *) N_("Tile _Path..."), (GImage *) "elementtilepath.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Tile Path...|No Shortcut"), NULL, NULL, CVMenuTilePath, MID_TilePath },
12023     { { (unichar_t *) N_("Tile Pattern..."), (GImage *) "elementtilepattern.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Tile Pattern...|No Shortcut"), NULL, NULL, CVMenuPatternTile, 0 },
12024 #endif
12025     { { (unichar_t *) N_("O_verlap"), (GImage *) "overlaprm.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'v' }, H_("Overlap|No Shortcut"), rmlist, NULL, NULL, MID_RmOverlap },
12026     { { (unichar_t *) N_("_Simplify"), (GImage *) "elementsimplify.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Simplify|No Shortcut"), smlist, smlistcheck, NULL, MID_Simplify },
12027     { { (unichar_t *) N_("Add E_xtrema"), (GImage *) "elementaddextrema.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'x' }, H_("Add Extrema|No Shortcut"), NULL, NULL, CVMenuAddExtrema, MID_AddExtrema },
12028     { { (unichar_t *) N_("Autot_race"), (GImage *) "elementautotrace.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'r' }, H_("Autotrace|No Shortcut"), NULL, NULL, CVMenuAutotrace, MID_Autotrace },
12029     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12030     { { (unichar_t *) N_("A_lign"), (GImage *) "elementalign.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Align|No Shortcut"), allist, allistcheck, NULL, MID_Align },
12031     { { (unichar_t *) N_("Roun_d"), (GImage *) "elementround.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Round|No Shortcut"), rndlist, rndlistcheck, NULL, MID_Round },
12032     { { (unichar_t *) N_("_Order"), (GImage *) "elementorder.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Order|No Shortcut"), orlist, orlistcheck, NULL, 0 },
12033     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12034     { { (unichar_t *) N_("Check Self-Intersection"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("Check Self-Intersection|No Shortcut"), NULL, NULL, CVMenuCheckSelf, MID_CheckSelf },
12035     { { (unichar_t *) N_("Glyph Self-Intersects"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("Glyph Self-Intersects|No Shortcut"), NULL, NULL, CVMenuGlyphSelfIntersects, MID_GlyphSelfIntersects },
12036     { { (unichar_t *) N_("Cloc_kwise"), (GImage *) "elementclockwise.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("Clockwise|No Shortcut"), NULL, NULL, CVMenuDir, MID_Clockwise },
12037     { { (unichar_t *) N_("Cou_nter Clockwise"), (GImage *) "elementanticlock.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'n' }, H_("Counter Clockwise|No Shortcut"), NULL, NULL, CVMenuDir, MID_Counter },
12038     { { (unichar_t *) N_("_Correct Direction"), (GImage *) "elementcorrectdir.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Correct Direction|No Shortcut"), NULL, NULL, CVMenuCorrectDir, MID_Correct },
12039     { { (unichar_t *) N_("Reverse Direction"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Reverse Direction|No Shortcut"), NULL, NULL, CVMenuReverseDir, MID_ReverseDir },
12040     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12041     { { (unichar_t *) N_("Insert Text Outlines..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Insert Text Outlines...|No Shortcut"), NULL, NULL, CVMenuInsertText, MID_InsertText },
12042     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12043     { { (unichar_t *) N_("B_uild"), (GImage *) "elementbuildaccent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'u' }, H_("Build|No Shortcut"), balist, balistcheck, NULL, MID_BuildAccent },
12044     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12045     { { (unichar_t *) N_("Compare Layers..."), (GImage *) "elementcomparelayers.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'u' }, H_("Compare Layers...|No Shortcut"), NULL, NULL, CVMenuCompareL2L, 0 },
12046     GMENUITEM2_EMPTY
12047 };
12048 
12049 static GMenuItem2 htlist[] = {
12050     { { (unichar_t *) N_("Auto_Hint"), (GImage *) "hintsautohint.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("AutoHint|No Shortcut"), NULL, NULL, CVMenuAutoHint, MID_AutoHint },
12051     { { (unichar_t *) N_("Hint _Substitution Pts"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Hint Substitution Pts|No Shortcut"), NULL, NULL, CVMenuAutoHintSubs, MID_HintSubsPt },
12052     { { (unichar_t *) N_("Auto _Counter Hint"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Auto Counter Hint|No Shortcut"), NULL, NULL, CVMenuAutoCounter, MID_AutoCounter },
12053     { { (unichar_t *) N_("_Don't AutoHint"), (GImage *) "hintsdontautohint.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'H' }, H_("Don't AutoHint|No Shortcut"), NULL, NULL, CVMenuDontAutoHint, MID_DontAutoHint },
12054     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12055     { { (unichar_t *) N_("Auto_Instr"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("AutoInstr|No Shortcut"), NULL, NULL, CVMenuNowakAutoInstr, MID_AutoInstr },
12056     { { (unichar_t *) N_("_Edit Instructions..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Edit Instructions...|No Shortcut"), NULL, NULL, CVMenuEditInstrs, MID_EditInstructions },
12057     { { (unichar_t *) N_("_Debug..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Debug...|No Shortcut"), NULL, NULL, CVMenuDebug, MID_Debug },
12058     { { (unichar_t *) N_("S_uggest Deltas..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Suggest Deltas...|No Shortcut"), NULL, NULL, CVMenuDeltas, MID_Deltas },
12059     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12060     { { (unichar_t *) N_("_Clear HStem"), (GImage *) "hintsclearhstems.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Clear HStem|No Shortcut"), NULL, NULL, CVMenuClearHints, MID_ClearHStem },
12061     { { (unichar_t *) N_("Clear _VStem"), (GImage *) "hintsclearvstems.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Clear VStem|No Shortcut"), NULL, NULL, CVMenuClearHints, MID_ClearVStem },
12062     { { (unichar_t *) N_("Clear DStem"), (GImage *) "hintscleardstems.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Clear DStem|No Shortcut"), NULL, NULL, CVMenuClearHints, MID_ClearDStem },
12063     { { (unichar_t *) N_("Clear Instructions"),  (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Clear Instructions|No Shortcut"), NULL, NULL, CVMenuClearInstrs, MID_ClearInstr },
12064     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12065     { { (unichar_t *) N_("_Add HHint"), (GImage *) "hintsaddhstem.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Add HHint|No Shortcut"), NULL, NULL, CVMenuAddHint, MID_AddHHint },
12066     { { (unichar_t *) N_("Add VHi_nt"), (GImage *) "hintsaddvstem.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 's' }, H_("Add VHint|No Shortcut"), NULL, NULL, CVMenuAddHint, MID_AddVHint },
12067     { { (unichar_t *) N_("Add DHint"), (GImage *) "hintsadddstem.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 't' }, H_("Add DHint|No Shortcut"), NULL, NULL, CVMenuAddHint, MID_AddDHint },
12068     { { (unichar_t *) N_("Crea_te HHint..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'r' }, H_("Create HHint...|No Shortcut"), NULL, NULL, CVMenuCreateHint, MID_CreateHHint },
12069     { { (unichar_t *) N_("Cr_eate VHint..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'e' }, H_("Create VHint...|No Shortcut"), NULL, NULL, CVMenuCreateHint, MID_CreateVHint },
12070     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12071     { { (unichar_t *) N_("_Review Hints..."), (GImage *) "hintsreviewhints.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Review Hints...|No Shortcut"), NULL, NULL, CVMenuReviewHints, MID_ReviewHints },
12072     GMENUITEM2_EMPTY
12073 };
12074 
12075 static GMenuItem2 ap2list[] = {
12076     GMENUITEM2_EMPTY
12077 };
12078 
ap2listbuild(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12079 static void ap2listbuild(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12080     CharView *cv = (CharView *) GDrawGetUserData(gw);
12081     char buf[300];
12082     GMenuItem *sub;
12083     int k, cnt;
12084     AnchorPoint *ap;
12085 
12086     if ( mi->sub!=NULL ) {
12087 	GMenuItemArrayFree(mi->sub);
12088 	mi->sub = NULL;
12089     }
12090 
12091     for ( k=0; k<2; ++k ) {
12092 	cnt = 0;
12093 	for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next ) {
12094 	    if ( k ) {
12095 		if ( ap->type==at_baselig )
12096 /* GT: In the next few lines the "%s" is the name of an anchor class, and the */
12097 /* GT: rest of the string identifies the type of the anchor */
12098 		    snprintf(buf,sizeof(buf), _("%s at ligature pos %d"), ap->anchor->name, ap->lig_index );
12099 		else
12100 		    snprintf(buf,sizeof(buf),
12101 			ap->type==at_cexit ? _("%s exit"):
12102 			ap->type==at_centry ? _("%s entry"):
12103 			ap->type==at_mark ? _("%s mark"):
12104 			    _("%s base"),ap->anchor->name );
12105 		sub[cnt].ti.text = utf82u_copy(buf);
12106 		sub[cnt].ti.userdata = ap;
12107 		sub[cnt].ti.bg = sub[cnt].ti.fg = COLOR_DEFAULT;
12108 		sub[cnt].invoke = CVMenuAnchorsAway;
12109 	    }
12110 	    ++cnt;
12111 	}
12112 	if ( !k )
12113 	    sub = calloc(cnt+1,sizeof(GMenuItem));
12114     }
12115     mi->sub = sub;
12116 }
12117 
CVMenuKernByClasses(GWindow gw,struct gmenuitem * mi,GEvent * e)12118 static void CVMenuKernByClasses(GWindow gw,struct gmenuitem *mi,GEvent *e) {
12119     CharView *cv = (CharView *) GDrawGetUserData(gw);
12120     MetricsView *mv = 0;
12121     SplineFont *sf = cv->b.sc->parent;
12122     int cvlayer = CVLayer((CharViewBase *) cv);
12123     ShowKernClasses(sf, mv, cvlayer, false);
12124 }
12125 
CVMenuVKernByClasses(GWindow gw,struct gmenuitem * mi,GEvent * e)12126 static void CVMenuVKernByClasses(GWindow gw,struct gmenuitem *mi,GEvent *e) {
12127     CharView *cv = (CharView *) GDrawGetUserData(gw);
12128     MetricsView *mv = 0;
12129     SplineFont *sf = cv->b.sc->parent;
12130     int cvlayer = CVLayer((CharViewBase *) cv);
12131     ShowKernClasses(sf, mv, cvlayer, true);
12132 }
12133 
CVMenuVKernFromHKern(GWindow gw,struct gmenuitem * mi,GEvent * e)12134 static void CVMenuVKernFromHKern(GWindow gw,struct gmenuitem *mi,GEvent *e) {
12135     CharView *cv = (CharView *) GDrawGetUserData(gw);
12136     FVVKernFromHKern((FontViewBase *) cv->b.fv);
12137 }
12138 
12139 static GMenuItem2 mtlist[] = {
12140     { { (unichar_t *) N_("New _Metrics Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("New Metrics Window|No Shortcut"), NULL, NULL, CVMenuOpenMetrics, 0 },
12141     GMENUITEM2_LINE,
12142     { { (unichar_t *) N_("_Center in Width"), (GImage *) "metricscenter.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Center in Width|No Shortcut"), NULL, NULL, CVMenuCenter, MID_Center },
12143     { { (unichar_t *) N_("_Thirds in Width"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Thirds in Width|No Shortcut"), NULL, NULL, CVMenuCenter, MID_Thirds },
12144     { { (unichar_t *) N_("Set _Width..."), (GImage *) "metricssetwidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Set Width...|No Shortcut"), NULL, NULL, CVMenuSetWidth, MID_SetWidth },
12145     { { (unichar_t *) N_("Set _LBearing..."), (GImage *) "metricssetlbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'L' }, H_("Set LBearing...|No Shortcut"), NULL, NULL, CVMenuSetWidth, MID_SetLBearing },
12146     { { (unichar_t *) N_("Set _RBearing..."), (GImage *) "metricssetrbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Set RBearing...|No Shortcut"), NULL, NULL, CVMenuSetWidth, MID_SetRBearing },
12147     { { (unichar_t *) N_("Set Both Bearings..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Set Both Bearings...|No Shortcut"), NULL, NULL, CVMenuSetWidth, MID_SetBearings },
12148     GMENUITEM2_LINE,
12149     { { (unichar_t *) N_("Set _Vertical Advance..."), (GImage *) "metricssetvwidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Set Vertical Advance...|No Shortcut"), NULL, NULL, CVMenuSetWidth, MID_SetVWidth },
12150     GMENUITEM2_LINE,
12151     { { (unichar_t *) N_("Ker_n By Classes..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Kern By Classes...|No Shortcut"), NULL, NULL, CVMenuKernByClasses, 0 },
12152     { { (unichar_t *) N_("VKern By Classes..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("VKern By Classes...|No Shortcut"), NULL, NULL, CVMenuVKernByClasses, MID_VKernClass },
12153     { { (unichar_t *) N_("VKern From HKern"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("VKern From HKern|No Shortcut"), NULL, NULL, CVMenuVKernFromHKern, MID_VKernFromHKern },
12154     { { (unichar_t *) N_("Remove Kern _Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Remove Kern Pairs|No Shortcut"), NULL, NULL, CVMenuRemoveKern, MID_RemoveKerns },
12155     { { (unichar_t *) N_("Remove VKern Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Remove VKern Pairs|No Shortcut"), NULL, NULL, CVMenuRemoveVKern, MID_RemoveVKerns },
12156     { { (unichar_t *) N_("Kern Pair Closeup..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Kern Pair Closeup...|No Shortcut"), NULL, NULL, CVMenuKPCloseup, MID_KPCloseup },
12157     GMENUITEM2_EMPTY
12158 };
12159 
12160 static GMenuItem2 pllist[] = {
12161     { { (unichar_t *) N_("_Tools"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Tools|No Shortcut"), NULL, NULL, CVMenuPaletteShow, MID_Tools },
12162     { { (unichar_t *) N_("_Layers"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'L' }, H_("Layers|No Shortcut"), NULL, NULL, CVMenuPaletteShow, MID_Layers },
12163     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12164     { { (unichar_t *) N_("_Docked Palettes"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'D' }, H_("Docked Palettes|No Shortcut"), NULL, NULL, CVMenuPalettesDock, MID_DockPalettes },
12165     GMENUITEM2_EMPTY
12166 };
12167 
12168 static GMenuItem2 aplist[] = {
12169     { { (unichar_t *) N_("_Detach"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'K' }, H_("Detach|No Shortcut"), NULL, NULL, CVMenuAPDetach, 0 },
12170     GMENUITEM2_EMPTY
12171 };
12172 
aplistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12173 static void aplistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12174     CharView *cv = (CharView *) GDrawGetUserData(gw);
12175     SplineChar *sc = cv->b.sc, **glyphs;
12176     SplineFont *sf = sc->parent;
12177     AnchorPoint *ap, *found;
12178     GMenuItem2 *mit;
12179     int cnt;
12180 
12181     found = NULL;
12182     for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) {
12183 	if ( ap->selected ) {
12184 	    if ( found==NULL )
12185 		found = ap;
12186 	    else {
12187 		/* Can't deal with two selected anchors */
12188 		found = NULL;
12189     break;
12190 	    }
12191 	}
12192     }
12193 
12194     GMenuItemArrayFree(mi->sub);
12195     if ( found==NULL )
12196 	glyphs = NULL;
12197     else
12198 	glyphs = GlyphsMatchingAP(sf,found);
12199     if ( glyphs==NULL ) {
12200 	mi->sub = GMenuItem2ArrayCopy(aplist,NULL);
12201 	mi->sub->ti.disabled = (cv->apmine==NULL);
12202 return;
12203     }
12204 
12205     for ( cnt = 0; glyphs[cnt]!=NULL; ++cnt );
12206     mit = calloc(cnt+2,sizeof(GMenuItem2));
12207     mit[0] = aplist[0];
12208     mit[0].ti.text = (unichar_t *) copy( (char *) mit[0].ti.text );
12209     mit[0].ti.disabled = (cv->apmine==NULL);
12210     for ( cnt = 0; glyphs[cnt]!=NULL; ++cnt ) {
12211 	mit[cnt+1].ti.text = (unichar_t *) copy(glyphs[cnt]->name);
12212 	mit[cnt+1].ti.text_is_1byte = true;
12213 	mit[cnt+1].ti.fg = mit[cnt+1].ti.bg = COLOR_DEFAULT;
12214 	mit[cnt+1].ti.userdata = glyphs[cnt];
12215 	mit[cnt+1].invoke = CVMenuAPAttachSC;
12216 	if ( glyphs[cnt]==cv->apsc )
12217 	    mit[cnt+1].ti.checked = mit[cnt+1].ti.checkable = true;
12218     }
12219     free(glyphs);
12220     mi->sub = GMenuItem2ArrayCopy(mit,NULL);
12221     GMenuItem2ArrayFree(mit);
12222 }
12223 
CVMoveInWordListByOffset(CharView * cv,int offset)12224 static void CVMoveInWordListByOffset( CharView* cv, int offset )
12225 {
12226     Wordlist_MoveByOffset( cv->charselector, &cv->charselectoridx, offset );
12227 }
12228 
CVMenuNextLineInWordList(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12229 static void CVMenuNextLineInWordList(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12230     CharView* cv = (CharView*) GDrawGetUserData(gw);
12231     CVMoveInWordListByOffset( cv, 1 );
12232 }
CVMenuPrevLineInWordList(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12233 static void CVMenuPrevLineInWordList(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12234     CharView* cv = (CharView*) GDrawGetUserData(gw);
12235     CVMoveInWordListByOffset( cv, -1 );
12236 }
12237 
12238 static GMenuItem2 cblist[] = {
12239     { { (unichar_t *) N_("_Kern Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'K' }, H_("Kern Pairs|No Shortcut"), NULL, NULL, CVMenuKernPairs, MID_KernPairs },
12240     { { (unichar_t *) N_("_Anchored Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Anchored Pairs|No Shortcut"), NULL, NULL, CVMenuAnchorPairs, MID_AnchorPairs },
12241     { { (unichar_t *) N_("_Anchor Control..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Anchor Control...|No Shortcut"), ap2list, ap2listbuild, NULL, MID_AnchorControl },
12242     { { (unichar_t *) N_("Anchor _Glyph at Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Anchor Glyph at Point|No Shortcut"), aplist, aplistcheck, NULL, MID_AnchorGlyph },
12243     { { (unichar_t *) N_("_Ligatures"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'L' }, H_("Ligatures|No Shortcut"), NULL, NULL, CVMenuLigatures, MID_Ligatures },
12244     GMENUITEM2_EMPTY
12245 };
12246 
12247 static GMenuItem2 nplist[] = {
12248     { { (unichar_t *) N_("PointNumbers|_None"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'K' }, H_("None|No Shortcut"), NULL, NULL, CVMenuNumberPoints, MID_PtsNone },
12249     { { (unichar_t *) N_("_TrueType"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'A' }, H_("TrueType|No Shortcut"), NULL, NULL, CVMenuNumberPoints, MID_PtsTrue },
12250     { { (unichar_t *) NU_("_PostScript®"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'L' }, H_("PostScript|No Shortcut"), NULL, NULL, CVMenuNumberPoints, MID_PtsPost },
12251     { { (unichar_t *) N_("_SVG"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'L' }, H_("SVG|No Shortcut"), NULL, NULL, CVMenuNumberPoints, MID_PtsSVG },
12252     { { (unichar_t *) N_("P_ositions"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'L' }, H_("Positions|No Shortcut"), NULL, NULL, CVMenuNumberPoints, MID_PtsPos },
12253     GMENUITEM2_EMPTY
12254 };
12255 
12256 static GMenuItem2 gflist[] = {
12257     { { (unichar_t *) N_("Show _Grid Fit..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'l' }, H_("Show Grid Fit...|No Shortcut"), NULL, NULL, CVMenuShowGridFit, MID_ShowGridFit },
12258     { { (unichar_t *) N_("Show _Grid Fit (Live Update)..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'l' }, H_("Show Grid Fit (Live Update)...|No Shortcut"), NULL, NULL, CVMenuShowGridFitLiveUpdate, MID_ShowGridFitLiveUpdate },
12259     { { (unichar_t *) N_("_Bigger Point Size"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Bigger Point Size|No Shortcut"), NULL, NULL, CVMenuChangePointSize, MID_Bigger },
12260     { { (unichar_t *) N_("_Smaller Point Size"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Smaller Point Size|No Shortcut"), NULL, NULL, CVMenuChangePointSize, MID_Smaller },
12261     { { (unichar_t *) N_("_Anti Alias"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'L' }, H_("Anti Alias|No Shortcut"), NULL, NULL, CVMenuChangePointSize, MID_GridFitAA },
12262     { { (unichar_t *) N_("_Off"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Off|No Shortcut"), NULL, NULL, CVMenuChangePointSize, MID_GridFitOff },
12263     GMENUITEM2_EMPTY
12264 };
12265 
12266 static GMenuItem2 swlist[] = {
12267     { { (unichar_t *) N_("_Points"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("Points|No Shortcut"), NULL, NULL, CVMenuShowHide, MID_HidePoints },
12268     { { (unichar_t *) N_("Control Points (Always_)"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, ')' }, H_("Control Points (Always)|No Shortcut"), NULL, NULL, CVMenuShowHideControlPoints, MID_HideControlPoints },
12269     { { (unichar_t *) N_("_Control Point Info"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Control Point Info|No Shortcut"), NULL, NULL, CVMenuShowCPInfo, MID_ShowCPInfo },
12270     { { (unichar_t *) N_("_Extrema"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Extrema|No Shortcut"), NULL, NULL, CVMenuMarkExtrema, MID_MarkExtrema },
12271     { { (unichar_t *) N_("Points of _Inflection"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Points of Inflection|No Shortcut"), NULL, NULL, CVMenuMarkPointsOfInflection, MID_MarkPointsOfInflection },
12272     { { (unichar_t *) N_("Almost Horizontal/Vertical Lines"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Almost Horizontal/Vertical Lines|No Shortcut"), NULL, NULL, CVMenuShowAlmostHV, MID_ShowAlmostHV },
12273     { { (unichar_t *) N_("Almost Horizontal/Vertical Curves"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Almost Horizontal/Vertical Curves|No Shortcut"), NULL, NULL, CVMenuShowAlmostHVCurves, MID_ShowAlmostHVCurves },
12274     { { (unichar_t *) N_("(Define \"Almost\")"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("(Define \"Almost\")|No Shortcut"), NULL, NULL, CVMenuDefineAlmost, MID_DefineAlmost },
12275     { { (unichar_t *) N_("_Side Bearings"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Side Bearings|No Shortcut"), NULL, NULL, CVMenuShowSideBearings, MID_ShowSideBearings },
12276     { { (unichar_t *) N_("Reference Names"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'M' }, H_("Reference Names|No Shortcut"), NULL, NULL, CVMenuShowRefNames, MID_ShowRefNames },
12277     { { (unichar_t *) N_("_Fill"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'l' }, H_("Fill|No Shortcut"), NULL, NULL, CVMenuFill, MID_Fill },
12278     { { (unichar_t *) N_("Previe_w"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'l' }, H_("Preview|No Shortcut"), NULL, NULL, CVMenuPreview, MID_Preview },
12279     { { (unichar_t *) N_("Dragging Comparison Outline"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'l' }, H_("Dragging Comparison Outline|No Shortcut"), NULL, NULL, CVMenuDraggingComparisonOutline, MID_DraggingComparisonOutline },
12280     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12281     { { (unichar_t *) N_("Pale_ttes"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Palettes|No Shortcut"), pllist, pllistcheck, NULL, 0 },
12282     { { (unichar_t *) N_("_Glyph Tabs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Glyph Tabs|No Shortcut"), NULL, NULL, CVMenuShowTabs, MID_ShowTabs },
12283     { { (unichar_t *) N_("_Rulers"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Rulers|No Shortcut"), NULL, NULL, CVMenuShowHideRulers, MID_HideRulers },
12284     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12285     { { (unichar_t *) N_("_Horizontal Hints"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Horizontal Hints|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowHHints },
12286     { { (unichar_t *) N_("_Vertical Hints"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Vertical Hints|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowVHints },
12287     { { (unichar_t *) N_("_Diagonal Hints"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Diagonal Hints|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowDHints },
12288 /* GT: You might not want to translate this, it's a keyword in PostScript font files */
12289     { { (unichar_t *) N_("_BlueValues"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("BlueValues|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowBlueValues },
12290 /* GT: You might not want to translate this, it's a keyword in PostScript font files */
12291     { { (unichar_t *) N_("FamilyBl_ues"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("FamilyBlues|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowFamilyBlues },
12292     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12293     { { (unichar_t *) N_("_Anchors"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Anchors|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowAnchors },
12294     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12295     { { (unichar_t *) N_("Debug Raster Cha_nges"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Debug Raster Changes|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowDebugChanges },
12296     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12297     { { (unichar_t *) N_("Hori_zontal Metric Lines"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Horizontal Metric Lines|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowHMetrics },
12298     { { (unichar_t *) N_("Vertical _Metric Lines"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Vertical Metric Lines|No Shortcut"), NULL, NULL, CVMenuShowHints, MID_ShowVMetrics },
12299     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12300     { { (unichar_t *) N_("Snap Outlines to Pi_xel Grid"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'R' }, H_("Snap Outlines to Pixel Grid|No Shortcut"), NULL, NULL, CVMenuSnapOutlines, MID_SnapOutlines },
12301     GMENUITEM2_EMPTY
12302 };
12303 
12304 static GMenuItem2 vwlist[] = {
12305     { { (unichar_t *) N_("_Fit"), (GImage *) "viewfit.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Fit|No Shortcut"), NULL, NULL, CVMenuScale, MID_Fit },
12306     { { (unichar_t *) N_("Z_oom out"), (GImage *) "viewzoomout.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Zoom out|No Shortcut"), NULL, NULL, CVMenuScale, MID_ZoomOut },
12307     { { (unichar_t *) N_("Zoom _in"), (GImage *) "viewzoomin.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'i' }, H_("Zoom in|No Shortcut"), NULL, NULL, CVMenuScale, MID_ZoomIn },
12308 #if HANYANG
12309     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12310     { { (unichar_t *) N_("_Display Compositions..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'i' }, H_("Display Compositions...|No Shortcut"), NULL, NULL, CVDisplayCompositions, MID_DisplayCompositions },
12311 #endif
12312     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12313     { { (unichar_t *) N_("_Next Glyph"), (GImage *) "viewnext.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, H_("Next Glyph|No Shortcut"), NULL, NULL, CVMenuChangeChar, MID_Next },
12314     { { (unichar_t *) N_("_Prev Glyph"), (GImage *) "viewprev.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Prev Glyph|No Shortcut"), NULL, NULL, CVMenuChangeChar, MID_Prev },
12315     { { (unichar_t *) N_("Next _Defined Glyph"), (GImage *) "viewnextdef.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Next Defined Glyph|No Shortcut"), NULL, NULL, CVMenuChangeChar, MID_NextDef },
12316     { { (unichar_t *) N_("Prev Defined Gl_yph"), (GImage *) "viewprevdef.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'a' }, H_("Prev Defined Glyph|No Shortcut"), NULL, NULL, CVMenuChangeChar, MID_PrevDef },
12317     { { (unichar_t *) N_("Form_er Glyph"), (GImage *) "viewformer.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'a' }, H_("Former Glyph|No Shortcut"), NULL, NULL, CVMenuChangeChar, MID_Former },
12318     { { (unichar_t *) N_("_Goto"), (GImage *) "viewgoto.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'G' }, H_("Goto|No Shortcut"), NULL, NULL, CVMenuGotoChar, MID_Goto },
12319     { { (unichar_t *) N_("Find In Font _View"), (GImage *) "viewfindinfont.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Find In Font View|No Shortcut"), NULL, NULL, CVMenuFindInFontView, MID_FindInFontView },
12320     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12321     { { (unichar_t *) N_("N_umber Points"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Number Points|No Shortcut"), nplist, nplistcheck, NULL, 0 },
12322     { { (unichar_t *) N_("Grid Fi_t"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Grid Fit|No Shortcut"), gflist, gflistcheck, NULL, MID_ShowGridFit },
12323     { { (unichar_t *) N_("Sho_w"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Show|No Shortcut"), swlist, swlistcheck, NULL, 0 },
12324     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12325     { { (unichar_t *) N_("Com_binations"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'b' }, H_("Combinations|No Shortcut"), cblist, cblistcheck, NULL, 0 },
12326     GMENUITEM2_LINE,
12327     { { (unichar_t *) N_("Next _Line in Word List"),     NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'L' }, H_("Next Line in Word List|No Shortcut"), NULL, NULL, CVMenuNextLineInWordList, MID_NextLineInWordList },
12328     { { (unichar_t *) N_("Previous Line in _Word List"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Previous Line in Word List|No Shortcut"), NULL, NULL, CVMenuPrevLineInWordList, MID_PrevLineInWordList },
12329     GMENUITEM2_EMPTY
12330 };
12331 
CVMenuShowMMMask(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12332 static void CVMenuShowMMMask(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12333     CharView *cv = (CharView *) GDrawGetUserData(gw);
12334     uint32 changemask = (uint32) (intpt) mi->ti.userdata;
12335     /* Change which mms get displayed in the "background" */
12336 
12337     if ( mi->mid==MID_MMAll ) {
12338 	if ( (cv->mmvisible&changemask)==changemask ) cv->mmvisible = 0;
12339 	else cv->mmvisible = changemask;
12340     } else if ( mi->mid == MID_MMNone ) {
12341 	if ( cv->mmvisible==0 ) cv->mmvisible = (1<<(cv->b.sc->parent->mm->instance_count+1))-1;
12342 	else cv->mmvisible = 0;
12343     } else
12344 	cv->mmvisible ^= changemask;
12345     GDrawRequestExpose(cv->v,NULL,false);
12346 }
12347 
12348 static GMenuItem2 mvlist[] = {
12349     { { (unichar_t *) N_("SubFonts|_All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, (void *) 0xffffffff, NULL, 0, 0, 1, 0, 0, 0, 1, 1, 0, '\0' }, H_("All|No Shortcut"), NULL, NULL, CVMenuShowMMMask, MID_MMAll },
12350     { { (unichar_t *) N_("SubFonts|_None"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, (void *) 0, NULL, 0, 0, 1, 0, 0, 0, 1, 1, 0, '\0' }, H_("None|No Shortcut"), NULL, NULL, CVMenuShowMMMask, MID_MMNone },
12351     GMENUITEM2_EMPTY
12352 };
12353 
mvlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12354 static void mvlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12355     CharView *cv = (CharView *) GDrawGetUserData(gw);
12356     int i, base, j;
12357     MMSet *mm = cv->b.sc->parent->mm;
12358     uint32 submask;
12359     SplineFont *sub;
12360     GMenuItem2 *mml;
12361 
12362     base = 3;
12363     if ( mm==NULL )
12364 	mml = mvlist;
12365     else {
12366 	mml = calloc(base+mm->instance_count+2,sizeof(GMenuItem2));
12367 	memcpy(mml,mvlist,sizeof(mvlist));
12368 	mml[base-1].ti.fg = mml[base-1].ti.bg = COLOR_DEFAULT;
12369 	mml[base-1].ti.line = true;
12370 	submask = 0;
12371 	for ( j = 0, i=base; j<mm->instance_count+1; ++i, ++j ) {
12372 	    if ( j==0 )
12373 		sub = mm->normal;
12374 	    else
12375 		sub = mm->instances[j-1];
12376 	    mml[i].ti.text = uc_copy(sub->fontname);
12377 	    mml[i].ti.checkable = true;
12378 	    mml[i].ti.checked = (cv->mmvisible & (1<<j))?1:0;
12379 	    mml[i].ti.userdata = (void *) (intpt) (1<<j);
12380 	    mml[i].invoke = CVMenuShowMMMask;
12381 	    mml[i].ti.fg = mml[i].ti.bg = COLOR_DEFAULT;
12382 	    if ( sub==cv->b.sc->parent )
12383 		submask = (1<<j);
12384 	}
12385 	/* All */
12386 	mml[0].ti.userdata = (void *) (intpt) ((1<<j)-1);
12387 	mml[0].ti.checked = (cv->mmvisible == (uint32) (intpt) mml[0].ti.userdata);
12388 	    /* None */
12389 	mml[1].ti.checked = (cv->mmvisible == 0 || cv->mmvisible == submask);
12390     }
12391     GMenuItemArrayFree(mi->sub);
12392     mi->sub = GMenuItem2ArrayCopy(mml,NULL);
12393     if ( mml!=mvlist ) {
12394 	for ( i=base; mml[i].ti.text!=NULL; ++i )
12395 	    free( mml[i].ti.text);
12396 	free(mml);
12397     }
12398 }
12399 
CVMenuReblend(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))12400 static void CVMenuReblend(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
12401     CharView *cv = (CharView *) GDrawGetUserData(gw);
12402     char *err;
12403     MMSet *mm = cv->b.sc->parent->mm;
12404 
12405     if ( mm==NULL || mm->apple || cv->b.sc->parent!=mm->normal )
12406 return;
12407     err = MMBlendChar(mm,cv->b.sc->orig_pos);
12408     if ( mm->normal->glyphs[cv->b.sc->orig_pos]!=NULL )
12409 	_SCCharChangedUpdate(mm->normal->glyphs[cv->b.sc->orig_pos],CVLayer((CharViewBase *)cv->b.sc),-1);
12410     if ( err!=0 )
12411 	ff_post_error(_("Bad Multiple Master Font"),err);
12412 }
12413 
12414 static GMenuItem2 mmlist[] = {
12415 /* GT: Here (and following) MM means "MultiMaster" */
12416     { { (unichar_t *) N_("MM _Reblend"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("MM Reblend|No Shortcut"), NULL, NULL, CVMenuReblend, MID_MMReblend },
12417     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
12418     { { (unichar_t *) N_("_View"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, NULL, mvlist, mvlistcheck, NULL, 0 },
12419     GMENUITEM2_EMPTY
12420 };
12421 
CVMenuShowSubChar(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12422 static void CVMenuShowSubChar(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12423     CharView *cv = (CharView *) GDrawGetUserData(gw);
12424     SplineFont *new = mi->ti.userdata;
12425     /* Change to the same char in a different instance font of the mm */
12426 
12427     CVChangeSC(cv,SFMakeChar(new,cv->b.fv->map,CVCurEnc(cv)));
12428     cv->b.layerheads[dm_grid] = &new->grid;
12429 }
12430 
mmlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))12431 static void mmlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
12432     CharView *cv = (CharView *) GDrawGetUserData(gw);
12433     int i, base, j;
12434     MMSet *mm = cv->b.sc->parent->mm;
12435     SplineFont *sub;
12436     GMenuItem2 *mml;
12437 
12438     base = sizeof(mmlist)/sizeof(mmlist[0]);
12439     if ( mm==NULL )
12440 	mml = mmlist;
12441     else {
12442 	mml = calloc(base+mm->instance_count+2,sizeof(GMenuItem2));
12443 	memcpy(mml,mmlist,sizeof(mmlist));
12444 	mml[base-1].ti.fg = mml[base-1].ti.bg = COLOR_DEFAULT;
12445 	mml[base-1].ti.line = true;
12446 	for ( j = 0, i=base; j<mm->instance_count+1; ++i, ++j ) {
12447 	    if ( j==0 )
12448 		sub = mm->normal;
12449 	    else
12450 		sub = mm->instances[j-1];
12451 	    mml[i].ti.text = uc_copy(sub->fontname);
12452 	    mml[i].ti.checkable = true;
12453 	    mml[i].ti.checked = sub==cv->b.sc->parent;
12454 	    mml[i].ti.userdata = sub;
12455 	    mml[i].invoke = CVMenuShowSubChar;
12456 	    mml[i].ti.fg = mml[i].ti.bg = COLOR_DEFAULT;
12457 	}
12458     }
12459     mml[0].ti.disabled = (mm==NULL || cv->b.sc->parent!=mm->normal || mm->apple);
12460     GMenuItemArrayFree(mi->sub);
12461     mi->sub = GMenuItem2ArrayCopy(mml,NULL);
12462     if ( mml!=mmlist ) {
12463 	for ( i=base; mml[i].ti.text!=NULL; ++i )
12464 	    free( mml[i].ti.text);
12465 	free(mml);
12466     }
12467 }
12468 
CVMenuContextualHelp(GWindow UNUSED (gw),struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))12469 static void CVMenuContextualHelp(GWindow UNUSED(gw), struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
12470     help("ui/mainviews/charview.html", NULL);
12471 }
12472 
12473 static GMenuItem2 mblist[] = {
12474     { { (unichar_t *) N_("_File"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("File|No Shortcut"), fllist, fllistcheck, NULL, 0 },
12475     { { (unichar_t *) N_("_Edit"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'E' }, H_("Edit|No Shortcut"), edlist, edlistcheck, NULL, 0 },
12476     { { (unichar_t *) N_("_Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Point|No Shortcut"), ptlist, ptlistcheck, NULL, 0 },
12477     { { (unichar_t *) N_("E_lement"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Element|No Shortcut"), ellist, ellistcheck, NULL, 0 },
12478 #ifndef _NO_PYTHON
12479     { { (unichar_t *) N_("_Tools"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Tools|No Shortcut"), NULL, cvpy_tllistcheck, NULL, 0 },
12480 #endif
12481 #ifdef NATIVE_CALLBACKS
12482     { { (unichar_t *) N_("Tools_2"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Tools2|No Shortcut"), NULL, cv_tl2listcheck, NULL, 0},
12483 #endif
12484     { { (unichar_t *) N_("H_ints"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Hints|No Shortcut"), htlist, htlistcheck, NULL, 0 },
12485     { { (unichar_t *) N_("_View"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("View|No Shortcut"), vwlist, vwlistcheck, NULL, 0 },
12486     { { (unichar_t *) N_("_Metrics"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Metrics|No Shortcut"), mtlist, mtlistcheck, NULL, 0 },
12487 /* GT: Here (and following) MM means "MultiMaster" */
12488     { { (unichar_t *) N_("MM"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("MM|No Shortcut"), mmlist, mmlistcheck, NULL, 0 },
12489     { { (unichar_t *) N_("_Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Window|No Shortcut"), wnmenu, CVWindowMenuBuild, NULL, 0 },
12490     { { (unichar_t *) N_("_Help"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Help|No Shortcut"), helplist, NULL, NULL, 0 },
12491     GMENUITEM2_EMPTY
12492 };
12493 
12494 static GMenuItem2 mblist_nomm[] = {
12495     { { (unichar_t *) N_("_File"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("File|No Shortcut"), fllist, fllistcheck, NULL, 0 },
12496     { { (unichar_t *) N_("_Edit"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'E' }, H_("Edit|No Shortcut"), edlist, edlistcheck, NULL, 0 },
12497     { { (unichar_t *) N_("_Point"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Point|No Shortcut"), ptlist, ptlistcheck, NULL, 0 },
12498     { { (unichar_t *) N_("E_lement"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Element|No Shortcut"), ellist, ellistcheck, NULL, 0 },
12499 #ifndef _NO_PYTHON
12500     { { (unichar_t *) N_("_Tools"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 0, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Tools|No Shortcut"), NULL, cvpy_tllistcheck, NULL, 0 },
12501 #endif
12502 #ifdef NATIVE_CALLBACKS
12503     { { (unichar_t *) N_("Tools_2"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 0, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Tools2|No Shortcut"), NULL, cv_tl2listcheck, NULL, 0},
12504 #endif
12505     { { (unichar_t *) N_("H_ints"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Hints|No Shortcut"), htlist, htlistcheck, NULL, 0 },
12506     { { (unichar_t *) N_("_View"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("View|No Shortcut"), vwlist, vwlistcheck, NULL, 0 },
12507     { { (unichar_t *) N_("_Metrics"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Metrics|No Shortcut"), mtlist, mtlistcheck, NULL, 0 },
12508     { { (unichar_t *) N_("_Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Window|No Shortcut"), wnmenu, CVWindowMenuBuild, NULL, 0 },
12509     { { (unichar_t *) N_("_Help"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Help|No Shortcut"), helplist, NULL, NULL, 0 },
12510     GMENUITEM2_EMPTY
12511 };
12512 
_CharViewCreate(CharView * cv,SplineChar * sc,FontView * fv,int enc,int show)12513 static void _CharViewCreate(CharView *cv, SplineChar *sc, FontView *fv,int enc,int show) {
12514     CharViewTab* tab = CVGetActiveTab(cv);
12515     GRect pos;
12516     GWindowAttrs wattrs;
12517     GGadgetData gd;
12518     int sbsize;
12519     FontRequest rq;
12520     int as, ds, ld;
12521     extern int updateflex;
12522     static char *infofamily=NULL;
12523     GTextBounds textbounds;
12524     /* extern int cv_auto_goto; */
12525     extern enum cvtools cv_b1_tool, cv_cb1_tool, cv_b2_tool, cv_cb2_tool;
12526 
12527     if ( !cvcolsinited )
12528 	CVColInit();
12529 
12530     static int firstCharView = 1;
12531     if( firstCharView )
12532     {
12533 	firstCharView = 0;
12534 	CVShows.alwaysshowcontrolpoints = prefs_cv_show_control_points_always_initially;
12535     }
12536 
12537     cv->b.sc = sc;
12538     tab->scale = .5;
12539     tab->xoff = tab->yoff = 20;
12540     cv->b.next = sc->views;
12541     sc->views = &cv->b;
12542     cv->b.fv = &fv->b;
12543     cv->map_of_enc = fv->b.map;
12544     cv->enc = enc;
12545     cv->p.pretransform_spl = NULL;
12546     cv->b.drawmode = dm_fore;
12547 
12548     memset(cv->showback,-1,sizeof(cv->showback));
12549     if ( !CVShows.showback )
12550 	cv->showback[0] &= ~1;
12551     cv->showfore = CVShows.showfore;
12552     cv->showgrids = CVShows.showgrids;
12553     cv->showhhints = CVShows.showhhints;
12554     cv->showvhints = CVShows.showvhints;
12555     cv->showdhints = CVShows.showdhints;
12556     cv->showpoints = CVShows.showpoints;
12557     cv->alwaysshowcontrolpoints = CVShows.alwaysshowcontrolpoints;
12558     cv->showrulers = CVShows.showrulers;
12559     cv->showfilled = CVShows.showfilled;
12560     cv->showrounds = CVShows.showrounds;
12561     cv->showmdx = CVShows.showmdx;
12562     cv->showmdy = CVShows.showmdy;
12563     cv->showhmetrics = CVShows.showhmetrics;
12564     cv->showvmetrics = sc->parent->hasvmetrics ? CVShows.showvmetrics : 0;
12565     cv->markextrema = CVShows.markextrema;
12566     cv->showsidebearings = CVShows.showsidebearings;
12567     cv->showrefnames = CVShows.showrefnames;
12568     cv->snapoutlines = CVShows.snapoutlines;
12569     cv->markpoi = CVShows.markpoi;
12570     cv->showalmosthvlines = CVShows.showalmosthvlines;
12571     cv->showalmosthvcurves = CVShows.showalmosthvcurves;
12572     cv->hvoffset = CVShows.hvoffset;
12573     cv->showblues = CVShows.showblues;
12574     cv->showfamilyblues = CVShows.showfamilyblues;
12575     cv->showanchor = CVShows.showanchor;
12576     cv->showcpinfo = CVShows.showcpinfo;
12577     cv->showtabs = CVShows.showtabs;
12578     cv->inPreviewMode = 0;
12579     cv->checkselfintersects = CVShows.checkselfintersects;
12580 
12581     cv->showdebugchanges = CVShows.showdebugchanges;
12582 
12583     cv->infoh = 13;
12584 #if defined(__MINGW32__)||defined(__CYGWIN__)
12585     cv->infoh = 26;
12586 #endif
12587     cv->rulerh = 16;
12588 
12589     GDrawGetSize(cv->gw,&pos);
12590     memset(&gd,0,sizeof(gd));
12591     gd.pos.y = cv->mbh+cv->charselectorh+cv->infoh;
12592     gd.pos.width = sbsize = GDrawPointsToPixels(cv->gw,_GScrollBar_Width);
12593     gd.pos.height = pos.height-cv->mbh-cv->charselectorh-cv->infoh - sbsize;
12594     gd.pos.x = pos.width-sbsize;
12595     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
12596     cv->vsb = GScrollBarCreate(cv->gw,&gd,cv);
12597 
12598     gd.pos.y = pos.height-sbsize; gd.pos.height = sbsize;
12599     gd.pos.width = pos.width - sbsize;
12600     gd.pos.x = 0;
12601     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_text_xim;
12602     cv->hsb = GScrollBarCreate(cv->gw,&gd,cv);
12603 
12604     GDrawGetSize(cv->gw,&pos);
12605     pos.y = cv->mbh+cv->charselectorh+cv->infoh; pos.height -= cv->mbh + cv->charselectorh + sbsize + cv->infoh;
12606     pos.x = 0; pos.width -= sbsize;
12607     if ( cv->showrulers ) {
12608 	pos.y += cv->rulerh; pos.height -= cv->rulerh;
12609 	pos.x += cv->rulerh; pos.width -= cv->rulerh;
12610     }
12611     memset(&wattrs,0,sizeof(wattrs));
12612     wattrs.mask = wam_events|wam_cursor|wam_backcol;
12613     wattrs.background_color = view_bgcol;
12614     wattrs.event_masks = -1;
12615     wattrs.cursor = ct_mypointer;
12616     cv->v = GWidgetCreateSubWindow(cv->gw,&pos,v_e_h,cv,&wattrs);
12617     GDrawSetWindowTypeName(cv->v, "CharView");
12618 
12619     if ( GDrawRequestDeviceEvents(cv->v,input_em_cnt,input_em)>0 ) {
12620 	/* Success! They've got a wacom tablet */
12621     }
12622 
12623     if ( infofamily==NULL ) {
12624 	infofamily = copy(GResourceFindString("CharView.InfoFamily"));
12625 	/* FontConfig doesn't have access to all the X11 bitmap fonts */
12626 	/*  so the font I used to use isn't found, and a huge monster is */
12627 	/*  inserted instead */
12628 	if ( infofamily==NULL )
12629 	    infofamily = SANS_UI_FAMILIES;
12630     }
12631 
12632     memset(&rq,0,sizeof(rq));
12633     rq.utf8_family_name = infofamily;
12634     rq.point_size = GResourceFindInt("CharView.Rulers.FontSize", -10);
12635     rq.weight = 400;
12636     cv->small = GDrawInstanciateFont(cv->gw,&rq);
12637     GDrawWindowFontMetrics(cv->gw,cv->small,&as,&ds,&ld);
12638     cv->sfh = as+ds; cv->sas = as;
12639     GDrawSetFont(cv->gw,cv->small);
12640     GDrawGetText8Bounds(cv->gw,"0123456789",10,&textbounds);
12641     cv->sdh = textbounds.as+textbounds.ds+1;
12642     rq.point_size = 10;
12643     cv->normal = GDrawInstanciateFont(cv->gw,&rq);
12644     GDrawWindowFontMetrics(cv->gw,cv->normal,&as,&ds,&ld);
12645     cv->nfh = as+ds; cv->nas = as;
12646 
12647     cv->height = pos.height; cv->width = pos.width;
12648     cv->gi.u.image = calloc(1,sizeof(struct _GImage));
12649     cv->gi.u.image->image_type = it_mono;
12650     cv->gi.u.image->clut = calloc(1,sizeof(GClut));
12651     cv->gi.u.image->clut->trans_index = cv->gi.u.image->trans = 0;
12652     cv->gi.u.image->clut->clut_len = 2;
12653     cv->gi.u.image->clut->clut[0] = view_bgcol;
12654     cv->gi.u.image->clut->clut[1] = fillcol;
12655     cv->b1_tool = cv_b1_tool; cv->cb1_tool = cv_cb1_tool;
12656     cv->b1_tool_old = cv->b1_tool;
12657     cv->b2_tool = cv_b2_tool; cv->cb2_tool = cv_cb2_tool;
12658     cv->s1_tool = cvt_freehand; cv->s2_tool = cvt_pen;
12659     cv->er_tool = cvt_knife;
12660     cv->showing_tool = cvt_pointer;
12661     cv->pressed_tool = cv->pressed_display = cv->active_tool = cvt_none;
12662     cv->spacebar_hold = 0;
12663     cv->b.layerheads[dm_fore] = &sc->layers[ly_fore];
12664     cv->b.layerheads[dm_back] = &sc->layers[ly_back];
12665     cv->b.layerheads[dm_grid] = &fv->b.sf->grid;
12666     if ( !sc->parent->multilayer && fv->b.active_layer!=ly_fore ) {
12667 	cv->b.layerheads[dm_back] = &sc->layers[fv->b.active_layer];
12668 	cv->b.drawmode = dm_back;
12669     }
12670 
12671 #if HANYANG
12672     if ( sc->parent->rules!=NULL && sc->compositionunit )
12673 	Disp_DefaultTemplate(cv);
12674 #endif
12675 
12676     cv->olde.x = -1;
12677 
12678     cv->ft_dpi = 72; cv->ft_pointsizex = cv->ft_pointsizey = 12.0;
12679     cv->ft_ppemx = cv->ft_ppemy = 12;
12680 
12681     /*GWidgetHidePalettes();*/
12682     /*cv->tools = CVMakeTools(cv);*/
12683     /*cv->layers = CVMakeLayers(cv);*/
12684 
12685     CVFit(cv);
12686     GDrawSetVisible(cv->v,true);
12687     GWindowClearFocusGadgetOfWindow(cv->v);
12688 
12689     /*if ( cv_auto_goto )*/		/* Chinese input method steals hot key key-strokes */
12690 	/* But if we don't do this, then people can't type menu short-cuts */
12691 	cv->gic   = GDrawCreateInputContext(cv->v,gic_root|gic_orlesser);
12692 	GDrawSetGIC(cv->v,cv->gic,0,20);
12693 	cv->gwgic = GDrawCreateInputContext(cv->gw,gic_root|gic_orlesser);
12694 	GDrawSetGIC(cv->gw,cv->gwgic,0,20);
12695 	if( show )
12696 	{
12697 	    GDrawSetVisible(cv->gw,true);
12698 	}
12699 
12700     if ( (CharView *) (sc->views)==NULL && updateflex )
12701 	SplineCharIsFlexible(sc,CVLayer((CharViewBase *) cv));
12702     if ( sc->inspiro && !hasspiro() && !sc->parent->complained_about_spiros ) {
12703 	sc->parent->complained_about_spiros = true;
12704 #ifdef _NO_LIBSPIRO
12705 	ff_post_error(_("You may not use spiros"),_("This glyph should display spiro points, but unfortunately this version of fontforge was not linked with the spiro library, so only normal bezier points will be displayed."));
12706 #else
12707 	ff_post_error(_("You may not use spiros"),_("This glyph should display spiro points, but unfortunately FontForge was unable to load libspiro, spiros are not available for use, and normal bezier points will be displayed instead."));
12708 #endif
12709     }
12710 
12711 }
12712 
DefaultY(GRect * pos)12713 void DefaultY(GRect *pos) {
12714     static int nexty=0;
12715     GRect size;
12716 
12717     GDrawGetSize(GDrawGetRoot(NULL),&size);
12718     if ( nexty!=0 ) {
12719 	FontView *fv;
12720 	int any=0, i;
12721 	BDFFont *bdf;
12722 	/* are there any open cv/bv windows? */
12723 	for ( fv = fv_list; fv!=NULL && !any; fv = (FontView *) (fv->b.next) ) {
12724 	    for ( i=0; i<fv->b.sf->glyphcnt; ++i ) if ( fv->b.sf->glyphs[i]!=NULL ) {
12725 		if ( fv->b.sf->glyphs[i]->views!=NULL ) {
12726 		    any = true;
12727 	    break;
12728 		}
12729 	    }
12730 	    for ( bdf = fv->b.sf->bitmaps; bdf!=NULL && !any; bdf=bdf->next ) {
12731 		for ( i=0; i<bdf->glyphcnt; ++i ) if ( bdf->glyphs[i]!=NULL ) {
12732 		    if ( bdf->glyphs[i]->views!=NULL ) {
12733 			any = true;
12734 		break;
12735 		    }
12736 		}
12737 	    }
12738 	}
12739 	if ( !any ) nexty = 0;
12740     }
12741     pos->y = nexty;
12742     nexty += 200;
12743     if ( nexty+pos->height > size.height )
12744 	nexty = 0;
12745 }
12746 
12747 static void CharViewInit(void);
12748 
CV_OnCharSelectorTextChanged(GGadget * g,GEvent * e)12749 static int CV_OnCharSelectorTextChanged( GGadget *g, GEvent *e )
12750 {
12751     CharView* cv = GGadgetGetUserData(g);
12752     CharViewTab* tab = CVGetActiveTab(cv);
12753     SplineChar *sc = cv->b.sc;
12754     SplineFont* sf = sc->parent;
12755 
12756     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged )
12757     {
12758 	int pos = e->u.control.u.tf_changed.from_pulldown;
12759 
12760 	if ( pos!=-1 )
12761 	{
12762 	    int32 len;
12763 	    GTextInfo **ti = GGadgetGetList(g,&len);
12764 	    GTextInfo *cur = ti[pos];
12765 	    int type = (intpt) cur->userdata;
12766 	    if ( type < 0 )
12767 	    {
12768 		TRACE("load wordlist...! pos:%d\n",pos);
12769 
12770 		WordlistLoadToGTextInfo( cv->charselector, &cv->charselectoridx );
12771 		return 0;
12772 	    }
12773 	}
12774 
12775 	cv->charselectoridx = pos;
12776 	char* txt = GGadgetGetTitle8( cv->charselector );
12777 	TRACE("char selector @%d changed to:%s\n", pos, txt );
12778 	{
12779 	    int tabnum = GTabSetGetSel(cv->tabs);
12780 	    CharViewTab* t = &cv->cvtabs[tabnum];
12781 	    strncpy( t->tablabeltxt, txt, charviewtab_charselectedsz );
12782 	    TRACE("tab num: %d set to %s\n", tabnum, t->tablabeltxt);
12783 	    GTabSetChangeTabName(cv->tabs,t->tablabeltxt,tabnum);
12784 	    GTabSetRemetric(cv->tabs);
12785 	    GTabSetSetSel(cv->tabs,tabnum);	/* This does a redraw */
12786 	}
12787 
12788 	memset( cv->additionalCharsToShow, 0, sizeof(SplineChar*) * additionalCharsToShowLimit );
12789 	cv->additionalCharsToShowActiveIndex = 0;
12790 	cv->additionalCharsToShow[0] = cv->b.sc;
12791 
12792 	int hadSelection = 0;
12793 	if( txt[0] == '\0' )
12794 	{
12795 	    CVSetCharSelectorValueFromSC( cv, cv->b.sc );
12796 	}
12797 	else if( strlen(txt) > 1 )
12798 	{
12799 	    int i=0;
12800 	    unichar_t *ret = GGadgetGetTitle( cv->charselector );
12801 	    WordListLine wll = WordlistEscapedInputStringToParsedData( sf, ret );
12802 	    WordListLine pt = wll;
12803 	    WordListLine ept = WordListLine_end(wll);
12804 	    WordListLine tpt = 0;
12805 	    for ( tpt=pt; tpt<ept; ++tpt )
12806 	    {
12807 		if( tpt == pt )
12808 		{
12809 		    // your own char at the leading of the text
12810 		    cv->additionalCharsToShow[i] = tpt->sc;
12811 		    i++;
12812 		    continue;
12813 		}
12814 
12815 		cv->additionalCharsToShow[i] = tpt->sc;
12816 
12817 		i++;
12818 		if( i >= additionalCharsToShowLimit )
12819 		    break;
12820 	    }
12821 	    free(ret);
12822 
12823 	    if( wll->sc )
12824 	    {
12825 		if( wll->isSelected )
12826 		{
12827 		    // first char selected, nothing to do!
12828 		}
12829 		else
12830 		{
12831 		    while( wll->sc && !(wll->isSelected))
12832 			wll++;
12833 		    if( wll->sc && wll->isSelected )
12834 		    {
12835 			SplineChar* xc = wll->sc;
12836 			if( xc )
12837 			{
12838 			    TRACE("selected v:%d xc:%s\n", wll->currentGlyphIndex, xc->name );
12839 			    int xoff = tab->xoff;
12840 			    CVSwitchActiveSC( cv, xc, wll->currentGlyphIndex );
12841 			    CVHScrollSetPos( cv, xoff );
12842 			    hadSelection = 1;
12843 			}
12844 
12845 		    }
12846 		}
12847 	    }
12848 	}
12849 	free(txt);
12850 
12851 	int i=0;
12852 	for( i=0; cv->additionalCharsToShow[i]; i++ )
12853 	{
12854 	    TRACE("i:%d %p .. ", i, cv->additionalCharsToShow[i] );
12855 	    TRACE(" %s\n", cv->additionalCharsToShow[i]->name );
12856 	}
12857 
12858 	if( !hadSelection )
12859 	    CVSwitchActiveSC( cv, 0, 0 );
12860 	GDrawRequestExpose(cv->v,NULL,false);
12861     }
12862     return( true );
12863 }
12864 
12865 GTextInfo cv_charselector_init[] = {
12866     { (unichar_t *) "", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 1, 0, 1, 0, 0, '\0'},
12867     { NULL, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0'},
12868     { (unichar_t *) N_("Load Word List..."), NULL, 0, 0, (void *) -1, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
12869 //    { (unichar_t *) N_("Load Glyph Name List..."), NULL, 0, 0, (void *) -2, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
12870     GTEXTINFO_EMPTY
12871 };
12872 
12873 
CharViewCreateExtended(SplineChar * sc,FontView * fv,int enc,int show)12874 CharView *CharViewCreateExtended(SplineChar *sc, FontView *fv,int enc, int show )
12875 {
12876     CharView *cv = calloc(1,sizeof(CharView));
12877     GWindowAttrs wattrs;
12878     GRect pos, zoom;
12879     GWindow gw;
12880     GGadgetData gd;
12881     GTabInfo aspects[2];
12882     GRect gsize;
12883     char buf[300];
12884     GTextInfo label[9];
12885 
12886     CharViewInit();
12887 
12888     cv->b.sc = sc;
12889     cv->b.fv = &fv->b;
12890     cv->enc = enc;
12891     cv->map_of_enc = fv->b.map;		/* I know this is done again in _CharViewCreate, but it needs to be done before creating the title */
12892 
12893     cv->infoh = 13;
12894 #if defined(__MINGW32__)||defined(__CYGWIN__)
12895     cv->infoh = 26;
12896 #endif
12897     cv->rulerh = 16;
12898 
12899     cv->ctpos = -1;
12900 
12901 
12902     SCLigCaretCheck(sc,false);
12903 
12904     memset(&wattrs,0,sizeof(wattrs));
12905     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_utf8_ititle;
12906     wattrs.event_masks = -1;
12907     wattrs.cursor = ct_mypointer;
12908     wattrs.utf8_icon_title = (const char*)CVMakeTitles(cv,buf,sizeof(buf));
12909     wattrs.utf8_window_title = buf;
12910     wattrs.icon = CharIcon(cv, fv);
12911     if ( wattrs.icon )
12912 	wattrs.mask |= wam_icon;
12913     pos.x = GGadgetScale(104)+6;
12914     pos.width = (cv_width > 0) ? cv_width : default_cv_width;
12915     pos.height = (cv_height > 0) ? cv_height : default_cv_height;
12916     DefaultY(&pos);
12917 
12918     cv->gw = gw = GDrawCreateTopWindow(NULL,&pos,cv_e_h,cv,&wattrs);
12919     free( (unichar_t *) wattrs.icon_title );
12920     free((char*)wattrs.utf8_icon_title);
12921     GDrawSetWindowTypeName(cv->gw, "CharView");
12922 
12923     // FIXME: cant do this until gw is shown?
12924     GTextBounds textbounds;
12925     GDrawGetText8Bounds(cv->gw,"0123456789hl",10,&textbounds);
12926     TRACE("XXXXXX as:%d  ds:%d\n",  textbounds.as, textbounds.ds );
12927     cv->charselectorh = textbounds.as+textbounds.ds+1;
12928     TRACE("XXXXXX h:%d\n", GDrawGetText8Height( cv->gw, "0123456AZgplh", 10));
12929     cv->charselectorh = 35;
12930 
12931 
12932     GDrawGetSize(GDrawGetRoot(screen_display),&zoom);
12933     zoom.x = CVPalettesWidth(); zoom.width -= zoom.x-10;
12934     zoom.height -= 30;			/* Room for title bar & such */
12935     GDrawSetZoom(gw,&zoom,-1);
12936 
12937     memset(&gd,0,sizeof(gd));
12938     gd.flags = gg_visible | gg_enabled;
12939     helplist[0].invoke = CVMenuContextualHelp;
12940 #ifndef _NO_PYTHON
12941     if ( cvpy_menu!=NULL )
12942 	mblist[4].ti.disabled = mblist_nomm[4].ti.disabled = false;
12943     mblist[4].sub = mblist_nomm[4].sub = cvpy_menu;
12944 #define CALLBACKS_INDEX 5 /* FIXME: There has to be a better way than this. */
12945 #else
12946 #define CALLBACKS_INDEX 4 /* FIXME: There has to be a better way than this. */
12947 #endif		/* _NO_PYTHON */
12948 #ifdef NATIVE_CALLBACKS
12949     if ( cv_menu!=NULL )
12950 	mblist[CALLBACKS_INDEX].ti.disabled = mblist_nomm[CALLBACKS_INDEX].ti.disabled = false;
12951     mblist[CALLBACKS_INDEX].sub = mblist_nomm[CALLBACKS_INDEX].sub = cv_menu;
12952 #endif		/* NATIVE_CALLBACKS */
12953     gd.u.menu2 = sc->parent->mm==NULL ? mblist_nomm : mblist;
12954     cv->mb = GMenu2BarCreate( gw, &gd, NULL);
12955     GGadgetGetSize(cv->mb,&gsize);
12956     cv->mbh = gsize.height;
12957 
12958 //    TRACE("pos.x:%d pos.y:%d pos.w:%d pos.h:%d\n", pos.x, pos.y, pos.width, pos.height );
12959     GDrawGetSize(cv->gw,&pos);
12960     memset(&gd,0,sizeof(gd));
12961 //    gd.pos.x = pos.x;
12962     gd.pos.x = 3;
12963     gd.pos.y = cv->mbh+2;
12964     gd.pos.height = cv->charselectorh-4;
12965     gd.pos.width  = cv_width / 2;
12966     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_text_xim;
12967     gd.handle_controlevent = CV_OnCharSelectorTextChanged;
12968     gd.u.list = cv_charselector_init;
12969     cv->charselector = GListFieldCreate(cv->gw,&gd,cv);
12970     CVSetCharSelectorValueFromSC( cv, sc );
12971     GGadgetSetSkipUnQualifiedHotkeyProcessing( cv->charselector, 1 );
12972 
12973     // Up and Down buttons for moving through the word list.
12974     {
12975         GGadgetData xgd = gd;
12976         gd.pos.width += 2 * xgd.pos.height + 4;
12977         memset(label, '\0', sizeof(GTextInfo));
12978         xgd.pos.x += xgd.pos.width + 2;
12979         xgd.pos.width = xgd.pos.height;
12980         xgd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
12981         xgd.handle_controlevent = CVMoveToPrevInWordList;
12982         xgd.label = &label[0];
12983         label[0].text = (unichar_t *) "⇞";
12984         label[0].text_is_1byte = true;
12985         cv->charselectorPrev = GButtonCreate(cv->gw,&xgd,cv);
12986         memset(label, '\0', sizeof(GTextInfo));
12987         xgd.pos.x += xgd.pos.width + 2;
12988         xgd.pos.width = xgd.pos.height;
12989         xgd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
12990         xgd.handle_controlevent = CVMoveToNextInWordList;
12991         xgd.label = &label[0];
12992         label[0].text = (unichar_t *) "⇟";
12993         label[0].text_is_1byte = true;
12994         cv->charselectorNext = GButtonCreate(cv->gw,&xgd,cv);
12995     }
12996 
12997 
12998     memset(aspects,0,sizeof(aspects));
12999     aspects[0].text = (unichar_t *) sc->name;
13000     aspects[0].text_is_1byte = true;
13001 	/* NOT visible until we add a second glyph to the stack */
13002     gd.flags = gg_enabled|gg_tabset_nowindow|gg_tabset_scroll|gg_pos_in_pixels;
13003     gd.u.menu = NULL;
13004     gd.u.tabs = aspects;
13005     gd.pos.x = 0;
13006     gd.pos.y = cv->mbh;
13007     gd.handle_controlevent = CVChangeToFormer;
13008     cv->tabs = GTabSetCreate( gw, &gd, NULL );
13009     cv->former_cnt = 1;
13010     cv->former_names[0] = copy(sc->name);
13011     GTabSetSetClosable(cv->tabs, true);
13012     GTabSetSetMovable(cv->tabs, true);
13013     GTabSetSetRemoveSync(cv->tabs, CVTabSetRemoveSync);
13014     GTabSetSetSwapSync(cv->tabs, CVTabSetSwapSync);
13015     GGadgetTakesKeyboard(cv->tabs,false);
13016 
13017     _CharViewCreate( cv, sc, fv, enc, show );
13018     // Frank wants to avoid needing to implement scaling twice.
13019     CVResize(cv);
13020 
13021     return( cv );
13022 }
13023 
CharViewCreate(SplineChar * sc,FontView * fv,int enc)13024 CharView *CharViewCreate(SplineChar *sc, FontView *fv,int enc)
13025 {
13026     return CharViewCreateExtended( sc, fv, enc, 1 );
13027 }
13028 
CharViewFree(CharView * cv)13029 void CharViewFree(CharView *cv) {
13030     int i;
13031 
13032     if ( cv->qg != NULL )
13033 	QGRmCharView(cv->qg,cv);
13034     BDFCharFree(cv->filled);
13035     if ( cv->ruler_w ) {
13036 	GDrawDestroyWindow(cv->ruler_w);
13037 	cv->ruler_w = NULL;
13038     }
13039     if ( cv->ruler_linger_w ) {
13040 	GDrawDestroyWindow(cv->ruler_linger_w);
13041 	cv->ruler_linger_w = NULL;
13042     }
13043     free(cv->gi.u.image->clut);
13044     free(cv->gi.u.image);
13045 #if HANYANG
13046     if ( cv->jamodisplay!=NULL )
13047 	Disp_DoFinish(cv->jamodisplay,true);
13048 #endif
13049 
13050     CVDebugFree(cv->dv);
13051 
13052     SplinePointListsFree(cv->b.gridfit);
13053     FreeType_FreeRaster(cv->oldraster);
13054     FreeType_FreeRaster(cv->raster);
13055 
13056     CVDebugFree(cv->dv);
13057 
13058     for ( i=0; i<cv->former_cnt; ++i )
13059 	free(cv->former_names[i]);
13060 
13061     free(cv->ruler_intersections);
13062     free(cv);
13063 }
13064 
CVValid(SplineFont * sf,SplineChar * sc,CharView * cv)13065 int CVValid(SplineFont *sf, SplineChar *sc, CharView *cv) {
13066     /* A charview may have been closed. A splinechar may have been removed */
13067     /*  from a font */
13068     CharView *test;
13069 
13070     if ( cv->b.sc!=sc || sc->parent!=sf )
13071 return( false );
13072     if ( sc->orig_pos<0 || sc->orig_pos>sf->glyphcnt )
13073 return( false );
13074     if ( sf->glyphs[sc->orig_pos]!=sc )
13075 return( false );
13076     for ( test=(CharView *) (sc->views); test!=NULL; test=(CharView *) (test->b.next) )
13077 	if ( test==cv )
13078 return( true );
13079 
13080 return( false );
13081 }
13082 
13083 static int charview_ready = false;
13084 
CharViewFinish()13085 static void CharViewFinish() {
13086   // The memory leak is limited and reachable.
13087   if ( !charview_ready ) return;
13088     charview_ready = false;
13089     mb2FreeGetText(mblist);
13090     mb2FreeGetText(spiroptlist);
13091     int i;
13092     for ( i=0; mblist_nomm[i].ti.text!=NULL; ++i ) {
13093       free(mblist_nomm[i].ti.text_untranslated); mblist_nomm[i].ti.text_untranslated = NULL;
13094     }
13095 }
13096 
CharViewFinishNonStatic()13097 void CharViewFinishNonStatic() {
13098   CharViewFinish();
13099 }
13100 
CharViewInit(void)13101 static void CharViewInit(void) {
13102     int i;
13103     // static int done = false; // superseded by charview_ready.
13104 
13105     if ( charview_ready )
13106 return;
13107     charview_ready = true;
13108 //    TRACE("CharViewInit(top) mblist[0].text before translation: %s\n", mblist[0].ti.text );
13109 
13110     mb2DoGetText(mblist);
13111 
13112 //    TRACE("CharViewInit(2) mblist[0].text after    translation: %s\n", u_to_c(mblist[0].ti.text) );
13113 //    TRACE("CharViewInit(2) mblist[0].text_untranslated notrans: %s\n", mblist[0].ti.text_untranslated );
13114 
13115     mb2DoGetText(spiroptlist);
13116     for ( i=0; mblist_nomm[i].ti.text!=NULL; ++i )
13117     {
13118 	// Note that because we are doing this ourself we have to set
13119 	// the text_untranslated ourself too.
13120  	if( mblist_nomm[i].shortcut )
13121 	    mblist_nomm[i].ti.text_untranslated = copy(mblist_nomm[i].shortcut);
13122 	else
13123 	    mblist_nomm[i].ti.text_untranslated = copy((char*)mblist_nomm[i].ti.text);
13124 
13125 	mblist_nomm[i].ti.text = (unichar_t *) _((char *) mblist_nomm[i].ti.text);
13126     }
13127     atexit(&CharViewFinishNonStatic);
13128 }
13129 
nested_cv_e_h(GWindow gw,GEvent * event)13130 static int nested_cv_e_h(GWindow gw, GEvent *event) {
13131     CharView *cv = (CharView *) GDrawGetUserData(gw);
13132 
13133     switch ( event->type ) {
13134       case et_expose:
13135 	InfoExpose(cv,gw,event);
13136 	CVLogoExpose(cv,gw,event);
13137       break;
13138       case et_char:
13139 	(cv->b.container->funcs->charEvent)(cv->b.container,event);
13140       break;
13141       case et_charup:
13142 	CVCharUp(cv,event);
13143       break;
13144       case et_controlevent:
13145 	switch ( event->u.control.subtype ) {
13146 	  case et_scrollbarchange:
13147 	    if ( event->u.control.g == cv->hsb )
13148 		CVHScroll(cv,&event->u.control.u.sb);
13149 	    else
13150 		CVVScroll(cv,&event->u.control.u.sb);
13151 	  break;
13152 	}
13153       break;
13154       case et_map:
13155 	if ( event->u.map.is_visible )
13156 	    CVPaletteActivate(cv);
13157 	else
13158 	    CVPalettesHideIfMine(cv);
13159       break;
13160       case et_resize:
13161 	if ( event->u.resize.sized )
13162 	    CVResize(cv);
13163       break;
13164       case et_destroy:
13165 	if ( cv->backimgs!=NULL ) {
13166 	    GDrawDestroyWindow(cv->backimgs);
13167 	    cv->backimgs = NULL;
13168 	}
13169       break;
13170       case et_mouseup: case et_mousedown:
13171 	GGadgetEndPopup();
13172 	CVPaletteActivate(cv);
13173       break;
13174     }
13175 return( true );
13176 }
13177 
SVCharViewInits(SearchView * sv)13178 void SVCharViewInits(SearchView *sv) {
13179     GGadgetData gd;
13180     GWindowAttrs wattrs;
13181     GRect pos, gsize;
13182 
13183     CharViewInit();
13184 
13185     memset(&gd,0,sizeof(gd));
13186     gd.flags = gg_visible | gg_enabled;
13187     helplist[0].invoke = CVMenuContextualHelp;
13188     gd.u.menu2 = mblist_nomm;
13189     sv->mb = GMenu2BarCreate( sv->gw, &gd, NULL);
13190     GGadgetGetSize(sv->mb,&gsize);
13191     sv->mbh = gsize.height;
13192 
13193     pos.y = sv->mbh+sv->fh+10; pos.height = 220;
13194     pos.width = pos.height; pos.x = 10+pos.width+20;	/* Do replace first so palettes appear propperly */
13195     sv->rpl_x = pos.x; sv->cv_y = pos.y;
13196     sv->cv_height = pos.height; sv->cv_width = pos.width;
13197     memset(&wattrs,0,sizeof(wattrs));
13198     wattrs.mask = wam_events|wam_cursor;
13199     wattrs.event_masks = -1;
13200     wattrs.cursor = ct_mypointer;
13201     sv->cv_rpl.gw = GWidgetCreateSubWindow(sv->gw,&pos,nested_cv_e_h,&sv->cv_rpl,&wattrs);
13202     _CharViewCreate(&sv->cv_rpl, &sv->sd.sc_rpl, &sv->dummy_fv, 1, 1);
13203 
13204     pos.x = 10;
13205     sv->cv_srch.gw = GWidgetCreateSubWindow(sv->gw,&pos,nested_cv_e_h,&sv->cv_srch,&wattrs);
13206     _CharViewCreate(&sv->cv_srch, &sv->sd.sc_srch, &sv->dummy_fv, 0, 1);
13207 }
13208 
13209 /* Same for the MATH Kern dlg */
13210 
MKDCharViewInits(MathKernDlg * mkd)13211 void MKDCharViewInits(MathKernDlg *mkd) {
13212     GGadgetData gd;
13213     GWindowAttrs wattrs;
13214     GRect pos, gsize;
13215     int i;
13216 
13217     CharViewInit();
13218 
13219     memset(&gd,0,sizeof(gd));
13220     gd.flags = gg_visible | gg_enabled;
13221     helplist[0].invoke = CVMenuContextualHelp;
13222     gd.u.menu2 = mblist_nomm;
13223     mkd->mb = GMenu2BarCreate( mkd->gw, &gd, NULL);
13224     GGadgetGetSize(mkd->mb,&gsize);
13225     mkd->mbh = gsize.height;
13226 
13227     mkd->mid_space = 20;
13228     for ( i=3; i>=0; --i ) {	/* Create backwards so palettes get set in topright (last created) */
13229 	pos.y = mkd->fh+10; pos.height = 220;	/* Size doesn't matter, adjusted later */
13230 	pos.width = pos.height; pos.x = 10+i*(pos.width+20);
13231 	mkd->cv_y = pos.y;
13232 	mkd->cv_height = pos.height; mkd->cv_width = pos.width;
13233 	memset(&wattrs,0,sizeof(wattrs));
13234 	wattrs.mask = wam_events|wam_cursor;
13235 	wattrs.event_masks = -1;
13236 	wattrs.cursor = ct_mypointer;
13237 	(&mkd->cv_topright)[i].gw = GWidgetCreateSubWindow(mkd->cvparent_w,&pos,nested_cv_e_h,(&mkd->cv_topright)+i,&wattrs);
13238 	_CharViewCreate((&mkd->cv_topright)+i, (&mkd->sc_topright)+i, &mkd->dummy_fv, i, 1);
13239     }
13240 }
13241 
13242 /* Same for the Tile Path dlg */
13243 
13244 #ifdef FONTFORGE_CONFIG_TILEPATH
13245 
TPDCharViewInits(TilePathDlg * tpd,int cid)13246 void TPDCharViewInits(TilePathDlg *tpd, int cid) {
13247     GGadgetData gd;
13248     GWindowAttrs wattrs;
13249     GRect pos, gsize;
13250     int i;
13251 
13252     CharViewInit();
13253 
13254     memset(&gd,0,sizeof(gd));
13255     gd.flags = gg_visible | gg_enabled;
13256     helplist[0].invoke = CVMenuContextualHelp;
13257     gd.u.menu2 = mblist_nomm;
13258     tpd->mb = GMenu2BarCreate( tpd->gw, &gd, NULL);
13259     GGadgetGetSize(tpd->mb,&gsize);
13260     tpd->mbh = gsize.height;
13261 
13262     tpd->mid_space = 20;
13263     for ( i=3; i>=0; --i ) {	/* Create backwards so palettes get set in topright (last created) */
13264 	pos.y = 0; pos.height = 220;	/* Size doesn't matter, adjusted later */
13265 	pos.width = pos.height; pos.x = 0;
13266 	tpd->cv_y = pos.y;
13267 	tpd->cv_height = pos.height; tpd->cv_width = pos.width;
13268 	memset(&wattrs,0,sizeof(wattrs));
13269 	wattrs.mask = wam_events|wam_cursor;
13270 	wattrs.event_masks = -1;
13271 	wattrs.cursor = ct_mypointer;
13272 	(&tpd->cv_first)[i].gw = GWidgetCreateSubWindow(GDrawableGetWindow(GWidgetGetControl(tpd->gw,cid+i)),
13273 		&pos,nested_cv_e_h,(&tpd->cv_first)+i,&wattrs);
13274 	_CharViewCreate((&tpd->cv_first)+i, (&tpd->sc_first)+i, &tpd->dummy_fv, i, 1 );
13275     }
13276 }
13277 
PTDCharViewInits(TilePathDlg * tpd,int cid)13278 void PTDCharViewInits(TilePathDlg *tpd, int cid) {
13279     GGadgetData gd;
13280     GWindowAttrs wattrs;
13281     GRect pos, gsize;
13282 
13283     CharViewInit();
13284 
13285     memset(&gd,0,sizeof(gd));
13286     gd.flags = gg_visible | gg_enabled;
13287     helplist[0].invoke = CVMenuContextualHelp;
13288     gd.u.menu2 = mblist_nomm;
13289     tpd->mb = GMenu2BarCreate( tpd->gw, &gd, NULL);
13290     GGadgetGetSize(tpd->mb,&gsize);
13291     tpd->mbh = gsize.height;
13292 
13293     tpd->mid_space = 20;
13294      {
13295 	pos.y = 0; pos.height = 220;	/* Size doesn't matter, adjusted later */
13296 	pos.width = pos.height; pos.x = 0;
13297 	tpd->cv_y = pos.y;
13298 	tpd->cv_height = pos.height; tpd->cv_width = pos.width;
13299 	memset(&wattrs,0,sizeof(wattrs));
13300 	wattrs.mask = wam_events|wam_cursor;
13301 	wattrs.event_masks = -1;
13302 	wattrs.cursor = ct_mypointer;
13303 	tpd->cv_first.gw = GWidgetCreateSubWindow(GDrawableGetWindow(GWidgetGetControl(tpd->gw,cid)),
13304 		&pos,nested_cv_e_h,&tpd->cv_first,&wattrs);
13305 	_CharViewCreate(&tpd->cv_first, &tpd->sc_first, &tpd->dummy_fv, 0, 1 );
13306     }
13307 }
13308 #endif		/* TilePath */
13309 
GDDCharViewInits(GradientDlg * gdd,int cid)13310 void GDDCharViewInits(GradientDlg *gdd, int cid) {
13311     GGadgetData gd;
13312     GWindowAttrs wattrs;
13313     GRect pos, gsize;
13314 
13315     CharViewInit();
13316 
13317     memset(&gd,0,sizeof(gd));
13318     gd.flags = gg_visible | gg_enabled;
13319     helplist[0].invoke = CVMenuContextualHelp;
13320     gd.u.menu2 = mblist_nomm;
13321     gdd->mb = GMenu2BarCreate( gdd->gw, &gd, NULL);
13322     GGadgetGetSize(gdd->mb,&gsize);
13323     gdd->mbh = gsize.height;
13324 
13325     memset(&wattrs,0,sizeof(wattrs));
13326     wattrs.mask = wam_events|wam_cursor;
13327     wattrs.event_masks = -1;
13328     wattrs.cursor = ct_mypointer;
13329 
13330     pos.y = 1; pos.height = 220;
13331     pos.width = pos.height; pos.x = 0;
13332     gdd->cv_grad.gw = GWidgetCreateSubWindow(
13333 	    GDrawableGetWindow(GWidgetGetControl(gdd->gw,cid)),
13334 	    &pos,nested_cv_e_h,&gdd->cv_grad,&wattrs);
13335     _CharViewCreate(&gdd->cv_grad, &gdd->sc_grad, &gdd->dummy_fv, 0, 1 );
13336 }
13337 
StrokeCharViewInits(StrokeDlg * sd,int cid)13338 void StrokeCharViewInits(StrokeDlg *sd, int cid) {
13339     GGadgetData gd;
13340     GWindowAttrs wattrs;
13341     GRect pos, gsize;
13342 
13343     CharViewInit();
13344 
13345     memset(&gd,0,sizeof(gd));
13346     gd.flags = gg_visible | gg_enabled;
13347     helplist[0].invoke = CVMenuContextualHelp;
13348     gd.u.menu2 = mblist_nomm;
13349     sd->mb = GMenu2BarCreate( sd->gw, &gd, NULL);
13350     GGadgetGetSize(sd->mb,&gsize);
13351     sd->mbh = gsize.height;
13352 
13353     memset(&wattrs,0,sizeof(wattrs));
13354     wattrs.mask = wam_events|wam_cursor;
13355     wattrs.event_masks = -1;
13356     wattrs.cursor = ct_mypointer;
13357 
13358     pos.y = 1; pos.height = 220;
13359     pos.width = pos.height; pos.x = 0;
13360     sd->cv_stroke.gw = GWidgetCreateSubWindow(
13361 	    GDrawableGetWindow(GWidgetGetControl(sd->gw,cid)),
13362 	    &pos,nested_cv_e_h,&sd->cv_stroke,&wattrs);
13363     _CharViewCreate(&sd->cv_stroke, &sd->sc_stroke, &sd->dummy_fv, 0, 1 );
13364 }
13365 
SC_CloseAllWindows(SplineChar * sc)13366 static void SC_CloseAllWindows(SplineChar *sc) {
13367     CharViewBase *cv, *next;
13368     BitmapView *bv, *bvnext;
13369     BDFFont *bdf;
13370     BDFChar *bfc;
13371     FontView *fvs;
13372 
13373     if ( sc->views ) {
13374 	for ( cv = sc->views; cv!=NULL; cv=next ) {
13375 	    next = cv->next;
13376 	    GDrawDestroyWindow(((CharView *) cv)->gw);
13377 	}
13378 	GDrawSync(NULL);
13379 	GDrawProcessPendingEvents(NULL);
13380 	GDrawSync(NULL);
13381 	GDrawProcessPendingEvents(NULL);
13382     }
13383 
13384     for ( bdf=sc->parent->bitmaps; bdf!=NULL; bdf = bdf->next ) {
13385 	if ( sc->orig_pos<bdf->glyphcnt && (bfc = bdf->glyphs[sc->orig_pos])!= NULL ) {
13386 	    if ( bfc->views!=NULL ) {
13387 		for ( bv= bfc->views; bv!=NULL; bv=bvnext ) {
13388 		    bvnext = bv->next;
13389 		    GDrawDestroyWindow(bv->gw);
13390 		}
13391 		GDrawSync(NULL);
13392 		GDrawProcessPendingEvents(NULL);
13393 		GDrawSync(NULL);
13394 		GDrawProcessPendingEvents(NULL);
13395 	    }
13396 	}
13397     }
13398 
13399     /* Turn any searcher references to this glyph into inline copies of it */
13400     for ( fvs=(FontView *) sc->parent->fv; fvs!=NULL; fvs=(FontView *) fvs->b.nextsame ) {
13401 	if ( fvs->sv!=NULL ) {
13402 	    RefChar *rf, *rnext;
13403 	    for ( rf = fvs->sv->sd.sc_srch.layers[ly_fore].refs; rf!=NULL; rf=rnext ) {
13404 		rnext = rf->next;
13405 		if ( rf->sc==sc )
13406 		    SCRefToSplines(&fvs->sv->sd.sc_srch,rf,ly_fore);
13407 	    }
13408 	    for ( rf = fvs->sv->sd.sc_rpl.layers[ly_fore].refs; rf!=NULL; rf=rnext ) {
13409 		rnext = rf->next;
13410 		if ( rf->sc==sc )
13411 		    SCRefToSplines(&fvs->sv->sd.sc_rpl,rf,ly_fore);
13412 	    }
13413 	}
13414     }
13415 }
13416 
13417 struct sc_interface gdraw_sc_interface = {
13418     SC_UpdateAll,
13419     SC_OutOfDateBackground,
13420     SC_RefreshTitles,
13421     SC_HintsChanged,
13422     SC_CharChangedUpdate,
13423     _SC_CharChangedUpdate,
13424     SC_MarkInstrDlgAsChanged,
13425     SC_CloseAllWindows,
13426     SC_MoreLayers
13427 };
13428 
UI_CVGlyphRenameFixup(SplineFont * sf,const char * oldname,const char * newname)13429 static void UI_CVGlyphRenameFixup(SplineFont *sf, const char *oldname, const char *newname) {
13430     int gid, i;
13431     SplineChar *sc;
13432     CharView *cv;
13433 
13434     if ( no_windowing_ui )
13435 return;
13436 
13437     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL) {
13438 	for ( cv=(CharView *) (sc->views); cv!=NULL; cv = (CharView *) (cv->b.next)) {
13439 	    for ( i=0; i<cv->former_cnt; ++i ) if ( strcmp(oldname,cv->former_names[i])==0 ) {
13440 		free( cv->former_names[i] );
13441 		cv->former_names[i] = copy( newname );
13442 		if ( cv->tabs!=NULL ) {
13443 		    GTabSetChangeTabName(cv->tabs,newname,i);
13444 		    GTabSetRemetric(cv->tabs);
13445 		    GGadgetRedraw(cv->tabs);
13446 		}
13447 	    }
13448 	}
13449     }
13450 }
13451 
13452 struct cv_interface gdraw_cv_interface = {
13453     (void (*)(CharViewBase *)) CV_CharChangedUpdate,
13454     (void (*)(CharViewBase *,int)) _CV_CharChangedUpdate,
13455     UI_CVGlyphRenameFixup,
13456     CV_LayerPaletteCheck
13457 };
13458 
13459 extern GResInfo bitmapview_ri;
13460 GResInfo charview2_ri = {
13461     &bitmapview_ri, NULL,NULL, NULL,
13462     NULL,
13463     NULL,
13464     NULL,
13465     charview2_re,
13466     N_("Outline View 2"),
13467     N_("This window displays a single outline glyph (more data)"),
13468     "CharView",
13469     "fontforge",
13470     false,
13471     0,
13472     NULL,
13473     GBOX_EMPTY,
13474     NULL,
13475     NULL,
13476     NULL
13477 };
13478 GResInfo charview_ri = {
13479     &charview2_ri, NULL,NULL, NULL,
13480     NULL,
13481     NULL,
13482     NULL,
13483     charview_re,
13484     N_("Outline View"),
13485     N_("This window displays a single outline glyph"),
13486     "CharView",
13487     "fontforge",
13488     false,
13489     0,
13490     NULL,
13491     GBOX_EMPTY,
13492     NULL,
13493     NULL,
13494     NULL
13495 };
13496 
13497 
SPSelectNextPoint(SplinePoint * sp,int state)13498 void SPSelectNextPoint( SplinePoint *sp, int state )
13499 {
13500     if( !sp )
13501 	return;
13502     if( !sp->next )
13503 	return;
13504     if( !sp->next->to )
13505 	return;
13506     sp->next->to->selected = state;
13507 }
13508 
SPSelectPrevPoint(SplinePoint * sp,int state)13509 void SPSelectPrevPoint( SplinePoint *sp, int state )
13510 {
13511     if( !sp )
13512 	return;
13513     if( !sp->prev )
13514 	return;
13515     if( !sp->prev->from )
13516 	return;
13517     sp->prev->from->selected = state;
13518 }
13519 
13520 
13521 
SPIsNextCPSelectedSingle(SplinePoint * sp,CharView * cv)13522 bool SPIsNextCPSelectedSingle( SplinePoint *sp, CharView *cv )
13523 {
13524     if( cv )
13525     {
13526 	int iscurrent = sp == (cv->p.sp!=NULL?cv->p.sp:cv->lastselpt);
13527 	if( iscurrent && cv->p.nextcp )
13528 	    return true;
13529     }
13530     return false;
13531 }
13532 
13533 
SPIsNextCPSelected(SplinePoint * sp,CharView * cv)13534 bool SPIsNextCPSelected( SplinePoint *sp, CharView *cv )
13535 {
13536     if( SPIsNextCPSelectedSingle( sp, cv ))
13537 	return true;
13538     return sp->nextcpselected;
13539 }
13540 
SPIsPrevCPSelectedSingle(SplinePoint * sp,CharView * cv)13541 bool SPIsPrevCPSelectedSingle( SplinePoint *sp, CharView *cv )
13542 {
13543     if( cv )
13544     {
13545 	int iscurrent = sp == (cv->p.sp!=NULL?cv->p.sp:cv->lastselpt);
13546 	if( iscurrent && cv->p.prevcp )
13547 	    return true;
13548     }
13549     return false;
13550 }
13551 
SPIsPrevCPSelected(SplinePoint * sp,CharView * cv)13552 bool SPIsPrevCPSelected( SplinePoint *sp, CharView *cv )
13553 {
13554     if( SPIsPrevCPSelectedSingle( sp, cv ))
13555     {
13556 	return true;
13557     }
13558     return sp->prevcpselected;
13559 }
13560 
CVShouldInterpolateCPsOnMotion(CharView * cv)13561 bool CVShouldInterpolateCPsOnMotion( CharView* cv )
13562 {
13563     bool ret = interpCPsOnMotion;
13564 
13565     if( cv->activeModifierControl && cv->activeModifierAlt )
13566 	ret = !ret;
13567 
13568     return ret;
13569 }
13570 
13571