# programmable_signals_gui.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "programmable_signals.h"
    00014 #include "command_func.h"
    00015 #include "window_func.h"
    00016 #include "strings_func.h"
    00017 #include "string_func.h"
    00018 #include "viewport_func.h"
    00019 #include "textbuf_gui.h"
    00020 #include "company_func.h"
    00021 #include "widgets/dropdown_func.h"
    00022 #include "gui.h"
    00023 #include "gfx_func.h"
    00024 #include "tilehighlight_func.h"
    00025 #include "rail_map.h"
    00026 #include "tile_cmd.h"
    00027
    00028 #include "table/sprites.h"
    00029 #include "table/strings.h"
    00030
    00031 enum ProgramWindowWidgets {
    00032   PROGRAM_WIDGET_CAPTION,
    00033   PROGRAM_WIDGET_INSTRUCTION_LIST,
    00034   PROGRAM_WIDGET_SCROLLBAR,
    00035
    00036   PROGRAM_WIDGET_SEL_TOP_LEFT,
    00037   PROGRAM_WIDGET_SEL_TOP_MIDDLE,
    00038   PROGRAM_WIDGET_SEL_TOP_RIGHT,
    00039
    00040   PROGRAM_WIDGET_SET_STATE,
    00041   PROGRAM_WIDGET_COND_VARIABLE,
    00042   PROGRAM_WIDGET_COND_COMPARATOR,
    00043   PROGRAM_WIDGET_COND_VALUE,
    00044   PROGRAM_WIDGET_COND_GOTO_SIGNAL,
    00045   PROGRAM_WIDGET_COND_SET_SIGNAL,
    00046
    00047   PROGRAM_WIDGET_GOTO_SIGNAL,
    00048   PROGRAM_WIDGET_INSERT,
    00049   PROGRAM_WIDGET_REMOVE,
    00050 };
    00051
    00052 enum PanelWidgets {
    00053   /* Left. */
    00054   DPL_COND_VARIABLE = 0,
    00055   DPL_SET_STATE,
    00056
    00057   /* Middle. */
    00058   DPM_COND_COMPARATOR = 0,
    00059   DPM_COND_GOTO_SIGNAL,
    00060
    00061   /* Right. */
    00062   DPR_COND_VALUE = 0,
    00063   DPR_COND_SET_SIGNAL
    00064 };
    00065
    00066 static const StringID _program_insert[] = {
    00067   STR_PROGSIG_INSERT_IF,
    00068   STR_PROGSIG_INSERT_SET_SIGNAL,
    00069   INVALID_STRING_ID
    00070 };
    00071
    00072 static SignalOpcode OpcodeForIndex(int index)
    00073 {
    00074   switch (index) {
    00075     case 0: return PSO_IF;
    00076     case 1: return PSO_SET_SIGNAL;
    00077     default: NOT_REACHED();
    00078   }
    00079 }
    00080
    00081 static bool IsConditionComparator(SignalCondition *cond)
    00082 {
    00083   switch (cond->ConditionCode()) {
    00084     case PSC_NUM_GREEN:
    00085     case PSC_NUM_RED:
    00086       return true;
    00087
    00088     default:
    00089       return false;
    00090   }
    00091 }
    00092
    00093 static const StringID _program_condvar[] = {
    00094   STR_PROGSIG_COND_ALWAYS,       // PSC_ALWAYS
    00095   STR_PROGSIG_COND_NEVER,        // PSC_NEVER
    00096   STR_PROGSIG_CONDVAR_NUM_GREEN, // PSC_NUM_GREEN
    00097   STR_PROGSIG_CONDVAR_NUM_RED,   // PSC_NUM_RED
    00098   STR_PROGSIG_COND_SIGNAL_STATE, // PSC_SIGNAL_STATE
    00099   INVALID_STRING_ID
    00100 };
    00101
    00102 // TODO: These should probably lose the ORDER
    00103 static const StringID _program_comparator[] = {
    00104   STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS,      // SGC_EQUALS
    00105   STR_ORDER_CONDITIONAL_COMPARATOR_NOT_EQUALS,  // SGC_NOT_EQUALS
    00106   STR_ORDER_CONDITIONAL_COMPARATOR_LESS_THAN,   // SGC_LESS_THAN
    00107   STR_ORDER_CONDITIONAL_COMPARATOR_LESS_EQUALS, // SGC_LESS_THAN_EQUALS
    00108   STR_ORDER_CONDITIONAL_COMPARATOR_MORE_THAN,   // SGC_MORE_THAN
    00109   STR_ORDER_CONDITIONAL_COMPARATOR_MORE_EQUALS, // SGC_MORE_THAN_EQUALS
    00110   STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE,     // SGC_IS_TRUE
    00111   STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE,    // SGC_IS_FALSE
    00112   INVALID_STRING_ID
    00113 };
    00114
    00115 static const StringID _program_sigstate[] = {
    00116   STR_COLOUR_RED,
    00117   STR_COLOUR_GREEN,
    00118   INVALID_STRING_ID
    00119 };
    00120
    00122 static char *GetConditionString(SignalCondition *cond, char *buf, char *buflast, bool selected)
    00123 {
    00124   StringID string = INVALID_STRING_ID;
    00125   bool comparator = IsConditionComparator(cond);
    00126
    00127   if (comparator) {
    00128     SignalVariableCondition *cv = static_cast<SignalVariableCondition*>(cond);
    00129     string = STR_PROGSIG_COND_COMPARE;
    00130     SetDParam(0, _program_condvar[cond->ConditionCode()]);
    00131     SetDParam(1, _program_comparator[cv->comparator]);
    00132     SetDParam(2, cv->value);
    00133   } else {
    00134     string = _program_condvar[cond->ConditionCode()];
    00135     if (cond->ConditionCode() == PSC_SIGNAL_STATE) {
    00136       string = STR_PROGSIG_CONDVAR_SIGNAL_STATE;
    00137       SetDParam(0, static_cast<SignalStateCondition*>(cond)->IsSignalValid()
    00138         ? STR_PROGSIG_CONDVAR_SIGNAL_STATE_SPECIFIED : STR_PROGSIG_CONDVAR_SIGNAL_STATE_UNSPECIFIED);
    00139       SetDParam(1, selected ? STR_WHITE : STR_BLACK);
    00140     }
    00141   }
    00142   return GetString(buf, string, buflast);
    00143 }
    00144
    00154 static void DrawInstructionString(SignalInstruction *instruction, int y, bool selected, int indent, int left, int right)
    00155 {
    00156   StringID instruction_string = INVALID_STRING_ID;
    00157
    00158   char condstr[512];
    00159
    00160   switch (instruction->Opcode()) {
    00161     case PSO_FIRST:
    00162       instruction_string = STR_PROGSIG_FIRST;
    00163       break;
    00164
    00165     case PSO_LAST:
    00166       instruction_string = STR_PROGSIG_LAST;
    00167       break;
    00168
    00169     case PSO_IF: {
    00170       SignalIf *if_ins = static_cast<SignalIf*>(instruction);
    00171       GetConditionString(if_ins->condition, condstr, lastof(condstr), selected);
    00172       SetDParamStr(0, condstr);
    00173       instruction_string = STR_PROGSIG_IF;
    00174       break;
    00175     }
    00176
    00177     case PSO_IF_ELSE:
    00178       instruction_string = STR_PROGSIG_ELSE;
    00179       break;
    00180
    00181     case PSO_IF_ENDIF:
    00182       instruction_string = STR_PROGSIG_ENDIF;
    00183       break;
    00184
    00185     case PSO_SET_SIGNAL: {
    00186       instruction_string = STR_PROGSIG_SET_SIGNAL;
    00187       SignalSet *set = static_cast<SignalSet*>(instruction);
    00188       SetDParam(0, _program_sigstate[set->to_state]);
    00189       break;
    00190     }
    00191
    00192     default: NOT_REACHED();
    00193   }
    00194
    00195   DrawString(left + indent * 16, right, y, instruction_string, selected ? TC_WHITE : TC_BLACK);
    00196 }
    00197
    00198 struct GuiInstruction {
    00199   SignalInstruction *insn;
    00200   uint indent;
    00201 };
    00202
    00203 typedef SmallVector<GuiInstruction, 4> GuiInstructionList;
    00204
    00205 class ProgramWindow: public Window {
    00206 public:
    00207   ProgramWindow(const WindowDesc *desc, SignalReference ref)
    00208   {
    00209     this->tile = ref.tile;
    00210     this->track = ref.track;
    00211     this->selected_instruction = -1;
    00212
    00213     this->CreateNestedTree(desc);
    00214     this->vscroll = this->GetScrollbar(PROGRAM_WIDGET_SCROLLBAR);
    00215     this->FinishInitNested(desc, (ref.tile << 3) | ref.track);
    00216
    00217     program = GetSignalProgram(ref);
    00218     RebuildInstructionList();
    00219   }
    00220
    00221   virtual void OnClick(Point pt, int widget, int click_count)
    00222   {
    00223     switch (widget) {
    00224       case PROGRAM_WIDGET_INSTRUCTION_LIST: {
    00225         int sel = this->GetInstructionFromPt(pt.y);
    00226
    00227         this->DeleteChildWindows();
    00228         HideDropDownMenu(this);
    00229
    00230         if (sel == -1 || this->GetOwner() != _local_company) {
    00231           /* Deselect. */
    00232           this->selected_instruction = -1;
    00233         } else {
    00234           this->selected_instruction = sel;
    00235         }
    00236
    00237         this->UpdateButtonState();
    00238       } break;
    00239
    00240       case PROGRAM_WIDGET_INSERT: {
    00241         DEBUG(misc, 5, "Selection is %d", this->selected_instruction);
    00242         if (this->GetOwner() != _local_company || this->selected_instruction < 1)
    00243           return;
    00244         ShowDropDownMenu(this, _program_insert, -1, PROGRAM_WIDGET_INSERT, 0, 0, 0);
    00245       } break;
    00246
    00247       case PROGRAM_WIDGET_REMOVE: {
    00248         SignalInstruction *ins = GetSelected();
    00249         if (this->GetOwner() != _local_company || !ins)
    00250           return;
    00251
    00252         uint32 p1 = 0;
    00253         SB(p1, 0, 3, this->track);
    00254         SB(p1, 3, 16, ins->Id());
    00255
    00256         DoCommandP(this->tile, p1, 0, CMD_REMOVE_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
    00257       } break;
    00258
    00259       case PROGRAM_WIDGET_SET_STATE: {
    00260         SignalInstruction *si = this->GetSelected();
    00261         if (!si || si->Opcode() != PSO_SET_SIGNAL) return;
    00262         SignalSet *ss = static_cast <SignalSet*>(si);
    00263
    00264         ShowDropDownMenu(this, _program_sigstate, ss->to_state, PROGRAM_WIDGET_SET_STATE, 0, 0, 0);
    00265       } break;
    00266
    00267       case PROGRAM_WIDGET_COND_VARIABLE: {
    00268         SignalInstruction *si = this->GetSelected();
    00269         if (!si || si->Opcode() != PSO_IF) return;
    00270         SignalIf *sif = static_cast <SignalIf*>(si);
    00271
    00272         ShowDropDownMenu(this, _program_condvar, sif->condition->ConditionCode(), PROGRAM_WIDGET_COND_VARIABLE, 0, 0, 0);
    00273       } break;
    00274
    00275       case PROGRAM_WIDGET_COND_COMPARATOR: {
    00276         SignalInstruction *si = this->GetSelected();
    00277         if (!si || si->Opcode() != PSO_IF) return;
    00278         SignalIf *sif = static_cast <SignalIf*>(si);
    00279         if (!IsConditionComparator(sif->condition)) return;
    00280         SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(sif->condition);
    00281
    00282         ShowDropDownMenu(this, _program_comparator, vc->comparator, PROGRAM_WIDGET_COND_COMPARATOR, 0, 0, 0);
    00283       } break;
    00284
    00285       case PROGRAM_WIDGET_COND_VALUE: {
    00286         SignalInstruction *si = this->GetSelected();
    00287         if (!si || si->Opcode() != PSO_IF) return;
    00288         SignalIf *sif = static_cast <SignalIf*>(si);
    00289         if (!IsConditionComparator(sif->condition)) return;
    00290         SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(sif->condition);
    00291
    00292         SetDParam(0, vc->value);
    00293         ShowQueryString(STR_JUST_INT, STR_PROGSIG_CONDITION_VALUE_CAPT, 5, this, CS_NUMERAL, QSF_NONE);
    00294       } break;
    00295
    00296       case PROGRAM_WIDGET_COND_GOTO_SIGNAL: {
    00297         SignalInstruction *si = this->GetSelected();
    00298         if (!si || si->Opcode() != PSO_IF) return;
    00299         SignalIf *sif = static_cast <SignalIf*>(si);
    00300         if (sif->condition->ConditionCode() != PSC_SIGNAL_STATE) return;
    00301         SignalStateCondition *sc = static_cast<SignalStateCondition*>(sif->condition);
    00302
    00303         if (sc->IsSignalValid()) {
    00304           ScrollMainWindowToTile(sc->sig_tile);
    00305         } else {
    00306           ShowErrorMessage(STR_ERROR_CAN_T_GOTO_UNDEFINED_SIGNAL, STR_EMPTY, WL_INFO);
    00307         }
    00308         this->RaiseWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
    00309       } break;
    00310
    00311       case PROGRAM_WIDGET_COND_SET_SIGNAL: {
    00312         this->SetWidgetDirty(PROGRAM_WIDGET_COND_SET_SIGNAL);
    00313         if (this->IsWidgetLowered(PROGRAM_WIDGET_COND_SET_SIGNAL)) {
    00314           SetObjectToPlaceWnd(ANIMCURSOR_BUILDSIGNALS, PAL_NONE, HT_RECT, this);
    00315         } else {
    00316           ResetObjectToPlace();
    00317         }
    00318       } break;
    00319
    00320       case PROGRAM_WIDGET_GOTO_SIGNAL: {
    00321         ScrollMainWindowToTile(this->tile);
    00322         this->RaiseWidget(PROGRAM_WIDGET_GOTO_SIGNAL);
    00323       } break;
    00324     }
    00325   }
    00326
    00327   virtual void OnPlaceObject(Point pt, TileIndex tile)
    00328   {
    00329     SignalInstruction *si = this->GetSelected();
    00330     if (!si || si->Opcode() != PSO_IF) return;
    00331     SignalIf *sif = static_cast <SignalIf*>(si);
    00332     if (sif->condition->ConditionCode() != PSC_SIGNAL_STATE) return;
    00333
    00334     if (!IsPlainRailTile(tile)) {
    00335       return;
    00336     }
    00337
    00338     TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0));
    00339     if (trackbits & TRACK_BIT_VERT) { // N-S direction.
    00340       trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
    00341     }
    00342
    00343     if (trackbits & TRACK_BIT_HORZ) { // E-W direction.
    00344       trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
    00345     }
    00346     Track track = FindFirstTrack(trackbits);
    00347
    00348     if (track == INVALID_TRACK) {
    00349       return;
    00350     }
    00351
    00352     Trackdir td = TrackToTrackdir(track);
    00353     Trackdir tdr = ReverseTrackdir(td);
    00354
    00355     if (HasSignalOnTrackdir(tile, td) && HasSignalOnTrackdir(tile, tdr)) {
    00356       ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_CAN_T_DEPEND_UPON_BIDIRECTIONAL_SIGNALS, WL_INFO);
    00357       return;
    00358     } else if (HasSignalOnTrackdir(tile, tdr) && !HasSignalOnTrackdir(tile, td)) {
    00359       td = tdr;
    00360     }
    00361
    00362     if (!HasSignalOnTrackdir(tile, td)) {
    00363       return;
    00364     }
    00365
    00366     if (!IsPresignalExit(tile, track)) {
    00367       ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_NOT_AN_EXIT_SIGNAL, WL_INFO);
    00368       return;
    00369     }
    00370
    00371     uint32 p1 = 0, p2 = 0;
    00372     SB(p1, 0, 3, this->track);
    00373     SB(p1, 3, 16, si->Id());
    00374
    00375     SB(p2, 0, 1, 1);
    00376     SB(p2, 1, 5,  td);
    00377     SB(p2, 5, 27, tile);
    00378
    00379     DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
    00380     ResetObjectToPlace();
    00381     this->RaiseWidget(PROGRAM_WIDGET_COND_SET_SIGNAL);
    00382   }
    00383
    00384   virtual void OnQueryTextFinished(char *str)
    00385   {
    00386     if (!StrEmpty(str)) {
    00387       SignalInstruction *si = this->GetSelected();
    00388       if (!si || si->Opcode() != PSO_IF) return;
    00389       SignalIf *sif = static_cast <SignalIf*>(si);
    00390       if (!IsConditionComparator(sif->condition)) return;
    00391
    00392       uint value = atoi(str);
    00393
    00394       uint32 p1 = 0, p2 = 0;
    00395       SB(p1, 0, 3, this->track);
    00396       SB(p1, 3, 16, si->Id());
    00397
    00398       SB(p2, 0, 1, 1);
    00399       SB(p2, 1, 2, SCF_VALUE);
    00400       SB(p2, 3, 27, value);
    00401
    00402       DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
    00403     }
    00404   }
    00405
    00406   virtual void OnDropdownSelect(int widget, int index)
    00407   {
    00408     SignalInstruction *ins = this->GetSelected();
    00409     if (!ins) return;
    00410
    00411     switch (widget) {
    00412       case PROGRAM_WIDGET_INSERT: {
    00413         uint32 p1 = 0;
    00414         SB(p1, 0, 3, this->track);
    00415         SB(p1, 3, 16, ins->Id());
    00416         SB(p1, 19, 8, OpcodeForIndex(index));
    00417
    00418         DoCommandP(this->tile, p1, 0, CMD_INSERT_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
    00419         break;
    00420       }
    00421
    00422       case PROGRAM_WIDGET_SET_STATE: {
    00423         uint32 p1 = 0;
    00424         SB(p1, 0, 3, this->track);
    00425         SB(p1, 3, 16, ins->Id());
    00426
    00427         DoCommandP(this->tile, p1, index, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
    00428         break;
    00429       }
    00430
    00431       case PROGRAM_WIDGET_COND_VARIABLE: {
    00432         uint32 p1 = 0, p2 = 0;
    00433         SB(p1, 0, 3, this->track);
    00434         SB(p1, 3, 16, ins->Id());
    00435
    00436         SB(p2, 0, 1, 0);
    00437         SB(p2, 1, 8, index);
    00438
    00439         DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
    00440         break;
    00441       }
    00442
    00443       case PROGRAM_WIDGET_COND_COMPARATOR: {
    00444         uint32 p1 = 0, p2 = 0;
    00445         SB(p1, 0, 3, this->track);
    00446         SB(p1, 3, 16, ins->Id());
    00447
    00448         SB(p2, 0, 1, 1);
    00449         SB(p2, 1, 2, SCF_COMPARATOR);
    00450         SB(p2, 3, 27, index);
    00451
    00452         DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
    00453         break;
    00454       }
    00455     }
    00456   }
    00457
    00458   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00459   {
    00460     switch (widget) {
    00461       case PROGRAM_WIDGET_INSTRUCTION_LIST:
    00462         resize->height = FONT_HEIGHT_NORMAL;
    00463         size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00464         break;
    00465     }
    00466   }
    00467
    00468   virtual void OnResize()
    00469   {
    00470     /* Update the scroll bar. */
    00471     this->vscroll->SetCapacityFromWidget(this, PROGRAM_WIDGET_INSTRUCTION_LIST);
    00472   }
    00473
    00474   virtual void OnPaint()
    00475   {
    00476     this->DrawWidgets();
    00477   }
    00478
    00479   virtual void DrawWidget(const Rect &r, int widget) const
    00480   {
    00481     if (widget != PROGRAM_WIDGET_INSTRUCTION_LIST) return;
    00482
    00483     int y = r.top + WD_FRAMERECT_TOP;
    00484     int line_height = this->GetWidget<NWidgetBase>(PROGRAM_WIDGET_INSTRUCTION_LIST)->resize_y;
    00485
    00486     int no = this->vscroll->GetPosition();
    00487
    00488     for (const GuiInstruction *i = instructions.Begin() + no, *e = instructions.End();
    00489         i != e; ++i, no++) {
    00490       /* Don't draw anything if it extends past the end of the window. */
    00491       if (!this->vscroll->IsVisible(no)) break;
    00492
    00493       DrawInstructionString(i->insn, y, no == this->selected_instruction, i->indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT);
    00494       y += line_height;
    00495     }
    00496   }
    00497
    00498   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00499   {
    00500     if (!gui_scope) return;
    00501     RebuildInstructionList();
    00502   }
    00503
    00504   virtual void SetStringParameters(int widget) const
    00505   {
    00506     switch (widget) {
    00507       case PROGRAM_WIDGET_COND_VALUE: {
    00508         SetDParam(0, 0);
    00509         SignalInstruction *insn = this->GetSelected();
    00510         if (!insn || insn->Opcode() != PSO_IF) return;
    00511         SignalIf *si = static_cast<SignalIf*>(insn);
    00512         if (!IsConditionComparator(si->condition)) return;
    00513         SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(si->condition);
    00514         SetDParam(0, vc->value);
    00515       } break;
    00516     }
    00517   }
    00518
    00519 private:
    00520   SignalInstruction *GetSelected() const
    00521   {
    00522     if (this->selected_instruction == -1
    00523         || this->selected_instruction >= int(this->instructions.Length()))
    00524       return NULL;
    00525
    00526     return this->instructions[this->selected_instruction].insn;
    00527   }
    00528
    00529   Owner GetOwner()
    00530   {
    00531     return GetTileOwner(this->tile);
    00532   }
    00533
    00534   int GetInstructionFromPt(int y)
    00535   {
    00536     NWidgetBase *nwid = this->GetWidget<NWidgetBase>(PROGRAM_WIDGET_INSTRUCTION_LIST);
    00537     int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line.
    00538
    00539     if ((uint)sel >= this->vscroll->GetCapacity()) return -1;
    00540
    00541     sel += this->vscroll->GetPosition();
    00542
    00543     return (sel <= int(this->instructions.Length()) && sel >= 0) ? sel : -1;
    00544   }
    00545
    00546   void RebuildInstructionList()
    00547   {
    00548     uint old_len = this->instructions.Length();
    00549     this->instructions.Clear();
    00550     SignalInstruction *insn = program->first_instruction;
    00551     uint indent = 0;
    00552
    00553     do {
    00554       DEBUG(misc, 5, "PSig Gui: Opcode %d", insn->Opcode());
    00555       switch (insn->Opcode()) {
    00556         case PSO_FIRST:
    00557         case PSO_LAST: {
    00558           SignalSpecial *s = static_cast<SignalSpecial*>(insn);
    00559           GuiInstruction *gi = this->instructions.Append();
    00560           gi->insn   = s;
    00561           gi->indent = indent;
    00562           insn = s->next;
    00563           break;
    00564         }
    00565
    00566         case PSO_IF: {
    00567           SignalIf *i = static_cast<SignalIf*>(insn);
    00568           GuiInstruction *gi = this->instructions.Append();
    00569           gi->insn   = i;
    00570           gi->indent = indent++;
    00571           insn = i->if_true;
    00572           break;
    00573         }
    00574
    00575         case PSO_IF_ELSE: {
    00576           SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
    00577           GuiInstruction *gi = this->instructions.Append();
    00578           gi->insn   = p;
    00579           gi->indent = indent - 1;
    00580           insn = p->block->if_false;
    00581           break;
    00582         }
    00583
    00584         case PSO_IF_ENDIF: {
    00585           SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
    00586           GuiInstruction *gi = this->instructions.Append();
    00587           gi->insn   = p;
    00588           gi->indent = --indent;
    00589           insn = p->block->after;
    00590           break;
    00591         }
    00592
    00593         case PSO_SET_SIGNAL: {
    00594           SignalSet *s = static_cast<SignalSet*>(insn);
    00595           GuiInstruction *gi = this->instructions.Append();
    00596           gi->insn   = s;
    00597           gi->indent = indent;
    00598           insn = s->next;
    00599           break;
    00600         }
    00601
    00602         default: NOT_REACHED();
    00603       }
    00604     } while (insn);
    00605
    00606     this->vscroll->SetCount(this->instructions.Length());
    00607     if (this->instructions.Length() != old_len)
    00608       selected_instruction = -1;
    00609     UpdateButtonState();
    00610   }
    00611
    00612   void UpdateButtonState()
    00613   {
    00614 //    ResetObjectToPlace();
    00615     this->RaiseWidget(PROGRAM_WIDGET_INSERT);
    00616     this->RaiseWidget(PROGRAM_WIDGET_REMOVE);
    00617     this->RaiseWidget(PROGRAM_WIDGET_SET_STATE);
    00618     this->RaiseWidget(PROGRAM_WIDGET_COND_VARIABLE);
    00619     this->RaiseWidget(PROGRAM_WIDGET_COND_COMPARATOR);
    00620     this->RaiseWidget(PROGRAM_WIDGET_COND_VALUE);
    00621     this->RaiseWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
    00622
    00623     NWidgetStacked *left_sel   = this->GetWidget<NWidgetStacked>(PROGRAM_WIDGET_SEL_TOP_LEFT);
    00624     NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(PROGRAM_WIDGET_SEL_TOP_MIDDLE);
    00625     NWidgetStacked *right_sel  = this->GetWidget<NWidgetStacked>(PROGRAM_WIDGET_SEL_TOP_RIGHT);
    00626
    00627     /* Disable all the modifier buttons - we will re-enable them if applicable. */
    00628     this->DisableWidget(PROGRAM_WIDGET_SET_STATE);
    00629     this->DisableWidget(PROGRAM_WIDGET_COND_VARIABLE);
    00630     this->DisableWidget(PROGRAM_WIDGET_COND_COMPARATOR);
    00631     this->DisableWidget(PROGRAM_WIDGET_COND_VALUE);
    00632     this->DisableWidget(PROGRAM_WIDGET_COND_SET_SIGNAL);
    00633     this->DisableWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
    00634
    00635     /* Don't allow modifications if don't own, or have selected invalid instruction. */
    00636     if (this->GetOwner() != _local_company || this->selected_instruction < 1) {
    00637       this->DisableWidget(PROGRAM_WIDGET_INSERT);
    00638       this->DisableWidget(PROGRAM_WIDGET_REMOVE);
    00639       this->SetDirty();
    00640       return;
    00641     } else {
    00642       this->EnableWidget(PROGRAM_WIDGET_INSERT);
    00643       this->EnableWidget(PROGRAM_WIDGET_REMOVE);
    00644     }
    00645
    00646     SignalInstruction *insn = GetSelected();
    00647     if (!insn) return;
    00648
    00649     switch (insn->Opcode()) {
    00650       case PSO_IF: {
    00651         SignalIf *i = static_cast<SignalIf*>(insn);
    00652         left_sel->SetDisplayedPlane(DPL_COND_VARIABLE);
    00653         middle_sel->SetDisplayedPlane(DPM_COND_COMPARATOR);
    00654         right_sel->SetDisplayedPlane(DPR_COND_VALUE);
    00655
    00656         this->EnableWidget(PROGRAM_WIDGET_COND_VARIABLE);
    00657         this->GetWidget<NWidgetCore>(PROGRAM_WIDGET_COND_VARIABLE)->widget_data =
    00658             _program_condvar[i->condition->ConditionCode()];
    00659
    00660         if (IsConditionComparator(i->condition)) {
    00661           SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(i->condition);
    00662           this->EnableWidget(PROGRAM_WIDGET_COND_COMPARATOR);
    00663           this->EnableWidget(PROGRAM_WIDGET_COND_VALUE);
    00664
    00665           this->GetWidget<NWidgetCore>(PROGRAM_WIDGET_COND_COMPARATOR)->widget_data =
    00666             _program_comparator[vc->comparator];
    00667
    00668         } else if (i->condition->ConditionCode() == PSC_SIGNAL_STATE) {
    00669           this->EnableWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
    00670           this->EnableWidget(PROGRAM_WIDGET_COND_SET_SIGNAL);
    00671           middle_sel->SetDisplayedPlane(DPM_COND_GOTO_SIGNAL);
    00672           right_sel->SetDisplayedPlane(DPR_COND_SET_SIGNAL);
    00673         }
    00674       } break;
    00675
    00676       case PSO_SET_SIGNAL: {
    00677         SignalSet *s = static_cast<SignalSet*>(insn);
    00678         left_sel->SetDisplayedPlane(DPL_SET_STATE);
    00679         this->SetWidgetDisabledState(PROGRAM_WIDGET_SET_STATE, false);
    00680         this->GetWidget<NWidgetCore>(PROGRAM_WIDGET_SET_STATE)->widget_data =
    00681             _program_sigstate[s->to_state];
    00682       } break;
    00683
    00684       case PSO_FIRST:
    00685       case PSO_LAST:
    00686       case PSO_IF_ELSE:
    00687       case PSO_IF_ENDIF:
    00688         /* All cannot be modified. */
    00689         this->DisableWidget(PROGRAM_WIDGET_REMOVE);
    00690         break;
    00691
    00692       default:
    00693         NOT_REACHED();
    00694     }
    00695
    00696     this->SetDirty();
    00697   }
    00698
    00699   TileIndex tile;
    00700   Track track;
    00701   SignalProgram *program;
    00702   GuiInstructionList instructions;
    00703   int selected_instruction;
    00704   Scrollbar *vscroll;
    00705 };
    00706
    00707 static const NWidgetPart _nested_program_widgets[] = {
    00708   /* Title bar. */
    00709   NWidget(NWID_HORIZONTAL),
    00710     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00711     NWidget(WWT_CAPTION, COLOUR_GREY, PROGRAM_WIDGET_CAPTION), SetDataTip(STR_PROGSIG_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00712     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00713     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00714   EndContainer(),
    00715
    00716   /* Program display. */
    00717   NWidget(NWID_HORIZONTAL),
    00718     NWidget(WWT_PANEL, COLOUR_GREY, PROGRAM_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_PROGSIG_CAPTION), SetResize(1, 1), SetScrollbar(PROGRAM_WIDGET_SCROLLBAR), EndContainer(),
    00719     NWidget(NWID_VSCROLLBAR, COLOUR_GREY, PROGRAM_WIDGET_SCROLLBAR),
    00720   EndContainer(),
    00721
    00722   /* Button Bar. */
    00723   NWidget(NWID_HORIZONTAL),
    00724     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    00725       NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_LEFT),
    00726         NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
    00727                             SetDataTip(STR_NULL, STR_PROGSIG_COND_VARIABLE_TOOLTIP), SetResize(1, 0),
    00728         NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_SET_STATE), SetMinimalSize(124, 12), SetFill(1, 0),
    00729                             SetDataTip(STR_NULL, STR_PROGSIG_SIGNAL_STATE_TOOLTIP), SetResize(1, 0),
    00730       EndContainer(),
    00731       NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_MIDDLE),
    00732         NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0),
    00733                             SetDataTip(STR_NULL, STR_PROGSIG_COND_COMPARATOR_TOOLTIP), SetResize(1, 0),
    00734         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COND_GOTO_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0),
    00735                             SetDataTip(STR_PROGSIG_GOTO_SIGNAL, STR_PROGSIG_GOTO_SIGNAL_TOOLTIP), SetResize(1, 0),
    00736       EndContainer(),
    00737       NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_RIGHT),
    00738         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0),
    00739                             SetDataTip(STR_BLACK_COMMA, STR_PROGSIG_COND_VALUE_TOOLTIP), SetResize(1, 0),
    00740         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COND_SET_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0),
    00741                             SetDataTip(STR_PROGSIG_COND_SET_SIGNAL, STR_PROGSIG_COND_SET_SIGNAL_TOOLTIP), SetResize(1, 0),
    00742       EndContainer(),
    00743     EndContainer(),
    00744     NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, PROGRAM_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_PROGSIG_GOTO_SIGNAL_TOOLTIP),
    00745   EndContainer(),
    00746
    00747   /* Second button row. */
    00748   NWidget(NWID_HORIZONTAL),
    00749     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    00750         NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0),
    00751                             SetDataTip(STR_PROGSIG_INSERT, STR_PROGSIG_INSERT_TOOLTIP), SetResize(1, 0),
    00752         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, PROGRAM_WIDGET_REMOVE), SetMinimalSize(186, 12), SetFill(1, 0),
    00753                             SetDataTip(STR_PROGSIG_REMOVE, STR_PROGSIG_REMOVE_TOOLTIP), SetResize(1, 0),
    00754     EndContainer(),
    00755     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
    00756   EndContainer(),
    00757 };
    00758
    00759 static const WindowDesc _program_desc(
    00760   WDP_AUTO, 384, 100,
    00761   WC_SIGNAL_PROGRAM, WC_BUILD_SIGNAL,
    00762   WDF_CONSTRUCTION,
    00763   _nested_program_widgets, lengthof(_nested_program_widgets)
    00764 );
    00765
    00766 void ShowSignalProgramWindow(SignalReference ref)
    00767 {
    00768   uint32 window_id = (ref.tile << 3) | ref.track;
    00769   if (BringWindowToFrontById(WC_SIGNAL_PROGRAM, window_id) != NULL) return;
    00770
    00771   new ProgramWindow(&_program_desc, ref);
    00772 }
    00773
    00774 extern uint ConvertSpeedToDisplaySpeed(uint speed);
    00775 extern uint ConvertDisplaySpeedToSpeed(uint speed);
    00776
    00777 enum SpeedWindowWidgets {
    00778   SPEED_WIDGET_CAPTION,
    00779   SPEED_WIDGET_VALUE,
    00780   SPEED_WIDGET_CLEAR,
    00781   SPEED_WIDGET_CHANGE,
    00782 };
    00783
    00784 class SpeedWindow: public Window {
    00785 public:
    00786   SpeedWindow(const WindowDesc *desc, SignalReference ref)
    00787   {
    00788     this->InitNested(desc, (ref.tile << 3) | ref.track);
    00789     this->tile = ref.tile;
    00790     this->track = ref.track;
    00791
    00792     this->InvalidateData();
    00793     this->SetWidgetLoweredState(SPEED_WIDGET_CHANGE, true);
    00794     this->ShowChangeSpeedLimit();
    00795   }
    00796
    00797   void ShowChangeSpeedLimit()
    00798   {
    00799     uint16 speed = GetSignalSpeed(SignalReference(this->tile, this->track));
    00800     speed = ConvertSpeedToDisplaySpeed((speed * 10) / 16);
    00801
    00802     SetDParam(0, speed);
    00803     ShowQueryString(STR_JUST_INT, STR_SPEEDSIG_CAPTION, 31, this, CS_NUMERAL, QSF_NONE);
    00804   }
    00805
    00806   virtual void OnClick(Point pt, int widget, int click_count)
    00807   {
    00808     switch (widget) {
    00809       case SPEED_WIDGET_CLEAR: {
    00810         this->SetSpeedLimit(0);
    00811         break;
    00812       }
    00813       case SPEED_WIDGET_CHANGE: {
    00814         this->ShowChangeSpeedLimit();
    00815         break;
    00816       }
    00817     }
    00818   }
    00819
    00820   virtual void OnQueryTextFinished(char *str)
    00821   {
    00822     this->SetWidgetLoweredState(SPEED_WIDGET_CHANGE, false);
    00823     this->SetWidgetDirty(SPEED_WIDGET_CHANGE);
    00824
    00825     if (str == NULL) return;
    00826
    00827     uint16 speed = StrEmpty(str) ? 0 : strtoul(str, NULL, 10);
    00828     speed = ConvertDisplaySpeedToSpeed((speed * 16) / 10);
    00829
    00830     this->SetSpeedLimit(speed);
    00831   }
    00832
    00833   void SetSpeedLimit(uint16 speed)
    00834   {
    00835     uint32 p1 = 0;
    00836     SB(p1, 0, 3, this->track);
    00837
    00838     DoCommandP(this->tile, p1, (uint32)speed, CMD_SET_SIGNAL_SPEED_LIMIT | CMD_MSG(STR_ERROR_CAN_T_SET_SPEED));
    00839   }
    00840
    00841   virtual void OnPaint()
    00842   {
    00843     if (this->GetOwner() == _local_company) {
    00844       this->EnableWidget(SPEED_WIDGET_CLEAR);
    00845       this->EnableWidget(SPEED_WIDGET_CHANGE);
    00846     } else {
    00847       this->DisableWidget(SPEED_WIDGET_CLEAR);
    00848       this->DisableWidget(SPEED_WIDGET_CHANGE);
    00849     }
    00850     this->DrawWidgets();
    00851   }
    00852
    00853   virtual void OnInvalidateData(int data) {
    00854     uint16 speed = GetSignalSpeed(SignalReference(this->tile, this->track));
    00855
    00856     NWidgetCore *w = this->GetWidget<NWidgetCore>(SPEED_WIDGET_VALUE);
    00857
    00858     if (speed == 0) {
    00859       w->SetDataTip(STR_SPEEDSIG_VALUE_NONE, STR_NULL);
    00860     } else {
    00861       w->SetDataTip(STR_SPEEDSIG_VALUE, STR_NULL);
    00862     }
    00863   }
    00864
    00865   virtual void SetStringParameters(int widget) const
    00866   {
    00867     switch (widget) {
    00868       case SPEED_WIDGET_VALUE: {
    00869          uint16 speed = GetSignalSpeed(SignalReference(this->tile, this->track));
    00870          SetDParam(0, speed);
    00871          break;
    00872       }
    00873     }
    00874   }
    00875
    00876 private:
    00877   Owner GetOwner()
    00878   {
    00879     return GetTileOwner(this->tile);
    00880   }
    00881
    00882   TileIndex tile;
    00883   Track track;
    00884 };
    00885
    00886 static const NWidgetPart _nested_speed_widgets[] = {
    00887   NWidget(NWID_HORIZONTAL),
    00888     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00889     NWidget(WWT_CAPTION, COLOUR_GREY, SPEED_WIDGET_CAPTION), SetDataTip(STR_SPEEDSIG_CAPTION, STR_NULL),
    00890     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00891   EndContainer(),
    00892   NWidget(WWT_PANEL, COLOUR_GREY),
    00893     NWidget(WWT_LABEL, COLOUR_GREY, SPEED_WIDGET_VALUE), SetMinimalSize(128, 12), SetFill(1, 1), SetPadding(2, 2, 2, 2),
    00894   EndContainer(),
    00895   NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    00896     NWidget(WWT_TEXTBTN, COLOUR_GREY, SPEED_WIDGET_CLEAR), SetDataTip(STR_SPEEDSIG_CLEAR, STR_NULL), SetMinimalSize(87, 12), SetFill(1, 0),
    00897     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SPEED_WIDGET_CHANGE), SetDataTip(STR_SPEEDSIG_CHANGE, STR_NULL), SetMinimalSize(87, 12), SetFill(1, 0),
    00898   EndContainer(),
    00899 };
    00900
    00901 static const WindowDesc _speed_desc(
    00902   WDP_AUTO, 384, 100,
    00903   WC_SPEED_SIGNAL, WC_BUILD_SIGNAL,
    00904   WDF_CONSTRUCTION,
    00905   _nested_speed_widgets, lengthof(_nested_speed_widgets)
    00906 );
    00907
    00908 void ShowSpeedSignalWindow(SignalReference ref)
    00909 {
    00910   uint32 window_id = (ref.tile << 3) | ref.track;
    00911   if (BringWindowToFrontById(WC_SPEED_SIGNAL, window_id) != NULL) return;
    00912
    00913   new SpeedWindow(&_speed_desc, ref);
    00914 }