]> git.tdb.fi Git - r2c2.git/blob - source/engineer/timetablepanel.cpp
Allow direction to be specified for routing waypoints
[r2c2.git] / source / engineer / timetablepanel.cpp
1 #include <msp/core/maputils.h>
2 #include <msp/gltk/button.h>
3 #include <msp/gltk/root.h>
4 #include <msp/input/keys.h>
5 #include <msp/strings/format.h>
6 #include <msp/strings/regex.h>
7 #include "libr2c2/zone.h"
8 #include "engineer.h"
9 #include "timetablepanel.h"
10
11 using namespace std;
12 using namespace Msp;
13 using namespace R2C2;
14
15 class TimetableRowItem: public GLtk::List::MultiColumnItem
16 {
17 public:
18         typedef const Timetable::Row *ValueType;
19
20         TimetableRowItem(ValueType);
21 };
22
23
24 string format_time(const Time::TimeDelta &time)
25 {
26         unsigned second = time/Time::sec;
27         unsigned hour = second/3600;
28         unsigned minute = second/60%60;
29         second %= 60;
30         return format("%02d:%02d:%02d", hour, minute, second);
31 }
32
33
34 TimetablePanel::TimetablePanel(Engineer &e, R2C2::Train &t):
35         engineer(e),
36         train(t),
37         target(0),
38         target_pick(false),
39         picked_target(0),
40         pick_highlight(0)
41 {
42         Loader::WidgetMap widgets;
43         DataFile::load(*this, "data/timetablepanel.ui", widgets);
44
45         lst_timetable = dynamic_cast<GLtk::List *>(get_item(widgets, "lst_timetable"));
46         lst_timetable->set_data(rows);
47         lst_timetable->set_item_type<TimetableRowItem>();
48         lst_timetable->signal_item_selected.connect(sigc::mem_fun(this, &TimetablePanel::row_selected));
49
50         drp_type = dynamic_cast<GLtk::Dropdown *>(get_item(widgets, "drp_type"));
51         lbl_target = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_target"));
52         ent_time = dynamic_cast<GLtk::Entry *>(get_item(widgets, "ent_time"));
53         drp_direction = dynamic_cast<GLtk::Dropdown *>(get_item(widgets, "drp_direction"));
54
55         dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_pick"))->signal_clicked.connect(sigc::mem_fun(this, &TimetablePanel::pick_clicked));
56         dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_insert"))->signal_clicked.connect(sigc::mem_fun(this, &TimetablePanel::insert_clicked));
57         dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_delete"))->signal_clicked.connect(sigc::mem_fun(this, &TimetablePanel::delete_clicked));
58         dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_apply"))->signal_clicked.connect(sigc::mem_fun(this, &TimetablePanel::apply_clicked));
59
60         timetable = train.get_ai_of_type<Timetable>();
61         if(!timetable)
62                 timetable = new Timetable(train);
63
64         unsigned length = timetable->get_length();
65         for(unsigned i=0; i<length; ++i)
66                 rows.append(&timetable->get_row(i));
67         rows.append(0);
68         timetable->signal_row_added.connect(sigc::mem_fun(this, &TimetablePanel::row_added));
69         timetable->signal_row_modified.connect(sigc::mem_fun(this, &TimetablePanel::row_modified));
70         timetable->signal_row_removed.connect(sigc::mem_fun(this, &TimetablePanel::row_removed));
71 }
72
73 void TimetablePanel::row_added(unsigned i, const Timetable::Row &row)
74 {
75         rows.insert(i, &row);
76 }
77
78 void TimetablePanel::row_modified(unsigned i, const Timetable::Row &)
79 {
80         rows.refresh(i);
81 }
82
83 void TimetablePanel::row_removed(unsigned i)
84 {
85         rows.remove(i);
86 }
87
88 Timetable::Row TimetablePanel::create_row()
89 {
90         Timetable::Row row;
91
92         row.type = static_cast<Timetable::RowType>(drp_type->get_selected_index()+1);
93         row.target = target;
94         row.direction = static_cast<TrackChain::Direction>(drp_direction->get_selected_index());
95
96         Regex r_time("([01]?[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?");
97         RegMatch m = r_time.match(ent_time->get_text());
98         if(m)
99         {
100                 row.time = lexical_cast<unsigned>(m[1].str)*Time::hour;
101                 row.time += lexical_cast<unsigned>(m[2].str)*Time::min;
102                 if(m[3])
103                         row.time += lexical_cast<unsigned>(m[4].str)*Time::sec;
104         }
105
106         return row;
107 }
108
109 void TimetablePanel::row_selected(unsigned i)
110 {
111         const Timetable::Row *row = rows.get(i);
112         if(row)
113         {
114                 drp_type->set_selected_index(row->type-1);
115                 target = row->target;
116                 if(target)
117                         lbl_target->set_text(target->get_name());
118                 drp_direction->set_selected_index(row->direction);
119                 ent_time->set_text(format_time(row->time));
120         }
121 }
122
123 void TimetablePanel::pick_clicked()
124 {
125         target_pick = true;
126         picked_target = 0;
127         shift = false;
128         signal_grab_pointer.emit();
129 }
130
131 void TimetablePanel::insert_clicked()
132 {
133         int index = lst_timetable->get_selected_index();
134         if(index<0)
135                 index = timetable->get_length();
136         timetable->insert_row(index, create_row());
137 }
138
139 void TimetablePanel::delete_clicked()
140 {
141         int index = lst_timetable->get_selected_index();
142         if(index>=0)
143                 timetable->remove_row(index);
144 }
145
146 void TimetablePanel::apply_clicked()
147 {
148         int index = lst_timetable->get_selected_index();
149         if(index>=0)
150                 timetable->modify_row(index, create_row());
151 }
152
153 void TimetablePanel::key_press(unsigned key, unsigned mod)
154 {
155         Panel::key_press(key, mod);
156
157         if(key==Input::KEY_SHIFT_R || key==Input::KEY_SHIFT_L)
158                 shift = true;
159 }
160
161 void TimetablePanel::key_release(unsigned key, unsigned mod)
162 {
163         Panel::key_release(key, mod);
164
165         if(key==Input::KEY_SHIFT_R || key==Input::KEY_SHIFT_L)
166                 shift = false;
167 }
168
169 void TimetablePanel::button_press(int x, int y, unsigned btn)
170 {
171         Panel::button_press(x, y, btn);
172
173         if(target_pick)
174         {
175                 signal_ungrab_pointer.emit();
176                 target_pick = false;
177
178                 delete pick_highlight;
179                 pick_highlight = 0;
180
181                 if(picked_target && btn==1)
182                 {
183                         target = picked_target;
184                         lbl_target->set_text(target->get_name());
185                 }
186         }
187 }
188
189 void TimetablePanel::pointer_motion(int x, int y)
190 {
191         Panel::pointer_motion(x, y);
192
193         if(target_pick)
194         {
195                 int rx = x;
196                 int ry = y;
197                 map_coords_to_ancestor(rx, ry, *find_ancestor<GLtk::Root>());
198                 Ray ray = engineer.get_main_view().create_ray(rx, ry);
199                 Track *track = engineer.get_layout().pick<Track>(ray);
200                 if(track)
201                 {
202                         TrackChain *t = 0;
203                         if(!shift)
204                         {
205                                 const set<Zone *> &zones = engineer.get_layout().get_all<Zone>();
206                                 for(set<Zone *>::const_iterator i=zones.begin(); (!t && i!=zones.end()); ++i)
207                                         if((*i)->has_track(*track))
208                                                 t = *i;
209                         }
210
211                         if(!t)
212                                 t = &track->get_block();
213
214                         if(t!=picked_target)
215                         {
216                                 picked_target = t;
217                                 delete pick_highlight;
218                                 pick_highlight = new TrackChain3D(engineer.get_layout_3d(), *picked_target);
219                         }
220                 }
221         }
222 }
223
224
225 TimetableRowItem::TimetableRowItem(ValueType row)
226 {
227         if(row)
228         {
229                 add(*new GLtk::Label(format_time(row->time)));
230                 if(row->target)
231                 {
232                         string type;
233                         switch(row->type)
234                         {
235                         case Timetable::ARRIVE: type = "Arrive at "; break;
236                         case Timetable::DEPART: type = "Depart from "; break;
237                         case Timetable::THROUGH: type = "Go through "; break;
238                         }
239
240                         string dir;
241                         switch(row->direction)
242                         {
243                         case TrackChain::UP: dir = " up"; break;
244                         case TrackChain::DOWN: dir = " down"; break;
245                         default:;
246                         }
247
248                         add(*new GLtk::Label(type+row->target->get_name()+dir));
249                 }
250                 else
251                         add(*new GLtk::Label);
252         }
253         else
254         {
255                 add(*new GLtk::Label("--:--:--"));
256                 add(*new GLtk::Label("End of timetable"));
257         }
258 }