/** @file Control (which make up panel) related Functions for H2O display engine driver. ;****************************************************************************** ;* Copyright (c) 2013 - 2019, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "LTDEControl.h" #include "LTDEPanels.h" #include "LTDEPrint.h" #include "LTDEMisc.h" H2O_LTDE_CONTROL * GetControlById ( IN H2O_LTDE_PANEL *Panel, IN UINT32 ItemId, IN UINT32 ControlId ) { H2O_LTDE_PANEL_ITEM *PanelItem; PanelItem = GetPanelItem (Panel, ItemId); if (PanelItem == NULL) { return NULL; } return FindControlByControlId (PanelItem->ControlList, PanelItem->ControlCount, ControlId, 0); } H2O_LTDE_CONTROL * FindControlByControlId ( IN H2O_LTDE_CONTROL *ControlArray, IN UINT32 ControlArrayCount, IN UINT32 ControlId, IN UINT32 SequenceIndex ) { UINT32 Index; UINT32 Count; if (ControlArray == NULL) { return NULL; } Count = 0; for (Index = 0; Index < ControlArrayCount; Index++) { if (ControlArray[Index].ControlId != ControlId) { continue; } if (Count == SequenceIndex) { return &ControlArray[Index]; } Count++; } return NULL; } H2O_LTDE_CONTROL * GetControlByQuestionId ( IN H2O_LTDE_CONTROL *ControlList, IN UINT32 ControlCount, IN EFI_QUESTION_ID QuestionId, IN EFI_IFR_OP_HEADER *IfrOpCode ) { UINT32 Index; if (ControlList == NULL || ControlCount == 0) { return NULL; } for (Index = 0; Index < ControlCount; Index++) { if ((QuestionId != 0 && ControlList[Index].QuestionId == QuestionId) || (IfrOpCode != NULL && ControlList[Index].IfrOpCode == IfrOpCode)) { return &ControlList[Index]; } } return NULL; } /** Will copy LineWidth amount of a string in the OutputString buffer and return the number of CHAR16 characters that were copied into the OutputString buffer. The output string format is: Glyph Info + String info + '\0'. In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g. @param InputString String description for this option. @param LineWidth Width of the desired string to extract in CHAR16 characters @param GlyphWidth The glyph width of the begin of the char in the string. @param Index Where in InputString to start the copy process @param OutputString Buffer to copy the string into @return Returns the number of CHAR16 characters that were copied into the OutputString buffer, include extra glyph info and '\0' info. **/ STATIC UINT32 GetLineByWidth ( IN CHAR16 *InputString, IN UINT32 LineWidth, IN OUT UINT32 *GlyphWidth, IN OUT UINTN *Index, OUT CHAR16 **OutputString OPTIONAL ) { UINT32 StrOffset; UINT32 GlyphOffset; UINT32 OriginalGlyphWidth; BOOLEAN ReturnFlag; UINT32 LastSpaceOffset; UINT32 LastGlyphWidth; if (InputString == NULL || Index == NULL) { return 0; } if (LineWidth == 0 || *GlyphWidth == 0) { return 0; } // // Save original glyph width. // OriginalGlyphWidth = *GlyphWidth; LastGlyphWidth = OriginalGlyphWidth; ReturnFlag = FALSE; LastSpaceOffset = 0; // // NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen. // To avoid displaying this empty line in screen, just skip the two CHARs here. // if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) { *Index = *Index + 2; } // // Fast-forward the string and see if there is a carriage-return in the string // for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) { switch (InputString[*Index + StrOffset]) { case NARROW_CHAR: *GlyphWidth = 1; break; case WIDE_CHAR: *GlyphWidth = 2; break; case CHAR_CARRIAGE_RETURN: case CHAR_LINEFEED: case CHAR_NULL: ReturnFlag = TRUE; break; default: GlyphOffset = GlyphOffset + *GlyphWidth; // // Record the last space info in this line. Will be used in rewind. // if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) { LastSpaceOffset = StrOffset; LastGlyphWidth = *GlyphWidth; } break; } if (ReturnFlag) { break; } } // // Rewind the string from the maximum size until we see a space to break the line // if (GlyphOffset > LineWidth) { // // Rewind the string to last space char in this line. // if (LastSpaceOffset != 0) { StrOffset = LastSpaceOffset; *GlyphWidth = LastGlyphWidth; } else { // // Roll back to last char in the line width. // StrOffset--; } } // // The CHAR_NULL has process last time, this time just return 0 to stand for the end. // if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) { return 0; } if (OutputString != NULL) { // // Need extra glyph info and '\0' info, so +2. // *OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16))); if (*OutputString == NULL) { return 0; } // // Save the glyph info at the begin of the string, will used by Print function. // if (OriginalGlyphWidth == 1) { *(*OutputString) = NARROW_CHAR; } else { *(*OutputString) = WIDE_CHAR; } CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16)); } if (InputString[*Index + StrOffset] == CHAR_SPACE) { // // Skip the space info at the begin of next line. // *Index = (UINT16) (*Index + StrOffset + 1); } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) { // // Skip the /n or /n/r info. // if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) { *Index = (UINT16) (*Index + StrOffset + 2); } else { *Index = (UINT16) (*Index + StrOffset + 1); } } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) { // // Skip the /r or /r/n info. // if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) { *Index = (UINT16) (*Index + StrOffset + 2); } else { *Index = (UINT16) (*Index + StrOffset + 1); } } else { *Index = (UINT16) (*Index + StrOffset); } // // Include extra glyph info and '\0' info, so +2. // return StrOffset + 2; } UINT32 GetStringHeight ( IN CHAR16 *String, IN UINT32 LineWidth ) { UINT32 LineCount; UINT32 GlyphWidth; UINTN StringIndex; if (String == NULL || LineWidth == 0) { return 0; } GlyphWidth = 1; StringIndex = 0; LineCount = 0; while (GetLineByWidth (String, LineWidth, &GlyphWidth, &StringIndex, NULL) != 0) { LineCount++; } return LineCount; } EFI_STATUS GetStringArrayByWidth ( IN CHAR16 *String, IN UINT32 LineWidth, OUT UINT32 *StringArrayNum, OUT CHAR16 ***StringArray ) { UINTN StrPtrIndex; UINTN StrPtrCount; CHAR16 **StrPtrArray; UINT32 GlyphWidth; UINTN LineIndex; CHAR16 *LineString; if (String == NULL || *String == CHAR_NULL || LineWidth == 0 || StringArrayNum == NULL || StringArray == NULL) { return EFI_INVALID_PARAMETER; } StrPtrIndex = 0; StrPtrCount = 10; StrPtrArray = AllocateZeroPool (StrPtrCount * sizeof (CHAR16 *)); if (StrPtrArray == NULL) { return EFI_OUT_OF_RESOURCES; } GlyphWidth = 1; LineIndex = 0; LineString = NULL; while (GetLineByWidth (String, LineWidth, &GlyphWidth, &LineIndex, &LineString) != 0) { if (LineString == NULL) { continue; } if (StrPtrIndex >= StrPtrCount) { StrPtrArray = ReallocatePool ( StrPtrCount * sizeof (CHAR16 *), (StrPtrCount + 10) * sizeof (CHAR16 *), StrPtrArray ); if (StrPtrArray == NULL) { return EFI_OUT_OF_RESOURCES; } StrPtrCount += 10; } StrPtrArray[StrPtrIndex] = LineString; StrPtrIndex++; } *StringArrayNum = (UINT32) StrPtrIndex; *StringArray = StrPtrArray; return EFI_SUCCESS; } EFI_STATUS CalculateRequireSize ( IN CHAR16 *DisplayString, IN UINT32 LimitLineWidth, OUT UINT32 *RequireWidth, OUT UINT32 *RequireHeight ) { EFI_STATUS Status; UINTN MaxStringWidth; UINT32 Index; UINT32 SeparateStringNum; CHAR16 **SeparateString; if (DisplayString == NULL || RequireWidth == NULL || RequireHeight == NULL) { return EFI_INVALID_PARAMETER; } if (*DisplayString == CHAR_NULL) { *RequireWidth = 0; *RequireHeight = 0; return EFI_SUCCESS; } Status = GetStringArrayByWidth (DisplayString, LimitLineWidth, &SeparateStringNum, &SeparateString); if (EFI_ERROR (Status)) { return Status; } MaxStringWidth = 0; for (Index = 0; Index < SeparateStringNum; Index++) { MaxStringWidth = MAX (MaxStringWidth, GetStringDisplayWidth (SeparateString[Index])); SafeFreePool ((VOID **) &SeparateString[Index]); } SafeFreePool ((VOID **) &SeparateString); *RequireWidth = (UINT32) MaxStringWidth; *RequireHeight = SeparateStringNum; return EFI_SUCCESS; } CHAR16 * GetAlignmentString ( IN CHAR16 *String, IN UINT32 LineWidth, IN UINT32 AlignmentAction ) { UINT32 DisplayWidth; UINT32 StringLen; UINT32 Difference; UINT32 PrefixCount; UINT32 SuffixCount; CHAR16 *ResultString; CHAR16 *ResultStringPtr; UINT32 Index; if (String == NULL || LineWidth == 0 || AlignmentAction >= LTDE_STRING_ALIGNMENT_ACTION_MAX) { return NULL; } DisplayWidth = (UINT32) GetStringDisplayWidth (String); StringLen = (UINT32) StrLen (String); if (DisplayWidth >= LineWidth) { ResultString = AllocateCopyPool ((StringLen + 1) * sizeof (CHAR16), String); if (ResultString == NULL) { return NULL; } while (DisplayWidth > LineWidth) { StringLen--; DisplayWidth -= ConsoleLibGetGlyphWidth (ResultString[StringLen]); ResultString[StringLen] = CHAR_NULL; } return ResultString; } Difference = LineWidth - DisplayWidth; ResultString = AllocateZeroPool ((StringLen + 1 + Difference + 2) * sizeof (CHAR16)); // 2 for extra glyph info if (ResultString == NULL) { return NULL; } ResultStringPtr = ResultString; switch (AlignmentAction) { case LTDE_STRING_ALIGNMENT_ACTION_FLUSH_LEFT: CopyMem (ResultStringPtr, String, (StringLen + 1) * sizeof (CHAR16)); ResultStringPtr += StringLen; *ResultStringPtr = NARROW_CHAR; ResultStringPtr++; for (Index = 0; Index < Difference; Index++, ResultStringPtr++) { *ResultStringPtr = ' '; } break; case LTDE_STRING_ALIGNMENT_ACTION_FLUSH_RIGHT: *ResultStringPtr = NARROW_CHAR; ResultStringPtr++; for (Index = 0; Index < Difference; Index++, ResultStringPtr++) { *ResultStringPtr = ' '; } CopyMem (ResultStringPtr, String, (StringLen + 1) * sizeof (CHAR16)); break; case LTDE_STRING_ALIGNMENT_ACTION_CENTERED: PrefixCount = Difference / 2; SuffixCount = Difference - PrefixCount; *ResultStringPtr = NARROW_CHAR; ResultStringPtr++; for (Index = 0; Index < PrefixCount; Index++, ResultStringPtr++) { *ResultStringPtr = ' '; } CopyMem (ResultStringPtr, String, (StringLen + 1) * sizeof (CHAR16)); ResultStringPtr += StringLen; for (Index = 0; Index < SuffixCount; Index++, ResultStringPtr++) { *ResultStringPtr = ' '; } break; default: FreePool (ResultString); return NULL; } return ResultString; } /** Clear field with specific color attribute @param[in] Attribute Color attribute for clear field @param[in] Field Pointer to clear field @retval EFI_SUCCESS Clear field successfully @retval EFI_OUT_OF_RESOURCES Fail to allocate pool @retval Other Fail to get screen resolution **/ EFI_STATUS ClearField ( IN CONST UINT32 Attribute, IN RECT *Field ) { EFI_STATUS Status; INT32 Index; INT32 ClearWidth; CHAR16 *ClearString; INT32 ScreenRow; INT32 ScreenColumn; BOOLEAN SkipLastChar; Status = DEConOutQueryModeWithoutModeNumer ((UINT32 *) &ScreenColumn, (UINT32 *) &ScreenRow); if (EFI_ERROR (Status)) { return Status; } ScreenColumn--; ScreenRow--; ClearWidth = H2O_LTDE_FIELD_WIDTH (Field); ClearString = CreateString (ClearWidth, ' '); if (ClearString == NULL) { return EFI_OUT_OF_RESOURCES; } if (Field->bottom == ScreenRow && Field->left + ClearWidth - 1 == ScreenColumn) { SkipLastChar = TRUE; } else { SkipLastChar = FALSE; } DEConOutSetAttribute ((UINTN) Attribute); for (Index = Field->top; Index <= Field->bottom; Index++) { if (SkipLastChar && (Index == ScreenRow)) { ClearString[ClearWidth - 1] = CHAR_NULL; } DisplayString (Field->left, Index, ClearString); } SafeFreePool ((VOID **) &ClearString); return EFI_SUCCESS; } VOID FreeControlInfo ( IN H2O_LTDE_CONTROL *Control ) { if (Control == NULL) { return; } SafeFreePool ((VOID **) &Control->Text.String); SafeFreePool ((VOID **) &Control->ValueStrInfo.String); SafeFreePool ((VOID **) &Control->HiiValue.Buffer); } VOID FreePanelItemList ( IN H2O_LTDE_PANEL_ITEM *ItemList, IN UINT32 ItemCount ) { UINT32 ItemIndex; UINT32 ControlIndex; if (ItemList == NULL || ItemCount == 0) { return; } for (ItemIndex = 0; ItemIndex < ItemCount; ItemIndex++) { if (ItemList[ItemIndex].ControlList == NULL) { continue; } for (ControlIndex = 0; ControlIndex < ItemList[ItemIndex].ControlCount; ControlIndex++) { FreeControlInfo (&ItemList[ItemIndex].ControlList[ControlIndex]); } FreePool (ItemList[ItemIndex].ControlList); } FreePool (ItemList); } H2O_LTDE_PANEL * CreatePanel ( VOID ) { H2O_LTDE_PANEL *Panel; Panel = AllocateZeroPool (sizeof (H2O_LTDE_PANEL)); if (Panel == NULL) { return NULL; } Panel->Signature = H2O_LTDE_PANEL_SIGNATURE; InitializeListHead (&Panel->Link); InsertTailList (&mDEPrivate->PanelListHead, &Panel->Link); return Panel; } VOID FreePanel ( IN H2O_LTDE_PANEL *Panel ) { if (Panel == NULL) { return; } FreePanelItemList (Panel->ItemList, Panel->ItemCount); SafeFreePool ((VOID **) &Panel->VfcfPanelInfo->ContentsImage.CurrentBlt); } H2O_LTDE_PANEL * GetPanel ( IN UINT32 PanelId ) { LIST_ENTRY *Link; H2O_LTDE_PANEL *Panel; if (IsListEmpty (&mDEPrivate->PanelListHead)) { return NULL; } Link = GetFirstNode (&mDEPrivate->PanelListHead); while (!IsNull (&mDEPrivate->PanelListHead, Link)) { Panel = H2O_LTDE_PANEL_FROM_LINK (Link); if (Panel->VfcfPanelInfo != NULL && Panel->VfcfPanelInfo->PanelId == PanelId) { return Panel; } Link = GetNextNode (&mDEPrivate->PanelListHead, Link); } return NULL; } H2O_LTDE_PANEL_ITEM * GetPanelItem ( IN H2O_LTDE_PANEL *Panel, IN UINT32 ItemId ) { UINT32 Index; if (Panel == NULL || Panel->ItemList == NULL) { return NULL; } for (Index = 0; Index < Panel->ItemCount; Index++) { if (Panel->ItemList[Index].ItemId == ItemId) { return &Panel->ItemList[Index]; } } return NULL; } H2O_LTDE_PANEL_ITEM * GetPanelItemByControl ( IN H2O_LTDE_PANEL *Panel, IN H2O_LTDE_CONTROL *Control ) { UINT32 Index; UINT32 ControlIndex; H2O_LTDE_PANEL_ITEM *PanelItem; if (Panel == NULL || Control == NULL || Panel->ItemList == NULL) { return NULL; } for (Index = 0; Index < Panel->ItemCount; Index++) { PanelItem = &Panel->ItemList[Index]; for (ControlIndex = 0; ControlIndex < PanelItem->ControlCount; ControlIndex++) { if (&PanelItem->ControlList[ControlIndex] == Control) { return PanelItem; } } } return NULL; } STATIC H2O_LTDE_PANEL_ITEM * GetPanelItemByMouse ( IN H2O_LTDE_PANEL *Panel, IN INT32 MouseX, IN INT32 MouseY ) { UINT32 Index; H2O_LTDE_PANEL_ITEM *PanelItem; if (Panel == NULL) { return NULL; } for (Index = 0; Index < Panel->ItemCount; Index++) { PanelItem = &Panel->ItemList[Index]; if (!PanelItem->Hidden && IsPointOnField (&PanelItem->ItemField, MouseX, MouseY)) { return PanelItem; } } return NULL; } EFI_STATUS GetControlByMouse ( IN H2O_LTDE_PANEL *Panel, IN INT32 MouseX, IN INT32 MouseY, OUT H2O_LTDE_PANEL_ITEM **SelectedPanelItem, OUT H2O_LTDE_CONTROL **SelectedControl ) { H2O_LTDE_PANEL_ITEM *PanelItem; INT32 MouseClientX; INT32 MouseClientY; UINT32 Index; H2O_LTDE_CONTROL *Control; if (Panel == NULL || SelectedPanelItem == NULL || SelectedControl == NULL) { return EFI_INVALID_PARAMETER; } PanelItem = GetPanelItemByMouse (Panel, MouseX, MouseY); if (PanelItem == NULL) { return EFI_NOT_FOUND; } if (PanelItem->Vertical) { MouseClientX = MouseX - PanelItem->ItemField.left; MouseClientY = MouseY - PanelItem->ItemField.top + PanelItem->CurrentPos; } else { MouseClientX = MouseX - PanelItem->ItemField.left + PanelItem->CurrentPos; MouseClientY = MouseY - PanelItem->ItemField.top; } for (Index = 0; Index < PanelItem->ControlCount; Index++) { Control = &PanelItem->ControlList[Index]; if (IsPointOnField (&Control->ControlField, MouseClientX, MouseClientY)) { *SelectedPanelItem = PanelItem; *SelectedControl = Control; return EFI_SUCCESS; } } return EFI_NOT_FOUND; } H2O_LTDE_PANEL_ITEM * GetNextSelectablePanelItem ( IN H2O_LTDE_PANEL *Panel, IN H2O_LTDE_PANEL_ITEM *CurrentPanelItem, IN BOOLEAN IsLoop ) { UINT32 Index; H2O_LTDE_PANEL_ITEM *PanelItem; H2O_LTDE_PANEL_ITEM *NextControl; H2O_LTDE_PANEL_ITEM *FirstControl; if (Panel == NULL) { return NULL; } NextControl = NULL; FirstControl = NULL; for (Index = 0; Index < Panel->ItemCount; Index++) { PanelItem = &Panel->ItemList[Index]; if (PanelItem->Hidden || !PanelItem->Selectable || PanelItem == CurrentPanelItem) { continue; } if (CurrentPanelItem == NULL || IsLoop) { if (FirstControl == NULL) { FirstControl = PanelItem; } else if ((PanelItem->ItemField.top < FirstControl->ItemField.top) || (PanelItem->ItemField.top == FirstControl->ItemField.top && PanelItem->ItemField.left < FirstControl->ItemField.left)) { FirstControl = PanelItem; } if (CurrentPanelItem == NULL) { continue; } } if ((PanelItem->ItemField.top < CurrentPanelItem->ItemField.top) || (PanelItem->ItemField.top == CurrentPanelItem->ItemField.top && PanelItem->ItemField.left < CurrentPanelItem->ItemField.left)) { continue; } if (NextControl == NULL) { NextControl = PanelItem; continue; } if ((PanelItem->ItemField.top < NextControl->ItemField.top) || (PanelItem->ItemField.top == NextControl->ItemField.top && PanelItem->ItemField.left < NextControl->ItemField.left)) { NextControl = PanelItem; } } if (CurrentPanelItem == NULL || (NextControl == NULL && IsLoop)) { NextControl = FirstControl; } return NextControl; } H2O_LTDE_CONTROL * GetNextSelectableControl ( IN H2O_LTDE_PANEL_ITEM *PanelItem, IN H2O_LTDE_CONTROL *CurrentControl, IN BOOLEAN IsLoop ) { UINT32 Index; H2O_LTDE_CONTROL *FirstControl; H2O_LTDE_CONTROL *Control; BOOLEAN IsCurrentControlFound; if (PanelItem == NULL || PanelItem->ControlList == NULL) { return NULL; } FirstControl = NULL; IsCurrentControlFound = (CurrentControl == NULL) ? TRUE : FALSE; for (Index = 0; Index < PanelItem->ControlCount; Index++) { Control = &PanelItem->ControlList[Index]; if (Control->Selectable) { if (IsLoop && FirstControl == NULL) { FirstControl = Control; } } if (!IsCurrentControlFound) { if (Control == CurrentControl) { IsCurrentControlFound = TRUE; } continue; } if (Control->Selectable) { return Control; } } if (IsLoop && FirstControl != NULL && FirstControl != CurrentControl) { return FirstControl; } return NULL; } H2O_LTDE_CONTROL * GetPreviousSelectableControl ( IN H2O_LTDE_PANEL_ITEM *PanelItem, IN H2O_LTDE_CONTROL *CurrentControl, IN BOOLEAN IsLoop ) { UINT32 Index; H2O_LTDE_CONTROL *PreviousControl; H2O_LTDE_CONTROL *Control; if (PanelItem == NULL || PanelItem->ControlList == NULL) { return NULL; } PreviousControl = NULL; for (Index = 0; Index < PanelItem->ControlCount; Index++) { Control = &PanelItem->ControlList[Index]; if (Control == CurrentControl) { if (PreviousControl != NULL || !IsLoop) { break; } continue; } if (Control->Selectable) { PreviousControl = Control; } } return PreviousControl; } BOOLEAN UpdatePanelContentItemPos ( IN H2O_LTDE_PANEL *Panel ) { BOOLEAN Updated; INT32 EndPos; INT32 ContentItemHeight; H2O_LTDE_PANEL_ITEM *ContentItem; H2O_LTDE_PANEL_ITEM *PanelItem; H2O_LTDE_CONTROL *SelectedControl; Updated = FALSE; if (Panel == NULL || Panel->SelectedControl == NULL) { return Updated; } ContentItem = GetPanelItem (Panel, LTDE_PANEL_ITEM_ID_CONTENT); if (ContentItem == NULL) { return Updated; } if (ContentItem != GetPanelItemByControl (Panel, Panel->SelectedControl)) { return Updated; } ContentItemHeight = H2O_LTDE_FIELD_HEIGHT (&ContentItem->ItemField); EndPos = ContentItem->CurrentPos + ContentItemHeight - 1; SelectedControl = Panel->SelectedControl; if ((!IN_RANGE (SelectedControl->ControlField.top , ContentItem->CurrentPos, EndPos) || !IN_RANGE (SelectedControl->ControlField.bottom, ContentItem->CurrentPos, EndPos))) { if (SelectedControl->ControlField.top < ContentItem->CurrentPos) { ContentItem->CurrentPos = SelectedControl->ControlField.top; } else { ContentItem->CurrentPos = SelectedControl->ControlField.bottom - ContentItemHeight + 1; } PanelItem = GetPanelItem (Panel, LTDE_PANEL_ITEM_ID_CONTENT_SCROLL_UP); if (PanelItem != NULL) { PanelItem->Hidden = (ContentItem->CurrentPos == 0) ? TRUE : FALSE; } PanelItem = GetPanelItem (Panel, LTDE_PANEL_ITEM_ID_CONTENT_SCROLL_DOWN); if (PanelItem != NULL) { PanelItem->Hidden = (ContentItem->CurrentPos + ContentItemHeight - 1 >= ContentItem->MaxPos) ? TRUE : FALSE; } PanelItem = GetPanelItem (Panel, LTDE_PANEL_ITEM_ID_CONTENT_PAGE_UP); if (PanelItem != NULL) { PanelItem->Hidden = (ContentItem->CurrentPos == 0) ? TRUE : FALSE; } PanelItem = GetPanelItem (Panel, LTDE_PANEL_ITEM_ID_CONTENT_PAGE_DOWN); if (PanelItem != NULL) { PanelItem->Hidden = (ContentItem->CurrentPos + ContentItemHeight - 1 >= ContentItem->MaxPos) ? TRUE : FALSE; } Updated = TRUE; } return Updated; }