xref: /reactos/base/setup/usetup/genlist.c (revision 40462c92)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2004 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            base/setup/usetup/genlist.c
23  * PURPOSE:         Generic list functions
24  * PROGRAMMER:      Christoph von Wittich <christoph at reactos.org>
25  */
26 
27 /* INCLUDES *****************************************************************/
28 
29 #include "usetup.h"
30 
31 #define NDEBUG
32 #include <debug.h>
33 
34 /* FUNCTIONS ****************************************************************/
35 
36 VOID
37 InitGenericListUi(
38     IN OUT PGENERIC_LIST_UI ListUi,
39     IN PGENERIC_LIST List,
40     IN PGET_ENTRY_DESCRIPTION GetEntryDescriptionProc)
41 {
42     ListUi->List = List;
43     ListUi->FirstShown = NULL;
44     ListUi->LastShown = NULL;
45     ListUi->BackupEntry = NULL;
46 
47     ListUi->GetEntryDescriptionProc = GetEntryDescriptionProc;
48 
49     ListUi->Left = 0;
50     ListUi->Top = 0;
51     ListUi->Right = 0;
52     ListUi->Bottom = 0;
53     ListUi->Redraw = TRUE;
54 
55     ListUi->CurrentItemText[0] = ANSI_NULL;
56 
57     /* SaveGenericListUiState(ListUi); */
58     ListUi->BackupEntry = ListUi->List->CurrentEntry;
59 }
60 
61 VOID
62 RestoreGenericListUiState(
63     IN PGENERIC_LIST_UI ListUi)
64 {
65     ListUi->List->CurrentEntry = ListUi->BackupEntry;
66 }
67 
68 static
69 VOID
70 DrawListFrame(
71     IN PGENERIC_LIST_UI ListUi)
72 {
73     COORD coPos;
74     DWORD Written;
75     SHORT i;
76 
77     /* Draw upper left corner */
78     coPos.X = ListUi->Left;
79     coPos.Y = ListUi->Top;
80     FillConsoleOutputCharacterA(StdOutput,
81                                 0xDA, // '+',
82                                 1,
83                                 coPos,
84                                 &Written);
85 
86     /* Draw upper edge */
87     coPos.X = ListUi->Left + 1;
88     coPos.Y = ListUi->Top;
89     FillConsoleOutputCharacterA(StdOutput,
90                                 0xC4, // '-',
91                                 ListUi->Right - ListUi->Left - 1,
92                                 coPos,
93                                 &Written);
94 
95     /* Draw upper right corner */
96     coPos.X = ListUi->Right;
97     coPos.Y = ListUi->Top;
98     FillConsoleOutputCharacterA(StdOutput,
99                                 0xBF, // '+',
100                                 1,
101                                 coPos,
102                                 &Written);
103 
104     /* Draw left and right edge */
105     for (i = ListUi->Top + 1; i < ListUi->Bottom; i++)
106     {
107         coPos.X = ListUi->Left;
108         coPos.Y = i;
109         FillConsoleOutputCharacterA(StdOutput,
110                                     0xB3, // '|',
111                                     1,
112                                     coPos,
113                                     &Written);
114 
115         coPos.X = ListUi->Right;
116         FillConsoleOutputCharacterA(StdOutput,
117                                     0xB3, //'|',
118                                     1,
119                                     coPos,
120                                     &Written);
121     }
122 
123     /* Draw lower left corner */
124     coPos.X = ListUi->Left;
125     coPos.Y = ListUi->Bottom;
126     FillConsoleOutputCharacterA(StdOutput,
127                                 0xC0, // '+',
128                                 1,
129                                 coPos,
130                                 &Written);
131 
132     /* Draw lower edge */
133     coPos.X = ListUi->Left + 1;
134     coPos.Y = ListUi->Bottom;
135     FillConsoleOutputCharacterA(StdOutput,
136                                 0xC4, // '-',
137                                 ListUi->Right - ListUi->Left - 1,
138                                 coPos,
139                                 &Written);
140 
141     /* Draw lower right corner */
142     coPos.X = ListUi->Right;
143     coPos.Y = ListUi->Bottom;
144     FillConsoleOutputCharacterA(StdOutput,
145                                 0xD9, // '+',
146                                 1,
147                                 coPos,
148                                 &Written);
149 }
150 
151 static
152 VOID
153 DrawListEntries(
154     IN PGENERIC_LIST_UI ListUi)
155 {
156     PGENERIC_LIST List = ListUi->List;
157     PGENERIC_LIST_ENTRY ListEntry;
158     PLIST_ENTRY Entry;
159     COORD coPos;
160     DWORD Written;
161     USHORT Width;
162 
163     coPos.X = ListUi->Left + 1;
164     coPos.Y = ListUi->Top + 1;
165     Width = ListUi->Right - ListUi->Left - 1;
166 
167     Entry = ListUi->FirstShown;
168     while (Entry != &List->ListHead)
169     {
170         ListEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
171 
172         if (coPos.Y == ListUi->Bottom)
173             break;
174         ListUi->LastShown = Entry;
175 
176         ListUi->CurrentItemText[0] = ANSI_NULL;
177         if (ListUi->GetEntryDescriptionProc)
178         {
179             ListUi->GetEntryDescriptionProc(ListEntry,
180                                             ListUi->CurrentItemText,
181                                             ARRAYSIZE(ListUi->CurrentItemText));
182         }
183 
184         FillConsoleOutputAttribute(StdOutput,
185                                    (List->CurrentEntry == ListEntry) ?
186                                    FOREGROUND_BLUE | BACKGROUND_WHITE :
187                                    FOREGROUND_WHITE | BACKGROUND_BLUE,
188                                    Width,
189                                    coPos,
190                                    &Written);
191 
192         FillConsoleOutputCharacterA(StdOutput,
193                                     ' ',
194                                     Width,
195                                     coPos,
196                                     &Written);
197 
198         coPos.X++;
199         WriteConsoleOutputCharacterA(StdOutput,
200                                      ListUi->CurrentItemText,
201                                      min(strlen(ListUi->CurrentItemText), (SIZE_T)Width - 2),
202                                      coPos,
203                                      &Written);
204         coPos.X--;
205 
206         coPos.Y++;
207         Entry = Entry->Flink;
208     }
209 
210     while (coPos.Y < ListUi->Bottom)
211     {
212         FillConsoleOutputAttribute(StdOutput,
213                                    FOREGROUND_WHITE | BACKGROUND_BLUE,
214                                    Width,
215                                    coPos,
216                                    &Written);
217 
218         FillConsoleOutputCharacterA(StdOutput,
219                                     ' ',
220                                     Width,
221                                     coPos,
222                                     &Written);
223         coPos.Y++;
224     }
225 }
226 
227 static
228 VOID
229 DrawScrollBarGenericList(
230     IN PGENERIC_LIST_UI ListUi)
231 {
232     PGENERIC_LIST List = ListUi->List;
233     COORD coPos;
234     DWORD Written;
235 
236     coPos.X = ListUi->Right + 1;
237     coPos.Y = ListUi->Top;
238 
239     if (ListUi->FirstShown != List->ListHead.Flink)
240     {
241         FillConsoleOutputCharacterA(StdOutput,
242                                     '\x18',
243                                     1,
244                                     coPos,
245                                     &Written);
246     }
247     else
248     {
249         FillConsoleOutputCharacterA(StdOutput,
250                                     ' ',
251                                     1,
252                                     coPos,
253                                     &Written);
254     }
255 
256     coPos.Y = ListUi->Bottom;
257     if (ListUi->LastShown != List->ListHead.Blink)
258     {
259         FillConsoleOutputCharacterA(StdOutput,
260                                     '\x19',
261                                     1,
262                                     coPos,
263                                     &Written);
264     }
265     else
266     {
267         FillConsoleOutputCharacterA(StdOutput,
268                                     ' ',
269                                     1,
270                                     coPos,
271                                     &Written);
272     }
273 }
274 
275 static
276 VOID
277 CenterCurrentListItem(
278     IN PGENERIC_LIST_UI ListUi)
279 {
280     PGENERIC_LIST List = ListUi->List;
281     PLIST_ENTRY Entry;
282     ULONG MaxVisibleItems, ItemCount, i;
283 
284     if ((ListUi->Top == 0 && ListUi->Bottom == 0) ||
285         IsListEmpty(&List->ListHead) ||
286         List->CurrentEntry == NULL)
287     {
288         return;
289     }
290 
291     MaxVisibleItems = (ULONG)(ListUi->Bottom - ListUi->Top - 1);
292 
293 /*****************************************
294     ItemCount = 0;
295     Entry = List->ListHead.Flink;
296     while (Entry != &List->ListHead)
297     {
298         ItemCount++;
299         Entry = Entry->Flink;
300     }
301 *****************************************/
302     ItemCount = List->NumOfEntries; // GetNumberOfListEntries(List);
303 
304     if (ItemCount > MaxVisibleItems)
305     {
306         Entry = &List->CurrentEntry->Entry;
307         for (i = 0; i < MaxVisibleItems / 2; i++)
308         {
309             if (Entry->Blink != &List->ListHead)
310                 Entry = Entry->Blink;
311         }
312 
313         ListUi->FirstShown = Entry;
314 
315         for (i = 0; i < MaxVisibleItems; i++)
316         {
317             if (Entry->Flink != &List->ListHead)
318                 Entry = Entry->Flink;
319         }
320 
321         ListUi->LastShown = Entry;
322     }
323 }
324 
325 VOID
326 DrawGenericList(
327     IN PGENERIC_LIST_UI ListUi,
328     IN SHORT Left,
329     IN SHORT Top,
330     IN SHORT Right,
331     IN SHORT Bottom)
332 {
333     PGENERIC_LIST List = ListUi->List;
334 
335     ListUi->FirstShown = List->ListHead.Flink;
336     ListUi->Left = Left;
337     ListUi->Top = Top;
338     ListUi->Right = Right;
339     ListUi->Bottom = Bottom;
340 
341     DrawListFrame(ListUi);
342 
343     if (IsListEmpty(&List->ListHead))
344         return;
345 
346     CenterCurrentListItem(ListUi);
347 
348     DrawListEntries(ListUi);
349     DrawScrollBarGenericList(ListUi);
350 }
351 
352 VOID
353 DrawGenericListCurrentItem(
354     IN PGENERIC_LIST List,
355     IN PGET_ENTRY_DESCRIPTION GetEntryDescriptionProc,
356     IN SHORT Left,
357     IN SHORT Top)
358 {
359     CHAR CurrentItemText[256];
360 
361     if (GetEntryDescriptionProc &&
362         GetNumberOfListEntries(List) > 0)
363     {
364         GetEntryDescriptionProc(GetCurrentListEntry(List),
365                                 CurrentItemText,
366                                 ARRAYSIZE(CurrentItemText));
367         CONSOLE_SetTextXY(Left, Top, CurrentItemText);
368     }
369     else
370     {
371         CONSOLE_SetTextXY(Left, Top, "");
372     }
373 }
374 
375 VOID
376 ScrollDownGenericList(
377     IN PGENERIC_LIST_UI ListUi)
378 {
379     PGENERIC_LIST List = ListUi->List;
380     PLIST_ENTRY Entry;
381 
382     if (List->CurrentEntry == NULL)
383         return;
384 
385     if (List->CurrentEntry->Entry.Flink != &List->ListHead)
386     {
387         Entry = List->CurrentEntry->Entry.Flink;
388         if (ListUi->LastShown == &List->CurrentEntry->Entry)
389         {
390             ListUi->FirstShown = ListUi->FirstShown->Flink;
391             ListUi->LastShown = ListUi->LastShown->Flink;
392         }
393         List->CurrentEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
394 
395         if (ListUi->Redraw)
396         {
397             DrawListEntries(ListUi);
398             DrawScrollBarGenericList(ListUi);
399         }
400     }
401 }
402 
403 VOID
404 ScrollUpGenericList(
405     IN PGENERIC_LIST_UI ListUi)
406 {
407     PGENERIC_LIST List = ListUi->List;
408     PLIST_ENTRY Entry;
409 
410     if (List->CurrentEntry == NULL)
411         return;
412 
413     if (List->CurrentEntry->Entry.Blink != &List->ListHead)
414     {
415         Entry = List->CurrentEntry->Entry.Blink;
416         if (ListUi->FirstShown == &List->CurrentEntry->Entry)
417         {
418             ListUi->FirstShown = ListUi->FirstShown->Blink;
419             ListUi->LastShown = ListUi->LastShown->Blink;
420         }
421         List->CurrentEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
422 
423         if (ListUi->Redraw)
424         {
425             DrawListEntries(ListUi);
426             DrawScrollBarGenericList(ListUi);
427         }
428     }
429 }
430 
431 VOID
432 ScrollPageDownGenericList(
433     IN PGENERIC_LIST_UI ListUi)
434 {
435     SHORT i;
436 
437     /* Suspend auto-redraw */
438     ListUi->Redraw = FALSE;
439 
440     for (i = ListUi->Top + 1; i < ListUi->Bottom - 1; i++)
441     {
442         ScrollDownGenericList(ListUi);
443     }
444 
445     /* Update user interface */
446     DrawListEntries(ListUi);
447     DrawScrollBarGenericList(ListUi);
448 
449     /* Re enable auto-redraw */
450     ListUi->Redraw = TRUE;
451 }
452 
453 VOID
454 ScrollPageUpGenericList(
455     IN PGENERIC_LIST_UI ListUi)
456 {
457     SHORT i;
458 
459     /* Suspend auto-redraw */
460     ListUi->Redraw = FALSE;
461 
462     for (i = ListUi->Bottom - 1; i > ListUi->Top + 1; i--)
463     {
464         ScrollUpGenericList(ListUi);
465     }
466 
467     /* Update user interface */
468     DrawListEntries(ListUi);
469     DrawScrollBarGenericList(ListUi);
470 
471     /* Re enable auto-redraw */
472     ListUi->Redraw = TRUE;
473 }
474 
475 VOID
476 ScrollToPositionGenericList(
477     IN PGENERIC_LIST_UI ListUi,
478     IN ULONG uIndex)
479 {
480     PGENERIC_LIST List = ListUi->List;
481     PLIST_ENTRY Entry;
482     ULONG uCount = 0;
483 
484     if (List->CurrentEntry == NULL || uIndex == 0)
485         return;
486 
487     do
488     {
489         if (List->CurrentEntry->Entry.Flink != &List->ListHead)
490         {
491             Entry = List->CurrentEntry->Entry.Flink;
492             if (ListUi->LastShown == &List->CurrentEntry->Entry)
493             {
494                 ListUi->FirstShown = ListUi->FirstShown->Flink;
495                 ListUi->LastShown = ListUi->LastShown->Flink;
496             }
497             List->CurrentEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
498         }
499         uCount++;
500     }
501     while (uIndex != uCount);
502 
503     if (ListUi->Redraw)
504     {
505         DrawListEntries(ListUi);
506         DrawScrollBarGenericList(ListUi);
507     }
508 }
509 
510 VOID
511 RedrawGenericList(
512     IN PGENERIC_LIST_UI ListUi)
513 {
514     if (ListUi->List->CurrentEntry == NULL)
515         return;
516 
517     if (ListUi->Redraw)
518     {
519         DrawListEntries(ListUi);
520         DrawScrollBarGenericList(ListUi);
521     }
522 }
523 
524 VOID
525 GenericListKeyPress(
526     IN PGENERIC_LIST_UI ListUi,
527     IN CHAR AsciiChar)
528 {
529     PGENERIC_LIST List = ListUi->List;
530     PGENERIC_LIST_ENTRY ListEntry;
531     PGENERIC_LIST_ENTRY OldListEntry;
532     BOOLEAN Flag = FALSE;
533 
534     ListEntry = List->CurrentEntry;
535     OldListEntry = List->CurrentEntry;
536 
537     ListUi->Redraw = FALSE;
538 
539     ListUi->CurrentItemText[0] = ANSI_NULL;
540     if (ListUi->GetEntryDescriptionProc)
541     {
542         ListUi->GetEntryDescriptionProc(ListEntry,
543                                         ListUi->CurrentItemText,
544                                         ARRAYSIZE(ListUi->CurrentItemText));
545     }
546 
547     if ((strlen(ListUi->CurrentItemText) > 0) && (tolower(ListUi->CurrentItemText[0]) == AsciiChar) &&
548          (List->CurrentEntry->Entry.Flink != &List->ListHead))
549     {
550         ScrollDownGenericList(ListUi);
551         ListEntry = List->CurrentEntry;
552 
553         ListUi->CurrentItemText[0] = ANSI_NULL;
554         if (ListUi->GetEntryDescriptionProc)
555         {
556             ListUi->GetEntryDescriptionProc(ListEntry,
557                                             ListUi->CurrentItemText,
558                                             ARRAYSIZE(ListUi->CurrentItemText));
559         }
560 
561         if ((strlen(ListUi->CurrentItemText) > 0) && (tolower(ListUi->CurrentItemText[0]) == AsciiChar))
562             goto End;
563     }
564 
565     while (List->CurrentEntry->Entry.Blink != &List->ListHead)
566         ScrollUpGenericList(ListUi);
567 
568     ListEntry = List->CurrentEntry;
569 
570     for (;;)
571     {
572         ListUi->CurrentItemText[0] = ANSI_NULL;
573         if (ListUi->GetEntryDescriptionProc)
574         {
575             ListUi->GetEntryDescriptionProc(ListEntry,
576                                             ListUi->CurrentItemText,
577                                             ARRAYSIZE(ListUi->CurrentItemText));
578         }
579 
580         if ((strlen(ListUi->CurrentItemText) > 0) && (tolower(ListUi->CurrentItemText[0]) == AsciiChar))
581         {
582             Flag = TRUE;
583             break;
584         }
585 
586         if (List->CurrentEntry->Entry.Flink == &List->ListHead)
587             break;
588 
589         ScrollDownGenericList(ListUi);
590         ListEntry = List->CurrentEntry;
591     }
592 
593     if (!Flag)
594     {
595         while (List->CurrentEntry->Entry.Blink != &List->ListHead)
596         {
597             if (List->CurrentEntry != OldListEntry)
598                 ScrollUpGenericList(ListUi);
599             else
600                 break;
601         }
602     }
603 
604 End:
605     DrawListEntries(ListUi);
606     DrawScrollBarGenericList(ListUi);
607 
608     ListUi->Redraw = TRUE;
609 }
610 
611 /* EOF */
612