Merge branch 'ntfs_rebase'
[reactos.git] / base / setup / usetup / genlist.c
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 /* COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS text-mode setup
21 * FILE: base/setup/usetup/genlist.c
22 * PURPOSE: Generic list functions
23 * PROGRAMMER: Eric Kohl
24 * 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 typedef struct _GENERIC_LIST_ENTRY
37 {
38 LIST_ENTRY Entry;
39 PGENERIC_LIST List;
40 PVOID UserData;
41 CHAR Text[1];
42 } GENERIC_LIST_ENTRY;
43
44
45 typedef struct _GENERIC_LIST
46 {
47 LIST_ENTRY ListHead;
48
49 PLIST_ENTRY FirstShown;
50 PLIST_ENTRY LastShown;
51 SHORT Left;
52 SHORT Top;
53 SHORT Right;
54 SHORT Bottom;
55 BOOL Redraw;
56
57 PGENERIC_LIST_ENTRY CurrentEntry;
58 PGENERIC_LIST_ENTRY BackupEntry;
59 } GENERIC_LIST;
60
61 PGENERIC_LIST
62 CreateGenericList(VOID)
63 {
64 PGENERIC_LIST List;
65
66 List = (PGENERIC_LIST)RtlAllocateHeap(ProcessHeap,
67 0,
68 sizeof(GENERIC_LIST));
69 if (List == NULL)
70 return NULL;
71
72 InitializeListHead(&List->ListHead);
73
74 List->Left = 0;
75 List->Top = 0;
76 List->Right = 0;
77 List->Bottom = 0;
78 List->Redraw = TRUE;
79
80 List->CurrentEntry = NULL;
81
82 return List;
83 }
84
85
86 VOID
87 DestroyGenericList(
88 PGENERIC_LIST List,
89 BOOLEAN FreeUserData)
90 {
91 PGENERIC_LIST_ENTRY ListEntry;
92 PLIST_ENTRY Entry;
93
94 /* Release list entries */
95 while (!IsListEmpty (&List->ListHead))
96 {
97 Entry = RemoveHeadList (&List->ListHead);
98 ListEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
99
100 /* Release user data */
101 if (FreeUserData && ListEntry->UserData != NULL)
102 RtlFreeHeap (ProcessHeap, 0, ListEntry->UserData);
103
104 /* Release list entry */
105 RtlFreeHeap (ProcessHeap, 0, ListEntry);
106 }
107
108 /* Release list head */
109 RtlFreeHeap (ProcessHeap, 0, List);
110 }
111
112
113 BOOLEAN
114 AppendGenericListEntry(
115 PGENERIC_LIST List,
116 PCHAR Text,
117 PVOID UserData,
118 BOOLEAN Current)
119 {
120 PGENERIC_LIST_ENTRY Entry;
121
122 Entry = (PGENERIC_LIST_ENTRY)RtlAllocateHeap(ProcessHeap,
123 0,
124 sizeof(GENERIC_LIST_ENTRY) + strlen(Text));
125 if (Entry == NULL)
126 return FALSE;
127
128 strcpy (Entry->Text, Text);
129 Entry->List = List;
130 Entry->UserData = UserData;
131
132 InsertTailList(&List->ListHead,
133 &Entry->Entry);
134
135 if (Current || List->CurrentEntry == NULL)
136 {
137 List->CurrentEntry = Entry;
138 }
139
140 return TRUE;
141 }
142
143
144 static
145 VOID
146 DrawListFrame(
147 PGENERIC_LIST GenericList)
148 {
149 COORD coPos;
150 DWORD Written;
151 SHORT i;
152
153 /* Draw upper left corner */
154 coPos.X = GenericList->Left;
155 coPos.Y = GenericList->Top;
156 FillConsoleOutputCharacterA (StdOutput,
157 0xDA, // '+',
158 1,
159 coPos,
160 &Written);
161
162 /* Draw upper edge */
163 coPos.X = GenericList->Left + 1;
164 coPos.Y = GenericList->Top;
165 FillConsoleOutputCharacterA (StdOutput,
166 0xC4, // '-',
167 GenericList->Right - GenericList->Left - 1,
168 coPos,
169 &Written);
170
171 /* Draw upper right corner */
172 coPos.X = GenericList->Right;
173 coPos.Y = GenericList->Top;
174 FillConsoleOutputCharacterA (StdOutput,
175 0xBF, // '+',
176 1,
177 coPos,
178 &Written);
179
180 /* Draw left and right edge */
181 for (i = GenericList->Top + 1; i < GenericList->Bottom; i++)
182 {
183 coPos.X = GenericList->Left;
184 coPos.Y = i;
185 FillConsoleOutputCharacterA (StdOutput,
186 0xB3, // '|',
187 1,
188 coPos,
189 &Written);
190
191 coPos.X = GenericList->Right;
192 FillConsoleOutputCharacterA (StdOutput,
193 0xB3, //'|',
194 1,
195 coPos,
196 &Written);
197 }
198
199 /* Draw lower left corner */
200 coPos.X = GenericList->Left;
201 coPos.Y = GenericList->Bottom;
202 FillConsoleOutputCharacterA (StdOutput,
203 0xC0, // '+',
204 1,
205 coPos,
206 &Written);
207
208 /* Draw lower edge */
209 coPos.X = GenericList->Left + 1;
210 coPos.Y = GenericList->Bottom;
211 FillConsoleOutputCharacterA (StdOutput,
212 0xC4, // '-',
213 GenericList->Right - GenericList->Left - 1,
214 coPos,
215 &Written);
216
217 /* Draw lower right corner */
218 coPos.X = GenericList->Right;
219 coPos.Y = GenericList->Bottom;
220 FillConsoleOutputCharacterA (StdOutput,
221 0xD9, // '+',
222 1,
223 coPos,
224 &Written);
225 }
226
227
228 static
229 VOID
230 DrawListEntries(
231 PGENERIC_LIST GenericList)
232 {
233 PGENERIC_LIST_ENTRY ListEntry;
234 PLIST_ENTRY Entry;
235 COORD coPos;
236 DWORD Written;
237 USHORT Width;
238
239 coPos.X = GenericList->Left + 1;
240 coPos.Y = GenericList->Top + 1;
241 Width = GenericList->Right - GenericList->Left - 1;
242
243 Entry = GenericList->FirstShown;
244 while (Entry != &GenericList->ListHead)
245 {
246 ListEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
247
248 if (coPos.Y == GenericList->Bottom)
249 break;
250 GenericList->LastShown = Entry;
251
252 FillConsoleOutputAttribute (StdOutput,
253 (GenericList->CurrentEntry == ListEntry) ?
254 FOREGROUND_BLUE | BACKGROUND_WHITE :
255 FOREGROUND_WHITE | BACKGROUND_BLUE,
256 Width,
257 coPos,
258 &Written);
259
260 FillConsoleOutputCharacterA (StdOutput,
261 ' ',
262 Width,
263 coPos,
264 &Written);
265
266 coPos.X++;
267 WriteConsoleOutputCharacterA (StdOutput,
268 ListEntry->Text,
269 min (strlen(ListEntry->Text), (SIZE_T)Width - 2),
270 coPos,
271 &Written);
272 coPos.X--;
273
274 coPos.Y++;
275 Entry = Entry->Flink;
276 }
277
278 while (coPos.Y < GenericList->Bottom)
279 {
280 FillConsoleOutputAttribute (StdOutput,
281 FOREGROUND_WHITE | BACKGROUND_BLUE,
282 Width,
283 coPos,
284 &Written);
285
286 FillConsoleOutputCharacterA (StdOutput,
287 ' ',
288 Width,
289 coPos,
290 &Written);
291 coPos.Y++;
292 }
293 }
294
295
296 static
297 VOID
298 DrawScrollBarGenericList(
299 PGENERIC_LIST GenericList)
300 {
301 COORD coPos;
302 DWORD Written;
303
304 coPos.X = GenericList->Right + 1;
305 coPos.Y = GenericList->Top;
306
307 if (GenericList->FirstShown != GenericList->ListHead.Flink)
308 {
309 FillConsoleOutputCharacterA (StdOutput,
310 '\x18',
311 1,
312 coPos,
313 &Written);
314 }
315 else
316 {
317 FillConsoleOutputCharacterA (StdOutput,
318 ' ',
319 1,
320 coPos,
321 &Written);
322 }
323
324 coPos.Y = GenericList->Bottom;
325 if (GenericList->LastShown != GenericList->ListHead.Blink)
326 {
327 FillConsoleOutputCharacterA (StdOutput,
328 '\x19',
329 1,
330 coPos,
331 &Written);
332 }
333 else
334 {
335 FillConsoleOutputCharacterA (StdOutput,
336 ' ',
337 1,
338 coPos,
339 &Written);
340 }
341 }
342
343
344 static
345 VOID
346 CenterCurrentListItem(
347 PGENERIC_LIST List)
348 {
349 PLIST_ENTRY Entry;
350 ULONG MaxVisibleItems, ItemCount, i;
351
352 if ((List->Top == 0 && List->Bottom == 0) ||
353 IsListEmpty(&List->ListHead) ||
354 List->CurrentEntry == NULL)
355 return;
356
357 MaxVisibleItems = (ULONG)(List->Bottom - List->Top - 1);
358
359 ItemCount = 0;
360 Entry = List->ListHead.Flink;
361 while (Entry != &List->ListHead)
362 {
363 ItemCount++;
364 Entry = Entry->Flink;
365 }
366
367 if (ItemCount > MaxVisibleItems)
368 {
369 Entry = &List->CurrentEntry->Entry;
370 for (i = 0; i < MaxVisibleItems / 2; i++)
371 {
372 if (Entry->Blink != &List->ListHead)
373 Entry = Entry->Blink;
374 }
375
376 List->FirstShown = Entry;
377
378 for (i = 0; i < MaxVisibleItems; i++)
379 {
380 if (Entry->Flink != &List->ListHead)
381 Entry = Entry->Flink;
382 }
383
384 List->LastShown = Entry;
385 }
386 }
387
388
389 VOID
390 DrawGenericList(
391 PGENERIC_LIST List,
392 SHORT Left,
393 SHORT Top,
394 SHORT Right,
395 SHORT Bottom)
396 {
397 List->FirstShown = List->ListHead.Flink;
398 List->Left = Left;
399 List->Top = Top;
400 List->Right = Right;
401 List->Bottom = Bottom;
402
403 DrawListFrame(List);
404
405 if (IsListEmpty(&List->ListHead))
406 return;
407
408 CenterCurrentListItem(List);
409
410 DrawListEntries(List);
411 DrawScrollBarGenericList(List);
412 }
413
414
415 VOID
416 ScrollPageDownGenericList(
417 PGENERIC_LIST List)
418 {
419 SHORT i;
420
421 /* Suspend auto-redraw */
422 List->Redraw = FALSE;
423
424 for (i = List->Top + 1; i < List->Bottom - 1; i++)
425 {
426 ScrollDownGenericList (List);
427 }
428
429 /* Update user interface */
430 DrawListEntries(List);
431 DrawScrollBarGenericList(List);
432
433 /* Re enable auto-redraw */
434 List->Redraw = TRUE;
435 }
436
437
438 VOID
439 ScrollPageUpGenericList(
440 PGENERIC_LIST List)
441 {
442 SHORT i;
443
444 /* Suspend auto-redraw */
445 List->Redraw = FALSE;
446
447 for (i = List->Bottom - 1; i > List->Top + 1; i--)
448 {
449 ScrollUpGenericList (List);
450 }
451
452 /* Update user interface */
453 DrawListEntries(List);
454 DrawScrollBarGenericList(List);
455
456 /* Re enable auto-redraw */
457 List->Redraw = TRUE;
458 }
459
460
461 VOID
462 ScrollDownGenericList(
463 PGENERIC_LIST List)
464 {
465 PLIST_ENTRY Entry;
466
467 if (List->CurrentEntry == NULL)
468 return;
469
470 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
471 {
472 Entry = List->CurrentEntry->Entry.Flink;
473 if (List->LastShown == &List->CurrentEntry->Entry)
474 {
475 List->FirstShown = List->FirstShown->Flink;
476 List->LastShown = List->LastShown->Flink;
477 }
478 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
479
480 if (List->Redraw)
481 {
482 DrawListEntries(List);
483 DrawScrollBarGenericList(List);
484 }
485 }
486 }
487
488
489 VOID
490 ScrollToPositionGenericList(
491 PGENERIC_LIST List,
492 ULONG uIndex)
493 {
494 PLIST_ENTRY Entry;
495 ULONG uCount = 0;
496
497 if (List->CurrentEntry == NULL || uIndex == 0)
498 return;
499
500 do
501 {
502 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
503 {
504 Entry = List->CurrentEntry->Entry.Flink;
505 if (List->LastShown == &List->CurrentEntry->Entry)
506 {
507 List->FirstShown = List->FirstShown->Flink;
508 List->LastShown = List->LastShown->Flink;
509 }
510 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
511 }
512 uCount++;
513 }
514 while (uIndex != uCount);
515
516 if (List->Redraw)
517 {
518 DrawListEntries(List);
519 DrawScrollBarGenericList(List);
520 }
521 }
522
523
524 VOID
525 ScrollUpGenericList(
526 PGENERIC_LIST List)
527 {
528 PLIST_ENTRY Entry;
529
530 if (List->CurrentEntry == NULL)
531 return;
532
533 if (List->CurrentEntry->Entry.Blink != &List->ListHead)
534 {
535 Entry = List->CurrentEntry->Entry.Blink;
536 if (List->FirstShown == &List->CurrentEntry->Entry)
537 {
538 List->FirstShown = List->FirstShown->Blink;
539 List->LastShown = List->LastShown->Blink;
540 }
541 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
542
543 if (List->Redraw)
544 {
545 DrawListEntries(List);
546 DrawScrollBarGenericList(List);
547 }
548 }
549 }
550
551
552 VOID
553 RedrawGenericList(
554 PGENERIC_LIST List)
555 {
556 if (List->CurrentEntry == NULL)
557 return;
558
559 if (List->Redraw)
560 {
561 DrawListEntries(List);
562 DrawScrollBarGenericList(List);
563 }
564 }
565
566
567 VOID
568 SetCurrentListEntry(
569 PGENERIC_LIST List,
570 PGENERIC_LIST_ENTRY Entry)
571 {
572 if (Entry->List != List)
573 return;
574 List->CurrentEntry = Entry;
575 }
576
577
578 PGENERIC_LIST_ENTRY
579 GetCurrentListEntry(
580 PGENERIC_LIST List)
581 {
582 return List->CurrentEntry;
583 }
584
585
586 PGENERIC_LIST_ENTRY
587 GetFirstListEntry(
588 PGENERIC_LIST List)
589 {
590 PLIST_ENTRY Entry = List->ListHead.Flink;
591
592 if (Entry == &List->ListHead)
593 return NULL;
594 return CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
595 }
596
597
598 PGENERIC_LIST_ENTRY
599 GetNextListEntry(
600 PGENERIC_LIST_ENTRY Entry)
601 {
602 PLIST_ENTRY Next = Entry->Entry.Flink;
603
604 if (Next == &Entry->List->ListHead)
605 return NULL;
606 return CONTAINING_RECORD(Next, GENERIC_LIST_ENTRY, Entry);
607 }
608
609
610 PVOID
611 GetListEntryUserData(
612 PGENERIC_LIST_ENTRY List)
613 {
614 return List->UserData;
615 }
616
617
618 LPCSTR
619 GetListEntryText(
620 PGENERIC_LIST_ENTRY List)
621 {
622 return List->Text;
623 }
624
625
626 VOID
627 GenericListKeyPress(
628 PGENERIC_LIST GenericList,
629 CHAR AsciiChar)
630 {
631 PGENERIC_LIST_ENTRY ListEntry;
632 PGENERIC_LIST_ENTRY OldListEntry;
633 BOOLEAN Flag = FALSE;
634
635 ListEntry = GenericList->CurrentEntry;
636 OldListEntry = GenericList->CurrentEntry;
637
638 GenericList->Redraw = FALSE;
639
640 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar) &&
641 (GenericList->CurrentEntry->Entry.Flink != &GenericList->ListHead))
642 {
643 ScrollDownGenericList(GenericList);
644 ListEntry = GenericList->CurrentEntry;
645
646 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar))
647 goto End;
648 }
649
650 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
651 ScrollUpGenericList(GenericList);
652
653 ListEntry = GenericList->CurrentEntry;
654
655 for (;;)
656 {
657 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar))
658 {
659 Flag = TRUE;
660 break;
661 }
662
663 if (GenericList->CurrentEntry->Entry.Flink == &GenericList->ListHead)
664 break;
665
666 ScrollDownGenericList(GenericList);
667 ListEntry = GenericList->CurrentEntry;
668 }
669
670 if (!Flag)
671 {
672 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
673 {
674 if (GenericList->CurrentEntry != OldListEntry)
675 ScrollUpGenericList(GenericList);
676 else
677 break;
678 }
679 }
680 End:
681 DrawListEntries(GenericList);
682 DrawScrollBarGenericList(GenericList);
683
684 GenericList->Redraw = TRUE;
685 }
686
687
688 VOID
689 SaveGenericListState(
690 PGENERIC_LIST List)
691 {
692 List->BackupEntry = List->CurrentEntry;
693 }
694
695
696 VOID
697 RestoreGenericListState(
698 PGENERIC_LIST List)
699 {
700 List->CurrentEntry = List->BackupEntry;
701 }
702
703
704 BOOL
705 GenericListHasSingleEntry(
706 PGENERIC_LIST List)
707 {
708 if (!IsListEmpty(&List->ListHead) && List->ListHead.Flink == List->ListHead.Blink)
709 return TRUE;
710
711 /* if both list head pointers (which normally point to the first and last list member, respectively)
712 point to the same entry then it means that there's just a single thing in there, otherwise... false! */
713 return FALSE;
714 }
715
716 /* EOF */