/*
* File name: mdispgtkview.cpp
* Location: See Matrox Example Launcher in the MIL Control Center
* 
*/
//
// Copyright � Matrox Electronic Systems Ltd., 1992-2020.
// 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 "mdispgtkapp.h"
#include "childframe.h"
#include <algorithm>

#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_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(gtk_widget_get_window(m_window)):0);

      // Allow panning and zooming with the mouse [CALL TO MIL]
      MdispControl(m_MilDisplay, M_MOUSE_USE, M_ENABLE);
      
      // Tell Mil Display we are using Gtk toolkit So background will be
      // cleared by  MIL when resizing
      MdispControl(m_MilDisplay, M_GTK_MODE, 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 *tmp;
   
   // 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, M_USE_EXTENSION, 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(gtk_widget_get_window(m_window))
            {
            if(gtk_widget_get_window(m_window))
               gdk_window_invalidate_rect(gtk_widget_get_window(m_window), NULL, false);
            }
         }
      
      //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_AXIS_ALIGNED_RECT);
         m_PrimitiveInCreation = M_AXIS_ALIGNED_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_CIRCLE);
         m_PrimitiveInCreation = M_CIRCLE;
         }
      }
   }
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::OnGraOrientedRect()
   {
   if(m_MilDisplay)
      {
      if(m_isGraphicsAnnotationsEnabled)
         {
         MgraColor(m_MilGraphContext, M_COLOR_BLUE);
         MgraInteractive(m_MilGraphContext, m_MilGraphList, M_GRAPHIC_TYPE_RECT, M_DEFAULT, M_ORIENTED_RECT);
         m_PrimitiveInCreation = M_ORIENTED_RECT;
         }
      }
   }

void MdispGtkView::OnGraArcThreePoints()
   {
   if(m_MilDisplay)
      {
      if(m_isGraphicsAnnotationsEnabled)
         {
         MgraColor(m_MilGraphContext, M_COLOR_GREEN);
         MgraInteractive(m_MilGraphContext, m_MilGraphList, M_GRAPHIC_TYPE_ARC, M_DEFAULT, M_ARC_THREE_POINTS);
         m_PrimitiveInCreation = M_ARC_THREE_POINTS;
         }
      }
   }
void MdispGtkView::OnGraChooseColor()
   {
   if(m_MilDisplay && m_MilGraphList)
      {
      GdkRGBA gColor;
      GtkWidget *dialog = gtk_color_chooser_dialog_new ("Changing color", GTK_WINDOW(m_cf->ParentWindow()));
      gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
      gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (dialog), &gColor);
      gint result = gtk_dialog_run (GTK_DIALOG (dialog));
      if(result == GTK_RESPONSE_OK)
         {
         gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog), &gColor);
         MIL_INT R = (MIL_INT) (gColor.red * 255.0);
         MIL_INT G = (MIL_INT) (gColor.green * 255.0);
         MIL_INT B = (MIL_INT) (gColor.blue * 255.0);
         MIL_INT NewColor = M_RGB888(R, G, B);
         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::OnDrawDir()
   {
   if(m_MilDisplay && m_MilGraphList)
      {
      const MIL_INT NbValues = 4;
      MIL_INT DrawDirValues[NbValues] = 
         {
         M_NONE,
         M_PRIMARY_DIRECTION,      
         M_SECONDARY_DIRECTION,
         M_PRIMARY_DIRECTION + M_SECONDARY_DIRECTION
         };

      MIL_INT NbGrph = 0;
      MgraInquireList(m_MilGraphList, M_LIST, M_DEFAULT, M_NUMBER_OF_GRAPHICS, &NbGrph);
      MIL_INT DrawDirCurValueIdx = 0;
      MIL_INT NbSelectedGrph = 0;
      for(MIL_INT g = 0; g < NbGrph; g++) // Finds the highest draw direction among selected graphics
         {
         if(MgraInquireList(m_MilGraphList, M_GRAPHIC_INDEX(g), M_DEFAULT, M_GRAPHIC_SELECTED, M_NULL) == M_TRUE)
            {
            MIL_INT GrphDrawDir =
               MgraInquireList(m_MilGraphList, M_GRAPHIC_INDEX(g), M_DEFAULT, M_DRAW_DIRECTION, M_NULL);
            if(GrphDrawDir == M_DEFAULT)
               { GrphDrawDir = M_NONE; }

            MIL_INT FoundIdx = std::find(&DrawDirValues[0], &DrawDirValues[NbValues], GrphDrawDir) - &DrawDirValues[0];
            if(FoundIdx < NbValues)
               { DrawDirCurValueIdx = std::max(DrawDirCurValueIdx, FoundIdx); }
            ++NbSelectedGrph;
            }
         }

      if(NbSelectedGrph > 0)
         {
         // toggle current value
         DrawDirCurValueIdx = (DrawDirCurValueIdx + 1) % NbValues;
         MgraControlList(m_MilGraphList, M_ALL_SELECTED, M_DEFAULT, M_DRAW_DIRECTION, DrawDirValues[DrawDirCurValueIdx]);      
         }
      }
   }


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::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"));
         
            //Initialize graphic list
            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]
         gdk_x11_display_error_trap_push(gdk_display_get_default());
         
         Pixmap XPixmap = (Pixmap)MbufInquire(m_MilOverlayImage, M_XPIXMAP_HANDLE, M_NULL);
         cairo_t *cr;
         cairo_surface_t *surface = cairo_xlib_surface_create (GDK_WINDOW_XDISPLAY(gtk_widget_get_window(m_window)),
                                                               XPixmap,
                                                               DefaultVisual(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(m_window)),0),
                                                               imageWidth,
                                                               imageHeight);
         
         cr = cairo_create (surface);
         
         cairo_set_source_rgb (cr, 0, 0, 1);
         // Draw a blue cross in the overlay buffer.
         cairo_move_to (cr, 0, imageHeight/2);
         cairo_line_to (cr, imageWidth, imageHeight/2);
         cairo_stroke (cr);
         cairo_move_to (cr, imageWidth/2, 0);
         cairo_line_to (cr, imageWidth/2, imageHeight);
         cairo_stroke (cr);
         
         // Write Red text in the overlay buffer.
         MosStrcpy(chText, 80, "X Overlay Text "); 
         cairo_set_source_rgb (cr, 1, 0, 0);
         cairo_set_font_size(cr, 13);
         cairo_move_to(cr, imageWidth*3/18, imageHeight*4/6);
         cairo_show_text(cr, chText);

         // Write Yellow text in the overlay buffer.
         cairo_set_source_rgb (cr, 1, 1, 0);
         cairo_set_font_size(cr, 13);
         cairo_move_to(cr,  imageWidth*12/18,imageHeight*4/6);
         cairo_show_text(cr, chText);
         
         cairo_surface_flush(surface);

         //    /* flush */
         gdk_display_flush(gdk_display_get_default());
            
         // 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;
         
         cairo_destroy(cr);         
         gdk_x11_display_error_trap_pop_ignored (gdk_display_get_default());
         }
      }
   }


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(cairo_t *cr)
   {
   GtkAllocation allocation;
   cairo_text_extents_t extents;
   if(!m_MilDisplay)
      {
      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_text_extents(cr, "Display Allocation failed", &extents);
      cairo_move_to (cr, (m_imageSizeX/2 - extents.width/2), 20.0);
      cairo_show_text (cr, "Display Allocation failed");
      }
   else if(!m_isWindowed)
      {
      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_text_extents(cr, "Image displayed on external screen", &extents);
      cairo_move_to (cr, (m_imageSizeX/2 - extents.width/2), 20.0);
      cairo_show_text (cr, "Image displayed on external screen");
      }
   }