Sync with trunk revision 63128.
[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(PGENERIC_LIST List,
88 BOOLEAN FreeUserData)
89 {
90 PGENERIC_LIST_ENTRY ListEntry;
91 PLIST_ENTRY Entry;
92
93 /* Release list entries */
94 while (!IsListEmpty (&List->ListHead))
95 {
96 Entry = RemoveHeadList (&List->ListHead);
97 ListEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
98
99 /* Release user data */
100 if (FreeUserData && ListEntry->UserData != NULL)
101 RtlFreeHeap (ProcessHeap, 0, ListEntry->UserData);
102
103 /* Release list entry */
104 RtlFreeHeap (ProcessHeap, 0, ListEntry);
105 }
106
107 /* Release list head */
108 RtlFreeHeap (ProcessHeap, 0, List);
109 }
110
111
112 BOOLEAN
113 AppendGenericListEntry(PGENERIC_LIST List,
114 PCHAR Text,
115 PVOID UserData,
116 BOOLEAN Current)
117 {
118 PGENERIC_LIST_ENTRY Entry;
119
120 Entry = (PGENERIC_LIST_ENTRY)RtlAllocateHeap(ProcessHeap,
121 0,
122 sizeof(GENERIC_LIST_ENTRY) + strlen(Text));
123 if (Entry == NULL)
124 return FALSE;
125
126 strcpy (Entry->Text, Text);
127 Entry->List = List;
128 Entry->UserData = UserData;
129
130 InsertTailList(&List->ListHead,
131 &Entry->Entry);
132
133 if (Current || List->CurrentEntry == NULL)
134 {
135 List->CurrentEntry = Entry;
136 }
137
138 return TRUE;
139 }
140
141
142 static VOID
143 DrawListFrame(PGENERIC_LIST GenericList)
144 {
145 COORD coPos;
146 DWORD Written;
147 SHORT i;
148
149 /* Draw upper left corner */
150 coPos.X = GenericList->Left;
151 coPos.Y = GenericList->Top;
152 FillConsoleOutputCharacterA (StdOutput,
153 0xDA, // '+',
154 1,
155 coPos,
156 &Written);
157
158 /* Draw upper edge */
159 coPos.X = GenericList->Left + 1;
160 coPos.Y = GenericList->Top;
161 FillConsoleOutputCharacterA (StdOutput,
162 0xC4, // '-',
163 GenericList->Right - GenericList->Left - 1,
164 coPos,
165 &Written);
166
167 /* Draw upper right corner */
168 coPos.X = GenericList->Right;
169 coPos.Y = GenericList->Top;
170 FillConsoleOutputCharacterA (StdOutput,
171 0xBF, // '+',
172 1,
173 coPos,
174 &Written);
175
176 /* Draw left and right edge */
177 for (i = GenericList->Top + 1; i < GenericList->Bottom; i++)
178 {
179 coPos.X = GenericList->Left;
180 coPos.Y = i;
181 FillConsoleOutputCharacterA (StdOutput,
182 0xB3, // '|',
183 1,
184 coPos,
185 &Written);
186
187 coPos.X = GenericList->Right;
188 FillConsoleOutputCharacterA (StdOutput,
189 0xB3, //'|',
190 1,
191 coPos,
192 &Written);
193 }
194
195 /* Draw lower left corner */
196 coPos.X = GenericList->Left;
197 coPos.Y = GenericList->Bottom;
198 FillConsoleOutputCharacterA (StdOutput,
199 0xC0, // '+',
200 1,
201 coPos,
202 &Written);
203
204 /* Draw lower edge */
205 coPos.X = GenericList->Left + 1;
206 coPos.Y = GenericList->Bottom;
207 FillConsoleOutputCharacterA (StdOutput,
208 0xC4, // '-',
209 GenericList->Right - GenericList->Left - 1,
210 coPos,
211 &Written);
212
213 /* Draw lower right corner */
214 coPos.X = GenericList->Right;
215 coPos.Y = GenericList->Bottom;
216 FillConsoleOutputCharacterA (StdOutput,
217 0xD9, // '+',
218 1,
219 coPos,
220 &Written);
221 }
222
223
224 static VOID
225 DrawListEntries(PGENERIC_LIST GenericList)
226 {
227 PGENERIC_LIST_ENTRY ListEntry;
228 PLIST_ENTRY Entry;
229 COORD coPos;
230 DWORD Written;
231 USHORT Width;
232
233 coPos.X = GenericList->Left + 1;
234 coPos.Y = GenericList->Top + 1;
235 Width = GenericList->Right - GenericList->Left - 1;
236
237 Entry = GenericList->FirstShown;
238 while (Entry != &GenericList->ListHead)
239 {
240 ListEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
241
242 if (coPos.Y == GenericList->Bottom)
243 break;
244 GenericList->LastShown = Entry;
245
246 FillConsoleOutputAttribute (StdOutput,
247 (GenericList->CurrentEntry == ListEntry) ?
248 FOREGROUND_BLUE | BACKGROUND_WHITE :
249 FOREGROUND_WHITE | BACKGROUND_BLUE,
250 Width,
251 coPos,
252 &Written);
253
254 FillConsoleOutputCharacterA (StdOutput,
255 ' ',
256 Width,
257 coPos,
258 &Written);
259
260 coPos.X++;
261 WriteConsoleOutputCharacterA (StdOutput,
262 ListEntry->Text,
263 min (strlen(ListEntry->Text), (SIZE_T)Width - 2),
264 coPos,
265 &Written);
266 coPos.X--;
267
268 coPos.Y++;
269 Entry = Entry->Flink;
270 }
271
272 while (coPos.Y < GenericList->Bottom)
273 {
274 FillConsoleOutputAttribute (StdOutput,
275 FOREGROUND_WHITE | BACKGROUND_BLUE,
276 Width,
277 coPos,
278 &Written);
279
280 FillConsoleOutputCharacterA (StdOutput,
281 ' ',
282 Width,
283 coPos,
284 &Written);
285 coPos.Y++;
286 }
287 }
288
289 static VOID
290 DrawScrollBarGenericList(PGENERIC_LIST GenericList)
291 {
292 COORD coPos;
293 DWORD Written;
294
295 coPos.X = GenericList->Right + 1;
296 coPos.Y = GenericList->Top;
297
298 if (GenericList->FirstShown != GenericList->ListHead.Flink)
299 {
300 FillConsoleOutputCharacterA (StdOutput,
301 '\x18',
302 1,
303 coPos,
304 &Written);
305 }
306 else
307 {
308 FillConsoleOutputCharacterA (StdOutput,
309 ' ',
310 1,
311 coPos,
312 &Written);
313 }
314
315 coPos.Y = GenericList->Bottom;
316 if (GenericList->LastShown != GenericList->ListHead.Blink)
317 {
318 FillConsoleOutputCharacterA (StdOutput,
319 '\x19',
320 1,
321 coPos,
322 &Written);
323 }
324 else
325 {
326 FillConsoleOutputCharacterA (StdOutput,
327 ' ',
328 1,
329 coPos,
330 &Written);
331 }
332 }
333
334 VOID
335 DrawGenericList(PGENERIC_LIST List,
336 SHORT Left,
337 SHORT Top,
338 SHORT Right,
339 SHORT Bottom)
340 {
341 List->FirstShown = List->ListHead.Flink;
342 List->Left = Left;
343 List->Top = Top;
344 List->Right = Right;
345 List->Bottom = Bottom;
346
347 DrawListFrame(List);
348
349 if (IsListEmpty(&List->ListHead))
350 return;
351
352 DrawListEntries(List);
353 DrawScrollBarGenericList(List);
354 }
355
356 VOID
357 ScrollPageDownGenericList (PGENERIC_LIST List)
358 {
359 SHORT i;
360
361 /* Suspend auto-redraw */
362 List->Redraw = FALSE;
363
364 for (i = List->Top + 1; i < List->Bottom - 1; i++)
365 {
366 ScrollDownGenericList (List);
367 }
368
369 /* Update user interface */
370 DrawListEntries(List);
371 DrawScrollBarGenericList(List);
372
373 /* Re enable auto-redraw */
374 List->Redraw = TRUE;
375 }
376
377 VOID
378 ScrollPageUpGenericList (PGENERIC_LIST List)
379 {
380 SHORT i;
381
382 /* Suspend auto-redraw */
383 List->Redraw = FALSE;
384
385 for (i = List->Bottom - 1; i > List->Top + 1; i--)
386 {
387 ScrollUpGenericList (List);
388 }
389
390 /* Update user interface */
391 DrawListEntries(List);
392 DrawScrollBarGenericList(List);
393
394 /* Re enable auto-redraw */
395 List->Redraw = TRUE;
396 }
397
398 VOID
399 ScrollDownGenericList (PGENERIC_LIST List)
400 {
401 PLIST_ENTRY Entry;
402
403 if (List->CurrentEntry == NULL)
404 return;
405
406 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
407 {
408 Entry = List->CurrentEntry->Entry.Flink;
409 if (List->LastShown == &List->CurrentEntry->Entry)
410 {
411 List->FirstShown = List->FirstShown->Flink;
412 List->LastShown = List->LastShown->Flink;
413 }
414 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
415
416 if (List->Redraw)
417 {
418 DrawListEntries(List);
419 DrawScrollBarGenericList(List);
420 }
421 }
422 }
423
424
425 VOID
426 ScrollToPositionGenericList (PGENERIC_LIST List, ULONG uIndex)
427 {
428 PLIST_ENTRY Entry;
429 ULONG uCount = 0;
430
431 if (List->CurrentEntry == NULL || uIndex == 0)
432 return;
433
434 do
435 {
436 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
437 {
438 Entry = List->CurrentEntry->Entry.Flink;
439 if (List->LastShown == &List->CurrentEntry->Entry)
440 {
441 List->FirstShown = List->FirstShown->Flink;
442 List->LastShown = List->LastShown->Flink;
443 }
444 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
445 }
446 uCount++;
447 }
448 while (uIndex != uCount);
449
450 if (List->Redraw)
451 {
452 DrawListEntries(List);
453 DrawScrollBarGenericList(List);
454 }
455 }
456
457
458 VOID
459 ScrollUpGenericList (PGENERIC_LIST List)
460 {
461 PLIST_ENTRY Entry;
462
463 if (List->CurrentEntry == NULL)
464 return;
465
466 if (List->CurrentEntry->Entry.Blink != &List->ListHead)
467 {
468 Entry = List->CurrentEntry->Entry.Blink;
469 if (List->FirstShown == &List->CurrentEntry->Entry)
470 {
471 List->FirstShown = List->FirstShown->Blink;
472 List->LastShown = List->LastShown->Blink;
473 }
474 List->CurrentEntry = CONTAINING_RECORD (Entry, GENERIC_LIST_ENTRY, Entry);
475
476 if (List->Redraw)
477 {
478 DrawListEntries(List);
479 DrawScrollBarGenericList(List);
480 }
481 }
482 }
483
484
485 VOID
486 RedrawGenericList(PGENERIC_LIST List)
487 {
488 if (List->CurrentEntry == NULL)
489 return;
490
491 if (List->Redraw)
492 {
493 DrawListEntries(List);
494 DrawScrollBarGenericList(List);
495 }
496 }
497
498
499 VOID
500 SetCurrentListEntry(PGENERIC_LIST List, PGENERIC_LIST_ENTRY Entry)
501 {
502 if (Entry->List != List)
503 return;
504 List->CurrentEntry = Entry;
505 }
506
507
508 PGENERIC_LIST_ENTRY
509 GetCurrentListEntry(PGENERIC_LIST List)
510 {
511 return List->CurrentEntry;
512 }
513
514
515 PGENERIC_LIST_ENTRY
516 GetFirstListEntry(PGENERIC_LIST List)
517 {
518 PLIST_ENTRY Entry = List->ListHead.Flink;
519
520 if (Entry == &List->ListHead)
521 return NULL;
522 return CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
523 }
524
525
526 PGENERIC_LIST_ENTRY
527 GetNextListEntry(PGENERIC_LIST_ENTRY Entry)
528 {
529 PLIST_ENTRY Next = Entry->Entry.Flink;
530
531 if (Next == &Entry->List->ListHead)
532 return NULL;
533 return CONTAINING_RECORD(Next, GENERIC_LIST_ENTRY, Entry);
534 }
535
536
537 PVOID
538 GetListEntryUserData(PGENERIC_LIST_ENTRY List)
539 {
540 return List->UserData;
541 }
542
543
544 LPCSTR
545 GetListEntryText(PGENERIC_LIST_ENTRY List)
546 {
547 return List->Text;
548 }
549
550
551 VOID
552 GenericListKeyPress (PGENERIC_LIST GenericList, CHAR AsciChar)
553 {
554 PGENERIC_LIST_ENTRY ListEntry;
555 PGENERIC_LIST_ENTRY OldListEntry;
556 BOOLEAN Flag = FALSE;
557
558 ListEntry = GenericList->CurrentEntry;
559 OldListEntry = GenericList->CurrentEntry;
560
561 GenericList->Redraw = FALSE;
562
563 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar) &&
564 (GenericList->CurrentEntry->Entry.Flink != &GenericList->ListHead))
565 {
566 ScrollDownGenericList(GenericList);
567 ListEntry = GenericList->CurrentEntry;
568
569 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar))
570 goto End;
571 }
572
573 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
574 ScrollUpGenericList(GenericList);
575
576 ListEntry = GenericList->CurrentEntry;
577
578 for (;;)
579 {
580 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar))
581 {
582 Flag = TRUE;
583 break;
584 }
585
586 if (GenericList->CurrentEntry->Entry.Flink == &GenericList->ListHead)
587 break;
588
589 ScrollDownGenericList(GenericList);
590 ListEntry = GenericList->CurrentEntry;
591 }
592
593 if (!Flag)
594 {
595 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
596 {
597 if (GenericList->CurrentEntry != OldListEntry)
598 ScrollUpGenericList(GenericList);
599 else
600 break;
601 }
602 }
603 End:
604 DrawListEntries(GenericList);
605 DrawScrollBarGenericList(GenericList);
606
607 GenericList->Redraw = TRUE;
608 }
609
610
611 VOID
612 SaveGenericListState(PGENERIC_LIST List)
613 {
614 List->BackupEntry = List->CurrentEntry;
615 }
616
617
618 VOID
619 RestoreGenericListState(PGENERIC_LIST List)
620 {
621 List->CurrentEntry = List->BackupEntry;
622 }
623
624 /* EOF */