timetable_gui.cpp

Go to the documentation of this file.
00001 /* $Id: timetable_gui.cpp 15723 2009-03-15 15:12:06Z rubidium $ */
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      * Calculation description:
00066      * 15 = 14 (this->widget[TTV_ORDER_VIEW].top) + 1 (frame-line)
00067      * 10 = order text hight
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         /* Autoreplace replaced the vehicle */
00083         this->vehicle = GetVehicle(this->window_number);
00084         break;
00085 
00086       case -1:
00087         /* Removed / replaced all orders (after deleting / sharing) */
00088         if (this->sel_index == -1) break;
00089 
00090         this->DeleteChildWindows();
00091         this->sel_index = -1;
00092         break;
00093 
00094       default: {
00095         /* Moving an order. If one of these is INVALID_VEH_ORDER_ID, then
00096          * the order is being created / removed */
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; // no need to change anything
00103 
00104         /* if from == INVALID_VEH_ORDER_ID, one order was added; if to == INVALID_VEH_ORDER_ID, one order was removed */
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; // when last travel time is selected, it belongs to order 0
00109 
00110         bool travel = HasBit(this->sel_index, 0);
00111 
00112         if (from != selected_order) {
00113           /* Moving from preceeding order? */
00114           selected_order -= (int)(from <= selected_order);
00115           /* Moving to   preceeding order? */
00116           selected_order += (int)(to   <= selected_order);
00117         } else {
00118           /* Now we are modifying the selected order */
00119           if (to == INVALID_VEH_ORDER_ID) {
00120             /* Deleting selected order */
00121             this->DeleteChildWindows();
00122             this->sel_index = -1;
00123             break;
00124           } else {
00125             /* Moving selected order */
00126             selected_order = to;
00127           }
00128         }
00129 
00130         /* recompute new sel_index */
00131         this->sel_index = 2 * selected_order - (int)travel;
00132         /* travel time of first order needs special handling */
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       /* Don't draw anything if it extends past the end of the window. */
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: // Order view button
00260         ShowOrdersWindow(v);
00261         break;
00262 
00263       case TTV_TIMETABLE_PANEL: { // Main 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: { // "Wait For" button.
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: { // Clear waiting time button.
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: // Reset the vehicle's late counter.
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: { // Autofill the timetable.
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     /* Update the scroll + matrix */
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},                // TTV_WIDGET_CLOSEBOX
00337   {    WWT_CAPTION,   RESIZE_RIGHT,  COLOUR_GREY,    11,   326,     0,    13, STR_TIMETABLE_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},      // TTV_CAPTION
00338   { WWT_PUSHTXTBTN,   RESIZE_LR,     COLOUR_GREY,   327,   387,     0,    13, STR_ORDER_VIEW,             STR_ORDER_VIEW_TOOLTIP},               // TTV_ORDER_VIEW
00339   {  WWT_STICKYBOX,   RESIZE_LR,     COLOUR_GREY,   388,   399,     0,    13, STR_NULL,                   STR_STICKY_BUTTON},                    // TTV_STICKY
00340 
00341   {      WWT_PANEL,   RESIZE_RB,     COLOUR_GREY,     0,   387,    14,    95, STR_NULL,                   STR_TIMETABLE_TOOLTIP},                // TTV_TIMETABLE_PANEL
00342   {  WWT_SCROLLBAR,   RESIZE_LRB,    COLOUR_GREY,   388,   399,    14,    95, STR_NULL,                   STR_0190_SCROLL_BAR_SCROLLS_LIST},     // TTV_SCROLLBAR
00343 
00344   {      WWT_PANEL,   RESIZE_RTB,    COLOUR_GREY,     0,   399,    96,   117, STR_NULL,                   STR_NULL},                             // TTV_SUMMARY_PANEL
00345 
00346   { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_GREY,     0,   109,   118,   129, STR_TIMETABLE_CHANGE_TIME,  STR_TIMETABLE_WAIT_TIME_TOOLTIP},      // TTV_CHANGE_TIME
00347   { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_GREY,   110,   219,   118,   129, STR_CLEAR_TIME,             STR_TIMETABLE_CLEAR_TIME_TOOLTIP},     // TTV_CLEAR_TIME
00348   { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_GREY,   220,   337,   118,   129, STR_RESET_LATENESS,         STR_TIMETABLE_RESET_LATENESS_TOOLTIP}, // TTV_RESET_LATENESS
00349   { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_GREY,   338,   387,   118,   129, STR_TIMETABLE_AUTOFILL,     STR_TIMETABLE_AUTOFILL_TOOLTIP},       // TTV_AUTOFILL
00350 
00351   {      WWT_PANEL,   RESIZE_RTB,    COLOUR_GREY,   388,   387,   118,   129, STR_NULL,                   STR_NULL},                             // TTV_EMPTY
00352   {  WWT_RESIZEBOX,   RESIZE_LRTB,   COLOUR_GREY,   388,   399,   118,   129, STR_NULL,                   STR_RESIZE_BUTTON},                    // TTV_RESIZE
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 }

Generated on Wed Jul 15 20:36:03 2009 for OpenTTD by  doxygen 1.5.6