Sync with trunk.
[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 SetCurrentListEntry(PGENERIC_LIST List, PGENERIC_LIST_ENTRY Entry)
487 {
488 if (Entry->List != List)
489 return;
490 List->CurrentEntry = Entry;
491 }
492
493
494 PGENERIC_LIST_ENTRY
495 GetCurrentListEntry(PGENERIC_LIST List)
496 {
497 return List->CurrentEntry;
498 }
499
500
501 PGENERIC_LIST_ENTRY
502 GetFirstListEntry(PGENERIC_LIST List)
503 {
504 PLIST_ENTRY Entry = List->ListHead.Flink;
505
506 if (Entry == &List->ListHead)
507 return NULL;
508 return CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
509 }
510
511
512 PGENERIC_LIST_ENTRY
513 GetNextListEntry(PGENERIC_LIST_ENTRY Entry)
514 {
515 PLIST_ENTRY Next = Entry->Entry.Flink;
516
517 if (Next == &Entry->List->ListHead)
518 return NULL;
519 return CONTAINING_RECORD(Next, GENERIC_LIST_ENTRY, Entry);
520 }
521
522
523 PVOID
524 GetListEntryUserData(PGENERIC_LIST_ENTRY List)
525 {
526 return List->UserData;
527 }
528
529
530 LPCSTR
531 GetListEntryText(PGENERIC_LIST_ENTRY List)
532 {
533 return List->Text;
534 }
535
536
537 VOID
538 GenericListKeyPress (PGENERIC_LIST GenericList, CHAR AsciChar)
539 {
540 PGENERIC_LIST_ENTRY ListEntry;
541 PGENERIC_LIST_ENTRY OldListEntry;
542 BOOLEAN Flag = FALSE;
543
544 ListEntry = GenericList->CurrentEntry;
545 OldListEntry = GenericList->CurrentEntry;
546
547 GenericList->Redraw = FALSE;
548
549 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar) &&
550 (GenericList->CurrentEntry->Entry.Flink != &GenericList->ListHead))
551 {
552 ScrollDownGenericList(GenericList);
553 ListEntry = GenericList->CurrentEntry;
554
555 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar))
556 goto End;
557 }
558
559 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
560 ScrollUpGenericList(GenericList);
561
562 ListEntry = GenericList->CurrentEntry;
563
564 for (;;)
565 {
566 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciChar))
567 {
568 Flag = TRUE;
569 break;
570 }
571
572 if (GenericList->CurrentEntry->Entry.Flink == &GenericList->ListHead)
573 break;
574
575 ScrollDownGenericList(GenericList);
576 ListEntry = GenericList->CurrentEntry;
577 }
578
579 if (!Flag)
580 {
581 while (GenericList->CurrentEntry->Entry.Blink != &GenericList->ListHead)
582 {
583 if (GenericList->CurrentEntry != OldListEntry)
584 ScrollUpGenericList(GenericList);
585 else
586 break;
587 }
588 }
589 End:
590 DrawListEntries(GenericList);
591 DrawScrollBarGenericList(GenericList);
592
593 GenericList->Redraw = TRUE;
594 }
595
596
597 VOID
598 SaveGenericListState(PGENERIC_LIST List)
599 {
600 List->BackupEntry = List->CurrentEntry;
601 }
602
603
604 VOID
605 RestoreGenericListState(PGENERIC_LIST List)
606 {
607 List->CurrentEntry = List->BackupEntry;
608 }
609
610 /* EOF */