bnc#821916: Better algorithm to find placeholder shape.

Placeholder type seems to be more relevant than index.

Change-Id: I9d6c6cad8e0a51b2385801f65d7d1c697ad7998e
diff --git a/include/oox/ppt/pptshape.hxx b/include/oox/ppt/pptshape.hxx
index 41941fd..18126d3 100644
--- a/include/oox/ppt/pptshape.hxx
+++ b/include/oox/ppt/pptshape.hxx
@@ -58,8 +58,11 @@ public:
    void setPlaceholder( oox::drawingml::ShapePtr pPlaceholder ) { mpPlaceholder = pPlaceholder; }
    void setModelId( const OUString& rId ) { msModelId = rId; }

    static oox::drawingml::ShapePtr findPlaceholder( const sal_Int32 nMasterPlaceholder, std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly = false );
    static oox::drawingml::ShapePtr findPlaceholderByIndex( const sal_Int32 nIdx, std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly = false );
    static oox::drawingml::ShapePtr findPlaceholder( const sal_Int32 nFirstSubType,
            const sal_Int32 nSecondSubType, const OptValue< sal_Int32 >& oSubTypeIndex,
            std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly = false );
    static oox::drawingml::ShapePtr findPlaceholderByIndex( const sal_Int32 nIdx,
            std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly = false );

    static oox::drawingml::TextListStylePtr getSubTypeTextListStyle( const SlidePersist& rSlidePersist, sal_Int32 nSubType );

diff --git a/oox/source/ppt/pptgraphicshapecontext.cxx b/oox/source/ppt/pptgraphicshapecontext.cxx
index 124b35e..229867d 100644
--- a/oox/source/ppt/pptgraphicshapecontext.cxx
+++ b/oox/source/ppt/pptgraphicshapecontext.cxx
@@ -53,13 +53,6 @@ PPTGraphicShapeContext::PPTGraphicShapeContext( ContextHandler2Helper& rParent, 
{
}

// if nFirstPlaceholder can't be found, it will be searched for nSecondPlaceholder
static oox::drawingml::ShapePtr findPlaceholder( sal_Int32 nFirstPlaceholder, sal_Int32 nSecondPlaceholder, std::vector< oox::drawingml::ShapePtr >& rShapes )
{
    oox::drawingml::ShapePtr pPlaceholder = PPTShape::findPlaceholder( nFirstPlaceholder, rShapes );
    return !nSecondPlaceholder || pPlaceholder.get() ? pPlaceholder : PPTShape::findPlaceholder( nSecondPlaceholder, rShapes );
}

ContextHandlerRef PPTGraphicShapeContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
{
    switch( aElementToken )
@@ -135,12 +128,14 @@ ContextHandlerRef PPTGraphicShapeContext::onCreateContext( sal_Int32 aElementTok
                    if ( nFirstPlaceholder )
                    {
                        if ( eShapeLocation == Layout )     // for layout objects the referenced object can be found within the same shape tree
                            pPlaceholder = findPlaceholder( nFirstPlaceholder, nSecondPlaceholder, mpSlidePersistPtr->getShapes()->getChildren() );
                            pPlaceholder = PPTShape::findPlaceholder( nFirstPlaceholder, nSecondPlaceholder,
                                    pPPTShapePtr->getSubTypeIndex(), mpSlidePersistPtr->getShapes()->getChildren(), true );
                        else if ( eShapeLocation == Slide ) // normal slide shapes have to search within the corresponding master tree for referenced objects
                        {
                            SlidePersistPtr pMasterPersist( mpSlidePersistPtr->getMasterPersist() );
                            if ( pMasterPersist.get() )
                                pPlaceholder = findPlaceholder( nFirstPlaceholder, nSecondPlaceholder, pMasterPersist->getShapes()->getChildren() );
                                pPlaceholder = PPTShape::findPlaceholder( nFirstPlaceholder, nSecondPlaceholder,
                                        pPPTShapePtr->getSubTypeIndex(), pMasterPersist->getShapes()->getChildren() );
                        }
                    }
                }
diff --git a/oox/source/ppt/pptshape.cxx b/oox/source/ppt/pptshape.cxx
index 8b02702..cca5e37 100644
--- a/oox/source/ppt/pptshape.cxx
+++ b/oox/source/ppt/pptshape.cxx
@@ -227,7 +227,7 @@ void PPTShape::addShape(
            if( mnSubType && getSubTypeIndex().has() && meShapeLocation == Layout ) {
                oox::drawingml::ShapePtr pPlaceholder = PPTShape::findPlaceholderByIndex( getSubTypeIndex().get(), rSlidePersist.getShapes()->getChildren(), true );
                if (!pPlaceholder.get())
                    pPlaceholder = PPTShape::findPlaceholder( mnSubType, rSlidePersist.getShapes()->getChildren(), true );
                    pPlaceholder = PPTShape::findPlaceholder( mnSubType, 0, getSubTypeIndex(), rSlidePersist.getShapes()->getChildren(), true );

                if (pPlaceholder.get()) {
                    if( maSize.Width == 0 || maSize.Height == 0 ) {
@@ -386,25 +386,67 @@ namespace
    }
}

oox::drawingml::ShapePtr PPTShape::findPlaceholder( const sal_Int32 nMasterPlaceholder, std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly )
// Function to find placeholder (ph) for a shape. No idea how MSO implements this, but
// this order seems to work quite well (probably it's unnecessary complicated / wrong):
// 1. ph with nFirstSubType and the same oSubTypeIndex
// 2. ph with nFirstSubType
// 3. ph with nSecondSubType and the same oSubTypeIndex
// 4. ph with the same oSubTypeIndex
oox::drawingml::ShapePtr PPTShape::findPlaceholder( sal_Int32 nFirstSubType, sal_Int32 nSecondSubType,
    const OptValue< sal_Int32 >& oSubTypeIndex, std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly )
{
    oox::drawingml::ShapePtr aShapePtr;
    oox::drawingml::ShapePtr aChoiceShapePtr1;
    oox::drawingml::ShapePtr aChoiceShapePtr2;
    oox::drawingml::ShapePtr aChoiceShapePtr3;
    std::vector< oox::drawingml::ShapePtr >::reverse_iterator aRevIter( rShapes.rbegin() );
    while( aRevIter != rShapes.rend() )
    while (aRevIter != rShapes.rend())
    {
        if ( (*aRevIter)->getSubType() == nMasterPlaceholder &&
             ( !bMasterOnly || ShapeLocationIsMaster((*aRevIter).get()) ) )
        if (!bMasterOnly || ShapeLocationIsMaster((*aRevIter).get()))
        {
            aShapePtr = *aRevIter;
            break;
            if ((*aRevIter)->getSubTypeIndex() == oSubTypeIndex)
            {
                if ((*aRevIter)->getSubType() == nFirstSubType)
                {
                    aShapePtr = *aRevIter;
                    break;
                }
                else if ((*aRevIter)->getSubType() == nSecondSubType && !aChoiceShapePtr2.get())
                    aChoiceShapePtr2 = *aRevIter;
                else if (!aChoiceShapePtr3.get())
                    aChoiceShapePtr3 = *aRevIter;
            }
            else if ((*aRevIter)->getSubType() == nFirstSubType && !aChoiceShapePtr1.get())
                aChoiceShapePtr1 = *aRevIter;
        }
        std::vector< oox::drawingml::ShapePtr >& rChildren = (*aRevIter)->getChildren();
        aShapePtr = findPlaceholder( nMasterPlaceholder, rChildren, bMasterOnly );
        if ( aShapePtr.get() )
        aChoiceShapePtr3 = findPlaceholder( nFirstSubType, nSecondSubType, oSubTypeIndex, rChildren, bMasterOnly );
        if (aChoiceShapePtr3.get())
        {
            if (aChoiceShapePtr3->getSubType() == nFirstSubType)
            {
                if (aChoiceShapePtr3->getSubTypeIndex() == oSubTypeIndex)
                    aShapePtr = aChoiceShapePtr3;
                else
                    aChoiceShapePtr1 = aChoiceShapePtr3;
            }
            else if (aChoiceShapePtr3->getSubType() == nSecondSubType &&
                    aChoiceShapePtr3->getSubTypeIndex() == oSubTypeIndex)
            {
                aChoiceShapePtr2 = aChoiceShapePtr3;
            }
        }
        if (aShapePtr.get())
            break;
        ++aRevIter;
    }
    return aShapePtr;
    if (aShapePtr.get())
        return aShapePtr;
    if (aChoiceShapePtr1.get())
        return aChoiceShapePtr1;
    if (aChoiceShapePtr2.get())
        return aChoiceShapePtr2;
    return aChoiceShapePtr3;
}

oox::drawingml::ShapePtr PPTShape::findPlaceholderByIndex( const sal_Int32 nIdx, std::vector< oox::drawingml::ShapePtr >& rShapes, bool bMasterOnly )
diff --git a/oox/source/ppt/pptshapecontext.cxx b/oox/source/ppt/pptshapecontext.cxx
index f4dd004..c80134f 100644
--- a/oox/source/ppt/pptshapecontext.cxx
+++ b/oox/source/ppt/pptshapecontext.cxx
@@ -53,45 +53,6 @@ PPTShapeContext::PPTShapeContext( ContextHandler2Helper& rParent, const SlidePer
{
}

oox::drawingml::ShapePtr findPlaceholder( const sal_Int32 nMasterPlaceholder, const OptValue< sal_Int32 >& oSubTypeIndex, std::vector< oox::drawingml::ShapePtr >& rShapes )
{
    oox::drawingml::ShapePtr aShapePtr;
    oox::drawingml::ShapePtr aChoiceShapePtr1;
    oox::drawingml::ShapePtr aChoiceShapePtr2;
    std::vector< oox::drawingml::ShapePtr >::reverse_iterator aRevIter( rShapes.rbegin() );
    while( aRevIter != rShapes.rend() )
    {
        if ( (*aRevIter)->getSubType() == nMasterPlaceholder )
        {
            if( !oSubTypeIndex.has() && aChoiceShapePtr1 == 0 )
                aChoiceShapePtr1 = *aRevIter;
            else if( aChoiceShapePtr2 == 0 )
                aChoiceShapePtr2 = *aRevIter;
            if( (*aRevIter)->getSubTypeIndex() == oSubTypeIndex )
            {
                aShapePtr = *aRevIter;
                break;
            }
        }
        std::vector< oox::drawingml::ShapePtr >& rChildren = (*aRevIter)->getChildren();
        aShapePtr = findPlaceholder( nMasterPlaceholder, oSubTypeIndex, rChildren );
        if ( aShapePtr.get() )
            break;
        ++aRevIter;
    }
    if( aShapePtr == 0 )
        return aChoiceShapePtr1 ? aChoiceShapePtr1 : aChoiceShapePtr2;
    return aShapePtr;
}

// if nFirstPlaceholder can't be found, it will be searched for nSecondPlaceholder
oox::drawingml::ShapePtr findPlaceholder( sal_Int32 nFirstPlaceholder, sal_Int32 nSecondPlaceholder,
    const OptValue< sal_Int32 >& oSubTypeIndex, std::vector< oox::drawingml::ShapePtr >& rShapes )
{
    oox::drawingml::ShapePtr pPlaceholder = findPlaceholder( nFirstPlaceholder, oSubTypeIndex, rShapes );
    return !nSecondPlaceholder || pPlaceholder.get() ? pPlaceholder : findPlaceholder( nSecondPlaceholder, oSubTypeIndex, rShapes );
}

ContextHandlerRef PPTShapeContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
{
    if( getNamespace( aElementToken ) == NMSP_dsp )
@@ -165,25 +126,16 @@ ContextHandlerRef PPTShapeContext::onCreateContext( sal_Int32 aElementToken, con
                              oox::drawingml::ShapePtr pPlaceholder;
                              if ( eShapeLocation == Layout )       // for layout objects the referenced object can be found within the same shape tree
                              {
                                  if( pPPTShapePtr->getSubTypeIndex().has() )
                                      pPlaceholder = PPTShape::findPlaceholderByIndex( pPPTShapePtr->getSubTypeIndex().get(), mpSlidePersistPtr->getShapes()->getChildren() );
                                  if ( !pPlaceholder.get() )
                                      pPlaceholder = findPlaceholder( nFirstPlaceholder, nSecondPlaceholder, pPPTShapePtr->getSubTypeIndex(),
                                                                      mpSlidePersistPtr->getShapes()->getChildren() );
                                  pPlaceholder = PPTShape::findPlaceholder( nFirstPlaceholder, nSecondPlaceholder,
                                          pPPTShapePtr->getSubTypeIndex(), mpSlidePersistPtr->getShapes()->getChildren(), true );
                              }
                              else if ( eShapeLocation == Slide )   // normal slide shapes have to search within the corresponding master tree for referenced objects
                              {
                                  SlidePersistPtr pMasterPersist( mpSlidePersistPtr->getMasterPersist() );
                                  if ( pMasterPersist.get() ) {
                                      if( pPPTShapePtr->getSubTypeIndex().has() )
                                          pPlaceholder = PPTShape::findPlaceholderByIndex( pPPTShapePtr->getSubTypeIndex().get(), pMasterPersist->getShapes()->getChildren() );
                                      // TODO: Check if this is required for non-notes pages as well...
                                      if ( !pPlaceholder.get() || ( pMasterPersist->isNotesPage() && pPlaceholder->getSubType() != nFirstPlaceholder &&
                                                                                                     pPlaceholder->getSubType() != nSecondPlaceholder ) )
                                      {
                                          pPlaceholder = findPlaceholder( nFirstPlaceholder, nSecondPlaceholder,
                                                                          pPPTShapePtr->getSubTypeIndex(), pMasterPersist->getShapes()->getChildren() );
                                      }
                                  if ( pMasterPersist.get() )
                                  {
                                      pPlaceholder = PPTShape::findPlaceholder( nFirstPlaceholder, nSecondPlaceholder,
                                              pPPTShapePtr->getSubTypeIndex(), pMasterPersist->getShapes()->getChildren() );
                                  }
                              }
                              if ( pPlaceholder.get() )