Manual group draw
ManualGroupDraw.h
// 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 <stdio.h>
#include <QtGui/QtGui>
#include <ArcSDK.h>
#include <Ao/AoControls.h>
#include <AxCtl/qt4axctl.h>
#include <olb/esridefensesolutions.h>

#define ABORT {                                              \
    printf ("abort on line %d of %s\n", __LINE__, __FILE__); \
    shutdown();                                              \
}


// functions defined in local C++ source files
void addTool (IToolbarControlPtr &ipToolbar, const char *name);
double dRandom (double low, double high);
CComBSTR dsDataPath (const char *relativeDatasetPath);
const char *getSIC();
void shutdown();


// the application's main window
class ManualGroupDraw : public QWidget
{
    Q_OBJECT


    QPushButton          *m_button;
    QComboBox            *m_comboBox;
    IMapControl4Ptr       m_ipMapControl;
    IMoleGroupElementPtr  m_ipMoleGroup;


public slots:


    void addGroupElement()
    {
        // tell the user that we are busy for a few seconds
        QCursor oldCursor = cursor();
        setCursor (Qt::WaitCursor);

        // pressing this button allows decluttering selections, and we only do it once
        m_button->setEnabled (false);
        m_comboBox->setEnabled (true);

        // tell MOLE to leave it cluttered, for now
        m_ipMoleGroup.CreateInstance (CLSID_MoleGroupElement);
        m_ipMoleGroup->put_DeclutterOption (moleDeclutterNone);
        m_ipMoleGroup->put_EnableDeclutter (false);

        // generate some elements (vary the number of elements as much as desired)
        IPointPtr ipPoint (CLSID_Point);
        IGroupElementPtr ipGroupElement (m_ipMoleGroup);
        for ( unsigned int i = 0; i < 10; ++i )
        {
            // create a MarkerElement with a MOLE symbol
            IMoleSymbolPtr ipMoleSymbol (CLSID_MoleMarkerSymbol);
            ipMoleSymbol->put_SymbolID (CComBSTR(getSIC()));
            IMarkerElementPtr ipMarkerElement (CLSID_MarkerElement);
            ipMarkerElement->put_Symbol (IMarkerSymbolPtr(ipMoleSymbol));
            IElementPtr ipElement (ipMarkerElement);

            // add the element to the group at a random clustered location
            ipPoint->PutCoords (dRandom(-15, 15), dRandom(-15, 15));
            ipElement->put_Geometry (IGeometryPtr(ipPoint));
            ipGroupElement->AddElement (ipElement);
        }
        // add the group to the current map and refresh the display
        IActiveViewPtr ipActiveView;
        m_ipMapControl->get_ActiveView (&ipActiveView);
        if ( ! ipActiveView ) ABORT;
        IGraphicsContainerPtr ipGraphicsContainer;
        ipActiveView->get_GraphicsContainer (&ipGraphicsContainer);
        if ( ! ipGraphicsContainer ) ABORT;
        ipGraphicsContainer->AddElement (IElementPtr(m_ipMoleGroup), 0);
        ipActiveView->PartialRefresh (esriViewGraphics, NULL, NULL);

        // tell the user that we are no longer busy
        setCursor (oldCursor);
    }


    void declutter (int index)
    {
        if ( ! m_comboBox->isEnabled() ) return;

        // a simple array map for the combo box
        static const int numDeclutterOptions = 4;
        static moleDeclutterOptionEnum declutterOptions[numDeclutterOptions] = {
            moleDeclutterNone,
            moleDeclutterLeader,
            moleDeclutterManual,
            moleDeclutterStack
        };

        // get the selected option from the combo box
        moleDeclutterOptionEnum currentOption;
        m_ipMoleGroup->get_DeclutterOption (&currentOption);
        moleDeclutterOptionEnum newOption = currentOption;
        if ( index >= 0 && index < numDeclutterOptions )
            newOption = declutterOptions[index];
        if ( newOption != currentOption )
        {
            // the option has changed - set the new option and recalculate if necessary
            m_ipMoleGroup->put_DeclutterOption (newOption);
            if ( newOption != moleDeclutterNone )
                m_ipMoleGroup->put_EnableDeclutter (VARIANT_TRUE);
            else
                m_ipMoleGroup->put_EnableDeclutter (VARIANT_FALSE);
            IGroupElementPtr ipGroupElement (m_ipMoleGroup);
            long elementCount;
            ipGroupElement->get_ElementCount (&elementCount);
            if ( newOption == moleDeclutterManual && elementCount > 0 )
            {
                // calculate the decluttered positions manually - in decimal degrees
                double xStep = 25;
                double xDeclutter = -0.5 * elementCount * xStep;
                if ( xDeclutter < -180 )
                {
                    // don't fall off the end of the world
                    xStep = 360 / elementCount;
                    xDeclutter = -180 - 0.5 * xStep;
                }
                else
                    xDeclutter -= 0.5 * xStep;
                IPointPtr ipPoint (CLSID_Point);
                IMoleDeclutterElementPtr ipMoleDeclutterElement (m_ipMoleGroup);
                for ( long i = 0; i < elementCount; ++i )
                {
                    // set a declutter point for each element
                    // these lines of code have no effect in stack or leader mode
                    IElementPtr ipElement;
                    ipGroupElement->get_Element (i, &ipElement);
                    xDeclutter += xStep;
                    ipPoint->PutCoords (xDeclutter, 45);
                    ipMoleDeclutterElement->DeclutterElement (ipElement, IGeometryPtr(ipPoint));
                }
            }
            // refresh the display
            IActiveViewPtr ipActiveView;
            m_ipMapControl->get_ActiveView (&ipActiveView);
            if ( ! ipActiveView ) ABORT;
            ipActiveView->PartialRefresh (esriViewGraphics, NULL, NULL);
        }
    }


public:


    ManualGroupDraw()
    {
        printf ("creating widgets\n");

        // create the child widgets of this window
        QWidget   *toolbar    = new QWidget     (this);
                   m_button   = new QPushButton ("Add Group Element", toolbar);
                   m_comboBox = new QComboBox   (toolbar);
        QSplitter *split      = new QSplitter   (this);
        QAxCtl    *tlbControl = new QAxCtl      (AoPROGID_ToolbarControl, toolbar, "Toolbar Control");
        QAxCtl    *tocControl = new QAxCtl      (AoPROGID_TOCControl,     split,   "TOC Control");
        QAxCtl    *mapControl = new QAxCtl      (AoPROGID_MapControl,     split,   "Map Control");

        // set properties on some of the widgets
        tlbControl->setMinimumHeight (28);
        tlbControl->setMaximumHeight (28);
        split->setOpaqueResize (false);
        QList<int> sizes (split->sizes());
        sizes[0] = 250;
        sizes[1] = 550;
        split->setSizes (sizes);
        m_comboBox->addItem ("No Decluttering");
        m_comboBox->addItem ("Leader Decluttering");
        m_comboBox->addItem ("Manual Decluttering");
        m_comboBox->addItem ("Stack Decluttering");
        m_comboBox->setEditable (false);
        m_comboBox->setEnabled (false);

        // establish the toolbar layout
        QHBoxLayout *toolbarLayout = new QHBoxLayout (toolbar);
        toolbarLayout->setMargin (0);
        toolbarLayout->addWidget (tlbControl, 1);
        toolbarLayout->addWidget (m_button);
        toolbarLayout->addWidget (m_comboBox);

        // establish the main window layout
        QVBoxLayout *rootLayout = new QVBoxLayout (this);
        rootLayout->setSpacing (0);
        rootLayout->setMargin (0);
        rootLayout->addWidget (toolbar);
        rootLayout->addWidget (split, 1);

        // set properties on this widget and connect event handlers
        resize (800, 600);
        setWindowTitle ("MOLE Manual Group Draw");
        connect (m_button, SIGNAL(clicked()), SLOT(addGroupElement()));
        connect (m_comboBox, SIGNAL(currentIndexChanged(int)), SLOT(declutter(int)));

        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;
        tlbControl->getInterface (&ipUnknown);
        IToolbarControlPtr ipToolbarControl (ipUnknown);
        if ( ! ipToolbarControl ) ABORT;
        tocControl->getInterface (&ipUnknown);
        ITOCControlPtr ipTocControl (ipUnknown);
        if ( ! ipTocControl ) ABORT;

        // buddy up and load the toolbar
        ipToolbarControl->SetBuddyControl (m_ipMapControl);
        ipTocControl->SetBuddyControl (m_ipMapControl);
        addTool (ipToolbarControl, "ControlsOpenDocCommand");
        addTool (ipToolbarControl, "ControlsAddDataCommand");
        addTool (ipToolbarControl, "ControlsSaveAsDocCommand");
        addTool (ipToolbarControl, "ControlsMapZoomPanTool");
        addTool (ipToolbarControl, "ControlsMapFullExtentCommand");
        addTool (ipToolbarControl, "ControlsMapZoomToLastExtentBackCommand");
        addTool (ipToolbarControl, "ControlsMapZoomToLastExtentForwardCommand");
        addTool (ipToolbarControl, "ControlsMapZoomToolControl");
        addTool (ipToolbarControl, "ControlsSelectTool");
        addTool (ipToolbarControl, "ControlsMapGoToCommand");
        addTool (ipToolbarControl, "ControlsMapIdentifyTool");
        addTool (ipToolbarControl, "ControlsMapMeasureTool");

        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);
    }


    ~ManualGroupDraw()
    {
        printf ("releasing MOLE resources\n");
        IMoleCoreHelperPtr ipMoleCoreHelper (CLSID_MoleCoreHelper);
        ipMoleCoreHelper->ReleaseForceElementRenderer();
    }
};