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

[XaraXtreme-dev] Tab rulers/margins update



Find attached a big GUI update for tab rulers and margins to fix most 
outstanding issues. It also includes new ruler graphics. As usual, the 
graphics go in wxOil/xrc. Note to whoever checks this in: The file 
wxOil/xrc/IDCSR_TEXT_TAB.cur can be deleted from svn.

Text ruler functional changes/fixes:
   - Decimal tab stops used to format the decimal point character 
     centered around the tab position. This does not look right 
     when mixing numbers with and without a decimal point, so the 
     decimal point should be fully to the right of the tab 
     position. [thanks to Tobias Burnus for spotting this]
   - Up to now, the decimal point was hardwired as the dot 
     character. Now, the decimal point character as stored in the 
     tab stop is used. That character is set according to the 
     current locale when the tab stop is created and saved with 
     the document. For instance, in German locales, numbers are 
     aligned at commas instead of dots.

Text ruler GUI changes/fixes:
   - Fixed ruler display problems if a text object far to the 
     right (e.g., on the pasteboard) was selected
   - Selecting the text tool with text characters selected (as 
     opposed to just having the caret in a text object) now 
     correctly displays the text ruler.
   - Escape during drags is handled correctly (original ruler 
     display is restored)
   - Grid snapping for tab stops and margins works correctly and 
     the mouse followers (lines in the ruler that move with the 
     pointer during the drag) correctly indicate the snapping
   - Double-clicks in the text ruler area are ignored (instead of 
     creating a guide line as happened before), right clicks 
     bring up the standard ruler menu (now, only left button 
     clicks are offered to the current tool)
   - The graphic for implicit tabs has been changed to a grey "L" 
     similar to that used in Word
   - The mouse cursors now indicate what is being dragged (which 
     margin or which kind of tab)
   - The status line shows help text when the pointer is over the 
     "current tab" icon and when it is over the highlighted text 
     ruler area
   - The status line shows information about the current drag 
     operation
   - When the pointer is moved off the ruler while dragging a tab 
     stop, the mouse cursor changes to a "delete tab" cursor with 
     a red cross to indicate that releasing the button will 
     delete the tab stop and the status line text reflects this

Non ruler-related text tool fix:
   - Temporary fix to prevent function key presses from entering 
     funny Unicode characters into text objects.

Martin

Attachment: IDCSR_TEXT_FIRSTIND.cur
Description: application/riscos

Attachment: IDCSR_TEXT_LEFTMAR.cur
Description: application/riscos

Attachment: IDCSR_TEXT_RIGHTMAR.cur
Description: application/riscos

Attachment: IDCSR_TEXT_DELTAB.cur
Description: application/riscos

Attachment: IDCSR_TEXT_CENTTAB.cur
Description: application/riscos

Attachment: IDCSR_TEXT_DECTAB.cur
Description: application/riscos

Attachment: IDCSR_TEXT_LEFTTAB.cur
Description: application/riscos

Attachment: IDCSR_TEXT_RIGHTTAB.cur
Description: application/riscos

Attachment: imptab.png
Description: PNG image

Index: Kernel/tool.h
===================================================================
--- Kernel/tool.h	(Revision 1528)
+++ Kernel/tool.h	(Arbeitskopie)
@@ -365,6 +365,10 @@
 	// Allow the current tool to render additional blobs on the Ruler
 	virtual void RenderRulerBlobs(RulerBase* pRuler, UserRect& UpdateRect, BOOL IsBackground);
 
+	// Allow the current tool to change the ruler help text
+	virtual BOOL GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
+										Spread* pSpread, RulerBase* pRuler);
+
 	// Allow the Current Tool to handle Ruler clicks
 	virtual BOOL OnRulerClick( UserCoord PointerPos,
 							   ClickType Click,
Index: Kernel/tool.cpp
===================================================================
--- Kernel/tool.cpp	(Revision 1528)
+++ Kernel/tool.cpp	(Arbeitskopie)
@@ -1571,7 +1571,28 @@
 	// as you enter and leave your tool.
 }
 
+/********************************************************************************************
 
+>	virtual BOOL Tool_v1::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos, Spread* pSpread,
+												 RulerBase* pRuler)
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	25/07/2006
+	Inputs:		PointerPos	- user coordinates of click on ruler (relative to origin set by tool)
+				pSpread		- pointer to spread upon which click occurred
+				pRuler		- pointer to ruler which generated click
+	Outputs:	pText - status line text set (if return value is TRUE)
+	Returns:	TRUE if the text has been set
+	Purpose:	Gives the current tool the chance to change the status line text displayed for the ruler
+
+********************************************************************************************/
+
+BOOL Tool_v1::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos, Spread* pSpread,
+									 RulerBase* pRuler)
+{
+	// Override me if you want!
+	return FALSE;
+}
+
 /********************************************************************************************
 
 >	virtual BOOL Tool_v1::OnRulerClick( DocCoord PointerPos,
Index: Kernel/nodetext.h
===================================================================
--- Kernel/nodetext.h	(Revision 1528)
+++ Kernel/nodetext.h	(Arbeitskopie)
@@ -311,7 +311,6 @@
 	virtual BOOL IsASpace()        { return Ch==' '; }
 	virtual BOOL IsAVisibleSpace() { return iswspace(Ch); }
 	virtual BOOL IsAHyphen()       { return Ch=='-'; }
-	virtual BOOL IsADecimalPoint() { return Ch=='.'; }     // needs to be internationalized
 
 	virtual WCHAR GetUnicodeValue() { return Ch; }
 	virtual void SetUnicodeValue(WCHAR Char) { Ch = Char; }
Index: Kernel/rulers.h
===================================================================
--- Kernel/rulers.h	(Revision 1528)
+++ Kernel/rulers.h	(Arbeitskopie)
@@ -137,11 +137,19 @@
 	virtual UserCoord  MakeCoord(const MILLIPOINT ord)   = 0;
 	virtual BOOL    IsHorizontal()                       = 0;
 
+	/////////////////////
+	// OilRuler interface
+
 	// Called by the OIL ruler when the ruler needs to be redrawn.
 	// We do all the platform independent computations and call the
 	// OIL ruler back to render specific things (e.g., graticules)
 	BOOL Redraw(OilRect* pOilRect);
+	virtual BOOL GetStatusLineText(String_256* pText, OilCoord PointerPos);
+	virtual BOOL OnRulerClick(OilCoord, ClickType, ClickModifiers);
 
+	/////////////////
+	// Tool interface
+	
 	// For use by tools that wish to render extra things on the ruler
 	// (by responding to the RenderRulerBlobs method)
 	BOOL HighlightSection(MILLIPOINT ord_lo, MILLIPOINT ord_hi);
@@ -150,9 +158,8 @@
 	// Drag events will be dispatched via OnRulerClick.
 	BOOL StartToolDrag(ClickModifiers Mods, UserCoord, String_256* pOpToken, OpParam* pParam);
 
-	virtual BOOL OnRulerClick(OilCoord, ClickType, ClickModifiers);
-
 protected:
+	UserCoord OilToToolCoords(OilCoord PointerPos, Spread* pSpread);
 	RulerPair* pRulerPair;
 	OILRuler*  pOILRuler;
 };
Index: Kernel/rulers.cpp
===================================================================
--- Kernel/rulers.cpp	(Revision 1528)
+++ Kernel/rulers.cpp	(Arbeitskopie)
@@ -179,13 +179,6 @@
 	DocRect drect = pUpdateOilRect->ToDoc(pSpread,pDocView).ToSpread(pSpread,pDocView);
 	UserRect UpdateRect = drect.ToUser(pSpread);
 
-	MILLIPOINT LoLimit = GetOrd(pRD->PasteBoardUserRect.lo);
-	MILLIPOINT HiLimit = GetOrd(pRD->PasteBoardUserRect.hi);
-	if (GetOrd(UpdateRect.lo)<LoLimit) UpdateRect.lo=MakeCoord(LoLimit);
-	if (GetOrd(UpdateRect.hi)>HiLimit) UpdateRect.hi=MakeCoord(HiLimit);
-	if (GetOrd(UpdateRect.hi)<GetOrd(UpdateRect.lo))
-		return TRUE;	// no region to redraw
-
 	// Give the current tool the chance to modify the UserCoord displayed by the ruler
 	UserCoord Offsets(0, 0);
 	if (Tool::GetCurrent())
@@ -197,6 +190,13 @@
 	UpdateRect.hi.x -= Offsets.x;
 	UpdateRect.hi.y -= Offsets.y;
 
+	MILLIPOINT LoLimit = GetOrd(pRD->PasteBoardUserRect.lo);
+	MILLIPOINT HiLimit = GetOrd(pRD->PasteBoardUserRect.hi);
+	if (GetOrd(UpdateRect.lo)<LoLimit) UpdateRect.lo=MakeCoord(LoLimit);
+	if (GetOrd(UpdateRect.hi)>HiLimit) UpdateRect.hi=MakeCoord(HiLimit);
+	if (GetOrd(UpdateRect.hi)<GetOrd(UpdateRect.lo))
+		return TRUE;	// no region to redraw
+
 	// allow the current tool to render background blobs
 	if (Tool::GetCurrent())
 		Tool::GetCurrent()->RenderRulerBlobs(this, UpdateRect, TRUE);
@@ -357,7 +357,6 @@
 	
 	// Find the spread in which the click happened
 	Spread *pSpread = pRulerPair->GetpSpread();
-	DocView* pDocView = pRulerPair->GetpDocView();
 
 	if (pSpread == NULL)
 	{
@@ -371,8 +370,31 @@
 	// must make the choice about setting the selected spread IFF it handles the click
 //	Document::SetSelectedViewAndSpread(pDoc, this, pSpread);
 
+	UserCoord UserPos = OilToToolCoords(PointerPos, pSpread);
+	if (Tool::GetCurrent())
+		return Tool::GetCurrent()->OnRulerClick(UserPos, Click, Mods, pSpread, this);
+
+	return FALSE;
+}
+
+/********************************************************************************************
+
+> 	BOOL RulerBase::GetStatusLineText(String_256* pText, OilCoord PointerPos)
+
+    Author: 	Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+    Created:	25/07/06
+	Inputs:		PointerPos - pointer position in Oil coords
+	Returns:    TRUE if text was set
+	Outputs:	Status text in pText (if return value is TRUE), else undefined
+    Purpose:    Allows tools to override the ruler help text
+                   			                                     
+********************************************************************************************/
+UserCoord RulerBase::OilToToolCoords(OilCoord PointerPos, Spread* pSpread)
+{
+	DocView* pDocView = pRulerPair->GetpDocView();
+
 	// First of all convert the OilRect into device coords
-	DocCoord DocPos = PointerPos.ToDoc( pSpread, pRulerPair->GetpDocView() );
+	DocCoord DocPos = PointerPos.ToDoc(pSpread, pDocView);
 
 	// Convert the coord to spread coords
 	pSpread->DocCoordToSpreadCoord(&DocPos);
@@ -390,12 +412,33 @@
 	UserCoord Offsets(0, 0);
 	if (Tool::GetCurrent())
 		Tool::GetCurrent()->GetRulerOrigin(pSpread, &Offsets);
-	UserPos.translate(-Offsets.x, -Offsets.y);
 
-	if (Tool::GetCurrent())
-		return Tool::GetCurrent()->OnRulerClick(UserPos, Click, Mods, pSpread, this);
+	// Apply the tool origin, but only in the significant direction
+	if (IsHorizontal())
+		UserPos.x -= Offsets.x;
+	else
+		UserPos.y -= Offsets.y;
+	return UserPos;
+}
 
-	return FALSE;
+/********************************************************************************************
+
+> 	BOOL RulerBase::GetStatusLineText(String_256* pText, OilCoord PointerPos)
+
+    Author: 	Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+    Created:	25/07/06
+	Inputs:		PointerPos - pointer position in Oil coords
+	Returns:    TRUE if text was set
+	Outputs:	Status text in pText (if return value is TRUE), else undefined
+    Purpose:    Allows tools to override the ruler help text
+                   			                                     
+********************************************************************************************/
+
+BOOL RulerBase::GetStatusLineText(String_256* pText, OilCoord PointerPos)
+{
+	Spread *pSpread = pRulerPair->GetpSpread();
+	UserCoord UserPos = OilToToolCoords(PointerPos, pSpread);
+	return (Tool::GetCurrent()) ? Tool::GetCurrent()->GetRulerStatusLineText(pText, UserPos, pSpread, this) : FALSE;
 }
 
 /********************************************************************************************
Index: Kernel/nodetxtl.h
===================================================================
--- Kernel/nodetxtl.h	(Revision 1528)
+++ Kernel/nodetxtl.h	(Arbeitskopie)
@@ -222,8 +222,9 @@
 	// tab character position (AnchorPos) and the position of the tab stop to which it advances
 	// (ActiveTabPos). It is updated as text is formatted to the left of the tab stop.
 	MILLIPOINT       RemainingSpace;
-	// if the most recently encountered tab was a Decimal tab, keep track of whether we have
-	// met the decimal point already
+	// if the most recently encountered tab was a Decimal tab, keep track of what its decimal point
+	// character is and whether we have met the decimal point already
+	WCHAR            DecimalPointChar;
 	BOOL             DecimalPointFound;
 
 	// default constructor (no need to initialise RemainingSpace and DecimalPointFound)
@@ -236,9 +237,9 @@
 		ExtraOnChars(_ExtraOnChars), ExtraOnSpaces(_ExtraOnSpaces),
 		Width(Indent), ActiveTabType(LeftTab), ActiveTabPos(0), 
 		pLastTabVTN(NULL), AnchorPos(Indent) {};
-	void AdvanceBy(MILLIPOINT Advance);
+	void AdvanceBy(MILLIPOINT Advance, BOOL IsADecimalPoint);
 	BOOL FinishTabSection(VisibleTextNode* pLastFormattedNode, BOOL IsLastTabSection);
-	BOOL IsAvailable(MILLIPOINT CharWidth, BOOL IsATab);
+	BOOL IsAvailable(MILLIPOINT CharWidth, BOOL IsATab, BOOL IsADecimalPoint);
 };
 
 /********************************************************************************************
Index: Kernel/nodetxtl.cpp
===================================================================
--- Kernel/nodetxtl.cpp	(Revision 1528)
+++ Kernel/nodetxtl.cpp	(Arbeitskopie)
@@ -1124,10 +1124,13 @@
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>			
 	Created:	23/06/06
 	Inputs:		CharWidth - the amount to advance by
+				IsADecimalPoint - TRUE if the currently active tab is a decimal tab and the
+								  character to be formatted is its decimal point character
 	Purpose:	Account for a char with width CharWidth to be fitted into the current line.
+	See also:   TextLine::FindBreakChar for a description of the formatting algorithm used
 ********************************************************************************************/
 
-void FormatState::AdvanceBy(MILLIPOINT CharWidth)
+void FormatState::AdvanceBy(MILLIPOINT CharWidth, BOOL IsADecimalPoint)
 {
 	// Note: This routine may only update Width and RemainingSpace, otherwise
 	// the backtracking in IsAvailable() does not work as expected.
@@ -1150,7 +1153,8 @@
 		// and the remainder to the right
 		Width += CharWidth - SpaceToLeftUsed;
 	}
-	else if ((ActiveTabType == RightTab || (ActiveTabType == DecimalTab && !DecimalPointFound))
+	else if ((ActiveTabType == RightTab || (ActiveTabType == DecimalTab && !DecimalPointFound
+											&& !IsADecimalPoint))
 			 && RemainingSpace > 0)
 	{
 		if (RemainingSpace >= CharWidth) RemainingSpace -= CharWidth;
@@ -1162,7 +1166,14 @@
 	}
 	else
 	{
-		// last tab was left tab or decimal tab and we have already found the decimal point
+		// We end up here if:
+		// - the last tab was left tab, or
+		// - the last tab was a decimal tab and we have already found the decimal point, or
+		// - the last tab was a decimal tab and this is a decimal point character (NB. - the
+		//   decimal point is always formatted to the right of the tab position), or, finally,
+		// - this is any kind of tab and we have no more remaining space to the left of it
+		// In all these cases, we simply format the character to the right of the current position,
+		// which is the easiest thing to do anyway
 		Width += CharWidth;
 	}
 }
@@ -1174,10 +1185,12 @@
 	Created:	23/06/06
 	Inputs:		CharWidth - the amount to advance by
 				IsATab - TRUE if the character to be fitted is a tab
+				IsADecimalPoint - TRUE if the currently active tab is a decimal tab and the
+								  character to be formatted is its decimal point character
 	Purpose:	Test whether a char with width CharWidth can be fitted into the current line.
 ********************************************************************************************/
 
-BOOL FormatState::IsAvailable(MILLIPOINT CharWidth, BOOL IsATab)
+BOOL FormatState::IsAvailable(MILLIPOINT CharWidth, BOOL IsATab, BOOL IsADecimalPoint)
 {
 	// If the full CharWidth still fits, then we can return TRUE straight away
 	// without having to look at the active tab type
@@ -1193,7 +1206,7 @@
 	// duplicating all the AdvanceBy code, we simply advance and backtrack.
 	MILLIPOINT OldWidth = Width;
 	MILLIPOINT OldRemainingSpace = RemainingSpace;
-	AdvanceBy(CharWidth);
+	AdvanceBy(CharWidth, IsADecimalPoint);
 	MILLIPOINT NewWidth = Width;
 	Width = OldWidth;
 	RemainingSpace = OldRemainingSpace;
@@ -1258,6 +1271,13 @@
 	while(pVTN!=NULL)
 	{
 		BOOL IsATab = IS_A(pVTN, HorizontalTab);
+		BOOL IsADecimalPoint = FALSE;
+		if (State.ActiveTabType == DecimalTab && pVTN->IsATextChar())
+		{
+			// if the currently active tab is a decimal tab check whether we have hit a decimal point
+			// NB. - the decimal point character is stored in the tab stop
+			IsADecimalPoint = (((TextChar*)pVTN)->GetUnicodeValue() == State.DecimalPointChar);
+		}
 		if (IsATab)
 		{
 			TxtTabType ThisTabType;
@@ -1269,7 +1289,9 @@
 
 			// find the next tab stop (always returns usable values - if there are no more
 			// tab stops on the ruler the routine assumes left tabs at equidistant positions)
-			TxtRulerAttribute::FindTabStopInRuler(mpRuler, State.Width, &ThisTabType, &ThisTabPos);
+			WCHAR DecimalPointChar;
+			TxtRulerAttribute::FindTabStopInRuler(mpRuler, State.Width, &ThisTabType, &ThisTabPos,
+												  &DecimalPointChar);
 			TabWidth = ThisTabPos - State.Width;
 			((HorizontalTab*)pVTN)->SetCharAdvance(TabWidth);
 			((HorizontalTab*)pVTN)->SetCharWidth(TabWidth);
@@ -1280,7 +1302,10 @@
 				State.RemainingSpace = TabWidth;
 			}
 			if (ThisTabType == DecimalTab)
+			{
+				State.DecimalPointChar = DecimalPointChar;
 				State.DecimalPointFound = FALSE;
+			}
 			State.pLastTabVTN = pVTN;
 			State.ActiveTabType = ThisTabType;
 			State.ActiveTabPos  = ThisTabPos;
@@ -1296,16 +1321,16 @@
 			}
 			else if (pVTN->IsASpace())
 			{
-				State.AdvanceBy(pVTN->GetCharAdvance());
+				State.AdvanceBy(pVTN->GetCharAdvance(), IsADecimalPoint);
 				pBreakChar  = pVTN;
 			}
-			else if (!CharOnLine || State.IsAvailable(pVTN->GetCharWidth(), IsATab))
+			else if (!CharOnLine || State.IsAvailable(pVTN->GetCharWidth(), IsATab, IsADecimalPoint))
 			{
 				if (IsATab) State.Width += pVTN->GetCharAdvance();
-				else State.AdvanceBy(pVTN->GetCharAdvance());
+				else State.AdvanceBy(pVTN->GetCharAdvance(), IsADecimalPoint);
 				if (pVTN->IsAHyphen())
 					pBreakChar = pVTN;
-				if (State.ActiveTabType == DecimalTab && !State.DecimalPointFound && pVTN->IsADecimalPoint())
+				if (State.ActiveTabType == DecimalTab && !State.DecimalPointFound && IsADecimalPoint)
 					State.DecimalPointFound = TRUE;
 			}
 			else
Index: Kernel/txtattr.h
===================================================================
--- Kernel/txtattr.h	(Revision 1528)
+++ Kernel/txtattr.h	(Arbeitskopie)
@@ -662,7 +662,7 @@
 	void AddTabStop(TxtTabType Type, MILLIPOINT Position);
 	void AddTabStop(TxtTabType Type, MILLIPOINT Position, WCHAR DecimalPointChar, WCHAR TabFillerChar);
 	void AddTabStop(TxtTabStop TabStop);
-	void FindTabStop (MILLIPOINT MinPos, TxtTabType* pType, MILLIPOINT* pPos) const;
+	void FindTabStop (MILLIPOINT MinPos, TxtTabType* pType, MILLIPOINT* pPos, WCHAR* pDecimalPointChar) const;
 	const_TxtTabStopIterator begin() const { return Value->begin(); }
 	const_TxtTabStopIterator end() const { return Value->end(); }
 	TxtTabStopIterator begin() { return Value->begin(); }
@@ -671,7 +671,7 @@
 
 	static BOOL Init();
 	static void FindTabStopInRuler(const TxtRuler* pRuler, MILLIPOINT MinPos,
-								   TxtTabType* pType, MILLIPOINT* pPos);
+								   TxtTabType* pType, MILLIPOINT* pPos, WCHAR* pDecimalPointChar);
 	// the actual value is a pointer to a list of tab stops
 	// this means that we will have to take care when destructing/copying
 	// objects of this class
Index: Kernel/txtattr.cpp
===================================================================
--- Kernel/txtattr.cpp	(Revision 1528)
+++ Kernel/txtattr.cpp	(Arbeitskopie)
@@ -3147,35 +3147,39 @@
 
 /********************************************************************************************
 
->	void TxtRulerAttribute::FindTabStop(MILLIPOINT width, TxtTabType* pType, MILLIPOINT* pPos)
+>	void TxtRulerAttribute::FindTabStop(MILLIPOINT MinPos, TxtTabType* pType, MILLIPOINT* pPos,
+										WCHAR* pDecimalPointChar) const
 
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
 	Created:	21/6/06
 	Inputs:		MinPos - minimum position of the tab stop
-	Outputs:	stores type in pType and position in pPos
+	Outputs:	stores the tab stop's type in pType, its position in pPos and its decimal point
+				character in pDecimalPointChar (if applicable, else undefined)
 	Purpose:	Finds a tab stop at the given position or higher
 
 ********************************************************************************************/
-void TxtRulerAttribute::FindTabStop(MILLIPOINT MinPos, TxtTabType* pType, MILLIPOINT* pPos) const
+void TxtRulerAttribute::FindTabStop(MILLIPOINT MinPos, TxtTabType* pType, MILLIPOINT* pPos,
+									WCHAR* pDecimalPointChar) const
 {
-	FindTabStopInRuler(Value, MinPos, pType, pPos);
+	FindTabStopInRuler(Value, MinPos, pType, pPos, pDecimalPointChar);
 }
 
 /********************************************************************************************
 
 >	void TxtRulerAttribute::FindTabStopInRuler(const TxtRuler* pRuler, MILLIPOINT width, TxtTabType* pType,
-										MILLIPOINT* pPos)
+										MILLIPOINT* pPos, WCHAR *pDecimalPointChar)
 
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
 	Created:	21/6/06
 	Inputs:		pRuler - the ruler list or NULL
 				MinPos - minimum position of the tab stop
-	Outputs:	stores type in pType and position in pPos
+	Outputs:	stores the tab stop's type in pType, its position in pPos and its decimal point
+				character in pDecimalPointChar (if applicable, else undefined)
 	Purpose:	Finds a tab stop at the given position or higher
 
 ********************************************************************************************/
 void TxtRulerAttribute::FindTabStopInRuler(const TxtRuler* pRuler, MILLIPOINT MinPos, TxtTabType* pType,
-										   MILLIPOINT* pPos)
+										   MILLIPOINT* pPos, WCHAR *pDecimalPointChar)
 {
 	// we cope with pRuler being NULL, in which case we simply treat it as empty
 	if (pRuler)
@@ -3188,7 +3192,8 @@
 			if ((*It).GetPosition() > MinPos) {
 				*pType = (*It).GetType();
 				*pPos  = (*It).GetPosition();
-				
+				if ((*It).GetType() == DecimalTab)
+					*pDecimalPointChar = (*It).GetDecimalPointChar();
 				return;
 			}
 		}
@@ -7724,7 +7729,7 @@
 	NodeAttribute::GetDebugDetails( Str );
 
 	String_256 TempStr;
-	TempStr._MakeMsg( TEXT("\r\nRulers="));
+	TempStr._MakeMsg( TEXT("\r\nRuler(#1%08x)="), Value.Value);
 	(*Str) += TempStr;
 	for (TxtTabStopIterator It = Value.begin(); It != Value.end(); ++It)
 	{
Index: wxOil/oilruler.cpp
===================================================================
--- wxOil/oilruler.cpp	(Revision 1528)
+++ wxOil/oilruler.cpp	(Arbeitskopie)
@@ -1014,7 +1014,10 @@
 		ocoord.y = RenderWidth - point.y;
 	else
 		ocoord.x = RenderWidth - point.x;
-	return pKernelRuler->OnRulerClick(ocoord, t, m_LastClickMods);
+	// pass only left clicks to the ruler
+	if (Button == MK_LBUTTON)
+		return pKernelRuler->OnRulerClick(ocoord, t, m_LastClickMods);
+	return FALSE;
 }
 
 
@@ -1160,7 +1163,7 @@
 
 	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xxxxxxxx>
 	Created:	8/10/95
-	Inputs:		MousePos - position in window (not used)
+	Inputs:		MousePos - position in window
 				hWnd     - handle of window
 	Outputs:	pText    -
 	Returns:	TRUE if ptext hold valid text, else FALSE if not over a pane
@@ -1174,6 +1177,12 @@
 	if (this!=hWnd)
 		return FALSE;
 
+	// allow the current tool to set the status line if it claims the ruler
+	DocView* pDocView = m_pOwnerView->GetDocViewPtr();
+	OilCoord ocoord = ClientToOil(pDocView, MousePos);
+	if (pKernelRuler->GetStatusLineText(pText, ocoord))
+		return TRUE;
+
 	UINT32 StatusHelpID = IsHorizontal() ? _R(IDS_HRULER_SH) : _R(IDS_VRULER_SH);
 	return (pText->Load(StatusHelpID)!=0);
 }
Index: wxOil/ftfonts.cpp
===================================================================
--- wxOil/ftfonts.cpp	(Revision 1528)
+++ wxOil/ftfonts.cpp	(Arbeitskopie)
@@ -732,7 +732,7 @@
 	ERROR2IF(pFontDataItem==NULL,FALSE,"FTFontMan::GetCharOutline could not find cached font");
   	ENUMLOGFONT*    pEnumLogFont  = pFontDataItem->GetEnumLogFont();
 	ERROR2IF(pEnumLogFont==NULL,FALSE,"FTFontMan::GetCharOutline could not find cached EnumLogFont");
-	DumpString64User("wuerthne", _T("GetFacenameFromCharDesc returning"), &pEnumLogFont->elfLogFont.FaceName);
+	// DumpString64User("wuerthne", _T("GetFacenameFromCharDesc returning"), &pEnumLogFont->elfLogFont.FaceName);
 	return &pEnumLogFont->elfLogFont.FaceName;
 }
 
@@ -1222,7 +1222,7 @@
 							   UINT32* pNumCoords,
 							   wxDC* pDC)
 {
-	TRACEUSER("wuerthne", _T("FTFontMan::GetCharOutline %04x"), ChDesc.GetCharCode());
+	// TRACEUSER("wuerthne", _T("FTFontMan::GetCharOutline %04x"), ChDesc.GetCharCode());
 	// Check some input parameters
 	ERROR2IF(ppCoords==NULL,FALSE,"FTFontMan::GetCharOutline ppCoords==NULL");
 	ERROR2IF(ppVerbs==NULL,FALSE,"FTFontMan::GetCharOutline ppVerbs==NULL");
@@ -1245,8 +1245,8 @@
 
 	FT_GlyphSlotRec *pGlyph = pFreeTypeFace->glyph;
 
-	TRACEUSER("wuerthne", _T("found outline, %d contours, %d points"),
-			  pGlyph->outline.n_contours, pGlyph->outline.n_points);
+	// TRACEUSER("wuerthne", _T("found outline, %d contours, %d points"),
+	//		  pGlyph->outline.n_contours, pGlyph->outline.n_points);
 	// finally, we have the glyph we want, so transfer it to the outlines cache
 	OILFontMan::InitialiseOutlineCache();
 
@@ -1272,7 +1272,7 @@
 	OILFontMan::FinaliseOutlineCache();
 	pango_fc_font_unlock_face(pPangoFcFont);
 	(*pNumCoords) = OILFontMan::GetOutlineCache(ppCoords,ppVerbs);
-	TRACEUSER("wuerthne", _T("FTFontMan::GetCharOutline Returning %d coords"), *pNumCoords);
+	// TRACEUSER("wuerthne", _T("FTFontMan::GetCharOutline Returning %d coords"), *pNumCoords);
 	return TRUE;
 }
 
@@ -1299,7 +1299,7 @@
 	String_64* pFaceName = &pLogFont->FaceName;
 	PangoFcFont* pPangoFcFont;
 	FT_Face pFreeTypeFace;
-	DumpString64User("wuerthne", _T("FTFontMan::GetOutlineTextMetric"), pFaceName);
+	// DumpString64User("wuerthne", _T("FTFontMan::GetOutlineTextMetric"), pFaceName);
 
 	// first of all, retrieve the underlying font information
 	if (!GetPangoFcFontAndFreeTypeFaceForFaceName(pFaceName, &pPangoFcFont, &pFreeTypeFace)) return NULL;
@@ -1325,7 +1325,7 @@
 	// will free the structure.
 	OUTLINETEXTMETRIC* pMetric = new OUTLINETEXTMETRIC;
 	pMetric->otmPanoseNumber = *((struct PANOSE*)&pOS2_Table->panose);
-	TRACEUSER("wuerthne", _T("returning valid OUTLINEFONTMETRIC structure"));
+	// TRACEUSER("wuerthne", _T("returning valid OUTLINEFONTMETRIC structure"));
 
 	pango_fc_font_unlock_face(pPangoFcFont);
 	return pMetric;
@@ -1350,14 +1350,14 @@
 {
     PangoFcFont* pPangoFcFont;
 	FT_Face pFreeTypeFace;
-	TRACEUSER("wuerthne", _T("GetAscentDescent"));
+	// TRACEUSER("wuerthne", _T("GetAscentDescent"));
 	if (!GetPangoFcFontAndFreeTypeFaceForCharDesc(ChDesc, &pPangoFcFont, &pFreeTypeFace)) return FALSE;
 
 	// get the design size
 	INT32 DesignSize = pFreeTypeFace->units_per_EM;
 	*pAscent = ScaleToDefaultHeight(pFreeTypeFace->ascender, DesignSize);
 	*pDescent = ScaleToDefaultHeight(-pFreeTypeFace->descender, DesignSize);
-	TRACEUSER("wuerthne", _T("returning ascent = %d, descent = %d"), *pAscent, *pDescent);
+	// TRACEUSER("wuerthne", _T("returning ascent = %d, descent = %d"), *pAscent, *pDescent);
 	pango_fc_font_unlock_face(pPangoFcFont);
 	return TRUE;
 }
@@ -1383,7 +1383,7 @@
 BOOL FTFontMan::GetCharWidth(CharDescription& ChDesc, TCHAR FirstChar, TCHAR LastChar,
 							 INT32* pCharWidthsBuf)
 {
-	TRACEUSER("wuerthne", _T("FTFontMan::GetCharWidth first=%04x last=%04x"), FirstChar, LastChar);
+	// TRACEUSER("wuerthne", _T("FTFontMan::GetCharWidth first=%04x last=%04x"), FirstChar, LastChar);
 	UINT32 NumChars = LastChar - FirstChar + 1;
     PangoFcFont* pPangoFcFont;
 	FT_Face pFreeTypeFace;
@@ -1426,7 +1426,7 @@
     PangoFcFont* pPangoFcFont;
 	FT_Face pFreeTypeFace;
 
-	TRACEUSER("wuerthne", _T("GetCharsKerning, %04x %04x"), LeftChar, RightChar);
+	// TRACEUSER("wuerthne", _T("GetCharsKerning, %04x %04x"), LeftChar, RightChar);
 	if (!GetPangoFcFontAndFreeTypeFaceForCharDesc(FontDesc, &pPangoFcFont, &pFreeTypeFace)) return 0;
 
 	// get the design size
@@ -1443,7 +1443,7 @@
 		return 0;
 	}
 
-	TRACEUSER("wuerthne", _T("Got kerning: %d"), kerning.x);
+	// TRACEUSER("wuerthne", _T("Got kerning: %d"), kerning.x);
 	pango_fc_font_unlock_face(pPangoFcFont);
 	return ScaleToDefaultHeight(kerning.x, DesignSize);
 }
Index: wxOil/fontbase.cpp
===================================================================
--- wxOil/fontbase.cpp	(Revision 1528)
+++ wxOil/fontbase.cpp	(Arbeitskopie)
@@ -823,7 +823,7 @@
 							 UINT32* pNumCoords,
 							 wxDC *pDC)
 {
-	TRACEUSER("wuerthne",_T("OILFontMan::GetCharPath"));
+	// TRACEUSER("wuerthne",_T("OILFontMan::GetCharPath"));
 	BOOL Success=FALSE;
 	switch (fclass)
 	{
Index: wxOil/xrc/EN/peter-strings.xrc
===================================================================
--- wxOil/xrc/EN/peter-strings.xrc	(Revision 1528)
+++ wxOil/xrc/EN/peter-strings.xrc	(Arbeitskopie)
@@ -1386,6 +1386,60 @@
 			</object>
 			<object class="sizeritem">
 				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_RULERHELP">
+					<label>Click to create a tab stop, drag markers to move them</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_CURRENTTABHELP">
+					<label>Click to change the type given to newly created tab stops</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_DRAGLEFTMARGIN">
+					<label>Move the mouse left and right to move the left margin</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_DRAGRIGHTMARGIN">
+					<label>Move the mouse left and right to move the right margin</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_DRAGFIRSTINDENT">
+					<label>Move the mouse left and right to move the first line indent</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_DRAGTABSTOP">
+					<label>Move the mouse left and right to move the tab stop</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_CREATETABSTOP">
+					<label>Move the mouse left and right to position the new tab stop</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_DELTABSTOP">
+					<label>Release the mouse button here to delete the tab stop</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
+				<object class="wxStaticText" name="IDS_TEXTTOOL_CANCELTABSTOP">
+					<label>Release the mouse button here to cancel the new tab stop</label>
+				</object>
+			</object>
+			<object class="sizeritem">
+				<flag>wxALIGN_LEFT|wxALL|wxADJUST_MINSIZE</flag>
 				<object class="wxStaticText" name="IDS_TEXTTOOL_OVERDOCUMENT">
 					<label>Click to create a simple text object; Drag to create a text column</label>
 				</object>
Index: tools/texttool.h
===================================================================
--- tools/texttool.h	(Revision 1528)
+++ tools/texttool.h	(Arbeitskopie)
@@ -214,6 +214,7 @@
 	void RenderRulerBlobs(RulerBase* pRuler, UserRect& UpdateRect, BOOL IsBackground);
 	BOOL OnRulerClick( UserCoord PointerPos, ClickType Click, ClickModifiers Mods,
 					   Spread* pSpread, RulerBase* pRuler);
+	BOOL GetRulerStatusLineText(String_256* pText, UserCoord PointerPos, Spread* pSpread, RulerBase* pRuler);
 
 
 	BOOL OnKeyPress(KeyPress* pKeyPress);
Index: tools/texttool.cpp
===================================================================
--- tools/texttool.cpp	(Revision 1528)
+++ tools/texttool.cpp	(Arbeitskopie)
@@ -1018,8 +1018,29 @@
 	return TextInfoBarOp::OnRulerClick(PointerPos, Click, Mods, pSpread, pRuler);
 }
 
+/********************************************************************************************
 
+>   BOOL TextTool::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
+										  Spread* pSpread, RulerBase* pRuler)
 
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	25/07/06
+	Inputs:     PointerPos	- user coordinates of click on ruler (relative to origin set by tool)
+				pSpread		- pointer to spread upon which click occurred
+				pRuler		- pointer to ruler which generated click
+	Outputs:    Status line text written to pText (if returning TRUE)
+	Returns:    TRUE if the text has been set
+	Purpose:    Allows the tool to set the status line text for the ruler
+
+********************************************************************************************/
+
+BOOL TextTool::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
+									  Spread* pSpread, RulerBase* pRuler)
+{
+	if (!TextInfoBarOp::IsRulerOriginClaimed()) return FALSE;
+	return TextInfoBarOp::GetRulerStatusLineText(pText, PointerPos, pSpread, pRuler);
+}
+
 /********************************************************************************************
 
 	>	BOOL TextTool::OnKeyPress(KeyPress* pKeyPress)
@@ -1145,6 +1166,11 @@
 		return pKeyPress->GetUnicode() == _T(' ');          // always claim the Space key (tool switch)
 	}
 
+	// temporary fix: do not allow function keys to insert funny Unicode characters
+	// (e.g., F8 to select text tool inserts an s circumflex character
+	if (pKeyPress->GetVirtKey() >= CAMKEY(F1) && pKeyPress->GetVirtKey() <= CAMKEY(F12))
+		return FALSE;
+
 	if ( (!pKeyPress->IsAlternative()) ||				    // Alt not down
 		 (pKeyPress->IsAlternative() && pKeyPress->IsExtended()) || // Right alt down
 		 (pKeyPress->IsAlternative() && pKeyPress->IsConstrain()) ) // Ctrl & left alt down
@@ -1399,16 +1425,13 @@
 			break;
  		case CAMKEY(W):
 			// Swap case
-			TRACEUSER("wuerthne", _T("W pressed"));
 			if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust() )
 			{
-				TRACEUSER("wuerthne", _T("Ctrl-W pressed"));
 				if (IsNonCharEvent && TextStory::GetFocusStory()->GetCaret()->FindNextTextCharInStory() != NULL)
 				{
 					OpTextFormat* pOp = new OpTextFormat();
 					if (pOp != NULL)
 					{
-						TRACEUSER("wuerthne", _T("DoSwapCase"));
 						pOp->DoSwapCase();
 					}
 					else
Index: tools/textinfo.h
===================================================================
--- tools/textinfo.h	(Revision 1528)
+++ tools/textinfo.h	(Arbeitskopie)
@@ -317,6 +317,8 @@
 	static void RenderRulerBlobs(RulerBase* pRuler, UserRect& UpdateRect);
 	static BOOL OnRulerClick(UserCoord PointerPos, ClickType Click, ClickModifiers Mods,
 							 Spread* pSpread, RulerBase* pRuler);
+	static BOOL GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
+									   Spread* pSpread, RulerBase* pRuler);
 
 	static void ForceRulerRedraw();
 	static void TabStopDragStarting(TabStopDragType);
@@ -328,7 +330,6 @@
 	static void DoChangeRightMargin(MILLIPOINT Ordinate);
 	static void DoChangeFirstIndent(MILLIPOINT Ordinate);
 
-
 public:
 // the current infobar object - allow static member access
 	static InformationBarOp * pTextInfoBar;
@@ -504,10 +505,10 @@
 {
 public:
 	TabStopDragOpParam(TabStopDragType Type, TxtTabStop DraggedTabStop, UserCoord Pos):
-		m_DragType(Type), m_DraggedTabStop(DraggedTabStop), m_StartPos(Pos) {}
-	TabStopDragType m_DragType;
-	TxtTabStop      m_DraggedTabStop;   // only for Type == DragTabStop
-	UserCoord       m_StartPos;
+		DragType(Type), DraggedTabStop(DraggedTabStop), StartPos(Pos) {}
+	TabStopDragType DragType;
+	TxtTabStop      DraggedTabStop;   // only for Type == DragNew or DragTabStop
+	UserCoord       StartPos;
 };
 
 #define OPTOKEN_TABSTOPDRAG _T("TabStopDrag")
@@ -526,14 +527,17 @@
 {
 	CC_DECLARE_DYNCREATE(TabStopDragOp)
 public:
-	TabStopDragOp(): m_pCursor(NULL), m_pParam(NULL) {}
-	~TabStopDragOp() { if (m_pCursor) delete m_pCursor; if (m_pParam) delete m_pParam; }
+	TabStopDragOp();
+	~TabStopDragOp();
 
 	static BOOL Init();
 	static OpState GetState(String_256* Description, OpDescriptor*);
 
 	// The main entry point
 	void DoWithParam(OpDescriptor *pOpDesc, OpParam* pParam);
+    void DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread*, BOOL bSolidDrag);
+	ResourceID GetStatusLineID();
+	void UpdateStatusLineAndPos(UserCoord* ToolPos, Spread* pSpread);
 	virtual void DragFinished(	DocCoord PointerPos, 
 								ClickModifiers ClickMods, Spread*, 
 								BOOL Success, BOOL bSolidDrag);
@@ -545,7 +549,10 @@
 	Spread*    pSpread;
 	MILLIPOINT Ordinate;
 	INT32	   CursorStackID;
-	Cursor*	   m_pCursor;
+	Cursor*	   m_pTabCursor;
+	Cursor*    m_pDelCursor;
+	BOOL       m_TabCursorShown;
+	ResourceID m_CurrentStatusTextID;
 	TabStopDragOpParam* m_pParam;
 };
 
Index: tools/textinfo.cpp
===================================================================
--- tools/textinfo.cpp	(Revision 1528)
+++ tools/textinfo.cpp	(Arbeitskopie)
@@ -140,6 +140,7 @@
 #include "rulers.h"
 #include "usercord.h"
 #include "dlgmgr.h"
+#include "statline.h"
 
 // For tab stop dragging
 #include "csrstack.h"
@@ -3050,7 +3051,6 @@
 void TextInfoBarOp::HighlightRulerSection(RulerBase* pRuler, UserRect& UpdateRect)
 {
 	if (!pRuler->IsHorizontal()) return;
-	TRACEUSER("wuerthne", _T("TextInfoBarOp::Highlight %d"), RulerData.CurrentRulerSectionWidth);
 
 	// we call RulerBase::HighlightSection, which expects coordinates in user space but
 	// knows about our tool origin
@@ -3134,7 +3134,6 @@
 		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
 			// tab stop, but only while we are not dragging
 			while(LastPos < UpdateRect.hi.x)
@@ -3181,6 +3180,46 @@
 
 /********************************************************************************************
 
+>   BOOL TextTool::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
+										  Spread* pSpread, RulerBase* pRuler)
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	25/07/06
+	Inputs:     PointerPos	- user coordinates of click on ruler (relative to origin set by tool)
+				pSpread		- pointer to spread upon which click occurred
+				pRuler		- pointer to ruler which generated click
+	Outputs:    Status line text written to pText (if returning TRUE)
+	Returns:    TRUE if the text has been set
+	Purpose:    Allows us to set the status line text for the ruler
+
+********************************************************************************************/
+
+BOOL TextInfoBarOp::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
+										   Spread* pSpread, RulerBase* pRuler)
+{
+	if (!pRuler->IsHorizontal()) return FALSE;
+
+	// first of all, check whether we are over the current tab type button
+	DocView* pDocView = pRuler->GetpRulerPair()->GetpDocView();
+	MILLIPOINT PixelSize = pDocView->GetScaledPixelWidth().MakeLong();
+	MILLIPOINT Distance1 = ((CurrentTabButtonPos - CurrentTabButtonWidth/2)*PixelSize);
+	MILLIPOINT Distance2 = ((CurrentTabButtonPos + CurrentTabButtonWidth/2)*PixelSize);
+	if (PointerPos.x >= Distance1 && PointerPos.x <= Distance2)
+	{
+		return pText->Load(_R(IDS_TEXTTOOL_CURRENTTABHELP));
+	}
+
+	// otherwise, check whether we are within the highlighted section
+	if (PointerPos.x >= 0 && (RulerData.CurrentRulerSectionWidth == -1
+							  || PointerPos.x < RulerData.CurrentRulerSectionWidth))
+	{
+		return pText->Load(_R(IDS_TEXTTOOL_RULERHELP));
+	}
+	return FALSE;
+}
+
+/********************************************************************************************
+
 >   BOOL TextInfoBarOp::OnRulerClick(UserCoord PointerPos, ClickType Click, ClickModifiers Mods,
 									 Spread* pSpread, RulerBase* pRuler)
 
@@ -3201,6 +3240,12 @@
 {
 	if (!pRuler->IsHorizontal()) return FALSE;
 	TRACEUSER("wuerthne", _T("ruler click (%d,%d)"), PointerPos.x, PointerPos.y);
+
+	// check whether the click was within the highlight section
+	BOOL InHighlightSection = PointerPos.x >= 0
+		&& (RulerData.CurrentRulerSectionWidth == -1
+			|| PointerPos.x < RulerData.CurrentRulerSectionWidth);
+
 	if (Click == CLICKTYPE_SINGLE)
 	{
 		// check whether the user has clicked on our homegrown "current tab" button
@@ -3256,11 +3301,6 @@
 			return TRUE;
 		}
 
-		// not clicked on our button, so check whether the click was within the highlight section
-		BOOL InHighlightSection = PointerPos.x >= 0
-			&& (RulerData.CurrentRulerSectionWidth == -1
-				|| PointerPos.x < RulerData.CurrentRulerSectionWidth);
-
 		// we do allow dragging tab stops outside the highlight section but we do not allow creating
 		// new ones by clicking outside
 
@@ -3270,7 +3310,7 @@
 		Document::SetSelectedViewAndSpread(pDocView->GetDoc(), pDocView, pSpread);
 		
 		INT32 MatchedIndex = -1;
-		TxtTabStop DraggedTabStop(LeftTab, 0);
+		TxtTabStop DraggedTabStop(RulerData.CurrentTabType, 0);
 		if (RulerData.IsRulerValid)
 		{
 			INT32 Index = 0;
@@ -3330,7 +3370,7 @@
 				if (PointerPos.x >= FirstIndentPos - MILLIPOINT(LeftMarginBitmapWidth / 2 * PixelSize)
 					&& PointerPos.x <= FirstIndentPos + MILLIPOINT(LeftMarginBitmapWidth / 2 * PixelSize))
 				{
-					TRACEUSER("wuerthne", _T("drag left margin"));
+					TRACEUSER("wuerthne", _T("drag first indent"));
 					DragType = DragFirstIndent;
 				}
 				else
@@ -3369,8 +3409,8 @@
 		}
 	}
 
-	// we have not claimed the click
-	return FALSE;
+	// we swallow all other clicks within the highlight section
+	return InHighlightSection;
 }
 
 /********************************************************************************************
@@ -3455,8 +3495,8 @@
 	// 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();
+	// nothing has changed and not redraw the ruler - therefore, the caller will have
+	// to call ForceRulerRedraw(), which is done in TabStopDragOp::DragFinished() below
 }
 
 /********************************************************************************************
@@ -3472,6 +3512,10 @@
 
 void TextInfoBarOp::DoChangeLeftMargin(MILLIPOINT Ordinate)
 {
+	// Technically, we could allow the left margin to become negative, i.e., to be located
+	// to the left of the start of the text object, but this might confuse users, so prevent
+	// it from happening.
+	if (Ordinate < 0) Ordinate = 0;
 	RulerData.IsLeftMarginValid = TRUE;
 	RulerData.LeftMargin = Ordinate;
 	OnFieldChange(LeftMarginA);
@@ -3491,6 +3535,10 @@
 
 void TextInfoBarOp::DoChangeFirstIndent(MILLIPOINT Ordinate)
 {
+	// Technically, we could allow the firstindent to become negative, i.e., to be located
+	// to the left of the start of the text object, but this might confuse users, so prevent
+	// it from happening.
+	if (Ordinate < 0) Ordinate = 0;
 	RulerData.IsFirstIndentValid = TRUE;
 	RulerData.FirstIndent = Ordinate;
 	OnFieldChange(FirstIndentA);
@@ -3514,9 +3562,10 @@
 	// 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;
+	// Technically, we could allow the margin to become negative, i.e., to be located to the
+	// right of the column width, but this might confuse users, so prevent it from happening
+	if (RulerData.RightMargin < 0) RulerData.RightMargin = 0;
 	OnFieldChange(RightMarginA);
 	ForceRulerRedraw();
 }
@@ -3567,7 +3616,6 @@
 
 BOOL TextInfoBarOp::UpdateRulerBar(SelRange* pSelection, BOOL DoUpdate)
 {
-	TRACEUSER("wuerthne", _T("UpdateRulerBar"));
 	BOOL changed = FALSE;
 
 	// first of all, check whether we want to change the ruler origin
@@ -3577,7 +3625,6 @@
 	if (pFocusStory)
 	{
 		// 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);
@@ -3594,6 +3641,11 @@
 		Node* pNode = pSelection->FindFirst();
 		while (pNode != NULL)
 		{
+			// we might have text nodes selected, so find their text story
+			while (IS_A(pNode, TextLine) || pNode->IsKindOf(CC_RUNTIME_CLASS(VisibleTextNode)))
+			{
+				pNode = pNode->FindParent();
+			}
 			if (IS_A(pNode, TextStory))
 			{
 				TextStory* pStory = (TextStory*)pNode;
@@ -3617,7 +3669,6 @@
 
 	if (ShouldWeClaimRuler)
 	{
-		TRACEUSER("wuerthne", _T("claim ruler"));
 		if (RulerData.IsRulerOriginClaimed)
 		{
 			// we have already claimed the ruler, is it for the same origin?
@@ -3776,6 +3827,38 @@
 
 /********************************************************************************************
 
+>	TabStopDragOp::TabStopDragOp()
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	23/07/06
+	Purpose:	Constructor
+
+********************************************************************************************/
+
+TabStopDragOp::TabStopDragOp(): CursorStackID(TABSTOPDRAG_CURSORID_UNSET),
+								m_pTabCursor(NULL), m_pDelCursor(NULL), m_pParam(NULL)
+{
+}
+
+/********************************************************************************************
+
+>	TabStopDragOp::~TabStopDragOp()
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	23/07/06
+	Purpose:	Destructor
+
+********************************************************************************************/
+
+TabStopDragOp::~TabStopDragOp()
+{
+	if (m_pTabCursor) delete m_pTabCursor;
+	if (m_pDelCursor) delete m_pDelCursor;
+	if (m_pParam) delete m_pParam;
+}
+
+/********************************************************************************************
+
 >	static OpState TabStopDragOp::GetState(String_256* Description, OpDescriptor*)
 
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
@@ -3838,28 +3921,208 @@
 		return;
 	}
 
-	Ordinate = m_pParam->m_StartPos.x;
+	Ordinate = m_pParam->StartPos.x;
 	TRACEUSER("wuerthne", _T("starting drag at %d"), Ordinate);
 
-	if (m_pCursor == NULL)
+	if (m_pTabCursor != NULL) delete m_pTabCursor;
+
+	ResourceID CursorID = _R(IDCSR_TEXT_LEFTTAB);
+	switch(m_pParam->DragType)
 	{
-		m_pCursor = new Cursor(TOOLID_TEXT,_R(IDCSR_TEXT_TAB));
-		
-		if (m_pCursor != NULL)
-			CursorStackID = CursorStack::GPush(m_pCursor);
+		case DragTabStop: case DragNew:
+			switch(m_pParam->DraggedTabStop.GetType())
+			{
+				case RightTab:
+					CursorID = _R(IDCSR_TEXT_RIGHTTAB);
+					break;
+				case CentreTab:
+					CursorID = _R(IDCSR_TEXT_CENTTAB);
+					break;
+				case DecimalTab:
+					CursorID = _R(IDCSR_TEXT_DECTAB);
+					break;
+				default:
+					break;
+			}
+			break;
+		case DragLeftMargin:
+			CursorID = _R(IDCSR_TEXT_LEFTMAR);
+			break;
+		case DragRightMargin:
+			CursorID = _R(IDCSR_TEXT_RIGHTMAR);
+			break;
+		case DragFirstIndent:
+			CursorID = _R(IDCSR_TEXT_FIRSTIND);
+			break;
+		default:
+			break;
 	}
+	m_pTabCursor = new Cursor(TOOLID_TEXT, CursorID);
+	
+	if (m_pTabCursor != NULL)
+		CursorStackID = CursorStack::GPush(m_pTabCursor);
+	m_TabCursorShown = TRUE;
 
-	//##UpdateStatusLine();
-
 	// Tell the Dragging system to start a drag operation
 	// We would like to use DRAGTYPE_DEFERRED here, but unfortunately, that also scrolls
 	// vertically, which is very much in the way. So, until there is something like
 	// DRAGTYPE_DEFERRED_HORIZONTAL, disable auto-scrolling.
 	StartDrag(DRAGTYPE_NOSCROLL);
+
+	// show the mouse follower at the (possibly snapped) starting position
+	// and update the status text
+	m_CurrentStatusTextID = 0;
+	UpdateStatusLineAndPos(&m_pParam->StartPos, pSpread);
+	// Temporary fix: The status bar does not want to show this message, so at least
+	// make sure that the text is updated after first mouse move
+	m_CurrentStatusTextID = 0;
 }
 
 /***********************************************************************************************
 
+>	void TabStopDragOp::UpdateStatusLineAndPos(UserCoord* DragPos, Spread* pSpread)
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	25/07/06
+	Inputs:		DragPos - mouse position (in tool coords, i.e., user coords with the tool
+				origin applied)
+				pSpread - the current spread
+	Purpose:    Display appropriate status text and update displayed coordinates
+
+***********************************************************************************************/
+void TabStopDragOp::UpdateStatusLineAndPos(UserCoord* DragPos, Spread* pSpread)
+{
+	DocView *pDocView = DocView::GetCurrent();
+	// NB - to fool the snapping code we transform our user coords with origin applied
+	//      to spread coordinates and then let it snap, then we apply the tool origin
+	//      in the opposite direction, i.e., the origin shift is only applied for the
+	//      snapping operation and we end up with normal spread coordinates that can
+	//      be passed to the status line
+	DocCoord VirtualSpreadCoords = DragPos->ToSpread(pSpread);
+	if (pDocView->GetSnapToGridState())
+		pDocView->ForceSnapToGrid(pSpread, &VirtualSpreadCoords);
+	DocCoord PointerPos = VirtualSpreadCoords;
+	PointerPos.x += TextInfoBarOp::GetRulerOrigin();
+
+	StatusLine* pStatusLine=GetApplication()->GetpStatusLine();
+	if (pStatusLine)
+	{
+		// always pass Snap=FALSE otherwise the pointer changes to the "snapped" pointer
+		pStatusLine->UpdateMousePosAndSnap(&PointerPos, pSpread, pDocView, FALSE);
+		ResourceID ID = GetStatusLineID();
+		if (ID != m_CurrentStatusTextID)
+		{
+			// put up some status line help
+			TRACEUSER("wuerthne", _T("Resource ID is %d"), ID);
+			m_CurrentStatusTextID = ID;
+			String_256 Str;
+			if (Str.Load(ID))
+			{
+				TRACEUSER("wuerthne", _T("updating status line %s"), (TCHAR*)Str);
+				pStatusLine->UpdateText(&Str, TRUE);
+			}
+		}
+	}
+}
+
+/***********************************************************************************************
+
+>	ResourceID TabStopDragOp::GetStatusLineID()
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	25/07/06
+	Purpose:    Return resource ID appropriate for current drag (and mouse on/off ruler status)
+
+***********************************************************************************************/
+ResourceID TabStopDragOp::GetStatusLineID()
+{
+	UINT32 IDS = _R(IDS_TEXTTOOL_DRAGTABSTOP);
+	switch(m_pParam->DragType)
+	{
+		case DragLeftMargin:
+			IDS = _R(IDS_TEXTTOOL_DRAGLEFTMARGIN);
+			break;
+		case DragRightMargin:
+			IDS = _R(IDS_TEXTTOOL_DRAGRIGHTMARGIN);
+			break;
+		case DragFirstIndent:
+			IDS = _R(IDS_TEXTTOOL_DRAGFIRSTINDENT);
+			break;
+		case DragTabStop:
+			if (m_TabCursorShown)
+				IDS = _R(IDS_TEXTTOOL_DRAGTABSTOP);
+			else
+				IDS = _R(IDS_TEXTTOOL_DELTABSTOP);
+			break;
+		case DragNew:
+			if (m_TabCursorShown)
+				IDS = _R(IDS_TEXTTOOL_CREATETABSTOP);
+			else
+				IDS = _R(IDS_TEXTTOOL_CANCELTABSTOP);
+			break;
+	}
+	return IDS;
+}
+
+/***********************************************************************************************
+
+>	void TabStopDragOp::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread*,
+										BOOL bSolidDrag)
+
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	23/07/06
+	Inputs:		PointerPos        = current mouse coords
+				ClickMods         = modifiers
+				pSpread           = the spread
+				bSolidDrag        = whether the drag is solid
+	Purpose:    Called when the mouse moves during a drag
+
+***********************************************************************************************/
+
+void TabStopDragOp::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread,
+									BOOL bSolidDrag)
+{
+	// find out whether we need to change the mouse cursor
+	if (m_pParam->DragType == DragNew || m_pParam->DragType == DragTabStop)
+	{
+		if (IsMouseOverRuler())
+		{
+			// the mouse is over the ruler - are we showing the correct "drag tab" cursor?
+			if (!m_TabCursorShown && m_pTabCursor)
+			{
+				// no, so show it
+				CursorStack::GSetTop(m_pTabCursor, CursorStackID);
+				m_TabCursorShown = TRUE;
+			}
+		}
+		else
+		{
+			// the mouse is not over the ruler - are we showing the correct "delete tab" cursor?
+			if (m_TabCursorShown)
+			{
+				// no, so show it
+				if (!m_pDelCursor)
+				{
+					m_pDelCursor = new Cursor(TOOLID_TEXT, _R(IDCSR_TEXT_DELTAB));
+				}
+				if (m_pDelCursor)
+				{
+					CursorStack::GSetTop(m_pDelCursor, CursorStackID);
+					m_TabCursorShown = FALSE;
+				}
+			}
+		}
+	}
+	
+	// we update the mouse follower - we need to make sure that snapping is done in
+	// our tool space (i.e., in text ruler coordinates), not in document space
+	UserCoord UserPos = PointerPos.ToUser(pSpread);
+	UserPos.x -= TextInfoBarOp::GetRulerOrigin();
+	UpdateStatusLineAndPos(&UserPos, pSpread);
+}
+
+/***********************************************************************************************
+
 >	virtual void TabStopDragOp::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread*,
 											 BOOL Success, BOOL bSolidDrag)
 
@@ -3873,7 +4136,7 @@
 		
 ***********************************************************************************************/
 
-void TabStopDragOp::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread*, BOOL Success,
+void TabStopDragOp::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success,
 								 BOOL bSolidDrag)
 {
 	TRACEUSER("wuerthne", _T("tab stop drag ended"));
@@ -3881,37 +4144,38 @@
 	TextInfoBarOp::TabStopDragFinished();
 	if (Success)
 	{
-		DocView::SnapCurrent(pSpread,&PointerPos);
+		DocView *pDocView = DocView::GetCurrent();
 
+		// we need to snap in tool space, i.e., text ruler space
 		UserCoord UserPos = PointerPos.ToUser(pSpread);
 		UserPos.x -= TextInfoBarOp::GetRulerOrigin();
+		DocCoord VirtualSpreadCoords = UserPos.ToSpread(pSpread);
+		if (pDocView->GetSnapToGridState())
+			pDocView->ForceSnapToGrid(pSpread, &VirtualSpreadCoords);
+		UserPos = VirtualSpreadCoords.ToUser(pSpread);
 		Ordinate = UserPos.x;
 		TRACEUSER("wuerthne", _T("with success at %d"), Ordinate);
 		
-		switch(m_pParam->m_DragType)
+		switch(m_pParam->DragType)
 		{
 			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();
-				}
+				// if the mouse is not over the ruler, we simply refrain from adding a tab stop
+				// so there is nothing to do
 				break;
 			case DragTabStop:
 				if (IsMouseOverRuler())
 				{
-					TxtTabStop NewTabStop(m_pParam->m_DraggedTabStop);
+					TxtTabStop NewTabStop(m_pParam->DraggedTabStop);
 					NewTabStop.SetPosition(Ordinate);
 					TextInfoBarOp::DoAddTabStop(NewTabStop);
 				}
 				else
 				{
-					// delete the tab stop; we can do that by simply applying the shown
+					// delete the dragged 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();
@@ -3928,7 +4192,10 @@
 				break;
 		}
 	}
-
+	// in any case, we redraw the ruler - this is necessary if the drag was aborted, too, because
+	// we want to restore the dragged tab stop or margin marker and we also want to restore the
+	// implicit tab stops
+	TextInfoBarOp::ForceRulerRedraw();
 	// End the Drag
 	EndDrag();
 
@@ -3939,11 +4206,16 @@
 		CursorStackID = TABSTOPDRAG_CURSORID_UNSET;
 	}
 
-	if (m_pCursor != NULL)
+	if (m_pTabCursor != NULL)
 	{
-		delete m_pCursor;
-		m_pCursor = NULL;
+		delete m_pTabCursor;
+		m_pTabCursor = NULL;
 	}
+	if (m_pDelCursor != NULL)
+	{
+		delete m_pDelCursor;
+		m_pDelCursor = NULL;
+	}
 	if (m_pParam != NULL)
 	{
 		delete m_pParam;