]> git.tdb.fi Git - r2c2.git/commitdiff
Complete rewrite of the engineer UI
authorMikko Rasa <tdb@tdb.fi>
Mon, 1 Jul 2013 13:38:59 +0000 (16:38 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 1 Jul 2013 13:38:59 +0000 (16:38 +0300)
The fixed column of train controls was getting unwieldy with lots of
trains, and side dialogs didn't move with their associated train panel.
Now, all dialogs are movable and train controls only appear when
requested.  Most of the former side dialogs have been integrated into the
train dialog as tabs.

43 files changed:
data/controlpanel.ui [new file with mode: 0644]
data/mainwindow.ui [new file with mode: 0644]
data/newtraindialog.ui [new file with mode: 0644]
data/newvehicledialog.ui [new file with mode: 0644]
data/routerpanel.ui [new file with mode: 0644]
data/traindialog.ui [new file with mode: 0644]
data/trainlistdialog.ui [new file with mode: 0644]
data/trainview.ui [new file with mode: 0644]
data/vehiclespanel.ui [new file with mode: 0644]
gui.blend
gui.png
r2c2.res
source/engineer/controlpanel.cpp [new file with mode: 0644]
source/engineer/controlpanel.h [new file with mode: 0644]
source/engineer/dialog.cpp [deleted file]
source/engineer/dialog.h [deleted file]
source/engineer/engineer.cpp
source/engineer/engineer.h
source/engineer/mainpanel.cpp [deleted file]
source/engineer/mainpanel.h [deleted file]
source/engineer/mainwindow.cpp [new file with mode: 0644]
source/engineer/mainwindow.h [new file with mode: 0644]
source/engineer/newtraindialog.cpp [new file with mode: 0644]
source/engineer/newtraindialog.h [new file with mode: 0644]
source/engineer/newvehicledialog.cpp [new file with mode: 0644]
source/engineer/newvehicledialog.h [new file with mode: 0644]
source/engineer/routerpanel.cpp [new file with mode: 0644]
source/engineer/routerpanel.h [new file with mode: 0644]
source/engineer/routeselect.cpp [deleted file]
source/engineer/routeselect.h [deleted file]
source/engineer/traindialog.cpp [new file with mode: 0644]
source/engineer/traindialog.h [new file with mode: 0644]
source/engineer/trainlistdialog.cpp [new file with mode: 0644]
source/engineer/trainlistdialog.h [new file with mode: 0644]
source/engineer/trainpanel.cpp [deleted file]
source/engineer/trainpanel.h [deleted file]
source/engineer/trainproperties.cpp [deleted file]
source/engineer/trainproperties.h [deleted file]
source/engineer/trainselectdialog.h [new file with mode: 0644]
source/engineer/trainview.cpp
source/engineer/trainview.h
source/engineer/vehiclespanel.cpp [new file with mode: 0644]
source/engineer/vehiclespanel.h [new file with mode: 0644]

diff --git a/data/controlpanel.ui b/data/controlpanel.ui
new file mode 100644 (file)
index 0000000..dceab53
--- /dev/null
@@ -0,0 +1,64 @@
+style "group";
+layout
+{
+       margin
+       {
+               horizontal 0;
+               vertical 0;
+       };
+};
+
+column
+{
+       row
+       {
+               label "lbl1"
+               {
+                       text "Protocol:";
+               };
+
+               label "lbl_protocol";
+       };
+
+       row
+       {
+               label "lbl2"
+               {
+                       text "Address:";
+               };
+               constraint COPY_WIDTH "lbl1";
+
+               label "lbl_address";
+       };
+
+       panel "pnl_functions"
+       {
+               style "group";
+               layout
+               {
+                       margin
+                       {
+                               horizontal 0;
+                               vertical 0;
+                       };
+               };
+       };
+
+       row
+       {
+               button "btn_place"
+               {
+                       text "Place";
+               };
+
+               button "btn_take"
+               {
+                       text "Take";
+               };
+
+               button "btn_view"
+               {
+                       text "View";
+               };
+       };
+};
diff --git a/data/mainwindow.ui b/data/mainwindow.ui
new file mode 100644 (file)
index 0000000..e796913
--- /dev/null
@@ -0,0 +1,81 @@
+layout
+{
+       margin
+       {
+               top 2;
+               horizontal 8;
+               bottom 6;
+       };
+};
+
+column
+{
+       stack
+       {
+               label "lbl_title"
+               {
+                       text "Railroad Computer Control";
+               };
+
+               draghandle "";
+       };
+
+       row
+       {
+               indicator "ind_off"
+               {
+                       style "red";
+               };
+
+               indicator "ind_on"
+               {
+                       style "green";
+               };
+
+               indicator "ind_halt";
+       };
+
+       row
+       {
+               button "btn_off"
+               {
+                       style "red";
+                       text "Off";
+               };
+               constraint ALIGN_LEFT "ind_off";
+               constraint COPY_WIDTH "ind_off";
+
+               button "btn_on"
+               {
+                       style "green";
+                       text "On";
+               };
+               constraint ALIGN_LEFT "ind_on";
+               constraint COPY_WIDTH "ind_on";
+
+               button "btn_halt"
+               {
+                       text "Halt";
+               };
+               constraint ALIGN_LEFT "ind_halt";
+               constraint COPY_WIDTH "ind_halt";
+
+               button "btn_trains"
+               {
+                       text "Trains";
+               };
+
+               split;
+
+               button "btn_quit"
+               {
+                       style "red";
+                       text "Quit";
+               };
+       };
+
+       label "lbl_status"
+       {
+               style "digital";
+       };
+};
diff --git a/data/newtraindialog.ui b/data/newtraindialog.ui
new file mode 100644 (file)
index 0000000..b7899bc
--- /dev/null
@@ -0,0 +1,94 @@
+layout
+{
+       margin
+       {
+               top 2;
+               horizontal 8;
+               bottom 6;
+       };
+};
+
+column
+{
+       row
+       {
+               label "lbl_title"
+               {
+                       text "New train";
+               };
+
+               split;
+
+               action_button "btn_close" 0
+               {
+                       style "red_cross";
+               };
+       };
+
+       row
+       {
+               label "lbl_protocol"
+               {
+                       text "Protocol";
+               };
+
+               dropdown "drp_protocol";
+       };
+
+       row
+       {
+               label "lbl_address"
+               {
+                       text "Address:";
+               };
+               constraint COPY_WIDTH "lbl_protocol";
+               
+               entry "ent_address"
+               {
+                       edit_size 8 1;
+               };
+       };
+
+       row
+       {
+               label "lbl_name"
+               {
+                       text "Name:";
+               };
+               constraint COPY_WIDTH "lbl_address";
+
+               entry "ent_name"
+               {
+                       edit_size 30 1;
+               };
+       };
+
+       row
+       {
+               label "lbl_loco_type"
+               {
+                       text "Locomotive:";
+               };
+               constraint COPY_WIDTH "lbl_name";
+
+               dropdown "drp_loco_type"
+               {
+               };
+       };
+
+       row
+       {
+               split;
+
+               action_button "btn_ok" 1
+               {
+                       style "green";
+                       text "Ok";
+               };
+       };
+};
+
+draghandle "";
+expand true false;
+constraint COPY_HEIGHT "lbl_title";
+constraint LEFT_OF "btn_close";
diff --git a/data/newvehicledialog.ui b/data/newvehicledialog.ui
new file mode 100644 (file)
index 0000000..b835c7e
--- /dev/null
@@ -0,0 +1,92 @@
+layout
+{
+       margin
+       {
+               top 2;
+               horizontal 8;
+               bottom 6;
+       };
+};
+
+column
+{
+       row
+       {
+               label "lbl_title"
+               {
+                       text "Add vehicle";
+               };
+
+               split;
+
+               action_button "btn_close" 0
+               {
+                       style "red_cross";
+               };
+       };
+
+       label "lbl_target";
+
+       row
+       {
+               panel ""
+               {
+                       style "group";
+
+                       layout
+                       {
+                               margin
+                               {
+                                       horizontal 0;
+                                       vertical 0;
+                               };
+
+                               row_spacing 0;
+                       };
+
+                       column
+                       {
+                               toggle "tgl_loco_types"
+                               {
+                                       style "pointer_left";
+                                       text "Loco";
+                                       exclusive true;
+                                       value true;
+                               };
+
+                               toggle "tgl_wagon_types"
+                               {
+                                       style "pointer_left";
+                                       text "Wagn";
+                                       exclusive true;
+                               };
+                       };
+               };
+
+               stack
+               {
+                       list "lst_loco_types"
+                       {
+                               view_size 10;
+                       };
+
+                       list "lst_wagon_types"
+                       {
+                               visible false;
+                       };
+               };
+       };
+
+       row
+       {
+               button "btn_add"
+               {
+                       text "Add";
+               };
+       };
+};
+
+draghandle "";
+constraint COPY_HEIGHT "lbl_title";
+constraint LEFT_OF "btn_close";
+expand true false;
diff --git a/data/routerpanel.ui b/data/routerpanel.ui
new file mode 100644 (file)
index 0000000..eacd4e7
--- /dev/null
@@ -0,0 +1,28 @@
+style "group";
+layout
+{
+       margin
+       {
+               horizontal 0;
+               vertical 0;
+       };
+};
+
+column
+{
+       label "lbl_route"
+       {
+               style "digital";
+       };
+       expand true false;
+
+       dropdown "drp_routes";
+
+       row
+       {
+               button "btn_goto"
+               {
+                       text "GoTo";
+               };
+       };
+};
diff --git a/data/traindialog.ui b/data/traindialog.ui
new file mode 100644 (file)
index 0000000..0b9e70c
--- /dev/null
@@ -0,0 +1,112 @@
+layout
+{
+       margin
+       {
+               top 2;
+               horizontal 8;
+               bottom 6;
+       };
+};
+
+column
+{
+       row
+       {
+               label "lbl_title";
+
+               split;
+
+               button "btn_expand"
+               {
+                       style "arrow_down";
+               };
+
+               action_button "btn_close" 0
+               {
+                       style "red_cross";
+               };
+       };
+
+       row
+       {
+               label "lbl_speed"
+               {
+                       style "digital";
+                       text "  0";
+               };
+
+               hslider "sld_speed"
+               {
+                       range 0 200;
+                       step 5;
+               };
+               expand;
+
+               toggle "tgl_forward"
+               {
+                       style "lever";
+                       text "Fwd";
+                       value true;
+               };
+       };
+
+       panel "pnl_expander"
+       {
+               layout
+               {
+                       margin
+                       {
+                               horizontal 0;
+                               vertical 0;
+                       };
+                       row_spacing 0;
+               };
+
+               style "group";
+               visible false;
+               row
+               {
+                       column
+                       {
+                               toggle "tgl_vehicles"
+                               {
+                                       text "Vehs";
+                                       style "pointer_left";
+                                       exclusive true;
+                                       value true;
+                               };
+
+                               toggle "tgl_control"
+                               {
+                                       text "Ctrl";
+                                       style "pointer_left";
+                                       exclusive true;
+                               };
+
+                               toggle "tgl_router"
+                               {
+                                       text "Rte";
+                                       style "pointer_left";
+                                       exclusive true;
+                               };
+                       };
+
+                       label "lbl_detail_placeholder"
+                       {
+                               visible false;
+                       };
+                       expand;
+               };
+       };
+
+       label "lbl_status"
+       {
+               style "digital";
+       };
+       constraint FAR_BELOW "lbl_speed";
+};
+
+draghandle "";
+expand true false;
+constraint COPY_HEIGHT "lbl_title";
+constraint LEFT_OF "btn_expand";
diff --git a/data/trainlistdialog.ui b/data/trainlistdialog.ui
new file mode 100644 (file)
index 0000000..a1ebf79
--- /dev/null
@@ -0,0 +1,50 @@
+layout
+{
+       margin
+       {
+               top 2;
+               horizontal 8;
+               bottom 6;
+       };
+};
+
+column
+{
+       row
+       {
+               label "lbl_title"
+               {
+                       text "Trains";
+               };
+
+               split;
+
+               action_button "btn_close" 0
+               {
+                       style "red_cross";
+               };
+       };
+
+       list "lst_trains"
+       {
+               view_size 10;
+       };
+
+       row
+       {
+               button "btn_new"
+               {
+                       text "New";
+               };
+
+               button "btn_show"
+               {
+                       text "Show";
+               };
+       };
+};
+
+draghandle "";
+expand true false;
+constraint COPY_HEIGHT "lbl_title";
+constraint LEFT_OF "btn_close";
diff --git a/data/trainview.ui b/data/trainview.ui
new file mode 100644 (file)
index 0000000..e9c59be
--- /dev/null
@@ -0,0 +1,82 @@
+layout
+{
+       margin
+       {
+               top 2;
+               horizontal 8;
+               bottom 6;
+       };
+};
+
+column
+{
+       row
+       {
+               label "lbl_title";
+
+               split;
+
+               action_button "btn_close" 0
+               {
+                       style "red_cross";
+               };
+       };
+
+       row
+       {
+               column
+               {
+                       toggle "tgl_forward"
+                       {
+                               text "Fwd";
+                               value true;
+                       };
+
+                       panel "pnl_mode"
+                       {
+                               style "group";
+                               layout
+                               {
+                                       margin
+                                       {
+                                               horizontal 0;
+                                               vertical 0;
+                                       };
+                                       row_spacing 0;
+                               };
+
+                               column
+                               {
+                                       toggle "tgl_roof"
+                                       {
+                                               style "pointer_left";
+                                               text "Roof";
+                                               exclusive true;
+                                       };
+
+                                       toggle "tgl_side"
+                                       {
+                                               style "pointer_left";
+                                               text "Side";
+                                               exclusive true;
+                                               value true;
+                                       };
+
+                                       toggle "tgl_head"
+                                       {
+                                               style "pointer_left";
+                                               text "Head";
+                                               exclusive true;
+                                       };
+                               };
+                       };
+               };
+
+               image "img_view";
+       };
+};
+
+draghandle "";
+expand true false;
+constraint COPY_HEIGHT "lbl_title";
+constraint LEFT_OF "btn_close";
diff --git a/data/vehiclespanel.ui b/data/vehiclespanel.ui
new file mode 100644 (file)
index 0000000..3b726cc
--- /dev/null
@@ -0,0 +1,28 @@
+style "group";
+layout
+{
+       margin
+       {
+               horizontal 0;
+               vertical 0;
+       };
+};
+
+column
+{
+       list "lst_vehicles";
+       expand true true;
+
+       row
+       {
+               button "btn_add"
+               {
+                       text "Add";
+               };
+
+               button "btn_remove"
+               {
+                       text "Rem";
+               };
+       };
+};
index 6171856ded86cb3d1691a9390bd3fd1f30bd1e6a..0755f2ec4f40c489ae650de8868c66e047b78e2c 100644 (file)
Binary files a/gui.blend and b/gui.blend differ
diff --git a/gui.png b/gui.png
index 0f2310062bbf48dd756c8f639b7eab7637c4527c..8753418b86813fc09dfa66f4b895fee486dc4cae 100644 (file)
Binary files a/gui.png and b/gui.png differ
index 20bae38b1c41ce7910a79e995822b8f453d29a3e..c2f72c1a94c1ecf89c02dbc1c6f970baea8e04bd 100644 (file)
--- a/r2c2.res
+++ b/r2c2.res
@@ -162,6 +162,20 @@ graphic "left_arrow_button_pressed"
        shadow { right 1; bottom 1; };
 };
 
+graphic "red_cross_button"
+{
+       texture "gui.png";
+       slice 74 2 13 14;
+       shadow { right 1; bottom 2; };
+};
+
+graphic "red_cross_button_pressed"
+{
+       texture "gui.png";
+       slice 89 3 13 13;
+       shadow { right 1; bottom 1; };
+};
+
 style "button"
 {
        font_color 0 0 0;
@@ -269,6 +283,17 @@ style "button-arrow_left"
        };
 };
 
+style "button-red_cross"
+{
+       part
+       {
+               graphic NORMAL "red_cross_button";
+               graphic ACTIVE "red_cross_button_pressed";
+               align 0.5 0.5;
+               fill 0.0 0.0;
+       };
+};
+
 graphic "yellow_lamp"
 {
        texture "gui.png";
@@ -380,7 +405,7 @@ style "label-digital"
        {
                fill 0.0 0.0;
                align 0.5 0.5;
-               margin { top 1; };
+               margin { horizontal 4; top 2; bottom 1; };
        };
 };
 
@@ -546,6 +571,7 @@ style "toggle"
        {
                align 0.5 1.0;
                fill 0.0 0.0;
+               margin { bottom 10; };
        };
 
        part
@@ -584,6 +610,7 @@ style "hslider"
        part "slider"
        {
                graphic NORMAL "horizontal_knob";
+               align 0.0 0.5;
                fill 0.0 0.0;
        };
 };
@@ -619,3 +646,86 @@ style "vslider"
                align 0.0 1.0;
        };
 };
+
+style "draghandle";
+
+graphic "pointer_down"
+{
+       texture "gui.png";
+       slice 98 36 13 14;
+       shadow { right 1; bottom 2; };
+};
+
+graphic "pointer_up"
+{
+       texture "gui.png";
+       slice 83 36 13 14;
+       shadow { right 1; bottom 2; };
+};
+
+graphic "pointer_left"
+{
+       texture "gui.png";
+       slice 83 21 13 14;
+       shadow { right 1; bottom 2; };
+};
+
+graphic "pointer_right"
+{
+       texture "gui.png";
+       slice 98 21 13 14;
+       shadow { right 1; bottom 2; };
+};
+
+style "toggle-pointer_left"
+{
+       font_color 0.0 0.0 0.0;
+
+       part "text"
+       {
+               align 0.0 0.5;
+               fill 0.0 0.0;
+               margin { vertical 2; right 4; left 2; };
+       };
+
+       part
+       {
+               graphic NORMAL "vertical_slot";
+               align 1.0 0.0;
+               fill 0.0 1.0;
+       };
+
+       part
+       {
+               graphic ACTIVE "pointer_left";
+               align 1.0 0.5;
+               fill 0.0 0.0;
+       };
+};
+
+graphic "lever_down"
+{
+       texture "gui.png";
+       slice 103 52 20 22;
+       shadow { right 3; bottom 4; };
+};
+
+graphic "lever_up"
+{
+       texture "gui.png";
+       slice 103 76 20 22;
+       shadow { right 3; bottom 4; };
+};
+
+style "toggle-lever"
+{
+       font_color 0.0 0.0 0.0;
+
+       part
+       {
+               align 0.5 0.5;
+               fill 0.0 0.0;
+               graphic NORMAL "lever_down";
+               graphic ACTIVE "lever_up";
+       };
+};
diff --git a/source/engineer/controlpanel.cpp b/source/engineer/controlpanel.cpp
new file mode 100644 (file)
index 0000000..e81d25c
--- /dev/null
@@ -0,0 +1,85 @@
+#include <msp/core/maputils.h>
+#include <msp/core/raii.h>
+#include <msp/gltk/button.h>
+#include <msp/gltk/label.h>
+#include <msp/gltk/row.h>
+#include "libr2c2/vehicle.h"
+#include "controlpanel.h"
+#include "engineer.h"
+#include "trainview.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+ControlPanel::ControlPanel(Engineer &e, Train &t):
+       engineer(e),
+       train(t),
+       updating(false)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/controlpanel.ui", widgets);
+
+       dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_protocol"))->set_text(train.get_protocol());
+       dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_address"))->set_text(lexical_cast<string>(train.get_address()));
+
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_place"))->signal_clicked.connect(sigc::mem_fun(this, &ControlPanel::place_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_take"))->signal_clicked.connect(sigc::mem_fun(this, &ControlPanel::take_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_view"))->signal_clicked.connect(sigc::mem_fun(this, &ControlPanel::view_clicked));
+
+       GLtk::Panel *pnl_functions = dynamic_cast<GLtk::Panel *>(get_item(widgets, "pnl_functions"));
+
+       const VehicleType::FunctionMap &funcs = train.get_vehicle(0).get_type().get_functions();
+       GLtk::Row row(*pnl_functions->get_layout());
+       for(VehicleType::FunctionMap::const_iterator i=funcs.begin(); i!=funcs.end(); ++i)
+       {
+               GLtk::Toggle *tgl = new GLtk::Toggle(i->second);
+               tgl->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &ControlPanel::ui_function_toggled), i->first));
+               pnl_functions->add(*tgl);
+               tgl_funcs[i->first] = tgl;
+       }
+
+       train.signal_function_changed.connect(sigc::mem_fun(this, &ControlPanel::train_function_changed));
+}
+
+void ControlPanel::ui_function_toggled(bool value, unsigned func)
+{
+       if(!updating)
+               train.set_function(func, value);
+}
+
+void ControlPanel::train_function_changed(unsigned func, bool value)
+{
+       SetFlag setf(updating);
+       map<unsigned, GLtk::Toggle *>::iterator i = tgl_funcs.find(func);
+       if(i!=tgl_funcs.end())
+               i->second->set_value(value);
+}
+
+void ControlPanel::place_clicked()
+{
+       pick_conn = engineer.signal_pick_done.connect(sigc::mem_fun(this, &ControlPanel::place_pick_done));
+       engineer.pick(true);
+}
+
+void ControlPanel::place_pick_done(Track *track, unsigned ep)
+{
+       pick_conn.disconnect();
+       train.place(TrackIter(track, ep).block_iter());
+}
+
+void ControlPanel::take_clicked()
+{
+       train.unplace();
+}
+
+void ControlPanel::view_clicked()
+{
+       GLtk::Container *root = parent;
+       while(root->get_parent())
+               root = root->get_parent();
+
+       TrainView *dlg = new TrainView(engineer, train);
+       root->add(*dlg);
+       dlg->autosize();
+}
diff --git a/source/engineer/controlpanel.h b/source/engineer/controlpanel.h
new file mode 100644 (file)
index 0000000..c99ca17
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef CONTROLPANEL_H_
+#define CONTROLPANEL_H_
+
+#include <map>
+#include <msp/gltk/panel.h>
+#include <msp/gltk/toggle.h>
+#include "libr2c2/train.h"
+
+class Engineer;
+
+class ControlPanel: public Msp::GLtk::Panel
+{
+private:
+       Engineer &engineer;
+       R2C2::Train &train;
+       bool updating;
+       std::map<unsigned, Msp::GLtk::Toggle *> tgl_funcs;
+       sigc::connection pick_conn;
+
+public:
+       ControlPanel(Engineer &, R2C2::Train &);
+
+private:
+       void ui_function_toggled(bool, unsigned);
+       void train_function_changed(unsigned, bool);
+       void place_clicked();
+       void place_pick_done(R2C2::Track *, unsigned);
+       void take_clicked();
+       void view_clicked();
+};
+
+#endif
diff --git a/source/engineer/dialog.cpp b/source/engineer/dialog.cpp
deleted file mode 100644 (file)
index 61eb468..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "dialog.h"
-
-using namespace Msp;
-
-Dialog::Dialog():
-       stale(false)
-{
-       add(*(btn_ok=new GLtk::Button("OK")));
-       btn_ok->set_style("green");
-       btn_ok->set_geometry(GLtk::Geometry(geom.w-40, 10, 30, 25));
-       btn_ok->signal_clicked.connect(sigc::mem_fun(this, &Dialog::on_ok_clicked));
-       btn_ok->signal_clicked.connect(signal_ok);
-       btn_ok->signal_clicked.connect(sigc::mem_fun(this, &Dialog::dismiss));
-
-       add(*(btn_cancel=new GLtk::Button("Cncl")));
-       btn_cancel->set_style("red");
-       btn_cancel->set_geometry(GLtk::Geometry(geom.w-80, 10, 30, 25));
-       btn_cancel->signal_clicked.connect(signal_cancel);
-       btn_cancel->signal_clicked.connect(sigc::mem_fun(this, &Dialog::dismiss));
-}
-
-void Dialog::button_release(int x, int y, unsigned btn)
-{
-       Panel::button_release(x, y, btn);
-       if(stale)
-               delete this;
-}
-
-void Dialog::on_geometry_change()
-{
-       btn_ok->set_geometry(GLtk::Geometry(geom.w-40, 10, 30, 25));
-       btn_cancel->set_geometry(GLtk::Geometry(geom.w-80, 10, 30, 25));
-}
-
-void Dialog::dismiss()
-{
-       stale = true;
-}
diff --git a/source/engineer/dialog.h b/source/engineer/dialog.h
deleted file mode 100644 (file)
index 266a76a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef DIALOG_H_
-#define DIALOG_H_
-
-#include <msp/gltk/button.h>
-#include <msp/gltk/panel.h>
-
-class Dialog: public Msp::GLtk::Panel
-{
-public:
-       sigc::signal<void> signal_ok;
-       sigc::signal<void> signal_cancel;
-
-protected:
-       Msp::GLtk::Button *btn_ok;
-       Msp::GLtk::Button *btn_cancel;
-       bool stale;
-
-       Dialog();
-
-       virtual void button_release(int, int, unsigned);
-       virtual void on_geometry_change();
-
-       virtual void on_ok_clicked() { }
-       void dismiss();
-};
-
-#endif
index 9a3b930a01da1b55137c9a45b9279672f3d44565..61dd49a1c72d86136baa9fd462e05b2458b398e5 100644 (file)
@@ -23,9 +23,8 @@
 #include "3d/track.h"
 #include "3d/vehicle.h"
 #include "engineer.h"
-#include "mainpanel.h"
-#include "trainpanel.h"
-#include "trainproperties.h"
+#include "mainwindow.h"
+#include "traindialog.h"
 #include "trainview.h"
 
 using namespace std;
@@ -52,15 +51,15 @@ Engineer::Engineer(int argc, char **argv):
        window.set_title("Railroad Engineer");
        window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Engineer::exit), 0));
 
-       root = new GLtk::Root(ui_res, window);
+       root = new GLtk::Root(ui_res, &window, &keyboard, &mouse);
        mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Engineer::button_press), false));
        mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Engineer::axis_motion), false));
        root->set_visible(true);
 
-       main_panel = new MainPanel(*this);
-       root->add(*main_panel);
-       main_panel->set_position(0, window.get_height()-main_panel->get_geometry().h);
-       main_panel->set_visible(true);
+       main_wnd = new MainWindow(*this);
+       root->add(*main_wnd);
+       main_wnd->autosize();
+       main_wnd->set_position(0, window.get_height()-main_wnd->get_geometry().h);
 
        overlay = new Overlay3D(ui_res.get_default_font());
 
@@ -141,20 +140,10 @@ Engineer::~Engineer()
 
 void Engineer::set_status(const string &text)
 {
-       main_panel->set_status_text(text);
+       main_wnd->set_status_text(text);
        status_timeout = Time::now()+10*Time::sec;
 }
 
-void Engineer::rearrange_panels()
-{
-       int y = main_panel->get_geometry().y;
-       for(list<TrainPanel *>::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
-       {
-               y -= (*i)->get_geometry().h;
-               (*i)->set_position(0, y);
-       }
-}
-
 void Engineer::add_train_view(TrainView &tv)
 {
        train_views.push_back(&tv);
@@ -195,7 +184,7 @@ void Engineer::tick()
 
        if(status_timeout && Time::now()>status_timeout)
        {
-               main_panel->set_status_text(string());
+               main_wnd->set_status_text(string());
                status_timeout = Time::TimeStamp();
        }
 
@@ -209,7 +198,7 @@ void Engineer::tick()
 
                if(picking)
                {
-                       Track *track = pick_track(pointer);
+                       Track *track = dynamic_cast<Track *>(pick_object(pointer));
                        if(track && track!=picking_track)
                        {
                                picking_track = track;
@@ -265,8 +254,8 @@ void Engineer::button_press(unsigned btn)
        }
        else
        {
-               Track *track = pick_track(pointer);
-               if(track)
+               Object *obj = pick_object(pointer);
+               if(Track *track = dynamic_cast<Track *>(obj))
                {
                        if(track->get_turnout_id())
                        {
@@ -295,6 +284,12 @@ void Engineer::button_press(unsigned btn)
                                set_status(format("Sensor %d", sid));
                        }
                }
+               else if(Vehicle *veh = dynamic_cast<Vehicle *>(obj))
+               {
+                       TrainDialog *dlg = new TrainDialog(*this, *veh->get_train());
+                       root->add(*dlg);
+                       dlg->autosize();
+               }
        }
 }
 
@@ -311,7 +306,7 @@ void Engineer::view_all()
 {
        const Layout3D::TrackMap &tracks = layout_3d.get_tracks();
 
-       float view_aspect = float(window.get_width()-200)/window.get_height();
+       float view_aspect = float(window.get_width())/window.get_height();
        float view_height = tan(camera.get_field_of_view()/2.0f)*2.0f;
        float best_score = 0;
        GL::Vector3 pos;
@@ -338,7 +333,7 @@ void Engineer::view_all()
                        float size = max(width/view_aspect, height);
                        float c = cos(angle);
                        float s = sin(angle);
-                       float x = (minp.x+maxp.x)/2-size*105/window.get_height();
+                       float x = (minp.x+maxp.x)/2;
                        float y = (minp.y+maxp.y)/2;
                        float z = max(size*1.05/view_height, 0.15);
 
@@ -383,21 +378,19 @@ void Engineer::reset_block_color(const Block &block)
                set_block_color(block, GL::Color(1));
 }
 
-Track *Engineer::pick_track(const Vector &p)
+Object *Engineer::pick_object(const Vector &p)
 {
        const GL::Vector3 &start = camera.get_position();
        GL::Vector4 ray = camera.unproject(GL::Vector4(p.x, p.y, 0, 0));
 
+       // XXX Do this better; make this function a template?
+       if(Vehicle *veh = layout.pick<Vehicle>(Ray(start, Vector(ray))))
+               return veh;
        return layout.pick<Track>(Ray(start, Vector(ray)));
 }
 
 void Engineer::process_new_train(Train &train)
 {
-       TrainPanel *tpanel = new TrainPanel(*this, train);
-       root->add(*tpanel);
-       train_panels.push_back(tpanel);
-       rearrange_panels();
-
        Vehicle3D &loco3d = layout_3d.get_vehicle(train.get_vehicle(0));
        overlay->set_label(loco3d, train.get_name());
        train.signal_name_changed.connect(sigc::bind<0>(sigc::mem_fun(overlay, &Overlay3D::set_label), sigc::ref(loco3d)));
index 72fdd29c877fcd778f0a829c9fec6f51fd4a049e..7997242e17cf0c82409e08d80b376d2e86c68566 100644 (file)
@@ -17,8 +17,7 @@
 #include "network/server.h"
 #include "options.h"
 
-class MainPanel;
-class TrainPanel;
+class MainWindow;
 class TrainView;
 
 class Engineer: public Msp::RegisteredApplication<Engineer>
@@ -49,9 +48,8 @@ private:
        Msp::GL::Pipeline pipeline;
        Msp::GL::Mesh arrow_mesh;
 
-       MainPanel *main_panel;
+       MainWindow *main_wnd;
        std::list<R2C2::Train *> new_trains;
-       std::list<TrainPanel *> train_panels;
        std::list<TrainView *> train_views;
        Msp::Time::TimeStamp status_timeout;
        bool picking;
@@ -72,7 +70,6 @@ public:
        R2C2::Layout3D &get_layout_3d() { return layout_3d; }
        const Msp::GL::Lighting &get_lighting() const { return lighting; }
        void set_status(const std::string &);
-       void rearrange_panels();
        void add_train_view(TrainView &);
        void remove_train_view(TrainView &);
        void pick(bool);
@@ -87,7 +84,7 @@ private:
        void reset_block_color(const R2C2::Block &);
        void sensor_event(unsigned, bool);
        void block_reserved(const R2C2::Block &, const R2C2::Train *);
-       R2C2::Track *pick_track(const R2C2::Vector &);
+       R2C2::Object *pick_object(const R2C2::Vector &);
        void train_added(R2C2::Train &);
        void process_new_train(R2C2::Train &);
        virtual void sighandler(int);
diff --git a/source/engineer/mainpanel.cpp b/source/engineer/mainpanel.cpp
deleted file mode 100644 (file)
index 0071f4b..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#include <msp/gltk/button.h>
-#include "libr2c2/driver.h"
-#include "engineer.h"
-#include "mainpanel.h"
-#include "trainproperties.h"
-
-using namespace std;
-using namespace Msp;
-
-MainPanel::MainPanel(Engineer &e):
-       engineer(e)
-{
-       set_size(200, 117);
-
-       GLtk::Button *btn;
-
-       add(*(btn=new GLtk::Button("Off")));
-       btn->set_geometry(GLtk::Geometry(10, 70, 40, 25));
-       btn->set_style("red");
-       btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::power_off));
-
-       add(*(ind_off=new GLtk::Indicator));
-       ind_off->set_geometry(GLtk::Geometry(10, 95, 40, 12));
-       ind_off->set_style("red");
-
-       add(*(btn=new GLtk::Button("On")));
-       btn->set_geometry(GLtk::Geometry(50, 70, 40, 25));
-       btn->set_style("green");
-       btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::power_on));
-
-       add(*(ind_on=new GLtk::Indicator));
-       ind_on->set_geometry(GLtk::Geometry(50, 95, 40, 12));
-       ind_on->set_style("green");
-
-       add(*(btn=new GLtk::Button("Halt")));
-       btn->set_geometry(GLtk::Geometry(90, 70, 40, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::toggle_halt));
-
-       add(*(ind_halt=new GLtk::Indicator));
-       ind_halt->set_geometry(GLtk::Geometry(90, 95, 40, 12));
-
-       add(*(btn=new GLtk::Button("Quit")));
-       btn->set_geometry(GLtk::Geometry(150, 70, 40, 25));
-       btn->set_style("red");
-       btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::quit));
-
-       add(*(btn=new GLtk::Button("+Loc")));
-       btn->set_geometry(GLtk::Geometry(10, 40, 40, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::new_loc));
-
-       add(*(lbl_status=new GLtk::Label));
-       lbl_status->set_geometry(GLtk::Geometry(10, 10, 180, 20));
-       lbl_status->set_style("digital");
-
-       R2C2::Driver &driver = engineer.get_layout().get_driver();
-       if(driver.get_power())
-               ind_on->set_active(true);
-       else
-               ind_off->set_active(true);
-
-       driver.signal_power.connect(sigc::mem_fun(this, &MainPanel::power_event));
-       driver.signal_halt.connect(sigc::mem_fun(this, &MainPanel::halt_event));
-}
-
-void MainPanel::set_status_text(const string &txt)
-{
-       lbl_status->set_text(txt);
-}
-
-void MainPanel::power_on()
-{
-       engineer.get_layout().get_driver().set_power(true);
-}
-
-void MainPanel::power_off()
-{
-       engineer.get_layout().get_driver().set_power(false);
-}
-
-void MainPanel::toggle_halt()
-{
-       R2C2::Driver &driver = engineer.get_layout().get_driver();
-       driver.halt(!driver.is_halted());
-}
-
-void MainPanel::new_loc()
-{
-       TrainProperties *dialog = new TrainProperties(engineer, 0);
-       engineer.get_root().add(*dialog);
-       dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h);
-       dialog->set_visible(true);
-}
-
-void MainPanel::quit()
-{
-       engineer.quit();
-}
-
-void MainPanel::power_event(bool p)
-{
-       ind_on->set_active(p);
-       ind_off->set_active(!p);
-}
-
-void MainPanel::halt_event(bool h)
-{
-       ind_halt->set_active(h);
-}
diff --git a/source/engineer/mainpanel.h b/source/engineer/mainpanel.h
deleted file mode 100644 (file)
index 4501be7..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef MAINPANEL_H_
-#define MAINPANEL_H_
-
-#include <sigc++/trackable.h>
-#include <msp/gltk/indicator.h>
-#include <msp/gltk/label.h>
-#include <msp/gltk/panel.h>
-
-class Engineer;
-
-class MainPanel: public Msp::GLtk::Panel, public sigc::trackable
-{
-private:
-       Engineer &engineer;
-       Msp::GLtk::Indicator *ind_on;
-       Msp::GLtk::Indicator *ind_off;
-       Msp::GLtk::Label *lbl_status;
-       Msp::GLtk::Indicator *ind_halt;
-
-public:
-       MainPanel(Engineer &);
-
-       void set_status_text(const std::string &);
-private:
-       void power_on();
-       void power_off();
-       void toggle_halt();
-       void new_loc();
-       void quit();
-       void power_event(bool);
-       void halt_event(bool);
-};
-
-#endif
diff --git a/source/engineer/mainwindow.cpp b/source/engineer/mainwindow.cpp
new file mode 100644 (file)
index 0000000..6e3888c
--- /dev/null
@@ -0,0 +1,83 @@
+#include <msp/gltk/button.h>
+#include "libr2c2/driver.h"
+#include "engineer.h"
+#include "mainwindow.h"
+#include "trainlistdialog.h"
+
+using namespace std;
+using namespace Msp;
+
+MainWindow::MainWindow(Engineer &e):
+       engineer(e)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/mainwindow.ui", widgets);
+
+       ind_on = dynamic_cast<GLtk::Indicator *>(get_item(widgets, "ind_on"));
+       ind_off = dynamic_cast<GLtk::Indicator *>(get_item(widgets, "ind_off"));
+       ind_halt = dynamic_cast<GLtk::Indicator *>(get_item(widgets, "ind_halt"));
+       lbl_status = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_status"));
+
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_on"))->signal_clicked.connect(sigc::mem_fun(this, &MainWindow::on_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_off"))->signal_clicked.connect(sigc::mem_fun(this, &MainWindow::off_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_halt"))->signal_clicked.connect(sigc::mem_fun(this, &MainWindow::halt_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_trains"))->signal_clicked.connect(sigc::mem_fun(this, &MainWindow::trains_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_quit"))->signal_clicked.connect(sigc::mem_fun(this, &MainWindow::quit_clicked));
+
+       R2C2::Driver &driver = engineer.get_layout().get_driver();
+       if(driver.get_power())
+               ind_on->set_active(true);
+       else
+               ind_off->set_active(true);
+
+       driver.signal_power.connect(sigc::mem_fun(this, &MainWindow::power_event));
+       driver.signal_halt.connect(sigc::mem_fun(this, &MainWindow::halt_event));
+}
+
+void MainWindow::set_status_text(const string &txt)
+{
+       lbl_status->set_text(txt);
+}
+
+void MainWindow::on_clicked()
+{
+       engineer.get_layout().get_driver().set_power(true);
+}
+
+void MainWindow::off_clicked()
+{
+       engineer.get_layout().get_driver().set_power(false);
+}
+
+void MainWindow::halt_clicked()
+{
+       R2C2::Driver &driver = engineer.get_layout().get_driver();
+       driver.halt(!driver.is_halted());
+}
+
+void MainWindow::trains_clicked()
+{
+       GLtk::Container *root = parent;
+       while(root->get_parent())
+               root = root->get_parent();
+
+       TrainListDialog *dlg = new TrainListDialog(engineer);
+       root->add(*dlg);
+       dlg->autosize();
+}
+
+void MainWindow::quit_clicked()
+{
+       engineer.quit();
+}
+
+void MainWindow::power_event(bool p)
+{
+       ind_on->set_active(p);
+       ind_off->set_active(!p);
+}
+
+void MainWindow::halt_event(bool h)
+{
+       ind_halt->set_active(h);
+}
diff --git a/source/engineer/mainwindow.h b/source/engineer/mainwindow.h
new file mode 100644 (file)
index 0000000..fcdb803
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MAINWINDOW_H_
+#define MAINWINDOW_H_
+
+#include <sigc++/trackable.h>
+#include <msp/gltk/indicator.h>
+#include <msp/gltk/label.h>
+#include <msp/gltk/panel.h>
+
+class Engineer;
+
+class MainWindow: public Msp::GLtk::Panel, public sigc::trackable
+{
+private:
+       Engineer &engineer;
+       Msp::GLtk::Indicator *ind_on;
+       Msp::GLtk::Indicator *ind_off;
+       Msp::GLtk::Indicator *ind_halt;
+       Msp::GLtk::Label *lbl_status;
+
+public:
+       MainWindow(Engineer &);
+
+       void set_status_text(const std::string &);
+private:
+       void on_clicked();
+       void off_clicked();
+       void halt_clicked();
+       void trains_clicked();
+       void quit_clicked();
+       void power_event(bool);
+       void halt_event(bool);
+};
+
+#endif
diff --git a/source/engineer/newtraindialog.cpp b/source/engineer/newtraindialog.cpp
new file mode 100644 (file)
index 0000000..11fd5c0
--- /dev/null
@@ -0,0 +1,78 @@
+#include <msp/core/maputils.h>
+#include <msp/strings/format.h>
+#include "libr2c2/catalogue.h"
+#include "libr2c2/driver.h"
+#include "libr2c2/layout.h"
+#include "libr2c2/vehicletype.h"
+#include "engineer.h"
+#include "newtraindialog.h"
+#include "traindialog.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+static string vehicle_type_name(const VehicleType *const &vehtype)
+{
+       return format("%s %s", vehtype->get_article_number(), vehtype->get_name());
+}
+
+NewTrainDialog::NewTrainDialog(Engineer &e):
+       engineer(e),
+       loco_types(vehicle_type_name)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/newtraindialog.ui", widgets);
+
+       drp_protocol = dynamic_cast<GLtk::Dropdown *>(get_item(widgets, "drp_protocol"));
+       drp_protocol->set_data(protocols);
+       ent_address = dynamic_cast<GLtk::Entry *>(get_item(widgets, "ent_address"));
+       ent_name = dynamic_cast<GLtk::Entry *>(get_item(widgets, "ent_name"));
+       drp_loco_type = dynamic_cast<GLtk::Dropdown *>(get_item(widgets, "drp_loco_type"));
+       drp_loco_type->set_data(loco_types);
+
+       const Driver &driver = engineer.get_layout().get_driver();
+       for(unsigned i=0;; ++i)
+       {
+               if(const char *proto = driver.enumerate_protocols(i))
+                       protocols.append(proto);
+               else
+                       break;
+       }
+
+       drp_protocol->set_selected_index(0);
+
+       const Catalogue::VehicleMap &cat_vtypes = engineer.get_layout().get_catalogue().get_vehicles();
+       for(Catalogue::VehicleMap::const_iterator i=cat_vtypes.begin(); i!=cat_vtypes.end(); ++i)
+               if(i->second->is_locomotive())
+                       loco_types.append(i->second);
+}
+
+void NewTrainDialog::on_response(int code)
+{
+       if(code)
+       {
+               int index = drp_protocol->get_selected_index();
+               if(index<0)
+                       return;
+               string protocol = protocols.get(index);
+
+               unsigned address = lexical_cast<unsigned>(ent_address->get_text());
+
+               index = drp_loco_type->get_selected_index();
+               if(index<0)
+                       return;
+               const VehicleType &type = *loco_types.get(index);
+
+               Train *train = new Train(engineer.get_layout(), type, address, protocol);
+               train->set_name(ent_name->get_text());
+
+               GLtk::Container *root = parent;
+               while(root->get_parent())
+                       root = root->get_parent();
+
+               TrainDialog *dlg = new TrainDialog(engineer, *train);
+               root->add(*dlg);
+               dlg->autosize();
+       }
+}
diff --git a/source/engineer/newtraindialog.h b/source/engineer/newtraindialog.h
new file mode 100644 (file)
index 0000000..9cec84e
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef NEWTRAINDIALOG_H_
+#define NEWTRAINDIALOG_H_
+
+#include <msp/gltk/dialog.h>
+#include <msp/gltk/dropdown.h>
+#include <msp/gltk/entry.h>
+#include <msp/gltk/listdata.h>
+#include "libr2c2/catalogue.h"
+
+class Engineer;
+
+class NewTrainDialog: public Msp::GLtk::Dialog
+{
+private:
+       Engineer &engineer;
+       Msp::GLtk::BasicListData<std::string> protocols;
+       Msp::GLtk::Dropdown *drp_protocol;
+       Msp::GLtk::Entry *ent_address;
+       Msp::GLtk::Entry *ent_name;
+       Msp::GLtk::FunctionListData<const R2C2::VehicleType *> loco_types;
+       Msp::GLtk::Dropdown *drp_loco_type;
+
+public:
+       NewTrainDialog(Engineer &);
+
+private:
+       virtual void on_response(int);
+};
+
+#endif
diff --git a/source/engineer/newvehicledialog.cpp b/source/engineer/newvehicledialog.cpp
new file mode 100644 (file)
index 0000000..f27370f
--- /dev/null
@@ -0,0 +1,65 @@
+#include <msp/core/maputils.h>
+#include <msp/gltk/button.h>
+#include <msp/gltk/toggle.h>
+#include <msp/strings/format.h>
+#include "libr2c2/catalogue.h"
+#include "libr2c2/layout.h"
+#include "newvehicledialog.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+static string vehicle_type_name(const VehicleType *const &vehtype)
+{
+       return format("%s %s", vehtype->get_article_number(), vehtype->get_name());
+}
+
+NewVehicleDialog::NewVehicleDialog(Train &t):
+       train(t),
+       loco_types(&vehicle_type_name),
+       wagon_types(&vehicle_type_name)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/newvehicledialog.ui", widgets);
+
+       dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_title"))->set_text(format("Add vehicle to %s", train.get_name()));
+
+       GLtk::List *lst_loco_types = dynamic_cast<GLtk::List *>(get_item(widgets, "lst_loco_types"));
+       lst_loco_types->set_data(loco_types);
+       active_list = lst_loco_types;
+
+       GLtk::List *lst_wagon_types = dynamic_cast<GLtk::List *>(get_item(widgets, "lst_wagon_types"));
+       lst_wagon_types->set_data(wagon_types);
+
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_loco_types"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &NewVehicleDialog::toggle_list), lst_loco_types));
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_wagon_types"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &NewVehicleDialog::toggle_list), lst_wagon_types));
+
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_add"))->signal_clicked.connect(sigc::mem_fun(this, &NewVehicleDialog::add_clicked));
+
+       const Catalogue::VehicleMap &cat_vtypes = train.get_layout().get_catalogue().get_vehicles();
+       for(Catalogue::VehicleMap::const_iterator i=cat_vtypes.begin(); i!=cat_vtypes.end(); ++i)
+       {
+               if(i->second->is_locomotive())
+                       loco_types.append(i->second);
+               else
+                       wagon_types.append(i->second);
+       }
+}
+
+void NewVehicleDialog::toggle_list(bool show, GLtk::List *lst)
+{
+       lst->set_visible(show);
+       if(show)
+               active_list = lst;
+}
+
+void NewVehicleDialog::add_clicked()
+{
+       int index = active_list->get_selected_index();
+       if(index>=0)
+       {
+               const VehicleType *vtype = static_cast<GLtk::ListDataStore<const R2C2::VehicleType *> &>(active_list->get_data()).get(index);
+               train.add_vehicle(*vtype);
+       }
+}
diff --git a/source/engineer/newvehicledialog.h b/source/engineer/newvehicledialog.h
new file mode 100644 (file)
index 0000000..7fee803
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef NEWVEHICLEDIALOG_H_
+#define NEWVEHICLEDIALOG_H_
+
+#include <msp/gltk/dialog.h>
+#include <msp/gltk/list.h>
+#include <msp/gltk/listdata.h>
+#include "libr2c2/train.h"
+#include "libr2c2/vehicletype.h"
+
+class NewVehicleDialog: public Msp::GLtk::Dialog
+{
+private:
+       R2C2::Train &train;
+       Msp::GLtk::FunctionListData<const R2C2::VehicleType *> loco_types;
+       Msp::GLtk::FunctionListData<const R2C2::VehicleType *> wagon_types;
+       Msp::GLtk::List *active_list;
+
+public:
+       NewVehicleDialog(R2C2::Train &);
+
+private:
+       void toggle_list(bool, Msp::GLtk::List *);
+       void add_clicked();
+};
+
+#endif
diff --git a/source/engineer/routerpanel.cpp b/source/engineer/routerpanel.cpp
new file mode 100644 (file)
index 0000000..e69a436
--- /dev/null
@@ -0,0 +1,83 @@
+#include <msp/core/maputils.h>
+#include <msp/core/raii.h>
+#include <msp/gltk/button.h>
+#include <msp/gltk/column.h>
+#include "libr2c2/layout.h"
+#include "libr2c2/route.h"
+#include "libr2c2/trainrouter.h"
+#include "engineer.h"
+#include "routerpanel.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+string route_name(const Route *const &route)
+{
+       return route->get_name();
+}
+
+RouterPanel::RouterPanel(Engineer &e, Train &t):
+       engineer(e),
+       train(t),
+       routes(&route_name),
+       updating(false)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/routerpanel.ui", widgets);
+
+       lbl_route = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_route"));
+       drp_routes = dynamic_cast<GLtk::Dropdown *>(get_item(widgets, "drp_routes"));
+       drp_routes->set_data(routes);
+       drp_routes->signal_item_selected.connect(sigc::mem_fun(this, &RouterPanel::route_selected));
+
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_goto"))->signal_clicked.connect(sigc::mem_fun(this, &RouterPanel::goto_clicked));
+
+       const set<Route *> &lroutes = train.get_layout().get_all<Route>();
+       for(set<Route *>::const_iterator i=lroutes.begin(); i!=lroutes.end(); ++i)
+               routes.append(*i);
+
+       TrainRouter *router = train.get_ai_of_type<TrainRouter>();
+       if(!router)
+               router = new TrainRouter(train);
+       update_route(router->get_route());
+
+       train.signal_ai_event.connect(sigc::mem_fun(this, &RouterPanel::ai_event));
+}
+
+void RouterPanel::ai_event(TrainAI &, const TrainAI::Message &msg)
+{
+       if(msg.type=="route-changed")
+               update_route(msg.value.value<const Route *>());
+}
+
+void RouterPanel::update_route(const Route *route)
+{
+       SetFlag setf(updating);
+       if(route)
+               lbl_route->set_text(route->get_name());
+       else
+               lbl_route->set_text("Free run");
+       drp_routes->set_selected_index(routes.find(route));
+}
+
+void RouterPanel::route_selected(unsigned index)
+{
+       if(!updating)
+       {
+               const Route *route = routes.get(index);
+               train.ai_message(TrainAI::Message("set-route", route));
+       }
+}
+
+void RouterPanel::goto_clicked()
+{
+       engineer.pick(false);
+       pick_conn = engineer.signal_pick_done.connect(sigc::mem_fun(this, &RouterPanel::goto_pick_done));
+}
+
+void RouterPanel::goto_pick_done(Track *track, unsigned)
+{
+       pick_conn.disconnect();
+       train.ai_message(TrainAI::Message("set-destination-block", &track->get_block()));
+}
diff --git a/source/engineer/routerpanel.h b/source/engineer/routerpanel.h
new file mode 100644 (file)
index 0000000..826e9a7
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef ROUTERPANEL_H_
+#define ROUTERPANEL_H_
+
+#include <msp/gltk/dropdown.h>
+#include <msp/gltk/label.h>
+#include <msp/gltk/panel.h>
+#include "libr2c2/train.h"
+
+class Engineer;
+
+class RouterPanel: public Msp::GLtk::Panel, public sigc::trackable
+{
+private:
+       Engineer &engineer;
+       R2C2::Train &train;
+       Msp::GLtk::Label *lbl_route;
+       Msp::GLtk::FunctionListData<const R2C2::Route *> routes;
+       Msp::GLtk::Dropdown *drp_routes;
+       sigc::connection pick_conn;
+       bool updating;
+
+public:
+       RouterPanel(Engineer &, R2C2::Train &);
+
+private:
+       void ai_event(R2C2::TrainAI &, const R2C2::TrainAI::Message &);
+       void update_route(const R2C2::Route *);
+       void route_selected(unsigned);
+       void goto_clicked();
+       void goto_pick_done(R2C2::Track *, unsigned);
+};
+
+#endif
diff --git a/source/engineer/routeselect.cpp b/source/engineer/routeselect.cpp
deleted file mode 100644 (file)
index ae3dd83..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#include <msp/gltk/label.h>
-#include "engineer.h"
-#include "libr2c2/route.h"
-#include "libr2c2/trainrouter.h"
-#include "routeselect.h"
-
-using namespace std;
-using namespace Msp;
-using namespace R2C2;
-
-RouteSelect::RouteSelect(Engineer &e, Train &t):
-       engineer(e),
-       train(t)
-{
-       set_size(200, 95);
-
-       GLtk::Label *label;
-       add(*(label = new GLtk::Label("Select route")));
-       label->set_geometry(GLtk::Geometry(10, geom.h-25, geom.w-20, 20));
-
-       add(*(drp_route = new GLtk::Dropdown));
-       drp_route->set_geometry(GLtk::Geometry(10, geom.h-50, geom.w-20, 20));
-       drp_route->append("(none)");
-       drp_route->set_selected_index(0);
-
-       const Route *current_route = 0;
-       if(TrainRouter *router = train.get_ai_of_type<TrainRouter>())
-               current_route = router->get_route();
-
-       const set<Route *> &routes = engineer.get_layout().get_all<Route>();
-       unsigned n = 1;
-       for(set<Route *>::const_iterator i=routes.begin(); i!=routes.end(); ++i)
-               if(!(*i)->is_temporary())
-               {
-                       drp_route->append((*i)->get_name());
-                       if(*i==current_route)
-                               drp_route->set_selected_index(n);
-                       ++n;
-               }
-}
-
-void RouteSelect::on_ok_clicked()
-{
-       if(drp_route->get_selected_index()>0)
-       {
-               const set<Route *> &routes = engineer.get_layout().get_all<Route>();
-               set<Route *>::const_iterator i = routes.begin();
-               unsigned n = drp_route->get_selected_index()-1;
-               while(i!=routes.end())
-               {
-                       if(!(*i)->is_temporary())
-                       {
-                               if(!n)
-                                       break;
-                               --n;
-                       }
-                       ++i;
-               }
-
-               train.ai_message(TrainAI::Message("set-route", *i));
-       }
-       else
-               train.ai_message(TrainAI::Message("clear-route"));
-}
diff --git a/source/engineer/routeselect.h b/source/engineer/routeselect.h
deleted file mode 100644 (file)
index e8dcbf7..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef ROUTESELECT_H_
-#define ROUTESELECT_H_
-
-#include <msp/gltk/dropdown.h>
-#include "libr2c2/train.h"
-#include "dialog.h"
-
-class Engineer;
-
-class RouteSelect: public Dialog
-{
-private:
-       Engineer &engineer;
-       R2C2::Train &train;
-       Msp::GLtk::Dropdown *drp_route;
-
-public:
-       RouteSelect(Engineer &, R2C2::Train &);
-private:
-       virtual void on_ok_clicked();
-};
-
-#endif
diff --git a/source/engineer/traindialog.cpp b/source/engineer/traindialog.cpp
new file mode 100644 (file)
index 0000000..4d1d492
--- /dev/null
@@ -0,0 +1,125 @@
+#include <msp/core/maputils.h>
+#include <msp/core/raii.h>
+#include <msp/gltk/stack.h>
+#include <msp/strings/format.h>
+#include "libr2c2/aicontrol.h"
+#include "libr2c2/catalogue.h"
+#include "libr2c2/layout.h"
+#include "libr2c2/trainstatus.h"
+#include "controlpanel.h"
+#include "routerpanel.h"
+#include "traindialog.h"
+#include "vehiclespanel.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+TrainDialog::TrainDialog(Engineer &e, R2C2::Train &t):
+       engineer(e),
+       train(t),
+       updating(false)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/traindialog.ui", widgets);
+
+       lbl_title = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_title"));
+       lbl_title->set_text(t.get_name());
+
+       btn_expand = dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_expand"));
+       btn_expand->signal_clicked.connect(sigc::mem_fun(this, &TrainDialog::expand_clicked));
+       lbl_speed = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_speed"));
+       sld_speed = dynamic_cast<GLtk::Slider *>(get_item(widgets, "sld_speed"));
+       sld_speed->signal_value_changed.connect(sigc::mem_fun(this, &TrainDialog::ui_speed_changed));
+       tgl_forward = dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_forward"));
+       tgl_forward->signal_toggled.connect(sigc::mem_fun(this, &TrainDialog::ui_forward_toggled));
+       lbl_status = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_status"));
+       pnl_expander = dynamic_cast<GLtk::Panel *>(get_item(widgets, "pnl_expander"));
+
+       Panel *pnl;
+
+       GLtk::Stack stack(*pnl_expander->get_layout());
+       stack.arrange(*get_item(widgets, "lbl_detail_placeholder"));
+
+       pnl_expander->add(*(pnl = new VehiclesPanel(train)));
+       panels.push_back(pnl);
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_vehicles"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainDialog::toggle_panel), pnl));
+
+       pnl_expander->add(*(pnl = new ControlPanel(engineer, train)));
+       pnl->set_visible(false);
+       panels.push_back(pnl);
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_control"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainDialog::toggle_panel), pnl));
+
+       pnl_expander->add(*(pnl = new RouterPanel(engineer, train)));
+       pnl->set_visible(false);
+       panels.push_back(pnl);
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_router"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainDialog::toggle_panel), pnl));
+
+       AIControl *control = train.get_ai_of_type<AIControl>();
+       if(!control)
+               control = new AIControl(train);
+       update_forward(!control->get_reverse());
+       update_speed_display(control->get_target_speed());
+
+       TrainStatus *status = train.get_ai_of_type<TrainStatus>();
+       if(!status)
+               status = new TrainStatus(train);
+       lbl_status->set_text(status->get_status());
+
+       train.signal_ai_event.connect(sigc::mem_fun(this, &TrainDialog::ai_event));
+}
+
+void TrainDialog::ai_event(TrainAI &, const TrainAI::Message &msg)
+{
+       if(msg.type=="status-changed")
+               lbl_status->set_text(msg.value.value<string>());
+       else if(msg.type=="target-speed-changed")
+               update_speed_display(msg.value.value<float>());
+       else if(msg.type=="reverse-changed")
+               update_forward(!msg.value.value<bool>());
+}
+
+void TrainDialog::update_speed_display(float speed)
+{
+       SetFlag setf(updating);
+       float scale_speed = speed*3.6/train.get_layout().get_catalogue().get_scale();
+       lbl_speed->set_text(format("%3.0f", scale_speed));
+       sld_speed->set_value(scale_speed);
+}
+
+void TrainDialog::ui_speed_changed(double value)
+{
+       if(!updating)
+       {
+               float real_speed = value/3.6*train.get_layout().get_catalogue().get_scale();
+               train.ai_message(TrainAI::Message("set-target-speed", real_speed));
+       }
+}
+
+void TrainDialog::update_forward(bool value)
+{
+       SetFlag setf(updating);
+       tgl_forward->set_value(value);
+}
+
+void TrainDialog::ui_forward_toggled(bool value)
+{
+       if(!updating)
+               train.ai_message(TrainAI::Message("set-reverse", !value));
+}
+
+void TrainDialog::expand_clicked()
+{
+       pnl_expander->set_visible(!pnl_expander->is_visible());
+       btn_expand->set_style(pnl_expander->is_visible() ? "arrow_up" : "arrow_down");
+       GLtk::Geometry ageom = geom;
+       ageom.h = 0;
+       layout->autosize(ageom);
+       ageom.y = geom.y+geom.h-ageom.h;
+       set_geometry(ageom);
+}
+
+void TrainDialog::toggle_panel(bool show, GLtk::Panel *panel)
+{
+       panel->set_visible(show);
+}
diff --git a/source/engineer/traindialog.h b/source/engineer/traindialog.h
new file mode 100644 (file)
index 0000000..e862487
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef TRAINDIALOG_H_
+#define TRAINDIALOG_H_
+
+#include <msp/gltk/button.h>
+#include <msp/gltk/dialog.h>
+#include <msp/gltk/hslider.h>
+#include <msp/gltk/label.h>
+#include <msp/gltk/toggle.h>
+#include "libr2c2/train.h"
+
+class Engineer;
+
+class TrainDialog: public Msp::GLtk::Dialog, public sigc::trackable
+{
+private:
+       Engineer &engineer;
+       R2C2::Train &train;
+       Msp::GLtk::Label *lbl_title;
+       Msp::GLtk::Button *btn_expand;
+       Msp::GLtk::Label *lbl_speed;
+       Msp::GLtk::Slider *sld_speed;
+       Msp::GLtk::Toggle *tgl_forward;
+       Msp::GLtk::Label *lbl_status;
+       Msp::GLtk::Panel *pnl_expander;
+       std::list<Msp::GLtk::Panel *> panels;
+       bool updating;
+
+public:
+       TrainDialog(Engineer &, R2C2::Train &);
+
+private:
+       void ai_event(R2C2::TrainAI &, const R2C2::TrainAI::Message &);
+       void update_speed_display(float);
+       void ui_speed_changed(double);
+       void update_forward(bool);
+       void ui_forward_toggled(bool);
+       void expand_clicked();
+       void toggle_panel(bool, Msp::GLtk::Panel *);
+};
+
+#endif
diff --git a/source/engineer/trainlistdialog.cpp b/source/engineer/trainlistdialog.cpp
new file mode 100644 (file)
index 0000000..0a31d67
--- /dev/null
@@ -0,0 +1,98 @@
+#include <msp/core/maputils.h>
+#include <msp/gltk/button.h>
+#include <msp/gltk/style.h>
+#include "engineer.h"
+#include "libr2c2/train.h"
+#include "newtraindialog.h"
+#include "traindialog.h"
+#include "trainlistdialog.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+class TrainItem: public GLtk::List::Item
+{
+private:
+       GLtk::Label address;
+       GLtk::Label name;
+
+public:        
+       typedef R2C2::Train *ValueType;
+
+       TrainItem(ValueType);
+
+private:
+       virtual void on_style_change();
+};
+
+
+TrainListDialog::TrainListDialog(Engineer &e):
+       engineer(e),
+       layout(engineer.get_layout())
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/trainlistdialog.ui", widgets);
+
+       lst_trains = dynamic_cast<GLtk::List *>(get_item(widgets, "lst_trains"));
+       lst_trains->set_data(trains);
+       lst_trains->set_item_type<TrainItem>();
+
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_new"))->signal_clicked.connect(sigc::mem_fun(this, &TrainListDialog::new_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_show"))->signal_clicked.connect(sigc::mem_fun(this, &TrainListDialog::show_clicked));
+
+       const map<unsigned, Train *> &ltrains = layout.get_trains();
+       for(map<unsigned, Train *>::const_iterator i=ltrains.begin(); i!=ltrains.end(); ++i)
+               trains.append(i->second);
+}
+
+void TrainListDialog::new_clicked()
+{
+       GLtk::Container *root = parent;
+       while(root->get_parent())
+               root = root->get_parent();
+
+       NewTrainDialog *dlg = new NewTrainDialog(engineer);
+       root->add(*dlg);
+       dlg->autosize();
+}
+
+void TrainListDialog::show_clicked()
+{
+       int index = lst_trains->get_selected_index();
+       if(index>=0)
+       {
+               GLtk::Container *root = parent;
+               while(root->get_parent())
+                       root = root->get_parent();
+
+               TrainDialog *dlg = new TrainDialog(engineer, *trains.get(index));
+               root->add(*dlg);
+               dlg->autosize();
+       }
+}
+
+
+TrainItem::TrainItem(ValueType train):
+       address(lexical_cast<string>(train->get_address())),
+       name(train->get_name())
+{
+       add(address);
+       add(name);
+}
+
+void TrainItem::on_style_change()
+{
+       if(!style)
+               return;
+
+       address.autosize();
+       name.autosize();
+
+       if(const GLtk::Part *part = style->get_part("children"))
+       {
+               const GLtk::Sides &margin = part->get_margin();
+               address.set_position(margin.left, margin.bottom);
+               name.set_position(margin.left+30, margin.bottom);
+       }
+}
diff --git a/source/engineer/trainlistdialog.h b/source/engineer/trainlistdialog.h
new file mode 100644 (file)
index 0000000..c12bcd4
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef TRAINLISTDIALOG_H_
+#define TRAINLISTDIALOG_H_
+
+#include <msp/gltk/dialog.h>
+#include <msp/gltk/list.h>
+#include <msp/gltk/listdata.h>
+#include "libr2c2/layout.h"
+
+class Engineer;
+
+class TrainListDialog: public Msp::GLtk::Dialog
+{
+private:
+       Engineer &engineer;
+       R2C2::Layout &layout;
+       Msp::GLtk::BasicListData<R2C2::Train *> trains;
+       Msp::GLtk::List *lst_trains;
+
+public:
+       TrainListDialog(Engineer &);
+
+private:
+       void new_clicked();
+       void show_clicked();
+};
+
+#endif
diff --git a/source/engineer/trainpanel.cpp b/source/engineer/trainpanel.cpp
deleted file mode 100644 (file)
index e239b1e..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-#include <cmath>
-#include <msp/strings/format.h>
-#include "libr2c2/aicontrol.h"
-#include "libr2c2/trackiter.h"
-#include "libr2c2/trainrouter.h"
-#include "libr2c2/vehicletype.h"
-#include "engineer.h"
-#include "routeselect.h"
-#include "trainpanel.h"
-#include "trainproperties.h"
-#include "trainview.h"
-
-using namespace std;
-using namespace Msp;
-using namespace R2C2;
-
-TrainPanel::TrainPanel(Engineer &e, Train &t):
-       engineer(e),
-       train(t),
-       status(train),
-       expanded(false)
-{
-       set_size(200, 65);
-
-       AIControl *ai = new AIControl(train);
-       ai->signal_event.connect(sigc::mem_fun(this, &TrainPanel::ai_event));
-
-       add(*(pnl_basic = new GLtk::Panel));
-       pnl_basic->set_style("group");
-       pnl_basic->set_geometry(GLtk::Geometry(0, geom.h-58, geom.w, 45));
-
-       pnl_basic->add(*(lbl_addr = new GLtk::Label(format("%2d", train.get_address()))));
-       lbl_addr->set_style("digital");
-       lbl_addr->set_geometry(GLtk::Geometry(10, 28, 35, 20));
-
-       pnl_basic->add(*(lbl_name = new GLtk::Label(train.get_name())));
-       lbl_name->set_style("digital");
-       lbl_name->set_geometry(GLtk::Geometry(50, 28, geom.w-77, 20));
-       train.signal_name_changed.connect(sigc::mem_fun(lbl_name, &GLtk::Label::set_text));
-
-       pnl_basic->add(*(lbl_speed = new GLtk::Label("  0")));
-       lbl_speed->set_style("digital");
-       lbl_speed->set_geometry(GLtk::Geometry(10, 3, 35, 20));
-
-       pnl_basic->add(*(sld_speed = new GLtk::HSlider));
-       sld_speed->set_geometry(GLtk::Geometry(50, 8, geom.w-80, 10));
-       sld_speed->set_range(0, 200);
-       sld_speed->set_step(5);
-       sld_speed->signal_value_changed.connect(sigc::mem_fun(this, &TrainPanel::speed_slider_changed));
-
-       pnl_basic->add(*(tgl_forward = new GLtk::Toggle));
-       tgl_forward->set_text("Fwd");
-       tgl_forward->set_geometry(GLtk::Geometry(geom.w-30, 0, 20, 27));
-       tgl_forward->set_value(true);
-       tgl_forward->signal_toggled.connect(sigc::mem_fun(this, &TrainPanel::forward_toggled));
-
-       pnl_basic->add(*(btn_expand = new GLtk::Button));
-       btn_expand->set_style("arrow_down");
-       btn_expand->set_geometry(GLtk::Geometry(geom.w-22, 28, 12, 20));
-       btn_expand->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::expand_clicked));
-
-       add(*(pnl_extra = new GLtk::Panel));
-       pnl_extra->set_style("group");
-       pnl_extra->set_geometry(GLtk::Geometry(0, 10, geom.w, 135));
-       pnl_extra->set_visible(false);
-
-       TrainRouter *router = train.get_ai_of_type<TrainRouter>();
-       if(!router)
-               router = new TrainRouter(train);
-
-       const Route *route = router->get_route();
-       pnl_extra->add(*(lbl_route = new GLtk::Label((route ? route->get_name() : "Free run"))));
-       lbl_route->set_style("digital");
-       lbl_route->set_geometry(GLtk::Geometry(10, 85, geom.w-20, 20));
-       router->signal_route_changed.connect(sigc::mem_fun(this, &TrainPanel::train_route_changed));
-
-       pnl_extra->add(*(lbl_status = new GLtk::Label(status.get_status())));
-       lbl_status->set_style("digital");
-       lbl_status->set_geometry(GLtk::Geometry(10, 60, geom.w-20, 20));
-       status.signal_changed.connect(sigc::mem_fun(this, &TrainPanel::train_status_changed));
-
-       const VehicleType::FunctionMap &funcs = train.get_locomotive_type().get_functions();
-       unsigned x = 10;
-       for(VehicleType::FunctionMap::const_iterator i=funcs.begin(); i!=funcs.end(); ++i, x+=36)
-       {
-               string fname = i->second;
-               fname[0] = toupper(fname[0]);
-               GLtk::Toggle *tgl;
-               pnl_extra->add(*(tgl = new GLtk::Toggle));
-               tgl->set_text(fname);
-               tgl->set_geometry(GLtk::Geometry(x, 108, 36, 27));
-               tgl->set_value(train.get_function(i->first));
-               tgl->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainPanel::func_toggled), i->first));
-
-               tgl_funcs[i->first] = tgl;
-       }
-       train.signal_function_changed.connect(sigc::mem_fun(this, &TrainPanel::train_function_changed));
-
-       GLtk::Button *btn;
-
-       pnl_extra->add(*(btn = new GLtk::Button("Edit")));
-       btn->set_geometry(GLtk::Geometry(10, 30, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::edit_clicked));
-
-       pnl_extra->add(*(btn = new GLtk::Button("Place")));
-       btn->set_geometry(GLtk::Geometry(10, 0, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::place_clicked));
-
-       pnl_extra->add(*(btn = new GLtk::Button("Take")));
-       btn->set_geometry(GLtk::Geometry(46, 0, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::take_clicked));
-
-       pnl_extra->add(*(btn = new GLtk::Button("GoTo")));
-       btn->set_geometry(GLtk::Geometry(100, 0, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::goto_clicked));
-
-       pnl_extra->add(*(btn = new GLtk::Button("Route")));
-       btn->set_geometry(GLtk::Geometry(100, 30, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::route_clicked));
-
-       pnl_extra->add(*(btn = new GLtk::Button("View")));
-       btn->set_geometry(GLtk::Geometry(geom.w-46, 30, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::view_clicked));
-}
-
-void TrainPanel::expand(bool e)
-{
-       expanded = e;
-       pnl_extra->set_visible(expanded);
-       if(expanded)
-       {
-               set_size(geom.w, 205);
-               btn_expand->set_style("arrow_up");
-       }
-       else
-       {
-               set_size(geom.w, 65);
-               btn_expand->set_style("arrow_down");
-       }
-       pnl_basic->set_geometry(GLtk::Geometry(0, geom.h-58, geom.w, 45));
-       engineer.rearrange_panels();
-}
-
-void TrainPanel::ai_event(const TrainAI::Message &msg)
-{
-       if(msg.type=="target-speed-changed")
-       {
-               float speed = msg.value.value<float>()/engineer.get_layout().get_catalogue().get_scale()*3.6;
-               sld_speed->set_value(speed);
-               lbl_speed->set_text(format("%3.0f", speed));
-       }
-       else if(msg.type=="reverse-changed")
-               tgl_forward->set_value(!msg.value.value<bool>());
-}
-
-void TrainPanel::train_function_changed(unsigned func, bool value)
-{
-       map<unsigned, GLtk::Toggle *>::iterator i = tgl_funcs.find(func);
-       if(i!=tgl_funcs.end())
-               i->second->set_value(value);
-}
-
-void TrainPanel::train_route_changed(const Route *r)
-{
-       if(r)
-               lbl_route->set_text(r->get_name());
-       else
-               lbl_route->set_text("Free run");
-}
-
-void TrainPanel::train_status_changed(const string &s)
-{
-       lbl_status->set_text(s);
-}
-
-void TrainPanel::place_clicked()
-{
-       engineer.pick(true);
-       pick_conn = engineer.signal_pick_done.connect(sigc::mem_fun(this, &TrainPanel::place));
-}
-
-void TrainPanel::take_clicked()
-{
-       train.unplace();
-}
-
-void TrainPanel::edit_clicked()
-{
-       TrainProperties *dialog = new TrainProperties(engineer, &train);
-       engineer.get_root().add(*dialog);
-       dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h);
-}
-
-void TrainPanel::route_clicked()
-{
-       RouteSelect *dialog = new RouteSelect(engineer, train);
-       engineer.get_root().add(*dialog);
-       dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h);
-}
-
-void TrainPanel::goto_clicked()
-{
-       engineer.pick(false);
-       pick_conn = engineer.signal_pick_done.connect(sigc::mem_fun(this, &TrainPanel::go_to));
-}
-
-void TrainPanel::view_clicked()
-{
-       TrainView *dialog = new TrainView(engineer, train);
-       engineer.get_root().add(*dialog);
-       dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h);
-}
-
-void TrainPanel::expand_clicked()
-{
-       expand(!expanded);
-}
-
-void TrainPanel::speed_slider_changed(double value)
-{
-       float speed = value/3.6*engineer.get_layout().get_catalogue().get_scale();
-       train.ai_message(TrainAI::Message("set-target-speed", speed));
-}
-
-void TrainPanel::forward_toggled(bool value)
-{
-       train.ai_message(TrainAI::Message("set-reverse", !value));
-}
-
-void TrainPanel::func_toggled(bool value, unsigned func)
-{
-       train.set_function(func, value);
-}
-
-void TrainPanel::place(Track *track, unsigned ep)
-{
-       pick_conn.disconnect();
-
-       BlockIter block = TrackIter(track, ep).block_iter();
-       if(block)
-               train.place(block);
-}
-
-void TrainPanel::go_to(Track *track, unsigned)
-{
-       pick_conn.disconnect();
-
-       TrainRouter *router = train.get_ai_of_type<TrainRouter>();
-       if(router)
-               router->set_destination(track->get_block());
-}
diff --git a/source/engineer/trainpanel.h b/source/engineer/trainpanel.h
deleted file mode 100644 (file)
index 1881035..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef TRAINPANEL_H_
-#define TRAINPANEL_H_
-
-#include <sigc++/trackable.h>
-#include <msp/gltk/button.h>
-#include <msp/gltk/hslider.h>
-#include <msp/gltk/label.h>
-#include <msp/gltk/panel.h>
-#include <msp/gltk/toggle.h>
-#include "libr2c2/route.h"
-#include "libr2c2/train.h"
-#include "libr2c2/trainstatus.h"
-
-class Engineer;
-
-class TrainPanel: public Msp::GLtk::Panel, public sigc::trackable
-{
-private:
-       Engineer &engineer;
-       R2C2::Train &train;
-       R2C2::TrainStatus status;
-       Msp::GLtk::Panel *pnl_basic;
-       Msp::GLtk::Panel *pnl_extra;
-       Msp::GLtk::Button *btn_expand;
-       Msp::GLtk::Label *lbl_addr;
-       Msp::GLtk::Label *lbl_name;
-       Msp::GLtk::HSlider *sld_speed;
-       Msp::GLtk::Label *lbl_speed;
-       Msp::GLtk::Label *lbl_route;
-       Msp::GLtk::Label *lbl_status;
-       Msp::GLtk::Toggle *tgl_forward;
-       std::map<unsigned, Msp::GLtk::Toggle *> tgl_funcs;
-       sigc::connection pick_conn;
-       bool expanded;
-
-public:
-       TrainPanel(Engineer &, R2C2::Train &);
-       void expand(bool = true);
-
-private:
-       void ai_event(const R2C2::TrainAI::Message &);
-       void train_function_changed(unsigned, bool);
-       void train_route_changed(const R2C2::Route *);
-       void train_status_changed(const std::string &);
-       void place_clicked();
-       void take_clicked();
-       void edit_clicked();
-       void route_clicked();
-       void goto_clicked();
-       void view_clicked();
-       void expand_clicked();
-       void speed_slider_changed(double);
-       void forward_toggled(bool);
-       void func_toggled(bool, unsigned);
-       void place(R2C2::Track *, unsigned);
-       void go_to(R2C2::Track *, unsigned);
-};
-
-#endif
diff --git a/source/engineer/trainproperties.cpp b/source/engineer/trainproperties.cpp
deleted file mode 100644 (file)
index d92750e..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-#include <msp/gltk/label.h>
-#include <msp/strings/format.h>
-#include <msp/strings/lexicalcast.h>
-#include "libr2c2/driver.h"
-#include "libr2c2/trainrouter.h"
-#include "libr2c2/vehicle.h"
-#include "libr2c2/vehicletype.h"
-#include "engineer.h"
-#include "trainproperties.h"
-
-using namespace std;
-using namespace Msp;
-using namespace R2C2;
-
-TrainProperties::TrainProperties(Engineer &e, Train *t):
-       engineer(e),
-       train(t)
-{
-       set_size(250, 305);
-
-       GLtk::Label *label;
-       add(*(label = new GLtk::Label("Train properties")));
-       label->set_geometry(GLtk::Geometry(10, geom.h-25, geom.w-20, 20));
-
-       add(*(ent_addr = new GLtk::Entry));
-       ent_addr->set_geometry(GLtk::Geometry(10, geom.h-50, 40, 20));
-
-       add(*(drp_protocol = new GLtk::Dropdown));
-       drp_protocol->set_geometry(GLtk::Geometry(60, geom.h-50, 60, 20));
-       for(unsigned i=0;; ++i)
-       {
-               if(const char *proto = engineer.get_layout().get_driver().enumerate_protocols(i))
-                       drp_protocol->append(proto);
-               else
-                       break;
-       }
-       drp_protocol->set_selected_index(0);
-
-       add(*(drp_type = new GLtk::Dropdown));
-       drp_type->set_geometry(GLtk::Geometry(130, geom.h-50, geom.w-140, 20));
-
-       const Catalogue::VehicleMap &vehs = engineer.get_catalogue().get_vehicles();
-       unsigned n = 0;
-       for(Catalogue::VehicleMap::const_iterator i=vehs.begin(); i!=vehs.end(); ++i)
-       {
-               if(!i->second->is_locomotive())
-                       continue;
-
-               drp_type->append(format("%d %s", i->second->get_article_number(), i->second->get_name()));
-               if(train && i->second==&train->get_locomotive_type())
-                       drp_type->set_selected_index(n);
-
-               ++n;
-       }
-
-       add(*(ent_name = new GLtk::Entry));
-       ent_name->set_geometry(GLtk::Geometry(10, geom.h-75, geom.w-20, 20));
-
-       add(*(drp_priority = new GLtk::Dropdown));
-       drp_priority->set_geometry(GLtk::Geometry(10, geom.h-100, geom.w-20, 20));
-       drp_priority->append("Standard freight");
-       drp_priority->append("Express freight");
-       drp_priority->append("Unspecified");
-       drp_priority->append("Standard passenger");
-       drp_priority->append("Express passenger");
-
-       add(*(lst_vehicles = new GLtk::List));
-       lst_vehicles->set_geometry(GLtk::Geometry(10, 100, geom.w-20, geom.h-205));
-
-       GLtk::Button *btn;
-
-       add(*(btn = new GLtk::Button("Rem")));
-       btn->set_geometry(GLtk::Geometry(10, 70, 40, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainProperties::remove_vehicle_clicked));
-
-       add(*(drp_new_vehicle = new GLtk::Dropdown));
-       drp_new_vehicle->set_geometry(GLtk::Geometry(10, 45, geom.w-20, 20));
-       drp_new_vehicle->append("(new vehicle)");
-       drp_new_vehicle->set_selected_index(0);
-       for(Catalogue::VehicleMap::const_iterator i=vehs.begin(); i!=vehs.end(); ++i)
-       {
-               if(i->second->is_locomotive())
-                       continue;
-
-               drp_new_vehicle->append(format("%d %s", i->second->get_article_number(), i->second->get_name()));
-       }
-       drp_new_vehicle->signal_item_selected.connect(sigc::mem_fun(this, &TrainProperties::new_vehicle_selected));
-
-       if(train)
-       {
-               ent_addr->set_text(lexical_cast<string>(train->get_address()));
-               ent_name->set_text(train->get_name());
-               if(TrainRouter *router = train->get_ai_of_type<TrainRouter>())
-                       drp_priority->set_selected_index(router->get_priority()+2);
-
-               unsigned n_vehicles = train->get_n_vehicles();
-               for(unsigned i=1; i<n_vehicles; ++i)
-               {
-                       const VehicleType &type = train->get_vehicle(i).get_type();
-                       lst_vehicles->append(format("%d %s", type.get_article_number(), type.get_name()));
-               }
-       }
-       else
-       {
-               ent_name->set_text(format("Train %d", engineer.get_layout().get_trains().size()+1));
-               drp_priority->set_selected_index(2);
-       }
-}
-
-void TrainProperties::on_ok_clicked()
-{
-       if(!train)
-       {
-               const VehicleType &type = get_vehicle_type(drp_type->get_selected_index(), true);
-               unsigned addr = lexical_cast<unsigned>(ent_addr->get_text());
-               train = new Train(engineer.get_layout(), type, addr, drp_protocol->get_selected());
-       }
-
-       train->set_name(ent_name->get_text());
-       if(TrainRouter *router = train->get_ai_of_type<TrainRouter>())
-               router->set_priority(drp_priority->get_selected_index()-2);
-
-       // The locomotive is vehicle 0 so we need to add 1
-       for(set<unsigned>::const_iterator i=rem_vehicles.end(); i!=rem_vehicles.begin();)
-               train->remove_vehicle(*--i+1);
-       for(vector<const VehicleType *>::const_iterator i=add_vehicles.begin(); i!=add_vehicles.end(); ++i)
-               train->add_vehicle(**i);
-}
-
-void TrainProperties::new_vehicle_selected(unsigned n, const string &)
-{
-       if(n==0)
-               return;
-
-       const VehicleType &type = get_vehicle_type(n-1, false);
-       add_vehicles.push_back(&type);
-       lst_vehicles->append(format("%d %s", type.get_article_number(), type.get_name()));
-
-       drp_new_vehicle->set_selected_index(0);
-}
-
-void TrainProperties::remove_vehicle_clicked()
-{
-       int selected = lst_vehicles->get_selected_index();
-       if(selected<0)
-               return;
-
-       lst_vehicles->remove(selected);
-
-       unsigned n_vehicles = (train ? train->get_n_vehicles()-rem_vehicles.size() : 0);
-       if(static_cast<unsigned>(selected)>=n_vehicles)
-               add_vehicles.erase(add_vehicles.begin()+(selected-n_vehicles));
-       else
-       {
-               for(set<unsigned>::const_iterator i=rem_vehicles.begin(); i!=rem_vehicles.end(); ++i)
-                       if(*i<=static_cast<unsigned>(selected))
-                               ++selected;
-               rem_vehicles.insert(selected);
-       }
-}
-
-const VehicleType &TrainProperties::get_vehicle_type(unsigned n, bool loco)
-{
-       const Catalogue::VehicleMap &vehs = engineer.get_catalogue().get_vehicles();
-       Catalogue::VehicleMap::const_iterator i = vehs.begin();
-       while(i!=vehs.end())
-       {
-               if(i->second->is_locomotive()==loco)
-               {
-                       if(!n)
-                               return *i->second;
-                       --n;
-               }
-               ++i;
-       }
-
-       throw out_of_range("TrainProperties::get_vehicle_type");
-}
diff --git a/source/engineer/trainproperties.h b/source/engineer/trainproperties.h
deleted file mode 100644 (file)
index 8cd0b2f..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef TRAINPROPERTIES_H_
-#define TRAINPROPERTIES_H_
-
-#include <msp/gltk/dropdown.h>
-#include <msp/gltk/entry.h>
-#include <libr2c2/train.h>
-#include "dialog.h"
-
-class Engineer;
-
-class TrainProperties: public Dialog
-{
-private:
-       Engineer &engineer;
-       R2C2::Train *train;
-       Msp::GLtk::Entry *ent_addr;
-       Msp::GLtk::Dropdown *drp_protocol;
-       Msp::GLtk::Dropdown *drp_type;
-       Msp::GLtk::Entry *ent_name;
-       Msp::GLtk::Dropdown *drp_priority;
-       Msp::GLtk::List *lst_vehicles;
-       Msp::GLtk::Dropdown *drp_new_vehicle;
-       std::vector<const R2C2::VehicleType *> add_vehicles;
-       std::set<unsigned> rem_vehicles;
-
-public:
-       TrainProperties(Engineer &, R2C2::Train *);
-private:
-       virtual void on_ok_clicked();
-       void new_vehicle_selected(unsigned, const std::string &);
-       void remove_vehicle_clicked();
-       const R2C2::VehicleType &get_vehicle_type(unsigned, bool);
-};
-
-#endif
diff --git a/source/engineer/trainselectdialog.h b/source/engineer/trainselectdialog.h
new file mode 100644 (file)
index 0000000..6bc88ef
--- /dev/null
@@ -0,0 +1 @@
+#ifndef 
index dffa04dde651be1768bda8b6ee20ed9cf4840ab6..ad534acee085872161d030ae6742fc909029a330 100644 (file)
@@ -1,6 +1,9 @@
+#include <msp/core/maputils.h>
 #include <msp/gl/tests.h>
 #include <msp/gltk/button.h>
 #include <msp/gltk/image.h>
+#include <msp/gltk/label.h>
+#include <msp/strings/format.h>
 #include "libr2c2/vehicle.h"
 #include "libr2c2/vehicletype.h"
 #include "engineer.h"
@@ -14,16 +17,22 @@ TrainView::TrainView(Engineer &e, const Train &t):
        train(t),
        mode(SIDE),
        forward(true),
-       pipeline(280, 280, false),
-       stale(false)
+       pipeline(400, 300, false)
 {
-       set_size(300, 330);
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/trainview.ui", widgets);
+
+       dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_title"))->set_text(format("View of %s", train.get_name()));
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_forward"))->signal_toggled.connect(sigc::mem_fun(this, &TrainView::set_forward));
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_roof"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainView::ui_mode_toggled), ROOF));
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_side"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainView::ui_mode_toggled), SIDE));
+       dynamic_cast<GLtk::Toggle *>(get_item(widgets, "tgl_head"))->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainView::ui_mode_toggled), HEAD));
 
        tex.set_min_filter(GL::LINEAR);
-       tex.storage(GL::RGB, 280, 280);
+       tex.storage(GL::RGB, 400, 300);
        tex.image(0, GL::RGB, GL::UNSIGNED_BYTE, 0);
        fbo.attach(GL::COLOR_ATTACHMENT0, tex, 0);
-       depth.storage(GL::DEPTH_COMPONENT, 280, 280);
+       depth.storage(GL::DEPTH_COMPONENT, 400, 300);
        fbo.attach(GL::DEPTH_ATTACHMENT, depth);
 
        camera.set_up_direction(GL::Vector3(0, 0, 1));
@@ -37,32 +46,7 @@ TrainView::TrainView(Engineer &e, const Train &t):
        pass->set_depth_test(&GL::DepthTest::lequal());
        pass->set_lighting(&engineer.get_lighting());
 
-       GLtk::Image *image;
-       add(*(image = new GLtk::Image(&tex)));
-       image->set_geometry(GLtk::Geometry(10, 40, geom.w-20, geom.h-50));
-
-       GLtk::Button *btn;
-
-       add(*(btn = new GLtk::Button("Roof")));
-       btn->set_geometry(GLtk::Geometry(10, 10, 36, 25));
-       btn->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, &TrainView::set_mode), ROOF));
-
-       add(*(btn = new GLtk::Button("Side")));
-       btn->set_geometry(GLtk::Geometry(46, 10, 36, 25));
-       btn->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, &TrainView::set_mode), SIDE));
-
-       add(*(btn = new GLtk::Button("Head")));
-       btn->set_geometry(GLtk::Geometry(82, 10, 36, 25));
-       btn->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, &TrainView::set_mode), HEAD));
-
-       add(*(tgl_forward = new GLtk::Toggle("Fwd")));
-       tgl_forward->set_geometry(GLtk::Geometry(118, 8, 36, 27));
-       tgl_forward->set_value(true);
-       tgl_forward->signal_toggled.connect(sigc::mem_fun(this, &TrainView::set_forward));
-
-       add(*(btn = new GLtk::Button("Close")));
-       btn->set_geometry(GLtk::Geometry(geom.w-46, 10, 36, 25));
-       btn->signal_clicked.connect(sigc::mem_fun(this, &TrainView::close_clicked));
+       dynamic_cast<GLtk::Image *>(get_item(widgets, "img_view"))->set_image(&tex);
 
        engineer.add_train_view(*this);
 }
@@ -77,6 +61,12 @@ void TrainView::set_mode(Mode m)
        mode = m;
 }
 
+void TrainView::ui_mode_toggled(bool v, Mode m)
+{
+       if(v)
+               set_mode(m);
+}
+
 void TrainView::set_forward(bool f)
 {
        forward = f;
@@ -113,15 +103,3 @@ void TrainView::prepare()
        fbo.clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
        pipeline.render();
 }
-
-void TrainView::button_release(int x, int y, unsigned btn)
-{
-       GLtk::Panel::button_release(x, y, btn);
-       if(stale)
-               delete this;
-}
-
-void TrainView::close_clicked()
-{
-       stale = true;
-}
index 4e0cb39e58cfe85ecf3ad156c47cde3fc64641c3..6ee9b4e77e4a66669b84859c23b444345b277711 100644 (file)
@@ -4,13 +4,13 @@
 #include <msp/gl/renderbuffer.h>
 #include <msp/gl/framebuffer.h>
 #include <msp/gl/texture2d.h>
-#include <msp/gltk/panel.h>
+#include <msp/gltk/dialog.h>
 #include <msp/gltk/toggle.h>
 #include "libr2c2/train.h"
 
 class Engineer;
 
-class TrainView: public Msp::GLtk::Panel
+class TrainView: public Msp::GLtk::Dialog
 {
 public:
        enum Mode
@@ -31,18 +31,17 @@ private:
        Msp::GL::Renderbuffer depth;
        Msp::GL::Camera camera;
        Msp::GL::Pipeline pipeline;
-       bool stale;
 
 public:
        TrainView(Engineer &, const R2C2::Train &);
        ~TrainView();
 
        void set_mode(Mode);
+private:
+       void ui_mode_toggled(bool, Mode);
+public:
        void set_forward(bool);
        void prepare();
-private:
-       virtual void button_release(int, int, unsigned);
-       void close_clicked();
 };
 
 #endif
diff --git a/source/engineer/vehiclespanel.cpp b/source/engineer/vehiclespanel.cpp
new file mode 100644 (file)
index 0000000..d906f6c
--- /dev/null
@@ -0,0 +1,70 @@
+#include <msp/core/maputils.h>
+#include <msp/gltk/button.h>
+#include <msp/strings/format.h>
+#include "libr2c2/layout.h"
+#include "libr2c2/vehicle.h"
+#include "newvehicledialog.h"
+#include "vehiclespanel.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+static string vehicle_name(Vehicle *const &veh)
+{
+       return format("%s %s", veh->get_type().get_article_number(), veh->get_type().get_name());
+}
+
+VehiclesPanel::VehiclesPanel(Train &t):
+       train(t),
+       vehicles(&vehicle_name)
+{
+       Loader::WidgetMap widgets;
+       DataFile::load(*this, "data/vehiclespanel.ui", widgets);
+
+       lst_vehicles = dynamic_cast<GLtk::List *>(get_item(widgets, "lst_vehicles"));
+       //lst_vehicles->set_data(vehicles);
+
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_add"))->signal_clicked.connect(sigc::mem_fun(this, &VehiclesPanel::add_clicked));
+       dynamic_cast<GLtk::Button *>(get_item(widgets, "btn_remove"))->signal_clicked.connect(sigc::mem_fun(this, &VehiclesPanel::remove_clicked));
+
+       unsigned n_veh = train.get_n_vehicles();
+       for(unsigned i=0; i<n_veh; ++i)
+               vehicles.append(&train.get_vehicle(i));
+
+       train.signal_vehicle_added.connect(sigc::mem_fun(this, &VehiclesPanel::vehicle_added));
+       train.signal_vehicle_removed.connect(sigc::mem_fun(this, &VehiclesPanel::vehicle_removed));
+
+       lst_vehicles->set_data(vehicles);
+}
+
+void VehiclesPanel::add_clicked()
+{
+       GLtk::Container *root = parent;
+       while(root->get_parent())
+               root = root->get_parent();
+
+       NewVehicleDialog *dlg = new NewVehicleDialog(train);
+       root->add(*dlg);
+       dlg->autosize();
+}
+
+void VehiclesPanel::remove_clicked()
+{
+       int index = lst_vehicles->get_selected_index();
+       if(index>0)
+       {
+               train.remove_vehicle(index);
+               lst_vehicles->set_selected_index(static_cast<unsigned>(index)<vehicles.size() ? index : index-1);
+       }
+}
+
+void VehiclesPanel::vehicle_added(unsigned i, Vehicle &veh)
+{
+       vehicles.insert(i, &veh);
+}
+
+void VehiclesPanel::vehicle_removed(unsigned i, Vehicle &)
+{
+       vehicles.remove(i);
+}
diff --git a/source/engineer/vehiclespanel.h b/source/engineer/vehiclespanel.h
new file mode 100644 (file)
index 0000000..4ce674d
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef VEHICLESPANEL_H_
+#define VEHICLESPANEL_H_
+
+#include <msp/gltk/list.h>
+#include <msp/gltk/listdata.h>
+#include <msp/gltk/panel.h>
+#include "libr2c2/train.h"
+
+class VehiclesPanel: public Msp::GLtk::Panel
+{
+private:
+       R2C2::Train &train;
+       Msp::GLtk::FunctionListData<R2C2::Vehicle *> vehicles;
+       Msp::GLtk::List *lst_vehicles;
+
+public:
+       VehiclesPanel(R2C2::Train &);
+
+private:
+       void add_clicked();
+       void remove_clicked();
+       void vehicle_added(unsigned, R2C2::Vehicle &);
+       void vehicle_removed(unsigned, R2C2::Vehicle &);
+};
+
+#endif