diff --git a/src/lang/english.txt b/src/lang/english.txt index 8b690d96d9..4efc7db0ce 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2396,14 +2396,23 @@ STR_TRACE_RESTRICT_PF_ALLOW_LONG :Allow (cancel p STR_TRACE_RESTRICT_PF_PENALTY :Penalty STR_TRACE_RESTRICT_VALUE_CAPTION :{WHITE}Value STR_TRACE_RESTRICT_CAPTION :{WHITE}Routefinding restriction +STR_TRACE_RESTRICT_CAPTION_SHARED :{WHITE}Routefinding restriction - shared by {COMMA} signals STR_TRACE_RESTRICT_TYPE_TOOLTIP :{BLACK}Type STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP :{BLACK}Comparison operator STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP :{BLACK}Value STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP :{BLACK}Go to signal STR_TRACE_RESTRICT_INSERT :{BLACK}Insert STR_TRACE_RESTRICT_REMOVE :{BLACK}Remove +STR_TRACE_RESTRICT_RESET :{BLACK}Reset +STR_TRACE_RESTRICT_COPY :{BLACK}Copy +STR_TRACE_RESTRICT_SHARE :{BLACK}Share +STR_TRACE_RESTRICT_UNSHARE :{BLACK}Unshare STR_TRACE_RESTRICT_INSERT_TOOLTIP :{BLACK}Insert an instruction STR_TRACE_RESTRICT_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction +STR_TRACE_RESTRICT_RESET_TOOLTIP :{BLACK}Reset the current signal (without affecting shared programs) +STR_TRACE_RESTRICT_COPY_TOOLTIP :{BLACK}Copy program from another signal +STR_TRACE_RESTRICT_SHARE_TOOLTIP :{BLACK}Share program with another signal +STR_TRACE_RESTRICT_UNSHARE_TOOLTIP :{BLACK}Stop sharing program with other signals, create a copy of the program STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP :{BLACK}Routefinding restriction STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't insert instruction STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM :{WHITE}Can't modify instruction @@ -2417,6 +2426,11 @@ STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK :Validation fail STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF :Validation failed: else/endif without opening if STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE :Validation failed: duplicate else STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF :Validation failed: else if without opening if +STR_TRACE_RESTRICT_ERROR_SOURCE_SAME_AS_TARGET :Source and target signals are the same +STR_TRACE_RESTRICT_ERROR_CAN_T_RESET_SIGNAL :{WHITE}Can't reset signal +STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM :{WHITE}Can't copy program +STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM :{WHITE}Can't share program +STR_TRACE_RESTRICT_ERROR_CAN_T_UNSHARE_PROGRAM :{WHITE}Can't unshare program # Bridge selection window STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Select Rail Bridge diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 74b182b4f0..1f8bea6be5 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -19,7 +19,40 @@ #include "pathfinder/yapf/yapf_cache.h" #include -/** Initialize theprogram pool */ +/** Trace Restrict Data Storage Model Notes: + * + * Signals may have 0, 1 or 2 trace restrict programs attached to them, + * up to one for each track. Two-way signals share the same program. + * + * The mapping between signals and programs is defined in terms of + * TraceRestrictRefId to TraceRestrictProgramID, + * where TraceRestrictRefId is formed of the tile index and track, + * and TraceRestrictProgramID is an index into the program pool. + * + * If one or more mappings exist for a given signal tile, bit 12 of M3 will be set to 1. + * This is updated whenever mappings are added/removed for that tile. This is to avoid + * needing to do a mapping lookup for the common case where there is no trace restrict + * program mapping for the given tile. + * + * Programs in the program pool are refcounted based on the number of mappings which exist. + * When this falls to 0, the program is deleted from the pool. + * If a program has a refcount greater than 1, it is a shared program. + * + * In all cases, an empty program is evaluated the same as the absence of a program. + * Therefore it is not necessary to store mappings to empty unshared programs. + * Any editing action which would otherwise result in a mapping to an empty program + * which has no other references, instead removes the mapping. + * This is not done for shared programs as this would delete the shared aspect whenever + * the program became empty. + * + * Empty programs with a refcount of 1 may still exist due to the edge case where: + * 1: There is an empty program with refcount 2 + * 2: One of the two mappings is deleted + * Finding the other mapping would entail a linear search of the mappings, and there is little + * to be gained by doing so. + */ + +/** Initialize the program pool */ TraceRestrictProgramPool _tracerestrictprogram_pool("TraceRestrictProgram"); INSTANTIATE_POOL_METHODS(TraceRestrictProgram) @@ -353,27 +386,8 @@ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommand DoCommandP(tile, p1, value, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg)); } -/** - * The main command for editing a signal tracerestrict program. - * @param tile The tile which contains the signal. - * @param flags Internal command handler stuff. - * @param p1 Bitstuffed items - * @param p2 Item, for insert and modify operations - * @return the cost of this operation (which is free), or an error - */ -CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track) { - Track track = static_cast(GB(p1, 0, 3)); - TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); - uint32 offset = GB(p1, 8, 16); - TraceRestrictItem item = static_cast(p2); - - // Check tile ownership - CommandCost ret = CheckTileOwnership(tile); - if (ret.Failed()) { - return ret; - } - // Check that there actually is a signal here if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) { return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); @@ -382,6 +396,41 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); } + // Check tile ownership, do this afterwards to avoid tripping up on house/industry tiles + CommandCost ret = CheckTileOwnership(tile); + if (ret.Failed()) { + return ret; + } + + return CommandCost(); +} + +/** + * The main command for editing a signal tracerestrict program. + * @param tile The tile which contains the signal. + * @param flags Internal command handler stuff. + * Below apply for instruction modification actions only + * @param p1 Bitstuffed items + * @param p2 Item, for insert and modify operations + * @return the cost of this operation (which is free), or an error + */ +CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); + + if (type >= TRDCT_PROG_COPY) { + return CmdProgramSignalTraceRestrictProgMgmt(tile, flags, p1, p2, text); + } + + Track track = static_cast(GB(p1, 0, 3)); + uint32 offset = GB(p1, 8, 16); + TraceRestrictItem item = static_cast(p2); + + CommandCost ret = TraceRestrictCheckTileIsUsable(tile, track); + if (ret.Failed()) { + return ret; + } + bool can_make_new = (type == TRDCT_INSERT_ITEM) && (flags & DC_EXEC); bool need_existing = (type != TRDCT_INSERT_ITEM); TraceRestrictProgram *prog = GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), can_make_new); @@ -508,3 +557,120 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u return CommandCost(); } + +void TraceRestrictProgMgmtWithSourceDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, + TileIndex source_tile, Track source_track, StringID error_msg) +{ + uint32 p1 = 0; + SB(p1, 0, 3, track); + SB(p1, 3, 5, type); + SB(p1, 8, 3, source_track); + DoCommandP(tile, p1, source_tile, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg)); +} + +/** + * Sub command for copy/share/unshare operations on signal tracerestrict programs. + * @param tile The tile which contains the signal. + * @param flags Internal command handler stuff. + * @param p1 Bitstuffed items + * @param p2 Source tile, for share/copy operations + * @return the cost of this operation (which is free), or an error + */ +CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); + Track track = static_cast(GB(p1, 0, 3)); + Track source_track = static_cast(GB(p1, 8, 3)); + TileIndex source_tile = static_cast(p2); + + TraceRestrictRefId self = MakeTraceRestrictRefId(tile, track); + TraceRestrictRefId source = MakeTraceRestrictRefId(source_tile, source_track); + + assert(type >= TRDCT_PROG_COPY); + + CommandCost ret = TraceRestrictCheckTileIsUsable(tile, track); + if (ret.Failed()) { + return ret; + } + + if (type == TRDCT_PROG_SHARE || type == TRDCT_PROG_COPY) { + if (self == source) { + return_cmd_error(STR_TRACE_RESTRICT_ERROR_SOURCE_SAME_AS_TARGET); + } + + ret = TraceRestrictCheckTileIsUsable(source_tile, source_track); + if (ret.Failed()) { + return ret; + } + } + + if (!(flags & DC_EXEC)) { + return CommandCost(); + } + + switch (type) { + case TRDCT_PROG_COPY: { + TraceRestrictRemoveProgramMapping(self); + TraceRestrictProgram *prog = GetTraceRestrictProgram(self, true); + if (!prog) { + // allocation failed + return CMD_ERROR; + } + + TraceRestrictProgram *source_prog = GetTraceRestrictProgram(source, false); + if (source_prog) { + prog->items = source_prog->items; // copy + } + break; + } + + case TRDCT_PROG_SHARE: { + TraceRestrictRemoveProgramMapping(self); + TraceRestrictProgram *source_prog = GetTraceRestrictProgram(source, true); + if (!source_prog) { + // allocation failed + return CMD_ERROR; + } + + TraceRestrictCreateProgramMapping(self, source_prog); + break; + } + + case TRDCT_PROG_UNSHARE: { + std::vector items; + TraceRestrictProgram *prog = GetTraceRestrictProgram(self, false); + if (prog) { + // copy program into temporary + items = prog->items; + } + // remove old program + TraceRestrictRemoveProgramMapping(self); + + if (items.size()) { + // if prog is non-empty, create new program and move temporary in + TraceRestrictProgram *new_prog = GetTraceRestrictProgram(self, true); + if (!new_prog) { + // allocation failed + return CMD_ERROR; + } + + new_prog->items.swap(items); + } + break; + } + + case TRDCT_PROG_RESET: { + TraceRestrictRemoveProgramMapping(self); + break; + } + + default: + NOT_REACHED(); + break; + } + + // update windows + InvalidateWindowClassesData(WC_TRACE_RESTRICT); + + return CommandCost(); +} diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 1309220669..43dd55c6d8 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -269,15 +269,30 @@ static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIn } } +// do not re-order enum TraceRestrictDoCommandType { TRDCT_INSERT_ITEM = 0, TRDCT_MODIFY_ITEM = 1, TRDCT_REMOVE_ITEM = 2, + + TRDCT_PROG_COPY = 3, + TRDCT_PROG_SHARE = 4, + TRDCT_PROG_UNSHARE = 5, + TRDCT_PROG_RESET = 6, }; void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg); +void TraceRestrictProgMgmtWithSourceDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, + TileIndex source_tile, Track source_track, StringID error_msg); + +inline void TraceRestrictProgMgmtDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, StringID error_msg) +{ + TraceRestrictProgMgmtWithSourceDoCommandP(tile, track, type, static_cast(0), static_cast(0), error_msg); +} + CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); +CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 1cfd46ccc5..55c6b39d39 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -18,6 +18,7 @@ #include "viewport_func.h" #include "textbuf_gui.h" #include "company_func.h" +#include "tilehighlight_func.h" #include "widgets/dropdown_func.h" #include "gui.h" #include "gfx_func.h" @@ -34,6 +35,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_SEL_TOP_LEFT, TR_WIDGET_SEL_TOP_MIDDLE, TR_WIDGET_SEL_TOP_RIGHT, + TR_WIDGET_SEL_SHARE, TR_WIDGET_TYPE, TR_WIDGET_COMPARATOR, @@ -47,6 +49,10 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_GOTO_SIGNAL, TR_WIDGET_INSERT, TR_WIDGET_REMOVE, + TR_WIDGET_RESET, + TR_WIDGET_COPY, + TR_WIDGET_SHARE, + TR_WIDGET_UNSHARE, }; enum PanelWidgets { @@ -62,6 +68,10 @@ enum PanelWidgets { DPR_VALUE_INT = 0, DPR_VALUE_DROPDOWN, DPR_BLANK, + + // Share + DPS_SHARE = 0, + DPS_UNSHARE, }; /// value_array *must* be at least as long as string_array, @@ -293,6 +303,7 @@ class TraceRestrictWindow: public Window { Scrollbar *vscroll; std::map drop_down_list_mapping; TraceRestrictItem expecting_inserted_item; + int current_placement_widget; public: TraceRestrictWindow(WindowDesc *desc, TileIndex tile, Track track) @@ -302,6 +313,7 @@ public: this->track = track; this->selected_instruction = -1; this->expecting_inserted_item = static_cast(0); + this->current_placement_widget = -1; this->CreateNestedTree(); this->vscroll = this->GetScrollbar(TR_WIDGET_SCROLLBAR); @@ -389,6 +401,21 @@ public: case TR_WIDGET_GOTO_SIGNAL: ScrollMainWindowToTile(this->tile); break; + + case TR_WIDGET_RESET: { + TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_RESET, STR_TRACE_RESTRICT_ERROR_CAN_T_RESET_SIGNAL); + break; + } + + case TR_WIDGET_COPY: + case TR_WIDGET_SHARE: + SelectSignalAction(widget); + break; + + case TR_WIDGET_UNSHARE: { + TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_UNSHARE, STR_TRACE_RESTRICT_ERROR_CAN_T_UNSHARE_PROGRAM); + break; + } } } @@ -457,6 +484,72 @@ public: } } + virtual void OnPlaceObject(Point pt, TileIndex source_tile) + { + int widget = this->current_placement_widget; + this->current_placement_widget = -1; + + this->RaiseButtons(); + ResetObjectToPlace(); + + if (widget < 0) { + return; + } + + int error_message = (widget == TR_WIDGET_COPY) ? STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM : STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM; + + if (!IsPlainRailTile(source_tile)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(source_tile, TRANSPORT_RAIL, 0)); + if (trackbits & TRACK_BIT_VERT) { // N-S direction + trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT; + } + + if (trackbits & TRACK_BIT_HORZ) { // E-W direction + trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER; + } + Track source_track = FindFirstTrack(trackbits); + if(source_track == INVALID_TRACK) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + if (!HasTrack(source_tile, source_track)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + if (!HasSignalOnTrack(source_tile, source_track)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO); + return; + } + + switch (widget) { + case TR_WIDGET_COPY: + TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_COPY, + source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM); + break; + + case TR_WIDGET_SHARE: + TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_SHARE, + source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM); + break; + + default: + NOT_REACHED(); + break; + } + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + this->current_placement_widget = -1; + } + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { @@ -531,7 +624,18 @@ public: if (GetTraceRestrictTypeProperties(item).value_type == TRVT_INT) { SetDParam(0, GetTraceRestrictValue(item)); } - } break; + break; + } + + case TR_WIDGET_CAPTION: { + const TraceRestrictProgram *prog = this->GetProgram(); + if (prog) { + SetDParam(0, prog->refcount); + } else { + SetDParam(0, 1); + } + break; + } } } @@ -646,6 +750,7 @@ private: NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); NWidgetStacked *middle_sel = this->GetWidget(TR_WIDGET_SEL_TOP_MIDDLE); NWidgetStacked *right_sel = this->GetWidget(TR_WIDGET_SEL_TOP_RIGHT); + NWidgetStacked *share_sel = this->GetWidget(TR_WIDGET_SEL_SHARE); this->DisableWidget(TR_WIDGET_TYPE); this->DisableWidget(TR_WIDGET_COMPARATOR); @@ -654,6 +759,10 @@ private: this->DisableWidget(TR_WIDGET_INSERT); this->DisableWidget(TR_WIDGET_REMOVE); + this->DisableWidget(TR_WIDGET_RESET); + this->DisableWidget(TR_WIDGET_COPY); + this->DisableWidget(TR_WIDGET_SHARE); + this->DisableWidget(TR_WIDGET_UNSHARE); this->DisableWidget(TR_WIDGET_BLANK_L); this->DisableWidget(TR_WIDGET_BLANK_M); @@ -662,14 +771,40 @@ private: left_sel->SetDisplayedPlane(DPL_BLANK); middle_sel->SetDisplayedPlane(DPM_BLANK); right_sel->SetDisplayedPlane(DPR_BLANK); + share_sel->SetDisplayedPlane(DPS_SHARE); - // Don't allow modifications if don't own, or have selected invalid instruction - if (this->GetOwner() != _local_company || this->selected_instruction < 1) { + const TraceRestrictProgram *prog = this->GetProgram(); + + this->GetWidget(TR_WIDGET_CAPTION)->widget_data = + (prog && prog->refcount > 1) ? STR_TRACE_RESTRICT_CAPTION_SHARED : STR_TRACE_RESTRICT_CAPTION; + + // Don't allow modifications if don't own + if (this->GetOwner() != _local_company) { this->SetDirty(); return; } - TraceRestrictItem item = this->GetSelected(); + if (prog && prog->refcount > 1) { + // program is shared, show and enable unshare button, and reset button + share_sel->SetDisplayedPlane(DPS_UNSHARE); + this->EnableWidget(TR_WIDGET_UNSHARE); + this->EnableWidget(TR_WIDGET_RESET); + } else if (this->GetItemCount(prog) > 2) { + // program is non-empty and not shared, enable reset button + this->EnableWidget(TR_WIDGET_RESET); + } else { + // program is empty and not shared, show copy and share buttons + this->EnableWidget(TR_WIDGET_COPY); + this->EnableWidget(TR_WIDGET_SHARE); + } + + // haven't selected instruction + if (this->selected_instruction < 1) { + this->SetDirty(); + return; + } + + TraceRestrictItem item = this->GetItem(prog, this->selected_instruction); if (item != 0) { if (GetTraceRestrictType(item) == TRIT_NULL) { switch (GetTraceRestrictValue(item)) { @@ -736,6 +871,19 @@ private: int selected = GetDropDownListIndexByValue(list_set, value, missing_ok); ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width); } + + void SelectSignalAction(int widget) + { + this->ToggleWidgetLoweredState(widget); + this->SetWidgetDirty(widget); + if (this->IsWidgetLowered(widget)) { + SetObjectToPlaceWnd(ANIMCURSOR_BUILDSIGNALS, PAL_NONE, HT_RECT, this); + this->current_placement_widget = widget; + } else { + ResetObjectToPlace(); + this->current_placement_widget = -1; + } + } }; static const NWidgetPart _nested_program_widgets[] = { @@ -785,8 +933,18 @@ static const NWidgetPart _nested_program_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_INSERT, STR_TRACE_RESTRICT_INSERT_TOOLTIP), SetResize(1, 0), - NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(186, 12), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_REMOVE, STR_TRACE_RESTRICT_REMOVE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_RESET), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_RESET, STR_TRACE_RESTRICT_RESET_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_COPY), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_COPY, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0), + NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_SHARE), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_SHARE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_SHARE, STR_TRACE_RESTRICT_SHARE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_UNSHARE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_UNSHARE, STR_TRACE_RESTRICT_UNSHARE_TOOLTIP), SetResize(1, 0), + EndContainer(), EndContainer(), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(),