Implement reset, copy, share and unshare of trace restrict programs.

Add documentation on data storage model, wrt lookup, mapping and sharing.
pull/3/head
Jonathan G Rennison 9 years ago
parent 5f1b148cf9
commit 627288277e

@ -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

@ -19,7 +19,40 @@
#include "pathfinder/yapf/yapf_cache.h"
#include <vector>
/** 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,35 +386,51 @@ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommand
DoCommandP(tile, p1, value, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg));
}
static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track)
{
// Check that there actually is a signal here
if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
if (!HasSignalOnTrack(tile, track)) {
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)
{
Track track = static_cast<Track>(GB(p1, 0, 3));
TraceRestrictDoCommandType type = static_cast<TraceRestrictDoCommandType>(GB(p1, 3, 5));
if (type >= TRDCT_PROG_COPY) {
return CmdProgramSignalTraceRestrictProgMgmt(tile, flags, p1, p2, text);
}
Track track = static_cast<Track>(GB(p1, 0, 3));
uint32 offset = GB(p1, 8, 16);
TraceRestrictItem item = static_cast<TraceRestrictItem>(p2);
// Check tile ownership
CommandCost ret = CheckTileOwnership(tile);
CommandCost ret = TraceRestrictCheckTileIsUsable(tile, track);
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);
}
if (!HasSignalOnTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
}
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<TraceRestrictDoCommandType>(GB(p1, 3, 5));
Track track = static_cast<Track>(GB(p1, 0, 3));
Track source_track = static_cast<Track>(GB(p1, 8, 3));
TileIndex source_tile = static_cast<TileIndex>(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<TraceRestrictItem> 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();
}

@ -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<TileIndex>(0), static_cast<Track>(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);

@ -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<int, const TraceRestrictDropDownListSet *> 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<TraceRestrictItem>(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<NWidgetStacked>(TR_WIDGET_SEL_TOP_LEFT);
NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_MIDDLE);
NWidgetStacked *right_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_RIGHT);
NWidgetStacked *share_sel = this->GetWidget<NWidgetStacked>(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);
const TraceRestrictProgram *prog = this->GetProgram();
this->GetWidget<NWidgetCore>(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;
}
// Don't allow modifications if don't own, or have selected invalid instruction
if (this->GetOwner() != _local_company || this->selected_instruction < 1) {
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->GetSelected();
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(),

Loading…
Cancel
Save