home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Exec 3 / CD_Magazyn_EXEC_nr_3.iso / Recent / misc / edu / WhirlDisc.lha / WhirlDisc / Source / article.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-10  |  28.0 KB  |  1,081 lines

  1. /*
  2.  
  3. File: article.c
  4. Author: Neil Cafferkey
  5. Copyright (C) 2000 Neil Cafferkey
  6.  
  7. This program is free software; you can redistribute it and/or
  8. modify it under the terms of the GNU General Public License
  9. as published by the Free Software Foundation; either version 2
  10. of the License, or (at your option) any later version.
  11.  
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with this program; if not, write to the Free Software
  19. Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  20. MA 02111-1307, USA.
  21.  
  22. */
  23.  
  24.  
  25. #include "viewer.h"
  26. #include <stdlib.h>
  27. #include <exec/memory.h>
  28. #include <intuition/screens.h>
  29. #include <intuition/imageclass.h>
  30. #include <intuition/gadgetclass.h>
  31.  
  32. #include "article_protos.h"
  33. #include "section_protos.h"
  34. #include "general_protos.h"
  35. #include <proto/intuition.h>
  36. #include <proto/graphics.h>
  37. #include <proto/exec.h>
  38. #include <proto/gadtools.h>
  39.  
  40.  
  41. const TEXT *sysiclass_name="sysiclass";
  42. const TEXT *buttongclass_name="buttongclass";
  43.  
  44.  
  45. static VOID RefreshArticle(Article article);
  46. static VOID DrawLines(Article article,UWORD line_count,WORD top,
  47.    UWORD section_no,UWORD line_no);
  48. static BOOL RethinkArticle(Article article);
  49. static VOID WriteScrollBar(Article article);
  50. static VOID CorrectTextPosition(Article article);
  51. static UWORD LineNoToSectionNo(Article article,UWORD global_line_no);
  52.  
  53.  
  54.  
  55. /* Function: CreateArticle
  56.  * =======================
  57.  * Creates an Article.
  58.  */
  59.  
  60. Article CreateArticle(Sequence raw_article,struct MsgPort *msg_port)
  61. {
  62.    UBYTE *data=raw_article->data,*section_data;
  63.    Article article;
  64.    BOOL success=TRUE;
  65.    ULONG i;
  66.    struct Screen *screen=NULL;
  67.    struct DrawInfo *draw_info;
  68.  
  69.    if((article=(Article)AllocMem(sizeof(Article_imp),MEMF_CLEAR))!=NULL)
  70.    {
  71.  
  72.       article->node.ln_Type=NT_ARTICLE;
  73.  
  74.       article->data=raw_article;
  75.  
  76.       /* Skip uninteresting strings */
  77.  
  78.       data+=0x28;
  79.  
  80.       /* Extract info from article header */
  81.  
  82.       article->section_count=*(data++);
  83.       article->something29_count=*(data++);
  84.       article->reference_count=GetLittleEndianUWord(data);
  85.       data+=2;
  86.       article->something2c_count=GetLittleEndianUWord(data);
  87.       data+=2;
  88.       article->something2e_count=GetLittleEndianUWord(data);
  89.       data+=2;
  90.       article->string_block_length=GetLittleEndianUWord(data);
  91.       data+=2;
  92.       article->body_length=GetLittleEndianUWord(data);
  93.  
  94.       /* Skip more uninteresting strings */
  95.  
  96.       data+=6;
  97.  
  98.       /* Extract more info from article header */
  99.  
  100.       data+=14;
  101.       data+=2;
  102.  
  103.       article->something48_count=*(data++);
  104.  
  105.       data+=9;
  106.       section_data=data;
  107.  
  108.       /* Skip ahead to start of string block */
  109.  
  110.       article->title=data+article->section_count*8+
  111.          article->something29_count*8+article->reference_count*10+
  112.          article->something2c_count*6+
  113.          article->something2e_count*4+
  114.          article->something48_count*10;
  115.  
  116.       data=article->title+article->string_block_length+12;
  117.  
  118. /*      printf("title=%s\n",article->title);
  119.       printf("title offset=0x%lx\n",article->title-raw_article->data);*/
  120.  
  121.       /* Create sections from raw data */
  122.  
  123.       if(article->sections=AllocMem(sizeof(Section)*
  124.          article->section_count,MEMF_CLEAR))
  125.       {
  126.          for(i=0;(i<article->section_count)&&success;i++)
  127.          {
  128.             article->sections[i]=CreateSection(data+GetLittleEndianULong(
  129.                section_data));
  130.             if(article->sections[i]==NULL)
  131.                success=FALSE;
  132.             section_data+=8;
  133.          }
  134.       }
  135.       else
  136.       {
  137.          ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
  138.          success=FALSE;
  139.       }
  140.  
  141.       if(success)
  142.       {
  143.  
  144.          /* Set default window settings */
  145.  
  146.          article->left=20;
  147.          article->top=20;
  148.          article->width=300;
  149.          article->height=200;
  150.  
  151.          /* Open a window for this article */
  152.  
  153.          article->window=OpenWindowTags(NULL,
  154.             WA_Left,article->left,
  155.             WA_Top,article->top,
  156.             WA_Width,article->width,
  157.             WA_Height,article->height,
  158.             WA_Flags,WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|
  159.                WFLG_SIZEGADGET|WFLG_GIMMEZEROZERO|WFLG_ACTIVATE|
  160.                WFLG_NEWLOOKMENUS,
  161.             WA_Title,article->title,
  162.             WA_MinWidth,50,                /*250,*/
  163.             WA_MinHeight,100,
  164.             WA_MaxWidth,-1,
  165.             WA_MaxHeight,-1,
  166.             TAG_END);
  167.  
  168.  
  169.          if(article->window)
  170.          {
  171.  
  172.             /* Set up the window's message port and IDCMP flags */
  173.  
  174.             article->window->UserPort=msg_port;
  175.  
  176.             ModifyIDCMP(article->window,IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|
  177.                IDCMP_GADGETUP|IDCMP_GADGETDOWN|IDCMP_MOUSEBUTTONS|
  178.                IDCMP_RAWKEY);
  179.  
  180.             /* Store article pointer in its window */
  181.  
  182.             (Article)article->window->UserData=article;
  183.  
  184.             /* Set drawing colours */
  185.  
  186.             SetAPen(article->window->RPort,1);
  187.             SetBPen(article->window->RPort,0);
  188.  
  189.             /* Set the screen font as the font to be used for the article
  190.              * text */
  191.  
  192.             screen=article->window->WScreen;
  193.             article->font=OpenFont(screen->Font);
  194.             if(article->font!=NULL)
  195.                SetFont(article->window->RPort,article->font);
  196.  
  197.             /* Create a size gadget image for the sole purpose of finding
  198.              * out how big it is */
  199.  
  200.             draw_info=GetScreenDrawInfo(screen);
  201.  
  202.             if(draw_info!=NULL)
  203.             {
  204.                article->button_resolution=(screen->Flags&SCREENHIRES)?
  205.                   SYSISIZE_MEDRES:SYSISIZE_LOWRES;
  206.  
  207.                article->size_image=NewObject(NULL,sysiclass_name,
  208.                   SYSIA_DrawInfo,draw_info,
  209.                   SYSIA_Which,SIZEIMAGE,
  210.                   SYSIA_Size,article->button_resolution,
  211.                   TAG_END);
  212.  
  213.                /* Create the images for the up and down buttons */
  214.  
  215.                article->up_image=NewObject(NULL,sysiclass_name,
  216.                   SYSIA_DrawInfo,draw_info,
  217.                   SYSIA_Which,UPIMAGE,
  218.                   SYSIA_Size,article->button_resolution,
  219.                   TAG_END);
  220.  
  221.                article->down_image=NewObject(NULL,sysiclass_name,
  222.                   SYSIA_DrawInfo,draw_info,
  223.                   SYSIA_Which,DOWNIMAGE,
  224.                   SYSIA_Size,article->button_resolution,
  225.                   TAG_END);
  226.  
  227.                FreeScreenDrawInfo(screen,draw_info);
  228.             }
  229.  
  230.             /* Allocate memory for scroll bar gadget */
  231.  
  232.             struct Image *knob_image=AllocMem(sizeof(
  233.                struct Image),MEMF_PUBLIC|MEMF_CLEAR);
  234.             struct PropInfo *prop_info=AllocMem(sizeof(
  235.                struct PropInfo),MEMF_PUBLIC|MEMF_CLEAR);
  236.             struct Gadget *scroll_bar=AllocMem(sizeof(
  237.                struct Gadget),MEMF_PUBLIC|MEMF_CLEAR);
  238.  
  239.             article->scroll_bar=scroll_bar;
  240.             article->prop_info=prop_info;
  241.             article->knob_image=knob_image;
  242.  
  243.             if((article->size_image!=NULL)&&(article->up_image!=NULL)&&
  244.                (article->down_image!=NULL)&&
  245.                (scroll_bar!=NULL)&&(prop_info!=NULL)&&(knob_image!=NULL))
  246.             {
  247.  
  248.                /* Initialise scroll bar structures */
  249.  
  250.                scroll_bar->NextGadget=NULL;
  251.                scroll_bar->LeftEdge=-(article->window->BorderRight-5);
  252.                scroll_bar->TopEdge=article->window->BorderTop+1;
  253.                scroll_bar->Width=article->window->BorderRight-8;
  254.                scroll_bar->Height=-(article->window->BorderTop+
  255.                   article->size_image->Height+article->down_image->Height+
  256.                   article->up_image->Height+2);
  257.                if(article->button_resolution==SYSISIZE_LOWRES)
  258.                {
  259.                   scroll_bar->LeftEdge--;
  260.                   scroll_bar->Width+=2;
  261.                }
  262.                scroll_bar->Flags=GFLG_GADGHNONE|GFLG_RELRIGHT|GFLG_RELHEIGHT;
  263.                scroll_bar->Activation=GACT_RIGHTBORDER|GACT_IMMEDIATE|
  264.                   GACT_RELVERIFY;
  265.                scroll_bar->GadgetRender=knob_image;
  266.                scroll_bar->GadgetType=GTYP_PROPGADGET|GTYP_GZZGADGET;
  267.                scroll_bar->SpecialInfo=prop_info;
  268.  
  269.                prop_info->Flags=AUTOKNOB|FREEVERT|PROPNEWLOOK|PROPBORDERLESS;
  270.                prop_info->VertPot=0;
  271.                prop_info->VertBody=MAXBODY;
  272.  
  273.                /* Create the up and down buttons */
  274.  
  275.                article->up_button=NewObject(NULL,buttongclass_name,
  276.                   GA_Previous,article->scroll_bar,
  277.                   GA_Immediate,TRUE,
  278.                   GA_RelRight,-(article->window->BorderRight-1),
  279.                   GA_RelBottom,-(article->size_image->Height+
  280.                      article->down_image->Height+article->up_image->Height-1),
  281.                   GA_Height,article->up_image->Height,
  282.                   GA_RelVerify,TRUE,
  283.                   GA_GZZGadget,TRUE,
  284.                   GA_RightBorder,TRUE,
  285.                   GA_Image,article->up_image,
  286.                   GA_ID,1,
  287.                   TAG_END);
  288.  
  289.                article->down_button=NewObject(NULL,buttongclass_name,
  290.                   GA_Previous,article->up_button,
  291.                   GA_Immediate,TRUE,
  292.                   GA_RelRight,-(article->window->BorderRight-1),
  293.                   GA_RelBottom,-(article->size_image->Height+
  294.                      article->down_image->Height-1),
  295.                   GA_Height,article->down_image->Height,
  296.                   GA_RelVerify,TRUE,
  297.                   GA_GZZGadget,TRUE,
  298.                   GA_RightBorder,TRUE,
  299.                   GA_Image,article->down_image,
  300.                   GA_ID,2,
  301.                   TAG_END);
  302.  
  303.                /* Add scroll gadgets to window and make them visible */
  304.  
  305.                if(article->up_button&&article->down_button)
  306.                {
  307.                   AddGList(article->window,article->scroll_bar,-1,3,NULL);
  308.                   RefreshGList(article->scroll_bar,article->window,NULL,3);
  309.                }
  310.                else
  311.                {
  312.                   ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
  313.                   success=FALSE;
  314.                }
  315.  
  316.                /* Initialise article display */
  317.  
  318.                if(RethinkArticle(article))
  319.                {
  320.                   RefreshArticle(article);
  321.                   WriteScrollBar(article);
  322.                }
  323.                else
  324.                {
  325.                   success=FALSE;
  326.                }
  327.             }
  328.             else
  329.             {
  330.                ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
  331.                success=FALSE;
  332.             }
  333.          }
  334.          else
  335.          {
  336.             ReportError(NULL,ERROR_REPORT_WIN,NULL,0);
  337.             success=FALSE;
  338.          }
  339.       }
  340.  
  341.       if(!success)
  342.       {
  343.          KillArticle(article);
  344.          article=NULL;
  345.       }
  346.    }
  347.    else
  348.    {
  349.       ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
  350.    }
  351.  
  352.    return article;
  353. }
  354.  
  355.  
  356.  
  357. /* Function: UpdateArticleDimensions
  358.  * =================================
  359.  * Adjust article's window contents to a new size.
  360.  */
  361. /*
  362. VOID UpdateArticleDimensions(Article article)
  363. {
  364.    RethinkArticle(article);
  365.    RefreshArticle(article);
  366.  
  367.    return;
  368. }
  369. */
  370.  
  371.  
  372. /* Function: RethinkArticle
  373.  * ========================
  374.  * Adjust article's window contents to a new size.
  375.  */
  376.  
  377. static BOOL RethinkArticle(Article article)
  378. {
  379.    BOOL success=TRUE;
  380.    Paragraph paragraph;
  381.    Section section;
  382.    UWORD line_count,i,j,char_count,current_char,*q,char_base,
  383.       font_height,window_height,global_line_no;
  384.    UBYTE *p;
  385.  
  386.    SetWindowPointer(article->window,WA_BusyPointer,TRUE,
  387.       WA_PointerDelay,TRUE,TAG_END);
  388.  
  389.    article->line_count=0;
  390.  
  391.    for(i=0;i<article->section_count;i++)
  392.    {
  393.  
  394.       section=article->sections[i];
  395.  
  396.       section->start_line_no=article->line_count;
  397.  
  398.       /* Free old line point memory */
  399.  
  400.       if(section->line_count!=0)
  401.          FreeMem(section->line_points,section->line_count*2*sizeof(UWORD));
  402.  
  403.  
  404.       /* Count lines in this section */
  405.  
  406.       line_count=0;
  407.  
  408.       for(j=0;j<section->paragraph_count;j++)
  409.       {
  410.  
  411.          paragraph=section->paragraphs[j];
  412.  
  413.          /* Count lines in this paragraph */
  414.  
  415.          char_count=0;
  416.  
  417.          for(current_char=0;(char_count=FitParagraphLine(paragraph,
  418.             article->window,current_char))!=0;line_count++)
  419.          {
  420.  
  421.             /* Move on to first word of next line */
  422.  
  423.             current_char+=char_count;
  424.             for(p=paragraph->text+current_char;*(p++)==' ';current_char++);
  425.  
  426.          }
  427.  
  428.          /* Put a blank line between each paragraph */
  429.  
  430.          line_count++;
  431.       }
  432.       section->line_count=line_count;
  433.       article->line_count+=line_count;
  434.  
  435.       /* Allocate new line point memory */
  436.  
  437.       section->line_points=AllocMem(section->line_count*2
  438.          *sizeof(UWORD),MEMF_ANY);
  439.  
  440.       if(section->line_points)
  441.       {
  442.  
  443.          char_base=0;
  444.          q=section->line_points;
  445.          for(j=0;j<section->paragraph_count;j++)
  446.          {
  447.  
  448.             paragraph=section->paragraphs[j];
  449.  
  450.             /* Get line start points and widths */
  451.  
  452.             current_char=0;
  453.  
  454.             for(line_count=0;(char_count=FitParagraphLine(paragraph,
  455.                article->window,current_char))!=0;line_count++)
  456.             {
  457.  
  458.                *(q++)=char_base+current_char;
  459.                *(q++)=char_count;
  460.  
  461.                /* Move on to first word of next line */
  462.  
  463.                current_char+=char_count;
  464.                for(p=paragraph->text+current_char;*(p++)==' ';
  465.                   current_char++);
  466.  
  467.             }
  468.  
  469.             char_base+=paragraph->length;
  470.  
  471.             /* Set position and length of blank line between paragraphs */
  472.  
  473.             *(q++)=0;
  474.             *(q++)=0;
  475.          }
  476.       }
  477.       else
  478.       {
  479.          success=FALSE;
  480.       }
  481.    }
  482.  
  483.    if(success)
  484.    {
  485.  
  486.       /* Recalculate number of complete lines that fit in window */
  487.  
  488.       font_height=article->window->RPort->TxHeight;
  489.       window_height=article->window->GZZHeight;
  490.  
  491.       article->window_line_count=window_height/font_height;
  492.  
  493.       /* Calculate the latest line and pixel that should be displayed at the
  494.        * top of the window. */
  495.  
  496.       if(article->line_count-1>article->window_line_count)
  497.       {
  498.          global_line_no=article->line_count-1-article->window_line_count;
  499.          article->max_pixel_no=font_height-window_height%font_height;
  500.       }
  501.       else
  502.       {
  503.          global_line_no=0;
  504.          article->max_pixel_no=0;
  505.       }
  506.       article->max_section_no=LineNoToSectionNo(article,global_line_no);
  507.       article->max_line_no=global_line_no-article->sections[article->
  508.          max_section_no]->start_line_no;
  509.  
  510.  
  511.  
  512.       article->current_line_no=0;
  513.       article->current_pixel_no=0;
  514.  
  515.       CorrectTextPosition(article);
  516.  
  517.       article->new_size=TRUE;
  518.  
  519.    }
  520.    else
  521.    {
  522.       ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
  523.    }
  524.  
  525.    SetWindowPointer(article->window,TAG_END);
  526.  
  527.    return success;
  528. }
  529.  
  530.  
  531.  
  532. /* Function: RefreshArticle
  533.  * ========================
  534.  * Redraws an article's display.
  535.  */
  536.  
  537. static VOID RefreshArticle(Article article)
  538. {
  539.    struct Window *window=article->window;
  540.    Section section;
  541.    UWORD window_height,font_height,window_width,draw_line_count,
  542.       draw_line_no,draw_section_no;
  543.    ULONG old_pixel_position,new_pixel_position;
  544.    LONG scroll_amount;
  545.  
  546.    /* Calculate useful numbers */
  547.  
  548.    font_height=article->window->RPort->TxHeight;
  549.    window_width=article->window->GZZWidth;
  550.    window_height=article->window->GZZHeight;
  551.  
  552.    old_pixel_position=article->old_pixel_position;
  553.  
  554.    article->old_pixel_position=new_pixel_position=(article->sections[
  555.       article->current_section_no]->start_line_no+
  556.       article->current_line_no)*font_height+article->current_pixel_no;
  557.  
  558.    scroll_amount=(LONG)new_pixel_position-old_pixel_position;
  559.  
  560.    /* Check if some visible text will be kept */
  561.  
  562.    if(!article->new_size&&(abs(scroll_amount)<window_height))
  563.    {
  564.  
  565.       /* Scroll text that should remain visible */
  566.  
  567.       ScrollRaster(article->window->RPort,0,scroll_amount,0,0,window_width,
  568.          window_height);
  569.  
  570.       /* Draw newly visible text */
  571.  
  572.       if(scroll_amount>0)
  573.       {
  574.  
  575.          /* Draw new lines at bottom of window */
  576.  
  577.          draw_line_count=(scroll_amount-(LONG)(font_height
  578.             +article->current_pixel_no-1))/(LONG)font_height+2;
  579.  
  580.          draw_line_no=(new_pixel_position+window_height-scroll_amount)
  581.             /font_height;
  582.  
  583.          draw_section_no=LineNoToSectionNo(article,draw_line_no);
  584.          section=article->sections[draw_section_no];
  585.  
  586.          DrawLines(article,(window_height-draw_line_no*font_height
  587.             +new_pixel_position-1)/font_height+1,draw_line_no*font_height
  588.             -new_pixel_position,draw_section_no,
  589.             draw_line_no-section->start_line_no);
  590.  
  591.       }
  592.       else if(scroll_amount<0)
  593.       {
  594.  
  595.          /* Draw new lines at top of window */
  596.  
  597.          draw_line_count=(-scroll_amount-(LONG)(font_height
  598.             -article->current_pixel_no+1))/(LONG)font_height+2;
  599.  
  600. /*
  601. printf("scroll_amount=%ld\n",scroll_amount);
  602. printf("draw_line_count=%u\n",draw_line_count);
  603. */
  604.  
  605.          DrawLines(article,draw_line_count,-article->current_pixel_no,
  606.             article->current_section_no,article->current_line_no);
  607.  
  608.       }
  609.    }
  610.    else
  611.    {
  612.       article->new_size=FALSE;
  613.  
  614.       /* Clear window */
  615.  
  616.       SetRast(window->RPort,0);
  617.  
  618.       /* Fill entire window with text */
  619.  
  620.       DrawLines(article,(window_height-1)/font_height+1,
  621.          -article->current_pixel_no,article->current_section_no,
  622.          article->current_line_no);
  623.  
  624.    }
  625.  
  626.    return;
  627. }
  628.  
  629.  
  630.  
  631. /* Function: DrawLines
  632.  * ===================
  633.  * Draw a number of article text lines.
  634.  */
  635.  
  636. static VOID DrawLines(Article article,UWORD line_count,WORD top,
  637.    UWORD section_no,UWORD line_no)
  638. {
  639.    UWORD text_left,i,j,k,*p;
  640.    WORD baseline;
  641.    Section section;
  642.    struct RastPort *rast_port=article->window->RPort;
  643.  
  644.    /* Calculate starting point for text drawing */
  645.  
  646.    text_left=MARGIN_WIDTH;
  647.    baseline=top+rast_port->TxBaseline;
  648.  
  649.    k=line_count;
  650.  
  651.    for(i=section_no;(i<article->section_count)&&(k>0);i++)
  652.    {
  653.  
  654.       section=article->sections[i];
  655.       p=section->line_points+(line_no<<1);
  656.  
  657.       for(j=line_no;(j<section->line_count)&&(k>0);j++,k--)
  658.       {
  659.  
  660.          /* Move pen position to beginning of line */
  661.  
  662.          Move(rast_port,text_left,baseline);
  663.  
  664.          /* Draw line of text */
  665.  
  666.          Text(rast_port,section->text+*p,*(p+1));
  667.          p+=2;
  668.  
  669.          /* Move baseline down a line */
  670.  
  671.          baseline+=rast_port->TxHeight;
  672.       }
  673.  
  674.       /* Reset line number for next section */
  675.  
  676.       line_no=0;
  677.    }
  678.  
  679.    return;
  680. }
  681.  
  682.  
  683.  
  684. /* Function: HandleArticleInput
  685.  * ============================
  686.  */
  687.  
  688. BOOL HandleArticleInput(Article article,struct IntuiMessage *msg)
  689. {
  690.    BOOL close=FALSE;
  691.    struct IntuiMessage *mod_msg;
  692.  
  693.    mod_msg=GT_FilterIMsg(msg);
  694.    if(mod_msg!=NULL)
  695.    {
  696.  
  697.       switch(msg->Class)
  698.       {
  699.  
  700.       case IDCMP_NEWSIZE:
  701.          if(RethinkArticle(article))
  702.          {
  703.             RefreshArticle(article);
  704.             WriteScrollBar(article);
  705.             break;
  706.          }
  707.  
  708.       case IDCMP_CLOSEWINDOW:
  709.          close=TRUE;
  710.          break;
  711.  
  712.       case IDCMP_MOUSEBUTTONS:
  713.       case IDCMP_GADGETUP:
  714.          ModifyIDCMP(article->window,article->window->IDCMPFlags&
  715.             ~IDCMP_INTUITICKS);
  716.          break;
  717.  
  718.       case IDCMP_GADGETDOWN:
  719.          ModifyIDCMP(article->window,article->window->IDCMPFlags|
  720.             IDCMP_INTUITICKS);
  721.          article->current_gadget=(struct Gadget *)msg->IAddress;
  722.          article->tick_count=0;
  723.  
  724.       case IDCMP_INTUITICKS:
  725.          article->tick_count++;
  726.          if(!(article->current_gadget->Flags&GFLG_SELECTED)&&
  727.             (article->tick_count>2)&&(article->current_gadget->GadgetID!=0))
  728.          {
  729.             ModifyIDCMP(article->window,article->window->IDCMPFlags&
  730.                ~IDCMP_INTUITICKS);
  731.          }
  732.          else
  733.          {
  734.             switch(article->current_gadget->GadgetID)
  735.             {
  736.             case 0:
  737.                ReadScrollBar(article);
  738.                RefreshArticle(article);
  739.                break;
  740.             case 1:
  741.                if(article->current_section_no||article->current_line_no)
  742.                {
  743.                   if(article->current_line_no)
  744.                      article->current_line_no--;
  745.                   else
  746.                   {
  747.                      article->current_section_no--;
  748.                      article->current_line_no=article->sections[
  749.                         article->current_section_no]->line_count-1;
  750.                   }
  751.                }
  752.                else
  753.                   article->current_pixel_no=0;
  754.                RefreshArticle(article);
  755.                WriteScrollBar(article);
  756.                break;
  757.             case 2:
  758.                article->current_line_no++;
  759.                if(article->current_line_no==article->sections[article->
  760.                   current_section_no]->line_count)
  761.                {
  762.                   article->current_section_no++;
  763.                   article->current_line_no=0;
  764.                }
  765.                CorrectTextPosition(article);
  766.  
  767.                RefreshArticle(article);
  768.                WriteScrollBar(article);
  769.             }
  770.          }
  771.          break;
  772.  
  773.       case IDCMP_RAWKEY:
  774.  
  775.          if(!(msg->Code&IECODE_UP_PREFIX))
  776.          {
  777.             switch(msg->Code)
  778.             {
  779.             case CURSORUP:
  780.                if(article->current_section_no||article->current_line_no)
  781.                {
  782.                   if(article->current_line_no)
  783.                      article->current_line_no--;
  784.                   else
  785.                   {
  786.                      article->current_section_no--;
  787.                      article->current_line_no=article->sections[
  788.                         article->current_section_no]->line_count-1;
  789.                   }
  790.                }
  791.                else
  792.                   article->current_pixel_no=0;
  793.                RefreshArticle(article);
  794.                WriteScrollBar(article);
  795.                break;
  796.  
  797.             case CURSORDOWN:
  798.                article->current_line_no++;
  799.                if(article->current_line_no==article->sections[article->
  800.                   current_section_no]->line_count)
  801.                {
  802.                   article->current_section_no++;
  803.                   article->current_line_no=0;
  804.                }
  805.                CorrectTextPosition(article);
  806.  
  807.                RefreshArticle(article);
  808.                WriteScrollBar(article);
  809.  
  810.             default:
  811.             }
  812.          }
  813.  
  814.       default:
  815.  
  816.       }
  817.  
  818.       GT_PostFilterIMsg(mod_msg);
  819.    }
  820.  
  821.    ReplyMsg((struct Message *)msg);
  822.  
  823.    if(close)
  824.    {
  825.       Remove(&article->node);
  826.       KillArticle(article);
  827.    }
  828.  
  829.    return;
  830. }
  831.  
  832.  
  833.  
  834. /* Function: ReadScrollBar
  835.  * =======================
  836.  * Convert the scroll bar position into a position within the article text.
  837.  */
  838.  
  839. VOID ReadScrollBar(Article article)
  840. {
  841.    UWORD global_line_no,font_height,window_height;
  842.    ULONG pixel_no;
  843.  
  844.    /* Get new position in article in terms of pixels */
  845.  
  846.    font_height=article->window->RPort->TxHeight;
  847.    window_height=article->window->GZZHeight;
  848.  
  849.    if(article->line_count*font_height>window_height)
  850.       pixel_no=((ULONG)article->prop_info->VertPot*(article->line_count
  851.          *font_height-window_height))/MAXPOT;
  852.    else
  853.       pixel_no=0;
  854.  
  855.    /* Calculate corresponding overall line number and pixel */
  856.  
  857.    article->current_pixel_no=pixel_no%font_height;
  858.    global_line_no=pixel_no/font_height;
  859.  
  860.    /* Find the right section */
  861.  
  862.    article->current_section_no=LineNoToSectionNo(article,global_line_no);
  863.    article->current_line_no=global_line_no-article->sections[article->
  864.       current_section_no]->start_line_no;
  865.  
  866.  
  867. /*   printf("%u\t%u\t%u\n",article->current_section_no,article->current_line_no,article->current_pixel_no);*/
  868.  
  869. /*   article->current_section_no=0;
  870.    article->current_line_no=0;
  871. */
  872.  
  873.  
  874.    return;
  875. }
  876.  
  877.  
  878.  
  879. /* Function: WriteScrollBar
  880.  * ========================
  881.  * Update the scroll bar to reflect the current article settings.
  882.  */
  883.  
  884. static VOID WriteScrollBar(Article article)
  885. {
  886.    UWORD font_height,window_height,scroll_pot,scroll_body;
  887.  
  888.    font_height=article->window->RPort->TxHeight;
  889.    window_height=article->window->GZZHeight;
  890.  
  891.    /* Update scroll bar settings */
  892.  
  893. /*printf("wsb:%u\t%u\t%u\n",article->current_section_no,article->current_line_no,article->current_pixel_no);*/
  894.  
  895.    if(article->line_count*font_height>window_height)
  896.       scroll_pot=(UWORD)(((ULONG)(
  897.          (article->sections[article->current_section_no]->start_line_no+
  898.          article->current_line_no)*font_height+article->current_pixel_no)
  899.          *MAXPOT)/(article->line_count*font_height-window_height));
  900.    else
  901.       scroll_pot=0;
  902.  
  903.    if(article->line_count*font_height>window_height)
  904.       scroll_body=(UWORD)(((ULONG)window_height*MAXBODY)/
  905.          (article->line_count*font_height));
  906.    else
  907.       scroll_body=MAXBODY;
  908.  
  909.    /* Update scroll bar to reflect new settings */
  910.  
  911.    NewModifyProp(article->scroll_bar,article->window,NULL,
  912.       AUTOKNOB|FREEVERT|PROPNEWLOOK|PROPBORDERLESS,0,scroll_pot,0,
  913.       scroll_body,1);
  914.  
  915. /*
  916. printf("wsb:scroll_pot=%u\tscroll_body=%u\n",scroll_pot,scroll_body);
  917. printf("wsb:VertPot=%u\tVertBody=%u\n",article->prop_info->VertPot,article->prop_info->VertBody);
  918. */
  919.  
  920. /*ReadScrollBar(article);*/
  921.  
  922.  
  923.    return;
  924. }
  925.  
  926.  
  927.  
  928. /* Function: CorrectTextPosition
  929.  * =============================
  930.  * Ensure that the current text position is valid.
  931.  */
  932.  
  933. static VOID CorrectTextPosition(Article article)
  934. {
  935.  
  936.    if((article->current_section_no>article->max_section_no)||
  937.       (article->current_section_no==article->max_section_no)&&
  938.       ((article->current_line_no>article->max_line_no)||
  939.       (article->current_line_no==article->max_line_no)&&
  940.       (article->current_pixel_no>article->max_pixel_no)))
  941.    {
  942.       article->current_section_no=article->max_section_no;
  943.       article->current_line_no=article->max_line_no;
  944.       article->current_pixel_no=article->max_pixel_no;
  945.    }
  946.  
  947.    return;
  948. }
  949.  
  950.  
  951.  
  952. /* Function: LineNoToSectionNo
  953.  * ===========================
  954.  * Locate the section containing the given global line number.
  955.  */
  956.  
  957. static UWORD LineNoToSectionNo(Article article,UWORD global_line_no)
  958. {
  959.    UWORD l,m,h;
  960.    Section *p;
  961.  
  962.    /* Do a binary search to find the right section */
  963.  
  964.    p=article->sections;
  965.    l=0;
  966.    h=article->section_count;
  967.    while(h-l!=1)
  968.    {
  969.       m=l+(h-l)/2;
  970.       if(global_line_no<p[m]->start_line_no)
  971.          h=m;
  972.       else
  973.          l=m;
  974.    }
  975.  
  976.    return l;
  977. }
  978.  
  979.  
  980.  
  981. /* Function: SetArticlePosition
  982.  * ============================
  983.  * Sets the position of an article's window.
  984.  */
  985.  
  986. VOID SetArticlePosition(Article article,UWORD left,UWORD top)
  987. {
  988.    article->left=left;
  989.    article->top=top;
  990.    return;
  991. }
  992.  
  993.  
  994.  
  995. /* Function: SetArticleDimensions
  996.  * ==============================
  997.  * Sets the dimensions of an article's window.
  998.  */
  999.  
  1000. VOID SetArticleDimensions(Article article,UWORD width,UWORD height)
  1001. {
  1002.    article->width=width;
  1003.    article->height=height;
  1004.    return;
  1005. }
  1006.  
  1007.  
  1008. /* Function: KillArticle
  1009.  * =====================
  1010.  * Destroys an article.
  1011.  */
  1012.  
  1013. VOID KillArticle(Article article)
  1014. {
  1015.    UWORD i;
  1016.  
  1017.    if(article->sections!=NULL)
  1018.    {
  1019.       for(i=0;i<article->section_count;i++)
  1020.          if(article->sections[i]!=NULL)
  1021.             KillSection(article->sections[i]);
  1022.       FreeMem(article->sections,article->section_count*sizeof(Section));
  1023.    }
  1024.  
  1025.    if(article->window!=NULL)
  1026.    {
  1027.       Forbid();
  1028.       StripWindowMessages(article->window);
  1029.       article->window->UserPort=NULL;
  1030.       ModifyIDCMP(article->window,0);
  1031.       Permit();
  1032.       CloseWindow(article->window);
  1033.    }
  1034.  
  1035.    if(article->font!=NULL)
  1036.       CloseFont(article->font);
  1037.  
  1038.    /* Free images */
  1039.  
  1040.    if(article->size_image!=NULL)
  1041.       DisposeObject(article->size_image);
  1042.  
  1043.    if(article->up_image!=NULL)
  1044.       DisposeObject(article->up_image);
  1045.  
  1046.    if(article->down_image!=NULL)
  1047.       DisposeObject(article->down_image);
  1048.  
  1049.    /* Free memory for scroll bar structures */
  1050.  
  1051.    if(article->knob_image!=NULL)
  1052.       FreeMem(article->knob_image,sizeof(struct Image));
  1053.  
  1054.    if(article->prop_info!=NULL)
  1055.       FreeMem(article->prop_info,sizeof(struct PropInfo));
  1056.  
  1057.    if(article->scroll_bar!=NULL)
  1058.       FreeMem(article->scroll_bar,sizeof(struct Gadget));
  1059.  
  1060.    /* Free up and down gadgets */
  1061.  
  1062.    if(article->up_button!=NULL)
  1063.       DisposeObject(article->up_button);
  1064.  
  1065.    if(article->down_button!=NULL)
  1066.       DisposeObject(article->down_button);
  1067.  
  1068.    /* Free raw article data */
  1069.  
  1070.    FreeMem(article->data->data,article->data->length);
  1071.    KillSequence(article->data);
  1072.  
  1073.    /* Free article structure */
  1074.  
  1075.    FreeMem(article,sizeof(Article_imp));
  1076.  
  1077.    return;
  1078. }
  1079.  
  1080.  
  1081.