Merge remote-tracking branch 'upstream/next' into next
This commit is contained in:
commit
b4746123bd
179 changed files with 5969 additions and 1476 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,3 +20,4 @@ build*/
|
|||
*.qmlc
|
||||
CMakeLists.txt.user
|
||||
*.pro.user
|
||||
nbproject
|
||||
|
|
2
3rdparty/CMakeLists.txt
vendored
2
3rdparty/CMakeLists.txt
vendored
|
@ -28,6 +28,8 @@ if (ENABLE_HID_INPUT)
|
|||
add_subdirectory(hidapi)
|
||||
endif()
|
||||
|
||||
add_subdirectory(fonts)
|
||||
|
||||
if (ENABLE_PLIB_JOYSTICK)
|
||||
add_subdirectory(joystick)
|
||||
endif()
|
||||
|
|
14
3rdparty/fonts/CMakeLists.txt
vendored
Normal file
14
3rdparty/fonts/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
set (FNT_SOURCES
|
||||
fnt.cxx
|
||||
fntTXF.cxx
|
||||
fntBitmap.cxx
|
||||
)
|
||||
|
||||
add_library(PLIBFont STATIC ${FNT_SOURCES})
|
||||
|
||||
target_link_libraries(PLIBFont SimGearCore)
|
||||
|
||||
target_include_directories(PLIBFont PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
2
3rdparty/fonts/README
vendored
Normal file
2
3rdparty/fonts/README
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
This is PLIB's 'fnt' library, extracted and modified
|
||||
- added UTF8 path support, especially for Windows
|
257
3rdparty/fonts/fnt.cxx
vendored
Normal file
257
3rdparty/fonts/fnt.cxx
vendored
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
PLIB - A Suite of Portable Game Libraries
|
||||
Copyright (C) 1998,2002 Steve Baker
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
For further information visit http://plib.sourceforge.net
|
||||
|
||||
$Id: fnt.cxx 1939 2004-08-05 00:41:53Z puggles $
|
||||
*/
|
||||
|
||||
#include "fntLocal.h"
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
fntFont:: fntFont () {}
|
||||
fntFont::~fntFont () {}
|
||||
|
||||
int fntTexFont::load ( const SGPath& path, GLenum mag, GLenum min )
|
||||
{
|
||||
if (path.extension() == "txf") {
|
||||
return loadTXF ( path, mag, min ) ;
|
||||
} else {
|
||||
const auto ps = path.utf8Str();
|
||||
fntSetError ( SG_WARN,
|
||||
"fnt::load: Error - Unrecognised file format for '%s'", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
}
|
||||
|
||||
float fntTexFont::low_putch ( sgVec3 curpos, float pointsize,
|
||||
float italic, char c )
|
||||
{
|
||||
unsigned int cc = (unsigned char) c ;
|
||||
|
||||
/* Auto case-convert if character is absent from font. */
|
||||
|
||||
if ( ! exists [ cc ] )
|
||||
{
|
||||
if ( cc >= 'A' && cc <= 'Z' )
|
||||
cc = cc - 'A' + 'a' ;
|
||||
else
|
||||
if ( cc >= 'a' && cc <= 'z' )
|
||||
cc = cc - 'a' + 'A' ;
|
||||
|
||||
if ( cc == ' ' )
|
||||
{
|
||||
curpos [ 0 ] += pointsize / 2.0f ;
|
||||
return pointsize / 2.0f ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
We might want to consider making some absent characters from
|
||||
others (if they exist): lowercase 'l' could be made into digit '1'
|
||||
or letter 'O' into digit '0'...or vice versa. We could also
|
||||
make 'b', 'd', 'p' and 'q' by mirror-imaging - this would
|
||||
save a little more texture memory in some fonts.
|
||||
*/
|
||||
|
||||
if ( ! exists [ cc ] )
|
||||
return 0.0f ;
|
||||
|
||||
glBegin ( GL_TRIANGLE_STRIP ) ;
|
||||
glTexCoord2f ( t_left [cc], t_bot[cc] ) ;
|
||||
glVertex3f ( curpos[0] + v_left [cc] * pointsize,
|
||||
curpos[1] + v_bot [cc] * pointsize,
|
||||
curpos[2] ) ;
|
||||
|
||||
glTexCoord2f ( t_left [cc], t_top[cc] ) ;
|
||||
glVertex3f ( curpos[0] + (italic + v_left [cc]) * pointsize,
|
||||
curpos[1] + v_top [cc] * pointsize,
|
||||
curpos[2] ) ;
|
||||
|
||||
glTexCoord2f ( t_right[cc], t_bot[cc] ) ;
|
||||
glVertex3f ( curpos[0] + v_right[cc] * pointsize,
|
||||
curpos[1] + v_bot [cc] * pointsize,
|
||||
curpos[2] ) ;
|
||||
|
||||
glTexCoord2f ( t_right[cc], t_top[cc] ) ;
|
||||
glVertex3f ( curpos[0] + (italic + v_right[cc]) * pointsize,
|
||||
curpos[1] + v_top [cc] * pointsize,
|
||||
curpos[2] ) ;
|
||||
glEnd () ;
|
||||
|
||||
float ww = ( gap + ( fixed_pitch ? width : widths[cc] ) ) * pointsize ;
|
||||
curpos[0] += ww ;
|
||||
return ww ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void fntTexFont::setGlyph ( char c, float wid,
|
||||
float tex_left, float tex_right,
|
||||
float tex_bot , float tex_top ,
|
||||
float vtx_left, float vtx_right,
|
||||
float vtx_bot , float vtx_top )
|
||||
{
|
||||
unsigned int cc = (unsigned char) c ;
|
||||
|
||||
exists[cc] = FNT_TRUE ;
|
||||
|
||||
widths[cc] = wid;
|
||||
|
||||
t_left[cc] = tex_left ; t_right[cc] = tex_right ;
|
||||
t_bot [cc] = tex_bot ; t_top [cc] = tex_top ;
|
||||
|
||||
v_left[cc] = vtx_left ; v_right[cc] = vtx_right ;
|
||||
v_bot [cc] = vtx_bot ; v_top [cc] = vtx_top ;
|
||||
}
|
||||
|
||||
|
||||
int fntTexFont::getGlyph ( char c, float* wid,
|
||||
float *tex_left, float *tex_right,
|
||||
float *tex_bot , float *tex_top ,
|
||||
float *vtx_left, float *vtx_right,
|
||||
float *vtx_bot , float *vtx_top )
|
||||
{
|
||||
unsigned int cc = (unsigned char) c ;
|
||||
|
||||
if ( ! exists[cc] ) return FNT_FALSE ;
|
||||
|
||||
if ( wid != NULL ) *wid = widths [cc] ;
|
||||
|
||||
if ( tex_left != NULL ) *tex_left = t_left [cc] ;
|
||||
if ( tex_right != NULL ) *tex_right = t_right[cc] ;
|
||||
if ( tex_bot != NULL ) *tex_bot = t_bot [cc] ;
|
||||
if ( tex_top != NULL ) *tex_top = t_top [cc] ;
|
||||
|
||||
if ( vtx_left != NULL ) *vtx_left = v_left [cc] ;
|
||||
if ( vtx_right != NULL ) *vtx_right = v_right[cc] ;
|
||||
if ( vtx_bot != NULL ) *vtx_bot = v_bot [cc] ;
|
||||
if ( vtx_top != NULL ) *vtx_top = v_top [cc] ;
|
||||
|
||||
return FNT_TRUE ;
|
||||
}
|
||||
|
||||
|
||||
void fntTexFont::getBBox ( const char *s,
|
||||
float pointsize, float italic,
|
||||
float *left, float *right,
|
||||
float *bot , float *top )
|
||||
{
|
||||
float h_pos = 0.0f ;
|
||||
float v_pos = 0.0f ;
|
||||
float l, r, b, t ;
|
||||
|
||||
l = r = b = t = 0.0f ;
|
||||
|
||||
while ( *s != '\0' )
|
||||
{
|
||||
if ( *s == '\n' )
|
||||
{
|
||||
h_pos = 0.0f ;
|
||||
v_pos -= 1.333f ;
|
||||
s++ ;
|
||||
continue ;
|
||||
}
|
||||
|
||||
unsigned int cc = (unsigned char) *(s++) ;
|
||||
|
||||
if ( ! exists [ cc ] )
|
||||
{
|
||||
if ( cc >= 'A' && cc <= 'Z' )
|
||||
cc = cc - 'A' + 'a' ;
|
||||
else
|
||||
if ( cc >= 'a' && cc <= 'z' )
|
||||
cc = cc - 'a' + 'A' ;
|
||||
|
||||
if ( cc == ' ' )
|
||||
{
|
||||
r += 0.5f ;
|
||||
h_pos += 0.5f ;
|
||||
|
||||
continue ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! exists [ cc ] )
|
||||
continue ;
|
||||
|
||||
if ( italic >= 0 )
|
||||
{
|
||||
if ( l > h_pos + v_left [cc] ) l = h_pos + v_left [cc] ;
|
||||
if ( r < gap + h_pos + v_right[cc]+italic ) r = gap + h_pos + v_right[cc] + italic ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( l > h_pos + v_left [cc]+italic ) l = h_pos + v_left [cc] + italic ;
|
||||
if ( r < gap + h_pos + v_right[cc] ) r = gap + h_pos + v_right[cc] ;
|
||||
}
|
||||
|
||||
|
||||
if ( b > v_pos + v_bot [cc] ) b = v_pos + v_bot [cc] ;
|
||||
if ( t < v_pos + v_top [cc] ) t = v_pos + v_top [cc] ;
|
||||
|
||||
h_pos += gap + ( fixed_pitch ? width : widths[cc] ) ;
|
||||
}
|
||||
|
||||
if ( left != NULL ) *left = l * pointsize ;
|
||||
if ( right != NULL ) *right = r * pointsize ;
|
||||
if ( top != NULL ) *top = t * pointsize ;
|
||||
if ( bot != NULL ) *bot = b * pointsize ;
|
||||
}
|
||||
|
||||
|
||||
void fntTexFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s )
|
||||
{
|
||||
SGfloat origx = curpos[0] ;
|
||||
|
||||
if ( ! bound )
|
||||
bind_texture () ;
|
||||
|
||||
while ( *s != '\0' )
|
||||
{
|
||||
if (*s == '\n')
|
||||
{
|
||||
curpos[0] = origx ;
|
||||
curpos[1] -= pointsize ;
|
||||
}
|
||||
else
|
||||
low_putch ( curpos, pointsize, italic, *s ) ;
|
||||
|
||||
s++ ;
|
||||
}
|
||||
}
|
||||
|
||||
void fntInit ()
|
||||
{
|
||||
/* Empty right now */
|
||||
}
|
||||
|
||||
|
||||
void fntSetError(int level, const char* format, ...)
|
||||
{
|
||||
const int bufferLength = 2048;
|
||||
char buffer[2048];
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
vsnprintf(buffer,bufferLength,format, args);
|
||||
va_end (args);
|
||||
|
||||
SG_LOG(SG_GUI, static_cast<sgDebugPriority>(level), buffer);
|
||||
}
|
357
3rdparty/fonts/fnt.h
vendored
Normal file
357
3rdparty/fonts/fnt.h
vendored
Normal file
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
PLIB - A Suite of Portable Game Libraries
|
||||
Copyright (C) 1998,2002 Steve Baker
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
For further information visit http://plib.sourceforge.net
|
||||
|
||||
$Id: fnt.h 1923 2004-04-06 12:53:17Z sjbaker $
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FNT_H_
|
||||
#define _FNT_H_ 1
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <plib/sg.h>
|
||||
#include <osg/GL>
|
||||
|
||||
#define FNTMAX_CHAR 256
|
||||
#define FNT_TRUE 1
|
||||
#define FNT_FALSE 0
|
||||
|
||||
class SGPath;
|
||||
|
||||
class fntFont
|
||||
{
|
||||
public:
|
||||
fntFont () ;
|
||||
|
||||
virtual ~fntFont () ;
|
||||
|
||||
virtual void getBBox ( const char *s, float pointsize, float italic,
|
||||
float *left, float *right,
|
||||
float *bot , float *top ) = 0 ;
|
||||
virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) = 0 ;
|
||||
virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) = 0 ;
|
||||
virtual void begin () = 0 ;
|
||||
virtual void end () = 0 ;
|
||||
|
||||
virtual int load ( const SGPath& fname, GLenum mag = GL_NEAREST,
|
||||
GLenum min = GL_LINEAR_MIPMAP_LINEAR ) = 0 ;
|
||||
|
||||
virtual void setFixedPitch ( int fix ) = 0 ;
|
||||
virtual int isFixedPitch () const = 0 ;
|
||||
|
||||
virtual void setWidth ( float w ) = 0 ;
|
||||
virtual void setGap ( float g ) = 0 ;
|
||||
|
||||
virtual float getWidth () const = 0 ;
|
||||
virtual float getGap () const = 0 ;
|
||||
|
||||
virtual int hasGlyph ( char c ) const = 0 ;
|
||||
} ;
|
||||
|
||||
|
||||
class fntTexFont : public fntFont
|
||||
{
|
||||
private:
|
||||
GLuint texture ;
|
||||
int bound ;
|
||||
int fixed_pitch ;
|
||||
|
||||
float width ; /* The width of a character in fixed-width mode */
|
||||
float gap ; /* Set the gap between characters */
|
||||
|
||||
int exists [ FNTMAX_CHAR ] ; /* TRUE if character exists in tex-map */
|
||||
|
||||
/*
|
||||
The quadrilaterals that describe the characters
|
||||
in the font are drawn with the following texture
|
||||
and spatial coordinates. The texture coordinates
|
||||
are in (S,T) space, with (0,0) at the bottom left
|
||||
of the image and (1,1) at the top-right.
|
||||
|
||||
The spatial coordinates are relative to the current
|
||||
'cursor' position. They should be scaled such that
|
||||
1.0 represent the height of a capital letter. Hence,
|
||||
characters like 'y' which have a descender will be
|
||||
given a negative v_bot. Most capitals will have
|
||||
v_bot==0.0 and v_top==1.0.
|
||||
*/
|
||||
|
||||
/* Nominal baseline widths */
|
||||
|
||||
float widths [ FNTMAX_CHAR ] ;
|
||||
|
||||
/* Texture coordinates */
|
||||
|
||||
float t_top [ FNTMAX_CHAR ] ; /* Top edge of each character [0..1] */
|
||||
float t_bot [ FNTMAX_CHAR ] ; /* Bottom edge of each character [0..1] */
|
||||
float t_left [ FNTMAX_CHAR ] ; /* Left edge of each character [0..1] */
|
||||
float t_right [ FNTMAX_CHAR ] ; /* Right edge of each character [0..1] */
|
||||
|
||||
/* Vertex coordinates. */
|
||||
|
||||
float v_top [ FNTMAX_CHAR ] ;
|
||||
float v_bot [ FNTMAX_CHAR ] ;
|
||||
float v_left [ FNTMAX_CHAR ] ;
|
||||
float v_right [ FNTMAX_CHAR ] ;
|
||||
|
||||
void bind_texture ()
|
||||
{
|
||||
glEnable ( GL_TEXTURE_2D ) ;
|
||||
#ifdef GL_VERSION_1_1
|
||||
glBindTexture ( GL_TEXTURE_2D, texture ) ;
|
||||
#else
|
||||
/* For ancient SGI machines */
|
||||
glBindTextureEXT ( GL_TEXTURE_2D, texture ) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
float low_putch ( sgVec3 curpos, float pointsize, float italic, char c ) ;
|
||||
|
||||
int loadTXF ( const SGPath& path, GLenum mag = GL_NEAREST,
|
||||
GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ;
|
||||
public:
|
||||
|
||||
fntTexFont ()
|
||||
{
|
||||
bound = FNT_FALSE ;
|
||||
fixed_pitch = FNT_TRUE ;
|
||||
texture = 0 ;
|
||||
width = 1.0f ;
|
||||
gap = 0.1f ;
|
||||
|
||||
memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ;
|
||||
}
|
||||
|
||||
fntTexFont ( const SGPath& path, GLenum mag = GL_NEAREST,
|
||||
GLenum min = GL_LINEAR_MIPMAP_LINEAR ) : fntFont ()
|
||||
{
|
||||
bound = FNT_FALSE ;
|
||||
fixed_pitch = FNT_TRUE ;
|
||||
texture = 0 ;
|
||||
width = 1.0f ;
|
||||
gap = 0.1f ;
|
||||
|
||||
memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ;
|
||||
load ( path, mag, min ) ;
|
||||
}
|
||||
|
||||
~fntTexFont ()
|
||||
{
|
||||
if ( texture != 0 )
|
||||
{
|
||||
#ifdef GL_VERSION_1_1
|
||||
glDeleteTextures ( 1, &texture ) ;
|
||||
#else
|
||||
/* For ancient SGI machines */
|
||||
glDeleteTexturesEXT ( 1, &texture ) ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int load ( const SGPath& path, GLenum mag = GL_NEAREST,
|
||||
GLenum min = GL_LINEAR_MIPMAP_LINEAR ) override ;
|
||||
|
||||
void setFixedPitch ( int fix ) { fixed_pitch = fix ; }
|
||||
int isFixedPitch () const { return fixed_pitch ; }
|
||||
|
||||
void setWidth ( float w ) { width = w ; }
|
||||
void setGap ( float g ) { gap = g ; }
|
||||
|
||||
float getWidth () const { return width ; }
|
||||
float getGap () const { return gap ; }
|
||||
|
||||
|
||||
void setGlyph ( char c, float wid,
|
||||
float tex_left, float tex_right,
|
||||
float tex_bot , float tex_top ,
|
||||
float vtx_left, float vtx_right,
|
||||
float vtx_bot , float vtx_top ) ;
|
||||
void setGlyph ( char c,
|
||||
float tex_left, float tex_right,
|
||||
float tex_bot , float tex_top ,
|
||||
float vtx_left, float vtx_right,
|
||||
float vtx_bot , float vtx_top ) /* deprecated */
|
||||
{
|
||||
setGlyph ( c, vtx_right,
|
||||
tex_left, tex_right, tex_bot, tex_top,
|
||||
vtx_left, vtx_right, vtx_bot, vtx_top ) ;
|
||||
}
|
||||
|
||||
int getGlyph ( char c, float* wid,
|
||||
float *tex_left = NULL, float *tex_right = NULL,
|
||||
float *tex_bot = NULL, float *tex_top = NULL,
|
||||
float *vtx_left = NULL, float *vtx_right = NULL,
|
||||
float *vtx_bot = NULL, float *vtx_top = NULL) ;
|
||||
int getGlyph ( char c,
|
||||
float *tex_left = NULL, float *tex_right = NULL,
|
||||
float *tex_bot = NULL, float *tex_top = NULL,
|
||||
float *vtx_left = NULL, float *vtx_right = NULL,
|
||||
float *vtx_bot = NULL, float *vtx_top = NULL) /* deprecated */
|
||||
{
|
||||
return getGlyph ( c, NULL,
|
||||
tex_left, tex_right, tex_bot, tex_top,
|
||||
vtx_left, vtx_right, vtx_bot, vtx_top ) ;
|
||||
}
|
||||
|
||||
int hasGlyph ( char c ) const { return exists[ (GLubyte) c ] ; }
|
||||
|
||||
void getBBox ( const char *s, float pointsize, float italic,
|
||||
float *left, float *right,
|
||||
float *bot , float *top ) ;
|
||||
|
||||
void begin ()
|
||||
{
|
||||
bind_texture () ;
|
||||
bound = FNT_TRUE ;
|
||||
}
|
||||
|
||||
void end ()
|
||||
{
|
||||
bound = FNT_FALSE ;
|
||||
}
|
||||
|
||||
void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ;
|
||||
|
||||
void putch ( sgVec3 curpos, float pointsize, float italic, char c )
|
||||
{
|
||||
if ( ! bound )
|
||||
bind_texture () ;
|
||||
|
||||
low_putch ( curpos, pointsize, italic, c ) ;
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
class fntBitmapFont : public fntFont
|
||||
{
|
||||
protected:
|
||||
|
||||
const GLubyte **data;
|
||||
int first;
|
||||
int count;
|
||||
int height;
|
||||
float xorig, yorig;
|
||||
int fix;
|
||||
float wid, gap;
|
||||
|
||||
public:
|
||||
|
||||
// data is a null-terminated list of glyph images:
|
||||
// data[i][0] - image width
|
||||
// data[i][1..n] - packed bitmap
|
||||
|
||||
fntBitmapFont ( const GLubyte **data, int first, int height,
|
||||
float xorig, float yorig ) ;
|
||||
|
||||
virtual ~fntBitmapFont () ;
|
||||
|
||||
virtual void getBBox ( const char *s, float pointsize, float italic,
|
||||
float *left, float *right,
|
||||
float *bot , float *top ) ;
|
||||
|
||||
virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) ;
|
||||
virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ;
|
||||
|
||||
virtual void begin () ;
|
||||
virtual void end () ;
|
||||
|
||||
virtual int load ( const SGPath& fname, GLenum mag, GLenum min ) override { return -1; }
|
||||
|
||||
virtual void setFixedPitch ( int f ) { fix = f; }
|
||||
virtual int isFixedPitch () const { return fix; }
|
||||
|
||||
virtual void setWidth ( float w ) { wid = w; }
|
||||
virtual void setGap ( float g ) { gap = g; }
|
||||
|
||||
virtual float getWidth () const { return wid; }
|
||||
virtual float getGap () const { return gap; }
|
||||
|
||||
virtual int hasGlyph ( char c ) const ;
|
||||
};
|
||||
|
||||
|
||||
/* Builtin Bitmap Fonts */
|
||||
|
||||
#define FNT_BITMAP_8_BY_13 0
|
||||
#define FNT_BITMAP_9_BY_15 1
|
||||
#define FNT_BITMAP_HELVETICA_10 2
|
||||
#define FNT_BITMAP_HELVETICA_12 3
|
||||
#define FNT_BITMAP_HELVETICA_18 4
|
||||
#define FNT_BITMAP_TIMES_ROMAN_10 5
|
||||
#define FNT_BITMAP_TIMES_ROMAN_24 6
|
||||
|
||||
fntBitmapFont *fntGetBitmapFont(int id);
|
||||
|
||||
|
||||
|
||||
class fntRenderer
|
||||
{
|
||||
fntFont *font ;
|
||||
|
||||
sgVec3 curpos ;
|
||||
|
||||
float pointsize ;
|
||||
float italic ;
|
||||
|
||||
public:
|
||||
fntRenderer ()
|
||||
{
|
||||
start2f ( 0.0f, 0.0f ) ;
|
||||
font = NULL ;
|
||||
pointsize = 10 ;
|
||||
italic = 0 ;
|
||||
}
|
||||
|
||||
void start3fv ( sgVec3 pos ) { sgCopyVec3 ( curpos, pos ) ; }
|
||||
void start2fv ( sgVec2 pos ) { sgCopyVec2 ( curpos, pos ) ; curpos[2]=0.0f ; }
|
||||
void start2f ( float x, float y ) { sgSetVec3 ( curpos, x, y, 0.0f ) ; }
|
||||
void start3f ( float x, float y, float z ) { sgSetVec3 ( curpos, x, y, z ) ; }
|
||||
|
||||
void getCursor ( float *x, float *y, float *z ) const
|
||||
{
|
||||
if ( x != NULL ) *x = curpos [ 0 ] ;
|
||||
if ( y != NULL ) *y = curpos [ 1 ] ;
|
||||
if ( z != NULL ) *z = curpos [ 2 ] ;
|
||||
}
|
||||
|
||||
void setFont ( fntFont *f ) { font = f ; }
|
||||
fntFont *getFont () const { return font ; }
|
||||
|
||||
void setSlant ( float i ) { italic = i ; }
|
||||
void setPointSize ( float p ) { pointsize = p ; }
|
||||
|
||||
float getSlant () const { return italic ; }
|
||||
float getPointSize () const { return pointsize ; }
|
||||
|
||||
void begin () { font->begin () ; }
|
||||
void end () { font->end () ; }
|
||||
|
||||
void putch ( char c ) { font->putch ( curpos, pointsize, italic, c ) ; }
|
||||
void puts ( const char *s ) { font->puts ( curpos, pointsize, italic, s ) ; }
|
||||
} ;
|
||||
|
||||
|
||||
void fntInit () ;
|
||||
|
||||
#endif
|
||||
|
1017
3rdparty/fonts/fntBitmap.cxx
vendored
Normal file
1017
3rdparty/fonts/fntBitmap.cxx
vendored
Normal file
File diff suppressed because it is too large
Load diff
112
3rdparty/fonts/fntLocal.h
vendored
Normal file
112
3rdparty/fonts/fntLocal.h
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
PLIB - A Suite of Portable Game Libraries
|
||||
Copyright (C) 1998,2002 Steve Baker
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
For further information visit http://plib.sourceforge.net
|
||||
|
||||
$Id: fntLocal.h 1727 2002-11-09 21:27:21Z ude $
|
||||
*/
|
||||
|
||||
|
||||
#include "fnt.h"
|
||||
|
||||
|
||||
extern int _fntIsSwapped ;
|
||||
extern FILE *_fntCurrImageFd ;
|
||||
|
||||
|
||||
inline void _fnt_swab_short ( unsigned short *x )
|
||||
{
|
||||
if ( _fntIsSwapped )
|
||||
*x = (( *x >> 8 ) & 0x00FF ) |
|
||||
(( *x << 8 ) & 0xFF00 ) ;
|
||||
}
|
||||
|
||||
|
||||
inline void _fnt_swab_int ( unsigned int *x )
|
||||
{
|
||||
if ( _fntIsSwapped )
|
||||
*x = (( *x >> 24 ) & 0x000000FF ) |
|
||||
(( *x >> 8 ) & 0x0000FF00 ) |
|
||||
(( *x << 8 ) & 0x00FF0000 ) |
|
||||
(( *x << 24 ) & 0xFF000000 ) ;
|
||||
}
|
||||
|
||||
|
||||
inline void _fnt_swab_int_array ( int *x, int leng )
|
||||
{
|
||||
if ( ! _fntIsSwapped )
|
||||
return ;
|
||||
|
||||
for ( int i = 0 ; i < leng ; i++ )
|
||||
{
|
||||
*x = (( *x >> 24 ) & 0x000000FF ) |
|
||||
(( *x >> 8 ) & 0x0000FF00 ) |
|
||||
(( *x << 8 ) & 0x00FF0000 ) |
|
||||
(( *x << 24 ) & 0xFF000000 ) ;
|
||||
x++ ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline unsigned char _fnt_readByte ()
|
||||
{
|
||||
unsigned char x ;
|
||||
fread ( & x, sizeof(unsigned char), 1, _fntCurrImageFd ) ;
|
||||
return x ;
|
||||
}
|
||||
|
||||
inline unsigned short _fnt_readShort ()
|
||||
{
|
||||
unsigned short x ;
|
||||
fread ( & x, sizeof(unsigned short), 1, _fntCurrImageFd ) ;
|
||||
_fnt_swab_short ( & x ) ;
|
||||
return x ;
|
||||
}
|
||||
|
||||
inline unsigned int _fnt_readInt ()
|
||||
{
|
||||
unsigned int x ;
|
||||
fread ( & x, sizeof(unsigned int), 1, _fntCurrImageFd ) ;
|
||||
_fnt_swab_int ( & x ) ;
|
||||
return x ;
|
||||
}
|
||||
|
||||
|
||||
#define FNT_BYTE_FORMAT 0
|
||||
#define FNT_BITMAP_FORMAT 1
|
||||
|
||||
struct TXF_Glyph
|
||||
{
|
||||
unsigned short ch ;
|
||||
unsigned char w ;
|
||||
unsigned char h ;
|
||||
signed char x_off ;
|
||||
signed char y_off ;
|
||||
signed char step ;
|
||||
signed char unknown ;
|
||||
short x ;
|
||||
short y ;
|
||||
|
||||
sgVec2 tx0 ; sgVec2 vx0 ;
|
||||
sgVec2 tx1 ; sgVec2 vx1 ;
|
||||
sgVec2 tx2 ; sgVec2 vx2 ;
|
||||
sgVec2 tx3 ; sgVec2 vx3 ;
|
||||
} ;
|
||||
|
||||
|
||||
void fntSetError(int, const char* format, ...);
|
341
3rdparty/fonts/fntTXF.cxx
vendored
Executable file
341
3rdparty/fonts/fntTXF.cxx
vendored
Executable file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
PLIB - A Suite of Portable Game Libraries
|
||||
Copyright (C) 1998,2002 Steve Baker
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
For further information visit http://plib.sourceforge.net
|
||||
|
||||
$Id: fntTXF.cxx 1735 2002-12-01 18:21:48Z sjbaker $
|
||||
*/
|
||||
|
||||
|
||||
#include "fntLocal.h"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
FILE *_fntCurrImageFd ;
|
||||
int _fntIsSwapped = FALSE ;
|
||||
|
||||
static void tex_make_mip_maps ( GLubyte *image, int xsize,
|
||||
int ysize, int zsize )
|
||||
{
|
||||
GLubyte *texels [ 20 ] ; /* One element per level of MIPmap */
|
||||
|
||||
for ( int l = 0 ; l < 20 ; l++ )
|
||||
texels [ l ] = NULL ;
|
||||
|
||||
texels [ 0 ] = image ;
|
||||
|
||||
int lev ;
|
||||
|
||||
for ( lev = 0 ; (( xsize >> (lev+1) ) != 0 ||
|
||||
( ysize >> (lev+1) ) != 0 ) ; lev++ )
|
||||
{
|
||||
/* Suffix '1' is the higher level map, suffix '2' is the lower level. */
|
||||
|
||||
int l1 = lev ;
|
||||
int l2 = lev+1 ;
|
||||
int w1 = xsize >> l1 ;
|
||||
int h1 = ysize >> l1 ;
|
||||
int w2 = xsize >> l2 ;
|
||||
int h2 = ysize >> l2 ;
|
||||
|
||||
if ( w1 <= 0 ) w1 = 1 ;
|
||||
if ( h1 <= 0 ) h1 = 1 ;
|
||||
if ( w2 <= 0 ) w2 = 1 ;
|
||||
if ( h2 <= 0 ) h2 = 1 ;
|
||||
|
||||
texels [ l2 ] = new GLubyte [ w2 * h2 * zsize ] ;
|
||||
|
||||
for ( int x2 = 0 ; x2 < w2 ; x2++ )
|
||||
for ( int y2 = 0 ; y2 < h2 ; y2++ )
|
||||
for ( int c = 0 ; c < zsize ; c++ )
|
||||
{
|
||||
int x1 = x2 + x2 ;
|
||||
int x1_1 = ( x1 + 1 ) % w1 ;
|
||||
int y1 = y2 + y2 ;
|
||||
int y1_1 = ( y1 + 1 ) % h1 ;
|
||||
|
||||
int t1 = texels [ l1 ] [ (y1 * w1 + x1 ) * zsize + c ] ;
|
||||
int t2 = texels [ l1 ] [ (y1_1 * w1 + x1 ) * zsize + c ] ;
|
||||
int t3 = texels [ l1 ] [ (y1 * w1 + x1_1) * zsize + c ] ;
|
||||
int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * zsize + c ] ;
|
||||
|
||||
texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] =
|
||||
( t1 + t2 + t3 + t4 ) / 4 ;
|
||||
}
|
||||
}
|
||||
|
||||
texels [ lev+1 ] = NULL ;
|
||||
|
||||
if ( ! ((xsize & (xsize-1))==0) ||
|
||||
! ((ysize & (ysize-1))==0) )
|
||||
{
|
||||
fntSetError ( SG_ALERT,
|
||||
"TXFloader: TXF Map is not a power-of-two in size!" ) ;
|
||||
}
|
||||
|
||||
glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ;
|
||||
|
||||
int map_level = 0 ;
|
||||
|
||||
#ifdef PROXY_TEXTURES_ARE_NOT_BROKEN
|
||||
int ww ;
|
||||
|
||||
do
|
||||
{
|
||||
glTexImage2D ( GL_PROXY_TEXTURE_2D,
|
||||
map_level, zsize, xsize, ysize, FALSE /* Border */,
|
||||
(zsize==1)?GL_LUMINANCE:
|
||||
(zsize==2)?GL_LUMINANCE_ALPHA:
|
||||
(zsize==3)?GL_RGB:
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, NULL ) ;
|
||||
|
||||
glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &ww ) ;
|
||||
|
||||
if ( ww == 0 )
|
||||
{
|
||||
delete [] texels [ 0 ] ;
|
||||
xsize >>= 1 ;
|
||||
ysize >>= 1 ;
|
||||
|
||||
for ( int l = 0 ; texels [ l ] != NULL ; l++ )
|
||||
texels [ l ] = texels [ l+1 ] ;
|
||||
|
||||
if ( xsize < 64 && ysize < 64 )
|
||||
{
|
||||
fntSetError ( SG_ALERT,
|
||||
"FNT: OpenGL will not accept a font texture?!?" ) ;
|
||||
}
|
||||
}
|
||||
} while ( ww == 0 ) ;
|
||||
#endif
|
||||
|
||||
for ( int i = 0 ; texels [ i ] != NULL ; i++ )
|
||||
{
|
||||
int w = xsize>>i ;
|
||||
int h = ysize>>i ;
|
||||
|
||||
if ( w <= 0 ) w = 1 ;
|
||||
if ( h <= 0 ) h = 1 ;
|
||||
|
||||
glTexImage2D ( GL_TEXTURE_2D,
|
||||
map_level, zsize, w, h, FALSE /* Border */,
|
||||
(zsize==1)?GL_LUMINANCE:
|
||||
(zsize==2)?GL_LUMINANCE_ALPHA:
|
||||
(zsize==3)?GL_RGB:
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ;
|
||||
map_level++ ;
|
||||
delete [] texels [ i ] ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int fntTexFont::loadTXF ( const SGPath& path, GLenum mag, GLenum min )
|
||||
{
|
||||
FILE *fd ;
|
||||
const auto ps = path.utf8Str();
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring fp = path.wstr();
|
||||
fd = _wfopen(fp.c_str(), L"rb");
|
||||
#else
|
||||
fd = fopen(ps.c_str(), "rb");
|
||||
#endif
|
||||
if ( fd == nullptr )
|
||||
{
|
||||
fntSetError ( SG_WARN,
|
||||
"fntLoadTXF: Failed to open '%s' for reading.", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
|
||||
_fntCurrImageFd = fd ;
|
||||
|
||||
unsigned char magic [ 4 ] ;
|
||||
|
||||
if ( (int)fread ( &magic, sizeof (unsigned int), 1, fd ) != 1 )
|
||||
{
|
||||
fntSetError ( SG_WARN,
|
||||
"fntLoadTXF: '%s' an empty file!", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
|
||||
if ( magic [ 0 ] != 0xFF || magic [ 1 ] != 't' ||
|
||||
magic [ 2 ] != 'x' || magic [ 3 ] != 'f' )
|
||||
{
|
||||
fntSetError ( SG_WARN,
|
||||
"fntLoadTXF: '%s' is not a 'txf' font file.", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
|
||||
_fntIsSwapped = FALSE ;
|
||||
int endianness = _fnt_readInt () ;
|
||||
|
||||
_fntIsSwapped = ( endianness != 0x12345678 ) ;
|
||||
|
||||
int format = _fnt_readInt () ;
|
||||
int tex_width = _fnt_readInt () ;
|
||||
int tex_height = _fnt_readInt () ;
|
||||
int max_height = _fnt_readInt () ;
|
||||
_fnt_readInt () ;
|
||||
int num_glyphs = _fnt_readInt () ;
|
||||
|
||||
int w = tex_width ;
|
||||
int h = tex_height ;
|
||||
|
||||
float xstep = 0.5f / (float) w ;
|
||||
float ystep = 0.5f / (float) h ;
|
||||
|
||||
int i, j ;
|
||||
|
||||
/*
|
||||
Load the TXF_Glyph array
|
||||
*/
|
||||
|
||||
TXF_Glyph glyph ;
|
||||
|
||||
for ( i = 0 ; i < num_glyphs ; i++ )
|
||||
{
|
||||
glyph . ch = _fnt_readShort () ;
|
||||
|
||||
glyph . w = _fnt_readByte () ;
|
||||
glyph . h = _fnt_readByte () ;
|
||||
glyph . x_off = _fnt_readByte () ;
|
||||
glyph . y_off = _fnt_readByte () ;
|
||||
glyph . step = _fnt_readByte () ;
|
||||
glyph . unknown = _fnt_readByte () ;
|
||||
glyph . x = _fnt_readShort () ;
|
||||
glyph . y = _fnt_readShort () ;
|
||||
|
||||
setGlyph ( (char) glyph.ch,
|
||||
(float) glyph.step / (float) max_height,
|
||||
(float) glyph.x / (float) w + xstep,
|
||||
(float)( glyph.x + glyph.w ) / (float) w + xstep,
|
||||
(float) glyph.y / (float) h + ystep,
|
||||
(float)( glyph.y + glyph.h ) / (float) h + ystep,
|
||||
(float) glyph.x_off / (float) max_height,
|
||||
(float)( glyph.x_off + glyph.w ) / (float) max_height,
|
||||
(float) glyph.y_off / (float) max_height,
|
||||
(float)( glyph.y_off + glyph.h ) / (float) max_height ) ;
|
||||
}
|
||||
|
||||
exists [ static_cast<int>(' ') ] = FALSE ;
|
||||
|
||||
/*
|
||||
Load the image part of the file
|
||||
*/
|
||||
|
||||
int ntexels = w * h ;
|
||||
|
||||
unsigned char *teximage ;
|
||||
unsigned char *texbitmap ;
|
||||
|
||||
switch ( format )
|
||||
{
|
||||
case FNT_BYTE_FORMAT:
|
||||
{
|
||||
unsigned char *orig = new unsigned char [ ntexels ] ;
|
||||
|
||||
if ( (int)fread ( orig, 1, ntexels, fd ) != ntexels )
|
||||
{
|
||||
fntSetError ( SG_WARN,
|
||||
"fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
|
||||
teximage = new unsigned char [ 2 * ntexels ] ;
|
||||
|
||||
for ( i = 0 ; i < ntexels ; i++ )
|
||||
{
|
||||
teximage [ i*2 ] = orig [ i ] ;
|
||||
teximage [ i*2 + 1 ] = orig [ i ] ;
|
||||
}
|
||||
|
||||
delete [] orig ;
|
||||
}
|
||||
break ;
|
||||
|
||||
case FNT_BITMAP_FORMAT:
|
||||
{
|
||||
int stride = (w + 7) >> 3;
|
||||
|
||||
texbitmap = new unsigned char [ stride * h ] ;
|
||||
|
||||
if ( (int)fread ( texbitmap, 1, stride * h, fd ) != stride * h )
|
||||
{
|
||||
delete [] texbitmap ;
|
||||
fntSetError ( SG_WARN,
|
||||
"fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
|
||||
teximage = new unsigned char [ 2 * ntexels ] ;
|
||||
|
||||
for ( i = 0 ; i < 2 * ntexels ; i++ )
|
||||
teximage [ i ] = 0 ;
|
||||
|
||||
for (i = 0; i < h; i++)
|
||||
for (j = 0; j < w; j++)
|
||||
if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7)))
|
||||
{
|
||||
teximage[(i * w + j) * 2 ] = 255;
|
||||
teximage[(i * w + j) * 2 + 1] = 255;
|
||||
}
|
||||
|
||||
delete [] texbitmap ;
|
||||
}
|
||||
break ;
|
||||
|
||||
default:
|
||||
fntSetError ( SG_WARN,
|
||||
"fntLoadTXF: Unrecognised format type in '%s'.", ps.c_str() ) ;
|
||||
return FNT_FALSE ;
|
||||
}
|
||||
|
||||
fclose ( fd ) ;
|
||||
|
||||
fixed_pitch = FALSE ;
|
||||
|
||||
#ifdef GL_VERSION_1_1
|
||||
glGenTextures ( 1, & texture ) ;
|
||||
glBindTexture ( GL_TEXTURE_2D, texture ) ;
|
||||
#else
|
||||
/* This is only useful on some ancient SGI hardware */
|
||||
glGenTexturesEXT ( 1, & texture ) ;
|
||||
glBindTextureEXT ( GL_TEXTURE_2D, texture ) ;
|
||||
#endif
|
||||
|
||||
tex_make_mip_maps ( teximage, w, h, 2 ) ;
|
||||
|
||||
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ;
|
||||
|
||||
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag ) ;
|
||||
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min ) ;
|
||||
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
|
||||
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ;
|
||||
#ifdef GL_VERSION_1_1
|
||||
glBindTexture ( GL_TEXTURE_2D, 0 ) ;
|
||||
#else
|
||||
glBindTextureEXT ( GL_TEXTURE_2D, 0 ) ;
|
||||
#endif
|
||||
|
||||
return FNT_TRUE ;
|
||||
}
|
||||
|
||||
|
39
3rdparty/hts_engine_API/lib/HTS_misc.c
vendored
Normal file → Executable file
39
3rdparty/hts_engine_API/lib/HTS_misc.c
vendored
Normal file → Executable file
|
@ -75,14 +75,51 @@ typedef struct _HTS_Data {
|
|||
size_t index;
|
||||
} HTS_Data;
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define MAX_PATH_SIZE 8192
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
|
||||
// wbuf and wbuf_len is a target buffer and its length.
|
||||
static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
|
||||
char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
|
||||
|
||||
strncpy(buf, path, sizeof(buf));
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
// Trim trailing slashes. Leave backslash for paths like "X:\"
|
||||
p = buf + strlen(buf) - 1;
|
||||
while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
|
||||
|
||||
// Convert to Unicode and back. If doubly-converted string does not
|
||||
// match the original, something is fishy, reject.
|
||||
memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
|
||||
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
|
||||
NULL, NULL);
|
||||
if (strcmp(buf, buf2) != 0) {
|
||||
wbuf[0] = L'\0';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* HTS_fopen_from_fn: wrapper for fopen */
|
||||
HTS_File *HTS_fopen_from_fn(const char *name, const char *opt)
|
||||
{
|
||||
HTS_File *fp = (HTS_File *) HTS_calloc(1, sizeof(HTS_File));
|
||||
|
||||
fp->type = HTS_FILE;
|
||||
#if defined(_WIN32)
|
||||
wchar_t wpath[MAX_PATH_SIZE], wmode[10];
|
||||
to_wchar(name, wpath, ARRAY_SIZE(wpath));
|
||||
to_wchar(opt, wmode, ARRAY_SIZE(wmode));
|
||||
fp->pointer = (void*) _wfopen(wpath, wmode);
|
||||
#else
|
||||
fp->pointer = (void *) fopen(name, opt);
|
||||
|
||||
#endif
|
||||
if (fp->pointer == NULL) {
|
||||
HTS_error(0, "HTS_fopen: Cannot open %s.\n", name);
|
||||
HTS_free(fp);
|
||||
|
|
|
@ -370,10 +370,7 @@ if (ENABLE_QT)
|
|||
find_package(Qt5 5.4 COMPONENTS Widgets Network Qml Quick Svg)
|
||||
if (Qt5Widgets_FOUND)
|
||||
message(STATUS "Will enable Qt launcher GUI")
|
||||
message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}")
|
||||
message(STATUS " Qt5Widgets include dir: ${Qt5Widgets_INCLUDE_DIRS}")
|
||||
set(HAVE_QT 1)
|
||||
|
||||
include (Translations)
|
||||
else()
|
||||
# don't try to build FGQCanvas if Qt wasn't found correctly
|
||||
|
@ -385,7 +382,7 @@ else()
|
|||
endif (ENABLE_QT)
|
||||
##############################################################################
|
||||
|
||||
find_package(PLIB REQUIRED puaux pu fnt)
|
||||
find_package(PLIB REQUIRED puaux pu)
|
||||
|
||||
# FlightGear and SimGear versions need to match major + minor
|
||||
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
||||
|
|
|
@ -109,9 +109,9 @@ if(${PLIB_LIBRARIES} STREQUAL "PLIB_LIBRARIES-NOTFOUND")
|
|||
# handle MSVC confusion over pu/pui naming, by removing
|
||||
# 'pu' and then adding it back
|
||||
list(REMOVE_ITEM outDeps "pu" "fnt" "sg")
|
||||
list(APPEND outDeps ${PUNAME} "fnt" "sg")
|
||||
list(APPEND outDeps ${PUNAME} "sg")
|
||||
elseif (${c} STREQUAL "puaux")
|
||||
list(APPEND outDeps ${PUNAME} "fnt" "sg")
|
||||
list(APPEND outDeps ${PUNAME} "sg")
|
||||
elseif (${c} STREQUAL "ssg")
|
||||
list(APPEND outDeps "sg")
|
||||
endif()
|
||||
|
|
|
@ -66,6 +66,8 @@ function(setup_fgfs_libraries target)
|
|||
target_link_libraries(${target} PLIBJoystick)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${target} PLIBFont)
|
||||
|
||||
if(SYSTEM_HTS_ENGINE)
|
||||
target_link_libraries(${target} flite_hts ${HTS_ENGINE_LIBRARIES})
|
||||
else()
|
||||
|
|
|
@ -19,9 +19,10 @@ if (${do_translate} AND NOT TARGET Qt5::lrelease)
|
|||
"\n(on Linux You may need to install an additional package containing the Qt5 translation tools)")
|
||||
endif()
|
||||
|
||||
# FIXME - determine this based on subdirs of TRANSLATIONS_SRC_DIR
|
||||
set(LANGUAGES en_US de es nl fr it pl pt ru zh_CN)
|
||||
|
||||
if (${do_translate})
|
||||
# FIXME - determine this based on subdirs of TRANSLATIONS_SRC_DIR
|
||||
set(LANGUAGES en_US de es nl fr it pl pt ru zh_CN)
|
||||
set(translation_res "${PROJECT_BINARY_DIR}/translations.qrc")
|
||||
|
||||
add_custom_target(fgfs_qm_files ALL)
|
||||
|
@ -55,3 +56,14 @@ if (${do_translate})
|
|||
# set this so config.h can detect it
|
||||
set(HAVE_QRC_TRANSLATIONS TRUE)
|
||||
endif() # of do translate
|
||||
|
||||
add_custom_target(ts)
|
||||
foreach(lang ${LANGUAGES})
|
||||
add_custom_target(
|
||||
ts_${lang}
|
||||
COMMAND Qt5::lupdate ${CMAKE_SOURCE_DIR}/src/GUI
|
||||
-locations relative -no-ui-lines -ts ${TRANSLATIONS_SRC_DIR}/${lang}/FlightGear-Qt.xlf
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
add_dependencies(ts ts_${lang})
|
||||
endforeach()
|
|
@ -184,6 +184,7 @@ class DirIndex:
|
|||
def __init__(self, dirIndexFile):
|
||||
self.d = []
|
||||
self.f = []
|
||||
self.t = []
|
||||
self.version = 0
|
||||
self.path = None # will be a VirtualPath instance when set
|
||||
|
||||
|
@ -221,6 +222,9 @@ class DirIndex:
|
|||
elif tokens[0] == "f":
|
||||
self.f.append({ 'name': tokens[1], 'hash': tokens[2], 'size': tokens[3] })
|
||||
|
||||
elif tokens[0] == "t":
|
||||
self.t.append({ 'name': tokens[1], 'hash': tokens[2], 'size': tokens[3] })
|
||||
|
||||
def _sanityCheck(self):
|
||||
if self.path is None:
|
||||
assert self._rawContents is not None
|
||||
|
@ -239,6 +243,9 @@ class DirIndex:
|
|||
def getDirectories(self):
|
||||
return self.d
|
||||
|
||||
def getTarballs(self):
|
||||
return self.t
|
||||
|
||||
def getFiles(self):
|
||||
return self.f
|
||||
|
||||
|
@ -639,6 +646,14 @@ class TerraSync:
|
|||
subdir['hash'])
|
||||
serverDirs.append(d)
|
||||
|
||||
for tarball in dirIndex.getTarballs():
|
||||
# Tarballs are handled the same as normal files.
|
||||
f = tarball['name']
|
||||
self.processFileEntry(virtualBase / f,
|
||||
join(relativeBase, f),
|
||||
tarball['hash'])
|
||||
serverFiles.append(f)
|
||||
|
||||
localFullPath = join(self.target, relativeBase)
|
||||
localFiles = [ f for f in listdir(localFullPath)
|
||||
if isfile(join(localFullPath, f)) ]
|
||||
|
|
|
@ -516,7 +516,7 @@ void FGAIBase::Transform() {
|
|||
*/
|
||||
std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder)
|
||||
{
|
||||
std::vector<std::string> path_list;
|
||||
string_list path_list;
|
||||
|
||||
if (searchOrder == DATA_ONLY) {
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only");
|
||||
|
@ -536,7 +536,8 @@ std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
|
|||
}
|
||||
} else {
|
||||
// No model, so fall back to the default
|
||||
path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model));
|
||||
const SGPath defaultModelPath = SGPath::fromUtf8(fgGetString("/sim/multiplay/default-model", default_model));
|
||||
path_list.push_back(defaultModelPath.utf8Str());
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: PREFER_AI/PREFER_DATA");
|
||||
|
@ -544,8 +545,8 @@ std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
|
|||
for (SGPath p : globals->get_data_paths("AI")) {
|
||||
p.append(model_path);
|
||||
if (p.exists()) {
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p.local8BitStr());
|
||||
path_list.push_back(p.local8BitStr());
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p);
|
||||
path_list.push_back(p.utf8Str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -566,8 +567,8 @@ std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
|
|||
for (SGPath p : globals->get_data_paths()) {
|
||||
p.append(fallback_path);
|
||||
if (p.exists()) {
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p.local8BitStr());
|
||||
path_list.push_back(p.local8BitStr());
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p);
|
||||
path_list.push_back(p.utf8Str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <config.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
@ -35,11 +33,13 @@
|
|||
|
||||
#include "AICarrier.hxx"
|
||||
|
||||
FGAICarrier::FGAICarrier() : FGAIShip(otCarrier), deck_altitude(65.0065) {
|
||||
FGAICarrier::FGAICarrier() :
|
||||
FGAIShip(otCarrier),
|
||||
deck_altitude(65.0065)
|
||||
{
|
||||
}
|
||||
|
||||
FGAICarrier::~FGAICarrier() {
|
||||
}
|
||||
FGAICarrier::~FGAICarrier() = default;
|
||||
|
||||
void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) {
|
||||
if (!scFileNode)
|
||||
|
@ -67,11 +67,14 @@ void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) {
|
|||
// Transform to the right coordinate frame, configuration is done in
|
||||
// the usual x-back, y-right, z-up coordinates, computations
|
||||
// in the simulation usual body x-forward, y-right, z-down coordinates
|
||||
flols_off(0) = - flols->getDoubleValue("x-offset-m", 0);
|
||||
flols_off(1) = flols->getDoubleValue("y-offset-m", 0);
|
||||
flols_off(2) = - flols->getDoubleValue("z-offset-m", 0);
|
||||
_flolsPosOffset(0) = - flols->getDoubleValue("x-offset-m", 0);
|
||||
_flolsPosOffset(1) = flols->getDoubleValue("y-offset-m", 0);
|
||||
_flolsPosOffset(2) = - flols->getDoubleValue("z-offset-m", 0);
|
||||
|
||||
_flolsHeadingOffsetDeg = flols->getDoubleValue("heading-offset-deg", 0.0);
|
||||
_flolsApproachAngle = flols->getDoubleValue("glidepath-angle-deg", 3.0);
|
||||
} else
|
||||
flols_off = SGVec3d::zeros();
|
||||
_flolsPosOffset = SGVec3d::zeros();
|
||||
|
||||
std::vector<SGPropertyNode_ptr> props = scFileNode->getChildren("parking-pos");
|
||||
std::vector<SGPropertyNode_ptr>::const_iterator it;
|
||||
|
@ -180,13 +183,13 @@ void FGAICarrier::update(double dt) {
|
|||
// rotate the eyepoint wrt carrier vector into the carriers frame
|
||||
eyeWrtCarrier = ec2body.transform(eyeWrtCarrier);
|
||||
// the eyepoints vector wrt the flols position
|
||||
SGVec3d eyeWrtFlols = eyeWrtCarrier - flols_off;
|
||||
SGVec3d eyeWrtFlols = eyeWrtCarrier - _flolsPosOffset;
|
||||
|
||||
// the distance from the eyepoint to the flols
|
||||
dist = norm(eyeWrtFlols);
|
||||
|
||||
// now the angle, positive angles are upwards
|
||||
if (fabs(dist) < SGLimits<float>::min()) {
|
||||
if (fabs(dist) < SGLimits<double>::min()) {
|
||||
angle = 0;
|
||||
} else {
|
||||
double sAngle = -eyeWrtFlols(2)/dist;
|
||||
|
@ -327,11 +330,9 @@ bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos,
|
|||
{
|
||||
|
||||
// FIXME: does not yet cover rotation speeds.
|
||||
list<ParkPosition>::iterator it = ppositions.begin();
|
||||
while (it != ppositions.end()) {
|
||||
for (const auto& ppos : ppositions) {
|
||||
// Take either the specified one or the first one ...
|
||||
if ((*it).name == id || id.empty()) {
|
||||
ParkPosition ppos = *it;
|
||||
if (ppos.name == id || id.empty()) {
|
||||
SGVec3d cartPos = getCartPosAt(ppos.offset);
|
||||
geodPos = SGGeod::fromCart(cartPos);
|
||||
hdng = hdg + ppos.heading_deg;
|
||||
|
@ -341,12 +342,28 @@ bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos,
|
|||
uvw = SGVec3d(chdng*speed_fps, shdng*speed_fps, 0);
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FGAICarrier::getFLOLSPositionHeading(SGGeod& geodPos, double& heading) const
|
||||
{
|
||||
SGVec3d cartPos = getCartPosAt(_flolsPosOffset);
|
||||
geodPos = SGGeod::fromCart(cartPos);
|
||||
|
||||
// at present we don't support a heading offset for the FLOLS, so
|
||||
// heading is just the carrier heading
|
||||
heading = hdg + _flolsHeadingOffsetDeg;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double FGAICarrier::getFLOLFSGlidepathAngleDeg() const
|
||||
{
|
||||
return _flolsApproachAngle;
|
||||
}
|
||||
|
||||
// find relative wind
|
||||
void FGAICarrier::UpdateWind( double dt) {
|
||||
|
||||
|
@ -371,8 +388,7 @@ void FGAICarrier::UpdateWind( double dt) {
|
|||
+ (rel_wind_speed_from_north_kts * rel_wind_speed_from_north_kts));
|
||||
|
||||
//calculate the relative wind direction
|
||||
rel_wind_from_deg = atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts)
|
||||
* SG_RADIANS_TO_DEGREES;
|
||||
rel_wind_from_deg = SGMiscd::rad2deg(atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts));
|
||||
|
||||
//calculate rel wind
|
||||
rel_wind = rel_wind_from_deg - hdg;
|
||||
|
@ -640,7 +656,7 @@ SGSharedPtr<FGAICarrier> FGAICarrier::findCarrierByNameOrPennant(const std::stri
|
|||
return {};
|
||||
}
|
||||
|
||||
for (const auto aiObject : aiManager->get_ai_list()) {
|
||||
for (const auto& aiObject : aiManager->get_ai_list()) {
|
||||
if (aiObject->isa(FGAIBase::otCarrier)) {
|
||||
SGSharedPtr<FGAICarrier> c = static_cast<FGAICarrier*>(aiObject.get());
|
||||
if ((c->sign == namePennant) || (c->_getName() == namePennant)) {
|
||||
|
@ -652,7 +668,7 @@ SGSharedPtr<FGAICarrier> FGAICarrier::findCarrierByNameOrPennant(const std::stri
|
|||
return {};
|
||||
}
|
||||
|
||||
void FGAICarrier::extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario)
|
||||
void FGAICarrier::extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario)
|
||||
{
|
||||
for (auto c : xmlNode->getChildren("entry")) {
|
||||
if (c->getStringValue("type") != std::string("carrier"))
|
||||
|
@ -673,6 +689,14 @@ void FGAICarrier::extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, S
|
|||
// the find code above just looks for anything called a name (so alias
|
||||
// are possible, for example)
|
||||
if (!name.empty()) carrierNode->addChild("name")->setStringValue(name);
|
||||
if (!pennant.empty()) carrierNode->addChild("name")->setStringValue(pennant);
|
||||
if (!pennant.empty()) {
|
||||
carrierNode->addChild("name")->setStringValue(pennant);
|
||||
carrierNode->addChild("pennant-number")->setStringValue(pennant);
|
||||
}
|
||||
|
||||
// extact parkings
|
||||
for (auto p : c->getChildren("parking-pos")) {
|
||||
carrierNode->addChild("parking-pos")->setStringValue(p->getStringValue("name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,8 +86,11 @@ public:
|
|||
* This is used to support 'start on a carrier', since we can quickly find
|
||||
* the corresponding scenario file to be loaded.
|
||||
*/
|
||||
static void extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario);
|
||||
static void extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario);
|
||||
|
||||
bool getFLOLSPositionHeading(SGGeod &pos, double &heading) const;
|
||||
|
||||
double getFLOLFSGlidepathAngleDeg() const;
|
||||
private:
|
||||
/// Is sufficient to be private, stores a possible position to place an
|
||||
/// aircraft on start
|
||||
|
@ -115,8 +118,10 @@ private:
|
|||
list<ParkPosition> ppositions; // List of positions where an aircraft can start.
|
||||
string sign; // The sign of this carrier.
|
||||
|
||||
// these describe the flols
|
||||
SGVec3d flols_off;
|
||||
// these describe the flols
|
||||
SGVec3d _flolsPosOffset;
|
||||
double _flolsHeadingOffsetDeg = 0.0; ///< angle in degrees offset from the carrier centerline
|
||||
double _flolsApproachAngle = 3.0; ///< glidepath angle for the FLOLS
|
||||
|
||||
double dist; // the distance of the eyepoint from the flols
|
||||
double angle;
|
||||
|
|
|
@ -71,11 +71,11 @@ public:
|
|||
_unloadScript = nasalScripts->getStringValue("unload");
|
||||
std::string loadScript = nasalScripts->getStringValue("load");
|
||||
if (!loadScript.empty()) {
|
||||
FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
|
||||
FGNasalSys* nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
std::string moduleName = "scenario_" + _internalName;
|
||||
nasalSys->createModule(moduleName.c_str(), moduleName.c_str(),
|
||||
loadScript.c_str(), loadScript.size(),
|
||||
0);
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ public:
|
|||
[](FGAIBasePtr ai) { ai->setDie(true); });
|
||||
|
||||
|
||||
FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
|
||||
FGNasalSys* nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
if (!nasalSys)
|
||||
return;
|
||||
|
||||
|
@ -93,7 +93,7 @@ public:
|
|||
if (!_unloadScript.empty()) {
|
||||
nasalSys->createModule(moduleName.c_str(), moduleName.c_str(),
|
||||
_unloadScript.c_str(), _unloadScript.size(),
|
||||
0);
|
||||
nullptr);
|
||||
}
|
||||
|
||||
nasalSys->deleteModule(moduleName.c_str());
|
||||
|
@ -165,16 +165,19 @@ FGAIManager::init() {
|
|||
registerScenarios();
|
||||
}
|
||||
|
||||
void FGAIManager::registerScenarios()
|
||||
void FGAIManager::registerScenarios(SGPropertyNode_ptr root)
|
||||
{
|
||||
// depending on if we're using a carrier startup, this function may get
|
||||
// called early or during normal FGAIManager init, so guard against double
|
||||
// invocation.
|
||||
// we clear this flag on shudtdown so reset works as expected
|
||||
if (static_haveRegisteredScenarios)
|
||||
return;
|
||||
|
||||
static_haveRegisteredScenarios = true;
|
||||
if (!root) {
|
||||
// depending on if we're using a carrier startup, this function may get
|
||||
// called early or during normal FGAIManager init, so guard against double
|
||||
// invocation.
|
||||
// we clear this flag on shudtdown so reset works as expected
|
||||
if (static_haveRegisteredScenarios)
|
||||
return;
|
||||
|
||||
static_haveRegisteredScenarios = true;
|
||||
root = globals->get_props();
|
||||
}
|
||||
|
||||
// find all scenarios at standard locations (for driving the GUI)
|
||||
std::vector<SGPath> scenarioSearchPaths;
|
||||
|
@ -184,27 +187,29 @@ void FGAIManager::registerScenarios()
|
|||
|
||||
// add-on scenario directories
|
||||
const auto& addonsManager = flightgear::addons::AddonManager::instance();
|
||||
for (auto a : addonsManager->registeredAddons()) {
|
||||
scenarioSearchPaths.push_back(a->getBasePath() / "Scenarios");
|
||||
if (addonsManager) {
|
||||
for (auto a : addonsManager->registeredAddons()) {
|
||||
scenarioSearchPaths.push_back(a->getBasePath() / "Scenarios");
|
||||
}
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr scenariosNode = fgGetNode("/sim/ai/scenarios", true);
|
||||
|
||||
SGPropertyNode_ptr scenariosNode = root->getNode("/sim/ai/scenarios", true);
|
||||
for (auto p : scenarioSearchPaths) {
|
||||
if (!p.exists())
|
||||
continue;
|
||||
|
||||
simgear::Dir dir(p);
|
||||
for (auto xmlPath : dir.children(simgear::Dir::TYPE_FILE, ".xml")) {
|
||||
registerScenarioFile(xmlPath);
|
||||
registerScenarioFile(root, xmlPath);
|
||||
} // of xml files in the scenario dir iteration
|
||||
} // of scenario dirs iteration
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr FGAIManager::registerScenarioFile(const SGPath& xmlPath)
|
||||
SGPropertyNode_ptr FGAIManager::registerScenarioFile(SGPropertyNode_ptr root, const SGPath& xmlPath)
|
||||
{
|
||||
if (!xmlPath.exists()) return {};
|
||||
|
||||
auto scenariosNode = fgGetNode("/sim/ai/scenarios", true);
|
||||
auto scenariosNode = root->getNode("/sim/ai/scenarios", true);
|
||||
SGPropertyNode_ptr sNode;
|
||||
|
||||
try {
|
||||
|
@ -235,7 +240,7 @@ SGPropertyNode_ptr FGAIManager::registerScenarioFile(const SGPath& xmlPath)
|
|||
sNode->setStringValue("description", xs->getStringValue("description"));
|
||||
}
|
||||
|
||||
FGAICarrier::extractNamesPennantsFromScenario(xs, sNode);
|
||||
FGAICarrier::extractCarriersFromScenario(xs, sNode);
|
||||
} // of scenarios in the XML file
|
||||
} catch (std::exception&) {
|
||||
SG_LOG(SG_AI, SG_WARN, "Skipping malformed scenario file:" << xmlPath);
|
||||
|
@ -367,7 +372,7 @@ FGAIManager::update(double dt)
|
|||
for (FGAIBase* base : ai_list) {
|
||||
try {
|
||||
if (base->isa(FGAIBase::otThermal)) {
|
||||
processThermal(dt, (FGAIThermal*)base);
|
||||
processThermal(dt, static_cast<FGAIThermal*>(base));
|
||||
} else {
|
||||
base->update(dt);
|
||||
}
|
||||
|
@ -428,7 +433,7 @@ bool FGAIManager::isVisible(const SGGeod& pos) const
|
|||
int
|
||||
FGAIManager::getNumAiObjects() const
|
||||
{
|
||||
return ai_list.size();
|
||||
return static_cast<int>(ai_list.size());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -489,12 +494,14 @@ bool FGAIManager::loadScenarioCommand(const SGPropertyNode* args, SGPropertyNode
|
|||
|
||||
bool FGAIManager::unloadScenarioCommand(const SGPropertyNode * arg, SGPropertyNode * root)
|
||||
{
|
||||
SG_UNUSED(root);
|
||||
std::string name = arg->getStringValue("name");
|
||||
return unloadScenario(name);
|
||||
}
|
||||
|
||||
bool FGAIManager::addObjectCommand(const SGPropertyNode* arg, const SGPropertyNode* root)
|
||||
{
|
||||
SG_UNUSED(root);
|
||||
if (!arg){
|
||||
return false;
|
||||
}
|
||||
|
@ -506,7 +513,7 @@ FGAIBasePtr FGAIManager::addObject(const SGPropertyNode* definition)
|
|||
{
|
||||
const std::string& type = definition->getStringValue("type", "aircraft");
|
||||
|
||||
FGAIBase* ai = NULL;
|
||||
FGAIBase* ai = nullptr;
|
||||
if (type == "tanker") { // refueling scenarios
|
||||
ai = new FGAITanker;
|
||||
} else if (type == "wingman") {
|
||||
|
@ -545,6 +552,7 @@ FGAIBasePtr FGAIManager::addObject(const SGPropertyNode* definition)
|
|||
|
||||
bool FGAIManager::removeObjectCommand(const SGPropertyNode* arg, const SGPropertyNode* root)
|
||||
{
|
||||
SG_UNUSED(root);
|
||||
if (!arg) {
|
||||
return false;
|
||||
}
|
||||
|
@ -703,7 +711,7 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range
|
|||
}
|
||||
++ai_list_itr;
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
double
|
||||
|
|
|
@ -73,8 +73,8 @@ public:
|
|||
* we need carrier scenarios to start the position-init process for a
|
||||
* carrier start.
|
||||
*/
|
||||
static void registerScenarios();
|
||||
static SGPropertyNode_ptr registerScenarioFile(const SGPath& p);
|
||||
static void registerScenarios(SGPropertyNode_ptr root = {});
|
||||
static SGPropertyNode_ptr registerScenarioFile(SGPropertyNode_ptr root, const SGPath& p);
|
||||
static SGPropertyNode_ptr loadScenarioFile(const std::string& id);
|
||||
|
||||
FGAIBasePtr addObject(const SGPropertyNode* definition);
|
||||
|
|
|
@ -50,6 +50,12 @@ FGAIMultiplayer::FGAIMultiplayer() :
|
|||
lastUpdateTime = 0;
|
||||
playerLag = 0.03;
|
||||
compensateLag = 1;
|
||||
realTime = false;
|
||||
lastTime=0.0;
|
||||
lagPpsAveraged = 1.0;
|
||||
rawLag = 0.0;
|
||||
rawLagMod = 0.0;
|
||||
lagModAveraged = 0.0;
|
||||
_searchOrder = PREFER_DATA;
|
||||
}
|
||||
|
||||
|
@ -152,73 +158,96 @@ void FGAIMultiplayer::update(double dt)
|
|||
// requested time to the most recent available packet. This is the
|
||||
// target we want to reach in average.
|
||||
double lag = it->second.lag;
|
||||
|
||||
rawLag = curentPkgTime - curtime;
|
||||
realTime = false; //default behaviour
|
||||
|
||||
if (!mTimeOffsetSet) {
|
||||
mTimeOffsetSet = true;
|
||||
mTimeOffset = curentPkgTime - curtime - lag;
|
||||
lastTime = curentPkgTime;
|
||||
lagModAveraged = remainder((curentPkgTime - curtime), 3600.0);
|
||||
props->setDoubleValue("lag/pps-averaged", lagPpsAveraged);
|
||||
props->setDoubleValue("lag/lag-mod-averaged", lagModAveraged);
|
||||
} else {
|
||||
double offset = 0.0;
|
||||
if ((curentPkgTime - lastTime) != 0) {
|
||||
lagPpsAveraged = 0.99 * lagPpsAveraged + 0.01 * fabs( 1 / (lastTime - curentPkgTime));
|
||||
lastTime = curentPkgTime;
|
||||
rawLagMod = remainder(rawLag, 3600.0);
|
||||
lagModAveraged = lagModAveraged *0.99 + 0.01 * rawLagMod;
|
||||
props->setDoubleValue("lag/pps-averaged", lagPpsAveraged);
|
||||
props->setDoubleValue("lag/lag-mod-averaged", lagModAveraged);
|
||||
}
|
||||
|
||||
//spectator mode, more late to be in the interpolation zone
|
||||
double offset = 0.0;
|
||||
|
||||
//spectator mode, more late to be in the interpolation zone
|
||||
if (compensateLag == 3) { offset = curentPkgTime -curtime -lag + playerLag;
|
||||
|
||||
// old behaviour
|
||||
// old behaviour
|
||||
} else if (compensateLag == 1) { offset = curentPkgTime - curtime - lag;
|
||||
|
||||
// using the prediction mode to display the mpaircraft in the futur/past with given playerlag value
|
||||
//currently compensatelag = 2
|
||||
} else if (fabs(lagModAveraged) < 0.3) {
|
||||
mTimeOffset = (round(rawLag/3600))*3600; //real time mode if close enough
|
||||
realTime = true;
|
||||
|
||||
} else { offset = curentPkgTime - curtime + 0.48*lag + playerLag;
|
||||
}
|
||||
if ((!mAllowExtrapolation && offset + lag < mTimeOffset)
|
||||
|| (offset - 10 > mTimeOffset)) {
|
||||
mTimeOffset = offset;
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Resetting time offset adjust system to "
|
||||
"avoid extrapolation: time offset = " << mTimeOffset);
|
||||
} else {
|
||||
// the error of the offset, respectively the negative error to avoid
|
||||
// a minus later ...
|
||||
double err = offset - mTimeOffset;
|
||||
// limit errors leading to shorter lag values somehow, that is late
|
||||
// arriving packets will pessimize the overall lag much more than
|
||||
// early packets will shorten the overall lag
|
||||
double sysSpeed;
|
||||
|
||||
//trying to slow the rudderlag phenomenon thus using more the prediction system
|
||||
//if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s
|
||||
if (fabs(err) < 1.5) {
|
||||
if (err < 0) {
|
||||
sysSpeed = mLagAdjustSystemSpeed*err*0.01;
|
||||
} else {
|
||||
sysSpeed = SGMiscd::min(0.5*err*err, 0.05);
|
||||
}
|
||||
} else {
|
||||
if (err < 0) {
|
||||
if (!realTime) {
|
||||
|
||||
// Ok, we have some very late packets and nothing newer increase the
|
||||
// lag by the given speedadjust
|
||||
sysSpeed = mLagAdjustSystemSpeed*err;
|
||||
} else {
|
||||
// We have a too pessimistic display delay shorten that a small bit
|
||||
sysSpeed = SGMiscd::min(0.1*err*err, 0.5);
|
||||
}
|
||||
}
|
||||
if ((!mAllowExtrapolation && offset + lag < mTimeOffset)
|
||||
|| (offset - 10 > mTimeOffset)) {
|
||||
mTimeOffset = offset;
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Resetting time offset adjust system to "
|
||||
"avoid extrapolation: time offset = " << mTimeOffset);
|
||||
} else {
|
||||
// the error of the offset, respectively the negative error to avoid
|
||||
// a minus later ...
|
||||
double err = offset - mTimeOffset;
|
||||
// limit errors leading to shorter lag values somehow, that is late
|
||||
// arriving packets will pessimize the overall lag much more than
|
||||
// early packets will shorten the overall lag
|
||||
double sysSpeed;
|
||||
//trying to slow the rudderlag phenomenon thus using more the prediction system
|
||||
//if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s
|
||||
if (fabs(err) < 1.5) {
|
||||
if (err < 0) {
|
||||
sysSpeed = mLagAdjustSystemSpeed*err*0.01;
|
||||
} else {
|
||||
sysSpeed = SGMiscd::min(0.5*err*err, 0.05);
|
||||
}
|
||||
} else {
|
||||
if (err < 0) {
|
||||
|
||||
// Ok, we have some very late packets and nothing newer increase the
|
||||
// lag by the given speedadjust
|
||||
sysSpeed = mLagAdjustSystemSpeed*err;
|
||||
} else {
|
||||
// We have a too pessimistic display delay shorten that a small bit
|
||||
sysSpeed = SGMiscd::min(0.1*err*err, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// simple euler integration for that first order system including some
|
||||
// overshooting guard to prevent to aggressive system speeds
|
||||
// (stiff systems) to explode the systems state
|
||||
double systemIncrement = dt*sysSpeed;
|
||||
if (fabs(err) < fabs(systemIncrement))
|
||||
systemIncrement = err;
|
||||
mTimeOffset += systemIncrement;
|
||||
double systemIncrement = dt*sysSpeed;
|
||||
if (fabs(err) < fabs(systemIncrement))
|
||||
systemIncrement = err;
|
||||
mTimeOffset += systemIncrement;
|
||||
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = "
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = "
|
||||
<< mTimeOffset << ", expected longitudinal position error due to "
|
||||
" current adjustment of the offset: "
|
||||
<< fabs(norm(it->second.linearVel)*systemIncrement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute the time in the feeders time scale which fits the current time
|
||||
// we need to
|
||||
double tInterp = curtime + mTimeOffset;
|
||||
|
@ -227,9 +256,10 @@ void FGAIMultiplayer::update(double dt)
|
|||
SGQuatf ecOrient;
|
||||
SGVec3f ecLinearVel;
|
||||
|
||||
if (tInterp <= curentPkgTime) {
|
||||
if (tInterp < curentPkgTime) {
|
||||
// Ok, we need a time prevous to the last available packet,
|
||||
// that is good ...
|
||||
// the case tInterp = curentPkgTime need to be in the interpolation, to avoid a bug zeroing the position
|
||||
|
||||
// Find the first packet before the target time
|
||||
MotionInfo::iterator nextIt = mMotionInfo.upper_bound(tInterp);
|
||||
|
|
|
@ -86,11 +86,15 @@ private:
|
|||
typedef std::map<unsigned, SGSharedPtr<SGPropertyNode> > PropertyMap;
|
||||
PropertyMap mPropertyMap;
|
||||
|
||||
double mTimeOffset;
|
||||
bool mTimeOffsetSet;
|
||||
double playerLag;
|
||||
bool realTime;
|
||||
int compensateLag;
|
||||
double playerLag;
|
||||
double mTimeOffset;
|
||||
double lastUpdateTime;
|
||||
double lastTime;
|
||||
double lagPpsAveraged;
|
||||
double rawLag, rawLagMod, lagModAveraged;
|
||||
|
||||
/// Properties which are for now exposed for testing
|
||||
bool mAllowExtrapolation;
|
||||
|
|
|
@ -287,6 +287,8 @@ void FGAIShip::Run(double dt) {
|
|||
if (speed_diff < 0.0)
|
||||
speed -= _speed_constant * dt;
|
||||
|
||||
} else {
|
||||
speed = tgt_speed;
|
||||
}
|
||||
|
||||
// do not allow unreasonable speeds
|
||||
|
|
|
@ -162,6 +162,8 @@ void FGATCDialogNew::frequencyDisplay(const std::string& ident)
|
|||
buf[7] = '\0';
|
||||
|
||||
entry->setStringValue("text[1]/label", buf);
|
||||
entry->setStringValue("button[0]/binding/value", buf);
|
||||
entry->setStringValue("button[1]/binding/value", buf);
|
||||
}
|
||||
|
||||
_gui->showDialog(dialog_name);
|
||||
|
|
|
@ -139,12 +139,13 @@ FGReplay::clear()
|
|||
void
|
||||
FGReplay::init()
|
||||
{
|
||||
disable_replay = fgGetNode("/sim/replay/disable", true);
|
||||
replay_master = fgGetNode("/sim/replay/replay-state", true);
|
||||
replay_time = fgGetNode("/sim/replay/time", true);
|
||||
replay_time_str = fgGetNode("/sim/replay/time-str", true);
|
||||
replay_looped = fgGetNode("/sim/replay/looped", true);
|
||||
speed_up = fgGetNode("/sim/speed-up", true);
|
||||
disable_replay = fgGetNode("/sim/replay/disable", true);
|
||||
replay_master = fgGetNode("/sim/replay/replay-state", true);
|
||||
replay_time = fgGetNode("/sim/replay/time", true);
|
||||
replay_time_str = fgGetNode("/sim/replay/time-str", true);
|
||||
replay_looped = fgGetNode("/sim/replay/looped", true);
|
||||
replay_duration_act = fgGetNode("/sim/replay/duration-act", true);
|
||||
speed_up = fgGetNode("/sim/speed-up", true);
|
||||
|
||||
// alias to keep backward compatibility
|
||||
fgGetNode("/sim/freeze/replay-state", true)->alias(replay_master);
|
||||
|
@ -431,8 +432,8 @@ FGReplay::update( double dt )
|
|||
fgSetDouble( "/sim/replay/start-time", startTime );
|
||||
fgSetDouble( "/sim/replay/end-time", endTime );
|
||||
double duration = 0;
|
||||
if (replay_looped->getBoolValue())
|
||||
fgGetDouble("/sim/replay/duration");
|
||||
if (replay_duration_act->getBoolValue())
|
||||
duration = fgGetDouble("/sim/replay/duration");
|
||||
if( duration && (duration < (endTime - startTime)) ) {
|
||||
current_time = endTime - duration;
|
||||
} else {
|
||||
|
@ -822,7 +823,7 @@ FGReplay::saveTape(const SGPath& Filename, SGPropertyNode* MetaDataProps)
|
|||
bool ok = true;
|
||||
|
||||
/* open output stream *******************************************/
|
||||
gzContainerWriter output(Filename.local8BitStr(), FlightRecorderFileMagic);
|
||||
gzContainerWriter output(Filename, FlightRecorderFileMagic);
|
||||
if (!output.good())
|
||||
{
|
||||
SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file" << Filename);
|
||||
|
@ -940,7 +941,7 @@ FGReplay::loadTape(const SGPath& Filename, bool Preview, SGPropertyNode* UserDat
|
|||
bool ok = true;
|
||||
|
||||
/* open input stream ********************************************/
|
||||
gzContainerReader input(Filename.local8BitStr(), FlightRecorderFileMagic);
|
||||
gzContainerReader input(Filename, FlightRecorderFileMagic);
|
||||
if (input.eof() || !input.good())
|
||||
{
|
||||
SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file " << Filename);
|
||||
|
|
|
@ -118,6 +118,7 @@ private:
|
|||
SGPropertyNode_ptr replay_time;
|
||||
SGPropertyNode_ptr replay_time_str;
|
||||
SGPropertyNode_ptr replay_looped;
|
||||
SGPropertyNode_ptr replay_duration_act;
|
||||
SGPropertyNode_ptr speed_up;
|
||||
|
||||
double m_high_res_time; // default: 60 secs of high res data
|
||||
|
|
|
@ -148,15 +148,10 @@ void RunwayList::set(const std::string & tp, const std::string & lst)
|
|||
// timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
|
||||
// timeCopy = timeCopy.substr(2,timeCopy.length());
|
||||
type = tp;
|
||||
|
||||
|
||||
|
||||
BOOST_FOREACH(std::string s, strutils::split(lst, ",")) {
|
||||
std::string ident = strutils::strip(s);
|
||||
|
||||
for (const auto s : strutils::split(lst, ",")) {
|
||||
auto ident = strutils::strip(s);
|
||||
// http://code.google.com/p/flightgear-bugs/issues/detail?id=1137
|
||||
if ((ident.size() < 2) || !isdigit(ident[1])) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "RunwayList::set: padding runway ident '" << ident << "'");
|
||||
ident = "0" + ident;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace canvas
|
|||
"canvas::Text: using font file " << path
|
||||
);
|
||||
|
||||
simgear::canvas::FontPtr font = osgText::readFontFile(path.local8BitStr());
|
||||
simgear::canvas::FontPtr font = osgText::readFontFile(path.utf8Str());
|
||||
if( !font )
|
||||
SG_LOG
|
||||
(
|
||||
|
@ -86,9 +86,9 @@ namespace canvas
|
|||
SGPath valid_path = fgValidatePath(p, false);
|
||||
if( !valid_path.isNull() )
|
||||
#if OSG_VERSION_LESS_THAN(3,4,0)
|
||||
return osgDB::readRefImageFile(valid_path.local8BitStr());
|
||||
return osgDB::readRefImageFile(valid_path.utf8Str());
|
||||
#else
|
||||
return osgDB::readRefImageFile(valid_path.local8BitStr());
|
||||
return osgDB::readRefImageFile(valid_path.utf8Str());
|
||||
#endif
|
||||
|
||||
SG_LOG(SG_IO, SG_ALERT, "canvas::Image: reading '" << path << "' denied");
|
||||
|
@ -98,9 +98,9 @@ namespace canvas
|
|||
SGPath tpath = globals->resolve_resource_path(path);
|
||||
if( !tpath.isNull() )
|
||||
#if OSG_VERSION_LESS_THAN(3,4,0)
|
||||
return osgDB::readImageFile(tpath.local8BitStr());
|
||||
return osgDB::readImageFile(tpath.utf8Str());
|
||||
#else
|
||||
return osgDB::readRefImageFile(tpath.local8BitStr());
|
||||
return osgDB::readRefImageFile(tpath.utf8Str());
|
||||
#endif
|
||||
|
||||
SG_LOG(SG_IO, SG_ALERT, "canvas::Image: No such image: '" << path << "'");
|
||||
|
@ -127,7 +127,7 @@ namespace canvas
|
|||
SG_LOG( SG_IO,
|
||||
SG_ALERT,
|
||||
"FGCanvasSystemAdapter: Failed to get HTTP subsystem" );
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,21 +71,23 @@ CanvasMgr::CanvasMgr():
|
|||
void CanvasMgr::init()
|
||||
{
|
||||
_gui_camera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
|
||||
assert(_gui_camera.valid());
|
||||
|
||||
sc::Canvas::addPlacementFactory
|
||||
(
|
||||
"object",
|
||||
boost::bind
|
||||
(
|
||||
&FGODGauge::set_aircraft_texture,
|
||||
_1,
|
||||
boost::bind(&sc::Canvas::getTexture, _2),
|
||||
boost::bind(&sc::Canvas::getCullCallback, _2),
|
||||
_2
|
||||
)
|
||||
);
|
||||
sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement);
|
||||
if (_gui_camera.valid()) {
|
||||
// add our two placement factories
|
||||
sc::Canvas::addPlacementFactory
|
||||
(
|
||||
"object",
|
||||
boost::bind
|
||||
(
|
||||
&FGODGauge::set_aircraft_texture,
|
||||
_1,
|
||||
boost::bind(&sc::Canvas::getTexture, _2),
|
||||
boost::bind(&sc::Canvas::getCullCallback, _2),
|
||||
_2
|
||||
)
|
||||
);
|
||||
|
||||
sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement);
|
||||
}
|
||||
|
||||
simgear::canvas::CanvasMgr::init();
|
||||
}
|
||||
|
@ -98,7 +100,7 @@ void CanvasMgr::shutdown()
|
|||
sc::Canvas::removePlacementFactory("object");
|
||||
sc::Canvas::removePlacementFactory("scenery-object");
|
||||
|
||||
_gui_camera = 0;
|
||||
_gui_camera = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -777,7 +777,7 @@ NavDisplay::updateFont()
|
|||
}
|
||||
|
||||
osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
|
||||
osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.local8BitStr(), fontOptions.get());
|
||||
osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.utf8Str(), fontOptions.get());
|
||||
|
||||
if (font != 0) {
|
||||
_font = font;
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <plib/fnt.h>
|
||||
#include "fnt.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
|
|
@ -1084,7 +1084,7 @@ wxRadarBg::updateFont()
|
|||
}
|
||||
|
||||
osg::ref_ptr<osgDB::ReaderWriter::Options> fontOptions = new osgDB::ReaderWriter::Options("monochrome");
|
||||
osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.local8BitStr(), fontOptions.get());
|
||||
osg::ref_ptr<osgText::Font> font = osgText::readFontFile(tpath.utf8Str(), fontOptions.get());
|
||||
|
||||
if (font != 0) {
|
||||
_font = font;
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
add_executable(fgrcc fgrcc.cxx fgrcc.hxx)
|
||||
target_link_libraries(fgrcc SimGearCore ${PLATFORM_LIBS})
|
||||
|
||||
# On Windows, make sure fgrcc can be run (it needs third-party libraries) in add_custom_target
|
||||
if(MSVC)
|
||||
set_target_properties(fgrcc PROPERTIES DEBUG_POSTFIX d)
|
||||
if(MSVC_3RDPARTY_ROOT AND MSVC_3RDPARTY_DIR)
|
||||
set(CMAKE_MSVCIDE_RUN_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/bin)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Either MSVC_3RDPARTY_ROOT or MSVC_3RDPARTY_DIR is empty or unset")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
embeddedresources
|
||||
COMMAND
|
||||
|
|
|
@ -54,7 +54,7 @@ void Ephemeris::init()
|
|||
{
|
||||
SGPath ephem_data_path(globals->get_fg_root());
|
||||
ephem_data_path.append("Astro");
|
||||
_impl.reset(new SGEphemeris(ephem_data_path.local8BitStr()));
|
||||
_impl.reset(new SGEphemeris(ephem_data_path));
|
||||
|
||||
tieStar("/ephemeris/sun/xs", _impl->get_sun(), &Star::getxs);
|
||||
tieStar("/ephemeris/sun/ys", _impl->get_sun(), &Star::getys);
|
||||
|
|
|
@ -198,6 +198,7 @@ FGJSBsim::FGJSBsim( double dt )
|
|||
|
||||
PropertyManager = new FGPropertyManager( (FGPropertyNode*)globals->get_props() );
|
||||
fdmex = new FGFDMExec( PropertyManager );
|
||||
fdmex->Hold();
|
||||
|
||||
// Register ground callback.
|
||||
fdmex->SetGroundCallback( new FGFSGroundCallback(this) );
|
||||
|
@ -383,6 +384,15 @@ FGJSBsim::FGJSBsim( double dt )
|
|||
|
||||
mesh = new AircraftMesh(fgGetDouble("/fdm/jsbsim/metrics/bw-ft"),
|
||||
fgGetDouble("/fdm/jsbsim/metrics/cbarw-ft"));
|
||||
|
||||
// Trim once to initialize all output parameters
|
||||
FGTrim *fgtrim = new FGTrim(fdmex,tFull);
|
||||
fgtrim->DoTrim();
|
||||
delete fgtrim;
|
||||
|
||||
string directive_file = fgGetString("/sim/jsbsim/output-directive-file");
|
||||
if (!directive_file.empty())
|
||||
fdmex->SetOutputDirectives(directive_file);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -427,6 +437,7 @@ void FGJSBsim::init()
|
|||
common_init();
|
||||
|
||||
copy_to_JSBsim();
|
||||
fdmex->Resume();
|
||||
fdmex->RunIC(); //loop JSBSim once w/o integrating
|
||||
if (fgGetBool("/sim/presets/running")) {
|
||||
Propulsion->InitRunning(-1);
|
||||
|
|
|
@ -57,7 +57,7 @@ Element_ptr FGModelLoader::Open(Element *el)
|
|||
|
||||
if (!fname.empty()) {
|
||||
FGXMLFileRead XMLFileRead;
|
||||
SGPath path(SGPath::fromLocal8Bit(fname.c_str()));
|
||||
SGPath path(SGPath::fromUtf8(fname));
|
||||
|
||||
if (path.isRelative())
|
||||
path = model->FindFullPathName(path);
|
||||
|
|
|
@ -236,10 +236,10 @@ void FGFCSComponent::Clip(void)
|
|||
double range = vmax - vmin;
|
||||
|
||||
if (range < 0.0) {
|
||||
cerr << "Trying to clip with a max value " << ClipMax->GetName()
|
||||
<< " lower than the min value " << ClipMin->GetName()
|
||||
<< endl;
|
||||
throw("JSBSim aborts");
|
||||
cerr << "Trying to clip with a max value (" << vmax << ") from "
|
||||
<< ClipMax->GetName() << " lower than the min value (" << vmin
|
||||
<< ") from " << ClipMin->GetName() << "." << endl
|
||||
<< "Clipping is ignored." << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -189,6 +189,11 @@ void FGUFO::update( double dt ) {
|
|||
set_V_north(cos(heading) * velocity * SG_METER_TO_FEET);
|
||||
set_V_east(sin(heading) * velocity * SG_METER_TO_FEET);
|
||||
set_V_down(-real_climb_rate);
|
||||
|
||||
//we assume the ufo only fly along the roll axis, providing a velocity to allow lag compensation
|
||||
_set_Velocities_Body( velocity * SG_METER_TO_FEET,
|
||||
0 ,
|
||||
0 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,26 +18,26 @@ public:
|
|||
FGGround(FGInterface *iface);
|
||||
virtual ~FGGround();
|
||||
|
||||
virtual void getGroundPlane(const double pos[3],
|
||||
void getGroundPlane(const double pos[3],
|
||||
double plane[4], float vel[3],
|
||||
unsigned int &body);
|
||||
unsigned int &body) override;
|
||||
|
||||
virtual void getGroundPlane(const double pos[3],
|
||||
void getGroundPlane(const double pos[3],
|
||||
double plane[4], float vel[3],
|
||||
const simgear::BVHMaterial **material,
|
||||
unsigned int &body);
|
||||
unsigned int &body) override;
|
||||
|
||||
virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3],
|
||||
double angularVel[3], unsigned int &id);
|
||||
bool getBody(double t, double bodyToWorld[16], double linearVel[3],
|
||||
double angularVel[3], unsigned int &id) override;
|
||||
|
||||
virtual bool caughtWire(const double pos[4][3]);
|
||||
bool caughtWire(const double pos[4][3]) override;
|
||||
|
||||
virtual bool getWire(double end[2][3], float vel[2][3]);
|
||||
bool getWire(double end[2][3], float vel[2][3]) override;
|
||||
|
||||
virtual void releaseWire(void);
|
||||
void releaseWire(void) override;
|
||||
|
||||
virtual float getCatapult(const double pos[3],
|
||||
double end[2][3], float vel[2][3]);
|
||||
float getCatapult(const double pos[3],
|
||||
double end[2][3], float vel[2][3]) override;
|
||||
|
||||
void setTimeOffset(double toff);
|
||||
|
||||
|
|
|
@ -8,14 +8,6 @@
|
|||
#include "Ground.hpp"
|
||||
namespace yasim {
|
||||
|
||||
Ground::Ground()
|
||||
{
|
||||
}
|
||||
|
||||
Ground::~Ground()
|
||||
{
|
||||
}
|
||||
|
||||
void Ground::getGroundPlane(const double pos[3],
|
||||
double plane[4], float vel[3],
|
||||
unsigned int &body)
|
||||
|
@ -47,8 +39,7 @@ void Ground::getGroundPlane(const double pos[3],
|
|||
bool Ground::getBody(double t, double bodyToWorld[16], double linearVel[3],
|
||||
double angularVel[3], unsigned int &body)
|
||||
{
|
||||
return getBody(t, bodyToWorld, linearVel,
|
||||
angularVel, body);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Ground::caughtWire(const double pos[4][3])
|
||||
|
|
|
@ -8,8 +8,7 @@ namespace yasim {
|
|||
|
||||
class Ground {
|
||||
public:
|
||||
Ground();
|
||||
virtual ~Ground();
|
||||
virtual ~Ground() = default;
|
||||
|
||||
virtual void getGroundPlane(const double pos[3],
|
||||
double plane[4], float vel[3],
|
||||
|
@ -20,8 +19,8 @@ public:
|
|||
const simgear::BVHMaterial **material,
|
||||
unsigned int &body);
|
||||
|
||||
virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3],
|
||||
double angularVel[3], unsigned int &id);
|
||||
virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3],
|
||||
double angularVel[3], unsigned int &id);
|
||||
|
||||
virtual bool caughtWire(const double pos[4][3]);
|
||||
|
||||
|
|
|
@ -190,6 +190,12 @@ void FDMShell::update(double dt)
|
|||
|
||||
fgSetBool("/sim/fdm-initialized", true);
|
||||
fgSetBool("/sim/signals/fdm-initialized", true);
|
||||
|
||||
if (!copyProperties(_props->getNode("fdm", true),
|
||||
_initialFdmProperties))
|
||||
{
|
||||
SG_LOG(SG_FLIGHT, SG_ALERT, "Failed to save initial FDM property state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,12 +22,15 @@
|
|||
#include "AddonsModel.hxx"
|
||||
#include "InstallSceneryDialog.hxx"
|
||||
#include "QtLauncher.hxx"
|
||||
#include "PathListModel.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AddOnsController::AddOnsController(LauncherMainWindow *parent) :
|
||||
AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* config) :
|
||||
QObject(parent),
|
||||
m_launcher(parent)
|
||||
m_launcher(parent),
|
||||
m_config(config)
|
||||
{
|
||||
m_catalogs = new CatalogListModel(this,
|
||||
simgear::pkg::RootRef(globals->packageRoot()));
|
||||
|
@ -38,10 +41,32 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) :
|
|||
connect(m_addonsModuleModel, &AddonsModel::modulesChanged, this, &AddOnsController::onAddonsChanged);
|
||||
|
||||
using namespace flightgear::addons;
|
||||
QSettings settings;
|
||||
m_sceneryPaths = settings.value("scenery-paths").toStringList();
|
||||
m_aircraftPaths = settings.value("aircraft-paths").toStringList();
|
||||
|
||||
m_sceneryPaths = new PathListModel(this);
|
||||
connect(m_sceneryPaths, &PathListModel::enabledPathsChanged, [this] () {
|
||||
m_sceneryPaths->saveToSettings("scenery-paths-v2");
|
||||
flightgear::launcherSetSceneryPaths();
|
||||
});
|
||||
m_sceneryPaths->loadFromSettings("scenery-paths-v2");
|
||||
|
||||
m_aircraftPaths = new PathListModel(this);
|
||||
m_aircraftPaths->loadFromSettings("aircraft-paths-v2");
|
||||
|
||||
// sync up the aircraft cache now
|
||||
auto aircraftCache = LocalAircraftCache::instance();
|
||||
aircraftCache->setPaths(m_aircraftPaths->enabledPaths());
|
||||
aircraftCache->scanDirs();
|
||||
|
||||
// watch for future changes
|
||||
connect(m_aircraftPaths, &PathListModel::enabledPathsChanged, [this] () {
|
||||
m_aircraftPaths->saveToSettings("aircraft-paths-v2");
|
||||
|
||||
auto aircraftCache = LocalAircraftCache::instance();
|
||||
aircraftCache->setPaths(m_aircraftPaths->enabledPaths());
|
||||
aircraftCache->scanDirs();
|
||||
});
|
||||
|
||||
QSettings settings;
|
||||
int size = settings.beginReadArray("addon-modules");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
|
@ -66,14 +91,18 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) :
|
|||
qmlRegisterUncreatableType<AddOnsController>("FlightGear.Launcher", 1, 0, "AddOnsControllers", "no");
|
||||
qmlRegisterUncreatableType<CatalogListModel>("FlightGear.Launcher", 1, 0, "CatalogListModel", "no");
|
||||
qmlRegisterUncreatableType<AddonsModel>("FlightGear.Launcher", 1, 0, "AddonsModel", "no");
|
||||
qmlRegisterUncreatableType<PathListModel>("FlightGear.Launcher", 1, 0, "PathListMode", "no");
|
||||
|
||||
connect(m_config, &LaunchConfig::collect,
|
||||
this, &AddOnsController::collectArgs);
|
||||
}
|
||||
|
||||
QStringList AddOnsController::aircraftPaths() const
|
||||
PathListModel* AddOnsController::aircraftPaths() const
|
||||
{
|
||||
return m_aircraftPaths;
|
||||
}
|
||||
|
||||
QStringList AddOnsController::sceneryPaths() const
|
||||
PathListModel* AddOnsController::sceneryPaths() const
|
||||
{
|
||||
return m_sceneryPaths;
|
||||
}
|
||||
|
@ -119,6 +148,7 @@ QString AddOnsController::addAircraftPath() const
|
|||
}
|
||||
}
|
||||
|
||||
m_aircraftPaths->appendPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -206,6 +236,7 @@ QString AddOnsController::addSceneryPath() const
|
|||
}
|
||||
}
|
||||
|
||||
m_sceneryPaths->appendPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -227,37 +258,6 @@ void AddOnsController::openDirectory(QString path)
|
|||
QDesktopServices::openUrl(u);
|
||||
}
|
||||
|
||||
void AddOnsController::setAircraftPaths(QStringList aircraftPaths)
|
||||
{
|
||||
if (m_aircraftPaths == aircraftPaths)
|
||||
return;
|
||||
|
||||
m_aircraftPaths = aircraftPaths;
|
||||
emit aircraftPathsChanged(m_aircraftPaths);
|
||||
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("aircraft-paths", m_aircraftPaths);
|
||||
auto aircraftCache = LocalAircraftCache::instance();
|
||||
aircraftCache->setPaths(m_aircraftPaths);
|
||||
aircraftCache->scanDirs();
|
||||
}
|
||||
|
||||
void AddOnsController::setSceneryPaths(QStringList sceneryPaths)
|
||||
{
|
||||
if (m_sceneryPaths == sceneryPaths)
|
||||
return;
|
||||
|
||||
m_sceneryPaths = sceneryPaths;
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("scenery-paths", m_sceneryPaths);
|
||||
|
||||
flightgear::launcherSetSceneryPaths();
|
||||
|
||||
emit sceneryPathsChanged(m_sceneryPaths);
|
||||
}
|
||||
|
||||
void AddOnsController::setAddons(AddonsModel* addons)
|
||||
{
|
||||
|
||||
|
@ -342,3 +342,31 @@ void AddOnsController::onAddonsChanged()
|
|||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
||||
void AddOnsController::collectArgs()
|
||||
{
|
||||
// scenery paths
|
||||
Q_FOREACH(QString path, m_sceneryPaths->enabledPaths()) {
|
||||
m_config->setArg("fg-scenery", path);
|
||||
}
|
||||
|
||||
// aircraft paths
|
||||
Q_FOREACH(QString path, m_aircraftPaths->enabledPaths()) {
|
||||
m_config->setArg("fg-aircraft", path);
|
||||
}
|
||||
|
||||
// add-on module paths
|
||||
// we could query this directly from AddonsModel, but this is simpler right now
|
||||
QSettings settings;
|
||||
int size = settings.beginReadArray("addon-modules");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
|
||||
QString path = settings.value("path").toString();
|
||||
bool enable = settings.value("enable").toBool();
|
||||
if (enable) {
|
||||
m_config->setArg("addon", path);
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
class CatalogListModel;
|
||||
class AddonsModel;
|
||||
class LauncherMainWindow;
|
||||
class PathListModel;
|
||||
class LaunchConfig;
|
||||
|
||||
class AddOnsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QStringList aircraftPaths READ aircraftPaths WRITE setAircraftPaths NOTIFY aircraftPathsChanged)
|
||||
Q_PROPERTY(QStringList sceneryPaths READ sceneryPaths WRITE setSceneryPaths NOTIFY sceneryPathsChanged)
|
||||
Q_PROPERTY(PathListModel* aircraftPaths READ aircraftPaths CONSTANT)
|
||||
Q_PROPERTY(PathListModel* sceneryPaths READ sceneryPaths CONSTANT)
|
||||
Q_PROPERTY(QStringList modulePaths READ modulePaths WRITE setModulePaths NOTIFY modulePathsChanged)
|
||||
|
||||
Q_PROPERTY(CatalogListModel* catalogs READ catalogs CONSTANT)
|
||||
|
@ -22,10 +24,10 @@ class AddOnsController : public QObject
|
|||
Q_PROPERTY(bool showNoOfficialHangar READ showNoOfficialHangar NOTIFY showNoOfficialHangarChanged)
|
||||
|
||||
public:
|
||||
explicit AddOnsController(LauncherMainWindow *parent = nullptr);
|
||||
explicit AddOnsController(LauncherMainWindow *parent, LaunchConfig* config);
|
||||
|
||||
QStringList aircraftPaths() const;
|
||||
QStringList sceneryPaths() const;
|
||||
PathListModel* aircraftPaths() const;
|
||||
PathListModel* sceneryPaths() const;
|
||||
QStringList modulePaths() const;
|
||||
|
||||
CatalogListModel* catalogs() const
|
||||
|
@ -50,8 +52,6 @@ public:
|
|||
Q_INVOKABLE void officialCatalogAction(QString s);
|
||||
|
||||
signals:
|
||||
void aircraftPathsChanged(QStringList aircraftPaths);
|
||||
void sceneryPathsChanged(QStringList sceneryPaths);
|
||||
void modulePathsChanged(QStringList modulePaths);
|
||||
void modulesChanged();
|
||||
|
||||
|
@ -59,12 +59,12 @@ signals:
|
|||
void showNoOfficialHangarChanged();
|
||||
|
||||
public slots:
|
||||
void setAircraftPaths(QStringList aircraftPaths);
|
||||
void setSceneryPaths(QStringList sceneryPaths);
|
||||
void setModulePaths(QStringList modulePaths);
|
||||
void setAddons(AddonsModel* addons);
|
||||
void onAddonsChanged(void);
|
||||
|
||||
void collectArgs();
|
||||
|
||||
private:
|
||||
bool shouldShowOfficialCatalogMessage() const;
|
||||
void onCatalogsChanged();
|
||||
|
@ -72,9 +72,10 @@ private:
|
|||
LauncherMainWindow* m_launcher;
|
||||
CatalogListModel* m_catalogs = nullptr;
|
||||
AddonsModel* m_addonsModuleModel = nullptr;
|
||||
LaunchConfig* m_config = nullptr;
|
||||
|
||||
QStringList m_aircraftPaths;
|
||||
QStringList m_sceneryPaths;
|
||||
PathListModel* m_aircraftPaths = nullptr;
|
||||
PathListModel* m_sceneryPaths = nullptr;
|
||||
QStringList m_addonModulePaths;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ AddonsModel::AddonsModel(QObject* pr) :
|
|||
int roleValue = IdRole;
|
||||
|
||||
for (auto it = m_roles.begin(); it != m_roles.end(); ++it) {
|
||||
QByteArray name = (*it).toLocal8Bit();
|
||||
QByteArray name = it->toUtf8();
|
||||
m_roleToName[roleValue] = name;
|
||||
m_nameToRole[*it] = roleValue++;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
#include <QSharedPointer>
|
||||
#include <QSettings>
|
||||
|
||||
// Simgear
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
@ -38,10 +39,21 @@
|
|||
|
||||
#include "QmlAircraftInfo.hxx"
|
||||
|
||||
const int STANDARD_THUMBNAIL_HEIGHT = 128;
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
bool isPackageFailure(Delegate::StatusCode status)
|
||||
{
|
||||
switch (status) {
|
||||
case Delegate::STATUS_SUCCESS:
|
||||
case Delegate::STATUS_REFRESHED:
|
||||
case Delegate::STATUS_IN_PROGRESS:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class PackageDelegate : public simgear::pkg::Delegate
|
||||
{
|
||||
public:
|
||||
|
@ -57,18 +69,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override
|
||||
{
|
||||
if (aReason == STATUS_IN_PROGRESS) {
|
||||
// nothing to do
|
||||
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
|
||||
m_model->refreshPackages();
|
||||
} else {
|
||||
qWarning() << "failed refresh of"
|
||||
<< QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override;
|
||||
void startInstall(InstallRef aInstall) override
|
||||
{
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
|
@ -77,8 +78,8 @@ protected:
|
|||
|
||||
void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override
|
||||
{
|
||||
Q_UNUSED(bytes);
|
||||
Q_UNUSED(total);
|
||||
Q_UNUSED(bytes)
|
||||
Q_UNUSED(total)
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ protected:
|
|||
|
||||
void installStatusChanged(InstallRef aInstall, StatusCode aReason) override
|
||||
{
|
||||
Q_UNUSED(aReason);
|
||||
Q_UNUSED(aReason)
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
@ -115,44 +116,6 @@ protected:
|
|||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
||||
|
||||
virtual void dataForThumbnail(const std::string& aThumbnailUrl,
|
||||
size_t length, const uint8_t* bytes) override
|
||||
{
|
||||
QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes), length));
|
||||
if (img.isNull()) {
|
||||
qWarning() << "failed to load image data for URL:" <<
|
||||
QString::fromStdString(aThumbnailUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
QPixmap pix = QPixmap::fromImage(img);
|
||||
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
QString url = QString::fromStdString(aThumbnailUrl);
|
||||
m_model->m_downloadedPixmapCache.insert(url, pix);
|
||||
|
||||
// notify any affected items. Linear scan here avoids another map/dict structure.
|
||||
for (auto pkg : m_model->m_packages) {
|
||||
const size_t variantCount = pkg->variants().size();
|
||||
bool notifyChanged = false;
|
||||
|
||||
for (size_t v=0; v < variantCount; ++v) {
|
||||
const Package::Thumbnail& thumb(pkg->thumbnailForVariant(v));
|
||||
if (thumb.url == aThumbnailUrl) {
|
||||
notifyChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (notifyChanged) {
|
||||
QModelIndex mi = indexForPackage(pkg);
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
} // of packages iteration
|
||||
}
|
||||
|
||||
private:
|
||||
QModelIndex indexForPackage(const PackageRef& ref) const
|
||||
{
|
||||
|
@ -169,6 +132,22 @@ private:
|
|||
AircraftItemModel* m_model;
|
||||
};
|
||||
|
||||
void PackageDelegate::catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
|
||||
{
|
||||
if (aReason == STATUS_IN_PROGRESS) {
|
||||
// nothing to do
|
||||
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
|
||||
m_model->refreshPackages();
|
||||
} else {
|
||||
qWarning() << "failed refresh of"
|
||||
<< QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief AircraftItemModel::AircraftItemModel
|
||||
/// \param pr
|
||||
///
|
||||
AircraftItemModel::AircraftItemModel(QObject* pr) :
|
||||
QAbstractListModel(pr)
|
||||
{
|
||||
|
@ -179,6 +158,8 @@ AircraftItemModel::AircraftItemModel(QObject* pr) :
|
|||
this, &AircraftItemModel::onScanAddedItems);
|
||||
connect(cache, &LocalAircraftCache::cleared,
|
||||
this, &AircraftItemModel::onLocalCacheCleared);
|
||||
|
||||
loadFavourites();
|
||||
}
|
||||
|
||||
AircraftItemModel::~AircraftItemModel()
|
||||
|
@ -260,8 +241,14 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
|||
return m_delegateStates.at(row).variant;
|
||||
}
|
||||
|
||||
if (role == AircraftIsFavouriteRole) {
|
||||
// recursive call here, hope that's okay
|
||||
const auto uri = data(index, AircraftURIRole).toUrl();
|
||||
return m_favourites.contains(uri);
|
||||
}
|
||||
|
||||
if (row >= m_cachedLocalAircraftCount) {
|
||||
quint32 packageIndex = row - m_cachedLocalAircraftCount;
|
||||
quint32 packageIndex = static_cast<quint32>(row - m_cachedLocalAircraftCount);
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
InstallRef ex = pkg->existingInstall();
|
||||
|
||||
|
@ -309,16 +296,12 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
|||
}
|
||||
|
||||
return item->description;
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
return item->thumbnail();
|
||||
} else if (role == AircraftPathRole) {
|
||||
return item->path;
|
||||
} else if (role == AircraftAuthorsRole) {
|
||||
return item->authors;
|
||||
} else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
|
||||
return item->ratings[role - AircraftRatingRole];
|
||||
} else if (role == AircraftThumbnailRole) {
|
||||
return item->thumbnail();
|
||||
} else if (role == AircraftPackageIdRole) {
|
||||
// can we fake an ID? otherwise fall through to a null variant
|
||||
} else if (role == AircraftPackageStatusRole) {
|
||||
|
@ -354,10 +337,6 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
|||
|
||||
QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const
|
||||
{
|
||||
if (role == Qt::DecorationRole) {
|
||||
role = AircraftThumbnailRole;
|
||||
}
|
||||
|
||||
if (role >= AircraftVariantDescriptionRole) {
|
||||
int variantIndex = role - AircraftVariantDescriptionRole;
|
||||
QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
|
||||
|
@ -393,6 +372,10 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega
|
|||
return LocalAircraftCache::PackageUpdateAvailable;
|
||||
}
|
||||
|
||||
const auto status = i->status();
|
||||
if (isPackageFailure(status))
|
||||
return LocalAircraftCache::PackageInstallFailed;
|
||||
|
||||
return LocalAircraftCache::PackageInstalled;
|
||||
} else {
|
||||
return LocalAircraftCache::PackageNotInstalled;
|
||||
|
@ -401,8 +384,6 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega
|
|||
// this value wants the number of aditional variants, i.e not
|
||||
// including the primary. Hence the -1 term.
|
||||
return static_cast<quint32>(item->variants().size() - 1);
|
||||
} else if (role == AircraftThumbnailRole) {
|
||||
return packageThumbnail(item, state);
|
||||
} else if (role == AircraftAuthorsRole) {
|
||||
std::string authors = item->getLocalisedProp("author", state.variant);
|
||||
if (!authors.empty()) {
|
||||
|
@ -436,42 +417,6 @@ QVariant AircraftItemModel::packageRating(const PackageRef& p, int ratingIndex)
|
|||
return LocalAircraftCache::ratingFromProperties(p->properties()->getChild("rating"), ratingIndex);
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState& ds, bool download) const
|
||||
{
|
||||
const Package::Thumbnail& thumb(p->thumbnailForVariant(ds.variant));
|
||||
if (thumb.url.empty()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString urlQString(QString::fromStdString(thumb.url));
|
||||
if (m_downloadedPixmapCache.contains(urlQString)) {
|
||||
// cache hit, easy
|
||||
return m_downloadedPixmapCache.value(urlQString);
|
||||
}
|
||||
|
||||
// check the on-disk store.
|
||||
InstallRef ex = p->existingInstall();
|
||||
if (ex.valid()) {
|
||||
SGPath thumbPath = ex->path() / thumb.path;
|
||||
if (thumbPath.exists()) {
|
||||
QPixmap pix;
|
||||
pix.load(QString::fromStdString(thumbPath.utf8Str()));
|
||||
// resize to the standard size
|
||||
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
||||
}
|
||||
m_downloadedPixmapCache[urlQString] = pix;
|
||||
return pix;
|
||||
}
|
||||
} // of have existing install
|
||||
|
||||
if (download) {
|
||||
m_packageRoot->requestThumbnailData(thumb.url);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
int row = index.row();
|
||||
|
@ -485,6 +430,18 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
m_delegateStates[row].variant = newValue;
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
} else if (role == AircraftIsFavouriteRole) {
|
||||
bool f = value.toBool();
|
||||
const auto uri = data(index, AircraftURIRole).toUrl();
|
||||
const auto cur = m_favourites.contains(uri);
|
||||
if (f && !cur) {
|
||||
m_favourites.append(uri);
|
||||
} else if (!f && cur) {
|
||||
m_favourites.removeOne(uri);
|
||||
}
|
||||
|
||||
saveFavourites();
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -500,12 +457,12 @@ QHash<int, QByteArray> AircraftItemModel::roleNames() const
|
|||
result[AircraftAuthorsRole] = "authors";
|
||||
result[AircraftVariantCountRole] = "variantCount";
|
||||
result[AircraftLongDescriptionRole] = "description";
|
||||
result[AircraftThumbnailRole] = "thumbnail";
|
||||
result[AircraftPackageSizeRole] = "packageSizeBytes";
|
||||
result[AircraftPackageStatusRole] = "packageStatus";
|
||||
|
||||
result[AircraftInstallDownloadedSizeRole] = "downloadedBytes";
|
||||
result[AircraftVariantRole] = "activeVariant";
|
||||
result[AircraftIsFavouriteRole] = "favourite";
|
||||
|
||||
result[AircraftStatusRole] = "aircraftStatus";
|
||||
result[AircraftMinVersionRole] = "requiredFGVersion";
|
||||
|
@ -625,7 +582,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
QString ident = uri.path();
|
||||
PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
|
||||
if (pkg) {
|
||||
int variantIndex = pkg->indexOfVariant(ident.toStdString());
|
||||
const auto variantIndex = pkg->indexOfVariant(ident.toStdString());
|
||||
return QString::fromStdString(pkg->nameForVariant(variantIndex));
|
||||
}
|
||||
} else {
|
||||
|
@ -637,7 +594,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
|
||||
void AircraftItemModel::onScanAddedItems(int addedCount)
|
||||
{
|
||||
Q_UNUSED(addedCount);
|
||||
Q_UNUSED(addedCount)
|
||||
const auto items = LocalAircraftCache::instance()->allItems();
|
||||
const int newItemCount = items.size() - m_cachedLocalAircraftCount;
|
||||
const int firstRow = m_cachedLocalAircraftCount;
|
||||
|
@ -696,7 +653,7 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
|
|||
return true; // local file, always runnable
|
||||
}
|
||||
|
||||
quint32 packageIndex = index.row() - m_cachedLocalAircraftCount;
|
||||
quint32 packageIndex = static_cast<quint32>(index.row() - m_cachedLocalAircraftCount);
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
InstallRef ex = pkg->existingInstall();
|
||||
if (!ex.valid()) {
|
||||
|
@ -706,4 +663,21 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
|
|||
return !ex->isDownloading();
|
||||
}
|
||||
|
||||
void AircraftItemModel::loadFavourites()
|
||||
{
|
||||
m_favourites.clear();
|
||||
QSettings settings;
|
||||
Q_FOREACH(auto v, settings.value("favourite-aircraft").toList()) {
|
||||
m_favourites.append(v.toUrl());
|
||||
}
|
||||
}
|
||||
|
||||
void AircraftItemModel::saveFavourites()
|
||||
{
|
||||
QVariantList favs;
|
||||
Q_FOREACH(auto u, m_favourites) {
|
||||
favs.append(u);
|
||||
}
|
||||
QSettings settings;
|
||||
settings.setValue("favourite-aircraft", favs);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ const int AircraftURIRole = Qt::UserRole + 14;
|
|||
const int AircraftIsHelicopterRole = Qt::UserRole + 16;
|
||||
const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
|
||||
const int AircraftPackageRefRole = Qt::UserRole + 19;
|
||||
const int AircraftThumbnailRole = Qt::UserRole + 20;
|
||||
const int AircraftIsFavouriteRole = Qt::UserRole + 20;
|
||||
|
||||
const int AircraftStatusRole = Qt::UserRole + 22;
|
||||
const int AircraftMinVersionRole = Qt::UserRole + 23;
|
||||
|
@ -146,15 +146,20 @@ private:
|
|||
void installSucceeded(QModelIndex index);
|
||||
void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason);
|
||||
|
||||
void loadFavourites();
|
||||
void saveFavourites();
|
||||
|
||||
private:
|
||||
PackageDelegate* m_delegate = nullptr;
|
||||
|
||||
QVector<DelegateState> m_delegateStates;
|
||||
|
||||
simgear::pkg::RootRef m_packageRoot;
|
||||
simgear::pkg::PackageList m_packages;
|
||||
|
||||
mutable QHash<QString, QPixmap> m_downloadedPixmapCache;
|
||||
|
||||
QVector<QUrl> m_favourites;
|
||||
int m_cachedLocalAircraftCount = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // of FG_GUI_AIRCRAFT_MODEL
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "AircraftSearchFilterModel.hxx"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
|
||||
#include "AircraftModel.hxx"
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
|
@ -11,6 +14,8 @@ AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source)
|
|||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
|
||||
|
||||
// important we sort on the primary name role and not Qt::DisplayRole
|
||||
// otherwise the aircraft jump when switching variant
|
||||
setSortRole(AircraftVariantDescriptionRole);
|
||||
|
@ -110,6 +115,15 @@ void AircraftProxyModel::setHaveUpdateFilterEnabled(bool e)
|
|||
invalidate();
|
||||
}
|
||||
|
||||
void AircraftProxyModel::setShowFavourites(bool e)
|
||||
{
|
||||
if (e == m_onlyShowFavourites)
|
||||
return;
|
||||
|
||||
m_onlyShowFavourites = e;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
@ -142,6 +156,11 @@ bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
|||
}
|
||||
}
|
||||
|
||||
if (m_onlyShowFavourites) {
|
||||
if (!index.data(AircraftIsFavouriteRole).toBool())
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there is no search active, i.e we are browsing, we might apply the
|
||||
// ratings filter.
|
||||
if (m_filterString.isEmpty() && !m_onlyShowInstalled && m_ratingsFilter) {
|
||||
|
@ -187,3 +206,28 @@ bool AircraftProxyModel::filterAircraft(const QModelIndex &sourceIndex) const
|
|||
return false;
|
||||
}
|
||||
|
||||
void AircraftProxyModel::loadRatingsSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
m_ratingsFilter = settings.value("enable-ratings-filter", true).toBool();
|
||||
QVariantList vRatings = settings.value("ratings-filter").toList();
|
||||
if (vRatings.size() == 4) {
|
||||
for (int i=0; i < 4; ++i) {
|
||||
m_ratings[i] = vRatings.at(i).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void AircraftProxyModel::saveRatingsSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
settings.setValue("enable-ratings-filter", m_ratingsFilter);
|
||||
QVariantList vRatings;
|
||||
for (int i=0; i < 4; ++i) {
|
||||
vRatings.append(m_ratings.at(i));
|
||||
}
|
||||
settings.setValue("ratings-filter", vRatings);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,10 @@ public:
|
|||
|
||||
Q_INVOKABLE void selectVariantForAircraftURI(QUrl uri);
|
||||
|
||||
Q_INVOKABLE void loadRatingsSettings();
|
||||
|
||||
Q_INVOKABLE void saveRatingsSettings();
|
||||
|
||||
QList<int> ratings() const
|
||||
{
|
||||
return m_ratings;
|
||||
|
@ -56,6 +60,8 @@ public slots:
|
|||
void setInstalledFilterEnabled(bool e);
|
||||
|
||||
void setHaveUpdateFilterEnabled(bool e);
|
||||
|
||||
void setShowFavourites(bool e);
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
|
@ -65,6 +71,7 @@ private:
|
|||
bool m_ratingsFilter = true;
|
||||
bool m_onlyShowInstalled = false;
|
||||
bool m_onlyShowWithUpdate = false;
|
||||
bool m_onlyShowFavourites = false;
|
||||
|
||||
QList<int> m_ratings;
|
||||
QString m_filterString;
|
||||
|
|
|
@ -124,6 +124,10 @@ if (HAVE_QT)
|
|||
AddonsModel.hxx
|
||||
PixmapImageItem.cxx
|
||||
PixmapImageItem.hxx
|
||||
PathListModel.cxx
|
||||
PathListModel.hxx
|
||||
CarriersLocationModel.cxx
|
||||
CarriersLocationModel.hxx
|
||||
${uic_sources}
|
||||
${qrc_sources}
|
||||
${qml_sources})
|
||||
|
@ -168,6 +172,8 @@ if (HAVE_QT)
|
|||
RouteDiagram.hxx
|
||||
ModelDataExtractor.cxx
|
||||
ModelDataExtractor.hxx
|
||||
HoverArea.cxx
|
||||
HoverArea.hxx
|
||||
)
|
||||
|
||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||
|
|
132
src/GUI/CarriersLocationModel.cxx
Normal file
132
src/GUI/CarriersLocationModel.cxx
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include "CarriersLocationModel.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
#include "AIModel/AIManager.hxx"
|
||||
|
||||
CarriersLocationModel::CarriersLocationModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
SGPropertyNode_ptr localRoot(new SGPropertyNode);
|
||||
FGAIManager::registerScenarios(localRoot);
|
||||
|
||||
// this code encodes some scenario structre, sorry
|
||||
for (auto s : localRoot->getNode("sim/ai/scenarios")->getChildren("scenario")) {
|
||||
const std::string scenarioId = s->getStringValue("id");
|
||||
for (auto c : s->getChildren("carrier")) {
|
||||
processCarrier(scenarioId, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CarriersLocationModel::processCarrier(const string &scenario, SGPropertyNode_ptr carrierNode)
|
||||
{
|
||||
const auto name = QString::fromStdString(carrierNode->getStringValue("name"));
|
||||
const auto pennant = QString::fromStdString(carrierNode->getStringValue("pennant-number"));
|
||||
const auto tacan = QString::fromStdString(carrierNode->getStringValue("TACAN-channel-ID"));
|
||||
SGGeod geod = SGGeod::fromDeg(carrierNode->getDoubleValue("longitude"),
|
||||
carrierNode->getDoubleValue("latitude"));
|
||||
|
||||
QStringList parkings;
|
||||
for (auto c : carrierNode->getChildren("parking-pos")) {
|
||||
parkings.append(QString::fromStdString(c->getStringValue()));
|
||||
}
|
||||
|
||||
mCarriers.push_back(Carrier{
|
||||
QString::fromStdString(scenario),
|
||||
pennant,
|
||||
name,
|
||||
geod,
|
||||
tacan,
|
||||
parkings
|
||||
});
|
||||
}
|
||||
|
||||
int CarriersLocationModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
// For list models only the root node (an invalid parent) should return the list's size. For all
|
||||
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
|
||||
return static_cast<int>(mCarriers.size());
|
||||
}
|
||||
|
||||
QVariant CarriersLocationModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
const auto& c = mCarriers.at(static_cast<size_t>(index.row()));
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case NameRole: return c.mName;
|
||||
// case GeodRole: return QVariant::fromValue(c.mInitialLocation);
|
||||
case IdentRole: return c.mCallsign;
|
||||
case IconRole: return QPixmap(":/svg/aircraft-carrier");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> CarriersLocationModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> result = QAbstractListModel::roleNames();
|
||||
|
||||
result[GeodRole] = "geod";
|
||||
result[GuidRole] = "guid";
|
||||
result[IdentRole] = "ident";
|
||||
result[NameRole] = "name";
|
||||
result[IconRole] = "icon";
|
||||
result[TypeRole] = "type";
|
||||
result[NavFrequencyRole] = "frequency";
|
||||
return result;
|
||||
}
|
||||
|
||||
int CarriersLocationModel::indexOf(const QString name) const
|
||||
{
|
||||
auto it = std::find_if(mCarriers.begin(), mCarriers.end(), [name]
|
||||
(const Carrier& carrier)
|
||||
{ return name == carrier.mName || name == carrier.mCallsign; });
|
||||
if (it == mCarriers.end())
|
||||
return -1;
|
||||
|
||||
return static_cast<int>(std::distance(mCarriers.begin(), it));
|
||||
}
|
||||
|
||||
SGGeod CarriersLocationModel::geodForIndex(int index) const
|
||||
{
|
||||
const auto uIndex = static_cast<size_t>(index);
|
||||
if ((index < 0) || (uIndex >= mCarriers.size())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& c = mCarriers.at(uIndex);
|
||||
return c.mInitialLocation;
|
||||
}
|
||||
|
||||
QString CarriersLocationModel::pennantForIndex(int index) const
|
||||
{
|
||||
const auto uIndex = static_cast<size_t>(index);
|
||||
if ((index < 0) || (uIndex >= mCarriers.size())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& c = mCarriers.at(uIndex);
|
||||
return c.mCallsign;
|
||||
}
|
||||
|
||||
QStringList CarriersLocationModel::parkingsForIndex(int index) const
|
||||
{
|
||||
const auto uIndex = static_cast<size_t>(index);
|
||||
if ((index < 0) || (uIndex >= mCarriers.size())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& c = mCarriers.at(uIndex);
|
||||
return c.mParkings;
|
||||
}
|
61
src/GUI/CarriersLocationModel.hxx
Normal file
61
src/GUI/CarriersLocationModel.hxx
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef CARRIERSMODEL_H
|
||||
#define CARRIERSMODEL_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class CarriersLocationModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CarriersLocationModel(QObject *parent = nullptr);
|
||||
|
||||
// Basic functionality:
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
// copied from NavaidSearchModel
|
||||
enum Roles {
|
||||
GeodRole = Qt::UserRole + 1,
|
||||
GuidRole = Qt::UserRole + 2,
|
||||
IdentRole = Qt::UserRole + 3,
|
||||
NameRole = Qt::UserRole + 4,
|
||||
IconRole = Qt::UserRole + 5,
|
||||
TypeRole = Qt::UserRole + 6,
|
||||
NavFrequencyRole = Qt::UserRole + 7
|
||||
};
|
||||
|
||||
int indexOf(const QString name) const;
|
||||
|
||||
SGGeod geodForIndex(int index) const;
|
||||
QString pennantForIndex(int index) const;
|
||||
|
||||
QStringList parkingsForIndex(int index) const;
|
||||
private:
|
||||
|
||||
struct Carrier
|
||||
{
|
||||
QString mScenario; // scenario ID for loading
|
||||
QString mCallsign; // pennant-number
|
||||
QString mName;
|
||||
SGGeod mInitialLocation;
|
||||
// icon?
|
||||
QString mTACAN;
|
||||
QStringList mParkings;
|
||||
};
|
||||
|
||||
using CarrierVec = std::vector<Carrier>;
|
||||
CarrierVec mCarriers;
|
||||
|
||||
void processCarrier(const std::string& scenario, SGPropertyNode_ptr carrierNode);
|
||||
};
|
||||
|
||||
#endif // CARRIERSMODEL_H
|
|
@ -93,13 +93,13 @@ void CatalogListModel::resetData()
|
|||
|
||||
int CatalogListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return m_catalogs.size();
|
||||
Q_UNUSED(parent)
|
||||
return static_cast<int>(m_catalogs.size());
|
||||
}
|
||||
|
||||
QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
simgear::pkg::CatalogRef cat = m_catalogs.at(index.row());
|
||||
|
||||
const auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
|
||||
if (role == Qt::DisplayRole) {
|
||||
QString name = QString::fromStdString(cat->name());
|
||||
QString desc;
|
||||
|
@ -139,6 +139,8 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
|||
return translateStatusForCatalog(cat);
|
||||
} else if (role == CatalogIsNewlyAdded) {
|
||||
return (cat == m_newlyAddedCatalog);
|
||||
} else if (role == CatalogEnabled) {
|
||||
return cat->isUserEnabled();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -146,13 +148,18 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
|||
|
||||
bool CatalogListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
|
||||
if (role == CatalogEnabled) {
|
||||
cat->setUserEnabled(value.toBool());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags CatalogListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags r = Qt::ItemIsSelectable;
|
||||
const auto cat = m_catalogs.at(index.row());
|
||||
const auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
|
||||
if (cat->isEnabled()) {
|
||||
r |= Qt::ItemIsEnabled;
|
||||
}
|
||||
|
@ -168,28 +175,27 @@ QHash<int, QByteArray> CatalogListModel::roleNames() const
|
|||
result[CatalogNameRole] = "name";
|
||||
result[CatalogStatusRole] = "status";
|
||||
result[CatalogIsNewlyAdded] = "isNewlyAdded";
|
||||
|
||||
result[CatalogEnabled] = "enabled";
|
||||
return result;
|
||||
}
|
||||
|
||||
void CatalogListModel::removeCatalog(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= m_catalogs.size())) {
|
||||
if ((index < 0) || (index >= static_cast<int>(m_catalogs.size()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string removeId = m_catalogs.at(index)->id();
|
||||
const std::string removeId = m_catalogs.at(static_cast<size_t>(index))->id();
|
||||
m_packageRoot->removeCatalogById(removeId);
|
||||
resetData();
|
||||
}
|
||||
|
||||
void CatalogListModel::refreshCatalog(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= m_catalogs.size())) {
|
||||
if ((index < 0) || (index >= static_cast<int>(m_catalogs.size()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_catalogs.at(index)->refresh();
|
||||
m_catalogs.at(static_cast<size_t>(index))->refresh();
|
||||
}
|
||||
|
||||
void CatalogListModel::installDefaultCatalog()
|
||||
|
@ -221,7 +227,7 @@ int CatalogListModel::indexOf(QUrl url)
|
|||
if (it == m_catalogs.end())
|
||||
return -1;
|
||||
|
||||
return std::distance(m_catalogs.begin(), it);
|
||||
return static_cast<int>(std::distance(m_catalogs.begin(), it));
|
||||
}
|
||||
|
||||
void CatalogListModel::finalizeAddCatalog()
|
||||
|
@ -237,7 +243,7 @@ void CatalogListModel::finalizeAddCatalog()
|
|||
return;
|
||||
}
|
||||
|
||||
const int row = std::distance(m_catalogs.begin(), it);
|
||||
const int row = static_cast<int>(std::distance(m_catalogs.begin(), it));
|
||||
m_newlyAddedCatalog.clear();
|
||||
emit isAddingCatalogChanged();
|
||||
emit statusOfAddingCatalogChanged();
|
||||
|
|
|
@ -38,6 +38,7 @@ const int CatalogStatusRole = Qt::UserRole + 5;
|
|||
const int CatalogDescriptionRole = Qt::UserRole + 6;
|
||||
const int CatalogNameRole = Qt::UserRole + 7;
|
||||
const int CatalogIsNewlyAdded = Qt::UserRole + 8;
|
||||
const int CatalogEnabled = Qt::UserRole + 9;
|
||||
|
||||
class CatalogDelegate;
|
||||
|
||||
|
|
41
src/GUI/FGFontCache.cxx
Normal file → Executable file
41
src/GUI/FGFontCache.cxx
Normal file → Executable file
|
@ -13,20 +13,17 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <config.h>
|
||||
|
||||
#include "FGFontCache.hxx"
|
||||
|
||||
#include <plib/fnt.h>
|
||||
// this is our one in 3rdparty
|
||||
#include "fnt.h"
|
||||
|
||||
#include <plib/pu.h>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
|
@ -211,26 +208,18 @@ FGFontCache::getfntpath(const std::string& name)
|
|||
|
||||
bool FGFontCache::initializeFonts()
|
||||
{
|
||||
static std::string fontext("txf");
|
||||
static std::string fontext(".txf");
|
||||
init();
|
||||
std::string fontPath = _path.local8BitStr();
|
||||
ulDir* fontdir = ulOpenDir(fontPath.c_str());
|
||||
if (!fontdir)
|
||||
return false;
|
||||
const ulDirEnt *dirEntry;
|
||||
while ((dirEntry = ulReadDir(fontdir)) != 0) {
|
||||
SGPath path(_path);
|
||||
path.append(dirEntry->d_name);
|
||||
if (path.extension() == fontext) {
|
||||
fntTexFont* f = new fntTexFont;
|
||||
std::string ps = path.local8BitStr();
|
||||
if (f->load(ps.c_str()))
|
||||
_texFonts[std::string(dirEntry->d_name)] = f;
|
||||
else
|
||||
delete f;
|
||||
}
|
||||
|
||||
auto dir = simgear::Dir(_path);
|
||||
for (auto p : dir.children(simgear::Dir::TYPE_FILE, fontext)) {
|
||||
fntTexFont* f = new fntTexFont;
|
||||
if (f->load(p))
|
||||
_texFonts[p.file()] = f;
|
||||
else
|
||||
delete f;
|
||||
}
|
||||
ulCloseDir(fontdir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
33
src/GUI/HoverArea.cxx
Normal file
33
src/GUI/HoverArea.cxx
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "HoverArea.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QQuickWindow>
|
||||
|
||||
HoverArea::HoverArea()
|
||||
{
|
||||
connect(this, &QQuickItem::windowChanged, [this](QQuickWindow* win) {
|
||||
if (win) {
|
||||
win->installEventFilter(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool HoverArea::eventFilter(QObject *sender, QEvent *event)
|
||||
{
|
||||
Q_UNUSED(sender)
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
QMouseEvent* me = static_cast<QMouseEvent*>(event);
|
||||
const auto local = mapFromScene(me->pos());
|
||||
const bool con = contains(local);
|
||||
if (con != m_containsMouse) {
|
||||
m_containsMouse = con;
|
||||
emit containsMouseChanged(con);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
31
src/GUI/HoverArea.hxx
Normal file
31
src/GUI/HoverArea.hxx
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef HOVERAREA_HXX
|
||||
#define HOVERAREA_HXX
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
class HoverArea : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged);
|
||||
|
||||
public:
|
||||
HoverArea();
|
||||
|
||||
bool containsMouse() const
|
||||
{
|
||||
return m_containsMouse;
|
||||
}
|
||||
|
||||
signals:
|
||||
|
||||
void containsMouseChanged(bool containsMouse);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* sender, QEvent* event) override;
|
||||
|
||||
private:
|
||||
bool m_containsMouse = false;
|
||||
};
|
||||
|
||||
#endif // HOVERAREA_HXX
|
|
@ -52,6 +52,9 @@
|
|||
#include "NavaidSearchModel.hxx"
|
||||
#include "FlightPlanController.hxx"
|
||||
#include "ModelDataExtractor.hxx"
|
||||
#include "CarriersLocationModel.hxx"
|
||||
#include "SetupRootDialog.hxx"
|
||||
#include "HoverArea.hxx"
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
|
@ -90,6 +93,9 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) :
|
|||
|
||||
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
|
||||
m_favouriteAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
m_favouriteAircraftModel->setShowFavourites(true);
|
||||
|
||||
m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this);
|
||||
|
||||
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
|
||||
|
@ -102,14 +108,12 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) :
|
|||
this, &LauncherController::updateSelectedAircraft);
|
||||
|
||||
QSettings settings;
|
||||
LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList());
|
||||
LocalAircraftCache::instance()->scanDirs();
|
||||
m_aircraftModel->setPackageRoot(globals->packageRoot());
|
||||
|
||||
m_aircraftGridMode = settings.value("aircraftGridMode").toBool();
|
||||
|
||||
m_subsystemIdleTimer = new QTimer(this);
|
||||
m_subsystemIdleTimer->setInterval(10);
|
||||
m_subsystemIdleTimer->setInterval(5);
|
||||
connect(m_subsystemIdleTimer, &QTimer::timeout, []()
|
||||
{globals->get_subsystem_mgr()->update(0.0);});
|
||||
m_subsystemIdleTimer->start();
|
||||
|
@ -144,6 +148,7 @@ void LauncherController::initQML()
|
|||
qmlRegisterUncreatableType<MPServersModel>("FlightGear.Launcher", 1, 0, "MPServers", "Singleton API");
|
||||
|
||||
qmlRegisterType<NavaidSearchModel>("FlightGear", 1, 0, "NavaidSearch");
|
||||
qmlRegisterType<CarriersLocationModel>("FlightGear", 1, 0, "CarriersModel");
|
||||
|
||||
qmlRegisterUncreatableType<Units>("FlightGear", 1, 0, "Units", "Only for enum");
|
||||
qmlRegisterType<UnitsModel>("FlightGear", 1, 0, "UnitsModel");
|
||||
|
@ -166,6 +171,7 @@ void LauncherController::initQML()
|
|||
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
|
||||
qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram");
|
||||
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
|
||||
qmlRegisterType<HoverArea>("FlightGear", 1, 0, "HoverArea");
|
||||
|
||||
qmlRegisterType<ModelDataExtractor>("FlightGear", 1, 0, "ModelDataExtractor");
|
||||
|
||||
|
@ -185,6 +191,7 @@ void LauncherController::setInAppMode()
|
|||
m_inAppMode = true;
|
||||
m_keepRunningInAppMode = true;
|
||||
m_appModeResult = true;
|
||||
emit inAppChanged();
|
||||
}
|
||||
|
||||
bool LauncherController::keepRunningInAppMode() const
|
||||
|
@ -280,31 +287,6 @@ void LauncherController::collectAircraftArgs()
|
|||
m_config->setArg("state", state);
|
||||
}
|
||||
}
|
||||
|
||||
// scenery paths
|
||||
QSettings settings;
|
||||
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
|
||||
m_config->setArg("fg-scenery", path);
|
||||
}
|
||||
|
||||
// aircraft paths
|
||||
Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) {
|
||||
m_config->setArg("fg-aircraft", path);
|
||||
}
|
||||
|
||||
// add-on module paths
|
||||
int size = settings.beginReadArray("addon-modules");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
|
||||
QString path = settings.value("path").toString();
|
||||
bool enable = settings.value("enable").toBool();
|
||||
if (enable) {
|
||||
m_config->setArg("addon", path);
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
}
|
||||
|
||||
void LauncherController::saveAircraft()
|
||||
|
@ -722,6 +704,20 @@ QVariantList LauncherController::defaultSplashUrls() const
|
|||
return urls;
|
||||
}
|
||||
|
||||
QVariant LauncherController::loadUISetting(QString name, QVariant defaultValue) const
|
||||
{
|
||||
QSettings settings;
|
||||
if (!settings.contains(name))
|
||||
return defaultValue;
|
||||
return settings.value(name);
|
||||
}
|
||||
|
||||
void LauncherController::saveUISetting(QString name, QVariant value) const
|
||||
{
|
||||
QSettings settings;
|
||||
settings.setValue(name, value);
|
||||
}
|
||||
|
||||
void LauncherController::onAircraftInstalledCompleted(QModelIndex index)
|
||||
{
|
||||
maybeUpdateSelectedAircraft(index);
|
||||
|
@ -777,7 +773,7 @@ void LauncherController::requestChangeDataPath()
|
|||
{
|
||||
QString currentLocText;
|
||||
QSettings settings;
|
||||
QString root = settings.value("fg-root").toString();
|
||||
QString root = settings.value(SetupRootDialog::rootPathKey()).toString();
|
||||
if (root.isNull()) {
|
||||
currentLocText = tr("Currently the built-in data files are being used");
|
||||
}
|
||||
|
@ -801,12 +797,7 @@ void LauncherController::requestChangeDataPath()
|
|||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QSettings settings;
|
||||
// set the option to the magic marker value
|
||||
settings.setValue("fg-root", "!ask");
|
||||
} // scope the ensure settings are written nicely
|
||||
|
||||
SetupRootDialog::askRootOnNextLaunch();
|
||||
flightgear::restartTheApp();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ class LauncherController : public QObject
|
|||
Q_PROPERTY(AircraftProxyModel* aircraftWithUpdatesModel MEMBER m_aircraftWithUpdatesModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* favouriteAircraftModel MEMBER m_favouriteAircraftModel CONSTANT)
|
||||
|
||||
Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT)
|
||||
|
||||
|
@ -85,6 +86,8 @@ class LauncherController : public QObject
|
|||
Q_PROPERTY(QSize minimumWindowSize READ minWindowSize WRITE setMinWindowSize NOTIFY minWindowSizeChanged)
|
||||
|
||||
Q_PROPERTY(QUrl flyIconUrl READ flyIconUrl NOTIFY selectedAircraftChanged)
|
||||
|
||||
Q_PROPERTY(bool inAppMode READ inApp NOTIFY inAppChanged)
|
||||
|
||||
Q_PROPERTY(bool aircraftGridMode READ aircraftGridMode WRITE setAircraftGridMode NOTIFY aircraftGridModeChanged)
|
||||
public:
|
||||
|
@ -142,7 +145,8 @@ public:
|
|||
// used on the summary screen
|
||||
Q_INVOKABLE QVariantList defaultSplashUrls() const;
|
||||
|
||||
|
||||
Q_INVOKABLE QVariant loadUISetting(QString name, QVariant defaultValue) const;
|
||||
Q_INVOKABLE void saveUISetting(QString name, QVariant value) const;
|
||||
|
||||
LaunchConfig* config() const
|
||||
{ return m_config; }
|
||||
|
@ -193,6 +197,10 @@ public:
|
|||
return m_aircraftGridMode;
|
||||
}
|
||||
|
||||
bool inApp() const
|
||||
{
|
||||
return m_inAppMode;
|
||||
}
|
||||
signals:
|
||||
|
||||
void selectedAircraftChanged(QUrl selectedAircraft);
|
||||
|
@ -207,6 +215,7 @@ signals:
|
|||
|
||||
void aircraftGridModeChanged(bool aircraftGridMode);
|
||||
|
||||
void inAppChanged();
|
||||
public slots:
|
||||
void setSelectedAircraft(QUrl selectedAircraft);
|
||||
|
||||
|
@ -261,6 +270,8 @@ private:
|
|||
AircraftProxyModel* m_aircraftSearchModel;
|
||||
AircraftProxyModel* m_browseAircraftModel;
|
||||
AircraftProxyModel* m_aircraftWithUpdatesModel;
|
||||
AircraftProxyModel* m_favouriteAircraftModel;
|
||||
|
||||
MPServersModel* m_serversModel = nullptr;
|
||||
LocationController* m_location = nullptr;
|
||||
FlightPlanController* m_flightPlan = nullptr;
|
||||
|
|
|
@ -68,9 +68,8 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
connect(qa, &QAction::triggered, m_controller, &LauncherController::quit);
|
||||
|
||||
m_controller->initialRestoreSettings();
|
||||
flightgear::launcherSetSceneryPaths();
|
||||
|
||||
auto addOnsCtl = new AddOnsController(this);
|
||||
auto addOnsCtl = new AddOnsController(this, m_controller->config());
|
||||
|
||||
////////////
|
||||
#if defined(Q_OS_WIN)
|
||||
|
|
|
@ -177,23 +177,6 @@ void AircraftItem::toDataStream(QDataStream& ds) const
|
|||
ds << tags;
|
||||
}
|
||||
|
||||
QPixmap AircraftItem::thumbnail(bool loadIfRequired) const
|
||||
{
|
||||
if (m_thumbnail.isNull() && loadIfRequired) {
|
||||
QFileInfo info(path);
|
||||
QDir dir = info.dir();
|
||||
if (dir.exists(thumbnailPath)) {
|
||||
m_thumbnail.load(dir.filePath(thumbnailPath));
|
||||
// resize to the standard size
|
||||
if (m_thumbnail.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||
m_thumbnail = m_thumbnail.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_thumbnail;
|
||||
}
|
||||
|
||||
int AircraftItem::indexOfVariant(QUrl uri) const
|
||||
{
|
||||
const QString path = uri.toLocalFile();
|
||||
|
@ -253,6 +236,7 @@ public:
|
|||
{
|
||||
m_done = true;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void addedItems();
|
||||
|
||||
|
@ -437,12 +421,11 @@ void LocalAircraftCache::scanDirs()
|
|||
|
||||
QStringList dirs = m_paths;
|
||||
|
||||
Q_FOREACH(SGPath ap, globals->get_aircraft_paths()) {
|
||||
for (SGPath ap : globals->get_aircraft_paths()) {
|
||||
dirs << QString::fromStdString(ap.utf8Str());
|
||||
}
|
||||
|
||||
SGPath rootAircraft(globals->get_fg_root());
|
||||
rootAircraft.append("Aircraft");
|
||||
SGPath rootAircraft = globals->get_fg_root() / "Aircraft";
|
||||
dirs << QString::fromStdString(rootAircraft.utf8Str());
|
||||
|
||||
m_scanThread.reset(new AircraftScanThread(dirs));
|
||||
|
@ -522,10 +505,15 @@ AircraftItemPtr LocalAircraftCache::findItemWithUri(QUrl aircraftUri) const
|
|||
void LocalAircraftCache::abandonCurrentScan()
|
||||
{
|
||||
if (m_scanThread) {
|
||||
// don't fire onScanFinished when the thread ends
|
||||
disconnect(m_scanThread.get(), &AircraftScanThread::finished, this,
|
||||
&LocalAircraftCache::onScanFinished);
|
||||
|
||||
m_scanThread->setDone();
|
||||
m_scanThread->wait(1000);
|
||||
if (!m_scanThread->wait(2000)) {
|
||||
qWarning() << Q_FUNC_INFO << "scan thread failed to terminate";
|
||||
}
|
||||
m_scanThread.reset();
|
||||
qWarning() << Q_FUNC_INFO << "current scan abandonded";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,6 @@ struct AircraftItem
|
|||
|
||||
void toDataStream(QDataStream& ds) const;
|
||||
|
||||
QPixmap thumbnail(bool loadIfRequired = true) const;
|
||||
|
||||
int indexOfVariant(QUrl uri) const;
|
||||
|
||||
bool excluded = false;
|
||||
|
@ -75,7 +73,6 @@ struct AircraftItem
|
|||
QUrl supportUrl;
|
||||
QVariant status(int variant);
|
||||
private:
|
||||
mutable QPixmap m_thumbnail;
|
||||
};
|
||||
|
||||
class LocalAircraftCache : public QObject
|
||||
|
@ -126,7 +123,8 @@ public:
|
|||
PackageUpdateAvailable,
|
||||
PackageQueued,
|
||||
PackageDownloading,
|
||||
NotPackaged
|
||||
NotPackaged,
|
||||
PackageInstallFailed
|
||||
};
|
||||
|
||||
Q_ENUMS(PackageStatus)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "LaunchConfig.hxx"
|
||||
#include "DefaultAircraftLocator.hxx"
|
||||
#include "NavaidSearchModel.hxx"
|
||||
#include "CarriersLocationModel.hxx"
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Airports/groundnetwork.hxx>
|
||||
|
@ -53,10 +54,8 @@ const unsigned int MAX_RECENT_LOCATIONS = 64;
|
|||
QVariant savePositionList(const FGPositionedList& posList)
|
||||
{
|
||||
QVariantList vl;
|
||||
FGPositionedList::const_iterator it;
|
||||
for (it = posList.begin(); it != posList.end(); ++it) {
|
||||
for (const auto& pos : posList) {
|
||||
QVariantMap vm;
|
||||
FGPositionedRef pos = *it;
|
||||
vm.insert("ident", QString::fromStdString(pos->ident()));
|
||||
vm.insert("type", pos->type());
|
||||
vm.insert("lat", pos->geod().getLatitudeDeg());
|
||||
|
@ -70,7 +69,7 @@ FGPositionedList loadPositionedList(QVariant v)
|
|||
{
|
||||
QVariantList vl = v.toList();
|
||||
FGPositionedList result;
|
||||
result.reserve(vl.size());
|
||||
result.reserve(static_cast<size_t>(vl.size()));
|
||||
NavDataCache* cache = NavDataCache::instance();
|
||||
|
||||
Q_FOREACH(QVariant v, vl) {
|
||||
|
@ -93,9 +92,10 @@ FGPositionedList loadPositionedList(QVariant v)
|
|||
LocationController::LocationController(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_searchModel = new NavaidSearchModel;
|
||||
m_searchModel = new NavaidSearchModel(this);
|
||||
m_detailQml = new QmlPositioned(this);
|
||||
m_baseQml = new QmlPositioned(this);
|
||||
m_carriersModel = new CarriersLocationModel(this);
|
||||
|
||||
m_defaultAltitude = QuantityValue{Units::FeetMSL, 6000};
|
||||
m_defaultAirspeed = QuantityValue{Units::Knots, 120};
|
||||
|
@ -112,9 +112,7 @@ LocationController::LocationController(QObject *parent) :
|
|||
this, &LocationController::descriptionChanged);
|
||||
}
|
||||
|
||||
LocationController::~LocationController()
|
||||
{
|
||||
}
|
||||
LocationController::~LocationController() = default;
|
||||
|
||||
void LocationController::setLaunchConfig(LaunchConfig *config)
|
||||
{
|
||||
|
@ -196,35 +194,62 @@ void LocationController::setBaseGeod(QmlGeod geod)
|
|||
if (m_locationIsLatLon && (m_geodLocation == geod.geod()))
|
||||
return;
|
||||
|
||||
clearLocation();
|
||||
m_locationIsLatLon = true;
|
||||
m_geodLocation = geod.geod();
|
||||
emit baseLocationChanged();
|
||||
}
|
||||
|
||||
QString LocationController::carrierName() const
|
||||
{
|
||||
return m_carrierName;
|
||||
}
|
||||
|
||||
void LocationController::setCarrierLocation(QString name)
|
||||
{
|
||||
const auto cIndex = m_carriersModel->indexOf(name);
|
||||
clearLocation();
|
||||
if (cIndex < 0) {
|
||||
qWarning() << "invalid carrier name:" << name;
|
||||
return;
|
||||
}
|
||||
|
||||
m_locationIsCarrier = true;
|
||||
m_carrierName = name;
|
||||
m_geodLocation = m_carriersModel->geodForIndex(cIndex);
|
||||
m_carrierParkings = m_carriersModel->parkingsForIndex(cIndex);
|
||||
|
||||
emit baseLocationChanged();
|
||||
}
|
||||
|
||||
void LocationController::clearLocation()
|
||||
{
|
||||
m_locationIsLatLon = false;
|
||||
m_locationIsCarrier = false;
|
||||
m_location.clear();
|
||||
m_carrierName.clear();
|
||||
m_airportLocation.clear();
|
||||
m_detailLocation.clear();
|
||||
m_detailQml->setGuid(0);
|
||||
m_baseQml->setGuid(0);
|
||||
m_carrierParkings.clear();
|
||||
m_carrierParking.clear();
|
||||
emit baseLocationChanged();
|
||||
}
|
||||
|
||||
void LocationController::setBaseLocation(QmlPositioned* pos)
|
||||
{
|
||||
if (!pos) {
|
||||
m_location.clear();
|
||||
m_detailLocation.clear();
|
||||
m_detailQml->setGuid(0);
|
||||
m_baseQml->setGuid(0);
|
||||
m_airportLocation.clear();
|
||||
m_locationIsLatLon = false;
|
||||
emit baseLocationChanged();
|
||||
clearLocation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos->inner() == m_location)
|
||||
return;
|
||||
|
||||
m_locationIsLatLon = false;
|
||||
clearLocation();
|
||||
m_location = pos->inner();
|
||||
m_baseQml->setGuid(pos->guid());
|
||||
m_detailLocation.clear();
|
||||
m_detailQml->setGuid(0);
|
||||
|
||||
if (FGPositioned::isAirportType(m_location.ptr())) {
|
||||
m_airportLocation = static_cast<FGAirport*>(m_location.ptr());
|
||||
|
@ -249,7 +274,6 @@ void LocationController::setDetailLocation(QmlPositioned* pos)
|
|||
m_detailLocation.clear();
|
||||
m_detailQml->setInner({});
|
||||
} else {
|
||||
qInfo() << Q_FUNC_INFO << "pos:" << pos->ident();
|
||||
m_detailLocation = pos->inner();
|
||||
m_useActiveRunway = false;
|
||||
m_useAvailableParking = false;
|
||||
|
@ -261,7 +285,7 @@ void LocationController::setDetailLocation(QmlPositioned* pos)
|
|||
|
||||
QmlGeod LocationController::baseGeod() const
|
||||
{
|
||||
if (m_locationIsLatLon)
|
||||
if (m_locationIsLatLon || m_locationIsCarrier)
|
||||
return m_geodLocation;
|
||||
|
||||
if (m_location)
|
||||
|
@ -368,6 +392,33 @@ QmlGeod LocationController::parseStringAsGeod(QString string) const
|
|||
return QmlGeod(g);
|
||||
}
|
||||
|
||||
QString LocationController::carrierParking() const
|
||||
{
|
||||
if (!m_locationIsCarrier)
|
||||
return {};
|
||||
return m_carrierParking;
|
||||
}
|
||||
|
||||
void LocationController::setCarrierParking(QString name)
|
||||
{
|
||||
if (!m_locationIsCarrier) {
|
||||
qWarning() << "active location is not a carrier";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_carrierParking == name)
|
||||
return;
|
||||
|
||||
if (!m_carrierParkings.contains(name)) {
|
||||
qWarning() << "parking '" << name << "' not found in carrier parking list";
|
||||
return;
|
||||
}
|
||||
|
||||
m_carrierParking = name;
|
||||
m_useCarrierFLOLS = false;
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
QmlPositioned *LocationController::detail() const
|
||||
{
|
||||
return m_detailQml;
|
||||
|
@ -378,6 +429,11 @@ QmlPositioned *LocationController::baseLocation() const
|
|||
return m_baseQml;
|
||||
}
|
||||
|
||||
QStringList LocationController::carrierParkings() const
|
||||
{
|
||||
return m_carrierParkings;
|
||||
}
|
||||
|
||||
void LocationController::setOffsetRadial(QuantityValue offsetRadial)
|
||||
{
|
||||
if (m_offsetRadial == offsetRadial)
|
||||
|
@ -436,27 +492,26 @@ void LocationController::setUseAvailableParking(bool useAvailableParking)
|
|||
emit configChanged();
|
||||
}
|
||||
|
||||
void LocationController::setUseCarrierFLOLS(bool useCarrierFLOLS)
|
||||
{
|
||||
if (!m_locationIsCarrier) {
|
||||
qWarning() << "location is not a carrier";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_useCarrierFLOLS == useCarrierFLOLS)
|
||||
return;
|
||||
|
||||
m_useCarrierFLOLS = useCarrierFLOLS;
|
||||
m_carrierParking.clear();
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
void LocationController::restoreLocation(QVariantMap l)
|
||||
{
|
||||
try {
|
||||
if (l.contains("location-lat")) {
|
||||
m_locationIsLatLon = true;
|
||||
m_geodLocation = SGGeod::fromDeg(l.value("location-lon").toDouble(),
|
||||
l.value("location-lat").toDouble());
|
||||
m_location.clear();
|
||||
m_airportLocation.clear();
|
||||
m_baseQml->setInner(nullptr);
|
||||
} else if (l.contains("location-id")) {
|
||||
m_location = NavDataCache::instance()->loadById(l.value("location-id").toULongLong());
|
||||
m_locationIsLatLon = false;
|
||||
if (FGPositioned::isAirportType(m_location.ptr())) {
|
||||
m_airportLocation = static_cast<FGAirport*>(m_location.ptr());
|
||||
} else {
|
||||
m_airportLocation.clear();
|
||||
}
|
||||
m_baseQml->setInner(m_location);
|
||||
}
|
||||
clearLocation();
|
||||
|
||||
try {
|
||||
m_altitudeEnabled = l.contains("altitude");
|
||||
m_speedEnabled = l.contains("speed");
|
||||
m_headingEnabled = l.contains("heading");
|
||||
|
@ -470,10 +525,33 @@ void LocationController::restoreLocation(QVariantMap l)
|
|||
m_offsetDistance = l.value("offset-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>();
|
||||
m_tuneNAV1 = l.value("tune-nav1-radio").toBool();
|
||||
|
||||
if (l.contains("location-lat")) {
|
||||
m_locationIsLatLon = true;
|
||||
m_geodLocation = SGGeod::fromDeg(l.value("location-lon").toDouble(),
|
||||
l.value("location-lat").toDouble());
|
||||
} else if (l.contains("carrier")) {
|
||||
setCarrierLocation(l.value("carrier").toString());
|
||||
if (l.contains("carrier-flols")) {
|
||||
setUseCarrierFLOLS(l.value("carrier-flols").toBool());
|
||||
// overwrite value form above, intentionally
|
||||
m_offsetDistance = l.value("location-carrier-flols-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>();
|
||||
} else if (l.contains("carrier-parking")) {
|
||||
setCarrierParking(l.value("carrier-parking").toString());
|
||||
}
|
||||
} else if (l.contains("location-id")) {
|
||||
m_location = NavDataCache::instance()->loadById(l.value("location-id").toLongLong());
|
||||
m_locationIsLatLon = false;
|
||||
if (FGPositioned::isAirportType(m_location.ptr())) {
|
||||
m_airportLocation = static_cast<FGAirport*>(m_location.ptr());
|
||||
} else {
|
||||
m_airportLocation.clear();
|
||||
}
|
||||
m_baseQml->setInner(m_location);
|
||||
}
|
||||
|
||||
if (m_airportLocation) {
|
||||
m_useActiveRunway = false;
|
||||
m_useAvailableParking = false;
|
||||
m_detailLocation.clear();
|
||||
|
||||
if (l.contains("location-apt-runway")) {
|
||||
QString runway = l.value("location-apt-runway").toString().toUpper();
|
||||
|
@ -502,10 +580,7 @@ void LocationController::restoreLocation(QVariantMap l)
|
|||
} // of location is an airport
|
||||
} catch (const sg_exception&) {
|
||||
qWarning() << "Errors restoring saved location, clearing";
|
||||
m_location.clear();
|
||||
m_airportLocation.clear();
|
||||
m_baseQml->setInner(nullptr);
|
||||
m_offsetEnabled = false;
|
||||
clearLocation();
|
||||
}
|
||||
|
||||
baseLocationChanged();
|
||||
|
@ -515,6 +590,10 @@ void LocationController::restoreLocation(QVariantMap l)
|
|||
|
||||
bool LocationController::shouldStartPaused() const
|
||||
{
|
||||
if (m_useCarrierFLOLS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_location) {
|
||||
return false; // defaults to on-ground at the default airport
|
||||
}
|
||||
|
@ -535,6 +614,14 @@ QVariantMap LocationController::saveLocation() const
|
|||
if (m_locationIsLatLon) {
|
||||
locationSet.insert("location-lat", m_geodLocation.getLatitudeDeg());
|
||||
locationSet.insert("location-lon", m_geodLocation.getLongitudeDeg());
|
||||
} else if (m_locationIsCarrier) {
|
||||
locationSet.insert("carrier", m_carrierName);
|
||||
if (m_useCarrierFLOLS) {
|
||||
locationSet.insert("carrier-flols", true);
|
||||
locationSet.insert("location-carrier-flols-distance", QVariant::fromValue(m_offsetDistance));
|
||||
} else if (!m_carrierParking.isEmpty()) {
|
||||
locationSet.insert("carrier-parking", m_carrierParking);
|
||||
}
|
||||
} else if (m_location) {
|
||||
locationSet.insert("location-id", static_cast<qlonglong>(m_location->guid()));
|
||||
|
||||
|
@ -588,7 +675,7 @@ void LocationController::setLocationProperties()
|
|||
"runway-requested" << "navaid-id" << "offset-azimuth-deg" <<
|
||||
"offset-distance-nm" << "glideslope-deg" <<
|
||||
"speed-set" << "on-ground" << "airspeed-kt" <<
|
||||
"airport-id" << "runway" << "parkpos";
|
||||
"airport-id" << "runway" << "parkpos" << "carrier";
|
||||
|
||||
Q_FOREACH(QString s, props) {
|
||||
SGPropertyNode* c = presets->getChild(s.toStdString());
|
||||
|
@ -604,6 +691,8 @@ void LocationController::setLocationProperties()
|
|||
fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg());
|
||||
|
||||
applyPositionOffset();
|
||||
applyAltitude();
|
||||
applyAirspeed();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -612,6 +701,27 @@ void LocationController::setLocationProperties()
|
|||
fgSetDouble("/sim/presets/altitude-ft", -9999.0);
|
||||
fgSetDouble("/sim/presets/heading-deg", 9999.0);
|
||||
|
||||
if (m_locationIsCarrier) {
|
||||
fgSetString("/sim/presets/carrier", m_carrierName.toStdString());
|
||||
|
||||
if (m_useCarrierFLOLS) {
|
||||
fgSetBool("/sim/presets/runway-requested", true );
|
||||
// treat the FLOLS as a runway, for the purposes of communication with position-init
|
||||
fgSetString("/sim/presets/runway", "FLOLS");
|
||||
fgSetDouble("/sim/presets/offset-distance-nm", m_offsetDistance.convertToUnit(Units::NauticalMiles).value);
|
||||
|
||||
applyAirspeed();
|
||||
} else if (!m_carrierParking.isEmpty()) {
|
||||
fgSetString("/sim/presets/parkpos", m_carrierParking.toStdString());
|
||||
}
|
||||
|
||||
if (m_tuneNAV1) {
|
||||
// tune TACAN to the carrier
|
||||
qInfo() << "Implement TACAN tuning";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_location) {
|
||||
return;
|
||||
}
|
||||
|
@ -681,7 +791,7 @@ void LocationController::setLocationProperties()
|
|||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// set disambiguation property
|
||||
globals->get_props()->setIntValue("/sim/presets/navaid-id",
|
||||
|
@ -796,6 +906,21 @@ void LocationController::onCollectConfig()
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_locationIsCarrier) {
|
||||
m_config->setArg("carrier", m_carrierName);
|
||||
|
||||
if (!m_carrierParking.isEmpty()) {
|
||||
m_config->setArg("parkpos", m_carrierParking);
|
||||
} else if (m_useCarrierFLOLS) {
|
||||
m_config->setArg("runway", QStringLiteral("FLOLS"));
|
||||
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
|
||||
m_config->setArg("offset-distance", QString::number(offsetNm));
|
||||
applyAirspeed();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_location) {
|
||||
return;
|
||||
}
|
||||
|
@ -852,7 +977,7 @@ void LocationController::onCollectConfig()
|
|||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// set disambiguation property
|
||||
m_config->setProperty("/sim/presets/navaid-id", QString::number(m_location->guid()));
|
||||
|
@ -912,6 +1037,11 @@ QString LocationController::description() const
|
|||
return tr("at position %1").arg(QString::fromStdString(s));
|
||||
}
|
||||
|
||||
if (m_locationIsCarrier) {
|
||||
QString pennant = m_carriersModel->pennantForIndex(m_carriersModel->indexOf(m_carrierName));
|
||||
return tr("on carrier %1 (%2)").arg(m_carrierName).arg(pennant);
|
||||
}
|
||||
|
||||
return tr("No location selected");
|
||||
}
|
||||
|
||||
|
@ -952,7 +1082,7 @@ QString LocationController::description() const
|
|||
if (m_offsetEnabled) {
|
||||
offsetDesc = tr("%1nm %2 of").
|
||||
arg(offsetNm, 0, 'f', 1).
|
||||
arg(compassPointFromHeading(m_offsetRadial.value));
|
||||
arg(compassPointFromHeading(static_cast<int>(m_offsetRadial.value)));
|
||||
}
|
||||
|
||||
QString navaidType;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "UnitsModel.hxx"
|
||||
|
||||
class NavaidSearchModel;
|
||||
class CarriersLocationModel;
|
||||
|
||||
class LocationController : public QObject
|
||||
{
|
||||
|
@ -40,6 +41,7 @@ class LocationController : public QObject
|
|||
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
|
||||
|
||||
Q_PROPERTY(NavaidSearchModel* searchModel MEMBER m_searchModel CONSTANT)
|
||||
Q_PROPERTY(CarriersLocationModel* carriersModel MEMBER m_carriersModel CONSTANT)
|
||||
|
||||
Q_PROPERTY(QList<QObject*> airportRunways READ airportRunways NOTIFY baseLocationChanged)
|
||||
Q_PROPERTY(QList<QObject*> airportParkings READ airportParkings NOTIFY baseLocationChanged)
|
||||
|
@ -68,6 +70,13 @@ class LocationController : public QObject
|
|||
Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT)
|
||||
Q_PROPERTY(bool isBaseLatLon READ isBaseLatLon NOTIFY baseLocationChanged)
|
||||
|
||||
Q_PROPERTY(bool isCarrier READ isCarrier NOTIFY baseLocationChanged)
|
||||
Q_PROPERTY(QString carrier READ carrierName WRITE setCarrierLocation NOTIFY baseLocationChanged)
|
||||
Q_PROPERTY(QStringList carrierParkings READ carrierParkings NOTIFY baseLocationChanged)
|
||||
Q_PROPERTY(bool useCarrierFLOLS READ useCarrierFLOLS WRITE setUseCarrierFLOLS NOTIFY configChanged)
|
||||
Q_PROPERTY(QString carrierParking READ carrierParking WRITE setCarrierParking NOTIFY configChanged)
|
||||
|
||||
|
||||
// allow collecting the location properties to be disabled, if the
|
||||
// user is setting conflicting ones
|
||||
Q_PROPERTY(bool skipFromArgs MEMBER m_skipFromArgs NOTIFY skipFromArgsChanged)
|
||||
|
@ -110,6 +119,9 @@ public:
|
|||
QmlGeod baseGeod() const;
|
||||
void setBaseGeod(QmlGeod geod);
|
||||
|
||||
QString carrierName() const;
|
||||
void setCarrierLocation(QString name);
|
||||
|
||||
bool isAirportLocation() const;
|
||||
|
||||
bool offsetEnabled() const
|
||||
|
@ -138,6 +150,9 @@ public:
|
|||
|
||||
Q_INVOKABLE QmlGeod parseStringAsGeod(QString string) const;
|
||||
|
||||
QString carrierParking() const;
|
||||
void setCarrierParking(QString name);
|
||||
|
||||
bool tuneNAV1() const
|
||||
{
|
||||
return m_tuneNAV1;
|
||||
|
@ -161,6 +176,19 @@ public:
|
|||
{
|
||||
return m_altitude;
|
||||
}
|
||||
|
||||
bool isCarrier() const
|
||||
{
|
||||
return m_locationIsCarrier;
|
||||
}
|
||||
|
||||
QStringList carrierParkings() const;
|
||||
|
||||
bool useCarrierFLOLS() const
|
||||
{
|
||||
return m_useCarrierFLOLS;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setOffsetRadial(QuantityValue offsetRadial);
|
||||
|
||||
|
@ -174,6 +202,8 @@ public slots:
|
|||
|
||||
void setUseAvailableParking(bool useAvailableParking);
|
||||
|
||||
void setUseCarrierFLOLS(bool useCarrierFLOLS);
|
||||
|
||||
Q_SIGNALS:
|
||||
void descriptionChanged();
|
||||
void offsetChanged();
|
||||
|
@ -186,6 +216,7 @@ private Q_SLOTS:
|
|||
void onRestoreCurrentLocation();
|
||||
void onSaveCurrentLocation();
|
||||
private:
|
||||
void clearLocation();
|
||||
|
||||
void onSearchComplete();
|
||||
void addToRecent(FGPositionedRef pos);
|
||||
|
@ -197,12 +228,15 @@ private:
|
|||
void applyOnFinal();
|
||||
|
||||
NavaidSearchModel* m_searchModel = nullptr;
|
||||
CarriersLocationModel* m_carriersModel = nullptr;
|
||||
|
||||
FGPositionedRef m_location;
|
||||
FGAirportRef m_airportLocation; // valid if m_location is an FGAirport
|
||||
FGPositionedRef m_detailLocation; // parking stand or runway detail
|
||||
bool m_locationIsLatLon = false;
|
||||
SGGeod m_geodLocation;
|
||||
bool m_locationIsCarrier = false;
|
||||
QString m_carrierName;
|
||||
|
||||
FGPositionedList m_recentLocations;
|
||||
LaunchConfig* m_config = nullptr;
|
||||
|
@ -227,6 +261,10 @@ private:
|
|||
bool m_speedEnabled = false;
|
||||
bool m_altitudeEnabled = false;
|
||||
bool m_skipFromArgs = false;
|
||||
|
||||
bool m_useCarrierFLOLS = false;
|
||||
QString m_carrierParking;
|
||||
QStringList m_carrierParkings;
|
||||
};
|
||||
|
||||
#endif // LOCATION_CONTROLLER_HXX
|
||||
|
|
|
@ -2002,7 +2002,7 @@ MapWidget::DrawAIObject::DrawAIObject(SGPropertyNode* m, const SGGeod& g) :
|
|||
heading = model->getDoubleValue("orientation/true-heading-deg");
|
||||
|
||||
if ((name == "aircraft") || (name == "multiplayer") ||
|
||||
(name == "wingman") || (name == "tanker"))
|
||||
(name == "wingman") || (name == "tanker") || (name == "swift"))
|
||||
{
|
||||
speedKts = static_cast<int>(model->getDoubleValue("velocities/true-airspeed-kt"));
|
||||
label = model->getStringValue("callsign", "<>");
|
||||
|
|
|
@ -24,35 +24,49 @@ QVariant ModelDataExtractor::data() const
|
|||
return m_model->data(m, role);
|
||||
}
|
||||
|
||||
if (m_value.isArray()) {
|
||||
if (!m_stringsModel.empty()) {
|
||||
if ((m_index < 0) || (m_index >= m_stringsModel.size()))
|
||||
return {};
|
||||
|
||||
return m_stringsModel.at(m_index);
|
||||
}
|
||||
|
||||
if (m_rawModel.isArray()) {
|
||||
quint32 uIndex = static_cast<quint32>(m_index);
|
||||
auto v = m_value.property(uIndex);
|
||||
auto v = m_rawModel.property(uIndex);
|
||||
if (v.isQObject()) {
|
||||
// handle the QList<QObject*> case
|
||||
auto obj = v.toQObject();
|
||||
return obj->property(m_role.toUtf8().constData());
|
||||
}
|
||||
|
||||
return m_value.property(uIndex).toVariant();
|
||||
return m_rawModel.property(uIndex).toVariant();
|
||||
}
|
||||
|
||||
qWarning() << "Unable to convert model data:" << m_rawModel.toString();
|
||||
return {};
|
||||
}
|
||||
|
||||
void ModelDataExtractor::setModel(QJSValue model)
|
||||
void ModelDataExtractor::clear()
|
||||
{
|
||||
if (m_value.equals(model))
|
||||
return;
|
||||
|
||||
if (m_model) {
|
||||
// disconnect from everything
|
||||
disconnect(m_model, nullptr, this, nullptr);
|
||||
m_model = nullptr;
|
||||
}
|
||||
|
||||
m_value = model;
|
||||
if (m_value.isQObject()) {
|
||||
m_model = qobject_cast<QAbstractItemModel*>(m_value.toQObject());
|
||||
}
|
||||
|
||||
void ModelDataExtractor::setModel(QJSValue raw)
|
||||
{
|
||||
if (m_rawModel.strictlyEquals(raw))
|
||||
return;
|
||||
|
||||
clear();
|
||||
m_rawModel = raw;
|
||||
|
||||
if (raw.isQObject()) {
|
||||
m_model = qobject_cast<QAbstractItemModel*>(raw.toQObject());
|
||||
if (m_model) {
|
||||
connect(m_model, &QAbstractItemModel::modelReset,
|
||||
this, &ModelDataExtractor::dataChanged);
|
||||
|
@ -61,10 +75,24 @@ void ModelDataExtractor::setModel(QJSValue model)
|
|||
|
||||
// ToDo: handle rows added / removed
|
||||
} else {
|
||||
qWarning() << "object but not a QAIM" << m_value.toQObject();
|
||||
qWarning() << "object but not a QAIM" << raw.toQObject();
|
||||
}
|
||||
} else if (raw.isArray()) {
|
||||
|
||||
} else if (raw.isVariant() || raw.isObject()) {
|
||||
// special case the QStringList case
|
||||
|
||||
// for reasons I don't understand yet, QStringList returned as a
|
||||
// property value to JS, does not show up as a variant-in-JS-Value above
|
||||
// (so ::isVariant returns false), but conversion to a variant
|
||||
// works. Hence the 'raw.isObject' above
|
||||
|
||||
const auto var = raw.toVariant();
|
||||
if (var.type() == QVariant::StringList) {
|
||||
m_stringsModel = var.toStringList();
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "variant but not a QStringList" << var;
|
||||
}
|
||||
} else {
|
||||
// might be null, or an array
|
||||
}
|
||||
|
||||
emit modelChanged();
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
|
||||
QJSValue model() const
|
||||
{
|
||||
return m_value;
|
||||
return m_rawModel;
|
||||
}
|
||||
|
||||
int index() const
|
||||
|
@ -52,8 +52,12 @@ private slots:
|
|||
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
||||
QAbstractItemModel* m_model = nullptr;
|
||||
QJSValue m_value;
|
||||
QJSValue m_rawModel;
|
||||
QStringList m_stringsModel;
|
||||
|
||||
int m_index = 0;
|
||||
QString m_role;
|
||||
};
|
||||
|
|
|
@ -34,6 +34,9 @@ QString fixNavaidName(QString s)
|
|||
QStringList words = s.split(QChar(' '));
|
||||
QStringList changedWords;
|
||||
Q_FOREACH(QString w, words) {
|
||||
if (w.isEmpty())
|
||||
continue;
|
||||
|
||||
QString up = w.toUpper();
|
||||
|
||||
// expand common abbreviations
|
||||
|
@ -150,10 +153,17 @@ void NavaidSearchModel::clear()
|
|||
|
||||
qlonglong NavaidSearchModel::guidAtIndex(int index) const
|
||||
{
|
||||
if ((index < 0) || (index >= m_ids.size()))
|
||||
const size_t uIndex = static_cast<size_t>(index);
|
||||
if ((index < 0) || (uIndex >= m_ids.size()))
|
||||
return 0;
|
||||
|
||||
return m_ids.at(index);
|
||||
return m_ids.at(uIndex);
|
||||
}
|
||||
|
||||
NavaidSearchModel::NavaidSearchModel(QObject *parent) :
|
||||
QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NavaidSearchModel::setSearch(QString t, NavaidSearchModel::AircraftType aircraft)
|
||||
|
|
|
@ -51,7 +51,7 @@ class NavaidSearchModel : public QAbstractListModel
|
|||
};
|
||||
|
||||
public:
|
||||
NavaidSearchModel() { }
|
||||
NavaidSearchModel(QObject* parent = nullptr);
|
||||
|
||||
enum AircraftType
|
||||
{
|
||||
|
|
188
src/GUI/PathListModel.cxx
Normal file
188
src/GUI/PathListModel.cxx
Normal file
|
@ -0,0 +1,188 @@
|
|||
#include "PathListModel.hxx"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
|
||||
PathListModel::PathListModel(QObject *pr) :
|
||||
QAbstractListModel(pr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PathListModel::~PathListModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PathListModel::loadFromSettings(QString key)
|
||||
{
|
||||
QSettings settings;
|
||||
if (!settings.contains(key))
|
||||
return;
|
||||
|
||||
QVariantList vl = settings.value(key).toList();
|
||||
mPaths.clear();
|
||||
mPaths.reserve(static_cast<size_t>(vl.size()));
|
||||
|
||||
beginResetModel();
|
||||
|
||||
Q_FOREACH(QVariant v, vl) {
|
||||
QVariantMap m = v.toMap();
|
||||
PathEntry entry;
|
||||
entry.path = m.value("path").toString();
|
||||
if (entry.path.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.enabled = m.value("enabled", QVariant{true}).toBool();
|
||||
mPaths.push_back(entry);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
emit enabledPathsChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void PathListModel::saveToSettings(QString key) const
|
||||
{
|
||||
QVariantList vl;
|
||||
for (const auto &e : mPaths) {
|
||||
QVariantMap v;
|
||||
v["path"] = e.path;
|
||||
v["enabled"] = e.enabled;
|
||||
vl.append(v);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue(key, vl);
|
||||
}
|
||||
|
||||
QStringList PathListModel::readEnabledPaths(QString settingsKey)
|
||||
{
|
||||
QSettings settings;
|
||||
if (!settings.contains(settingsKey))
|
||||
return {};
|
||||
|
||||
QStringList result;
|
||||
QVariantList vl = settings.value(settingsKey).toList();
|
||||
Q_FOREACH(QVariant v, vl) {
|
||||
QVariantMap m = v.toMap();
|
||||
if (!m.value("enabled").toBool())
|
||||
continue;
|
||||
|
||||
result.append(m.value("path").toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList PathListModel::enabledPaths() const
|
||||
{
|
||||
QStringList result;
|
||||
for (const auto& e : mPaths) {
|
||||
if (e.enabled) {
|
||||
result.append(e.path);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PathListModel::count()
|
||||
{
|
||||
return static_cast<int>(mPaths.size());
|
||||
}
|
||||
|
||||
int PathListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return static_cast<int>(mPaths.size());
|
||||
}
|
||||
|
||||
QVariant PathListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
int row = index.row();
|
||||
const auto& entry = mPaths.at(static_cast<size_t>(row));
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case PathRole:
|
||||
return entry.path;
|
||||
|
||||
case PathEnabledRole:
|
||||
return entry.enabled;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool PathListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
int row = index.row();
|
||||
auto& entry = mPaths.at(static_cast<size_t>(row));
|
||||
if (role == PathEnabledRole) {
|
||||
entry.enabled = value.toBool();
|
||||
emit dataChanged(index, index, {PathEnabledRole});
|
||||
emit enabledPathsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PathListModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> result = QAbstractListModel::roleNames();
|
||||
result[Qt::DisplayRole] = "path";
|
||||
result[PathEnabledRole] = "enabled";
|
||||
return result;
|
||||
}
|
||||
|
||||
void PathListModel::removePath(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= static_cast<int>(mPaths.size()))) {
|
||||
qWarning() << Q_FUNC_INFO << "index invalid:" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, index, index);
|
||||
auto it = mPaths.begin() + index;
|
||||
mPaths.erase(it);
|
||||
endRemoveRows();
|
||||
emit enabledPathsChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void PathListModel::appendPath(QString path)
|
||||
{
|
||||
PathEntry entry;
|
||||
entry.path = path;
|
||||
entry.enabled = true; // enable by default
|
||||
const int newRow = static_cast<int>(mPaths.size());
|
||||
beginInsertRows({}, newRow, newRow);
|
||||
mPaths.push_back(entry);
|
||||
endInsertRows();
|
||||
emit enabledPathsChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void PathListModel::swapIndices(int indexA, int indexB)
|
||||
{
|
||||
if ((indexA < 0) || (indexA >= static_cast<int>(mPaths.size()))) {
|
||||
qWarning() << Q_FUNC_INFO << "index invalid:" << indexA;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((indexB < 0) || (indexB >= static_cast<int>(mPaths.size()))) {
|
||||
qWarning() << Q_FUNC_INFO << "index invalid:" << indexB;
|
||||
return;
|
||||
}
|
||||
|
||||
std::swap(mPaths[static_cast<size_t>(indexA)],
|
||||
mPaths[static_cast<size_t>(indexB)]);
|
||||
emit dataChanged(index(indexA), index(indexA));
|
||||
emit dataChanged(index(indexB), index(indexB));
|
||||
emit enabledPathsChanged();
|
||||
}
|
||||
|
56
src/GUI/PathListModel.hxx
Normal file
56
src/GUI/PathListModel.hxx
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef PATHLISTMODEL_HXX
|
||||
#define PATHLISTMODEL_HXX
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
const int PathRole = Qt::UserRole + 1;
|
||||
const int PathEnabledRole = Qt::UserRole + 2;
|
||||
|
||||
class PathListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
public:
|
||||
|
||||
PathListModel(QObject* pr);
|
||||
~PathListModel() override;
|
||||
|
||||
void loadFromSettings(QString key);
|
||||
void saveToSettings(QString key) const;
|
||||
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
static QStringList readEnabledPaths(QString settingsKey);
|
||||
|
||||
QStringList enabledPaths() const;
|
||||
|
||||
int count();
|
||||
signals:
|
||||
void enabledPathsChanged();
|
||||
void countChanged();
|
||||
|
||||
public slots:
|
||||
void removePath(int index);
|
||||
void appendPath(QString path);
|
||||
|
||||
void swapIndices(int indexA, int indexB);
|
||||
|
||||
private:
|
||||
struct PathEntry {
|
||||
QString path;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
std::vector<PathEntry> mPaths;
|
||||
};
|
||||
|
||||
#endif // PATHLISTMODEL_HXX
|
|
@ -38,16 +38,6 @@ QtFileDialog::~QtFileDialog() {}
|
|||
|
||||
void QtFileDialog::exec()
|
||||
{
|
||||
int fakeargc = 1;
|
||||
static char fakeargv0[] = "fgfs";
|
||||
static char * fakeargv[2] = {fakeargv0, 0};
|
||||
// This does nothing if it has already been run, so the fake argc/argv are
|
||||
// only used if run without launcher. Don't attempt to initialize the
|
||||
// QSettings, because this would require FGGlobals to be initialized (for
|
||||
// globals->get_fg_home()), which would prevent using this function at
|
||||
// early startup.
|
||||
flightgear::initApp(fakeargc, fakeargv, false /* doInitQSettings */);
|
||||
|
||||
// concatenate filter patterns, as Qt uses a single string
|
||||
std::string filter="";
|
||||
for( string_list::const_iterator it = _filterPatterns.begin(); it != _filterPatterns.end();++it ) {
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Install.hxx>
|
||||
|
@ -74,6 +75,7 @@
|
|||
#include "LauncherMainWindow.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
#include "UnitsModel.hxx"
|
||||
#include "PathListModel.hxx"
|
||||
|
||||
using namespace flightgear;
|
||||
using namespace simgear::pkg;
|
||||
|
@ -354,19 +356,22 @@ void initQSettings()
|
|||
bool checkKeyboardModifiersForSettingFGRoot()
|
||||
{
|
||||
initQSettings();
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const auto altState = GetAsyncKeyState(VK_MENU);
|
||||
const auto shiftState = GetAsyncKeyState(VK_SHIFT);
|
||||
if ((altState < 0) || (shiftState < 0))
|
||||
#else
|
||||
Qt::KeyboardModifiers mods = qApp->queryKeyboardModifiers();
|
||||
if (mods & (Qt::AltModifier | Qt::ShiftModifier)) {
|
||||
if (mods & (Qt::AltModifier | Qt::ShiftModifier))
|
||||
#endif
|
||||
{
|
||||
qWarning() << "Alt/shift pressed during launch";
|
||||
QSettings settings;
|
||||
settings.setValue("fg-root", "!ask");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void restartTheApp()
|
||||
{
|
||||
QStringList fgArgs;
|
||||
|
@ -403,7 +408,7 @@ void launcherSetSceneryPaths()
|
|||
// positions
|
||||
QSettings settings;
|
||||
// append explicit scenery paths
|
||||
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
|
||||
Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths-v2")) {
|
||||
globals->append_fg_scenery(path.toStdString());
|
||||
}
|
||||
|
||||
|
@ -451,6 +456,15 @@ bool runLauncherDialog()
|
|||
|
||||
fgInitPackageRoot();
|
||||
|
||||
// setup package language
|
||||
auto lang = options->valueForOption("language");
|
||||
if (!lang.empty()) {
|
||||
globals->packageRoot()->setLocale(lang);
|
||||
} else {
|
||||
const auto langName = QLocale::languageToString(QLocale{}.language());
|
||||
globals->packageRoot()->setLocale(langName.toStdString());
|
||||
}
|
||||
|
||||
// startup the HTTP system now since packages needs it
|
||||
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
||||
|
||||
|
|
20
src/GUI/SetupRootDialog.cxx
Normal file → Executable file
20
src/GUI/SetupRootDialog.cxx
Normal file → Executable file
|
@ -40,7 +40,9 @@
|
|||
#include <Include/version.h>
|
||||
#include <Viewer/WindowBuilder.hxx>
|
||||
|
||||
static QString rootPathKey()
|
||||
#include "QtLauncher.hxx"
|
||||
|
||||
QString SetupRootDialog::rootPathKey()
|
||||
{
|
||||
// return a settings key like fg-root-2018-3-0
|
||||
return QString("fg-root-") + QString(FLIGHTGEAR_VERSION).replace('.', '-');
|
||||
|
@ -99,7 +101,8 @@ SGPath SetupRootDialog::restoreUserSelectedRoot()
|
|||
{
|
||||
QSettings settings;
|
||||
QString path = settings.value(rootPathKey()).toString();
|
||||
if (path == "!ask") {
|
||||
bool ask = flightgear::checkKeyboardModifiersForSettingFGRoot();
|
||||
if (ask || (path == QStringLiteral("!ask"))) {
|
||||
bool ok = runDialog(ManualChoiceRequested);
|
||||
Q_ASSERT(ok);
|
||||
// run dialog either exit()s or sets fg_root, so this
|
||||
|
@ -108,7 +111,7 @@ SGPath SetupRootDialog::restoreUserSelectedRoot()
|
|||
}
|
||||
|
||||
if (path.isEmpty()) {
|
||||
return std::string(); // use the default path
|
||||
return SGPath{}; // use the default path
|
||||
}
|
||||
|
||||
if (validatePath(path) && validateVersion(path)) {
|
||||
|
@ -118,7 +121,7 @@ SGPath SetupRootDialog::restoreUserSelectedRoot()
|
|||
// let's see if the default root is acceptable, in which case we will
|
||||
// switch to it. (This gives a more friendly upgrade experience).
|
||||
if (defaultRootAcceptable()) {
|
||||
return std::string(); // use the default path
|
||||
return SGPath{}; // use the default path
|
||||
}
|
||||
|
||||
// okay, we don't have an acceptable FG_DATA anywhere we can find, we
|
||||
|
@ -131,6 +134,13 @@ SGPath SetupRootDialog::restoreUserSelectedRoot()
|
|||
}
|
||||
}
|
||||
|
||||
void SetupRootDialog::askRootOnNextLaunch()
|
||||
{
|
||||
QSettings settings;
|
||||
// set the option to the magic marker value
|
||||
settings.setValue(rootPathKey(), "!ask");
|
||||
}
|
||||
|
||||
bool SetupRootDialog::validatePath(QString path)
|
||||
{
|
||||
// check assorted files exist in the root location, to avoid any chance of
|
||||
|
@ -216,7 +226,7 @@ void SetupRootDialog::onUseDefaults()
|
|||
m_browsedPath = QString::fromStdString(r.utf8Str());
|
||||
globals->set_fg_root(r);
|
||||
QSettings settings;
|
||||
settings.remove("fg-root"); // remove any setting
|
||||
settings.remove(rootPathKey()); // remove any setting
|
||||
accept();
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,10 @@ public:
|
|||
static bool runDialog(bool usingDefaultRoot);
|
||||
|
||||
static SGPath restoreUserSelectedRoot();
|
||||
|
||||
static void askRootOnNextLaunch();
|
||||
|
||||
static QString rootPathKey();
|
||||
private slots:
|
||||
|
||||
void onBrowse();
|
||||
|
|
|
@ -27,30 +27,33 @@ public:
|
|||
void startInstall(pkg::InstallRef) override {}
|
||||
void installProgress(pkg::InstallRef, unsigned int, unsigned int) override {}
|
||||
void finishInstall(pkg::InstallRef, StatusCode ) override {}
|
||||
|
||||
void dataForThumbnail(const std::string& aThumbnailUrl,
|
||||
size_t length, const uint8_t* bytes) override
|
||||
{
|
||||
if (aThumbnailUrl != owner->url().toString().toStdString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes), length));
|
||||
if (img.isNull()) {
|
||||
if (length > 0) {
|
||||
// warn if we had valid bytes but couldn't load it, i.e corrupted data or similar
|
||||
qWarning() << "failed to load image data for URL:" << QString::fromStdString(aThumbnailUrl);
|
||||
owner->clearImage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
owner->setImage(img);
|
||||
}
|
||||
size_t length, const uint8_t* bytes) override;
|
||||
|
||||
ThumbnailImageItem* owner;
|
||||
};
|
||||
|
||||
void ThumbnailImageItem::ThumbnailPackageDelegate::dataForThumbnail(const std::string& aThumbnailUrl,
|
||||
size_t length, const uint8_t* bytes)
|
||||
{
|
||||
if (aThumbnailUrl != owner->url().toString().toStdString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto iLength = static_cast<int>(length);
|
||||
QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes), iLength));
|
||||
if (img.isNull()) {
|
||||
if (length > 0) {
|
||||
// warn if we had valid bytes but couldn't load it, i.e corrupted data or similar
|
||||
qWarning() << "failed to load image data for URL:" << QString::fromStdString(aThumbnailUrl);
|
||||
owner->clearImage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
owner->setImage(img);
|
||||
}
|
||||
|
||||
ThumbnailImageItem::ThumbnailImageItem(QQuickItem* parent) :
|
||||
QQuickItem(parent),
|
||||
m_delegate(new ThumbnailPackageDelegate(this)),
|
||||
|
@ -81,7 +84,7 @@ QSGNode *ThumbnailImageItem::updatePaintNode(QSGNode* oldNode, QQuickItem::Updat
|
|||
textureNode->setOwnsTexture(true);
|
||||
}
|
||||
|
||||
QSGTexture* tex = window()->createTextureFromImage(m_image);
|
||||
QSGTexture* tex = window()->createTextureFromImage(m_image, QQuickWindow::TextureIsOpaque);
|
||||
textureNode->setTexture(tex);
|
||||
textureNode->markDirty(QSGBasicGeometryNode::DirtyMaterial);
|
||||
m_imageDirty = false;
|
||||
|
@ -123,7 +126,7 @@ void ThumbnailImageItem::setAircraftUri(QString uri)
|
|||
pkg::Root* root = globals->packageRoot();
|
||||
pkg::PackageRef package = root->getPackageById(packageId);
|
||||
if (package) {
|
||||
int variant = package->indexOfVariant(packageId);
|
||||
auto variant = package->indexOfVariant(packageId);
|
||||
const auto thumbnail = package->thumbnailForVariant(variant);
|
||||
m_imageUrl = QUrl(QString::fromStdString(thumbnail.url));
|
||||
if (m_imageUrl.isValid()) {
|
||||
|
|
|
@ -47,9 +47,8 @@ static int CALLBACK BrowseFolderCallback(
|
|||
if (uMsg == BFFM_INITIALIZED) {
|
||||
// set the initial directory now
|
||||
WindowsFileDialog* dlg = reinterpret_cast<WindowsFileDialog*>(lpData);
|
||||
std::string s = dlg->getDirectory().local8BitStr();
|
||||
LPCTSTR path = s.c_str();
|
||||
::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path);
|
||||
const auto w = dlg->getDirectory().wstr();
|
||||
::SendMessageW(hwnd, BFFM_SETSELECTIONW, true, (LPARAM) w.c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
BIN
src/GUI/assets/aircraft-carrier-icon.png
Normal file
BIN
src/GUI/assets/aircraft-carrier-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
src/GUI/assets/icons8-cargo-ship-50.png
Normal file
BIN
src/GUI/assets/icons8-cargo-ship-50.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 890 B |
BIN
src/GUI/assets/icons8-christmas-star-filled.png
Normal file
BIN
src/GUI/assets/icons8-christmas-star-filled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 612 B |
BIN
src/GUI/assets/icons8-christmas-star-outline.png
Normal file
BIN
src/GUI/assets/icons8-christmas-star-outline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 497 B |
BIN
src/GUI/assets/icons8-hide-50.png
Normal file
BIN
src/GUI/assets/icons8-hide-50.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -3,7 +3,9 @@
|
|||
#endif
|
||||
|
||||
#include <plib/pu.h>
|
||||
#include <plib/fnt.h>
|
||||
|
||||
#include "fnt.h"
|
||||
|
||||
|
||||
/**
|
||||
* fonts.cxx generated by the genfonts utility by Pawel W. Olszta.
|
||||
|
|
|
@ -102,12 +102,7 @@ Item {
|
|||
description: qsTr("To use aircraft you download yourself, FlightGear needs to " +
|
||||
"know the folder(s) containing the aircraft data.")
|
||||
showAddButton: true
|
||||
onAdd: {
|
||||
var newPath =_addOns.addAircraftPath();
|
||||
if (newPath !== "") {
|
||||
_addOns.aircraftPaths.push(newPath)
|
||||
}
|
||||
}
|
||||
onAdd: _addOns.addAircraftPath();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -127,21 +122,10 @@ Item {
|
|||
model: _addOns.aircraftPaths
|
||||
delegate: PathListDelegate {
|
||||
width: aircraftPathsColumn.width
|
||||
deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
|
||||
modelCount: _addOns.aircraftPaths.length
|
||||
|
||||
onPerformDelete: {
|
||||
var modifiedPaths = _addOns.aircraftPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
_addOns.aircraftPaths = modifiedPaths;
|
||||
}
|
||||
|
||||
onPerformMove: {
|
||||
var modifiedPaths = _addOns.aircraftPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
modifiedPaths.splice(newIndex, 0, modelData)
|
||||
_addOns.aircraftPaths = modifiedPaths;
|
||||
}
|
||||
deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path);
|
||||
modelCount: _addOns.aircraftPaths.count
|
||||
onPerformDelete: _addOns.aircraftPaths.removePath(model.index)
|
||||
onPerformMove: _addOns.aircraftPaths.swapIndices(model.index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,12 +225,7 @@ Item {
|
|||
"to know the folders containing the scenery data. " +
|
||||
"Adjust the order of the list to control which scenery is used in a region.");
|
||||
showAddButton: true
|
||||
onAdd: {
|
||||
var newPath =_addOns.addSceneryPath();
|
||||
if (newPath !== "") {
|
||||
_addOns.sceneryPaths.push(newPath)
|
||||
}
|
||||
}
|
||||
onAdd: _addOns.addSceneryPath();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -267,21 +246,10 @@ Item {
|
|||
|
||||
delegate: PathListDelegate {
|
||||
width: sceneryPathsColumn.width
|
||||
deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
|
||||
modelCount: _addOns.sceneryPaths.length
|
||||
|
||||
onPerformDelete: {
|
||||
var modifiedPaths = _addOns.sceneryPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
_addOns.sceneryPaths = modifiedPaths;
|
||||
}
|
||||
|
||||
onPerformMove: {
|
||||
var modifiedPaths = _addOns.sceneryPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
modifiedPaths.splice(newIndex, 0, modelData)
|
||||
_addOns.sceneryPaths = modifiedPaths;
|
||||
}
|
||||
deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path);
|
||||
modelCount: _addOns.sceneryPaths.count
|
||||
onPerformDelete: _addOns.sceneryPaths.removePath(model.index)
|
||||
onPerformMove: _addOns.sceneryPaths.swapIndices(model.index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,14 +272,10 @@ Item {
|
|||
var path = _addOns.installCustomScenery();
|
||||
if (path !== "") {
|
||||
// insert into scenery paths if not already present
|
||||
var sceneryPaths = _addOns.sceneryPaths
|
||||
for (var i = 0; i < sceneryPaths.length; i++) {
|
||||
if (sceneryPaths[i] === path)
|
||||
return; // found, we are are done
|
||||
}
|
||||
|
||||
|
||||
// not found, add it
|
||||
_addOns.sceneryPaths.push(path);
|
||||
_addOns.sceneryPaths.appendPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,9 @@ Item {
|
|||
width: delegateRoot.width
|
||||
|
||||
Checkbox {
|
||||
id: chkbox
|
||||
id: enableCheckbox
|
||||
checked: model.enable
|
||||
width: 30
|
||||
anchors.left: parent.left
|
||||
anchors.right: addonsDelegateHover.left
|
||||
anchors.rightMargin: Style.margin
|
||||
height: contentRect.height
|
||||
onCheckedChanged: {
|
||||
_addOns.modules.enable(model.index, checked)
|
||||
|
@ -59,7 +56,7 @@ Item {
|
|||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: chkbox.right
|
||||
anchors.left: enableCheckbox.right
|
||||
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import QtQuick 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
import FlightGear 1.0
|
||||
|
||||
import "."
|
||||
|
||||
Item {
|
||||
|
@ -77,19 +79,39 @@ Item {
|
|||
|
||||
spacing: Style.margin
|
||||
|
||||
AircraftVariantChoice {
|
||||
id: titleBox
|
||||
|
||||
Item {
|
||||
height: titleBox.height
|
||||
width: parent.width
|
||||
|
||||
aircraft: model.uri;
|
||||
currentIndex: model.activeVariant
|
||||
onSelected: {
|
||||
model.activeVariant = index
|
||||
root.select(model.uri)
|
||||
FavouriteToggleButton {
|
||||
id: favourite
|
||||
checked: model.favourite
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onToggle: {
|
||||
model.favourite = on;
|
||||
}
|
||||
}
|
||||
|
||||
AircraftVariantChoice {
|
||||
id: titleBox
|
||||
|
||||
anchors {
|
||||
left: favourite.right
|
||||
leftMargin: Style.margin
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
aircraft: model.uri;
|
||||
currentIndex: model.activeVariant
|
||||
onSelected: {
|
||||
model.activeVariant = index
|
||||
root.select(model.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
StyledText {
|
||||
id: description
|
||||
width: parent.width
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
import FlightGear 1.0
|
||||
import "."
|
||||
|
||||
Item {
|
||||
|
@ -87,10 +88,21 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
FavouriteToggleButton {
|
||||
id: favourite
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
margins: Style.margin
|
||||
}
|
||||
|
||||
visible: hover.containsMouse || model.favourite
|
||||
checked: model.favourite
|
||||
onToggle: { model.favourite = on; }
|
||||
}
|
||||
|
||||
HoverArea {
|
||||
id: hover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
} // of root item
|
||||
|
|
|
@ -11,6 +11,9 @@ Item {
|
|||
|
||||
function updateSelectionFromLauncher()
|
||||
{
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
var row = model.indexForURI(_launcher.selectedAircraft);
|
||||
if (row >= 0) {
|
||||
view.currentIndex = row;
|
||||
|
|
|
@ -17,6 +17,10 @@ FocusScope
|
|||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
_launcher.browseAircraftModel.loadRatingsSettings();
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: tabBar
|
||||
|
@ -24,6 +28,7 @@ FocusScope
|
|||
width: parent.width
|
||||
|
||||
GridToggleButton {
|
||||
id: gridModeToggle
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.margin
|
||||
|
@ -45,6 +50,16 @@ FocusScope
|
|||
active: root.state == "installed"
|
||||
}
|
||||
|
||||
TabButton {
|
||||
id: favouritesButton
|
||||
text: qsTr("Favourites")
|
||||
onClicked: {
|
||||
root.state = "favourites"
|
||||
root.updateSelectionFromLauncher();
|
||||
}
|
||||
active: root.state == "favourites"
|
||||
}
|
||||
|
||||
TabButton {
|
||||
id: browseButton
|
||||
text: qsTr("Browse")
|
||||
|
@ -128,7 +143,8 @@ FocusScope
|
|||
|
||||
Loader {
|
||||
id: aircraftContent
|
||||
source: _launcher.aircraftGridMode ? "qrc:///qml/AircraftGridView.qml"
|
||||
// we use gridModeToggle vis to mean enabled, effectively
|
||||
source: (gridModeToggle.visible && _launcher.aircraftGridMode) ? "qrc:///qml/AircraftGridView.qml"
|
||||
: "qrc:///qml/AircraftListView.qml"
|
||||
|
||||
anchors {
|
||||
|
@ -182,6 +198,10 @@ FocusScope
|
|||
__model: _launcher.installedAircraftModel
|
||||
__header: emptyHeader
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: gridModeToggle; visible: true
|
||||
}
|
||||
},
|
||||
|
||||
State {
|
||||
|
@ -191,6 +211,10 @@ FocusScope
|
|||
__model: _launcher.searchAircraftModel
|
||||
__header: emptyHeader
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: gridModeToggle; visible: true
|
||||
}
|
||||
},
|
||||
|
||||
State {
|
||||
|
@ -200,6 +224,10 @@ FocusScope
|
|||
__model: _launcher.browseAircraftModel
|
||||
__header: _addOns.showNoOfficialHangar ? noDefaultCatalogHeader : ratingsHeader
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: gridModeToggle; visible: true
|
||||
}
|
||||
},
|
||||
|
||||
State {
|
||||
|
@ -209,7 +237,26 @@ FocusScope
|
|||
__model: _launcher.aircraftWithUpdatesModel
|
||||
__header: (_launcher.aircraftWithUpdatesModel.count > 0) ? updateAllHeader : emptyHeader
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: gridModeToggle; visible: false
|
||||
}
|
||||
},
|
||||
|
||||
State {
|
||||
name: "favourites"
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
__model: _launcher.favouriteAircraftModel
|
||||
__header: emptyHeader
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: gridModeToggle; visible: true
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
function showDetails(uri)
|
||||
|
|
|
@ -12,6 +12,9 @@ Item {
|
|||
|
||||
function updateSelectionFromLauncher()
|
||||
{
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
model.selectVariantForAircraftURI(_launcher.selectedAircraft);
|
||||
var row = model.indexForURI(_launcher.selectedAircraft);
|
||||
if (row >= 0) {
|
||||
|
|
|
@ -15,6 +15,7 @@ ListHeaderBox
|
|||
|
||||
onCheckedChanged: {
|
||||
_launcher.browseAircraftModel.ratingsFilterEnabled = checked
|
||||
_launcher.saveUISetting("enable-ratings-filter", checked);
|
||||
}
|
||||
|
||||
label: qsTr("Filter using ratings")
|
||||
|
|
|
@ -92,10 +92,9 @@ Rectangle {
|
|||
screenPos = _launcher.mapToGlobal(title, Qt.point(0, 0))
|
||||
}
|
||||
|
||||
popupFrame.x = screenPos.x;
|
||||
popupFrame.y = screenPos.y;
|
||||
popupFrame.show()
|
||||
tracker.window = popupFrame
|
||||
var pop = popup.createObject(root, {x:screenPos.x, y:screenPos.y })
|
||||
tracker.window = pop;
|
||||
pop.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,59 +107,63 @@ Rectangle {
|
|||
id: tracker
|
||||
}
|
||||
|
||||
Window {
|
||||
id: popupFrame
|
||||
Component {
|
||||
id: popup
|
||||
|
||||
width: root.width
|
||||
flags: Qt.Popup
|
||||
height: choicesColumn.childrenRect.height
|
||||
color: "white"
|
||||
Window {
|
||||
id: popupFrame
|
||||
|
||||
Rectangle {
|
||||
border.width: 1
|
||||
border.color: Style.minorFrameColor
|
||||
anchors.fill: parent
|
||||
}
|
||||
width: root.width
|
||||
flags: Qt.Popup
|
||||
height: choicesColumn.childrenRect.height
|
||||
color: "white"
|
||||
|
||||
Column {
|
||||
id: choicesColumn
|
||||
Rectangle {
|
||||
border.width: 1
|
||||
border.color: Style.minorFrameColor
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Repeater {
|
||||
// would prefer the model to be conditional on visiblity,
|
||||
// but this trips up the Window sizing on Linux (Ubuntu at
|
||||
// least) and we get a mis-aligned origin
|
||||
model: aircraftInfo.variantNames
|
||||
Column {
|
||||
id: choicesColumn
|
||||
|
||||
delegate: Item {
|
||||
width: popupFrame.width
|
||||
height: choiceText.implicitHeight
|
||||
Repeater {
|
||||
// would prefer the model to be conditional on visiblity,
|
||||
// but this trips up the Window sizing on Linux (Ubuntu at
|
||||
// least) and we get a mis-aligned origin
|
||||
model: aircraftInfo.variantNames
|
||||
|
||||
Text {
|
||||
id: choiceText
|
||||
text: modelData
|
||||
delegate: Item {
|
||||
width: popupFrame.width
|
||||
height: choiceText.implicitHeight
|
||||
|
||||
// allow override the size in case the title size is enormous
|
||||
font.pixelSize: (popupFontPixelSize > 0) ? popupFontPixelSize : title.font.pixelSize
|
||||
Text {
|
||||
id: choiceText
|
||||
text: modelData
|
||||
|
||||
color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 4
|
||||
// allow override the size in case the title size is enormous
|
||||
font.pixelSize: (root.popupFontPixelSize > 0) ? root.popupFontPixelSize : title.font.pixelSize
|
||||
|
||||
color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: choiceArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popupFrame.hide()
|
||||
root.selected(model.index)
|
||||
MouseArea {
|
||||
id: choiceArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popupFrame.close()
|
||||
root.selected(model.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of delegate Item
|
||||
} // of Repeater
|
||||
}
|
||||
} // of popup frame
|
||||
} // of delegate Item
|
||||
} // of Repeater
|
||||
}
|
||||
} // of popup frame
|
||||
} // of popup component
|
||||
}
|
||||
|
|
|
@ -26,12 +26,19 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function isDisabled()
|
||||
{
|
||||
return (model.status !== CatalogListModel.Ok) ||
|
||||
(enableCheckbox.checked === false);
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
anchors.top: divider.bottom
|
||||
height: catalogTextColumn.childrenRect.height + Style.margin * 2
|
||||
width: parent.width
|
||||
|
||||
|
||||
Column {
|
||||
id: catalogTextColumn
|
||||
|
||||
|
@ -42,11 +49,28 @@ Item {
|
|||
anchors.rightMargin: Style.margin
|
||||
spacing: Style.margin
|
||||
|
||||
StyledText {
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
font.bold: true
|
||||
width: parent.width
|
||||
text: model.name
|
||||
Row {
|
||||
spacing: Style.margin
|
||||
height: headingText.height
|
||||
|
||||
Checkbox {
|
||||
id: enableCheckbox
|
||||
checked: model.enabled
|
||||
height: parent.height
|
||||
onCheckedChanged: model.enable = checked;
|
||||
// only allow the user to toggle enable/disable if
|
||||
// the catalog is valid
|
||||
visible: (model.status === CatalogListModel.Ok)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: headingText
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
font.bold: true
|
||||
width: catalogTextColumn.width - enableCheckbox.width
|
||||
text: model.name
|
||||
font.strikeout: delegateRoot.isDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import QtQuick 2.4
|
||||
import "."
|
||||
|
||||
Item {
|
||||
property bool checked: false
|
||||
|
@ -11,7 +12,7 @@ Item {
|
|||
id: checkBox
|
||||
width: 18
|
||||
height: 18
|
||||
border.color: mouseArea.containsMouse ? "#68A6E1" : "#9f9f9f"
|
||||
border.color: mouseArea.containsMouse ? Style.frameColor : Style.inactiveThemeColor
|
||||
border.width: 1
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
|
@ -22,7 +23,7 @@ Item {
|
|||
height: 12
|
||||
anchors.centerIn: parent
|
||||
id: checkMark
|
||||
color: "#9f9f9f"
|
||||
color: Style.themeColor
|
||||
visible: checked
|
||||
}
|
||||
}
|
||||
|
@ -41,5 +42,6 @@ Item {
|
|||
onClicked: {
|
||||
checked = !checked
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
|
36
src/GUI/qml/EnableDisableButton.qml
Normal file
36
src/GUI/qml/EnableDisableButton.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.0
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
width: height
|
||||
height: icon.implicitHeight + 1
|
||||
|
||||
property bool enable: true
|
||||
|
||||
signal clicked();
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
source: "qrc:///svg/icon-hide"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
// hoverEnabled: true
|
||||
onClicked: root.clicked();
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// Text {
|
||||
// anchors.right: root.left
|
||||
// anchors.rightMargin: Style.margin
|
||||
// anchors.verticalCenter: root.verticalCenter
|
||||
// visible: mouse.containsMouse
|
||||
// color: Style.baseTextColor
|
||||
// font.pixelSize: Style.baseFontPixelSize
|
||||
// text: root.enable ? qsTr("Click_to_disable")
|
||||
// : qsTr("Click_to_enable")
|
||||
// }
|
||||
}
|
32
src/GUI/qml/FavouriteToggleButton.qml
Normal file
32
src/GUI/qml/FavouriteToggleButton.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import QtQuick 2.4
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool checked: false
|
||||
|
||||
implicitWidth: icon.width + Style.margin
|
||||
implicitHeight: icon.height + Style.margin
|
||||
|
||||
signal toggle(var on);
|
||||
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
source: {
|
||||
var b = mouse.containsMouse ? !root.checked : root.checked;
|
||||
return b ? "qrc:///favourite-icon-filled" : "qrc:///favourite-icon-outline";
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: root.toggle(!root.checked);
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
|
@ -37,11 +37,12 @@ Item {
|
|||
hoverEnabled: root.enabled
|
||||
enabled: root.enabled
|
||||
onClicked: {
|
||||
var screenPos = _launcher.mapToGlobal(button, Qt.point(-popupFrame.width, 0))
|
||||
popupFrame.x = screenPos.x;
|
||||
popupFrame.y = screenPos.y;
|
||||
popupFrame.visible = true
|
||||
tracker.window = popupFrame
|
||||
var pop = popup.createObject(root)
|
||||
var screenPos = _launcher.mapToGlobal(button, Qt.point(-pop.width, 0))
|
||||
pop.y = screenPos.y;
|
||||
pop.x = screenPos.x;
|
||||
tracker.window = pop;
|
||||
pop.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,51 +51,54 @@ Item {
|
|||
id: tracker
|
||||
}
|
||||
|
||||
Window {
|
||||
id: popupFrame
|
||||
Component {
|
||||
id: popup
|
||||
|
||||
flags: Qt.Popup
|
||||
height: choicesColumn.childrenRect.height + Style.margin * 2
|
||||
width: choicesColumn.childrenRect.width + Style.margin * 2
|
||||
visible: false
|
||||
color: "white"
|
||||
Window {
|
||||
id: popupFrame
|
||||
|
||||
Rectangle {
|
||||
border.width: 1
|
||||
border.color: Style.minorFrameColor
|
||||
anchors.fill: parent
|
||||
}
|
||||
flags: Qt.Popup
|
||||
height: choicesColumn.childrenRect.height + Style.margin * 2
|
||||
width: choicesColumn.childrenRect.width + Style.margin * 2
|
||||
color: "white"
|
||||
|
||||
// text repeater
|
||||
Column {
|
||||
id: choicesColumn
|
||||
spacing: Style.margin
|
||||
x: Style.margin
|
||||
y: Style.margin
|
||||
Rectangle {
|
||||
border.width: 1
|
||||
border.color: Style.minorFrameColor
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: choicesRepeater
|
||||
model: root.model
|
||||
delegate:
|
||||
StyledText {
|
||||
id: choiceText
|
||||
// text repeater
|
||||
Column {
|
||||
id: choicesColumn
|
||||
spacing: Style.margin
|
||||
x: Style.margin
|
||||
y: Style.margin
|
||||
|
||||
// Taken from TableViewItemDelegateLoader.qml to follow QML role conventions
|
||||
text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel
|
||||
: modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject
|
||||
: modelData != undefined ? modelData : "" // Models without role
|
||||
height: implicitHeight + Style.margin
|
||||
Repeater {
|
||||
id: choicesRepeater
|
||||
model: root.model
|
||||
delegate:
|
||||
StyledText {
|
||||
id: choiceText
|
||||
|
||||
MouseArea {
|
||||
width: popupFrame.width // full width of the popup
|
||||
height: parent.height
|
||||
onClicked: {
|
||||
popupFrame.visible = false
|
||||
root.selected(model.index);
|
||||
// Taken from TableViewItemDelegateLoader.qml to follow QML role conventions
|
||||
text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel
|
||||
: modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject
|
||||
: modelData != undefined ? modelData : "" // Models without role
|
||||
height: implicitHeight + Style.margin
|
||||
|
||||
MouseArea {
|
||||
width: popupFrame.width // full width of the popup
|
||||
height: parent.height
|
||||
onClicked: {
|
||||
root.selected(model.index);
|
||||
popupFrame.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of Text delegate
|
||||
} // text repeater
|
||||
} // text column
|
||||
} // of popup Window
|
||||
} // of Text delegate
|
||||
} // text repeater
|
||||
} // text column
|
||||
} // of popup Window
|
||||
}
|
||||
}
|
||||
|
|
30
src/GUI/qml/IconButton.qml
Normal file
30
src/GUI/qml/IconButton.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import QtQuick 2.0
|
||||
import "."
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
radius: Style.roundRadius
|
||||
border.width: 1
|
||||
border.color: Style.themeColor
|
||||
width: height
|
||||
height: Style.baseFontPixelSize + Style.margin * 2
|
||||
color: mouse.containsMouse ? Style.minorFrameColor : "white"
|
||||
|
||||
property alias icon: icon.source
|
||||
|
||||
signal clicked();
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
width: parent.width - Style.margin
|
||||
height: parent.height - Style.margin
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
hoverEnabled: true
|
||||
onClicked: root.clicked();
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ Item {
|
|||
id: root
|
||||
// order of this model sets the order of buttons in the sidebar
|
||||
ListModel {
|
||||
id: pagesModel
|
||||
id: startupPagesModel
|
||||
ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///svg/toolbox-summary"; state:"loader" }
|
||||
ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///svg/toolbox-aircraft"; state:"loader" }
|
||||
|
||||
|
@ -28,6 +28,18 @@ Item {
|
|||
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: inAppPagesModel
|
||||
ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///svg/toolbox-summary"; state:"loader" }
|
||||
ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///svg/toolbox-aircraft"; state:"loader" }
|
||||
|
||||
ListElement {
|
||||
title: qsTr("Location"); pageSource: "qrc:///qml/Location.qml";
|
||||
iconPath: "qrc:///toolbox-location"; state:"loader"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
_launcher.minimumWindowSize = Qt.size(Style.strutSize * 12, sidebar.minimumHeight);
|
||||
|
@ -35,7 +47,7 @@ Item {
|
|||
|
||||
Connections {
|
||||
target: _location
|
||||
onSkipFromArgsChanged: pagesModel.setProperty(2, "buttonDisabled", _location.skipFromArgs)
|
||||
onSkipFromArgsChanged: startupPagesModel.setProperty(2, "buttonDisabled", _location.skipFromArgs)
|
||||
}
|
||||
|
||||
state: "loader"
|
||||
|
@ -82,7 +94,7 @@ Item {
|
|||
id: sidebar
|
||||
height: parent.height
|
||||
z: 1
|
||||
pagesModel: pagesModel
|
||||
pagesModel: _launcher.inAppMode ? inAppPagesModel : startupPagesModel
|
||||
selectedPage: 0 // open on the summary page
|
||||
|
||||
onShowMenu: menu.show();
|
||||
|
@ -127,7 +139,7 @@ Item {
|
|||
function selectPage(index)
|
||||
{
|
||||
sidebar.setSelectedPage(index);
|
||||
var page = pagesModel.get(index);
|
||||
var page = sidebar.pagesModel.get(index);
|
||||
pageLoader.source = page.pageSource
|
||||
root.state = page.state
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ Item {
|
|||
property bool __searchActive: false
|
||||
property string lastSearch
|
||||
|
||||
property bool showCarriers: false
|
||||
readonly property var locationModel: showCarriers ? _location.carriersModel : _location.searchModel
|
||||
|
||||
function backToSearch()
|
||||
{
|
||||
detailLoader.sourceComponent = null
|
||||
|
@ -33,6 +36,13 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function selectCarrier(name)
|
||||
{
|
||||
selectedLocation.guid = 0;
|
||||
_location.carrier = name;
|
||||
detailLoader.sourceComponent = carrierDetails
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// important so we can leave the location page and return to it,
|
||||
// preserving the state
|
||||
|
@ -46,6 +56,8 @@ Item {
|
|||
}
|
||||
} else if (_location.isBaseLatLon) {
|
||||
detailLoader.sourceComponent = navaidDetails
|
||||
} else if (_location.isCarrier) {
|
||||
detailLoader.sourceComponent = carrierDetails;
|
||||
} else {
|
||||
_location.showHistoryInSearchModel();
|
||||
}
|
||||
|
@ -69,6 +81,13 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: carrierDetails
|
||||
LocationCarrierView {
|
||||
id: carrierView
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "white"
|
||||
|
@ -129,7 +148,11 @@ Item {
|
|||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
root.selectLocation(model.guid, model.type);
|
||||
if (root.showCarriers) {
|
||||
root.selectCarrier(model.name);
|
||||
} else {
|
||||
root.selectLocation(model.guid, model.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,10 +183,12 @@ Item {
|
|||
anchors.topMargin: Style.margin
|
||||
}
|
||||
|
||||
|
||||
|
||||
SearchButton {
|
||||
id: searchButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.right: carriersButton.left
|
||||
anchors.top: headerText.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Style.margin
|
||||
|
@ -172,7 +197,8 @@ Item {
|
|||
placeholder: qsTr("Search for an airport or navaid");
|
||||
|
||||
onSearch: {
|
||||
// when th search term is cleared, show the history
|
||||
root.showCarriers = false;
|
||||
// when the search term is cleared, show the history
|
||||
if (term == "") {
|
||||
_location.showHistoryInSearchModel();
|
||||
return;
|
||||
|
@ -191,6 +217,18 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: carriersButton
|
||||
anchors.top: headerText.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Style.margin
|
||||
icon: "qrc:///svg/icon-carrier"
|
||||
|
||||
onClicked: {
|
||||
root.showCarriers = true;
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: searchHelpText
|
||||
anchors.right: parent.right
|
||||
|
@ -220,12 +258,12 @@ Item {
|
|||
anchors.topMargin: Style.margin
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
model: _location.searchModel
|
||||
model: root.locationModel
|
||||
delegate: locationSearchDelegate
|
||||
clip: true
|
||||
|
||||
header: Item {
|
||||
visible: _location.searchModel.isSearchActive
|
||||
visible: !root.showCarriers && _location.searchModel.isSearchActive
|
||||
width: parent.width
|
||||
height: visible ? 50 : 0
|
||||
|
||||
|
@ -246,7 +284,7 @@ Item {
|
|||
footer: Item {
|
||||
width: parent.width
|
||||
height: noResultsText.height
|
||||
visible: (parent.count === 0) && !_location.searchModel.isSearchActive
|
||||
visible: !root.showCarriers && (parent.count === 0) && !_location.searchModel.isSearchActive
|
||||
Text {
|
||||
id: noResultsText
|
||||
width: parent.width
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue