Sync with trunk r63270.
[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: subsys/system/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 VOID
345 DrawGenericList(
346 PGENERIC_LIST List,
347 SHORT Left,
348 SHORT Top,
349 SHORT Right,
350 SHORT Bottom)
351 {
352 List->FirstShown = List->ListHead.Flink;
353 List->Left = Left;
354 List->Top = Top;
355 List->Right = Right;
356 List->Bottom = Bottom;
357
358 DrawListFrame(List);
359
360 if (IsListEmpty(&List->ListHead))
361 return;
362
363 DrawListEntries(List);
364 DrawScrollBarGenericList(List);
365 }
366
367
368 VOID
369 ScrollPageDownGenericList(
370 PGENERIC_LIST List)
371 {
372 SHORT i;
373
374 /* Suspend auto-redraw */
375 List->Redraw = FALSE;
376
377 for (i = List->Top + 1; i < List->Bottom - 1; i++)
378 {
379 ScrollDownGenericList (List);
380 }
381
382 /* Update user interface */
383 DrawListEntries(List);
384 DrawScrollBarGenericList(List);
385
386 /* Re enable auto-redraw */
387 List->Redraw = TRUE;
388 }
389
390
391 VOID
392 ScrollPageUpGenericList(
393 PGENERIC_LIST List)
394 {
395 SHORT i;
396
397 /* Suspend auto-redraw */
398 List->Redraw = FALSE;
399
400 for (i = List->Bottom - 1; i > List->Top + 1; i--)
401 {
402 ScrollUpGenericList (List);
403 }
404
405 /* Update user interface */
406 DrawListEntries(List);
407 DrawScrollBarGenericList(List);
408
409 /* Re enable auto-redraw */
410 List->Redraw = TRUE;
411 }
412
413
414 VOID
415 ScrollDownGenericList(
416 PGENERIC_LIST List)
417 {
418 PLIST_ENTRY Entry;
419
420 if (List->CurrentEntry == NULL)
421 return;
422
423 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
424 {
425 Entry = List->CurrentEntry->Entry.Flink;
426 if (List->LastShown == &List->CurrentEntry->Entry)
427 {
428 List->FirstShown = List->FirstShown->Flink;
429 List->LastShown = List->LastShown->Flink;
430 }
431 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
432
433 if (List->Redraw)
434 {
435 DrawListEntries(List);
436 DrawScrollBarGenericList(List);
437 }
438 }
439 }
440
441
442 VOID
443 ScrollToPositionGenericList(
444 PGENERIC_LIST List,
445 ULONG uIndex)
446 {
447 PLIST_ENTRY Entry;
448 ULONG uCount = 0;
449
450 if (List->CurrentEntry == NULL || uIndex == 0)
451 return;
452
453 do
454 {
455 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
456 {
457 Entry = List->CurrentEntry->Entry.Flink;
458 if (List->LastShown == &List->CurrentEntry->Entry)
459 {
460 List->FirstShown = List->FirstShown->Flink;
461 List->LastShown = List->LastShown->Flink;
462 }
463 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
464 }
465 uCount++;
466 }
467 while (uIndex != uCount);
468
469 if (List->Redraw)
470 {
471 DrawListEntries(List);
472 DrawScrollBarGenericList(List);
473 }
474 }
475
476
477 VOID
478 ScrollUpGenericList(
479 PGENERIC_LIST List)
480 {
481 PLIST_ENTRY Entry;
482
483 if (List->CurrentEntry == NULL)
484 return;
485
486 if (List->CurrentEntry->Entry.Blink != &List->ListHead)
487 {
488 Entry = List->CurrentEntry->Entry.Blink;
489 if (List->FirstShown == &List->CurrentEntry->Entry)
490 {
491 List->FirstShown = List->FirstShown->Blink;
492 List->LastShown = List->LastShown->Blink;
493 }
494 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
495
496 if (List->Redraw)
497 {
498 DrawListEntries(List);
499 DrawScrollBarGenericList(List);
500 }
501 }
502 }
503
504
505 VOID
506 RedrawGenericList(
507 PGENERIC_LIST List)
508 {
509 if (List->CurrentEntry == NULL)
510 return;
511
512 if (List->Redraw)
513 {
514 DrawListEntries(List);
515 DrawScrollBarGenericList(List);
516 }
517 }
518
519
520 VOID
521 SetCurrentListEntry(
522 PGENERIC_LIST List,
523 PGENERIC_LIST_ENTRY Entry)
524 {
525 if (Entry->List != List)
526 return;
527 List->CurrentEntry = Entry;
528 }
529
530
531 PGENERIC_LIST_ENTRY
532 GetCurrentListEntry(
533 PGENERIC_LIST List)
534 {
535 return List->CurrentEntry;
536 }
537
538
539 PGENERIC_LIST_ENTRY
540 GetFirstListEntry(
541 PGENERIC_LIST List)
542 {
543 PLIST_ENTRY Entry = List->ListHead.Flink;
544
545 if (Entry == &List->ListHead)
546 return NULL;
547 return CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
548 }
549
550
551 PGENERIC_LIST_ENTRY
552 GetNextListEntry(
553 PGENERIC_LIST_ENTRY Entry)
554 {
555 PLIST_ENTRY Next = Entry->Entry.Flink;
556
557 if (Next == &Entry->List->ListHead)
558 return NULL;
559 return CONTAINING_RECORD(Next, GENERIC_LIST_ENTRY, Entry);
560 }
561
562
563 PVOID
564 GetListEntryUserData(
565 PGENERIC_LIST_ENTRY List)
566 {
567 return List->UserData;
568 }
569
570
571 LPCSTR
572 GetListEntryText(
573 PGENERIC_LIST_ENTRY List)
574 {
575 return List->Text;
576 }
577
578
579 VOID
580 GenericListKeyPress(
581 PGENERIC_LIST GenericList,
582 CHAR AsciChar)
583 {
584 PGENERIC_LIST_ENTRY ListEntry;
585 PGENERIC_LIST_ENTRY OldListEntry;
586 BOOLEAN Flag = FALSE;
587
588 ListEntry = GenericList->CurrentEntry;
589 OldListEntry = GenericList->CurrentEntry;
590
591 GenericList->Redraw = FALSE;
592
593 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar) &&
594 (GenericList->CurrentEntry->Entry.Flink != &GenericList->ListHead))
595 {
596 ScrollDownGenericList(GenericList);
597 ListEntry = GenericList->CurrentEntry;
598
599 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar))
600 goto End;
601 }
602
603 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
604 ScrollUpGenericList(GenericList);
605
606 ListEntry = GenericList->CurrentEntry;
607
608 for (;;)
609 {
610 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar))
611 {
612 Flag = TRUE;
613 break;
614 }
615
616 if (GenericList->CurrentEntry->Entry.Flink == &GenericList->ListHead)
617 break;
618
619 ScrollDownGenericList(GenericList);
620 ListEntry = GenericList->CurrentEntry;
621 }
622
623 if (!Flag)
624 {
625 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
626 {
627 if (GenericList->CurrentEntry != OldListEntry)
628 ScrollUpGenericList(GenericList);
629 else
630 break;
631 }
632 }
633 End:
634 DrawListEntries(GenericList);
635 DrawScrollBarGenericList(GenericList);
636
637 GenericList->Redraw = TRUE;
638 }
639
640
641 VOID
642 SaveGenericListState(
643 PGENERIC_LIST List)
644 {
645 List->BackupEntry = List->CurrentEntry;
646 }
647
648
649 VOID
650 RestoreGenericListState(
651 PGENERIC_LIST List)
652 {
653 List->CurrentEntry = List->BackupEntry;
654 }
655
656 /* EOF */