]> git.tdb.fi Git - r2c2.git/blob - source/designer/designer.cpp
Add an internal layout to Catalogue for selecting tracks
[r2c2.git] / source / designer / designer.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <signal.h>
9 #include <cmath>
10 #include <GL/gl.h>
11 #include <msp/gl/blend.h>
12 #include <msp/gl/matrix.h>
13 #include <msp/gl/misc.h>
14 #include <msp/gl/projection.h>
15 #include <msp/gl/rendermode.h>
16 #include <msp/gl/select.h>
17 #include <msp/gl/tests.h>
18 #include <msp/gl/texture2d.h>
19 #include <msp/input/keys.h>
20 #include <msp/io/print.h>
21 #include <msp/strings/codec.h>
22 #include <msp/strings/lexicalcast.h>
23 #include <msp/strings/utf8.h>
24 #include <msp/strings/utils.h>
25 #include <msp/time/units.h>
26 #include <msp/time/utils.h>
27 #include "libmarklin/tracktype.h"
28 #include "designer.h"
29 #include "input.h"
30 #include "manipulator.h"
31 #include "measure.h"
32 #include "selection.h"
33 #include "toolbar.h"
34
35 using namespace std;
36 using namespace Marklin;
37 using namespace Msp;
38
39 Application::RegApp<Designer> Designer::reg;
40
41 Designer::Designer(int argc, char **argv):
42         window(1280, 960),
43         base_object(0),
44         cur_route(0),
45         mode(SELECT),
46         manipulator(*this, selection),
47         measure(*this),
48         input(0),
49         cam_yaw(M_PI/2),
50         cam_pitch(-M_PI/4),
51         cam_pos(0, -0.5, 0.5),
52         shift(false),
53         move_x(0),
54         move_y(0),
55         zoom(0),
56         rotate(0),
57         pitch(0)
58 {
59         window.set_title("Railway Designer");
60         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Designer::exit), 0));
61         window.signal_key_press.connect(sigc::mem_fun(this, &Designer::key_press));
62         window.signal_key_release.connect(sigc::mem_fun(this, &Designer::key_release));
63         window.signal_button_press.connect(sigc::mem_fun(this, &Designer::button_press));
64         window.signal_pointer_motion.connect(sigc::mem_fun(this, &Designer::pointer_motion));
65
66         manipulator.signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
67         manipulator.signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done));
68         measure.signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed));
69         measure.signal_done.connect(sigc::mem_fun(this, &Designer::measure_done));
70
71         // Setup catalogue and layout
72         DataFile::load(catalogue, "tracks.dat");
73
74         cat_layout_3d = new Layout3D(catalogue.get_layout());
75
76         layout = new Layout(catalogue);
77         layout_3d = new Layout3D(*layout);
78
79         if(argc>1)
80         {
81                 filename = argv[1];
82                 DataFile::load(*layout, argv[1]);
83                 const list<Track3D *> &ltracks = layout_3d->get_tracks();
84                 for(list<Track3D *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
85                         update_track_color(**i);
86
87                 if(!layout->get_base().empty())
88                 {
89                         base_object = new GL::Object;
90                         DataFile::load(*base_object, layout->get_base());
91                 }
92         }
93
94         // Setup OpenGL
95         GL::enable(GL::DEPTH_TEST);
96         GL::enable(GL::BLEND);
97         GL::blend_func(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);
98         GL::enable(GL_CULL_FACE);
99
100         pipeline = new GL::Pipeline(window.get_width(), window.get_height(), false);
101         pipeline->add_renderable(layout_3d->get_scene());
102         if(base_object)
103                 pipeline->add_renderable(*base_object);
104
105         light.set_position(0, -0.259, 0.966, 0);
106         lighting.attach(0, light);
107
108         GL::PipelinePass *pass = &pipeline->add_pass(GL::Tag());
109         pass->lighting = &lighting;
110
111         // Setup UI
112         DataFile::load(ui_res, "marklin.res");
113         root = new GLtk::Root(ui_res, window);
114
115         lbl_tooltip = new GLtk::Label(ui_res);
116         lbl_tooltip->set_style("tooltip");
117         root->add(*lbl_tooltip);
118         lbl_tooltip->set_visible(false);
119
120         toolbar = new Toolbar(*this);
121         root->add(*toolbar);
122         toolbar->set_position(0, window.get_height()-toolbar->get_geometry().h);
123         toolbar->set_visible(true);
124 }
125
126 Designer::~Designer()
127 {
128         delete root;
129         delete layout;
130         delete layout_3d;
131         delete cat_layout_3d;
132 }
133
134 int Designer::main()
135 {
136         window.show();
137
138         mode = SELECT;
139
140         return Application::main();
141 }
142
143 void Designer::save()
144 {
145         input = new ::Input(*this, "Save layout", filename);
146         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
147         input->signal_accept.connect(sigc::mem_fun(this, &Designer::save_accept));
148         mode = INPUT;
149 }
150
151 void Designer::quit()
152 {
153         exit(0);
154 }
155
156 void Designer::edit_route(Route &r)
157 {
158         cur_route = &r;
159 }
160
161 void Designer::add_selection_to_route()
162 {
163         if(!cur_route)
164                 return;
165
166         try
167         {
168                 const set<Track *> &stracks = selection.get_tracks();
169                 set<const Track *> tracks(stracks.begin(), stracks.end());
170                 cur_route->add_tracks(tracks);
171         }
172         catch(const Exception &e)
173         {
174                 IO::print("%s\n", e.what());
175         }
176 }
177
178 void Designer::map_pointer_coords(int x, int y, float &gx, float &gy)
179 {
180         float cos_pitch = cos(cam_pitch);
181         float sin_pitch = sin(cam_pitch);
182         float cos_yaw = cos(cam_yaw);
183         float sin_yaw = sin(cam_yaw);
184
185         float rx = sin_yaw*0.55228;
186         float ry = -cos_yaw*0.55228;
187
188         float ux = cos_yaw*-sin_pitch*0.41421;
189         float uy = sin_yaw*-sin_pitch*0.41421;
190         float uz = cos_pitch*0.41421;
191
192         float xf = static_cast<float>(x)*2/window.get_width()-1;
193         float yf = static_cast<float>(y)*2/window.get_height()-1;
194
195         float vx = cos_yaw*cos_pitch + xf*rx + yf*ux;
196         float vy = sin_yaw*cos_pitch + xf*ry + yf*uy;
197         float vz = sin_pitch + yf*uz;
198
199         gx = cam_pos.x-vx*cam_pos.z/vz;
200         gy = cam_pos.y-vy*cam_pos.z/vz;
201 }
202
203 void Designer::tick()
204 {
205         const Msp::Time::TimeStamp t = Msp::Time::now();
206         float dt = (t-last_tick)/Msp::Time::sec;
207         last_tick = t;
208
209         window.get_display().tick();
210
211         if(move_y)
212         {
213                 cam_pos.x += cos(cam_yaw)*dt*move_y;
214                 cam_pos.y += sin(cam_yaw)*dt*move_y;
215         }
216         if(move_x)
217         {
218                 cam_pos.x += sin(cam_yaw)*dt*move_x;
219                 cam_pos.y += -cos(cam_yaw)*dt*move_x;
220         }
221         if(zoom)
222         {
223                 cam_pos.x += cos(cam_yaw)*cos(cam_pitch)*dt*zoom;
224                 cam_pos.y += sin(cam_yaw)*cos(cam_pitch)*dt*zoom;
225                 cam_pos.z += sin(cam_pitch)*dt*zoom;
226         }
227         if(rotate)
228         {
229                 float vx = cos(cam_yaw)*cos(cam_pitch);
230                 float vy = sin(cam_yaw)*cos(cam_pitch);
231                 float vz = sin(cam_pitch);
232
233                 float gx = cam_pos.x-vx*cam_pos.z/vz;
234                 float gy = cam_pos.y-vy*cam_pos.z/vz;
235                 float d = sqrt(vx*vx+vy*vy)*cam_pos.z/vz;
236
237                 cam_yaw += M_PI*dt*rotate;
238                 if(cam_yaw>M_PI*2)
239                         cam_yaw -= M_PI*2;
240                 else if(cam_yaw<0)
241                         cam_yaw += M_PI*2;
242
243                 cam_pos.x = gx+cos(cam_yaw)*d;
244                 cam_pos.y = gy+sin(cam_yaw)*d;
245         }
246         if(pitch)
247         {
248                 cam_pitch += M_PI/2*dt*pitch;
249                 if(cam_pitch>M_PI/12)
250                         cam_pitch = M_PI/12;
251                 else if(cam_pitch<-M_PI/2)
252                         cam_pitch = -M_PI/2;
253         }
254
255         if(tooltip_timeout && t>tooltip_timeout)
256         {
257                 Track3D *t3d = 0;
258
259                 if(mode==CATALOGUE)
260                         t3d = pick_track(pointer_x, pointer_y);
261                 else
262                         t3d = pick_track(pointer_x, pointer_y);
263
264                 if(t3d)
265                 {
266                         const Track &track = t3d->get_track();
267                         const TrackType &ttype = track.get_type();
268                         string info = format("%d %s", ttype.get_article_number(), ttype.get_description());
269                         if(mode!=CATALOGUE && abs(track.get_slope())>1e-4)
270                                 info += format(" (slope %.1f%%)", abs(track.get_slope()/ttype.get_total_length()*100));
271                         if(track.get_turnout_id())
272                                 info += format(" (turnout %d)", track.get_turnout_id());
273                         else if(track.get_sensor_id())
274                                 info += format(" (sensor %d)", track.get_sensor_id());
275
276                         set_tooltip(pointer_x, pointer_y, info);
277                 }
278                 else
279                         clear_tooltip();
280
281                 tooltip_timeout = Msp::Time::TimeStamp();
282         }
283
284         render();
285
286         window.swap_buffers();
287 }
288
289 void Designer::key_press(unsigned code, unsigned mod, wchar_t)
290 {
291         unsigned key = Msp::Input::key_from_sys(code);
292
293         if(mode==INPUT)
294                 return;
295
296         if(key==Msp::Input::KEY_SHIFT_L || key==Msp::Input::KEY_SHIFT_R)
297                 shift = true;
298
299         if(key==Msp::Input::KEY_N)
300                 mode = CATALOGUE;
301         else if(key==Msp::Input::KEY_G)
302         {
303                 manipulator.start_move();
304                 mode = MANIPULATE;
305         }
306         else if(key==Msp::Input::KEY_R)
307         {
308                 manipulator.start_rotate();
309                 mode = MANIPULATE;
310         }
311         else if(key==Msp::Input::KEY_D)
312         {
313                 manipulator.duplicate();
314                 manipulator.start_move();
315                 mode = MANIPULATE;
316         }
317         else if(key==Msp::Input::KEY_W)
318                 save();
319         else if(key==Msp::Input::KEY_PLUS)
320                 selection.select_more();
321         else if(key==Msp::Input::KEY_L && (mod&1))
322         {
323                 const set<Track *> &tracks = layout->get_tracks();
324                 float len = 0;
325                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
326                         len += (*i)->get_type().get_total_length();
327                 IO::print("Total length: %.1fm\n", len);
328         }
329         else if(key==Msp::Input::KEY_L)
330                 selection.select_linked();
331         else if(key==Msp::Input::KEY_M)
332         {
333                 measure.start();
334                 mode = MEASURE;
335         }
336         else if(key==Msp::Input::KEY_Z)
337         {
338                 manipulator.start_elevate();
339                 mode = MANIPULATE;
340         }
341         else if(key==Msp::Input::KEY_ESC)
342         {
343                 if(mode==MANIPULATE)
344                         manipulator.cancel();
345                 else if(mode==CATALOGUE)
346                         mode = SELECT;
347                 else
348                         selection.clear();
349         }
350         else if(key==Msp::Input::KEY_X)
351         {
352                 set<Track *> tracks = selection.get_tracks();
353                 selection.clear();
354                 for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
355                 {
356                         layout->remove_track(**i);
357                         delete *i;
358                 }
359         }
360         else if(key==Msp::Input::KEY_F && (mod&1))
361         {
362                 const set<Track *> &tracks = selection.get_tracks();
363                 const set<Track *> &ltracks = layout->get_tracks();
364                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
365                 {
366                         (*i)->set_flex(!(*i)->get_flex());
367                         (*i)->break_links();
368                         for(set<Track *>::const_iterator j=ltracks.begin(); j!=ltracks.end(); ++j)
369                                 if(*j!=*i)
370                                         (*i)->snap_to(**j, true);
371
372                         update_track_color(layout_3d->get_track(**i));
373                 }
374         }
375         else if(key==Msp::Input::KEY_F)
376                 manipulator.flatten();
377         else if(key==Msp::Input::KEY_E && (mod&1))
378                 manipulator.even_slope(true);
379         else if(key==Msp::Input::KEY_E)
380                 manipulator.even_slope();
381         else if(key==Msp::Input::KEY_T)
382         {
383                 Track *track = selection.get_track();
384                 if(selection.size()==1 && track->get_type().get_n_paths()>1)
385                 {
386                         input = new ::Input(*this, "Turnout ID", lexical_cast(track->get_turnout_id()));
387                         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
388                         input->signal_accept.connect(sigc::mem_fun(this, &Designer::turnout_id_accept));
389                         mode = INPUT;
390                 }
391         }
392         else if(key==Msp::Input::KEY_S)
393         {
394                 const set<Track *> &tracks = selection.get_tracks();
395                 bool ok = false;
396                 int id = -1;
397                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
398                 {
399                         if((*i)->get_type().get_n_paths()==1)
400                                 ok = true;
401                         if(static_cast<int>((*i)->get_sensor_id())!=id)
402                         {
403                                 if(id==-1)
404                                         id = (*i)->get_sensor_id();
405                                 else
406                                         id = -2;
407                         }
408                 }
409                 if(ok)
410                 {
411                         input = new ::Input(*this, "Sensor ID", (id>=0 ? lexical_cast(id) : string()));
412                         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
413                         input->signal_accept.connect(sigc::mem_fun(this, &Designer::sensor_id_accept));
414                         mode = INPUT;
415                 }
416         }
417         else if(key==Msp::Input::KEY_A)
418                 add_selection_to_route();
419         else if(key==Msp::Input::KEY_RIGHT)
420                 rotate = -1;
421         else if(key==Msp::Input::KEY_LEFT)
422                 rotate = 1;
423         else if(key==Msp::Input::KEY_UP)
424                 move_y = 1;
425         else if(key==Msp::Input::KEY_DOWN)
426                 move_y = -1;
427         else if(key==Msp::Input::KEY_INSERT)
428                 zoom = -1;
429         else if(key==Msp::Input::KEY_PGUP)
430                 zoom = 1;
431         else if(key==Msp::Input::KEY_HOME)
432                 pitch = 1;
433         else if(key==Msp::Input::KEY_END)
434                 pitch = -1;
435         else if(key==Msp::Input::KEY_DELETE)
436                 move_x = -1;
437         else if(key==Msp::Input::KEY_PGDN)
438                 move_x = 1;
439 }
440
441 void Designer::key_release(unsigned code, unsigned)
442 {
443         unsigned key = Msp::Input::key_from_sys(code);
444
445         if(mode==INPUT)
446                 return;
447
448         if(key==Msp::Input::KEY_SHIFT_L || key==Msp::Input::KEY_SHIFT_R)
449                 shift = false;
450         else if(key==Msp::Input::KEY_RIGHT || key==Msp::Input::KEY_LEFT)
451                 rotate = 0;
452         else if(key==Msp::Input::KEY_UP || key==Msp::Input::KEY_DOWN)
453                 move_y = 0;
454         else if(key==Msp::Input::KEY_INSERT || key==Msp::Input::KEY_PGUP)
455                 zoom = 0;
456         else if(key==Msp::Input::KEY_HOME || key==Msp::Input::KEY_END)
457                 pitch = 0;
458         else if(key==Msp::Input::KEY_DELETE || key==Msp::Input::KEY_PGDN)
459                 move_x = 0;
460 }
461
462 void Designer::button_press(int x, int y, unsigned btn, unsigned)
463 {
464         y = window.get_height()-y-1;
465
466         float gx, gy;
467         map_pointer_coords(x, y, gx, gy);
468
469         if(mode==CATALOGUE)
470         {
471                 if(btn==1)
472                 {
473                         Track3D *ctrack = pick_track(x, y);
474                         if(ctrack)
475                         {
476                                 Track *track = ctrack->get_track().copy();
477                                 track->set_position(Point(gx, gy, 0));
478                                 layout->add_track(*track);
479
480                                 selection.clear();
481                                 selection.add_track(track);
482
483                                 mode = SELECT;
484                         }
485                 }
486                 else
487                         mode = SELECT;
488         }
489         else if(mode==SELECT)
490         {
491                 if(btn==1)
492                 {
493                         Track3D *track = pick_track(x, y);
494                         if(track)
495                         {
496                                 if(!shift)
497                                         selection.clear();
498                                 selection.toggle_track(&track->get_track());
499                         }
500                 }
501         }
502         else if(mode==MANIPULATE)
503                 manipulator.button_press(x, y, gx, gy, btn);
504         else if(mode==MEASURE)
505                 measure.button_press(x, y, gx, gy, btn);
506 }
507
508 void Designer::pointer_motion(int x, int y)
509 {
510         y = window.get_height()-y-1;
511
512         pointer_x = x;
513         pointer_y = y;
514
515         if(mode==SELECT || mode==CATALOGUE)
516                 tooltip_timeout = Msp::Time::now()+100*Msp::Time::msec;
517
518         clear_tooltip();
519
520         if(mode!=INPUT)
521         {
522                 float gx, gy;
523                 map_pointer_coords(x, y, gx, gy);
524                 manipulator.pointer_motion(x, y, gx, gy);
525                 measure.pointer_motion(x, y, gx, gy);
526         }
527 }
528
529 void Designer::project_3d()
530 {
531         glMatrixMode(GL_PROJECTION);
532         glLoadIdentity();
533         glFrustum(-0.055228, 0.055228, -0.041421, 0.041421, 0.1, 10);
534         glMatrixMode(GL_MODELVIEW);
535 }
536
537 void Designer::apply_camera()
538 {
539         glLoadIdentity();
540         if(mode==CATALOGUE)
541                 glTranslatef(0, 0, -1);
542         else
543         {
544                 glRotatef(-cam_pitch*180/M_PI-90, 1, 0, 0);
545                 glRotatef(90-cam_yaw*180/M_PI, 0, 0, 1);
546                 glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z);
547         }
548 }
549
550 void Designer::render()
551 {
552         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
553         GL::enable(GL::DEPTH_TEST);
554         GL::Texture::unbind();
555
556         project_3d();
557         apply_camera();
558         if(mode==CATALOGUE)
559                 cat_layout_3d->get_scene().render();
560         else
561         {
562                 pipeline->render_all();
563                 layout_3d->get_endpoint_scene().render();
564                 GL::enable(GL_CULL_FACE);
565                 /*if(cur_route)
566                 {
567                         glColor4f(0.5, 0.8, 1.0, 1.0);
568                         const set<const Track *> &rtracks = cur_route->get_tracks();
569                         const map<unsigned, int> &turnouts = cur_route->get_turnouts();
570                         for(set<const Track *>::const_iterator i=rtracks.begin(); i!=rtracks.end(); ++i)
571                         {
572                                 unsigned path = 0;
573                                 if(unsigned tid=(*i)->get_turnout_id())
574                                 {
575                                         map<unsigned, int>::const_iterator j = turnouts.find(tid);
576                                         if(j!=turnouts.end())
577                                                 path = j->second;
578                                 }
579                                 layout_3d->get_track(**i).render_path(path);
580                         }
581                 }*/
582
583                 manipulator.render();
584                 if(mode==MEASURE)
585                         measure.render();
586         }
587
588         GL::matrix_mode(GL::PROJECTION);
589         GL::load_identity();
590         GL::ortho_bottomleft(window.get_width(), window.get_height());
591         GL::matrix_mode(GL::MODELVIEW);
592         GL::load_identity();
593
594         GL::disable(GL::DEPTH_TEST);
595
596         root->render();
597 }
598
599 Track3D *Designer::pick_track(int x, int y)
600 {
601         Layout3D *l = layout_3d;
602         if(mode==CATALOGUE)
603                 l = cat_layout_3d;
604
605         float xx = ((float(x)-window.get_width()/2)/window.get_height())*0.82843;
606         float yy = (float(y)/window.get_height()-0.5)*0.82843;
607         float size = 4.0/window.get_height()*0.82843;
608
609         project_3d();
610         apply_camera();
611
612         return l->pick_track(xx, yy, size);
613 }
614
615 void Designer::update_track_color(Track3D &track)
616 {
617         if(track.get_track().get_sensor_id())
618         {
619                 if(track.get_track().get_flex())
620                         track.set_color(GL::Color(1, 0.6, 1));
621                 else
622                         track.set_color(GL::Color(0.7, 0.7, 1));
623         }
624         else if(track.get_track().get_turnout_id())
625                 track.set_color(GL::Color(0.8, 1, 0.8));
626         else if(track.get_track().get_flex())
627                 track.set_color(GL::Color(1, 0.8, 0.8));
628         else
629                 track.set_color(GL::Color(1, 1, 1));
630 }
631
632 void Designer::manipulation_status(const string &status)
633 {
634         set_tooltip(pointer_x, pointer_y, status);
635 }
636
637 void Designer::manipulation_done(bool)
638 {
639         mode = SELECT;
640 }
641
642 void Designer::measure_changed()
643 {
644         float pard = measure.get_parallel_distance()*1000;
645         float perpd = measure.get_perpendicular_distance()*1000;
646         float d = sqrt(pard*pard+perpd*perpd);
647         float adiff = measure.get_angle_difference()*180/M_PI;
648         string info = format("Par %.1fmm - Perp %.1fmm - Total %.1fmm - Angle %.1f°", pard, perpd, d, adiff);
649         set_tooltip(pointer_x, pointer_y, info);
650 }
651
652 void Designer::measure_done()
653 {
654         mode = SELECT;
655 }
656
657 void Designer::set_tooltip(int x, int y, const std::string &text)
658 {
659         int fontsize = ui_res.get_default_font().get_default_size();
660         int h = fontsize+6;
661         int w = static_cast<int>(ui_res.get_default_font().get_string_width(text)*fontsize)+6;
662         x = max(min(static_cast<int>(window.get_width())-w, x), 0);
663         y = max(min(static_cast<int>(window.get_height())-h, y), 0);
664         lbl_tooltip->set_text(text);
665         lbl_tooltip->set_geometry(GLtk::Geometry(x, y, w, h));
666         lbl_tooltip->set_visible(true);
667 }
668
669 void Designer::clear_tooltip()
670 {
671         lbl_tooltip->set_visible(false);
672 }
673
674 void Designer::save_accept()
675 {
676         layout->save(input->get_text());
677
678         input_dismiss();
679 }
680
681 void Designer::turnout_id_accept()
682 {
683         Track *track = selection.get_track();
684         unsigned id = lexical_cast<unsigned>(input->get_text());
685         track->set_turnout_id(id);
686
687         update_track_color(layout_3d->get_track(*track));
688
689         input_dismiss();
690 }
691
692 void Designer::sensor_id_accept()
693 {
694         const set<Track *> &tracks = selection.get_tracks();
695         unsigned id = lexical_cast<unsigned>(input->get_text());
696         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
697         {
698                 (*i)->set_sensor_id(id);
699
700                 update_track_color(layout_3d->get_track(**i));
701         }
702
703         input_dismiss();
704 }
705
706 void Designer::input_dismiss()
707 {
708         delete input;
709         input = 0;
710         mode = SELECT;
711 }