[Date Prev][Date Next][Thread Prev][Thread Next][Thread Index]

[XaraXtreme-dev] Text margin support



Find attached below a patch to enable margin support for text objects. 
The left margin, first indent and right margin markers as displayed in 
the ruler can be dragged. This finally allows hanging indents, 
bulleted lists, etc.

When there are different margins applied to parts of the selection, 
then the ruler shows hollow markers at the default positions to 
indicate that. Dragging them applies the changed margin to the 
complete selection.

The attached bitmaps go in wxOil/xrc - these are still only 
placeholders but should improve things. In particular, the tab 
dragging cursor is now the correct way round: It has arrows pointing 
left and right and a vertical line to allow precise positioning (it is 
simply a copy of the vertical guideline dragging cursor).

Martin

Attachment: gleftmar.png
Description: PNG image

Attachment: gfirstind.png
Description: PNG image

Attachment: IDCSR_TEXT_TAB.cur
Description: application/riscos

Attachment: rightmar.png
Description: PNG image

Attachment: leftmar.png
Description: PNG image

Attachment: firstind.png
Description: PNG image

Attachment: grightmar.png
Description: PNG image

Index: tools/textinfo.h
===================================================================
--- tools/textinfo.h	(Revision 1462)
+++ tools/textinfo.h	(Arbeitskopie)
@@ -155,6 +155,18 @@
 
 /********************************************************************************************
 
+>	enum TabStopDragType
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	13/07/06
+	Purpose:	Different types of drag operations on the ruler bar
+
+********************************************************************************************/
+
+enum TabStopDragType { DragNew, DragTabStop, DragLeftMargin, DragRightMargin, DragFirstIndent };
+
+/********************************************************************************************
+
 >	class TextRulerBarData:
 
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
@@ -194,6 +206,7 @@
 	INT32 CurrentRulerOrigin;           // origin of our ruler section (in user space)
 	INT32 CurrentRulerSectionWidth;     // width of our ruler section (-1 for infinite)
 	BOOL TabStopDragRunning;
+	TabStopDragType CurrentDragType;
 };
 
 /********************************************************************************************
@@ -238,18 +251,6 @@
 
 /********************************************************************************************
 
->	enum TabStopDragType
-
-	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
-	Created:	13/07/06
-	Purpose:	Different types of drag operations on the ruler bar
-
-********************************************************************************************/
-
-enum TabStopDragType { DragNew, DragTabStop, DragLeftMargin, DragRightMargin, DragFirstIndent };
-
-/********************************************************************************************
-
 >	class TextInfoBarOp : public InformationBarOp
 
 	Author:		Chris_Parks (Xara Group Ltd) <camelotdev@xxxxxxxx>
@@ -323,7 +324,11 @@
 	static void DoAddTabStop(MILLIPOINT Position);
 	static void DoAddTabStop(TxtTabStop NewTabStop);
 	static void DoApplyShownRuler();
+	static void DoChangeLeftMargin(MILLIPOINT Ordinate);
+	static void DoChangeRightMargin(MILLIPOINT Ordinate);
+	static void DoChangeFirstIndent(MILLIPOINT Ordinate);
 
+
 public:
 // the current infobar object - allow static member access
 	static InformationBarOp * pTextInfoBar;
@@ -433,10 +438,11 @@
 	static CommonAttrSet CommonAttrsToFindSet; 
 
 	// cached bitmap sizes
+	static UINT32 CurrentTabButtonWidth;
 	static UINT32 TabBitmapWidth;
-	static UINT32 TabBitmapHeight;
-	static UINT32 CurrentTabButtonWidth;
-	static UINT32 CurrentTabButtonHeight;
+	static UINT32 LeftMarginBitmapWidth;
+	static UINT32 LeftMarginBitmapHeight;
+	static UINT32 RightMarginBitmapWidth;
 };
 	
 
Index: tools/textinfo.cpp
===================================================================
--- tools/textinfo.cpp	(Revision 1462)
+++ tools/textinfo.cpp	(Arbeitskopie)
@@ -196,9 +196,10 @@
 														// that we need to find common attributes for
 // cached bitmap sizes
 UINT32 TextInfoBarOp::TabBitmapWidth;
-UINT32 TextInfoBarOp::TabBitmapHeight;
 UINT32 TextInfoBarOp::CurrentTabButtonWidth;
-UINT32 TextInfoBarOp::CurrentTabButtonHeight;
+UINT32 TextInfoBarOp::LeftMarginBitmapWidth;
+UINT32 TextInfoBarOp::LeftMarginBitmapHeight;
+UINT32 TextInfoBarOp::RightMarginBitmapWidth;
 
 FontDropDown	*TextInfoBarOp::NameDropDown = NULL;	// Font name drop-down list support for the font list and
 
@@ -435,10 +436,15 @@
 	// the sizes into the mouse click detection code in OnRulerClick())
 	// first, the size of the "current tab type" button - we ask for the left tab variant,
 	// but they should all be the same size
-	if (ok) ok = FindBitmapSize(_R(clefttab), &CurrentTabButtonWidth, &CurrentTabButtonHeight);
+	UINT32 Dummy;
+	if (ok) ok = FindBitmapSize(_R(clefttab), &CurrentTabButtonWidth, &Dummy);
 	// find out about the size of tab stop blobs - we ask for the left tab variant, but they
 	// should all be the same size
-	if (ok) ok = FindBitmapSize(_R(lefttab), &TabBitmapWidth, &TabBitmapHeight);
+	if (ok) ok = FindBitmapSize(_R(lefttab), &TabBitmapWidth, &Dummy);
+	// find out about the width and height of the first indent blob
+	if (ok) ok = FindBitmapSize(_R(leftmar), &LeftMarginBitmapWidth, &LeftMarginBitmapHeight);
+	// find out about the width of the left/right margin blobs
+	if (ok) ok = FindBitmapSize(_R(rightmar), &RightMarginBitmapWidth, &Dummy);	
 	return ok;
 }
 
@@ -3009,11 +3015,8 @@
 
 void TextInfoBarOp::TabStopDragStarting(TabStopDragType TheType)
 {
-	if (TheType == DragTabStop || TheType == DragNew)
-	{
-		TRACEUSER("wuerthne", _T("tab stop drag starting"));
-		RulerData.TabStopDragRunning = TRUE;
-	}
+	RulerData.TabStopDragRunning = TRUE;
+	RulerData.CurrentDragType = TheType;
 }
 
 /********************************************************************************************
@@ -3075,17 +3078,26 @@
 {
 	if (!pRuler->IsHorizontal()) return;
 
-	if (RulerData.IsLeftMarginValid)
-		pRuler->DrawBitmap(RulerData.LeftMargin, _R(leftmar));
-	else
-		pRuler->DrawBitmap(0, _R(gleftmar));
+	if (!(RulerData.TabStopDragRunning && RulerData.CurrentDragType == DragLeftMargin))
+	{
+		// not currently dragging the left margin, so display it if it is the same across
+		// the selection, else show the greyed out margin icon at the origin
+		if (RulerData.IsLeftMarginValid)
+			pRuler->DrawBitmap(RulerData.LeftMargin, _R(leftmar));
+		else
+			pRuler->DrawBitmap(0, _R(gleftmar));
+	}
 
-	if (RulerData.IsFirstIndentValid)
-		pRuler->DrawBitmap(RulerData.FirstIndent, _R(firstind));
-	else
-		pRuler->DrawBitmap(0, _R(gfirstind));
+	if (!(RulerData.TabStopDragRunning && RulerData.CurrentDragType == DragFirstIndent))
+	{
+		if (RulerData.IsFirstIndentValid)
+			pRuler->DrawBitmap(RulerData.FirstIndent, _R(firstind));
+		else
+			pRuler->DrawBitmap(0, _R(gfirstind));
+	}
 
-	if (RulerData.CurrentRulerSectionWidth != -1)
+	if (RulerData.CurrentRulerSectionWidth != -1
+		&& !(RulerData.TabStopDragRunning && RulerData.CurrentDragType == DragRightMargin))
 	{
 		// we only draw a right margin if we have a fixed formatting width
 		if (RulerData.IsRightMarginValid)
@@ -3119,7 +3131,8 @@
 			LastPos = (*it).GetPosition();
 		}
 
-		if (!RulerData.TabStopDragRunning)
+		if (!(RulerData.TabStopDragRunning &&
+			  (RulerData.CurrentDragType == DragNew || RulerData.CurrentDragType == DragTabStop)))
 		{
 			TRACEUSER("wuerthne", _T("redraw implicit tab stops"));
 			// draw greyed out left align tab stops at equidistant positions beyond the last defined
@@ -3187,6 +3200,7 @@
 								 Spread* pSpread, RulerBase* pRuler)
 {
 	if (!pRuler->IsHorizontal()) return FALSE;
+	TRACEUSER("wuerthne", _T("ruler click (%d,%d)"), PointerPos.x, PointerPos.y);
 	if (Click == CLICKTYPE_SINGLE)
 	{
 		// check whether the user has clicked on our homegrown "current tab" button
@@ -3255,41 +3269,83 @@
 		// to the right have priority
 		Document::SetSelectedViewAndSpread(pDocView->GetDoc(), pDocView, pSpread);
 		
-		INT32 Index = 0;
 		INT32 MatchedIndex = -1;
-		for (TxtTabStopIterator it = RulerData.pShownRuler->begin(); it != RulerData.pShownRuler->end(); ++it)
+		TxtTabStop DraggedTabStop(LeftTab, 0);
+		if (RulerData.IsRulerValid)
 		{
-			MILLIPOINT Pos = (*it).GetPosition();
-			if (PointerPos.x >= Pos - MILLIPOINT(TabBitmapWidth / 2 * PixelSize)
-				&& PointerPos.x <= Pos + MILLIPOINT(TabBitmapWidth / 2 * PixelSize))
+			INT32 Index = 0;
+			for (TxtTabStopIterator it = RulerData.pShownRuler->begin(); it != RulerData.pShownRuler->end(); ++it)
 			{
-				TRACEUSER("wuerthne", _T("hit tab no %d"), Index);
-				MatchedIndex = Index;
+				MILLIPOINT Pos = (*it).GetPosition();
+				if (PointerPos.x >= Pos - MILLIPOINT(TabBitmapWidth / 2 * PixelSize)
+					&& PointerPos.x <= Pos + MILLIPOINT(TabBitmapWidth / 2 * PixelSize))
+				{
+					TRACEUSER("wuerthne", _T("hit tab no %d"), Index);
+					MatchedIndex = Index;
+				}
+				Index++;
 			}
-			Index++;
+			
+			if (MatchedIndex != -1)
+			{
+				// delete the tab stop from the shown ruler list
+				Index = MatchedIndex;
+				TxtTabStopIterator it = RulerData.pShownRuler->begin();
+				while(it != RulerData.pShownRuler->end() && Index--) ++it;
+				if (it != RulerData.pShownRuler->end())
+				{
+					DraggedTabStop = *it;
+					RulerData.pShownRuler->erase(it);
+				}
+				else
+				{
+					// cannot really happen, but exit gracefully - we still claim the click
+					return TRUE;
+				}
+			}
 		}
+		TabStopDragType DragType = (MatchedIndex == -1) ? DragNew : DragTabStop;
 		
-		TxtTabStop DraggedTabStop(LeftTab, 0);
-		if (MatchedIndex != -1)
+		if (MatchedIndex == -1)
 		{
-			// delete the tab stop from the shown ruler list
-			Index = MatchedIndex;
-			TxtTabStopIterator it = RulerData.pShownRuler->begin();
-			while(it != RulerData.pShownRuler->end() && Index--) ++it;
-			if (it != RulerData.pShownRuler->end())
+			// no tab stop hit, but maybe the margin markers?
+			MILLIPOINT LeftMarginPos = RulerData.IsLeftMarginValid ? RulerData.LeftMargin : 0;
+			MILLIPOINT FirstIndentPos = RulerData.IsFirstIndentValid ? RulerData.FirstIndent : 0;
+			MILLIPOINT RightMarginPos = RulerData.CurrentRulerSectionWidth
+											- (RulerData.IsRightMarginValid ? RulerData.RightMargin : 0);
+
+			// We test for the left margin first because we check the vertical position to distinguish
+			// between left margin and first indent drags even when they are at the same position
+			if (PointerPos.x >= LeftMarginPos - MILLIPOINT(LeftMarginBitmapWidth / 2 * PixelSize)
+				&& PointerPos.x <= LeftMarginPos + MILLIPOINT(LeftMarginBitmapWidth / 2 * PixelSize)
+				&& PointerPos.y <= MILLIPOINT(LeftMarginBitmapHeight * PixelSize))
 			{
-				DraggedTabStop = *it;
-				RulerData.pShownRuler->erase(it);
+				TRACEUSER("wuerthne", _T("drag left margin"));
+				DragType = DragLeftMargin;
 			}
 			else
 			{
-				// cannot really happen, but exit gracefully - we still claim the click
-				return TRUE;
+				// we do not check the vertical position for other margin drags - if there was a tab stop
+				// at the same position it got priority anyway
+				if (PointerPos.x >= FirstIndentPos - MILLIPOINT(LeftMarginBitmapWidth / 2 * PixelSize)
+					&& PointerPos.x <= FirstIndentPos + MILLIPOINT(LeftMarginBitmapWidth / 2 * PixelSize))
+				{
+					TRACEUSER("wuerthne", _T("drag left margin"));
+					DragType = DragFirstIndent;
+				}
+				else
+				{
+					if (PointerPos.x >= RightMarginPos - MILLIPOINT(RightMarginBitmapWidth / 2 * PixelSize)
+						&& PointerPos.x <= RightMarginPos + MILLIPOINT(RightMarginBitmapWidth / 2 * PixelSize))
+					{
+						TRACEUSER("wuerthne", _T("drag right margin"));
+						DragType = DragRightMargin;
+					}
+				}
 			}
 		}
-		TabStopDragType DragType = (MatchedIndex == -1) ? DragNew : DragTabStop;
-		
-		if (DragType == DragNew && InHighlightSection || DragType != DragNew)
+
+		if (InHighlightSection || DragType != DragNew)
 		{
 			String_256 OpToken(OPTOKEN_TABSTOPDRAG);
 			TRACEUSER("wuerthne", _T("starting drag"));
@@ -3371,6 +3427,7 @@
 	// let us forget about it
 	RulerData.pNewRuler = NULL;
 }
+
 /********************************************************************************************
 
 >	void TextInfoBarOp::DoApplyShownRuler()
@@ -3397,10 +3454,75 @@
 	// just to avoid confusion - OnFieldChange has taken control of the object, so
 	// let us forget about it
 	RulerData.pNewRuler = NULL;
+	// we just applied what we had in pShownRuler, so UpdateRuler() will think that
+	// nothing has changed - but we want the implicit tabs to reappear
+	ForceRulerRedraw();
 }
 
 /********************************************************************************************
 
+>	void TextInfoBarOp::DoChangeLeftMargin(MILLIPOINT Ordinate)
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	18/07/06
+	Inputs:     Ordinate - the new position
+	Purpose:	Apply the changed left margin
+
+********************************************************************************************/
+
+void TextInfoBarOp::DoChangeLeftMargin(MILLIPOINT Ordinate)
+{
+	RulerData.IsLeftMarginValid = TRUE;
+	RulerData.LeftMargin = Ordinate;
+	OnFieldChange(LeftMarginA);
+	ForceRulerRedraw();
+}
+
+/********************************************************************************************
+
+>	void TextInfoBarOp::DoChangeFirstIndent(MILLIPOINT Ordinate)
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	18/07/06
+	Inputs:     Ordinate - the new position
+	Purpose:	Apply the changed first line indent
+
+********************************************************************************************/
+
+void TextInfoBarOp::DoChangeFirstIndent(MILLIPOINT Ordinate)
+{
+	RulerData.IsFirstIndentValid = TRUE;
+	RulerData.FirstIndent = Ordinate;
+	OnFieldChange(FirstIndentA);
+	ForceRulerRedraw();
+}
+
+/********************************************************************************************
+
+>	void TextInfoBarOp::DoChangeRightMargin(MILLIPOINT Ordinate)
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	18/07/06
+	Inputs:     Ordinate - the new position
+	Purpose:	Apply the changed right margin
+
+********************************************************************************************/
+
+void TextInfoBarOp::DoChangeRightMargin(MILLIPOINT Ordinate)
+{
+	// the right margin is actually an indent, i.e., an offset from the column width (otherwise
+	// we could not have a default attribute that makes sense and text objects with different
+	// widths would have to have different right margin attributes)
+	RulerData.IsRightMarginValid = TRUE;
+	// we allow the margin to become negative, i.e., to be located to the right of the
+	// column width
+	RulerData.RightMargin = RulerData.CurrentRulerSectionWidth - Ordinate;
+	OnFieldChange(RightMarginA);
+	ForceRulerRedraw();
+}
+
+/********************************************************************************************
+
 >	INT32 TextInfoBarOp::GetLogicalStoryWidth(TextStory* pStory)
 
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
@@ -3457,8 +3579,11 @@
 		// there is a focus story, so the ruler should adapt to it
 		TRACEUSER("wuerthne", _T("Focus story present"));
 		ShouldWeClaimRuler = TRUE;
-		DocRect StoryBounds=pFocusStory->GetBoundingRect();
-		DocCoord TopLeft(StoryBounds.lo.x, StoryBounds.hi.y);
+		// DocRect StoryBounds=pFocusStory->GetBoundingRect();
+		// DocCoord TopLeft(StoryBounds.lo.x, StoryBounds.hi.y);
+		const Matrix* pStoryMatrix = pFocusStory->GetpStoryMatrix();
+		DocCoord TopLeft(0,0);
+		pStoryMatrix->transform(&TopLeft);
 		UserCoord UserTopLeft = TopLeft.ToUser(pFocusStory->FindParentSpread());
 		RulerOrigin = UserTopLeft.x;
 		StoryWidth = GetLogicalStoryWidth(pFocusStory);
@@ -3472,8 +3597,10 @@
 			if (IS_A(pNode, TextStory))
 			{
 				TextStory* pStory = (TextStory*)pNode;
-				DocRect StoryBounds=pStory->GetBoundingRect();
-				DocCoord TopLeft(StoryBounds.lo.x, StoryBounds.hi.y);
+
+				const Matrix* pStoryMatrix = pStory->GetpStoryMatrix();
+				DocCoord TopLeft(0,0);
+				pStoryMatrix->transform(&TopLeft);
 				UserCoord UserTopLeft = TopLeft.ToUser(pStory->FindParentSpread());
 				
 				if (!ShouldWeClaimRuler || UserTopLeft.x < RulerOrigin)
@@ -3760,51 +3887,46 @@
 		UserPos.x -= TextInfoBarOp::GetRulerOrigin();
 		Ordinate = UserPos.x;
 		TRACEUSER("wuerthne", _T("with success at %d"), Ordinate);
-
-		if (m_pParam->m_DragType == DragNew)
+		
+		switch(m_pParam->m_DragType)
 		{
-			if (IsMouseOverRuler())
-			{
-				TextInfoBarOp::DoAddTabStop(Ordinate);
-			}
-			else
-			{
-				// do nothing (apart from redrawing the ruler bar, so the implicit
-				// tabs will be displayed again)
-				TextInfoBarOp::ForceRulerRedraw();
-			}
+			case DragNew:
+				if (IsMouseOverRuler())
+				{
+					TextInfoBarOp::DoAddTabStop(Ordinate);
+				}
+				else
+				{
+					// do nothing (apart from redrawing the ruler bar, so the implicit
+					// tabs will be displayed again)
+					TextInfoBarOp::ForceRulerRedraw();
+				}
+				break;
+			case DragTabStop:
+				if (IsMouseOverRuler())
+				{
+					TxtTabStop NewTabStop(m_pParam->m_DraggedTabStop);
+					NewTabStop.SetPosition(Ordinate);
+					TextInfoBarOp::DoAddTabStop(NewTabStop);
+				}
+				else
+				{
+					// delete the tab stop; we can do that by simply applying the shown
+					// ruler - we have removed the tab stop from the shown ruler when the
+					// drag started
+					TextInfoBarOp::DoApplyShownRuler();
+				}
+				break;
+			case DragLeftMargin:
+				TextInfoBarOp::DoChangeLeftMargin(Ordinate);
+				break;
+			case DragFirstIndent:
+				TextInfoBarOp::DoChangeFirstIndent(Ordinate);
+				break;
+			case DragRightMargin:
+				TextInfoBarOp::DoChangeRightMargin(Ordinate);
+				break;
 		}
-		else if (m_pParam->m_DragType == DragTabStop)
-		{
-			if (IsMouseOverRuler())
-			{
-				TxtTabStop NewTabStop(m_pParam->m_DraggedTabStop);
-				NewTabStop.SetPosition(Ordinate);
-				TextInfoBarOp::DoAddTabStop(NewTabStop);
-			}
-			else
-			{
-				// delete the tab stop; we can do that by simply applying the shown
-				// ruler - we have removed the tab stop from the shown ruler when the
-				// drag started
-				TextInfoBarOp::DoApplyShownRuler();
-			}
-		}
-
-#if 0
-		if (pDraggedGuideline != NULL)
-		{
-			if (IsMouseOverRuler())
-			{
-				UndoIDS = _R(IDS_OPDELETEGUIDELINE);
-				Success = DoDeleteGuideline(pDraggedGuideline);
-			}
-			else
-				Success = DoTranslateGuideline(pDraggedGuideline,Ordinate);
-		}
-		else
-			Success = !IsMouseOverRuler() && DoNewGuideline(NULL,NEXT,Type,Ordinate);
-#endif
 	}
 
 	// End the Drag
Index: Kernel/nodetxtl.cpp
===================================================================
--- Kernel/nodetxtl.cpp	(Revision 1462)
+++ Kernel/nodetxtl.cpp	(Arbeitskopie)
@@ -377,6 +377,7 @@
 	NodeCopy->mLineSpacing    = mLineSpacing;
 	NodeCopy->mLineSpaceRatio = mLineSpaceRatio;
 	NodeCopy->mLeftMargin = mLeftMargin;
+	NodeCopy->mFirstIndent = mFirstIndent;
 	NodeCopy->mRightMargin = mRightMargin;
 	NodeCopy->mpRuler = mpRuler;
 
@@ -717,25 +718,41 @@
 	SetLineSpacing(   pFormatRegion->GetLineSpacing());
 	SetLineSpaceRatio(pFormatRegion->GetLineSpaceRatio());
 
+	SetParaLeftMargin(pFormatRegion->GetLeftMargin());
+	SetParaFirstIndent(pFormatRegion->GetFirstIndent());
+	SetParaRightMargin(pFormatRegion->GetRightMargin());
+	SetRuler(pFormatRegion->GetRuler());
+	return TRUE;
+}
+
+/********************************************************************************************
+>	MILLIPOINT TextLine::GetEffectiveLeftMargin()
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	18/07/06
+	Inputs:		-
+	Returns:	the left margin value to use
+	Purpose:	Select the left margin or first indent value depending on whether this is
+				the first line in the paragraph
+********************************************************************************************/
+
+MILLIPOINT TextLine::GetEffectiveLeftMargin()
+{
 	// we need to find out whether this is the first line in the paragraph, so we know
 	// whether we need to use FirstLineIndent or LeftMargin
+
 	TextLine* pPrevLine = FindPrevLine();
 	if (pPrevLine==NULL || pPrevLine->FindEOLNode() != NULL)
 	{
 		// first line in paragraph
-		SetParaLeftMargin(pFormatRegion->GetFirstIndent());
+		return mFirstIndent;
 	}
 	else
 	{
-		// not first line, so use normal left margin
-		SetParaLeftMargin(pFormatRegion->GetLeftMargin());
+		return mLeftMargin;
 	}
-	SetParaRightMargin(pFormatRegion->GetRightMargin());
-	SetRuler(pFormatRegion->GetRuler());
-	return TRUE;
 }
 
-
 /********************************************************************************************
 >	BOOL TextLine::Format(TextStoryInfo* pStoryInfo)
 
@@ -758,7 +775,7 @@
 	MILLIPOINT PhysicalRightMargin  = pStoryInfo->StoryWidth - pStoryInfo->RightPathIndent;
 	BOOL       WordWrapping = pStoryInfo->WordWrapping;
 	MILLIPOINT RightMargin = PhysicalRightMargin - mRightMargin;
-	MILLIPOINT LeftMargin = PhysicalLeftMargin + mLeftMargin;
+	MILLIPOINT LeftMargin = PhysicalLeftMargin + GetEffectiveLeftMargin();
 
 	// if word wrapping, and not text at a point, and undoably 'do'ing op, word wrap the line
 	MILLIPOINT WrapWidth = 0;
@@ -1405,7 +1422,7 @@
 	pLineInfo->NumChars        = NumCharsToLastNonSpace;
 	pLineInfo->NumSpaces       = NumSpacesToLastNonSpace;
 	pLineInfo->justification   = GetJustification();
-	pLineInfo->ParaLeftMargin  = GetParaLeftMargin();
+	pLineInfo->ParaLeftMargin  = GetEffectiveLeftMargin();
 	pLineInfo->ParaRightMargin = GetParaRightMargin();
 	pLineInfo->Ruler           = GetRuler();
 	return TRUE;
Index: Kernel/rulers.cpp
===================================================================
--- Kernel/rulers.cpp	(Revision 1462)
+++ Kernel/rulers.cpp	(Arbeitskopie)
@@ -357,6 +357,7 @@
 	
 	// Find the spread in which the click happened
 	Spread *pSpread = pRulerPair->GetpSpread();
+	DocView* pDocView = pRulerPair->GetpDocView();
 
 	if (pSpread == NULL)
 	{
@@ -377,6 +378,14 @@
 	pSpread->DocCoordToSpreadCoord(&DocPos);
 	UserCoord UserPos = DocPos.ToUser(pSpread);
 
+	// the insignificant position on the ruler (in the narrow dimension) should not be converted to
+	// document coordinates - the result makes no sense, but we scale from OIL pixels to User coords
+	MILLIPOINT PixelSize = pDocView->GetScaledPixelWidth().MakeLong();
+	if (IsHorizontal())
+		UserPos.y = MILLIPOINT(PointerPos.y * PixelSize);
+	else
+		UserPos.x = MILLIPOINT(PointerPos.x * PixelSize);
+
 	// take the current tool ruler origin into account
 	UserCoord Offsets(0, 0);
 	if (Tool::GetCurrent())
Index: Kernel/nodetext.cpp
===================================================================
--- Kernel/nodetext.cpp	(Revision 1462)
+++ Kernel/nodetext.cpp	(Arbeitskopie)
@@ -3863,8 +3863,37 @@
 	return NodeCopy;
 }
 
+/*******************************************************************************************
+>	BOOL HorizontalTab::ExportRender(RenderRegion* pRegion)
+
+ 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+ 	Created:	18/07/06
+ 	Inputs:		pRegion - points to the export render region
+	Returns:	TRUE if rendered OK (FALSE=>use normal rendering)
+ 	Purpose:	This function is called when the render function passes through this node
+ 				It outputs the	Text Object start and end tokens
+ 	See also:   KernCode::ExportRender, on which this routine is based
+********************************************************************************************/
 BOOL HorizontalTab::ExportRender(RenderRegion* pRegion)
 {
-	/*##*/
+	// a tab is exported as a horizontal kern code
+#if EXPORT_TEXT
+ 	if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
+	{
+		// Output any valid text attributes necessary
+		((EPSRenderRegion*)pRegion)->GetValidPathAttributes();
+		((EPSRenderRegion*)pRegion)->GetValidTextAttributes();
+
+		EPSExportDC *pDC=(EPSExportDC*)pRegion->GetRenderDC();
+
+		// Use illustrator 3.0 compatible token.
+		INT32 autokern = 0;
+		pDC->OutputValue(autokern);
+		pDC->OutputValue(GetCharWidth());
+ 		pDC->OutputToken(_T("Tk"));
+		pDC->OutputNewLine();
+		return TRUE;
+	}
+#endif
 	return FALSE;
 }
Index: Kernel/nodetxtl.h
===================================================================
--- Kernel/nodetxtl.h	(Revision 1462)
+++ Kernel/nodetxtl.h	(Arbeitskopie)
@@ -235,7 +235,7 @@
 		CharPosOffset(_CharPosOffset),
 		ExtraOnChars(_ExtraOnChars), ExtraOnSpaces(_ExtraOnSpaces),
 		Width(Indent), ActiveTabType(LeftTab), ActiveTabPos(0), 
-		pLastTabVTN(NULL), AnchorPos(0) {};
+		pLastTabVTN(NULL), AnchorPos(Indent) {};
 	void AdvanceBy(MILLIPOINT Advance);
 	BOOL FinishTabSection(VisibleTextNode* pLastFormattedNode, BOOL IsLastTabSection);
 	BOOL IsAvailable(MILLIPOINT CharWidth, BOOL IsATab);
@@ -369,18 +369,22 @@
 	MILLIPOINT    GetLineSpacing()     { return mLineSpacing; }
 	FIXED16       GetLineSpaceRatio()  { return mLineSpaceRatio; }
 	MILLIPOINT    GetParaLeftMargin()  { return mLeftMargin; }
+	MILLIPOINT    GetParaFirstIndent() { return mFirstIndent; }
 	MILLIPOINT    GetParaRightMargin() { return mRightMargin; }
 	const TxtRuler* GetRuler()         { return mpRuler; }
 	void SetJustification(  Justification justification) { mJustification  = justification; }
 	void SetLineSpacing(    MILLIPOINT    Spacing)       { mLineSpacing    = Spacing; }
 	void SetLineSpaceRatio( FIXED16       SpaceRatio)    { mLineSpaceRatio = SpaceRatio; }
 	void SetParaLeftMargin( MILLIPOINT    Margin)        { mLeftMargin = Margin; }
+	void SetParaFirstIndent(MILLIPOINT    Indent)        { mFirstIndent = Indent; }
 	void SetParaRightMargin(MILLIPOINT    Margin)        { mRightMargin = Margin; }
 	void SetRuler(          const TxtRuler* pRuler)      { mpRuler = pRuler; }
 
 	MILLIPOINT GetPosInStory() { return mPosInStory; }
 	void SetPosInStory(MILLIPOINT pos) { mPosInStory=pos; }
 
+protected:
+    MILLIPOINT GetEffectiveLeftMargin();
 private:
 	MILLIPOINT mLineDescent;	// largest descent of any char in all fonts on the line
 	MILLIPOINT mLineAscent;		// largest  ascent of any char in all fonts on the line
@@ -390,6 +394,7 @@
 	MILLIPOINT    mLineSpacing;		// cache for value read from attr stack
 	FIXED16       mLineSpaceRatio;	// cache for value read from attr stack
 	MILLIPOINT mLeftMargin;	        // cache for value read from attr stack
+	MILLIPOINT mFirstIndent;        // cache for value read from attr stack
 	MILLIPOINT mRightMargin;        // cache for value read from attr stack
 	const TxtRuler* mpRuler;        // cache for value read from attr stack
 									// NB - this is a shared pointer to a list object owned by the attribute!
Index: wxOil/oilruler.cpp
===================================================================
--- wxOil/oilruler.cpp	(Revision 1462)
+++ wxOil/oilruler.cpp	(Arbeitskopie)
@@ -1002,6 +1002,15 @@
 
 	// Convert the click position to OIL coordinates before passing to the kernel.
 	OilCoord ocoord = ClientToOil(pDocView, point);
+	// Only the ordinate in the main ruler direction should be converted, the coordinate
+	// translation does not make any sense in the other direction. Instead, we keep the
+	// window-relative coordinate but we change it in such a way that the paper side of
+	// the ruler is coordinate 0 because the Kernel does not know the RenderWidth of the
+	// ruler and the paper side is the significant side where the blobs are drawn.
+	if (IsHorizontal())
+		ocoord.y = RenderWidth - point.y;
+	else
+		ocoord.x = RenderWidth - point.x;
 	return pKernelRuler->OnRulerClick(ocoord, t, m_LastClickMods);
 }