00001
00002
00005 #include "stdafx.h"
00006 #include "command_func.h"
00007 #include "gui.h"
00008 #include "window_gui.h"
00009 #include "window_func.h"
00010 #include "textbuf_gui.h"
00011 #include "strings_func.h"
00012 #include "vehicle_base.h"
00013 #include "string_func.h"
00014 #include "gfx_func.h"
00015 #include "company_func.h"
00016 #include "settings_type.h"
00017
00018 #include "table/strings.h"
00019
00020 enum TimetableViewWindowWidgets {
00021 TTV_WIDGET_CLOSEBOX = 0,
00022 TTV_CAPTION,
00023 TTV_ORDER_VIEW,
00024 TTV_STICKY,
00025 TTV_TIMETABLE_PANEL,
00026 TTV_SCROLLBAR,
00027 TTV_SUMMARY_PANEL,
00028 TTV_CHANGE_TIME,
00029 TTV_CLEAR_TIME,
00030 TTV_RESET_LATENESS,
00031 TTV_AUTOFILL,
00032 TTV_EMPTY,
00033 TTV_RESIZE,
00034 };
00035
00036 void SetTimetableParams(int param1, int param2, uint32 time)
00037 {
00038 if (_settings_client.gui.timetable_in_ticks) {
00039 SetDParam(param1, STR_TIMETABLE_TICKS);
00040 SetDParam(param2, time);
00041 } else {
00042 SetDParam(param1, STR_TIMETABLE_DAYS);
00043 SetDParam(param2, time / DAY_TICKS);
00044 }
00045 }
00046
00047 struct TimetableWindow : Window {
00048 int sel_index;
00049 const Vehicle *vehicle;
00050
00051 TimetableWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
00052 {
00053 this->vehicle = GetVehicle(window_number);
00054 this->owner = this->vehicle->owner;
00055 this->vscroll.cap = 8;
00056 this->resize.step_height = 10;
00057 this->sel_index = -1;
00058
00059 this->FindWindowPlacementAndResize(desc);
00060 }
00061
00062 int GetOrderFromTimetableWndPt(int y, const Vehicle *v)
00063 {
00064
00065
00066
00067
00068
00069 int sel = (y - 15) / 10;
00070
00071 if ((uint)sel >= this->vscroll.cap) return INVALID_ORDER;
00072
00073 sel += this->vscroll.pos;
00074
00075 return (sel < v->GetNumOrders() * 2 && sel >= 0) ? sel : INVALID_ORDER;
00076 }
00077
00078 virtual void OnInvalidateData(int data)
00079 {
00080 switch (data) {
00081 case 0:
00082
00083 this->vehicle = GetVehicle(this->window_number);
00084 break;
00085
00086 case -1:
00087
00088 if (this->sel_index == -1) break;
00089
00090 this->DeleteChildWindows();
00091 this->sel_index = -1;
00092 break;
00093
00094 default: {
00095
00096
00097 if (this->sel_index == -1) break;
00098
00099 VehicleOrderID from = GB(data, 0, 8);
00100 VehicleOrderID to = GB(data, 8, 8);
00101
00102 if (from == to) break;
00103
00104
00105 uint old_num_orders = this->vehicle->GetNumOrders() - (uint)(from == INVALID_VEH_ORDER_ID) + (uint)(to == INVALID_VEH_ORDER_ID);
00106
00107 VehicleOrderID selected_order = (this->sel_index + 1) / 2;
00108 if (selected_order == old_num_orders) selected_order = 0;
00109
00110 bool travel = HasBit(this->sel_index, 0);
00111
00112 if (from != selected_order) {
00113
00114 selected_order -= (int)(from <= selected_order);
00115
00116 selected_order += (int)(to <= selected_order);
00117 } else {
00118
00119 if (to == INVALID_VEH_ORDER_ID) {
00120
00121 this->DeleteChildWindows();
00122 this->sel_index = -1;
00123 break;
00124 } else {
00125
00126 selected_order = to;
00127 }
00128 }
00129
00130
00131 this->sel_index = 2 * selected_order - (int)travel;
00132
00133 if (this->sel_index == -1) this->sel_index = this->vehicle->GetNumOrders() * 2 - 1;
00134 } break;
00135 }
00136 }
00137
00138
00139 virtual void OnPaint()
00140 {
00141 const Vehicle *v = this->vehicle;
00142 int selected = this->sel_index;
00143
00144 SetVScrollCount(this, v->GetNumOrders() * 2);
00145
00146 if (v->owner == _local_company) {
00147 bool disable = true;
00148 if (selected != -1) {
00149 const Order *order = GetVehicleOrder(v, ((selected + 1) / 2) % v->GetNumOrders());
00150 if (selected % 2 == 1) {
00151 disable = order != NULL && order->IsType(OT_CONDITIONAL);
00152 } else {
00153 disable = order == NULL || ((!order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) && !order->IsType(OT_CONDITIONAL));
00154 }
00155 }
00156
00157 this->SetWidgetDisabledState(TTV_CHANGE_TIME, disable);
00158 this->SetWidgetDisabledState(TTV_CLEAR_TIME, disable);
00159
00160 this->EnableWidget(TTV_RESET_LATENESS);
00161 this->EnableWidget(TTV_AUTOFILL);
00162 } else {
00163 this->DisableWidget(TTV_CHANGE_TIME);
00164 this->DisableWidget(TTV_CLEAR_TIME);
00165 this->DisableWidget(TTV_RESET_LATENESS);
00166 this->DisableWidget(TTV_AUTOFILL);
00167 }
00168
00169 this->SetWidgetLoweredState(TTV_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE));
00170
00171 SetDParam(0, v->index);
00172 this->DrawWidgets();
00173
00174 int y = 15;
00175 int i = this->vscroll.pos;
00176 VehicleOrderID order_id = (i + 1) / 2;
00177 bool final_order = false;
00178
00179 const Order *order = GetVehicleOrder(v, order_id);
00180
00181 while (order != NULL) {
00182
00183 if (i - this->vscroll.pos >= this->vscroll.cap) break;
00184
00185 if (i % 2 == 0) {
00186 DrawOrderString(v, order, order_id, y, i == selected, true, this->widget[TTV_TIMETABLE_PANEL].right - 4);
00187
00188 order_id++;
00189
00190 if (order_id >= v->GetNumOrders()) {
00191 order = GetVehicleOrder(v, 0);
00192 final_order = true;
00193 } else {
00194 order = order->next;
00195 }
00196 } else {
00197 StringID string;
00198
00199 if (order->IsType(OT_CONDITIONAL)) {
00200 string = STR_TIMETABLE_NO_TRAVEL;
00201 } else if (order->travel_time == 0) {
00202 string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED;
00203 } else {
00204 SetTimetableParams(0, 1, order->travel_time);
00205 string = STR_TIMETABLE_TRAVEL_FOR;
00206 }
00207
00208 DrawStringTruncated(2, y, string, (i == selected) ? TC_WHITE : TC_BLACK, this->widget[TTV_TIMETABLE_PANEL].right - 4);
00209
00210 if (final_order) break;
00211 }
00212
00213 i++;
00214 y += 10;
00215 }
00216
00217 y = this->widget[TTV_SUMMARY_PANEL].top + 1;
00218
00219 {
00220 uint total_time = 0;
00221 bool complete = true;
00222
00223 for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) {
00224 total_time += order->travel_time + order->wait_time;
00225 if (order->travel_time == 0 && !order->IsType(OT_CONDITIONAL)) complete = false;
00226 if (order->wait_time == 0 && order->IsType(OT_GOTO_STATION) && !(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) complete = false;
00227 }
00228
00229 if (total_time != 0) {
00230 SetTimetableParams(0, 1, total_time);
00231 DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, TC_BLACK);
00232 }
00233 }
00234 y += 10;
00235
00236 if (v->lateness_counter == 0 || (!_settings_client.gui.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) {
00237 DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, TC_BLACK);
00238 } else {
00239 SetTimetableParams(0, 1, abs(v->lateness_counter));
00240 DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, TC_BLACK);
00241 }
00242 }
00243
00244 static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected)
00245 {
00246 uint order_number = (selected + 1) / 2;
00247 uint is_journey = (selected % 2 == 1) ? 1 : 0;
00248
00249 if (order_number >= v->GetNumOrders()) order_number = 0;
00250
00251 return v->index | (order_number << 16) | (is_journey << 24);
00252 }
00253
00254 virtual void OnClick(Point pt, int widget)
00255 {
00256 const Vehicle *v = this->vehicle;
00257
00258 switch (widget) {
00259 case TTV_ORDER_VIEW:
00260 ShowOrdersWindow(v);
00261 break;
00262
00263 case TTV_TIMETABLE_PANEL: {
00264 int selected = GetOrderFromTimetableWndPt(pt.y, v);
00265
00266 this->DeleteChildWindows();
00267 this->sel_index = (selected == INVALID_ORDER || selected == this->sel_index) ? -1 : selected;
00268 } break;
00269
00270 case TTV_CHANGE_TIME: {
00271 int selected = this->sel_index;
00272 VehicleOrderID real = (selected + 1) / 2;
00273
00274 if (real >= v->GetNumOrders()) real = 0;
00275
00276 const Order *order = GetVehicleOrder(v, real);
00277 StringID current = STR_EMPTY;
00278
00279 if (order != NULL) {
00280 uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time;
00281 if (!_settings_client.gui.timetable_in_ticks) time /= DAY_TICKS;
00282
00283 if (time != 0) {
00284 SetDParam(0, time);
00285 current = STR_CONFIG_SETTING_INT32;
00286 }
00287 }
00288
00289 ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, this, CS_NUMERAL, QSF_NONE);
00290 } break;
00291
00292 case TTV_CLEAR_TIME: {
00293 uint32 p1 = PackTimetableArgs(v, this->sel_index);
00294 DoCommandP(0, p1, 0, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
00295 } break;
00296
00297 case TTV_RESET_LATENESS:
00298 DoCommandP(0, v->index, 0, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
00299 break;
00300
00301 case TTV_AUTOFILL: {
00302 uint32 p2 = 0;
00303 if (!HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) SetBit(p2, 0);
00304 if (_ctrl_pressed) SetBit(p2, 1);
00305 DoCommandP(0, v->index, p2, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
00306 } break;
00307 }
00308
00309 this->SetDirty();
00310 }
00311
00312 virtual void OnQueryTextFinished(char *str)
00313 {
00314 if (str == NULL) return;
00315
00316 const Vehicle *v = this->vehicle;
00317
00318 uint32 p1 = PackTimetableArgs(v, this->sel_index);
00319
00320 uint64 time = StrEmpty(str) ? 0 : strtoul(str, NULL, 10);
00321 if (!_settings_client.gui.timetable_in_ticks) time *= DAY_TICKS;
00322
00323 uint32 p2 = minu(time, UINT16_MAX);
00324
00325 DoCommandP(0, p1, p2, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE));
00326 }
00327
00328 virtual void OnResize(Point new_size, Point delta)
00329 {
00330
00331 this->vscroll.cap = (this->widget[TTV_TIMETABLE_PANEL].bottom - this->widget[TTV_TIMETABLE_PANEL].top) / 10;
00332 }
00333 };
00334
00335 static const Widget _timetable_widgets[] = {
00336 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00337 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 326, 0, 13, STR_TIMETABLE_TITLE, STR_018C_WINDOW_TITLE_DRAG_THIS},
00338 { WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 327, 387, 0, 13, STR_ORDER_VIEW, STR_ORDER_VIEW_TOOLTIP},
00339 { WWT_STICKYBOX, RESIZE_LR, COLOUR_GREY, 388, 399, 0, 13, STR_NULL, STR_STICKY_BUTTON},
00340
00341 { WWT_PANEL, RESIZE_RB, COLOUR_GREY, 0, 387, 14, 95, STR_NULL, STR_TIMETABLE_TOOLTIP},
00342 { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_GREY, 388, 399, 14, 95, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00343
00344 { WWT_PANEL, RESIZE_RTB, COLOUR_GREY, 0, 399, 96, 117, STR_NULL, STR_NULL},
00345
00346 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 0, 109, 118, 129, STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP},
00347 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 110, 219, 118, 129, STR_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP},
00348 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 220, 337, 118, 129, STR_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP},
00349 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 338, 387, 118, 129, STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP},
00350
00351 { WWT_PANEL, RESIZE_RTB, COLOUR_GREY, 388, 387, 118, 129, STR_NULL, STR_NULL},
00352 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 388, 399, 118, 129, STR_NULL, STR_RESIZE_BUTTON},
00353
00354 { WIDGETS_END }
00355 };
00356
00357 static const WindowDesc _timetable_desc = {
00358 WDP_AUTO, WDP_AUTO, 400, 130, 400, 130,
00359 WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW,
00360 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE | WDF_CONSTRUCTION,
00361 _timetable_widgets,
00362 };
00363
00364 void ShowTimetableWindow(const Vehicle *v)
00365 {
00366 DeleteWindowById(WC_VEHICLE_DETAILS, v->index, false);
00367 DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false);
00368 AllocateWindowDescFront<TimetableWindow>(&_timetable_desc, v->index);
00369 }