/*
* File name: mdispgtkview.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\LinuxSpecific\MdispGtk
*             
*/
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2015.
// All Rights Reserved

#include <gtk/gtk.h>
#include <mil.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <cairo-xlib.h>
#include "mdispgtkview.h"
#include "MdispGtk.h"
#include "childframe.h"

#define IMAGE_FILE   M_IMAGE_PATH MIL_TEXT("BaboonRGB.mim")

// Do not grab display inside gtk libs
// this prevent Xserver freeze when annotation is enabled
int XGrabServer(Display *dpy)   { return 1;}
int XUngrabServer(Display *dpy) { return 1;}

MIL_INT MFTYPE MouseFct(MIL_INT /*HookType*/, MIL_ID EventID, void* UserDataPtr)
   {
   MdispGtkView* pCurrentView = (MdispGtkView *)UserDataPtr;
   
   if(pCurrentView)
      {
      MOUSEPOSITION MousePosition;
      MdispGetHookInfo(EventID, M_MOUSE_POSITION_X,         &MousePosition.m_DisplayPositionX);
      MdispGetHookInfo(EventID, M_MOUSE_POSITION_Y,         &MousePosition.m_DisplayPositionY);
      MdispGetHookInfo(EventID, M_MOUSE_POSITION_BUFFER_X,  &MousePosition.m_BufferPositionX);
      MdispGetHookInfo(EventID, M_MOUSE_POSITION_BUFFER_Y,  &MousePosition.m_BufferPositionY);

      pCurrentView->SetMousePosition(MousePosition);
      pCurrentView->UpdateStatusBarWithMousePosition();
      }
   return 0;
   }

MIL_INT MFTYPE GraphicListModifiedHookFct(MIL_INT HookType, MIL_ID EventID, void* UserDataPtr)
   {
   MdispGtkView *pCurrentView = (MdispGtkView *)UserDataPtr;

   if(pCurrentView)
      {
      MIL_INT State = M_NULL;
      MgraGetHookInfo(EventID, M_INTERACTIVE_GRAPHIC_STATE, &State);

      if((State != M_STATE_WAITING_FOR_CREATION) && (State != M_STATE_BEING_CREATED))
         {
         pCurrentView->ResetPrimitiveCreation();
         }
      }
   return 0;
   }

MdispGtkView::MdispGtkView(ChildFrame* cf)
   {
   m_cf                         = cf;
   m_Modified                   = false;
   m_window                     = cf->DrawingArea();
   m_MilOverlayImage            = M_NULL;   // Overlay image buffer identifier
   m_MilDisplay                 = M_NULL;   // Display identifier.
   m_MilGraphContext            = M_NULL;
   m_MilGraphList               = M_NULL;

   static int viewNumber = 0;
   m_filename = g_strdup_printf("Image%d.mim",++viewNumber);


   m_isWindowed                 = true;
   m_isExclusive                = false;
   m_isOverlayEnabled           = false;    // Overlay state
   m_isOverlayInitialized       = false;
   m_isScaleDisplayEnabled      = false;
   m_isX11AnnotationsEnabled    = false;
   m_isGraphicsAnnotationsEnabled = false;
   m_currentViewMode            = M_TRANSPARENT;
   m_currentShiftValue          = M_NULL;
   m_isInAsynchronousMode          = false;
   m_currentCompressionType        = M_NULL;
   m_currentAsynchronousFrameRate  = M_INFINITE;
   m_currentQFactor                = M_DEFAULT;
   m_currentRestrictCursor         = M_ENABLE;
   m_PrimitiveInCreation           = M_NULL;
   m_FrameTimeOutTag            = g_timeout_add(100,MdispGtkView::timerEvent,this);

   m_imageSizeX                = DEFAULTSIZEX;
   m_imageSizeY                = DEFAULTSIZEY;
   m_currentZoomFactorX        = 1.0;
   m_currentZoomFactorY        = 1.0;

   UpdateContentSize();

   }

MdispGtkView::~MdispGtkView()
   {
   // Halt the grab, deselected the display, free the display and the image buffer
   // only if MbufAlloc was successful
   if (m_MilImage)
      {
      // Make sure display is deselected and grab is halt
      RemoveFromDisplay();

      // Free image buffer [CALL TO MIL]
      MbufFree(m_MilImage);
      }

   // remove timer
   g_source_remove(m_FrameTimeOutTag);
   
   }

MdispGtkApp* MdispGtkView::dispGtkApp()
   {
   void *UserData = g_object_get_data(G_OBJECT(m_window),"App");
   return (MdispGtkApp *) UserData;
   }

void MdispGtkView::GrabStart()
   {
   // TODO: Add your command handler code here
  
  /////////////////////////////////////////////////////////////////////////
  // MIL: Write code that will be executed on a grab start
  /////////////////////////////////////////////////////////////////////////

   // If there is a grab in a view, halt the grab before starting a new one
   if(((MdispGtkApp*)dispGtkApp())->m_isGrabStarted)
      ((MdispGtkApp*)dispGtkApp())->m_pGrabView->GrabStop();

   // Start a continuous grab in this view
   MdigGrabContinuous(((MdispGtkApp*)dispGtkApp())->m_MilDigitizer, m_MilImage);

   // Update the variable GrabIsStarted
   ((MdispGtkApp*)dispGtkApp())->m_isGrabStarted = true;

   // GrabInViewPtr is now a pointer to m_pGrabView view
   ((MdispGtkApp*)dispGtkApp())->m_pGrabView = this;

   // Document has been modified
   m_Modified = true;

  /////////////////////////////////////////////////////////////////////////  
  // MIL: Write code that will be executed on a grab start
  /////////////////////////////////////////////////////////////////////////

   }
void MdispGtkView::GrabStop()
   {
   // TODO: Add your command handler code here
 
   /////////////////////////////////////////////////////////////////////////
  // MIL: Write code that will be executed on a grab stop 
  /////////////////////////////////////////////////////////////////////////
   // Halt the grab
   MdigHalt(((MdispGtkApp*)dispGtkApp())->m_MilDigitizer);
   ((MdispGtkApp*)dispGtkApp())->m_isGrabStarted = false;

   /////////////////////////////////////////////////////////////////////////
  // MIL: Write code that will be executed on a grab stop 
  /////////////////////////////////////////////////////////////////////////

   }

void MdispGtkView::Overlay(bool on)
   {
   // Enable overlay
   if (on && !m_isOverlayEnabled)
      {
      MdispControl(m_MilDisplay, M_OVERLAY, M_ENABLE);

      //If overlay buffer as not been initialized yet, do it now.
      if(!m_isOverlayInitialized)
         InitializeOverlay();

      m_isOverlayEnabled = true;
      }

   // Disable overlay
   else if (!on && m_isOverlayEnabled)
      {
      // Disable the overlay display. [CALL TO MIL]
      MdispControl(m_MilDisplay, M_OVERLAY, M_DISABLE);

      m_isOverlayInitialized = false;
      m_isOverlayEnabled     = false;
      }

   /////////////////////////////////////////////////////////////////////////
   // MIL: Write code that will be executed when 'add overlay' is selected
   /////////////////////////////////////////////////////////////////////////
   }



void MdispGtkView::Initialize()
   {
   // Allocate a display [CALL TO MIL]
   MdispAlloc(((MdispGtkApp*)dispGtkApp())->m_MilSystem, M_DEFAULT, "M_DEFAULT", M_DEFAULT, &m_MilDisplay);

   if(m_MilDisplay)
      {
      MIL_INT DisplayType = MdispInquire(m_MilDisplay, M_DISPLAY_TYPE, M_NULL);
      
      // Check display type [CALL TO MIL]
      if((DisplayType&(M_WINDOWED|M_EXCLUSIVE)) !=M_WINDOWED)
         m_isWindowed = false;

      if(DisplayType&(M_EXCLUSIVE))
         m_isExclusive = true;

      // Initially set view mode to default
      ChangeViewMode(M_DEFAULT);

      if(IsNetworkedSystem())
         {
         // Check compression type [CALL TO MIL]
         MdispInquire(m_MilDisplay, M_COMPRESSION_TYPE, &m_currentCompressionType);
         
         // Check asynchronous mode [CALL TO MIL]
         m_isInAsynchronousMode = (MdispInquire(m_MilDisplay, M_ASYNC_UPDATE, M_NULL) == M_ENABLE);

         // Check asynchronous frame rate [CALL TO MIL]
         MdispInquire(m_MilDisplay, M_UPDATE_RATE_MAX, &m_currentAsynchronousFrameRate);
         
         // Check Q factor [CALL TO MIL]
         MdispInquire(m_MilDisplay, M_Q_FACTOR, &m_currentQFactor);
         }

      if(m_isExclusive)
         {
         MdispInquire(m_MilDisplay, M_RESTRICT_CURSOR,    &m_currentRestrictCursor);
         }

      //Select the buffer from it's display object and given window [CALL TO MIL]
      MdispSelectWindow(m_MilDisplay, m_MilImage, m_isWindowed?GDK_WINDOW_XID(m_window->window):0);

      // Allow panning and zooming with the mouse [CALL TO MIL]
      MdispControl(m_MilDisplay, M_MOUSE_USE, M_ENABLE);

      // Allow mouse cursor handling [CALL TO MIL]
      MdispControl(m_MilDisplay, M_MOUSE_CURSOR_CHANGE, M_ENABLE);

      // Hook a function to mouse-movement event, to update cursor position in status bar.
      MdispHookFunction(m_MilDisplay, M_MOUSE_MOVE, MouseFct, (void*)this);
      }

   /////////////////////////////////////////////////////////////////////////
   // MIL: Code that will be executed when a view is first attached to the document
   /////////////////////////////////////////////////////////////////////////
   }

void MdispGtkView::RemoveFromDisplay()
   {
   //Halt grab if in process in THIS view
   if ((((MdispGtkApp*)dispGtkApp())->m_pGrabView == this) &&
       ((MdispGtkApp*)dispGtkApp())->m_isGrabStarted)
      {
      //Ask the digitizer to halt the grab [CALL TO MIL]
      MdigHalt(((MdispGtkApp*)dispGtkApp())->m_MilDigitizer);

      ((MdispGtkApp*)dispGtkApp())->m_isGrabStarted = false;
      }

   if (m_MilImage && m_MilDisplay)
      {
      //Deselect the buffer from it's display object and given window [CALL TO MIL]
      MdispDeselect(m_MilDisplay,m_MilImage);

      // Hook from mouse-movement event.
      MdispHookFunction(m_MilDisplay, M_MOUSE_MOVE+M_UNHOOK, MouseFct, (void*)this);

      //Free the display [CALL TO MIL]
      if(m_MilGraphList)
         {
         MgraFree(m_MilGraphList);
         m_MilGraphList = M_NULL;
         }
      if(m_MilGraphContext)
         {
         MgraFree(m_MilGraphContext);
         m_MilGraphContext = M_NULL;
         }
      
      //Free the display [CALL TO MIL]
      MdispFree(m_MilDisplay);
      m_MilDisplay = M_NULL;
      }
   }
bool MdispGtkView::newDoc()
   {
    // Set buffer attributes
   if(((MdispGtkApp*)dispGtkApp())->m_numberOfDigitizer)
      {
      m_bufferAttributes=M_IMAGE+M_DISP+M_GRAB+M_PROC;
      m_imageSizeX = ((MdispGtkApp*)dispGtkApp())->m_digitizerSizeX;
      m_imageSizeY = ((MdispGtkApp*)dispGtkApp())->m_digitizerSizeY;
      m_NbBands    = ((MdispGtkApp*)dispGtkApp())->m_digitizerNbBands;

      // Allocate a buffer [CALL TO MIL]
      MbufAllocColor(((MdispGtkApp*)dispGtkApp())->m_MilSystem,
                     m_NbBands,
                     m_imageSizeX,
                     m_imageSizeY,
                     8+M_UNSIGNED,
                     m_bufferAttributes,
                     &m_MilImage);

      // Clear the buffer [CALL TO MIL] 
      MbufClear(m_MilImage,M_COLOR_BLACK);
      }
   else
      {
      //Import image in buffer [CALL TO MIL]
      MbufImport(IMAGE_FILE,M_DEFAULT,M_RESTORE,((MdispGtkApp*)dispGtkApp())->m_MilSystem,&m_MilImage);

      // Set SizeX and SizeY variable to the size of the buffer [CALL TO MIL]
      if (m_MilImage) 
         {
         m_imageSizeX   = MbufInquire(m_MilImage, M_SIZE_X, M_NULL);
         m_imageSizeY   = MbufInquire(m_MilImage, M_SIZE_Y, M_NULL);
         m_NbBands      = MbufInquire(m_MilImage, M_SIZE_BAND, M_NULL);
         }
      }

   UpdateContentSize();

   // If not able to allocate a buffer, do not create a new document
   if(!m_MilImage)
      return false;

   Initialize();

   return true;
   }

bool MdispGtkView::load(const char *filename)
   {
   //Import image in buffer [CALL TO MIL]
   MbufImport(filename,M_DEFAULT,M_RESTORE,((MdispGtkApp*)dispGtkApp())->m_MilSystem,&m_MilImage);

   // Set SizeX and SizeY variable to the size of the buffer [CALL TO MIL]
   if (m_MilImage)
      {
      Initialize();
      m_imageSizeX = MbufInquire(m_MilImage,M_SIZE_X,M_NULL);
      m_imageSizeY = MbufInquire(m_MilImage,M_SIZE_Y,M_NULL);
      m_filename = g_strdup(g_path_get_basename(filename));
      UpdateContentSize();
      return true;
      }
   else
      {
      return false;
      }
   }

bool MdispGtkView::save(const char *filename)
   {
   gboolean SaveStatus;
   gchar * TempPath;
   long FileFormat = M_MIL;
   gchar *tmp;
   
   // Get extension for file format determination
   TempPath = g_ascii_strup(filename,-1);
   //Set the file format to M_MIL when the filepath extension is ".MIM"
   if (g_str_has_suffix(TempPath,".MIM"))
      FileFormat = M_MIL;
   //Set the file format to M_TIFF when the filepath extension is ".TIF"
   else if (g_str_has_suffix(TempPath,".TIF"))
      FileFormat = M_TIFF;
   //Set the file format to M_BMP when the filepath extension is ".BMP"
   else if (g_str_has_suffix(TempPath,".BMP"))
      FileFormat = M_BMP;
   //Set the file format to M_JPEG_LOSSY when the filepath extension is ".JPG"
   else if (g_str_has_suffix(TempPath,".JPG"))
      FileFormat = M_JPEG_LOSSY;
   //Set the file format to M_JPEG2000_LOSSLESS when the filepath extension is ".JP2"
   else if (g_str_has_suffix(TempPath,".JP2"))
      FileFormat = M_JPEG2000_LOSSLESS;
   //Set the file format to M_RAW when the filepath extension is ".RAW"
   else if (g_str_has_suffix(TempPath,".RAW"))
      FileFormat = M_RAW;
   //Set the file format to M_PNG when the filepath extension is ".PNG"
   else if (g_str_has_suffix(TempPath,".PNG"))
      FileFormat = M_PNG;
   
   // Halt the grab if the current view has it [CALL TO MIL]
   if((((MdispGtkApp*)dispGtkApp())->m_pGrabView == this) &&
      (((MdispGtkApp*)dispGtkApp())->m_isGrabStarted == true))
      MdigHalt(((MdispGtkApp*)dispGtkApp())->m_MilDigitizer);
   
   // Save the current buffer [CALL TO MIL]
   tmp = g_strdup(filename); 
   MbufExport(tmp, FileFormat,m_MilImage);
   g_free(tmp);
   
   // Verify if save operation was successful [CALL TO MIL]
   SaveStatus = (MappGetError(M_DEFAULT, M_CURRENT,M_NULL) == M_NULL_ERROR);

   // Document has been saved
   if (!((((MdispGtkApp*)dispGtkApp())->m_pGrabView == this) &&
         (((MdispGtkApp*)dispGtkApp())->m_isGrabStarted == true)))
      m_Modified = false;

   // Restart the grab if the current view had it [CALL TO MIL]
   if((((MdispGtkApp*)dispGtkApp())->m_pGrabView == this) &&
      (((MdispGtkApp*)dispGtkApp())->m_isGrabStarted == true))
      MdigGrabContinuous(((MdispGtkApp*)dispGtkApp())->m_MilDigitizer, m_MilImage);

   return SaveStatus;

   }

   
void MdispGtkView::ChangeViewMode(long ViewMode,long ShiftValue)
   {
   if(m_MilDisplay)
      {
      //Apply view mode on display [CALL TO MIL]
      MdispControl(m_MilDisplay, M_VIEW_MODE, ViewMode);

      if(ViewMode == M_BIT_SHIFT)
         MdispControl(m_MilDisplay, M_VIEW_BIT_SHIFT, ShiftValue);

      //Check if control worked correctly before considering it as successful [CALL TO MIL]
      if(MdispInquire(m_MilDisplay, M_VIEW_MODE,M_NULL)==ViewMode)
         {
         m_currentViewMode   = ViewMode;
         m_currentShiftValue = ShiftValue;
         }
      }
   }

void MdispGtkView::ChangeCompressionType(MIL_INT CompressionType)
   {
   if(m_MilDisplay)
      {
      // Apply compression type to display [CALL TO MIL]
      MdispControl(m_MilDisplay, M_COMPRESSION_TYPE, CompressionType);
   
      // Check if control worked correctly before considering it successful [CALL TO MIL]
      if(MdispInquire(m_MilDisplay, M_COMPRESSION_TYPE, M_NULL) == CompressionType)
         {
         m_currentCompressionType = CompressionType;
         }
      }
   }

void MdispGtkView::ChangeAsynchronousMode(bool Enabled, MIL_INT FrameRate)
   {
   if(Enabled && (FrameRate != m_currentAsynchronousFrameRate))
      {
      if(m_MilDisplay)
         {
         // Apply asynchronous frame rate to display [CALL TO MIL]
         MdispControl(m_MilDisplay, M_UPDATE_RATE_MAX, FrameRate);
      
         // Check if control worked correctly before considering it successful [CALL TO MIL]
         if(MdispInquire(m_MilDisplay, M_UPDATE_RATE_MAX, M_NULL) == FrameRate)
            {
            m_currentAsynchronousFrameRate = FrameRate;
            }
         }
      }

   if((Enabled && !m_isInAsynchronousMode) ||
      (!Enabled && m_isInAsynchronousMode))
      {
      if(m_MilDisplay)
         {
         // Apply asynchronous update to display [CALL TO MIL]
         MdispControl(m_MilDisplay, M_ASYNC_UPDATE, (Enabled ? M_ENABLE : M_DISABLE));
      
         // Check if control worked correctly before considering it successful [CALL TO MIL]
         if(MdispInquire(m_MilDisplay, M_ASYNC_UPDATE, M_NULL) == (Enabled ? M_ENABLE : M_DISABLE))
            {
            m_isInAsynchronousMode = Enabled;
            }
         }
      }
   }

void MdispGtkView::ChangeQFactor(MIL_INT QFactor)
   {
   if(m_MilDisplay)
      {
      // Apply Q factor to display [CALL TO MIL]
      MdispControl(m_MilDisplay, M_Q_FACTOR, QFactor);
   
      // Check if control worked correctly before considering it successful [CALL TO MIL]
      if(MdispInquire(m_MilDisplay, M_Q_FACTOR, M_NULL) == QFactor)
         {
         m_currentQFactor = QFactor;
         }
      }
   }

bool MdispGtkView::IsNetworkedSystem()
   {
   bool NetworkedSystem = false;
   MIL_ID SystemId = ((MdispGtkApp*)dispGtkApp())->m_MilSystem;

   // Check if system is networked (DistributedMIL) [CALL TO MIL]
   if(SystemId)
      NetworkedSystem = (MsysInquire(SystemId, M_LOCATION, M_NULL) == M_REMOTE);

   return NetworkedSystem;
   }

void MdispGtkView::ZoomIn()
   {
   /////////////////////////////////////////////////////////////////////////
   // MIL: Write code that will be executed when 'Zoom In' button or menu is clicked
   /////////////////////////////////////////////////////////////////////////
   if(m_MilDisplay)
      {
      //Calculate new zoom factors
      MIL_DOUBLE ZoomX = 1.0, ZoomY = 1.0;
      MdispInquire(m_MilDisplay, M_ZOOM_FACTOR_X, &ZoomX);
      MdispInquire(m_MilDisplay, M_ZOOM_FACTOR_Y, &ZoomY);
      
      if((ZoomX <= 8.0) && (ZoomY<=8.0))
         {
         ZoomX*=2.0;
         ZoomY*=2.0;
         }
      
      //Perform zooming with MIL (using MdispZoom)
      Zoom(ZoomX, ZoomY);
      }
   }

void MdispGtkView::ZoomOut()
   {
   /////////////////////////////////////////////////////////////////////////
   // MIL: Write code that will be executed when 'Zoom Out' button or menu is clicked
   /////////////////////////////////////////////////////////////////////////
   if(m_MilDisplay)
      {
      //Calculate new zoom factors
      MIL_DOUBLE ZoomX = 1.0, ZoomY = 1.0;
      MdispInquire(m_MilDisplay, M_ZOOM_FACTOR_X, &ZoomX);
      MdispInquire(m_MilDisplay, M_ZOOM_FACTOR_Y, &ZoomY);
      
      if((ZoomX >= 0.125) && (ZoomY>=0.125))
         {
         ZoomX/=2.0;
         ZoomY/=2.0;
         }

      //Perform zooming with MIL (using MdispZoom)
      Zoom(ZoomX, ZoomY);
      }
   }

void MdispGtkView::NoZoom()
   {
   /////////////////////////////////////////////////////////////////////////
   // MIL: Write code that will be executed when 'No Zoom' button or menu is clicked
   /////////////////////////////////////////////////////////////////////////
   if(m_MilDisplay)
      {
      //Perform zooming with MIL
      Zoom(1.0, 1.0);
      MdispPan(m_MilDisplay, M_NULL, M_NULL);
      }
   }

void MdispGtkView::Zoom( MIL_DOUBLE ZoomFactorToApplyX, MIL_DOUBLE ZoomFactorToApplyY)
   {
   /////////////////////////////////////////////////////////////////////////
   // MIL: Apply current zoom factor on display 
   /////////////////////////////////////////////////////////////////////////

   //Apply zoom  [CALL TO MIL]
   MdispZoom(m_MilDisplay, ZoomFactorToApplyX, ZoomFactorToApplyY);
   m_currentZoomFactorX = ZoomFactorToApplyX;
   m_currentZoomFactorY = ZoomFactorToApplyY;
   }

void MdispGtkView::ScaleDisplay( bool on )
   {
   if(m_MilDisplay)
      {
      if(!on)
         {
         if(m_window->window)
            {
            gdk_window_clear_area_e(GDK_WINDOW(m_window->window), 0, 0, m_window->allocation.width, m_window->allocation.height);
            }
         }
      
      //Using MIL, enable/disable Scale Display Mode [CALL TO MIL]
      MdispControl(m_MilDisplay, M_SCALE_DISPLAY, on ? M_ENABLE : M_DISABLE);

      m_isScaleDisplayEnabled = on;

      }
   }

void MdispGtkView::OnGraRectangle()
   {
   if(m_MilDisplay)
      {
      if(m_isGraphicsAnnotationsEnabled)
         {
         MgraColor(m_MilGraphContext, M_COLOR_WHITE);
         MgraInteractive(m_MilGraphContext, m_MilGraphList, M_GRAPHIC_TYPE_RECT, M_DEFAULT, M_DEFAULT);
         m_PrimitiveInCreation = M_GRAPHIC_TYPE_RECT;
         }
      }
   }
void MdispGtkView::OnGraCircle()
   {
   if(m_MilDisplay)
      {
      if(m_isGraphicsAnnotationsEnabled)
         {
         MgraColor(m_MilGraphContext, M_COLOR_YELLOW);
         MgraInteractive(m_MilGraphContext, m_MilGraphList, M_GRAPHIC_TYPE_ARC, M_DEFAULT, M_DEFAULT);
         m_PrimitiveInCreation = M_GRAPHIC_TYPE_ARC;
         }
      }
   }
void MdispGtkView::OnGraPolygon()
   {
   if(m_MilDisplay)
      {
      if(m_isGraphicsAnnotationsEnabled)
         {
         MgraColor(m_MilGraphContext, M_COLOR_MAGENTA);
         MgraInteractive(m_MilGraphContext, m_MilGraphList, M_GRAPHIC_TYPE_POLYGON, M_DEFAULT, M_DEFAULT);
         m_PrimitiveInCreation = M_GRAPHIC_TYPE_POLYGON;
         }
      }
   }
void MdispGtkView::OnGraChooseColor()
   {
   if(m_MilDisplay && m_MilGraphList)
      {
      GdkColor   gColor;
      GtkWidget* dialog =  gtk_color_selection_dialog_new("Select a color");
      gint result = gtk_dialog_run (GTK_DIALOG (dialog));
      if(result == GTK_RESPONSE_OK)
         {
         GtkWidget *Selection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog));
         gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(Selection), &gColor);
         MIL_INT NewColor = M_RGB888(gColor.red>>8, gColor.green>>8, gColor.blue>>8);
         MgraControlList(m_MilGraphList, M_ALL_SELECTED, M_DEFAULT, M_COLOR, (MIL_INT)NewColor);
         MgraControlList(m_MilGraphList, M_ALL, M_DEFAULT, M_GRAPHIC_SELECTED, M_FALSE);
         }
      gtk_widget_destroy (dialog);
      }
   }

void MdispGtkView::OnGraFill()
   {
   if(m_MilDisplay && m_MilGraphList)
      {
      MgraControlList(m_MilGraphList, M_ALL_SELECTED, M_DEFAULT, M_FILLED, M_TRUE);
      MgraControlList(m_MilGraphList, M_ALL, M_DEFAULT, M_GRAPHIC_SELECTED, M_FALSE);
      }
   }

void MdispGtkView::X11Annotations( bool on )
   {
   m_isX11AnnotationsEnabled = on;

   if(on)
      {
      // The connection to the X display must be given to Mil so it
      // can update the window annotation.
      MdispControl(m_MilDisplay,M_WINDOW_ANNOTATIONS,M_PTR_TO_DOUBLE(GDK_WINDOW_XDISPLAY(m_window->window)));
      }
   else
      {
      MdispControl(m_MilDisplay,M_WINDOW_ANNOTATIONS,M_NULL);
      }

   if(on)
      {
      gtk_widget_queue_draw_area(m_window,0,0,1,1);
      }
   // make sur the window is mapped
   else if(m_window->window)
      {
      XEvent ev;
      ev.type = Expose;
      ev.xexpose.window = GDK_WINDOW_XID(m_window->window);
      ev.xexpose.x = 0;
      ev.xexpose.y = 0;
      ev.xexpose.width  = m_window->allocation.width;
      ev.xexpose.height = m_window->allocation.height;
      XSendEvent(GDK_WINDOW_XDISPLAY(m_window->window),GDK_WINDOW_XID(m_window->window), true, ExposureMask, &ev);
      }
  
   }

void MdispGtkView::GraphicsAnnotations( bool on )
   {
   if(m_MilDisplay)
      {
      m_isGraphicsAnnotationsEnabled = on;
      
      if(m_isGraphicsAnnotationsEnabled)
         {
         if(!m_MilGraphContext && !m_MilGraphList)
            {
            MIL_INT BufSizeX  = 0, BufSizeY = 0;
            MIL_INT Offset    = 15;

            MgraAlloc(((MdispGtkApp*)dispGtkApp())->m_MilSystem, &m_MilGraphContext);
            MgraAllocList(((MdispGtkApp*)dispGtkApp())->m_MilSystem, M_DEFAULT, &m_MilGraphList);
            MdispControl(m_MilDisplay, M_ASSOCIATED_GRAPHIC_LIST_ID, m_MilGraphList);



            MdispControl(m_MilDisplay, M_UPDATE_GRAPHIC_LIST, M_DISABLE);
            MbufInquire(m_MilImage, M_SIZE_X, &BufSizeX);
            MbufInquire(m_MilImage, M_SIZE_Y, &BufSizeY);

            MgraClear(m_MilGraphContext, m_MilGraphList);

            MgraColor(m_MilGraphContext, M_COLOR_LIGHT_BLUE);
            MgraRect(m_MilGraphContext, m_MilGraphList, Offset, Offset, BufSizeX - Offset, BufSizeY - Offset);

            MgraColor(m_MilGraphContext, M_COLOR_GREEN);
            MgraControl(m_MilGraphContext, M_BACKGROUND_MODE, M_TRANSPARENT);
            MgraControl(m_MilGraphContext, M_TEXT_ALIGN_HORIZONTAL, M_CENTER);
            MgraControl(m_MilGraphContext, M_TEXT_ALIGN_VERTICAL, M_CENTER);
            MgraControl(m_MilGraphContext, M_FONT_SIZE, 24);
            MgraFont(m_MilGraphContext, MIL_FONT_NAME(M_FONT_DEFAULT_TTF));
            MgraText(m_MilGraphContext, m_MilGraphList, BufSizeX/2, Offset, MIL_TEXT("Interactive Graphic Annotations"));
            MdispControl(m_MilDisplay, M_UPDATE_GRAPHIC_LIST, M_ENABLE);
            MdispControl(m_MilDisplay, M_GRAPHIC_LIST_INTERACTIVE, M_ENABLE);
            
            MgraHookFunction(m_MilGraphList, M_INTERACTIVE_GRAPHIC_STATE_MODIFIED, GraphicListModifiedHookFct, (void*)this);
            }
         }
      else
         {
         MgraHookFunction(m_MilGraphList, M_INTERACTIVE_GRAPHIC_STATE_MODIFIED+M_UNHOOK, GraphicListModifiedHookFct, (void*)this);
         MdispControl(m_MilDisplay, M_ASSOCIATED_GRAPHIC_LIST_ID, M_NULL);
         
         if(m_MilGraphList)
            {
            MgraFree(m_MilGraphList);
            m_MilGraphList = M_NULL;
            }
         if(m_MilGraphContext)
            {
            MgraFree(m_MilGraphContext);
            m_MilGraphContext = M_NULL;
            }
         }
      }
   }

void MdispGtkView::InitializeOverlay()
   {
   MIL_TEXT_CHAR chText[80]; 

   // Initialize overlay if not already done
   if ((!m_isOverlayInitialized) && (m_MilDisplay))
      {
      //Only do it on a valid windowed display [CALL TO MIL]
      if (m_MilImage && m_MilDisplay )
         {
         // Prepare overlay buffer //
         ////////////////////////////

         // Enable display overlay annotations.
         MdispControl(m_MilDisplay, M_OVERLAY, M_ENABLE);

         // Inquire the Overlay buffer associated with the displayed buffer [CALL TO MIL]
         MdispInquire(m_MilDisplay, M_OVERLAY_ID, &m_MilOverlayImage);

         // Clear the overlay to transparent.
         MdispControl(m_MilDisplay, M_OVERLAY_CLEAR, M_DEFAULT);
         
         // Disable the overlay display update to accelerate annotations.
         MdispControl(m_MilDisplay, M_OVERLAY_SHOW, M_DISABLE);


         // Draw MIL monochrome overlay annotation *
         //*****************************************

         // Inquire MilOverlayImage size x and y [CALL TO MIL]
         long imageWidth  = MbufInquire(m_MilOverlayImage,M_SIZE_X,M_NULL);
         long imageHeight = MbufInquire(m_MilOverlayImage,M_SIZE_Y,M_NULL);

         // Set graphic text to transparent background. [CALL TO MIL]
         MgraControl(M_DEFAULT, M_BACKGROUND_MODE, M_TRANSPARENT);

         // Set drawing color to white. [CALL TO MIL]
         MgraColor(M_DEFAULT, M_COLOR_WHITE);

         // Print a string in the overlay image buffer. [CALL TO MIL]
         MgraText(M_DEFAULT, m_MilOverlayImage, imageWidth/9, imageHeight/5,    " -------------------- ");
         MgraText(M_DEFAULT, m_MilOverlayImage, imageWidth/9, imageHeight/5+25, " - MIL Overlay Text - ");
         MgraText(M_DEFAULT, m_MilOverlayImage, imageWidth/9, imageHeight/5+50, " -------------------- ");

         // Print a green string in the green component overlay image buffer. [CALL TO MIL]
         MgraColor(M_DEFAULT, M_COLOR_GREEN);
         MgraText(M_DEFAULT, m_MilOverlayImage, imageWidth*11/18, imageHeight/5,    " -------------------- ");
         MgraText(M_DEFAULT, m_MilOverlayImage, imageWidth*11/18, imageHeight/5+25, " - MIL Overlay Text - ");
         MgraText(M_DEFAULT, m_MilOverlayImage, imageWidth*11/18, imageHeight/5+50, " -------------------- ");

         // Draw GDI color overlay annotation *
         //************************************

         // Disable hook to MIL error because control might not be supported
         MappControl(M_DEFAULT, M_ERROR_HOOKS, M_DISABLE);

         // Create a device context to draw in the overlay buffer with GDI.  [CALL TO MIL]
         MbufControl(m_MilOverlayImage, M_XPIXMAP_ALLOC, M_COMPENSATION_ENABLE);

         // Reenable hook to MIL error
         MappControl(M_DEFAULT, M_ERROR_HOOKS, M_ENABLE);

         // Retrieve the XPIXMAP of the overlay [CALL TO MIL]
         Pixmap XPixmap = (Pixmap)MbufInquire(m_MilOverlayImage, M_XPIXMAP_HANDLE, M_NULL);

         /* convert it to gdkpixmap */
         GdkPixmap *gdkpixmap = NULL;
         if(XPixmap != None)
            gdkpixmap = gdk_pixmap_foreign_new(XPixmap);
            
         if(gdkpixmap)
            {
            GdkPoint Hor[2];
            GdkPoint Ver[2];
            GdkColor color[3];               
            GdkFont *font = NULL;
            font = gdk_font_load ("-misc-*-*-r-*-*-*-140-*-*-*-*-*-1");
            int i;
            
            /* get graphic context from pixmap*/
            GdkGC *gc = gdk_gc_new(gdkpixmap);
            
            /* allocate colors */
            gdk_color_parse("blue",&color[0]);
            gdk_color_parse("red",&color[1]);
            gdk_color_parse("yellow",&color[2]);
            for(i=0;i<3;i++)
               gdk_color_alloc(gdk_colormap_get_system(), &color[i]);
            
            /* set the foreground to our color */
            gdk_gc_set_foreground(gc, &color[0]);
            // Draw a blue cross in the overlay buffer.
            Hor[0].x = 0;
            Hor[0].y = imageHeight/2;
            Hor[1].x = imageWidth;
            Hor[1].y = imageHeight/2;
            gdk_draw_lines(gdkpixmap,gc,Hor,2);
            
            Ver[0].x = imageWidth/2;
            Ver[0].y = 0;
            Ver[1].x = imageWidth/2;
            Ver[1].y = imageHeight;
            gdk_draw_lines(gdkpixmap,gc,Ver,2);
            

            // Write Red text in the overlay buffer. 
            MosStrcpy(chText, 80, "X Overlay Text "); 
            gdk_gc_set_foreground(gc, &color[1]);
            gdk_draw_string(gdkpixmap, 
                            font,
                            gc, 
                            imageWidth*3/18,
                            imageHeight*4/6,
                            chText);
            
            // Write Yellow text in the overlay buffer. 
            gdk_gc_set_foreground(gc, &color[2]);
            gdk_draw_string(gdkpixmap, 
                            font,
                            gc, 
                            imageWidth*12/18,
                            imageHeight*4/6,
                            chText);
            
            /* flush */
            gdk_display_flush(gdk_display_get_default());
            
            /* Free graphic context.*/
            g_object_unref(gc);

            // Delete created Pixmap.  [CALL TO MIL]
            MbufControl(m_MilOverlayImage, M_XPIXMAP_FREE, M_DEFAULT);
            
            // Signal MIL that the overlay buffer was modified. [CALL TO MIL]
            MbufControl(m_MilOverlayImage, M_MODIFIED, M_DEFAULT);
            }

         // Now that overlay buffer is correctly prepared, we can show it [CALL TO MIL]
         MdispControl(m_MilDisplay, M_OVERLAY_SHOW, M_ENABLE);

         // Overlay is now initialized
         m_isOverlayInitialized = true;
         }
      }
   }


gboolean MdispGtkView::timerEvent(gpointer user_data)
   {
   MdispGtkView* pCurrentView = (MdispGtkView *) user_data;
   if(pCurrentView && pCurrentView->m_MilDisplay)
      {

      MIL_DOUBLE CurrentFrameRate = M_NULL;
      MdispInquire(pCurrentView->m_MilDisplay, M_UPDATE_RATE, &CurrentFrameRate);
      g_signal_emit(G_OBJECT(pCurrentView->m_cf->Window()), pCurrentView->m_cf->frameRateChangedSignal(), 0, CurrentFrameRate);

      MIL_DOUBLE ZoomX =1.0, ZoomY = 1.0;
      MdispInquire(pCurrentView->m_MilDisplay, M_ZOOM_FACTOR_X, &ZoomX);
      MdispInquire(pCurrentView->m_MilDisplay, M_ZOOM_FACTOR_Y, &ZoomY);
      g_signal_emit(G_OBJECT(pCurrentView->m_cf->Window()), pCurrentView->m_cf->zoomFactorChangedSignal(), 0, 
                    ZoomX, 
                    ZoomY);

      g_signal_emit(G_OBJECT(pCurrentView->m_cf->Window()), pCurrentView->m_cf->mousePositionChangedSignal(), 0, 
                    pCurrentView->m_LastMousePosition.m_DisplayPositionX,
                    pCurrentView->m_LastMousePosition.m_DisplayPositionY,
                    pCurrentView->m_LastMousePosition.m_BufferPositionX,
                    pCurrentView->m_LastMousePosition.m_BufferPositionY);

      // Reset mouse position
      //m_LastMousePosition.Set(M_INVALID, M_INVALID, M_INVALID, M_INVALID);
    
      }
   return true;
   }

void MdispGtkView::RestrictCursor(bool on)
   {
   /////////////////////////////////////////////////////////////////////////
   // MIL: Write code that will be executed when 'Restrict Cursor' menu item is clicked
   /////////////////////////////////////////////////////////////////////////

   if(m_MilDisplay)
      {
      MdispControl(m_MilDisplay, M_RESTRICT_CURSOR,on?M_ENABLE:M_DISABLE);

      // Check if control worked correctly before considering it successful [CALL TO MIL]
      MdispInquire(m_MilDisplay, M_RESTRICT_CURSOR, &m_currentRestrictCursor);

      }
   }

void MdispGtkView::UpdateContentSize()
   {
   int sizeX, sizeY;
   if(m_MilDisplay)
      {
      MdispInquire(m_MilDisplay, M_ZOOM_FACTOR_X, &m_currentZoomFactorX);
      MdispInquire(m_MilDisplay, M_ZOOM_FACTOR_Y, &m_currentZoomFactorY);
      }

   sizeX = int( m_imageSizeX * m_currentZoomFactorX );
   sizeY = int( m_imageSizeY * m_currentZoomFactorY );

   // tell this child frame to update the window
   if(m_cf)
      m_cf->UpdateContentSize(sizeX, sizeY);
   }

void MdispGtkView::UpdateStatusBarWithMousePosition()
   {
   }

void MdispGtkView::Paint()
   {
   if(!m_MilDisplay)
      {
      cairo_t *cr;
      cr = gdk_cairo_create(m_window->window);
      cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
      cairo_set_font_size (cr, 12.0);
      cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
      cairo_move_to (cr, (m_imageSizeX/2),10.0);
      cairo_show_text (cr, "Display Allocation failed");
      cairo_destroy(cr);
      }
   else if(m_isWindowed)
      {
      if(m_isX11AnnotationsEnabled)
         {
         cairo_t *cr;
         cr = gdk_cairo_create(m_window->window);
         cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
         cairo_set_font_size (cr, 12.0);
         cairo_set_source_rgb (cr, 1.0, 0.0, 1.0);
         cairo_move_to (cr, (m_isScaleDisplayEnabled)?(m_window->allocation.width/2):(m_imageSizeX/2),10.0);
         cairo_show_text (cr, "Window Annotation");
         cairo_destroy(cr);
         }
      }
   else{
      cairo_t *cr;
      cr = gdk_cairo_create(m_window->window);
      cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
      cairo_set_font_size (cr, 12.0);
      cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
      cairo_move_to (cr, (m_imageSizeX/2),10.0);
      cairo_show_text (cr, "Image displayed on external screen");
      cairo_destroy(cr);
      }
   }