[SETUPLIB][USETUP] Introduce a 'SetupLib' library. CORE-13544
[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 VOID
37 InitGenericListUi(
38 IN OUT PGENERIC_LIST_UI ListUi,
39 IN PGENERIC_LIST List)
40 {
41 ListUi->List = List;
42 ListUi->FirstShown = NULL;
43 ListUi->LastShown = NULL;
44
45 ListUi->Left = 0;
46 ListUi->Top = 0;
47 ListUi->Right = 0;
48 ListUi->Bottom = 0;
49 ListUi->Redraw = TRUE;
50 }
51
52 static
53 VOID
54 DrawListFrame(
55 IN PGENERIC_LIST_UI ListUi)
56 {
57 COORD coPos;
58 DWORD Written;
59 SHORT i;
60
61 /* Draw upper left corner */
62 coPos.X = ListUi->Left;
63 coPos.Y = ListUi->Top;
64 FillConsoleOutputCharacterA (StdOutput,
65 0xDA, // '+',
66 1,
67 coPos,
68 &Written);
69
70 /* Draw upper edge */
71 coPos.X = ListUi->Left + 1;
72 coPos.Y = ListUi->Top;
73 FillConsoleOutputCharacterA (StdOutput,
74 0xC4, // '-',
75 ListUi->Right - ListUi->Left - 1,
76 coPos,
77 &Written);
78
79 /* Draw upper right corner */
80 coPos.X = ListUi->Right;
81 coPos.Y = ListUi->Top;
82 FillConsoleOutputCharacterA (StdOutput,
83 0xBF, // '+',
84 1,
85 coPos,
86 &Written);
87
88 /* Draw left and right edge */
89 for (i = ListUi->Top + 1; i < ListUi->Bottom; i++)
90 {
91 coPos.X = ListUi->Left;
92 coPos.Y = i;
93 FillConsoleOutputCharacterA (StdOutput,
94 0xB3, // '|',
95 1,
96 coPos,
97 &Written);
98
99 coPos.X = ListUi->Right;
100 FillConsoleOutputCharacterA (StdOutput,
101 0xB3, //'|',
102 1,
103 coPos,
104 &Written);
105 }
106
107 /* Draw lower left corner */
108 coPos.X = ListUi->Left;
109 coPos.Y = ListUi->Bottom;
110 FillConsoleOutputCharacterA (StdOutput,
111 0xC0, // '+',
112 1,
113 coPos,
114 &Written);
115
116 /* Draw lower edge */
117 coPos.X = ListUi->Left + 1;
118 coPos.Y = ListUi->Bottom;
119 FillConsoleOutputCharacterA (StdOutput,
120 0xC4, // '-',
121 ListUi->Right - ListUi->Left - 1,
122 coPos,
123 &Written);
124
125 /* Draw lower right corner */
126 coPos.X = ListUi->Right;
127 coPos.Y = ListUi->Bottom;
128 FillConsoleOutputCharacterA (StdOutput,
129 0xD9, // '+',
130 1,
131 coPos,
132 &Written);
133 }
134
135 static
136 VOID
137 DrawListEntries(
138 IN PGENERIC_LIST_UI ListUi)
139 {
140 PGENERIC_LIST List = ListUi->List;
141 PGENERIC_LIST_ENTRY ListEntry;
142 PLIST_ENTRY Entry;
143 COORD coPos;
144 DWORD Written;
145 USHORT Width;
146
147 coPos.X = ListUi->Left + 1;
148 coPos.Y = ListUi->Top + 1;
149 Width = ListUi->Right - ListUi->Left - 1;
150
151 Entry = ListUi->FirstShown;
152 while (Entry != &List->ListHead)
153 {
154 ListEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
155
156 if (coPos.Y == ListUi->Bottom)
157 break;
158 ListUi->LastShown = Entry;
159
160 FillConsoleOutputAttribute (StdOutput,
161 (List->CurrentEntry == ListEntry) ?
162 FOREGROUND_BLUE | BACKGROUND_WHITE :
163 FOREGROUND_WHITE | BACKGROUND_BLUE,
164 Width,
165 coPos,
166 &Written);
167
168 FillConsoleOutputCharacterA (StdOutput,
169 ' ',
170 Width,
171 coPos,
172 &Written);
173
174 coPos.X++;
175 WriteConsoleOutputCharacterA (StdOutput,
176 ListEntry->Text,
177 min (strlen(ListEntry->Text), (SIZE_T)Width - 2),
178 coPos,
179 &Written);
180 coPos.X--;
181
182 coPos.Y++;
183 Entry = Entry->Flink;
184 }
185
186 while (coPos.Y < ListUi->Bottom)
187 {
188 FillConsoleOutputAttribute (StdOutput,
189 FOREGROUND_WHITE | BACKGROUND_BLUE,
190 Width,
191 coPos,
192 &Written);
193
194 FillConsoleOutputCharacterA (StdOutput,
195 ' ',
196 Width,
197 coPos,
198 &Written);
199 coPos.Y++;
200 }
201 }
202
203 static
204 VOID
205 DrawScrollBarGenericList(
206 IN PGENERIC_LIST_UI ListUi)
207 {
208 PGENERIC_LIST List = ListUi->List;
209 COORD coPos;
210 DWORD Written;
211
212 coPos.X = ListUi->Right + 1;
213 coPos.Y = ListUi->Top;
214
215 if (ListUi->FirstShown != List->ListHead.Flink)
216 {
217 FillConsoleOutputCharacterA (StdOutput,
218 '\x18',
219 1,
220 coPos,
221 &Written);
222 }
223 else
224 {
225 FillConsoleOutputCharacterA (StdOutput,
226 ' ',
227 1,
228 coPos,
229 &Written);
230 }
231
232 coPos.Y = ListUi->Bottom;
233 if (ListUi->LastShown != List->ListHead.Blink)
234 {
235 FillConsoleOutputCharacterA (StdOutput,
236 '\x19',
237 1,
238 coPos,
239 &Written);
240 }
241 else
242 {
243 FillConsoleOutputCharacterA (StdOutput,
244 ' ',
245 1,
246 coPos,
247 &Written);
248 }
249 }
250
251 static
252 VOID
253 CenterCurrentListItem(
254 IN PGENERIC_LIST_UI ListUi)
255 {
256 PGENERIC_LIST List = ListUi->List;
257 PLIST_ENTRY Entry;
258 ULONG MaxVisibleItems, ItemCount, i;
259
260 if ((ListUi->Top == 0 && ListUi->Bottom == 0) ||
261 IsListEmpty(&List->ListHead) ||
262 List->CurrentEntry == NULL)
263 {
264 return;
265 }
266
267 MaxVisibleItems = (ULONG)(ListUi->Bottom - ListUi->Top - 1);
268
269 /*****************************************
270 ItemCount = 0;
271 Entry = List->ListHead.Flink;
272 while (Entry != &List->ListHead)
273 {
274 ItemCount++;
275 Entry = Entry->Flink;
276 }
277 *****************************************/
278 ItemCount = List->NumOfEntries; // GetNumberOfListEntries(List);
279
280 if (ItemCount > MaxVisibleItems)
281 {
282 Entry = &List->CurrentEntry->Entry;
283 for (i = 0; i < MaxVisibleItems / 2; i++)
284 {
285 if (Entry->Blink != &List->ListHead)
286 Entry = Entry->Blink;
287 }
288
289 ListUi->FirstShown = Entry;
290
291 for (i = 0; i < MaxVisibleItems; i++)
292 {
293 if (Entry->Flink != &List->ListHead)
294 Entry = Entry->Flink;
295 }
296
297 ListUi->LastShown = Entry;
298 }
299 }
300
301 VOID
302 DrawGenericList(
303 IN PGENERIC_LIST_UI ListUi,
304 IN SHORT Left,
305 IN SHORT Top,
306 IN SHORT Right,
307 IN SHORT Bottom)
308 {
309 PGENERIC_LIST List = ListUi->List;
310
311 ListUi->FirstShown = List->ListHead.Flink;
312 ListUi->Left = Left;
313 ListUi->Top = Top;
314 ListUi->Right = Right;
315 ListUi->Bottom = Bottom;
316
317 DrawListFrame(ListUi);
318
319 if (IsListEmpty(&List->ListHead))
320 return;
321
322 CenterCurrentListItem(ListUi);
323
324 DrawListEntries(ListUi);
325 DrawScrollBarGenericList(ListUi);
326 }
327
328 VOID
329 ScrollDownGenericList(
330 IN PGENERIC_LIST_UI ListUi)
331 {
332 PGENERIC_LIST List = ListUi->List;
333 PLIST_ENTRY Entry;
334
335 if (List->CurrentEntry == NULL)
336 return;
337
338 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
339 {
340 Entry = List->CurrentEntry->Entry.Flink;
341 if (ListUi->LastShown == &List->CurrentEntry->Entry)
342 {
343 ListUi->FirstShown = ListUi->FirstShown->Flink;
344 ListUi->LastShown = ListUi->LastShown->Flink;
345 }
346 List->CurrentEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
347
348 if (ListUi->Redraw)
349 {
350 DrawListEntries(ListUi);
351 DrawScrollBarGenericList(ListUi);
352 }
353 }
354 }
355
356 VOID
357 ScrollUpGenericList(
358 IN PGENERIC_LIST_UI ListUi)
359 {
360 PGENERIC_LIST List = ListUi->List;
361 PLIST_ENTRY Entry;
362
363 if (List->CurrentEntry == NULL)
364 return;
365
366 if (List->CurrentEntry->Entry.Blink != &List->ListHead)
367 {
368 Entry = List->CurrentEntry->Entry.Blink;
369 if (ListUi->FirstShown == &List->CurrentEntry->Entry)
370 {
371 ListUi->FirstShown = ListUi->FirstShown->Blink;
372 ListUi->LastShown = ListUi->LastShown->Blink;
373 }
374 List->CurrentEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
375
376 if (ListUi->Redraw)
377 {
378 DrawListEntries(ListUi);
379 DrawScrollBarGenericList(ListUi);
380 }
381 }
382 }
383
384 VOID
385 ScrollPageDownGenericList(
386 IN PGENERIC_LIST_UI ListUi)
387 {
388 SHORT i;
389
390 /* Suspend auto-redraw */
391 ListUi->Redraw = FALSE;
392
393 for (i = ListUi->Top + 1; i < ListUi->Bottom - 1; i++)
394 {
395 ScrollDownGenericList(ListUi);
396 }
397
398 /* Update user interface */
399 DrawListEntries(ListUi);
400 DrawScrollBarGenericList(ListUi);
401
402 /* Re enable auto-redraw */
403 ListUi->Redraw = TRUE;
404 }
405
406 VOID
407 ScrollPageUpGenericList(
408 IN PGENERIC_LIST_UI ListUi)
409 {
410 SHORT i;
411
412 /* Suspend auto-redraw */
413 ListUi->Redraw = FALSE;
414
415 for (i = ListUi->Bottom - 1; i > ListUi->Top + 1; i--)
416 {
417 ScrollUpGenericList(ListUi);
418 }
419
420 /* Update user interface */
421 DrawListEntries(ListUi);
422 DrawScrollBarGenericList(ListUi);
423
424 /* Re enable auto-redraw */
425 ListUi->Redraw = TRUE;
426 }
427
428 VOID
429 ScrollToPositionGenericList(
430 IN PGENERIC_LIST_UI ListUi,
431 IN ULONG uIndex)
432 {
433 PGENERIC_LIST List = ListUi->List;
434 PLIST_ENTRY Entry;
435 ULONG uCount = 0;
436
437 if (List->CurrentEntry == NULL || uIndex == 0)
438 return;
439
440 do
441 {
442 if (List->CurrentEntry->Entry.Flink != &List->ListHead)
443 {
444 Entry = List->CurrentEntry->Entry.Flink;
445 if (ListUi->LastShown == &List->CurrentEntry->Entry)
446 {
447 ListUi->FirstShown = ListUi->FirstShown->Flink;
448 ListUi->LastShown = ListUi->LastShown->Flink;
449 }
450 List->CurrentEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
451 }
452 uCount++;
453 }
454 while (uIndex != uCount);
455
456 if (ListUi->Redraw)
457 {
458 DrawListEntries(ListUi);
459 DrawScrollBarGenericList(ListUi);
460 }
461 }
462
463 VOID
464 RedrawGenericList(
465 IN PGENERIC_LIST_UI ListUi)
466 {
467 if (ListUi->List->CurrentEntry == NULL)
468 return;
469
470 if (ListUi->Redraw)
471 {
472 DrawListEntries(ListUi);
473 DrawScrollBarGenericList(ListUi);
474 }
475 }
476
477 VOID
478 GenericListKeyPress(
479 IN PGENERIC_LIST_UI ListUi,
480 IN CHAR AsciiChar)
481 {
482 PGENERIC_LIST List = ListUi->List;
483 PGENERIC_LIST_ENTRY ListEntry;
484 PGENERIC_LIST_ENTRY OldListEntry;
485 BOOLEAN Flag = FALSE;
486
487 ListEntry = List->CurrentEntry;
488 OldListEntry = List->CurrentEntry;
489
490 ListUi->Redraw = FALSE;
491
492 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar) &&
493 (List->CurrentEntry->Entry.Flink != &List->ListHead))
494 {
495 ScrollDownGenericList(ListUi);
496 ListEntry = List->CurrentEntry;
497
498 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar))
499 goto End;
500 }
501
502 while (List->CurrentEntry->Entry.Blink != &List->ListHead)
503 ScrollUpGenericList(ListUi);
504
505 ListEntry = List->CurrentEntry;
506
507 for (;;)
508 {
509 if ((strlen(ListEntry->Text) > 0) && (tolower(ListEntry->Text[0]) == AsciiChar))
510 {
511 Flag = TRUE;
512 break;
513 }
514
515 if (List->CurrentEntry->Entry.Flink == &List->ListHead)
516 break;
517
518 ScrollDownGenericList(ListUi);
519 ListEntry = List->CurrentEntry;
520 }
521
522 if (!Flag)
523 {
524 while (List->CurrentEntry->Entry.Blink != &List->ListHead)
525 {
526 if (List->CurrentEntry != OldListEntry)
527 ScrollUpGenericList(ListUi);
528 else
529 break;
530 }
531 }
532
533 End:
534 DrawListEntries(ListUi);
535 DrawScrollBarGenericList(ListUi);
536
537 ListUi->Redraw = TRUE;
538 }
539
540 /* EOF */