(svn r11547) -Add: signal selection GUI for the ones that really like to use that over CTRL. Patch by BigBB.
authorrubidium
Sat, 01 Dec 2007 21:40:18 +0000
changeset 7991 d6a95a5593ba
parent 7990 63211992f5c1
child 7992 c729c4a80630
(svn r11547) -Add: signal selection GUI for the ones that really like to use that over CTRL. Patch by BigBB.
bin/data/openttdd.grf
bin/data/openttdw.grf
src/lang/english.txt
src/openttd.h
src/rail_cmd.cpp
src/rail_gui.cpp
src/settings.cpp
src/settings_gui.cpp
src/table/files.h
src/table/sprites.h
src/variables.h
Binary file bin/data/openttdd.grf has changed
Binary file bin/data/openttdw.grf has changed
--- a/src/lang/english.txt	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/lang/english.txt	Sat Dec 01 21:40:18 2007 +0000
@@ -1169,6 +1169,7 @@
 STR_CONFIG_PATCHES_ALLOW_SHARES                                 :{LTBLUE}Allow buying shares from other companies
 STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY                         :{LTBLUE}When dragging, place signals every: {ORANGE}{STRING1} tile(s)
 STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE                  :{LTBLUE}Automatically build semaphores before: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_ENABLE_SIGNAL_GUI                            :{LTBLUE}Enable the signal GUI: {ORANGE}{STRING1}
 
 STR_CONFIG_PATCHES_TOWN_LAYOUT_INVALID                          :{WHITE}The town layout "no more roads" isn't valid in the scenario editor
 STR_CONFIG_PATCHES_TOWN_LAYOUT                                  :{LTBLUE}Select town-road layout: {ORANGE}{STRING1}
@@ -3445,3 +3446,20 @@
 STR_FACE_EARRING                                                :Earring:
 STR_FACE_TIE_EARRING_TIP                                        :{BLACK}Change tie or earring
 ########
+
+############ signal GUI
+STR_SIGNAL_SELECTION                                            :{WHITE}Signal Selection
+STR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE                           :{WHITE}Can't convert signals here...
+STR_BUILD_SIGNAL_SEMAPHORE_NORM_TIP                             :{BLACK}Standard Signal (semaphore){}Signals are necessary to keep trains from crashing on railway networks with more than one train.
+STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TIP                            :{BLACK}Entry-Signal (semaphore){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red.
+STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TIP                             :{BLACK}Exit-Signal (semaphore){}Behaves in the same way as a normal signal but is necessary to trigger the correct colour on entry & combo pre-signals.
+STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TIP                            :{BLACK}Combo-Signal (semaphore){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of presignals.
+STR_BUILD_SIGNAL_ELECTRIC_NORM_TIP                              :{BLACK}Standard Signal (electric){}Signals are necessary to keep trains from crashing on railway networks with more than one train.
+STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TIP                             :{BLACK}Entry-Signal (electric){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red.
+STR_BUILD_SIGNAL_ELECTRIC_EXIT_TIP                              :{BLACK}Exit-Signal (electric){}Behaves in the same way as a normal signal but is necessary to trigger the correct colour on entry & combo pre-signals.
+STR_BUILD_SIGNAL_ELECTRIC_COMBO_TIP                             :{BLACK}Combo-Signal (electric){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of presignals.
+STR_SIGNAL_CONVERT_TIP                                          :{BLACK}Signal Convert{}When selected, clicking an existing signal will convert it to the selected signal type and variant, CTRL-click will toggle the existing variant.
+STR_DRAG_SIGNALS_DENSITY_TIP                                    :{BLACK}Dragging signal density
+STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP                           :{BLACK}Decrease dragging signal density
+STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP                           :{BLACK}Increase dragging signal density
+########
--- a/src/openttd.h	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/openttd.h	Sat Dec 01 21:40:18 2007 +0000
@@ -581,6 +581,7 @@
 	WC_CONFIRM_POPUP_QUERY,
 	WC_TRANSPARENCY_TOOLBAR,
 	WC_VEHICLE_TIMETABLE,
+	WC_BUILD_SIGNAL,
 };
 
 
--- a/src/rail_cmd.cpp	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/rail_cmd.cpp	Sat Dec 01 21:40:18 2007 +0000
@@ -776,16 +776,20 @@
  * @param flags operation to perform
  * @param p1 various bitstuffed elements
  * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
- * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
+ * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle)
  * - p1 = (bit 4)   - 0 = signals, 1 = semaphores
+ * - p1 = (bit 5-6) - type of the signal, for valid values see enum SignalType in rail_map.h
+ * - p1 = (bit 7)   - convert the present signal type and variant
  * @param p2 used for CmdBuildManySignals() to copy direction of first signal
  * TODO: p2 should be replaced by two bits for "along" and "against" the track.
  */
 CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	Track track = (Track)GB(p1, 0, 3);
-	bool pre_signal = HasBit(p1, 3);
-	SignalVariant sigvar = (pre_signal ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC;
+	bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed
+	SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
+	SignalType sigtype = (SignalType)GB(p1, 5, 2); // the signal type of the new signal
+	bool convert_signal = HasBit(p1, 7); // convert button pressed
 	CommandCost cost;
 
 	if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoTrainOnTrack(tile, track))
@@ -799,18 +803,19 @@
 
 	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 
-	_error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK;
-
 	{
 		/* See if this is a valid track combination for signals, (ie, no overlap) */
 		TrackBits trackbits = GetTrackBits(tile);
 		if (KillFirstBit(trackbits) != TRACK_BIT_NONE && /* More than one track present */
 				trackbits != TRACK_BIT_HORZ &&
 				trackbits != TRACK_BIT_VERT) {
-			return CMD_ERROR;
+			return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK);
 		}
 	}
 
+	/* you can not convert a signal if no signal is on track */
+	if (convert_signal && !HasSignalOnTrack(tile, track)) return CMD_ERROR;
+
 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
 	if (!HasSignalOnTrack(tile, track)) {
@@ -820,6 +825,17 @@
 		if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) {
 			/* convert signals <-> semaphores */
 			cost = CommandCost(_price.build_signals + _price.remove_signals);
+
+		} else if (convert_signal) {
+			/* convert button pressed */
+			if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
+				/* convert electric <-> semaphore */
+				cost = CommandCost(_price.build_signals + _price.remove_signals);
+			} else {
+				/* it is free to change signal type: normal-pre-exit-combo */
+				cost = CommandCost();
+			}
+
 		} else {
 			/* it is free to change orientation/pre-exit-combo signals */
 			cost = CommandCost();
@@ -832,7 +848,7 @@
 			SetHasSignals(tile, true);
 			SetSignalStates(tile, 0xF); // all signals are on
 			SetPresentSignals(tile, 0); // no signals built by default
-			SetSignalType(tile, track, SIGTYPE_NORMAL);
+			SetSignalType(tile, track, sigtype);
 			SetSignalVariant(tile, track, sigvar);
 		}
 
@@ -840,15 +856,28 @@
 			if (!HasSignalOnTrack(tile, track)) {
 				/* build new signals */
 				SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track));
-				SetSignalType(tile, track, SIGTYPE_NORMAL);
+				SetSignalType(tile, track, sigtype);
 				SetSignalVariant(tile, track, sigvar);
 			} else {
-				if (pre_signal) {
+				if (convert_signal) {
+					/* convert signal button pressed */
+					if (ctrl_pressed) {
+						/* toggle the pressent signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
+						SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+
+					} else {
+						/* convert the present signal to the chosen type and variant */
+						SetSignalType(tile, track, sigtype);
+						SetSignalVariant(tile, track, sigvar);
+					}
+
+				} else if (ctrl_pressed) {
 					/* cycle between normal -> pre -> exit -> combo -> ... */
-					SignalType type = GetSignalType(tile, track);
-
-					SetSignalType(tile, track, type == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(type + 1));
+					sigtype = GetSignalType(tile, track);
+
+					SetSignalType(tile, track, sigtype == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(sigtype + 1));
 				} else {
+					/* cycle the signal side: both -> left -> right -> both -> ... */
 					CycleSignalSide(tile, track);
 				}
 			}
--- a/src/rail_gui.cpp	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/rail_gui.cpp	Sat Dec 01 21:40:18 2007 +0000
@@ -38,6 +38,9 @@
 static DiagDirection _build_depot_direction;
 static byte _waypoint_count = 1;
 static byte _cur_waypoint_type;
+static bool _convert_signal_button; // convert signal button in the signal GUI pressed
+static SignalVariant _cur_signal_variant; // set the signal variant (for signal GUI)
+static SignalType _cur_signal_type; // set the signal type (for signal GUI)
 
 static struct {
 	byte orientation;
@@ -56,6 +59,7 @@
 static void ShowBuildTrainDepotPicker();
 static void ShowBuildWaypointPicker();
 static void ShowStationBuilder();
+static void ShowSignalBuilder();
 
 void CcPlaySound1E(bool success, TileIndex tile, uint32 p1, uint32 p2)
 {
@@ -170,6 +174,11 @@
 	}
 }
 
+/**
+ * Build a new signal or edit/remove a present signal, use CmdBuildSingleSignal() or CmdRemoveSingleSignal() in rail_cmd.cpp
+ *
+ * @param tile The tile where the signal will build or edit
+ */
 static void GenericPlaceSignals(TileIndex tile)
 {
 	TrackBits trackbits = (TrackBits)GB(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0), 0, 6);
@@ -188,12 +197,17 @@
 		DoCommandP(tile, track, 0, CcPlaySound1E,
 			CMD_REMOVE_SIGNALS | CMD_MSG(STR_1013_CAN_T_REMOVE_SIGNALS_FROM));
 	} else {
+		if (!_patches.enable_signal_gui) _cur_signal_variant = _cur_year < _patches.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC;
+
+		/* various bitstuffed elements for CmdBuildSingleSignal() */
 		uint32 p1 = track;
 		SB(p1, 3, 1, _ctrl_pressed);
-		SB(p1, 4, 1, _cur_year < _patches.semaphore_build_before);
+		SB(p1, 4, 1, _cur_signal_variant);
+		SB(p1, 5, 2, _patches.enable_signal_gui ? _cur_signal_type : SIGTYPE_NORMAL);
+		SB(p1, 7, 1, _convert_signal_button);
 
-		DoCommandP(tile, p1, 0, CcPlaySound1E,
-			CMD_BUILD_SIGNALS | CMD_MSG(STR_1010_CAN_T_BUILD_SIGNALS_HERE));
+		DoCommandP(tile, p1, 0, CcPlaySound1E, CMD_BUILD_SIGNALS |
+			CMD_MSG(_convert_signal_button ? STR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_1010_CAN_T_BUILD_SIGNALS_HERE));
 	}
 }
 
@@ -307,9 +321,14 @@
 	if (HandlePlacePushButton(w, RTW_BUILD_STATION, SPR_CURSOR_RAIL_STATION, VHM_RECT, PlaceRail_Station)) ShowStationBuilder();
 }
 
+/** The "build signal"-button proc from BuildRailToolbWndProc() (start ShowSignalBuilder() and/or HandleAutoSignalPlacement()) */
 static void BuildRailClick_AutoSignals(Window *w)
 {
-	HandlePlacePushButton(w, RTW_BUILD_SIGNALS, ANIMCURSOR_BUILDSIGNALS, VHM_RECT, PlaceRail_AutoSignals);
+	if (_patches.enable_signal_gui) {
+		if (HandlePlacePushButton(w, RTW_BUILD_SIGNALS, ANIMCURSOR_BUILDSIGNALS, VHM_RECT, PlaceRail_AutoSignals)) ShowSignalBuilder();
+	} else {
+		HandlePlacePushButton(w, RTW_BUILD_SIGNALS, ANIMCURSOR_BUILDSIGNALS, VHM_RECT, PlaceRail_AutoSignals);
+	}
 }
 
 static void BuildRailClick_Bridge(Window *w)
@@ -366,6 +385,12 @@
 	DoRailroadTrack(trackstat);
 }
 
+/**
+ * Build new signals or remove signals or (if only one tile marked) edit a signal.
+ *
+ * If one tile marked abort and use GenericPlaceSignals()
+ * else use CmdBuildSingleSignal() or CmdRemoveSingleSignal() in rail_cmd.cpp to build many signals
+ */
 static void HandleAutoSignalPlacement()
 {
 	TileHighlightData *thd = &_thd;
@@ -463,6 +488,12 @@
 	}
 }
 
+/**
+ * Railway toolbar window event definition
+ *
+ * @param w window pointer
+ * @param e event been triggered
+ */
 static void BuildRailToolbWndProc(Window *w, WindowEvent *e)
 {
 	switch (e->event) {
@@ -496,6 +527,9 @@
 		return;
 
 	case WE_PLACE_DRAG: {
+		/* no dragging if you have pressed the convert button */
+		if (_convert_signal_button && IsWindowWidgetLowered(w, RTW_BUILD_SIGNALS)) return;
+
 		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method);
 		return;
 	}
@@ -548,10 +582,12 @@
 		DisableWindowWidget(w, RTW_REMOVE);
 		InvalidateWidget(w, RTW_REMOVE);
 
+		w = FindWindowById(WC_BUILD_SIGNAL, 0);
+		if (w != NULL) WP(w, def_d).close = true;
 		w = FindWindowById(WC_BUILD_STATION, 0);
-		if (w != NULL) WP(w,def_d).close = true;
+		if (w != NULL) WP(w, def_d).close = true;
 		w = FindWindowById(WC_BUILD_DEPOT, 0);
-		if (w != NULL) WP(w,def_d).close = true;
+		if (w != NULL) WP(w, def_d).close = true;
 		break;
 
 	case WE_PLACE_PRESIZE: {
@@ -752,7 +788,7 @@
 		DrawPixelInfo tmp_dpi, *old_dpi;
 		const StationSpec *statspec = newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL;
 
-		if (WP(w,def_d).close) return;
+		if (WP(w, def_d).close) return;
 
 		if (_railstation.dragdrop) {
 			SetTileSelectSize(1, 1);
@@ -995,7 +1031,7 @@
 		break;
 
 	case WE_MOUSELOOP:
-		if (WP(w,def_d).close) {
+		if (WP(w, def_d).close) {
 			DeleteWindow(w);
 			return;
 		}
@@ -1003,7 +1039,7 @@
 		break;
 
 	case WE_DESTROY:
-		if (!WP(w,def_d).close) ResetObjectToPlace();
+		if (!WP(w, def_d).close) ResetObjectToPlace();
 		break;
 	}
 }
@@ -1107,6 +1143,166 @@
 	}
 }
 
+/** Enum referring to the widgets of the signal window */
+enum BuildSignalWidgets {
+	BSW_CLOSEBOX = 0,
+	BSW_CAPTION,
+	BSW_SEMAPHORE_NORM,
+	BSW_SEMAPHORE_ENTRY,
+	BSW_SEMAPHORE_EXIT,
+	BSW_SEMAPHORE_COMBO,
+	BSW_ELECTRIC_NORM,
+	BSW_ELECTRIC_ENTRY,
+	BSW_ELECTRIC_EXIT,
+	BSW_ELECTRIC_COMBO,
+	BSW_CONVERT,
+	BSW_DRAG_SIGNALS_DENSITY,
+	BSW_DRAG_SIGNALS_DENSITY_DECREASE,
+	BSW_DRAG_SIGNALS_DENSITY_INCREASE,
+};
+
+/**
+ * Draw dynamic a signal-sprite in a button in the signal GUI
+ * Draw the sprite +1px to the right and down if the button is lowered and change the sprite to sprite + 1 (red to green light)
+ *
+ * @param w            Window on which the widget is located
+ * @param widget_index index of this widget in the window
+ * @param image        the sprite to draw
+ * @param xrel         the relativ x value of the sprite in the grf
+ * @param xsize        the width of the sprite
+ */
+static const void DrawSignalSprite(const Window *w, byte widget_index, SpriteID image, int8 xrel, uint8 xsize)
+{
+	DrawSprite(image + IsWindowWidgetLowered(w, widget_index), PAL_NONE,
+			w->widget[widget_index].left + (w->widget[widget_index].right - w->widget[widget_index].left) / 2 - xrel - xsize / 2 +
+			IsWindowWidgetLowered(w, widget_index), w->widget[widget_index].bottom - 3 + IsWindowWidgetLowered(w, widget_index));
+}
+
+/**
+ * Signal selection window event definition
+ *
+ * @param w window pointer
+ * @param e event been triggered
+ */
+static void SignalBuildWndProc(Window *w, WindowEvent *e)
+{
+	switch (e->event) {
+		case WE_PAINT:
+			LowerWindowWidget(w, (_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type);
+
+			SetWindowWidgetLoweredState(w, BSW_CONVERT, _convert_signal_button);
+
+			SetWindowWidgetDisabledState(w, BSW_DRAG_SIGNALS_DENSITY_DECREASE, _patches.drag_signals_density == 1);
+			SetWindowWidgetDisabledState(w, BSW_DRAG_SIGNALS_DENSITY_INCREASE, _patches.drag_signals_density == 20);
+
+			DrawWindowWidgets(w);
+
+			/* The 'hardcoded' off sets are needed because they are reused sprites. */
+			DrawSignalSprite(w, BSW_SEMAPHORE_NORM,  SPR_IMG_SIGNAL_SEMAPHORE_NORM,   0, 12); // xsize of sprite + 1 ==  9
+			DrawSignalSprite(w, BSW_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, -1, 13); // xsize of sprite + 1 == 10
+			DrawSignalSprite(w, BSW_SEMAPHORE_EXIT,  SPR_IMG_SIGNAL_SEMAPHORE_EXIT,   0, 12); // xsize of sprite + 1 ==  9
+			DrawSignalSprite(w, BSW_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_COMBO,  0, 12); // xsize of sprite + 1 ==  9
+			DrawSignalSprite(w, BSW_ELECTRIC_NORM,   SPR_IMG_SIGNAL_ELECTRIC_NORM,   -1,  4);
+			DrawSignalSprite(w, BSW_ELECTRIC_ENTRY,  SPR_IMG_SIGNAL_ELECTRIC_ENTRY,  -2,  6);
+			DrawSignalSprite(w, BSW_ELECTRIC_EXIT,   SPR_IMG_SIGNAL_ELECTRIC_EXIT,   -2,  6);
+			DrawSignalSprite(w, BSW_ELECTRIC_COMBO,  SPR_IMG_SIGNAL_ELECTRIC_COMBO,  -2,  6);
+
+			/* Draw dragging signal density value in the BSW_DRAG_SIGNALS_DENSITY widget */
+			SetDParam(0, _patches.drag_signals_density);
+			DrawStringCentered(w->widget[BSW_DRAG_SIGNALS_DENSITY].left + (w->widget[BSW_DRAG_SIGNALS_DENSITY].right -
+					w->widget[BSW_DRAG_SIGNALS_DENSITY].left) / 2 + 1,
+					w->widget[BSW_DRAG_SIGNALS_DENSITY].top + 2, STR_JUST_INT, TC_ORANGE);
+			break;
+
+		case WE_CLICK:
+			switch (e->we.click.widget) {
+				case BSW_SEMAPHORE_NORM:
+				case BSW_SEMAPHORE_ENTRY:
+				case BSW_SEMAPHORE_EXIT:
+				case BSW_SEMAPHORE_COMBO:
+				case BSW_ELECTRIC_NORM:
+				case BSW_ELECTRIC_ENTRY:
+				case BSW_ELECTRIC_EXIT:
+				case BSW_ELECTRIC_COMBO:
+					RaiseWindowWidget(w, (_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type);
+
+					_cur_signal_type = (SignalType)((uint)((e->we.click.widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_COMBO + 1)));
+					_cur_signal_variant = e->we.click.widget >= BSW_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE;
+					break;
+
+				case BSW_CONVERT:
+					_convert_signal_button = !_convert_signal_button;
+					break;
+
+				case BSW_DRAG_SIGNALS_DENSITY_DECREASE:
+					if (_patches.drag_signals_density > 1) _patches.drag_signals_density--;
+					break;
+
+				case BSW_DRAG_SIGNALS_DENSITY_INCREASE:
+					if (_patches.drag_signals_density < 20) _patches.drag_signals_density++;
+					break;
+
+				default: break;
+			}
+
+			SetWindowDirty(w);
+			break;
+
+		case WE_MOUSELOOP:
+			if (WP(w, def_d).close) DeleteWindow(w);
+			return;
+
+		case WE_DESTROY:
+			if (!WP(w, def_d).close) ResetObjectToPlace();
+			break;
+		}
+}
+
+/** Widget definition of the build signal window */
+static const Widget _signal_builder_widgets[] = {
+{   WWT_CLOSEBOX,   RESIZE_NONE,  7,   0,  10,   0,  13, STR_00C5,               STR_018B_CLOSE_WINDOW},                 // BSW_CLOSEBOX
+{    WWT_CAPTION,   RESIZE_NONE,  7,  11, 109,   0,  13, STR_SIGNAL_SELECTION,   STR_018C_WINDOW_TITLE_DRAG_THIS},       // BSW_CAPTION
+
+{      WWT_PANEL,   RESIZE_NONE,  7,   0,  21,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_NORM_TIP},   // BSW_SEMAPHORE_NORM
+{      WWT_PANEL,   RESIZE_NONE,  7,  22,  43,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TIP},  // BSW_SEMAPHORE_ENTRY
+{      WWT_PANEL,   RESIZE_NONE,  7,  44,  65,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TIP},   // BSW_SEMAPHORE_EXIT
+{      WWT_PANEL,   RESIZE_NONE,  7,  66,  87,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TIP},  // BSW_SEMAPHORE_COMBO
+
+{      WWT_PANEL,   RESIZE_NONE,  7,   0,  21,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_NORM_TIP},    // BSW_ELECTRIC_NORM
+{      WWT_PANEL,   RESIZE_NONE,  7,  22,  43,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TIP},   // BSW_ELECTRIC_ENTRY
+{      WWT_PANEL,   RESIZE_NONE,  7,  44,  65,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_EXIT_TIP},    // BSW_ELECTRIC_EXIT
+{      WWT_PANEL,   RESIZE_NONE,  7,  66,  87,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_COMBO_TIP},   // BSW_ELECTRIC_COMBO
+
+{     WWT_IMGBTN,   RESIZE_NONE,  7,  88, 109,  14,  40, SPR_IMG_SIGNAL_CONVERT, STR_SIGNAL_CONVERT_TIP},                // BSW_CONVERT
+{      WWT_PANEL,   RESIZE_NONE,  7,  88, 109,  41,  67, STR_NULL,               STR_DRAG_SIGNALS_DENSITY_TIP},          // BSW_DRAG_SIGNALS_DENSITY
+{ WWT_PUSHIMGBTN,   RESIZE_NONE, 14,  90,  98,  54,  65, SPR_ARROW_LEFT,         STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_DECREASE
+{ WWT_PUSHIMGBTN,   RESIZE_NONE, 14,  99, 107,  54,  65, SPR_ARROW_RIGHT,        STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_INCREASE
+
+{   WIDGETS_END},
+};
+
+/** Signal selection window description */
+static const WindowDesc _signal_builder_desc = {
+	WDP_AUTO, WDP_AUTO, 110, 68, 110, 68,
+	WC_BUILD_SIGNAL, WC_BUILD_TOOLBAR,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	_signal_builder_widgets,
+	SignalBuildWndProc
+};
+
+/**
+ * Open the signal selection window
+ * @pre reset all signal GUI relevant variables
+ */
+static void ShowSignalBuilder()
+{
+	_convert_signal_button = false;
+	_cur_signal_variant = _cur_year < _patches.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC;
+	_cur_signal_type = SIGTYPE_NORMAL;
+
+	AllocateWindowDesc(&_signal_builder_desc);
+}
+
 /** Enum referring to the widgets of the build rail depot window */
 enum BuildRailDepotWidgets {
 	BRDW_CLOSEBOX = 0,
@@ -1149,11 +1345,11 @@
 		break;
 
 	case WE_MOUSELOOP:
-		if (WP(w,def_d).close) DeleteWindow(w);
+		if (WP(w, def_d).close) DeleteWindow(w);
 		return;
 
 	case WE_DESTROY:
-		if (!WP(w,def_d).close) ResetObjectToPlace();
+		if (!WP(w, def_d).close) ResetObjectToPlace();
 		break;
 	}
 }
@@ -1248,11 +1444,11 @@
 	}
 
 	case WE_MOUSELOOP:
-		if (WP(w,def_d).close) DeleteWindow(w);
+		if (WP(w, def_d).close) DeleteWindow(w);
 		break;
 
 	case WE_DESTROY:
-		if (!WP(w,def_d).close) ResetObjectToPlace();
+		if (!WP(w, def_d).close) ResetObjectToPlace();
 		break;
 	}
 }
--- a/src/settings.cpp	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/settings.cpp	Sat Dec 01 21:40:18 2007 +0000
@@ -1382,6 +1382,7 @@
 	SDT_BOOL(Patches, longbridges,                   0,NN,  true,        STR_CONFIG_PATCHES_LONGBRIDGES,         NULL),
 	SDT_BOOL(Patches, signal_side,                   N,NN,  true,        STR_CONFIG_PATCHES_SIGNALSIDE,          RedrawScreen),
 	SDT_BOOL(Patches, always_small_airport,          0,NN, false,        STR_CONFIG_PATCHES_SMALL_AIRPORTS,      NULL),
+	SDT_BOOL(Patches, enable_signal_gui,             S, 0, false,        STR_CONFIG_PATCHES_ENABLE_SIGNAL_GUI,   NULL),
 	 SDT_VAR(Patches, drag_signals_density,SLE_UINT8,S, 0,  4, 1, 20, 0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY,NULL),
 	 SDT_VAR(Patches, semaphore_build_before,SLE_INT32, S, NC, 1975, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE, NULL),
 	SDT_CONDVAR(Patches, town_layout, SLE_UINT8, 59, SL_MAX_VERSION, 0, MS, TL_ORIGINAL, TL_NO_ROADS, NUM_TLS - 1, 1, STR_CONFIG_PATCHES_TOWN_LAYOUT, CheckTownLayout),
--- a/src/settings_gui.cpp	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/settings_gui.cpp	Sat Dec 01 21:40:18 2007 +0000
@@ -669,6 +669,7 @@
 	"longbridges",
 	"signal_side",
 	"always_small_airport",
+	"enable_signal_gui",
 	"drag_signals_density",
 	"oil_refinery_limit",
 	"semaphore_build_before",
--- a/src/table/files.h	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/table/files.h	Sat Dec 01 21:40:18 2007 +0000
@@ -34,7 +34,7 @@
 	},
 	{ "SAMPLE.CAT",    {0x42, 0x2e, 0xa3, 0xdd, 0x07, 0x4d, 0x28, 0x59, 0xbb, 0x51, 0x63, 0x9a, 0x6e, 0x0e, 0x85, 0xda} },
 	{ "CHARS.GRF",     {0x5f, 0x2e, 0xbf, 0x05, 0xb6, 0x12, 0x65, 0x81, 0xd2, 0x10, 0xa9, 0x19, 0x62, 0x41, 0x70, 0x64} },
-	{ "OPENTTDD.GRF",  {0x4a, 0x9e, 0x0d, 0x41, 0x9e, 0x66, 0x13, 0x17, 0xb9, 0x61, 0x81, 0xc7, 0xca, 0xef, 0x6b, 0xdc} }
+	{ "OPENTTDD.GRF",  {0x7d, 0xd3, 0xc7, 0x29, 0x9b, 0x6c, 0x33, 0x57, 0x98, 0x89, 0x4c, 0x38, 0xf1, 0x2b, 0x3e, 0x13} }
 };
 
 static FileList files_win = {
@@ -48,5 +48,5 @@
 	},
 	{ "SAMPLE.CAT",    {0x92, 0x12, 0xe8, 0x1e, 0x72, 0xba, 0xdd, 0x4b, 0xbe, 0x1e, 0xae, 0xae, 0x66, 0x45, 0x8e, 0x10} },
 	{ "CHARS.GRF",     {0x5f, 0x2e, 0xbf, 0x05, 0xb6, 0x12, 0x65, 0x81, 0xd2, 0x10, 0xa9, 0x19, 0x62, 0x41, 0x70, 0x64} },
-	{ "OPENTTDW.GRF",  {0xe7, 0xe5, 0x4a, 0x12, 0x5c, 0xec, 0x46, 0x53, 0x1d, 0x37, 0x0a, 0xf4, 0x69, 0xf7, 0x4a, 0x9c} }
+	{ "OPENTTDW.GRF",  {0x44, 0x86, 0x24, 0x9f, 0x62, 0x0b, 0xa5, 0xab, 0x04, 0xf5, 0xbc, 0xe2, 0xfb, 0xf0, 0x62, 0x7e} }
 };
--- a/src/table/sprites.h	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/table/sprites.h	Sat Dec 01 21:40:18 2007 +0000
@@ -47,7 +47,7 @@
 
 	/* Extra graphic spritenumbers */
 	SPR_OPENTTD_BASE     = 4896,
-	OPENTTD_SPRITE_COUNT = 135,
+	OPENTTD_SPRITE_COUNT = 136,
 
 	/* Halftile-selection sprites */
 	SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE,
@@ -1263,6 +1263,16 @@
 	SPR_IMG_RAIL_STATION = 1298,
 	SPR_IMG_RAIL_SIGNALS = 1291,
 
+	SPR_IMG_SIGNAL_ELECTRIC_NORM   = 1287,
+	SPR_IMG_SIGNAL_ELECTRIC_ENTRY  = SPR_SIGNALS_BASE +  12,
+	SPR_IMG_SIGNAL_ELECTRIC_EXIT   = SPR_SIGNALS_BASE +  28,
+	SPR_IMG_SIGNAL_ELECTRIC_COMBO  = SPR_SIGNALS_BASE +  44,
+	SPR_IMG_SIGNAL_SEMAPHORE_NORM  = SPR_SIGNALS_BASE +  60,
+	SPR_IMG_SIGNAL_SEMAPHORE_ENTRY = SPR_SIGNALS_BASE +  76,
+	SPR_IMG_SIGNAL_SEMAPHORE_EXIT  = SPR_SIGNALS_BASE +  92,
+	SPR_IMG_SIGNAL_SEMAPHORE_COMBO = SPR_SIGNALS_BASE + 108,
+	SPR_IMG_SIGNAL_CONVERT         = SPR_OPENTTD_BASE + 135,
+
 	SPR_IMG_TUNNEL_RAIL   = 2430,
 	SPR_IMG_TUNNEL_MONO   = 2431,
 	SPR_IMG_TUNNEL_MAGLEV = 2432,
--- a/src/variables.h	Sat Dec 01 14:04:16 2007 +0000
+++ b/src/variables.h	Sat Dec 01 21:40:18 2007 +0000
@@ -247,6 +247,8 @@
 
 	bool exclusive_rights;   ///< allow buying exclusive rights
 	bool give_money;         ///< allow giving other players money
+
+	bool enable_signal_gui;  ///< Show the signal GUI when the signal button is pressed
 };
 
 VARDEF Patches _patches;