Map tips
MapTip.cpp
// Copyright 2011 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

 

//MapTips.cpp
//
//This sample illustrates how to display "maptips"- tooltips for map
//elements- in Motif.  It uses the mouseevents of a class that extends
//CAoToolBase to accomplish this.  

#include "MapTip.h"

// Global variables
// 
// Motif 
Widget g_topLevel;                // application
Widget g_mainWindow;              // main program window
Widget g_mainForm;                // main prog window's form
Widget g_mapWidget;               // map control
Widget g_tocWidget;               // TOC control
Widget g_toolbarWidget;           // toolbar control
Widget g_addLayerWidget;
Widget g_fileSelectionDialog;  
Widget g_layerComboWidget;
Widget g_fieldComboWidget;
Widget g_tooltipWidget;
Widget g_layerLabel;
Widget g_fieldLabel;
XtAppContext g_appContext;
Atom g_wmDeleteWindow;

// Other
long layerIndex = -1;    // which layer is selected in layercombobox
long numLayers = 0;    // number of layers which have been added

int main(int argc, char* argv[])
{
  // Interfaces for the Controls
  IMapControl3Ptr ipMapControl;
  IToolbarControlPtr ipToolbarControl;
  ITOCControlPtr ipTOCControl;

  // Structure used to pass data to most of the callback routines
  MapTipsClientDataStruct mtcds;

  // Initialize the engine
  ::AoInitialize(NULL);
  {
    IAoInitializePtr ipAOinit(CLSID_AoInitialize);
    esriLicenseStatus status;
    ipAOinit->Initialize(esriLicenseProductCodeEngine, &status);
    if (status != esriLicenseCheckedOut)
    {
      std::cerr << "License not available for Engine\n";
      std::cerr << "status is " << status << std::endl;
      ipAOinit->Shutdown();
      ipAOinit = 0;
      ::AoUninitialize();      
      AoExit(0);
    }      
  }
    
  XtSetLanguageProc(NULL, NULL, NULL);

  // Initialize the Motif toolkit and create the parent widget
  g_topLevel = XtVaAppInitialize(&g_appContext, "XApplication", 
                                 NULL, 0, &argc, argv, NULL, NULL);
  XtVaSetValues(g_topLevel, 
                XmNtitle, "MapTips Motif Example",
                NULL);

  XtResizeWidget(g_topLevel, 999, 800, 1);

  // Create g_mainWindow
  g_mainWindow = XtVaCreateWidget("g_mainWindow", 
                                  xmMainWindowWidgetClass, g_topLevel,
                                  NULL);

  // Create g_mainForm - it will fill the g_mainWindow
  g_mainForm = XtVaCreateWidget("g_mainForm",
                                xmFormWidgetClass,        g_mainWindow,
                                XmNtopAttachment,         XmATTACH_WIDGET,
                                XmNtopWidget,             g_mainWindow,
                                XmNbottomAttachment,      XmATTACH_WIDGET,
                                XmNbottomWidget,          g_mainWindow,
                                XmNleftAttachment,        XmATTACH_WIDGET,
                                XmNleftWidget,            g_mainWindow,
                                XmNrightAttachment,       XmATTACH_WIDGET,
                                XmNrightWidget,           g_mainWindow,
                                XmNfractionBase,          100,
                                NULL);

  //Create button to add a feature layer
  XmString label;
  label = XmStringCreateLocalized(const_cast<char*>("Add Feature Layer"));
  g_addLayerWidget = XtVaCreateWidget("g_addLayerWidget",
                                      xmPushButtonWidgetClass,   g_mainForm,
                                      XmNlabelString, label,
                                      XmNtopAttachment,   XmATTACH_FORM,
                                      XmNleftAttachment,  XmATTACH_FORM,
                                      XmNheight,          40,
                                      XmNwidth,           200,
                                      NULL);
  XmStringFree(label);

  // NOTE: traversal is turned off for comboboxes and textwidgets
  // b/c this interferes w/ the MapTipTool's reception of mouse
  // events

  // Create the field combobox label
  g_layerLabel = XtVaCreateWidget("g_layerLabel",
                                  xmLabelWidgetClass,
                                  g_mainForm,
                                  XtVaTypedArg, XmNlabelString, XmRString, " Layer:  ", 10,
                                  XmNtopAttachment,   XmATTACH_FORM,
                                  XmNleftAttachment,  XmATTACH_WIDGET,
                                  XmNleftWidget, g_addLayerWidget,
                                  XmNheight,          35,
                                  XmNtraversalOn, False,
                                  NULL) ;
    
  // Create the combobox for selecting a layer
  g_layerComboWidget = XtVaCreateWidget("g_layerCombo",
                                        xmComboBoxWidgetClass,
                                        g_mainForm,
                                        XmNcomboBoxType, XmDROP_DOWN_LIST,
                                        XmNtopAttachment,   XmATTACH_FORM,
                                        XmNleftAttachment,  XmATTACH_WIDGET,
                                        XmNleftWidget,      g_layerLabel,
                                        XmNheight,          40,
                                        XmNwidth,           200,
                                        XmNtraversalOn, False,
                                        NULL) ;
  XtAddCallback(g_layerComboWidget, XmNselectionCallback, processLayerCombo, &mtcds);

  // Create the field combobox label
  g_fieldLabel = XtVaCreateWidget("g_fieldLabel",
                                  xmLabelWidgetClass,
                                  g_mainForm,
                                  XtVaTypedArg, XmNlabelString, XmRString, " Field:  ", 10,
                                  XmNtopAttachment,   XmATTACH_FORM,
                                  XmNleftAttachment,  XmATTACH_WIDGET,
                                  XmNleftWidget, g_layerComboWidget,
                                  XmNheight,          35,
                                  XmNtraversalOn, False,
                                  NULL) ;
    
  // Create the combobox for selecting a field
  g_fieldComboWidget = XtVaCreateWidget("g_fieldCombo",
                                        xmComboBoxWidgetClass,
                                        g_mainForm,
                                        XmNcomboBoxType, XmDROP_DOWN_LIST,
                                        XmNtopAttachment,   XmATTACH_FORM,
                                        XmNleftAttachment,  XmATTACH_WIDGET,
                                        XmNleftWidget,      g_fieldLabel,
                                        XmNheight,          40,
                                        XmNwidth,           200,
                                        XmNtraversalOn, False,
                                        NULL) ;
  XtAddCallback(g_fieldComboWidget, XmNselectionCallback, processFieldCombo, &mtcds);

  // Create a FileSelectionDialog with a *.lyr mask
  XmString mask, title;
  Arg args[2];
  mask  = XmStringCreateLocalized(const_cast<char*>("*.lyr"));
  title = XmStringCreateLocalized(const_cast<char*>("Select Layer"));
  XtSetArg(args[0], XmNdirMask, mask);
  XtSetArg(args[1], XmNdialogTitle, title);
  g_fileSelectionDialog = XmCreateFileSelectionDialog(g_mainForm, const_cast<char*>("Select"), args, 2);
  XmStringFree(mask);
  XmStringFree(title);
  XtAddCallback(g_fileSelectionDialog, XmNokCallback, processFileSelect, &mtcds);
  XtAddCallback(g_fileSelectionDialog, XmNcancelCallback, processFileSelect, &mtcds);
  
  // Remove the Help button
  Widget remove1 = XmFileSelectionBoxGetChild(g_fileSelectionDialog, XmDIALOG_HELP_BUTTON);
  XtUnmanageChild(remove1);

  // Create the TextField for the tooltip
  g_tooltipWidget = XtVaCreateWidget("g_tooltipWidget",
                                     xmTextFieldWidgetClass,   g_mainForm,
                                     XmNleftAttachment,  XmATTACH_FORM,
                                     XmNrightAttachment,  XmATTACH_FORM,
                                     XmNbottomAttachment,  XmATTACH_FORM,
                                     XmNheight,          35,
                                     XmNeditable, False,
                                     XmNtraversalOn, False,
                                     NULL);

  // Toolbar Control setup 
  g_toolbarWidget = XtVaCreateWidget("toolbarWidget",
                                     mwCtlWidgetClass,   g_mainForm,
                                     XmNleftAttachment,   XmATTACH_FORM,
                                     XmNrightAttachment, XmATTACH_FORM,
                                     XmNtopAttachment,  XmATTACH_WIDGET,
                                     XmNtopWidget,      g_addLayerWidget,
                                     MwNprogID,        AoPROGID_ToolbarControl,
                                     NULL);
  XtVaSetValues(g_toolbarWidget, XmNheight, 25, NULL);
  MwCtlGetInterface(g_toolbarWidget, (IUnknown**)&ipToolbarControl);
  
  // TOC Control setup
  g_tocWidget = XtVaCreateWidget("tocWidget",
                                 mwCtlWidgetClass,        g_mainForm,
                                 XmNtopAttachment,        XmATTACH_WIDGET,
                                 XmNtopWidget,            g_toolbarWidget,
                                 XmNleftAttachment,       XmATTACH_FORM,
                                 XmNbottomAttachment,     XmATTACH_WIDGET,
                                 XmNbottomWidget,         g_tooltipWidget,
                                 MwNprogID,               AoPROGID_TOCControl,
                                 NULL);
  XtVaSetValues(g_tocWidget, XmNwidth, 200, NULL);
  MwCtlGetInterface(g_tocWidget, (IUnknown**)&ipTOCControl);

  // Map Control setup
  g_mapWidget = XtVaCreateWidget("mapWidget",
                                 mwCtlWidgetClass,        g_mainForm,
                                 XmNtopAttachment,        XmATTACH_WIDGET,
                                 XmNtopWidget,            g_toolbarWidget,
                                 XmNleftAttachment,       XmATTACH_WIDGET,
                                 XmNleftWidget,           g_tocWidget,
                                 XmNrightAttachment,      XmATTACH_FORM,
                                 XmNbottomAttachment,     XmATTACH_WIDGET,
                                 XmNbottomWidget,            g_tooltipWidget,
                                 MwNprogID,               AoPROGID_MapControl,
                                 NULL);
  MwCtlGetInterface(g_mapWidget, (IUnknown**)&ipMapControl);

  // Manage the non-parent widgets
  XtManageChild(g_mainWindow);
  XtManageChild(g_mainForm);
  XtManageChild(g_addLayerWidget);
  XtManageChild(g_layerLabel);
  XtManageChild(g_layerComboWidget);
  XtManageChild(g_fieldLabel);
  XtManageChild(g_fieldComboWidget);;
  XtManageChild(g_toolbarWidget);
  XtManageChild(g_tocWidget);
  XtManageChild(g_mapWidget);
  XtManageChild(g_tooltipWidget);

  // Buddy the toolbar and TOC with the map
  ipToolbarControl->SetBuddyControl(ipMapControl);
  ipTOCControl->SetBuddyControl(ipMapControl);

  // Add tools to toolbar
  ::AddStandardToolbarItems(ipToolbarControl);

  // Add custom MapTip tool
  // Custom Tool
  MapTipTool* maptipAoTool = new MapTipTool(g_tooltipWidget);
  AoToolbarAddTool(ipToolbarControl, maptipAoTool, esriCommandStyleIconOnly);

  // Set up structures used to pass data to callback routines
  CloseFormClientDataStruct cfcds;
  cfcds.pMapControl = ipMapControl;
  cfcds.pToolbarControl = ipToolbarControl;
  cfcds.pTOCControl = ipTOCControl;

  mtcds.pMapControl = ipMapControl;
  mtcds.pMapTipTool = maptipAoTool;

  // Handle the window manager message that the window is about to be closed 
  g_wmDeleteWindow = XmInternAtom(XtDisplay(g_topLevel), "WM_DELETE_WINDOW", FALSE);
  XmAddWMProtocolCallback(g_topLevel, g_wmDeleteWindow, processClickCloseForm, &cfcds);

  // Realize the container widget  
  XtRealizeWidget(g_topLevel);

  XtResizeWidget(g_topLevel, 1000, 800, 1);

  XtAddCallback(g_addLayerWidget, XmNactivateCallback, processClick, NULL);

  // Turn application control over to the X Toolkit Intrinsics
  MwCtlAppMainLoop(g_appContext);
}

// Function called when WM_DELETE_WINDOW protocol is passed
void processClickCloseForm(Widget w, XtPointer client_data, XtPointer call_data)
{
  // To be on the safe side we will make sure the controls are
  // destroyed before uninitializing
  CloseFormClientDataStruct *cfcds = (CloseFormClientDataStruct *) client_data;
  IMapControl3Ptr ipMapControl = cfcds->pMapControl;
  ipMapControl = 0;
  IToolbarControlPtr ipToolbarControl = cfcds->pToolbarControl;
  ipToolbarControl = 0;
  ITOCControlPtr ipTOCControl = cfcds->pTOCControl;
  ipTOCControl = 0;
    
  // Uninitialize the engine
  {
    IAoInitializePtr ipAOinit(CLSID_AoInitialize);
    ipAOinit->Shutdown();
  }    
  ::AoUninitialize();

  AoExit(0);
}

// Open a FileSelectionDialog window to allow the user to select a .lyr file
// to add to the map control and TOC
void processClick(Widget w, XtPointer client_data, XtPointer call_data)
{
  XtManageChild(g_fileSelectionDialog);
}

// Add the .lyr file that the user selected to the map control and TOC
void processFileSelect(Widget w, XtPointer client_data, XtPointer call_data)
{
  USES_CONVERSION;
  
  MapTipsClientDataStruct *mtcds = (MapTipsClientDataStruct *) client_data;
  IMapControl3Ptr ipMapControl (mtcds->pMapControl);
  MapTipTool *maptipAoTool = mtcds->pMapTipTool;
    
  XmFileSelectionBoxCallbackStruct *cbs;
  cbs = (XmFileSelectionBoxCallbackStruct *)call_data;

  // Kill the dialog if they canceled
  if (cbs->reason == XmCR_CANCEL)
  {
    XtUnmanageChild(g_fileSelectionDialog);
    return;
  }

  // Get the file name and check for errors
  char *fileName = 0;
  if (!XmStringGetLtoR(cbs->value, XmFONTLIST_DEFAULT_TAG, &fileName))
  {
    XtUnmanageChild(g_fileSelectionDialog);
    return;
  }
  if (!*fileName)
  {
    XtFree(fileName);
    return;
  }
      
  // Try to add the layer to the map
  ipMapControl->AddLayerFromFile(CComBSTR(fileName), numLayers);
  XtFree(fileName);
    
  // Add the layer's name to layercombo if it is a FeatureLayer
  ILayerPtr ipLayer;
  ipMapControl->get_Layer(numLayers++, &ipLayer);
  IFeatureLayerPtr ipFeatLayer (ipLayer); //QI
  if (ipFeatLayer == 0)
  {
    XtUnmanageChild(g_fileSelectionDialog);
    return;
  }

  // Clear the fields combobox
  ::ClearCombo(g_fieldComboWidget);      

  // Get the layer name and add it to the combobox
  BSTR layerName;
  XmString cboName;
  ipFeatLayer->get_Name(&layerName);
  cboName = XmStringCreateLocalized(OLE2A(layerName));
  ::AoFreeBSTR(layerName); // remember to free the BSTR that was returned
  XmComboBoxAddItem(g_layerComboWidget, cboName, 0, True);
  XmStringFree(cboName);

  // Hide file selection dialog      
  XtUnmanageChild(g_fileSelectionDialog);

  // Set the textfield of the layercombobox and the fieldcombobox
  Widget txtwidget;
  XtVaGetValues(g_layerComboWidget, XmNtextField, &txtwidget, NULL);
  XmTextSetString(txtwidget, const_cast<char*>("Select Layer..."));
  XtVaGetValues(g_fieldComboWidget, XmNtextField, &txtwidget, NULL);
  XmTextSetString(txtwidget, NULL);

  // Reset MapTipsTool
  maptipAoTool->SetLayerIndex(-1);
  maptipAoTool->SetFieldIsSet(false);  
}

// Callback for when layer combobox selection changes.  Gets the
// current text of the combobox (which is a layer name) and adds this
// layer's fields to the field combobox.  One thing to notice in this
// function is the "cast" of ipLayer from ILayer to ILayerFields:
//
// ILayerPtr ipLayer;
// ipMapControl->get_Layer(layerIndex, &ipLayer);
// if (0 == ipLayer) return;
// ((ILayerFieldsPtr)ipLayer)->get_FieldCount(&count);
//
// This is perfectly OK to do as it performs a QI behind the scense,
// just be sure that you know that the QI will succeed. Here we know
// that the layer is a FeatureLayer (b/c only FeatureLayers are added
// to the combobox).
void processLayerCombo(Widget menu_item, XtPointer client_data, XtPointer call_data)
{
  USES_CONVERSION;
  
  MapTipsClientDataStruct *mtcds = (MapTipsClientDataStruct *) client_data;
  IMapControl3Ptr ipMapControl (mtcds->pMapControl);
  MapTipTool *maptipAoTool = mtcds->pMapTipTool;

  XmComboBoxCallbackStruct *cb = (XmComboBoxCallbackStruct *) call_data;

  // If nothing was selected then just return
  if (cb->event == NULL) return;

  layerIndex = cb->item_position;
  maptipAoTool->SetLayerIndex(layerIndex);

  // Add the fields of the feature class to the combobox
  ILayerPtr ipLayer;
  ipMapControl->get_Layer(layerIndex, &ipLayer);
  if (0 == ipLayer) return;
  long count;
  ((ILayerFieldsPtr)ipLayer)->get_FieldCount(&count);
  IFieldPtr ipField;
  esriFieldType fType;
  BSTR fieldName;
  XmString cboName;

  ::ClearCombo(g_fieldComboWidget);
  for (long i=0; i<count; i++)
  {
    ((ILayerFieldsPtr)ipLayer)->get_Field(i, &ipField);
    ipField->get_Type(&fType);
    if (fType != esriFieldTypeGeometry)
    {
      ipField->get_Name(&fieldName);
      cboName = XmStringCreateLocalized(OLE2A(fieldName));
      ::AoFreeBSTR(fieldName); // remember to free the BSTR that was returned
      XmComboBoxAddItem(g_fieldComboWidget, cboName, 0, True);
      XmStringFree(cboName);
    }
  }

  // Set the textfield of the fieldcombobox
  Widget txtwidget;
  XtVaGetValues(g_fieldComboWidget, XmNtextField, &txtwidget, NULL);
  XmTextSetString(txtwidget, const_cast<char*>("Select Field..."));
}

// Callback for when field combobox selection changes.  Gets the
// current text of the combobox (which is a field name) and sets the
// currently selected FeatureLayer's DisplayField to this field
void processFieldCombo(Widget menu_item, XtPointer client_data, XtPointer call_data)
{
  MapTipsClientDataStruct *mtcds = (MapTipsClientDataStruct *) client_data;
  IMapControl3Ptr ipMapControl (mtcds->pMapControl);
  MapTipTool *maptipAoTool = mtcds->pMapTipTool;

  XmComboBoxCallbackStruct *cb = (XmComboBoxCallbackStruct *) call_data;

  // If nothing was selected then just return
  if (cb->event == NULL) return;

  // Get the selected text (i.e. the bookmark name)
  char *value = (char *) XmStringUnparse(cb->item_or_text,
                                         NULL,
                                         XmCHARSET_TEXT,
                                         XmCHARSET_TEXT,
                                         NULL,
                                         0,
                                         XmOUTPUT_ALL);
  
  // Set the FeatureLayer's DisplayField
  ILayerPtr ipLayer;
  ipMapControl->get_Layer(layerIndex, &ipLayer);
  ((IFeatureLayerPtr)ipLayer)->put_DisplayField(CComBSTR(value));
  maptipAoTool->SetFieldIsSet(true);
  
  XtFree(value);
}