|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "functions.h" |
|
6 #include "variables.h" |
|
7 #include "gfx.h" |
|
8 #include "gui.h" |
|
9 #include "window.h" |
|
10 #include "strings.h" |
|
11 #include "table/strings.h" |
|
12 #include "table/sprites.h" |
|
13 #include "newgrf_config.h" |
|
14 |
|
15 |
|
16 /* Build a space separated list of parameters, and terminate */ |
|
17 static char *BuildParamList(char *dst, const GRFConfig *c, const char *last) |
|
18 { |
|
19 uint i; |
|
20 |
|
21 /* Return an empty string if there are no parameters */ |
|
22 if (c->num_params == 0) return strecpy(dst, "", last); |
|
23 |
|
24 for (i = 0; i < c->num_params; i++) { |
|
25 if (i > 0) dst = strecpy(dst, " ", last); |
|
26 dst += snprintf(dst, last - dst, "%d", c->param[i]); |
|
27 } |
|
28 return dst; |
|
29 } |
|
30 |
|
31 |
|
32 /** Parse an integerlist string and set each found value |
|
33 * @param p the string to be parsed. Each element in the list is seperated by a |
|
34 * comma or a space character |
|
35 * @param items pointer to the integerlist-array that will be filled with values |
|
36 * @param maxitems the maximum number of elements the integerlist-array has |
|
37 * @return returns the number of items found, or -1 on an error */ |
|
38 static int parse_intlist(const char *p, int *items, int maxitems) |
|
39 { |
|
40 int n = 0, v; |
|
41 char *end; |
|
42 |
|
43 for (;;) { |
|
44 v = strtol(p, &end, 0); |
|
45 if (p == end || n == maxitems) return -1; |
|
46 p = end; |
|
47 items[n++] = v; |
|
48 if (*p == '\0') break; |
|
49 if (*p != ',' && *p != ' ') return -1; |
|
50 p++; |
|
51 } |
|
52 |
|
53 return n; |
|
54 } |
|
55 |
|
56 |
|
57 static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, bool show_params) |
|
58 { |
|
59 char buff[512]; |
|
60 char *s; |
|
61 uint i; |
|
62 |
|
63 /* Draw filename */ |
|
64 SetDParamStr(0, c->filename); |
|
65 y += DrawStringMultiLine(x, y, STR_NEWGRF_FILENAME, w); |
|
66 |
|
67 /* Prepare and draw GRF ID */ |
|
68 snprintf(buff, lengthof(buff), "%08X", (uint32)BSWAP32(c->grfid)); |
|
69 SetDParamStr(0, buff); |
|
70 y += DrawStringMultiLine(x, y, STR_NEWGRF_GRF_ID, w); |
|
71 |
|
72 /* Prepare and draw MD5 sum */ |
|
73 s = buff; |
|
74 for (i = 0; i < lengthof(c->md5sum); i++) { |
|
75 s += snprintf(s, lastof(buff) - s, "%02X", c->md5sum[i]); |
|
76 } |
|
77 SetDParamStr(0, buff); |
|
78 y += DrawStringMultiLine(x, y, STR_NEWGRF_MD5SUM, w); |
|
79 |
|
80 /* Show GRF parameter list */ |
|
81 if (show_params) { |
|
82 if (c->num_params > 0) { |
|
83 BuildParamList(buff, c, lastof(buff)); |
|
84 SetDParamStr(0, buff); |
|
85 } else { |
|
86 SetDParam(0, STR_01A9_NONE); |
|
87 } |
|
88 y += DrawStringMultiLine(x, y, STR_NEWGRF_PARAMETER, w); |
|
89 } |
|
90 |
|
91 /* Show flags */ |
|
92 if (HASBIT(c->flags, GCF_NOT_FOUND)) y += DrawStringMultiLine(x, y, STR_NEWGRF_NOT_FOUND, w); |
|
93 if (HASBIT(c->flags, GCF_DISABLED)) y += DrawStringMultiLine(x, y, STR_NEWGRF_DISABLED, w); |
|
94 |
|
95 /* Draw GRF info if it exists */ |
|
96 if (c->info != NULL && strlen(c->info) != 0) { |
|
97 SetDParamStr(0, c->info); |
|
98 y += DrawStringMultiLine(x, y, STR_02BD, w); |
|
99 } else { |
|
100 y += DrawStringMultiLine(x, y, STR_NEWGRF_NO_INFO, w); |
|
101 } |
|
102 } |
|
103 |
|
104 |
|
105 /* Dialogue for adding NewGRF files to the selection */ |
|
106 typedef struct newgrf_add_d { |
|
107 GRFConfig **list; |
|
108 const GRFConfig *sel; |
|
109 } newgrf_add_d; |
|
110 |
|
111 |
|
112 static void NewGRFAddDlgWndProc(Window *w, WindowEvent *e) |
|
113 { |
|
114 switch (e->event) { |
|
115 case WE_PAINT: { |
|
116 const GRFConfig *c; |
|
117 int y; |
|
118 int n = 0; |
|
119 |
|
120 /* Count the number of GRFs */ |
|
121 for (c = _all_grfs; c != NULL; c = c->next) n++; |
|
122 |
|
123 w->vscroll.cap = (w->widget[3].bottom - w->widget[3].top) / 10; |
|
124 SetVScrollCount(w, n); |
|
125 |
|
126 SetWindowWidgetDisabledState(w, 6, WP(w, newgrf_add_d).sel == NULL); |
|
127 DrawWindowWidgets(w); |
|
128 |
|
129 GfxFillRect(w->widget[3].left + 1, w->widget[3].top + 1, w->widget[3].right, w->widget[3].bottom, 0xD7); |
|
130 |
|
131 n = 0; |
|
132 y = w->widget[3].top + 1; |
|
133 |
|
134 for (c = _all_grfs; c != NULL; c = c->next) { |
|
135 if (n >= w->vscroll.pos && n < w->vscroll.pos + w->vscroll.cap) { |
|
136 bool h = c == WP(w, newgrf_add_d).sel; |
|
137 const char *text = (c->name != NULL && strlen(c->name) != 0) ? c->name : c->filename; |
|
138 |
|
139 /* Draw selection background */ |
|
140 if (h) GfxFillRect(3, y, w->width - 15, y + 9, 156); |
|
141 DoDrawStringTruncated(text, 4, y, h ? 0xC : 0x6, w->width - 18); |
|
142 y += 10; |
|
143 } |
|
144 n++; |
|
145 } |
|
146 |
|
147 if (WP(w, newgrf_add_d).sel != NULL) { |
|
148 const Widget *wi = &w->widget[5]; |
|
149 ShowNewGRFInfo(WP(w, newgrf_add_d).sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, false); |
|
150 } |
|
151 break; |
|
152 } |
|
153 |
|
154 case WE_CLICK: |
|
155 switch (e->we.click.widget) { |
|
156 case 3: { |
|
157 // Get row... |
|
158 const GRFConfig *c; |
|
159 uint i = (e->we.click.pt.y - w->widget[3].top) / 10 + w->vscroll.pos; |
|
160 |
|
161 for (c = _all_grfs; c != NULL && i > 0; c = c->next, i--); |
|
162 WP(w, newgrf_add_d).sel = c; |
|
163 SetWindowDirty(w); |
|
164 break; |
|
165 } |
|
166 |
|
167 case 6: /* Add selection to list */ |
|
168 if (WP(w, newgrf_add_d).sel != NULL) { |
|
169 const GRFConfig *src = WP(w, newgrf_add_d).sel; |
|
170 GRFConfig *c = calloc(1, sizeof(*c)); |
|
171 *c = *src; |
|
172 c->filename = strdup(src->filename); |
|
173 if (src->name != NULL) c->name = strdup(src->name); |
|
174 if (src->info != NULL) c->info = strdup(src->info); |
|
175 c->next = NULL; |
|
176 *WP(w, newgrf_add_d).list = c; |
|
177 |
|
178 DeleteWindowByClass(WC_SAVELOAD); |
|
179 InvalidateWindowData(WC_GAME_OPTIONS, 0); |
|
180 } |
|
181 break; |
|
182 |
|
183 case 7: /* Rescan list */ |
|
184 WP(w, newgrf_add_d).sel = NULL; |
|
185 ScanNewGRFFiles(); |
|
186 SetWindowDirty(w); |
|
187 break; |
|
188 } |
|
189 break; |
|
190 } |
|
191 } |
|
192 |
|
193 |
|
194 static const Widget _newgrf_add_dlg_widgets[] = { |
|
195 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, |
|
196 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 306, 0, 13, STR_NEWGRF_ADD_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS }, |
|
197 |
|
198 /* List of files */ |
|
199 { WWT_PANEL, RESIZE_RB, 14, 0, 294, 14, 221, 0x0, STR_NULL }, |
|
200 { WWT_INSET, RESIZE_RB, 14, 2, 292, 16, 219, 0x0, STR_NULL }, |
|
201 { WWT_SCROLLBAR, RESIZE_LRB, 14, 295, 306, 14, 221, 0x0, STR_NULL }, |
|
202 |
|
203 /* NewGRF file info */ |
|
204 { WWT_PANEL, RESIZE_RTB, 14, 0, 306, 222, 311, 0x0, STR_NULL }, |
|
205 |
|
206 { WWT_PUSHTXTBTN, RESIZE_RTB, 14, 0, 146, 312, 323, STR_NEWGRF_ADD_FILE, STR_NEWGRF_ADD_FILE_TIP }, |
|
207 { WWT_PUSHTXTBTN, RESIZE_LRTB, 14, 147, 294, 312, 323, STR_NEWGRF_RESCAN_FILES, STR_NEWGRF_RESCAN_FILES_TIP }, |
|
208 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 295, 306, 312, 323, 0x0, STR_RESIZE_BUTTON }, |
|
209 { WIDGETS_END }, |
|
210 }; |
|
211 |
|
212 |
|
213 static const WindowDesc _newgrf_add_dlg_desc = { |
|
214 WDP_CENTER, WDP_CENTER, 307, 324, |
|
215 WC_SAVELOAD, 0, |
|
216 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, |
|
217 _newgrf_add_dlg_widgets, |
|
218 NewGRFAddDlgWndProc, |
|
219 }; |
|
220 |
|
221 |
|
222 /* 'NewGRF Settings' dialogue */ |
|
223 typedef struct newgrf_d { |
|
224 GRFConfig **list; |
|
225 GRFConfig *sel; |
|
226 bool editable; |
|
227 bool show_params; |
|
228 } newgrf_d; |
|
229 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(newgrf_d)); |
|
230 |
|
231 |
|
232 static void SetupNewGRFState(Window *w) |
|
233 { |
|
234 bool disable_all = WP(w, newgrf_d).sel == NULL || !WP(w, newgrf_d).editable; |
|
235 |
|
236 SetWindowWidgetDisabledState(w, 3, !WP(w, newgrf_d).editable); |
|
237 SetWindowWidgetsDisabledState(w, disable_all, 4, 5, 6, WIDGET_LIST_END); |
|
238 SetWindowWidgetDisabledState(w, 10, !WP(w, newgrf_d).show_params || disable_all); |
|
239 |
|
240 if (!disable_all) { |
|
241 /* All widgets are now enabled, so disable widgets we can't use */ |
|
242 if (WP(w, newgrf_d).sel == *WP(w, newgrf_d).list) DisableWindowWidget(w, 5); |
|
243 if (WP(w, newgrf_d).sel->next == NULL) DisableWindowWidget(w, 6); |
|
244 } |
|
245 } |
|
246 |
|
247 |
|
248 static void SetupNewGRFWindow(Window *w) |
|
249 { |
|
250 GRFConfig *c; |
|
251 int i; |
|
252 |
|
253 for (c = *WP(w, newgrf_d).list, i = 0; c != NULL; c = c->next, i++); |
|
254 |
|
255 w->vscroll.cap = (w->widget[7].bottom - w->widget[7].top) / 14 + 1; |
|
256 SetVScrollCount(w, i); |
|
257 } |
|
258 |
|
259 |
|
260 static void NewGRFWndProc(Window *w, WindowEvent *e) |
|
261 { |
|
262 switch (e->event) { |
|
263 case WE_PAINT: { |
|
264 GRFConfig *c; |
|
265 int i, y; |
|
266 |
|
267 SetupNewGRFState(w); |
|
268 |
|
269 DrawWindowWidgets(w); |
|
270 |
|
271 /* Draw NewGRF list */ |
|
272 y = w->widget[7].top; |
|
273 for (c = *WP(w, newgrf_d).list, i = 0; c != NULL; c = c->next, i++) { |
|
274 if (i >= w->vscroll.pos && i < w->vscroll.pos + w->vscroll.cap) { |
|
275 const char *text = (c->name != NULL && strlen(c->name) != 0) ? c->name : c->filename; |
|
276 PalSpriteID pal; |
|
277 |
|
278 /* Pick a colour */ |
|
279 if (HASBIT(c->flags, GCF_NOT_FOUND) || HASBIT(c->flags, GCF_DISABLED)) { |
|
280 pal = PALETTE_TO_RED; |
|
281 } else if (HASBIT(c->flags, GCF_ACTIVATED)) { |
|
282 pal = PALETTE_TO_GREEN; |
|
283 } else { |
|
284 pal = PALETTE_TO_BLUE; |
|
285 } |
|
286 |
|
287 DrawSprite(SPRITE_PALETTE(SPR_SQUARE | pal), 5, y + 2); |
|
288 DoDrawString(text, 25, y + 3, WP(w, newgrf_d).sel == c ? 0xC : 0x10); |
|
289 y += 14; |
|
290 } |
|
291 } |
|
292 |
|
293 if (WP(w, newgrf_d).sel != NULL) { |
|
294 /* Draw NewGRF file info */ |
|
295 const Widget *wi = &w->widget[9]; |
|
296 ShowNewGRFInfo(WP(w, newgrf_d).sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, WP(w, newgrf_d).show_params); |
|
297 } |
|
298 |
|
299 break; |
|
300 } |
|
301 |
|
302 case WE_CLICK: |
|
303 switch (e->we.click.widget) { |
|
304 case 3: { /* Add GRF */ |
|
305 GRFConfig **list = WP(w, newgrf_d).list; |
|
306 Window *w; |
|
307 |
|
308 DeleteWindowByClass(WC_SAVELOAD); |
|
309 w = AllocateWindowDesc(&_newgrf_add_dlg_desc); |
|
310 w->resize.step_height = 10; |
|
311 |
|
312 /* Find the last entry in the list */ |
|
313 for (; *list != NULL; list = &(*list)->next); |
|
314 WP(w, newgrf_add_d).list = list; |
|
315 break; |
|
316 } |
|
317 |
|
318 case 4: { /* Remove GRF */ |
|
319 GRFConfig **pc, *c; |
|
320 for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) { |
|
321 if (c == WP(w, newgrf_d).sel) { |
|
322 *pc = c->next; |
|
323 free(c); |
|
324 break; |
|
325 } |
|
326 } |
|
327 WP(w, newgrf_d).sel = NULL; |
|
328 SetupNewGRFWindow(w); |
|
329 SetWindowDirty(w); |
|
330 break; |
|
331 } |
|
332 |
|
333 case 5: { /* Move GRF up */ |
|
334 GRFConfig **pc, *c; |
|
335 if (WP(w, newgrf_d).sel == NULL) break; |
|
336 |
|
337 for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) { |
|
338 if (c->next == WP(w, newgrf_d).sel) { |
|
339 c->next = WP(w, newgrf_d).sel->next; |
|
340 WP(w, newgrf_d).sel->next = c; |
|
341 *pc = WP(w, newgrf_d).sel; |
|
342 break; |
|
343 } |
|
344 } |
|
345 SetWindowDirty(w); |
|
346 break; |
|
347 } |
|
348 |
|
349 case 6: { /* Move GRF down */ |
|
350 GRFConfig **pc, *c; |
|
351 if (WP(w, newgrf_d).sel == NULL) break; |
|
352 |
|
353 for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) { |
|
354 if (c == WP(w, newgrf_d).sel) { |
|
355 *pc = c->next; |
|
356 c->next = c->next->next; |
|
357 (*pc)->next = c; |
|
358 break; |
|
359 } |
|
360 } |
|
361 SetWindowDirty(w); |
|
362 break; |
|
363 } |
|
364 |
|
365 case 7: { /* Select a GRF */ |
|
366 GRFConfig *c; |
|
367 uint i = (e->we.click.pt.y - w->widget[7].top) / 14 + w->vscroll.pos; |
|
368 |
|
369 for (c = *WP(w, newgrf_d).list; c != NULL && i > 0; c = c->next, i--); |
|
370 WP(w, newgrf_d).sel = c; |
|
371 |
|
372 SetWindowDirty(w); |
|
373 break; |
|
374 } |
|
375 |
|
376 case 10: { /* Edit parameters */ |
|
377 char buff[512]; |
|
378 if (WP(w, newgrf_d).sel == NULL) break; |
|
379 |
|
380 BuildParamList(buff, WP(w, newgrf_d).sel, lastof(buff)); |
|
381 ShowQueryString(BindCString(buff), STR_NEWGRF_PARAMETER_QUERY, 63, 250, w->window_class, w->window_number, CS_ALPHANUMERAL); |
|
382 break; |
|
383 } |
|
384 } |
|
385 break; |
|
386 |
|
387 case WE_ON_EDIT_TEXT: |
|
388 if (e->we.edittext.str != NULL) { |
|
389 /* Parse our new "int list" */ |
|
390 GRFConfig *c = WP(w, newgrf_d).sel; |
|
391 c->num_params = parse_intlist(e->we.edittext.str, (int*)c->param, lengthof(c->param)); |
|
392 |
|
393 /* parse_intlist returns -1 on error */ |
|
394 if (c->num_params == (byte)-1) c->num_params = 0; |
|
395 } |
|
396 SetWindowDirty(w); |
|
397 break; |
|
398 |
|
399 case WE_RESIZE: |
|
400 w->vscroll.cap += e->we.sizing.diff.y / 14; |
|
401 w->widget[7].data = (w->vscroll.cap << 8) + 1; |
|
402 break; |
|
403 } |
|
404 } |
|
405 |
|
406 |
|
407 static const Widget _newgrf_widgets[] = { |
|
408 { WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, |
|
409 { WWT_CAPTION, RESIZE_RIGHT, 10, 11, 299, 0, 13, STR_NEWGRF_SETTINGS_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS }, |
|
410 |
|
411 /* NewGRF file Add, Remove, Move up, Move down */ |
|
412 { WWT_PANEL, RESIZE_RIGHT, 10, 0, 299, 14, 29, STR_NULL, STR_NULL }, |
|
413 { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 10, 79, 16, 27, STR_NEWGRF_ADD, STR_NEWGRF_ADD_TIP }, |
|
414 { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 80, 149, 16, 27, STR_NEWGRF_REMOVE, STR_NEWGRF_REMOVE_TIP }, |
|
415 { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 150, 219, 16, 27, STR_NEWGRF_MOVEUP, STR_NEWGRF_MOVEUP_TIP }, |
|
416 { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 220, 289, 16, 27, STR_NEWGRF_MOVEDOWN, STR_NEWGRF_MOVEDOWN_TIP }, |
|
417 |
|
418 /* NewGRF file list */ |
|
419 { WWT_MATRIX, RESIZE_RB, 10, 0, 287, 30, 99, 0x501, STR_NEWGRF_FILE_TIP }, |
|
420 { WWT_SCROLLBAR, RESIZE_LRB, 10, 288, 299, 30, 99, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST }, |
|
421 |
|
422 /* NewGRF file info */ |
|
423 { WWT_PANEL, RESIZE_RTB, 10, 0, 299, 100, 199, STR_NULL, STR_NULL }, |
|
424 |
|
425 /* Edit parameter button... */ |
|
426 { WWT_PUSHTXTBTN, RESIZE_RTB, 10, 0, 287, 200, 211, STR_NEWGRF_SET_PARAMETERS, STR_NULL }, |
|
427 { WWT_RESIZEBOX, RESIZE_LRTB, 10, 288, 299, 200, 211, 0x0, STR_RESIZE_BUTTON }, |
|
428 |
|
429 { WIDGETS_END }, |
|
430 }; |
|
431 |
|
432 |
|
433 static const WindowDesc _newgrf_desc = { |
|
434 WDP_CENTER, WDP_CENTER, 300, 212, |
|
435 WC_GAME_OPTIONS, 0, |
|
436 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, |
|
437 _newgrf_widgets, |
|
438 NewGRFWndProc, |
|
439 }; |
|
440 |
|
441 |
|
442 void ShowNewGRFSettings(bool editable, bool show_params, GRFConfig **config) |
|
443 { |
|
444 Window *w; |
|
445 |
|
446 DeleteWindowByClass(WC_GAME_OPTIONS); |
|
447 w = AllocateWindowDesc(&_newgrf_desc); |
|
448 if (w == NULL) return; |
|
449 |
|
450 w->resize.step_height = 14; |
|
451 |
|
452 /* Clear selections */ |
|
453 WP(w, newgrf_d).sel = NULL; |
|
454 WP(w, newgrf_d).list = config; |
|
455 WP(w, newgrf_d).editable = editable; |
|
456 WP(w, newgrf_d).show_params = show_params; |
|
457 |
|
458 SetupNewGRFWindow(w); |
|
459 } |