// mainwin2.cpp -- main window // // Written by Frederic Bouvier, started October 2001. // // Copyright (C) 2001 Frederic Bouvier - fredb@users.sourceforge.net // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // $Id: mainwin2.cpp,v 1.16 2005/06/25 16:31:14 fredb2 Exp $ #ifdef _MSC_VER #pragma warning( disable : 4503 4786 4800 ) #define _USE_MATH_DEFINES #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mainwin.hpp" #include "objlibwin.hpp" #include "objectstatic.hpp" #include "statobjact.hpp" #include "messwin.hpp" #include "scenerytools.hpp" #include "baseobjact.hpp" #include "curveact.hpp" #include "curvtypwin.hpp" #include "matwin.hpp" #include "sdutil.hpp" #include "tileact.hpp" #include "runways.hpp" #include "tile.hpp" #include "curvetools.hpp" #include #include #include #include #include #include #include extern FGSD_Preferences generalPreferences; // 200 = nombre de points // 120 = 1 / 0:00'30'' // ==> 200 points d'ecran pour 30 sec d'arc ( 1km sur un meridien ) #define SCALE_FACTOR ( 200 * 120 ) static const double FGSD_EPSILON = 0.000005; void FGSD_MainWindow::center_cb( Fl_Widget *o, void *v ) { static_cast( v )->center_cb( o ); } void FGSD_MainWindow::center_cb(Fl_Widget *) { centerPosition( _curx, _cury ); } void FGSD_MainWindow::changeObjectHeading_cb( Fl_Widget *o, void *v ) { static_cast( v )->changeObjectHeading_cb( o ); } void FGSD_MainWindow::changeObjectHeading_cb(Fl_Widget *) { FGSD_ObjectStatic *obj = (FGSD_ObjectStatic *)_curObject->convertTo( FGSD_BaseObject::ObjectStatic ); double hdg = obj->getHeading(); FGSD_Window *w = new FGSD_Window( 250, 240, "Change object heading" ); w->begin(); Fl_Output *o = new Fl_Output( 10, 10, 230, 30 ); o->value( obj->name().c_str() ); Fl_Float_Input *f = new Fl_Float_Input( 160, 110, 80, 30, "Heading :" ); f->align( FL_ALIGN_TOP | FL_ALIGN_LEFT ); f->when( FL_WHEN_CHANGED ); std::ostringstream str; str << hdg << std::ends; f->value( str.str().c_str() ); Fl_Dial *d = new Fl_Dial( 10, 50, 140, 140 ); d->angles( 360, 0 ); d->step( 1.0 / 360.0 ); d->value( hdg / 360.0 ); d->callback( &FGSD_MainWindow::changeObjectHeading_dial_cb, f ); f->callback( &FGSD_MainWindow::changeObjectHeading_input_cb, d ); Fl_Button *b = new Fl_Button( 10, 200, 40, 30, "Ok" ); b->callback( &FGSD_MainWindow::changeObjectHeading_button_cb, (void *)1 ); b = new Fl_Button( 60, 200, 60, 30, "Cancel" ); b->callback( &FGSD_MainWindow::changeObjectHeading_button_cb, (void *)0 ); w->end(); if ( w->exec() ) { double hdg = d->value() * 360.0; FGSD_ObjectStatic *obj = (FGSD_ObjectStatic *)_curObject->convertTo( FGSD_BaseObject::ObjectStatic ); newAction( new FGSD_ChangeStaticObjectAction( obj->getName().c_str(), obj->getLon(), obj->getLat(), obj->getElev(), obj->getHeading(), hdg - obj->getHeading() ) ); obj->changeHeading( hdg ); _area->redraw(); } delete w; } void FGSD_MainWindow::changeObjectHeading_dial_cb(Fl_Widget *o, void *v) { std::ostringstream str; str << ((Fl_Valuator *)o)->value() * 360.0 << std::ends; ((Fl_Float_Input *)v)->value( str.str().c_str() ); } void FGSD_MainWindow::changeObjectHeading_input_cb(Fl_Widget *o, void *v) { std::istringstream str( ((Fl_Float_Input *)o)->value() ); double hdg = 0; str >> hdg; ((Fl_Dial *)v)->value( hdg / 360 ); } void FGSD_MainWindow::changeObjectHeading_button_cb(Fl_Widget *o, void *v) { FGSD_Window *w = static_cast( o->parent() ); int val = (int)v; if ( v ) w->set_value(); else w->clear_value(); w->hide(); } void FGSD_MainWindow::addNewStaticObject_cb(Fl_Widget *o, void *v) { static_cast( v )->addNewStaticObject_cb( o ); } void FGSD_MainWindow::addNewStaticObject_cb(Fl_Widget *) { _lockMouse = true; _objlib->setMode( FGSD_ObjectLibraryWindow::Select ); if ( _objlib->exec( _window, true ) ) { std::string model = _objlib->getCurrentModel(); if ( model.size() ) { std::vector textures = _objlib->getTexturesForCurrentModel(); addStaticObject( model.c_str(), textures, _curx, _cury, 0, 0 ); newAction( new FGSD_AddRemoveStaticObjectAction( true, model.c_str(), _curx, _cury, 0, 0, textures ) ); } } _lockMouse = false; _area->redraw(); } void FGSD_MainWindow::removeStaticObject_cb(Fl_Widget *o, void *v) { static_cast( v )->removeStaticObject_cb( o ); } void FGSD_MainWindow::removeStaticObject_cb(Fl_Widget *) { FGSD_MessageWindow mess( "FlightGear Scenery Designer", "The current object will be removed from the project.\nDo you want to continue ?", FGSD_MessageWindow::Warning, FGSD_MessageWindow::Yes | FGSD_MessageWindow::No ); FGSD_MessageWindow::Button b = mess.exec(); if ( b == FGSD_MessageWindow::Yes ) { FGSD_Tile *tile = _curObject->tile(); if ( tile->removeObject( _curObject ) ) { FGSD_ObjectStatic *obj = (FGSD_ObjectStatic *)_curObject->convertTo( FGSD_BaseObject::ObjectStatic ); FGSD_AddRemoveStaticObjectAction *act = 0; if ( obj ) act = new FGSD_AddRemoveStaticObjectAction( false, obj->getName().c_str(), obj->getLon(), obj->getLat(), obj->getElev(), obj->getHeading(), obj->getTextures() ); delete _curObject; _curObject = 0; _area->redraw(); if ( act ) newAction( act ); } } } void FGSD_MainWindow::changeVertexHeight_cb(Fl_Widget *o, void *v) { static_cast( v )->changeVertexHeight_cb( o ); } void FGSD_MainWindow::changeVertexHeight_cb(Fl_Widget *) { const char *buf = _sceneryTools->heightValue(); if ( strlen( buf ) > 0 ) { double height; std::istringstream str( buf ); str >> height; FGSD_TriangleObject *obj = (FGSD_TriangleObject *)_curObject->convertTo( FGSD_BaseObject::TriangleObject ); Point3D p0 = obj->current(); if ( !obj->interior() ) { bool xmin, ymin; bool xlock = obj->xlock( xmin ); bool ylock = obj->ylock( ymin ); if ( !( xlock || ylock ) ) { newAction( new FGSD_ChangeHeightBaseObjectAction( obj->getName().c_str(), p0.x(), p0.y(), height - p0.z() ) ); obj->changeCurrentPointHeight( height ); obj->tile()->recalcStaticObjectHeight(); } else if ( !( xlock && ylock ) ) { FGSD_TriangleObject *adjObject = 0; if ( xlock ) adjObject = getAdjacentTerrainObject( obj->tile(), true, xmin ); else adjObject = getAdjacentTerrainObject( obj->tile(), false, ymin ); adjObject->setCurrent( p0 ); newAction( new FGSD_ChangeHeightBaseObjectAction( obj->getName().c_str(), p0.x(), p0.y(), height - p0.z() ) ); obj->changeCurrentPointHeight( height ); Point3D p = obj->current(); adjObject->changeCurrentPointHeight( p.z() ); obj->tile()->recalcStaticObjectHeight(); adjObject->tile()->recalcStaticObjectHeight(); } else { FGSD_TriangleObject *adjxObject = getAdjacentTerrainObject( obj->tile(), true, xmin ); FGSD_TriangleObject *adjyObject = getAdjacentTerrainObject( obj->tile(), false, ymin ); FGSD_TriangleObject *adjxyObject = getAdjacentTerrainObject( adjxObject->tile(), false, ymin ); adjxObject->setCurrent( p0 ); adjyObject->setCurrent( p0 ); adjxyObject->setCurrent( p0 ); newAction( new FGSD_ChangeHeightBaseObjectAction( obj->getName().c_str(), p0.x(), p0.y(), height - p0.z() ) ); obj->changeCurrentPointHeight( height ); Point3D p = obj->current(); adjxObject->changeCurrentPointHeight( p.z() ); adjyObject->changeCurrentPointHeight( p.z() ); adjxyObject->changeCurrentPointHeight( p.z() ); obj->tile()->recalcStaticObjectHeight(); adjxObject->tile()->recalcStaticObjectHeight(); adjyObject->tile()->recalcStaticObjectHeight(); adjxyObject->tile()->recalcStaticObjectHeight(); } _area->redraw(); } } } void FGSD_MainWindow::copyCurrentHeight_cb(Fl_Widget *o, void *v) { static_cast( v )->copyCurrentHeight_cb( o ); } void FGSD_MainWindow::copyCurrentHeight_cb(Fl_Widget *) { std::ostringstream str; str << (int)(_curz * 100.0 + .5) / 100.0 << std::ends; _sceneryTools->heightValue( str.str().c_str() ); } void FGSD_MainWindow::remCurvePoint_cb(Fl_Widget *o, void *v) { static_cast( v )->remCurvePoint_cb( false ); } void FGSD_MainWindow::remCurvePoint_cb( bool selected ) { FGSD_Curve *curve = selected ? _curCurve : (FGSD_Curve *)_curObject->convertTo( FGSD_BaseObject::Curve ); if ( curve ) { Point3D p0 = curve->firstPoint(); Point3D p = selected ? curve->currentPoint() : curve->mousePoint(); size_t index = selected ? curve->current() : curve->mouseIndex(); if ( curve->removeMousePoint( selected ) ) { bool removed = false; if ( !curve->hasPoints() ) { for ( CurveList::iterator ci = _curveList.begin(); ci != _curveList.end(); ++ci ) { if ( *ci == curve ) { if ( _curCurve == curve ) _curCurve = 0; _curveList.erase( ci ); delete curve; _curObject = 0; removed = true; break; } } } if ( !removed && p0 == p ) p0 = curve->firstPoint(); newAction( new FGSD_RemoveCurvePointAction( p0.x(), p0.y(), index, p.x(), p.y(), removed ) ); _area->redraw(); } } } void FGSD_MainWindow::remCurve_cb(Fl_Widget *o, void *v) { static_cast( v )->remCurve_cb( false ); } void FGSD_MainWindow::remCurve_cb( bool selected ) { FGSD_Curve *curve = selected ? _curCurve : (FGSD_Curve *)_curObject->convertTo( FGSD_BaseObject::Curve ); if ( curve ) { for ( CurveList::iterator ci = _curveList.begin(); ci != _curveList.end(); ++ci ) { if ( *ci == curve ) { if ( _curCurve == curve ) _curCurve = 0; _curveList.erase( ci ); delete curve; _curObject = 0; break; } } _area->redraw(); } } void FGSD_MainWindow::embedCurve_cb(Fl_Widget *o, void *v) { ((FGSD_MainWindow *)v)->embedCurve_cb( false ); } void FGSD_MainWindow::embedCurve_cb( bool selected ) { FGSD_Curve *curve = selected ? _curCurve : (FGSD_Curve *)_curObject->convertTo( FGSD_BaseObject::Curve ); if ( curve && curve->curveType() != FGSD_Curve::UnSet ) { curve->embedInTiles( this ); _area->redraw(); } else if ( curve && curve->curveType() == FGSD_Curve::UnSet ) { FGSD_MessageWindow mess( "FlightGear Scenery Designer", "The curve type has to be set before embedding.\nUse the property window to set curve type", FGSD_MessageWindow::Error, FGSD_MessageWindow::Ok ); mess.exec(); } } void FGSD_MainWindow::curveType_cb(Fl_Widget *o, void *v) { static_cast( v )->curveType_cb( false ); } void FGSD_MainWindow::curveType_cb( bool selected ) { FGSD_Curve *curve = selected ? _curCurve : (FGSD_Curve *)_curObject->convertTo( FGSD_BaseObject::Curve ); if ( curve ) { FGSD_CurveTypeWindow win( curve ); if ( win.exec() ) { newAction( new FGSD_CurvePropertiesAction( *curve, win.type(), win.height(), win.material(), win.water() ) ); curve->curveType( win.type() ); curve->height( win.height() ); curve->material( win.material() ); curve->water( win.water() ); } } } void FGSD_MainWindow::addTile_cb( Fl_Widget *, void *v ) { static_cast( v )->addTile_cb(); } void FGSD_MainWindow::addTile_cb() { } void FGSD_MainWindow::startFlightGear_cb( Fl_Widget *, void *v ) { static_cast( v )->startFlightGear_cb(); } void FGSD_MainWindow::startFlightGear_cb() { /* std::string cmd = generalPreferences.fgCommandLine(); size_t pos = 0; while ( ( pos = cmd.find( '%', pos ) ) != std::string::npos ) { size_t l = cmd.length(); if ( pos + 1 < l && cmd[ pos + 1 ] == '%' ) cmd.erase( pos, 1 ); else if ( pos + 3 < l && cmd.substr( pos + 1, 3 ) == "lon" ) { std::string lon = FGSD_Util::positionToString( _curx, true ); lon.erase( 1, 1 ); if ( lon[ 0 ] == 'W' ) lon[ 0 ] = '-'; else if ( lon[ 0 ] == 'E' ) lon.erase( 0, 1 ); cmd.replace( pos, 4, lon.c_str() ); } else if ( pos + 3 < l && cmd.substr( pos + 1, 3 ) == "lat" ) { std::string lat = FGSD_Util::positionToString( _cury, false ); lat.erase( 1, 1 ); if ( lat[ 0 ] == 'S' ) lat[ 0 ] = '-'; else if ( lat[ 0 ] == 'N' ) lat.erase( 0, 1 ); cmd.replace( pos, 4, lat.c_str() ); } pos += 1; } char *c = new char[ cmd.length() + 1 ]; strcpy( c, cmd.c_str() ); Fl_Thread t; Fl_create_thread( t, &FGSD_MainWindow::startFlightGear, (void*)c ); */ } void *FGSD_MainWindow::startFlightGear( void *param ) { char *c = static_cast( param ); system( c ); delete[] c; return 0; } void FGSD_MainWindow::changeMaterial_cb(Fl_Widget *, void *v) { static_cast( v )->changeMaterial_cb(); } void FGSD_MainWindow::changeMaterial_cb() { FGSD_MaterialWindow mat( _curMaterial ); if ( mat.exec() ) { changeMaterial( mat.material(), _curx, _cury ); _lastMaterialSet = mat.material(); _area->redraw(); } } void FGSD_MainWindow::repeatChangeMaterial_cb(Fl_Widget *, void *v) { static_cast( v )->repeatChangeMaterial_cb(); } void FGSD_MainWindow::repeatChangeMaterial_cb() { if ( !_lastMaterialSet.empty() ) { changeMaterial( _lastMaterialSet, _curx, _cury ); _area->redraw(); } } bool FGSD_MainWindow::checkModified() { bool load = true; if ( _actionMgr.modified() ) { FGSD_MessageWindow mess( "FlightGear Scenery Designer", "The current project has been modified.\nDo you want to save it ?", FGSD_MessageWindow::Warning, FGSD_MessageWindow::Yes | FGSD_MessageWindow::No | FGSD_MessageWindow::Cancel ); FGSD_MessageWindow::Button b = mess.exec(); if ( b == FGSD_MessageWindow::Yes ) load = saveProject( false ); else if ( b == FGSD_MessageWindow::Cancel ) load = false; } return load; } void FGSD_MainWindow::load( const char *__fileName ) { SGPropertyNode root; try { readProperties( __fileName, &root ); } catch (sg_io_exception& e) { std::cerr << "Error reading '" << __fileName << "': " << e.getFormattedMessage() << "\n"; return; } size_t nbActions = 0; if ( ( nbActions = _actionMgr.getWeight( __fileName, &root ) ) ) { _progress->value( 0 ); _progress->labelcolor(FL_RED); _progress->maximum( nbActions ); Fl::check(); _actionMgr.loadAndExecute( __fileName, &root ); std::vector cCurves = root.getChildren( "curve" ); size_t nb = cCurves.size(); for ( size_t i = 0; i < nb; i++ ) { FGSD_Curve *curve = new FGSD_Curve; if ( curve->load( cCurves[ i ] ) ) { curve->setNewCurve( true ); _curveList.push_back( curve ); } else delete curve; } postTileLoad( _tileList ); _progress->labelcolor(FL_WHITE); _progress->value( 0 ); updateUndoMenu(); _valid = ( _fragmentList.size() > 0 ) || ( _tileList.size() > 0 ); overallBoundingBox( _minx, _miny, _maxx, _maxy ); } else { // Legacy project SGPropertyNode *fnode = root.getChild( "fragments" ); if ( fnode ) { std::vector cPath = fnode->getChildren( "path" ); std::vector cRelpath = fnode->getChildren( "relpath" ); _progress->value( 0 ); _progress->labelcolor(FL_RED); _progress->maximum( cPath.size() + cRelpath.size() ); Fl::check(); size_t nb = cPath.size(); size_t i; for ( i = 0; i < nb; i++ ) { SGPropertyNode *pnode = cPath[ i ]; loadMapFragment( pnode->getStringValue() ); _progress->value( _progress->value() + 1 ); Fl::check(); } size_t nbr = cRelpath.size(); for ( i = 0; i < nbr; i++ ) { SGPropertyNode *pnode = cRelpath[ i ]; FGSD_MapFragment *fragment = createFragment( __fileName, pnode->getStringValue() ); _fragmentList.push_back( fragment ); _progress->value( _progress->value() + 1 ); Fl::check(); } _valid = ( _fragmentList.size() > 0 ); _progress->labelcolor(FL_WHITE); _progress->value( 0 ); overallBoundingBox( _minx, _miny, _maxx, _maxy ); } SGPropertyNode *bnode = root.getChild( "tile-boundary" ); if ( bnode ) { SGPropertyNode *node; node = bnode->getChild( "min-lon-deg" ); _minx = node->getDoubleValue(); node = bnode->getChild( "min-lat-deg" ); _miny = node->getDoubleValue(); node = bnode->getChild( "max-lon-deg" ); _maxx = node->getDoubleValue(); node = bnode->getChild( "max-lat-deg" ); _maxy = node->getDoubleValue(); TileList newTiles = importTiles( generalPreferences.fgRootPath().c_str(), generalPreferences.fgSceneryPath().c_str() ); postTileLoad( newTiles ); for ( TileList::iterator it = newTiles.begin(); it != newTiles.end(); ++it ) _tileList[ it->first ] = it->second; } } FGSD_BaseObject::_factor = cos( _miny * M_PI / 180 ); refreshAirportList(); } FGSD_MainWindow::TileList FGSD_MainWindow::importTiles( const char *root, const char *base ) { TileList newTiles; _minBucket.set_bucket( _minx, _miny ); SGBucket b( _maxx, _maxy ); if ( b.get_chunk_lon() + b.get_x() * b.get_width() == _maxx && b.get_chunk_lat() + b.get_y() * b.get_height() == _maxy ) { _maxBucket.set_bucket( b.get_chunk_lon() + ( b.get_x() - 1 ) * b.get_width(), b.get_chunk_lat() + ( b.get_y() - 1 ) * b.get_height() ); } else _maxBucket.set_bucket( _maxx, _maxy ); _minx = _minBucket.get_chunk_lon() + _minBucket.get_x() * _minBucket.get_width(); _miny = _minBucket.get_chunk_lat() + _minBucket.get_y() * _minBucket.get_height(); if ( _maxx < _maxBucket.get_chunk_lon() + _maxBucket.get_x() * _maxBucket.get_width() ) _maxx = _maxBucket.get_chunk_lon() + ( _maxBucket.get_x() + 1 ) * _maxBucket.get_width(); if ( _maxy < _maxBucket.get_chunk_lat() + _maxBucket.get_y() * _maxBucket.get_height() ) _maxy = _maxBucket.get_chunk_lat() + ( _maxBucket.get_y() + 1 ) * _maxBucket.get_height(); FGSD_AddRemoveTileAction *act = 0; int dx = 0, dy = 0; sgBucketDiff( _minBucket, _maxBucket, &dx, &dy ); _progress->labelcolor(FL_RED); _progress->maximum( ( dx + 1 ) * ( dy + 1 ) + _tileList.size() ); _progress->value( 0 ); Fl::check(); // Only remove unwanted tiles TileList::iterator it = _tileList.begin(); while ( it != _tileList.end() ) { FGSD_Tile *t = it->second; SGBucket b = t->getBucket(); if ( b.get_chunk_lon() + b.get_x() * b.get_width() > _maxx || b.get_chunk_lon() + ( b.get_x() + 1 ) * b.get_width() <= _minx || b.get_chunk_lat() + b.get_y() * b.get_height() > _maxy || b.get_chunk_lat() + ( b.get_y() + 1 ) * b.get_height() <= _miny ) { newAction( new FGSD_AddRemoveTileAction( false, b.gen_index_str().c_str() ) ); _tileList.erase( it++ ); delete t; } else it++; _progress->value( _progress->value() + 1 ); Fl::check(); } FGSD_BaseObject::_factor = cos( _miny * M_PI / 180 ); for ( int j = 0; j <= dy; j++ ) { for ( int i = 0; i <= dx; i++ ) { SGBucket b = sgBucketOffset( _minx, _miny, i, j ); // To take care of variable bucket span if ( b.get_chunk_lon() + ( b.get_x() + 1 ) * b.get_width() <= _maxx ) { if ( !findTile( b.get_chunk_lon() + b.get_x() * b.get_width(), b.get_chunk_lat() + b.get_y() * b.get_height() ) ) { FGSD_Tile *tile = new FGSD_Tile( b ); bool ok = tile->load( root, base ); if ( ok ) { newAction( new FGSD_AddRemoveTileAction( true, b.gen_index_str().c_str() ) ); newTiles[ tile->getName() ] = tile; } else delete tile; } _progress->value( _progress->value() + 1 ); Fl::check(); } else { _progress->value( _progress->value() + 1 ); Fl::check(); break; } } } return newTiles; } void FGSD_MainWindow::save( const char *__fileName ) { setCursor( FL_CURSOR_WAIT ); SGPropertyNode root; _actionMgr.save( __fileName, &root ); size_t i = 0; for ( CurveList::iterator ci = _curveList.begin(); ci != _curveList.end(); ++ci ) { SGPropertyNode *cNode = root.getChild( "curve", i, true ); cNode->setAttributes( SGPropertyNode::READ | SGPropertyNode::WRITE | SGPropertyNode::ARCHIVE ); FGSD_Curve *curve = *ci; curve->save( cNode ); i++; } writeProperties( __fileName, &root ); setCursor( FL_CURSOR_DEFAULT ); } void FGSD_MainWindow::refreshRecentFiles() { _recentSubMenu.clear(); if ( generalPreferences.historySize() ) { for ( size_t i = 0; i < generalPreferences.historySize(); i++ ) { std::ostringstream str; if ( i < 9 ) str << "&" << i + 1 << " "; else if ( i == 9 ) str << "1&0 "; str << generalPreferences.getFileFromHistory( i ); _recentSubMenu.add( str.str().c_str(), 0, &FGSD_MainWindow::recentFile_cb, this ); } } else _recentSubMenu.addNone(); } void FGSD_MainWindow::refreshAirportList() { _aptList.clear(); TileList::const_iterator i; for ( i = _tileList.begin(); i != _tileList.end(); ++i ) { FGSD_Tile *tile = i->second; tile->getAirportList( _aptList ); } _airportSubMenu.clear(); if ( _aptList.size() ) { std::sort( _aptList.begin(), _aptList.end() ); for ( size_t i = 0; i < _aptList.size(); i++ ) { FGSD_Airport apt; _airports->search( _aptList[ i ].c_str(), apt ); std::ostringstream str; if ( i < 9 ) str << "&" << i + 1 << " "; else if ( i == 9 ) str << "1&0 "; str << _aptList[ i ] << " - " << apt.name; _airportSubMenu.add( str.str().c_str(), 0, &FGSD_MainWindow::gotoApt_cb, this ); } } else _airportSubMenu.addNone(); } void FGSD_MainWindow::centerPosition( double lon, double lat ) { gotoPosition( lon, lat, _area->w(), _area->h() ); } void FGSD_MainWindow::gotoPosition( double lon, double lat, int __2x, int __2y ) { // To avoid precision loss on int, position on screen is multiplied by 2 // the division by 2 implied by this is made on double // double fy = SCALE_FACTOR * _zLevel; double fx = fy * cos( _miny * M_PI / 180 ); int W = _area->w(); int H = _area->h(); double posx = lon - ( _visw * __2x ) / ( 2 * W ); if ( _visw < _maxx - _minx ) { if ( posx + _visw > _maxx ) posx = _maxx - _visw; if ( posx < _minx ) posx = _minx; } double posy = lat - _vish + ( _vish * __2y ) / ( 2 * H ); if ( _vish < _maxy - _miny ) { if ( posy + _vish > _maxy ) posy = _maxy - _vish; if ( posy < _miny ) posy = _miny; } _posx = posx; _posy = posy; double lv = ( _maxy - _miny ) * fy; int p = (int)( lv - H - ( _posy - _miny ) * fy ); _vscroll->value( p, H, 0, (int)lv ); if ( p >= 0 ) _vscroll->activate(); else _vscroll->deactivate(); lv = ( _maxx - _minx ) * fx; p = (int)( ( _posx - _minx ) * fx ); _hscroll->value( p, W, 0, (int)lv ); if ( p >= 0 ) _hscroll->activate(); else _hscroll->deactivate(); _area->valid(0); _area->redraw(); } void FGSD_MainWindow::overallBoundingBox( double &x1, double &y1, double &x2, double &y2 ) const { bool first = true; FragmentList::const_iterator i = _fragmentList.begin(); while ( i != _fragmentList.end() ) { double a1, b1, a2, b2; getFragmentBoundingBox( *i, a1, b1, a2, b2 ); if ( first ) { x1 = a1; x2 = a2; y1 = b1; y2 = b2; first = false; } else { if ( a1 < x1 ) x1 = a1; if ( b1 < y1 ) y1 = b1; if ( a2 > x2 ) x2 = a2; if ( b2 > y2 ) y2 = b2; } ++i; } TileList::const_iterator ti = _tileList.begin(); while ( ti != _tileList.end() ) { SGBucket b = ti->second->getBucket(); double a1 = b.get_chunk_lon() + b.get_x() * b.get_width(); double b1 = b.get_chunk_lat() + b.get_y() * b.get_height(); double a2 = a1 + b.get_width(); double b2 = b1 + b.get_height(); if ( first ) { x1 = a1; x2 = a2; y1 = b1; y2 = b2; first = false; } else { if ( a1 < x1 ) x1 = a1; if ( b1 < y1 ) y1 = b1; if ( a2 > x2 ) x2 = a2; if ( b2 > y2 ) y2 = b2; } ++ti; } x1 -= 0.05; y1 -= 0.02; x2 += 0.05; y2 += 0.02; } bool FGSD_MainWindow::propertiesUnderMouse( double lon, double lat, double &height, std::string &material, std::string &airportID ) const { bool ret = false; TileList::const_iterator i; for ( i = _tileList.begin(); !ret && i != _tileList.end(); ++i ) { FGSD_Tile *tile = i->second; ret = tile->propertiesUnderMouse( lon, lat, height, material, airportID ); } return ret; } FGSD_BaseObject *FGSD_MainWindow::objectUnderMouse() { FGSD_BaseObject *ret = 0; CurveList::const_iterator ci; for ( ci = _curveList.begin(); !ret && ci != _curveList.end(); ++ci ) { FGSD_Curve *curve = *ci; if ( curve->objectUnderMouse( _curx, _cury, _zLevel ) ) ret = curve; } TileList::const_iterator i; for ( i = _tileList.begin(); !ret && i != _tileList.end(); ++i ) { FGSD_Tile *tile = i->second; ret = tile->objectUnderMouse( _curx, _cury, _zLevel ); } return ret; } FGSD_Tile *FGSD_MainWindow::findTile( double lon, double lat ) const { FGSD_Tile *ret = 0; TileList::const_iterator i; for ( i = _tileList.begin(); !ret && i != _tileList.end(); ++i ) { FGSD_Tile *tile = i->second; if ( tile->positionInTile( lon, lat ) ) { ret = tile; break; } } return ret; } FGSD_TriangleObject *FGSD_MainWindow::getAdjacentTerrainObject( FGSD_Tile *__tile, bool __lon, bool __min ) { FGSD_TriangleObject *ret = 0; std::string name = __tile->getName(); long index; std::istringstream str( name ); str >> index; SGBucket b( index ); double lon = b.get_chunk_lon() + b.get_x() * b.get_width(); double lat = b.get_chunk_lat() + b.get_y() * b.get_height(); if ( __lon ) { double offset = b.get_width(); if ( __min ) offset = -offset; lon += offset; } else { double offset = b.get_height(); if ( __min ) offset = -offset; lat += offset; SGBucket k( lon, lat ); if ( k.get_width() < b.get_width() ) { // Special case : adjacent tile has a different width than the primary tile // In this case, we must limit x in [0,w/2[ or [w/2,w[ so we need another // flag indicating which half of the edge is affected } } FGSD_Tile *adj = findTile( lon, lat ); if ( adj ) ret = adj->getTerrain(); return ret; } void FGSD_MainWindow::setCursor( Fl_Cursor __cursor ) { //std::cout << "cursor p " << (void *)__cursor << " l " << l << std::endl; delete _currentShape; _currentShape = 0; _currentCursor = __cursor; if ( __cursor == FL_CURSOR_DEFAULT ) _window->cursor( FL_CURSOR_NONE ); _window->cursor( __cursor ); } static unsigned int cursorAndMasks[ FGSD_MainWindow::NbShapes ][ 32 ] = { { 0xffffffff, 0xff3fffff, 0xf207ffff, 0xe003ffff, 0xe002ffff, 0xf0007fff, 0xf0007fff, 0xc8007fff, 0x80007fff, 0x8000ffff, 0xc000ffff, 0xe000ffff, 0xe001ffff, 0xf001ffff, 0xf803ffff, 0xfc03ffff, 0xfc03ffff, 0xffffffff, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF } }; static unsigned int cursorXorMasks[ FGSD_MainWindow::NbShapes ][ 32 ] = { { 0x00000000, 0x00000000, 0x00c00000, 0x0cd80000, 0x0cd80000, 0x06d90000, 0x06db0000, 0x03fb0000, 0x33ff0000, 0x3bfe0000, 0x1ffe0000, 0x0ffe0000, 0x0ffc0000, 0x07fc0000, 0x03f80000, 0x01f80000, 0x01f80000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }; static int hotSpot[ FGSD_MainWindow::NbShapes ][ 2 ] = { { 9, 9 } }; void FGSD_MainWindow::setCursor( Cursor_Shape __shape ) { //std::cout << "cursor s " << __shape << std::endl; _currentCursor = FL_CURSOR_DEFAULT; delete _currentShape; _currentShape = new Fl_Cursor_Shape( hotSpot[ __shape ][ 0 ], hotSpot[ __shape ][ 1 ], cursorAndMasks[ __shape ], cursorXorMasks[ __shape ] ); fl_cursor( _currentShape ); } void FGSD_MainWindow::updateUndoMenu() { FGSD_Action *act = _actionMgr.firstUndoable(); if ( act ) { std::string text = "&Undo '" + act->description() + "'"; _undoItem.label( text.c_str() ); _undoItem.activate(); _ubutton->activate(); setButtonImage( _ubutton, Undo_i, false ); _ulbutton->activate(); } else { _undoItem.label( "&Undo" ); _undoItem.deactivate(); _ubutton->deactivate(); setButtonImage( _ubutton, Undo_i, true ); _ulbutton->deactivate(); } act = _actionMgr.firstRedoable(); if ( act ) { std::string text = "&Redo '" + act->description() + "'"; _redoItem.label( text.c_str() ); _redoItem.activate(); _rbutton->activate(); setButtonImage( _rbutton, Redo_i, false ); _rlbutton->activate(); } else { _redoItem.label( "&Redo" ); _redoItem.deactivate(); _rbutton->deactivate(); setButtonImage( _rbutton, Redo_i, true ); _rlbutton->deactivate(); } setTitle(); } void FGSD_MainWindow::stepBanner_cb( size_t o, void *v ) { static_cast( v )->stepBanner_cb( o ); } void FGSD_MainWindow::stepBanner_cb( size_t n ) { static size_t cnt = 0; cnt += n; _banProgress->value( _banProgress->value() + 1 ); _banProgress->redraw(); if ( cnt > 200 ) { Fl::check(); cnt -= 200; } } void FGSD_MainWindow::showBanner_cb( void *v ) { static_cast( v )->showBanner( true ); } void FGSD_MainWindow::showBanner( bool progress ) { _banWindow = new FGSD_Window( ( Fl::w() - 600 ) / 2, ( Fl::h() - 120 ) / 2, 600, 120 ); _banWindow->clear_border(); _banWindow->begin(); _banImage = new Fl_Box( 0, 0, 600, 60 ); _banImage->image( _banner ); _banText = new Fl_Box( 0, 60, 600, 30 ); _banText->box( FL_NO_BOX ); if ( progress ) { _banText->align( FL_ALIGN_INSIDE | FL_ALIGN_LEFT ); _banProgress = new Flu_Progress( 0, 90, 600, 30 ); _banProgress->box( FL_DOWN_BOX ); _banProgress->align( FL_ALIGN_BOTTOM ); _banProgress->selection_color(FL_BLUE); _banProgress->color(FL_WHITE); _banProgress->labelcolor(FL_WHITE); } else { _banText->align( FL_ALIGN_INSIDE | FL_ALIGN_CENTER ); _banText->label( "Version " VERSION ); Fl_Button *o = new Fl_Button( 260, 90, 80, 30, "Close" ); o->callback( &FGSD_MainWindow::closeAbout_cb, this ); } _banWindow->end(); if ( progress ) { size_t nbApt = generalPreferences.airportNumber(); _banProgress->value( 0 ); _banProgress->labelcolor(FL_RED); _banProgress->maximum( nbApt ); _banText->label( "Loading airport database..." ); Fl_Window* saved_modal = Fl::modal(); Fl_Window* saved_grab = Fl::grab(); _banWindow->set_modal(); _banWindow->show(); Fl::check(); FGSD_AirportUtil::loadDatabase( generalPreferences.fgRootPath().c_str(), *_airports, *_runways, *_taxiways, nbApt, &FGSD_MainWindow::stepBanner_cb, this ); _banWindow->set_non_modal(); _banWindow->hide(); generalPreferences.airportNumber( nbApt ); delete _banWindow; _banWindow = 0; } else { _banWindow->exec(); } } void FGSD_MainWindow::setTitle() { std::ostringstream str; str << "FlightGear Scenery Designer"; if ( _fileName.size() ) { std::string shortName = _fileName; size_t pos = _fileName.find_last_of( '/' ); if ( pos != std::string::npos ) shortName.erase( 0, pos + 1 ); std::string mod = ( _actionMgr.modified() ? " *" : "" ); str << " - [" << shortName.c_str() << mod.c_str() << "]" << std::ends; } _windowTitle = str.str(); _window->label( _windowTitle.c_str() ); } void FGSD_MainWindow::setButtonImage( Fl_Widget *w, int n, bool grayed ) { if ( n < NbImages ) w->image( _images[ n ][ grayed ? 1 : 0 ] ); } void FGSD_MainWindow::incZoomValue( int inc ) { if ( ( inc < 0 && _zoom->value() >= -10 - inc ) || ( inc > 0 && _zoom->value() <= 5 - inc ) ) { _zoom->value( _zoom->value() + inc ); zoom_cb(); } } void FGSD_MainWindow::postTileLoad( TileList &newTiles ) { std::vector &osl = FGSD_TriangleObject::_outSurfaceList; std::vector &oosl = FGSD_TriangleObject::_outObjectList; size_t nb = osl.size(); size_t i; for ( i = 0; i < osl.size(); i++ ) { FGSD_TriangleObject::Polyhedron *s = osl[ i ]; if ( s == 0 ) continue; size_t iObj = oosl[ i ]; double minx = 1000.0; double maxx = -1000.0; double miny = 1000.0; double maxy = -1000.0; for ( FGSD_TriangleObject::Polyhedron::Vertex_iterator vi = s->vertices_begin(); vi != s->vertices_end(); ++vi ) { if ( minx > vi->point().x() ) minx = vi->point().x(); if ( maxx < vi->point().x() ) maxx = vi->point().x(); if ( miny > vi->point().y() ) miny = vi->point().y(); if ( maxy < vi->point().y() ) maxy = vi->point().y(); } TileList::iterator it; for ( it = newTiles.begin(); osl[ i ] != 0 && it != newTiles.end(); ++it ) { FGSD_Tile *tile = it->second; FGSD_TriangleObject *o = tile->getTerrain(); if ( o->visible( minx, maxx, miny, maxy ) ) { FGSD_TriangleObject::_objectSpan[ iObj ].push_back( o ); if ( o->addPolyhedron( *s, false, iObj, minx, maxx, miny, maxy ) ) { s = 0; osl[ i ] = 0; } } } for ( it = _tileList.begin(); osl[ i ] != 0 && it != _tileList.end(); ++it ) { FGSD_Tile *tile = it->second; FGSD_TriangleObject *o = tile->getTerrain(); if ( o->visible( minx, maxx, miny, maxy ) ) { FGSD_TriangleObject::_objectSpan[ iObj ].push_back( o ); if ( o->addPolyhedron( *s, false, iObj, minx, maxx, miny, maxy ) ) { s = 0; osl[ i ] = 0; o->postLoad(); } } } } bool done = false; size_t first = 0; size_t free = (size_t)-1; while ( !done ) { size_t i; for ( i = first; i < nb; i++ ) { if ( free == (size_t)-1 && osl[ i ] == 0 ) free = i; else if ( free != (size_t)-1 && osl[ i ] != 0 ) { osl[ free ] = osl[ i ]; osl[ i ] = 0; oosl[ free ] = oosl[ i ]; first = free; break; } } if ( i == nb ) done = true; } if ( free != (size_t)-1 ) { osl.resize( free ); oosl.resize( free ); } TileList::iterator it; for ( it = newTiles.begin(); it != newTiles.end(); ++it ) { FGSD_Tile *tile = it->second; FGSD_TriangleObject *o = tile->getTerrain(); if ( o ) o->postLoad(); } for ( CurveList::iterator ci = _curveList.begin(); ci != _curveList.end(); ++ci ) { FGSD_Curve *curve = *ci; if ( curve->embedded() && curve->newCurve() ) curve->embedInTiles( this ); } _doPostLoad = false; } void FGSD_MainWindow::changeMaterial( const std::string &mat, double lon, double lat ) { bool ret = false; TileList::const_iterator i; for ( i = _tileList.begin(); !ret && i != _tileList.end(); ++i ) { FGSD_Tile *tile = i->second; ret = tile->changeMaterial( mat, lon, lat ); if ( ret ) { FGSD_TriangleObject *obj = tile->getTerrain(); newAction( new FGSD_ChangeMaterialBaseObjectAction( obj->getName().c_str(), lon, lat, _curMaterial, mat ) ); } } }