home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / Show_help / Show_help.c < prev   
Encoding:
C/C++ Source or Header  |  1991-06-15  |  27.4 KB  |  1,020 lines  |  [TEXT/KAHL]

  1. #define A4_PROJECT    0    /* Set to 1 for a code resource (DA, cdev, etc.) */
  2. #define USE_PICTS    1    /* 1 if PICTs are to be displayed, 0 for text only */
  3. #define ONE_RESOURCE 1    /* 1 for Get1Resource, 0 for GetResource, etc. */
  4.  
  5. #include <MacProto.h>    /* optional */
  6.  
  7. /*
  8.     Show_help by James W. Walker, June 1991
  9.     This code is freely usable.
  10.     
  11.     e-mail:
  12.         Internet            76367.2271@compuserve.com
  13.         CIS                    76367,2271
  14.         America Online        JWWalker
  15.     
  16.     This code displays scrolling text in a dialog box.  The text comes
  17.     from TEXT/styl resources, which can be created with ResEdit 2.1.
  18.     Pictures can be included in the text using the same scheme as
  19.     TeachText: Each option-space character indicates where the top
  20.     edge of a picture should go, and pictures are centered horizontally.
  21.     Pictures come from consecutively-numbered PICT resources.  The text
  22.     cannot be edited, but one can select text and copy it to the
  23.     clipboard using command-C, or save it as a TeachText file.
  24.     
  25.     The fact that our window is not resizeable has its advantages.  We
  26.     can compute where the pictures go, once and for all, except to take
  27.     vertical scrolling into account.
  28.     
  29.     A popup menu can be used to jump to "bookmarks", which are indicated
  30.     by tab characters at ends of lines.
  31.     
  32.     Prototype:
  33.     
  34.     void Show_help( short help_dlog_id, short help_text_id,
  35.         short base_pict_id, StringPtr default_filename,
  36.         StringPtr default_menuname );
  37.     
  38.     help_dlog_id  is the resource ID of the DLOG resource.  The dialog
  39.                   should have an OK button as item 1 and a userItem, to
  40.                   display the text, in item 2.
  41.     
  42.     help_text_id is the resource ID of the TEXT and styl resources.
  43.     
  44.     base_pict_id is the resource ID of the first PICT resource.
  45.  
  46.     default_filename is the filename initially presented in the Save dialog
  47.                   when the user saves the help text as TeachText.
  48.     
  49.     default_menuname is the menu item used for a line that consists of
  50.                   a tab and nothing else.
  51. */
  52.  
  53.  
  54. #ifndef NIL
  55. #define        NIL        0L
  56. #endif
  57.  
  58. #if A4_PROJECT
  59. #include <SetUpA4.h>
  60. #endif
  61.  
  62. #if ONE_RESOURCE
  63. #define GetResource            Get1Resource
  64. #define CountResources        Count1Resources
  65. #endif
  66.  
  67. #define CHECKPOINTS 0
  68. #define ASSERTIONS    0
  69.  
  70. #if ASSERTIONS
  71. #define        ASSERT(x,y)        if (!(x)) {DebugStr("\p" y)}
  72. #else
  73. #define        ASSERT(x,y)
  74. #endif
  75.  
  76. #if CHECKPOINTS
  77. #define CKPT(x)        DebugStr( "\p" x );
  78. #else
  79. #define CKPT(x)
  80. #endif
  81.  
  82. enum {
  83.     c_OK = 1,    /* OK button */
  84.     c_help,        /* userItem for our help display */
  85.     c_save,        /* Button to save as TeachText */
  86.     c_menu        /* userItem for popup menu */
  87. };
  88.  
  89. #define        SCROLLBAR_WIDTH    16
  90. #define        TEXT_INSET        4
  91.  
  92. typedef struct {
  93.     Rect        bounds;
  94.     PicHandle    pict;
  95. } pict_info;
  96.  
  97. typedef struct {
  98.     DialogRecord    dialog;
  99.     ControlHandle    scrollbar;
  100. #if USE_PICTS
  101.     Boolean            high_defer_flag;
  102.     Rect            high_rect[3];
  103.     short            high_waiting;
  104.     short            pict_count;    /* how many pictures */
  105.     pict_info        *pict_data;    /* pointer to an array */
  106. #endif
  107. }    help_record, *help_ptr;
  108.  
  109. /* private global variables */
  110. static    RgnHandle    save_clip;
  111. static    CursHandle    watch_cursor, ibeam_cursor;
  112.  
  113. /* Prototypes of public routines */
  114. void Show_help( short help_dlog_id, short help_text_id,
  115.     short base_pict_id, StringPtr default_filename,
  116.     StringPtr default_menuname );
  117. void Flash_button( DialogPtr the_dialog, short item_number );
  118.  
  119. /* Prototypes of private routines
  120.  */
  121. static pascal Boolean Help_filter( DialogPtr dialog,
  122.     EventRecord    *event, short *itemHit);
  123. static pascal void  userItem_proc( WindowPtr the_window, short item_num );
  124. static pascal void  Menu_userItem_proc( WindowPtr the_window, short item_num );
  125. static pascal void Scroll_text( ControlHandle the_bar, int part_code );
  126. static pascal Auto_scroll( void );
  127. static void Handle_scroll( DialogPtr dialog,
  128.     short the_part, Point local_point );
  129. static void Adjust_text( DialogPtr dialog );
  130. static void Save_text( TEHandle the_text, short base_pict_id,
  131.     StringPtr default_filename );
  132. static void Topic_menu( DialogPtr dlog, MenuHandle help_popup );
  133. static MenuHandle Build_popup( TEHandle    the_text, StringPtr default_menuname );
  134.  
  135. #if USE_PICTS
  136. static void Find_pictures( DialogPtr dlog, short first_pict_id );
  137. static void Draw_picts( WindowPtr the_window, Rect *update_rect );
  138. static pascal void High_hook( Rect *high_rect );
  139. static void High_hook_glue( void );
  140. static void Do_deferred_hilites( help_ptr  hptr, Rect *update_rect );
  141. #endif
  142.  
  143. /* ------------------------- Show_help --------------------------------- */
  144. void Show_help( short help_dlog_id, short help_text_id,
  145.     short base_pict_id, StringPtr default_filename,
  146.     StringPtr default_menuname )
  147. {
  148.     register    DialogPtr    dptr;
  149.     register    TEHandle    the_text;
  150.     short        itype, ihit;
  151.     Handle        item_h;
  152.     Rect        help_item_box, box;
  153.     Handle            help_TEXT;
  154.     StScrpHandle    help_styl;
  155.     GrafPtr        save_port;
  156.     Rect        dest, view;
  157.     ControlHandle    the_bar;
  158.     short        max_scroll, nLines;
  159.     Point        place;
  160.     MenuHandle    help_popup;
  161.     
  162.     watch_cursor = GetCursor( watchCursor );
  163.     SetCursor( *watch_cursor );
  164.     ibeam_cursor = GetCursor( iBeamCursor );
  165.     dptr = (DialogPtr) NewPtr( sizeof(help_record) );
  166.     dptr = GetNewDialog( help_dlog_id, (DialogPeek) dptr, (WindowPtr)-1L );
  167.     ASSERT( dptr != NIL, "Failed GetNewDialog" );
  168.     GetPort( &save_port );
  169.     SetPort( dptr );
  170.     
  171.     help_TEXT = GetResource( 'TEXT', help_text_id );
  172.     if (help_TEXT == NIL)
  173.     {
  174.         ASSERT(false, "Failed to find help TEXT resource" );
  175.         SysBeep(1);
  176.         goto getout;
  177.     }
  178.     help_styl = (StScrpHandle) GetResource( 'styl', help_text_id );
  179.     if (help_styl == NIL)
  180.     {
  181.         DisposHandle( help_TEXT );
  182.         ASSERT( false, "Failed to find styl resource" );
  183.         SysBeep(1);
  184.         goto getout;
  185.     }
  186.     HLock( help_TEXT );
  187.     
  188.     GetDItem( dptr, c_help, &itype, &item_h, &help_item_box );
  189.     SetDItem( dptr, c_help, itype, (Handle) userItem_proc, &help_item_box );
  190.     view = help_item_box;
  191.     InsetRect( &view, 1, 1 );
  192.     view.right -= SCROLLBAR_WIDTH;
  193.     dest = view;
  194.     InsetRect( &dest, TEXT_INSET, 0 );
  195.     the_text = TEStylNew( &dest, &view );
  196.     ASSERT( the_text != NIL, "Failed TEStylNew." );
  197.     
  198.     TEStylInsert( *help_TEXT, GetHandleSize(help_TEXT),
  199.         help_styl, the_text );
  200.     TECalText( the_text );    /* Maybe not needed, but can't hurt. */
  201.     TEActivate( the_text );
  202.     ReleaseResource( (Handle) help_styl );
  203.     ReleaseResource( help_TEXT );
  204.     nLines = (**the_text).nLines;
  205.     SetWRefCon( dptr, (long)the_text );
  206.     max_scroll = TEGetHeight( (long) nLines, 1L, the_text )
  207.         - (view.bottom - view.top);
  208.     
  209.     help_item_box.left = help_item_box.right - SCROLLBAR_WIDTH;
  210.     the_bar = NewControl( dptr, &help_item_box, "\p", true,
  211.         0, 0, max_scroll, scrollBarProc, NIL );
  212.     ASSERT( the_bar != NIL, "Failed NewControl for scroll bar." );
  213.     ((help_ptr) dptr)->scrollbar = the_bar;
  214.  
  215. #if USE_PICTS
  216.     Find_pictures( dptr, base_pict_id );
  217. #endif
  218.     help_popup = Build_popup( the_text, default_menuname );
  219.  
  220.     TEAutoView( TRUE, the_text );    /* Permit auto-scrolling */
  221.     CKPT( "Installing ClikLoop" );
  222.     (**the_text).clikLoop = (ProcPtr)Auto_scroll;
  223. #if USE_PICTS
  224.     (**the_text).highHook = (ProcPtr) High_hook_glue;
  225.     ((help_ptr)dptr)->high_defer_flag = false;
  226. #endif
  227.  
  228.     GetDItem( dptr, c_menu, &itype, &item_h, &box );
  229.     SetDItem( dptr, c_menu, itype, (Handle) Menu_userItem_proc, &box );
  230.     
  231.     save_clip = NewRgn();    /* Used in Draw_picts */
  232.     ShowWindow( dptr );
  233. #if A4_PROJECT
  234.     RememberA4();
  235. #endif
  236.     InitCursor();
  237.  
  238.     do {
  239.         ModalDialog( (ProcPtr) Help_filter, &ihit );
  240.         if (ihit == c_save)
  241.             Save_text( the_text, base_pict_id, default_filename );
  242.         else if (ihit == c_menu)
  243.             Topic_menu( dptr, help_popup );
  244.     } while (ihit != c_OK);
  245.     
  246.     
  247.     DisposeRgn( save_clip );
  248. #if USE_PICTS
  249.     DisposPtr( (Ptr) ((help_ptr)dptr)->pict_data );
  250. #endif
  251.     TEDispose( the_text );
  252. getout:
  253.     DisposDialog( dptr );
  254.     DisposeMenu( help_popup );
  255.     SetPort( save_port );
  256. }
  257.  
  258. /* --------------------------- Build_popup ------------------------- */
  259. /*
  260.     Build a popup menu of the sections of the help text.  We scan for
  261.     tab characters.  The text between the tab character and the preceding
  262.     line break will be a menu item, unless it is the null string; then we
  263.     use the default menu name that was passed to Show_help.
  264. */
  265. static MenuHandle Build_popup( TEHandle    the_text, StringPtr default_menuname )
  266. {
  267.     MenuHandle    popup;
  268.     short        menu_id;
  269.     SignedByte    text_state;
  270.     Handle        text_h;    /* handle to just the text */
  271.     Str255        menu_data;
  272.     char        *text;    /* pointer to the help text */
  273.     register short        scan, line_start;
  274.     short        text_size, title_length;
  275.     
  276.     /* Find an unused menu ID */
  277.     menu_id = 1300; /* no particular reason */
  278.     while (GetMHandle(menu_id))
  279.         ++menu_id;
  280.     
  281.     popup = NewMenu( menu_id, "\p" );
  282.  
  283.     text_h = (**the_text).hText;
  284.     text_state = HGetState( text_h );
  285.     HLock( text_h );
  286.     text = *text_h;
  287.     text_size = (short) GetHandleSize( text_h );
  288.     line_start = 0;
  289.     for (scan = 0; scan < text_size; scan++ )
  290.     {
  291.         if (text[scan] == '\r')
  292.         {
  293.             line_start = scan + 1;
  294.         }
  295.         else if (text[scan] == '\t')
  296.         {
  297.             title_length = scan - line_start;
  298.             if (title_length == 0)
  299.                 AppendMenu( popup, default_menuname );
  300.             else if (title_length <= 255)
  301.             {
  302.                 menu_data[0] = title_length;
  303.                 BlockMove( &text[line_start], &menu_data[1], title_length );
  304.                 AppendMenu( popup, menu_data );
  305.             }
  306.         }
  307.     }
  308.     
  309.     HSetState( text_h, text_state );
  310.     return popup;
  311. }
  312.  
  313. /* ------------------------- Topic_menu ------------------------ */
  314. /*
  315.     This routine is called when the menu title is clicked.
  316.     It pops up the menu and scrolls to the indicated tab character.
  317. */
  318. static void Topic_menu( DialogPtr dptr, MenuHandle menu )
  319. {
  320.     short            menu_id;
  321.     Handle            item_h;
  322.     short            itype;
  323.     Rect            box;
  324.     Point            where;
  325.     long            menu_return;
  326.     short            menu_choice;
  327.     ControlHandle    bar;
  328.     char            tab_char[2] = "\t";
  329.     register short        i;
  330.     register TEHandle    the_text;
  331.     Handle            text_h;
  332.     SignedByte        text_state;
  333.     register long        offset;
  334.     TextStyle        what_style;
  335.     short            line_height, font_ascent;
  336.     
  337.     if (menu == NIL) return;
  338.     InsertMenu( menu, -1 );
  339.     GetDItem( dptr, c_menu, &itype, &item_h, &box );
  340.     where.h = box.left;
  341.     where.v = box.bottom;
  342.     LocalToGlobal( &where );
  343.     InvertRect( &box );
  344.     menu_return = PopUpMenuSelect( menu, where.v, where.h, 0 );
  345.     InvertRect( &box );
  346.     if (HiWord(menu_return))    /* Something selected */
  347.     {
  348.         menu_choice = LoWord( menu_return );
  349.         bar = ((help_ptr)dptr)->scrollbar;
  350.         the_text = (TEHandle) GetWRefCon( dptr );
  351.         text_h = (**the_text).hText;
  352.         text_state = HGetState( text_h );
  353.         HLock( text_h );
  354.         
  355.         /* Find tab character number menu_choice */
  356.         offset = -1L;
  357.         for (i = 1; i <= menu_choice; ++i)
  358.         {
  359.             ++offset; /* so we don't find the same thing twice */
  360.             offset = Munger(text_h, offset, tab_char, 1L, NIL, NIL);
  361.         }
  362.  
  363.         *(long *)&where = TEGetPoint( (short)offset, the_text );
  364.         TEGetStyle( (short)offset, &what_style, &line_height,
  365.             &font_ascent, the_text );
  366.         where.v -= line_height;    /* align to TOP of tab */
  367.         /*
  368.             Now where.v is in local coordinates.
  369.         */
  370.         where.v -= (**the_text).destRect.top;
  371.         SetCtlValue( bar,  where.v );
  372.         HSetState( text_h, text_state );
  373.         
  374.         Adjust_text( dptr );
  375.     }
  376.     menu_id = (**menu).menuID;
  377.     DeleteMenu( menu_id );
  378. }
  379.  
  380. /* ------------------------- Save_text ------------------------ */
  381. /*
  382.     This is called when the user clicks on the "Save as TeachText"
  383.     button.
  384. */
  385. static void Save_text( TEHandle the_text, short base_pict_id,
  386.     StringPtr default_filename )
  387. {
  388.     Point    where;
  389.     static Str255    prompt = "\pName of TeachText file:";
  390.     SFReply        reply;
  391.     OSErr        err;
  392.     short        data_refnum, res_refnum, old_resfile;
  393.     Handle        text_data;
  394.     SignedByte    state;
  395.     long        count;
  396. #if USE_PICTS
  397.     register short        num_picts, pict_id;
  398.     Handle        old_pict, new_pict;
  399. #endif
  400.     
  401.     where.h = where.v = 100;
  402.     SFPutFile( where, prompt, default_filename, NIL, &reply );
  403.     if (reply.good)
  404.     {
  405.         old_resfile = CurResFile();
  406. #if USE_PICTS
  407.         num_picts = CountResources( 'PICT' );
  408. #endif
  409.         
  410.         err = FSDelete( reply.fName, reply.vRefNum );
  411.         err = Create( reply.fName, reply.vRefNum, 'ttxt', 'ttro' );
  412.         err = FSOpen( reply.fName, reply.vRefNum, &data_refnum );
  413.         text_data = (**the_text).hText;
  414.         state = HGetState(text_data );
  415.         HLock( text_data );
  416.         count = GetHandleSize( text_data );
  417.         err = FSWrite( data_refnum, &count, *text_data );
  418.         err = FSClose( data_refnum );
  419.         HSetState( text_data, state );
  420.  
  421. #if USE_PICTS
  422.         if (num_picts > 0)
  423.         {
  424.             err = SetVol( NIL, reply.vRefNum );
  425.             CreateResFile( reply.fName );
  426.             err = ResError();
  427.             res_refnum = OpenResFile( reply.fName );
  428.             err = ResError();
  429.             for (pict_id = base_pict_id;
  430.                 pict_id < base_pict_id + num_picts; ++pict_id )
  431.             {
  432.                 UseResFile( old_resfile );
  433.                 old_pict = GetResource( 'PICT', pict_id );
  434.                 if (old_pict == NIL)
  435.                     break;
  436.                 new_pict = old_pict;
  437.                 err = HandToHand( &new_pict );
  438.                 UseResFile( res_refnum );
  439.                 AddResource( new_pict, 'PICT',
  440.                     pict_id - base_pict_id + 1000, "\p" );
  441.                 err = ResError();
  442.             }
  443.             CloseResFile( res_refnum );
  444.             err = FlushVol( NIL, reply.vRefNum );
  445.         }
  446. #endif
  447.     }
  448. }
  449.  
  450. #if USE_PICTS
  451. /* ------------------------- High_hook_glue ------------------------ */
  452. static void High_hook_glue( void )
  453. {
  454.     asm {
  455.         move.L    (SP)+, A0    ; get address of rectangle
  456.         movem.L    A2-A5/D3-D7, -(SP)    ; save registers
  457.         move.L    A0, -(SP)
  458.     }
  459.     CKPT( "High_hook_glue" );
  460.     asm {
  461.         JSR        High_hook
  462.         movem.L    (SP)+, A2-A5/D3-D7    ; restore registers
  463.         RTS
  464.     }
  465. }
  466.  
  467. /* ------------------------- High_hook -------------------------- */
  468. /*
  469.     This deferred highlighting scheme is used to ensure that highlighting
  470.     will be done after any pictures have been drawn, not before.  To do
  471.     otherwise can cause pictures to be incorrectly highlighted during
  472.     auto-scrolling.  This effect can be seen in TeachText.
  473. */
  474. static pascal void High_hook( Rect *the_rect )
  475. {
  476.     register help_ptr    front;
  477.     
  478.     CKPT( "High_hook");
  479.     front = (help_ptr) FrontWindow();
  480.     if ( (front == NIL) ||
  481.         (GetPtrSize((Ptr)front) != sizeof(help_record)) )
  482.         return;
  483.     if (!front->high_defer_flag)
  484.         InvertRect( the_rect );
  485.     else
  486.     {
  487.         if ( front->high_waiting >= 3 )
  488.             DebugStr("\pHighlight overflow");
  489.         else
  490.         {
  491.             front->high_rect[ front->high_waiting ]
  492.                 = *the_rect;
  493.             front->high_waiting++;
  494.         }
  495.     }
  496. }
  497. #endif /* USE_PICTS */
  498.  
  499. /* ------------------------- Auto_scroll ----------------------------- */
  500. /*
  501.     This is a ClikLoop routine, called repeatedly by TEClick when the
  502.     mouse is down.
  503. */
  504. static pascal Auto_scroll()
  505. {
  506.     register    WindowPtr    the_display;
  507.     register    ControlHandle    the_bar;
  508.     Point                    mouse_point;
  509.     Rect                    view_rect;
  510.     register     TEHandle    the_text;
  511.     
  512.     asm {
  513.         movem.l        a1-a5/d1-d7, -(SP)
  514.     }
  515.     CKPT( "Auto_scroll");
  516.     the_display = FrontWindow();
  517.     if ( (the_display != NIL) &&
  518.         (GetPtrSize((Ptr)the_display) == sizeof(help_record)) )
  519.     {
  520.         the_text = (TEHandle) GetWRefCon( the_display );
  521.         the_bar = ((help_ptr) the_display)->scrollbar;
  522.         
  523.         GetMouse( &mouse_point );
  524.         view_rect = (**the_text).viewRect;
  525.         if (mouse_point.v < view_rect.top)
  526.             Scroll_text( the_bar, inUpButton );
  527.         else if (mouse_point.v > view_rect.bottom)
  528.             Scroll_text( the_bar, inDownButton );
  529.     }
  530.     asm {
  531.         movem.L        (SP)+, a1-a5/d1-d7
  532.         moveQ        #1, D0
  533.     }
  534. }
  535.  
  536. #if USE_PICTS
  537. /* ------------------------- Draw_picts --------------------------------- */
  538. /*
  539.     Called by Adjust_text and userItem_proc to draw pictures.
  540. */
  541. static void Draw_picts( WindowPtr the_window, Rect *update_rect )
  542. {
  543.     register TEHandle    the_text;
  544.     register short        pict_count, pict_index;
  545.     PicHandle    the_pict;
  546.     short         v_offset;
  547.     Rect        pict_loc, dummy;
  548.     
  549.     CKPT( "Draw_picts");
  550.     the_text = (TEHandle) GetWRefCon( the_window );
  551.     v_offset = (**the_text).destRect.top - (**the_text).viewRect.top
  552.         - TEXT_INSET;
  553.     pict_count = ((help_ptr) the_window)->pict_count;
  554.     for (pict_index = 0; pict_index < pict_count; pict_index++)
  555.     {
  556.         pict_loc = ((help_ptr) the_window)->pict_data[pict_index].bounds;
  557.         OffsetRect( &pict_loc, 0, v_offset );
  558.         if (!SectRect( &pict_loc, update_rect, &dummy ))
  559.             continue;
  560.         the_pict = ((help_ptr) the_window)->pict_data[pict_index].pict;
  561.         LoadResource( (Handle) the_pict );
  562.         HLock( (Handle) the_pict );
  563.         GetClip( save_clip );
  564.         ClipRect( update_rect );
  565.         DrawPicture( the_pict, &pict_loc );
  566.         SetClip( save_clip );
  567.         HUnlock( (Handle) the_pict );
  568.     }
  569. }
  570.  
  571. /* ---------------------- Find_pictures ---------------------------- */
  572. static void Find_pictures( DialogPtr dlog, short first_pict_id )
  573. {
  574.     register TEHandle    the_text;
  575.     Handle        text_h;
  576.     SignedByte    text_state;
  577.     register long        offset;
  578.     long        text_size;
  579.     short        num_picts;
  580.     register short        which_pict;
  581.     char        option_space[2] = "\xCA";
  582.     pict_info    *pict;
  583.     Point        place;
  584.     short        line_height, font_ascent;
  585.     TextStyle    what_style;
  586.     
  587.     CKPT( "Find_pictures");
  588.     the_text = (TEHandle) GetWRefCon( dlog );
  589.     text_h = (**the_text).hText;
  590.     text_state = HGetState( text_h );
  591.     HLock( text_h );
  592.     
  593.     /* Count option-space characters in the text. */
  594.     text_size = GetHandleSize( text_h );
  595.     offset = 0L;
  596.     num_picts = 0;
  597.     offset = Munger(text_h, offset, option_space, 1L, NIL, NIL );
  598.     while ( (offset >= 0L) && (offset <= text_size) )
  599.     {
  600.         num_picts++;
  601.         offset++;
  602.         offset = Munger(text_h, offset, option_space, 1L, NIL, NIL );
  603.         
  604.     }
  605.     
  606.     /* Allocate storage for an array of picture bounds. */
  607.     pict = (pict_info *) NewPtr( sizeof(pict_info) * num_picts );
  608.     ((help_ptr)dlog)->pict_data = pict;
  609.     
  610.     /*
  611.         Initialize the picture info.  For each picture we record the
  612.         picture handle and its rectangle, in unscrolled window
  613.         coordinates.
  614.     */
  615.     offset = 0L;
  616.     for (which_pict = 0; which_pict < num_picts; which_pict++)
  617.     {
  618.         pict[which_pict].pict = (PicHandle) GetResource( 'PICT',
  619.             first_pict_id + which_pict );
  620.         if ( pict[which_pict].pict == NIL )
  621.             break;
  622.         offset = Munger(text_h, offset, option_space, 1L, NIL, NIL );
  623.         *(long *)&place = TEGetPoint( (short)offset, the_text );
  624.         TEGetStyle( offset, &what_style, &line_height,
  625.             &font_ascent, the_text );
  626.         place.v -= line_height;    /* align picture with TOP of option-space */
  627.         offset++;
  628.         pict[which_pict].bounds = (**pict[which_pict].pict).picFrame;
  629.         OffsetRect( &pict[which_pict].bounds,
  630.             ( ((**the_text).destRect.right + (**the_text).destRect.left) -
  631.             (pict[which_pict].bounds.right + pict[which_pict].bounds.left)
  632.             ) / 2,
  633.             - pict[which_pict].bounds.top + place.v );
  634.     }
  635.     ((help_ptr)dlog)->pict_count = which_pict;
  636.     
  637. getout:
  638.     HSetState( text_h, text_state );
  639. }
  640. #endif /* USE_PICTS */
  641.  
  642. /* ---------------------- Scroll_text ---------------------------- */
  643. /*
  644.     This is used as a TrackControl actionProc for scrolling, and also
  645.     called by Auto_scroll for automatic scrolling.
  646. */
  647. static pascal void Scroll_text( ControlHandle the_bar, int part_code )
  648. {
  649.     register TEHandle    the_text;
  650.     register short        delta;
  651.     register WindowPtr    the_display;
  652.     short                old_value;
  653.     short                offset, line;
  654.     Point                place;
  655.     Rect                view;
  656.     TextStyle            style;
  657.     short                line_height, font_ascent;
  658.     
  659.     CKPT( "Scroll_text");
  660. #if A4_PROJECT
  661.     SetUpA4();
  662. #endif
  663.     if (part_code != 0)
  664.     {
  665.         the_display = (**the_bar).contrlOwner;
  666.         the_text = (TEHandle) GetWRefCon( the_display );
  667.         view = (**the_text).viewRect;
  668.         place.h = view.left + TEXT_INSET;
  669.         
  670.         switch (part_code)
  671.         {
  672.             case inUpButton:
  673.                 place.v = view.top - 4;
  674.                 offset = TEGetOffset( place, the_text );
  675.                 *(long *)&place = TEGetPoint( offset, the_text );
  676.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  677.                     the_text );
  678.                 delta = place.v - line_height - view.top;
  679.                 break;
  680.             case inDownButton:
  681.                 place.v = view.bottom + 2;
  682.                 offset = TEGetOffset( place, the_text );
  683.                 *(long *)&place = TEGetPoint( offset, the_text );
  684.                 /* Now place.v is at the baseline of the border line. */
  685.                 delta = place.v - view.bottom;
  686.                 break;
  687.             case inPageUp:
  688.                 /*
  689.                     I want top border line to remain visible, and
  690.                     the top of a line should end up at view.top.
  691.                 */
  692.                 place.v = view.top + 2;
  693.                 offset = TEGetOffset( place, the_text );
  694.                 *(long *)&place = TEGetPoint( offset, the_text );
  695.                 /* place.v is at the baseline of the top border line. */
  696.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  697.                     the_text );
  698.                 place.v += line_height - font_ascent;
  699.                 place.v -= view.bottom - view.top;
  700.                 offset = TEGetOffset( place, the_text );
  701.                 *(long *)&place = TEGetPoint( offset, the_text );
  702.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  703.                     the_text );
  704.                 delta = place.v - view.top;
  705.                 if (offset == 0)
  706.                     delta -= line_height;
  707.                 break;
  708.             case inPageDown:
  709.                 /*
  710.                     I want bottom border line to remain visible, and
  711.                     the bottom of a line should end up at view.bottom.
  712.                 */
  713.                 place.v = view.bottom - 2;
  714.                 offset = TEGetOffset( place, the_text );
  715.                 *(long *)&place = TEGetPoint( offset, the_text );
  716.                 /* place.v is at the baseline of the bottom border line. */
  717.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  718.                     the_text );
  719.                 place.v -= font_ascent; /* Top edge of bottom border line */
  720.                 place.v += view.bottom - view.top;
  721.                 /* We're looking at the bottom border of the next page. */
  722.                 offset = TEGetOffset( place, the_text );
  723.                 *(long *)&place = TEGetPoint( offset, the_text );
  724.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  725.                     the_text );
  726.                 delta =  place.v - line_height - view.bottom;
  727.                 if (offset == (**the_text).teLength)
  728.                     delta += line_height;
  729.                 break;
  730.         }
  731.         old_value = GetCtlValue( the_bar );
  732.         if ( ((delta < 0) && (old_value > 0)) ||
  733.             ((delta > 0) && (old_value < GetCtlMax(the_bar))) )
  734.         {
  735.             /*
  736.                 When this routine is called, TextEdit may have set the
  737.                 clipping region to the view rectangle, so we reset it
  738.                 here to make sure the scroll bar gets drawn.
  739.             */
  740.             GetClip( save_clip );
  741.             ClipRect( &the_display->portRect );
  742.             SetCtlValue( the_bar, old_value + delta );
  743.             SetClip( save_clip );
  744.         }
  745.         Adjust_text( the_display );
  746.     }
  747. #if A4_PROJECT
  748.     RestoreA4();
  749. #endif
  750. }
  751.  
  752. /* ---------------------- Adjust_text ---------------------------- */
  753. /*
  754.     Called by Handle_scroll and Scroll_text to scroll the text and
  755.     pictures into sync with the scroll bar's control value.
  756. */
  757. static void Adjust_text( DialogPtr    dialog )
  758. {
  759.     register    TEHandle    the_text;
  760.     register    short    scroll_down;
  761.     short            old_scroll;
  762.     Rect            update_rect;
  763.     ControlHandle    the_bar;
  764.     
  765.     CKPT( "Adjust_text");
  766.     the_text = (TEHandle) GetWRefCon( dialog );
  767.     the_bar = ((help_ptr) dialog)->scrollbar;
  768.     old_scroll = (**the_text).viewRect.top - (**the_text).destRect.top;
  769.     scroll_down = old_scroll - GetCtlValue( the_bar );
  770.     if (scroll_down == 0)
  771.         return;
  772. #if USE_PICTS
  773.     ((help_ptr) dialog)->high_defer_flag = true;
  774.     ((help_ptr) dialog)->high_waiting = 0;
  775. #endif
  776.     TEScroll( 0, scroll_down, the_text );
  777. #if USE_PICTS
  778.     update_rect = (**the_text).viewRect;
  779.     if (scroll_down > 0)
  780.     {
  781.         if (scroll_down < (update_rect.bottom - update_rect.top))
  782.             update_rect.bottom = update_rect.top + scroll_down;
  783.     }
  784.     else
  785.         if (- scroll_down < (update_rect.bottom - update_rect.top))
  786.             update_rect.top = update_rect.bottom + scroll_down;
  787.     Draw_picts( dialog, &update_rect );
  788.     Do_deferred_hilites( (help_ptr) dialog, &update_rect );
  789. #endif
  790. }
  791.  
  792. #if USE_PICTS
  793. /* ---------------------- Do_deferred_hilites ---------------------- */
  794. static void Do_deferred_hilites( help_ptr  hptr, Rect *update_rect )
  795. {
  796.     Rect            *hilite;
  797.     
  798.     while (hptr->high_waiting > 0)
  799.     {
  800.         hptr->high_waiting--;
  801.         hilite =
  802.             &hptr->high_rect[hptr->high_waiting];
  803.         if (SectRect( hilite, update_rect, hilite ))
  804.             InvertRect( hilite );
  805.     }
  806.     hptr->high_defer_flag = false;
  807. }
  808. #endif
  809.  
  810. /* ---------------------- Handle_scroll ---------------------------- */
  811. /*
  812.     Called by Help_filter to handle mouseDown events in the scroll bar.
  813. */
  814. static void Handle_scroll( DialogPtr dialog, short the_part, Point where )
  815. {
  816.     register    ControlHandle the_bar;
  817.     
  818.     CKPT( "Handle_scroll"); SetPort( dialog );
  819.     the_bar = ((help_ptr) dialog)->scrollbar;
  820.     if (the_part == inThumb)
  821.     {
  822.         (void) TrackControl( the_bar, where, NIL );
  823.         Adjust_text( dialog );
  824.     }
  825.     else
  826.         (void) TrackControl( the_bar, where, (ProcPtr)Scroll_text );
  827.     
  828. }
  829.  
  830. /* ---------------------- Help_filter ------------------------- */
  831. /*
  832.     This is the dialog event filter for our help window.
  833. */
  834. #define RETURN_KEY    0x24
  835. #define ENTER_KEY    0x4C
  836. #define TILDE_KEY    0x32
  837. #define ESCAPE_KEY    0x35
  838.  
  839.  
  840. static pascal Boolean Help_filter( DialogPtr dialog,
  841.     EventRecord    *event, short *itemHit)
  842. {
  843.     Point    local_point;
  844.     short    the_part;
  845.     ControlHandle    the_control;
  846.     short    keycode, charcode;
  847.     char    the_char;
  848.     register TEHandle    the_text;
  849.     Rect    item_box;
  850.     short    cursor;
  851.     
  852. #if A4_PROJECT
  853.     SetUpA4();
  854. #endif
  855.     the_text = (TEHandle) GetWRefCon( dialog );
  856.     GetMouse( &local_point );
  857.     if (PtInRect( local_point, &(**the_text).viewRect ))
  858.         SetCursor( *ibeam_cursor );
  859.     else
  860.         InitCursor();
  861.     TEIdle( the_text );
  862.     switch (event->what) {
  863.         case nullEvent:
  864.             break;
  865.         case mouseDown:
  866.             CKPT( "Help_filter mousedown");
  867.             local_point = event->where;
  868.             GlobalToLocal( &local_point );
  869.             the_part = FindControl( local_point, dialog, &the_control );
  870.             if (the_part && ((**the_control).contrlMax > 1) )
  871.             {
  872.                 Handle_scroll( dialog, the_part, local_point );    
  873.                 *itemHit = 2;
  874.                 break;
  875.             }
  876.             if (PtInRect( local_point, &(**the_text).viewRect ))
  877.             {
  878.                 if (event->modifiers & shiftKey)
  879.                     TEClick( local_point, true, the_text );
  880.                 else
  881.                     TEClick( local_point, false, the_text );
  882.             }
  883.             break;
  884.         case keyDown :    
  885.         case autoKey :
  886.             keycode = (event->message & keyCodeMask) >> 8;
  887.             charcode = event->message & charCodeMask;
  888.             /*
  889.                 There's no Cancel button, so we treat the OK button
  890.                 the same as a Cancel button.
  891.             */
  892.             if ( (keycode == RETURN_KEY) || (keycode == ENTER_KEY) ||
  893.                 (keycode == TILDE_KEY) || (keycode == ESCAPE_KEY) ||
  894.                 ((charcode == '.') && (event->modifiers & cmdKey)) )
  895.             {
  896.                 *itemHit = c_OK;    /* OK */
  897.                 Flash_button( dialog, *itemHit );
  898. #if A4_PROJECT
  899.                 RestoreA4();
  900. #endif
  901.                 return( TRUE );
  902.             }
  903.             if ( (charcode == 'c') && (event->modifiers & cmdKey) )
  904.             {
  905.                 (void) ZeroScrap();
  906.                 TECopy( the_text );
  907.                 SystemEdit(3);
  908.                 local_point = (**the_text).selPoint;
  909.                 *(long *)&local_point = PinRect( &(**the_text).viewRect,
  910.                     local_point );
  911.                 cursor = TEGetOffset( local_point, the_text );
  912.                 TESetSelect( (long)cursor, (long)cursor, the_text );
  913.                 event->what = nullEvent;
  914.             }
  915.             break;
  916.     } /* end switch */
  917.     
  918.     /* tell the Dialog Manager that the event has NOT been handled and that it should
  919.     ** take further action on this event.
  920.     */
  921. #if A4_PROJECT
  922.     RestoreA4();
  923. #endif
  924.     return false;
  925. }
  926.  
  927.  
  928. /* ---------------------- userItem_proc ------------------------- */
  929. static pascal void  userItem_proc( WindowPtr the_window, short item_num )
  930. {
  931.     Handle        item_h;
  932.     Rect        item_box;
  933.     short        item_type;
  934.     TEHandle    the_text;
  935.     
  936.     asm {
  937.         MOVEM.L    a1-a5/d0-d7, -(SP)
  938.     }
  939.     
  940.     CKPT( "UserItem_proc");
  941. #if A4_PROJECT
  942.     SetUpA4();
  943. #endif
  944.     the_text = (TEHandle) GetWRefCon( the_window );
  945.     item_box = (**the_text).viewRect;
  946. #if USE_PICTS
  947.     ((help_ptr) the_window)->high_defer_flag = true;
  948.     ((help_ptr) the_window)->high_waiting = 0;
  949. #endif
  950.     TEUpdate( &item_box, the_text );
  951.     
  952. #if USE_PICTS
  953.     Draw_picts( the_window, &item_box );
  954.     Do_deferred_hilites( (help_ptr) the_window, &item_box );
  955. #endif
  956.  
  957.     /*
  958.         Get the item's rectangle, and frame it.
  959.     */
  960.     GetDItem( the_window, item_num, &item_type, &item_h, &item_box );
  961.     FrameRect( &item_box );
  962.  
  963. #if A4_PROJECT
  964.     RestoreA4();
  965. #endif
  966.     asm {
  967.         movem.l    (SP)+, a1-a5/d0-d7        ; restore registers
  968.     }
  969. }
  970.  
  971. /* ---------------------- Menu_userItem_proc ------------------------- */
  972. static pascal void  Menu_userItem_proc( WindowPtr the_window, short item_num )
  973. {
  974.     Handle        item_h;
  975.     Rect        item_box;
  976.     short        item_type;
  977.     
  978.     asm {
  979.         MOVEM.L    a1-a5/d0-d7, -(SP)
  980.     }
  981.     
  982.     CKPT( "Menu_UserItem_proc");
  983. #if A4_PROJECT
  984.     SetUpA4();
  985. #endif
  986.     
  987.     /*
  988.         Get the item's rectangle, and frame it.
  989.     */
  990.     GetDItem( the_window, item_num, &item_type, &item_h, &item_box );
  991.     InsetRect(&item_box, -1, -1);
  992.     FrameRect( &item_box );
  993.     
  994.     /* Draw the drop-shadow */
  995.     MoveTo( item_box.left + 3, item_box.bottom );
  996.     LineTo( item_box.right, item_box.bottom );
  997.     LineTo( item_box.right, item_box.top + 3 );        
  998.  
  999. #if A4_PROJECT
  1000.     RestoreA4();
  1001. #endif
  1002.     asm {
  1003.         movem.l    (SP)+, a1-a5/d0-d7        ; restore registers
  1004.     }
  1005. }
  1006.  
  1007. /* ---------------------- Flash_button ----------------------------- */
  1008. void Flash_button( DialogPtr the_dialog, short item_number )
  1009. {
  1010.     ControlHandle    item_h;
  1011.     long    time;
  1012.     short    itype;
  1013.     Rect    box;
  1014.     
  1015.     GetDItem( the_dialog, item_number, &itype, (Handle *)&item_h, &box );
  1016.     HiliteControl( item_h, inButton );
  1017.     Delay( 9L, &time );
  1018.     HiliteControl( item_h, 0 );
  1019. }
  1020.