// $Id: statline.cpp 1404 2006-07-04 19:57:01Z gerry $
/* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
================================XARAHEADERSTART===========================
 
               Xara LX, a vector drawing and manipulation program.
                    Copyright (C) 1993-2006 Xara Group Ltd.
       Copyright on certain contributions may be held in joint with their
              respective authors. See AUTHORS file for details.

LICENSE TO USE AND MODIFY SOFTWARE
----------------------------------

This file is part of Xara LX.

Xara LX is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as published
by the Free Software Foundation.

Xara LX and its component source files are distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with Xara LX (see the file GPL in the root directory of the
distribution); if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA  02110-1301 USA


ADDITIONAL RIGHTS
-----------------

Conditional upon your continuing compliance with the GNU General Public
License described above, Xara Group Ltd grants to you certain additional
rights. 

The additional rights are to use, modify, and distribute the software
together with the wxWidgets library, the wxXtra library, and the "CDraw"
library and any other such library that any version of Xara LX relased
by Xara Group Ltd requires in order to compile and execute, including
the static linking of that library to XaraLX. In the case of the
"CDraw" library, you may satisfy obligation under the GNU General Public
License to provide source code by providing a binary copy of the library
concerned and a copy of the license accompanying it.

Nothing in this section restricts any of the rights you have under
the GNU General Public License.


SCOPE OF LICENSE
----------------

This license applies to this program (XaraLX) and its constituent source
files only, and does not necessarily apply to other Xara products which may
in part share the same code base, and are subject to their own licensing
terms.

This license does not apply to files in the wxXtra directory, which
are built into a separate library, and are subject to the wxWindows
license contained within that directory in the file "WXXTRA-LICENSE".

This license does not apply to the binary libraries (if any) within
the "libs" directory, which are subject to a separate license contained
within that directory in the file "LIBS-LICENSE".


ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
----------------------------------------------

Subject to the terms of the GNU Public License (see above), you are
free to do whatever you like with your modifications. However, you may
(at your option) wish contribute them to Xara's source tree. You can
find details of how to do this at:
  http://www.xaraxtreme.org/developers/

Prior to contributing your modifications, you will need to complete our
contributor agreement. This can be found at:
  http://www.xaraxtreme.org/developers/contribute/

Please note that Xara will not accept modifications which modify any of
the text between the start and end of this header (marked
XARAHEADERSTART and XARAHEADEREND).


MARKS
-----

Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
designs are registered or unregistered trademarks, design-marks, and/or
service marks of Xara Group Ltd. All rights in these marks are reserved.


      Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
                        http://www.xara.com/

=================================XARAHEADEREND============================
 */
// status line 

/*
*/

#include "camtypes.h" 
//#include "resource.h"	// for _R(IDC_SNAPPED) - 'snappped' mouse pointer
#include "csrstack.h"
#include "camelot.h"
#include "statline.h"
//#include "ctrlhelp.h"
#include "optsmsgs.h"
//#include "scrvw.h"
//#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
//#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
//#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
//#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
//#include "cstatbar.h"
#include "ccolbar.h"
//#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
#include "basebar.h"
#include "colpick.h"
#include "rulers.h"
//#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]

//#include "bars.h" - in camtypes.h [AUTOMATICALLY REMOVED]
//#include "sgallery.h"

//#include "prevwdlg.h"

#include "colcontx.h"
#include "colormgr.h"
#include "opimgset.h"
#include "camview.h"
#include "prncamvw.h"

DECLARE_SOURCE("$Revision: 1404 $");

CC_IMPLEMENT_DYNCREATE(StatusLine, StandardBar);

#define new CAM_DEBUG_NEW

// constants ...
const String_8 SelDescSuffix(_T(":: "));
const String_8 TruncChar(_T(";"));
const INT32      TextDelay  =1000;			// milisecond
const INT32      RenderDelay=200;			// milisecond

//void FixFPControlRegister();

String_256 StatusLine::m_strPrefix(_T(""));
BOOL StatusLine::restrictStatusLineFunctionsToColourPicker = FALSE;
BOOL StatusLine::DoControlHelp = TRUE;
StatusLine * StatusLine::s_pStatusLine = NULL;

// render pane animation (could be in statline.cpp but kept here next to pane defn)
static UINT32 RenderAnimation[] = { _R(IDB_SL_REND1), _R(IDB_SL_REND2), _R(IDB_SL_REND3) };

/*

Timer mechanism and the status line
===================================

Working out the status line text is actually reasonably expensive, especially
given some of the arcane lengths we go to to get it. Painting it is
even more expensive. And doing an Update() on it to ensure it paints
immediately yet more so. The thing this interferes with the most is
typing in the text tool.

So we use the following algortithm:

We set a timer to go off every 250ms. This is the idle timer. A little
confusingly it is a one-shot timer, but bear with me.

If something happens that requires a status line update (or is likely
to) it calls StatusLine::NeedsUpdate(). By default what this does is
reset the timer (so it won't go off for another 250ms). However
it is possible to force an 'immediate' update (actually on the
next idle event) by calling NeedsUpdate(TRUE) which simulates
an immediate timer expiry.

When the timer expires (whether or not NeedsUpdate() has been called),
it signals to the idle event handler for the DialogOp at the lowest
possible priority. This ensures all rendering etc. will have finished.

When the idle event is received, it checks to see if NeedsUpdate()
has been called in the interim (i.e. if the timer is running). If
the timer is running, the idle event handler returns saying it wants
no more idles. If not, it attempts to process the status line text.

If the status line text has changed, it sets the new text (immediately)
but only invalidates it (i.e. does not do an update).

Whether or not the text changed, it then sets the timer off again
by calling NeedsUpdate().


*/




/*****************************************************************************
>	StatusLine::StatusLine()

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	15/11/94
	Purpose:	Constructor for StatusLine
*****************************************************************************/

StatusLine::StatusLine() : m_Timer(this)
{
	// ensure initial updates
	OldMemory=0;
	MousePosDP=2;
	TextTimer.Sample();
	RenderTimer.Sample();
	RenderAnimationState=0;
	MousePosNeedsUpdatingFlag=TRUE;
	MousePosPaneNeedsResizing=TRUE;
//	TransparencyNeedsUpdating=TRUE; 

	// create a 'snapped' mouse pointer
	pSnappedCursor = new Cursor(_R(IDCSR_SNAPPED));
	SnappedCursorID = 0;

	JobDescription=NULL;
	CurrentPercent=-1;
	ProgressShown=FALSE;

	GetApplication()->RegisterIdleProcessor(IDLEPRIORITY_LOW, this);
}


/*****************************************************************************
>	StatusLine::~StatusLine()

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	24/10/95
	Purpose:	destructor for StatusLine
*****************************************************************************/

StatusLine::~StatusLine()
{
	if (pSnappedCursor != NULL)
	{
		delete pSnappedCursor;
		pSnappedCursor = NULL;
	}

	if (JobDescription != NULL)
	{
		delete JobDescription;
		JobDescription = NULL;
	}

	if (this == s_pStatusLine)
	{
		// Only zap it out if there is a live status line. Status line objects
		// are temporarily created when invoking bar closure
		s_pStatusLine = NULL;
	}

	GetApplication()->RemoveIdleProcessor(IDLEPRIORITY_LOW, this);
}


/*****************************************************************************
>	BOOL StatusLine::OnIdleEvent()

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	15/11/94
	Purpose:	Idle event handler for status line
				Updates the text on the status bar if 'TextDelay' 
				has elapsed since it was last changed.
				Also updates the mouse position pane
	Returns:	FALSE (to indicate we want no more idle events)
				The wakeup from the timer will create one for us
	Errors:		-


*****************************************************************************/

BOOL StatusLine::OnIdleEvent()
{
	if (PrintMonitor::IsPrintingNow())
		return FALSE;

	// Don't do any status line updates if the timer is running
	if (!m_Timer.IsRunning())
	{

		// If the current doc doesn't have a doc view (e.g. it's a clipboard doc), then there's no point
		// carrying on, because it's not a proper document, hence doen't require up-to-date status bar info.
		if (DocView::GetSelected() == NULL)
			return FALSE;
	
	
#ifndef STANDALONE
		// if mouse position pane needs resizing, do it (ensuring it's enabled)
		if (MousePosPaneNeedsResizing)
		{
			MousePosPaneNeedsResizing=FALSE;
			Spread* pSpread=Document::GetSelectedSpread();
			if (pSpread)
			{
				SetMousePosPaneWidth(pSpread);
PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
				if (pCCStatusBar->PaneState(_R(IDS_SL_MOUSEPOS),Enable)==Fail)
					ReturnValue=FALSE;
#endif
			}
		}
#endif

		RefreshHelpText();

#ifdef _STATUSLINE_MEMORYPANE
		// establish if amount of memory changed BODGE
		MEMORYSTATUS MemState;
		MemState.dwLength=sizeof(MEMORYSTATUS);
		GlobalMemoryStatus(&MemState);
		INT32 Memory=MemState.dwAvailPhys;
		BOOL MemoryChanged=(OldMemory!=Memory);
		OldMemory=Memory;
	
		if (MemoryChanged)
		{
			// If this ever appears in retail builds it will have to be rewritten properly (it
			// isn't internationally portable at the moment).		
			String_256 StrBuf(TEXT(""));
			camSnprintf(StrBuf, 256, TEXT("%dK"), Memory / 1024);
			if (StrBuf.Length()>4)
				StrBuf.Insert(&String_8(TEXT(",")),StrBuf.Length()-4);
			BOOL ok = SetStringGadgetValue(_R(IDS_SL_MEMORY),StrBuf);
			if (ok)
				PaintGadgetNow(_R(IDS_SL_MEMORY))
			else
				ReturnValue=FALSE;
		}
#endif // _STATUSLINE_MEMORYPANE

		// The timer had expired, so we need to indicate we need another update
		SetNeedsUpdate();
	}

	// Always say we don't need more idle events
	return FALSE;
}

/*****************************************************************************
>	void StatusLine::SetNeedsUpdate(BOOL Immediate = FALSE);

	Author:		Alex Bligh <alex@alex.org.uk>
	Created:	16/5/2005
	Purpose:	Indicate the status line needs an update
	Returns:	-
	Errors:		-

*****************************************************************************/

void StatusLine::SetNeedsUpdate(BOOL Immediate /*=FALSE*/)
{
	if (Immediate)
	{
		// Simulate a timer event
		m_Timer.Stop();
		OnTimer();
		return;
	}

	m_Timer.Start(1000, TRUE);
}

/*****************************************************************************
>	BOOL StatusLine::RefreshHelpText()

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	3/1/95
	Purpose:	refresh the help text pane of the status line
	Returns:	FALSE if anything fails (see Errors)
	Errors:		CCamApp::GetMousePosAndWindowID() fails,
				pCCStatusBar->UpdatePaneText() fails,
				UpdateText() fails
*****************************************************************************/

BOOL StatusLine::RefreshHelpText()
{	
	// if drag op in progress, don't slow it down, just exit
	if (Operation::GetCurrentDragOp())
		return TRUE;

	BOOL ReturnValue=TRUE;
	BOOL PrefixSelDesc=STATUSLINE_SELDESC_DEFAULT;
	String_256 text(_T(""));
	BOOL TextValid=FALSE;

	// for new custom colour picker tool ....
	// (do NOT update status text if this tool is active!)

	if (IsRestrictedAccessToColourPicker () == TRUE)
	{
		return (TRUE);
	}

	if (!TextValid)
	{
		TextValid=BaseBar::GetStatusLineText(&text);		// try bar drag
		if (TextValid)
			PrefixSelDesc=STATUSLINE_SELDESC_BARDRAG;
	}

	WinCoord	WndPos(0,0);
	CWindowID	WinID=DialogManager::GetWindowUnderPointer(&WndPos);
	DocView*	pDocView=NULL;
	Spread*		pSpread=NULL;
	DocCoord	DocPos(0,0);

	// if no valid text so far (and valid current doc else something deep down goes BANG)
	// get mouse pos (in DocCoords), handle of window it is over, DocView ptr, spread ptr
	if (!TextValid && Document::GetCurrent()!=NULL)
	{

		if (WinID != NULL)
			pDocView=CCamView::GetDocViewFromWindowID(WinID);

		if (pDocView != NULL)
		{
			OilCoord OilPos = WndPos.ToOil(pDocView, TRUE);
			pSpread = pDocView->OilToSpreadCoord(OilPos, &DocPos);
		}
	}

	// if no text so far, try drag ops
	if (!TextValid)
	{
		Operation* pDragOp=Operation::GetCurrentDragOp();
		if (pDragOp)
		{
			TextValid=pDragOp->GetStatusLineText(&text,pSpread,DocPos,ClickModifiers::GetClickModifiers());
			ERROR3IF(!TextValid,"StatusLine::RefreshHelpText() - pDragOp->GetStatusLineText() failed");
			PrefixSelDesc=STATUSLINE_SELDESC_DRAGOPS;
			// drag op in progress so we must have valid text (stops tool being interogated)
			if (!TextValid)
			{
				text=_T("");	
				TextValid=TRUE;
			}
		}
	}

	// if no text so far, try other bits!
	if (!TextValid)
	{
		BOOL OverSelectedDoc=FALSE;
		if (pDocView)
		{
			if (pDocView==DocView::GetSelected())
			{
				OverSelectedDoc=TRUE;
				Tool* pTool=Tool::GetCurrent();
				ERROR3IF(pTool==NULL,"StatusLine::RefreshHelpText() - no selected tool");
				if (pTool)
				{
					TextValid=pTool->GetStatusLineText(&text,pSpread,DocPos,ClickModifiers::GetClickModifiers());
					if (TextValid)
						PrefixSelDesc=STATUSLINE_SELDESC_SELDOC;
				}
				else
					ReturnValue=FALSE;
			}
			else
			{
				TextValid=text.Load(_R(IDS_CLICKHERETOSELECTDOC));						// over unselected doc
				if (!TextValid)
					ReturnValue=FALSE;
				else
					PrefixSelDesc=STATUSLINE_SELDESC_OTHERDOC;
			}
		}

PORTNOTE("statline", "Removed use of ControlHelper")
#if !defined(EXCLUDE_FROM_XARALX)
		if (!TextValid)
		{
			TextValid=ControlHelper::GetStatusLineText(&text,WinID);				// try buttons/bars
			if (TextValid)
				PrefixSelDesc=STATUSLINE_SELDESC_BUTTONS;
		}
#endif

		if (!TextValid)
		{			
			TextValid=DialogManager::GetStatusLineText(&text,NULL);
			if (TextValid)
				PrefixSelDesc=STATUSLINE_SELDESC_BUTTONS;
		}

#ifndef STANDALONE
		if (!TextValid)
		{
			TextValid=CColourBar::GetStatusLineText(&text);						// try ColourBar
			if (TextValid)
				PrefixSelDesc=STATUSLINE_SELDESC_COLBAR;
		}

		if (!TextValid)
		{
			TextValid=ColourPicker::GetStatusLineText(&text);					// try Colour Editor
			if (TextValid)
				PrefixSelDesc=STATUSLINE_SELDESC_COLBAR;
		}

#endif //STANDALONE

PORTNOTE("StatusLine", "Removed use of PreviewDlg")
#ifndef EXCLUDE_FROM_XARALX
		if (!TextValid)
		{
			TextValid=PreviewDialog::GetStatusLineText(&text);					// try Preview Dialog
			if (TextValid)
				PrefixSelDesc=STATUSLINE_SELDESC_PREVIEWDLG;
		}
#endif

// WEBSTER - markn 15/1/97
// No rulers in webster
PORTNOTE("StatusLine", "Removed use of Rulers")
#ifndef EXCLUDE_FROM_XARALX
#ifndef WEBSTER
		if (!TextValid)
		{
			DocView*   pDocView   = DocView::GetSelected();
			RulerPair* pRulerPair = NULL;
			if (pDocView!=NULL)
				pRulerPair = pDocView->GetpRulerPair();
			if (pRulerPair!=NULL)
				TextValid = pRulerPair->GetStatusLineText(&text,WndPos,WinID);	// try Rulers
			if (TextValid)
				PrefixSelDesc=STATUSLINE_SELDESC_STATBAR;
		}
#endif // WEBSTER
#endif

PORTNOTE("StatusLine", "Removed use of Galleries")
#ifndef EXCLUDE_FROM_XARALX
#ifndef EXCLUDE_GALS
		if (!TextValid && WinID!=NULL)
		{
			// Lets try the Galleries ....
			// ** This uses Naughty Oily stuff. (Bodge for the Viewer).
			CWindowID ParentWnd = ::GetParent(WinID);
			if (ParentWnd != NULL)
			{
				DialogBarOp* pBar = DialogBarOp::FindDialogBarOp((UINT32)ParentWnd);
				if (pBar != NULL && pBar->IS_KIND_OF(SuperGallery))
				{
					CRect TargetRect;
					::GetWindowRect(WinID, &TargetRect);

					// Get the screen DPI
					INT32 DPI = 96;
					HDC ScreenDC = CreateCompatibleDC(NULL);
					if (ScreenDC == NULL)
						ERROR2(FALSE, "Unable to create screen DC");
					DPI = GetDeviceCaps(ScreenDC, LOGPIXELSY);
					DeleteDC(ScreenDC);

					INT32 WindowHeight = TargetRect.Height();

					DocCoord KernelMousePos;
					KernelMousePos.x = ((WndPos.x) * 72000) / DPI;
					KernelMousePos.y = ((WindowHeight - WndPos.y) * 72000) / DPI;

					TextValid = ((SuperGallery*)pBar)->GetStatusLineHelp(&KernelMousePos, &text);
					if (TextValid)
						PrefixSelDesc=STATUSLINE_SELDESC_STATBAR;
				}
			}
		}
#endif
#endif

#ifndef STANDALONE
		// blank mouse pos if not over selected doc
		if (!OverSelectedDoc)
			if (UpdateMousePosAndSnap(NULL,NULL,pDocView,FALSE)==FALSE)
				ReturnValue=FALSE;
#endif
	}

	// actually update the text
	if (!TextValid)
		text=_T("");
	if (!UpdateText(&text,PrefixSelDesc))
		ReturnValue=FALSE;

	return ReturnValue;
}



/********************************************************************************************
>	static void StatusLine::SetPrefix(const StringBase& str)

	Author:		Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
	Created:	1/10/96
	Inputs:		str		---		the string to display as the prefix on the status line
	Purpose:	Overrides the default behaviour of showing the selection state as
				the prefix on the status line.  SetDefaultPrefix restores the default
				behaviour of (usually) showing the selection state as the prefix.
	SeeAlso:	StatusLine::UpdateText; StatusLine::SetDefaultPrefix;
				StatusLine::GetPrefixWidth
********************************************************************************************/

void StatusLine::SetPrefix(const StringBase& str)
{
	m_strPrefix = str;
}


/********************************************************************************************
>	static void StatusLine::SetDefaultPrefix()

	Author:		Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
	Created:	1/10/96
	Purpose:	Undoes a call to SetPrefix, restoring the default behaviour of showing the
				selection state as a prefix on the status line.
	SeeAlso:	StatusLine::SetPrefix
********************************************************************************************/

void StatusLine::SetDefaultPrefix()
{
	m_strPrefix.Empty();
}


/********************************************************************************************
>	static INT32 StatusLine::GetPrefixWidth()

	Author:		Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
	Created:	1/10/96
	Returns:	The size of non-default prefix, in pixels.  If using the normal selection state
				prefix then always returns 0.
	SeeAlso:	StatusLine::SetPrefix
********************************************************************************************/

INT32 StatusLine::GetPrefixWidth()
{
PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
	CCStatusBar* pb = GetApplication()->GetpCCStatusBar();
	return pb && !m_strPrefix.IsEmpty() ? 4 + pb->GetTextWidth(&m_strPrefix, TRUE) : 0;
#else
	return 0;
#endif
}

/********************************************************************************************
>	BOOL StatusLine::RestrictStatusLineFunctionsToColourPicker (CWindowID colourPicker)

	Author:		Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
	Created:	22/10/99
	Inputs:		colourPicker - The HWND of the claimed colour picker control
	Returns:	TRUE - if successful
				FALSE - otherwise
	Purpose:	When the user is dragging a colour picker control, we need to restrict
				access to status line functionality to only the colour picker
	SeeAlso:	RestoreStatusLineFunctions
********************************************************************************************/

BOOL StatusLine::RestrictStatusLineFunctionsToColourPicker (CWindowID colourPicker)
{
	// firstly, lets check for obvious insanity

PORTNOTE("StatusLine", "Removed use of ColourPicker")
#ifndef EXCLUDE_FROM_XARALX
	String_256 ClassNameStr;  // The control type

	// Find out the class type of the gadget
	GetClassName (colourPicker, (TCHAR*) ClassNameStr, 255);
	
	if (ClassNameStr == String_8(TEXT("cc_colPicker")))
	{
		//if (restrictStatusLineFunctionsToColourPicker == FALSE)
		//{
			restrictStatusLineFunctionsToColourPicker = TRUE;
			return (TRUE);
		//}
	}

	ENSURE(FALSE, "RestrictStatusLineFunctionsToColourPicker called for invalid colour picker control");
#endif
	return (FALSE);
}



/********************************************************************************************
>	BOOL StatusLine::RestrictStatusLineFunctionsToColourPicker (CWindowID colourPicker)

	Author:		Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
	Created:	22/10/99
	Inputs:		colourPicker - The HWND of the claimed colour picker control
	Returns:	TRUE - if successful
				FALSE - otherwise
	SeeAlso:	RestoreStatusLineFunctions
********************************************************************************************/

BOOL StatusLine::RestoreStatusLineFunctions (CWindowID colourPicker)
{
	// firstly, lets check for obvious insanity

PORTNOTE("StatusLine", "Removed use of ColourPicker")
#ifndef EXCLUDE_FROM_XARALX
	String_256 ClassNameStr;  // The control type

	// Find out the class type of the gadget
	GetClassName (colourPicker, (TCHAR*) ClassNameStr, 255);
	
	if (ClassNameStr == String_8(TEXT("cc_colPicker")))
	{
		//if (restrictStatusLineFunctionsToColourPicker == TRUE)
		//{
			restrictStatusLineFunctionsToColourPicker = FALSE;
			return (TRUE);
		//}
	}

	ENSURE(FALSE, "RestoreStatusLineFunctions called for invalid colour picker control");
#endif
	return (FALSE);
}


/*******************************************************************************
>	BOOL StatusLine::UpdateText(String_256* pText, BOOL PrefixSelDesc=TRUE)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	15/11/94
	Purpose:	update the status line with the given text,
				prefixed with the selection description (if valid to do so)
	Inputs:		pText         - pointer to text for status line
				PrefixSelDesc - prefix text with selection description if possible (if TRUE)
								(this parameter is ignored if the default prefix
								has been overridden by StatusLine::SetPrefix
	Returns:	FALSE if fails (see Erros)
	Errors:		pCCStatusBar==NULL
				pCCStatusBar->UpdatePaneText() fails,
				pCCStatusBar->GetPaneRect() fails,
				pCCStatusBar->GetTextWidth() fails,
				TruncateTextToWidth() fails
*********************************************************************************/

extern BOOL SafeAppendString(StringBase*, const StringBase&, BOOL = TRUE);

BOOL StatusLine::UpdateText(String_256* pText, BOOL PrefixSelDesc)
{
//	FixFPControlRegister();

#ifdef STANDALONE
	PrefixSelDesc = FALSE;	// Never show Selection Description in Viewer
#endif

	// create a string by concatenating sel desc and help text (as required)
	String_256 text("");
	SelRange* pSelection = 0;

	if (m_strPrefix.IsEmpty())
	{
		if (PrefixSelDesc)
		{
			// Show the selection state.

			// DMc 19/5/99
			// Alter the range control before we do the describe
			pSelection = GetApplication()->FindSelection();
			
			if (pSelection) text = pSelection->Describe(STATUS_BAR);
		}
	}
	else
	{
		// Show the caller-defined prefix.
		text = m_strPrefix;
	}

	if (text.Length()>0) SafeAppendString(&text, SelDescSuffix, FALSE);
	if (pText) SafeAppendString(&text, *pText, FALSE);

	// re-sample time and update status line
	TextTimer.Sample();
	
	if ((IsRestrictedAccessToColourPicker () == FALSE) && (DoControlHelp == TRUE))
	{
		return SetStatusText(text);
	}
	else
	{
		return (TRUE);
	}

	return FALSE;
}

BOOL StatusLine::UpdateTextForColourPicker(String_256* pText, BOOL PrefixSelDesc)
{
//	FixFPControlRegister();

#ifdef STANDALONE
	PrefixSelDesc = FALSE;	// Never show Selection Description in Viewer
#endif

	ERROR2IF(restrictStatusLineFunctionsToColourPicker==FALSE,FALSE,"StatusLine::UpdateColourPickerText() - require TRUE to use this function!");

	// create a string by concatenating sel desc and help text (as required)
	String_256 text("");
	SelRange* pSelection = 0;

	RangeControl rg;

	if (m_strPrefix.IsEmpty())
	{
		if (PrefixSelDesc)
		{
			// Show the selection state.

			// DMc 19/5/99
			// Alter the range control before we do the describe
			pSelection = GetApplication()->FindSelection();
			
			if (pSelection) text = pSelection->Describe(STATUS_BAR);
		}
	}
	else
	{
		// Show the caller-defined prefix.
		text = m_strPrefix;
	}

	if (text.Length()>0) SafeAppendString(&text, SelDescSuffix, FALSE);
	if (pText) SafeAppendString(&text, *pText, FALSE);

	// re-sample time and update status line
	TextTimer.Sample();
	
	return SetStatusText(text);
}


/*****************************************************************************
>	BOOL StatusLine::TruncateTextToWidth(String_256* pText, INT32 width)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	5/1/95
	Purpose:	if too long for the given width,
				truncate the given text at one of the specified delimiters
	Inputs:		pText - pointer to original text
				width - width to truncate text to
	Outputs:	pText - pointer to the (truncated) text
	Returns:	FALSE if anything fails (see Errors)
	Errors:		pCCStatusBar==NULL,
				pText==NULL,
				pCCStatusBar->GetTextWidth() fails,
*****************************************************************************/

BOOL StatusLine::TruncateTextToWidth(String_256* pText, INT32 width)
{
	ERROR2IF(       pText==NULL,FALSE,"StatusLine::TruncateTextToPane() - pText==NULL");

	// try truncating text at each successive point until it no longer fits the pane then
	// use the last one that fitted (if text null or no truncation points use whole text)
	INT32 LastTruncPoint=pText->Length();
	String_256 text("");
	
	INT32 TruncPoint=pText->Sub(SelDescSuffix);
	if (TruncPoint==-1)
		TruncPoint=pText->Length();
	else
		TruncPoint+=SelDescSuffix.Length();
	pText->Left(&text,TruncPoint);

PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
	INT32 TextWidth=0;
	while ((TextWidth=pCCStatusBar->GetTextWidth(&text))<=width)
	{
		ERROR2IF(TextWidth==-1,FALSE,"StatusLine::TruncateTextToPane() - pCCStatusBar->GetTextWidth() failed");
		LastTruncPoint=TruncPoint;
		if (LastTruncPoint==pText->Length())
			break;
		TruncPoint=pText->Sub(TruncChar,LastTruncPoint+1);
		if (TruncPoint==-1)
			TruncPoint=pText->Length();
		pText->Left(&text,TruncPoint);
	}
#endif

	pText->Left(pText,LastTruncPoint);

	return TRUE;
}


/*****************************************************************************
>	BOOL StatusLine::GetMousePosText(String_256 pText, DocCoord MousePos, Spread* pSpread, INT32 dp = -1)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	14/12/94
	Purpose:	get mouse position as a string
	Inputs:		MousePos - position of mouse in DocCoords
				pSpread  - pointer to spread mouse is over
	Outputs:	pText    - mouse position as text
	Returns:	FALSE if fails (see Errors)
	Errors:     pCCStatusBar==NULL
				pSpread==NULL
				Spread::SpreadCoordToText() fails
*****************************************************************************/

BOOL StatusLine::GetMousePosText(String_256* pText, DocCoord MousePos, Spread* pSpread, INT32 dp)
{
	ERROR2IF(       pText==NULL,FALSE,"StatusLine::GetMousePosText() - pText==NULL");
	ERROR2IF(     pSpread==NULL,FALSE,"StatusLine::GetMousePosText() - pSpread==NULL");

	if (dp == -1)
		dp = MousePosDP;

	String_256 yPos("");
	if (pSpread->SpreadCoordToText(pText,&yPos,MousePos,FALSE,TRUE,dp)==FALSE)
		return FALSE;

	*pText+=String_64(_R(IDS_K_STATLINE_POSITIONSEP));
	*pText+=yPos;
	return TRUE;
}


/*****************************************************************************
>	BOOL StatusLine::SetMousePosPaneWidth(Spread* pSpread)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	22/12/94
	Purpose:	Set the mouse position pane width to be large enough to
				hold any coord in the spread in the current preference units
				Also gets, and caches, the number of decimal places required to resolve 1 pixel
	Inputs:		pSpread  - pointer to spread mouse is over
	Returns:	FALSE if fails (see Errors)
	Errors:     pCCStatusBar==NULL
				pSpread==NULL
				Spread::GetDecimalPlacesForPixelResolution() fails
				GetMousePosText() fails
				pCCStatusBar->GetTextWidth() fails
				pCCStatusBar->SetPaneWidth() fails
*****************************************************************************/

BOOL StatusLine::SetMousePosPaneWidth(Spread* pSpread)
{	
	ERROR2IF(     pSpread==NULL,FALSE,"StatusLine::SetMousePosPaneWidth() - pSpread==NULL");

	// find/cache number of decimal places required to resolve a pixel with current units & scale
	INT32 dp=2;
	INT32 ReturnValue=TRUE;
	if (pSpread->GetDecimalPlacesForPixelResolution(DocView::GetSelected(), &dp))
		MousePosDP=dp;
	else
		ReturnValue=FALSE;

	// get dimensions of spread PasteRect converted (in DocCoords)
	DocRect  PasteRect=pSpread->GetPasteboardRect();
	DocCoord PasteSize(PasteRect.Width(),PasteRect.Height());

	// get min coord as text, then find it's width
	String_256 MousePosText("");
	if (GetMousePosText(&MousePosText,DocCoord(0,0),pSpread,0)==FALSE)
		return FALSE;

	String_256 DPSChar(_R(IDS_CONVERT_DP_CHAR));

	String_256 ZeroChar(_R(IDS_CONVERT_ZERO_CHAR));

	INT32 i;
	for (i=0;i<dp;i++)
	{
		MousePosText += ZeroChar;
		MousePosText += ZeroChar;

	}

	MousePosText += DPSChar;
	MousePosText += DPSChar;

PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
	INT32 MinCoordTextWidth=pCCStatusBar->GetTextWidth(&MousePosText);
#else
	INT32 MinCoordTextWidth=100;
#endif
	
	if (MinCoordTextWidth==-1)
		return FALSE;
    
	// get max coord as text, then find it's width
	if (GetMousePosText(&MousePosText,PasteSize,pSpread,0)==FALSE)
		return FALSE;

	for (i=0;i<dp;i++)
	{
		MousePosText += ZeroChar;
		MousePosText += ZeroChar;
	}

	MousePosText += DPSChar;
	MousePosText += DPSChar;
	
PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
	INT32 MaxCoordTextWidth=pCCStatusBar->GetTextWidth(&MousePosText) ; 
#else
	INT32 MaxCoordTextWidth=100;
#endif

	if (MaxCoordTextWidth==-1)
		return FALSE;
	
// set pane width
PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
	INT32 TextWidth = max(MaxCoordTextWidth,MinCoordTextWidth);
	if (pCCStatusBar->SetPaneWidth(TextWidth,_R(IDS_SL_MOUSEPOS))==FALSE)
		return FALSE;
#endif
	return ReturnValue;
}


/*****************************************************************************
>	MsgResult StatusLine::Message(Msg* pMsg)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	31/12/94
	Purpose:	handle system messages
	Inputs:		pMsg - pointer to message
	Returns:	OK, EAT_MSG, FAIL (see Errors)
*****************************************************************************/

MsgResult StatusLine::Message(Msg* pMsg)
{
//	FixFPControlRegister();
	ERROR2IF(        pMsg==NULL,FAIL,"StatusLine::Message() - pMsg==NULL");

	if (IS_OUR_DIALOG_MSG(pMsg))
	{
		DialogMsg* Msg = (DialogMsg*)pMsg;
	
		switch(Msg->DlgMsg)
		{
			case DIM_CREATE:
			{
				if (s_pStatusLine)
				{
					ERROR3("Two status lines - that isn't meant to happen");
					delete s_pStatusLine;
				}
				s_pStatusLine = this;
				SetGadgetBitmap(_R(IDB_SL_SNAP), _R(IDB_SL_SNAPN)); // turn snap off by default
				nobreak; // FALL THROUGH!
			}
			case DIM_DLG_RESIZED:
			{
				if (!ProgressShown)
				{
					// For some reason we need to do a relayout with the gadgets shown
					HideGadget(_R(IDC_SL_PROGRESSGAUGE), FALSE);
					HideGadget(_R(IDC_SL_PROGRESSPERCENT), FALSE);
					Layout();					
					HideGadget(_R(IDC_SL_PROGRESSGAUGE), !ProgressShown);
					HideGadget(_R(IDC_SL_PROGRESSPERCENT), !ProgressShown);					
				}
				break;
			}
			default:
				break;

		}
		// Else fall through
	}



	// if pref units changed, or zoom changed flag mouse pos pane needs resizing
	if (MESSAGE_IS_A(pMsg,OptionsChangingMsg) /* && ((OptionsChangingMsg*)pMsg)->State==OptionsChangingMsg::NEWUNITS */
	||  MESSAGE_IS_A(pMsg,DocChangingMsg)        && ((DocChangingMsg*)pMsg)->State==DocChangingMsg::SELCHANGED
	||  MESSAGE_IS_A(pMsg,DocViewMsg)            && ((DocViewMsg*)pMsg)->State==DocViewMsg::SCALECHANGED)
		MousePosPaneNeedsResizing=TRUE;

	// if doc changing flag transparency needs updating & mouse pos pane needs resizing
	if (MESSAGE_IS_A(pMsg,DocChangingMsg) && ((DocChangingMsg*)pMsg)->State==DocChangingMsg::SELCHANGED)
	{
		//TransparencyNeedsUpdating=TRUE; 
		MousePosPaneNeedsResizing=TRUE;
	}

	if (MESSAGE_IS_A(pMsg, DocViewMsg) && ((DocViewMsg*)pMsg)->State==DocViewMsg::SELCHANGED)
	{
		CCamFrame* pMainFrame = GetMainFrame();
		if (pMainFrame)
		{
			DocView* pView = ((DocViewMsg*)pMsg)->pNewDocView;
			BOOL bState = TRUE;
			String_256 sPlateName;
			if (pView)
			{
				ColourPlateType PlateType = COLOURPLATE_NONE;
				ColourContext *cc = pView->GetColourContext(COLOURMODEL_RGBT);
				if (cc && cc->GetColourPlate())
				{
					PlateType = cc->GetColourPlate()->GetType();
				}

				switch (PlateType)
				{
				case COLOURPLATE_CYAN:
					sPlateName.Load(_R(IDS_PLATENAME_CYAN));
					break;

				case COLOURPLATE_MAGENTA:
					sPlateName.Load(_R(IDS_PLATENAME_MAGENTA));
					break;

				case COLOURPLATE_YELLOW:
					sPlateName.Load(_R(IDS_PLATENAME_YELLOW));
					break;

				case COLOURPLATE_KEY:
					sPlateName.Load(_R(IDS_PLATENAME_KEY));
					break;

				case COLOURPLATE_COMPOSITE:
					sPlateName.Load(_R(IDS_PLATENAME_COMPOSITE));
					break;

				case COLOURPLATE_SPOT:
					{
						IndexedColour* pSpotColour = cc->GetColourPlate()->GetSpotColour();
						if (pSpotColour)
							sPlateName = *(pSpotColour->GetName());
					}
					break;

				default:
					bState = FALSE;
					break;
				}
			}
			else
			{
				bState = FALSE;
			}
PORTNOTE("printing", "Removed SetPrinterColourStatus");
#ifndef EXCLUDE_FROM_XARALX
			pMainFrame->SetPrinterColourStatus(bState, &sPlateName);
#endif
		}

	}

	return StandardBar::Message(pMsg);
}


/*******************************************************************************************************
>	BOOL StatusLine::UpdateMousePosAndSnap(DocCoord* pDocCoord, Spread* pSpread,
										   DocView* pDocView,   BOOL Snapped)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	2/1/95
	Inputs:		pDocoord - 
				pSpread  - 
				pDocView - 
				Snapped  - 
	Returns:	FALSE if anything fails (see Errors)
	Purpose:	Update mouse position pane, and snapped indicator
				If pDocCoord==NULL && pSpread==NULL && Snapped==FALSE, the mouse pos will be blanked
********************************************************************************************************/
BOOL StatusLine::UpdateMousePosAndSnap(DocCoord* pDocCoord, Spread* pSpread,
									   DocView* pDocView,   BOOL Snapped)
{
#ifndef STANDALONE

	if (MousePosPaneNeedsResizing)
		return TRUE;

	BOOL Blank = (pDocCoord==NULL && pSpread==NULL && Snapped==FALSE);
	if (!Blank)
	{
		ERROR2IF(     pSpread==NULL,FALSE,"StatusLine::UpdateMousePosAndSnap() - pSpread==NULL");
		ERROR2IF(   pDocCoord==NULL,FALSE,"StatusLine::UpdateMousePosAndSnap() - pDocCoord==NULL");
	}

	BOOL ReturnValue=TRUE;

	// update mouse position pane if over selected doc, else blank it
	String_256 MousePosText("");
	if (!Blank && GetMousePosText(&MousePosText,*pDocCoord,pSpread)==FALSE)
		ReturnValue=FALSE;

	if (MousePosText != m_MousePosText)
	{
		m_MousePosText= MousePosText;

		ReturnValue &=SetStringGadgetValue(_R(IDC_SL_MOUSEPOS), MousePosText);
		//	PaintGadgetNow(_R(IDC_SL_MOUSEPOS)); - is there any need to paint this now? Flushes gtk buffer etc. Not a great idea.
	}

// WEBSTER - markn 15/1/97
// No rulers in Webster
#ifndef WEBSTER
	// and update ruler mouse followers
	DocView* pView = DocView::GetSelected();
	if (pView!=NULL)
	{
		RulerPair* pRulerPair = pView->GetpRulerPair();
		if (pRulerPair!=NULL)
			ReturnValue = pRulerPair->UpdateMouseFollowers(pDocCoord);
	}
#endif // WEBSTER

//	WEBSTER-ranbirr-13/11/96
#ifndef WEBSTER
	// if snap indicator not reflecting the desired state, update it.
	if (Snapped && (GetGadgetBitmap(_R(IDB_SL_SNAP)) != _R(IDB_SL_SNAP)))
	{
		SetGadgetBitmap(_R(IDB_SL_SNAP), _R(IDB_SL_SNAP));
		PaintGadgetNow(_R(IDB_SL_SNAP));
	}
	if (!Snapped && (GetGadgetBitmap(_R(IDB_SL_SNAP)) != _R(IDB_SL_SNAPN)))
	{
		SetGadgetBitmap(_R(IDB_SL_SNAP), _R(IDB_SL_SNAPN));
		PaintGadgetNow(_R(IDB_SL_SNAP));
	}
#endif	
	
	// if snap pointer not reflecting the desired state, update it.
	// (The snap pointer is only shown when snapped and during a 'SnappingDrag' drag)
	Operation* pDragOp = Operation::GetCurrentDragOp();
	if (Snapped && pDragOp!=NULL && pDragOp->SnappingDrag())
	{
		// Snapped and dragging so show pointer shape if not already shown
		if (SnappedCursorID==0)
		{
			SnappedCursorID = CursorStack::GPush(pSnappedCursor);
		}
	}
	else
	{
		// Not snapped or dragging so hide pointer shape if it was shown
		if (SnappedCursorID!=0)
		{
			CursorStack::GPop(SnappedCursorID);
			SnappedCursorID = 0;	// flag cursor no longer on stack
		}
	}

	// and mouse position no longer needs updating
	MousePosNeedsUpdatingFlag=FALSE;
	return ReturnValue;

#else
	return TRUE;
#endif
}



/*******************************************************************************************************
>	BOOL StatusLine::UpdateSolidDragIndicator(BOOL bSolidAllowed, BOOL bSolid)

	Author:		Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
	Created:	23/12/2003
	Inputs:		bSolid  - Is Solid mode turned on?
	Returns:	FALSE if anything fails (see Errors)
	Purpose:	Update mouse position pane, and snapped indicator
				If pDocCoord==NULL && pSpread==NULL && Snapped==FALSE, the mouse pos will be blanked
********************************************************************************************************/
BOOL StatusLine::UpdateSolidDragIndicator(BOOL bSolidAllowed, BOOL bSolid)
{
//	if (MousePosPaneNeedsResizing)
//		return TRUE;

	BOOL ReturnValue=TRUE;

	// if drag indicator not reflecting the desired state, update it.
PORTNOTE("StatusLine", "Removed use of CCStatusBar")
#ifndef EXCLUDE_FROM_XARALX
	FlagState DragState = pCCStatusBar->PaneState(_R(IDS_SL_SDRAG));
	if (bSolid && DragState==Disable)
	{
		if (pCCStatusBar->PaneState(_R(IDS_SL_SDRAG), Enable)==Fail)
			ReturnValue=FALSE;

		// force a redraw of the drag pane
		pCCStatusBar->UpdatePaneBitmap(_R(IDS_SL_SDRAG),0, TRUE);
	
	}
	if (!bSolid && DragState!=Disable)
	{
		if (pCCStatusBar->PaneState(_R(IDS_SL_SDRAG), Disable)==Fail)
			ReturnValue=FALSE;

		// force a redraw of the snap pane
		pCCStatusBar->UpdatePaneBitmap(_R(IDS_SL_SDRAG),0, TRUE);
	}
#endif
	
	return ReturnValue;
}


/*****************************************************************************
>	BOOL StatusLine::SetRenderIndicator(RenderState Action)

	Author:		Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
	Created:	27/1/95
	Purpose:	Change state of the render indicator
	Inputs:		Action - Rendering	  display 'Rendering'    indicator	
						 Paused		  display 'Paused'       indicator	
						 NotRendering display 'NotRendering' indicator (reset animation)
						 Animate	  display char in animation 
									  (if sufficient time elapsed, advance animation)
	Returns:	FALSE if anything fails (see Errors)
	Errors:		pCCStatusBar==NULL,
				Action invalid,
				CStatusBar::UpdatePaneText() fails,
				StringBase::Load() fails
*****************************************************************************/

BOOL StatusLine::SetRenderIndicator(RenderState Action)
{
	INT32  RenderAnimationStates=sizeof(RenderAnimation)/sizeof(UINT32);
	UINT32 BitmapID=0;
	switch (Action)
	{
		case NotRendering:
			BitmapID=_R(IDB_SL_RENDN);
			RenderAnimationState=0;
			break;
		case Paused:
			BitmapID=_R(IDB_SL_RENDP);
			break;
		case Rendering:
			BitmapID=_R(IDB_SL_RENDR);
			RenderAnimationState=0;
			break;
		case Animate:
			if (RenderTimer.Elapsed(RenderDelay))
			{
				if (RenderAnimationState<0 || RenderAnimationState>=RenderAnimationStates)
					RenderAnimationState=0;
				BitmapID=RenderAnimation[RenderAnimationState++];
			}
			break;
		default: ERROR2(FALSE,"StatusLine::SetRenderIndicator() - Action invalid");
	}

	if (!BitmapID)
		return TRUE;

	RenderTimer.Sample();

	if (BitmapID == GetGadgetBitmap(_R(IDB_SL_RENDN)))
		return TRUE;
	SetGadgetBitmap(_R(IDB_SL_RENDN), BitmapID);
	//PaintGadgetNow(_R(IDB_SL_RENDN));

	return TRUE;
}

/********************************************************************************************
>	BOOL StatusLine::ShowProgress (BOOL Show=TRUE, StringBase *JobDescrip = NULL)

	Author:		Alex Bligh
	Created:	09/05/2006
	Inputs:		BOOL Show : TRUE to show, else false
				JobDescription - A BRIEF string describing the job currently being undertaken.
						This will be displayed on the progress bar if possible
	Outputs:	-
	Returns:	TRUE if the initialisation of the progress bar was successful.
	Purpose:	Creates a window and associates it with this CProgressBar object. The window
				appears immediately over the status bar.
	Notes:		This currently assumes that it'll only ever be opened at the bottom of the
				Main Frame window.
	SeeAlso:	StatusLine::SetPercent; StatusLine::GetPercent
********************************************************************************************/

BOOL StatusLine::ShowProgress (BOOL Show, StringBase *JobDescrip)
{
	BOOL ShowChanged=(Show != ProgressShown);

	String_8 EmptyString(_T(""));

	// Make "NULL" mean "empty string"
	if (!JobDescrip)
		JobDescrip=&EmptyString;

	BOOL StringChanged=!JobDescription || (*JobDescription!=*JobDescrip);

	// Return if nothing changed
	if (!ShowChanged && !StringChanged)
		return TRUE;

	if (ShowChanged)
	{
		HideGadget(_R(IDC_SL_PROGRESSGAUGE), !Show);
		HideGadget(_R(IDC_SL_PROGRESSPERCENT), !Show);
		Layout();
		ProgressShown=Show;

		CurrentPercent=-1;		// Force a redraw
		SetPercent(0, TRUE, JobDescrip);	// Force redraw of window _including background_; always refresh string

		return TRUE;
	}

	return SetPercent(CurrentPercent, StringChanged, StringChanged?JobDescrip:NULL);
}


/********************************************************************************************
>	BOOL StatusLine::SetPercent(INT32 NewPercent,
									BOOL ClearBackground = FALSE, StringBase *JobDescrip = NULL)

	Author:		Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
	Created:	15/02/94
	Inputs:		NewPercent - the new percentage value to be displayed by the progress bar

				ClearBackground - Clears the entire bar background and redraws everything, rather
				than doing a (far more efficient) update of the bar/percentage alone. Use the default
				of FALSE unless absolutely necessary!

				JobDescrip - NULL, or a pointer to a new job description - pass NULL if this hasn't
				changed, as it makes a copy of the new string every time it is changed.	If this is
				non-NULL, then the ClearBackground flag will be forced to TRUE to draw the new text.

	Outputs:	-
	Returns:	TRUE if it successfully makes the change.
	Purpose:	Sets the currently displayed percentage of the progress bar. The bar
				will be immediately redrawn to reflect the new value. Values outside the
				range 0..99 (inclusive) are clipped to 0 or 99. No redraw will be done if
				NewPercent equals the currently displayed value.
	SeeAlso:	CProgressBar::GetPercent; CProgressBar::Create
********************************************************************************************/

BOOL StatusLine::SetPercent(INT32 NewPercent, BOOL ClearBackground /* =FALSE */,
								StringBase *JobDescrip /* =NULL */)
// The extra argument ClearBackground is not mentioned in the help, because it
// is used internally - When the window is first created, the background must be cleared,
// but on normal updates this causes horrible flicker, so is to be avoided.
{
	if (NewPercent < 0) NewPercent = 0;
	if (NewPercent > 99) NewPercent = 99;

	// If there's no change, don't bother updating
	if (JobDescrip == NULL && NewPercent == CurrentPercent)
		return(TRUE);

	CurrentPercent = NewPercent;

	String_64 PercentString;
	PercentString.MakePercent(CurrentPercent);
	SetStringGadgetValue(_R(IDC_SL_PROGRESSPERCENT), PercentString);
	SetLongGadgetValue(_R(IDC_SL_PROGRESSGAUGE), CurrentPercent);

	// If there is a new Job Description, change to use it
	if (JobDescrip != NULL)
	{
		delete JobDescription;
		JobDescription = new String_256(*JobDescrip);

		if (JobDescription)
		{
			SetStringGadgetValue(_R(IDC_SL_STATUSTEXT), ProgressShown?*JobDescription:StatusText);
		}
		else
		{
			SetStringGadgetValue(_R(IDC_SL_STATUSTEXT), ProgressShown?String_256(_T("")):StatusText);
		}

		InvalidateGadget(0, TRUE); // Repaint the whole bar
		PaintGadgetNow(0);
	}
	else
	{
		// Just repaint the gauge
		InvalidateGadget(_R(IDC_SL_PROGRESSGAUGE), ClearBackground);
		InvalidateGadget(_R(IDC_SL_PROGRESSPERCENT), ClearBackground);
		PaintGadgetNow(_R(IDC_SL_PROGRESSGAUGE));
		PaintGadgetNow(_R(IDC_SL_PROGRESSPERCENT));
	}

	return(TRUE);
}

/********************************************************************************************
>	BOOL StatusLine::SetStatusText(const String_256 &text)

	Author:		Alex Bligh <alex@alex.org.uk>
	Created:	09/05/2006
	Inputs:		text - the text to set it to

	Outputs:	-
	Returns:	TRUE if it successfully makes the change.
	Purpose:	Sets the status line text. Records it. Puts it in the gadget and invalidates
				it if the gadget is shown
	SeeAlso:	CProgressBar::GetPercent; CProgressBar::Create

********************************************************************************************/

BOOL StatusLine::SetStatusText(const String_256 &text)
{
	SetNeedsUpdate(); // Don't allow another update on idles for 250ms

	if (StatusText == text)
		return TRUE;

	StatusText = text;
	if (!ProgressShown)
	{
		TRACEUSER("amb", _T("updating status text"));
		BOOL ok=SetStringGadgetValue(_R(IDC_SL_STATUSTEXT), text);
//		PaintGadgetNow(_R(IDC_SL_STATUSTEXT));
		return ok;
	}
	return TRUE;
}
