/* * ReactOS kernel * Copyright (C) 2004 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is 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 this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: base/setup/usetup/genlist.c * PURPOSE: Generic list functions * PROGRAMMER: Eric Kohl * Christoph von Wittich */ /* INCLUDES *****************************************************************/ #include "usetup.h" #define NDEBUG #include /* FUNCTIONS ****************************************************************/ typedef struct _GENERIC_LIST_ENTRY { LIST_ENTRY Entry; PGENERIC_LIST List; PVOID UserData; CHAR Text[1]; } GENERIC_LIST_ENTRY; typedef struct _GENERIC_LIST { LIST_ENTRY ListHead; PLIST_ENTRY FirstShown; PLIST_ENTRY LastShown; SHORT Left; SHORT Top; SHORT Right; SHORT Bottom; BOOL Redraw; PGENERIC_LIST_ENTRY CurrentEntry; PGENERIC_LIST_ENTRY BackupEntry; } GENERIC_LIST; PGENERIC_LIST CreateGenericList(VOID) { PGENERIC_LIST List; List = (PGENERIC_LIST)RtlAllocateHeap(ProcessHeap, 0, sizeof(GENERIC_LIST)); if (List == NULL) return NULL; InitializeListHead(&List->ListHead); List->Left = 0; List->Top = 0; List->Right = 0; List->Bottom = 0; List->Redraw = TRUE; List->CurrentEntry = NULL; return List; } VOID DestroyGenericList( PGENERIC_LIST List, BOOLEAN FreeUserData) { PGENERIC_LIST_ENTRY ListEntry; PLIST_ENTRY Entry; /* Release list entries */ while (!IsListEmpty (&List->ListHead)) { Entry = RemoveHeadList (&List->ListHead); ListEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry); /* Release user data */ if (FreeUserData && ListEntry->UserData != NULL) RtlFreeHeap (ProcessHeap, 0, ListEntry->UserData); /* Release list entry */ RtlFreeHeap (ProcessHeap, 0, ListEntry); } /* Release list head */ RtlFreeHeap (ProcessHeap, 0, List); } BOOLEAN AppendGenericListEntry( PGENERIC_LIST List, PCHAR Text, PVOID UserData, BOOLEAN Current) { PGENERIC_LIST_ENTRY Entry; Entry = (PGENERIC_LIST_ENTRY)RtlAllocateHeap(ProcessHeap, 0, sizeof(GENERIC_LIST_ENTRY) + strlen(Text)); if (Entry == NULL) return FALSE; strcpy (Entry->Text, Text); Entry->List = List; Entry->UserData = UserData; InsertTailList(&List->ListHead, &Entry->Entry); if (Current || List->CurrentEntry == NULL) { List->CurrentEntry = Entry; } return TRUE; } static VOID DrawListFrame( PGENERIC_LIST GenericList) { COORD coPos; DWORD Written; SHORT i; /* Draw upper left corner */ coPos.X = GenericList->Left; coPos.Y = GenericList->Top; FillConsoleOutputCharacterA (StdOutput, 0xDA, // '+', 1, coPos, &Written); /* Draw upper edge */ coPos.X = GenericList->Left + 1; coPos.Y = GenericList->Top; FillConsoleOutputCharacterA (StdOutput, 0xC4, // '-', GenericList->Right - GenericList->Left - 1, coPos, &Written); /* Draw upper right corner */ coPos.X = GenericList->Right; coPos.Y = GenericList->Top; FillConsoleOutputCharacterA (StdOutput, 0xBF, // '+', 1, coPos, &Written); /* Draw left and right edge */ for (i = GenericList->Top + 1; i < GenericList->Bottom; i++) { coPos.X = GenericList->Left; coPos.Y = i; FillConsoleOutputCharacterA (StdOutput, 0xB3, // '|', 1, coPos, &Written); coPos.X = GenericList->Right; FillConsoleOutputCharacterA (StdOutput, 0xB3, //'|', 1, coPos, &Written); } /* Draw lower left corner */ coPos.X = GenericList->Left; coPos.Y = GenericList->Bottom; FillConsoleOutputCharacterA (StdOutput, 0xC0, // '+', 1, coPos, &Written); /* Draw lower edge */ coPos.X = GenericList->Left + 1; coPos.Y = GenericList->Bottom; FillConsoleOutputCharacterA (StdOutput, 0xC4, // '-', GenericList->Right - GenericList->Left - 1, coPos, &Written); /* Draw lower right corner */ coPos.X = GenericList->Right; coPos.Y = GenericList->Bottom; FillConsoleOutputCharacterA (StdOutput, 0xD9, // '+', 1, coPos, &Written); } static VOID DrawListEntries( PGENERIC_LIST GenericList) { PGENERIC_LIST_ENTRY ListEntry; PLIST_ENTRY Entry; COORD coPos; DWORD Written; USHORT Width; coPos.X = GenericList->Left + 1; coPos.Y = GenericList->Top + 1; Width = GenericList->Right - GenericList->Left - 1; Entry = GenericList->FirstShown; while (Entry != &GenericList->ListHead) { ListEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry); if (coPos.Y == GenericList->Bottom) break; GenericList->LastShown = Entry; FillConsoleOutputAttribute (StdOutput, (GenericList->CurrentEntry == ListEntry) ? FOREGROUND_BLUE | BACKGROUND_WHITE : FOREGROUND_WHITE | BACKGROUND_BLUE, Width, coPos, &Written); FillConsoleOutputCharacterA (StdOutput, ' ', Width, coPos, &Written); coPos.X++; WriteConsoleOutputCharacterA (StdOutput, ListEntry->Text, min (strlen(ListEntry->Text), (SIZE_T)Width - 2), coPos, &Written); coPos.X--; coPos.Y++; Entry = Entry->Flink; } while (coPos.Y < GenericList->Bottom) { FillConsoleOutputAttribute (StdOutput, FOREGROUND_WHITE | BACKGROUND_BLUE, Width, coPos, &Written); FillConsoleOutputCharacterA (StdOutput, ' ', Width, coPos, &Written); coPos.Y++; } } static VOID DrawScrollBarGenericList( PGENERIC_LIST GenericList) { COORD coPos; DWORD Written; coPos.X = GenericList->Right + 1; coPos.Y = GenericList->Top; if (GenericList->FirstShown != GenericList->ListHead.Flink) { FillConsoleOutputCharacterA (StdOutput, '\x18', 1, coPos, &Written); } else { FillConsoleOutputCharacterA (StdOutput, ' ', 1, coPos, &Written); } coPos.Y = GenericList->Bottom; if (GenericList->LastShown != GenericList->ListHead.Blink) { FillConsoleOutputCharacterA (StdOutput, '\x19', 1, coPos, &Written); } else { FillConsoleOutputCharacterA (StdOutput, ' ', 1, coPos, &Written); } } static VOID CenterCurrentListItem( PGENERIC_LIST List) { PLIST_ENTRY Entry; ULONG MaxVisibleItems, ItemCount, i; if ((List->Top == 0 && List->Bottom == 0) || IsListEmpty(&List->ListHead) || List->CurrentEntry == NULL) return; MaxVisibleItems = (ULONG)(List->Bottom - List->Top - 1); ItemCount = 0; Entry = List->ListHead.Flink; while (Entry != &List->ListHead) { ItemCount++; Entry = Entry->Flink; } if (ItemCount > MaxVisibleItems) { Entry = &List->CurrentEntry->Entry; for (i = 0; i < MaxVisibleItems / 2; i++) { if (Entry->Blink != &List->ListHead) Entry = Entry->Blink; } List->FirstShown = Entry; for (i = 0; i < MaxVisibleItems; i++) { if (Entry->Flink != &List->ListHead) Entry = Entry->Flink; } List->LastShown = Entry; } } VOID DrawGenericList( PGENERIC_LIST List, SHORT Left, SHORT Top, SHORT Right, SHORT Bottom) { List->FirstShown = List->ListHead.Flink; List->Left = Left; List->Top = Top; List->Right = Right; List->Bottom = Bottom; DrawListFrame(List); if (IsListEmpty(&List->ListHead)) return; CenterCurrentListItem(List); DrawListEntries(List); DrawScrollBarGenericList(List); } VOID ScrollPageDownGenericList( PGENERIC_LIST List) { SHORT i; /* Suspend auto-redraw */ List->Redraw = FALSE; for (i = List->Top + 1; i < List->Bottom - 1; i++) { ScrollDownGenericList (List); } /* Update user interface */ DrawListEntries(List); DrawScrollBarGenericList(List); /* Re enable auto-redraw */ List->Redraw = TRUE; } VOID ScrollPageUpGenericList( PGENERIC_LIST List) { SHORT i; /* Suspend auto-redraw */ List->Redraw = FALSE; for (i = List->Bottom - 1; i > List->Top + 1; i--) { ScrollUpGenericList (List); } /* Update user interface */ DrawListEntries(List); DrawScrollBarGenericList(List); /* Re enable auto-redraw */ List->Redraw = TRUE; } VOID ScrollDownGenericList( PGENERIC_LIST List) { PLIST_ENTRY Entry; if (List->CurrentEntry == NULL) return; if (List->CurrentEntry->Entry.Flink != &List->ListHead) { Entry = List->CurrentEntry->Entry.Flink; if (List->LastShown == &List->CurrentEntry->Entry) { List->FirstShown = List->FirstShown->Flink; List->LastShown = List->LastShown->Flink; } List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry); if (List->Redraw) { DrawListEntries(List); DrawScrollBarGenericList(List); } } } VOID ScrollToPositionGenericList( PGENERIC_LIST List, ULONG uIndex) { PLIST_ENTRY Entry; ULONG uCount = 0; if (List->CurrentEntry == NULL || uIndex == 0) return; do { if (List->CurrentEntry->Entry.Flink != &List->ListHead) { Entry = List->CurrentEntry->Entry.Flink; if (List->LastShown == &List->CurrentEntry->Entry) { List->FirstShown = List->FirstShown->Flink; List->LastShown = List->LastShown->Flink; } List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry); } uCount++; } while (uIndex != uCount); if (List->Redraw) { DrawListEntries(List); DrawScrollBarGenericList(List); } } VOID ScrollUpGenericList( PGENERIC_LIST List) { PLIST_ENTRY Entry; if (List->CurrentEntry == NULL) return; if (List->CurrentEntry->Entry.Blink != &List->ListHead) { Entry = List->CurrentEntry->Entry.Blink; if (List->FirstShown == &List->CurrentEntry->Entry) { List->FirstShown = List->FirstShown->Blink; List->LastShown = List->LastShown->Blink; } List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry); if (List->Redraw) { DrawListEntries(List); DrawScrollBarGenericList(List); } } } VOID RedrawGenericList( PGENERIC_LIST List) { if (List->CurrentEntry == NULL) return; if (List->Redraw) { DrawListEntries(List); DrawScrollBarGenericList(List); } } VOID SetCurrentListEntry( PGENERIC_LIST List, PGENERIC_LIST_ENTRY Entry) { if (Entry->List != List) return; List->CurrentEntry = Entry; } PGENERIC_LIST_ENTRY GetCurrentListEntry( PGENERIC_LIST List) { return List->CurrentEntry; } PGENERIC_LIST_ENTRY GetFirstListEntry( PGENERIC_LIST List) { PLIST_ENTRY Entry = List->ListHead.Flink; if (Entry == &List->ListHead) return NULL; return CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry); } PGENERIC_LIST_ENTRY GetNextListEntry( PGENERIC_LIST_ENTRY Entry) { PLIST_ENTRY Next = Entry->Entry.Flink; if (Next == &Entry->List->ListHead) return NULL; return CONTAINING_RECORD(Next, GENERIC_LIST_ENTRY, Entry); } PVOID GetListEntryUserData( PGENERIC_LIST_ENTRY List) { return List->UserData; } LPCSTR GetListEntryText( PGENERIC_LIST_ENTRY List) { return List->Text; } VOID GenericListKeyPress( PGENERIC_LIST GenericList, CHAR AsciiChar) { PGENERIC_LIST_ENTRY ListEntry; PGENERIC_LIST_ENTRY OldListEntry; BOOLEAN Flag = FALSE; ListEntry = GenericList->CurrentEntry; OldListEntry = GenericList->CurrentEntry; GenericList->Redraw = FALSE; if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar) && (GenericList->CurrentEntry->Entry.Flink != &GenericList->ListHead)) { ScrollDownGenericList(GenericList); ListEntry = GenericList->CurrentEntry; if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar)) goto End; } while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead) ScrollUpGenericList(GenericList); ListEntry = GenericList->CurrentEntry; for (;;) { if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar)) { Flag = TRUE; break; } if (GenericList->CurrentEntry->Entry.Flink == &GenericList->ListHead) break; ScrollDownGenericList(GenericList); ListEntry = GenericList->CurrentEntry; } if (!Flag) { while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead) { if (GenericList->CurrentEntry != OldListEntry) ScrollUpGenericList(GenericList); else break; } } End: DrawListEntries(GenericList); DrawScrollBarGenericList(GenericList); GenericList->Redraw = TRUE; } VOID SaveGenericListState( PGENERIC_LIST List) { List->BackupEntry = List->CurrentEntry; } VOID RestoreGenericListState( PGENERIC_LIST List) { List->CurrentEntry = List->BackupEntry; } BOOL GenericListHasSingleEntry( PGENERIC_LIST List) { if (!IsListEmpty(&List->ListHead) && List->ListHead.Flink == List->ListHead.Blink) return TRUE; /* if both list head pointers (which normally point to the first and last list member, respectively) point to the same entry then it means that there's just a single thing in there, otherwise... false! */ return FALSE; } /* EOF */