提交 ba4e310d 编写于 作者: 丁劲犇's avatar 丁劲犇 😸

Merge branch 'develop'

......@@ -438,11 +438,11 @@ namespace QTVOSM{
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "MOUSE_LBUTTON_DBLCLK";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "MOUSE_RBUTTON_BLCLK";
map_evt["name"] = "MOUSE_RBUTTON_DBLCLK";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "MOUSE_MBUTTON_BLCLK";
map_evt["name"] = "MOUSE_MBUTTON_DBLCLK";
else
map_evt["name"] = "MOUSE_BUTTON_BLCLK";
map_evt["name"] = "MOUSE_BUTTON_DBLCLK";
double tlat, tlon;
CV_DP2LLA(event->pos().x(),event->pos().y(),&tlat,&tlon);
map_evt["lat"] = tlat;
......
......@@ -3,6 +3,7 @@
#include <assert.h>
#include <QGraphicsSceneMouseEvent>
#include <math.h>
#include "qtvplugin_geomarker.h"
namespace QTVP_GEOMARKER{
geoGraphicsEllipseItem::geoGraphicsEllipseItem(
QString name,
......@@ -62,8 +63,68 @@ namespace QTVP_GEOMARKER{
QGraphicsEllipseItem::mousePressEvent(event);
bool bshow = this->props_visible();
this->show_props(!bshow);
//event->accept();
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_CLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_CLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_CLICKED";
else
map_evt["name"] = "ITEM_BUTTON_CLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
void geoGraphicsEllipseItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsEllipseItem::mouseDoubleClickEvent(event);
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_DBLCLICKED";
else
map_evt["name"] = "ITEM_BUTTON_DBLCLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
QPointF geoGraphicsEllipseItem::label_pos()
{
return QPointF(this->rect().right(),rect().top()-8);
......
......@@ -13,6 +13,7 @@ namespace QTVP_GEOMARKER{
qreal m_height;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);
public:
explicit geoGraphicsEllipseItem(QString name,QTVOSM::viewer_interface * pVi,
qreal cent_lat = 90,
......
......@@ -3,6 +3,8 @@
#include <assert.h>
#include <QGraphicsSceneMouseEvent>
#include <math.h>
#include "geographicsscene.h"
#include "qtvplugin_geomarker.h"
namespace QTVP_GEOMARKER{
geoGraphicsLineItem::geoGraphicsLineItem(
QString name,
......@@ -60,8 +62,67 @@ namespace QTVP_GEOMARKER{
QGraphicsLineItem::mousePressEvent(event);
bool bshow = this->props_visible();
this->show_props(!bshow);
//event->accept();
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_CLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_CLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_CLICKED";
else
map_evt["name"] = "ITEM_BUTTON_CLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
void geoGraphicsLineItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsLineItem::mouseDoubleClickEvent(event);
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_DBLCLICKED";
else
map_evt["name"] = "ITEM_BUTTON_DBLCLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
QPointF geoGraphicsLineItem::label_pos()
{
return QPointF(
......
......@@ -14,6 +14,8 @@ namespace QTVP_GEOMARKER{
void unwarrp();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);
public:
explicit geoGraphicsLineItem(QString name,QTVOSM::viewer_interface * pVi,
qreal lat1 = 90,
......
......@@ -3,6 +3,8 @@
#include <assert.h>
#include <QGraphicsSceneMouseEvent>
#include <math.h>
#include "geographicsscene.h"
#include "qtvplugin_geomarker.h"
namespace QTVP_GEOMARKER{
geoGraphicsPolygonItem::geoGraphicsPolygonItem(
QString name,
......@@ -68,8 +70,66 @@ namespace QTVP_GEOMARKER{
QGraphicsPolygonItem::mousePressEvent(event);
bool bshow = this->props_visible();
this->show_props(!bshow);
//event->accept();
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_CLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_CLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_CLICKED";
else
map_evt["name"] = "ITEM_BUTTON_CLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
void geoGraphicsPolygonItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsPolygonItem::mouseDoubleClickEvent(event);
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_DBLCLICKED";
else
map_evt["name"] = "ITEM_BUTTON_DBLCLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
QPointF geoGraphicsPolygonItem::label_pos()
{
QPolygonF p = this->polygon();
......
......@@ -12,6 +12,8 @@ namespace QTVP_GEOMARKER{
void unwarrp();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);
public:
explicit geoGraphicsPolygonItem(QString name,QTVOSM::viewer_interface * pVi,
const QPolygonF & lla_polygon
......
......@@ -4,6 +4,8 @@
#include <math.h>
#include <QGraphicsSceneMouseEvent>
#include <QMessageBox>
#include "geographicsscene.h"
#include "qtvplugin_geomarker.h"
namespace QTVP_GEOMARKER{
geoGraphicsRectItem::geoGraphicsRectItem(
QString name,
......@@ -62,8 +64,69 @@ namespace QTVP_GEOMARKER{
QGraphicsRectItem::mousePressEvent(event);
bool bshow = this->props_visible();
this->show_props(!bshow);
//event->accept();
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_CLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_CLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_CLICKED";
else
map_evt["name"] = "ITEM_BUTTON_CLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
void geoGraphicsRectItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsRectItem::mouseDoubleClickEvent(event);
//post enent
QMap<QString, QVariant > map_evt;
geoGraphicsScene * pscene = dynamic_cast<geoGraphicsScene *>(this->scene());
if (pscene)
{
QObject * pPlg = pscene->parent();
if (pPlg)
{
qtvplugin_geomarker * pMarker = dynamic_cast<qtvplugin_geomarker *>(pPlg) ;
if (pMarker)
{
map_evt["source"] = pMarker->get_name();
map_evt["destin"] = "ALL";
if (event->buttons() & Qt::LeftButton)
map_evt["name"] = "ITEM_LBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::RightButton)
map_evt["name"] = "ITEM_RBUTTON_DBLCLICKED";
else if (event->buttons() & Qt::MidButton)
map_evt["name"] = "ITEM_MBUTTON_DBLCLICKED";
else
map_evt["name"] = "ITEM_BUTTON_DBLCLICKED";
map_evt["id"] = this->item_name();
vi()->post_event(map_evt);
}
}
}
}
QPointF geoGraphicsRectItem::label_pos()
{
return QPointF(this->rect().right(),rect().top()-8);
......
......@@ -13,6 +13,8 @@ namespace QTVP_GEOMARKER{
qreal m_height;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);
public:
explicit geoGraphicsRectItem(QString name,QTVOSM::viewer_interface * pVi,
qreal cent_lat = 90,
......
......@@ -53,6 +53,7 @@ namespace QTVP_GEOMARKER{
QString m_name;
protected:
void setLevel (int currlevel);
QTVOSM::viewer_interface * vi(){return m_pVi;}
public:
int level () {return m_nCurrentLevel;}
geo_item_type item_type () const {return m_type;}
......
......@@ -235,45 +235,6 @@ bool qtvplugin_geomarker::cb_event(const QMap<QString, QVariant> para)
{
return false;
}
bool qtvplugin_geomarker::cb_mouseReleaseEvent(QMouseEvent * e)
{
if (!m_pVi)
return false;
QPoint mouse_view_pt = e->pos();
int winsz = 256 * (1<<m_pVi->level());
double wx,wy;
m_pVi->CV_DP2World(mouse_view_pt.x(),mouse_view_pt.y(),&wx,&wy);
//Warp
while (wx < 0) wx += winsz;
while (wx > winsz-1) wx -= winsz;
QPointF mouse_scene_pt(wx,wy);
QPoint mouse_screen_pt = e->globalPos();
Qt::MouseButton mouse_button = e->button();
QWidget * pwig = dynamic_cast<QWidget *> (m_pVi);
if (m_bVisible && pwig)
{
// Convert and deliver the mouse event to the scene.
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
mouseEvent.setWidget(pwig);
mouseEvent.setButtonDownScenePos(mouse_button, mouse_scene_pt);
mouseEvent.setButtonDownScreenPos(mouse_button, mouse_screen_pt);
mouseEvent.setScenePos(mouse_scene_pt);
mouseEvent.setScreenPos(mouse_screen_pt);
mouseEvent.setLastScenePos(mouse_scene_pt);
mouseEvent.setLastScreenPos(mouse_screen_pt);
mouseEvent.setButtons(e->buttons());
mouseEvent.setButton(e->button());
mouseEvent.setModifiers(e->modifiers());
mouseEvent.setAccepted(false);
QApplication::sendEvent(m_pScene, &mouseEvent);
bool isAccepted = mouseEvent.isAccepted();
e->setAccepted(isAccepted);
return isAccepted;
}
return false;
}
bool qtvplugin_geomarker::cb_mouseDoubleClickEvent(QMouseEvent * e)
{
......@@ -286,8 +247,8 @@ bool qtvplugin_geomarker::cb_mouseDoubleClickEvent(QMouseEvent * e)
double mlat, mlon;
m_pVi->CV_DP2World(mouse_view_pt.x(),mouse_view_pt.y(),&wx,&wy);
m_pVi->CV_DP2LLA(mouse_view_pt.x(),mouse_view_pt.y(),&mlat,&mlon);
ui->lineEdit_point_lat->setText(QString("%1").arg(mlat,0,'f',14));
ui->lineEdit_point_lon->setText(QString("%1").arg(mlon,0,'f',14));
ui->lineEdit_point_lat->setText(QString("%1").arg(mlat,0,'f',7));
ui->lineEdit_point_lon->setText(QString("%1").arg(mlon,0,'f',7));
//Warp
while (wx < 0) wx += winsz;
while (wx > winsz-1) wx -= winsz;
......@@ -415,7 +376,10 @@ QTVP_GEOMARKER::geoItemBase * qtvplugin_geomarker::update_line(const QString &
pitem->setPen(pen);
if (base == pitem)
{
pitem->setGeoLine(lat1,lon1,lat2,lon2);
res = pitem;
}
else if (false==this->m_pScene->addItem(pitem,0))
{
if (base != pitem)
......@@ -434,6 +398,8 @@ QTVP_GEOMARKER::geoItemBase * qtvplugin_geomarker::update_line(const QString &
}
return res;
}
QTVP_GEOMARKER::geoItemBase * qtvplugin_geomarker::update_region (const QString & name,const QPolygonF latlons, QPen pen, QBrush brush)
{
QTVP_GEOMARKER::geoItemBase * res = 0;
......@@ -457,7 +423,10 @@ QTVP_GEOMARKER::geoItemBase * qtvplugin_geomarker::update_region (const QStri
pitem->setPen(pen);
pitem->setBrush(brush);
if (base == pitem)
{
pitem->setGeoPolygon(latlons);
res = pitem;
}
else if (false==this->m_pScene->addItem(pitem,0))
{
if (base != pitem)
......
......@@ -4,6 +4,8 @@
#include <QWidget>
#include <QTranslator>
#include <QStandardItemModel>
#include <QMap>
#include <QXmlStreamAttributes>
#include "geographicsscene.h"
#include "../qtviewer_planetosm/osmtiles/layer_interface.h"
#include "../qtviewer_planetosm/osmtiles/viewer_interface.h"
......@@ -12,15 +14,46 @@ namespace Ui {
class qtvplugin_geomarker;
}
using namespace QTVOSM;
/*!
\brief qtvplugin_geomarker introduces QGraphicesView system, established a common approach for geo marking.
GEO marker is a vector symbol displays on the background OSM raster map. there are 3 different mark types supported by this plugin.
1. Point Marks. Include ellipse and rect style mark. these type of mark stand for a single point on earth, with a specified lat, lon .the with and height
for rect / ellipse circumrect can be specified by user at runtime, in PIXEL. with and height will stay still during map zoom.
2. Line Mark. Line mark is a beeline on map. ATTENTION, in Mercator Projection system, the geo shortest path between 2 points on earth is NOT a beeline, that means
beeline on a map is just for display performance and accessibility. the real path is a curve , which has 2 point of intersections exactly at start position and end position.
3. Polygon (Region) Mark. Polygon mark is a region on map. borders of a region is painted with lines, for a same reason above, the geo shortest path between 2 points on earth is NOT a beeline either.
Marks above shares a same style system provided by Qt painter system. pen, brush , font can be setted for each mark.
Each mark has a Uinque ID called "name", and a type enum called "type". It can also contain several user-defined properties, with a key-value style storage.
Especial, a user-defined property called "LABEL"(Upper case) is different agains others. the value of LABEL will be displayed as text items on map all the time,
but other properties will only visible when user click the mark.
When the mark is clicked, or double clicked, a event will be fired, so that all plugins and OCX containers will be noticed that a mark (with ID) is clicked.
\class qtvplugin_geomarker qtvplugin_geomarker.h "qtvplugin_geomarker/qtvplugin_geomarker.h"
*/
class qtvplugin_geomarker : public QWidget,public layer_interface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID OSMLayerInterface_iid )
Q_INTERFACES(QTVOSM::layer_interface)
private:
struct tag_xml_mark{
QString name;
int type;
QPolygonF geoPoints;
QMap<QString, QString> styles;
QMap<QString, QString> props;
};
public:
qtvplugin_geomarker(QWidget *parent = 0);
~qtvplugin_geomarker();
QWidget * get_propWindow() {return this;}
QString get_name();
/*! Get graphicalScence, the scence is a World coord in current level.
* in plugin system and draw tasks, user should divide class from QGraphicsItem
* and maintain lat, lon coords to world coords according to current level
......@@ -30,36 +63,54 @@ public:
private:
int m_nInstance;
QTVP_GEOMARKER::geoGraphicsScene * m_pScene;
QTVP_GEOMARKER::geoGraphicsScene * m_pScene; //! the graphics scene object pointer.
QTranslator pluginTranslator;
Ui::qtvplugin_geomarker *ui;
viewer_interface * m_pVi;
viewer_interface * m_pVi; //! viewer_interface interface provides coordinats convertions.
bool m_bVisible;
QString m_SLLibName;
QString m_SLLibPath;
private:
/*! a timer provides timing ui-refresh , instead of immediately refresh when item has been updated.
* This timer is just affects UI widgets, Map will be updated immediately otherwise.
*/
int m_nTimerID_refreshUI;
bool m_bNeedRefresh;
QStandardItemModel * m_pLineStyleModel;
QStandardItemModel * m_pFillStyleModel;
QStandardItemModel * m_pGeoItemModel;
QStandardItemModel * m_pGeoPropModel;
//persistent functions
private:
void SaveSettingsToIni();
void saveSettingsToIni();
void loadSettingsFromIni();
bool saveToXml(QString xml);
bool loadFromXml(QString xml);
bool readMark(QXmlStreamReader & reader, tag_xml_mark & mark,QString & errMsg);
bool readGeo(QXmlStreamReader & reader, tag_xml_mark & mark,QString & errMsg);
bool readStyle(QXmlStreamReader & reader, tag_xml_mark & mark,QString & errMsg);
bool readProps(QXmlStreamReader & reader, tag_xml_mark & mark,QString & errMsg);
QMap<QString,QString> attribs(QXmlStreamAttributes & ats);
void loadTranslations();
QString inifile();
//UI refreshing functions
private:
void refreshMarks();
void refreshItemUI(QString markname);
void refreshProps(QTVP_GEOMARKER::geoItemBase * itm);
QColor string2color(const QString & s);
QString color2string(const QColor & c);
//Geo mark updating functions
private:
template <class T>
QTVP_GEOMARKER::geoItemBase * update_point (const QString & name,double lat, double lon, int width, int height, QPen pen, QBrush brush);
QTVP_GEOMARKER::geoItemBase * update_line (const QString & name,double lat1, double lon1,double lat2, double lon2, QPen pen);
QTVP_GEOMARKER::geoItemBase * update_region (const QString & name,const QPolygonF latlons, QPen pen, QBrush brush);
bool update_mark(tag_xml_mark & mark);
//overloaded virtual funtions
protected:
layer_interface * load_initial_plugin(QString strSLibPath,viewer_interface * ptrviewer);
QWidget * load_prop_window();
......@@ -67,18 +118,18 @@ protected:
bool is_visible();
void set_visible(bool vb);
QString get_name();
void set_name(QString vb);
void cb_paintEvent( QPainter * pImage );
void cb_levelChanged(int);
bool cb_mousePressEvent(QMouseEvent *);
bool cb_mouseReleaseEvent(QMouseEvent *);
bool cb_mouseDoubleClickEvent(QMouseEvent *);
bool cb_event(const QMap<QString, QVariant>);
void timerEvent(QTimerEvent * e);
QMap<QString, QVariant> call_func(const QMap<QString, QVariant> /*paras*/);
//ui slots
protected slots:
void on_pushButton_update_clicked();
void on_radioButton_tool_point_toggled(bool);
......@@ -94,6 +145,8 @@ protected slots:
void on_pushButton_pickToLine1_clicked();
void on_pushButton_pickToLine2_clicked();
void on_pushButton_getRegion_clicked();
void on_pushButton_save_clicked();
void on_pushButton_load_clicked();
};
template <class T>
......@@ -124,8 +177,9 @@ QTVP_GEOMARKER::geoItemBase * qtvplugin_geomarker::update_point(const QString &
{
pitem->setCenter(lat,lon);
pitem->setSize(width,height);
res = pitem;
}
if (false==this->m_pScene->addItem(pitem,0))
else if (false==this->m_pScene->addItem(pitem,0))
{
if (base != pitem)
delete pitem;
......
......@@ -6,14 +6,29 @@
<rect>
<x>0</x>
<y>0</y>
<width>461</width>
<height>497</height>
<width>446</width>
<height>457</height>
</rect>
</property>
<property name="windowTitle">
<string>Grid</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
......@@ -77,6 +92,9 @@
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pushButton_del">
<property name="text">
......@@ -91,6 +109,20 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_save">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_load">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
......
#include "qtvplugin_geomarker.h"
#include "ui_qtvplugin_geomarker.h"
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "geographicsellipseitem.h"
#include "geographicslineitem.h"
#include "geographicspolygonitem.h"
#include "geographicsrectitem.h"
#include <QDebug>
#include <QXmlStreamAttributes>
#include <QMap>
/**
* geomarker supports these function calls.
*
......@@ -34,3 +42,428 @@ QMap<QString, QVariant> qtvplugin_geomarker::call_func(const QMap<QString, QVar
res["error"] = "\"function\" keyword not specified, nothing to do.";
return std::move(res);
}
bool qtvplugin_geomarker::saveToXml(QString xml)
{
QFile fp(xml);
if (fp.open(QIODevice::WriteOnly)==false)
return false;
QXmlStreamWriter stream(&fp);
stream.setAutoFormatting(true);
//0. Start Document
stream.writeStartDocument();
stream.writeStartElement("geomarker");
stream.writeAttribute("version","1.0");
//1. for each mark, write a root element
QList<QTVP_GEOMARKER::geoItemBase *> items = m_pScene->geo_items();
foreach (QTVP_GEOMARKER::geoItemBase * item, items)
{
QTVP_GEOMARKER::geo_item_type x_tp = item->item_type();
QString x_name = item->item_name();
//1.1. Mark
stream.writeStartElement("mark");
stream.writeAttribute("name",x_name);
stream.writeAttribute("type",QString("%1").arg((int)x_tp));
switch (x_tp)
{
case QTVP_GEOMARKER::ITEAMTYPE_RECT_POINT:
{
QTVP_GEOMARKER::geoGraphicsRectItem * pU = dynamic_cast<QTVP_GEOMARKER::geoGraphicsRectItem *>(item);
if (pU)
{
//1.2. geo
stream.writeStartElement("geo");
stream.writeAttribute("coords","1");
stream.writeTextElement("cod",QString("%1,%2").arg(pU->lat(),0,'f',7).arg(pU->lon(),0,'f',7));
stream.writeEndElement(); // geo
//1.2 style
stream.writeStartElement("style");
stream.writeTextElement("width",QString("%1").arg(pU->width()));
stream.writeTextElement("height",QString("%1").arg(pU->height()));
stream.writeTextElement("color_pen",color2string(pU->pen().color()));
stream.writeTextElement("style_pen",QString("%1").arg(int(pU->pen().style())));
stream.writeTextElement("width_pen",QString("%1").arg(int(pU->pen().width())));
stream.writeTextElement("color_brush",color2string(pU->brush().color()));
stream.writeTextElement("style_brush",QString("%1").arg(int(pU->brush().style())));
}
}
break;
case QTVP_GEOMARKER::ITEAMTYPE_ELLIPSE_POINT:
{
QTVP_GEOMARKER::geoGraphicsEllipseItem * pU = dynamic_cast<QTVP_GEOMARKER::geoGraphicsEllipseItem *>(item);
if (pU)
{
//1.2. geo
stream.writeStartElement("geo");
stream.writeAttribute("coords","1");
stream.writeTextElement("cod",QString("%1,%2").arg(pU->lat(),0,'f',7).arg(pU->lon(),0,'f',7));
stream.writeEndElement(); // geo
//1.2 style
stream.writeStartElement("style");
stream.writeTextElement("width",QString("%1").arg(pU->width()));
stream.writeTextElement("height",QString("%1").arg(pU->height()));
stream.writeTextElement("color_pen",color2string(pU->pen().color()));
stream.writeTextElement("style_pen",QString("%1").arg(int(pU->pen().style())));
stream.writeTextElement("width_pen",QString("%1").arg(int(pU->pen().width())));
stream.writeTextElement("color_brush",color2string(pU->brush().color()));
stream.writeTextElement("style_brush",QString("%1").arg(int(pU->brush().style())));
}
}
break;
case QTVP_GEOMARKER::ITEAMTYPE_LINE:
{
QTVP_GEOMARKER::geoGraphicsLineItem * pU = dynamic_cast<QTVP_GEOMARKER::geoGraphicsLineItem *>(item);
if (pU)
{
//1.2. geo
stream.writeStartElement("geo");
stream.writeAttribute("coords","2");
stream.writeTextElement("cod",QString("%1,%2").arg(pU->lat1(),0,'f',7).arg(pU->lon1(),0,'f',7));
stream.writeTextElement("cod",QString("%1,%2").arg(pU->lat2(),0,'f',7).arg(pU->lon2(),0,'f',7));
stream.writeEndElement(); // geo
//1.2 style
stream.writeStartElement("style");
stream.writeTextElement("color_pen",color2string(pU->pen().color()));
stream.writeTextElement("style_pen",QString("%1").arg(int(pU->pen().style())));
stream.writeTextElement("width_pen",QString("%1").arg(int(pU->pen().width())));
}
}
break;
case QTVP_GEOMARKER::ITEAMTYPE_REGION:
{
QTVP_GEOMARKER::geoGraphicsPolygonItem * pU = dynamic_cast<QTVP_GEOMARKER::geoGraphicsPolygonItem *>(item);
if (pU)
{
//1.2. geo
stream.writeStartElement("geo");
QPolygonF pl = pU->llas();
int nPl = pl.size();
stream.writeAttribute("coords",QString("%1").arg(nPl));
foreach (QPointF pf, pl)
stream.writeTextElement("cod",QString("%1,%2").arg(pf.y(),0,'f',7).arg(pf.x(),0,'f',7));
stream.writeEndElement(); // geo
//1.2 style
stream.writeStartElement("style");
stream.writeTextElement("color_pen",color2string(pU->pen().color()));
stream.writeTextElement("style_pen",QString("%1").arg(int(pU->pen().style())));
stream.writeTextElement("width_pen",QString("%1").arg(int(pU->pen().width())));
stream.writeTextElement("color_brush",color2string(pU->brush().color()));
stream.writeTextElement("style_brush",QString("%1").arg(int(pU->brush().style())));
}
}
break;
default:
break;
}
QColor colorText = item->labelColor();
stream.writeTextElement("color_label",color2string(colorText));
int fsize = item->labelFont().pointSize();
stream.writeTextElement("size_label",QString("%1").arg(fsize));
int weight = item->labelFont().weight();
stream.writeTextElement("weight_label",QString("%1").arg(weight));
stream.writeEndElement(); // style
//1.2 properties
stream.writeStartElement("properties");
int props = item->prop_counts();
QStringList lstNames = item->prop_names();
QVariantList lstValues = item->prop_values();
for (int i=0;i<props;++i)
stream.writeTextElement(lstNames[i],lstValues[i].toString());
//1.2 properties
stream.writeEndElement();
// 1.1mark
stream.writeEndElement();
}
stream.writeEndElement();
//0. End Document
stream.writeEndDocument();
fp.flush();
fp.close();
return true;
}
QMap<QString,QString> qtvplugin_geomarker::attribs(QXmlStreamAttributes & ats)
{
QMap<QString, QString > attr;
foreach(QXmlStreamAttribute a, ats)
attr[a.name().toString()] = a.value().toString();
return std::move(attr);
}
bool qtvplugin_geomarker::loadFromXml(QString xmlfile)
{
bool res = true;
QString errMessage;
QFile fp(xmlfile);
if (fp.open(QIODevice::ReadOnly)==false)
return false;
QXmlStreamReader xml(&fp);
if (xml.readNextStartElement())
{
if (xml.name()=="geomarker")
{
QMap<QString,QString> att_geoMarker = attribs(xml.attributes());
if (att_geoMarker["version"].toDouble()>=1)
{
while (!xml.atEnd() && res)
{
xml.readNext();
if (xml.tokenType()==QXmlStreamReader::StartElement)
{
if (xml.name()=="mark")
{
tag_xml_mark mark;
res = readMark(xml,mark,errMessage);
if (res)
res = update_mark(mark);
}
}
}
}
else
{
res = false;
errMessage = tr("Version must >=1.0.");
}
}
else
{
res = false;
errMessage = tr("This XML is not a geomarker format file.");
}
}
else
{
res = false;
errMessage = tr("Empty XML.");
}
fp.close();
if (res==false)
{
QMap<QString,QVariant> evt_error;
evt_error["source"] = get_name();
evt_error["destin"] = "ALL";
evt_error["name"] = "error";
evt_error["class"] = "QXmlStreamReader";
evt_error["file"] = xmlfile;
evt_error["detail"] = errMessage;
m_pVi->post_event(evt_error);
}
return res;
}
bool qtvplugin_geomarker::readMark(QXmlStreamReader & xml,tag_xml_mark & mark, QString & errMessage)
{
//mark
QMap<QString,QString > att_marker = attribs(xml.attributes());
mark.name = att_marker["name"];
mark.type = att_marker["type"].toInt();
if (mark.name.size()==0 || mark.type < 1)
{
errMessage = tr("mark name is null or type error .");
return false;
}
while (xml.readNextStartElement())
{
if (xml.name()=="geo")
{
if (false==readGeo(xml,mark,errMessage))
return false;
}
else if (xml.name()=="style")
{
if (false==readStyle(xml,mark,errMessage))
return false;
}
else if (xml.name()=="properties")
{
if (false==readProps(xml,mark,errMessage))
return false;
}
else
;
}
return true;
}
bool qtvplugin_geomarker::readGeo(QXmlStreamReader & xml,tag_xml_mark & mark, QString & errMessage)
{
QMap<QString,QString > att_geo = attribs(xml.attributes());
int coords = att_geo["coords"].toInt();
if (coords<1)
{
errMessage = tr("coords is 0 .");
return false;
}
for (int i=0;i<coords; ++i)
{
QPointF curCd;
if (false ==xml.readNextStartElement())
{
errMessage = tr("missing geo coords .");
return false;
}
QString txtCod = xml.readElementText();
QStringList lst = txtCod.split(",");
curCd.setY(lst.first().toFloat());
curCd.setX(lst.last().toFloat());
mark.geoPoints.push_back(curCd);
}
xml.skipCurrentElement();
return true;
}
bool qtvplugin_geomarker::readStyle(QXmlStreamReader & xml,tag_xml_mark & mark, QString & /*errMessage*/)
{
do
{
if (false==xml.readNextStartElement())
break;
else
{
QString prop_name = xml.name().toString();
QString prop_v = xml.readElementText();
mark.styles[prop_name] = prop_v;
}
}while(xml.error()==QXmlStreamReader::NoError);
return true;
}
bool qtvplugin_geomarker::readProps(QXmlStreamReader & xml,tag_xml_mark & mark, QString & /*errMessage*/)
{
do
{
if (false==xml.readNextStartElement())
break;
else
{
QString prop_name = xml.name().toString();
QString prop_v = xml.readElementText();
mark.props[prop_name] = prop_v;
}
}while(xml.error()==QXmlStreamReader::NoError);
return true;
}
bool qtvplugin_geomarker::update_mark(tag_xml_mark & mark)
{
if (mark.geoPoints.size()==0)
return false;
QString name = mark.name;
//Get pen and brush settings
Qt::PenStyle pst [] ={
Qt::NoPen ,
Qt::SolidLine ,
Qt::DashLine ,
Qt::DotLine ,
Qt::DashDotLine ,
Qt::DashDotDotLine ,
Qt::CustomDashLine
};
Qt::BrushStyle bst [] = {
Qt::NoBrush,
Qt::SolidPattern,
Qt::Dense1Pattern,
Qt::Dense2Pattern,
Qt::Dense3Pattern,
Qt::Dense4Pattern,
Qt::Dense5Pattern,
Qt::Dense6Pattern,
Qt::Dense7Pattern,
Qt::HorPattern,
Qt::VerPattern,
Qt::CrossPattern,
Qt::BDiagPattern,
Qt::FDiagPattern,
Qt::DiagCrossPattern
};
int ptdd = mark.styles["style_pen"].toInt();
if (ptdd < 0 || ptdd >=7)
ptdd = 1;
QColor penColor = string2color( mark.styles["color_pen"]);
int penWidth = mark.styles["width_pen"].toInt();
if (penWidth<0) penWidth = 1;
QPen pen;//(QBrush(color),width,pst[ptdd]);
pen.setColor(penColor);
pen.setWidth(penWidth);
pen.setStyle(pst[ptdd]);
int btdd = mark.styles["style_brush"].toInt();
if (btdd < 0 || btdd >=15)
btdd = 1;
QColor brushColor = string2color( mark.styles["color_brush"]);
QBrush brush;
brush.setColor(brushColor);
brush.setStyle(bst[btdd]);
QTVP_GEOMARKER::geoItemBase * newitem = 0;
if (mark.type==QTVP_GEOMARKER::ITEAMTYPE_RECT_POINT || mark.type==QTVP_GEOMARKER::ITEAMTYPE_ELLIPSE_POINT)
{
double lat = mark.geoPoints.first().y();
double lon = mark.geoPoints.first().x();
int width = mark.styles["width"].toInt();
if (width ==0) width = 8;
int height = mark.styles["height"].toInt();
if (height ==0) height = 8;
if (mark.type==QTVP_GEOMARKER::ITEAMTYPE_RECT_POINT)
newitem = update_point<QTVP_GEOMARKER::geoGraphicsRectItem>(name,lat,lon,width,height,pen,brush);
else
newitem = update_point<QTVP_GEOMARKER::geoGraphicsEllipseItem>(name,lat,lon,width,height,pen,brush);
}
else if (mark.type==QTVP_GEOMARKER::ITEAMTYPE_LINE)
{
if ( mark.geoPoints.size()<2)
return false;
double lat1 = mark.geoPoints.first().y();
double lon1 = mark.geoPoints.first().x();
double lat2 = mark.geoPoints.last().y();
double lon2 = mark.geoPoints.last().x();
newitem = update_line(name,lat1,lon1,lat2,lon2,pen);
}
else if (mark.type==QTVP_GEOMARKER::ITEAMTYPE_REGION)
{
newitem = update_region(name,mark.geoPoints,pen,brush);
}
else
return false;
if (newitem)
{
int fontSz = mark.styles["size_label"].toInt();
if (fontSz==0) fontSz = 9;
int fontWeight = mark.styles["weight_label"].toInt();
QColor textColor = string2color( mark.styles["color_label"]);
QFont f = newitem->labelFont();
f.setPointSize(fontSz);
f.setWeight(fontWeight);
newitem->setLabelFont(f);
newitem->setLabelColor(textColor);
for (QMap<QString,QString>::iterator p = mark.props.begin();p != mark.props.end();++p )
{
newitem->set_prop_data(p.key(),p.value());
}
}
return true;
}
......@@ -6,6 +6,8 @@
#include <QMap>
#include <QRegExp>
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include "geographicsellipseitem.h"
#include "geographicsrectitem.h"
#include "geographicslineitem.h"
......@@ -117,7 +119,7 @@ void qtvplugin_geomarker::on_pushButton_pickToLine2_clicked()
}
void qtvplugin_geomarker::SaveSettingsToIni()
void qtvplugin_geomarker::saveSettingsToIni()
{
QSettings settings(inifile(),QSettings::IniFormat);
int radioButton_tool_point = 0;
......@@ -241,7 +243,7 @@ void qtvplugin_geomarker::on_pushButton_update_clicked()
if (m_pVi==0 || !m_pScene)
return;
QString name = ui->lineEdit_currentID->text();
SaveSettingsToIni();
saveSettingsToIni();
//Get pen and brush settings
Qt::PenStyle pst [] ={
......@@ -375,6 +377,7 @@ void qtvplugin_geomarker::on_pushButton_del_clicked()
void qtvplugin_geomarker::on_pushButton_prop_update_clicked()
{
saveSettingsToIni();
QString name = ui->lineEdit_currentID->text();
//Fill in the pages
QTVP_GEOMARKER::geoItemBase * item = m_pScene->geoitem_by_name(name);
......@@ -382,6 +385,18 @@ void qtvplugin_geomarker::on_pushButton_prop_update_clicked()
{
item->set_prop_data(ui->lineEdit_prop_name->text(),ui->lineEdit_prop_string->text());
this->refreshProps(item);
this->refreshMarks();
//Update Font
int fontSz = ui->spinBox_fontSize->value();
int fontWeight = ui->spinBox_textWeight->value();
QColor textColor = string2color( ui->lineEdit_TextColor->text());
QFont f = item->labelFont();
f.setPointSize(fontSz);
f.setWeight(fontWeight);
item->setLabelFont(f);
item->setLabelColor(textColor);
m_pVi->UpdateWindow();
}
}
void qtvplugin_geomarker::on_pushButton_prop_delete_clicked()
......@@ -436,7 +451,7 @@ void qtvplugin_geomarker::on_pushButton_getRegion_clicked()
{
QString latkey = QString("lat%1").arg(i);
QString lonkey = QString("lon%1").arg(i);
strPlainText += QString("%1,%2;\n").arg(outPara[latkey].toDouble(),0,'f',14).arg(outPara[lonkey].toDouble(),0,'f',14);
strPlainText += QString("%1,%2;\n").arg(outPara[latkey].toDouble(),0,'f',7).arg(outPara[lonkey].toDouble(),0,'f',7);
}
}
ui->plainTextEdit_corners->setPlainText(strPlainText);
......@@ -462,8 +477,8 @@ void qtvplugin_geomarker::refreshItemUI(QString markname)
break;
pen = pitem->pen();
brush = pitem->brush();
ui->lineEdit_point_lat->setText(QString("%1").arg(pitem->lat(),0,'f',14));
ui->lineEdit_point_lon->setText(QString("%1").arg(pitem->lon(),0,'f',14));
ui->lineEdit_point_lat->setText(QString("%1").arg(pitem->lat(),0,'f',7));
ui->lineEdit_point_lon->setText(QString("%1").arg(pitem->lon(),0,'f',7));
ui->radioButton_PointRect->setChecked(true);
ui->spinBox_point_width->setValue(pitem->width());
ui->spinBox_point_size_height->setValue(pitem->height());
......@@ -477,8 +492,8 @@ void qtvplugin_geomarker::refreshItemUI(QString markname)
break;
pen = pitem->pen();
brush = pitem->brush();
ui->lineEdit_point_lat->setText(QString("%1").arg(pitem->lat(),0,'f',14));
ui->lineEdit_point_lon->setText(QString("%1").arg(pitem->lon(),0,'f',14));
ui->lineEdit_point_lat->setText(QString("%1").arg(pitem->lat(),0,'f',7));
ui->lineEdit_point_lon->setText(QString("%1").arg(pitem->lon(),0,'f',7));
ui->radioButton_PointRound->setChecked(true);
ui->spinBox_point_width->setValue(pitem->width());
ui->spinBox_point_size_height->setValue(pitem->height());
......@@ -491,10 +506,10 @@ void qtvplugin_geomarker::refreshItemUI(QString markname)
if (!pitem)
break;
pen = pitem->pen();
ui->lineEdit_lineLat1->setText(QString("%1").arg(pitem->lat1(),0,'f',14));
ui->lineEdit_lineLat2->setText(QString("%1").arg(pitem->lat2(),0,'f',14));
ui->lineEdit_lineLon1->setText(QString("%1").arg(pitem->lon1(),0,'f',14));
ui->lineEdit_lineLon2->setText(QString("%1").arg(pitem->lon2(),0,'f',14));
ui->lineEdit_lineLat1->setText(QString("%1").arg(pitem->lat1(),0,'f',7));
ui->lineEdit_lineLat2->setText(QString("%1").arg(pitem->lat2(),0,'f',7));
ui->lineEdit_lineLon1->setText(QString("%1").arg(pitem->lon1(),0,'f',7));
ui->lineEdit_lineLon2->setText(QString("%1").arg(pitem->lon2(),0,'f',7));
ui->radioButton_tool_line->setChecked(true);
}
break;
......@@ -508,7 +523,7 @@ void qtvplugin_geomarker::refreshItemUI(QString markname)
QPolygonF pol = pitem->llas();
QString strPlainText;
foreach (QPointF p, pol)
strPlainText += QString("%1,%2\n").arg(p.y(),0,'f',14).arg(p.x(),0,'f',14);
strPlainText += QString("%1,%2;\n").arg(p.y(),0,'f',7).arg(p.x(),0,'f',7);
ui->plainTextEdit_corners->setPlainText(strPlainText);
ui->radioButton_tool_polygon->setChecked(true);
}
......@@ -563,4 +578,42 @@ void qtvplugin_geomarker::refreshProps(QTVP_GEOMARKER::geoItemBase * itm)
lstValues.pop_front();
}
}
void qtvplugin_geomarker::on_pushButton_save_clicked()
{
QSettings settings(inifile(),QSettings::IniFormat);
QString strLastSaveImgDir = settings.value("history/last_save_xml_dir","./").toString();
QString newfm = QFileDialog::getSaveFileName(this,tr("save to xml"),strLastSaveImgDir,
"xml (*.xml);;All files(*.*)"
);
if (newfm.size()>2)
{
if (true==saveToXml(newfm))
{
settings.setValue("history/last_save_xml_dir",newfm);
QMessageBox::information(this,tr("succeed"),tr("Successfully saved XML file") + newfm);
}
else
QMessageBox::warning(this,tr("failed"),tr("Save XML file") + newfm + tr(" Failed"));
}
}
void qtvplugin_geomarker::on_pushButton_load_clicked()
{
QSettings settings(inifile(),QSettings::IniFormat);
QString strLastSaveImgDir = settings.value("history/last_open_xml_dir","./").toString();
QString newfm = QFileDialog::getOpenFileName(this,tr("load from xml"),strLastSaveImgDir,
"xml (*.xml);;All files(*.*)"
);
if (newfm.size()>2)
{
if (true==loadFromXml(newfm))
{
settings.setValue("history/last_open_xml_dir",newfm);
QMessageBox::information(this,tr("succeed"),tr("Successfully load XML file") + newfm);
}
else
QMessageBox::warning(this,tr("failed"),tr("Load XML file") + newfm + tr(" Failed"));
}
refreshMarks();
m_pVi->UpdateWindow();
}
......@@ -33,14 +33,22 @@ void testcontainer::closeEvent(QCloseEvent *)
}
void testcontainer::slot_message(QString message)
{
QAxBase * base = qobject_cast<QAxBase *>(sender());
QList<QStandardItem *> list_newrow;
list_newrow << new QStandardItem(QString("%1").arg((quint64)base));
list_newrow << new QStandardItem(QString("%1").arg(message));
m_pModel->appendRow(list_newrow);
while (m_pModel->rowCount()>1024)
m_pModel->removeRow(0);
ui->tableView_msg->scrollToBottom();
if (message.contains("MOUSE_MOVE"))
{
ui->label_mouseMove->setText(message);
}
else
{
QAxBase * base = qobject_cast<QAxBase *>(sender());
QList<QStandardItem *> list_newrow;
list_newrow << new QStandardItem(QString("%1").arg((quint64)base));
list_newrow << new QStandardItem(QString("%1").arg(message));
m_pModel->appendRow(list_newrow);
while (m_pModel->rowCount()>1024)
m_pModel->removeRow(0);
ui->tableView_msg->scrollToBottom();
}
}
void testcontainer::on_pushButton_test_adds_clicked()
......
......@@ -130,6 +130,13 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_mouseMove">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册