Picture.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /* Picture.cpp
  2. *
  3. * Copyright (C) 1992-2018 Paul Boersma, 2008 Stefan de Konink, 2010 Franz Brauße
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "melder.h"
  19. #include "Gui.h"
  20. #include "Printer.h"
  21. #include "Picture.h"
  22. #include "site.h"
  23. #ifdef _WIN32
  24. #include "GraphicsP.h"
  25. #endif
  26. Thing_implement (Picture, Thing, 0);
  27. static void drawMarkers (Picture me)
  28. /*
  29. * The drawing area is a square measuring 12x12 inches.
  30. */
  31. #define SIDE 12
  32. /*
  33. * The selection grid has a resolution of 1/2 inch.
  34. */
  35. #define SQUARES 24
  36. /*
  37. * Vertical and horizontal lines every 3 inches.
  38. */
  39. #define YELLOW_GRID 3
  40. {
  41. /* Fill the entire canvas with GC's background. */
  42. Graphics_setColour (my selectionGraphics.get(), Graphics_WHITE);
  43. Graphics_fillRectangle (my selectionGraphics.get(), 0, SIDE, 0, SIDE);
  44. /* Draw yellow grid lines for coarse navigation. */
  45. Graphics_setColour (my selectionGraphics.get(), Graphics_YELLOW);
  46. for (int i = YELLOW_GRID; i < SIDE; i += YELLOW_GRID) {
  47. Graphics_line (my selectionGraphics.get(), 0, i, SIDE, i);
  48. Graphics_line (my selectionGraphics.get(), i, 0, i, SIDE);
  49. }
  50. /* Draw red ticks and numbers for feedback on viewport measurement. */
  51. Graphics_setColour (my selectionGraphics.get(), Graphics_RED);
  52. for (int i = 1; i < SIDE; i ++) {
  53. double x = i;
  54. Graphics_setTextAlignment (my selectionGraphics.get(), Graphics_CENTRE, Graphics_TOP);
  55. Graphics_text (my selectionGraphics.get(), x, SIDE, i);
  56. Graphics_setTextAlignment (my selectionGraphics.get(), Graphics_CENTRE, Graphics_BOTTOM);
  57. Graphics_text (my selectionGraphics.get(), x, 0, i);
  58. }
  59. for (int i = 1; i < SQUARES ; i ++) { // vertical ticks
  60. double x = 0.5 * i;
  61. Graphics_line (my selectionGraphics.get(), x, SIDE - 0.04, x, SIDE);
  62. Graphics_line (my selectionGraphics.get(), x, 0, x, 0.04);
  63. }
  64. for (int i = 1; i < SIDE; i ++) {
  65. double y = SIDE - i;
  66. Graphics_setTextAlignment (my selectionGraphics.get(), Graphics_LEFT, Graphics_HALF);
  67. Graphics_text (my selectionGraphics.get(), 0.04, y, i);
  68. Graphics_setTextAlignment (my selectionGraphics.get(), Graphics_RIGHT, Graphics_HALF);
  69. Graphics_text (my selectionGraphics.get(), SIDE - 0.03, y, i);
  70. }
  71. for (int i = 1; i < SQUARES; i ++) { // horizontal ticks
  72. double y = SIDE - 0.5 * i;
  73. Graphics_line (my selectionGraphics.get(), SIDE - 0.04, y, SIDE, y);
  74. Graphics_line (my selectionGraphics.get(), 0, y, 0.04, y);
  75. }
  76. Graphics_setColour (my selectionGraphics.get(), Graphics_BLACK);
  77. }
  78. static void drawSelection (Picture me, int high) {
  79. if (my backgrounding) return;
  80. double dy = 2.8 * Graphics_inqFontSize (my graphics.get()) / 72.0;
  81. double dx = 1.5 * dy;
  82. if (dy > 0.4 * (my sely2 - my sely1)) dy = 0.4 * (my sely2 - my sely1);
  83. if (dx > 0.4 * (my selx2 - my selx1)) dx = 0.4 * (my selx2 - my selx1);
  84. if (high) {
  85. Graphics_highlight2 (my selectionGraphics.get(), my selx1, my selx2, my sely1, my sely2,
  86. my selx1 + dx, my selx2 - dx, my sely1 + dy, my sely2 - dy);
  87. } else {
  88. Graphics_unhighlight2 (my selectionGraphics.get(), my selx1, my selx2, my sely1, my sely2,
  89. my selx1 + dx, my selx2 - dx, my sely1 + dy, my sely2 - dy);
  90. }
  91. }
  92. //static double test = 0.0;
  93. static void gui_drawingarea_cb_expose (Picture me, GuiDrawingArea_ExposeEvent event) {
  94. #if gtk
  95. /*
  96. * The size of the viewable part of the drawing area may have changed.
  97. */
  98. Melder_assert (event -> widget);
  99. #if ALLOW_GDK_DRAWING
  100. gdk_cairo_reset_clip ((cairo_t *) Graphics_x_getCR (my graphics.get()), GDK_DRAWABLE (GTK_WIDGET (event -> widget -> d_widget) -> window));
  101. gdk_cairo_reset_clip ((cairo_t *) Graphics_x_getCR (my selectionGraphics.get()), GDK_DRAWABLE (GTK_WIDGET (event -> widget -> d_widget) -> window));
  102. #endif
  103. #else
  104. (void) event;
  105. #endif
  106. drawMarkers (me);
  107. Graphics_play (my graphics.get(), my graphics.get());
  108. drawSelection (me, 1);
  109. }
  110. // TODO: Paul, als praat nu 100dpi zou zijn waarom zie ik hier dan nog 72.0 onder?
  111. // Stefan, die 72.0 is het aantal font-punten per inch,
  112. // gewoon een vaste verhouding die niks met pixels te maken heeft.
  113. // TODO: Paul, deze code is bagger :) En dient door event-model-extremisten te worden veroordeeld.
  114. // Stefan, zoals gezegd, er zijn goede redenen waarom sommige platforms dit synchroon oplossen;
  115. // misschien maar splitsen tussen die platforms en platforms die met events kunnen werken.
  116. // (Tom:) On Cocoa this leads to flashing, it definitely needs to be event based.
  117. // (Paul:) No, running this through the normal event loop with mouse-down-drag-up events and generating exposes
  118. // redrew the entire picture window on every change of the selection during dragging. Much too slow!
  119. static void gui_drawingarea_cb_click (Picture me, GuiDrawingArea_ClickEvent event) {
  120. int xstart = event -> x;
  121. int ystart = event -> y;
  122. double xWC, yWC;
  123. int ixstart, iystart, ix, iy, oldix = 0, oldiy = 0;
  124. Graphics_DCtoWC (my selectionGraphics.get(), xstart, ystart, & xWC, & yWC);
  125. ix = ixstart = 1 + (int) floor (xWC * SQUARES / SIDE);
  126. iy = iystart = SQUARES - (int) floor (yWC * SQUARES / SIDE);
  127. if (ixstart < 1 || ixstart > SQUARES || iystart < 1 || iystart > SQUARES) return;
  128. if (event -> shiftKeyPressed) {
  129. int ix1 = 1 + (int) floor (my selx1 * SQUARES / SIDE);
  130. int ix2 = (int) floor (my selx2 * SQUARES / SIDE);
  131. int iy1 = SQUARES + 1 - (int) floor (my sely2 * SQUARES / SIDE);
  132. int iy2 = SQUARES - (int) floor (my sely1 * SQUARES / SIDE);
  133. ixstart = ix < (ix1 + ix2) / 2 ? ix2 : ix1;
  134. iystart = iy < (iy1 + iy2) / 2 ? iy2 : iy1;
  135. }
  136. //while (Graphics_mouseStillDown (my selectionGraphics)) {
  137. do {
  138. Graphics_getMouseLocation (my selectionGraphics.get(), & xWC, & yWC);
  139. ix = 1 + (int) floor (xWC * SQUARES / SIDE);
  140. iy = SQUARES - (int) floor (yWC * SQUARES / SIDE);
  141. if (ix >= 1 && ix <= SQUARES && iy >= 1 && iy <= SQUARES && (ix != oldix || iy != oldiy)) {
  142. int ix1, ix2, iy1, iy2;
  143. if (ix < ixstart) { ix1 = ix; ix2 = ixstart; }
  144. else { ix1 = ixstart; ix2 = ix; }
  145. if (iy < iystart) { iy1 = iy; iy2 = iystart; }
  146. else { iy1 = iystart; iy2 = iy; }
  147. if (my mouseSelectsInnerViewport) {
  148. int fontSize = Graphics_inqFontSize (my graphics.get());
  149. double xmargin = fontSize * 4.2 / 72.0, ymargin = fontSize * 2.8 / 72.0;
  150. if (xmargin > ix2 - ix1 + 1) xmargin = ix2 - ix1 + 1;
  151. if (ymargin > iy2 - iy1 + 1) ymargin = iy2 - iy1 + 1;
  152. Picture_setSelection (me, 0.5 * (ix1 - 1) - xmargin, 0.5 * ix2 + xmargin,
  153. 0.5 * (SQUARES - iy2) - ymargin, 0.5 * (SQUARES + 1 - iy1) + ymargin, false);
  154. } else {
  155. Picture_setSelection (me, 0.5 * (ix1 - 1), 0.5 * ix2,
  156. 0.5 * (SQUARES - iy2), 0.5 * (SQUARES + 1 - iy1), false);
  157. }
  158. oldix = ix; oldiy = iy;
  159. }
  160. } while (Graphics_mouseStillDown (my selectionGraphics.get()));
  161. // }
  162. #if cocoa
  163. Graphics_updateWs (my selectionGraphics.get()); // to change the dark red back into black
  164. #endif
  165. if (my selectionChangedCallback) {
  166. //Melder_casual (U"selectionChangedCallback from gui_drawingarea_cb_click");
  167. my selectionChangedCallback (me, my selectionChangedClosure,
  168. my selx1, my selx2, my sely1, my sely2);
  169. }
  170. }
  171. autoPicture Picture_create (GuiDrawingArea drawingArea, bool sensitive) {
  172. try {
  173. autoPicture me = Thing_new (Picture);
  174. my drawingArea = drawingArea;
  175. /*
  176. * The initial viewport is a rectangle 6 inches wide and 4 inches high.
  177. */
  178. my selx1 = 0.0;
  179. my selx2 = 6.0;
  180. my sely1 = 8.0;
  181. my sely2 = 12.0;
  182. my sensitive = sensitive && drawingArea;
  183. if (drawingArea) {
  184. /* The drawing area must have been realized; see manual at XtWindow. */
  185. my graphics = Graphics_create_xmdrawingarea (my drawingArea);
  186. GuiDrawingArea_setExposeCallback (my drawingArea, gui_drawingarea_cb_expose, me.get());
  187. } else {
  188. /*
  189. * Create a dummy Graphics.
  190. */
  191. my graphics = Graphics_create (600);
  192. }
  193. Graphics_setWsWindow (my graphics.get(), 0.0, 12.0, 0.0, 12.0);
  194. Graphics_setViewport (my graphics.get(), my selx1, my selx2, my sely1, my sely2);
  195. if (my sensitive) {
  196. my selectionGraphics = Graphics_create_xmdrawingarea (my drawingArea);
  197. Graphics_setWindow (my selectionGraphics.get(), 0, 12, 0, 12);
  198. GuiDrawingArea_setClickCallback (my drawingArea, gui_drawingarea_cb_click, me.get());
  199. }
  200. Graphics_startRecording (my graphics.get());
  201. return me;
  202. } catch (MelderError) {
  203. Melder_throw (U"Picture not created.");
  204. }
  205. }
  206. void Picture_setSelectionChangedCallback (Picture me,
  207. void (*selectionChangedCallback) (Picture, void *, double, double, double, double),
  208. void *selectionChangedClosure)
  209. {
  210. my selectionChangedCallback = selectionChangedCallback;
  211. my selectionChangedClosure = selectionChangedClosure;
  212. }
  213. void Picture_setMouseSelectsInnerViewport (Picture me, int mouseSelectsInnerViewport) {
  214. my mouseSelectsInnerViewport = mouseSelectsInnerViewport;
  215. }
  216. void structPicture :: v_destroy () noexcept {
  217. //Picture_erase (this); // dangerous if called from automatic destructor
  218. Picture_Parent :: v_destroy ();
  219. }
  220. Graphics Picture_peekGraphics (Picture me) { return my graphics.get(); }
  221. void Picture_unhighlight (Picture me) {
  222. if (my drawingArea) drawSelection (me, 0); // unselect
  223. }
  224. void Picture_highlight (Picture me) {
  225. if (my drawingArea) drawSelection (me, 1); // select
  226. }
  227. void Picture_erase (Picture me) {
  228. Graphics_clearRecording (my graphics.get());
  229. Graphics_clearWs (my graphics.get());
  230. if (my drawingArea) {
  231. drawMarkers (me);
  232. drawSelection (me, 1);
  233. }
  234. }
  235. void Picture_writeToPraatPictureFile (Picture me, MelderFile file) {
  236. try {
  237. autofile f = Melder_fopen (file, "wb");
  238. if (fprintf (f, "PraatPictureFile") < 0) Melder_throw (U"Write error.");
  239. Graphics_writeRecordings (my graphics.get(), f);
  240. f.close (file);
  241. } catch (MelderError) {
  242. Melder_throw (U"Cannot write Praat picture file ", file, U".");
  243. }
  244. }
  245. void Picture_readFromPraatPictureFile (Picture me, MelderFile file) {
  246. try {
  247. autofile f = Melder_fopen (file, "rb");
  248. char line [200];
  249. int n = fread (line, 1, 199, f);
  250. line [n] = '\0';
  251. const char *tag = "PraatPictureFile";
  252. char *end = strstr (line, tag);
  253. if (! end) Melder_throw (U"This is not a Praat picture file.");
  254. *end = '\0';
  255. rewind (f);
  256. fread (line, 1, end - line + strlen (tag), f);
  257. Graphics_readRecordings (my graphics.get(), f);
  258. Graphics_updateWs (my graphics.get());
  259. f.close (file);
  260. } catch (MelderError) {
  261. Melder_throw (U"Praat picture not read from file ", file);
  262. }
  263. }
  264. #ifdef macintosh
  265. static size_t appendBytes (void *info, const void *buffer, size_t count) {
  266. CFDataAppendBytes ((CFMutableDataRef) info, (const UInt8 *) buffer, count);
  267. return count;
  268. }
  269. void Picture_copyToClipboard (Picture me) {
  270. /*
  271. * Find the clipboard and clear it.
  272. */
  273. PasteboardRef clipboard = nullptr;
  274. PasteboardCreate (kPasteboardClipboard, & clipboard);
  275. PasteboardClear (clipboard);
  276. /*
  277. * Add a PDF flavour to the clipboard.
  278. */
  279. static CGDataConsumerCallbacks callbacks = { appendBytes, nullptr };
  280. CFDataRef data = CFDataCreateMutable (kCFAllocatorDefault, 0);
  281. CGDataConsumerRef consumer = CGDataConsumerCreate ((void *) data, & callbacks);
  282. int resolution = 600;
  283. CGRect rect = CGRectMake (0, 0, (my selx2 - my selx1) * resolution, (my sely1 - my sely2) * resolution);
  284. CGContextRef context = CGPDFContextCreate (consumer, & rect, nullptr);
  285. //my selx1 * RES, (12 - my sely2) * RES, my selx2 * RES, (12 - my sely1) * RES)
  286. {// scope
  287. autoGraphics graphics = Graphics_create_pdf (context, resolution, my selx1, my selx2, my sely1, my sely2);
  288. Graphics_play (my graphics.get(), graphics.get());
  289. }
  290. PasteboardPutItemFlavor (clipboard, (PasteboardItemID) 1, kUTTypePDF, data, kPasteboardFlavorNoFlags);
  291. CFRelease (data);
  292. /*
  293. * Forget the clipboard.
  294. */
  295. CFRelease (clipboard);
  296. }
  297. #endif
  298. #ifdef _WIN32
  299. /* Windows pictures.
  300. */
  301. #define WIN_WIDTH 7.5
  302. #define WIN_HEIGHT 11
  303. static HENHMETAFILE copyToMetafile (Picture me) {
  304. RECT rect;
  305. HDC dc;
  306. PRINTDLG defaultPrinter;
  307. int resolution;
  308. memset (& defaultPrinter, 0, sizeof (PRINTDLG));
  309. defaultPrinter. lStructSize = sizeof (PRINTDLG);
  310. defaultPrinter. Flags = PD_RETURNDEFAULT | PD_RETURNDC;
  311. PrintDlg (& defaultPrinter);
  312. SetRect (& rect, my selx1 * 2540, (12 - my sely2) * 2540, my selx2 * 2540, (12 - my sely1) * 2540);
  313. dc = CreateEnhMetaFile (defaultPrinter. hDC, nullptr, & rect, L"Praat\0");
  314. if (! dc) Melder_throw (U"Cannot create Windows metafile.");
  315. resolution = GetDeviceCaps (dc, LOGPIXELSX); // Virtual PC: 360; Parallels Desktop: 600
  316. //Melder_fatal (U"resolution ", resolution);
  317. if (Melder_debug == 6) {
  318. DEVMODE *devMode = * (DEVMODE **) defaultPrinter. hDevMode;
  319. MelderInfo_open ();
  320. MelderInfo_writeLine (U"DEVICE CAPS:");
  321. MelderInfo_writeLine (U"aspect x ", GetDeviceCaps (dc, ASPECTX),
  322. U" y ", GetDeviceCaps (dc, ASPECTY));
  323. MelderInfo_writeLine (U"res(pixels) hor ", GetDeviceCaps (dc, HORZRES),
  324. U" vert ", GetDeviceCaps (dc, VERTRES));
  325. MelderInfo_writeLine (U"size(mm) hor ", GetDeviceCaps (dc, HORZSIZE),
  326. U" vert ", GetDeviceCaps (dc, VERTSIZE));
  327. MelderInfo_writeLine (U"pixels/inch hor ", GetDeviceCaps (dc, LOGPIXELSX),
  328. U" vert ", GetDeviceCaps (dc, LOGPIXELSY));
  329. MelderInfo_writeLine (U"physicalOffset(pixels) hor ", GetDeviceCaps (dc, PHYSICALOFFSETX),
  330. U" vert ", GetDeviceCaps (dc, PHYSICALOFFSETY));
  331. MelderInfo_writeLine (U"PRINTER:");
  332. MelderInfo_writeLine (U"dmFields ", devMode -> dmFields);
  333. if (devMode -> dmFields & DM_YRESOLUTION)
  334. MelderInfo_writeLine (U"y resolution ", devMode -> dmYResolution);
  335. if (devMode -> dmFields & DM_PRINTQUALITY)
  336. MelderInfo_writeLine (U"print quality ", devMode -> dmPrintQuality);
  337. if (devMode -> dmFields & DM_PAPERWIDTH)
  338. MelderInfo_writeLine (U"paper width ", devMode -> dmPaperWidth);
  339. if (devMode -> dmFields & DM_PAPERLENGTH)
  340. MelderInfo_writeLine (U"paper length ", devMode -> dmPaperLength);
  341. if (devMode -> dmFields & DM_PAPERSIZE)
  342. MelderInfo_writeLine (U"paper size ", devMode -> dmPaperSize);
  343. if (devMode -> dmFields & DM_ORIENTATION)
  344. MelderInfo_writeLine (U"orientation ", devMode -> dmOrientation);
  345. MelderInfo_close ();
  346. }
  347. autoGraphics pictGraphics = Graphics_create_screen ((void *) dc, nullptr, resolution);
  348. Graphics_setWsViewport (pictGraphics.get(), 0, WIN_WIDTH * resolution, 0, WIN_HEIGHT * resolution);
  349. Graphics_setWsWindow (pictGraphics.get(), 0.0, WIN_WIDTH, 12.0 - WIN_HEIGHT, 12.0);
  350. Graphics_play (my graphics.get(), pictGraphics.get());
  351. HENHMETAFILE metafile = CloseEnhMetaFile (dc);
  352. return metafile;
  353. }
  354. void Picture_copyToClipboard (Picture me) {
  355. try {
  356. HENHMETAFILE metafile = copyToMetafile (me);
  357. OpenClipboard (((GraphicsScreen) my graphics.get()) -> d_winWindow);
  358. EmptyClipboard ();
  359. SetClipboardData (CF_ENHMETAFILE, metafile);
  360. CloseClipboard ();
  361. /*
  362. * We should NOT call DeleteEnhMetaFile,
  363. * because the clipboard becomes the owner of this global memory object.
  364. */
  365. } catch (MelderError) {
  366. Melder_throw (U"Picture not copied to clipboard.");
  367. }
  368. }
  369. void Picture_writeToWindowsMetafile (Picture me, MelderFile file) {
  370. try {
  371. HENHMETAFILE metafile = copyToMetafile (me);
  372. MelderFile_delete (file); // overwrite any existing file with the same name
  373. DeleteEnhMetaFile (CopyEnhMetaFile (metafile, Melder_peek32toW (file -> path)));
  374. DeleteEnhMetaFile (metafile);
  375. } catch (MelderError) {
  376. Melder_throw (U"Picture not written to Windows metafile ", file);
  377. }
  378. }
  379. #endif
  380. void Picture_writeToEpsFile (Picture me, MelderFile file, bool includeFonts, bool useSilipaPS) {
  381. try {
  382. MelderFile_delete (file); // to kill resources as well (fopen only kills data fork)
  383. /* BUG: no message if file cannot be deleted (e.g. because still open by Microsoft Word 2001 after reading). */
  384. {// scope
  385. autoGraphics ps = Graphics_create_epsfile (file, 600, thePrinter. spots,
  386. my selx1, my selx2, my sely1, my sely2, includeFonts, useSilipaPS);
  387. Graphics_play (my graphics.get(), ps.get());
  388. }
  389. } catch (MelderError) {
  390. Melder_throw (U"Picture not written to EPS file ", file);
  391. }
  392. }
  393. void Picture_writeToPdfFile (Picture me, MelderFile file) {
  394. try {
  395. autoGraphics graphics = Graphics_create_pdffile (file, 300, my selx1, my selx2, my sely1, my sely2);
  396. Graphics_play (my graphics.get(), graphics.get());
  397. } catch (MelderError) {
  398. Melder_throw (U"Picture not written to PDF file ", file, U".");
  399. }
  400. }
  401. void Picture_writeToPngFile_300 (Picture me, MelderFile file) {
  402. try {
  403. autoGraphics graphics = Graphics_create_pngfile (file, 300, my selx1, my selx2, my sely1, my sely2);
  404. Graphics_play (my graphics.get(), graphics.get());
  405. } catch (MelderError) {
  406. Melder_throw (U"Picture not written to PNG file ", file, U".");
  407. }
  408. }
  409. void Picture_writeToPngFile_600 (Picture me, MelderFile file) {
  410. try {
  411. autoGraphics graphics = Graphics_create_pngfile (file, 600, my selx1, my selx2, my sely1, my sely2);
  412. Graphics_play (my graphics.get(), graphics.get());
  413. } catch (MelderError) {
  414. Melder_throw (U"Picture not written to PNG file ", file, U".");
  415. }
  416. }
  417. static void print (void *void_me, Graphics printer) {
  418. iam (Picture);
  419. Graphics_play (my graphics.get(), printer);
  420. }
  421. void Picture_print (Picture me) {
  422. try {
  423. Printer_print (print, me);
  424. } catch (MelderError) {
  425. Melder_flushError (U"Picture not printed.");
  426. }
  427. }
  428. void Picture_setSelection
  429. (Picture me, double x1NDC, double x2NDC, double y1NDC, double y2NDC, bool notify)
  430. {
  431. if (my drawingArea) {
  432. Melder_assert (my drawingArea -> d_widget);
  433. drawSelection (me, 0); // unselect
  434. }
  435. my selx1 = x1NDC;
  436. my selx2 = x2NDC;
  437. my sely1 = y1NDC;
  438. my sely2 = y2NDC;
  439. if (my drawingArea) {
  440. drawSelection (me, 1); // select
  441. }
  442. if (notify && my selectionChangedCallback) {
  443. //Melder_casual (U"selectionChangedCallback from Picture_setSelection");
  444. my selectionChangedCallback (me, my selectionChangedClosure,
  445. my selx1, my selx2, my sely1, my sely2);
  446. }
  447. }
  448. void Picture_background (Picture me) { my backgrounding = true; }
  449. void Picture_foreground (Picture me) { my backgrounding = false; }
  450. /* End of file Picture.cpp */