/*

File: article.c
Author: Neil Cafferkey
Copyright (C) 2000 Neil Cafferkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.

*/


#include "viewer.h"
#include <stdlib.h>
#include <exec/memory.h>
#include <intuition/screens.h>
#include <intuition/imageclass.h>
#include <intuition/gadgetclass.h>

#include "article_protos.h"
#include "section_protos.h"
#include "general_protos.h"
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/gadtools.h>


const TEXT *sysiclass_name="sysiclass";
const TEXT *buttongclass_name="buttongclass";


static VOID RefreshArticle(Article article);
static VOID DrawLines(Article article,UWORD line_count,WORD top,
   UWORD section_no,UWORD line_no);
static BOOL RethinkArticle(Article article);
static VOID WriteScrollBar(Article article);
static VOID CorrectTextPosition(Article article);
static UWORD LineNoToSectionNo(Article article,UWORD global_line_no);



/* Function: CreateArticle
 * =======================
 * Creates an Article.
 */

Article CreateArticle(Sequence raw_article,struct MsgPort *msg_port)
{
   UBYTE *data=raw_article->data,*section_data;
   Article article;
   BOOL success=TRUE;
   ULONG i;
   struct Screen *screen=NULL;
   struct DrawInfo *draw_info;

   if((article=(Article)AllocMem(sizeof(Article_imp),MEMF_CLEAR))!=NULL)
   {

      article->node.ln_Type=NT_ARTICLE;

      article->data=raw_article;

      /* Skip uninteresting strings */

      data+=0x28;

      /* Extract info from article header */

      article->section_count=*(data++);
      article->something29_count=*(data++);
      article->reference_count=GetLittleEndianUWord(data);
      data+=2;
      article->something2c_count=GetLittleEndianUWord(data);
      data+=2;
      article->something2e_count=GetLittleEndianUWord(data);
      data+=2;
      article->string_block_length=GetLittleEndianUWord(data);
      data+=2;
      article->body_length=GetLittleEndianUWord(data);

      /* Skip more uninteresting strings */

      data+=6;

      /* Extract more info from article header */

      data+=14;
      data+=2;

      article->something48_count=*(data++);

      data+=9;
      section_data=data;

      /* Skip ahead to start of string block */

      article->title=data+article->section_count*8+
         article->something29_count*8+article->reference_count*10+
         article->something2c_count*6+
         article->something2e_count*4+
         article->something48_count*10;

      data=article->title+article->string_block_length+12;

/*      printf("title=%s\n",article->title);
      printf("title offset=0x%lx\n",article->title-raw_article->data);*/

      /* Create sections from raw data */

      if(article->sections=AllocMem(sizeof(Section)*
         article->section_count,MEMF_CLEAR))
      {
         for(i=0;(i<article->section_count)&&success;i++)
         {
            article->sections[i]=CreateSection(data+GetLittleEndianULong(
               section_data));
            if(article->sections[i]==NULL)
               success=FALSE;
            section_data+=8;
         }
      }
      else
      {
         ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
         success=FALSE;
      }

      if(success)
      {

         /* Set default window settings */

         article->left=20;
         article->top=20;
         article->width=300;
         article->height=200;

         /* Open a window for this article */

         article->window=OpenWindowTags(NULL,
            WA_Left,article->left,
            WA_Top,article->top,
            WA_Width,article->width,
            WA_Height,article->height,
            WA_Flags,WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|
               WFLG_SIZEGADGET|WFLG_GIMMEZEROZERO|WFLG_ACTIVATE|
               WFLG_NEWLOOKMENUS,
            WA_Title,article->title,
            WA_MinWidth,50,                /*250,*/
            WA_MinHeight,100,
            WA_MaxWidth,-1,
            WA_MaxHeight,-1,
            TAG_END);


         if(article->window)
         {

            /* Set up the window's message port and IDCMP flags */

            article->window->UserPort=msg_port;

            ModifyIDCMP(article->window,IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|
               IDCMP_GADGETUP|IDCMP_GADGETDOWN|IDCMP_MOUSEBUTTONS|
               IDCMP_RAWKEY);

            /* Store article pointer in its window */

            (Article)article->window->UserData=article;

            /* Set drawing colours */

            SetAPen(article->window->RPort,1);
            SetBPen(article->window->RPort,0);

            /* Set the screen font as the font to be used for the article
             * text */

            screen=article->window->WScreen;
            article->font=OpenFont(screen->Font);
            if(article->font!=NULL)
               SetFont(article->window->RPort,article->font);

            /* Create a size gadget image for the sole purpose of finding
             * out how big it is */

            draw_info=GetScreenDrawInfo(screen);

            if(draw_info!=NULL)
            {
               article->button_resolution=(screen->Flags&SCREENHIRES)?
                  SYSISIZE_MEDRES:SYSISIZE_LOWRES;

               article->size_image=NewObject(NULL,sysiclass_name,
                  SYSIA_DrawInfo,draw_info,
                  SYSIA_Which,SIZEIMAGE,
                  SYSIA_Size,article->button_resolution,
                  TAG_END);

               /* Create the images for the up and down buttons */

               article->up_image=NewObject(NULL,sysiclass_name,
                  SYSIA_DrawInfo,draw_info,
                  SYSIA_Which,UPIMAGE,
                  SYSIA_Size,article->button_resolution,
                  TAG_END);

               article->down_image=NewObject(NULL,sysiclass_name,
                  SYSIA_DrawInfo,draw_info,
                  SYSIA_Which,DOWNIMAGE,
                  SYSIA_Size,article->button_resolution,
                  TAG_END);

               FreeScreenDrawInfo(screen,draw_info);
            }

            /* Allocate memory for scroll bar gadget */

            struct Image *knob_image=AllocMem(sizeof(
               struct Image),MEMF_PUBLIC|MEMF_CLEAR);
            struct PropInfo *prop_info=AllocMem(sizeof(
               struct PropInfo),MEMF_PUBLIC|MEMF_CLEAR);
            struct Gadget *scroll_bar=AllocMem(sizeof(
               struct Gadget),MEMF_PUBLIC|MEMF_CLEAR);

            article->scroll_bar=scroll_bar;
            article->prop_info=prop_info;
            article->knob_image=knob_image;

            if((article->size_image!=NULL)&&(article->up_image!=NULL)&&
               (article->down_image!=NULL)&&
               (scroll_bar!=NULL)&&(prop_info!=NULL)&&(knob_image!=NULL))
            {

               /* Initialise scroll bar structures */

               scroll_bar->NextGadget=NULL;
               scroll_bar->LeftEdge=-(article->window->BorderRight-5);
               scroll_bar->TopEdge=article->window->BorderTop+1;
               scroll_bar->Width=article->window->BorderRight-8;
               scroll_bar->Height=-(article->window->BorderTop+
                  article->size_image->Height+article->down_image->Height+
                  article->up_image->Height+2);
               if(article->button_resolution==SYSISIZE_LOWRES)
               {
                  scroll_bar->LeftEdge--;
                  scroll_bar->Width+=2;
               }
               scroll_bar->Flags=GFLG_GADGHNONE|GFLG_RELRIGHT|GFLG_RELHEIGHT;
               scroll_bar->Activation=GACT_RIGHTBORDER|GACT_IMMEDIATE|
                  GACT_RELVERIFY;
               scroll_bar->GadgetRender=knob_image;
               scroll_bar->GadgetType=GTYP_PROPGADGET|GTYP_GZZGADGET;
               scroll_bar->SpecialInfo=prop_info;

               prop_info->Flags=AUTOKNOB|FREEVERT|PROPNEWLOOK|PROPBORDERLESS;
               prop_info->VertPot=0;
               prop_info->VertBody=MAXBODY;

               /* Create the up and down buttons */

               article->up_button=NewObject(NULL,buttongclass_name,
                  GA_Previous,article->scroll_bar,
                  GA_Immediate,TRUE,
                  GA_RelRight,-(article->window->BorderRight-1),
                  GA_RelBottom,-(article->size_image->Height+
                     article->down_image->Height+article->up_image->Height-1),
                  GA_Height,article->up_image->Height,
                  GA_RelVerify,TRUE,
                  GA_GZZGadget,TRUE,
                  GA_RightBorder,TRUE,
                  GA_Image,article->up_image,
                  GA_ID,1,
                  TAG_END);

               article->down_button=NewObject(NULL,buttongclass_name,
                  GA_Previous,article->up_button,
                  GA_Immediate,TRUE,
                  GA_RelRight,-(article->window->BorderRight-1),
                  GA_RelBottom,-(article->size_image->Height+
                     article->down_image->Height-1),
                  GA_Height,article->down_image->Height,
                  GA_RelVerify,TRUE,
                  GA_GZZGadget,TRUE,
                  GA_RightBorder,TRUE,
                  GA_Image,article->down_image,
                  GA_ID,2,
                  TAG_END);

               /* Add scroll gadgets to window and make them visible */

               if(article->up_button&&article->down_button)
               {
                  AddGList(article->window,article->scroll_bar,-1,3,NULL);
                  RefreshGList(article->scroll_bar,article->window,NULL,3);
               }
               else
               {
                  ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
                  success=FALSE;
               }

               /* Initialise article display */

               if(RethinkArticle(article))
               {
                  RefreshArticle(article);
                  WriteScrollBar(article);
               }
               else
               {
                  success=FALSE;
               }
            }
            else
            {
               ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
               success=FALSE;
            }
         }
         else
         {
            ReportError(NULL,ERROR_REPORT_WIN,NULL,0);
            success=FALSE;
         }
      }

      if(!success)
      {
         KillArticle(article);
         article=NULL;
      }
   }
   else
   {
      ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
   }

   return article;
}



/* Function: UpdateArticleDimensions
 * =================================
 * Adjust article's window contents to a new size.
 */
/*
VOID UpdateArticleDimensions(Article article)
{
   RethinkArticle(article);
   RefreshArticle(article);

   return;
}
*/


/* Function: RethinkArticle
 * ========================
 * Adjust article's window contents to a new size.
 */

static BOOL RethinkArticle(Article article)
{
   BOOL success=TRUE;
   Paragraph paragraph;
   Section section;
   UWORD line_count,i,j,char_count,current_char,*q,char_base,
      font_height,window_height,global_line_no;
   UBYTE *p;

   SetWindowPointer(article->window,WA_BusyPointer,TRUE,
      WA_PointerDelay,TRUE,TAG_END);

   article->line_count=0;

   for(i=0;i<article->section_count;i++)
   {

      section=article->sections[i];

      section->start_line_no=article->line_count;

      /* Free old line point memory */

      if(section->line_count!=0)
         FreeMem(section->line_points,section->line_count*2*sizeof(UWORD));


      /* Count lines in this section */

      line_count=0;

      for(j=0;j<section->paragraph_count;j++)
      {

         paragraph=section->paragraphs[j];

         /* Count lines in this paragraph */

         char_count=0;

         for(current_char=0;(char_count=FitParagraphLine(paragraph,
            article->window,current_char))!=0;line_count++)
         {

            /* Move on to first word of next line */

            current_char+=char_count;
            for(p=paragraph->text+current_char;*(p++)==' ';current_char++);

         }

         /* Put a blank line between each paragraph */

         line_count++;
      }
      section->line_count=line_count;
      article->line_count+=line_count;

      /* Allocate new line point memory */

      section->line_points=AllocMem(section->line_count*2
         *sizeof(UWORD),MEMF_ANY);

      if(section->line_points)
      {

         char_base=0;
         q=section->line_points;
         for(j=0;j<section->paragraph_count;j++)
         {

            paragraph=section->paragraphs[j];

            /* Get line start points and widths */

            current_char=0;

            for(line_count=0;(char_count=FitParagraphLine(paragraph,
               article->window,current_char))!=0;line_count++)
            {

               *(q++)=char_base+current_char;
               *(q++)=char_count;

               /* Move on to first word of next line */

               current_char+=char_count;
               for(p=paragraph->text+current_char;*(p++)==' ';
                  current_char++);

            }

            char_base+=paragraph->length;

            /* Set position and length of blank line between paragraphs */

            *(q++)=0;
            *(q++)=0;
         }
      }
      else
      {
         success=FALSE;
      }
   }

   if(success)
   {

      /* Recalculate number of complete lines that fit in window */

      font_height=article->window->RPort->TxHeight;
      window_height=article->window->GZZHeight;

      article->window_line_count=window_height/font_height;

      /* Calculate the latest line and pixel that should be displayed at the
       * top of the window. */

      if(article->line_count-1>article->window_line_count)
      {
         global_line_no=article->line_count-1-article->window_line_count;
         article->max_pixel_no=font_height-window_height%font_height;
      }
      else
      {
         global_line_no=0;
         article->max_pixel_no=0;
      }
      article->max_section_no=LineNoToSectionNo(article,global_line_no);
      article->max_line_no=global_line_no-article->sections[article->
         max_section_no]->start_line_no;



      article->current_line_no=0;
      article->current_pixel_no=0;

      CorrectTextPosition(article);

      article->new_size=TRUE;

   }
   else
   {
      ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
   }

   SetWindowPointer(article->window,TAG_END);

   return success;
}



/* Function: RefreshArticle
 * ========================
 * Redraws an article's display.
 */

static VOID RefreshArticle(Article article)
{
   struct Window *window=article->window;
   Section section;
   UWORD window_height,font_height,window_width,draw_line_count,
      draw_line_no,draw_section_no;
   ULONG old_pixel_position,new_pixel_position;
   LONG scroll_amount;

   /* Calculate useful numbers */

   font_height=article->window->RPort->TxHeight;
   window_width=article->window->GZZWidth;
   window_height=article->window->GZZHeight;

   old_pixel_position=article->old_pixel_position;

   article->old_pixel_position=new_pixel_position=(article->sections[
      article->current_section_no]->start_line_no+
      article->current_line_no)*font_height+article->current_pixel_no;

   scroll_amount=(LONG)new_pixel_position-old_pixel_position;

   /* Check if some visible text will be kept */

   if(!article->new_size&&(abs(scroll_amount)<window_height))
   {

      /* Scroll text that should remain visible */

      ScrollRaster(article->window->RPort,0,scroll_amount,0,0,window_width,
         window_height);

      /* Draw newly visible text */

      if(scroll_amount>0)
      {

         /* Draw new lines at bottom of window */

         draw_line_count=(scroll_amount-(LONG)(font_height
            +article->current_pixel_no-1))/(LONG)font_height+2;

         draw_line_no=(new_pixel_position+window_height-scroll_amount)
            /font_height;

         draw_section_no=LineNoToSectionNo(article,draw_line_no);
         section=article->sections[draw_section_no];

         DrawLines(article,(window_height-draw_line_no*font_height
            +new_pixel_position-1)/font_height+1,draw_line_no*font_height
            -new_pixel_position,draw_section_no,
            draw_line_no-section->start_line_no);

      }
      else if(scroll_amount<0)
      {

         /* Draw new lines at top of window */

         draw_line_count=(-scroll_amount-(LONG)(font_height
            -article->current_pixel_no+1))/(LONG)font_height+2;

/*
printf("scroll_amount=%ld\n",scroll_amount);
printf("draw_line_count=%u\n",draw_line_count);
*/

         DrawLines(article,draw_line_count,-article->current_pixel_no,
            article->current_section_no,article->current_line_no);

      }
   }
   else
   {
      article->new_size=FALSE;

      /* Clear window */

      SetRast(window->RPort,0);

      /* Fill entire window with text */

      DrawLines(article,(window_height-1)/font_height+1,
         -article->current_pixel_no,article->current_section_no,
         article->current_line_no);

   }

   return;
}



/* Function: DrawLines
 * ===================
 * Draw a number of article text lines.
 */

static VOID DrawLines(Article article,UWORD line_count,WORD top,
   UWORD section_no,UWORD line_no)
{
   UWORD text_left,i,j,k,*p;
   WORD baseline;
   Section section;
   struct RastPort *rast_port=article->window->RPort;

   /* Calculate starting point for text drawing */

   text_left=MARGIN_WIDTH;
   baseline=top+rast_port->TxBaseline;

   k=line_count;

   for(i=section_no;(i<article->section_count)&&(k>0);i++)
   {

      section=article->sections[i];
      p=section->line_points+(line_no<<1);

      for(j=line_no;(j<section->line_count)&&(k>0);j++,k--)
      {

         /* Move pen position to beginning of line */

         Move(rast_port,text_left,baseline);

         /* Draw line of text */

         Text(rast_port,section->text+*p,*(p+1));
         p+=2;

         /* Move baseline down a line */

         baseline+=rast_port->TxHeight;
      }

      /* Reset line number for next section */

      line_no=0;
   }

   return;
}



/* Function: HandleArticleInput
 * ============================
 */

BOOL HandleArticleInput(Article article,struct IntuiMessage *msg)
{
   BOOL close=FALSE;
   struct IntuiMessage *mod_msg;

   mod_msg=GT_FilterIMsg(msg);
   if(mod_msg!=NULL)
   {

      switch(msg->Class)
      {

      case IDCMP_NEWSIZE:
         if(RethinkArticle(article))
         {
            RefreshArticle(article);
            WriteScrollBar(article);
            break;
         }

      case IDCMP_CLOSEWINDOW:
         close=TRUE;
         break;

      case IDCMP_MOUSEBUTTONS:
      case IDCMP_GADGETUP:
         ModifyIDCMP(article->window,article->window->IDCMPFlags&
            ~IDCMP_INTUITICKS);
         break;

      case IDCMP_GADGETDOWN:
         ModifyIDCMP(article->window,article->window->IDCMPFlags|
            IDCMP_INTUITICKS);
         article->current_gadget=(struct Gadget *)msg->IAddress;
         article->tick_count=0;

      case IDCMP_INTUITICKS:
         article->tick_count++;
         if(!(article->current_gadget->Flags&GFLG_SELECTED)&&
            (article->tick_count>2)&&(article->current_gadget->GadgetID!=0))
         {
            ModifyIDCMP(article->window,article->window->IDCMPFlags&
               ~IDCMP_INTUITICKS);
         }
         else
         {
            switch(article->current_gadget->GadgetID)
            {
            case 0:
               ReadScrollBar(article);
               RefreshArticle(article);
               break;
            case 1:
               if(article->current_section_no||article->current_line_no)
               {
                  if(article->current_line_no)
                     article->current_line_no--;
                  else
                  {
                     article->current_section_no--;
                     article->current_line_no=article->sections[
                        article->current_section_no]->line_count-1;
                  }
               }
               else
                  article->current_pixel_no=0;
               RefreshArticle(article);
               WriteScrollBar(article);
               break;
            case 2:
               article->current_line_no++;
               if(article->current_line_no==article->sections[article->
                  current_section_no]->line_count)
               {
                  article->current_section_no++;
                  article->current_line_no=0;
               }
               CorrectTextPosition(article);

               RefreshArticle(article);
               WriteScrollBar(article);
            }
         }
         break;

      case IDCMP_RAWKEY:

         if(!(msg->Code&IECODE_UP_PREFIX))
         {
            switch(msg->Code)
            {
            case CURSORUP:
               if(article->current_section_no||article->current_line_no)
               {
                  if(article->current_line_no)
                     article->current_line_no--;
                  else
                  {
                     article->current_section_no--;
                     article->current_line_no=article->sections[
                        article->current_section_no]->line_count-1;
                  }
               }
               else
                  article->current_pixel_no=0;
               RefreshArticle(article);
               WriteScrollBar(article);
               break;

            case CURSORDOWN:
               article->current_line_no++;
               if(article->current_line_no==article->sections[article->
                  current_section_no]->line_count)
               {
                  article->current_section_no++;
                  article->current_line_no=0;
               }
               CorrectTextPosition(article);

               RefreshArticle(article);
               WriteScrollBar(article);

            default:
            }
         }

      default:

      }

      GT_PostFilterIMsg(mod_msg);
   }

   ReplyMsg((struct Message *)msg);

   if(close)
   {
      Remove(&article->node);
      KillArticle(article);
   }

   return;
}



/* Function: ReadScrollBar
 * =======================
 * Convert the scroll bar position into a position within the article text.
 */

VOID ReadScrollBar(Article article)
{
   UWORD global_line_no,font_height,window_height;
   ULONG pixel_no;

   /* Get new position in article in terms of pixels */

   font_height=article->window->RPort->TxHeight;
   window_height=article->window->GZZHeight;

   if(article->line_count*font_height>window_height)
      pixel_no=((ULONG)article->prop_info->VertPot*(article->line_count
         *font_height-window_height))/MAXPOT;
   else
      pixel_no=0;

   /* Calculate corresponding overall line number and pixel */

   article->current_pixel_no=pixel_no%font_height;
   global_line_no=pixel_no/font_height;

   /* Find the right section */

   article->current_section_no=LineNoToSectionNo(article,global_line_no);
   article->current_line_no=global_line_no-article->sections[article->
      current_section_no]->start_line_no;


/*   printf("%u\t%u\t%u\n",article->current_section_no,article->current_line_no,article->current_pixel_no);*/

/*   article->current_section_no=0;
   article->current_line_no=0;
*/


   return;
}



/* Function: WriteScrollBar
 * ========================
 * Update the scroll bar to reflect the current article settings.
 */

static VOID WriteScrollBar(Article article)
{
   UWORD font_height,window_height,scroll_pot,scroll_body;

   font_height=article->window->RPort->TxHeight;
   window_height=article->window->GZZHeight;

   /* Update scroll bar settings */

/*printf("wsb:%u\t%u\t%u\n",article->current_section_no,article->current_line_no,article->current_pixel_no);*/

   if(article->line_count*font_height>window_height)
      scroll_pot=(UWORD)(((ULONG)(
         (article->sections[article->current_section_no]->start_line_no+
         article->current_line_no)*font_height+article->current_pixel_no)
         *MAXPOT)/(article->line_count*font_height-window_height));
   else
      scroll_pot=0;

   if(article->line_count*font_height>window_height)
      scroll_body=(UWORD)(((ULONG)window_height*MAXBODY)/
         (article->line_count*font_height));
   else
      scroll_body=MAXBODY;

   /* Update scroll bar to reflect new settings */

   NewModifyProp(article->scroll_bar,article->window,NULL,
      AUTOKNOB|FREEVERT|PROPNEWLOOK|PROPBORDERLESS,0,scroll_pot,0,
      scroll_body,1);

/*
printf("wsb:scroll_pot=%u\tscroll_body=%u\n",scroll_pot,scroll_body);
printf("wsb:VertPot=%u\tVertBody=%u\n",article->prop_info->VertPot,article->prop_info->VertBody);
*/

/*ReadScrollBar(article);*/


   return;
}



/* Function: CorrectTextPosition
 * =============================
 * Ensure that the current text position is valid.
 */

static VOID CorrectTextPosition(Article article)
{

   if((article->current_section_no>article->max_section_no)||
      (article->current_section_no==article->max_section_no)&&
      ((article->current_line_no>article->max_line_no)||
      (article->current_line_no==article->max_line_no)&&
      (article->current_pixel_no>article->max_pixel_no)))
   {
      article->current_section_no=article->max_section_no;
      article->current_line_no=article->max_line_no;
      article->current_pixel_no=article->max_pixel_no;
   }

   return;
}



/* Function: LineNoToSectionNo
 * ===========================
 * Locate the section containing the given global line number.
 */

static UWORD LineNoToSectionNo(Article article,UWORD global_line_no)
{
   UWORD l,m,h;
   Section *p;

   /* Do a binary search to find the right section */

   p=article->sections;
   l=0;
   h=article->section_count;
   while(h-l!=1)
   {
      m=l+(h-l)/2;
      if(global_line_no<p[m]->start_line_no)
         h=m;
      else
         l=m;
   }

   return l;
}



/* Function: SetArticlePosition
 * ============================
 * Sets the position of an article's window.
 */

VOID SetArticlePosition(Article article,UWORD left,UWORD top)
{
   article->left=left;
   article->top=top;
   return;
}



/* Function: SetArticleDimensions
 * ==============================
 * Sets the dimensions of an article's window.
 */

VOID SetArticleDimensions(Article article,UWORD width,UWORD height)
{
   article->width=width;
   article->height=height;
   return;
}


/* Function: KillArticle
 * =====================
 * Destroys an article.
 */

VOID KillArticle(Article article)
{
   UWORD i;

   if(article->sections!=NULL)
   {
      for(i=0;i<article->section_count;i++)
         if(article->sections[i]!=NULL)
            KillSection(article->sections[i]);
      FreeMem(article->sections,article->section_count*sizeof(Section));
   }

   if(article->window!=NULL)
   {
      Forbid();
      StripWindowMessages(article->window);
      article->window->UserPort=NULL;
      ModifyIDCMP(article->window,0);
      Permit();
      CloseWindow(article->window);
   }

   if(article->font!=NULL)
      CloseFont(article->font);

   /* Free images */

   if(article->size_image!=NULL)
      DisposeObject(article->size_image);

   if(article->up_image!=NULL)
      DisposeObject(article->up_image);

   if(article->down_image!=NULL)
      DisposeObject(article->down_image);

   /* Free memory for scroll bar structures */

   if(article->knob_image!=NULL)
      FreeMem(article->knob_image,sizeof(struct Image));

   if(article->prop_info!=NULL)
      FreeMem(article->prop_info,sizeof(struct PropInfo));

   if(article->scroll_bar!=NULL)
      FreeMem(article->scroll_bar,sizeof(struct Gadget));

   /* Free up and down gadgets */

   if(article->up_button!=NULL)
      DisposeObject(article->up_button);

   if(article->down_button!=NULL)
      DisposeObject(article->down_button);

   /* Free raw article data */

   FreeMem(article->data->data,article->data->length);
   KillSequence(article->data);

   /* Free article structure */

   FreeMem(article,sizeof(Article_imp));

   return;
}


