loggingservices/eventlogger/LogCli/src/LogViewWindow.cpp
changeset 0 08ec8eefde2f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loggingservices/eventlogger/LogCli/src/LogViewWindow.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,732 @@
+// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "LogViewWindow.h"
+
+// User includes
+#include "logclientop.h"
+#include "logclipanic.h"
+#include "LogViewWindowChangeObserver.h"
+
+// Constants
+const TInt KWindowSlideSize = 2;
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// -----> CLogViewWindow (source)
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CLogViewWindow::CLogViewWindow(RLogSession& aSession, TLogViewId aViewId, TInt aWindowSize, MLogViewChangeObserver* aCascadeObserver, TInt aPriority)
+:	CLogActive(aPriority), iSession(aSession), iViewId(aViewId), iWindowSize(aWindowSize), iCascadeObserver(aCascadeObserver), iEvents(aWindowSize)
+	{
+	Reset();
+	}
+
+CLogViewWindow::~CLogViewWindow()
+	{
+	Cancel();
+	//
+	delete iWindowChangeObserver;
+	delete iWindowPreparer;
+	delete iWindowFetcher;
+	delete iWindowLockObserver;
+	//
+	iEvents.ResetAndDestroy();
+	iEvents.Close();
+	}
+
+void CLogViewWindow::ConstructL(CLogPackage& aPackage)
+	{
+	iWindowPreparer = new(ELeave) CLogViewSetupClientOp(iSession, aPackage, CActive::EPriorityStandard);
+	//
+	iWindowFetcher = new(ELeave) CLogViewWindowFetcher(iSession, iViewId, *this, CActive::EPriorityIdle + 1);
+	iWindowFetcher->ConstructL();
+	//
+	iWindowLockObserver = new(ELeave) CLogViewWindowLockObserver(iSession, iViewId, *this, CActive::EPriorityHigh);
+	iWindowLockObserver->ConstructL();
+	//
+	iWindowChangeObserver = new(ELeave) CLogViewWindowChangeObserver(*this);
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+TInt CLogViewWindow::Setup(const CLogFilterList& aFilterList, TInt aParam, TLogFilterConstructionType aFilterConstructionType)
+	{
+	Reset();
+	iViewRecordCount = iWindowPreparer->Start(iViewId, aFilterList, aParam, aFilterConstructionType);
+	return iViewRecordCount;
+	}
+
+TBool CLogViewWindow::NavigateL(TLogNavigation aNavigate, TRequestStatus& aStatus)
+	{
+	// Check why we're already active
+	switch(iState)
+		{
+	case EStateIdle:
+	case EStateLocked:
+		__ASSERT_DEBUG(!IsActive(), Panic(ELogWindowStateMachineNavigationError1));
+		break;
+	case EStateFetchingWindow:
+	case EStateProcessingWindow:
+	case EStateNavigateWithinWindow:
+		// We're trying to catch up with some changes.. use the requested
+		// window as the new window, but refetch everything.
+		__ASSERT_DEBUG(IsActive(), Panic(ELogWindowStateMachineNavigationError2));
+		SilentCancel();
+		iWindow = iWindowFetcher->RequestedWindow();
+		break;
+	default:
+		break;
+		}
+
+	// Perform boundary checks
+	TInt cursorPos = CalculateCursorPosition(aNavigate);
+	if	(cursorPos < 0 || cursorPos >= iViewRecordCount)
+		{
+		// Can't navigate to the specified position
+		return EFalse;
+		}
+
+	// Check whether the cursor position falls within the view window 
+	if	(iWindow.iValid && iWindow.Contains(cursorPos))
+		{
+		// Can return event from window
+		CompleteRequest(cursorPos);
+		ChangeState(EStateNavigateWithinWindow);
+		}
+	else
+		{
+		// Have to fetch a new window. Work it out...
+		TLogWindowAndCursor window;
+		CalculateWindowForCursorPosition(cursorPos, window);
+		window.iCursorPosition = cursorPos;
+		iWindowFetcher->PrepareToFetchWindowL(window);
+		//		
+		CompleteRequest(KErrNone);
+		ChangeState(EStateFetchingWindow);
+		}
+
+	// Okay to (try to) navigate here
+	Queue(aStatus);
+	return ETrue;
+	}
+
+void CLogViewWindow::RemoveFromWindowIfPresentL(TLogId aId)
+	{
+	TInt index = FindEvent(aId);
+	if	(index >= 0)
+		{
+		// Ignore next window removal event (since we're proactively removing
+		// the event from the client side without server prompting).
+		iWindowChangeObserver->IgnoreNextEventL(aId, CLogViewWindowChangeObserver::ELogEventTypeDelete);
+
+		// Delete item	
+		RemoveEvent(index);
+
+		// Map onto full view position
+		index += iWindow.iLower;
+
+		// Update window - when removing the last event from the window, we must
+		// ensure we mark the window as invalid.
+		if	(iWindow.AdjustForItemDeletion(index) == TLogWindowAndCursor::EWindowAffected && iEvents.Count() == 0)
+			{
+			iWindow.Reset();
+			}
+		}
+	//
+	--iViewRecordCount;
+	}
+
+MLogViewChangeObserverInternal& CLogViewWindow::ChangeObserver()
+	{
+	__ASSERT_ALWAYS(iWindowChangeObserver, Panic(ELogWindowNoChangeObserver));
+	return *iWindowChangeObserver;
+	}
+
+const CLogEvent& CLogViewWindow::CurrsorEvent() const
+	{
+	// Map the cursor position to fall within the window
+	const TInt mappedIndex = iWindow.WindowIndexFromCursorPosition();
+	const TInt count = iEvents.Count();
+	__ASSERT_ALWAYS(mappedIndex >= 0 && mappedIndex < count, Panic(ELogWindowCursorCalculationOutOfBounds));
+	return *iEvents[mappedIndex];
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::HandleWindowLockStatusChangeL(TLogViewLockStatus aStatus)
+	{
+	switch(aStatus)
+		{
+	case ELogViewWindowLocked:
+		SilentCancel();
+		iState = EStateLocked;
+		break;
+	case ELogViewWindowOpen:
+		if (iState == EStateLocked)
+			iState = EStateIdle; 
+		break;
+	default:
+		break;
+		}
+
+	// The window is never valid after a change in lock status
+	iWindow.iValid = EFalse;
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::HandleFetchedWindowItemL(TInt /*aItemIndex*/, CLogEvent* aEvent)
+	{
+	// IMPROVEMENT: could use aItemIndex as the insertion point?
+	User::LeaveIfError(iEvents.Append(aEvent));
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::HandleLogViewChangeEventAddedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
+	{
+	///////////////////////////////////////
+	// USE CASE 1:
+	///////////////////////////////////////
+	// Cursor position:  *
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item X is added => 
+	// 
+	// Cursor position:    *
+	// View Index:       0 1 2 3 4 5 6 7
+	// View Contents:    X A B C D E F G
+	// 
+	///////////////////////////////////////
+	// USE CASE 2:
+	///////////////////////////////////////
+	// Cursor position:              *
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item X is added => 
+	// 
+	// Cursor position:                *
+	// View Index:       0 1 2 3 4 5 6 7
+	// View Contents:    X A B C D E F G
+	// 
+	///////////////////////////////////////
+	// USE CASE 3:
+	///////////////////////////////////////
+	// Cursor position:    *          
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item X is added => 
+	// 
+	// Cursor position:      *          
+	// View Index:       0 1 2 3 4 5 6 7
+	// View Contents:    X A B C D E F G
+	// 
+	///////////////////////////////////////
+	// USE CASE 4:
+	///////////////////////////////////////
+	// Cursor position:    *          
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, change item Z so that it now appears in the view
+	// 
+	// Cursor position:    *          
+	// View Index:       0 1 2 3 4 5 6 7
+	// View Contents:    A B C Z D E F G
+	// 
+	///////////////////////////////////////
+	// USE CASE 5:
+	///////////////////////////////////////
+	// Cursor position:        *      
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, change item Z so that it now appears in the view
+	// 
+	// Cursor position:          *   
+	// View Index:       0 1 2 3 4 5 6 7
+	// View Contents:    A B C Z D E F G
+	// 
+	///////////////////////////////////////
+	//RDebug::Print(_L("CLogViewWindow::HandleLogViewChangeEventAddedL()   - aId: %3d, aViewIndex: %3d, aChangeIndex: %3d, aTotalChangeCount: %d, cursorPos: %3d, window: (%3d, %3d), viewCount: %3d, iState: %d"), aId, aViewIndex, aChangeIndex, aTotalChangeCount, iWindow.iCursorPosition, iWindow.iLower, iWindow.iUpper, iViewRecordCount, iState);
+
+	// Must compare against the current window (if this object is idle) or against
+	// the window fetcher's window if it is in the process of making a fetch
+	TLogWindowAndCursor currentWindow;
+	switch(iState)
+		{
+	case EStateFetchingWindow:
+	case EStateProcessingWindow:
+		currentWindow = iWindowFetcher->RequestedWindow();
+		break;
+	case EStateNavigateWithinWindow:
+	case EStateIdle:
+	case EStateLocked:
+		currentWindow = iWindow;
+		break;
+		}
+
+	// If the addition took place after the window, then we do nothing (except update the
+	// total view record count)
+	if	(aViewIndex <= currentWindow.iUpper)
+		{
+		// If the addition took place before our window, or at the very start, then we simply need to adjust 
+		// the window & cursor position. If the window fetcher was active then this will affect the window
+		// being fetched - in this case, we MUST refetch the whole window.
+		if	(iState == EStateIdle && aViewIndex <= currentWindow.iLower)
+			{
+			iWindow.AdjustForItemAddition(aViewIndex);
+			}
+		else
+			{
+			// If the addition took place within the window, then we have to refetch the window
+			TBool refetch = ETrue;
+			TLogWindowAndCursor newWindow;
+			//
+			switch(iState)
+				{
+			case EStateIdle:
+				newWindow = iWindow;
+				break;
+			case EStateNavigateWithinWindow:
+				// Since we complete our own request status with the new cursor position, 
+				// we can use that here as the desired cursor position after the fetch
+				newWindow = iWindow;
+				newWindow.iCursorPosition = iStatus.Int();
+				break;
+			case EStateFetchingWindow:
+			case EStateProcessingWindow:
+				newWindow = iWindowFetcher->RequestedWindow();
+				break;
+			case EStateLocked:
+				// Don't need to do anything. When the view is unlocked, we refetch anyway
+				refetch = EFalse;
+				break;
+			default:
+				break;
+				}
+			//
+			if	(refetch)
+				{
+				newWindow.AdjustForItemAddition(aViewIndex);
+				RefetchL(newWindow, newWindow.iCursorPosition);
+				}
+			}
+		}
+
+	// Increase the total view size
+	++iViewRecordCount;
+
+	if	(iCascadeObserver)
+		iCascadeObserver->HandleLogViewChangeEventAddedL(aId, aViewIndex, aChangeIndex, aTotalChangeCount);
+	}
+
+void CLogViewWindow::HandleLogViewChangeEventChangedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
+	{
+	//RDebug::Print(_L("CLogViewWindow::HandleLogViewChangeEventChangedL() - aId: %3d, aViewIndex: %3d, aChangeIndex: %3d, aTotalChangeCount: %d, cursorPos: %3d, window: (%3d, %3d), viewCount: %3d, iState: %d"), aId, aViewIndex, aChangeIndex, aTotalChangeCount, iWindow.iCursorPosition, iWindow.iLower, iWindow.iUpper, iViewRecordCount, iState);
+
+	// Must compare against the current window (if this object is idle) or against
+	// the window fetcher's window if it is in the process of making a fetch
+	TLogWindowAndCursor currentWindow;
+	switch(iState)
+		{
+	case EStateFetchingWindow:
+	case EStateProcessingWindow:
+		currentWindow = iWindowFetcher->RequestedWindow();
+		break;
+	case EStateNavigateWithinWindow:
+	case EStateIdle:
+	case EStateLocked:
+		currentWindow = iWindow;
+		break;
+	default:
+		break;
+		}
+
+	// If the event that changed was within the view, then we have to refetch it
+	if	(currentWindow.Contains(aViewIndex))
+		{
+		switch(iState)
+			{
+		case EStateIdle:
+			RefetchL(iWindow, iWindow.iCursorPosition);
+			break;
+		case EStateNavigateWithinWindow:
+			// Since we complete our own request status with the new cursor position, 
+			// we can use that here as the desired cursor position after the fetch
+			RefetchL(iWindow, iStatus.Int());
+			break;
+		case EStateFetchingWindow:
+		case EStateProcessingWindow:
+			RefetchL(iWindowFetcher->RequestedWindow(), iWindowFetcher->RequestedWindow().iCursorPosition);
+			break;
+		case EStateLocked:
+			// Don't need to do anything. When the view is unlocked, we refetch anyway
+			break;
+		default:
+			break;
+			}
+		}
+
+	if	(iCascadeObserver)
+		iCascadeObserver->HandleLogViewChangeEventChangedL(aId, aViewIndex, aChangeIndex, aTotalChangeCount);
+	}
+
+void CLogViewWindow::HandleLogViewChangeEventDeletedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
+	{
+	///////////////////////////////////////
+	// USE CASE 1:
+	///////////////////////////////////////
+	// Cursor position:            *
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item 5 is deleted => 
+	// 
+	// Cursor position:            *
+	// View Index:       0 1 2 3 4 5
+	// View Contents:    A B C D E G
+	// 
+	///////////////////////////////////////
+	// USE CASE 2:
+	///////////////////////////////////////
+	// Cursor position:            *
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item 4 is deleted => 
+	// 
+	// Cursor position:          *
+	// View Index:       0 1 2 3 4 5
+	// View Contents:    A B C D F G
+	// 
+	///////////////////////////////////////
+	// USE CASE 3:
+	///////////////////////////////////////
+	// Cursor position:              *
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item 6 is deleted => 
+	// 
+	// Cursor position:            *
+	// View Index:       0 1 2 3 4 5
+	// View Contents:    A B C D E F
+	// 
+	///////////////////////////////////////
+	// USE CASE 4:
+	///////////////////////////////////////
+	// Cursor position:  *            
+	// View Index:       0 1 2 3 4 5 6
+	// View Contents:    A B C D E F G
+	//
+	// Then, item 6 is deleted => 
+	// 
+	// Cursor position:  *
+	// View Index:       0 1 2 3 4 5
+	// View Contents:    B C D E F G
+	// 
+	///////////////////////////////////////
+	//RDebug::Print(_L("CLogViewWindow::HandleLogViewChangeEventDeletedL() - aId: %3d, aViewIndex: %3d, aChangeIndex: %3d, aTotalChangeCount: %d, cursorPos: %3d, window: (%3d, %3d), viewCount: %3d, iState: %d"), aId, aViewIndex, aChangeIndex, aTotalChangeCount, iWindow.iCursorPosition, iWindow.iLower, iWindow.iUpper, iViewRecordCount, iState);
+
+	// Must compare against the current window (if this object is idle) or against
+	// the window fetcher's window if it is in the process of making a fetch
+	TLogWindowAndCursor currentWindow;
+	switch(iState)
+		{
+	case EStateFetchingWindow:
+	case EStateProcessingWindow:
+		currentWindow = iWindowFetcher->RequestedWindow();
+		break;
+	case EStateNavigateWithinWindow:
+	case EStateIdle:
+	case EStateLocked:
+		currentWindow = iWindow;
+		break;
+	default:
+		break;
+		}
+
+	// Does the change alter our current window in any way?
+	if	(aViewIndex <= currentWindow.iUpper)
+		{
+		TBool refetch = ETrue;
+		TLogWindowAndCursor newWindow;
+		//
+		switch(iState)
+			{
+		case EStateIdle:
+			FindAndRemoveEvent(aId);
+
+			// When removing the last event from the window, we must ensure we mark
+			// the window as invalid.
+			if	(iWindow.AdjustForItemDeletion(aViewIndex) == TLogWindowAndCursor::EWindowAffected && iEvents.Count() == 0)
+				{
+				iWindow.Reset();
+				}
+
+			refetch = EFalse;
+			break;
+		case EStateNavigateWithinWindow:
+			newWindow = iWindow;
+			newWindow.iCursorPosition = iStatus.Int();
+			break;
+		case EStateFetchingWindow:
+		case EStateProcessingWindow:
+			newWindow = iWindowFetcher->RequestedWindow();
+			break;
+		case EStateLocked:
+			// Don't need to do anything. When the view is unlocked, we refetch anyway
+			refetch = EFalse;
+			break;
+		default:
+			break;
+			}
+		//
+		if	(refetch)
+			{
+			newWindow.AdjustForItemDeletion(aViewIndex);
+			RefetchL(newWindow, newWindow.iCursorPosition);
+			}
+		}
+
+	// Reduce the total view size
+	--iViewRecordCount;
+
+	if	(iCascadeObserver)
+		iCascadeObserver->HandleLogViewChangeEventDeletedL(aId, aViewIndex, aChangeIndex, aTotalChangeCount);
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::HandleLogViewChangeEventLogClearedL()
+	{
+	Cancel();
+	Reset();
+
+	// This event type is not cascaded to the client of the log engine
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::DoRunL()
+	{
+	switch(iState)
+		{
+	case EStateFetchingWindow:
+		StateHandleWindowFetchStarting();
+		break;
+	case EStateProcessingWindow:
+		StateHandleWindowFetchedL();
+		break;
+	case EStateNavigateWithinWindow:
+		StateHandleNavigation();
+		break;
+	case EStateIdle:
+	case EStateLocked:
+	default:
+		break;
+		}
+	}
+
+void CLogViewWindow::DoCancel()
+	{
+	if(iState == EStateProcessingWindow)
+		{
+		iWindowFetcher->Cancel();
+		}
+	CLogActive::DoCancel();
+	}
+
+void CLogViewWindow::DoComplete(TInt& aComplete)
+	{
+	switch(iState)
+		{
+	default:
+	case EStateIdle:
+		break;
+	case EStateNavigateWithinWindow:
+		if	(aComplete < KErrNone)
+			{
+			// Reset to known state
+			iState = EStateIdle;
+			}
+		break;
+	case EStateFetchingWindow:
+	case EStateProcessingWindow:
+		if	(aComplete < KErrNone)
+			{
+			// Reset to known state
+			iState = EStateIdle;
+			iWindow = iWindowFetcher->RequestedWindow();
+			}
+		break;
+	case EStateLocked:
+		aComplete = KErrAccessDenied;
+		break;
+		}
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::StateHandleWindowFetchStarting()
+	{
+	iEvents.ResetAndDestroy();
+	iWindowFetcher->Start(iStatus);
+	ChangeState(EStateProcessingWindow);
+	SetActive();
+	}
+
+void CLogViewWindow::StateHandleWindowFetchedL()
+	{
+	const TInt count = iEvents.Count();
+	const TInt expected = iWindowFetcher->RequestedWindow().Range();
+	if	(expected != count)
+		User::Leave(KErrGeneral);
+	//	
+	iWindow = iWindowFetcher->RequestedWindow();
+	CompleteRequest(iWindow.iCursorPosition);
+	ChangeState(EStateNavigateWithinWindow);
+	}
+
+void CLogViewWindow::StateHandleNavigation()
+	{
+	const TInt cursorPos = iStatus.Int();
+	__ASSERT_ALWAYS(iWindow.Contains(cursorPos), Panic(ELogWindowNavigationOutsideWindow));
+	iWindow.iCursorPosition = cursorPos;
+	ChangeState(EStateIdle);
+	}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogViewWindow::CompleteRequest(TInt aCompletionCode)
+	{
+	if	(!IsActive() || (IsActive() && iStatus == KRequestPending))
+		{
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, aCompletionCode);
+		if	(!IsActive())
+			SetActive();
+		}
+	}
+
+void CLogViewWindow::CalculateWindowForCursorPosition(TInt aCursorPosition, TLogWindow& aWindow) const
+	{
+	aWindow.iLower = Max(0, aCursorPosition - KWindowSlideSize - 1);
+	aWindow.iUpper = Min(iViewRecordCount - 1, aWindow.iLower + iWindowSize - 1);
+	}
+
+void CLogViewWindow::Reset()
+	{
+	Cancel();
+	//
+	iViewRecordCount = 0;
+	iWindow.Reset();
+	iEvents.ResetAndDestroy();
+	iState = EStateIdle;
+	}
+
+void CLogViewWindow::SilentCancel()
+	{
+	iWindowFetcher->SilentCancel();
+	}
+
+void CLogViewWindow::RefetchL(const TLogWindow& aWindow, TInt aCursor)
+	{
+	SilentCancel();
+	//
+	TLogWindowAndCursor newWindow(aWindow, aCursor);
+	iWindowFetcher->PrepareToFetchWindowL(newWindow);
+	CompleteRequest(KErrNone);
+	ChangeState(EStateFetchingWindow);
+	}
+
+void CLogViewWindow::ChangeState(TWindowState aNewState)
+	{
+	iState = aNewState;
+	}
+
+void CLogViewWindow::RemoveEvent(TInt aIndex)
+	{
+	CLogEvent* event = iEvents[aIndex];
+	delete event;
+	iEvents.Remove(aIndex);
+	}
+
+TInt CLogViewWindow::CalculateCursorPosition(TLogNavigation aNavigate) const
+	{
+	TInt position = 0;
+	switch(aNavigate)
+		{
+	case ELogNavigateForwards:
+		position = iWindow.iCursorPosition + 1;
+		break;
+	case ELogNavigateBackwards:
+		position = iWindow.iCursorPosition - 1;
+		break;
+	case ELogNavigateFirst:
+		position = 0;
+		break;
+	case ELogNavigateLast:
+		position = iViewRecordCount - 1;
+		break;
+	default:
+		break;
+		}
+	return position;
+	}
+
+TInt CLogViewWindow::FindEvent(TLogId aId)
+	{
+	const TInt count = iEvents.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		CLogEvent* event = iEvents[i];
+		if	(event->Id() == aId)
+			return i;
+		}
+	return KErrNotFound;
+	}
+
+TInt CLogViewWindow::FindAndRemoveEvent(TLogId aId)
+	{
+	TInt index = FindEvent(aId);
+	if	(index >= 0)
+		{
+		RemoveEvent(index);
+		index += iWindow.iLower;
+		}
+	return index;
+	}