PageRenderTime 62ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 2ms

/fontforgeexe/cvpalettes.c

https://github.com/lovelife/fontforge
C | 4323 lines | 3725 code | 421 blank | 177 comment | 945 complexity | 5dd9d24ee15b0e977c13dbaecbfd5702 MD5 | raw file
Possible License(s): GPL-3.0, Unlicense, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  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. * Redistributions of source code must retain the above copyright notice, this
  6. * list of conditions and the following disclaimer.
  7. * Redistributions in binary form must reproduce the above copyright notice,
  8. * this list of conditions and the following disclaimer in the documentation
  9. * and/or other materials provided with the distribution.
  10. * The name of the author may not be used to endorse or promote products
  11. * derived from this software without specific prior written permission.
  12. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
  13. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  14. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  15. * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  16. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  17. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  18. * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  19. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  20. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  21. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. #include "fontforgeui.h"
  24. int palettes_docked=1;
  25. int rectelipse=0, polystar=0, regular_star=1;
  26. int center_out[2] = { false, true };
  27. float rr_radius=0;
  28. int ps_pointcnt=6;
  29. float star_percent=1.7320508; /* Regular 6 pointed star */
  30. extern int interpCPsOnMotion;
  31. #include <gkeysym.h>
  32. #include <math.h>
  33. #include "splinefont.h"
  34. #include <ustring.h>
  35. #include <utype.h>
  36. #include <gresource.h>
  37. #include "charview_private.h"
  38. static void CVLCheckLayerCount(CharView *cv, int resize);
  39. extern void CVDebugFree(DebugView *dv);
  40. extern GBox _ggadget_Default_Box;
  41. #define ACTIVE_BORDER (_ggadget_Default_Box.active_border)
  42. #define MAIN_FOREGROUND (_ggadget_Default_Box.main_foreground)
  43. extern GDevEventMask input_em[];
  44. extern const int input_em_cnt;
  45. int cvvisible[2] = { 1, 1}, bvvisible[3]= { 1,1,1 };
  46. static GWindow cvlayers, cvtools, bvlayers, bvtools, bvshades;
  47. static GWindow cvlayers2=NULL;
  48. #define LSHOW_CUBIC 1
  49. #define LSHOW_FG 2
  50. #define LSHOW_PREVIEW 4
  51. static int layerscols = LSHOW_CUBIC|LSHOW_FG|LSHOW_PREVIEW; /* which columns to show in layers1 palette */
  52. static int layer_height = 0; /* height of each layer row in layers1 palette */
  53. static int layer_header_height = 0; /* height of initial stuff in layers1 palette */
  54. static int layer_footer_height = 0; /* height of +/- buttons at bottom of layers1 palette */
  55. static int layers_max = 2; /* Maximum number of layers for which widgets have been allocated in layers1 palette */
  56. struct l2 {
  57. int active; /* index of the active layer */
  58. int offtop; /* first layer to show at the top line in layers palette */
  59. int visible_layers; /* number of layers apart from the guides layer to show before using a scrollbar */
  60. int current_layers; /* number of layers for the current character, and the number used in l2.layers */
  61. int max_layers; /* maximum number of layers for which layer controls and previews have been allocated */
  62. BDFChar **layers; /* layer thumbnail previews */
  63. int sb_start; /* x pixel position of the scrollbar */
  64. int column_width; /* width of various indicator columns */
  65. int mo_col, mo_layer; /* mouse over column and layer */
  66. int rename_active; /* If >=2, layer number for which the edit box for layer names is active */
  67. GClut *clut;
  68. GFont *font; /* font to draw text in the palette with */
  69. } layerinfo = { /* info about the current layers in the layers palette */
  70. 2, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, NULL
  71. };
  72. struct l2 layer2 = { 2, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, NULL };
  73. static int layers2_active = -1;
  74. static GPoint cvtoolsoff = { -9999, -9999 }, cvlayersoff = { -9999, -9999 }, bvlayersoff = { -9999, -9999 }, bvtoolsoff = { -9999, -9999 }, bvshadesoff = { -9999, -9999 };
  75. int palettes_fixed=1;
  76. static GCursor tools[cvt_max+1] = { ct_pointer }, spirotools[cvt_max+1];
  77. enum cvtools cv_b1_tool = cvt_pointer, cv_cb1_tool = cvt_pointer,
  78. cv_b2_tool = cvt_magnify, cv_cb2_tool = cvt_ruler;
  79. static GFont *toolsfont=NULL, *layersfont=NULL;
  80. #define CV_TOOLS_WIDTH 53
  81. #define CV_TOOLS_HEIGHT (10*27+4*12+2)
  82. #define CV_LAYERS_WIDTH 104
  83. #define CV_LAYERS_HEIGHT 100
  84. #define CV_LAYERS_INITIALCNT 6
  85. #define CV_LAYERS_LINE_HEIGHT 25
  86. #define CV_LAYERS2_WIDTH 120
  87. #define CV_LAYERS2_HEIGHT 196
  88. #define CV_LAYERS2_LINE_HEIGHT 25
  89. #define CV_LAYERS2_HEADER_HEIGHT 20
  90. #define CV_LAYERS2_VISLAYERS ( (CV_LAYERS2_HEIGHT-CV_LAYERS2_HEADER_HEIGHT-2*CV_LAYERS2_LINE_HEIGHT)/CV_LAYERS2_LINE_HEIGHT )
  91. #define BV_TOOLS_WIDTH 53
  92. #define BV_TOOLS_HEIGHT 80
  93. #define BV_LAYERS_HEIGHT 73
  94. #define BV_LAYERS_WIDTH 73
  95. #define BV_SHADES_HEIGHT (8+9*16)
  96. static void ReparentFixup(GWindow child,GWindow parent, int x, int y, int width, int height ) {
  97. /* This is so nice */
  98. /* KDE does not honor my request for a border for top level windows */
  99. /* KDE does not honor my request for size (for narrow) top level windows */
  100. /* Gnome gets very confused by reparenting */
  101. /* If we've got a top level window, then reparenting it removes gnome's */
  102. /* decoration window, but sets the new parent to root (rather than what */
  103. /* we asked for */
  104. /* I have tried reparenting it twice, unmapping & reparenting. Nothing works */
  105. GWidgetReparentWindow(child,parent,x,y);
  106. if ( width!=0 )
  107. GDrawResize(child,width,height);
  108. GDrawSetWindowBorder(child,1,GDrawGetDefaultForeground(NULL));
  109. }
  110. /* Initialize a window that is to be used for a palette. Specific widgets and other functionality are added elsewhere. */
  111. static GWindow CreatePalette(GWindow w, GRect *pos, int (*eh)(GWindow,GEvent *), void *user_data, GWindowAttrs *wattrs, GWindow v) {
  112. GWindow gw;
  113. GPoint pt, base;
  114. GRect newpos;
  115. GWindow root;
  116. GRect ownerpos, screensize;
  117. pt.x = pos->x; pt.y = pos->y;
  118. if ( !palettes_fixed ) {
  119. root = GDrawGetRoot(NULL);
  120. GDrawGetSize(w,&ownerpos);
  121. GDrawGetSize(root,&screensize);
  122. GDrawTranslateCoordinates(w,root,&pt);
  123. base.x = base.y = 0;
  124. GDrawTranslateCoordinates(w,root,&base);
  125. if ( pt.x<0 ) {
  126. if ( base.x+ownerpos.width+20+pos->width+20 > screensize.width )
  127. pt.x=0;
  128. else
  129. pt.x = base.x+ownerpos.width+20;
  130. }
  131. if ( pt.y<0 ) pt.y=0;
  132. if ( pt.x+pos->width>screensize.width )
  133. pt.x = screensize.width-pos->width;
  134. if ( pt.y+pos->height>screensize.height )
  135. pt.y = screensize.height-pos->height;
  136. }
  137. wattrs->mask |= wam_bordcol|wam_bordwidth;
  138. wattrs->border_width = 1;
  139. wattrs->border_color = GDrawGetDefaultForeground(NULL);
  140. newpos.x = pt.x; newpos.y = pt.y; newpos.width = pos->width; newpos.height = pos->height;
  141. wattrs->mask|= wam_positioned;
  142. wattrs->positioned = true;
  143. gw = GDrawCreateTopWindow(NULL,&newpos,eh,user_data,wattrs);
  144. if ( palettes_docked )
  145. ReparentFixup(gw,v,0,pos->y,pos->width,pos->height);
  146. return( gw );
  147. }
  148. /* Return screen coordinates of the palette in off, relative to the root window origin. */
  149. static void SaveOffsets(GWindow main, GWindow palette, GPoint *off) {
  150. if ( !palettes_docked && !palettes_fixed && GDrawIsVisible(palette)) {
  151. GRect mr, pr;
  152. GWindow root, temp;
  153. root = GDrawGetRoot(NULL);
  154. while ( (temp=GDrawGetParentWindow(main))!=root )
  155. main = temp;
  156. GDrawGetSize(main,&mr);
  157. GDrawGetSize(palette,&pr);
  158. off->x = pr.x-mr.x;
  159. off->y = pr.y-mr.y;
  160. if ( off->x<0 ) off->x = 0;
  161. if ( off->y<0 ) off->y = 0;
  162. }
  163. }
  164. /* Set the palette window position to off, a point in the root window space. */
  165. static void RestoreOffsets(GWindow main, GWindow palette, GPoint *off) {
  166. GPoint pt;
  167. GWindow root,temp;
  168. GRect screensize, pos;
  169. if ( palettes_fixed )
  170. return;
  171. pt = *off;
  172. root = GDrawGetRoot(NULL);
  173. GDrawGetSize(root,&screensize);
  174. GDrawGetSize(palette,&pos);
  175. while ( (temp=GDrawGetParentWindow(main))!=root )
  176. main = temp;
  177. GDrawTranslateCoordinates(main,root,&pt);
  178. if ( pt.x<0 ) pt.x=0;
  179. if ( pt.y<0 ) pt.y=0;
  180. if ( pt.x+pos.width>screensize.width )
  181. pt.x = screensize.width-pos.width;
  182. if ( pt.y+pos.height>screensize.height )
  183. pt.y = screensize.height-pos.height;
  184. GDrawTrueMove(palette,pt.x,pt.y);
  185. GDrawRaise(palette);
  186. }
  187. static void CVMenuTool(GWindow gw,struct gmenuitem *mi,GEvent *e) {
  188. CharView *cv = (CharView *) GDrawGetUserData(gw);
  189. cv->b1_tool = mi->mid;
  190. if ( cvtools!=NULL )
  191. GDrawRequestExpose(cvtools,NULL,false);
  192. CVToolsSetCursor(cv,0,NULL);
  193. }
  194. static void CVChangeSpiroMode(CharView *cv);
  195. static void CVMenuSpiroSet(GWindow gw,struct gmenuitem *mi,GEvent *e) {
  196. CharView *cv = (CharView *) GDrawGetUserData(gw);
  197. CVChangeSpiroMode(cv);
  198. }
  199. void cvtoollist_check(GWindow gw,struct gmenuitem *mi,GEvent *e) {
  200. CharView *cv = (CharView *) GDrawGetUserData(gw);
  201. int order2 = cv->b.layerheads[cv->b.drawmode]->order2;
  202. for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
  203. mi->ti.checked = mi->mid==cv->b1_tool;
  204. switch ( mi->mid ) {
  205. case cvt_freehand:
  206. mi->ti.disabled = order2;
  207. break;
  208. case cvt_spiro:
  209. mi->ti.disabled = !hasspiro();
  210. break;
  211. }
  212. }
  213. }
  214. /* Note: If you change this ordering, change enum cvtools */
  215. static char *popupsres[] = {
  216. N_("Pointer"), N_("Magnify (Minify with alt)"),
  217. N_("Draw a freehand curve"), N_("Scroll by hand"),
  218. N_("Cut splines in two"), N_("Measure distance, angle between points"),
  219. N_("Add a point, then drag out its control points"), N_("Change whether spiro is active or not"),
  220. N_("Add a curve point"), N_("Add a curve point always either horizontal or vertical"),
  221. N_("Add a corner point"), N_("Add a tangent point"),
  222. N_("Scale the selection"), N_("Rotate the selection"),
  223. N_("Flip the selection"), N_("Skew the selection"),
  224. N_("Rotate the selection in 3D and project back to plain"), N_("Perform a perspective transformation on the selection"),
  225. N_("Rectangle or Ellipse"), N_("Polygon or Star"),
  226. N_("Rectangle or Ellipse"), N_("Polygon or Star")};
  227. GMenuItem2 cvtoollist[] = {
  228. { { (unichar_t *) N_("_Pointer"), (GImage *) "toolspointer.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Pointer|No Shortcut"), NULL, NULL, CVMenuTool, cvt_pointer },
  229. { { (unichar_t *) N_("_Magnify"), (GImage *) "toolsmagnify.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Magnify|No Shortcut"), NULL, NULL, CVMenuTool, cvt_magnify },
  230. { { (unichar_t *) N_("_Freehand"), (GImage *) "toolsfreehand.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Freehand|No Shortcut"), NULL, NULL, CVMenuTool, cvt_freehand },
  231. { { (unichar_t *) N_("_Scroll"), (GImage *) "toolsscroll.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Scroll|No Shortcut"), NULL, NULL, CVMenuTool, cvt_hand },
  232. { { (unichar_t *) N_("_Knife"), (GImage *) "toolsknife.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Knife|No Shortcut"), NULL, NULL, CVMenuTool, cvt_knife },
  233. { { (unichar_t *) N_("_Ruler"), (GImage *) "toolsruler.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Ruler|No Shortcut"), NULL, NULL, CVMenuTool, cvt_ruler },
  234. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  235. { { (unichar_t *) N_("P_en"), (GImage *) "toolspen.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Pen|No Shortcut"), NULL, NULL, CVMenuTool, cvt_pen },
  236. { { (unichar_t *) N_("_Activate Spiro"), (GImage *) "toolsspiro.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Activate Spiro|No Shortcut"), NULL, NULL, CVMenuSpiroSet, cvt_spiro },
  237. { { (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 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_curve },
  238. { { (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 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_hvcurve },
  239. { { (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 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_corner },
  240. { { (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 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_tangent },
  241. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  242. { { (unichar_t *) N_("Sca_le"), (GImage *) "toolsscale.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Scale|No Shortcut"), NULL, NULL, CVMenuTool, cvt_scale },
  243. { { (unichar_t *) N_("Rotate"), (GImage *) "toolsrotate.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Rotate|No Shortcut"), NULL, NULL, CVMenuTool, cvt_rotate },
  244. { { (unichar_t *) N_("Flip"), (GImage *) "toolsflip.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Flip|No Shortcut"), NULL, NULL, CVMenuTool, cvt_flip },
  245. { { (unichar_t *) N_("Ske_w"), (GImage *) "toolsskew.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Skew|No Shortcut"), NULL, NULL, CVMenuTool, cvt_skew },
  246. { { (unichar_t *) N_("_3D Rotate"), (GImage *) "tools3drotate.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("3D Rotate|No Shortcut"), NULL, NULL, CVMenuTool, cvt_3d_rotate },
  247. { { (unichar_t *) N_("Perspecti_ve"), (GImage *) "toolsperspective.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Perspective|No Shortcut"), NULL, NULL, CVMenuTool, cvt_perspective },
  248. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  249. { { (unichar_t *) N_("Rectan_gle"), (GImage *) "toolsrect.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Rectangle|No Shortcut"), NULL, NULL, CVMenuTool, cvt_rect },
  250. { { (unichar_t *) N_("Pol_ygon"), (GImage *) "toolspolygon.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Polygon|No Shortcut"), NULL, NULL, CVMenuTool, cvt_poly },
  251. { { (unichar_t *) N_("Ellipse"), (GImage *) "toolselipse.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Ellipse|No Shortcut"), NULL, NULL, CVMenuTool, cvt_elipse },
  252. { { (unichar_t *) N_("Star"), (GImage *) "toolsstar.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Star|No Shortcut"), NULL, NULL, CVMenuTool, cvt_star },
  253. GMENUITEM2_EMPTY
  254. };
  255. GMenuItem2 cvspirotoollist[] = {
  256. { { (unichar_t *) N_("_Pointer"), (GImage *) "toolspointer.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Pointer|No Shortcut"), NULL, NULL, CVMenuTool, cvt_pointer },
  257. { { (unichar_t *) N_("_Magnify"), (GImage *) "toolsmagnify.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Magnify|No Shortcut"), NULL, NULL, CVMenuTool, cvt_magnify },
  258. { { (unichar_t *) N_("_Freehand"), (GImage *) "toolsfreehand.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Freehand|No Shortcut"), NULL, NULL, CVMenuTool, cvt_freehand },
  259. { { (unichar_t *) N_("_Scroll"), (GImage *) "toolsscroll.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Scroll|No Shortcut"), NULL, NULL, CVMenuTool, cvt_hand },
  260. { { (unichar_t *) N_("_Knife"), (GImage *) "toolsknife.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Knife|No Shortcut"), NULL, NULL, CVMenuTool, cvt_knife },
  261. { { (unichar_t *) N_("_Ruler"), (GImage *) "toolsruler.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Ruler|No Shortcut"), NULL, NULL, CVMenuTool, cvt_ruler },
  262. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  263. { { (unichar_t *) N_("_Knife"), (GImage *) "toolsknife.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Knife|No Shortcut"), NULL, NULL, CVMenuTool, cvt_knife },
  264. { { (unichar_t *) N_("_Ruler"), (GImage *) "toolsruler.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Ruler|No Shortcut"), NULL, NULL, CVMenuTool, cvt_ruler },
  265. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  266. { { (unichar_t *) N_("De_activate Spiro"), (GImage *) "toolsspiro.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Activate Spiro|No Shortcut"), NULL, NULL, CVMenuSpiroSet, cvt_spiro },
  267. { { (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 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_spirocorner },
  268. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  269. { { (unichar_t *) N_("G_4"), (GImage *) "pointscurve.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("G4 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_spirog4 },
  270. { { (unichar_t *) N_("G_2"), (GImage *) "pointsG2curve.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'o' }, H_("G2 Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_spirog2 },
  271. { { (unichar_t *) N_("Lef_t"), (GImage *) "pointsspiroprev.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Left Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_spiroleft },
  272. { { (unichar_t *) N_("Rig_ht"), (GImage *) "pointsspironext.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Right Tool|No Shortcut"), NULL, NULL, CVMenuTool, cvt_spiroright },
  273. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  274. { { (unichar_t *) N_("Sca_le"), (GImage *) "toolsscale.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Scale|No Shortcut"), NULL, NULL, CVMenuTool, cvt_scale },
  275. { { (unichar_t *) N_("Rotate"), (GImage *) "toolsrotate.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Rotate|No Shortcut"), NULL, NULL, CVMenuTool, cvt_rotate },
  276. { { (unichar_t *) N_("Flip"), (GImage *) "toolsflip.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Flip|No Shortcut"), NULL, NULL, CVMenuTool, cvt_flip },
  277. { { (unichar_t *) N_("Ske_w"), (GImage *) "toolsskew.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Skew|No Shortcut"), NULL, NULL, CVMenuTool, cvt_skew },
  278. { { (unichar_t *) N_("_3D Rotate"), (GImage *) "tools3drotate.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("3D Rotate|No Shortcut"), NULL, NULL, CVMenuTool, cvt_3d_rotate },
  279. { { (unichar_t *) N_("Perspecti_ve"), (GImage *) "toolsperspective.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Perspective|No Shortcut"), NULL, NULL, CVMenuTool, cvt_perspective },
  280. { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
  281. { { (unichar_t *) N_("Rectan_gle"), (GImage *) "toolsrect.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Rectangle|No Shortcut"), NULL, NULL, CVMenuTool, cvt_rect },
  282. { { (unichar_t *) N_("Pol_ygon"), (GImage *) "toolspolygon.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Polygon|No Shortcut"), NULL, NULL, CVMenuTool, cvt_poly },
  283. { { (unichar_t *) N_("Ellipse"), (GImage *) "toolselipse.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Ellipse|No Shortcut"), NULL, NULL, CVMenuTool, cvt_elipse },
  284. { { (unichar_t *) N_("Star"), (GImage *) "toolsstar.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Star|No Shortcut"), NULL, NULL, CVMenuTool, cvt_star },
  285. GMENUITEM2_EMPTY
  286. };
  287. static char *editablelayers[] = {
  288. /* GT: Foreground, make it short */
  289. N_("F_ore"),
  290. /* GT: Background, make it short */
  291. N_("_Back"),
  292. /* GT: Guide layer, make it short */
  293. N_("_Guide")
  294. };
  295. static real raddiam_x = 20, raddiam_y = 20, rotate_by=0;
  296. static StrokeInfo expand = {
  297. 25, lj_round, lc_butt, si_centerline,
  298. false, /* removeexternal */
  299. false, /* removeinternal */
  300. false, /* leave users */
  301. 3.1415926535897932/4, 25, NULL, 50,
  302. 0.0, 0, 0, NULL, NULL
  303. };
  304. real CVRoundRectRadius(void) {
  305. return( rr_radius );
  306. }
  307. int CVRectElipseCenter(void) {
  308. return( center_out[rectelipse] );
  309. }
  310. int CVPolyStarPoints(void) {
  311. return( ps_pointcnt );
  312. }
  313. real CVStarRatio(void) {
  314. if ( regular_star )
  315. return( sin(3.1415926535897932/ps_pointcnt)*tan(2*3.1415926535897932/ps_pointcnt)+cos(3.1415926535897932/ps_pointcnt) );
  316. return( star_percent );
  317. }
  318. StrokeInfo *CVFreeHandInfo(void) {
  319. return( &expand );
  320. }
  321. struct ask_info {
  322. GWindow gw;
  323. int done;
  324. int ret;
  325. float *val;
  326. int *co;
  327. GGadget *rb1;
  328. GGadget *reg;
  329. GGadget *pts;
  330. int ispolystar;
  331. int haspos;
  332. char *lab;
  333. CharView *cv;
  334. };
  335. #define CID_ValText 1001
  336. #define CID_PointPercent 1002
  337. #define CID_CentCornLab 1003
  338. #define CID_CentCornX 1004
  339. #define CID_CentCornY 1005
  340. #define CID_RadDiamLab 1006
  341. #define CID_RadDiamX 1007
  342. #define CID_RadDiamY 1008
  343. #define CID_Angle 1009
  344. static void FakeShapeEvents(CharView *cv) {
  345. GEvent event;
  346. real trans[6];
  347. cv->active_tool = rectelipse ? cvt_elipse : cvt_rect;
  348. if ( cv->b.sc->inspiro && hasspiro() ) {
  349. GDrawSetCursor(cv->v,spirotools[cv->active_tool]);
  350. GDrawSetCursor(cvtools,spirotools[cv->active_tool]);
  351. } else {
  352. GDrawSetCursor(cv->v,tools[cv->active_tool]);
  353. GDrawSetCursor(cvtools,tools[cv->active_tool]);
  354. }
  355. cv->showing_tool = cv->active_tool;
  356. memset(&event,0,sizeof(event));
  357. event.type = et_mousedown;
  358. CVMouseDownShape(cv,&event);
  359. cv->info.x += raddiam_x;
  360. cv->info.y += raddiam_y;
  361. CVMouseMoveShape(cv);
  362. CVMouseUpShape(cv);
  363. if ( raddiam_x!=0 && raddiam_y!=0 && rotate_by!=0 ) {
  364. trans[0] = trans[3] = cos ( rotate_by*3.1415926535897932/180. );
  365. trans[1] = sin( rotate_by*3.1415926535897932/180. );
  366. trans[2] = -trans[1];
  367. trans[4] = -cv->p.x*trans[0] - cv->p.y*trans[2] + cv->p.x;
  368. trans[5] = -cv->p.x*trans[1] - cv->p.y*trans[3] + cv->p.y;
  369. SplinePointListTransform(cv->b.layerheads[cv->b.drawmode]->splines,trans,
  370. interpCPsOnMotion?tpt_OnlySelectedInterpCPs:tpt_OnlySelected);
  371. SCUpdateAll(cv->b.sc);
  372. }
  373. cv->active_tool = cvt_none;
  374. }
  375. static int TA_OK(GGadget *g, GEvent *e) {
  376. if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
  377. struct ask_info *d = GDrawGetUserData(GGadgetGetWindow(g));
  378. real val, val2=0;
  379. int err=0;
  380. int re = !GGadgetIsChecked(d->rb1);
  381. if ( d->ispolystar ) {
  382. val = GetInt8(d->gw,CID_ValText,d->lab,&err);
  383. if ( !(regular_star = GGadgetIsChecked(d->reg)))
  384. val2 = GetReal8(d->gw,CID_PointPercent,_("Size of Points"),&err);
  385. } else {
  386. val = GetReal8(d->gw,CID_ValText,d->lab,&err);
  387. d->co[re] = !GGadgetIsChecked(d->reg);
  388. }
  389. if ( err )
  390. return( true );
  391. if ( d->haspos ) {
  392. real x,y, radx,rady, ang;
  393. x = GetInt8(d->gw,CID_CentCornX,_("_X"),&err);
  394. y = GetInt8(d->gw,CID_CentCornY,_("_Y"),&err);
  395. radx = GetInt8(d->gw,CID_RadDiamX,_("Radius: "),&err);
  396. rady = GetInt8(d->gw,CID_RadDiamY,_("Radius: "),&err);
  397. ang = GetInt8(d->gw,CID_Angle,_("Angle:"),&err);
  398. if ( err )
  399. return( true );
  400. d->cv->p.x = d->cv->info.x = x;
  401. d->cv->p.y = d->cv->info.y = y;
  402. raddiam_x = radx; raddiam_y = rady;
  403. rotate_by = ang;
  404. rectelipse = re;
  405. *d->val = val;
  406. FakeShapeEvents(d->cv);
  407. }
  408. *d->val = val;
  409. d->ret = re;
  410. d->done = true;
  411. if ( !regular_star && d->ispolystar )
  412. star_percent = val2/100;
  413. SavePrefs(true);
  414. }
  415. return( true );
  416. }
  417. static int TA_Cancel(GGadget *g, GEvent *e) {
  418. if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
  419. struct ask_info *d = GDrawGetUserData(GGadgetGetWindow(g));
  420. d->done = true;
  421. }
  422. return( true );
  423. }
  424. static int TA_CenRadChange(GGadget *g, GEvent *e) {
  425. if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
  426. struct ask_info *d = GDrawGetUserData(GGadgetGetWindow(g));
  427. int is_bb = GGadgetIsChecked(d->reg);
  428. GGadgetSetTitle8(GWidgetGetControl(d->gw,CID_CentCornLab),
  429. is_bb ? _("Corner") : _("C_enter"));
  430. GGadgetSetTitle8(GWidgetGetControl(d->gw,CID_RadDiamLab),
  431. is_bb ? _("Diameter:") : _("Radius: "));
  432. }
  433. return( true );
  434. }
  435. static int TA_RadChange(GGadget *g, GEvent *e) {
  436. if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
  437. struct ask_info *d = GDrawGetUserData(GGadgetGetWindow(g));
  438. int is_ellipse = !GGadgetIsChecked(d->rb1);
  439. GGadgetSetEnabled(GWidgetGetControl(d->gw,CID_ValText), !is_ellipse );
  440. GGadgetSetChecked(d->reg,!center_out[is_ellipse]);
  441. GGadgetSetChecked(d->pts,center_out[is_ellipse]);
  442. if ( d->haspos )
  443. TA_CenRadChange(g,e);
  444. }
  445. return( true );
  446. }
  447. static int toolask_e_h(GWindow gw, GEvent *event) {
  448. if ( event->type==et_close ) {
  449. struct ask_info *d = GDrawGetUserData(gw);
  450. d->done = true;
  451. } else if ( event->type == et_map ) {
  452. /* Above palettes */
  453. GDrawRaise(gw);
  454. }
  455. return( event->type!=et_char );
  456. }
  457. static int Ask(char *rb1, char *rb2, int rb, char *lab, float *val, int *co,
  458. int ispolystar, CharView *cv ) {
  459. struct ask_info d;
  460. char buffer[20], buf[20];
  461. char cenx[20], ceny[20], radx[20], rady[20], angle[20];
  462. GRect pos;
  463. GWindowAttrs wattrs;
  464. GGadgetCreateData gcd[19];
  465. GTextInfo label[19];
  466. int off = ((ispolystar&1)?15:0) + ((ispolystar&2)?84:0);
  467. int haspos = (ispolystar&2)?1:0;
  468. ispolystar &= 1;
  469. d.done = false;
  470. d.ret = rb;
  471. d.val = val;
  472. d.co = co;
  473. d.ispolystar = ispolystar;
  474. d.haspos = haspos;
  475. d.lab = lab;
  476. d.cv = cv;
  477. memset(&wattrs,0,sizeof(wattrs));
  478. wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
  479. wattrs.event_masks = ~(1<<et_charup);
  480. wattrs.restrict_input_to_me = 1;
  481. wattrs.undercursor = 1;
  482. wattrs.cursor = ct_pointer;
  483. wattrs.utf8_window_title = _("Shape Type");
  484. wattrs.is_dlg = true;
  485. pos.x = pos.y = 0;
  486. pos.width = GGadgetScale(GDrawPointsToPixels(NULL,190));
  487. pos.height = GDrawPointsToPixels(NULL,120+off);
  488. d.gw = GDrawCreateTopWindow(NULL,&pos,toolask_e_h,&d,&wattrs);
  489. memset(&label,0,sizeof(label));
  490. memset(&gcd,0,sizeof(gcd));
  491. label[0].text = (unichar_t *) rb1;
  492. label[0].text_is_1byte = true;
  493. gcd[0].gd.label = &label[0];
  494. gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 5;
  495. gcd[0].gd.flags = gg_enabled|gg_visible | (rb==0?gg_cb_on:0);
  496. gcd[0].creator = GRadioCreate;
  497. label[1].text = (unichar_t *) rb2;
  498. label[1].text_is_1byte = true;
  499. gcd[1].gd.label = &label[1];
  500. gcd[1].gd.pos.x = ispolystar?65:75; gcd[1].gd.pos.y = 5;
  501. gcd[1].gd.flags = gg_enabled|gg_visible | (rb==1?gg_cb_on:0);
  502. gcd[1].creator = GRadioCreate;
  503. label[2].text = (unichar_t *) lab;
  504. label[2].text_is_1byte = true;
  505. gcd[2].gd.label = &label[2];
  506. gcd[2].gd.pos.x = 5; gcd[2].gd.pos.y = 25;
  507. gcd[2].gd.flags = gg_enabled|gg_visible ;
  508. gcd[2].creator = GLabelCreate;
  509. sprintf( buffer, "%g", *val );
  510. label[3].text = (unichar_t *) buffer;
  511. label[3].text_is_1byte = true;
  512. gcd[3].gd.label = &label[3];
  513. gcd[3].gd.pos.x = 5; gcd[3].gd.pos.y = 40;
  514. gcd[3].gd.flags = gg_enabled|gg_visible ;
  515. gcd[3].gd.cid = CID_ValText;
  516. gcd[3].creator = GTextFieldCreate;
  517. gcd[4].gd.pos.x = 20-3; gcd[4].gd.pos.y = 85+off;
  518. gcd[4].gd.pos.width = -1; gcd[4].gd.pos.height = 0;
  519. gcd[4].gd.flags = gg_visible | gg_enabled | gg_but_default;
  520. label[4].text = (unichar_t *) _("_OK");
  521. label[4].text_is_1byte = true;
  522. label[4].text_in_resource = true;
  523. gcd[4].gd.mnemonic = 'O';
  524. gcd[4].gd.label = &label[4];
  525. gcd[4].gd.handle_controlevent = TA_OK;
  526. gcd[4].creator = GButtonCreate;
  527. gcd[5].gd.pos.x = -20; gcd[5].gd.pos.y = 85+3+off;
  528. gcd[5].gd.pos.width = -1; gcd[5].gd.pos.height = 0;
  529. gcd[5].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
  530. label[5].text = (unichar_t *) _("_Cancel");
  531. label[5].text_is_1byte = true;
  532. label[5].text_in_resource = true;
  533. gcd[5].gd.label = &label[5];
  534. gcd[5].gd.mnemonic = 'C';
  535. gcd[5].gd.handle_controlevent = TA_Cancel;
  536. gcd[5].creator = GButtonCreate;
  537. if ( ispolystar ) {
  538. label[6].text = (unichar_t *) _("Regular");
  539. label[6].text_is_1byte = true;
  540. gcd[6].gd.label = &label[6];
  541. gcd[6].gd.pos.x = 5; gcd[6].gd.pos.y = 70;
  542. gcd[6].gd.flags = gg_enabled|gg_visible | (rb==0?gg_cb_on:0);
  543. gcd[6].creator = GRadioCreate;
  544. label[7].text = (unichar_t *) _("Points:");
  545. label[7].text_is_1byte = true;
  546. gcd[7].gd.label = &label[7];
  547. gcd[7].gd.pos.x = 65; gcd[7].gd.pos.y = 70;
  548. gcd[7].gd.flags = gg_enabled|gg_visible | (rb==1?gg_cb_on:0);
  549. gcd[7].creator = GRadioCreate;
  550. sprintf( buf, "%4g", star_percent*100 );
  551. label[8].text = (unichar_t *) buf;
  552. label[8].text_is_1byte = true;
  553. gcd[8].gd.label = &label[8];
  554. gcd[8].gd.pos.x = 125; gcd[8].gd.pos.y = 70; gcd[8].gd.pos.width=50;
  555. gcd[8].gd.flags = gg_enabled|gg_visible ;
  556. gcd[8].gd.cid = CID_PointPercent;
  557. gcd[8].creator = GTextFieldCreate;
  558. label[9].text = (unichar_t *) "%";
  559. label[9].text_is_1byte = true;
  560. gcd[9].gd.label = &label[9];
  561. gcd[9].gd.pos.x = 180; gcd[9].gd.pos.y = 70;
  562. gcd[9].gd.flags = gg_enabled|gg_visible ;
  563. gcd[9].creator = GLabelCreate;
  564. } else {
  565. label[6].text = (unichar_t *) _("Bounding Box");
  566. label[6].text_is_1byte = true;
  567. gcd[6].gd.label = &label[6];
  568. gcd[6].gd.pos.x = 5; gcd[6].gd.pos.y = 65;
  569. gcd[6].gd.flags = gg_enabled|gg_visible | (co[rb]==0?gg_cb_on:0);
  570. gcd[6].creator = GRadioCreate;
  571. label[7].text = (unichar_t *) _("Center Out");
  572. label[7].text_is_1byte = true;
  573. gcd[7].gd.label = &label[7];
  574. gcd[7].gd.pos.x = 90; gcd[7].gd.pos.y = 65;
  575. gcd[7].gd.flags = gg_enabled|gg_visible | (co[rb]==1?gg_cb_on:0);
  576. gcd[7].creator = GRadioCreate;
  577. if ( rb )
  578. gcd[3].gd.flags = gg_visible;
  579. gcd[0].gd.handle_controlevent = TA_RadChange;
  580. gcd[1].gd.handle_controlevent = TA_RadChange;
  581. if ( haspos ) {
  582. gcd[6].gd.handle_controlevent = TA_CenRadChange;
  583. gcd[7].gd.handle_controlevent = TA_CenRadChange;
  584. label[8].text = (unichar_t *) _("_X");
  585. label[8].text_is_1byte = true;
  586. label[8].text_in_resource = true;
  587. gcd[8].gd.label = &label[8];
  588. gcd[8].gd.pos.x = 70; gcd[8].gd.pos.y = gcd[7].gd.pos.y+15;
  589. gcd[8].gd.flags = gg_enabled|gg_visible;
  590. gcd[8].creator = GLabelCreate;
  591. label[9].text = (unichar_t *) _("_Y");
  592. label[9].text_is_1byte = true;
  593. label[9].text_in_resource = true;
  594. gcd[9].gd.label = &label[9];
  595. gcd[9].gd.pos.x = 120; gcd[9].gd.pos.y = gcd[8].gd.pos.y;
  596. gcd[9].gd.flags = gg_enabled|gg_visible;
  597. gcd[9].creator = GLabelCreate;
  598. label[10].text = (unichar_t *) (co[rb] ? _("C_enter") : _("C_orner") );
  599. label[10].text_is_1byte = true;
  600. label[10].text_in_resource = true;
  601. gcd[10].gd.label = &label[10];
  602. gcd[10].gd.pos.x = 5; gcd[10].gd.pos.y = gcd[8].gd.pos.y+17;
  603. gcd[10].gd.flags = gg_enabled|gg_visible;
  604. gcd[10].gd.cid = CID_CentCornLab;
  605. gcd[10].creator = GLabelCreate;
  606. sprintf( cenx, "%g", (double) cv->info.x );
  607. label[11].text = (unichar_t *) cenx;
  608. label[11].text_is_1byte = true;
  609. gcd[11].gd.label = &label[11];
  610. gcd[11].gd.pos.x = 60; gcd[11].gd.pos.y = gcd[10].gd.pos.y-4;
  611. gcd[11].gd.pos.width = 40;
  612. gcd[11].gd.flags = gg_enabled|gg_visible;
  613. gcd[11].gd.cid = CID_CentCornX;
  614. gcd[11].creator = GTextFieldCreate;
  615. sprintf( ceny, "%g", (double) cv->info.y );
  616. label[12].text = (unichar_t *) ceny;
  617. label[12].text_is_1byte = true;
  618. gcd[12].gd.label = &label[12];
  619. gcd[12].gd.pos.x = 110; gcd[12].gd.pos.y = gcd[11].gd.pos.y;
  620. gcd[12].gd.pos.width = gcd[11].gd.pos.width;
  621. gcd[12].gd.flags = gg_enabled|gg_visible;
  622. gcd[12].gd.cid = CID_CentCornY;
  623. gcd[12].creator = GTextFieldCreate;
  624. label[13].text = (unichar_t *) (co[rb] ? _("Radius: ") : _("Diameter:") );
  625. label[13].text_is_1byte = true;
  626. gcd[13].gd.label = &label[13];
  627. gcd[13].gd.pos.x = 5; gcd[13].gd.pos.y = gcd[10].gd.pos.y+24;
  628. gcd[13].gd.flags = gg_enabled|gg_visible;
  629. gcd[13].gd.cid = CID_RadDiamLab;
  630. gcd[13].creator = GLabelCreate;
  631. sprintf( radx, "%g", (double) raddiam_x );
  632. label[14].text = (unichar_t *) radx;
  633. label[14].text_is_1byte = true;
  634. gcd[14].gd.label = &label[14];
  635. gcd[14].gd.pos.x = gcd[11].gd.pos.x; gcd[14].gd.pos.y = gcd[13].gd.pos.y-4;
  636. gcd[14].gd.pos.width = gcd[11].gd.pos.width;
  637. gcd[14].gd.flags = gg_enabled|gg_visible;
  638. gcd[14].gd.cid = CID_RadDiamX;
  639. gcd[14].creator = GTextFieldCreate;
  640. sprintf( rady, "%g", (double) raddiam_y );
  641. label[15].text = (unichar_t *) rady;
  642. label[15].text_is_1byte = true;
  643. gcd[15].gd.label = &label[15];
  644. gcd[15].gd.pos.x = gcd[12].gd.pos.x; gcd[15].gd.pos.y = gcd[14].gd.pos.y;
  645. gcd[15].gd.pos.width = gcd[11].gd.pos.width;
  646. gcd[15].gd.flags = gg_enabled|gg_visible;
  647. gcd[15].gd.cid = CID_RadDiamY;
  648. gcd[15].creator = GTextFieldCreate;
  649. label[16].text = (unichar_t *) _("Angle:");
  650. label[16].text_is_1byte = true;
  651. gcd[16].gd.label = &label[16];
  652. gcd[16].gd.pos.x = 5; gcd[16].gd.pos.y = gcd[13].gd.pos.y+24;
  653. gcd[16].gd.flags = gg_enabled|gg_visible;
  654. gcd[16].creator = GLabelCreate;
  655. sprintf( angle, "%g", (double) rotate_by );
  656. label[17].text = (unichar_t *) angle;
  657. label[17].text_is_1byte = true;
  658. gcd[17].gd.label = &label[17];
  659. gcd[17].gd.pos.x = 60; gcd[17].gd.pos.y = gcd[16].gd.pos.y-4;
  660. gcd[17].gd.pos.width = gcd[11].gd.pos.width;
  661. gcd[17].gd.flags = gg_enabled|gg_visible;
  662. gcd[17].gd.cid = CID_Angle;
  663. gcd[17].creator = GTextFieldCreate;
  664. }
  665. }
  666. GGadgetsCreate(d.gw,gcd);
  667. d.rb1 = gcd[0].ret;
  668. d.reg = gcd[6].ret;
  669. d.pts = gcd[7].ret;
  670. GWidgetHidePalettes();
  671. GDrawSetVisible(d.gw,true);
  672. while ( !d.done )
  673. GDrawProcessOneEvent(NULL);
  674. GDrawDestroyWindow(d.gw);
  675. return( d.ret );
  676. }
  677. static void CVRectElipse(CharView *cv) {
  678. rectelipse = Ask(_("Rectangle"),_("Ellipse"),rectelipse,
  679. _("Round Rectangle Radius"),&rr_radius,center_out,false, cv);
  680. GDrawRequestExpose(cvtools,NULL,false);
  681. }
  682. void CVRectEllipsePosDlg(CharView *cv) {
  683. rectelipse = Ask(_("Rectangle"),_("Ellipse"),rectelipse,
  684. _("Round Rectangle Radius"),&rr_radius,center_out,2, cv);
  685. GDrawRequestExpose(cvtools,NULL,false);
  686. }
  687. static void CVPolyStar(CharView *cv) {
  688. float temp = ps_pointcnt;
  689. int foo[2];
  690. polystar = Ask(_("Polygon"),_("Star"),polystar,
  691. _("Number of star points/Polygon vertices"),&temp,foo,true, cv);
  692. ps_pointcnt = temp;
  693. }
  694. static void ToolsExpose(GWindow pixmap, CharView *cv, GRect *r) {
  695. GRect old;
  696. /* Note: If you change this ordering, change enum cvtools */
  697. static GImage *normbuttons[][2] = { { &GIcon_pointer, &GIcon_magnify },
  698. { &GIcon_freehand, &GIcon_hand },
  699. { &GIcon_knife, &GIcon_ruler },
  700. { &GIcon_pen, &GIcon_spirodisabled },
  701. { &GIcon_curve, &GIcon_hvcurve },
  702. { &GIcon_corner, &GIcon_tangent},
  703. { &GIcon_scale, &GIcon_rotate },
  704. { &GIcon_flip, &GIcon_skew },
  705. { &GIcon_3drotate, &GIcon_perspective },
  706. { &GIcon_rect, &GIcon_poly},
  707. { &GIcon_elipse, &GIcon_star}};
  708. static GImage *spirobuttons[][2] = { { &GIcon_pointer, &GIcon_magnify },
  709. { &GIcon_freehand, &GIcon_hand },
  710. { &GIcon_knife, &GIcon_ruler },
  711. { &GIcon_spiroright, &GIcon_spirodown },
  712. { &GIcon_spirocurve, &GIcon_spirog2curve },
  713. { &GIcon_spirocorner, &GIcon_spiroleft },
  714. { &GIcon_scale, &GIcon_rotate },
  715. { &GIcon_flip, &GIcon_skew },
  716. { &GIcon_3drotate, &GIcon_perspective },
  717. { &GIcon_rect, &GIcon_poly},
  718. { &GIcon_elipse, &GIcon_star}};
  719. static GImage *normsmalls[] = { &GIcon_smallpointer, &GIcon_smallmag,
  720. &GIcon_smallpencil, &GIcon_smallhand,
  721. &GIcon_smallknife, &GIcon_smallruler,
  722. &GIcon_smallpen, NULL,
  723. &GIcon_smallcurve, &GIcon_smallhvcurve,
  724. &GIcon_smallcorner, &GIcon_smalltangent,
  725. &GIcon_smallscale, &GIcon_smallrotate,
  726. &GIcon_smallflip, &GIcon_smallskew,
  727. &GIcon_small3drotate, &GIcon_smallperspective,
  728. &GIcon_smallrect, &GIcon_smallpoly,
  729. &GIcon_smallelipse, &GIcon_smallstar };
  730. static GImage *spirosmalls[] = { &GIcon_smallpointer, &GIcon_smallmag,
  731. &GIcon_smallpencil, &GIcon_smallhand,
  732. &GIcon_smallknife, &GIcon_smallruler,
  733. &GIcon_smallspiroright, NULL,
  734. &GIcon_smallspirocurve, &GIcon_smallspirog2curve,
  735. &GIcon_smallspirocorner, &GIcon_smallspiroleft,
  736. &GIcon_smallscale, &GIcon_smallrotate,
  737. &GIcon_smallflip, &GIcon_smallskew,
  738. &GIcon_small3drotate, &GIcon_smallperspective,
  739. &GIcon_smallrect, &GIcon_smallpoly,
  740. &GIcon_smallelipse, &GIcon_smallstar };
  741. static const unichar_t _Mouse[][9] = {
  742. { 'M', 's', 'e', '1', '\0' },
  743. { '^', 'M', 's', 'e', '1', '\0' },
  744. { 'M', 's', 'e', '2', '\0' },
  745. { '^', 'M', 's', 'e', '2', '\0' }};
  746. int i,j,norm, mi;
  747. int tool = cv->cntrldown?cv->cb1_tool:cv->b1_tool;
  748. int dither = GDrawSetDither(NULL,false);
  749. GRect temp;
  750. int canspiro = hasspiro(), inspiro = canspiro && cv->b.sc->inspiro;
  751. GImage *(*buttons)[2] = inspiro ? spirobuttons : normbuttons;
  752. GImage **smalls = inspiro ? spirosmalls : normsmalls;
  753. normbuttons[3][1] = canspiro ? &GIcon_spiroup : &GIcon_spirodisabled;
  754. GDrawPushClip(pixmap,r,&old);
  755. GDrawFillRect(pixmap,r,GDrawGetDefaultBackground(NULL));
  756. GDrawSetLineWidth(pixmap,0);
  757. for ( i=0; i<sizeof(normbuttons)/sizeof(normbuttons[0])-1; ++i ) for ( j=0; j<2; ++j ) {
  758. mi = i;
  759. if ( i==(cvt_rect)/2 && ((j==0 && rectelipse) || (j==1 && polystar)) )
  760. ++mi;
  761. /* if ( cv->b.sc->parent->order2 && buttons[mi][j]==&GIcon_freehand ) */
  762. /* GDrawDrawImage(pixmap,&GIcon_greyfree,NULL,j*27+1,i*27+1); */
  763. /* else */
  764. GDrawDrawImage(pixmap,buttons[mi][j],NULL,j*27+1,i*27+1);
  765. norm = (mi*2+j!=tool);
  766. GDrawDrawLine(pixmap,j*27,i*27,j*27+25,i*27,norm?0xe0e0e0:0x707070);
  767. GDrawDrawLine(pixmap,j*27,i*27,j*27,i*27+25,norm?0xe0e0e0:0x707070);
  768. GDrawDrawLine(pixmap,j*27,i*27+25,j*27+25,i*27+25,norm?0x707070:0xe0e0e0);
  769. GDrawDrawLine(pixmap,j*27+25,i*27,j*27+25,i*27+25,norm?0x707070:0xe0e0e0);
  770. }
  771. GDrawSetFont(pixmap,toolsfont);
  772. temp.x = 52-16; temp.y = i*27; temp.width = 16; temp.height = 4*12;
  773. GDrawFillRect(pixmap,&temp,GDrawGetDefaultBackground(NULL));
  774. for ( j=0; j<4; ++j ) {
  775. GDrawDrawText(pixmap,2,i*27+j*12+10,(unichar_t *) _Mouse[j],-1,GDrawGetDefaultForeground(NULL));
  776. if ( (&cv->b1_tool)[j]!=cvt_none && smalls[(&cv->b1_tool)[j]])
  777. GDrawDrawImage(pixmap,smalls[(&cv->b1_tool)[j]],NULL,52-16,i*27+j*12);
  778. }
  779. GDrawPopClip(pixmap,&old);
  780. GDrawSetDither(NULL,dither);
  781. }
  782. int TrueCharState(GEvent *event) {
  783. int bit = 0;
  784. /* X doesn't set the state until after the event. I want the state to */
  785. /* reflect whatever key just got depressed/released */
  786. int keysym = event->u.chr.keysym;
  787. if ( keysym == GK_Caps_Lock || keysym == GK_Shift_Lock ) {
  788. static int set_on_last_down = false;
  789. /* caps lock is sticky and doesn't work like the other modifiers */
  790. /* but it is even worse. the bit seems to be set on key down, but */
  791. /* unset on key up. In other words on key up, the bit will always */
  792. /* set and we have no idea which way it will go. So we guess, and */
  793. /* if they haven't messed with the key outside ff we should be right */
  794. if ( event->type == et_char ) {
  795. set_on_last_down = (event->u.chr.state ^ ksm_capslock)& ksm_capslock;
  796. return( event->u.chr.state ^ ksm_capslock );
  797. } else if ( !(event->u.chr.state & ksm_capslock) || set_on_last_down )
  798. return( event->u.chr.state );
  799. else
  800. return( event->u.chr.state & ~ksm_capslock );
  801. }
  802. if ( keysym == GK_Meta_L || keysym == GK_Meta_R ||
  803. keysym == GK_Alt_L || keysym == GK_Alt_R )
  804. bit = ksm_meta;
  805. else if ( keysym == GK_Shift_L || keysym == GK_Shift_R )
  806. bit = ksm_shift;
  807. else if ( keysym == GK_Control_L || keysym == GK_Control_R )
  808. bit = ksm_control;
  809. else if ( keysym == GK_Super_L || keysym == GK_Super_L )
  810. bit = ksm_super;
  811. else if ( keysym == GK_Hyper_L || keysym == GK_Hyper_L )
  812. bit = ksm_hyper;
  813. else
  814. return( event->u.chr.state );
  815. if ( event->type == et_char )
  816. return( event->u.chr.state | bit );
  817. else
  818. return( event->u.chr.state & ~bit );
  819. }
  820. void CVToolsSetCursor(CharView *cv, int state, char *device) {
  821. int shouldshow;
  822. int cntrl;
  823. if ( tools[0] == ct_pointer ) {
  824. tools[cvt_pointer] = ct_mypointer;
  825. tools[cvt_magnify] = ct_magplus;
  826. tools[cvt_freehand] = ct_pencil;
  827. tools[cvt_hand] = ct_myhand;
  828. tools[cvt_curve] = ct_circle;
  829. tools[cvt_hvcurve] = ct_hvcircle;
  830. tools[cvt_corner] = ct_square;
  831. tools[cvt_tangent] = ct_triangle;
  832. tools[cvt_pen] = ct_pen;
  833. tools[cvt_knife] = ct_knife;
  834. tools[cvt_ruler] = ct_ruler;
  835. tools[cvt_scale] = ct_scale;
  836. tools[cvt_flip] = ct_flip;
  837. tools[cvt_rotate] = ct_rotate;
  838. tools[cvt_skew] = ct_skew;
  839. tools[cvt_3d_rotate] = ct_3drotate;
  840. tools[cvt_perspective] = ct_perspective;
  841. tools[cvt_rect] = ct_rect;
  842. tools[cvt_poly] = ct_poly;
  843. tools[cvt_elipse] = ct_elipse;
  844. tools[cvt_star] = ct_star;
  845. tools[cvt_minify] = ct_magminus;
  846. memcpy(spirotools,tools,sizeof(tools));
  847. spirotools[cvt_spirog2] = ct_g2circle;
  848. spirotools[cvt_spiroleft] = ct_spiroleft;
  849. spirotools[cvt_spiroright] = ct_spiroright;
  850. }
  851. shouldshow = cvt_none;
  852. if ( cv->active_tool!=cvt_none )
  853. shouldshow = cv->active_tool;
  854. else if ( cv->pressed_display!=cvt_none )
  855. shouldshow = cv->pressed_display;
  856. else if ( device==NULL || strcmp(device,"Mouse1")==0 ) {
  857. if ( (state&(ksm_shift|ksm_control)) && (state&ksm_button4))
  858. shouldshow = cvt_magnify;
  859. else if ( (state&(ksm_shift|ksm_control)) && (state&ksm_button5))
  860. shouldshow = cvt_minify;
  861. else if ( (state&ksm_control) && (state&(ksm_button2|ksm_super)) )
  862. shouldshow = cv->cb2_tool;
  863. else if ( (state&(ksm_button2|ksm_super)) )
  864. shouldshow = cv->b2_tool;
  865. else if ( (state&ksm_control) )
  866. shouldshow = cv->cb1_tool;
  867. else
  868. shouldshow = cv->b1_tool;
  869. } else if ( strcmp(device,"eraser")==0 )
  870. shouldshow = cv->er_tool;
  871. else if ( strcmp(device,"stylus")==0 ) {
  872. if ( (state&(ksm_button2|ksm_control|ksm_super)) )
  873. shouldshow = cv->s2_tool;
  874. else
  875. shouldshow = cv->s1_tool;
  876. }
  877. if ( shouldshow==cvt_magnify && (state&ksm_meta))
  878. shouldshow = cvt_minify;
  879. if ( shouldshow!=cv->showing_tool ) {
  880. CPEndInfo(cv);
  881. if ( cv->b.sc->inspiro && hasspiro()) {
  882. GDrawSetCursor(cv->v,spirotools[shouldshow]);
  883. if ( cvtools!=NULL ) /* Might happen if window owning docked palette destroyed */
  884. GDrawSetCursor(cvtools,spirotools[shouldshow]);
  885. } else {
  886. GDrawSetCursor(cv->v,tools[shouldshow]);
  887. if ( cvtools!=NULL ) /* Might happen if window owning docked palette destroyed */
  888. GDrawSetCursor(cvtools,tools[shouldshow]);
  889. }
  890. cv->showing_tool = shouldshow;
  891. }
  892. if ( device==NULL || strcmp(device,"stylus")==0 ) {
  893. cntrl = (state&ksm_control)?1:0;
  894. if ( device!=NULL && (state&ksm_button2))
  895. cntrl = true;
  896. if ( cntrl != cv->cntrldown ) {
  897. cv->cntrldown = cntrl;
  898. GDrawRequestExpose(cvtools,NULL,false);
  899. }
  900. }
  901. }
  902. static int CVCurrentTool(CharView *cv, GEvent *event) {
  903. if ( event->u.mouse.device!=NULL && strcmp(event->u.mouse.device,"eraser")==0 )
  904. return( cv->er_tool );
  905. else if ( event->u.mouse.device!=NULL && strcmp(event->u.mouse.device,"stylus")==0 ) {
  906. if ( event->u.mouse.button==2 )
  907. /* Only thing that matters is touch which maps to button 1 */;
  908. else if ( cv->had_control )
  909. return( cv->s2_tool );
  910. else
  911. return( cv->s1_tool );
  912. }
  913. if ( cv->had_control && event->u.mouse.button==2 )
  914. return( cv->cb2_tool );
  915. else if ( event->u.mouse.button==2 )
  916. return( cv->b2_tool );
  917. else if ( cv->had_control ) {
  918. return( cv->cb1_tool );
  919. } else {
  920. return( cv->b1_tool );
  921. }
  922. }
  923. static void SCCheckForSSToOptimize(SplineChar *sc, SplineSet *ss,int order2) {
  924. for ( ; ss!=NULL ; ss = ss->next ) {
  925. if ( ss->beziers_need_optimizer ) {
  926. SplineSetAddExtrema(sc,ss,ae_only_good,sc->parent->ascent+sc->parent->descent);
  927. ss->beziers_need_optimizer = false;
  928. }
  929. if ( order2 && ss->first->next!=NULL && !ss->first->next->order2 ) {
  930. SplineSet *temp = SSttfApprox(ss), foo;
  931. foo = *ss;
  932. ss->first = temp->first; ss->last = temp->last;
  933. temp->first = foo.first; temp->last = foo.last;
  934. SplinePointListFree(temp);
  935. }
  936. }
  937. }
  938. static void CVChangeSpiroMode(CharView *cv) {
  939. if ( hasspiro() ) {
  940. cv->b.sc->inspiro = !cv->b.sc->inspiro;
  941. cv->showing_tool = cvt_none;
  942. CVClearSel(cv);
  943. if ( !cv->b.sc->inspiro )
  944. SCCheckForSSToOptimize(cv->b.sc,cv->b.layerheads[cv->b.drawmode]->splines,
  945. cv->b.layerheads[cv->b.drawmode]->order2);
  946. GDrawRequestExpose(cvtools,NULL,false);
  947. SCUpdateAll(cv->b.sc);
  948. } else
  949. #ifdef _NO_LIBSPIRO
  950. ff_post_error(_("You may not use spiros"),_("This version of fontforge was not linked with the spiro library, so you may not use them."));
  951. #else
  952. ff_post_error(_("You may not use spiros"),_("FontForge was unable to load libspiro, spiros are not available for use."));
  953. #endif
  954. }
  955. static void ToolsMouse(CharView *cv, GEvent *event) {
  956. int i = (event->u.mouse.y/27), j = (event->u.mouse.x/27), mi=i;
  957. int pos;
  958. int isstylus = event->u.mouse.device!=NULL && strcmp(event->u.mouse.device,"stylus")==0;
  959. int styluscntl = isstylus && (event->u.mouse.state&0x200);
  960. static int settings[2];
  961. if(j >= 2)
  962. return; /* If the wm gave me a window the wrong size */
  963. if ( i==(cvt_rect)/2 ) {
  964. int current = CVCurrentTool(cv,event);
  965. int changed = false;
  966. if ( event->type == et_mousedown && event->u.mouse.clicks>=2 ) {
  967. rectelipse = settings[0];
  968. polystar = settings[1];
  969. } else if ( event->type == et_mousedown ) {
  970. settings[0] = rectelipse; settings[1] = polystar;
  971. /* A double click will change the type twice, which leaves it where it was, which is desired */
  972. if ( j==0 && ((!rectelipse && current==cvt_rect) || (rectelipse && current==cvt_elipse)) ) {
  973. rectelipse = !rectelipse;
  974. changed = true;
  975. } else if (j==1 && ((!polystar && current==cvt_poly) || (polystar && current==cvt_star)) ) {
  976. polystar = !polystar;
  977. changed = true;
  978. }
  979. if ( changed ) {
  980. SavePrefs(true);
  981. GDrawRequestExpose(cvtools,NULL,false);
  982. }
  983. }
  984. if ( (j==0 && rectelipse) || (j==1 && polystar) )
  985. ++mi;
  986. }
  987. pos = mi*2 + j;
  988. GGadgetEndPopup();
  989. /* we have two fewer buttons than commands as two bottons each control two commands */
  990. if ( pos<0 || pos>=cvt_max )
  991. pos = cvt_none;
  992. if ( event->type == et_mousedown ) {
  993. if ( isstylus && event->u.mouse.button==2 )
  994. /* Not a real button press, only touch counts. This is a modifier */;
  995. else if ( pos==cvt_spiro ) {
  996. CVChangeSpiroMode(cv);
  997. /* This is just a button that indicates a state */
  998. } else {
  999. cv->pressed_tool = cv->pressed_display = pos;
  1000. cv->had_control = ((event->u.mouse.state&ksm_control) || styluscntl)?1:0;
  1001. event->u.mouse.state |= (1<<(7+event->u.mouse.button));
  1002. }
  1003. if ( event->u.mouse.clicks>=2 &&
  1004. (pos/2 == cvt_scale/2 || pos/2 == cvt_rotate/2 || pos == cvt_3d_rotate ))
  1005. CVDoTransform(cv,pos);
  1006. } else if ( event->type == et_mousemove ) {
  1007. if ( cv->pressed_tool==cvt_none && pos!=cvt_none ) {
  1008. /* Not pressed */
  1009. char *msg = _(popupsres[pos]);
  1010. if ( cv->b.sc->inspiro && hasspiro()) {
  1011. if ( pos==cvt_spirog2 )
  1012. msg = _("Add a g2 curve point");
  1013. else if ( pos==cvt_spiroleft )
  1014. msg = _("Add a prev constraint point (sometimes like a tangent)");
  1015. else if ( pos==cvt_spiroright )
  1016. msg = _("Add a next constraint point (sometimes like a tangent)");
  1017. }
  1018. GGadgetPreparePopup8(cvtools,msg);
  1019. } else if ( pos!=cv->pressed_tool || cv->had_control != (((event->u.mouse.state&ksm_control) || styluscntl)?1:0) )
  1020. cv->pressed_display = cvt_none;
  1021. else
  1022. cv->pressed_display = cv->pressed_tool;
  1023. } else if ( event->type == et_mouseup ) {
  1024. if ( pos==cvt_freehand && event->u.mouse.clicks==2 ) {
  1025. FreeHandStrokeDlg(&expand);
  1026. } else if ( pos==cvt_pointer && event->u.mouse.clicks==2 ) {
  1027. PointerDlg(cv);
  1028. } else if ( pos==cvt_ruler && event->u.mouse.clicks==2 ) {
  1029. RulerDlg(cv);
  1030. } else if ( i==cvt_rect/2 && event->u.mouse.clicks==2 ) {
  1031. ((j==0)?CVRectElipse:CVPolyStar)(cv);
  1032. mi = i;
  1033. if ( (j==0 && rectelipse) || (j==1 && polystar) )
  1034. ++mi;
  1035. pos = mi*2 + j;

Large files files are truncated, but you can click here to view the full file