diff -r 000000000000 -r 08ec8eefde2f loggingservices/eventlogger/LogCli/src/LogViewWindow.cpp --- /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; iId() == aId) + return i; + } + return KErrNotFound; + } + +TInt CLogViewWindow::FindAndRemoveEvent(TLogId aId) + { + TInt index = FindEvent(aId); + if (index >= 0) + { + RemoveEvent(index); + index += iWindow.iLower; + } + return index; + }