1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include "gui/guilistbox.h"
19 #include "gui/guimain.h"
20 #include "font/fonts.h"
21 #include "util/stream.h"
22 #include "gfx/bitmap.h"
23 #include "util/wgt2allg.h"
24 
25 using AGS::Common::Stream;
26 using AGS::Common::Bitmap;
27 
28 std::vector<GUIListBox> guilist;
29 int numguilist = 0;
30 
ChangeFont(int newfont)31 void GUIListBox::ChangeFont(int newfont) {
32   font = newfont;
33   rowheight = getfontheight(font) + get_fixed_pixel_size(2);
34   num_items_fit = hit / rowheight;
35 }
36 
Resized()37  void GUIListBox::Resized()
38 {
39 	if (rowheight == 0)
40 	{
41 	  check_font(&font);
42 	  ChangeFont(font);
43 	}
44 
45 	if (rowheight > 0)
46 	  num_items_fit = hit / rowheight;
47 }
48 
WriteToFile(Stream * out)49 void GUIListBox::WriteToFile(Stream *out)
50 {
51   int a;
52 
53   GUIObject::WriteToFile(out);
54   // MACPORT FIXES: swap
55   out->WriteArrayOfInt32(&numItems, 11);
56   out->WriteInt32(alignment);
57   out->WriteInt32(reserved1);
58   out->WriteInt32(selectedbgcol);
59   for (a = 0; a < numItems; a++)
60     items[a].Write(out);
61 
62   if (exflags & GLF_SGINDEXVALID)
63     out->WriteArrayOfInt16(&saveGameIndex[0], numItems);
64 }
65 
ReadFromFile(Stream * in,GuiVersion gui_version)66 void GUIListBox::ReadFromFile(Stream *in, GuiVersion gui_version)
67 {
68   int a, i;
69   char tempbuf[300];
70 
71   Clear();
72 
73   GUIObject::ReadFromFile(in, gui_version);
74   // MACPORT FIXES: swap
75   in->ReadArrayOfInt32(&numItems, 11);
76 
77   if (gui_version >= kGuiVersion_272b) {
78     alignment = in->ReadInt32();
79     reserved1 = in->ReadInt32();
80   }
81   else {
82     alignment = GALIGN_LEFT;
83     reserved1 = 0;
84   }
85 
86   if (gui_version >= kGuiVersion_unkn_107) {
87     selectedbgcol = in->ReadInt32();
88   }
89   else {
90     selectedbgcol = textcol;
91     if (selectedbgcol == 0)
92       selectedbgcol = 16;
93   }
94 
95   for (a = 0; a < numItems; a++) {
96     i = 0;
97     while ((tempbuf[i] = in->ReadInt8()) != 0)
98       i++;
99 
100     items[a] = tempbuf;
101     saveGameIndex[a] = -1;
102   }
103 
104   if ((gui_version >= kGuiVersion_272d) && (exflags & GLF_SGINDEXVALID)) {
105     in->ReadArrayOfInt16(&saveGameIndex[0], numItems);
106   }
107 
108   if (textcol == 0)
109     textcol = 16;
110 }
111 
AddItem(const char * toadd)112 int GUIListBox::AddItem(const char *toadd)
113 {
114   if (numItems >= MAX_LISTBOX_ITEMS)
115     return -1;
116 
117   guis_need_update = 1;
118   items[numItems] = toadd;
119   saveGameIndex[numItems] = -1;
120   numItems++;
121   return numItems - 1;
122 }
123 
InsertItem(int index,const char * toadd)124 int GUIListBox::InsertItem(int index, const char *toadd)
125 {
126   int aa;
127 
128   if (numItems >= MAX_LISTBOX_ITEMS)
129     return -1;
130 
131   if ((index < 0) || (index > numItems))
132     return -1;
133 
134   guis_need_update = 1;
135 
136   for (aa = numItems; aa > index; aa--) {
137     items[aa] = items[aa - 1];
138     saveGameIndex[aa] = saveGameIndex[aa - 1];
139   }
140 
141   items[index] = toadd;
142   saveGameIndex[index] = -1;
143   numItems++;
144 
145   if (selected >= index)
146     selected++;
147 
148   return numItems - 1;
149 }
150 
SetItemText(int item,const char * newtext)151 void GUIListBox::SetItemText(int item, const char *newtext)
152 {
153   if ((item >= numItems) || (item < 0))
154     return;
155 
156   guis_need_update = 1;
157   items[item] = newtext;
158 }
159 
Clear()160 void GUIListBox::Clear()
161 {
162   int aa;
163   for (aa = 0; aa < numItems; aa++)
164     items[aa].Free();
165 
166   numItems = 0;
167   selected = 0;
168   topItem = 0;
169   guis_need_update = 1;
170 }
171 
RemoveItem(int index)172 void GUIListBox::RemoveItem(int index)
173 {
174   int aa;
175 
176   if ((index < 0) || (index >= numItems))
177     return;
178 
179   numItems--;
180   for (aa = index; aa < numItems; aa++) {
181     items[aa] = items[aa + 1];
182     saveGameIndex[aa] = saveGameIndex[aa + 1];
183   }
184 
185   if (selected > index)
186     selected--;
187   if (selected >= numItems)
188     selected = -1;
189 
190   guis_need_update = 1;
191 }
192 
Draw(Common::Bitmap * ds)193 void GUIListBox::Draw(Common::Bitmap *ds)
194 {
195   wid--;
196   hit--;
197   int pixel_size = get_fixed_pixel_size(1);
198 
199   check_font(&font);
200   color_t text_color = ds->GetCompatibleColor(textcol);
201   color_t draw_color = ds->GetCompatibleColor(textcol);
202   if ((exflags & GLF_NOBORDER) == 0) {
203     ds->DrawRect(Rect(x, y, x + wid + (pixel_size - 1), y + hit + (pixel_size - 1)), draw_color);
204     if (pixel_size > 1)
205       ds->DrawRect(Rect(x + 1, y + 1, x + wid, y + hit), draw_color);
206   }
207 
208   int rightHandEdge = (x + wid) - pixel_size - 1;
209 
210   // use ChangeFont to update the rowheight and num_items_fit
211   ChangeFont(font);
212 
213   // draw the scroll bar in if necessary
214   if ((numItems > num_items_fit) && ((exflags & GLF_NOBORDER) == 0) && ((exflags & GLF_NOARROWS) == 0)) {
215     int xstrt, ystrt;
216     ds->DrawRect(Rect(x + wid - get_fixed_pixel_size(7), y, (x + (pixel_size - 1) + wid) - get_fixed_pixel_size(7), y + hit), draw_color);
217     ds->DrawRect(Rect(x + wid - get_fixed_pixel_size(7), y + hit / 2, x + wid, y + hit / 2 + (pixel_size - 1)), draw_color);
218 
219     xstrt = (x + wid - get_fixed_pixel_size(6)) + (pixel_size - 1);
220     ystrt = (y + hit - 3) - get_fixed_pixel_size(5);
221 
222     draw_color = ds->GetCompatibleColor(textcol);
223     ds->DrawTriangle(Triangle(xstrt, ystrt, xstrt + get_fixed_pixel_size(4), ystrt,
224              xstrt + get_fixed_pixel_size(2),
225              ystrt + get_fixed_pixel_size(5)), draw_color);
226 
227     ystrt = y + 3;
228     ds->DrawTriangle(Triangle(xstrt, ystrt + get_fixed_pixel_size(5),
229              xstrt + get_fixed_pixel_size(4),
230              ystrt + get_fixed_pixel_size(5),
231              xstrt + get_fixed_pixel_size(2), ystrt), draw_color);
232 
233     rightHandEdge -= get_fixed_pixel_size(7);
234   }
235 
236   Draw_items_fix();
237 
238   for (int a = 0; a < num_items_fit; a++) {
239     int thisyp;
240     if (a + topItem >= numItems)
241       break;
242 
243     thisyp = y + pixel_size + a * rowheight;
244     if (a + topItem == selected) {
245       int stretchto = (x + wid) - pixel_size;
246 
247       text_color = ds->GetCompatibleColor(backcol);
248 
249       if (selectedbgcol > 0) {
250         // draw the selected item bar (if colour not transparent)
251         draw_color = ds->GetCompatibleColor(selectedbgcol);
252         if ((num_items_fit < numItems) && ((exflags & GLF_NOBORDER) == 0) && ((exflags & GLF_NOARROWS) == 0))
253           stretchto -= get_fixed_pixel_size(7);
254 
255         ds->FillRect(Rect(x + pixel_size, thisyp, stretchto, thisyp + rowheight - pixel_size), draw_color);
256       }
257     }
258     else
259       text_color = ds->GetCompatibleColor(textcol);
260 
261     int item_index = a + topItem;
262     char oritext[200]; // items[] can be not longer than 200 characters due declaration
263     Draw_set_oritext(oritext, items[item_index]);
264 
265     if (alignment == GALIGN_LEFT)
266       wouttext_outline(ds, x + 1 + pixel_size, thisyp + 1, font, text_color, oritext);
267     else {
268       int textWidth = wgettextwidth(oritext, font);
269 
270       if (alignment == GALIGN_RIGHT)
271         wouttext_outline(ds, rightHandEdge - textWidth, thisyp + 1, font, text_color, oritext);
272       else
273         wouttext_outline(ds, ((rightHandEdge - x) / 2) + x - (textWidth / 2), thisyp + 1, font, text_color, oritext);
274     }
275 
276   }
277   wid++;
278   hit++;
279 
280   Draw_items_unfix();
281 }
282 
IsInRightMargin(int xx)283 int GUIListBox::IsInRightMargin(int xx) {
284 
285   if ((xx >= (wid - get_fixed_pixel_size(6))) && ((exflags & GLF_NOBORDER) == 0) && ((exflags & GLF_NOARROWS) == 0)) {
286     return 1;
287   }
288   return 0;
289 }
290 
GetIndexFromCoordinates(int xx,int yy)291 int GUIListBox::GetIndexFromCoordinates(int xx, int yy) {
292   if (rowheight <= 0 || IsInRightMargin(xx))
293     return -1;
294 
295   int onindex = yy / rowheight + topItem;
296   if ((onindex < 0) || (onindex >= numItems))
297     return -1;
298 
299   return onindex;
300 }
301 
MouseDown()302 int GUIListBox::MouseDown()
303 {
304   if (IsInRightMargin(mousexp)) {
305     if ((mouseyp < hit / 2) && (topItem > 0))
306       topItem--;
307 
308     if ((mouseyp >= hit / 2) && (numItems > topItem + num_items_fit))
309       topItem++;
310 
311     return 0;
312   }
313 
314   int newsel = GetIndexFromCoordinates(mousexp, mouseyp);
315   if (newsel < 0)
316     return 0;
317 
318   selected = newsel;
319   activated = 1;
320   return 0;
321 }
322