home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Moscow ML 1.42 / e_mac / e_graph.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-15  |  40.1 KB  |  1,541 lines  |  [TEXT/CWIE]

  1. #if defined(__MWERKS__) || defined (THINK_C)
  2. #ifdef THINK_C
  3. #include <MacHeaders>
  4. #endif
  5. #include <Sound.h>
  6. #include <Palettes.h>
  7. #include "mlvalues.h"
  8. #include "alloc.h"
  9. #include "memory.h"
  10. #include "fail.h"
  11. #include "str.h"
  12. #include "ui.h"
  13. #else
  14. /* MPW ? */
  15. #include <Events.h>
  16. #include <Fonts.h>
  17. #include <math.h>
  18. #include <Memory.h>
  19. #include <OSUtils.h>
  20. #include <QuickDraw.h>
  21. #include <Sound.h>
  22. #include "::runtime:mlvalues.h"
  23. #include "::runtime:alloc.h"
  24. #include "::runtime:memory.h"
  25. #include "::runtime:fail.h"
  26. #include "::runtime:str.h"
  27. #include "ui.h"
  28. #endif
  29.  
  30. extern void enter_blocking_section (void);
  31. extern void leave_blocking_section (void);
  32.  
  33. #define SBWIDTH            16        /* largeur scroll bar            */
  34. #define Visible            0xFF    /* controle visible                */
  35. #define inVisible        0        /* controle invisible            */
  36. #define FONTSIZE        9        /* taille des caracteres        */
  37. #define WMINSIZE        70        /* minimum taille d'une fenetre    */
  38. #define MARGINDRAG        4        /* marge limite pour drag        */
  39. #define SIZEX            480        /* taille par defaut CamlGraph    */
  40. #define SIZEY            280        /* taille par defaut CamlGraph    */
  41.  
  42. #define grafpk    ((graphPeek) CAMLOffScreen)
  43. #define offset_x    (grafpk->destRect.left - grafpk->viewRect.left)
  44. #define offset_y    (grafpk->destRect.top - grafpk->viewRect.top)
  45. #define Short_val(x)    ((short) Long_val(x))
  46. #define convert_y(y)    (CAMLOffScreen->portRect.bottom - 1 - Short_val(y))
  47.  
  48. #define Begin_offscreen \
  49.   { GDHandle old_device; \
  50.     if (color_qd){ \
  51.       old_device = GetGDevice (); \
  52.       SetGDevice (CAMLGDevice); \
  53.       SetPort (CAMLOffScreen); \
  54.     }
  55.     
  56. #define End_offscreen \
  57.     if (color_qd){ \
  58.       SetGDevice (old_device); \
  59.     } \
  60.   }
  61.  
  62. typedef struct graph {
  63.     GrafPort offScreen;
  64.     Rect destRect;
  65.     Rect viewRect;
  66.     Rect destRectZoom;
  67. } graphRecord, *graphPeek;
  68.  
  69. #define    gp                        ((graphPeek) CAMLOffScreen)
  70.  
  71. int color_qd = 0;
  72. int max_depth = 1;
  73. GDHandle CAMLGDevice = nil;
  74. WindowPtr CAMLGraph = nil;
  75. WindowPtr CAMLOffScreen = nil;
  76.  
  77. #define N_graph_events 20
  78. static EventRecord graph_event_queue [N_graph_events];
  79. static int graph_event_head = 0;
  80. static int graph_event_tail = 0;
  81.  
  82. #define Incr(x) (x) = ((x) + 1) % N_graph_events
  83.  
  84. static void push_graph_event (EventRecord *event)
  85. {  graph_event_queue [graph_event_head] = *event;
  86.   Incr (graph_event_head);
  87.   if (graph_event_head == graph_event_tail) Incr (graph_event_tail);
  88. }
  89.  
  90. static void copy_bits (const BitMap *srcBits, const BitMap *dstBits, const Rect *srcRect,
  91.                const Rect *dstRect, short mode, RgnHandle maskRgn)
  92. {
  93.   if (color_qd){
  94.     RGBColor fore, back;
  95.     RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
  96.     RGBColor black = {0, 0, 0};
  97.     
  98.     GetForeColor (&fore);
  99.     GetBackColor (&back);
  100.     RGBForeColor (&black);
  101.     RGBBackColor (&white);
  102.     CopyBits (srcBits, dstBits, srcRect, dstRect, mode, maskRgn);
  103.     RGBForeColor (&fore);
  104.     RGBBackColor (&back);
  105.   }else{
  106.     CopyBits (srcBits, dstBits, srcRect, dstRect, mode, maskRgn);
  107.   }
  108. }
  109.  
  110. static void copy_mask (const BitMap *srcBits, const BitMap *maskBits, const BitMap *dstBits,
  111.                 const Rect *srcRect, const Rect *maskRect, const Rect *dstRect)
  112. {
  113.   if (color_qd){
  114.     RGBColor fore, back;
  115.     RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
  116.     RGBColor black = {0, 0, 0};
  117.     
  118.     GetForeColor (&fore);
  119.     GetBackColor (&back);
  120.     RGBForeColor (&black);
  121.     RGBBackColor (&white);
  122.     CopyMask (srcBits, maskBits, dstBits, srcRect, maskRect, dstRect);
  123.     RGBForeColor (&fore);
  124.     RGBBackColor (&back);
  125.   }else{
  126.     CopyMask (srcBits, maskBits, dstBits, srcRect, maskRect, dstRect);
  127.   }
  128. }
  129.  
  130. static void DoClose(WindowPtr window)
  131. {
  132.     if (window && window == CAMLGraph)
  133.     { /* GetWindowPos(CAMLGraph, &Pref->GrWindowRect); */
  134.       if (color_qd){
  135.         if ((*((CGrafPtr) CAMLOffScreen)->portPixMap)->baseAddr != nil)
  136.           DisposePtr ((*((CGrafPtr) CAMLOffScreen)->portPixMap)->baseAddr);
  137.         CloseCPort ((CGrafPtr) CAMLOffScreen);
  138.         DisposeGDevice (CAMLGDevice);
  139.         CAMLGDevice = nil;
  140.       }else{
  141.         if (CAMLOffScreen->portBits.baseAddr != nil)
  142.           DisposePtr(CAMLOffScreen->portBits.baseAddr);
  143.         ClosePort(CAMLOffScreen);
  144.       }
  145.       DisposePtr((Ptr) CAMLOffScreen);
  146.       /* DisableItem(GetMHandle(mCaml), iShowGraph); */
  147.       CAMLGraph = nil;
  148.       CAMLOffScreen = nil;
  149.     }
  150.     CloseWindow(window);
  151.     DisposePtr((Ptr) window);
  152. }
  153.  
  154. typedef struct data {
  155.     WindowRecord window;
  156.     ControlHandle vSBar;
  157.     ControlHandle hSBar;
  158.     graphPeek h;
  159. } dataRecord, *dataPeek;
  160.  
  161. static void FixViewRect(WindowPtr window)
  162. {
  163.     Rect viewRect;
  164.     
  165.     viewRect = window->portRect;
  166.     viewRect.right -= SBWIDTH - 1;
  167.     viewRect.bottom -= SBWIDTH - 1;
  168.     ((graphPeek )CAMLOffScreen)->viewRect = viewRect;
  169. }
  170.  
  171. static void SetScrollBar(ControlHandle control, short max, short value)
  172. {
  173.     unsigned char state;
  174.     
  175.     if (max < value)
  176.         max = value;
  177.     if ((max != GetControlMaximum(control)) || (value != GetControlValue(control))) {
  178.         state = (*control)->contrlVis;
  179.         (*control)->contrlVis = inVisible;                            /* empeche SetCtl... de dessiner la scroll bar    */
  180.         SetControlMaximum(control, max);                                    /* pour pouvoir le faire seulement sur demande    */
  181.         SetControlValue(control, value);                                /* (ca sert pour DoGrowWindow...)                */
  182.         if (state == Visible)
  183.             ShowControl(control);
  184.     }
  185. }
  186.  
  187. static void FixScrollBar(dataPeek doc)
  188. {
  189.     Rect r;
  190.     
  191.     r = ((WindowPtr) doc)->portRect;
  192.     (*doc->vSBar)->contrlVis = inVisible;
  193.     (*doc->hSBar)->contrlVis = inVisible;
  194.     SizeControl(doc->vSBar, SBWIDTH, r.bottom - r.top - SBWIDTH + 3);
  195.     MoveControl(doc->vSBar, r.right - SBWIDTH + 1, -1);
  196.     SizeControl(doc->hSBar, r.right - r.left - SBWIDTH + 3, SBWIDTH);
  197.     MoveControl(doc->hSBar, -1, r.bottom - SBWIDTH + 1);
  198.     (*doc->vSBar)->contrlVis = Visible;
  199.     (*doc->hSBar)->contrlVis = Visible;
  200. }
  201.  
  202. static void AdjustScrollBars(dataPeek doc)
  203. {
  204.     short max, value, scale;
  205.     long x;
  206.     
  207.     max = gp->destRect.bottom - gp->destRect.top - gp->viewRect.bottom + gp->viewRect.top;
  208.     value = gp->viewRect.top - gp->destRect.top;
  209.     SetScrollBar(doc->vSBar, max, value);
  210.     max = gp->destRect.right - gp->destRect.left - gp->viewRect.right + gp->viewRect.left;
  211.     value = gp->viewRect.left - gp->destRect.left;
  212.     SetScrollBar(doc->hSBar, max, value);
  213. }
  214.  
  215. static void ScrollGraph(dataPeek doc, short dh, short dv)
  216. {
  217.     RgnHandle updateRgn;
  218.     Rect r, offRect;
  219.     short max, value;
  220.     
  221.     updateRgn = NewRgn();
  222.     ScrollRect(&gp->viewRect, dh, dv, updateRgn);
  223.     OffsetRect(&gp->destRect, dh, dv);
  224.     SectRect(&gp->viewRect, &(*updateRgn)->rgnBBox, &r);
  225.     offRect = r;
  226.     OffsetRect(&offRect, gp->viewRect.left - gp->destRect.left, gp->viewRect.top - gp->destRect.top);
  227.     SetPort (CAMLGraph);
  228.     copy_bits (&CAMLOffScreen->portBits, &CAMLGraph->portBits, &offRect, &r, srcCopy, nil);
  229.      DisposeRgn(updateRgn);
  230.     max = gp->destRect.bottom - gp->destRect.top - gp->viewRect.bottom + gp->viewRect.top;
  231.     value = gp->viewRect.top - gp->destRect.top;
  232.     SetScrollBar(doc->vSBar, max, value);
  233.     max = gp->destRect.right - gp->destRect.left - gp->viewRect.right + gp->viewRect.left;
  234.     value = gp->viewRect.left - gp->destRect.left;
  235.     SetScrollBar(doc->hSBar, max, value);
  236. }
  237.  
  238. #define NSPIXEL            10        /* nombre de pixels a scroller    */
  239.  
  240. static pascal void DoClickGraphSBar(ControlHandle control, short part)
  241. {
  242.     dataPeek doc;
  243.     graphPeek g;
  244.     short d, value;
  245.     
  246.     if (part) {
  247.         doc = (dataPeek) (*control)->contrlOwner;
  248.         g = (graphPeek) doc->h;
  249.         value = GetControlValue(control);
  250.         switch (part) {
  251.             case kControlUpButtonPart: // inUpButton
  252.             case kControlDownButtonPart: // inDownButton
  253.                 d = NSPIXEL;
  254.                 break;
  255.             case kControlPageUpPart: // inPageUp
  256.             case kControlPageDownPart: // inPageDown
  257.                 if (control == doc->vSBar)
  258.                     d = g->viewRect.bottom - g->viewRect.top;
  259.                 else
  260.                     d = g->viewRect.right - g->viewRect.left;
  261.                 d -= NSPIXEL;
  262.                 break;
  263.         }
  264.         if ((part == kControlDownButtonPart) || (part == kControlPageDownPart))
  265.             d = -d;
  266.         if (d > value)
  267.             d = value;
  268.         else
  269.             if (value - d > GetControlMaximum(control))
  270.                 d = value - GetControlMaximum(control);
  271.         if (control == doc->vSBar)
  272.             ScrollGraph(doc, 0, d);
  273.         else
  274.             ScrollGraph(doc, d, 0);
  275.     }
  276. }
  277.  
  278. ControlActionUPP tcUPP = 0;
  279.  
  280. #define BTstQ(arg, bitnbr)        (arg & (1 << bitnbr))
  281.  
  282. static WindowPtr DoNew(Rect *boundsRect, Str255 title)
  283. {
  284.     WindowPtr window;
  285.     dataPeek storage;
  286.     Boolean goAwayFlag;
  287.     Rect r;
  288.     long gesResponse;
  289.  
  290.     if( tcUPP == 0 ) /* first time */
  291.     { tcUPP = NewControlActionProc(DoClickGraphSBar);
  292.       if ( !Gestalt( gestaltQuickdrawFeatures, &gesResponse ) 
  293.               && BTstQ( gesResponse, gestaltHasColor ) )
  294.       { color_qd = 1; max_depth = 8; }
  295.       else
  296.       { color_qd = 0; max_depth = 1; }
  297.     }
  298.  
  299.     storage = (dataPeek )NewPtr(sizeof(dataRecord));
  300.     if (storage) {
  301.         /* GetIndString(title, rWTitle, index); */
  302.         goAwayFlag = 1;
  303.         if (color_qd){
  304.           window = (WindowPtr) NewCWindow (storage, boundsRect, title, false, zoomDocProc, nil, goAwayFlag, 0);
  305.         }else{
  306.           window = NewWindow(storage, boundsRect, title, false, zoomDocProc, nil, goAwayFlag, 0);
  307.         }
  308.         SetRect(&r, 0, 0, 16, 50);
  309.         storage->vSBar = NewControl(window, &r, "\p", false, 0, 0, 0, scrollBarProc, 0);
  310.         storage->hSBar = NewControl(window, &r, "\p", false, 0, 0, 0, scrollBarProc, 0);
  311.         SetPort(window);
  312.         SetOrigin(0,0);
  313.         TextFont(monaco);                            /* la largeur des caractères est constante dans cette font    */
  314.         TextSize(FONTSIZE);
  315.         FixScrollBar(storage);
  316.         (*storage->vSBar)->contrlVis = inVisible;    /* ShowWindow genere un updateEvt, qui prendra en charge le    */
  317.         (*storage->hSBar)->contrlVis = inVisible;    /* remplissage de la nouvelle fenetre; il faut forcer les    */
  318.         ShowWindow(window);                            /* scroll bars a etre redessinees par DoActivate.            */
  319.     }
  320.     return window;
  321. }
  322.  
  323. static Boolean OpenGraph(Str255 title)
  324. {
  325.     Rect r;
  326.     
  327.     SetRect( &r, 40, 40, 240, 240 ); /* fix this !! */
  328.     if ( CAMLGraph = DoNew(&r, title) )
  329.     {
  330.         graph_event_head = graph_event_tail = 0;
  331.         CAMLOffScreen = (GrafPtr )NewPtr(sizeof(graphRecord));
  332.         if (CAMLOffScreen) {
  333.             ((dataPeek )CAMLGraph)->h = gp;
  334.             FixViewRect(CAMLGraph);
  335.             r = gp->viewRect;
  336.             r.right = r.left + SIZEX;
  337.             r.bottom = r.top + SIZEY;
  338.             if (color_qd){
  339.               GDHandle the_max_device, old_device;
  340.               CTabHandle color_map;
  341.               Rect wide_open = {-32000, -32000, 32000, 32000};
  342.               CGrafPtr color_off_screen = (CGrafPtr) CAMLOffScreen;
  343.               long off_row_bytes;
  344.               
  345.               the_max_device = GetMaxDevice (&wide_open);
  346.               old_device = GetGDevice ();
  347.               CAMLGDevice = NewGDevice (0, 0);
  348.               max_depth = (*(*the_max_device)->gdPMap)->pixelSize;
  349.               **(*CAMLGDevice)->gdPMap = **(*the_max_device)->gdPMap;
  350.               off_row_bytes = (max_depth * (r.right - r.left) + 31) / 32 * 4;
  351.               (*(*CAMLGDevice)->gdPMap)->rowBytes = off_row_bytes + 0x8000;
  352.               (*(*CAMLGDevice)->gdPMap)->baseAddr = NewPtr (off_row_bytes * (long) (r.bottom - r.top));
  353.               if ((*(*CAMLGDevice)->gdPMap)->baseAddr == nil){
  354.                 DoClose (CAMLGraph);
  355.                 return false;
  356.               }
  357.               (*(*CAMLGDevice)->gdPMap)->bounds = r;
  358.               color_map = (*(*the_max_device)->gdPMap)->pmTable;
  359.               if (HandToHand ((Handle *) &color_map) != noErr){
  360.                 DoClose (CAMLGraph);
  361.                 return false;
  362.               }
  363.               (*(*CAMLGDevice)->gdPMap)->pmTable = color_map;
  364.               SetGDevice (CAMLGDevice);
  365.               MakeITable (nil, nil, (*the_max_device)->gdResPref);
  366.               if (QDError () != noErr){
  367.                 SetGDevice (old_device);
  368.                 DoClose (CAMLGraph);
  369.                 return false;
  370.               }
  371.               OpenCPort (color_off_screen);
  372.               SetGDevice (old_device);
  373.               SetPalette (CAMLGraph, nil, 1);
  374.             }else{
  375.               max_depth = 1;
  376.               OpenPort(CAMLOffScreen);
  377.               CAMLOffScreen->portBits.rowBytes = (r.right - r.left + 31) / 32 * 4;
  378.               CAMLOffScreen->portBits.baseAddr = NewPtr(CAMLOffScreen->portBits.rowBytes * (long) (r.bottom - r.top));
  379.               CAMLOffScreen->portBits.bounds = r;
  380.               if (CAMLOffScreen->portBits.baseAddr == nil){
  381.                 DoClose (CAMLGraph);
  382.                 return false;
  383.               }
  384.             }
  385.             CAMLOffScreen->portRect = r;
  386.             EraseRect(&r);
  387.             r.bottom = gp->viewRect.bottom;
  388.             r.top = r.bottom - SIZEY;
  389.             gp->destRect = r;
  390.             gp->destRectZoom = r;
  391.             r = qd.screenBits.bounds;
  392.             r.top += GetMBarHeight() + SBWIDTH;
  393.             InsetRect(&r, MARGINDRAG, MARGINDRAG);
  394.             if (r.right - r.left > SIZEX + SBWIDTH - 1)
  395.                 r.left = r.right - SIZEX - SBWIDTH + 1;
  396.             if (r.bottom - r.top > SIZEY + SBWIDTH - 1)
  397.                 r.top = r.bottom - SIZEY - SBWIDTH + 1;
  398.             (*((WStateDataHandle) ((WindowPeek) CAMLGraph)->dataHandle))->stdState = r;
  399.             TextFont(monaco);
  400.             TextSize(FONTSIZE);
  401.             SetPort(CAMLGraph);
  402.             AdjustScrollBars(((dataPeek )CAMLGraph));
  403.             /* EnableItem(GetMHandle(mCaml), iShowGraph); */
  404.             SelectWindow(CAMLGraph);
  405.             return true;
  406.         }
  407.         DoClose(CAMLGraph);
  408.     }
  409.     return false;
  410. }
  411.  
  412. static void DoGrow(WindowPtr window, EventRecord *event)
  413. {
  414.     Rect r, *destRect;
  415.     long newSize;
  416.     short bound;
  417.     
  418.     r.right = gp->destRect.right - gp->destRect.left + SBWIDTH;
  419.     r.bottom = gp->destRect.bottom - gp->destRect.top + SBWIDTH;
  420.     r.left = WMINSIZE;
  421.     r.top = WMINSIZE;
  422.     if (newSize = GrowWindow(window, event->where, &r)) {
  423.         SetPort(window);
  424.         InvalRect(&window->portRect);                                    /* on redessine tout !                            */
  425.         SizeWindow(window, LoWord(newSize), HiWord(newSize), true);        /* la fenetre sera dessinee via un updateEvt    */
  426.         destRect = &((graphPeek) CAMLOffScreen)->destRect;
  427.         bound = window->portRect.right - SBWIDTH + 1;
  428.         if (bound > destRect->right)
  429.             OffsetRect(destRect, bound - destRect->right, 0);
  430.         bound = window->portRect.bottom - SBWIDTH + 1;
  431.         if (bound > destRect->bottom)
  432.             OffsetRect(destRect, 0, bound - destRect->bottom);
  433.         ClipRect(&CAMLGraph->portRect);
  434.         FixViewRect(window);
  435.         (*((dataPeek) window)->vSBar)->contrlVis = inVisible;            /* pour que AdjustScrollBars ne redessine pas    */
  436.         (*((dataPeek) window)->hSBar)->contrlVis = inVisible;            /* les controles, ce sera fait par UpdtControls    */
  437.         AdjustScrollBars((dataPeek) window);
  438.         FixScrollBar((dataPeek) window);                                /* remet les controles a l'etat visible            */
  439.     }
  440. }
  441.  
  442. static void draw_grow_icon (WindowPtr window)
  443. {
  444.   PenState saved_pen;
  445.  
  446.   SetPort (window);
  447.   GetPenState (&saved_pen);
  448.   PenNormal ();
  449.   DrawGrowIcon (window);
  450.   SetPenState (&saved_pen);
  451. }
  452.  
  453. static void DoActivate(WindowPtr window, Boolean isActive)
  454. {
  455.     dataPeek doc;
  456.     Rect r;
  457.     RgnHandle clipRgn, tempRgn;
  458.     
  459.     doc = (dataPeek) window;
  460.     if (isActive) {
  461.         SetPort(window);
  462.         (*doc->vSBar)->contrlVis = Visible;
  463.         (*doc->hSBar)->contrlVis = Visible;
  464.         r = (*doc->vSBar)->contrlRect;
  465.         InvalRect(&r);                            /* un updateEvt redessinera les controles                */
  466.         r = window->portRect;
  467.         r.top = r.bottom - SBWIDTH + 1;
  468.         InvalRect(&r);
  469.     } else {
  470.         HideControl(doc->vSBar);
  471.         HideControl(doc->hSBar);
  472.         draw_grow_icon(window);
  473.         SetPort(window);
  474.         ValidRect(&(*doc->vSBar)->contrlRect);    /* permet d'eviter l'updateEvt genere par HideControl    */
  475.         ValidRect(&(*doc->hSBar)->contrlRect);
  476.     }
  477. }
  478.  
  479. static void DoUpdate(WindowPtr window)
  480. {
  481.     Rect r;
  482.     
  483.     BeginUpdate(window);
  484.     SetPort(window);
  485.     EraseRect(&window->portRect);
  486.     UpdateControls(window, window->visRgn);
  487.     draw_grow_icon(window);
  488.     r = window->portRect;
  489.     r.right -= SBWIDTH - 1;
  490.     r.bottom -= SBWIDTH - 1;
  491.     if (SectRect(&r, &(*window->visRgn)->rgnBBox, &r))
  492.     { Rect offRect = r;
  493.                 
  494.       OffsetRect (&offRect, gp->viewRect.left - gp->destRect.left,
  495.                   gp->viewRect.top - gp->destRect.top);
  496.       copy_bits (&CAMLOffScreen->portBits, &window->portBits,
  497.                   &offRect, &r, srcCopy, nil);
  498.     }
  499.     EndUpdate(window);
  500. }
  501.  
  502. /* in cursor adjust
  503.     if (isGraphWindow (window) && wait_graph_move){
  504.       wait_graph_move = 0;
  505.       GetMouse (&mouse);
  506.       mouseRgn = NewRgn ();
  507.       SetRectRgn (mouseRgn, mouse.h, mouse.v, mouse.h + 1, mouse.v + 1);
  508.       OffsetRgn (mouseRgn, -window->portBits.bounds.left, -window->portBits.bounds.top);
  509.       return mouseRgn;
  510.     }
  511. */
  512.  
  513. int DoGraphEvent(EventRecord *event, WindowPtr window)
  514. {   /* window = FrontWindow() */
  515.     Point mouse;
  516.     dataPeek doc;
  517.     WindowPtr mouse_window;
  518.     ControlHandle control;
  519.     graphPeek g;
  520.     short part;
  521.     
  522.     switch (event->what)
  523.     { case mouseDown:
  524.         part = FindWindow( event->where, &mouse_window );
  525.         if ( mouse_window != CAMLGraph ) return 0; /* not handled */
  526.         switch (part)
  527.         { case inGoAway:
  528.             DoClose(CAMLGraph);
  529.             return 1; /* handled */
  530.           case inGrow:
  531.             DoGrow(CAMLGraph,event);
  532.             return 1; /* handled */
  533.           case inDrag:
  534.             DragWindow(CAMLGraph,event->where,&qd.screenBits.bounds);
  535.             return 1; /* handled */
  536.           case inContent:
  537.             if (mouse_window != window)
  538.             { SelectWindow(mouse_window);
  539.               return 1; /* handled */
  540.             }
  541.             mouse = event->where;
  542.             SetPort(window);
  543.             GlobalToLocal(&mouse);
  544.             switch ( FindControl( mouse, window, &control ))
  545.             {  case 0:
  546.                     push_graph_event (event);
  547.                     break;
  548.                 case kControlIndicatorPart: // inThumb
  549.                     doc = (dataPeek) window;
  550.                     if (TrackControl(control, mouse, nil))
  551.                     {    g = (graphPeek) doc->h;
  552.                         if (control == doc->vSBar)
  553.                             ScrollGraph(doc, 0, g->viewRect.top - g->destRect.top - GetControlValue(control));
  554.                         else
  555.                             ScrollGraph(doc, g->viewRect.left - g->destRect.left - GetControlValue(control), 0);
  556.                     }    
  557.                     break;
  558.                 default:
  559.                     TrackControl(control, mouse, tcUPP);
  560.                     break;
  561.             }
  562.             return 1; /* handled */
  563.           case inZoomIn:
  564.           case inZoomOut:
  565.             if ((WindowPtr)event->message != CAMLGraph) return 0; /* not handled */
  566.             if (TrackBox(window, event->where, part))
  567.             {   SetPort(window);
  568.                 EraseRect(&window->portRect);
  569.                 ZoomWindow(window, part, window == FrontWindow());
  570.                 FixViewRect(window);
  571.                 switch (part)
  572.                 {    case inZoomIn:
  573.                         gp->destRect = gp->destRectZoom;
  574.                         break;
  575.                     case inZoomOut:
  576.                         gp->destRectZoom = gp->destRect;
  577.                         gp->destRect = gp->viewRect;
  578.                         gp->destRect.right = gp->destRect.left + SIZEX;
  579.                         gp->destRect.top = gp->destRect.bottom - SIZEY;
  580.                 }
  581.                 ClipRect(&CAMLGraph->portRect);
  582.                 InvalRect(&window->portRect);
  583.                 (*((dataPeek) window)->vSBar)->contrlVis = inVisible;        /* pour que AdjustScrollBars ne redessine pas    */
  584.                 (*((dataPeek) window)->hSBar)->contrlVis = inVisible;        /* les controles, ce sera fait via le updateEvt    */
  585.                 AdjustScrollBars((dataPeek) window);
  586.                 FixScrollBar((dataPeek) window);
  587.             }
  588.             return 1; /* handled */
  589.         }
  590.         return 0;
  591.       case mouseUp:
  592.         if (window != CAMLGraph) return 0; /* not handled */
  593.         push_graph_event (event);
  594.         return 1; /* handled */
  595.       case keyDown:
  596.       case autoKey:
  597.         if ( event->modifiers & cmdKey || window != CAMLGraph )
  598.           return 0; /* don't handle */
  599.         push_graph_event (event);
  600.         return 1; /* handled */
  601.       case activateEvt:
  602.         if ((WindowPtr)event->message != CAMLGraph) return 0; /* not handled */
  603.         DoActivate(CAMLGraph, (event->modifiers & activeFlag));
  604.         return 1; /* handled */
  605.       case updateEvt: 
  606.         if ((WindowPtr)event->message != CAMLGraph) return 0; /* not handled */
  607.         DoUpdate(CAMLGraph);
  608.         return 1; /* handled */
  609.       default:
  610.         return 0;
  611.     }
  612. }
  613.  
  614. #if 0
  615.  
  616. static long GetGraphEvent(EventRecord *event)
  617. {
  618.   return os_get_next_event(event); /* add queue! */
  619. }
  620.  
  621. #define PTL(a) *((long *)&(a)) /* coerce Point type to long */
  622.  
  623. static void LookGraphEvent (EventRecord *event, int move_ok, int poll)
  624. {
  625.   Point mouse_before, mouse_after;
  626.   int i, did_it, ret_it;
  627.   long res;
  628.  
  629.   OSEventAvail(0L, event); /* This returns a NULL event with the global mouse location */
  630.   mouse_after = event->where;
  631.   do
  632.   { res = GetGraphEvent(event);
  633.     mouse_before = mouse_after;
  634.     mouse_after = event->where;
  635.     did_it = 0;
  636.     ret_it = 0;
  637.     i = FindWindow(mouse_after,&event_window);
  638.     switch (event->what)
  639.     { case nullEvent:
  640.         if ( poll || ( move_ok && ( PTL(mouse_after) != PTL(mouse_before) ) ) )
  641.           ret_it = 1;
  642.         else
  643.           did_it = 1;
  644.         break;
  645.       default: ;
  646.     }
  647.     if ( did_it + ret_it == 0 ) os_handle_event(event);
  648.   } while ( !ret_it );
  649. }
  650.  
  651. static void LookGraphEvent (EventRecord *result, int move_ok, int poll)
  652. {
  653.   Point mouse_before, mouse_after;
  654.   int i;
  655.  
  656.   if (poll){
  657.     GetMouse (&result->where);
  658.     LocalToGlobal (&result->where);
  659.     result->modifiers = Button () ? 0 : btnState;
  660.     result->what = nullEvent;
  661.     for (i = graph_event_tail; i != graph_event_head; Incr (i)){
  662.       if (graph_event_queue [i].what == keyDown){
  663.         result->what = keyDown;
  664.         result->message = graph_event_queue [i].message;
  665.         break;
  666.       }
  667.     }
  668.     return;
  669.   }
  670.   if (graph_event_head != graph_event_tail){
  671.     *result = graph_event_queue [graph_event_tail];
  672.     Incr (graph_event_tail);
  673.     return;
  674.   }
  675.   GetMouse (&mouse_before);
  676.   LocalToGlobal (&mouse_before);
  677.   while (1){
  678.     /* wait_graph_move = move_ok; */
  679.     /* LookEvent (15); */
  680.     os_event_check();
  681.     GetMouse (&mouse_after);
  682.     LocalToGlobal (&mouse_after);
  683. #define PTL(a) *((long *)&(a)) /* coerce Point type to long */
  684.     if (move_ok && PTL(mouse_after) != PTL(mouse_before)
  685.         || graph_event_head != graph_event_tail)
  686.       break;
  687.   }
  688.   if (graph_event_head == graph_event_tail){
  689.     *result = graph_event_queue [graph_event_head];
  690.     result->what = nullEvent;
  691.   }else{
  692.     *result = graph_event_queue [graph_event_tail];
  693.     Incr (graph_event_tail);
  694.   }
  695. }
  696.  
  697. #endif
  698.  
  699. #define PTL(a) *((long *)&(a)) /* coerce Point type to long */
  700.  
  701. static void LookGraphEvent (EventRecord *event, int move_ok, int poll)
  702. {
  703.   Point mouse_before, mouse_after;
  704.   int i;
  705.  
  706.   OSEventAvail(0L, event); /* returns a NULL event with the global mouse location & button */
  707.   mouse_after = event->where;
  708.   if (poll) /* just look */
  709.   {    for (i = graph_event_tail; i != graph_event_head; Incr (i))
  710.     { if (graph_event_queue [i].what == keyDown)
  711.       { event->what = keyDown;
  712.         event->message = graph_event_queue [i].message;
  713.         break;
  714.       }
  715.     }
  716.   }
  717.   else do
  718.   { if (graph_event_head != graph_event_tail)
  719.     { *event = graph_event_queue [graph_event_tail];
  720.       Incr (graph_event_tail);
  721.       return;
  722.     }
  723.     mouse_before = mouse_after;
  724.     if ( os_get_next_event(event) ) os_handle_event(event);
  725.     else
  726.     { /* NULL event */
  727.       mouse_after = event->where;
  728.       if ( move_ok && ( PTL(mouse_after) != PTL(mouse_before) ) ) return;
  729.     }
  730.   } while ( 1 );
  731. }
  732.  
  733. #undef Incr
  734.  
  735. /* ***************************************************************** */
  736.  
  737. void graphic_fail(char * msg)
  738. {
  739.     raise_with_arg(GRAPHIC_FAILURE_EXN, copy_string(msg));
  740. }
  741.  
  742. static void check_graph()
  743. {
  744.     if (CAMLGraph == nil)
  745.         graphic_fail("graphic window not opened");
  746. }
  747.  
  748. value moveto(value x, value y)    /* ML */
  749. {
  750.     check_graph();
  751.     SetPort(CAMLOffScreen);
  752.     MoveTo(Short_val(x), convert_y(y));
  753.     return Atom(0);
  754. }
  755.  
  756. value open_graph(value str)    /* ML */
  757. {
  758.     Str255 tit;
  759.     unsigned char *p;
  760.     int i, len = string_length(str);
  761.  
  762.     if ( len > 64 ) len = 64;
  763.     tit[0] = len;
  764.     for (i = 1, p = (unsigned char *) String_val(str); i <= len; ) tit[i++] = *p++;
  765.  
  766.     if (CAMLGraph == nil) {
  767.         if (!OpenGraph(tit))
  768.             graphic_fail("open_graph: cannot open graphic window");
  769.         moveto(Val_long(0), Val_long(0));
  770.     }
  771.     return Atom(0);
  772. }
  773.  
  774. value close_graph()    /* ML */
  775. {
  776.     check_graph();
  777.     DoClose(CAMLGraph);
  778.     return Atom(0);
  779. }
  780.  
  781. value clear_graph()    /* ML */
  782. {
  783.     check_graph();
  784.     SetPort(CAMLGraph);
  785.     EraseRect(&grafpk->viewRect);
  786.     Begin_offscreen
  787.           EraseRect(&CAMLOffScreen->portRect);
  788.     End_offscreen
  789.     return Atom(0);
  790. }
  791.  
  792. value size_x()    /* ML */
  793. {
  794.     Rect * r;
  795.     
  796.     check_graph();
  797.     r = &CAMLOffScreen->portRect;
  798.     return Val_long(r->right - r->left);
  799. }
  800.  
  801. value size_y()    /* ML */
  802. {
  803.     Rect * r;
  804.     
  805.     check_graph();
  806.     r = &CAMLOffScreen->portRect;
  807.     return Val_long(r->bottom - r->top);
  808. }
  809.  
  810. value set_color(value color)    /* ML */
  811. {
  812.   long col = Long_val (color);
  813.   
  814.   check_graph();
  815.   if (color_qd){
  816.     RGBColor qd_col;
  817.     
  818.     qd_col.red = (col >> 16) * 257;
  819.     qd_col.green = ((col >> 8) & 0xff) * 257;
  820.     qd_col.blue = (col & 0xff) * 257;
  821.     SetPort (CAMLGraph);
  822.     RGBForeColor (&qd_col);
  823.     Begin_offscreen
  824.       RGBForeColor (&qd_col);
  825.     End_offscreen
  826.   }else{   
  827.     SetPort(CAMLGraph);
  828.     if (col == 0xffffff){
  829.       PenPat (&qd.white);
  830.       TextMode (srcBic);
  831.     }else{
  832.       PenPat (&qd.black);
  833.       TextMode (srcOr);
  834.     }
  835.     SetPort(CAMLOffScreen);
  836.     if (col == 0xffffff){
  837.       PenPat (&qd.white);
  838.       TextMode (srcBic);
  839.     }else{
  840.       PenPat (&qd.black);
  841.       TextMode (srcOr);
  842.     }
  843.   }
  844.   return Atom(0);
  845. }
  846.  
  847. value plot(value x, value y)    /* ML */
  848. {
  849.     short h, v;
  850.     Point old_pen_size;
  851.     
  852.     check_graph();
  853.     h = Short_val(x);
  854.     v = convert_y(y);
  855.     SetPort(CAMLOffScreen);
  856.     old_pen_size = CAMLOffScreen->pnSize;
  857.     PenSize (1, 1);
  858.     MoveTo(h, v);
  859.     LineTo(h, v);
  860.     PenSize (old_pen_size.h, old_pen_size.v);
  861.     SetPort(CAMLGraph);
  862.     ClipRect(&grafpk->viewRect);
  863.     h += offset_x;
  864.     v += offset_y;
  865.     PenSize (1, 1);
  866.     MoveTo(h, v);
  867.     LineTo(h, v);
  868.     PenSize (old_pen_size.h, old_pen_size.v);
  869.     ClipRect(&CAMLGraph->portRect);
  870.     return Atom(0);
  871. }
  872.  
  873. value point_color(value x, value y)    /* ML */
  874. {
  875.     Point p;
  876.     
  877.     check_graph();
  878.     SetPt(&p, Short_val(x), convert_y(y));
  879.     if (!PtInRect(p, &CAMLOffScreen->portRect))
  880.         graphic_fail("point_color: point out of graphic window");
  881.     if (color_qd){
  882.       RGBColor qd_col;
  883.       Begin_offscreen
  884.         GetCPixel (p.h, p.v, &qd_col);
  885.       End_offscreen
  886.       return Val_long ((qd_col.red / 256 << 16)
  887.                        + (qd_col.green / 256 << 8)
  888.                        + (qd_col.blue / 256));
  889.         }else{
  890.       SetPort(CAMLOffScreen);
  891.       return GetPixel(p.h, p.v) ? Val_long(0) : Val_long(0xFFFFFF);
  892.     }
  893. }
  894.  
  895. value current_point()    /* ML */
  896. {
  897.     value res;
  898.     Point p;
  899.     
  900.     check_graph();
  901.     SetPort(CAMLOffScreen);
  902.     GetPen(&p);
  903.     res = alloc_tuple(2);
  904.     Field(res, 0) = Val_long(p.h);
  905.     Field(res, 1) = Val_long(convert_y(Val_long(p.v)));
  906.     return res;
  907. }
  908.  
  909. value lineto(value x, value y)    /* ML */
  910. {
  911.     short h, v;
  912.     Point p;
  913.     
  914.     check_graph();
  915.     SetPort(CAMLOffScreen);
  916.     GetPen(&p);
  917.     h = Short_val(x);
  918.     v = convert_y(y);
  919.     LineTo(h, v);
  920.     SetPort(CAMLGraph);
  921.     ClipRect(&grafpk->viewRect);
  922.     MoveTo(p.h + offset_x, p.v + offset_y);
  923.     LineTo(h + offset_x, v + offset_y);
  924.     ClipRect(&CAMLGraph->portRect);
  925.     return Atom(0);
  926. }
  927.  
  928. value draw_arc(value * argv, int argn)    /* ML */
  929. {
  930. #pragma unused(argn)
  931.     short h, v, r_x, r_y, start, arc;
  932.     Rect r;
  933.     
  934.     check_graph();
  935.     r_x = Short_val(argv[2]);
  936.     r_y = Short_val(argv[3]);
  937.     if ((r_x < 0) || (r_y < 0))
  938.         graphic_fail("draw_arc: radius must be positives");
  939.     h = Short_val(argv[0]);
  940.     v = convert_y(argv[1]);
  941.     SetRect(&r, h - r_x, v - r_y, h + r_x + 1, v + r_y + 1);
  942.     SetPort(CAMLOffScreen);
  943.     start = Short_val(argv[4]);
  944.     arc = Short_val(argv[5]) - start;
  945.     while (arc < 0)
  946.         arc += 360;
  947.     FrameArc(&r, 90 - start, -arc);
  948.     SetPort(CAMLGraph);
  949.     ClipRect(&grafpk->viewRect);
  950.     OffsetRect(&r, offset_x, offset_y);
  951.     FrameArc(&r, 90 - start, -arc);
  952.     ClipRect(&CAMLGraph->portRect);
  953.     return Atom(0);
  954. }
  955.  
  956. value set_line_width(value width)    /* ML */
  957. {
  958.     short size;
  959.     
  960.     check_graph();
  961.     size = Short_val(width);
  962.     if (size < 0)
  963.         graphic_fail("set_line_width: width must be positive");
  964.     SetPort(CAMLOffScreen);
  965.     PenSize(size, size);
  966.     SetPort(CAMLGraph);
  967.     PenSize(size, size);
  968.     return Atom(0);
  969. }
  970.  
  971. value draw_char(value ch)    /* ML */
  972. {
  973.     Point p;
  974.     
  975.     check_graph();
  976.     Begin_offscreen
  977.       GetPen(&p);
  978.       DrawChar((char) Long_val(ch));
  979.     End_offscreen
  980.     SetPort(CAMLGraph);
  981.     ClipRect(&grafpk->viewRect);
  982.     MoveTo(p.h + offset_x, p.v + offset_y);
  983.     DrawChar((char) Long_val(ch));
  984.     ClipRect(&CAMLGraph->portRect);
  985.     return Atom(0);
  986. }
  987.  
  988. value draw_string(value str)    /* ML */
  989. {
  990.     mlsize_t len;
  991.     Point p;
  992.     
  993.     check_graph();
  994.     if ((len = string_length(str)) > 32767)
  995.         len = 32767;
  996.     Begin_offscreen
  997.       GetPen(&p);
  998.       DrawText(Bp_val(str), 0, (unsigned short) len);
  999.     End_offscreen
  1000.     SetPort(CAMLGraph);
  1001.     ClipRect(&grafpk->viewRect);
  1002.     MoveTo(p.h + offset_x, p.v + offset_y);
  1003.     DrawText(Bp_val(str), 0, (short) len);
  1004.     ClipRect(&CAMLGraph->portRect);
  1005.     return Atom(0);
  1006. }
  1007.  
  1008. value set_font(value str)    /* ML */
  1009. {
  1010.     Str255 name;
  1011.     short i, len, fontnum;
  1012.     
  1013.     check_graph();
  1014.     len = string_length(str);
  1015.     for(i = 0; (i < len) && (i < 255); i++)
  1016.         name[i + 1] = Byte(str, i);
  1017.     name[0] = i;
  1018.     GetFNum(name,&fontnum);
  1019.     SetPort(CAMLOffScreen);
  1020.     TextFont(fontnum);
  1021.     SetPort(CAMLGraph);
  1022.     TextFont(fontnum);
  1023.     return Atom(0);
  1024. }
  1025.  
  1026. value set_text_size(value size)    /* ML */
  1027. {
  1028.     short s;
  1029.     
  1030.     check_graph();
  1031.     SetPort(CAMLOffScreen);
  1032.     s = Short_val(size);
  1033.     if (s < 0)
  1034.         graphic_fail("set_text_size: size must be positive");
  1035.     TextSize(s);
  1036.     SetPort(CAMLGraph);
  1037.     TextSize(s);
  1038.     return Atom(0);
  1039. }
  1040.  
  1041. value text_size(value str)    /* ML */
  1042. {
  1043.     value res;
  1044.     FontInfo info;
  1045.     
  1046.     check_graph();
  1047.     SetPort(CAMLOffScreen);
  1048.     GetFontInfo(&info);
  1049.     res = alloc_tuple(2);
  1050.     Field(res, 0) = Val_long(TextWidth(Bp_val(str), 0, string_length(str)));
  1051.     Field(res, 1) = Val_long(info.ascent + info.descent);
  1052.     return res;
  1053. }
  1054.  
  1055. value fill_rect(value x, value y, value wdth, value hgth)    /* ML */
  1056. {
  1057.     short h, v, width, heigth;
  1058.     Rect r;
  1059.     
  1060.     check_graph();
  1061.     width = Short_val(wdth);
  1062.     heigth = Short_val(hgth);
  1063.     if ((width < 0) || (heigth < 0))
  1064.         graphic_fail("fill_rect: width and heigth must be positives");
  1065.     h = Short_val(x);
  1066.     v = convert_y(y) + 1;
  1067.     SetRect(&r, h, v - heigth, h + width, v);
  1068.     SetPort(CAMLOffScreen);
  1069.     PaintRect(&r);
  1070.     SetPort(CAMLGraph);
  1071.     ClipRect(&grafpk->viewRect);
  1072.     OffsetRect(&r, offset_x, offset_y);
  1073.     PaintRect(&r);
  1074.     ClipRect(&CAMLGraph->portRect);
  1075.     return Atom(0);
  1076. }
  1077.  
  1078. value fill_arc(value * argv, int argn)    /* ML */
  1079. {
  1080. #pragma unused(argn)
  1081.     short h, v, r_x, r_y, start, arc;
  1082.     Rect r;
  1083.     
  1084.     check_graph();
  1085.     r_x = Short_val(argv[2]);
  1086.     r_y = Short_val(argv[3]);
  1087.     if ((r_x < 0) || (r_y < 0))
  1088.         graphic_fail("draw_arc: radius must be positives");
  1089.     h = Short_val(argv[0]);
  1090.     v = convert_y(argv[1]);
  1091.     SetRect(&r, h - r_x, v - r_y, h + r_x + 1, v + r_y + 1);
  1092.     start = Short_val(argv[4]);
  1093.     arc = Short_val(argv[5]) - start;
  1094.     while (arc < 0)
  1095.         arc += 360;
  1096.     SetPort(CAMLOffScreen);
  1097.     PaintArc(&r, 90 - start, -arc);
  1098.     SetPort(CAMLGraph);
  1099.     ClipRect(&grafpk->viewRect);
  1100.     OffsetRect(&r, offset_x, offset_y);
  1101.     PaintArc(&r, 90 - start, -arc);
  1102.     ClipRect(&CAMLGraph->portRect);
  1103.     return Atom(0);
  1104. }
  1105.  
  1106. value fill_poly(value vect)    /* ML */
  1107. {
  1108.     int n_points, i;
  1109.     PolyHandle poly;
  1110.     
  1111.     check_graph();
  1112.     n_points = Wosize_val(vect);
  1113.     if (n_points < 3)
  1114.         graphic_fail("fill_poly: not enough points");
  1115.     SetPort(CAMLOffScreen);
  1116.     poly = OpenPoly();
  1117.     MoveTo(Short_val(Field(Field(vect, 0), 0)), convert_y(Field(Field(vect, 0), 1)));
  1118.     for(i = 1; i < n_points; i++)
  1119.         LineTo(Short_val(Field(Field(vect, i), 0)), convert_y(Field(Field(vect, i), 1)));
  1120.     LineTo(Short_val(Field(Field(vect, 0), 0)), convert_y(Field(Field(vect, 0), 1)));
  1121.     ClosePoly();
  1122.     PaintPoly(poly);
  1123.     SetPort(CAMLGraph);
  1124.     ClipRect(&grafpk->viewRect);
  1125.     OffsetPoly(poly, offset_x, offset_y);
  1126.     PaintPoly(poly);
  1127.     ClipRect(&CAMLGraph->portRect);
  1128.     KillPoly(poly);
  1129.     return Atom(0);
  1130. }
  1131.  
  1132. struct image {
  1133.   value w;
  1134.   value h;
  1135.   value data;
  1136.   value mask;
  1137. };
  1138.  
  1139. #define Width(i) (((struct image *) i)->w)
  1140. #define Height(i) (((struct image *) i)->h)
  1141. #define Data(i) (((struct image *) i)->data)
  1142. #define Mask(i) (((struct image *) i)->mask)
  1143.  
  1144. static value new_bits(int width, int height, int depth)
  1145. {
  1146.   int rowbytes, nbytes, nwords;
  1147.   value res;
  1148.  
  1149.   rowbytes = (depth * width + 31) / 32 * 4;
  1150.   nbytes = rowbytes * height;
  1151.   nwords = (nbytes + 3) / 4;
  1152.   if (nwords == 0) return Atom (Abstract_tag);
  1153.   if (nwords <= Max_young_wosize) {
  1154.     res = alloc(nwords, Abstract_tag);
  1155.   }else{
  1156.     res = alloc_shr(nwords, Abstract_tag);
  1157.   }
  1158.   return res;
  1159. }
  1160.  
  1161. static BitMap **image_to_bitmap (value image, int w, int h, int is_mask)
  1162. {
  1163.   if (color_qd && !is_mask){
  1164.     GDHandle old_device;
  1165.     PixMapHandle result;
  1166.     
  1167.     old_device = GetGDevice ();
  1168.     SetGDevice (CAMLGDevice);
  1169.     result = NewPixMap ();
  1170.     DisposeHandle ((Handle) (*result)->pmTable);
  1171.     (*result)->pmTable = (*((CGrafPtr) CAMLOffScreen)->portPixMap)->pmTable;
  1172.     (*result)->baseAddr = (Ptr) image;
  1173.     (*result)->rowBytes = (max_depth * w + 31) / 32 * 4 + 0x8000;
  1174.     SetRect (&(*result)->bounds, 0, 0, w, h);
  1175.     SetGDevice (old_device);
  1176.     return (BitMap **) result;
  1177.   }else{
  1178.     BitMap **result = (BitMap **) NewHandle (sizeof (BitMap));
  1179.  
  1180.     (*result)->baseAddr = (Ptr) image;
  1181.     (*result)->rowBytes = (w + 31) / 32 * 4;
  1182.     SetRect(&(*result)->bounds, 0, 0, w, h);
  1183.     return result;
  1184.   }
  1185. }
  1186.  
  1187. value make_image (value mat)    /* ML */
  1188. {
  1189.   int height, width, i, j;
  1190.   int has_transp;
  1191.   GrafPtr old_port;
  1192.   value res;
  1193.   Push_roots(roots, 3)
  1194. #define bdata (roots[0])
  1195. #define bmask (roots[1])
  1196. #define matrix (roots[2])
  1197.   
  1198.   check_graph ();
  1199.   matrix = mat;
  1200.   GetPort (&old_port);
  1201.   height = Wosize_val(matrix);
  1202.   if (height == 0) {
  1203.     width = 0;
  1204.   } else {
  1205.     width = Wosize_val(Field(matrix, 0));
  1206.     for (i = 1; i < height; i++) {
  1207.       if (width != Wosize_val(Field(matrix, i)))
  1208.     graphic_fail("make_image: non-rectangular matrix");
  1209.     }
  1210.   }
  1211.   bdata = new_bits (width, height, max_depth);
  1212.   has_transp = 0;
  1213.   if (color_qd){
  1214.     CGrafPort port;
  1215.     RGBColor qd_col;
  1216.     long col;
  1217.     
  1218.     OpenCPort (&port);
  1219.     DisposeHandle ((Handle) port.portPixMap);
  1220.     port.portPixMap = (PixMapHandle) image_to_bitmap (bdata, width, height, 0);
  1221.     port.portRect = (*port.portPixMap)->bounds;
  1222.     for (i = 0; i< height; i++){
  1223.       for (j = 0; j < width; j++){
  1224.         col = Long_val (Field (Field (matrix, i), j));
  1225.     if (col == -1){
  1226.       has_transp = 1;
  1227.     }else{
  1228.       qd_col.red = (col >> 16) * 256;
  1229.       qd_col.green = ((col >> 8) & 0xff) * 256;
  1230.       qd_col.blue = (col & 0xff) * 256;
  1231.           SetCPixel (j, i, &qd_col);
  1232.     }
  1233.       }
  1234.     }
  1235.     SetPort (old_port);
  1236.     CloseCPort (&port);
  1237.   }else{
  1238.     GrafPort port;
  1239.     BitMap **h;
  1240.     
  1241.     OpenPort (&port);
  1242.     h = image_to_bitmap (bdata, width, height, 0);
  1243.     port.portBits = **h;
  1244.     DisposeHandle ((Handle) h);
  1245.     port.portRect = port.portBits.bounds;
  1246.     EraseRect (&port.portBits.bounds);
  1247.     for (i = 0; i< height; i++){
  1248.       for (j = 0; j < width; j++){
  1249.         switch (Long_val (Field (Field (matrix, i), j))){
  1250.     case -1: has_transp = 1; break;
  1251.     case 0xFFFFFF: break;
  1252.     default: MoveTo (j, i); Line (0, 0); break;
  1253.     }
  1254.       }
  1255.     }
  1256.     SetPort (old_port);
  1257.     ClosePort (&port);
  1258.   }
  1259.   if (has_transp) {
  1260.     GrafPort port;
  1261.     BitMap **h;
  1262.  
  1263.     bmask = new_bits (width, height, 1);
  1264.     OpenPort (&port);
  1265.     h = image_to_bitmap (bmask, width, height, 1);
  1266.     port.portBits = **h;
  1267.     DisposeHandle ((Handle) h);
  1268.     port.portRect = port.portBits.bounds;
  1269.     EraseRect (&port.portBits.bounds);
  1270.     for (i = 0; i< height; i++){
  1271.       for (j = 0; j < width; j++){
  1272.         if (Long_val (Field (Field (matrix, i), j)) != -1){
  1273.       MoveTo (j, i); Line (0, 0);
  1274.     }
  1275.       }
  1276.     }
  1277.     SetPort (old_port);
  1278.     ClosePort (&port);
  1279.   }else{
  1280.     bmask = Val_long (0);
  1281.   }
  1282.   res = alloc_tuple (4);
  1283.   Width (res) = Val_int (width);
  1284.   Height (res) = Val_int (height);
  1285.   Data (res) = bdata;
  1286.   Mask (res) = bmask;
  1287.   Pop_roots ();
  1288.   return res;
  1289. #undef matrix
  1290. #undef bdata
  1291. #undef bmask
  1292. }
  1293.  
  1294. static value alloc_int_vect(mlsize_t size)
  1295. {
  1296.     value res;
  1297.     mlsize_t i;
  1298.     
  1299.     if (size == 0) return Atom(0);
  1300.     if (size <= Max_young_wosize) {
  1301.         res = alloc(size, 0);
  1302.     } else {
  1303.         res = alloc_shr(size, 0);
  1304.     }
  1305.     for (i = 0; i < size; i++) {
  1306.         Field(res, i) = Val_long(0);
  1307.     }
  1308.     return res;
  1309. }
  1310.     
  1311. value dump_image(value image)    /* ML */
  1312. {
  1313.   int height, width, i, j;
  1314.   GrafPtr old_port;
  1315.   Push_roots(roots, 2);
  1316. #define matrix (roots[0])
  1317. #define im (roots [1])
  1318.  
  1319.   check_graph ();
  1320.   im = image;
  1321.   GetPort (&old_port);
  1322.   height = Int_val (Height (im));
  1323.   width = Int_val (Width (im));
  1324.   matrix = alloc_int_vect (height);
  1325.   for (i = 0; i < height; i++) {
  1326.     modify (&Field (matrix, i), alloc_int_vect (width));
  1327.   }
  1328.  
  1329.   if (color_qd){
  1330.     CGrafPort port;
  1331.     RGBColor qd_col;
  1332.      
  1333.     OpenCPort (&port);
  1334.     DisposeHandle ((Handle) port.portPixMap);
  1335.     port.portPixMap
  1336.       = (PixMapHandle) image_to_bitmap (Data (im), width, height, 0);
  1337.     port.portRect = (*port.portPixMap)->bounds;
  1338.     for (i = 0; i< height; i++){
  1339.       for (j = 0; j < width; j++){
  1340.         GetCPixel (j, i, &qd_col);
  1341.     Field (Field (matrix, i), j) = Val_long ((qd_col.red / 256 << 16)
  1342.                                              + (qd_col.green / 256 << 8)
  1343.                          + qd_col.blue / 256);
  1344.       }
  1345.     }
  1346.     SetPort (old_port);
  1347.     CloseCPort (&port);
  1348.   }else{
  1349.     GrafPort port;
  1350.     BitMap **h;
  1351.     
  1352.     OpenPort (&port);
  1353.     h = image_to_bitmap (Data (im), width, height, 0);
  1354.     port.portBits = **h;
  1355.     DisposeHandle ((Handle) h);
  1356.     port.portRect = port.portBits.bounds;
  1357.     for (i = 0; i< height; i++){
  1358.       for (j = 0; j < width; j++){
  1359.         Field (Field (matrix, i), j)
  1360.       = Val_long (GetPixel (j, i) ? 0 : 0xFFFFFF);
  1361.       }
  1362.     }
  1363.     SetPort (old_port);
  1364.     ClosePort (&port);
  1365.   }
  1366.  
  1367.   if (Mask(im) != Val_long(0)) {
  1368.     GrafPort port;
  1369.     BitMap **h;
  1370.     
  1371.     OpenPort (&port);
  1372.     h = image_to_bitmap (Mask (im), width, height, 1);
  1373.     port.portBits = **h;
  1374.     DisposeHandle ((Handle) h);
  1375.     port.portRect = port.portBits.bounds;
  1376.     for (i = 0; i< height; i++){
  1377.       for (j = 0; j < width; j++){
  1378.         if (! GetPixel (j, i)) Field (Field (matrix, i), j) = -1;
  1379.       }
  1380.     }
  1381.     SetPort (old_port);
  1382.     ClosePort (&port);
  1383.   }
  1384.   Pop_roots();
  1385.   return matrix;
  1386. #undef matrix
  1387. #undef im
  1388. }
  1389.  
  1390. value draw_image(value image, value x, value y)    /* ML */
  1391. {
  1392.   short rx, ry;
  1393.   int w, h;
  1394.   BitMap **src_bitmap, **mask_bitmap;
  1395.   Rect dst_rect, src_rect;
  1396.   
  1397.   check_graph();
  1398.   w = Int_val (Width (image));
  1399.   h = Int_val (Height (image));
  1400.   rx = Long_val(x);
  1401.   ry = convert_y(y) - h + 1;
  1402.   SetRect (&dst_rect, rx, ry, rx + w, ry + h);
  1403.   SetRect (&src_rect, 0, 0, w, h);
  1404.   Begin_offscreen
  1405.     if (Mask (image) != Val_long(0)) {
  1406.       src_bitmap = image_to_bitmap (Data(image), w, h, 0);
  1407.       mask_bitmap = image_to_bitmap (Mask(image), w, h, 1);
  1408.       copy_mask (*src_bitmap, *mask_bitmap, &CAMLOffScreen->portBits,
  1409.              &src_rect, &src_rect, &dst_rect);
  1410.       DisposeHandle ((Handle) src_bitmap);
  1411.       DisposeHandle ((Handle) mask_bitmap);
  1412.     }else{
  1413.       src_bitmap = image_to_bitmap (Data(image), w, h, 0);
  1414.       copy_bits (*src_bitmap, &CAMLOffScreen->portBits,
  1415.                  &src_rect, &dst_rect, srcCopy, nil);
  1416.       DisposeHandle ((Handle) src_bitmap);
  1417.     }
  1418.   End_offscreen
  1419.   OffsetRect(&dst_rect, offset_x, offset_y);
  1420.   SectRect(&dst_rect, &grafpk->viewRect, &dst_rect);
  1421.   src_rect = dst_rect;
  1422.   OffsetRect(&src_rect, -offset_x, -offset_y);
  1423.   SetPort (CAMLGraph);
  1424.   copy_bits (&CAMLOffScreen->portBits, &CAMLGraph->portBits,
  1425.          &src_rect, &dst_rect, srcCopy, nil);
  1426.   return Atom(0);
  1427. }
  1428.  
  1429. value create_image (value w, value h)     /* ML */
  1430. {
  1431.   value res;
  1432.   Push_roots (roots, 1);
  1433. #define bdata (roots[0])
  1434.  
  1435.   check_graph ();
  1436.   if (Int_val (w) < 0 || Int_val (h) < 0)
  1437.     graphic_fail("get_image: width and height must be positive");
  1438.   bdata = new_bits (Int_val (w), Int_val (h), max_depth);
  1439.   res = alloc_tuple (4);
  1440.   Width (res) = w;
  1441.   Height (res) = h;
  1442.   Data (res) = bdata;
  1443.   Mask (res) = Val_long (0);
  1444.   Pop_roots ();
  1445.   return res;
  1446. #undef bdata
  1447. }
  1448.  
  1449. value blit_image (value i, value x, value y)    /* ML */
  1450. {
  1451.   short rx, ry, width, height;
  1452.   BitMap **dst_bitmap;
  1453.   Rect src_rect;
  1454.   
  1455.   check_graph();
  1456.   width = Short_val (Width (i));
  1457.   height = Short_val (Height (i));
  1458.   dst_bitmap = image_to_bitmap (Data (i), width, height, 0);
  1459.   rx = Short_val(x);
  1460.   ry = convert_y(y) + 1;
  1461.   SetRect (&src_rect, rx, ry - height, rx + width, ry);
  1462.   Begin_offscreen
  1463.   copy_bits (&CAMLOffScreen->portBits, *dst_bitmap,
  1464.              &src_rect, &(*dst_bitmap)->bounds, srcCopy, nil);
  1465.   End_offscreen
  1466.   return Atom (0);
  1467. }
  1468.  
  1469. value wait_event (value l)        /* ML */
  1470. {
  1471.   int b_down = 0;
  1472.   int b_up = 0;
  1473.   int key_press = 0;
  1474.   int motion = 0;
  1475.   int poll = 0;
  1476.   EventRecord event;
  1477.   value result;
  1478.  
  1479.   check_graph ();
  1480.   enter_blocking_section ();
  1481.   while (l != Atom (0)){
  1482.     switch (Tag_val (Field (l, 0))){
  1483.     case 0: b_down = 1; break;
  1484.     case 1: b_up = 1; break;
  1485.     case 2: key_press = 1; break;
  1486.     case 3: motion = 1; break;
  1487.     case 4: poll = 1; break;
  1488.     }
  1489.     l = Field (l, 1);
  1490.   }
  1491.   while (1){
  1492.     LookGraphEvent (&event, motion, poll);
  1493.     if (poll || motion
  1494.         || b_down && event.what == mouseDown
  1495.         || b_up && event.what == mouseUp
  1496.         || key_press && event.what == keyDown)
  1497.       break;
  1498.   }
  1499.   result = alloc_tuple (5);
  1500.   SetPort (CAMLGraph);
  1501.   GlobalToLocal (&event.where);
  1502.   Field (result, 0) = Val_int (event.where.h - offset_x);
  1503.   Field (result, 1) = Val_int (CAMLOffScreen->portRect.bottom - 1
  1504.                                - (event.where.v - offset_y));
  1505.   Field (result, 2) = Atom (!(event.modifiers & btnState));
  1506.   if (event.what == keyDown){
  1507.     Field (result, 3) = Atom (1);
  1508.     Field (result, 4) = Val_int (event.message & charCodeMask);
  1509.   }else{
  1510.     Field (result, 3) = Atom (0);
  1511.     Field (result, 4) = Val_int (0);
  1512.   }
  1513.   leave_blocking_section ();
  1514.   return result;
  1515. }
  1516.  
  1517. value sound(value freq, value delay)    /* ML */
  1518. {
  1519.   long f = Long_val (freq);
  1520.   long d = Long_val (delay);
  1521.   int note;
  1522.   SndCommand cmd;
  1523.   SndChannelPtr chan = NULL;
  1524.   
  1525.   enter_blocking_section();
  1526.   note = 69 + (log((double) f / 440.0) / log(twelfthRootTwo) + 0.5);
  1527.   if (note < 1) note = 1;
  1528.   if (note > 127) note = 127;
  1529.   cmd.cmd = freqDurationCmd;
  1530.   cmd.param1 = d * 2;
  1531.   cmd.param2 = 0xFF000000 + note;
  1532.   if (SndNewChannel (&chan, squareWaveSynth, 0, NULL) != noErr){
  1533.     SysBeep (1);
  1534.   }else{
  1535.     if (SndDoCommand (chan, &cmd, 0) != noErr) SysBeep(1);
  1536.     SndDisposeChannel (chan, 0);
  1537.   }
  1538.   leave_blocking_section();
  1539.   return Atom(0);
  1540. }
  1541.