#include <plib/puAux.h>
-#include <simgear/route/waypoint.hxx>
#include <simgear/structure/callback.hxx>
#include <simgear/sg_inlines.h>
#include <Main/fg_props.hxx>
#include <Navaids/positioned.hxx>
+#include <Navaids/routePath.hxx>
#include <Autopilot/route_mgr.hxx>
+// select if the widget grabs keys necessary to fly aircraft from the keyboard,
+// or not. See http://code.google.com/p/flightgear-bugs/issues/detail?id=338
+// for discussion about why / what is going on.
+#define AVOID_FLIGHT_KEYS 1
+
using namespace flightgear;
enum {
static const double BLINK_TIME = 0.3;
static const int DRAG_START_DISTANCE_PX = 5;
-class RouteManagerWaypointModel :
+class FlightPlanWaypointModel :
public WaypointList::Model,
public SGPropertyChangeListener
{
public:
- RouteManagerWaypointModel()
- {
- _rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-
+ FlightPlanWaypointModel(flightgear::FlightPlan* fp) :
+ _fp(fp)
+ {
SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
+ SGPropertyNode* flightplanChanged = fgGetNode("/autopilot/route-manager/signals/flightplan-changed", true);
routeEdited->addChangeListener(this);
+ flightplanChanged->addChangeListener(this);
}
- virtual ~RouteManagerWaypointModel()
+ ~FlightPlanWaypointModel()
{
SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
+ SGPropertyNode* flightplanChanged = fgGetNode("/autopilot/route-manager/signals/flightplan-changed", true);
routeEdited->removeChangeListener(this);
+ flightplanChanged->removeChangeListener(this);
}
// implement WaypointList::Model
virtual unsigned int numWaypoints() const
{
- return _rm->numWaypts();
+ return _fp->numLegs();
}
virtual int currentWaypoint() const
{
- return _rm->currentIndex();
+ return _fp->currentIndex();
}
virtual flightgear::Waypt* waypointAt(unsigned int index) const
return NULL;
}
- return _rm->wayptAtIndex(index);
+ return _fp->legAtIndex(index)->waypoint();
}
+ virtual flightgear::FlightPlan* flightplan() const
+ {
+ return _fp;
+ }
+
virtual void deleteAt(unsigned int index)
{
- _rm->removeWayptAtIndex(index);
+ _fp->deleteIndex(index);
}
virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex)
--destIndex;
}
- WayptRef w(_rm->removeWayptAtIndex(srcIndex));
- SG_LOG(SG_GENERAL, SG_INFO, "wpt:" << w->ident());
- _rm->insertWayptAtIndex(w, destIndex);
+ int currentWpIndex = currentWaypoint();
+
+ WayptRef w = _fp->legAtIndex(srcIndex)->waypoint();
+ _fp->deleteIndex(srcIndex);
+ _fp->insertWayptAtIndex(w, destIndex);
+
+ if ((signed int) srcIndex == currentWpIndex) {
+ // current waypoint was moved
+ _fp->setCurrentIndex(destIndex);
+ }
}
virtual void setUpdateCallback(SGCallback* cb)
// implement SGPropertyChangeListener
void valueChanged(SGPropertyNode *prop)
{
- if (_cb) {
- (*_cb)();
+ if (prop->getNameString() == "edited") {
+ if (_cb) {
+ (*_cb)();
+ }
+ }
+
+ if (prop->getNameString() == "flightplan-changed") {
+ _fp =
+ static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"))->flightPlan();
}
}
private:
- FGRouteMgr* _rm;
+ flightgear::FlightPlan* _fp;
SGCallback* _cb;
};
{
// pretend to be a list, so fgPopup doesn't mess with our mouse events
type |= PUCLASS_LIST;
- setModel(new RouteManagerWaypointModel());
+ flightgear::FlightPlan* fp =
+ static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"))->flightPlan();
+ setModel(new FlightPlanWaypointModel(fp));
setSize(width, height);
setValue(-1);
_dragSourceRow = rowForY(y - abox.min[1]);
Waypt* wp = _model->waypointAt(_dragSourceRow);
- if (!wp || wp->flag(WPT_GENERATED)) {
+ if (!wp || wp->flag(WPT_GENERATED) || (wp->type() == "discontinuity")) {
return; // don't allow generated points to be dragged
}
y -= (_scrollPx % rowHeight); // partially draw the first row
_arrowWidth = legendFont.getStringWidth(">");
+
+ RoutePath path(_model->flightplan());
+
for ( ; row <= final; ++row, y += rowHeight) {
- drawRow(dx, dy, row, y);
+ drawRow(dx, dy, row, y, path);
} // of row drawing iteration
glDisable(GL_SCISSOR_TEST);
}
}
-void WaypointList::drawRow(int dx, int dy, int rowIndex, int y)
+void WaypointList::drawRow(int dx, int dy, int rowIndex, int y,
+ const RoutePath& path)
{
flightgear::Waypt* wp(_model->waypointAt(rowIndex));
bool isSelected = (rowIndex == getSelected());
bool isCurrent = (rowIndex == _model->currentWaypoint());
bool isDragSource = (_dragging && (rowIndex == _dragSourceRow));
-
+
puBox bkgBox = abox;
bkgBox.min[1] = abox.max[1] - y;
bkgBox.max[1] = bkgBox.min[1] + rowHeightPx();
if (wp->flag(WPT_MISS)) {
drawBox = true;
puSetColor(col, 1.0, 0.0, 0.0, 0.3); // red
- } else if (wp->flag(WPT_ARRIVAL)) {
+ } else if (wp->flag(WPT_ARRIVAL) || wp->flag(WPT_DEPARTURE)) {
drawBox = true;
puSetColor(col, 0.0, 0.0, 0.0, 0.3);
- } else if (wp->flag(WPT_DEPARTURE)) {
+ } else if (wp->flag(WPT_APPROACH)) {
drawBox = true;
- puSetColor(col, 0.0, 0.0, 0.0, 0.3);
+ puSetColor(col, 0.0, 0.0, 0.1, 0.3);
}
if (isDragSource) {
int x = xx;
x += _arrowWidth + PUSTR_LGAP;
-
- // row textual data
+ drawRowText(x, yy, rowIndex, path);
- char buffer[128];
- int count = ::snprintf(buffer, 128, "%03d %-5s", rowIndex, wp->ident().c_str());
-
- FGPositioned* src = wp->source();
- if (src && !src->name().empty() && (src->name() != wp->ident())) {
- // append name if present, and different to id
- ::snprintf(buffer + count, 128 - count, " (%s)", src->name().c_str());
- }
- drawClippedString(legendFont, buffer, x, yy, 300);
- x += 300 + PUSTR_LGAP;
-
- if (_showLatLon) {
- SGGeod p(wp->position());
- char ns = (p.getLatitudeDeg() > 0.0) ? 'N' : 'S';
- char ew = (p.getLongitudeDeg() > 0.0) ? 'E' : 'W';
-
- ::snprintf(buffer, 128 - count, "%4.2f%c %4.2f%c",
- fabs(p.getLongitudeDeg()), ew, fabs(p.getLatitudeDeg()), ns);
- } else if (rowIndex > 0) {
- double courseDeg;
- double distanceM;
- Waypt* prev = _model->waypointAt(rowIndex - 1);
- boost::tie(courseDeg, distanceM) = wp->courseAndDistanceFrom(prev->position());
-
- ::snprintf(buffer, 128 - count, "%03.0f %5.1fnm",
- courseDeg, distanceM * SG_METER_TO_NM);
+ if (isDragSource) {
+ puSetColor(col, 1.0, 0.5, 0.0, 0.5);
+ bkgBox.draw(dx, dy, PUSTYLE_PLAIN, &col, false, 0);
}
+}
+
+void WaypointList::drawRowText(int x, int baseline, int rowIndex, const RoutePath& path)
+{
+ flightgear::Waypt* wp(_model->waypointAt(rowIndex));
+ const bool isDiscontinuity = (wp->type() == "discontinuity");
+ const bool isVia = (wp->type() == "via");
+
+ char buffer[128];
+ int count;
+
+ if (isVia) {
+ // VIA has long ident but no name
+ count = ::snprintf(buffer, 128, "%03d %s", rowIndex, wp->ident().c_str());
+ drawClippedString(legendFont, buffer, x, baseline, 300);
+ x += 300 + PUSTR_LGAP;
+ } else {
+ count = ::snprintf(buffer, 128, "%03d %-5s", rowIndex, wp->ident().c_str());
+
+ FGPositioned* src = wp->source();
+ if (src && !src->name().empty() && (src->name() != wp->ident())) {
+ // append name if present, and different to id
+ ::snprintf(buffer + count, 128 - count, " (%s)", src->name().c_str());
+ }
+
+ drawClippedString(legendFont, buffer, x, baseline, 300);
+ x += 300 + PUSTR_LGAP;
- f->drawString(buffer, x, yy);
+ if (isDiscontinuity) {
+ return;
+ }
+
+ if (_showLatLon) {
+ // only show for non-dynamic waypoints
+ if (!wp->flag(WPT_DYNAMIC)) {
+ SGGeod p(wp->position());
+ char ns = (p.getLatitudeDeg() > 0.0) ? 'N' : 'S';
+ char ew = (p.getLongitudeDeg() > 0.0) ? 'E' : 'W';
+
+ ::snprintf(buffer, 128 - count, "%4.2f%c %4.2f%c",
+ fabs(p.getLongitudeDeg()), ew, fabs(p.getLatitudeDeg()), ns);
+ } else {
+ buffer[0] = 0;
+ }
+ } else if (rowIndex > 0) {
+ double courseDeg = path.trackForIndex(rowIndex);
+ double distanceM = path.distanceForIndex(rowIndex);
+ ::snprintf(buffer, 128 - count, "%03.0f %5.1fnm",
+ courseDeg, distanceM * SG_METER_TO_NM);
+ }
+ } // of is not a VIA waypoint
+
+ puFont* f = &legendFont;
+ f->drawString(buffer, x, baseline);
x += 100 + PUSTR_LGAP;
-
+
if (wp->altitudeRestriction() != RESTRICT_NONE) {
+ char aboveAtBelow = ' ';
+ if (wp->altitudeRestriction() == RESTRICT_ABOVE) {
+ aboveAtBelow = 'A';
+ } else if (wp->altitudeRestriction() == RESTRICT_BELOW) {
+ aboveAtBelow = 'B';
+ }
+
int altHundredFt = (wp->altitudeFt() + 50) / 100; // round to nearest 100ft
if (altHundredFt < 100) {
- count = ::snprintf(buffer, 128, "%d'", altHundredFt * 100);
+ count = ::snprintf(buffer, 128, "%d'%c", altHundredFt * 100, aboveAtBelow);
} else { // display as a flight-level
- count = ::snprintf(buffer, 128, "FL%d", altHundredFt);
+ count = ::snprintf(buffer, 128, "FL%d%c", altHundredFt, aboveAtBelow);
}
-
- f->drawString(buffer, x, yy);
+
+ f->drawString(buffer, x, baseline);
} // of valid wp altitude
x += 60 + PUSTR_LGAP;
-
+
if (wp->speedRestriction() == SPEED_RESTRICT_MACH) {
count = ::snprintf(buffer, 126, "%03.2fM", wp->speedMach());
- f->drawString(buffer, x, yy);
+ f->drawString(buffer, x, baseline);
} else if (wp->speedRestriction() != RESTRICT_NONE) {
count = ::snprintf(buffer, 126, "%dKts", (int) wp->speedKts());
- f->drawString(buffer, x, yy);
- }
-
- if (isDragSource) {
- puSetColor(col, 1.0, 0.5, 0.0, 0.5);
- bkgBox.draw(dx, dy, PUSTYLE_PLAIN, &col, false, 0);
+ f->drawString(buffer, x, baseline);
}
}
if ((updown == PU_UP) || !isVisible () || !isActive () || (window != puGetWindow())) {
return FALSE ;
}
+
+#ifdef AVOID_FLIGHT_KEYS
+ return FALSE;
+#endif
switch (key)
{