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

[XaraXtreme-commits] Commit Complete



Commit by  : alex
Repository : xara
Revision   : 1535
Date       : Tue Jul 25 17:50:35 BST 2006

Changed paths:
   M /Trunk/XaraLX/Kernel/nodetext.h
   M /Trunk/XaraLX/Kernel/nodetxtl.cpp
   M /Trunk/XaraLX/Kernel/nodetxtl.h
   M /Trunk/XaraLX/Kernel/rulers.cpp
   M /Trunk/XaraLX/Kernel/rulers.h
   M /Trunk/XaraLX/Kernel/tool.cpp
   M /Trunk/XaraLX/Kernel/tool.h
   M /Trunk/XaraLX/Kernel/txtattr.cpp
   M /Trunk/XaraLX/Kernel/txtattr.h
   M /Trunk/XaraLX/tools/textinfo.cpp
   M /Trunk/XaraLX/tools/textinfo.h
   M /Trunk/XaraLX/tools/texttool.cpp
   M /Trunk/XaraLX/tools/texttool.h
   M /Trunk/XaraLX/wxOil/fontbase.cpp
   M /Trunk/XaraLX/wxOil/ftfonts.cpp
   M /Trunk/XaraLX/wxOil/oilruler.cpp
   M /Trunk/XaraLX/wxOil/xrc/EN/peter-strings.xrc
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_CENTTAB.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_DECTAB.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_DELTAB.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_FIRSTIND.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_LEFTMAR.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_LEFTTAB.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_RIGHTMAR.cur
   A /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_RIGHTTAB.cur
   D /Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_TAB.cur
   M /Trunk/XaraLX/wxOil/xrc/imptab.png

Martin W's ruler update patch 41


Diff:
Index: Trunk/XaraLX/tools/textinfo.cpp
===================================================================
--- Trunk/XaraLX/tools/textinfo.cpp	(revision 1534)
+++ Trunk/XaraLX/tools/textinfo.cpp	(revision 1535)
@@ -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;
Index: Trunk/XaraLX/tools/texttool.cpp
===================================================================
--- Trunk/XaraLX/tools/texttool.cpp	(revision 1534)
+++ Trunk/XaraLX/tools/texttool.cpp	(revision 1535)
@@ -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: Trunk/XaraLX/tools/textinfo.h
===================================================================
--- Trunk/XaraLX/tools/textinfo.h	(revision 1534)
+++ Trunk/XaraLX/tools/textinfo.h	(revision 1535)
@@ -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: Trunk/XaraLX/tools/texttool.h
===================================================================
--- Trunk/XaraLX/tools/texttool.h	(revision 1534)
+++ Trunk/XaraLX/tools/texttool.h	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/tool.h
===================================================================
--- Trunk/XaraLX/Kernel/tool.h	(revision 1534)
+++ Trunk/XaraLX/Kernel/tool.h	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/nodetext.h
===================================================================
--- Trunk/XaraLX/Kernel/nodetext.h	(revision 1534)
+++ Trunk/XaraLX/Kernel/nodetext.h	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/rulers.h
===================================================================
--- Trunk/XaraLX/Kernel/rulers.h	(revision 1534)
+++ Trunk/XaraLX/Kernel/rulers.h	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/nodetxtl.cpp
===================================================================
--- Trunk/XaraLX/Kernel/nodetxtl.cpp	(revision 1534)
+++ Trunk/XaraLX/Kernel/nodetxtl.cpp	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/txtattr.h
===================================================================
--- Trunk/XaraLX/Kernel/txtattr.h	(revision 1534)
+++ Trunk/XaraLX/Kernel/txtattr.h	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/rulers.cpp
===================================================================
--- Trunk/XaraLX/Kernel/rulers.cpp	(revision 1534)
+++ Trunk/XaraLX/Kernel/rulers.cpp	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/txtattr.cpp
===================================================================
--- Trunk/XaraLX/Kernel/txtattr.cpp	(revision 1534)
+++ Trunk/XaraLX/Kernel/txtattr.cpp	(revision 1535)
@@ -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("
Rulers="));
+	TempStr._MakeMsg( TEXT("
Ruler(#1%08x)="), Value.Value);
 	(*Str) += TempStr;
 	for (TxtTabStopIterator It = Value.begin(); It != Value.end(); ++It)
 	{
Index: Trunk/XaraLX/Kernel/nodetxtl.h
===================================================================
--- Trunk/XaraLX/Kernel/nodetxtl.h	(revision 1534)
+++ Trunk/XaraLX/Kernel/nodetxtl.h	(revision 1535)
@@ -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: Trunk/XaraLX/Kernel/tool.cpp
===================================================================
--- Trunk/XaraLX/Kernel/tool.cpp	(revision 1534)
+++ Trunk/XaraLX/Kernel/tool.cpp	(revision 1535)
@@ -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: Trunk/XaraLX/wxOil/ftfonts.cpp
===================================================================
--- Trunk/XaraLX/wxOil/ftfonts.cpp	(revision 1534)
+++ Trunk/XaraLX/wxOil/ftfonts.cpp	(revision 1535)
@@ -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: Trunk/XaraLX/wxOil/fontbase.cpp
===================================================================
--- Trunk/XaraLX/wxOil/fontbase.cpp	(revision 1534)
+++ Trunk/XaraLX/wxOil/fontbase.cpp	(revision 1535)
@@ -823,7 +823,7 @@
 							 UINT32* pNumCoords,
 							 wxDC *pDC)
 {
-	TRACEUSER("wuerthne",_T("OILFontMan::GetCharPath"));
+	// TRACEUSER("wuerthne",_T("OILFontMan::GetCharPath"));
 	BOOL Success=FALSE;
 	switch (fclass)
 	{
Index: Trunk/XaraLX/wxOil/xrc/imptab.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: Trunk/XaraLX/wxOil/xrc/EN/peter-strings.xrc
===================================================================
--- Trunk/XaraLX/wxOil/xrc/EN/peter-strings.xrc	(revision 1534)
+++ Trunk/XaraLX/wxOil/xrc/EN/peter-strings.xrc	(revision 1535)
@@ -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: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_FIRSTIND.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_FIRSTIND.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_DELTAB.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_DELTAB.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_LEFTMAR.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_LEFTMAR.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_RIGHTMAR.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_RIGHTMAR.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_LEFTTAB.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_LEFTTAB.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_RIGHTTAB.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_RIGHTTAB.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_DECTAB.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_DECTAB.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_CENTTAB.cur
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_CENTTAB.cur
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: Trunk/XaraLX/wxOil/xrc/IDCSR_TEXT_TAB.cur (deleted)
===================================================================
Index: Trunk/XaraLX/wxOil/oilruler.cpp
===================================================================
--- Trunk/XaraLX/wxOil/oilruler.cpp	(revision 1534)
+++ Trunk/XaraLX/wxOil/oilruler.cpp	(revision 1535)
@@ -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);
 }


Xara