1 /*
2  * Dialog window for defining a schedule
3  *
4  * Hj. Malthaner
5  *
6  * Juli 2000
7  */
8 
9 #include "../simline.h"
10 #include "../simcolor.h"
11 #include "../simhalt.h"
12 #include "../simworld.h"
13 #include "../simmenu.h"
14 #include "../simconvoi.h"
15 #include "../display/simgraph.h"
16 #include "../display/viewport.h"
17 
18 #include "../utils/simstring.h"
19 #include "../utils/cbuffer_t.h"
20 
21 #include "../boden/grund.h"
22 
23 #include "../obj/zeiger.h"
24 
25 #include "../dataobj/schedule.h"
26 #include "../dataobj/loadsave.h"
27 #include "../dataobj/translator.h"
28 #include "../dataobj/environment.h"
29 
30 #include "../player/simplay.h"
31 
32 #include "../tpl/vector_tpl.h"
33 
34 #include "depot_frame.h"
35 #include "schedule_gui.h"
36 #include "line_item.h"
37 
38 #include "components/gui_button.h"
39 #include "components/gui_image.h"
40 #include "components/gui_textarea.h"
41 #include "minimap.h"
42 
43 static karte_ptr_t welt;
44 
45 /**
46  * One entry in the list of schedule entries.
47  */
48 class gui_schedule_entry_t : public gui_aligned_container_t, public gui_action_creator_t
49 {
50 	schedule_entry_t entry;
51 	bool is_current;
52 	uint number;
53 	player_t* player;
54 	gui_image_t arrow;
55 	gui_label_buf_t stop;
56 
57 public:
gui_schedule_entry_t(player_t * pl,schedule_entry_t e,uint n)58 	gui_schedule_entry_t(player_t* pl, schedule_entry_t e, uint n)
59 	{
60 		player = pl;
61 		entry  = e;
62 		number = n;
63 		is_current = false;
64 		set_table_layout(2,1);
65 
66 		add_component(&arrow);
67 		arrow.set_image(gui_theme_t::pos_button_img[0], true);
68 
69 		add_component(&stop);
70 		update_label();
71 	}
72 
update_label()73 	void update_label()
74 	{
75 		stop.buf().printf("%i) ", number+1);
76 		schedule_t::gimme_stop_name(stop.buf(), welt, player, entry, -1);
77 		stop.update();
78 	}
79 
draw(scr_coord offset)80 	void draw(scr_coord offset) OVERRIDE
81 	{
82 		update_label();
83 		if (is_current) {
84 			display_fillbox_wh_clip_rgb(pos.x + offset.x, pos.y + offset.y, size.w, size.h, SYSCOL_LIST_BACKGROUND_SELECTED_F, false);
85 		}
86 		gui_aligned_container_t::draw(offset);
87 	}
88 
set_active(bool yesno)89 	void set_active(bool yesno)
90 	{
91 		is_current = yesno;
92 		arrow.set_image(gui_theme_t::pos_button_img[yesno ? 1: 0], true);
93 		stop.set_color(yesno ? SYSCOL_TEXT_HIGHLIGHT : SYSCOL_TEXT);
94 	}
95 
infowin_event(const event_t * ev)96 	bool infowin_event(const event_t *ev) OVERRIDE
97 	{
98 		if( ev->ev_class == EVENT_CLICK ) {
99 			if(  IS_RIGHTCLICK(ev)  ||  ev->mx < stop.get_pos().x) {
100 				// just center on it
101 				welt->get_viewport()->change_world_position( entry.pos );
102 			}
103 			else {
104 				call_listeners(number);
105 			}
106 			return true;
107 		}
108 		return false;
109 	}
110 };
111 
112 /**
113  * List of displayed schedule entries.
114  */
115 class schedule_gui_stats_t : public gui_aligned_container_t, action_listener_t, public gui_action_creator_t
116 {
117 	static cbuffer_t buf;
118 
119 	vector_tpl<gui_schedule_entry_t*> entries;
120 	schedule_t *last_schedule; ///< last displayed schedule
121 	zeiger_t *current_stop_mark; ///< mark current stop on map
122 public:
123 	schedule_t *schedule;      ///< schedule under editing
124 	player_t*  player;
125 
schedule_gui_stats_t()126 	schedule_gui_stats_t()
127 	{
128 		set_table_layout(1,0);
129 		last_schedule = NULL;
130 
131 		current_stop_mark = new zeiger_t(koord3d::invalid, NULL );
132 		current_stop_mark->set_image( tool_t::general_tool[TOOL_SCHEDULE_ADD]->cursor );
133 	}
~schedule_gui_stats_t()134 	~schedule_gui_stats_t()
135 	{
136 		delete current_stop_mark;
137 		delete last_schedule;
138 	}
139 
140 	// shows/deletes highlighting of tiles
highlight_schedule(bool marking)141 	void highlight_schedule(bool marking)
142 	{
143 		marking &= env_t::visualize_schedule;
144 		FOR(minivec_tpl<schedule_entry_t>, const& i, schedule->entries) {
145 			if (grund_t* const gr = welt->lookup(i.pos)) {
146 				for(  uint idx=0;  idx<gr->get_top();  idx++  ) {
147 					obj_t *obj = gr->obj_bei(idx);
148 					if(  marking  ) {
149 						if(  !obj->is_moving()  ) {
150 							obj->set_flag( obj_t::highlight );
151 						}
152 					}
153 					else {
154 						obj->clear_flag( obj_t::highlight );
155 					}
156 				}
157 				gr->set_flag( grund_t::dirty );
158 				// here on water
159 				if(  gr->is_water()  ||  gr->ist_natur()  ) {
160 					if(  marking  ) {
161 						gr->set_flag( grund_t::marked );
162 					}
163 					else {
164 						gr->clear_flag( grund_t::marked );
165 					}
166 				}
167 
168 			}
169 		}
170 		// always remove
171 		if(  grund_t *old_gr = welt->lookup(current_stop_mark->get_pos())  ) {
172 			current_stop_mark->mark_image_dirty( current_stop_mark->get_image(), 0 );
173 			old_gr->obj_remove( current_stop_mark );
174 			old_gr->set_flag( grund_t::dirty );
175 			current_stop_mark->set_pos( koord3d::invalid );
176 		}
177 		// add if required
178 		if(  marking  &&  schedule->get_current_stop() < schedule->get_count() ) {
179 			current_stop_mark->set_pos( schedule->entries[schedule->get_current_stop()].pos );
180 			if(  grund_t *gr = welt->lookup(current_stop_mark->get_pos())  ) {
181 				gr->obj_add( current_stop_mark );
182 				current_stop_mark->set_flag( obj_t::dirty );
183 				gr->set_flag( grund_t::dirty );
184 			}
185 		}
186 		current_stop_mark->clear_flag( obj_t::highlight );
187 	}
188 
update_schedule()189 	void update_schedule()
190 	{
191 		// compare schedules
192 		bool ok = (last_schedule != NULL)  &&  last_schedule->entries.get_count() == schedule->entries.get_count();
193 		for(uint i=0; ok  &&  i<last_schedule->entries.get_count(); i++) {
194 			ok = last_schedule->entries[i] == schedule->entries[i];
195 		}
196 		if (ok) {
197 			if (!last_schedule->empty()) {
198 				entries[ last_schedule->get_current_stop() ]->set_active(false);
199 				entries[ schedule->get_current_stop() ]->set_active(true);
200 				last_schedule->set_current_stop( schedule->get_current_stop() );
201 			}
202 		}
203 		else {
204 			remove_all();
205 			entries.clear();
206 			buf.clear();
207 			buf.append(translator::translate("Please click on the map to add\nwaypoints or stops to this\nschedule."));
208 			if (schedule->empty()) {
209 				new_component<gui_textarea_t>(&buf);
210 			}
211 			else {
212 				for(uint i=0; i<schedule->entries.get_count(); i++) {
213 					entries.append( new_component<gui_schedule_entry_t>(player, schedule->entries[i], i));
214 					entries.back()->add_listener( this );
215 				}
216 				entries[ schedule->get_current_stop() ]->set_active(true);
217 			}
218 			if (last_schedule) {
219 				last_schedule->copy_from(schedule);
220 			}
221 			else {
222 				last_schedule = schedule->copy();
223 			}
224 			set_size(get_min_size());
225 		}
226 		highlight_schedule(true);
227 	}
draw(scr_coord offset)228 	void draw(scr_coord offset) OVERRIDE
229 	{
230 		update_schedule();
231 
232 		gui_aligned_container_t::draw(offset);
233 	}
action_triggered(gui_action_creator_t *,value_t v)234 	bool action_triggered(gui_action_creator_t *, value_t v) OVERRIDE
235 	{
236 		// has to be one of the entries
237 		call_listeners(v);
238 		return true;
239 	}
240 };
241 
242 /**
243  * Entries in the waiting-time selection.
244  */
245 class gui_waiting_time_item_t : public gui_scrolled_list_t::const_text_scrollitem_t
246 {
247 private:
248 	cbuffer_t buf;
249 	sint8 wait;
250 
251 public:
gui_waiting_time_item_t(sint8 w)252 	gui_waiting_time_item_t(sint8 w) : gui_scrolled_list_t::const_text_scrollitem_t(NULL, SYSCOL_TEXT)
253 	{
254 		wait = w;
255 		if (wait == 0) {
256 			buf.append(translator::translate("off"));
257 		}
258 		else {
259 			buf.printf("1/%d",  1<<(16 - wait) );
260 		}
261 	}
262 
get_text() const263 	char const* get_text () const OVERRIDE { return buf; }
264 
get_wait_shift() const265 	sint8 get_wait_shift() const { return wait; }
266 };
267 
268 cbuffer_t schedule_gui_stats_t::buf;
269 
schedule_gui_t(schedule_t * schedule_,player_t * player_,convoihandle_t cnv_)270 schedule_gui_t::schedule_gui_t(schedule_t* schedule_, player_t* player_, convoihandle_t cnv_) :
271 	gui_frame_t( translator::translate("Fahrplan"), NULL),
272 	line_selector(line_scrollitem_t::compare),
273 	lb_waitlevel(SYSCOL_TEXT_HIGHLIGHT, gui_label_t::right),
274 	lb_wait("month wait time"),
275 	lb_load("Full load"),
276 	stats(new schedule_gui_stats_t() ),
277 	scrolly(stats)
278 {
279 	schedule = NULL;
280 	player   = NULL;
281 	if (schedule_) {
282 		init(schedule_, player_, cnv_);
283 	}
284 }
285 
~schedule_gui_t()286 schedule_gui_t::~schedule_gui_t()
287 {
288 	if(  player  ) {
289 		update_tool( false );
290 		// hide schedule on minimap (may not current, but for safe)
291 		minimap_t::get_instance()->set_selected_cnv( convoihandle_t() );
292 	}
293 	delete schedule;
294 	delete stats;
295 }
296 
init(schedule_t * schedule_,player_t * player,convoihandle_t cnv)297 void schedule_gui_t::init(schedule_t* schedule_, player_t* player, convoihandle_t cnv)
298 {
299 	// initialization
300 	this->old_schedule = schedule_;
301 	this->cnv = cnv;
302 	this->player = player;
303 	set_owner(player);
304 
305 	// prepare editing
306 	old_schedule->start_editing();
307 	schedule = old_schedule->copy();
308 	if(  !cnv.is_bound()  ) {
309 		old_line = new_line = linehandle_t();
310 	}
311 	else {
312 		// set this schedule as current to show on minimap if possible
313 		minimap_t::get_instance()->set_selected_cnv( cnv );
314 		old_line = new_line = cnv->get_line();
315 	}
316 	old_line_count = 0;
317 
318 	stats->player = player;
319 	stats->schedule = schedule;
320 	stats->update_schedule();
321 	stats->add_listener(this);
322 
323 	set_table_layout(1,0);
324 
325 	if(  cnv.is_bound()  ) {
326 		add_table(3,1);
327 		// things, only relevant to convois, like creating/selecting lines
328 		new_component<gui_label_t>("Serves Line:");
329 		bt_promote_to_line.init( button_t::roundbox, "promote to line");
330 		bt_promote_to_line.set_tooltip("Create a new line based on this schedule");
331 		bt_promote_to_line.add_listener(this);
332 		new_component<gui_fill_t>();
333 		add_component(&bt_promote_to_line);
334 		end_table();
335 
336 		line_selector.clear_elements();
337 
338 		init_line_selector();
339 		line_selector.add_listener(this);
340 		add_component(&line_selector);
341 	}
342 
343 	// loading level and waiting time
344 	add_table(2,2);
345 	{
346 		add_component(&lb_load);
347 
348 		numimp_load.set_width( 60 );
349 		numimp_load.set_value( schedule->get_current_entry().minimum_loading );
350 		numimp_load.set_limits( 0, 100 );
351 		numimp_load.set_increment_mode( gui_numberinput_t::PROGRESS );
352 		numimp_load.add_listener(this);
353 		add_component(&numimp_load);
354 
355 		add_component(&lb_wait);
356 
357 		add_component(&wait_load);
358 		wait_load.add_listener(this);
359 
360 		wait_load.new_component<gui_waiting_time_item_t>(0);
361 		for(sint8 w = 7; w<=16; w++) {
362 			wait_load.new_component<gui_waiting_time_item_t>(w);
363 		}
364 		wait_load.set_rigid(true);
365 	}
366 	end_table();
367 
368 	// return tickets
369 	if(  !env_t::hide_rail_return_ticket  ||  schedule->get_waytype()==road_wt  ||  schedule->get_waytype()==air_wt  ||  schedule->get_waytype()==water_wt  ) {
370 		//  hide the return ticket on rail stuff, where it causes much trouble
371 		bt_return.init(button_t::roundbox, "return ticket");
372 		bt_return.set_tooltip("Add stops for backward travel");
373 		bt_return.add_listener(this);
374 		add_component(&bt_return);
375 	}
376 
377 	// action button row
378 	add_table(3,1)->set_force_equal_columns(true);
379 	bt_add.init(button_t::roundbox_state | button_t::flexible, "Add Stop");
380 	bt_add.set_tooltip("Appends stops at the end of the schedule");
381 	bt_add.add_listener(this);
382 	bt_add.pressed = true;
383 	add_component(&bt_add);
384 
385 	bt_insert.init(button_t::roundbox_state | button_t::flexible, "Ins Stop");
386 	bt_insert.set_tooltip("Insert stop before the current stop");
387 	bt_insert.add_listener(this);
388 	bt_insert.pressed = false;
389 	add_component(&bt_insert);
390 
391 	bt_remove.init(button_t::roundbox_state | button_t::flexible, "Del Stop");
392 	bt_remove.set_tooltip("Delete the current stop");
393 	bt_remove.add_listener(this);
394 	bt_remove.pressed = false;
395 	add_component(&bt_remove);
396 	end_table();
397 
398 	scrolly.set_show_scroll_x(true);
399 	scrolly.set_scroll_amount_y(LINESPACE+1);
400 	add_component(&scrolly);
401 
402 	mode = adding;
403 	update_selection();
404 
405 	set_resizemode(diagonal_resize);
406 
407 	reset_min_windowsize();
408 	set_windowsize(get_min_windowsize());
409 }
410 
411 
update_tool(bool set)412 void schedule_gui_t::update_tool(bool set)
413 {
414 	if(!set  ||  mode==removing  ||  mode==undefined_mode) {
415 		// reset tools, if still selected ...
416 		if(welt->get_tool(player->get_player_nr())==tool_t::general_tool[TOOL_SCHEDULE_ADD]) {
417 			if(tool_t::general_tool[TOOL_SCHEDULE_ADD]->get_default_param()==(const char *)schedule) {
418 				welt->set_tool( tool_t::general_tool[TOOL_QUERY], player );
419 			}
420 		}
421 		else if(welt->get_tool(player->get_player_nr())==tool_t::general_tool[TOOL_SCHEDULE_INS]) {
422 			if(tool_t::general_tool[TOOL_SCHEDULE_INS]->get_default_param()==(const char *)schedule) {
423 				welt->set_tool( tool_t::general_tool[TOOL_QUERY], player );
424 			}
425 		}
426 	}
427 	else {
428 		//  .. or set them again
429 		if(mode==adding) {
430 			tool_t::general_tool[TOOL_SCHEDULE_ADD]->set_default_param((const char *)schedule);
431 			welt->set_tool( tool_t::general_tool[TOOL_SCHEDULE_ADD], player );
432 		}
433 		else if(mode==inserting) {
434 			tool_t::general_tool[TOOL_SCHEDULE_INS]->set_default_param((const char *)schedule);
435 			welt->set_tool( tool_t::general_tool[TOOL_SCHEDULE_INS], player );
436 		}
437 	}
438 }
439 
440 
update_selection()441 void schedule_gui_t::update_selection()
442 {
443 	lb_wait.set_color( SYSCOL_BUTTON_TEXT_DISABLED );
444 	wait_load.disable();
445 
446 	if(  !schedule->empty()  ) {
447 		schedule->set_current_stop( min(schedule->get_count()-1,schedule->get_current_stop()) );
448 		const uint8 current_stop = schedule->get_current_stop();
449 		if(  haltestelle_t::get_halt(schedule->entries[current_stop].pos, player).is_bound()  ) {
450 			lb_load.set_color( SYSCOL_TEXT );
451 			numimp_load.enable();
452 			numimp_load.set_value( schedule->entries[current_stop].minimum_loading );
453 
454 			sint8 wait = 0;
455 			if(  schedule->entries[current_stop].minimum_loading>0  ) {
456 				lb_wait.set_color( SYSCOL_TEXT );
457 				wait_load.enable();
458 
459 				wait = schedule->entries[current_stop].waiting_time_shift;
460 			}
461 
462 			for(int i=0; i<wait_load.count_elements(); i++) {
463 				if (gui_waiting_time_item_t *item = dynamic_cast<gui_waiting_time_item_t*>( wait_load.get_element(i) ) ) {
464 					if (item->get_wait_shift() == wait) {
465 						wait_load.set_selection(i);
466 						break;
467 					}
468 				}
469 			}
470 
471 		}
472 		else {
473 			lb_load.set_color( SYSCOL_BUTTON_TEXT_DISABLED );
474 			numimp_load.disable();
475 			numimp_load.set_value( 0 );
476 		}
477 	}
478 }
479 
480 
481 /**
482  * Mouse clicks are hereby reported to its GUI-Components
483  */
infowin_event(const event_t * ev)484 bool schedule_gui_t::infowin_event(const event_t *ev)
485 {
486 	if( (ev)->ev_class == EVENT_CLICK  &&  !((ev)->ev_code==MOUSE_WHEELUP  ||  (ev)->ev_code==MOUSE_WHEELDOWN)  &&  !line_selector.getroffen(ev->cx, ev->cy-D_TITLEBAR_HEIGHT)  )  {
487 
488 		// close combo box; we must do it ourselves, since the box does not receive outside events ...
489 		line_selector.close_box();
490 	}
491 	else if(  ev->ev_class == INFOWIN  &&  ev->ev_code == WIN_CLOSE  &&  schedule!=NULL  ) {
492 
493 		stats->highlight_schedule( false );
494 
495 		update_tool( false );
496 		schedule->cleanup();
497 		schedule->finish_editing();
498 		// now apply the changes
499 		if(  cnv.is_bound()  ) {
500 			// do not send changes if the convoi is about to be deleted
501 			if(  cnv->get_state() != convoi_t::SELF_DESTRUCT  ) {
502 				// if a line is selected
503 				if(  new_line.is_bound()  ) {
504 					// if the selected line is different to the convoi's line, apply it
505 					if(  new_line!=cnv->get_line()  ) {
506 						char id[16];
507 						sprintf( id, "%i,%i", new_line.get_id(), schedule->get_current_stop() );
508 						cnv->call_convoi_tool( 'l', id );
509 					}
510 					else {
511 						cbuffer_t buf;
512 						schedule->sprintf_schedule( buf );
513 						cnv->call_convoi_tool( 'g', buf );
514 					}
515 				}
516 				else {
517 					cbuffer_t buf;
518 					schedule->sprintf_schedule( buf );
519 					cnv->call_convoi_tool( 'g', buf );
520 				}
521 
522 				if(  cnv->in_depot()  ) {
523 					const grund_t *const ground = welt->lookup( cnv->get_home_depot() );
524 					if(  ground  ) {
525 						const depot_t *const depot = ground->get_depot();
526 						if(  depot  ) {
527 							depot_frame_t *const frame = dynamic_cast<depot_frame_t *>( win_get_magic( (ptrdiff_t)depot ) );
528 							if(  frame  ) {
529 								frame->update_data();
530 							}
531 						}
532 					}
533 				}
534 			}
535 		}
536 	}
537 	else if(  ev->ev_class == INFOWIN  &&  (ev->ev_code == WIN_TOP  ||  ev->ev_code == WIN_OPEN)  &&  schedule!=NULL  ) {
538 		// just to be sure, renew the tools ...
539 		update_tool( true );
540 	}
541 
542 	return gui_frame_t::infowin_event(ev);
543 }
544 
545 
action_triggered(gui_action_creator_t * comp,value_t p)546 bool schedule_gui_t::action_triggered( gui_action_creator_t *comp, value_t p)
547 {
548 DBG_MESSAGE("schedule_gui_t::action_triggered()","comp=%p combo=%p",comp,&line_selector);
549 
550 	if(comp == &bt_add) {
551 		mode = adding;
552 		bt_add.pressed = true;
553 		bt_insert.pressed = false;
554 		bt_remove.pressed = false;
555 		update_tool( true );
556 	}
557 	else if(comp == &bt_insert) {
558 		mode = inserting;
559 		bt_add.pressed = false;
560 		bt_insert.pressed = true;
561 		bt_remove.pressed = false;
562 		update_tool( true );
563 	}
564 	else if(comp == &bt_remove) {
565 		mode = removing;
566 		bt_add.pressed = false;
567 		bt_insert.pressed = false;
568 		bt_remove.pressed = true;
569 		update_tool( false );
570 	}
571 	else if(comp == &numimp_load) {
572 		if (!schedule->empty()) {
573 			schedule->entries[schedule->get_current_stop()].minimum_loading = (uint8)p.i;
574 			update_selection();
575 		}
576 	}
577 	else if(comp == &wait_load) {
578 		if(!schedule->empty()) {
579 			if (gui_waiting_time_item_t *item = dynamic_cast<gui_waiting_time_item_t*>( wait_load.get_selected_item())) {
580 				schedule->entries[schedule->get_current_stop()].waiting_time_shift = item->get_wait_shift();
581 
582 				update_selection();
583 			}
584 		}
585 	}
586 	else if(comp == &bt_return) {
587 		schedule->add_return_way();
588 	}
589 	else if(comp == &line_selector) {
590 		uint32 selection = p.i;
591 		if(  line_scrollitem_t *li = dynamic_cast<line_scrollitem_t*>(line_selector.get_element(selection))  ) {
592 			new_line = li->get_line();
593 			stats->highlight_schedule( false );
594 			schedule->copy_from( new_line->get_schedule() );
595 			schedule->start_editing();
596 		}
597 		else {
598 			// remove line
599 			new_line = linehandle_t();
600 			line_selector.set_selection( 0 );
601 		}
602 	}
603 	else if(comp == &bt_promote_to_line) {
604 		// update line schedule via tool!
605 		tool_t *tool = create_tool( TOOL_CHANGE_LINE | SIMPLE_TOOL );
606 		cbuffer_t buf;
607 		buf.printf( "c,0,%i,%ld,", (int)schedule->get_type(), (long)old_schedule );
608 		schedule->sprintf_schedule( buf );
609 		tool->set_default_param(buf);
610 		welt->set_tool( tool, player );
611 		// since init always returns false, it is safe to delete immediately
612 		delete tool;
613 	}
614 	else if (comp == stats) {
615 		// click on one of the schedule entries
616 		const int line = p.i;
617 
618 		if(  line >= 0 && line < schedule->get_count()  ) {
619 			schedule->set_current_stop( line );
620 			if(  mode == removing  ) {
621 				stats->highlight_schedule( false );
622 				schedule->remove();
623 				action_triggered( &bt_add, value_t() );
624 			}
625 			update_selection();
626 		}
627 	}
628 	// recheck lines
629 	if(  cnv.is_bound()  ) {
630 		// unequal to line => remove from line ...
631 		if(  new_line.is_bound()  &&  !schedule->matches(welt,new_line->get_schedule())  ) {
632 			new_line = linehandle_t();
633 			line_selector.set_selection(0);
634 		}
635 		// only assign old line, when new_line is not equal
636 		if(  !new_line.is_bound()  &&  old_line.is_bound()  &&   schedule->matches(welt,old_line->get_schedule())  ) {
637 			new_line = old_line;
638 			init_line_selector();
639 		}
640 	}
641 	return true;
642 }
643 
644 
init_line_selector()645 void schedule_gui_t::init_line_selector()
646 {
647 	line_selector.clear_elements();
648 	int selection = 0;
649 	vector_tpl<linehandle_t> lines;
650 
651 	player->simlinemgmt.get_lines(schedule->get_type(), &lines);
652 
653 	// keep assignment with identical schedules
654 	if(  new_line.is_bound()  &&  !schedule->matches( welt, new_line->get_schedule() )  ) {
655 		if(  old_line.is_bound()  &&  schedule->matches( welt, old_line->get_schedule() )  ) {
656 			new_line = old_line;
657 		}
658 		else {
659 			new_line = linehandle_t();
660 		}
661 	}
662 	int offset = 0;
663 	if(  !new_line.is_bound()  ) {
664 		selection = 0;
665 		offset = 1;
666 		line_selector.new_component<gui_scrolled_list_t::const_text_scrollitem_t>( translator::translate("<no line>"), SYSCOL_TEXT ) ;
667 	}
668 
669 	FOR(  vector_tpl<linehandle_t>,  line,  lines  ) {
670 		line_selector.new_component<line_scrollitem_t>(line) ;
671 		if(  !new_line.is_bound()  ) {
672 			if(  schedule->matches( welt, line->get_schedule() )  ) {
673 				selection = line_selector.count_elements()-1;
674 				new_line = line;
675 			}
676 		}
677 		else if(  new_line == line  ) {
678 			selection = line_selector.count_elements()-1;
679 		}
680 	}
681 
682 	line_selector.set_selection( selection );
683 	line_scrollitem_t::sort_mode = line_scrollitem_t::SORT_BY_NAME;
684 	line_selector.sort( offset );
685 	old_line_count = player->simlinemgmt.get_line_count();
686 	last_schedule_count = schedule->get_count();
687 }
688 
689 
690 
draw(scr_coord pos,scr_size size)691 void schedule_gui_t::draw(scr_coord pos, scr_size size)
692 {
693 	if(  player->simlinemgmt.get_line_count()!=old_line_count  ||  last_schedule_count!=schedule->get_count()  ) {
694 		// lines added or deleted
695 		init_line_selector();
696 		last_schedule_count = schedule->get_count();
697 	}
698 
699 	// after loading in network games, the schedule might still being updated
700 	if(  cnv.is_bound()  &&  cnv->get_state()==convoi_t::EDIT_SCHEDULE  &&  schedule->is_editing_finished()  ) {
701 		assert( convoi_t::EDIT_SCHEDULE==1 ); // convoi_t::EDIT_SCHEDULE is 1
702 		schedule->start_editing();
703 		cnv->call_convoi_tool( 's', "1" );
704 	}
705 
706 	// always dirty, to cater for shortening of halt names and change of selections
707 	set_dirty();
708 	gui_frame_t::draw(pos,size);
709 }
710 
711 
712 /**
713  * Set window size and adjust component sizes and/or positions accordingly
714  * @author Hj. Malthaner
715  * @date   16-Oct-2003
716  */
set_windowsize(scr_size size)717 void schedule_gui_t::set_windowsize(scr_size size)
718 {
719 	gui_frame_t::set_windowsize(size);
720 	// manually enlarge size of wait_load combobox
721 	wait_load.set_size( scr_size(numimp_load.get_size().w, wait_load.get_size().h) );
722 	// make scrolly take all of space
723 	scrolly.set_size( scr_size(scrolly.get_size().w, get_client_windowsize().h - scrolly.get_pos().y - D_MARGIN_BOTTOM));
724 
725 }
726 
727 
map_rotate90(sint16 y_size)728 void schedule_gui_t::map_rotate90( sint16 y_size)
729 {
730 	schedule->rotate90(y_size);
731 }
732 
733 
rdwr(loadsave_t * file)734 void schedule_gui_t::rdwr(loadsave_t *file)
735 {
736 	// this handles only schedules of bound convois
737 	// lines are handled by line_management_gui_t
738 
739 	// window size
740 	scr_size size = get_windowsize();
741 	size.rdwr( file );
742 
743 	// convoy data
744 	convoi_t::rdwr_convoihandle_t(file, cnv);
745 
746 	// save edited schedule
747 	if(  file->is_loading()  ) {
748 		// dummy types
749 		schedule = new truck_schedule_t();
750 	}
751 	schedule->rdwr(file);
752 
753 	if(  file->is_loading()  ) {
754 		if(  cnv.is_bound() ) {
755 
756 			schedule_t *save_schedule = schedule->copy();
757 
758 			init(cnv->get_schedule(), cnv->get_owner(), cnv);
759 			// init replaced schedule, restore
760 			schedule->copy_from(save_schedule);
761 			delete save_schedule;
762 
763 			set_windowsize(size);
764 
765 			// draw will init editing phase again, has to be synchronized
766 			cnv->get_schedule()->finish_editing();
767 			schedule->finish_editing();
768 
769 			win_set_magic(this, (ptrdiff_t)cnv->get_schedule());
770 		}
771 		else {
772 			player = NULL; // prevent destructor from updating
773 			destroy_win( this );
774 			dbg->error( "schedule_gui_t::rdwr", "Could not restore schedule window for (%d)", cnv.get_id() );
775 		}
776 	}
777 }
778