DrawForceElementsWindow.cpp
// Copyright 2010 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. // #include <math.h> #include "DrawForceElementsWindow.h" #include "DrawForceElements.h" using namespace std; #define FE_TYPE_CODES "UFNH" // construct a table of frame height factors based on Table VIII in 2525B // these are used to "normalize" the size of the exported images somewhat struct FrameHeight2525B { QString affiliations; QString dimensions; double heightRatio; }; static const int c_numFrameHeights = 13; static const int c_unknownSurface = 1; static const int c_friendlySeaSurface = 5; static const FrameHeight2525B c_frameHeights[c_numFrameHeights] = { // unknown frames { "PUGW", "PA", 1.3 }, // air and space { "PUGW", "GFS", 1.44 }, // surface { "PUGW", "U", 1.3 }, // subsurface // friendly frames { "FADMJK", "PA", 1.2 }, // air and space { "FADMJK", "GF", 1.0 }, // ground { "FADMJK", "S", 1.2 }, // sea surface and ground equipment { "FADMJK", "U", 1.2 }, // subsurface // neutral frames { "NL", "PA", 1.2 }, // air and space { "NL", "GFS", 1.1 }, // surface { "NL", "U", 1.2 }, // subsurface // hostile frames { "HS", "PA", 1.3 }, // air and space { "HS", "GFS", 1.44 }, // surface { "HS", "U", 1.3 } // subsurface // there are two special cases: // 1. battle dimension Z always uses an unknown surface dimension frame (1.44) // 2. friendly ground equipment (dimension "G") uses a sea surface frame (1.2) // so we check for code "E" at index 4 and anything other than "-" at index 5 }; ////////////////////////////////////////////////////////// // private member functions for DrawForceElementsWindow // ////////////////////////////////////////////////////////// // compute the union of the graphic's bounds with the refresh envelope // if the refresh envelope is null, create a new one and populate it with the FE's bounds void DrawForceElementsWindow::addRefresh (ICachedGraphicPtr ipFE) { IEnvelopePtr ipFEEnvelope (CLSID_Envelope); ipFE->QueryEnvelope (getScreenDisplay(), ipFEEnvelope); if ( ! m_ipRefreshEnvelope ) m_ipRefreshEnvelope = ipFEEnvelope; else m_ipRefreshEnvelope->Union (ipFEEnvelope); } // add the named tool to the toolbar control void DrawForceElementsWindow::addTool (const char *name) { CComBSTR toolID ("esriControlCommands."); toolID.Append (name); long itemIndex; m_ipToolbarControl->AddItem (CComVariant(toolID), 0, -1, VARIANT_FALSE, 0, esriCommandStyleIconOnly, &itemIndex); } // convenience method to extricate this much-used interface IActiveViewPtr DrawForceElementsWindow::getActiveView() { IActiveViewPtr ipActiveView; m_ipMapControl->get_ActiveView (&ipActiveView); return ipActiveView; } // compute the size of a force element in geographic units based on the current value of m_sldSize double DrawForceElementsWindow::computeSize() { // calculate the new symbol size as a fraction of the visible extent IEnvelopePtr ipExtent; getActiveView()->get_Extent (&ipExtent); double height; ipExtent->get_Height (&height); return ( 0.002 * height * double(m_sldSize->value()) ); } // get the text of the selected property from the selected FE and put it in the line editor void DrawForceElementsWindow::copyPropToText() { if ( m_ipSelectedFE ) { USES_CONVERSION; IFEGraphicPtr ipFEGraphic (m_ipSelectedFE); IForceElementPtr ipForceElement; IPropertySetPtr ipPropertySet; CComVariant variant; CComBSTR bstrValue; // update the label editor to reflect the attributes of the newly selected element ipFEGraphic->get_ForceElement (&ipForceElement); ipForceElement->get_PropertySet (&ipPropertySet); ipPropertySet->GetProperty (CComBSTR((const char*)m_cmbPropName->currentText().toAscii()), &variant); if ( variant.vt == VT_BSTR ) bstrValue = variant.bstrVal; if ( bstrValue.Length() > 0 ) m_txtPropValue->setText (OLE2A(bstrValue)); else m_txtPropValue->setText (""); } } // if UI is in edit mode, get the SIC of the selected FE and put it in the line editor void DrawForceElementsWindow::copySICToText() { if ( m_ipSelectedFE && m_uiMode == c_uiModeEditFE ) { USES_CONVERSION; static const QString feTypeCodes (FE_TYPE_CODES); // put the selected graphic's SIC in the editor widget and check the selected radio button IFEGraphicPtr ipFEGraphic (m_ipSelectedFE); CComBSTR bstrSIC; ipFEGraphic->get_SymbolID (&bstrSIC); m_txtSIC->setText (OLE2A(bstrSIC)); int feType = feTypeCodes.indexOf (m_txtSIC->text()[1]); if ( feType >= 0 && feType < NUM_FE_TYPES && feTypeCodes[feType] != QChar(m_forceElementType) ) { // change the selected radio button to the one matching the selected graphic's type m_suppressChangeFEType = true; m_rbFEType[feType]->setChecked (true); m_suppressChangeFEType = false; m_forceElementType = feTypeCodes[feType].toAscii(); } } } // get the line editor's text and put it in the selected property of the selected FE void DrawForceElementsWindow::copyTextToProp() { if ( m_ipSelectedFE ) { IFEGraphicPtr ipFEGraphic (m_ipSelectedFE); IForceElementPtr ipForceElement; IPropertySetPtr ipPropertySet; QString propValue (m_txtPropValue->text()); // set the selected annotation property on the force element ipFEGraphic->get_ForceElement (&ipForceElement); ipForceElement->get_PropertySet (&ipPropertySet); if ( propValue.length() > 0 ) ipPropertySet->SetProperty ( CComBSTR((const char*)m_cmbPropName->currentText().toAscii()), CComVariant(CComBSTR((const char*)propValue.toAscii())) ); else ipPropertySet->SetProperty ( CComBSTR((const char*)m_cmbPropName->currentText().toAscii()), CComVariant(CComBSTR("")) ); // tell the graphic to rebuild its labels m_ipSelectedFE->Refresh (getScreenDisplay()); } } // if UI is in edit mode, get the line editor's text and put it in the SIC of the selected FE void DrawForceElementsWindow::copyTextToSIC() { if ( m_ipSelectedFE && m_uiMode == c_uiModeEditFE ) { IFEGraphicPtr ipFEGraphic (m_ipSelectedFE); IForceElementPtr ipForceElement; IActiveViewPtr ipActiveView (getActiveView()); IScreenDisplayPtr ipScreenDisplay (getScreenDisplay(ipActiveView)); // change the selected element to the new type resetRefresh (m_ipSelectedFE); ipFEGraphic->get_ForceElement (&ipForceElement); ipForceElement->put_MessageString (CComBSTR((const char*)m_txtSIC->text().toAscii())); m_ipSelectedFE->Refresh (ipScreenDisplay); addRefresh (m_ipSelectedFE); ipActiveView->PartialRefresh (esriViewForeground, NULL, m_ipRefreshEnvelope); } } // enable and disable the delete and export buttons based on the application state void DrawForceElementsWindow::enableDisable() { m_btnDelete->setEnabled ( m_ipSelectedFE != 0 ); bool haveAnyForceElements = ( m_forceElements.size() > 0 ); m_btnClear->setEnabled ( haveAnyForceElements ); m_btnExport->setEnabled ( haveAnyForceElements ); } // convenience method to extricate this much-used interface IScreenDisplayPtr DrawForceElementsWindow::getScreenDisplay (IActiveViewPtr ipActiveView) { if ( ! ipActiveView ) ipActiveView = getActiveView(); IScreenDisplayPtr ipScreenDisplay; ipActiveView->get_ScreenDisplay (&ipScreenDisplay); return ipScreenDisplay; } // reset the refresh extent to null or that of the specified graphic void DrawForceElementsWindow::resetRefresh (ICachedGraphicPtr ipFE) { m_ipRefreshEnvelope = 0; if ( ipFE ) addRefresh (ipFE); } ////////////////////////////////////////////////////////////////////// // event handlers (Qt slots, IMapControlEvents2, IActiveViewEvents) // ////////////////////////////////////////////////////////////////////// void DrawForceElementsWindow::OnAfterDraw (VARIANT display, long viewDrawPhase) { if ( viewDrawPhase == esriViewForeground ) { // draw the force element symbols directly on the display - no caching // you can see the extent of m_ipRefreshEnvelope in the part of the map that flickers IScreenDisplayPtr ipScreenDisplay (getScreenDisplay()); ipScreenDisplay->StartDrawing (0, esriNoScreenCache); for ( list<ICachedGraphic*>::iterator itr = m_forceElements.begin(); itr != m_forceElements.end(); ++itr ) (*itr)->Draw (ipScreenDisplay, NULL); // if a force element is selected, highlight it using the default symbol if ( m_ipSelectedFE ) m_ipSelectedFE->Highlight (ipScreenDisplay, m_ipHighlightColor, NULL); ipScreenDisplay->FinishDrawing(); } } void DrawForceElementsWindow::OnMouseDown (long button, long shift, long x, long y, double mapX, double mapY) { // we only work on the LMB here if ( button != 1 ) return; if ( m_uiMode == c_uiModePlaceFE ) { // do some basic validation on the SIC QByteArray qsic (m_txtSIC->text().toAscii()); bool valid (true); const char *sic (qsic.data()); // there are many other ways to make an invalid FE SIC, but these are the most basic if ( qsic.length() != 15 ) { QMessageBox::critical ( this, "Invalid Symbol ID Code", "The symbol ID code must be 15 characters in length." ); valid = false; } else if ( qsic[0] != 'S' ) { QMessageBox::critical ( this, "Non-FE Symbol ID Code", "Force element symbol ID codes all begin with 'S'." ); valid = false; } // generate the next SIC, if selected if ( m_chkRandomizeSIC->isChecked() ) randomizeSIC(); if ( ! valid ) return; else printf ("Placing %s at (%f, %f).\n", sic, mapX, mapY); IForceElementPtr ipForceElement (CLSID_ForceElement); IPointPtr ipPoint (CLSID_Point); IFEGraphicPtr ipFEGraphic; IFEGraphicStylePtr ipFEGraphicStyle; ICachedGraphicPtr ipCachedGraphic; // construct a new force element graphic using the current settings ipForceElement->put_MessageString (CComBSTR(sic)); ipPoint->PutCoords (mapX, mapY); ipForceElement->putref_Shape (ipPoint); m_ipRenderer->GraphicByForceElement (ipForceElement, &ipCachedGraphic); ipCachedGraphic->put_Angle (m_angle); ipCachedGraphic->put_Size (computeSize()); // tell it to use polygon shapes instead of fonts to render text (because it's faster) ipFEGraphic = ipCachedGraphic; ipFEGraphic->get_Style (&ipFEGraphicStyle); ipFEGraphicStyle->put_UseFonts (VARIANT_FALSE); ipFEGraphic->putref_Style (ipFEGraphicStyle); // make the new force element the selected object and set its first label property, if any resetRefresh (m_ipSelectedFE); m_ipSelectedFE = ipCachedGraphic; m_forceElements.push_back (ipCachedGraphic.Detach()); copyTextToProp(); // refresh the display around where the new symbol was added addRefresh (m_ipSelectedFE); getActiveView()->PartialRefresh (esriViewForeground, NULL, m_ipRefreshEnvelope); enableDisable(); } else if ( m_uiMode == c_uiModeEditFE ) { // we only want to perform dragging when in edit mode m_mouseDown = true; m_prevX = mapX; m_prevY = mapY; // pick an existing force element from the map to replace the existing selection, if any resetRefresh (m_ipSelectedFE); m_ipSelectedFE = 0; // find the graphic under the mouse click that is closest to the end of the list, if any // use a forward search because we want to use the iterator to rearrange the list afterward list<ICachedGraphic*>::iterator found = m_forceElements.end(); for ( list<ICachedGraphic*>::iterator itr = m_forceElements.begin(); itr != m_forceElements.end(); ++itr ) { // loop through the pointers and perform a hit test on each one using the mouse location VARIANT_BOOL isHit (VARIANT_FALSE); (*itr)->HitTest (mapX, mapY, 0.0, &isHit); if ( isHit == VARIANT_TRUE ) found = itr; } if ( found != m_forceElements.end() ) { // there is a hit - replace the selected graphic and halt the search // move the new selection to the back of the list so it will render on top (no AddRef or Release needed) m_ipSelectedFE = *found; m_forceElements.erase (found); m_forceElements.push_back (m_ipSelectedFE); addRefresh (m_ipSelectedFE); } if ( m_ipSelectedFE ) { // update the control panel to reflect the attributes of the newly selected graphic copySICToText(); copyPropToText(); } // refresh the display - OnAfterDraw draws the symbols getActiveView()->PartialRefresh (esriViewForeground, NULL, m_ipRefreshEnvelope); enableDisable(); } } void DrawForceElementsWindow::OnMouseMove (long button, long shift, long x, long y, double mapX, double mapY) { if ( m_mouseDown && m_ipSelectedFE != 0 ) { IFEGraphicPtr ipFEGraphic (m_ipSelectedFE); IForceElementPtr ipForceElement; IPointPtr ipPoint; IActiveViewPtr ipActiveView (getActiveView()); double deltaX (mapX - m_prevX); double deltaY (mapY - m_prevY); double prevX; double prevY; // store the current mouse coordinates as the basis of the next delta m_prevX = mapX; m_prevY = mapY; // drag the selected FE along with the mouse resetRefresh (m_ipSelectedFE); ipFEGraphic->get_ForceElement (&ipForceElement); ipForceElement->get_Shape (&ipPoint); ipPoint->QueryCoords (&prevX, &prevY); ipPoint->PutCoords (prevX + deltaX, prevY + deltaY); // refresh the force element and the display m_ipSelectedFE->Refresh (getScreenDisplay(ipActiveView)); addRefresh (m_ipSelectedFE); ipActiveView->PartialRefresh (esriViewForeground, NULL, m_ipRefreshEnvelope); } } void DrawForceElementsWindow::OnMouseUp (long button, long shift, long x, long y, double mapX, double mapY) { m_mouseDown = false; } void DrawForceElementsWindow::changeAngle (int sliderValue) { // convert the slider value (degrees) into radians m_angle = double(sliderValue) * 0.0174532925199432957692369; if ( m_forceElements.size() > 0 ) { // update the graphics to the new angle and redraw everything for ( list<ICachedGraphic*>::iterator itr = m_forceElements.begin(); itr != m_forceElements.end(); ++itr ) (*itr)->put_Angle (m_angle); getActiveView()->PartialRefresh (esriViewForeground, NULL, NULL); } } // called when one of the Force Element Type radio buttons is clicked // alter the contents of the SIC line editor and change the SIC in the selected FE void DrawForceElementsWindow::changeFEType (int radioButtonID) { static const char *feTypeCodes (FE_TYPE_CODES); if ( m_suppressChangeFEType ) return; if ( radioButtonID < NUM_FE_TYPES && feTypeCodes[radioButtonID] != m_forceElementType ) { m_forceElementType = feTypeCodes[radioButtonID]; QString sic (m_txtSIC->text()); sic[1] = m_forceElementType; m_txtSIC->setText (sic); copyTextToSIC(); } } // called when one of the Mode radio buttons is clicked - change the UI mode void DrawForceElementsWindow::changeMode (int radioButtonID) { static const char *hints[] = { "Use the toolbar or change the mode.", "Click on the map to place a <b>Force Element</b>.", "Drag a <b>Force Element</b> or change its properties." }; UIMode newMode ((UIMode)radioButtonID); if ( newMode != m_uiMode ) { // switch the toolbar on and off depending on the mode if ( newMode == c_uiModeArcGIS ) { m_ipToolbarControl->put_Enabled (VARIANT_TRUE); m_ipToolbarControl->SetBuddyControl (m_ipMapControl); m_controlPanel->setEnabled (false); } else { m_ipToolbarControl->put_Enabled (VARIANT_FALSE); m_ipToolbarControl->SetBuddyControl (NULL); m_controlPanel->setEnabled (true); } // update the control panel m_uiMode = newMode; m_lblHint->setText (hints[m_uiMode]); // make sure the SIC is properly randomized before placement, if appropriate // otherwise make sure the SIC being edited is that of the selected FE if ( m_uiMode == c_uiModePlaceFE && m_chkRandomizeSIC->isChecked() ) randomizeSIC(); else copySICToText(); enableDisable(); } } // called when the label name combo box is changed // get the label text from the selected FE and put it in the line editor void DrawForceElementsWindow::changePropName (int newPropIndex) { m_lblPlacement->setText (m_labelPlacements[newPropIndex]); copyPropToText(); } // called when the current label text is edited - apply the new text to the selected FE void DrawForceElementsWindow::changePropText (const QString&) { if ( m_ipSelectedFE ) { // if the text gets smaller, we need to refresh the extent from _before_ the text is changed // if the text gets larger, we need to refresh the extent from _after_ the text is changed resetRefresh (m_ipSelectedFE); copyTextToProp(); addRefresh (m_ipSelectedFE); getActiveView()->PartialRefresh (esriViewForeground, NULL, m_ipRefreshEnvelope); } } // called when the SIC text is edited - apply the new SIC to the selected FE void DrawForceElementsWindow::changeSICText (const QString&) { copyTextToSIC(); } // called when the size slider is moved - resize all the force elements void DrawForceElementsWindow::changeSize (int) { if ( m_forceElements.size() > 0 ) { // update the graphics to the new size and redraw everything double newSize = computeSize(); for ( list<ICachedGraphic*>::iterator itr = m_forceElements.begin(); itr != m_forceElements.end(); ++itr ) (*itr)->put_Size (newSize); getActiveView()->PartialRefresh (esriViewForeground, NULL, NULL); } } // called when the clear button is clicked void DrawForceElementsWindow::clearFE() { // confirm this action with the user QMessageBox::StandardButton response = QMessageBox::question ( this, "Delete All Force Elements?", "Please confirm that you want to delete all <b>Force Elements</b> from the map.", QMessageBox::Yes | QMessageBox::No ); if ( response == QMessageBox::No ) return; // remove all the FE graphics and refresh the display m_ipSelectedFE = 0; while ( m_forceElements.size() > 0 ) { // be sure to dereference these pointers; otherwise they will leak m_forceElements.back()->Release(); m_forceElements.pop_back(); } getActiveView()->PartialRefresh (esriViewForeground, NULL, NULL); } // called when the delete button is clicked void DrawForceElementsWindow::deleteFE() { // take advantage of condition established by OnMouseDown - selected FE is at the end of the list m_forceElements.back()->Release(); m_forceElements.pop_back(); resetRefresh (m_ipSelectedFE); m_ipSelectedFE = 0; // there will be no selected element after this call getActiveView()->PartialRefresh (esriViewForeground, NULL, m_ipRefreshEnvelope); enableDisable(); } // called when the export button is clicked void DrawForceElementsWindow::exportFE() { USES_CONVERSION; static const double unitFrameHeight = 200; // unit frame height in pixels IFEGraphicPtr ipFEGraphic; IExportGraphicPtr ipExportGraphic; IScreenDisplayPtr ipScreenDisplay (getScreenDisplay()); IPolygonPtr ipFrameArea; IEnvelopePtr ipFrameBounds; IEnvelopePtr ipElementBounds (CLSID_Envelope); CComBSTR bstrSIC; VARIANT_BOOL succeeded; double frameHeight; double frameHeightRatio; double elementHeight; long imageHeight; char *sic; char fileName[128]; int sequenceNumber (0); printf ("exporting force elements\n"); for ( list<ICachedGraphic*>::iterator itr = m_forceElements.begin(); itr != m_forceElements.end(); ++itr ) { // build the file name from the FE properties bstrSIC.Empty(); ipFEGraphic = *itr; ipFEGraphic->get_SymbolID (&bstrSIC); sic = OLE2A (bstrSIC); sprintf (fileName, "fe_%03d_%s.png", ++sequenceNumber, sic); // look up the symbol's frame height ratio frameHeightRatio = 0.0; for ( int i = 0; i < c_numFrameHeights; ++i ) { // it's primarily determined by affiliation and battle space dimension if ( c_frameHeights[i].affiliations.contains(sic[1]) && c_frameHeights[i].dimensions.contains(sic[2]) ) { // in most cases, this will be the result frameHeightRatio = c_frameHeights[i].heightRatio; break; } } // check for a couple of degenerate cases if ( frameHeightRatio == 0.0 ) { // unknown battle space dimension ("Z") if ( sic[2] == 'Z' ) frameHeightRatio = c_frameHeights[c_unknownSurface].heightRatio; // friendly ground equipment ("**G*E*") else if ( c_frameHeights[c_friendlySeaSurface].affiliations.contains(sic[1]) && sic[2] == 'G' && sic[4] == 'E' && sic[5] != '-' ) frameHeightRatio = c_frameHeights[c_friendlySeaSurface].heightRatio; // sanity check for any other case we didn't account for else frameHeightRatio = 1.0; } // compute the height of the image to account for the varying text shapes ipFEGraphic->get_FrameArea (&ipFrameArea); ipFrameArea->get_Envelope (&ipFrameBounds); (*itr)->QueryEnvelope (ipScreenDisplay, ipElementBounds); ipFrameBounds->get_Height (&frameHeight); ipElementBounds->get_Height (&elementHeight); #if defined(ESRI_UNIX) imageHeight = long (round(unitFrameHeight * frameHeightRatio * elementHeight / frameHeight)); #elif defined(ESRI_WINDOWS) imageHeight = long (unitFrameHeight * frameHeightRatio * elementHeight / frameHeight); #endif // perform the export operation ipExportGraphic = (*itr); ipExportGraphic->ExportToFile ( ipScreenDisplay, CComBSTR(fileName), NULL, imageHeight, 0, 0, 0.0, 0.0, VARIANT_FALSE, &succeeded ); printf ("\texport %s %s\n", fileName, ((succeeded==VARIANT_TRUE) ? "succeeded" : "failed")); } printf ("export complete\n"); } // randomize the SIC that is currently displayed in the line editor void DrawForceElementsWindow::randomizeSIC() { QString sic (getSIC()); if ( m_forceElementType != 'F' ) sic[1] = m_forceElementType; m_txtSIC->setText (sic); copyTextToSIC(); } //////////////////////////////// // constructor and destructor // //////////////////////////////// DrawForceElementsWindow::DrawForceElementsWindow() : m_angle (0.0), m_forceElementType ('F'), m_ipHighlightColor (CLSID_RgbColor), m_ipMapControlEventsHelper (CLSID_MapControlEvents2Listener), //m_ipRenderer (CLSID_ForceElement2525BRenderer), m_mouseDown (false), m_suppressChangeFEType (false), m_uiMode (c_uiModeArcGIS) { USES_CONVERSION; printf ("creating widgets\n"); // create the child widgets of this window QSplitter *splitter = new QSplitter (this); QWidget *qtPanel = new QWidget (splitter); m_lblHint = new QLabel ("Use the toolbar or change the mode.", qtPanel); QGroupBox *gbMode = new QGroupBox ("Mode", qtPanel); QRadioButton *rbGIS = new QRadioButton ("ArcGIS Toolbar", gbMode); QRadioButton *rbPlace = new QRadioButton ("Place Force Element", gbMode); QRadioButton *rbEdit = new QRadioButton ("Edit Force Element", gbMode); m_controlPanel = new QWidget (qtPanel); QGroupBox *gbFEType = new QGroupBox ("Force Element Affiliation", m_controlPanel); m_rbFEType[0] = new QRadioButton ("Unknown", gbFEType); m_rbFEType[1] = new QRadioButton ("Friendly", gbFEType); m_rbFEType[2] = new QRadioButton ("Neutral", gbFEType); m_rbFEType[3] = new QRadioButton ("Hostile", gbFEType); m_chkRandomizeSIC = new QCheckBox ("Randomize symbol ID code after placement", m_controlPanel); QLabel *lblSIC = new QLabel ("Symbol ID Code:", m_controlPanel); m_txtSIC = new QLineEdit (getSIC(), m_controlPanel); m_cmbPropName = new QComboBox (m_controlPanel); m_txtPropValue = new QLineEdit (m_controlPanel); QLabel *lblPlacement = new QLabel ("Label Placement:", m_controlPanel); m_lblPlacement = new QLabel (m_controlPanel); QLabel *lblSize = new QLabel ("Symbol Size (% of display):", m_controlPanel); m_sldSize = new QSlider (Qt::Horizontal, m_controlPanel); QLabel *lblAngle = new QLabel ("Symbol Rotation:", m_controlPanel); QSlider *sldAngle = new QSlider (Qt::Horizontal, m_controlPanel); QPushButton *btnRandomizeSIC = new QPushButton ("Randomize Symbol ID", m_controlPanel); m_btnDelete = new QPushButton ("Delete Selected", m_controlPanel); m_btnClear = new QPushButton ("Clear All", m_controlPanel); m_btnExport = new QPushButton ("Export to PNG", m_controlPanel); QWidget *gisPanel = new QWidget (splitter); QAxCtl *toolbarControl = new QAxCtl (AoPROGID_ToolbarControl, gisPanel, "Toolbar Control"); QAxCtl *mapControl = new QAxCtl (AoPROGID_MapControl, gisPanel, "Map Control"); // set properties on some of the widgets toolbarControl->setMinimumHeight (28); toolbarControl->setMaximumHeight (28); splitter->setOpaqueResize (false); QList<int> sizes (splitter->sizes()); sizes[0] = 300; sizes[1] = 600; splitter->setSizes (sizes); m_sldSize->setRange (1, 100); m_sldSize->setSliderPosition (50); sldAngle->setRange (0, 360); sldAngle->setSliderPosition (0); m_chkRandomizeSIC->setChecked (true); m_btnDelete->setEnabled (false); m_btnExport->setEnabled (false); m_controlPanel->setEnabled (false); printf("Initilizing MOLE Renderer\n"); IMoleCoreHelperPtr ipMoleCoreHelper (CLSID_MoleCoreHelper); ICacheRendererPtr ipCR; ipMoleCoreHelper->get_ForceElementRenderer(&ipCR); m_ipRenderer = ipCR; // initialize the labeling controls from the FE graphic factory IFEGraphicFactoryPtr ipFEGraphicFactory; m_ipRenderer->get_GraphicFactory (&ipFEGraphicFactory); IEnumAttributeLabelPtr ipEnumAttributeLabel (ipFEGraphicFactory); ipEnumAttributeLabel->Reset(); IAttributeLabelPtr ipAttributeLabel; ipEnumAttributeLabel->NextLabel (&ipAttributeLabel); while ( ipAttributeLabel ) { // get the semicolon-separated list of field names and the label placement name CComBSTR bstrPlacementName, bstrFields; ipAttributeLabel->get_Name (&bstrPlacementName); ipAttributeLabel->get_Field (&bstrFields); // parse out the field names to populate the combo box QString qsPlacementName (OLE2A(bstrPlacementName)); QString qsFields (OLE2A(bstrFields)); QStringList qslFields (qsFields.split(QChar(';'), QString::SkipEmptyParts)); for ( int i = 0; i < qslFields.size(); ++i ) { m_cmbPropName->addItem (qslFields[i]); m_labelPlacements.append (qsPlacementName); } // make sure that all labels can be displayed ipAttributeLabel->put_IsVisible (VARIANT_TRUE); ipEnumAttributeLabel->NextLabel (&ipAttributeLabel); } // select the first attribute in the list m_cmbPropName->setCurrentIndex (0); m_lblPlacement->setText (m_labelPlacements[0]); // set up the mode radio button logic QButtonGroup *bgMode = new QButtonGroup (gbMode); bgMode->addButton (rbGIS, c_uiModeArcGIS); bgMode->addButton (rbPlace, c_uiModePlaceFE); bgMode->addButton (rbEdit, c_uiModeEditFE); rbGIS->setChecked (true); // set up the mode radio button layout QVBoxLayout *modeLayout = new QVBoxLayout (gbMode); modeLayout->addWidget (rbGIS); modeLayout->addWidget (rbPlace); modeLayout->addWidget (rbEdit); // set up the FE type radio button logic and layout; initially select friendly type QButtonGroup *bgFEType = new QButtonGroup (gbFEType); QVBoxLayout *feTypeLayout = new QVBoxLayout (gbFEType); for ( int i = 0; i < NUM_FE_TYPES; ++i ) { bgFEType->addButton (m_rbFEType[i], i); feTypeLayout->addWidget (m_rbFEType[i]); } m_rbFEType[1]->setChecked (true); // establish the control panel layout int row = 0; QGridLayout *controlPanelLayout = new QGridLayout (m_controlPanel); controlPanelLayout->setMargin (0); controlPanelLayout->addWidget (gbFEType, row++, 0, 1, 2); controlPanelLayout->addWidget (m_chkRandomizeSIC, row++, 0, 1, 2); controlPanelLayout->addWidget (lblSIC, row, 0, Qt::AlignRight); controlPanelLayout->addWidget (m_txtSIC, row++, 1); controlPanelLayout->addWidget (m_cmbPropName, row, 0); controlPanelLayout->addWidget (m_txtPropValue, row++, 1); controlPanelLayout->addWidget (lblPlacement, row, 0, Qt::AlignRight); controlPanelLayout->addWidget (m_lblPlacement, row++, 1); controlPanelLayout->addWidget (lblSize, row, 0, Qt::AlignRight); controlPanelLayout->addWidget (m_sldSize, row++, 1); controlPanelLayout->addWidget (lblAngle, row, 0, Qt::AlignRight); controlPanelLayout->addWidget (sldAngle, row++, 1); controlPanelLayout->addWidget (btnRandomizeSIC, row++, 1); controlPanelLayout->addWidget (m_btnDelete, row++, 1); controlPanelLayout->addWidget (m_btnClear, row++, 1); controlPanelLayout->addWidget (m_btnExport, row++, 1); controlPanelLayout->setRowStretch (row, 1); // establish the Qt panel layout QBoxLayout *qtLayout = new QVBoxLayout (qtPanel); qtLayout->addWidget (m_lblHint); qtLayout->addWidget (gbMode); qtLayout->addWidget (m_controlPanel); // establish the GIS layout QVBoxLayout *gisLayout = new QVBoxLayout (gisPanel); gisLayout->setMargin (0); gisLayout->setSpacing (0); gisLayout->addWidget (toolbarControl); gisLayout->addWidget (mapControl, 1); // establish the main layout QVBoxLayout *rootLayout = new QVBoxLayout (this); rootLayout->setMargin (0); rootLayout->setSpacing (0); rootLayout->addWidget (splitter); // set properties on this widget and connect event handlers resize (900, 600); setWindowTitle ("Draw Force Elements"); connect (bgMode, SIGNAL(buttonClicked(int)), SLOT(changeMode(int))); connect (bgFEType, SIGNAL(buttonClicked(int)), SLOT(changeFEType(int))); connect (m_sldSize, SIGNAL(valueChanged(int)), SLOT(changeSize(int))); connect (sldAngle, SIGNAL(valueChanged(int)), SLOT(changeAngle(int))); connect (m_cmbPropName, SIGNAL(currentIndexChanged(int)), SLOT(changePropName(int))); connect (m_txtPropValue, SIGNAL(textEdited(const QString&)), SLOT(changePropText(const QString&))); connect (m_txtSIC, SIGNAL(textEdited(const QString&)), SLOT(changeSICText(const QString&))); connect (btnRandomizeSIC, SIGNAL(clicked()), SLOT(randomizeSIC())); connect (m_btnDelete, SIGNAL(clicked()), SLOT(deleteFE())); connect (m_btnClear, SIGNAL(clicked()), SLOT(clearFE())); connect (m_btnExport, SIGNAL(clicked()), SLOT(exportFE())); printf ("setting up the controls\n"); // extricate the COM interfaces of the ArcGIS controls IUnknownPtr ipUnknown; mapControl->getInterface (&ipUnknown); m_ipMapControl = ipUnknown; if ( ! m_ipMapControl ) ABORT; toolbarControl->getInterface (&ipUnknown); m_ipToolbarControl = ipUnknown; if ( ! m_ipToolbarControl ) ABORT; // buddy up and load the toolbar m_ipToolbarControl->SetBuddyControl (m_ipMapControl); addTool ("ControlsOpenDocCommand"); addTool ("ControlsAddDataCommand"); addTool ("ControlsSaveAsDocCommand"); addTool ("ControlsMapZoomPanTool"); addTool ("ControlsMapZoomInFixedCommand"); addTool ("ControlsMapZoomOutFixedCommand"); addTool ("ControlsMapZoomInTool"); addTool ("ControlsMapZoomOutTool"); addTool ("ControlsMapFullExtentCommand"); addTool ("ControlsMapZoomToLastExtentBackCommand"); addTool ("ControlsMapZoomToLastExtentForwardCommand"); addTool ("ControlsMapZoomToolControl"); addTool ("ControlsSelectTool"); addTool ("ControlsMapGoToCommand"); addTool ("ControlsMapIdentifyTool"); addTool ("ControlsMapMeasureTool"); m_ipToolbarControl->put_ToolTips (VARIANT_TRUE); // register for map control events - use static cast because argument type != interface m_ipMapControlEventsHelper->Startup (static_cast<IMapControlEvents2Helper*>(this)); m_ipMapControlEventsHelper->AdviseEvents (m_ipMapControl, NULL); printf ("loading the map\n"); // load a default map file CComVariant vUndefined ((long)DISP_E_PARAMNOTFOUND, VT_ERROR); CComBSTR bstmp; m_ipMapControl->LoadMxFile ((bstmp = dsDataPath("Continents/Continents.mxd")), vUndefined, vUndefined); SysFreeString(bstmp); // initialize the highlight color m_ipHighlightColor->put_Red (0); m_ipHighlightColor->put_Green (255); m_ipHighlightColor->put_Blue (255); } DrawForceElementsWindow::~DrawForceElementsWindow() { printf ("deregistering event handler\n"); m_ipMapControlEventsHelper->UnadviseEvents(); m_ipMapControlEventsHelper->Shutdown(); printf ("releasing MOLE resources\n"); IMoleCoreHelperPtr ipMoleCoreHelper (CLSID_MoleCoreHelper); ipMoleCoreHelper->ReleaseForceElementRenderer(); while ( m_forceElements.size() > 0 ) { // be sure to dereference these pointers; otherwise they will leak m_forceElements.back()->Release(); m_forceElements.pop_back(); } // the controls are in the Qt tree, but dereference them for completeness m_ipMapControl = 0; m_ipMapControlEventsHelper = 0; }