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 }