newgrf_gui.c
changeset 5237 4fce3ea379c2
child 5241 4b5b63946288
equal deleted inserted replaced
5236:4c1289d5e45a 5237:4fce3ea379c2
       
     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 }