slightly better list selection coloring (don't loose visible color marks)
[k8-i-v-a-n.git] / src / felib / felist.cpp
blobbcbc6d123763c11a99a7ea0f4a8df4dff8e7595e
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 #include <fstream>
14 #include "felist.h"
15 #include "graphics.h"
16 #include "bitmap.h"
17 #include "whandler.h"
18 #include "rawbit.h"
19 #include "fesave.h"
20 #include "festring.h"
23 const felist *FelistCurrentlyDrawn = 0;
24 truth fastListMode = 0;
27 truth felist::GetFastListMode () { return fastListMode; }
28 void felist::SetFastListMode (truth modeon) { fastListMode = modeon; }
31 truth FelistDrawController () {
32 FelistCurrentlyDrawn->DrawPage(DOUBLE_BUFFER);
33 return true;
37 struct felistentry {
38 felistentry () : ImageKey(NO_IMAGE) {}
39 felistentry (cfestring &String, col16 Color, uInt Marginal, uInt ImageKey, truth Selectable) :
40 String(String),
41 Color(Color),
42 Marginal(Marginal),
43 ImageKey(ImageKey),
44 Selectable(Selectable) {}
46 festring String;
47 col16 Color;
48 uInt Marginal;
49 uInt ImageKey;
50 truth Selectable;
54 outputfile &operator << (outputfile &SaveFile, const felistentry *Entry) {
55 SaveFile << Entry->String << Entry->Color << Entry->Marginal << Entry->Selectable;
56 return SaveFile;
60 inputfile &operator >> (inputfile &SaveFile, felistentry *&Entry) {
61 Entry = new felistentry;
62 SaveFile >> Entry->String >> Entry->Color >> Entry->Marginal >> Entry->Selectable;
63 return SaveFile;
67 struct felistdescription {
68 felistdescription () {}
69 felistdescription (cfestring &String, col16 Color)
70 : String(String), Color(Color) {}
71 festring String;
72 col16 Color;
76 felist::felist (cfestring &Topic, col16 TopicColor, uInt Maximum) :
77 mSaveSelector(false),
78 mSaveDir(""),
79 Maximum(Maximum),
80 Selected(0),
81 Pos(10, 10),
82 Width(780),
83 PageLength(26+10),
84 BackColor(0),
85 Flags(SELECTABLE|FADE),
86 UpKey(KEY_UP),
87 DownKey(KEY_DOWN),
88 EntryDrawer(0)
90 AddDescription(Topic, TopicColor);
94 felist::~felist () {
95 Empty();
96 for (uInt c = 0; c < Description.size(); ++c) delete Description[c];
100 void felist::Pop () {
101 delete Entry[GetLastEntryIndex()];
102 Entry.pop_back();
106 uInt felist::Draw () {
107 while (Entry.size() && Entry[GetLastEntryIndex()]->String.IsEmpty()) Pop();
108 if (Entry.empty()) return LIST_WAS_EMPTY;
110 FelistCurrentlyDrawn = this;
111 if (globalwindowhandler::ControlLoopsInstalled()) globalwindowhandler::InstallControlLoop(FelistDrawController);
113 bitmap BackGround(RES);
114 bitmap*Buffer;
116 BackGround.ActivateFastFlag();
117 if (Flags&FADE) {
118 Buffer = new bitmap(RES, 0);
119 Buffer->ActivateFastFlag();
120 BackGround.ClearToColor(0);
121 } else {
122 Buffer = DOUBLE_BUFFER;
123 Buffer->FastBlit(&BackGround);
126 uInt c;
127 uInt Return, Selectables = 0;
128 truth JustSelectMove = false;
129 for (c = 0; c < Entry.size(); ++c) if (Entry[c]->Selectable) ++Selectables;
130 if (Selected >= Selectables) Selected = Selectables-1;
132 if (Flags&SELECTABLE) PageBegin = Selected-Selected%PageLength;
133 else if (Flags&INVERSE_MODE) PageBegin = GetLastEntryIndex()-GetLastEntryIndex()%PageLength;
134 else PageBegin = 0;
136 for (;;) {
137 truth AtTheEnd = DrawPage(Buffer);
138 if (Flags&FADE) {
139 if (JustSelectMove) {
140 Buffer->FastBlit(DOUBLE_BUFFER);
141 graphics::BlitDBToScreen();
142 } else {
143 Buffer->FadeToScreen();
145 JustSelectMove = false;
146 } else {
147 graphics::BlitDBToScreen();
150 uInt Pressed = GET_KEY(false);
152 if (Flags&SELECTABLE) {
153 // list movement and selections
154 int prs = -1;
156 if (Pressed >= 'a' && Pressed <= 'z') prs = Pressed-'a';
157 else if (Pressed >= 'A' && Pressed <= 'Z') prs = Pressed-'A';
158 else if (Pressed >= '0' && Pressed <= '9') prs = Pressed-'0'+26;
160 if (prs >= 0) {
161 if ((uInt)prs < PageLength && (uInt)prs+PageBegin < Selectables) {
162 Return = Selected = (uInt)prs+PageBegin;
163 if (fastListMode) {
164 JustSelectMove = true;
165 continue;
166 } else {
167 break;
170 continue;
173 if (Pressed == UpKey) {
174 if (Selected) {
175 --Selected;
176 if (Selected < PageBegin) {
177 BackGround.FastBlit(Buffer);
178 PageBegin -= PageLength;
179 } else {
180 JustSelectMove = true;
182 } else {
183 for (c = 0, Selected = 0; c < Entry.size(); ++c) if (Entry[c]->Selectable) ++Selected;
184 --Selected;
185 if (PageBegin == Selected-Selected%PageLength) JustSelectMove = true;
186 else {
187 BackGround.FastBlit(Buffer);
188 PageBegin = Selected-Selected%PageLength;
191 continue;
194 if (Pressed == DownKey) {
195 if (!AtTheEnd || Selected != Selectables-1) {
196 ++Selected;
197 if (Selected > PageBegin+PageLength-1) {
198 PageBegin += PageLength;
199 BackGround.FastBlit(Buffer);
200 } else {
201 JustSelectMove = true;
203 } else {
204 if (!PageBegin) JustSelectMove = true; else BackGround.FastBlit(Buffer);
205 Selected = PageBegin = 0;
207 continue;
210 if (Pressed == KEY_HOME) {
211 Selected = PageBegin;
212 JustSelectMove = true;
213 continue;
216 if (Pressed == KEY_END) {
217 //fprintf(stderr, "sel: %u; pb: %u; pl: %u; ss: %u; ae: %s\n", Selected, PageBegin, PageLength, Selectables, AtTheEnd ? "end" : "not end");
218 if (Selectables) {
219 Selected = PageBegin+PageLength-1;
220 if (Selected >= Selectables) Selected = Selectables-1;
222 JustSelectMove = true;
223 continue;
226 if (Pressed == KEY_LEFT) {
227 // previous page
228 if (Selectables && PageBegin > 0) {
229 PageBegin = (PageBegin < PageLength ? 0 : PageBegin-PageLength);
230 Selected = PageBegin;
231 BackGround.FastBlit(Buffer);
233 continue;
236 if (Pressed == KEY_RIGHT) {
237 // next page
238 if (Selectables) {
239 uInt pgend = PageBegin+PageLength;
240 if (pgend < Selectables) {
241 PageBegin += PageLength;
242 Selected = PageBegin;
243 BackGround.FastBlit(Buffer);
246 continue;
249 if (Pressed == KEY_ENTER) {
250 Return = Selected;
251 break;
253 } else {
254 // not selectable: allow navigation with left/right
255 if (Pressed == KEY_LEFT) {
256 if (PageBegin > 0) {
257 PageBegin -= (PageBegin < PageLength ? PageBegin : PageLength);
258 BackGround.FastBlit(Buffer);
260 continue;
263 if (Pressed == KEY_RIGHT) {
264 if (!AtTheEnd) {
265 PageBegin += PageLength;
266 BackGround.FastBlit(Buffer);
268 continue;
272 if (Pressed == KEY_ESC) {
273 Return = ESCAPED;
274 break;
277 if ((AtTheEnd && !(Flags&INVERSE_MODE)) || (!PageBegin && (Flags&INVERSE_MODE))) {
278 if (Flags&FELIST_NO_BADKEY_EXIT) continue;
279 Return = NOTHING_SELECTED;
280 break;
281 } else {
282 BackGround.FastBlit(Buffer);
283 if (Flags&INVERSE_MODE) PageBegin -= PageLength; else PageBegin += PageLength;
284 if (Flags&SELECTABLE) Selected = PageBegin;
288 if (!(Flags&FADE)) {
289 if (Flags&DRAW_BACKGROUND_AFTERWARDS) BackGround.FastBlit(DOUBLE_BUFFER);
290 if (Flags&BLIT_AFTERWARDS) graphics::BlitDBToScreen();
291 } else {
292 delete Buffer;
295 globalwindowhandler::DeInstallControlLoop(FelistDrawController);
296 return Return;
300 //static festring Str;
302 truth felist::DrawPage (bitmap *Buffer) const {
303 uInt LastFillBottom = Pos.Y+23+Description.size()*10;
304 festring Str;
306 DrawDescription(Buffer);
307 uInt c, i; // c == entry index, i == selectable index
308 int selIdx = -1;
309 for (c = 0, i = 0; i != PageBegin; ++c) if (Entry[c]->Selectable) ++i;
310 while (!Entry[c]->Selectable && Entry[c]->String.IsEmpty()) ++c;
311 std::vector<festring> Chapter;
313 //col16 selBack = BackColor^0xFFFF, selText = BackColor;
314 //col16 selBack = LIGHT_GRAY, selText = BLACK;
315 col16 selBack = SEL_BLUE, selText = WHITE;
317 for (;;) {
318 Str.Empty();
319 uInt Marginal = Entry[c]->Marginal;
320 if ((Flags&SELECTABLE) && Entry[c]->Selectable) {
321 if (i-PageBegin <= 25) Str << char('A'+(i-PageBegin)); else Str << char('0'+(i-PageBegin-26));
322 Str << ": ";
323 Marginal += 3;
325 Str << Entry[c]->String;
326 bool selected = (Flags&SELECTABLE && Entry[c]->Selectable && Selected == i);
327 if (selected) selIdx = (int)c;
328 col16 ecolor = Entry[c]->Color;
329 if (selected) {
330 if (ecolor != RED && ecolor != GREEN && ecolor != PINK) ecolor = selText;
332 if (Entry[c]->ImageKey != NO_IMAGE) {
333 if (Str.rawLength() <= (Width-50)>>3) {
334 Buffer->Fill(Pos.X+3, LastFillBottom, Width-6, 20, (!selected ? BackColor : selBack));
335 if (EntryDrawer) EntryDrawer(Buffer, v2(Pos.X+13, LastFillBottom), Entry[c]->ImageKey);
336 if (selected) {
337 FONT->PrintfUnshadedColored(Buffer, v2(Pos.X+37, LastFillBottom+4), ecolor, "%s", Str.CStr());
338 } else {
339 FONT->PrintfColored(Buffer, v2(Pos.X+37, LastFillBottom+4), ecolor, "%s", Str.CStr());
341 LastFillBottom += 20;
342 } else {
343 uInt ChapterSize = festring::SplitStringColored(Str, Chapter, (Width-50)>>3, Marginal);
344 uInt PictureTop = LastFillBottom+ChapterSize*5-9;
345 for (uInt l = 0; l < ChapterSize; ++l) {
346 Buffer->Fill(Pos.X+3, LastFillBottom, Width-6, 10, !selected ? BackColor : selBack);
347 if (selected) {
348 FONT->PrintfUnshadedColored(Buffer, v2(Pos.X+37, LastFillBottom), ecolor, "%s", Chapter[l].CStr());
349 } else {
350 FONT->PrintfColored(Buffer, v2(Pos.X+37, LastFillBottom), ecolor, "%s", Chapter[l].CStr());
352 LastFillBottom += 10;
354 if (EntryDrawer) EntryDrawer(Buffer, v2(Pos.X+13, PictureTop), Entry[c]->ImageKey);
356 } else {
357 uInt ChapterSize = festring::SplitStringColored(Str, Chapter, (Width-26)>>3, Marginal);
358 for (uInt l = 0; l < ChapterSize; ++l) {
359 Buffer->Fill(Pos.X+3, LastFillBottom, Width-6, 10, !selected ? BackColor : selBack);
360 if (selected) {
361 FONT->PrintfUnshadedColored(Buffer, v2(Pos.X+13, LastFillBottom), ecolor, "%s", Chapter[l].CStr());
362 } else {
363 FONT->PrintfColored(Buffer, v2(Pos.X+13, LastFillBottom), ecolor, "%s", Chapter[l].CStr());
365 LastFillBottom += 10;
368 if ((i-PageBegin == PageLength-1 && Entry[c]->Selectable) || c == Entry.size()-1) {
369 if ((!(Flags&INVERSE_MODE) && c != Entry.size()-1) || (Flags&INVERSE_MODE && PageBegin)) {
370 Buffer->Fill(Pos.X+3, LastFillBottom, Width-6, 30, BackColor);
371 FONT->PrintfColored(Buffer, v2(Pos.X+13, LastFillBottom+10), WHITE, "- Press SPACE to continue, ESC to exit -");
372 LastFillBottom += 30;
373 } else {
374 Buffer->Fill(Pos.X+3, LastFillBottom, Width-6, 10, BackColor);
375 LastFillBottom += 10;
377 Buffer->DrawRectangle(Pos.X+1, Pos.Y+1, Pos.X+Width-2, LastFillBottom+1, DARK_GRAY, true);
378 break;
380 if (Entry[c++]->Selectable) ++i;
382 if (selIdx != -1 && mSaveSelector) {
383 bitmap bmp(ZERO_V2);
384 festring imgName = Entry[selIdx]->String;
385 festring::sizetype pos = imgName.FindLast(".sav");
386 if (pos != festring::NPos) {
387 imgName.Erase(pos, 4); // remove '.sav'
388 #if defined(SGAME_SHOTS_IPU) || !defined(HAVE_IMLIB2)
389 imgName = mSaveDir+imgName+".ipu";
390 #else
391 imgName = mSaveDir+imgName+".png";
392 #endif
393 //fprintf(stderr, "sel=%d; img=%s\n", selIdx, imgName.CStr());
394 #if defined(SGAME_SHOTS_IPU) || !defined(HAVE_IMLIB2)
395 if (bmp.LoadIPU(imgName))
396 #else
397 if (bmp.LoadPNG(imgName))
398 #endif
400 //fprintf(stderr, " LOADED! %dx%d\n", bmp.GetSize().X, bmp.GetSize().Y);
401 int x = Buffer->GetSize().X-bmp.GetSize().X-2;
402 int y = Buffer->GetSize().Y-bmp.GetSize().Y-2;
403 int w = bmp.GetSize().X;
404 int h = bmp.GetSize().Y;
405 blitdata bd = {
406 Buffer,
407 {0, 0}, //src
408 {x, y}, //dest
409 {w, h}, //border
410 {0}, // luminance/flags
411 0xDEAD, // mask color
412 0 // custom data
414 bmp.NormalBlit(bd);
415 Buffer->DrawRectangle(x-2, y-2, x+w, y+h, DARK_GRAY, true);
419 return c == Entry.size()-1;
423 void felist::DrawDescription (bitmap *Buffer) const {
424 Buffer->Fill(Pos.X+3, Pos.Y+3, Width-6, 20, BackColor);
425 for (uInt c = 0; c < Description.size(); ++c) {
426 Buffer->Fill(Pos.X+3, Pos.Y+13+c*10, Width-6, 10, BackColor);
427 FONT->PrintfColored(Buffer, v2(Pos.X+13, Pos.Y+13+c*10), Description[c]->Color, "%s", Description[c]->String.CStr());
429 Buffer->Fill(Pos.X+3, Pos.Y+13+Description.size()*10, Width-6, 10, BackColor);
433 /* We suppose InverseMode != false here */
434 void felist::QuickDraw (bitmap *Bitmap, uInt PageLength) const {
435 static std::vector<festring> Chapter;
436 uInt Width = Bitmap->GetSize().X;
437 Bitmap->Fill(3, 3, Width-6, 20+PageLength*10, 0);
438 Bitmap->DrawRectangle(1, 1, Width-2, 24+PageLength*10, DARK_GRAY, true);
439 uInt LineSize = (Width-26)>>3;
440 uInt Index = 0;
441 uInt Bottom = PageLength*10+3;
442 for (uInt c1 = 0; c1 <= Selected; ++c1) {
443 const felistentry* CurrentEntry = Entry[Selected-c1];
444 uInt ChapterSize = festring::SplitStringColored(CurrentEntry->String, Chapter, LineSize, CurrentEntry->Marginal);
445 for (uInt c2 = 0; c2 < ChapterSize; ++c2) {
446 col16 Color = CurrentEntry->Color;
447 Color = MakeRGB16(
448 GetRed16(Color)-((GetRed16(Color)*3*Index/PageLength)>>2),
449 GetGreen16(Color)-((GetGreen16(Color)*3*Index/PageLength)>>2),
450 GetBlue16(Color)-((GetBlue16(Color)*3*Index/PageLength)>>2));
451 FONT->PrintfColored(Bitmap, v2(13, Bottom), Color, "%s", Chapter[ChapterSize-c2-1].CStr());
452 Bottom -= 10;
453 if (++Index == PageLength) return;
459 void felist::CreateQuickDrawFontCaches (rawbitmap *Font, col16 Color, uInt PageLength) {
460 if (PageLength < 2) return;
461 for (uInt c = 0; c < PageLength; ++c)
462 Font->CreateFontCache(
463 MakeRGB16(
464 GetRed16(Color)-((GetRed16(Color)*3*c/PageLength)>>2),
465 GetGreen16(Color)-((GetGreen16(Color)*3*c/PageLength)>>2),
466 GetBlue16(Color)-((GetBlue16(Color)*3*c/PageLength)>>2)));
470 void felist::Empty () {
471 for (uInt c = 0; c < Entry.size(); ++c) delete Entry[c];
472 Entry.clear();
476 void felist::AddEntry (cfestring &Str, col16 Color, uInt Marginal, uInt Key, truth Selectable) {
477 Entry.push_back(new felistentry(Str, Color, Marginal, Key, Selectable));
478 if (Maximum && Entry.size() > feuLong(Maximum)) {
479 delete Entry[0];
480 Entry.erase(Entry.begin());
485 void felist::Save (outputfile &SaveFile) const {
486 SaveFile << Entry << Maximum << Selected;
490 void felist::Load (inputfile& SaveFile) {
491 SaveFile >> Entry >> Maximum >> Selected;
495 void felist::PrintToFile (cfestring& FileName) {
496 std::ofstream SaveFile(FileName.CStr(), std::ios::out);
497 if (!SaveFile.is_open()) return;
498 uInt c;
499 for (c = 0; c < Description.size(); ++c) SaveFile << Description[c]->String.CStr() << std::endl;
500 SaveFile << std::endl;
501 for (c = 0; c < Entry.size(); ++c) {
502 if (Entry[c]->ImageKey != NO_IMAGE) SaveFile << " ";
503 SaveFile << Entry[c]->String.CStr() << std::endl;
508 void felist::AddDescription (cfestring &Str, col16 Color) {
509 Description.push_back(new felistdescription(Str, Color));
513 festring felist::GetEntry (uInt I) const {
514 return Entry[I]->String;
518 col16 felist::GetColor (uInt I) const {
519 return Entry[I]->Color;
523 void felist::SetColor (uInt I, col16 What) {
524 Entry[I]->Color = What;