summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2017-07-31 14:46:03 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2017-07-31 21:26:03 +0200
commitd62d07b3d29014f76c0d676c891cbafa80d0765f (patch)
tree0e5f5c4bcd2348a1ef98ad06142c0573e1c7684b
parent22334f8a003d26c71d772a8e00fc80b7e6d2bef1 (diff)
Corrected HitTest for layouted text
For text layouted using EditEngine the HitTest in SVX is identifying Field like URLs. Thus ist is better to use the anyways more precise primitives for HitTest (rotation/shear/ mirror, ...). This was necessary since the former mechanism which used a combination of primitive-beased HitTest and then using an Outliner to get the position/content of the Field landed on different positions e.g. when the layout needed to use multiple lines for the contained URL, but there could be more cases found. Adapted the text decompositon, the primitive HitTest and the TextHirearchyFieldPrimitive2D accordingly. Change-Id: Ice559e20d02547fdcfcf9783e7cc5481706aab03 Reviewed-on: https://gerrit.libreoffice.org/40591 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
-rw-r--r--drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx23
-rw-r--r--drawinglayer/source/processor2d/hittestprocessor2d.cxx9
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx12
-rw-r--r--include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx10
-rw-r--r--include/drawinglayer/processor2d/hittestprocessor2d.hxx15
-rw-r--r--include/svx/sdrhittesthelper.hxx9
-rw-r--r--svx/source/svdraw/sdrhittesthelper.cxx22
-rw-r--r--svx/source/svdraw/svdotextdecomposition.cxx13
-rw-r--r--svx/source/svdraw/svdview.cxx92
9 files changed, 152 insertions, 53 deletions
diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
index 86883e43b6cb..45fa8531bee3 100644
--- a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
@@ -95,11 +95,28 @@ namespace drawinglayer
TextHierarchyFieldPrimitive2D::TextHierarchyFieldPrimitive2D(
const Primitive2DContainer& rChildren,
const FieldType& rFieldType,
- const OUString& rString)
+ const std::vector< std::pair< OUString, OUString>>* pNameValue)
: GroupPrimitive2D(rChildren),
meType(rFieldType),
- maString(rString)
+ meNameValue()
{
+ if (nullptr != pNameValue)
+ {
+ meNameValue = *pNameValue;
+ }
+ }
+
+ OUString TextHierarchyFieldPrimitive2D::getValue(const OUString& rName) const
+ {
+ for (const std::pair< OUString, OUString >& candidate : meNameValue)
+ {
+ if (candidate.first.equals(rName))
+ {
+ return candidate.second;
+ }
+ }
+
+ return OUString();
}
bool TextHierarchyFieldPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -109,7 +126,7 @@ namespace drawinglayer
const TextHierarchyFieldPrimitive2D& rCompare = static_cast<const TextHierarchyFieldPrimitive2D&>(rPrimitive);
return (getType() == rCompare.getType()
- && getString() == rCompare.getString());
+ && getNameValue() == rCompare.getNameValue());
}
return false;
diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
index 82ccfd00b7fe..cae95d7e6a51 100644
--- a/drawinglayer/source/processor2d/hittestprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
@@ -45,6 +45,8 @@ namespace drawinglayer
: BaseProcessor2D(rViewInformation),
maDiscreteHitPosition(),
mfDiscreteHitTolerance(0.0),
+ maHitStack(),
+ mbCollectHitStack(false),
mbHit(false),
mbHitTextOnly(bHitTextOnly)
{
@@ -536,6 +538,13 @@ namespace drawinglayer
break;
}
}
+
+ if (getHit() && getCollectHitStack())
+ {
+ /// push candidate to HitStack to create it. This only happens when a hit is found and
+ /// creating the HitStack was requested (see collectHitStack)
+ maHitStack.append(primitive2d::Primitive2DReference(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate)));
+ }
}
} // end of namespace processor2d
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index efe3342b7cf9..c8ec56088085 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -977,6 +977,7 @@ namespace drawinglayer
const OString aCommentStringCommon("FIELD_SEQ_BEGIN");
const OString aCommentStringPage("FIELD_SEQ_BEGIN;PageField");
const OString aCommentStringEnd("FIELD_SEQ_END");
+ OUString aURL;
switch(rFieldPrimitive.getType())
{
@@ -992,8 +993,13 @@ namespace drawinglayer
}
case drawinglayer::primitive2d::FIELD_TYPE_URL :
{
- const OUString& rURL = rFieldPrimitive.getString();
- mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const sal_uInt8* >(rURL.getStr()), 2 * rURL.getLength()));
+ aURL = rFieldPrimitive.getValue("URL");
+
+ if (!aURL.isEmpty())
+ {
+ mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()), 2 * aURL.getLength()));
+ }
+
break;
}
}
@@ -1015,7 +1021,7 @@ namespace drawinglayer
(sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
vcl::PDFExtOutDevBookmarkEntry aBookmark;
aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
- aBookmark.aBookmark = rFieldPrimitive.getString();
+ aBookmark.aBookmark = aURL;
std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
rBookmarks.push_back( aBookmark );
}
diff --git a/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx b/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
index 093ccce65fa3..5234081584a3 100644
--- a/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
@@ -148,19 +148,23 @@ namespace drawinglayer
class DRAWINGLAYER_DLLPUBLIC TextHierarchyFieldPrimitive2D : public GroupPrimitive2D
{
private:
+ /// field type definition
FieldType meType;
- OUString maString;
+
+ /// field data as name/value pairs (dependent of field type definition)
+ std::vector< std::pair< OUString, OUString>> meNameValue;
public:
/// constructor
TextHierarchyFieldPrimitive2D(
const Primitive2DContainer& rChildren,
const FieldType& rFieldType,
- const OUString& rString);
+ const std::vector< std::pair< OUString, OUString>>* pNameValue = nullptr);
/// data read access
FieldType getType() const { return meType; }
- const OUString& getString() const { return maString; }
+ const std::vector< std::pair< OUString, OUString>>& getNameValue() const { return meNameValue; }
+ OUString getValue(const OUString& rName) const;
/// compare operator
virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
diff --git a/include/drawinglayer/processor2d/hittestprocessor2d.hxx b/include/drawinglayer/processor2d/hittestprocessor2d.hxx
index 466bb948df49..e5002cb6cf2f 100644
--- a/include/drawinglayer/processor2d/hittestprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/hittestprocessor2d.hxx
@@ -46,6 +46,13 @@ namespace drawinglayer
/// discrete HitTolerance
double mfDiscreteHitTolerance;
+ /// stack of HitPrimitives, taken care of during HitTest run
+ primitive2d::Primitive2DContainer maHitStack;
+
+ /// flag if HitStack shall be collected as part of the result, default is false
+ bool mbCollectHitStack : 1;
+
+ /// Boolean to flag if a hit was found. If yes, fast exit is taken
bool mbHit : 1;
/// flag to concentrate on text hits only
@@ -69,9 +76,17 @@ namespace drawinglayer
bool bHitTextOnly);
virtual ~HitTestProcessor2D() override;
+ /// switch on collecting primitives for a found hit on maHitStack, default is off
+ void collectHitStack(bool bCollect) { mbCollectHitStack = bCollect; }
+
+ /// get HitStack of primitives, first is the one that created the hit, last is the
+ /// top-most
+ const primitive2d::Primitive2DContainer& getHitStack() const { return maHitStack; }
+
/// data read access
const basegfx::B2DPoint& getDiscreteHitPosition() const { return maDiscreteHitPosition; }
double getDiscreteHitTolerance() const { return mfDiscreteHitTolerance; }
+ bool getCollectHitStack() const { return mbCollectHitStack; }
bool getHit() const { return mbHit; }
bool getHitTextOnly() const { return mbHitTextOnly; }
};
diff --git a/include/svx/sdrhittesthelper.hxx b/include/svx/sdrhittesthelper.hxx
index b8da54c7d839..17a36c4b1477 100644
--- a/include/svx/sdrhittesthelper.hxx
+++ b/include/svx/sdrhittesthelper.hxx
@@ -32,6 +32,7 @@ class SdrLayerIDSet;
class SdrObjList;
namespace sdr { namespace contact { class ViewObjectContact; }}
namespace basegfx { class B2DPoint; }
+namespace drawinglayer { namespace primitive2d { class Primitive2DContainer; }}
// Wrappers for classic Sdr* Mode/View classes
@@ -42,7 +43,9 @@ SVX_DLLPUBLIC SdrObject* SdrObjectPrimitiveHit(
sal_uInt16 nTol,
const SdrPageView& rSdrPageView,
const SdrLayerIDSet* pVisiLayer,
- bool bTextOnly);
+ bool bTextOnly,
+ /// allow getting back an evtl. resulting primitive stack which lead to a hit
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer = nullptr);
SVX_DLLPUBLIC SdrObject* SdrObjListPrimitiveHit(
const SdrObjList& rList,
@@ -59,7 +62,9 @@ SVX_DLLPUBLIC bool ViewObjectContactPrimitiveHit(
const sdr::contact::ViewObjectContact& rVOC,
const basegfx::B2DPoint& rHitPosition,
double fLogicHitTolerance,
- bool bTextOnly);
+ bool bTextOnly,
+ /// allow to get back the stack of primitives that lead to the hit
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer = nullptr);
#endif // INCLUDED_SVX_SDRHITTESTHELPER_HXX
diff --git a/svx/source/svdraw/sdrhittesthelper.cxx b/svx/source/svdraw/sdrhittesthelper.cxx
index a1bdb47f8879..cf9191c8428e 100644
--- a/svx/source/svdraw/sdrhittesthelper.cxx
+++ b/svx/source/svdraw/sdrhittesthelper.cxx
@@ -39,7 +39,8 @@ SdrObject* SdrObjectPrimitiveHit(
sal_uInt16 nTol,
const SdrPageView& rSdrPageView,
const SdrLayerIDSet* pVisiLayer,
- bool bTextOnly)
+ bool bTextOnly,
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
{
SdrObject* pResult = nullptr;
@@ -77,7 +78,7 @@ SdrObject* SdrObjectPrimitiveHit(
const sdr::contact::ViewObjectContact& rVOC = rObject.GetViewContact().GetViewObjectContact(
rSdrPageView.GetPageWindow(0)->GetObjectContact());
- if(ViewObjectContactPrimitiveHit(rVOC, aHitPosition, fLogicTolerance, bTextOnly))
+ if(ViewObjectContactPrimitiveHit(rVOC, aHitPosition, fLogicTolerance, bTextOnly, pHitContainer))
{
pResult = const_cast< SdrObject* >(&rObject);
}
@@ -117,7 +118,8 @@ bool ViewObjectContactPrimitiveHit(
const sdr::contact::ViewObjectContact& rVOC,
const basegfx::B2DPoint& rHitPosition,
double fLogicHitTolerance,
- bool bTextOnly)
+ bool bTextOnly,
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
{
basegfx::B2DRange aObjectRange(rVOC.getObjectRange());
@@ -146,11 +148,23 @@ bool ViewObjectContactPrimitiveHit(
fLogicHitTolerance,
bTextOnly);
+ // ask for HitStack
+ aHitTestProcessor2D.collectHitStack(true);
+
// feed it with the primitives
aHitTestProcessor2D.process(rSequence);
// deliver result
- return aHitTestProcessor2D.getHit();
+ if (aHitTestProcessor2D.getHit())
+ {
+ if (pHitContainer)
+ {
+ // fetch HitStack primitives if requested
+ *pHitContainer = aHitTestProcessor2D.getHitStack();
+ }
+
+ return true;
+ }
}
}
}
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx
index 0ba47e9b4c83..2e30cc3edf5f 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -479,15 +479,22 @@ namespace
if(pURLField)
{
- pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
+ // extended this to hold more of the contents of the original
+ // SvxURLField since that stuff is still used in HitTest and e.g. Calc
+ std::vector< std::pair< OUString, OUString>> meValues;
+ meValues.push_back(std::pair< OUString, OUString>("URL", pURLField->GetURL()));
+ meValues.push_back(std::pair< OUString, OUString>("Representation", pURLField->GetRepresentation()));
+ meValues.push_back(std::pair< OUString, OUString>("TargetFrame", pURLField->GetTargetFrame()));
+ meValues.push_back(std::pair< OUString, OUString>("SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat()))));
+ pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues);
}
else if(pPageField)
{
- pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, "");
+ pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE);
}
else
{
- pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, "");
+ pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON);
}
}
diff --git a/svx/source/svdraw/svdview.cxx b/svx/source/svdraw/svdview.cxx
index f11e6f6b0055..2ec9a93fef13 100644
--- a/svx/source/svdraw/svdview.cxx
+++ b/svx/source/svdraw/svdview.cxx
@@ -50,6 +50,7 @@
#include <svx/sdrhittesthelper.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <drawinglayer/processor2d/contourextractor2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
SdrViewEvent::SdrViewEvent()
@@ -440,45 +441,66 @@ SdrHitKind SdrView::PickAnything(const Point& rLogicPos, SdrViewEvent& rVEvt) co
SdrTextObj* pTextObj=dynamic_cast<SdrTextObj*>( pHitObj );
if (pTextObj!=nullptr && pTextObj->HasText())
{
- bool bTEHit(pPV &&
- SdrObjectPrimitiveHit(*pTextObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true));
-
- if (bTEHit)
+ // use the primitive-based HitTest which is more accurate anyways. It
+ // will correctly handle rotated/mirrored/sheared/scaled text and can
+ // now return a HitContainer containing the primitive hierarchy of the
+ // primitive that triggered the hit. The first entry is that primitive,
+ // the others are the full stack of primitives leading to that one which
+ // includes grouping primitives (like TextHierarchyPrimitives we deed here)
+ // but also all decomposed ones which lead to the creation of that primitive
+ drawinglayer::primitive2d::Primitive2DContainer aHitContainer;
+ const bool bTEHit(pPV && SdrObjectPrimitiveHit(*pTextObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true, &aHitContainer));
+
+ if (bTEHit && !aHitContainer.empty())
{
- tools::Rectangle aTextRect;
- tools::Rectangle aAnchor;
- SdrOutliner* pOutliner = &pTextObj->ImpGetDrawOutliner();
- if( pTextObj->GetModel() )
- pOutliner = &pTextObj->GetModel()->GetHitTestOutliner();
-
- pTextObj->TakeTextRect( *pOutliner, aTextRect, false, &aAnchor, false );
-
- // #i73628# Use a text-relative position for hit test in hit test outliner
- Point aTemporaryTextRelativePosition(aLocalLogicPosition - aTextRect.TopLeft());
-
- // account for FitToSize
- bool bFitToSize(pTextObj->IsFitToSize());
- if (bFitToSize) {
- Fraction aX(aTextRect.GetWidth()-1,aAnchor.GetWidth()-1);
- Fraction aY(aTextRect.GetHeight()-1,aAnchor.GetHeight()-1);
- ResizePoint(aTemporaryTextRelativePosition,Point(),aX,aY);
+ // search for TextHierarchyFieldPrimitive2D which contains the needed information
+ // about a possible URLField
+ const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D* pTextHierarchyFieldPrimitive2D = nullptr;
+
+ for (const drawinglayer::primitive2d::Primitive2DReference& xReference : aHitContainer)
+ {
+ if (xReference.is())
+ {
+ // try to cast to drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D implementation
+ pTextHierarchyFieldPrimitive2D = dynamic_cast<const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D*>(xReference.get());
+
+ if (pTextHierarchyFieldPrimitive2D)
+ {
+ break;
+ }
+ }
}
- // account for rotation
- const GeoStat& rGeo=pTextObj->GetGeoStat();
- if (rGeo.nRotationAngle!=0) RotatePoint(aTemporaryTextRelativePosition,Point(),-rGeo.nSin,rGeo.nCos); // -sin for Unrotate
- // we currently don't account for ticker text
- if(mpActualOutDev && mpActualOutDev->GetOutDevType() == OUTDEV_WINDOW)
+
+ if (nullptr != pTextHierarchyFieldPrimitive2D)
{
- OutlinerView aOLV(pOutliner, static_cast<vcl::Window*>(mpActualOutDev.get()));
- const EditView& aEV=aOLV.GetEditView();
- const SvxFieldItem* pItem=aEV.GetField(aTemporaryTextRelativePosition);
- if (pItem!=nullptr) {
- const SvxFieldData* pFld=pItem->GetField();
- const SvxURLField* pURL=dynamic_cast<const SvxURLField*>( pFld );
- if (pURL!=nullptr) {
- eHit=SdrHitKind::UrlField;
- rVEvt.pURLField=pURL;
+ if (drawinglayer::primitive2d::FieldType::FIELD_TYPE_URL == pTextHierarchyFieldPrimitive2D->getType())
+ {
+ // problem with the old code is that a *pointer* to an instance of
+ // SvxURLField is set in the Event which is per se not good since that
+ // data comes from a temporary EditEngine's data and could vanish any
+ // moment. Have to replace for now with a static instance that gets
+ // filled/initialized from the original data held in the TextHierarchyField-
+ // Primitive2D (see impTextBreakupHandler::impCheckFieldPrimitive).
+ // Unfortunately things like 'TargetFrame' are still used in Calc, so this
+ // can currently not get replaced. For the future the Name/Value vector or
+ // the TextHierarchyFieldPrimitive2D itself should/will be used for handling
+ // that data
+ static SvxURLField aSvxURLField;
+
+ aSvxURLField.SetURL(pTextHierarchyFieldPrimitive2D->getValue("URL"));
+ aSvxURLField.SetRepresentation(pTextHierarchyFieldPrimitive2D->getValue("Representation"));
+ aSvxURLField.SetTargetFrame(pTextHierarchyFieldPrimitive2D->getValue("TargetFrame"));
+ const OUString aFormat(pTextHierarchyFieldPrimitive2D->getValue("SvxURLFormat"));
+
+ if (!aFormat.isEmpty())
+ {
+ aSvxURLField.SetFormat(static_cast<SvxURLFormat>(aFormat.toInt32()));
}
+
+ // set HitKind and pointer to local static instance in the Event
+ // to comply to old stuff
+ eHit = SdrHitKind::UrlField;
+ rVEvt.pURLField = &aSvxURLField;
}
}
}