/*
   EventGraph.c : Event graph engine.
   Copyright (C) 1999, 2000, 2001 Karim Yaghmour (karym@opersys.com).
 
   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

   History :
      ROC., 01/01/2001, Optimized drawing and added selective icon suppression
      JAL,  05/12/2000, Added support for cross-platform trace decoding.
                        (andy_lowe@mvista.com)
      K.Y., 19/06/2000, Added RTAI traces support.
      K.Y., 10/05/2000, Scroll functions have been added to respond to
                        the user playing with the Up, Down, Left, Right and
                        Page Up, Page Down keys.
      K.Y., 10/02/2000, EGIDrawEvents() has undergone another major rewrite
                        because of the changes in the way the trace is read.
                        The event structure not holding the same information
                        it had, details are fetched dynamically using the
                        corresponding DBEventXXX() functions.
      K.Y., 20/10/1999, EGIDrawEvents() has gone a major rewrite. Now, it uses
                        IRQ entries and exits and has been corrected for some
                        nuissances and complexities it has. It is still complex
                        though, but there's no way around this if Linux' behavior
                        is to be presented as it really is.
      K.Y., 19/07/1999, Adding detail to the graph after separating EGIDrawTrace()
                        in smaller pieces.
      K.Y., 07/07/1999, Using a pixmap the size of drawable area. Initially
                        drawing was done using a "true" size pixmap containing
			all the trace. This took too much memory.
      K.Y., 28/06/1999, Initial typing.
*/

#include <sys/time.h>
#include <unistd.h>
#include <math.h>             /* This is for fmod */
#include <gdk/gdkkeysyms.h>

#include <EventOptions.h>
#include <Tables.h>
#include <EventDB.h>
#if SUPP_RTAI
#include <RTAIDB.h>
#endif

#include "MainWindow.h"
#include "EventGraph.h"

const gchar*  gProcessItemData = "Process Item Data";

/**********************************************************************************/
/******************************* Internal functions *******************************/
/**********************************************************************************/
/******************************************************************
 * Macro : EGIFindEventDistance()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
#if 1
#define EGIFindEventDistance(EGraph, EventDesc, DrawStart, DrawEnd) \
   (guint) ((((gdouble) EGraph->HDrawSize) * \
                (DBGetTimeInDouble(EventDesc.Time) - DrawStart)) / (DrawEnd - DrawStart))
#else /* This is for debugging only */
guint EGIFindEventDistance(eventGraph* pmEventGraph,
			   eventDescription pmEventDesc, 
			   gdouble pmDrawStart,
			   gdouble pmDrawEnd)
{
  guint lRetValue;
  gdouble lDistance;

  lDistance = (DBGetTimeInDouble(pmEventDesc.Time) - pmDrawStart) * ((gdouble) pmEventGraph->HDrawSize);
  lDistance /= pmDrawEnd - pmDrawStart;

  g_print("lDistance = %lf \n", lDistance);

  lRetValue = (guint) lDistance;

  g_print("lRetValue = %d \n", lRetValue);

  return lRetValue;
}
#endif

/******************************************************************
 * Macro : EGIFindProcessHeight()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
#define EGIFindProcessHeight(EGraph, Process) \
   EGraph->VTrueSize - \
   (EGraph->NbProc - Process->ListPos) * EGraph->LabelHeight + \
   EGraph->LabelHeight / 3

/******************************************************************
 * Macro : EGIFindKernelHeight()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
#define EGIFindKernelHeight(EGraph) \
   EGraph->VTrueSize - \
   (EGraph->NbTask + 1) * EGraph->LabelHeight + \
   EGraph->LabelHeight / 3

/******************************************************************
 * Macro : EGIFindTaskHeight()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
#if SUPP_RTAI
#if 0
#define EGIFindTaskHeight(EGraph, Task) \
   EGraph->VTrueSize - \
   (EGraph->NbProc - Task->ListPos) * EGraph->LabelHeight + \
   EGraph->LabelHeight / 3
#else
inline guint EGIFindTaskHeight(eventGraph* pmEventGraph, RTAItask* pmTask)
{
  if(pmTask != NULL)
    return (pmEventGraph->VTrueSize - \
	    (pmEventGraph->NbProc - pmTask->ListPos) * pmEventGraph->LabelHeight + \
	    pmEventGraph->LabelHeight / 3);
  else
    return EGIFindKernelHeight(pmEventGraph);
}
#endif
#endif /* SUPP_RTAI */

/******************************************************************
 * Macro : EGIFindRTAIHeight()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
#if SUPP_RTAI
#define EGIFindRTAIHeight(EGraph) \
   EGraph->VTrueSize - EGraph->LabelHeight + EGraph->LabelHeight / 3
#endif /* SUPP_RTAI */


/******************************************************************
 * Function : EGIDrawVLine()
 * Description : 
 *    Accept a vertical line draw request. Defer it as long as possible 
 *    (notably when the current X position has finally changed).  This 
 *    prevents needless overdraws in expanded time scales when USecPerPixel
 *    gets big (draw count is reduced by 10x).  Update the deferred request
 *    when the current request is for a longer line.  This preserves 
 *    the visual display of the original version of LTT.
 * Parameters :
 *    pmEGraph, The event graph where to draw line.
 *    pmGC, The graphic context in which to draw the line.
 *    pmX, The X coordinate where to draw.
 *    pmY1, The Y coordinate where to start drawing.
 *    pmY2, The Y coordinate where to stop drawing.
 * Return values :
 *    NONE
 * History :
 *    01/01, ROC Initial typing
 * Note :
 ******************************************************************/
void EGIDrawVLine(eventGraph* pmEGraph, GdkGC* pmGC, guint pmX, guint pmY1, guint pmY2)
{
  deferredLine* pDefLine = &(pmEGraph->VDef);  /* Block invariant */

  /* Should we draw fast vertical lines */
  if(pmEGraph->FastVLines == TRUE)
    {
    /* If X has changed, either draw the deferral or flag an error.  In either
       case, store the current request as a completely new deferral. */
    if(pDefLine->Same != pmX)
      {
      /* Draw and defer */
      if(pDefLine->Same > pmX)	/* idiot check */
	g_print("Visualizer: Current VLine X < deferral point!!!\n");
      /* First time check */
      if(pDefLine->Same != 0)
	{
	/* Draw a line */
	gdk_draw_line(pmEGraph->PixMap, pDefLine->GC,
		      pDefLine->Same, pDefLine->Start,
		      pDefLine->Same, pDefLine->End);
	pmEGraph->NbVLines++;
	}

      /* Keep track of the request details to differ it */
      pDefLine->GC    = pmGC;
      pDefLine->Same  = pmX;
      pDefLine->Start = pmY1;
      pDefLine->End   = pmY2;    
      }
    else
      {
      /* same == x: Is the current request larger than the deferral?
	 Don't update on delta GC cuz that's not what the original
	 LTT did; it just drew the latest request which means
	 the longest, latest line always won.  Yes there might
	 have been some inadvertent two-GC lines but too bad. 
	 The test is <= instead of just < so that the "latest"
	 line is drawn.  This more closely preserves visual
	 accuracy at high zoom out when the last control transfer
	 from a process to kernel has a different color. */
      if(abs(pDefLine->End - pDefLine->Start) <= abs(pmY2 - pmY1))
	{
	pDefLine->Start = pmY1;
	pDefLine->End   = pmY2;
	pDefLine->GC    = pmGC;	/* Might be different */
	}
      }
    }
  else
    {
    /* Slow behavior, draw EVERY request, even overdraws */
    gdk_draw_line(pmEGraph->PixMap, pmGC, pmX, pmY1, pmX, pmY2);
    pmEGraph->NbVLines++;
    }
}

/******************************************************************
 * Function : EGIDrawThickHLine()
 * Description :
 *    Accept a horizontal line draw request.  First, only draw the thick
 *    line at high enough zoom in; it's a 2/3 reduction in gdk_draw_line
 *    calls and does not affect the displayed info noticeably.
 *    As with vertical lines, defer the request as long as possible:
 *    when the current Y position GC (line type) has changed.  Thus the
 *    "last line" drawn in a spot will always be the proper color/style.
 *    Update the deferred request by extending it when X changes but 
 *    Y and GC do not.  This accumulation also saves on gdk calls.
 *    The combined savings can be a factor of 100 drop in gdk_draw_line
 *    calls. Theoretically this may not duplicate the original look 
 *    of LTT at high zoomout but I got over it :-)
 * Parameters :
 *    pmEGraph, The event graph where to draw line.
 *    pmGC, The graphic context in which to draw the line.
 *    pmY, The Y coordinate where to draw.
 *    pmX1, The X coordinate where to start drawing.
 *    pmX2, The X coordinate where to stop drawing.
 * Return values :
 *    NONE
 * History :
 *    01/01, ROC  change from macro to function and enhance
 *    mm/yy  K.Y. Initial typing
 * Note :
 *    This used to be a macro.
 ******************************************************************/
void EGIDrawThickHLine(eventGraph* pmEGraph, GdkGC* pmGC, guint pmY, guint pmX1, guint pmX2)
{
  deferredLine* pDefLine = &(pmEGraph->HDef);  /* Block invariant */

  /* Should we draw fast horizontal lines */
  if(pmEGraph->FastHLines == TRUE)
    {
    /* If Y OR GC has changed, either draw the deferral or flag an error.  
       In either case, store the current request as a new deferral. 
       Note that the start case of DeferY == 0 comes through here
       without a draw but makes the first deferral.  BTW, don't draw
       zero-length lines, either :-) */
    if((pDefLine->Same != pmY) || (pDefLine->GC != pmGC))
      {
      if((pDefLine->Same != 0) && (pDefLine->End > pDefLine->Start))
	{
	gdk_draw_line(pmEGraph->PixMap, pDefLine->GC, 
		      pDefLine->Start, pDefLine->Same,
		      pDefLine->End, pDefLine->Same);
	pmEGraph->NbHLines++;

	/* Thick lines? */
	if(pmEGraph->NbIntervalEvents < 200)
	  {	
	  gdk_draw_line(pmEGraph->PixMap, pDefLine->GC, 
	  		pDefLine->Start, pDefLine->Same + 1,
			pDefLine->End, pDefLine->Same + 1);
	  gdk_draw_line(pmEGraph->PixMap, pDefLine->GC, 
	  		pDefLine->Start, pDefLine->Same + 2,
			pDefLine->End, pDefLine->Same + 2);
	  pmEGraph->NbHLines += 2;
	  }
	}

      /* Keep record of request */
      pDefLine->GC    = pmGC;
      pDefLine->Same  = pmY;
      pDefLine->Start = pmX1;
      pDefLine->End   = pmX2;
      }
    else
      {
      /* Same Y and GC; should I extend the current deferral? */
      if(pDefLine->End > pmX2)
	g_print("Visualizer: Current HLine x2 < deferral point!!!\n");
      else 
	/* Also assigns on ==, I don't care */
	pDefLine->End = pmX2;	
      }
    }
  else
    {  
    /* Slow behavior, draw them now */
    gdk_draw_line(pmEGraph->PixMap, pmGC, pmX1, pmY, pmX2, pmY);
    gdk_draw_line(pmEGraph->PixMap, pmGC, pmX1, pmY + 1, pmX2, pmY + 1);
    gdk_draw_line(pmEGraph->PixMap, pmGC, pmX1, pmY + 2, pmX2, pmY + 2);
    pmEGraph->NbHLines += 3;
    }
}

/******************************************************************
 * Function : EGICreateColor()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
GdkColor* EGICreateColor(long pmRed, long pmGreen, long pmBlue)
{
  GdkColor*   pColor;     /* The color */

  /* Allocate space for color */
  pColor = (GdkColor*) g_malloc(sizeof(GdkColor));

  /* Set the color's attributes */
  pColor->red   = pmRed;
  pColor->green = pmGreen;
  pColor->blue  = pmBlue;

  /* Allocate the color in the colormap */
  gdk_color_alloc(gdk_colormap_get_system(), pColor);

  /* Give the color to the caller */
  return pColor;
}

/******************************************************************
 * Function : EGICreateGC()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
GdkGC* EGICreateGC(GdkPixmap*   pmPixMap,
		   GdkColor*    pmForegroundColor,
		   GdkColor*    pmBackgroundColor,
		   GdkLineStyle pmLineType)
{
  GdkGC*  pGC;   /* The graphic context */

  /* Allocate space for GC */
  pGC = gdk_gc_new(pmPixMap);

  /* Set the forground and background colors */
  gdk_gc_set_foreground(pGC, pmForegroundColor);
  gdk_gc_set_background(pGC, pmBackgroundColor);

  /* Set the Line Type */
  gdk_gc_set_line_attributes(pGC,
			     1,
			     pmLineType,
			     GDK_CAP_NOT_LAST,
			     GDK_JOIN_MITER);


  /* Return the graphic contex to the caller */
  return pGC;
}

/******************************************************************
 * Function : EGIFindDrawLimits()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void  EGIFindDrawLimits(eventGraph* pmEventGraph,
			gfloat   pmValue,     gfloat   pmPageSize,
			gfloat   pmMinValue,  gfloat   pmMaxValue, 
			gdouble* pmDrawStart, gdouble* pmDrawEnd)
{
  /* Are we drawing the last page */
  if(pmValue > (pmMaxValue - pmPageSize))
    {
    /* Set drawing times */
    *pmDrawStart = pmEventGraph->StartTime + pmEventGraph->Duration - pmEventGraph->Interval;
    *pmDrawEnd   = pmEventGraph->StartTime + pmEventGraph->Duration;
    }
  else if(pmValue == pmMinValue)
    {
    /* Set drawing times */
    *pmDrawStart = pmEventGraph->StartTime;
    *pmDrawEnd   = pmEventGraph->StartTime + pmEventGraph->Interval;
    }
  else
    {
    /* Set drawing times */
    *pmDrawStart = pmEventGraph->StartTime +
      (pmEventGraph->Duration *
      (gdouble) ((pmValue - pmMinValue)) / (gdouble) (pmMaxValue - pmMinValue));
    *pmDrawEnd   = *pmDrawStart + pmEventGraph->Interval;
    }
}

/******************************************************************
 * Function : EGIFindLimitEvents()
 * Description :
 *    Find the events at the limits of the draw area.
 * Parameters :
 *    pmEventGraph, The event graph for which the search is carried
 *                  out
 *    pmDrawStart, The time at which we start drawing.
 *    pmDrawEnd, The time at which we end drawing.
 *    pmFirstEvent, The pointer to the first event to be drawn.
 *    pmLastEvent, The pointer to the last event to be drawn.
 * Return values :
 *    TRUE, We found drawing limits.
 *    FALSE, We failed to find draw limits.
 * History :
 *    01/01 ROC Count events in the interval.  
 *    mm/yy K.Y. Initial typing
 * Note :
 *    ROC, Earlier I had needed this calculated before the main event loop.  
 *    That's not true anymore and perhaps it should be moved back there.
 ******************************************************************/
int EGIFindLimitEvents(eventGraph* pmEventGraph,
		       gdouble     pmDrawStart,  gdouble pmDrawEnd,
		       event*      pmFirstEvent, event*  pmLastEvent)
{
  event              lEvent;               /* Generic event pointer */
  event              lEventNext;           /* Next event */
  event              lEventPrev;           /* Previous event */
  event              lFirstEvent = {0, 0}; /* First event drawn */
  event              lLastEvent = {0, 0};  /* Last event drawn */
  struct timeval     lEventTime;           /* An event time */
  struct timeval     lEventNextTime;       /* Next event's time */
  struct timeval     lEventPrevTime;       /* Previous event's time */

  /* Were we dealing with the first event in the event list */
  if(pmDrawStart == pmEventGraph->StartTime)
    lFirstEvent = pmEventGraph->EventDB->FirstEventWithProc;
  else
    {
    /* Are we starting at the same time as the already draw event */
    if(pmDrawStart == pmEventGraph->DrawStartTime)
      /* Start at the same event as last time */
      lFirstEvent = pmEventGraph->DrawStartEvent;
    else
      {
      /* Start at the current draw start event */
      lEventPrev = lEventNext = lEvent = pmEventGraph->DrawStartEvent;

      /* Are we moving forward */
      if(pmDrawStart >= pmEventGraph->DrawStartTime)
	{
	/* Keep going on forward in the trace */
	while(DBEventNext(pmEventGraph->EventDB, &lEventNext) == TRUE)
	  {
	  /* Get the event time */
	  DBEventTime(pmEventGraph->EventDB, &lEvent, &lEventTime);
	  DBEventTime(pmEventGraph->EventDB, &lEventNext, &lEventNextTime);

	  /* Is the next event past the draw start time and the current one before */
	  if((DBGetTimeInDouble(lEventNextTime) >= pmDrawStart)
	   &&(DBGetTimeInDouble(lEventTime) < pmDrawStart))
	    {
	    /* We found the first event */
	    lFirstEvent = lEvent;
	    break;
	    }

	  /* Go to the next event */
	  lEvent = lEventNext;
	  }
	}
      else
	{
	/* Start at the next event since the current event could be the draw start event */
	DBEventNext(pmEventGraph->EventDB, &lEvent);

	/* Keep going on backwards in the trace */
	do
	  {
	  /* Get the event time */
	  DBEventTime(pmEventGraph->EventDB, &lEvent, &lEventTime);
	  DBEventTime(pmEventGraph->EventDB, &lEventPrev, &lEventPrevTime);

	  /* Is the current event past the draw start time and the previous one before */
	  if((DBGetTimeInDouble(lEventTime) >= pmDrawStart)
	   &&(DBGetTimeInDouble(lEventPrevTime) < pmDrawStart))
	    {
	    /* We found the first event */
	    lFirstEvent = lEventPrev;
	    break;
	    }

	  /* Go to the previous event */
	  lEvent = lEventPrev;

	  /* Is this the first event drawable */
	  if(DBEventsEqual(lEvent, pmEventGraph->EventDB->FirstEventWithProc))
	    {
	    lFirstEvent = lEvent;
	    break;
	    }
	  }  while(DBEventPrev(pmEventGraph->EventDB, &lEventPrev) == TRUE);
	} /* End of else */
      } /* End of else */
    } /* End of else */

  /* Just in case of early return... */
  pmEventGraph->NbIntervalEvents = 0;

  /* Did we find anything (Both are initialized as zero, so test if lFirstEvent remained zero) */
  if(DBEventsEqual(lFirstEvent, lLastEvent))
    return FALSE;

  /* Loop around the rest of the events start from the first event  */
  lEventNext = lEvent = lFirstEvent;
  do
    {
    /* Count operation (perhaps off by 2?) */
    pmEventGraph->NbIntervalEvents++;

    /* Get the event time */
    DBEventTime(pmEventGraph->EventDB, &lEvent, &lEventTime);
    DBEventTime(pmEventGraph->EventDB, &lEventNext, &lEventNextTime);

    /* Is the next event past the draw end time and the current one before it */
    if((DBGetTimeInDouble(lEventNextTime) > pmDrawEnd)
    && (DBGetTimeInDouble(lEventTime) <= pmDrawEnd))
      /* We found the last event */
      break;

    /* Go to the next event */
    lEvent = lEventNext;
    } while(DBEventNext(pmEventGraph->EventDB, &lEventNext) == TRUE);

  /* Set the last event drawn */
  lLastEvent = lEventNext;

  /* Are the last events and first events the same */
  if(DBEventsEqual(lFirstEvent, lLastEvent))
    return FALSE;

  /* Update draw start time */
  pmEventGraph->DrawStartTime = pmDrawStart;

  /* Set and update the limit events found */
  *pmFirstEvent = pmEventGraph->DrawStartEvent = lFirstEvent;
  *pmLastEvent  = pmEventGraph->DrawEndEvent   = lLastEvent;

  /* Tell the caller that the search was successfull */
  return TRUE;
}

/******************************************************************
 * Function : EGICommitEventDrawRequest()
 * Description :
 *    Commits a draw event info request. This means that the information
 *    gets drawn to the trace pixmap.
 * Parameters :
 *    pmEventInfo, The event info to be commited.
 *    pmEventGraph, The event graph where to commit the request.
 * Return values :
 *    NONE
 * History :
 *    01/01 ROC, initial typing
 * Note :
 ******************************************************************/
void EGICommitEventDrawRequest(eventInfo* pmEventInfo, eventGraph* pmEventGraph)
{
  /* Set Clipping information */
  gdk_gc_set_clip_mask   (pmEventGraph->BackgroundGC, 
  			  pmEventInfo->EventIcon.Mask);
  gdk_gc_set_clip_origin (pmEventGraph->BackgroundGC,
			  pmEventInfo->x, 
			  pmEventInfo->y - EGRAPH_TEXT_TO_LINE_DIST - EGRAPH_ICONS_HEIGHT);

  /* Draw an icon that represents the event */
  gdk_draw_pixmap(pmEventGraph->PixMap,
		  pmEventGraph->BackgroundGC,
		  pmEventInfo->EventIcon.Pixmap,
		  0, 0,
		  pmEventInfo->x, 
		  pmEventInfo->y - EGRAPH_TEXT_TO_LINE_DIST - EGRAPH_ICONS_HEIGHT,
		  EGRAPH_ICONS_WIDTH, 
		  EGRAPH_ICONS_HEIGHT);

  /* Stop clipping */
  gdk_gc_set_clip_mask(pmEventGraph->BackgroundGC, NULL);

  /* Suppressing text on icons saves less than 5% of the time of
     drawing the icons.  Calculated suppression is therefore not
     worth it, especially considering the risk of misinterpretation.
     Let the user decide for itself. */
  if(GTK_TOGGLE_BUTTON(((mainWindow*)pmEventGraph->MainWindow)->tlbHideIconText)->active != TRUE)
    gdk_draw_string(pmEventGraph->PixMap,
		    pmEventGraph->TextFont,
		    pmEventGraph->TextGC,
		    pmEventInfo->x + EGRAPH_ICONS_WIDTH,
		    pmEventInfo->y - EGRAPH_TEXT_TO_LINE_DIST,
		    pmEventInfo->EventText);
}

/******************************************************************
 * Function :
 *    EGIPendDrawEventInfoReq()
 * Description :
 *    This function pends a draw request for a clipped icon and
 *    text information on a pixmap.  
 * Description : 
 *    Not every event gets an icon but many do (average about 40%).
 *    Accept an icon draw request.  Only draw a new icon when it
 *    is far enough away from any other icon  (where "far enough" is
 *    an evolving concept). This prevents useless overdraws in
 *    expanded time scales when USecPerPixel gets big. This has
 *    shown a factor of 10 drop in actual icon calls.  Furthermore,
 *    suppress the text string until the graph is expanded "enough".
 *    ALWAYS update the deferred request (pixmap or label or Y may
 *    change).
 * Parameters :
 *    pmEventGraph, The event graph being drawn.
 *    pmEventIcon, The icon to be drawn.
 *    pmX, The X position of the icon.
 *    pmY, The Y position of the icon.
 *    pmEventText, The text to be drawn beside the icon.
 *    pmEvent, The event for which the icon is being drawn.
 * Return values :
 *    NONE.
 * History : 01/01, ROC Deferral rewrite
 * Note :
 ******************************************************************/
void EGIPendDrawEventInfoReq(eventGraph* pmEventGraph,
			     pixAndMask  pmEventIcon,
			     guint       pmX,
			     guint       pmY,
			     gchar*      pmEventText,
			     event*      pmEvent)
{
  guint       dx;             /* X distance */
  guint       dy;             /* Y distance */
  guint       lOverlapRange;  /* Range used to determine whether icons overlap */
  GSList*     pNextEventIcon; /* Next icon in event list */
  eventInfo*  pEventInfo;     /* Pointer to event info */
  mainWindow* pMainWindow;    /* Pointer to main window */

  /* Get main window */
  pMainWindow = pmEventGraph->MainWindow;

  /* A great philosophical argument could follow about the proper location
     for these tests but the bottom line(s) are:
     1. All tests are done here
     2. It's only two lines per icon
     3. I don't need to understand the logic of the calling routines. 
     
     I don't know how gcc does partial evals so these are separate "if"s */
  if(GTK_TOGGLE_BUTTON(pMainWindow->tlbHideSysCalls)->active)
    if(EGRAPH_PAM_EQUAL(pmEventIcon, pmEventGraph->SysCall))
      return;

  if(GTK_TOGGLE_BUTTON(pMainWindow->tlbHideIRQs)->active)
    if(EGRAPH_PAM_EQUAL(pmEventIcon, pmEventGraph->IRQ))
      return;

  if(GTK_TOGGLE_BUTTON(pMainWindow->tlbHideTraps)->active)
    if(EGRAPH_PAM_EQUAL(pmEventIcon, pmEventGraph->Trap))
      return;

  if(GTK_TOGGLE_BUTTON(pMainWindow->tlbHideSoftIRQs)->active)
    if(EGRAPH_PAM_EQUAL(pmEventIcon, pmEventGraph->BottomHalf))
      return;

  if(GTK_TOGGLE_BUTTON(pMainWindow->tlbHideSchedChange)->active)
    if(EGRAPH_PAM_EQUAL(pmEventIcon, pmEventGraph->SchedChange))
      return;

  if(GTK_TOGGLE_BUTTON(pMainWindow->tlbHideKernelTimer)->active)
    if(EGRAPH_PAM_EQUAL(pmEventIcon, pmEventGraph->KernelTimer))
      return;

  /* Should we draw icons rapidly */
  if(pmEventGraph->FastIcons)
    {
    /* Walk the list of already deferred icons, checking the distance
       from the most recent ones.  Stop when:
       1. Looked far enough back in the list (add to the list)
       2. An icon is too close (return)
       3. End of list (add to the list) */

    /* Determine overlap range */
    lOverlapRange = pmX - EGRAPH_ICON_FAR_ENOUGH;

    /* Start from the begining of the list of icons pending */
    pNextEventIcon = pmEventGraph->EventIcons;

    /* 'Til the last entry in the list */
    while(pNextEventIcon != NULL)
      {
      /* Get the event information to be drawn */
      pEventInfo = (eventInfo*) pNextEventIcon->data;

      /* Get the location of the icon */
      dx = pEventInfo->x;

      /* Is this icon far enough from the current icon */
      if(dx < lOverlapRange)
	break; /* Rest of icons are further than this */

      /* What's the difference between that icon and the requested draw location */
      dx = dx - pmX;
      dy = pEventInfo->y - pmY;

      /* Do the icons overlap  */
      if((dx * dx + dy * dy) < EGRAPH_ICON_CLEARANCE)
	/* The caller's request is refused since it overlaps with another request */
	return;

      /* Go to the next icon in the list */
      pNextEventIcon = pNextEventIcon->next;
      }
    }

  /* Are we drawing a process event (THIS IS UGLY CODE. K.Y.) */
  if(pmY < (EGIFindKernelHeight(pmEventGraph) - EGRAPH_TEXT_TO_LINE_DIST))
    pmEventGraph->NbUserIcons++;
  else
    pmEventGraph->NbKernelIcons++;

  /* Allocate a new event coordinate item and prepend it to the list so
     the most recent icons will be searched first next time through. */
  pEventInfo = (eventInfo*) g_malloc(sizeof(eventInfo));
  pEventInfo->EventIcon = pmEventIcon;
  pEventInfo->x = pmX;
  pEventInfo->y = pmY;
  strcpy(pEventInfo->EventText, pmEventText);	/* Copy dynamic data */
  pEventInfo->Event = *pmEvent;			/* Here, too */

  /* The prepend call returns a new head of list so update it */
  pmEventGraph->EventIcons = g_slist_prepend(pmEventGraph->EventIcons, 
  					    (gpointer) pEventInfo);
}

/******************************************************************
 * Function :
 *    EGIDrawLinuxEvents()
 * Description :
 *    Draws the event graph for Linux.
 * Parameters :
 *    pmEventGraph, The event graph in which trace is drawn.
 *    pmEventDB, The event database of events to be drawn.
 *    pmSystem, The system to which the database belongs.
 *    pmDrawStart, The draw start time.
 *    pmDrawEnd, The draw end time.
 *    pmFirstEvent, The first event to be drawn.
 *    pmLastEvent, The last event to be drawn.
 * Return values :
 *    NONE
 * History :
 *    K.Y., 06/06/2000, This function is largely what EGIDrawEvents()
 *                      used to be. I've taken the code out of there
 *                      in order to implement support for multiple
 *                      trace types.
 * Note :
 ******************************************************************/
void EGIDrawLinuxEvents(eventGraph* pmEventGraph,
			db*         pmEventDB,
			systemInfo* pmSystem,
			gdouble pmDrawStart,  gdouble pmDrawEnd,
			event*  pmFirstEvent, event*  pmLastEvent)
{
  gint              lPID = -1;              /* PID of event owner */
  gint              lLastCtrlEventID;       /* The event ID of the last control event */
  guint             x1, y1, x2, y2;         /* Origin and destination points for drawing */
  gchar             lString[30];            /* String used to display information on graph */
  gdouble           lDrawLinesTime = 0;     /* Time taken to draw the lines */
  gdouble           lDrawIconsTime;         /* Time taken to draw the icons */
  gboolean          lFirstCtrlEvent = TRUE; /* We are currently dealing with the first control% event */
  event             lEvent;                 /* Generic event */
  event             lLastCtrlEvent;         /* Last event that generated a change of control */
  GdkGC*            pTempGC;                /* Temporary graphic context */
  process*          pEventProcess = NULL;   /* Process associated with event */
  process*          pLastCtrlEventProcess;  /* Process to which last control event belongs */
  struct timeval    lDrawBeginTime;         /* Time at which we began drawing */
  struct timeval    lDrawDoneTime;	    /* Time at which we were done drawing */
  struct timeval    lLastCtrlEventTime;     /* The time of occurence of the last control event */
  eventDescription  lEventDesc;             /* Generic event decription */

  /* Go to the first significant event */
  lLastCtrlEvent = lEvent = *pmFirstEvent;

  /* Set last x and y */
  x2 = 0;
  y2 = EGIFindKernelHeight(pmEventGraph);

  /* Is this the first schedule-in */
  if(!DBEventsEqual(pmEventDB->FirstEventWithProc, lLastCtrlEvent))
    /* Go back to the last control event */
    while((DBEventControl(pmEventDB, &lLastCtrlEvent, pmSystem) != TRUE)
	&&(DBEventPrev(pmEventDB, &lLastCtrlEvent) == TRUE));

  /* Get the last control event's ID and it's process */
  lLastCtrlEventID = DBEventID(pmEventDB, &lLastCtrlEvent);
  pLastCtrlEventProcess = DBEventProcess(pmEventDB, &lLastCtrlEvent, pmSystem, FALSE);

  /* Get the current event's ID and it's process */
  pEventProcess = DBEventProcess(pmEventDB, &lEvent, pmSystem, FALSE);
  lPID = pEventProcess->PID;

  /* Initialize the last deferred event (ie, doesn't really exist) */
  pmEventGraph->HDef.Same = 0;
  pmEventGraph->VDef.Same = 0;

  /* Initialize benchmark counters and stats even if they're never printed */
  pmEventGraph->NbHLines      = 1;	/* Start at 1 to account for the final flush */
  pmEventGraph->NbVLines      = 1;
  pmEventGraph->NbUserIcons   = 0;	/* But not icons as they are batch-delayed */
  pmEventGraph->NbKernelIcons = 0;
  gettimeofday(&lDrawBeginTime, NULL);

  /* Draw the events */
  while((DBEventNext(pmEventDB, &lEvent) == TRUE)
     && (!DBEventsEqual(lEvent, (*pmLastEvent))))
    {
    /* Get the event's description */
    DBEventDescription(pmEventGraph->EventDB, &lEvent, FALSE, &lEventDesc);

    /* If the trace contains many CPUs only draw the first one */
    if((pmEventGraph->EventDB->LogCPUID == TRUE)
     &&(lEventDesc.CPUID != 0))
      continue;

#if 0
    printf("X1 = %u; X2 = %u; Y1 = %u; Y2 = %u \n", x1, x2, y1, y2);
#endif

    /* Depending on the event */
    switch(lEventDesc.ID)
      {
      /* Entry (syscall / trap / irq) */
      case TRACE_SYSCALL_ENTRY :
      case TRACE_TRAP_ENTRY :
      case TRACE_IRQ_ENTRY :
	/* Is this an IRQ entry while we were in the kernel */
	if((lEventDesc.ID == TRACE_IRQ_ENTRY)
	 &&(RFT8(pmEventDB, IRQ_EVENT(lEventDesc.Struct)->kernel)))
	  {
	  /* Display the IRQ information */
	  sprintf(lString, "%d:%s",
		  RFT8(pmEventDB, IRQ_EVENT(lEventDesc.Struct)->irq_id),
		  pmSystem->Interrupts[RFT8(pmEventDB, IRQ_EVENT(lEventDesc.Struct)->irq_id)]);

	  /* Draw the IRQ details */
	  EGIPendDrawEventInfoReq(pmEventGraph,
				  pmEventGraph->IRQ,
				  EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				  y2 - EGRAPH_TEXT_TO_LINE_DIST,
				  lString,
				  &lEvent);

	  /* Don't go any further */
	  continue;
	  }

	/* Is this a trap while idle task was running. On PPC, the system timer is a trap and not an interrupt */
	if((lEventDesc.ID == TRACE_TRAP_ENTRY)
	 &&(pEventProcess->PID == 0))
	  {
	  /* Display the trap entry information */
	  switch(pmEventDB->ArchType)
	    {
	    case TRACE_ARCH_TYPE_I386 :
	      sprintf(lString, "%s", pmEventDB->TrapString(pmEventDB, TRAP_EVENT(lEventDesc.Struct)->trap_id));
	      break;
	      
	    case TRACE_ARCH_TYPE_PPC :
	      sprintf(lString, "%s", pmEventDB->TrapString(pmEventDB, TRAP_EVENT(lEventDesc.Struct)->trap_id >> 8));
	      break;
	    }
	    
	  /* Draw the text and its icon */
	  EGIPendDrawEventInfoReq(pmEventGraph,
				  pmEventGraph->Trap,
				  EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				  y2 - EGRAPH_TEXT_TO_LINE_DIST,
				  lString,
				  &lEvent);

	  /* Don't go any further */
	  continue;
	  }

	/* Are we at the begining of the graph */
	if(lFirstCtrlEvent == TRUE)
	  {
	  /* Was the last control event an exit */
	  if(DBEventControlExit(pmEventDB, &lLastCtrlEvent, pEventProcess->PID) == TRUE)
	    {
	    /* Find the height of the process */
	    y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
	  
	    /* Use the process GC to draw the next horizontal line */
	    pTempGC = pmEventGraph->ProcessGC;
	    }
	  else
	    {
	    /* Find the height of the kernel */
	    y2 = EGIFindKernelHeight(pmEventGraph);
	  
	    /* Use the process GC to draw the next horizontal line */
	    pTempGC = pmEventGraph->KernelGC;
	    }
	  }
	else /* Not at beginning of graph */
	  {
	  /* Was the last control event an exit */
	  if(DBEventControlExit(pmEventDB, &lLastCtrlEvent, lPID) == TRUE)
	    {
	    /* Draw a line from the irq exit or trap exit to the process */
	    x1 = x2;
	    y1 = y2;
	    x2 = x1;
	    y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
	    switch(lLastCtrlEventID)
	      {
	      case TRACE_SYSCALL_EXIT :
		pTempGC = pmEventGraph->SysCallGC;
		break;
		
	      case TRACE_TRAP_EXIT :
		pTempGC = pmEventGraph->TrapGC;
		break;

	      default :
		pTempGC = pmEventGraph->InterruptGC;
		break;
	      }
	    EGIDrawVLine(pmEventGraph, pTempGC, x1, y1, y2);

	    /* Does this event belong to a process or to the kernel */
	    if((pEventProcess != NULL)
	     &&(pEventProcess->PID != 0))
	      /* Use the process GC to draw the next horizontal line */
	      pTempGC = pmEventGraph->ProcessGC;
	    else
	      /* Use the kernel GC to draw the next horizontal line */
	      pTempGC = pmEventGraph->KernelGC;
	    } /* end of else-if */
	  /* The previous control event was NOT an exit */
	  else
	    /* Use the kernel GC to draw the next horizontal line */
	    pTempGC = pmEventGraph->KernelGC;
	  } /* Not at beginning of graph */
	
	/* Draw a line from the last exit to now */
	x1 = x2;
	y1 = y2;
	x2 = EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd);
	y2 = y1;
	EGIDrawThickHLine(pmEventGraph, pTempGC, y2, x1, x2);

	/* Display the event information, depending on it's type */
	switch(lEventDesc.ID)
	  {
	  case TRACE_SYSCALL_ENTRY :
	    /* Display the syscall entry information */
	    sprintf(lString, "%s", pmEventDB->SyscallString(pmEventDB,
							    RFT8(pmEventDB, SYSCALL_EVENT(lEventDesc.Struct)->syscall_id)));

	    /* Draw the text and its icon */
	    EGIPendDrawEventInfoReq(pmEventGraph,
				    pmEventGraph->SysCall,
				    x2,
				    y2,
				    lString,
				    &lEvent);
	    
	    /* Set temp GC */
	    pTempGC = pmEventGraph->SysCallGC;
	    break;

	  case TRACE_TRAP_ENTRY :
	    /* Display the trap entry information */
	    switch(pmEventDB->ArchType)
	      {
	      case TRACE_ARCH_TYPE_I386 :
		sprintf(lString, "%s", pmEventDB->TrapString(pmEventDB,
							     RFT16(pmEventDB, TRAP_EVENT(lEventDesc.Struct)->trap_id)));
		break;
	      
	      case TRACE_ARCH_TYPE_PPC :
		sprintf(lString, "%s", pmEventDB->TrapString(pmEventDB,
							     RFT16(pmEventDB, TRAP_EVENT(lEventDesc.Struct)->trap_id >> 8)));
		break;
	      }
	    
	    /* Draw the text and its icon */
	    EGIPendDrawEventInfoReq(pmEventGraph,
				    pmEventGraph->Trap,
				    x2,
				    y2,
				    lString,
				    &lEvent);

	    /* Set temp GC */
	    pTempGC = pmEventGraph->TrapGC;
	    break;
	    
	  case TRACE_IRQ_ENTRY :
	    /* Display the IRQ information */
	    sprintf(lString, "%d:%s",
		    RFT8(pmEventDB, IRQ_EVENT(lEventDesc.Struct)->irq_id),
		    pmSystem->Interrupts[RFT8(pmEventDB, IRQ_EVENT(lEventDesc.Struct)->irq_id)]);

	    /* If we're at kernel height, then back up a little */
	    if(y2 == EGIFindKernelHeight(pmEventGraph))
	      /* Draw the string */
	      EGIPendDrawEventInfoReq(pmEventGraph,
				      pmEventGraph->IRQ,
				      x2,
				      y2 - EGRAPH_TEXT_TO_LINE_DIST,
				      lString,
				      &lEvent);
	    else
	      /* Draw the string */
	      EGIPendDrawEventInfoReq(pmEventGraph,
				      pmEventGraph->IRQ,
				      x2,
				      y2,
				      lString,
				      &lEvent);
	    
	    /* Set current GC */
	    pTempGC = pmEventGraph->InterruptGC;
	    break;
	  }

	/* Draw a line from the process to the kernel */
	x1 = x2;
	y1 = y2;
	x2 = x1;
	y2 = EGIFindKernelHeight(pmEventGraph);
	EGIDrawVLine(pmEventGraph, pTempGC, x1, y1, y2);

	/* Is this a control event */
	if(DBEventControlEntry(pmEventDB, &lEvent, lPID) == TRUE)
	  {
	  /* The first control event has been processed */
	  lFirstCtrlEvent = FALSE;

	  /* Set last control event */
	  lLastCtrlEvent = lEvent;

	  /* Get a pointer to the process to which this event belongs and the event's description */
	  pLastCtrlEventProcess = pEventProcess;
	  lLastCtrlEventID = lEventDesc.ID;
	  }
	break;

      /* Exit (syscall / trap /irq) */
      case TRACE_SYSCALL_EXIT :
      case TRACE_TRAP_EXIT :
      case TRACE_IRQ_EXIT :
	/* Draw a line from the last event to now */
	x1 = x2;
	y1 = y2;
	x2 = EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd);
	y2 = y1;
	EGIDrawThickHLine(pmEventGraph, pmEventGraph->KernelGC, y2, x1, x2);

	/* Is this a control event */
	if(DBEventControlExit(pmEventDB, &lEvent, lPID) == TRUE)
	  {
	  /* The first control event has been processed */
	  lFirstCtrlEvent = FALSE;

	  /* Set last control event */
	  lLastCtrlEvent = lEvent;

	  /* Get a pointer to the process to which this event belongs and the event's description */
	  pLastCtrlEventProcess = pEventProcess;
	  lLastCtrlEventID = lEventDesc.ID;
	  }
	break;

      /* Scheduling change / Kernel timer / Bottom Half / Process / Network */
      case TRACE_SCHEDCHANGE :
      case TRACE_KERNEL_TIMER :
      case TRACE_SOFT_IRQ :
      case TRACE_PROCESS :
      case TRACE_NETWORK :
	/* Display information according to event */
	switch(lEventDesc.ID)
	  {
	  /* Scheduling change */
	  case TRACE_SCHEDCHANGE :
	    /* Get the PID of the incoming process */
	    lPID = RFT32(pmEventDB, SCHED_EVENT(lEventDesc.Struct)->in);

	    /* Get a pointer to this process */
	    pEventProcess = DBGetProcByPID(lPID, pmSystem);

	    /* Display the PID of the incoming process */
	    sprintf(lString, "%d", lPID);

	    /* Draw the text and its icon */
	    EGIPendDrawEventInfoReq(pmEventGraph,
				    pmEventGraph->SchedChange, 
				    EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				    EGIFindKernelHeight(pmEventGraph),
				    lString, 
				    &lEvent);
	    break;
	    
	  /* Kernel timer */
	  case TRACE_KERNEL_TIMER :
	    /* Display the kernel timer information */
	    lString[0] = '\0';
	    
	    /* Draw the text and its icon */
	    EGIPendDrawEventInfoReq(pmEventGraph, pmEventGraph->KernelTimer,
				    EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				    EGIFindKernelHeight(pmEventGraph),
				    lString,
				    &lEvent);
	    break;

	  /* Bottom Half */
	  case TRACE_SOFT_IRQ :
	    /* Is this the kernel timer */
	    if((RFT8(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_sub_id) == TRACE_SOFT_IRQ_BOTTOM_HALF)
	     &&(RFT32(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_data) == 0))
	      break;
	    
	    /* Display the bottom half information */
	    switch(RFT8(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_sub_id))
	      {
	      /* TRACE_SOFT_IRQ_BOTTOM_HALF */
	      case TRACE_SOFT_IRQ_BOTTOM_HALF :
		sprintf(lString,
			"BH:%d",
			RFT32(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_data));
		break;

		/* TRACE_SOFT_IRQ_SOFT_IRQ */
	      case TRACE_SOFT_IRQ_SOFT_IRQ :
		sprintf(lString,
			"SIRQ:%d",
			RFT32(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_data));
		break;

		/* TRACE_SOFT_IRQ_TASKLET_ACTION */
	      case TRACE_SOFT_IRQ_TASKLET_ACTION :
		sprintf(lString,
			"TA: 0x%08X",
			RFT32(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_data));
		break;

		/* TRACE_SOFT_IRQ_TASKLET_HI_ACTION */
	      case TRACE_SOFT_IRQ_TASKLET_HI_ACTION :
		sprintf(lString,
			"THA: 0x%08X",
			RFT32(pmEventDB, SOFT_IRQ_EVENT(lEventDesc.Struct)->event_data));
		break;
	      }
	    
	    /* Draw the text and its icon */
	    EGIPendDrawEventInfoReq(pmEventGraph, pmEventGraph->BottomHalf, 
				    EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				    EGIFindKernelHeight(pmEventGraph),
				    lString,
				    &lEvent);
	    break;

	  /* Process */
	  case TRACE_PROCESS :
	    break;

	  /* Network */
	  case TRACE_NETWORK :
	    break;
	  }	  

	/* Is this a control event resulting in a return to a process */
	if(DBEventControlExit(pmEventDB, &lEvent, lPID) == TRUE)
	  {
	  /* Draw a line from the entry to the event */
	  x1 = x2;
	  y1 = y2;
	  x2 = EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd);
	  y2 = y1;
	  EGIDrawThickHLine(pmEventGraph, pmEventGraph->KernelGC, y2, x1, x2);
	  
	  /* Draw a line from the event change to the process */
	  x1 = x2;
	  y1 = y2;
	  x2 = x1;
	  y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
	  EGIDrawVLine(pmEventGraph, pmEventGraph->InterruptGC, x1, y1, y2);

	  /* The first control event has been processed */
	  lFirstCtrlEvent = FALSE;

	  /* Set last control event */
	  lLastCtrlEvent = lEvent;

	  /* Get a pointer to the process to which this event belongs and the event's description */
	  pLastCtrlEventProcess = pEventProcess;
	  lLastCtrlEventID = lEventDesc.ID;
	  }
	break;

      /* File system */
      case TRACE_FILE_SYSTEM :
	/* Are we starting to wait for I/O */
	if(RFT8(pmEventDB, FS_EVENT(lEventDesc.Struct)->event_sub_id) == TRACE_FILE_SYSTEM_BUF_WAIT_START)
	  {
	  sprintf(lString, "I/O:START");
	  EGIPendDrawEventInfoReq(pmEventGraph, pmEventGraph->IOStart,
				  EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				  EGIFindKernelHeight(pmEventGraph),
				  lString,
				  &lEvent);

	  }
	   
	/* Are we ending wait for I/O */
	if(RFT8(pmEventDB, FS_EVENT(lEventDesc.Struct)->event_sub_id) == TRACE_FILE_SYSTEM_BUF_WAIT_END)
	  {
	  sprintf(lString, "I/O:END");
	  EGIPendDrawEventInfoReq(pmEventGraph, pmEventGraph->IOEnd,
				  EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd),
				  EGIFindKernelHeight(pmEventGraph),
				  lString,
				  &lEvent);
	  }
	break;

      /* Timer */
      case TRACE_TIMER :
	break;

      /* Memory */
      case TRACE_MEMORY :
	break;

      /* Socket */
      case TRACE_SOCKET :
	break;

      /* IPC */
      case TRACE_IPC :
	break;

      /* Otherwise */
      default :
	break;
	g_print("Encountered unknow event \n");
	exit(1);
	break;
      } /* End switch type of the event to draw */
    } /* End drawing loop */

  /* Did we draw all the events in the window */
  if(DBEventsEqual((*pmFirstEvent), pmEventDB->FirstEvent)
   &&DBEventsEqual((*pmLastEvent),  pmEventDB->LastEvent))
    goto DoneDrawing;

  /* Get the last control event's time */
  DBEventTime(pmEventDB, &lLastCtrlEvent, &lLastCtrlEventTime);

#if 0
  printf("Last control event was %s \n", EventID[lLastCtrlEventID]);
#endif

  /* Was the last control event really a control event */
  if(DBEventControl(pmEventDB, &lLastCtrlEvent, pmSystem) == TRUE)
    {
    /* Was the last control event an exit */
    if(DBEventControlExit(pmEventDB, &lLastCtrlEvent, pEventProcess->PID) == TRUE)
      {
      /* Choose the right GC to draw */
      switch(lLastCtrlEventID)
	{
	case TRACE_SYSCALL_EXIT :
	  pTempGC = pmEventGraph->SysCallGC;
	  break;

	case TRACE_TRAP_EXIT :
	  pTempGC = pmEventGraph->TrapGC;
	  break;

	default :
	  pTempGC = pmEventGraph->InterruptGC;
	  break;
	}
      
      /* Draw a vertical line to the process */
      x1 = x2;
      y1 = y2 - 1;
      x2 = x1;
      y2 = EGIFindProcessHeight(pmEventGraph, pLastCtrlEventProcess);
      /* Is the event visible */
      if(DBGetTimeInDouble(lLastCtrlEventTime) >= pmDrawStart)
	EGIDrawVLine(pmEventGraph, pTempGC, x1, y1, y2);
	  
      /* Draw a horizontal line to the end */
      x1 = x2;
      y1 = y2;
      x2 = pmEventGraph->HDrawSize;
      y2 = y1;
      EGIDrawThickHLine(pmEventGraph, pmEventGraph->ProcessGC, y2, x1, x2);
      }
    else  /* Last control event was an entry into the kernel */
      {
      /* Draw a horizontal line to the end */
      x1 = x2;
      y1 = y2;
      x2 = pmEventGraph->HDrawSize;
      y2 = y1;
      EGIDrawThickHLine(pmEventGraph, pmEventGraph->KernelGC, y2, x1, x2);
      }
    }
  else /* This wasn't a control event */
    {
    /* Draw a horizontal line through the screen */
    x1 = 0;
      
    /* Were we idle all this time or is this the first control event */
    if((pLastCtrlEventProcess != NULL)
     &&(pLastCtrlEventProcess != 0)
     &&(!DBEventsEqual(lLastCtrlEvent, pmEventDB->FirstEventWithProc)))
      {
      /* This was a process */
      y1 = EGIFindProcessHeight(pmEventGraph, pLastCtrlEventProcess);
      pTempGC = pmEventGraph->ProcessGC;
      }
    else
      {
      /* This was in the kernel */
      y1 = EGIFindKernelHeight(pmEventGraph);
      pTempGC = pmEventGraph->KernelGC;
      }

    /* Draw t'ill the end */
    x2 = pmEventGraph->HDrawSize;
    y2 = y1;

    /* Draw the thick line */
    EGIDrawThickHLine(pmEventGraph, pTempGC, y2, x1, x2);
    }

 DoneDrawing:  /* OK, this doesn't look clean, but IT IS (try doing without ... you'll see) */

  /* Are we drawing horizontal lines fast */
  if(pmEventGraph->FastHLines)
    /* Flush any deferred lines by asking for a last line that is out of limits */
    EGIDrawThickHLine(pmEventGraph, NULL, 32000, 32000, 32000);
  /* Are we drawing vertical lines fast */
  if(pmEventGraph->FastVLines)
    /* Flush any deferred lines by asking for a last line that is out of limits */
    EGIDrawVLine(pmEventGraph, NULL, 32000, 32000, 32000);

  /* Are we benchmarking our operations */
  if(pmEventGraph->Benchmark)
    {
    /* We're done drawing the lines, get the current time */
    gettimeofday(&lDrawDoneTime, NULL);

    /* Compute the time it took to draw the lines (add 1 to avoid divide by zero) */
    lDrawLinesTime = (DBGetTimeInDouble(lDrawDoneTime) - DBGetTimeInDouble(lDrawBeginTime) + 1) / 1000000.0;

    /* Print out the benchmarks */
    printf("\n%6ld Hor  + %6ld Vert = %6ld lines in %6.3f s: %8.0f lines/sec\n", 
	   pmEventGraph->NbHLines,
	   pmEventGraph->NbVLines,
	   pmEventGraph->NbHLines + pmEventGraph->NbVLines,
	   lDrawLinesTime,
	   (float)(pmEventGraph->NbVLines + pmEventGraph->NbHLines) / lDrawLinesTime);

    /* Print out the begining of the icon draw information time */
    printf("%6ld User + %6ld Kern = %6ld icons in ", 
	   pmEventGraph->NbUserIcons,
	   pmEventGraph->NbKernelIcons,
	   pmEventGraph->NbUserIcons + pmEventGraph->NbKernelIcons);

    /* Flush out any pending output */
    fflush(stdout);

    /* Get the current time to see how much time it will take to draw the icons */
    gettimeofday(&lDrawBeginTime, NULL);
    }

  /* Now draw the deferred icons so they come out on top */
  g_slist_foreach(pmEventGraph->EventIcons, 
		  (GFunc) EGICommitEventDrawRequest,
		  pmEventGraph);

  /* Are we benchmarking our operations */
  if(pmEventGraph->Benchmark)
    {
    /* We're done drawing the icons, get the current time */
    gettimeofday(&lDrawDoneTime, NULL);

    /* Compute the time it took to draw the icons (add 1 to avoid divide by zero) */
    lDrawIconsTime = (DBGetTimeInDouble(lDrawDoneTime) - DBGetTimeInDouble(lDrawBeginTime) + 1) / 1000000.0;

    /* Print out the icon drawing information */
    printf("%6.3f s: %8.0f icons/sec\n",
	   lDrawIconsTime,
	   (float)(pmEventGraph->NbUserIcons + pmEventGraph->NbKernelIcons) / lDrawIconsTime);
    printf("\t    Total DB traversal + draw time: %6.3f s: %8.0f events/sec\n",
	   (lDrawLinesTime + lDrawIconsTime),
	   (float)pmEventGraph->NbIntervalEvents / (lDrawLinesTime + lDrawIconsTime));
    }
}

/******************************************************************
 * Function :
 *    EGIDrawEventInfo()
 * Description :
 *    Draws the information and icon for the given event at the
 *    given position.
 * Parameters :
 *    pmEventGraph, The event graph in which info is drawn.
 *    pmEventDesc, The event description of event to be drawn.
 *    pmX, The X position where to draw.
 *    pmY, The Y position where to draw.
 * Return values :
 *    NONE
 * History :
 *    K.Y., 06/07/2000, Initial typing.
 ******************************************************************/
void EGIDrawEventInfo(eventGraph*         pmEventGraph,
		      event*              pmEvent,
		      eventDescription*   pmEventDesc,
		      guint               pmX,
		      guint               pmY)
{
  db*               pEventDB;               /* The event database to which events belong */
  gchar             lString[30];            /* String used to display information on graph */
  
  /* Get the event database to which events belongs */
  pEventDB = pmEventGraph->EventDB;

  /* Depending on the event */
  switch(pmEventDesc->ID)
    {
    /* Syscall entry */
    case TRACE_SYSCALL_ENTRY :
      /* Display the syscall entry information */
      sprintf(lString, "%s", pmEventGraph->EventDB->SyscallString(pmEventGraph->EventDB,
								  RFT8(pEventDB, SYSCALL_EVENT(pmEventDesc->Struct)->syscall_id)));
      
      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->SysCall,
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;

    /* Trap entry */
    case TRACE_TRAP_ENTRY :
      /* Display the syscall entry information */
      switch(pmEventGraph->EventDB->ArchType)
	{
	case TRACE_ARCH_TYPE_I386 :
	  sprintf(lString, "%s", pmEventGraph->EventDB->TrapString(pmEventGraph->EventDB,
								   RFT16(pEventDB, TRAP_EVENT(pmEventDesc->Struct)->trap_id)));
	  break;
	      
	case TRACE_ARCH_TYPE_PPC :
	  sprintf(lString, "%s", pmEventGraph->EventDB->TrapString(pmEventGraph->EventDB,
								   RFT16(pEventDB, TRAP_EVENT(pmEventDesc->Struct)->trap_id >> 8)));
	  break;
	}
      
      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->Trap,
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;
	    
    /* IRQ entry */
    case TRACE_IRQ_ENTRY :
      /* Display the syscall entry information */
      sprintf(lString, "%d:%s", RFT8(pEventDB, IRQ_EVENT(pmEventDesc->Struct)->irq_id),
	      pmEventGraph->System->Interrupts[RFT8(pEventDB, IRQ_EVENT(pmEventDesc->Struct)->irq_id)]);

      /* If we're at kernel height, then back up a little */
      if(pmY == EGIFindKernelHeight(pmEventGraph))
	/* Draw the string */
	EGIPendDrawEventInfoReq(pmEventGraph,
				pmEventGraph->IRQ,
				pmX,
				pmY - EGRAPH_TEXT_TO_LINE_DIST,
				lString,
				pmEvent);
      else
	/* Draw the string */
	EGIPendDrawEventInfoReq(pmEventGraph,
				pmEventGraph->IRQ,
				pmX,
				pmY,
				lString,
				pmEvent);
      break;

    /* Syscall exit */
    case TRACE_SYSCALL_EXIT :
      break;

    /* Trap exit */
    case TRACE_TRAP_EXIT :
      break;

    /* IRQ exit */
    case TRACE_IRQ_EXIT :
      break;

    /* Scheduling change */
    case TRACE_SCHEDCHANGE :
      /* Display the PID of the incoming process */
      sprintf(lString, "%d", RFT32(pEventDB, SCHED_EVENT(pmEventDesc->Struct)->in));

      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->SchedChange, 
			      pmX,
			      pmY,
			      lString, 
			      pmEvent);
      break;
	    
    /* Kernel timer */
    case TRACE_KERNEL_TIMER :
      /* Display the kernel timer information */
      lString[0] = '\0';
	    
      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->KernelTimer,
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;

    /* Soft irq */
    case TRACE_SOFT_IRQ :
	    /* Is this the kernel timer */
	    if((RFT8(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_sub_id) == TRACE_SOFT_IRQ_BOTTOM_HALF)
	     &&(RFT32(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_data) == 0))
	      break;
	    
	    /* Display the bottom half information */
	    switch(RFT8(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_sub_id))
	      {
	      /* TRACE_SOFT_IRQ_BOTTOM_HALF */
	      case TRACE_SOFT_IRQ_BOTTOM_HALF :
		sprintf(lString,
			"BH:%d",
			RFT32(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_data));
		break;

		/* TRACE_SOFT_IRQ_SOFT_IRQ */
	      case TRACE_SOFT_IRQ_SOFT_IRQ :
		sprintf(lString,
			"SIRQ:%d",
			RFT32(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_data));
		break;

		/* TRACE_SOFT_IRQ_TASKLET_ACTION */
	      case TRACE_SOFT_IRQ_TASKLET_ACTION :
		sprintf(lString,
			"TA: 0x%08X",
			RFT32(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_data));
		break;

		/* TRACE_SOFT_IRQ_TASKLET_HI_ACTION */
	      case TRACE_SOFT_IRQ_TASKLET_HI_ACTION :
		sprintf(lString,
			"THA: 0x%08X",
			RFT32(pEventDB, SOFT_IRQ_EVENT(pmEventDesc->Struct)->event_data));
		break;
	      }
	    
      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->BottomHalf, 
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;

    /* Process */
    case TRACE_PROCESS :
      break;

    /* Network */
    case TRACE_NETWORK :
      break;


    /* File system */
    case TRACE_FILE_SYSTEM :
      /* Are we starting to wait for I/O */
      if(RFT8(pEventDB, FS_EVENT(pmEventDesc->Struct)->event_sub_id) == TRACE_FILE_SYSTEM_BUF_WAIT_START)
	{
	sprintf(lString, "I/O:START");
	EGIPendDrawEventInfoReq(pmEventGraph,
				pmEventGraph->IOStart,
				pmX,
				pmY,
				lString,
				pmEvent);
	}
	   
      /* Are we ending wait for I/O */
      if(RFT8(pEventDB, FS_EVENT(pmEventDesc->Struct)->event_sub_id) == TRACE_FILE_SYSTEM_BUF_WAIT_END)
	{
	sprintf(lString, "I/O:END");
	EGIPendDrawEventInfoReq(pmEventGraph,
				pmEventGraph->IOEnd,
				pmX,
				pmY,
				lString,
				pmEvent);
	}
      break;

    /* Timer */
    case TRACE_TIMER :
      break;

    /* Memory */
    case TRACE_MEMORY :
      break;

    /* Socket */
    case TRACE_SOCKET :
      break;

    /* IPC */
    case TRACE_IPC :
      break;

#if SUPP_RTAI
    /* RTAI has been mounted */
    case TRACE_RTAI_MOUNT :
      break;

    /* RTAI has been unmounted */
    case TRACE_RTAI_UMOUNT :
      break;

    /* Global IRQ entry */
    case TRACE_RTAI_GLOBAL_IRQ_ENTRY :
      /* Display the syscall entry information */
      sprintf(lString, "G:%d", RFT8(pEventDB, IRQ_EVENT(pmEventDesc->Struct)->irq_id));

      /* Draw the string */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->IRQ,
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;

    /* Global IRQ exit */
    case TRACE_RTAI_GLOBAL_IRQ_EXIT :
      break;

    /* CPU own IRQ entry */
    case TRACE_RTAI_OWN_IRQ_ENTRY :
      /* Display the syscall entry information */
      sprintf(lString, "C:%d", RFT8(pEventDB, IRQ_EVENT(pmEventDesc->Struct)->irq_id));

      /* Draw the string */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->IRQ,
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;

    /* CPU own IRQ exit */
    case TRACE_RTAI_OWN_IRQ_EXIT :
      break;

    /* Trap entry */
    case TRACE_RTAI_TRAP_ENTRY :
      break;

    /* Trap exit */
    case TRACE_RTAI_TRAP_EXIT :
      break;

    /* System ReQuest entry */
    case TRACE_RTAI_SRQ_ENTRY :
      /* Display the SRQ entry information */
      sprintf(lString, "SRQ: %d", RTAI_SRQ_ENTRY_EVENT(pmEventDesc->Struct)->srq_id);
      
      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->SysCall,
			      pmX,
			      pmY,
			      lString,
			      pmEvent);
      break;

    /* System ReQuest exit */
    case TRACE_RTAI_SRQ_EXIT :
      break;

    /* Switch to Linux */
    case TRACE_RTAI_SWITCHTO_LINUX :
      break;

    /* Switch to RTAI */
    case TRACE_RTAI_SWITCHTO_RT :
      break;

    /* Scheduling change */
    case TRACE_RTAI_SCHED_CHANGE :
      /* Display the PID of the incoming process */
      if(RTAI_SCHED_CHANGE_EVENT(pmEventDesc->Struct)->out_state != 0)
	sprintf(lString, "RT:%d", RTAI_SCHED_CHANGE_EVENT(pmEventDesc->Struct)->in);
      else
	sprintf(lString, "RT:%d", RTAI_SCHED_CHANGE_EVENT(pmEventDesc->Struct)->out);

      /* Draw the text and its icon */
      EGIPendDrawEventInfoReq(pmEventGraph,
			      pmEventGraph->SchedChange, 
			      pmX,
			      pmY,
			      lString, 
			      pmEvent);
      break;

    /* Hit key part of task services */
    case TRACE_RTAI_TASK :
      break;

    /* Hit key part of timer services */
    case TRACE_RTAI_TIMER :
      break;

    /* Hit key part of semaphore services */
    case TRACE_RTAI_SEM :
      break;

    /* Hit key part of message services */
    case TRACE_RTAI_MSG :
      break;

    /* Hit key part of RPC services */
    case TRACE_RTAI_RPC :
      break;

    /* Hit key part of mail box services */
    case TRACE_RTAI_MBX :
      break;

    /* Hit key part of FIFO services */
    case TRACE_RTAI_FIFO :
      break;

    /* Hit key part of shared memory services */
    case TRACE_RTAI_SHM :
      break;

    /* Hit key part of Posix services */
    case TRACE_RTAI_POSIX :
      break;

    /* Hit key part of LXRT services */
    case TRACE_RTAI_LXRT :
      if(RTAI_LXRT_EVENT((pmEventDesc)->Struct)->event_sub_id == TRACE_RTAI_LXRT_RTAI_SYSCALL_ENTRY)
	{
	/* Display the syscall entry information */
	sprintf(lString, "LXRT");
      
	/* Draw the text and its icon */
	EGIPendDrawEventInfoReq(pmEventGraph,
				pmEventGraph->SysCall,
				pmX,
				pmY,
				lString,
				pmEvent);
	}
      break;

    /* Hit key part of LXRT-Informed services */
    case TRACE_RTAI_LXRTI :
      break;
#endif /* SUPP_RTAI */

    /* Otherwise */
    default :
      g_print("Encountered unknow event \n");
      exit(1);
      break;
    } /* End switch type of the event to draw */
}

/******************************************************************
 * Function :
 *    EGIDrawRTAIEvents()
 * Description :
 *    Draws the event graph for RTAI modified Linux.
 * Parameters :
 *    pmEventGraph, The event graph in which trace is drawn.
 *    pmEventDB, The event database of events to be drawn.
 *    pmSystem, The system to which the database belongs.
 *    pmDrawStart, The draw start time.
 *    pmDrawEnd, The draw end time.
 *    pmFirstEvent, The first event to be drawn.
 *    pmLastEvent, The last event to be drawn.
 * Return values :
 *    NONE
 * History :
 *    K.Y., 06/06/2000, This function is largely inspired by what
 *                      EGIDrawEvents() used to be, but is the first
 *                      implementation of graph drawing using the
 *                      "state" concept.
 ******************************************************************/
#if SUPP_RTAI
void EGIDrawRTAIEvents(eventGraph* pmEventGraph,
		       db*         pmEventDB,
		       systemInfo* pmSystem,
		       gdouble pmDrawStart,  gdouble pmDrawEnd,
		       event*  pmFirstEvent, event*  pmLastEvent)
{
  gint              lPID = -1;              /* PID of event owner */
  gint              lTID = -1;              /* TID of event owner */
  guint             x1, y1, x2, y2;         /* Origin and destination points for drawing */
  event             lEvent;                 /* Generic event */
  event             lFirstNextEvent;        /* Event following first event */
  GdkGC*            pTempGC = NULL;         /* Temporary graphic context */
  process*          pEventProcess = NULL;   /* Process associated with event */
  RTAItask*         pEventTask = NULL;      /* Task associated with event */
  RTAIsystemState   lCurrSysState;          /* Current system state */
  RTAIsystemState   lNextSysState;          /* Next system state */
  eventDescription  lEventDesc;             /* Generic event decription */

  /* Go to the first significant event */
  lEvent = *pmFirstEvent;

  /* Get the task to which this event belongs and it's TID */
  pEventTask = RTAIDBEventTask(pmEventDB, &lEvent, pmSystem, TRUE);
  if(pEventTask != NULL)
    lTID = pEventTask->TID;

  /* Get the process to which this event belongs and it's PID */
  pEventProcess = DBEventProcess(pmEventDB, &lEvent, pmSystem, TRUE);
  lPID = pEventProcess->PID;

  /* Is the incoming task an RT buddy of a linux process */
  if((pEventTask != NULL)
   &&(pEventTask->TID != 0)
   &&(pEventTask->IsProcessShadow == TRUE)
   &&(DBGetTimeInDouble(pEventProcess->ReportedSwitchTime) < (DBGetTimeInDouble(pEventTask->ReportedSwitchTime))))
    {
    /* Get the process to which this event belongs and it's PID */
    pEventProcess = pEventTask->ShadowProcess;
    lPID = pEventProcess->PID;
    }

  /* Get the initial state */
  lFirstNextEvent = lEvent;
  DBEventNext(pmEventDB, &lFirstNextEvent);
  lCurrSysState = RTAIDBGetCurrentState(pmEventDB, &lFirstNextEvent, pmSystem);

  /* Set last x */
  x2 = 0;

  /* Set last y */
  switch(lCurrSysState)
    {
    /* We are in RTAI's core */
    case RTAI_CORE:
      y2 = EGIFindRTAIHeight(pmEventGraph);
      break;

    /* We are in an RTAI task */
    case RTAI_TASK:
      y2 = EGIFindTaskHeight(pmEventGraph, pEventTask);
      break;

    /* We are in the Linux kernel */
    case LINUX_KERNEL:
      y2 = EGIFindKernelHeight(pmEventGraph);
      break;

    /* We are in a Linux process */
    case LINUX_PROCESS:
      y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
      break;

    default:
      g_print("Unable to find current system state (%d) \n", lCurrSysState);
      exit(1);
    }

  /* Draw the events */
  while((DBEventNext(pmEventDB, &lEvent) == TRUE)
     && (!DBEventsEqual(lEvent, (*pmLastEvent))))
    {
    /* Get the event's description */
    DBEventDescription(pmEventDB, &lEvent, FALSE, &lEventDesc);

    /* If the trace contains many CPUs only draw the first one, for now */
    if((pmEventGraph->EventDB->LogCPUID == TRUE)
     &&(lEventDesc.CPUID != 0))
      continue;

    /* Get the next system state */
    lNextSysState = RTAIDBEventState(pmEventDB,
				     &lEvent,
				     pEventProcess,
				     lCurrSysState,
				     pEventTask);

    /* Is this an RTAI scheduling change */
    if(lEventDesc.ID == TRACE_RTAI_SCHED_CHANGE)
      {
      /* Get the task to which this event belongs and it's TID */
      pEventTask = RTAIDBEventTask(pmEventDB, &lEvent, pmSystem, TRUE);
      lTID = pEventTask->TID;

      /* Is the incoming task an RT shadow of a linux process */
      if(pEventTask->IsProcessShadow == TRUE)
	{
	pEventProcess = pEventTask->ShadowProcess;
	lPID = pEventProcess->PID;
	}
      /* Is the incoming task Linux */
      else if(pEventTask->TID == 0)
	{
	/* Get the process to which this event belongs and it's PID */
	pEventProcess = DBEventProcess(pmEventDB, &lEvent, pmSystem, TRUE);
	lPID = pEventProcess->PID;
	}
      }

    /* Is this a scheduling change */
    if((lEventDesc.ID == TRACE_SCHEDCHANGE)
#if 0
     &&(((pEventTask != NULL)
       &&((pEventTask->IsProcessShadow == FALSE)
	||(pEventTask->TID == 0)))
      ||(pEventTask == NULL)))
#else
      )
#endif
      {
      /* Get the process to which this event belongs and it's TID */
      pEventProcess = DBEventProcess(pmEventDB, &lEvent, pmSystem, FALSE);
      lPID = pEventProcess->PID;
      }

    /* Find current event's position */
    x1 = x2;
    y1 = y2;
    x2 = EGIFindEventDistance(pmEventGraph, lEventDesc, pmDrawStart, pmDrawEnd);
    y2 = y1;

    /* Draw the event's information */
    EGIDrawEventInfo(pmEventGraph,
		     &lEvent,
		     &lEventDesc,
		     x2,
		     y2);
	
    /* Depending on the current state */
    switch(lCurrSysState)
      {
      /* We are in RTAI's core */
      case RTAI_CORE:
	/* Draw a horizontal line since the last event */
	EGIDrawThickHLine(pmEventGraph, pmEventGraph->RTAIGC, y2, x1, x2);

	/* Depending on the next state */
	switch(lNextSysState)
	  {
	  /* We are remaining in the same state */
	  case RTAI_CORE:
	    break;

	  /* We are going in an RTAI task */
	  case RTAI_TASK:
	    /* Update drawing height */
	    y2 = EGIFindTaskHeight(pmEventGraph, pEventTask);
	    break;
	  
	  /* We are going in the Linux kernel */
	  case LINUX_KERNEL:
	    /* Update drawing height */
	    y2 = EGIFindKernelHeight(pmEventGraph);
	    break;

	  /* We are going in a Linux process */
	  case LINUX_PROCESS:
	    /* Update drawing height */
	    y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
	    break;

	  default:
	    break;
	  }	
	break;

      /* We are in an RTAI task */
      case RTAI_TASK:
	/* Draw a horizontal line since the last event */
	EGIDrawThickHLine(pmEventGraph, pmEventGraph->TaskGC, y2, x1, x2);

	/* Depending on the next state */
	switch(lNextSysState)
	  {
	  /* We are going in RTAI's core */
	  case RTAI_CORE:
	    /* Update drawing height */
	    y2 = EGIFindRTAIHeight(pmEventGraph);
	    break;

	  /* We are remaining in the same state */
	  case RTAI_TASK:
	    /* Is the current event an RTAI scheduling change */
	    if(lEventDesc.ID == TRACE_RTAI_SCHED_CHANGE)
	      y2 = EGIFindTaskHeight(pmEventGraph, pEventTask);
	    break;
	  
	  /* We are going in the Linux kernel */
	  case LINUX_KERNEL:
	    /* Update drawing height */
	    y2 = EGIFindKernelHeight(pmEventGraph);
	    break;

	  /* We are going in a Linux process */
	  case LINUX_PROCESS:
	    /* Update drawing height */
	    y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
	    break;

	  default:
	    break;
	  }	
	break;

      /* We are in the Linux kernel */
      case LINUX_KERNEL:
	/* Draw a horizontal line since the last event */
	EGIDrawThickHLine(pmEventGraph, pmEventGraph->KernelGC, y2, x1, x2);

	/* Depending on the next state */
	switch(lNextSysState)
	  {
	  /* We are going in RTAI's core */
	  case RTAI_CORE:
	    /* Update drawing height */
	    y2 = EGIFindRTAIHeight(pmEventGraph);
	    break;

	  /* We are going in an RTAI task */
	  case RTAI_TASK:
	    /* Update drawing height */
	    y2 = EGIFindTaskHeight(pmEventGraph, pEventTask);
	    break;
	  
	  /* We are remaining in the same state */
	  case LINUX_KERNEL:
	    break;

	  /* We are going in a Linux process */
	  case LINUX_PROCESS:
	    /* Update drawing height */
	    y2 = EGIFindProcessHeight(pmEventGraph, pEventProcess);
	    break;

	  default:
	    break;
	  }	
	break;

      /* We are in a Linux process */
      case LINUX_PROCESS:
	/* Draw a horizontal line since the last event */
	EGIDrawThickHLine(pmEventGraph, pmEventGraph->ProcessGC, y2, x1, x2);

	/* Depending on the next state */
	switch(lNextSysState)
	  {
	  /* We are going in RTAI's core */
	  case RTAI_CORE:
	    /* Update drawing height */
	    y2 = EGIFindRTAIHeight(pmEventGraph);
	    break;

	  /* We are going in an RTAI task */
	  case RTAI_TASK:
	    /* Update drawing height */
	    y2 = EGIFindTaskHeight(pmEventGraph, pEventTask);
	    break;
	  
	  /* We are going in the Linux kernel */
	  case LINUX_KERNEL:
	    /* Update drawing height */
	    y2 = EGIFindKernelHeight(pmEventGraph);
	    break;

	  /* We are remaining in the same state */
	  case LINUX_PROCESS:
	    break;

	  default:
	    break;
	  }	
	break;

      default:
	break;
      }

    /* Was there a state change */
    if((lCurrSysState != lNextSysState)
     ||((lCurrSysState == lNextSysState)
      &&(lEventDesc.ID == TRACE_RTAI_SCHED_CHANGE)))
      {
      /* Depending on the type of event */
      switch(lEventDesc.ID)
	{
	/* IRQ entries/exits */
	case TRACE_IRQ_ENTRY:
	case TRACE_IRQ_EXIT:
	case TRACE_RTAI_GLOBAL_IRQ_ENTRY:
	case TRACE_RTAI_GLOBAL_IRQ_EXIT:
	case TRACE_RTAI_OWN_IRQ_ENTRY:
	case TRACE_RTAI_OWN_IRQ_EXIT:
	  pTempGC = pmEventGraph->InterruptGC;
	  break;

	/* Trap entries/exits */
	case TRACE_TRAP_ENTRY:
	case TRACE_TRAP_EXIT:
	case TRACE_RTAI_TRAP_ENTRY:
	case TRACE_RTAI_TRAP_EXIT:
	  pTempGC = pmEventGraph->TrapGC;
	  break;

	/* Syscall entries/exits */
	case TRACE_SYSCALL_ENTRY:
	case TRACE_SYSCALL_EXIT:
	  pTempGC = pmEventGraph->SysCallGC;
	  break;

	/* Anything else */
	default:
	  pTempGC = pmEventGraph->InterruptGC;
	  break;
	}
      
      /* Draw line from initial state to final state */
      x1 = x2;
      x2 = x1;
      gdk_draw_line(pmEventGraph->PixMap,
		    pTempGC,
		    x1,
		    y1,
		    x2,
		    y2);

      /* Update the system's state */
      lCurrSysState = lNextSysState;
      } /* End state change handling */
    } /* End drawing loop */

  /* Did we draw all the event in the window */
  if(DBEventsEqual((*pmFirstEvent), pmEventDB->FirstEvent)
   &&DBEventsEqual((*pmLastEvent),  pmEventDB->LastEvent))
    /* Don't draw anything */
    return;

  /* Depending on the last state we are in */
  switch(lCurrSysState)
    {
    /* We are in RTAI's core */
    case RTAI_CORE:
      pTempGC = pmEventGraph->RTAIGC;
      break;

    /* We are in an RTAI task */
    case RTAI_TASK:
      pTempGC = pmEventGraph->TaskGC;
      break;

    /* We are in the Linux kernel */
    case LINUX_KERNEL:
      pTempGC = pmEventGraph->KernelGC;
      break;

    /* We are in a Linux process */
    case LINUX_PROCESS:
      pTempGC = pmEventGraph->ProcessGC;
      break;

    default:
      break;
    }

  /* Update line coordinates */
  x1 = x2;
  y1 = y2;
  x2 = pmEventGraph->HDrawSize;
  y2 = y1;

  /* Draw last line */
  EGIDrawThickHLine(pmEventGraph, pTempGC, y2, x1, x2);
}
#endif /* SUPP_RTAI */

/******************************************************************
 * Function :
 *    EGIDrawEvents()
 * Description :
 *    Calls the correct drawing function depending on the type
 *    of trace.
 * Parameters :
 *    pmEventGraph, The event graph in which trace is drawn.
 *    pmDrawStart, The draw start time.
 *    pmDrawEnd, The draw end time.
 *    pmFirstEvent, The first event to be drawn.
 *    pmLastEvent, The last event to be drawn.
 * Return values :
 *    NONE.
 * History :
 *    K.Y., 06/06/2000, Moved drawing code into trace type dependent
 *                      functions.
 *    K.Y., ..., Multiple modifications (see header).
 *    K.Y., 28/06/1999, Initial typing.
 * Note :
 ******************************************************************/
void EGIDrawEvents(eventGraph* pmEventGraph,
		   gdouble pmDrawStart,  gdouble pmDrawEnd,
		   event*  pmFirstEvent, event*  pmLastEvent)
{
  db*               pEventDB;               /* The event database */
  systemInfo*       pSystem;                /* System pointer */

#if 0
  struct timeval    lTime;                  /* Generic timevalue */
  printf("\n");
  DBEventTime(pmEventGraph->EventDB, pmFirstEvent, &lTime);  
  printf("First event time : (%ld, %ld) \n", lTime.tv_sec, lTime.tv_usec);
  DBEventTime(pmEventGraph->EventDB, pmLastEvent, &lTime);  
  printf("Last  event time : (%ld, %ld) \n", lTime.tv_sec, lTime.tv_usec);
#endif

  /* Initialize event database */
  pEventDB = pmEventGraph->EventDB;

  /* Initialize system pointer */
  pSystem = pmEventGraph->System;

  /* Make sure the event icon coordinates list is empty */
  if(pmEventGraph->EventIcons != NULL)
    {
    /* Free all allocated coordinate items */
    g_slist_foreach(pmEventGraph->EventIcons, (GFunc) g_free, NULL);

    /* Free the GList list items */
    g_slist_free(pmEventGraph->EventIcons);

    /* Reset list pointer */
    pmEventGraph->EventIcons = NULL;
    }

  /* Depending on the type of trace */
  switch(pEventDB->SystemType)
    {
    /* Vanilla Linux */
    case TRACE_SYS_TYPE_VANILLA_LINUX :
      EGIDrawLinuxEvents(pmEventGraph,
			 pEventDB,
			 pSystem,
			 pmDrawStart, pmDrawEnd,
			 pmFirstEvent, pmLastEvent);
      break;

#if SUPP_RTAI
    /* RTAI modified Linux */
    case TRACE_SYS_TYPE_RTAI_LINUX :
      EGIDrawRTAIEvents(pmEventGraph,
			pEventDB,
			pSystem,
			pmDrawStart, pmDrawEnd,
			pmFirstEvent, pmLastEvent);
      break;
#endif
    
    /* Just in case */
    default :
      g_print("TraceVisualizer: Unknow trace type to draw \n");
      return;
      break;
    }
}

/******************************************************************
 * Function : EGIDrawHorizon()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGIDrawHorizon(eventGraph* pmEventGraph)
{
  process*    pProc;     /* Generic process pointer */

  /* Go through all the processes in the process list */
  for(pProc = pmEventGraph->System->Processes;
      pProc != NULL;
      pProc = pProc->Next)
    {
    /* Is this process zero */
    if(pProc->PID != 0)
      gdk_draw_line(pmEventGraph->PixMap,
		    pmEventGraph->HorizonGC,
		    0,
		    EGIFindProcessHeight(pmEventGraph, pProc),
		    pmEventGraph->HDrawSize,
		    EGIFindProcessHeight(pmEventGraph, pProc));
    else
      gdk_draw_line(pmEventGraph->PixMap,
		    pmEventGraph->HorizonGC,
		    0,
		    EGIFindKernelHeight(pmEventGraph),
		    pmEventGraph->HDrawSize,
		    EGIFindKernelHeight(pmEventGraph));
    }
}

/******************************************************************
 * Function : EGIDrawSelectionLine()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGIDrawSelectionLine(eventGraph* pmEventGraph)
{
  /* If a process is selected */
  if(pmEventGraph->SelectedProc != NULL)
      gdk_draw_line(pmEventGraph->PixMap,
		    pmEventGraph->SelectedGC,
		    0,
		    EGIFindProcessHeight(pmEventGraph, pmEventGraph->SelectedProc),
		    pmEventGraph->HDrawSize,
		    EGIFindProcessHeight(pmEventGraph, pmEventGraph->SelectedProc));
}

/******************************************************************
 * Function : EGIDrawTrace()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGIDrawTrace(eventGraph* pmEventGraph, gboolean pmForce)
{
  event            lFirstEvent;         /* First event drawn */
  event            lLastEvent;          /* Last event drawn */
  gchar            lUnits[80];          /* Memory span units */
  glong            lMemorySpan;         /* Quantity of memory containing event data we have to go through */
  gfloat           lScrollDisplacement; /* Displacement between last and current scroll positions */
  gfloat           lValue;              /* Current position of scrollbar */
  gfloat           lPageSize;           /* Size of scroll page */
  gfloat           lMinValue;           /* Minimum value of scroll bar position */
  gfloat           lMaxValue;           /* Maximum value of scroll bar position */
  gdouble          lDrawStart;          /* Time of first event drawn */
  gdouble          lDrawEnd;            /* Time of last event drawn */
 
  /* Set globals from options. This routine is essentially the entry point to the EG "module" */
  pmEventGraph->Benchmark  = ((systemView*)(((mainWindow*)(pmEventGraph->MainWindow))->SystemView))->Options->Benchmark;
  pmEventGraph->FastHLines = !GTK_TOGGLE_BUTTON(((mainWindow*)pmEventGraph->MainWindow)->tlbSlowH)->active;
  pmEventGraph->FastVLines = !GTK_TOGGLE_BUTTON(((mainWindow*)pmEventGraph->MainWindow)->tlbSlowV)->active;
  pmEventGraph->FastIcons  = !GTK_TOGGLE_BUTTON(((mainWindow*)pmEventGraph->MainWindow)->tlbSlowI)->active;

  /* Get scrollbar parameters */
  lValue        = GTK_ADJUSTMENT(pmEventGraph->HAdjustment)->value;
  lPageSize     = GTK_ADJUSTMENT(pmEventGraph->HAdjustment)->page_size;
  lMinValue     = GTK_ADJUSTMENT(pmEventGraph->HAdjustment)->lower;
  lMaxValue     = GTK_ADJUSTMENT(pmEventGraph->HAdjustment)->upper;

  /* Get displacement */
  lScrollDisplacement = GTK_ADJUSTMENT(pmEventGraph->HAdjustment)->value - pmEventGraph->LastScrollVal;

#if 0
  /* Print out the sizes */
  g_print("\nHBar Value / Page Size\t: %.f / %.f\n", lValue, lPageSize);
  g_print("Min / Max Value\t\t: %.f / %.f\n", lMinValue, lMaxValue);
  g_print("Scroll Delta / Force Redraw : %.f / %s\n", lScrollDisplacement, pmForce ? "TRUE" : "FALSE");
#endif

  /* Is it worth it to redraw */
  if((lScrollDisplacement < 1) && (lScrollDisplacement > -1) && (pmForce != TRUE))
    return;

  /* Update the latest scroll value */
  pmEventGraph->LastScrollVal = lValue;

  /* Get the drawing interval */
  EGIFindDrawLimits(pmEventGraph, lValue, lPageSize, lMinValue, lMaxValue, &lDrawStart, &lDrawEnd);
  
  /* Print display interval in status bar */
  WDTimeSBarDisplay(pmEventGraph->MainWindow, lDrawStart, lDrawEnd);

  /* Don't go forward if there is nothing to draw */
  if(pmEventGraph->Interval < 0)
    return;

  /* Find the first and last event drawable */
  if(EGIFindLimitEvents(pmEventGraph, lDrawStart, lDrawEnd, &lFirstEvent, &lLastEvent) == FALSE)
    {
    /* Draw all the events */
    lFirstEvent = pmEventGraph->EventDB->FirstEventWithProc;
    lLastEvent  = pmEventGraph->EventDB->LastEvent;
    }

  /* Are we benchmarking? */
  if(pmEventGraph->Benchmark)
    {
    /* Compute the memory span we have to go through to draw the trace */
    lMemorySpan = (lLastEvent.EventPos - lFirstEvent.EventPos)
               + pmEventGraph->EventDB->BufferSize * (lLastEvent.BufID - lFirstEvent.BufID);

    /* Determine the memory span units */
    if(lMemorySpan < 9999)
      strcpy(lUnits, "bytes");
    else
      lMemorySpan /= 1024;
    if(lMemorySpan < 9999)
      strcpy(lUnits, "kilobytes");
    else
      {
      lMemorySpan /= 1024;
      strcpy(lUnits, "megabytes");
      }
#if 0 
    g_print("\nDrawable Events Time Interval \t\t: %.2f \n", pmEventGraph->Interval);
    g_print("Draw Start / End \t: %.f / %.f\n", lDrawStart, lDrawEnd);
    g_print("Graph Start / Duration \t: %.f / %.f\n", pmEventGraph->StartTime, pmEventGraph->Duration);
#endif
    g_print("\n-----------------------------------------------------------\n");
    g_print("%ld events in %ld %s span %.0f uSec; drawn @ %0.3f us/pixel...\n",
	    pmEventGraph->NbIntervalEvents, lMemorySpan, lUnits,
	    pmEventGraph->Interval, pmEventGraph->USecPerPix);
    }

  /* Empty the draw area */
  gdk_draw_rectangle(pmEventGraph->PixMap,
		     pmEventGraph->BackgroundGC,
		     TRUE,
		     0, 0,
		     pmEventGraph->HDrawSize,
		     pmEventGraph->VDrawSize);

#if 0
  g_print("First Event Drawn  : %f \n", EGIGetEventTimeInDouble(pFirstEvent));
  g_print("Last Event Drawn   : %f \n", EGIGetEventTimeInDouble(pLastEvent));
  g_print("NFirst Event Drawn : %f \n", EGIGetEventTimeInDouble(pFirstEvent->Next));
  g_print("PLast Event Drawn  : %f \n", EGIGetEventTimeInDouble(pLastEvent->Prev));
  g_print("Interval last/first : %f \n", EGIGetEventTimeInDouble(lLastEvent) - EGIGetEventTimeInDouble(lFirstEvent));
  g_print("Interval pre-set    : %f \n", pmEventGraph->Interval);
#endif

  /* Draw the horizon */
  if(pmEventGraph->ShowHorizon == TRUE)
    EGIDrawHorizon(pmEventGraph);

  /* Draw the viewable events */
  EGIDrawEvents(pmEventGraph,
		lDrawStart,  lDrawEnd,
		&lFirstEvent, &lLastEvent);

  /* Draw the selection line */
  EGIDrawSelectionLine(pmEventGraph);
}

/******************************************************************
 * Function : EGIResetHAdjustment()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGIResetHAdjustment(eventGraph* pmEGraph, gboolean pmKeepInRegion)
{
  /* Are we somewhere else than in the begining */
  if(pmEGraph->LastScrollVal != 0 && pmKeepInRegion)
    /* Keep the scrollbar in the same region */
    pmEGraph->LastScrollVal = 
      ((gfloat) pmEGraph->HTrueSize) *
      GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value / 
      GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper;

#if 0
  g_print("\nBefore \n");
  g_print("Value : %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value);
  g_print("Page size %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_size);
  g_print("Min Value %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower);
  g_print("Max Value %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper);
#endif

  /* Set scrollbar parameters */
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower = 0;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper = (gfloat) pmEGraph->HTrueSize;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value =  pmEGraph->LastScrollVal;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_size = (gfloat) pmEGraph->HDrawSize;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->step_increment = 10;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_increment = (gfloat) pmEGraph->HDrawSize; 
  gtk_adjustment_changed(GTK_ADJUSTMENT(pmEGraph->HAdjustment));

  /* Set drawable interval */
  pmEGraph->Interval = (pmEGraph->Duration * (gdouble) pmEGraph->HDrawSize) / ((gdouble) pmEGraph->HTrueSize);

#if 0
  g_print("After \n");
  g_print("Value : %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value);
  g_print("Page size %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_size);
  g_print("Min Value %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower);
  g_print("Max Value %f \n", GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper);
#endif
}

/**********************************************************************************/
/**************************** Signal handling functions ***************************/
/**********************************************************************************/
/******************************************************************
 * Function : EGSHExposeEvent()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGSHExposeEvent(GtkWidget* pmWidget, GdkEventExpose* pmEvent, eventGraph* pmEGraph)
{
  gfloat         lRStart;     /* Start value of ruler */
  gfloat         lREnd;       /* End value of ruler */

#if 0
  g_print("Expose event \n");
#endif

  /* Are we ready to expose something */
  if((pmEGraph->System == NULL)
  || (pmEGraph->PixMap == NULL)
  || (pmEGraph->Init   == FALSE))
    return;
  
  /* Draw the pixmap in the window */
  gdk_draw_pixmap(pmEGraph->DrawArea->window,
		  pmEGraph->DrawArea->style->fg_gc[GTK_WIDGET_STATE(pmEGraph->DrawArea)],
		  pmEGraph->PixMap,
#if 0
		  pmEvent->area.x, pmEvent->area.y,
		  pmEvent->area.x, pmEvent->area.y,
		  pmEvent->area.width, pmEvent->area.height);
#else
                  0, 0,
                  0, 0,
		  pmEGraph->HDrawSize, pmEGraph->VDrawSize);
#endif

  /* Set the ruler's values */
  if(pmEGraph->DrawStartTime != 0)
    {
    /* Are we displaying in seconds */
    if(pmEGraph->Interval >= 1000000)
      {
      lRStart = (gfloat) (pmEGraph->DrawStartTime / 1000000) / 1000;
      lREnd = lRStart + (gfloat) (pmEGraph->Interval / 1000000) / 1000;
      }
    else  /* Microseconds */
      {
      lRStart = (gfloat) fmod(pmEGraph->DrawStartTime, 1000000) / 1000;
      lREnd = (gfloat) fmod(pmEGraph->DrawStartTime + pmEGraph->Interval, 1000000) / 1000;
      }
    if(lREnd < lRStart)
      lREnd += 1000;
    gtk_ruler_set_range(GTK_RULER(pmEGraph->HRuler), lRStart, lREnd, lRStart, lREnd);
    }

  /* Make sure that the scrolled process list is well drawn (if not done, list isn't properly sized !?!?) */
  gtk_widget_queue_resize(pmEGraph->ScrolledList);

  /* Stop the signal from propagating */
  gtk_signal_emit_stop_by_name(GTK_OBJECT(pmWidget), "expose_event");
}

/******************************************************************
 * Function : EGSHConfigureEvent()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGSHConfigureEvent(GtkWidget* pmWidget, GdkEventExpose* pmEvent, eventGraph* pmEGraph)
{
  guint    lOldHDrawSize;     /* Old horizontal draw size */
  guint    lOldVDrawSize;     /* Old vertical draw size */

#if 0
  g_print("Configure event \n");
#endif

  /* Are we ready to expose something */
  if((pmEGraph->System == NULL)
  || (pmEGraph->Init == FALSE))
    return;

  /* Remember old draw sizes */
  lOldHDrawSize = pmEGraph->HDrawSize;
  lOldVDrawSize = pmEGraph->VDrawSize;

  /* Set new draw sizes */
  pmEGraph->HDrawSize = (guint) GTK_WIDGET(pmEGraph->DrawArea)->allocation.width;
  pmEGraph->VDrawSize = (guint) GTK_WIDGET(pmEGraph->DrawArea)->allocation.height;

  /* Do we need more space than is available */
  if(pmEGraph->VDrawSize < pmEGraph->VTrueSize)
     pmEGraph->VDrawSize = pmEGraph->VTrueSize;

  /* Does the horizontal drawing area have a significant size */
#if 0
  if(pmEGraph->HDrawSize < 10)
     return;
#endif

  /* Have the draw sizes changed */
  if((lOldHDrawSize == pmEGraph->HDrawSize)
  && (lOldVDrawSize == pmEGraph->VDrawSize))
    {
    /* Reset the HAdjustment */
    EGIResetHAdjustment(pmEGraph, TRUE);

    /* Get outta here */
    return;
    }

  /* Was there a pixmap already */
  if(pmEGraph->PixMap != NULL)
    {
    /* Free the already existing pixmap */
    gdk_pixmap_unref(pmEGraph->PixMap);
    pmEGraph->PixMap = NULL;
    }

  /* Create new pixmap where we can draw */
  pmEGraph->PixMap = gdk_pixmap_new(pmEGraph->DrawArea->window,
				    pmEGraph->HDrawSize,
				    pmEGraph->VDrawSize,
				    -1);

  /* Set draw area size  (if this isn't done, scrolling down doesn't enable the user to see more of the graph) */
  gtk_widget_set_usize(pmEGraph->DrawArea,
		       -1,
		       pmEGraph->VDrawSize);

  /* Did we have cached icons? */
  if(pmEGraph->KernelTimer.Pixmap != NULL)
    {
    /* Free the already existing icons */
    gdk_pixmap_unref(pmEGraph->KernelTimer.Pixmap);
    gdk_pixmap_unref(pmEGraph->SysCall.Pixmap);
    gdk_pixmap_unref(pmEGraph->Trap.Pixmap);
    gdk_pixmap_unref(pmEGraph->SchedChange.Pixmap);
    gdk_pixmap_unref(pmEGraph->BottomHalf.Pixmap);
    gdk_pixmap_unref(pmEGraph->IOStart.Pixmap);
    gdk_pixmap_unref(pmEGraph->IOEnd.Pixmap);
    gdk_pixmap_unref(pmEGraph->IRQ.Pixmap);

    gdk_bitmap_unref(pmEGraph->KernelTimer.Mask);
    gdk_bitmap_unref(pmEGraph->SysCall.Mask);
    gdk_bitmap_unref(pmEGraph->Trap.Mask);
    gdk_bitmap_unref(pmEGraph->SchedChange.Mask);
    gdk_bitmap_unref(pmEGraph->BottomHalf.Mask);
    gdk_bitmap_unref(pmEGraph->IOStart.Mask);
    gdk_bitmap_unref(pmEGraph->IOEnd.Mask);
    gdk_bitmap_unref(pmEGraph->IRQ.Mask);

    pmEGraph->KernelTimer.Pixmap = NULL;
    pmEGraph->SysCall.Pixmap     = NULL;
    pmEGraph->Trap.Pixmap        = NULL;
    pmEGraph->SchedChange.Pixmap = NULL;
    pmEGraph->BottomHalf.Pixmap  = NULL;
    pmEGraph->IOStart.Pixmap     = NULL;
    pmEGraph->IOEnd.Pixmap       = NULL;
    pmEGraph->IRQ.Pixmap         = NULL;
    }
    
  /* Create cached icons */
  pmEGraph->KernelTimer.Pixmap = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->KernelTimer.Mask,
                                                              NULL,
                                                              xpm_kernel_timer );
  pmEGraph->SysCall.Pixmap     = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->SysCall.Mask,
                                                              NULL,
                                                              xpm_sys_call );
  pmEGraph->Trap.Pixmap        = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->Trap.Mask,
                                                              NULL,
                                                              xpm_trap );
  pmEGraph->SchedChange.Pixmap = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->SchedChange.Mask,
                                                              NULL,
                                                              xpm_sched_change );
  pmEGraph->BottomHalf.Pixmap  = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->BottomHalf.Mask,
                                                              NULL,
                                                              xpm_bottom_half );
  pmEGraph->IOStart.Pixmap     = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->IOStart.Mask,
                                                              NULL,
                                                              xpm_io_start );
  pmEGraph->IOEnd.Pixmap       = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->IOEnd.Mask,
                                                              NULL,
                                                              xpm_io_end );
  pmEGraph->IRQ.Pixmap         = gdk_pixmap_create_from_xpm_d( pmEGraph->DrawArea->window,
                                                              &pmEGraph->IRQ.Mask,
                                                              NULL,
                                                              xpm_irq );

  /* Have the colors been allocated */
  if(pmEGraph->ColorsAllocated == FALSE)
    {
    /* Create the Graphic Context for the Selected item */

    /* Create default drawing contexts */
    pmEGraph->BackgroundGC= EGICreateGC(pmEGraph->PixMap,
					pmEGraph->BlackColor,
					pmEGraph->BlackColor, 
					GDK_LINE_SOLID);
    pmEGraph->HorizonGC   = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->YellowColor,
					pmEGraph->BlackColor, 
					GDK_LINE_SOLID);
    pmEGraph->ProcessGC   = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->GreenColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->Process0GC  = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->RedColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->KernelGC    = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->OrangeColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->TaskGC      = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->RedColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->RTAIGC      = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->RedColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->SysCallGC   = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->BlueColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->TrapGC      = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->GreyColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->InterruptGC = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->WhiteColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->TextGC      = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->TurquoiseColor,
					pmEGraph->BlackColor,
					GDK_LINE_SOLID);
    pmEGraph->SelectedGC  = EGICreateGC(pmEGraph->PixMap,
					pmEGraph->PurpleColor,
					pmEGraph->BlackColor,
					GDK_LINE_ON_OFF_DASH);

    /* We're done allocating colors */
    pmEGraph->ColorsAllocated = TRUE;
    }

  /* Reset HAdjustment settings */
  EGIResetHAdjustment(pmEGraph, TRUE);

  /* Draw the trace */
  EGIDrawTrace(pmEGraph, TRUE);

  /* Stop the signal from propagating */
  gtk_signal_emit_stop_by_name(GTK_OBJECT(pmWidget), "configure_event");
}

/******************************************************************
 * Function : EGSHHScrollChange()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGSHHScrollChange(GtkAdjustment* pmAdjustment, gpointer pmData)
{
  eventGraph*    pEGraph;     /* The event graph */

  /* Get the event graph */
  if((pEGraph = (eventGraph*) pmData) == NULL)
    return;

  /* Are we ready to expose something */
  if((pEGraph->System == NULL)
  || (pEGraph->Init == FALSE))
    return;

  /* Redraw trace */
  EGIDrawTrace(pEGraph, FALSE);

  /* Draw the pixmap in the window */
  gtk_widget_queue_draw(pEGraph->DrawArea);

  /* Stop the signal from being propagated */
  gtk_signal_emit_stop_by_name(GTK_OBJECT(pmAdjustment), "value-changed");
}

/******************************************************************
 * Function : EGSHDeSelectListItem()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note : 
 *    BUGFIX: removes the line on deselection.  Order of signal
 *    handler installation is critical:  the logic in here depends
 *    on a guarantee that it is only called by itself on a pure
 *    deselection, or AFTER the select of a new process.  This
 *    prevents a superfluous redraw from this routine.
 ******************************************************************/
void EGSHDeSelectListItem(GtkWidget* pmListItem, gpointer pmData)
{
  process*	 pSelProc;    /* Selected process */
  eventGraph*    pEGraph;     /* The event graph */

  /* Get the event graph */
  if((pEGraph = (eventGraph*) pmData) == NULL)
    return;

  /* Get the selected process */
  pSelProc = (process *) gtk_object_get_data(GTK_OBJECT(pmListItem),
					     gProcessItemData);

  /* Deselect event */
  if(pSelProc == pEGraph->SelectedProc)
    {
    /* Ensure that no process is selected in this graph */
    pEGraph->SelectedProc = NULL;

    /* Redraw the trace */
    EGIDrawTrace(pEGraph, TRUE);

    /* Make sure that the changes are seen on the draw area */
    gtk_widget_queue_resize(pEGraph->DrawArea);
    }
}

/******************************************************************
 * Function : EGSHSelectListItem()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 *   ROC, Install this signal handler BEFORE deselection.
 ******************************************************************/
void EGSHSelectListItem(GtkWidget* pmListItem, gpointer pmData)
{
  eventGraph*    pEGraph;     /* The event graph */

  /* Get the event graph */
  if((pEGraph = (eventGraph*) pmData) == NULL)
    return;

  /* Get the selected process */
  pEGraph->SelectedProc = (process *) gtk_object_get_data(GTK_OBJECT(pmListItem),
							  gProcessItemData);

  /* Redraw the event graph */
  EGIDrawTrace(pEGraph, TRUE);

  /* Make sure that the changes are seen on the draw area */
  gtk_widget_queue_resize(pEGraph->DrawArea);
}

/**********************************************************************************/
/******************************* Windowing functions ******************************/
/**********************************************************************************/
/******************************************************************
 * Function :
 *     EGForceTraceRedraw()
 * Description :
 *     Forcefully redraw the trace in the trace window.
 * Parameters :
 *     pmEGraph, The event graph who's redraw is to be forced.
 * Return values :
 *     NONE
 * History :
 * Note :
 *     K.Y., 28/03/2001, Initial typing.
 ******************************************************************/
void EGForceTraceRedraw(eventGraph* pmEGraph)
{
  EGIDrawTrace(pmEGraph, TRUE);
}

/******************************************************************
 * Function :
 *     EGMouseOnIcon()
 * Description :
 *     Tests for the presence of the mouse pointer within an eventgraph
 *     icon
 * Parameters :
 *     x, y:   Coordinates of the mouse pointer
 *     coords: eventInfo of the icon to test for
 * Return values :
 *     TRUE if within coordinates, FALSE otherwise
 * History :
 * Note :
 ******************************************************************/
gboolean EGMouseOnIcon(gint x, gint y, eventInfo* coords)
{ 
  return ((x >= coords->x && x <= (coords->x + EGRAPH_ICONS_WIDTH))
	&&(y <= coords->y && y >= (coords->y - EGRAPH_ICONS_HEIGHT)));
}

/******************************************************************
 * Function : EGZoomIn()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGZoomIn(eventGraph* pmEGraph)
{
  /* Change the number of micro-seconds per pixel */
  pmEGraph->USecPerPix = pmEGraph->USecPerPix / EGRAPH_ZOOM_IN_FACTOR;

  /* Is the new number of usec/pix too small */
  if(pmEGraph->USecPerPix < EGRAPH_USEC_PER_PIX_MIN)
    pmEGraph->USecPerPix = EGRAPH_USEC_PER_PIX_MIN;

  /* Set the new true size */
  pmEGraph->HTrueSize = (gulong) (pmEGraph->Duration / (gdouble) pmEGraph->USecPerPix);

  /* Reset the horizontal scrollbar */
  EGIResetHAdjustment(pmEGraph, TRUE);

  /* Redraw the trace */
  EGIDrawTrace(pmEGraph, TRUE);

  /* Update the draw area per se */
  gtk_widget_queue_resize(pmEGraph->DrawArea);
}

/******************************************************************
 * Function : EGZoomOut()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGZoomOut(eventGraph* pmEGraph)
{
  /* Change the number of micro-seconds per pixel */
  pmEGraph->USecPerPix = pmEGraph->USecPerPix * EGRAPH_ZOOM_OUT_FACTOR;

  /* Is the new number of usec/pix too small */
  if(pmEGraph->USecPerPix > EGRAPH_USEC_PER_PIX_MAX)
    pmEGraph->USecPerPix = EGRAPH_USEC_PER_PIX_MAX;

  /* Set the new true size */
  pmEGraph->HTrueSize = (gulong) (pmEGraph->Duration / (gdouble) pmEGraph->USecPerPix);

  /* Reset the horizontal scrollbar */
  EGIResetHAdjustment(pmEGraph, TRUE);

  /* Redraw the trace */
  EGIDrawTrace(pmEGraph, TRUE);

  /* Update the draw area per se */
  gtk_widget_queue_resize(pmEGraph->DrawArea);
}

/******************************************************************
 * Function : EGZoomTimeFrame()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGZoomTimeFrame(eventGraph* pmEGraph, gdouble StartTime, gdouble EndTime)
{
  gdouble Duration; /* The new value for the trace duration */
  gdouble TempSwap; /* Temporary value to swap variables */

  /* Restrict new times to allowed values */
  if(StartTime > EndTime)
    {
    TempSwap  = EndTime;
    EndTime   = StartTime;
    StartTime = TempSwap;
    }
  if(StartTime < pmEGraph->StartTime || StartTime > pmEGraph->StartTime + pmEGraph->Duration) 
    StartTime = pmEGraph->StartTime;
  if(EndTime < pmEGraph->StartTime || EndTime > pmEGraph->StartTime + pmEGraph->Duration)
    EndTime = pmEGraph->StartTime + pmEGraph->Duration;

  /* Calculate the newly desired duration */
  Duration = EndTime - StartTime;

  /* Change the number of micro-seconds per pixel */
  pmEGraph->USecPerPix = Duration / GTK_WIDGET(pmEGraph->DrawArea)->allocation.width;

  /* Is the new number of usec/pix too big */
  if(pmEGraph->USecPerPix > EGRAPH_USEC_PER_PIX_MAX)
    pmEGraph->USecPerPix = EGRAPH_USEC_PER_PIX_MAX;

  /* Is the new number of usec/pix too small */
  if(pmEGraph->USecPerPix < EGRAPH_USEC_PER_PIX_MIN)
    pmEGraph->USecPerPix = EGRAPH_USEC_PER_PIX_MIN;

  /* Set the new true size */
  pmEGraph->HTrueSize = (gulong) (pmEGraph->Duration / (gdouble) pmEGraph->USecPerPix);

  /* Set the new scroll value */
  pmEGraph->LastScrollVal = 
    ( ( (StartTime - pmEGraph->StartTime) / pmEGraph->Duration) * pmEGraph->HTrueSize);

  /* Reset the horizontal scrollbar */
  EGIResetHAdjustment(pmEGraph, FALSE);

  /* Redraw the trace */
  EGIDrawTrace(pmEGraph, TRUE);

  /* Update the draw area per se */
  gtk_widget_queue_resize(pmEGraph->DrawArea);
}

/******************************************************************
 * Function : EGOneClickZoom()
 * Description :
 *   A more directly useable zoom function using an existing routine
 *   driven by a single click on the event graph drawing area.
 *   I want a "click-persistent" zoom: the time point I click should
 *   be at the same relative pixel position in the new zoomed window.
 * Parameters :
 *   pmEGraph, Event graph where zooming occurs.
 *   pmXPosition, The horizontal position where click occured.
 *   pmZoomIn, If "true" then zoom in otherwise zoom out.
 * Return values :
 *   NONE
 * History :
 *   ROC, Initial typing
 * Note :
 *   Existing variables referenced:
 *
 *   pEButton->x		relative x coord of button press in pixels
 *   pEGraph->HDrawSize		horizontal width of drawing area in pixels
 *   pEGraph->DrawStartTime	current viewport left edge in useconds
 *   pEGraph->Interval		current viewport width in useconds
 *   pEGraph->PixPerUSec	always 0
 *   pEGraph->USecPerPix	changes but of dubious use
 ******************************************************************/
void EGOneClickZoom(eventGraph* pmEGraph, gdouble pmXPosition, gboolean pmZoomIn)
{
  gdouble lClickRelPos;        /* Relative position of click position */
  gdouble lNewStartTime;       /* New start drawing time */
  gdouble lNewTimeInterval;    /* New time interval */

  /* Compute relative position of click */
  lClickRelPos = pmXPosition / (float)pmEGraph->HDrawSize;

  /* PixPerUSec is always zero; USecPerPix gets fractional but I'm
     not sure where to stop as zooming in too far has some weird artifacts.
     I can't find a good time to stop zooming out but it doesn't seem to 
     hurt anything to do one or two extra clicks... ROC */

  /* Are we zooming in */
  if(pmZoomIn == TRUE)
    {
    /* Is the new interval too short */
    if(pmEGraph->Interval < 10.0)
      return;	/* arbitrary */

    /* Compute the new interval and start time */
    lNewTimeInterval = pmEGraph->Interval / (float)EGRAPH_ZOOM_IN_FACTOR;
    lNewStartTime = pmEGraph->DrawStartTime + lClickRelPos * lNewTimeInterval;
    }
  else
    {
    /* Is the new interval too long */
    if(pmEGraph->Interval + 10.0 > pmEGraph->Duration)
      return; 

    /* Compute the new interval and start time */
    lNewTimeInterval = pmEGraph->Interval * (float)EGRAPH_ZOOM_OUT_FACTOR;
    lNewStartTime = pmEGraph->DrawStartTime - lClickRelPos * pmEGraph->Interval;
    }

  /* Zoom to the new time frame the new  */
  EGZoomTimeFrame(pmEGraph, lNewStartTime, lNewStartTime + lNewTimeInterval);
}

/******************************************************************
 * Function : EGScrollToEvent()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGScrollToEvent(eventGraph* pmEGraph, event* pmEvent)
{
  gfloat            NewValue;     /* The new value for the adjustment */
  gdouble           EventTime;    /* The time of the event */
  struct timeval    lTime;        /* Event time */

  /* Is the Event graph passed valid? */
  if(pmEGraph == NULL)
    {
    g_print("ScrollToEvent called without graph passed! \n");
    exit(1);
    }

  /* Get event time */
  DBEventTime(pmEGraph->EventDB, pmEvent, &lTime);

  /* Get the time to scroll to */
  EventTime = DBGetTimeInDouble(lTime);

  /* Place the adjustment at the desired position */
  NewValue = ( ( (EventTime - pmEGraph->StartTime) / pmEGraph->Duration) * 
               (GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper - GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower)
             ) + GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower
               - (GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_size / 2);

  /* Confine the adjustment to reasonable values */
  if(NewValue < GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower)
    NewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower;
  if(NewValue > GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper)
    NewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper;

  /* Set the new adjustment */
  gtk_adjustment_set_value(GTK_ADJUSTMENT(pmEGraph->HAdjustment), NewValue);
}

/******************************************************************
 * Function : EGScrollLeft()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGScrollLeft(eventGraph* pmEGraph)
{
  gfloat            lNewValue;     /* The new value for the adjustment */

  /* Set the new value to scroll left */
  lNewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value;
  lNewValue -= GTK_ADJUSTMENT(pmEGraph->HAdjustment)->step_increment;

  /* Confine the adjustment to reasonable values */
  if(lNewValue < GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower)
    lNewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower;

  /* Update the adjustment's value */
  gtk_adjustment_set_value(GTK_ADJUSTMENT(pmEGraph->HAdjustment), lNewValue);
}

/******************************************************************
 * Function : EGScrollRight()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGScrollRight(eventGraph* pmEGraph)
{
  gfloat            lNewValue;     /* The new value for the adjustment */
  gfloat            lLimit;        /* Adjustment limit */

  /* Set the new value to scroll right */
  lNewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value;
  lNewValue += GTK_ADJUSTMENT(pmEGraph->HAdjustment)->step_increment;

  /* Set the limit */
  lLimit = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper
     - GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_increment;

  /* Confine the adjustment to reasonable values */
  if(lNewValue > lLimit)
    lNewValue = lLimit;

  /* Update the adjustment's value */
  gtk_adjustment_set_value(GTK_ADJUSTMENT(pmEGraph->HAdjustment), lNewValue);
}

/******************************************************************
 * Function : EGScrollUp()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGScrollUp(eventGraph* pmEGraph)
{
  gfloat          lNewValue;     /* The new value for the adjustment */
  GtkAdjustment*  pAdjustment;   /* The vertical adjustment of the scrolled list */

  /* Get the vertical adjustment of the scrolled process list */
  pAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(pmEGraph->ScrolledList));

  /* Set the new value to scroll up */
  lNewValue = pAdjustment->value;
  lNewValue -= pAdjustment->step_increment;

  /* Confine the adjustment to reasonable values */
  if(lNewValue < pAdjustment->lower)
    lNewValue = pAdjustment->lower;

  /* Update the adjustment's value */
  gtk_adjustment_set_value(pAdjustment, lNewValue);

  /* Make sure the list is drawn correctly */
  gtk_widget_queue_resize(pmEGraph->ScrolledList);
}

/******************************************************************
 * Function : EGScrollDown()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGScrollDown(eventGraph* pmEGraph)
{
  gfloat          lCompareValue;
  gfloat          lNewValue;     /* The new value for the adjustment */
  GtkAdjustment*  pAdjustment;   /* The vertical adjustment of the scrolled list */

  /* Get the vertical adjustment of the scrolled process list */
  pAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(pmEGraph->ScrolledList));

  /* Set the new value to scroll down */
  lNewValue = pAdjustment->value;
  lNewValue += pAdjustment->step_increment;

  /* Set the maximum value the new value should have */
  lCompareValue = pAdjustment->upper - 2 * pAdjustment->page_increment;

#if 0 /* DEBUG */
  g_print("Kernel height : %d \n", EGIFindKernelHeight(pmEGraph));
  g_print("Adjustment upper : %d \n", (guint)(pAdjustment->upper));
  g_print("Scrolled list height : %d \n", (guint) GTK_WIDGET(pmEGraph->ScrolledList)->allocation.height);
  g_print("Page increment : %d \n", (guint)(pAdjustment->page_increment));
#endif

  /* Confine the adjustment to reasonable values */
  if(lNewValue > lCompareValue)
    lNewValue = lCompareValue;

#if 0 /* DEBUG */
  g_print("New Value : %d \n", (guint) lNewValue);
#endif

  /* Update the adjustment's value */
  gtk_adjustment_set_value(pAdjustment, lNewValue);

  /* Make sure the list is drawn correctly */
  gtk_widget_queue_resize(pmEGraph->ScrolledList);
}

/******************************************************************
 * Function : EGPageUp()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGPageUp(eventGraph* pmEGraph)
{
  gfloat            lNewValue;     /* The new value for the adjustment */

  /* Set the new value to scroll page up */
  lNewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value;
  lNewValue -= GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_increment;

  /* Confine the adjustment to reasonable values */
  if(lNewValue < GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower)
    lNewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower;

  /* Update the adjustment's value */
  gtk_adjustment_set_value(GTK_ADJUSTMENT(pmEGraph->HAdjustment), lNewValue);
}
     
/******************************************************************
 * Function : EGPageDown()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGPageDown(eventGraph* pmEGraph)
{
  gfloat            lNewValue;     /* The new value for the adjustment */
  gfloat            lLimit;        /* Adjustment limit */

  /* Set the new value to scroll page down */
  lNewValue = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value;
  lNewValue += GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_increment;

  /* Set the limit */
  lLimit = GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper
     - GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_increment;

  /* Confine the adjustment to reasonable values */
  if(lNewValue > lLimit)
    lNewValue = lLimit;

  /* Update the adjustment's value */
  gtk_adjustment_set_value(GTK_ADJUSTMENT(pmEGraph->HAdjustment), lNewValue);
}

/******************************************************************
 * Function : EGSetHorizonView()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGSetHorizonView(eventGraph* pmEventGraph)
{
  /* Is the button active or not (I know this sounds like silly code, but it's "safer" this way) */
  if(pmEventGraph->ShowHorizon == FALSE)
    pmEventGraph->ShowHorizon = FALSE;
  else
    pmEventGraph->ShowHorizon = TRUE;

  /* Redraw the event graph */
  EGIDrawTrace(pmEventGraph, TRUE);

  /* Make sure that the changes are seen on the draw area */
  gtk_widget_queue_resize(pmEventGraph->DrawArea);
}

/******************************************************************
 * Function : EGConnectSignals()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGConnectSignals(eventGraph* pmEventGraph)
{
  /* Connect to the window events */
  gtk_signal_connect(GTK_OBJECT(pmEventGraph->DrawArea),
		     "expose_event",
		     GTK_SIGNAL_FUNC(EGSHExposeEvent),
		     pmEventGraph);
  gtk_signal_connect(GTK_OBJECT(pmEventGraph->DrawArea),
		     "configure_event",
		     GTK_SIGNAL_FUNC(EGSHConfigureEvent),
		     pmEventGraph);

  /* Enable draw area to cause various events to take place */
  gtk_widget_set_events(pmEventGraph->DrawArea, GDK_POINTER_MOTION_MASK    /* motion_notify_event */
			                      | GDK_BUTTON_PRESS_MASK);    /* button-press-event */

  /* Connect horizontal ruler */
  gtk_signal_connect_object(GTK_OBJECT(pmEventGraph->DrawArea),
			    "motion_notify_event",
			    GTK_SIGNAL_FUNC(GTK_WIDGET_CLASS(GTK_OBJECT(pmEventGraph->HRuler)->klass)->motion_notify_event),
			    GTK_OBJECT(pmEventGraph->HRuler));

  /* Connect to the horizontal adjustment */
  gtk_signal_connect(GTK_OBJECT(pmEventGraph->HAdjustment),
		     "value-changed",
		     GTK_SIGNAL_FUNC(EGSHHScrollChange),
		     pmEventGraph);
}

/******************************************************************
 * Function : EGShowEventGraph()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGShowEventGraph(eventGraph* pmEventGraph)
{
  /* Is there anything to be shown */
  if(!pmEventGraph)
    {
    g_print("pmEventGraph is null in EGShowEventGraph! \n");
    exit(1);
    };

  /* Show it all */
  gtk_widget_show(pmEventGraph->HPanned);
  gtk_widget_show(pmEventGraph->ProcVBox);
  gtk_widget_show(pmEventGraph->DummyBox);
  gtk_widget_show(pmEventGraph->DrawVBox);
  gtk_widget_show(pmEventGraph->ScrolledList);
  gtk_widget_show(pmEventGraph->List);
  gtk_widget_show(pmEventGraph->HRuler);
  gtk_widget_show(pmEventGraph->ScrolledDraw);
  gtk_widget_show(pmEventGraph->DrawArea);
  gtk_widget_show(pmEventGraph->HScrollBar);
}

/******************************************************************
 * Function : EGCreateEventGraph()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
eventGraph* EGCreateEventGraph(gpointer pmMainWindow)
{
  eventGraph*      pEGraph;       /* The event graph */
  mainWindow*      pMainWindow;   /* Main window to which event graph belongs */
  GtkRequisition   lRequisition;  /* Used to obtain widget sizes*/

  /* Allocate space for new even graph */
  pEGraph = (eventGraph*) g_malloc(sizeof(eventGraph));

  /* Get main window */
  pMainWindow = (mainWindow*) pmMainWindow;

  /* Set state */
  pEGraph->Init            = FALSE;
  pEGraph->ColorsAllocated = FALSE;
  pEGraph->ShowHorizon     = FALSE;

  /* Set data */
  pEGraph->EventDB      = NULL;
  pEGraph->System       = NULL;
  pEGraph->MainWindow   = pmMainWindow;
  pEGraph->EventIcons   = NULL;
  pEGraph->SelectedProc = NULL;

  /* Initialize icon cache */
  pEGraph->KernelTimer.Pixmap = NULL;
  pEGraph->SysCall.Pixmap     = NULL;
  pEGraph->Trap.Pixmap        = NULL;
  pEGraph->SchedChange.Pixmap = NULL;
  pEGraph->BottomHalf.Pixmap  = NULL;
  pEGraph->IOStart.Pixmap     = NULL;
  pEGraph->IOEnd.Pixmap       = NULL;
  pEGraph->IRQ.Pixmap         = NULL;

  /* Allocate main elements */
  pEGraph->HPanned = gtk_hpaned_new();
  gtk_box_pack_start(GTK_BOX(pMainWindow->GTHBox), pEGraph->HPanned, TRUE, TRUE, 0);

  /* Create boxes and put them in hpanned */
  pEGraph->ProcVBox = gtk_vbox_new(FALSE, 0);
  pEGraph->DummyBox = gtk_vbox_new(FALSE, 0);
  pEGraph->DrawVBox = gtk_vbox_new(FALSE, 0);
  gtk_paned_add1(GTK_PANED(pEGraph->HPanned), pEGraph->ProcVBox);
  gtk_paned_add2(GTK_PANED(pEGraph->HPanned), pEGraph->DrawVBox);

  /* Create scrolled list and place it in the hpanned widget */
  pEGraph->ScrolledList = gtk_scrolled_window_new(NULL, NULL);
  pEGraph->List         = gtk_list_new();
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pEGraph->ScrolledList),
				 GTK_POLICY_ALWAYS,
				 GTK_POLICY_ALWAYS);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(pEGraph->ScrolledList),
					pEGraph->List);
  gtk_widget_set_usize(GTK_WIDGET(pEGraph->ScrolledList), 150, -1);

  /* Create horizontal ruler */
  pEGraph->HRuler = gtk_hruler_new();

  /* Set dummy box size */
  gtk_widget_size_request(GTK_WIDGET(pEGraph->HRuler), &lRequisition);
  gtk_widget_set_usize(GTK_WIDGET(pEGraph->DummyBox), 0, lRequisition.height);
  /*  gtk_box_set_spacing(GTK_BOX(pEGraph->ProcVBox), lRequisition.height);*/ /* This can work too */

  /* Put scrolled list in VBox window */
  gtk_box_pack_start(GTK_BOX(pEGraph->ProcVBox), pEGraph->DummyBox, FALSE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(pEGraph->ProcVBox), pEGraph->ScrolledList, TRUE, TRUE, 0);

  /* Create drawing area elements and place them in the hpanned widget */
  pEGraph->DrawArea     = gtk_drawing_area_new();
  pEGraph->ScrolledDraw = gtk_scrolled_window_new
                          (NULL,
			   gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(pEGraph->ScrolledList)));
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pEGraph->ScrolledDraw),
				 GTK_POLICY_NEVER,
				 GTK_POLICY_NEVER);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(pEGraph->ScrolledDraw),
					pEGraph->DrawArea);

  /* Create horizontal scrollbar */
  pEGraph->HAdjustment = gtk_adjustment_new(0,0,0,0,0,0);
  pEGraph->HScrollBar  = gtk_hscrollbar_new(GTK_ADJUSTMENT(pEGraph->HAdjustment));

  /* Put ruler and draw area in hbox */
  gtk_box_pack_start(GTK_BOX(pEGraph->DrawVBox), pEGraph->HRuler, FALSE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(pEGraph->DrawVBox), pEGraph->ScrolledDraw, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(pEGraph->DrawVBox), pEGraph->HScrollBar, FALSE, TRUE, 0);

  /* Set pixmap */
  pEGraph->PixMap = NULL;

  /* Create the colors */
  pEGraph->RedColor       = EGICreateColor(0xffff, 0, 0);  /* (red, green, blue) */
  pEGraph->GreenColor     = EGICreateColor(0, 0xffff, 0);
  pEGraph->BlueColor      = EGICreateColor(0, 0, 0xffff);
  pEGraph->GreyColor      = EGICreateColor(0x8000, 0x8000, 0x8000);
  pEGraph->GreenColor     = EGICreateColor(0, 0xffff, 0);
  pEGraph->BlackColor     = EGICreateColor(0, 0, 0);
  pEGraph->WhiteColor     = EGICreateColor(0xffff, 0xffff, 0xffff);
  pEGraph->OrangeColor    = EGICreateColor(0xffff, 0x8000, 0);
  pEGraph->YellowColor    = EGICreateColor(0xffff, 0xffff, 0);
  pEGraph->TurquoiseColor = EGICreateColor(0, 0xffff, 0xffff);
  pEGraph->PurpleColor    = EGICreateColor(0xffff, 0, 0xffff);

  /* Load font to draw text */
  pEGraph->TextFont = gdk_font_load("-*-courier-medium-r-*-*-12-*-*-*-m-*-*-*");

  /* Initialize draw start time */
  pEGraph->DrawStartTime = 0;

  /* Return the new widget to the caller */
  return pEGraph;
}

/******************************************************************
 * Function : EGDisplayEventTrace()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 *    ROC, ???: This generates superfluous draw(s) at startup
 ******************************************************************/
void EGDisplayEventTrace(eventGraph* pmEventGraph, systemInfo* pmSystem, db* pmEventDB)
{
  /* Miscallaneous variables */
  gint             lNbProc;                 /* Number of processes in the system */
  gint             lNbTask;                 /* Number of RTAI tasks in system */
  gchar            lLabelString[270];       /* Label string */

  /* Process variables */
  process*         pProc;                   /* Generic process pointer */
  process*         pProcFirst;              /* First process in list */

#if SUPP_RTAI
  /* RTAI tasks variables */
  RTAItask*        pTask;                   /* Generic RTAI task pointer */
  RTAItask*        pTaskFirst;              /* First task in list */
#endif /* SUPP_RTAI */

  /* Time and size variables */
  struct timeval   lTime;                   /* The duration of the trace */

  /* Gtk widgets */
  GtkWidget*       pListItem;               /* Item to add to list */
  GtkRequisition   lRequisition;            /* Used to obtain widget sizes*/

  /* Set data */
  pmEventGraph->EventDB = pmEventDB;
  pmEventGraph->System  = pmSystem;

  /* Can we draw anything */
  if((ltt_test_bit(TRACE_SYSCALL_ENTRY, &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_SYSCALL_EXIT,  &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_TRAP_ENTRY,    &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_TRAP_EXIT,     &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_IRQ_ENTRY,     &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_IRQ_EXIT,      &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_SCHEDCHANGE,   &(pmEventGraph->EventDB->EventMask))
    &&ltt_test_bit(TRACE_SYSCALL_ENTRY, &(pmEventGraph->EventDB->DetailsMask))
    &&ltt_test_bit(TRACE_SYSCALL_EXIT,  &(pmEventGraph->EventDB->DetailsMask))
    &&ltt_test_bit(TRACE_TRAP_ENTRY,    &(pmEventGraph->EventDB->DetailsMask))
    &&ltt_test_bit(TRACE_TRAP_EXIT,     &(pmEventGraph->EventDB->DetailsMask))
    &&ltt_test_bit(TRACE_IRQ_ENTRY,     &(pmEventGraph->EventDB->DetailsMask))
    &&ltt_test_bit(TRACE_IRQ_EXIT,      &(pmEventGraph->EventDB->DetailsMask))
    &&ltt_test_bit(TRACE_SCHEDCHANGE,   &(pmEventGraph->EventDB->DetailsMask))) != 1)
    {
    WDStatusBarDisplay(pmEventGraph->MainWindow,
		       "Insufficient trace information to draw graph");
    return;
    }

  /* Do we have multiple CPUs */
  if(pmEventGraph->EventDB->LogCPUID == TRUE)
    WDStatusBarDisplay(pmEventGraph->MainWindow,
		       "Displaying only the information for CPU #0");

#if 0
  g_print("Start Time : (%ld, %ld) \n", pmEventDB->StartTime.tv_sec, pmEventDB->StartTime.tv_usec);
  g_print("First Event : (%ld, %ld) \n", pmEventDB->Events->Time.tv_sec, pmEventDB->Events->Time.tv_usec);
  g_print("Total Nb Events : %d \n", pmEventDB->Nb);
#endif

  /* Find trace start time */
  pmEventGraph->StartTime = DBGetTimeInDouble(pmEventDB->DrawStartTime);
  
  /* Find trace duration */
  DBTimeSub(lTime, pmEventDB->EndTime, pmEventDB->DrawStartTime);

  /* Find out the duration in microseconds */
  pmEventGraph->Duration = DBGetTimeInDouble(lTime);

  /* Initialize drawable time interval */
  pmEventGraph->Interval         = 0;
  pmEventGraph->NbIntervalEvents = 0;

  /* Add a bogus item in order to properly draw topmost item */
  pListItem  = gtk_list_item_new_with_label("");
  gtk_container_add(GTK_CONTAINER(pmEventGraph->List), pListItem);
  gtk_widget_show(pListItem);
  gtk_signal_connect (GTK_OBJECT(pListItem), 
		      "select",
		      GTK_SIGNAL_FUNC(EGSHSelectListItem),
		      pmEventGraph);

  /* Display the processes in the process list */
  lNbProc = 1;
  lNbTask = 0;
  pProcFirst = NULL;
  for(pProc = pmSystem->Processes; pProc != NULL; pProc = pProc->Next)
    {
    /* Keep count on the number of processes */
    lNbProc++;

    /* Create list item */
    if(pProc->Command != NULL)
      sprintf(lLabelString, "%s (%d)", pProc->Command, pProc->PID);
    else
      {
      /* Is this the 0 process */
      if(pProc->PID == 0)
	{
	pProcFirst = pProc;
	continue;
	}
      else
	sprintf(lLabelString, "Unnamed child (%d)", pProc->PID);
      }
    pListItem  = gtk_list_item_new_with_label(lLabelString);

    /* Put pointer to process in label */
    gtk_object_set_data(GTK_OBJECT(pListItem), gProcessItemData, pProc);

    /* Attach Signal handlers */
    gtk_signal_connect (GTK_OBJECT(pListItem), 
			"select",
			GTK_SIGNAL_FUNC(EGSHSelectListItem),
			pmEventGraph);
    gtk_signal_connect (GTK_OBJECT(pListItem), 
			"deselect",
			GTK_SIGNAL_FUNC(EGSHDeSelectListItem),
			pmEventGraph);

    /* Show the label */
    gtk_widget_show(pListItem);
    
    /* Append item into list */
    gtk_container_add(GTK_CONTAINER(pmEventGraph->List), pListItem);

    /* Set item position in list */
    pProc->ListPos = gtk_list_child_position(GTK_LIST(pmEventGraph->List), pListItem);
    }

  /* Put first process */
  if(pProcFirst != NULL)
    {
    /* Create new label */
    sprintf(lLabelString, "Kernel (%d)", pProcFirst->PID);
    pListItem  = gtk_list_item_new_with_label(lLabelString);
    gtk_signal_connect (GTK_OBJECT(pListItem), 
			"select",
			GTK_SIGNAL_FUNC(EGSHSelectListItem),
			pmEventGraph);

    /* Put pointer to process in label */
    gtk_object_set_data(GTK_OBJECT(pListItem), gProcessItemData, pProcFirst);

    /* Show the label */
    gtk_widget_show(pListItem);

    /* Append item into list */
    gtk_container_add(GTK_CONTAINER(pmEventGraph->List), pListItem);

    /* Set item position in list */
    pProcFirst->ListPos = gtk_list_child_position(GTK_LIST(pmEventGraph->List), pListItem);
    }

#if SUPP_RTAI
  /* Does this trace contain RTAI information */
  if(pmEventDB->SystemType == TRACE_SYS_TYPE_RTAI_LINUX)
    {
    /* Display the tasks in the tasks list */
    pTaskFirst = NULL;
    for(pTask = RTAI_SYSTEM(pmSystem->SystemSpec)->Tasks;
	pTask != NULL;
	pTask = pTask->Next)
      {
      /* Keep count on the number of processes and RTAI tasks */
      lNbProc++;
      lNbTask++;

      /* Create list item */
      if(pTask->TID != 0)
	sprintf(lLabelString, "RT: %d", pTask->TID);
      else
	{
	pTaskFirst = pTask;
	continue;
	}
      pListItem  = gtk_list_item_new_with_label(lLabelString);

      /* Put pointer to task in label */
      gtk_object_set_data(GTK_OBJECT(pListItem), gProcessItemData, pTask);

      /* Attach Signal handler */
      gtk_signal_connect (GTK_OBJECT(pListItem), 
			  "select",
			  GTK_SIGNAL_FUNC(EGSHSelectListItem),
			  pmEventGraph);

      /* Show the label */
      gtk_widget_show(pListItem);
    
      /* Append item into list */
      gtk_container_add(GTK_CONTAINER(pmEventGraph->List), pListItem);

      /* Set item position in list */
      pTask->ListPos = gtk_list_child_position(GTK_LIST(pmEventGraph->List), pListItem);
      }

    /* Put first task */
    if(pTaskFirst != NULL)
      {
      /* Create new label */
      sprintf(lLabelString, "RTAI");
      pListItem  = gtk_list_item_new_with_label(lLabelString);
      gtk_signal_connect (GTK_OBJECT(pListItem), 
			  "select",
			  GTK_SIGNAL_FUNC(EGSHSelectListItem),
			  pmEventGraph);

      /* Put pointer to task in label */
      gtk_object_set_data(GTK_OBJECT(pListItem), gProcessItemData, pTaskFirst);

      /* Show the label */
      gtk_widget_show(pListItem);

      /* Append item into list */
      gtk_container_add(GTK_CONTAINER(pmEventGraph->List), pListItem);

      /* Set item position in list */
      pTaskFirst->ListPos = gtk_list_child_position(GTK_LIST(pmEventGraph->List), pListItem);
      }
    }
#endif /* SUPP_RTAI */

  /* Set number of processes and tasks */
  pmEventGraph->NbProc = lNbProc;
  pmEventGraph->NbTask = lNbTask;

  /* Get the height of a label */
  gtk_widget_size_request(GTK_WIDGET(pListItem), &lRequisition);

  /* Set the label height */
  pmEventGraph->LabelHeight = lRequisition.height;

  /* Set the number of microseconds per pixel and pixels for start */
  pmEventGraph->USecPerPix = EGRAPH_USEC_PER_PIX;
  pmEventGraph->PixPerUSec = 0;
  pmEventGraph->PixStart = EGRAPH_PIX_START;

  /* Set size of image as if all events where displayed in the same pixmap */
  if(pmEventGraph->USecPerPix > 0)
    pmEventGraph->HTrueSize = ((gint) pmEventGraph->Duration) / pmEventGraph->USecPerPix;
  else
    pmEventGraph->HTrueSize = ((gint) pmEventGraph->Duration)  * (gint)  (1 / pmEventGraph->USecPerPix);
  pmEventGraph->VTrueSize = pmEventGraph->LabelHeight * pmEventGraph->NbProc;

  /* Initialize draw sizes */
  pmEventGraph->HDrawSize = 0;
  pmEventGraph->VDrawSize = 0;

  /* Set start draw time and event */
  memset(&(pmEventGraph->DrawStartEvent), 0 , sizeof(pmEventGraph->DrawStartEvent));
  memset(&(pmEventGraph->DrawEndEvent), 0 , sizeof(pmEventGraph->DrawEndEvent));
  pmEventGraph->DrawStartTime  = pmEventGraph->StartTime;

  /* Initialize scrollbar position */
  pmEventGraph->LastScrollVal = 0;
  
  /* We have finished initializing the graph */
  pmEventGraph->Init = TRUE;
}

/******************************************************************
 * Function : EGClearEventGraph()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 *    ROC, THIS IS NOT CALLED DURING NORMAL STARTUP!!!!!
 ******************************************************************/
void EGClearEventGraph(eventGraph* pmEGraph)
{
  /* Reset widget state */
  pmEGraph->Init = FALSE;
  pmEGraph->SelectedProc = NULL;

  /* Reset data */
  pmEGraph->EventDB = NULL;
  pmEGraph->System  = 0;

  /* Reset the draw variables */
  pmEGraph->NbProc      = 0;
  pmEGraph->LabelHeight = 0;
  pmEGraph->PixStart    = 0;
  pmEGraph->PixPerUSec  = 0;
  pmEGraph->USecPerPix  = 0;
  pmEGraph->HTrueSize   = 0;
  pmEGraph->VTrueSize   = 0;
  pmEGraph->HDrawSize   = 0;
  pmEGraph->VDrawSize   = 0;
  pmEGraph->StartTime   = 0;
  pmEGraph->Duration    = 0;
  pmEGraph->Interval    = 0;

  /* Reset scroll information */
  memset(&(pmEGraph->DrawStartEvent), 0 , sizeof(pmEGraph->DrawStartEvent));
  memset(&(pmEGraph->DrawEndEvent), 0 , sizeof(pmEGraph->DrawEndEvent));
  pmEGraph->DrawStartTime  = 0;
  pmEGraph->LastScrollVal  = 0;

  /* Empty event icon coordinates list */
  if(pmEGraph->EventIcons)
    {
    /* Free all allocated coordinate items */
    g_slist_foreach(pmEGraph->EventIcons, (GFunc) g_free, NULL);

    /* Free the GList list items */
    g_slist_free(pmEGraph->EventIcons);

    /* Reset list pointer */
    pmEGraph->EventIcons = NULL;
    }

  /* Remove the items in the process list */
  gtk_list_clear_items(GTK_LIST(pmEGraph->List), 0, -1);

  /* Reset the draw area size */
  gtk_widget_set_usize(pmEGraph->DrawArea,
		       0,
		       0);

  /* Reset the ruler */
  gtk_ruler_set_range(GTK_RULER(pmEGraph->HRuler), 0, 0, 0, 0);

  /* Reset the horizontal scrollbar */
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->lower = 0;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->upper = 0;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->value = 0 ;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_size = 0;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->step_increment = 0;
  GTK_ADJUSTMENT(pmEGraph->HAdjustment)->page_increment = 0;
  gtk_adjustment_changed(GTK_ADJUSTMENT(pmEGraph->HAdjustment));

  /* Make sure changes to the draw area are done */
  /* IF THIS ISN'T DONE, THE DRAWING SOMEWHAT SOMETIMES DOESN'T GET ERASED ??? */
  gtk_widget_hide(pmEGraph->ScrolledDraw);
  gtk_widget_show(pmEGraph->ScrolledDraw);
}

/******************************************************************
 * Function : EGDestroyEventGraph()
 * Description :
 * Parameters :
 * Return values :
 * History :
 * Note :
 ******************************************************************/
void EGDestroyEventGraph(eventGraph** pmEGraph)
{

  /* Has the graph already been freed? */
  if(!pmEGraph) return;
  if(!*pmEGraph) return;

  /* Clear the event graph */
  EGClearEventGraph(*pmEGraph);

  /* Was there a pixmap already */
  if((*pmEGraph)->PixMap != NULL)
    {
    /* Free the already existing pixmap */
    gdk_pixmap_unref((*pmEGraph)->PixMap);
    (*pmEGraph)->PixMap = NULL;
    }

  /* Did we have cached icons? */
  if((*pmEGraph)->KernelTimer.Pixmap != NULL)
    {
    /* Free the already existing icons */
    gdk_pixmap_unref((*pmEGraph)->KernelTimer.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->SysCall.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->Trap.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->SchedChange.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->BottomHalf.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->IOStart.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->IOEnd.Pixmap);
    gdk_pixmap_unref((*pmEGraph)->IRQ.Pixmap);

    gdk_bitmap_unref((*pmEGraph)->KernelTimer.Mask);
    gdk_bitmap_unref((*pmEGraph)->SysCall.Mask);
    gdk_bitmap_unref((*pmEGraph)->Trap.Mask);
    gdk_bitmap_unref((*pmEGraph)->SchedChange.Mask);
    gdk_bitmap_unref((*pmEGraph)->BottomHalf.Mask);
    gdk_bitmap_unref((*pmEGraph)->IOStart.Mask);
    gdk_bitmap_unref((*pmEGraph)->IOEnd.Mask);
    gdk_bitmap_unref((*pmEGraph)->IRQ.Mask);

    (*pmEGraph)->KernelTimer.Pixmap = NULL;
    (*pmEGraph)->SysCall.Pixmap     = NULL;
    (*pmEGraph)->Trap.Pixmap        = NULL;
    (*pmEGraph)->SchedChange.Pixmap = NULL;
    (*pmEGraph)->BottomHalf.Pixmap  = NULL;
    (*pmEGraph)->IOStart.Pixmap     = NULL;
    (*pmEGraph)->IOEnd.Pixmap       = NULL;
    (*pmEGraph)->IRQ.Pixmap         = NULL;
    }

  /* Free allocated GDK structures */

  /* Fonts */
  gdk_font_unref((*pmEGraph)->TextFont);

  /* Colors */
  g_free((*pmEGraph)->RedColor);
  g_free((*pmEGraph)->BlueColor); 
  g_free((*pmEGraph)->GreyColor);
  g_free((*pmEGraph)->GreenColor); 
  g_free((*pmEGraph)->BlackColor); 
  g_free((*pmEGraph)->WhiteColor);
  g_free((*pmEGraph)->OrangeColor);
  g_free((*pmEGraph)->YellowColor);
  g_free((*pmEGraph)->TurquoiseColor);
  g_free((*pmEGraph)->PurpleColor);

  /* The GCs, if any were allocated */
  if((*pmEGraph)->ColorsAllocated == TRUE)
    {
    gdk_gc_destroy((*pmEGraph)->BackgroundGC);
    gdk_gc_destroy((*pmEGraph)->HorizonGC);
    gdk_gc_destroy((*pmEGraph)->ProcessGC);
    gdk_gc_destroy((*pmEGraph)->Process0GC);
    gdk_gc_destroy((*pmEGraph)->KernelGC);
    gdk_gc_destroy((*pmEGraph)->SelectedGC);
    gdk_gc_destroy((*pmEGraph)->SysCallGC);
    gdk_gc_destroy((*pmEGraph)->TrapGC);
    gdk_gc_destroy((*pmEGraph)->InterruptGC);
    gdk_gc_destroy((*pmEGraph)->TextGC);
    }

  /* destroy the structure */
  g_free(*pmEGraph);

  /* reset the structure pointer */
  *pmEGraph = NULL;
}
