Repo created
251
qt/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
project(desktop)
|
||||
|
||||
QT6_ADD_RESOURCES(RES_SOURCES res/resources.qrc)
|
||||
|
||||
set(SRC
|
||||
about.cpp
|
||||
about.hpp
|
||||
bookmark_dialog.cpp
|
||||
bookmark_dialog.hpp
|
||||
build_style/build_common.cpp
|
||||
build_style/build_common.h
|
||||
build_style/build_drules.cpp
|
||||
build_style/build_drules.h
|
||||
build_style/build_phone_pack.cpp
|
||||
build_style/build_phone_pack.h
|
||||
build_style/build_skins.cpp
|
||||
build_style/build_skins.h
|
||||
build_style/build_statistics.cpp
|
||||
build_style/build_statistics.h
|
||||
build_style/build_style.cpp
|
||||
build_style/build_style.h
|
||||
build_style/run_tests.cpp
|
||||
build_style/run_tests.h
|
||||
create_feature_dialog.cpp
|
||||
create_feature_dialog.hpp
|
||||
draw_widget.cpp
|
||||
draw_widget.hpp
|
||||
editor_dialog.cpp
|
||||
editor_dialog.hpp
|
||||
info_dialog.cpp
|
||||
info_dialog.hpp
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.hpp
|
||||
mwms_borders_selection.cpp
|
||||
mwms_borders_selection.hpp
|
||||
osm_auth_dialog.cpp
|
||||
osm_auth_dialog.hpp
|
||||
place_page_dialog_common.cpp
|
||||
place_page_dialog_common.hpp
|
||||
place_page_dialog_developer.cpp
|
||||
place_page_dialog_developer.hpp
|
||||
place_page_dialog_user.cpp
|
||||
place_page_dialog_user.hpp
|
||||
preferences_dialog.cpp
|
||||
preferences_dialog.hpp
|
||||
popup_menu_holder.cpp
|
||||
popup_menu_holder.hpp
|
||||
routing_settings_dialog.cpp
|
||||
routing_settings_dialog.hpp
|
||||
routing_turns_visualizer.cpp
|
||||
routing_turns_visualizer.hpp
|
||||
ruler.cpp
|
||||
ruler.hpp
|
||||
screenshoter.cpp
|
||||
screenshoter.hpp
|
||||
search_panel.cpp
|
||||
search_panel.hpp
|
||||
selection.hpp
|
||||
update_dialog.cpp
|
||||
update_dialog.hpp
|
||||
)
|
||||
|
||||
omim_add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${RES_SOURCES} ${SRC})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC ON)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
generator # For borders::LoadBorders
|
||||
qt_common
|
||||
map
|
||||
gflags::gflags
|
||||
location_service
|
||||
)
|
||||
|
||||
if (BUILD_DESIGNER)
|
||||
target_link_libraries(${PROJECT_NAME} generator)
|
||||
set(BUNDLE_NAME "CoMaps.Designer")
|
||||
set(BUNDLE_DISPLAY_NAME "CoMaps Designer")
|
||||
set(BUNDLE_ICON designer.icns)
|
||||
else()
|
||||
set(BUNDLE_NAME "CoMaps")
|
||||
set(BUNDLE_DISPLAY_NAME "CoMaps Desktop")
|
||||
set(BUNDLE_ICON mac.icns)
|
||||
endif()
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${BUNDLE_NAME})
|
||||
|
||||
set(BUNDLE_EXECUTABLE ${BUNDLE_NAME})
|
||||
|
||||
set(BUNDLE_FOLDER ${CMAKE_BINARY_DIR}/${BUNDLE_NAME}.app)
|
||||
set(RESOURCES_FOLDER ${BUNDLE_FOLDER}/Contents/Resources)
|
||||
set(DATA_DIR ${OMIM_ROOT}/data)
|
||||
|
||||
execute_process(
|
||||
COMMAND mkdir -p ${RESOURCES_FOLDER}
|
||||
)
|
||||
function(copy_resources)
|
||||
foreach(file ${ARGN})
|
||||
execute_process(
|
||||
COMMAND cp -r ${DATA_DIR}/${file} ${RESOURCES_FOLDER}
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
copy_resources(
|
||||
countries-strings
|
||||
fonts
|
||||
symbols
|
||||
welcome.html
|
||||
|
||||
categories.txt
|
||||
categories_cuisines.txt
|
||||
classificator.txt
|
||||
colors.txt
|
||||
copyright.html
|
||||
countries.txt
|
||||
drules_proto_default_light.bin
|
||||
drules_proto_default_dark.bin
|
||||
drules_proto_outdoors_light.bin
|
||||
drules_proto_outdoors_dark.bin
|
||||
drules_proto_vehicle_light.bin
|
||||
drules_proto_vehicle_dark.bin
|
||||
editor.config
|
||||
packed_polygons.bin
|
||||
patterns.txt
|
||||
transit_colors.txt
|
||||
types.txt
|
||||
World.mwm
|
||||
WorldCoasts.mwm
|
||||
)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/)
|
||||
install(DIRECTORY ${OMIM_ROOT}/data DESTINATION ${CMAKE_INSTALL_PREFIX}/share/comaps/)
|
||||
|
||||
if (PLATFORM_LINUX)
|
||||
install(FILES ${OMIM_ROOT}/packaging/app.comaps.comaps.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo/)
|
||||
install(FILES ${OMIM_ROOT}/qt/res/linux/app.comaps.comaps.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications/)
|
||||
install(FILES ${OMIM_ROOT}/qt/res/logo.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/512x512/apps/ RENAME comaps.png)
|
||||
endif()
|
||||
|
||||
if (NOT PLATFORM_LINUX)
|
||||
# On Linux, ICU data is loaded from the shared library.
|
||||
copy_resources(icudt75l.dat)
|
||||
endif()
|
||||
|
||||
if (PLATFORM_MAC)
|
||||
if (BUILD_DESIGNER)
|
||||
execute_process(
|
||||
COMMAND cp ${PROJECT_SOURCE_DIR}/res/mac/designer.icns ${RESOURCES_FOLDER}
|
||||
)
|
||||
else ()
|
||||
execute_process(
|
||||
COMMAND cp ${PROJECT_SOURCE_DIR}/res/mac/mac.icns ${RESOURCES_FOLDER}
|
||||
)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (PLATFORM_MAC)
|
||||
if (NOT APP_VERSION)
|
||||
set(BUNDLE_VERSION "UNKNOWN")
|
||||
else()
|
||||
set(BUNDLE_VERSION ${APP_VERSION})
|
||||
endif()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/res/mac/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/generated/Info.plist)
|
||||
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
OUTPUT_NAME ${BUNDLE_NAME}
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/generated/Info.plist
|
||||
)
|
||||
endif()
|
||||
|
||||
if (PLATFORM_WIN)
|
||||
target_sources(${PROJECT_NAME} PRIVATE res/windows/windows.rc)
|
||||
endif()
|
||||
|
||||
if (BUILD_DESIGNER)
|
||||
execute_process(
|
||||
COMMAND cp -rf ${OMIM_ROOT}/data/symbols/mdpi/light/ ${OMIM_ROOT}/data/symbols/mdpi/design/
|
||||
COMMAND cp -rf ${OMIM_ROOT}/data/symbols/hdpi/light/ ${OMIM_ROOT}/data/symbols/hdpi/design/
|
||||
COMMAND cp -rf ${OMIM_ROOT}/data/symbols/xhdpi/light/ ${OMIM_ROOT}/data/symbols/xhdpi/design/
|
||||
COMMAND cp -rf ${OMIM_ROOT}/data/symbols/xxhdpi/light/ ${OMIM_ROOT}/data/symbols/xxhdpi/design/
|
||||
COMMAND cp -rf ${OMIM_ROOT}/data/symbols/6plus/light/ ${OMIM_ROOT}/data/symbols/6plus/design/
|
||||
COMMAND cp -rf ${OMIM_ROOT}/data/symbols/xxxhdpi/light/ ${OMIM_ROOT}/data/symbols/xxxhdpi/design/
|
||||
COMMAND cp -f ${OMIM_ROOT}/data/drules_proto_default_light.bin ${OMIM_ROOT}/data/drules_proto_default_design.bin
|
||||
COMMAND cp -f ${OMIM_ROOT}/data/colors.txt ${OMIM_ROOT}/data/colors_design.txt
|
||||
COMMAND cp -f ${OMIM_ROOT}/data/patterns.txt ${OMIM_ROOT}/data/patterns_design.txt
|
||||
)
|
||||
|
||||
copy_resources(
|
||||
colors_design.txt
|
||||
drules_proto_default_design.bin
|
||||
mapcss-dynamic.txt
|
||||
mapcss-mapping.csv
|
||||
patterns_design.txt
|
||||
symbols/mdpi/design
|
||||
symbols/hdpi/design
|
||||
symbols/xhdpi/design
|
||||
symbols/xxhdpi/design
|
||||
symbols/xxxhdpi/design
|
||||
symbols/6plus/design
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND cp ${OMIM_ROOT}/tools/python/recalculate_geom_index.py ${RESOURCES_FOLDER}
|
||||
COMMAND cp ${OMIM_ROOT}/tools/python/generate_styles_override.py ${RESOURCES_FOLDER}
|
||||
COMMAND cp -rf ${OMIM_ROOT}/tools/kothic ${RESOURCES_FOLDER}/kothic/
|
||||
COMMAND cp -f ${OMIM_ROOT}/tools/python/stylesheet/drules_info.py ${RESOURCES_FOLDER}/kothic/src/
|
||||
COMMAND cp -rf ${OMIM_ROOT}/tools/python/stylesheet/ ${RESOURCES_FOLDER}/kothic/src/
|
||||
COMMAND cp -f ${OMIM_ROOT}/3party/protobuf/protobuf-3.3.0-py2.7.egg ${RESOURCES_FOLDER}/kothic/
|
||||
)
|
||||
|
||||
# Generate DMG
|
||||
install(DIRECTORY ${DATA_DIR}/styles DESTINATION .)
|
||||
set(BUNDLES
|
||||
${CMAKE_BINARY_DIR}/generator_tool.app
|
||||
${CMAKE_BINARY_DIR}/skin_generator_tool.app
|
||||
${CMAKE_BINARY_DIR}/style_tests.app
|
||||
)
|
||||
install(CODE "
|
||||
foreach(BUNDLE ${BUNDLE_FOLDER} ${BUNDLES})
|
||||
execute_process(
|
||||
COMMAND \"${QT_PATH}/bin/macdeployqt\" \"\${BUNDLE}\"
|
||||
)
|
||||
endforeach()
|
||||
foreach(BUNDLE ${BUNDLES})
|
||||
execute_process(
|
||||
COMMAND cp -rf \"\${BUNDLE}\" \"${RESOURCES_FOLDER}\"
|
||||
)
|
||||
endforeach()
|
||||
")
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION .)
|
||||
|
||||
set(CPACK_GENERATOR DragNDrop)
|
||||
set(CPACK_DMG_FORMAT UDZO)
|
||||
set(CPACK_DMG_VOLUME_NAME ${BUNDLE_NAME})
|
||||
set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/res/mac/designer.icns)
|
||||
include(CPack)
|
||||
elseif (BUILD_STANDALONE)
|
||||
add_custom_command(TARGET desktop POST_BUILD
|
||||
COMMAND "${QT_PATH}/bin/macdeployqt" ${BUNDLE_FOLDER}
|
||||
COMMAND ${Python3_EXECUTABLE} "${OMIM_ROOT}/tools/macdeployqtfix/macdeployqtfix.py" -q -nl ${BUNDLE_FOLDER}/Contents/MacOS/${BUNDLE_NAME} ${QT_PATH}
|
||||
COMMAND echo "Fixing Qt dependencies finished."
|
||||
VERBATIM
|
||||
COMMENT "Fixing Qt dependencies for standalone desktop app"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(qt_common)
|
||||
70
qt/about.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "qt/about.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QTextBrowser>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
AboutDialog::AboutDialog(QWidget * parent)
|
||||
: QDialog(parent,
|
||||
Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint)
|
||||
{
|
||||
QIcon icon(":/ui/logo.png");
|
||||
setWindowIcon(icon);
|
||||
setWindowTitle(QMenuBar::tr("About"));
|
||||
|
||||
QLabel * labelIcon = new QLabel();
|
||||
labelIcon->setPixmap(icon.pixmap(128));
|
||||
|
||||
Platform & platform = GetPlatform();
|
||||
|
||||
QVBoxLayout * versionBox = new QVBoxLayout();
|
||||
versionBox->addWidget(new QLabel(QCoreApplication::applicationName()));
|
||||
QLabel * versionLabel = new QLabel("Version: " + QString::fromStdString(platform.Version()));
|
||||
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
versionBox->addWidget(versionLabel);
|
||||
// TODO: insert maps data version.
|
||||
// versionBox->addWidget(new QLabel(QString("Data: ") + DESIGNER_DATA_VERSION));
|
||||
|
||||
QHBoxLayout * hBox = new QHBoxLayout();
|
||||
hBox->addWidget(labelIcon);
|
||||
hBox->addLayout(versionBox);
|
||||
|
||||
std::string aboutText;
|
||||
try
|
||||
{
|
||||
ReaderPtr<Reader> reader = platform.GetReader("copyright.html");
|
||||
reader.ReadAsString(aboutText);
|
||||
}
|
||||
catch (RootException const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("About text error: ", ex.Msg()));
|
||||
}
|
||||
|
||||
if (!aboutText.empty())
|
||||
{
|
||||
QTextBrowser * aboutTextBrowser = new QTextBrowser();
|
||||
aboutTextBrowser->setReadOnly(true);
|
||||
aboutTextBrowser->setOpenLinks(true);
|
||||
aboutTextBrowser->setOpenExternalLinks(true);
|
||||
aboutTextBrowser->setText(aboutText.c_str());
|
||||
|
||||
QVBoxLayout * vBox = new QVBoxLayout();
|
||||
vBox->addLayout(hBox);
|
||||
vBox->addWidget(aboutTextBrowser);
|
||||
setLayout(vBox);
|
||||
}
|
||||
else
|
||||
setLayout(hBox);
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
12
qt/about.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class AboutDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AboutDialog(QWidget * parent);
|
||||
};
|
||||
318
qt/bookmark_dialog.cpp
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
#include "qt/bookmark_dialog.hpp"
|
||||
|
||||
#include "map/bookmark_manager.hpp"
|
||||
#include "map/framework.hpp"
|
||||
|
||||
#include "platform/measurement_utils.hpp"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QTreeWidget>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
BookmarkDialog::BookmarkDialog(QWidget * parent, Framework & framework)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
|
||||
, m_framework(framework)
|
||||
{
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
QPushButton * closeButton = new QPushButton(tr("Close"), this);
|
||||
closeButton->setDefault(true);
|
||||
connect(closeButton, &QAbstractButton::clicked, this, &BookmarkDialog::OnCloseClick);
|
||||
|
||||
QPushButton * deleteButton = new QPushButton(tr("Delete"), this);
|
||||
connect(deleteButton, &QAbstractButton::clicked, this, &BookmarkDialog::OnDeleteClick);
|
||||
|
||||
QPushButton * importButton = new QPushButton(tr("Import KML, KMZ, GPX"), this);
|
||||
connect(importButton, &QAbstractButton::clicked, this, &BookmarkDialog::OnImportClick);
|
||||
|
||||
QPushButton * exportButton = new QPushButton(tr("Export KMZ"), this);
|
||||
connect(exportButton, &QAbstractButton::clicked, this, &BookmarkDialog::OnExportClick);
|
||||
|
||||
m_tree = new QTreeWidget(this);
|
||||
m_tree->setColumnCount(2);
|
||||
QStringList columnLabels;
|
||||
columnLabels << tr("Bookmarks and tracks") << "";
|
||||
m_tree->setHeaderLabels(columnLabels);
|
||||
m_tree->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectItems);
|
||||
connect(m_tree, &QTreeWidget::itemClicked, this, &BookmarkDialog::OnItemClick);
|
||||
|
||||
QHBoxLayout * horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->addStretch();
|
||||
horizontalLayout->addWidget(importButton);
|
||||
horizontalLayout->addWidget(exportButton);
|
||||
horizontalLayout->addWidget(deleteButton);
|
||||
horizontalLayout->addWidget(closeButton);
|
||||
|
||||
QVBoxLayout * verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->addWidget(m_tree);
|
||||
verticalLayout->addLayout(horizontalLayout);
|
||||
setLayout(verticalLayout);
|
||||
|
||||
setWindowTitle(tr("Bookmarks and tracks"));
|
||||
resize(700, 600);
|
||||
|
||||
BookmarkManager::AsyncLoadingCallbacks callbacks;
|
||||
callbacks.m_onStarted = std::bind(&BookmarkDialog::OnAsyncLoadingStarted, this);
|
||||
callbacks.m_onFinished = std::bind(&BookmarkDialog::OnAsyncLoadingFinished, this);
|
||||
callbacks.m_onFileSuccess = std::bind(&BookmarkDialog::OnAsyncLoadingFileSuccess, this, _1, _2);
|
||||
callbacks.m_onFileError = std::bind(&BookmarkDialog::OnAsyncLoadingFileError, this, _1, _2);
|
||||
m_framework.GetBookmarkManager().SetAsyncLoadingCallbacks(std::move(callbacks));
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnAsyncLoadingStarted()
|
||||
{
|
||||
FillTree();
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnAsyncLoadingFinished()
|
||||
{
|
||||
FillTree();
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnAsyncLoadingFileSuccess(std::string const & fileName, bool isTemporaryFile)
|
||||
{
|
||||
LOG(LINFO, ("OnAsyncLoadingFileSuccess", fileName, isTemporaryFile));
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnAsyncLoadingFileError(std::string const & fileName, bool isTemporaryFile)
|
||||
{
|
||||
LOG(LERROR, ("OnAsyncLoadingFileError", fileName, isTemporaryFile));
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnItemClick(QTreeWidgetItem * item, int column)
|
||||
{
|
||||
if (column != 1)
|
||||
return;
|
||||
|
||||
auto const categoryIt = m_categories.find(item);
|
||||
if (categoryIt != m_categories.cend())
|
||||
{
|
||||
done(0);
|
||||
m_framework.ShowBookmarkCategory(categoryIt->second);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const bookmarkIt = m_bookmarks.find(item);
|
||||
if (bookmarkIt != m_bookmarks.cend())
|
||||
{
|
||||
done(0);
|
||||
m_framework.ShowBookmark(bookmarkIt->second);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const trackIt = m_tracks.find(item);
|
||||
if (trackIt != m_tracks.cend())
|
||||
{
|
||||
done(0);
|
||||
m_framework.ShowTrack(trackIt->second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnCloseClick()
|
||||
{
|
||||
done(0);
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnImportClick()
|
||||
{
|
||||
auto const files = QFileDialog::getOpenFileNames(this /* parent */, tr("Open KML, KMZ, GPX..."), QString() /* dir */,
|
||||
"KML, KMZ, GPX files (*.kml *.KML *.kmz *.KMZ, *.gpx *.GPX)");
|
||||
|
||||
for (auto const & name : files)
|
||||
{
|
||||
auto const file = name.toStdString();
|
||||
if (file.empty())
|
||||
continue;
|
||||
|
||||
m_framework.GetBookmarkManager().LoadBookmark(file, false /* isTemporaryFile */);
|
||||
}
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnExportClick()
|
||||
{
|
||||
auto const selected = m_tree->selectedItems();
|
||||
if (selected.empty())
|
||||
{
|
||||
QMessageBox ask(this);
|
||||
ask.setIcon(QMessageBox::Information);
|
||||
ask.setText(tr("Select one of the bookmark categories to export."));
|
||||
ask.addButton(tr("OK"), QMessageBox::NoRole);
|
||||
ask.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
auto const categoryIt = m_categories.find(selected.front());
|
||||
if (categoryIt == m_categories.cend())
|
||||
{
|
||||
QMessageBox ask(this);
|
||||
ask.setIcon(QMessageBox::Warning);
|
||||
ask.setText(tr("Selected item is not a bookmark category."));
|
||||
ask.addButton(tr("OK"), QMessageBox::NoRole);
|
||||
ask.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
auto const name =
|
||||
QFileDialog::getSaveFileName(this /* parent */, tr("Export KMZ..."), QString() /* dir */, "KMZ files (*.kmz)");
|
||||
if (name.isEmpty())
|
||||
return;
|
||||
|
||||
m_framework.GetBookmarkManager().PrepareFileForSharing({categoryIt->second},
|
||||
[this, name](BookmarkManager::SharingResult const & result)
|
||||
{
|
||||
if (result.m_code == BookmarkManager::SharingResult::Code::Success)
|
||||
{
|
||||
QFile::rename(QString(result.m_sharingPath.c_str()), name);
|
||||
|
||||
QMessageBox ask(this);
|
||||
ask.setIcon(QMessageBox::Information);
|
||||
ask.setText(tr("Bookmarks successfully exported."));
|
||||
ask.addButton(tr("OK"), QMessageBox::NoRole);
|
||||
ask.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox ask(this);
|
||||
ask.setIcon(QMessageBox::Critical);
|
||||
ask.setText(tr("Could not export bookmarks: ") + result.m_errorString.c_str());
|
||||
ask.addButton(tr("OK"), QMessageBox::NoRole);
|
||||
ask.exec();
|
||||
}
|
||||
}, KmlFileType::Text);
|
||||
}
|
||||
|
||||
void BookmarkDialog::OnDeleteClick()
|
||||
{
|
||||
auto & bm = m_framework.GetBookmarkManager();
|
||||
for (auto const item : m_tree->selectedItems())
|
||||
{
|
||||
auto const categoryIt = m_categories.find(item);
|
||||
if (categoryIt != m_categories.cend())
|
||||
{
|
||||
if (m_categories.size() == 1)
|
||||
{
|
||||
QMessageBox ask(this);
|
||||
ask.setIcon(QMessageBox::Information);
|
||||
ask.setText(tr("Could not delete the last category."));
|
||||
ask.addButton(tr("OK"), QMessageBox::NoRole);
|
||||
ask.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
bm.GetEditSession().DeleteBmCategory(categoryIt->second, true);
|
||||
FillTree();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto const bookmarkIt = m_bookmarks.find(item);
|
||||
if (bookmarkIt != m_bookmarks.cend())
|
||||
{
|
||||
bm.GetEditSession().DeleteBookmark(bookmarkIt->second);
|
||||
FillTree();
|
||||
return;
|
||||
}
|
||||
|
||||
auto const trackIt = m_tracks.find(item);
|
||||
if (trackIt != m_tracks.cend())
|
||||
{
|
||||
bm.GetEditSession().DeleteTrack(trackIt->second);
|
||||
FillTree();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox ask(this);
|
||||
ask.setIcon(QMessageBox::Warning);
|
||||
ask.setText(tr("Select category, bookmark or track to delete."));
|
||||
ask.addButton(tr("OK"), QMessageBox::NoRole);
|
||||
ask.exec();
|
||||
}
|
||||
|
||||
QTreeWidgetItem * BookmarkDialog::CreateTreeItem(std::string const & title, QTreeWidgetItem * parent)
|
||||
{
|
||||
QStringList labels;
|
||||
labels << QString::fromStdString(title) << tr(parent != nullptr ? "Show on the map" : "");
|
||||
|
||||
QTreeWidgetItem * item = new QTreeWidgetItem(labels);
|
||||
if (parent)
|
||||
parent->addChild(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void BookmarkDialog::FillTree()
|
||||
{
|
||||
m_tree->setSortingEnabled(false);
|
||||
m_tree->clear();
|
||||
m_categories.clear();
|
||||
m_bookmarks.clear();
|
||||
m_tracks.clear();
|
||||
|
||||
auto categoriesItem = CreateTreeItem("Categories", nullptr);
|
||||
|
||||
auto const & bm = m_framework.GetBookmarkManager();
|
||||
|
||||
if (!bm.IsAsyncLoadingInProgress())
|
||||
{
|
||||
for (auto catId : bm.GetUnsortedBmGroupsIdList())
|
||||
{
|
||||
auto categoryItem = CreateTreeItem(bm.GetCategoryName(catId), categoriesItem);
|
||||
m_categories[categoryItem] = catId;
|
||||
|
||||
for (auto bookmarkId : bm.GetUserMarkIds(catId))
|
||||
{
|
||||
auto const bookmark = bm.GetBookmark(bookmarkId);
|
||||
auto name = GetPreferredBookmarkStr(bookmark->GetName());
|
||||
if (name.empty())
|
||||
{
|
||||
name = measurement_utils::FormatLatLon(mercator::YToLat(bookmark->GetPivot().y),
|
||||
mercator::XToLon(bookmark->GetPivot().x), true /* withComma */);
|
||||
}
|
||||
auto bookmarkItem = CreateTreeItem(name + " (Bookmark)", categoryItem);
|
||||
m_bookmarks[bookmarkItem] = bookmarkId;
|
||||
}
|
||||
|
||||
for (auto trackId : bm.GetTrackIds(catId))
|
||||
{
|
||||
auto const track = bm.GetTrack(trackId);
|
||||
auto name = track->GetName();
|
||||
if (name.empty())
|
||||
name = "No name";
|
||||
auto trackItem = CreateTreeItem(name + " (Track)", categoryItem);
|
||||
trackItem->setForeground(0, Qt::darkGreen);
|
||||
m_tracks[trackItem] = trackId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateTreeItem("Loading in progress...", categoriesItem);
|
||||
}
|
||||
|
||||
m_tree->addTopLevelItem(categoriesItem);
|
||||
m_tree->expandAll();
|
||||
m_tree->setCurrentItem(categoriesItem);
|
||||
|
||||
m_tree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
m_tree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
}
|
||||
|
||||
void BookmarkDialog::ShowModal()
|
||||
{
|
||||
FillTree();
|
||||
exec();
|
||||
}
|
||||
} // namespace qt
|
||||
50
qt/bookmark_dialog.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "kml/types.hpp"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class Framework;
|
||||
|
||||
namespace qt
|
||||
{
|
||||
class BookmarkDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BookmarkDialog(QWidget * parent, Framework & framework);
|
||||
|
||||
void ShowModal();
|
||||
|
||||
private slots:
|
||||
void OnItemClick(QTreeWidgetItem * item, int column);
|
||||
void OnCloseClick();
|
||||
void OnImportClick();
|
||||
void OnExportClick();
|
||||
void OnDeleteClick();
|
||||
|
||||
private:
|
||||
void FillTree();
|
||||
QTreeWidgetItem * CreateTreeItem(std::string const & title, QTreeWidgetItem * parent);
|
||||
void OnAsyncLoadingStarted();
|
||||
void OnAsyncLoadingFinished();
|
||||
void OnAsyncLoadingFileSuccess(std::string const & fileName, bool isTemporaryFile);
|
||||
void OnAsyncLoadingFileError(std::string const & fileName, bool isTemporaryFile);
|
||||
|
||||
QTreeWidget * m_tree;
|
||||
Framework & m_framework;
|
||||
std::unordered_map<QTreeWidgetItem *, kml::MarkGroupId> m_categories;
|
||||
std::unordered_map<QTreeWidgetItem *, kml::MarkId> m_bookmarks;
|
||||
std::unordered_map<QTreeWidgetItem *, kml::TrackId> m_tracks;
|
||||
};
|
||||
} // namespace qt
|
||||
118
qt/build_style/build_common.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#include "build_common.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#include <exception>
|
||||
#include <iomanip> // std::quoted
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
QString ExecProcess(QString const & program, std::initializer_list<QString> args, QProcessEnvironment const * env)
|
||||
{
|
||||
// Quote all arguments.
|
||||
QStringList qargs(args);
|
||||
for (auto i = qargs.begin(); i != qargs.end(); ++i)
|
||||
*i = "\"" + *i + "\"";
|
||||
|
||||
QProcess p;
|
||||
if (nullptr != env)
|
||||
p.setProcessEnvironment(*env);
|
||||
|
||||
p.start(program, qargs, QIODevice::ReadOnly);
|
||||
p.waitForFinished(-1);
|
||||
|
||||
int const exitCode = p.exitCode();
|
||||
QString output = p.readAllStandardOutput();
|
||||
QString const error = p.readAllStandardError();
|
||||
if (exitCode != 0)
|
||||
{
|
||||
QString msg = "Error: " + program + " " + qargs.join(" ") + "\nReturned " + QString::number(exitCode);
|
||||
if (!output.isEmpty())
|
||||
msg += "\n" + output;
|
||||
if (!error.isEmpty())
|
||||
msg += "\nSTDERR:\n" + error;
|
||||
throw std::runtime_error(msg.toStdString());
|
||||
}
|
||||
if (!error.isEmpty())
|
||||
{
|
||||
QString const msg = "STDERR with a zero exit code:\n" + program + " " + qargs.join(" ");
|
||||
throw std::runtime_error(msg.toStdString());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
bool CopyFile(QString const & oldFile, QString const & newFile)
|
||||
{
|
||||
if (oldFile == newFile)
|
||||
return true;
|
||||
if (!QFile::exists(oldFile))
|
||||
return false;
|
||||
if (QFile::exists(newFile) && !QFile::remove(newFile))
|
||||
return false;
|
||||
return QFile::copy(oldFile, newFile);
|
||||
}
|
||||
|
||||
void CopyFromResources(QString const & name, QString const & output)
|
||||
{
|
||||
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
|
||||
if (!CopyFile(JoinPathQt({resourceDir, name}), JoinPathQt({output, name})))
|
||||
throw std::runtime_error(std::string("Cannot copy file ") + name.toStdString() + " to " + output.toStdString());
|
||||
}
|
||||
|
||||
void CopyToResources(QString const & name, QString const & input, QString const & newName)
|
||||
{
|
||||
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
|
||||
if (!CopyFile(JoinPathQt({input, name}), JoinPathQt({resourceDir, newName.isEmpty() ? name : newName})))
|
||||
throw std::runtime_error(std::string("Cannot copy file ") + name.toStdString() + " from " + input.toStdString());
|
||||
}
|
||||
|
||||
QString JoinPathQt(std::initializer_list<QString> folders)
|
||||
{
|
||||
QString result;
|
||||
bool firstInserted = false;
|
||||
for (auto it = folders.begin(); it != folders.end(); ++it)
|
||||
{
|
||||
if (it->isEmpty() || *it == QDir::separator())
|
||||
continue;
|
||||
|
||||
if (firstInserted)
|
||||
result.append(QDir::separator());
|
||||
|
||||
result.append(*it);
|
||||
firstInserted = true;
|
||||
}
|
||||
return QDir::cleanPath(result);
|
||||
}
|
||||
|
||||
QString GetExternalPath(QString const & name, QString const & primaryPath, QString const & secondaryPath)
|
||||
{
|
||||
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
|
||||
QString path = JoinPathQt({resourceDir, primaryPath, name});
|
||||
if (!QFileInfo::exists(path))
|
||||
path = JoinPathQt({resourceDir, secondaryPath, name});
|
||||
|
||||
// Special case for looking for in application folder.
|
||||
if (!QFileInfo::exists(path) && secondaryPath.isEmpty())
|
||||
{
|
||||
std::string const appPath = QCoreApplication::applicationDirPath().toStdString();
|
||||
|
||||
std::regex re("(/[^/]*\\.app)");
|
||||
std::smatch m;
|
||||
if (std::regex_search(appPath, m, re) && m.size() > 0)
|
||||
path.fromStdString(base::JoinPath(m[0], name.toStdString()));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
QString GetProtobufEggPath()
|
||||
{
|
||||
return GetExternalPath("protobuf-3.3.0-py2.7.egg", "kothic", "../3party/protobuf");
|
||||
}
|
||||
24
qt/build_style/build_common.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class QProcessEnvironment;
|
||||
|
||||
// Returns stdout output of the program, throws std::runtime_error in case of non-zero exit code.
|
||||
// Quotes all arguments to avoid issues with space-containing paths.
|
||||
QString ExecProcess(QString const & program, std::initializer_list<QString> args, QProcessEnvironment const * env = nullptr);
|
||||
|
||||
bool CopyFile(QString const & oldFile, QString const & newFile);
|
||||
|
||||
void CopyFromResources(QString const & name, QString const & output);
|
||||
void CopyToResources(QString const & name, QString const & input, QString const & newName = "");
|
||||
|
||||
QString JoinPathQt(std::initializer_list<QString> folders);
|
||||
|
||||
QString GetExternalPath(QString const & name, QString const & primaryPath,
|
||||
QString const & secondaryPath);
|
||||
QString GetProtobufEggPath();
|
||||
64
qt/build_style/build_drules.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include "build_drules.h"
|
||||
#include "build_common.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
void BuildDrawingRulesImpl(QString const & mapcssFile, QString const & outputDir)
|
||||
{
|
||||
QString const outputTemplate = JoinPathQt({outputDir, "drules_proto_design"});
|
||||
QString const outputFile = outputTemplate + ".bin";
|
||||
|
||||
// Caller ensures that output directory is clear
|
||||
if (QFile(outputFile).exists())
|
||||
throw std::runtime_error("Output directory is not clear");
|
||||
|
||||
// Add path to the protobuf EGG in the PROTOBUF_EGG_PATH environment variable
|
||||
QProcessEnvironment env{QProcessEnvironment::systemEnvironment()};
|
||||
env.insert("PROTOBUF_EGG_PATH", GetProtobufEggPath());
|
||||
|
||||
// Run the script
|
||||
(void)ExecProcess("python",
|
||||
{
|
||||
GetExternalPath("libkomwm.py", "kothic/src", "../tools/kothic/src"),
|
||||
"-s",
|
||||
mapcssFile,
|
||||
"-o",
|
||||
outputTemplate,
|
||||
"-x",
|
||||
"True",
|
||||
},
|
||||
&env);
|
||||
|
||||
// Ensure that generated file is not empty.
|
||||
if (QFile(outputFile).size() == 0)
|
||||
throw std::runtime_error("Drawing rules file has zero size");
|
||||
}
|
||||
|
||||
void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir)
|
||||
{
|
||||
CopyFromResources("mapcss-mapping.csv", outputDir);
|
||||
CopyFromResources("mapcss-dynamic.txt", outputDir);
|
||||
BuildDrawingRulesImpl(mapcssFile, outputDir);
|
||||
}
|
||||
|
||||
void ApplyDrawingRules(QString const & outputDir)
|
||||
{
|
||||
CopyToResources("drules_proto_design.bin", outputDir);
|
||||
CopyToResources("classificator.txt", outputDir);
|
||||
CopyToResources("types.txt", outputDir);
|
||||
CopyToResources("patterns.txt", outputDir, "patterns_design.txt");
|
||||
CopyToResources("colors.txt", outputDir, "colors_design.txt");
|
||||
}
|
||||
} // namespace build_style
|
||||
9
qt/build_style/build_drules.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir);
|
||||
void ApplyDrawingRules(QString const & outputDir);
|
||||
} // build_style
|
||||
27
qt/build_style/build_phone_pack.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "build_statistics.h"
|
||||
|
||||
#include "build_common.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
QString RunBuildingPhonePack(QString const & stylesDir, QString const & targetDir)
|
||||
{
|
||||
using std::to_string, std::runtime_error;
|
||||
|
||||
if (!QDir(stylesDir).exists())
|
||||
throw runtime_error("Styles directory does not exist " + stylesDir.toStdString());
|
||||
|
||||
if (!QDir(targetDir).exists())
|
||||
throw runtime_error("target directory does not exist" + targetDir.toStdString());
|
||||
|
||||
return ExecProcess("python",
|
||||
{GetExternalPath("generate_styles_override.py", "", "../tools/python"), stylesDir, targetDir});
|
||||
}
|
||||
} // namespace build_style
|
||||
8
qt/build_style/build_phone_pack.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
QString RunBuildingPhonePack(QString const & stylesFolder, QString const & targetFolder);
|
||||
} // namespace build_style
|
||||
216
qt/build_style/build_skins.cpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
#include "qt/build_style/build_skins.h"
|
||||
|
||||
#include "qt/build_style/build_common.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
|
||||
namespace
|
||||
{
|
||||
enum SkinType
|
||||
{
|
||||
SkinMDPI,
|
||||
SkinHDPI,
|
||||
SkinXHDPI,
|
||||
Skin6Plus,
|
||||
SkinXXHDPI,
|
||||
SkinXXXHDPI,
|
||||
|
||||
// SkinCount MUST BE last
|
||||
SkinCount
|
||||
};
|
||||
|
||||
using SkinInfo = std::tuple<char const *, int, bool>;
|
||||
SkinInfo const g_skinInfo[SkinCount] = {
|
||||
std::make_tuple("mdpi", 18, false), std::make_tuple("hdpi", 27, false), std::make_tuple("xhdpi", 36, false),
|
||||
std::make_tuple("6plus", 43, false), std::make_tuple("xxhdpi", 54, false), std::make_tuple("xxxhdpi", 64, false),
|
||||
};
|
||||
|
||||
std::array<SkinType, SkinCount> const g_skinTypes = {{
|
||||
SkinMDPI,
|
||||
SkinHDPI,
|
||||
SkinXHDPI,
|
||||
Skin6Plus,
|
||||
SkinXXHDPI,
|
||||
SkinXXXHDPI,
|
||||
}};
|
||||
|
||||
inline char const * SkinSuffix(SkinType s)
|
||||
{
|
||||
return std::get<0>(g_skinInfo[s]);
|
||||
}
|
||||
inline int SkinSize(SkinType s)
|
||||
{
|
||||
return std::get<1>(g_skinInfo[s]);
|
||||
}
|
||||
inline bool SkinCoorrectColor(SkinType s)
|
||||
{
|
||||
return std::get<2>(g_skinInfo[s]);
|
||||
}
|
||||
|
||||
QString GetSkinGeneratorPath()
|
||||
{
|
||||
QString const path = GetExternalPath("skin_generator_tool", "skin_generator_tool.app/Contents/MacOS", "");
|
||||
if (path.isEmpty())
|
||||
throw std::runtime_error("Can't find skin_generator_tool");
|
||||
ASSERT(QFileInfo::exists(path), (path.toStdString()));
|
||||
return path;
|
||||
}
|
||||
|
||||
class RAII
|
||||
{
|
||||
public:
|
||||
RAII(std::function<void()> && f) : m_f(std::move(f)) {}
|
||||
~RAII() { m_f(); }
|
||||
|
||||
private:
|
||||
std::function<void()> const m_f;
|
||||
};
|
||||
|
||||
std::string trim(std::string && s)
|
||||
{
|
||||
s.erase(std::remove_if(s.begin(), s.end(), &isspace), s.end());
|
||||
return std::move(s);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
std::unordered_map<std::string, int> GetSkinSizes(QString const & file)
|
||||
{
|
||||
std::unordered_map<std::string, int> skinSizes;
|
||||
|
||||
for (SkinType s : g_skinTypes)
|
||||
skinSizes.insert(std::make_pair(SkinSuffix(s), SkinSize(s)));
|
||||
|
||||
try
|
||||
{
|
||||
std::ifstream ifs(file.toStdString());
|
||||
|
||||
std::string line;
|
||||
while (std::getline(ifs, line))
|
||||
{
|
||||
size_t const pos = line.find('=');
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
std::string name(line.begin(), line.begin() + pos);
|
||||
std::string valueTxt(line.begin() + pos + 1, line.end());
|
||||
|
||||
name = trim(std::move(name));
|
||||
int value = std::stoi(trim(std::move(valueTxt)));
|
||||
|
||||
if (value <= 0)
|
||||
continue;
|
||||
|
||||
skinSizes[name] = value;
|
||||
}
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
// reset
|
||||
for (SkinType s : g_skinTypes)
|
||||
skinSizes[SkinSuffix(s)] = SkinSize(s);
|
||||
}
|
||||
|
||||
return skinSizes;
|
||||
}
|
||||
|
||||
void BuildSkinImpl(QString const & styleDir, QString const & suffix, int size, bool colorCorrection,
|
||||
QString const & outputDir)
|
||||
{
|
||||
QString const symbolsDir = JoinPathQt({styleDir, "symbols"});
|
||||
|
||||
// Check symbols directory exists
|
||||
if (!QDir(symbolsDir).exists())
|
||||
throw std::runtime_error("Symbols directory does not exist");
|
||||
|
||||
// Caller ensures that output directory is clear
|
||||
if (QDir(outputDir).exists())
|
||||
throw std::runtime_error("Output directory is not clear");
|
||||
|
||||
// Create output skin directory
|
||||
if (!QDir().mkdir(outputDir))
|
||||
throw std::runtime_error("Cannot create output skin directory");
|
||||
|
||||
// Create symbolic link for symbols/png
|
||||
QString const pngOriginDir = styleDir + suffix;
|
||||
QString const pngDir = JoinPathQt({styleDir, "symbols", "png"});
|
||||
QFile::remove(pngDir);
|
||||
if (!QFile::link(pngOriginDir, pngDir))
|
||||
throw std::runtime_error("Unable to create symbols/png link");
|
||||
RAII const cleaner([=]() { QFile::remove(pngDir); });
|
||||
|
||||
QString const strSize = QString::number(size);
|
||||
// Run the script.
|
||||
(void)ExecProcess(GetSkinGeneratorPath(), {
|
||||
"--symbolWidth",
|
||||
strSize,
|
||||
"--symbolHeight",
|
||||
strSize,
|
||||
"--symbolsDir",
|
||||
symbolsDir,
|
||||
"--skinName",
|
||||
JoinPathQt({outputDir, "basic"}),
|
||||
"--skinSuffix=",
|
||||
"--colorCorrection",
|
||||
(colorCorrection ? "true" : "false"),
|
||||
});
|
||||
|
||||
// Check if files were created.
|
||||
if (QFile(JoinPathQt({outputDir, "symbols.png"})).size() == 0 ||
|
||||
QFile(JoinPathQt({outputDir, "symbols.sdf"})).size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Skin files have not been created");
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSkins(QString const & styleDir, QString const & outputDir)
|
||||
{
|
||||
QString const resolutionFilePath = JoinPathQt({styleDir, "resolutions.txt"});
|
||||
|
||||
auto const resolution2size = GetSkinSizes(resolutionFilePath);
|
||||
|
||||
for (SkinType s : g_skinTypes)
|
||||
{
|
||||
QString const suffix = SkinSuffix(s);
|
||||
QString const outputSkinDir = JoinPathQt({outputDir, "symbols", suffix, "design"});
|
||||
int const size = resolution2size.at(suffix.toStdString()); // SkinSize(s);
|
||||
bool const colorCorrection = SkinCoorrectColor(s);
|
||||
|
||||
BuildSkinImpl(styleDir, suffix, size, colorCorrection, outputSkinDir);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplySkins(QString const & outputDir)
|
||||
{
|
||||
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
|
||||
|
||||
for (SkinType s : g_skinTypes)
|
||||
{
|
||||
QString const suffix = SkinSuffix(s);
|
||||
QString const outputSkinDir = JoinPathQt({outputDir, "symbols", suffix, "design"});
|
||||
QString const resourceSkinDir = JoinPathQt({resourceDir, "symbols", suffix, "design"});
|
||||
|
||||
if (!QFileInfo::exists(resourceSkinDir) && !QDir().mkdir(resourceSkinDir))
|
||||
throw std::runtime_error("Cannot create resource skin directory: " + resourceSkinDir.toStdString());
|
||||
|
||||
if (!CopyFile(JoinPathQt({outputSkinDir, "symbols.png"}), JoinPathQt({resourceSkinDir, "symbols.png"})) ||
|
||||
!CopyFile(JoinPathQt({outputSkinDir, "symbols.sdf"}), JoinPathQt({resourceSkinDir, "symbols.sdf"})))
|
||||
{
|
||||
throw std::runtime_error("Cannot copy skins files");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace build_style
|
||||
9
qt/build_style/build_skins.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
void BuildSkins(QString const & styleDir, QString const & outputDir);
|
||||
void ApplySkins(QString const & outputDir);
|
||||
} // namespace build_style
|
||||
46
qt/build_style/build_statistics.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "build_statistics.h"
|
||||
|
||||
#include "build_common.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
QString GetStyleStatistics(QString const & mapcssMappingFile, QString const & drulesFile)
|
||||
{
|
||||
if (!QFile(mapcssMappingFile).exists())
|
||||
throw std::runtime_error("mapcss-mapping file does not exist at " + mapcssMappingFile.toStdString());
|
||||
|
||||
if (!QFile(drulesFile).exists())
|
||||
throw std::runtime_error("drawing-rules file does not exist at " + drulesFile.toStdString());
|
||||
|
||||
// Add path to the protobuf EGG in the PROTOBUF_EGG_PATH environment variable.
|
||||
QProcessEnvironment env{QProcessEnvironment::systemEnvironment()};
|
||||
env.insert("PROTOBUF_EGG_PATH", GetProtobufEggPath());
|
||||
|
||||
// Run the script.
|
||||
return ExecProcess("python",
|
||||
{
|
||||
GetExternalPath("drules_info.py", "kothic/src", "../tools/python/stylesheet"),
|
||||
mapcssMappingFile,
|
||||
drulesFile,
|
||||
},
|
||||
&env);
|
||||
}
|
||||
|
||||
QString GetCurrentStyleStatistics()
|
||||
{
|
||||
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
|
||||
QString const mappingPath = JoinPathQt({resourceDir, "mapcss-mapping.csv"});
|
||||
QString const drulesPath = JoinPathQt({resourceDir, "drules_proto_design.bin"});
|
||||
return GetStyleStatistics(mappingPath, drulesPath);
|
||||
}
|
||||
} // namespace build_style
|
||||
9
qt/build_style/build_statistics.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
QString GetStyleStatistics(QString const & mapcssMappingFile, QString const & drulesFile);
|
||||
QString GetCurrentStyleStatistics();
|
||||
} // namespace build_style
|
||||
121
qt/build_style/build_style.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "build_style.h"
|
||||
#include "build_common.h"
|
||||
#include "build_drules.h"
|
||||
#include "build_skins.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
namespace
|
||||
{
|
||||
QString GetRecalculateGeometryScriptPath()
|
||||
{
|
||||
return GetExternalPath("recalculate_geom_index.py", "", "../tools/python");
|
||||
}
|
||||
|
||||
QString GetGeometryToolPath()
|
||||
{
|
||||
return GetExternalPath("generator_tool", "generator_tool.app/Contents/MacOS", "");
|
||||
}
|
||||
|
||||
QString GetGeometryToolResourceDir()
|
||||
{
|
||||
return GetExternalPath("", "generator_tool.app/Contents/Resources", "");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
void BuildAndApply(QString const & mapcssFile)
|
||||
{
|
||||
// Ensure mapcss exists
|
||||
if (!QFile(mapcssFile).exists())
|
||||
throw std::runtime_error("mapcss files does not exist");
|
||||
|
||||
QDir const projectDir = QFileInfo(mapcssFile).absoluteDir();
|
||||
QString const styleDir = projectDir.absolutePath() + QDir::separator();
|
||||
QString const outputDir = styleDir + "out" + QDir::separator();
|
||||
|
||||
// Ensure output directory is clear
|
||||
if (QDir(outputDir).exists() && !QDir(outputDir).removeRecursively())
|
||||
throw std::runtime_error("Unable to remove the output directory");
|
||||
if (!QDir().mkdir(outputDir))
|
||||
throw std::runtime_error("Unable to make the output directory");
|
||||
|
||||
bool const hasSymbols = QDir(styleDir + "symbols/").exists();
|
||||
if (hasSymbols)
|
||||
{
|
||||
auto future = std::async(std::launch::async, BuildSkins, styleDir, outputDir);
|
||||
BuildDrawingRules(mapcssFile, outputDir);
|
||||
future.get(); // may rethrow exception from the BuildSkin
|
||||
|
||||
ApplyDrawingRules(outputDir);
|
||||
ApplySkins(outputDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildDrawingRules(mapcssFile, outputDir);
|
||||
ApplyDrawingRules(outputDir);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildIfNecessaryAndApply(QString const & mapcssFile)
|
||||
{
|
||||
if (!QFile(mapcssFile).exists())
|
||||
throw std::runtime_error("mapcss files does not exist");
|
||||
|
||||
QDir const projectDir = QFileInfo(mapcssFile).absoluteDir();
|
||||
QString const styleDir = projectDir.absolutePath() + QDir::separator();
|
||||
QString const outputDir = styleDir + "out" + QDir::separator();
|
||||
|
||||
if (QDir(outputDir).exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplyDrawingRules(outputDir);
|
||||
ApplySkins(outputDir);
|
||||
}
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
BuildAndApply(mapcssFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildAndApply(mapcssFile);
|
||||
}
|
||||
}
|
||||
|
||||
void RunRecalculationGeometryScript(QString const & mapcssFile)
|
||||
{
|
||||
QString const resourceDir = GetPlatform().ResourcesDir().c_str();
|
||||
QString const writableDir = GetPlatform().WritableDir().c_str();
|
||||
|
||||
QString const generatorToolPath = GetGeometryToolPath();
|
||||
QString const appPath = QCoreApplication::applicationFilePath();
|
||||
|
||||
QString const geometryToolResourceDir = GetGeometryToolResourceDir();
|
||||
|
||||
CopyFromResources("drules_proto_design.bin", geometryToolResourceDir);
|
||||
CopyFromResources("classificator.txt", geometryToolResourceDir);
|
||||
CopyFromResources("types.txt", geometryToolResourceDir);
|
||||
|
||||
(void)ExecProcess("python", {
|
||||
GetRecalculateGeometryScriptPath(),
|
||||
resourceDir,
|
||||
writableDir,
|
||||
generatorToolPath,
|
||||
appPath,
|
||||
mapcssFile,
|
||||
});
|
||||
}
|
||||
|
||||
bool NeedRecalculate = false;
|
||||
} // namespace build_style
|
||||
12
qt/build_style/build_style.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
void BuildAndApply(QString const & mapcssFile);
|
||||
void BuildIfNecessaryAndApply(QString const & mapcssFile);
|
||||
void RunRecalculationGeometryScript(QString const & mapcssFile);
|
||||
|
||||
extern bool NeedRecalculate;
|
||||
} // namespace build_style
|
||||
22
qt/build_style/run_tests.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "run_tests.h"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "build_common.h"
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
std::pair<bool, QString> RunCurrentStyleTests()
|
||||
{
|
||||
QString const program = GetExternalPath("style_tests", "style_tests.app/Contents/MacOS", "");
|
||||
QString const resourcesDir = QString::fromStdString(GetPlatform().ResourcesDir());
|
||||
QString const output = ExecProcess(program, {
|
||||
"--user_resource_path=" + resourcesDir,
|
||||
"--data_path=" + resourcesDir,
|
||||
});
|
||||
|
||||
// Unfortunately test process returns 0 even if some test failed,
|
||||
// therefore phrase 'All tests passed.' is looked to be sure that everything is OK.
|
||||
return std::make_pair(output.contains("All tests passed."), output);
|
||||
}
|
||||
} // namespace build_style
|
||||
10
qt/build_style/run_tests.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace build_style
|
||||
{
|
||||
std::pair<bool, QString> RunCurrentStyleTests();
|
||||
} // namespace build_style
|
||||
45
qt/create_feature_dialog.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "qt/create_feature_dialog.hpp"
|
||||
|
||||
#include "editor/new_feature_categories.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
|
||||
#include "platform/preferred_languages.hpp"
|
||||
|
||||
#include <QtWidgets/QAbstractButton>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QListWidget>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
CreateFeatureDialog::CreateFeatureDialog(QWidget * parent, osm::NewFeatureCategories & cats) : QDialog(parent)
|
||||
{
|
||||
cats.AddLanguage("en");
|
||||
cats.AddLanguage(languages::GetCurrentNorm());
|
||||
|
||||
QListWidget * allSortedList = new QListWidget();
|
||||
|
||||
for (auto const & name : cats.GetAllCreatableTypeNames())
|
||||
new QListWidgetItem(QString::fromStdString(name), allSortedList);
|
||||
|
||||
connect(allSortedList, &QAbstractItemView::clicked, this, &CreateFeatureDialog::OnListItemSelected);
|
||||
|
||||
QDialogButtonBox * dbb = new QDialogButtonBox();
|
||||
dbb->addButton(QDialogButtonBox::Close);
|
||||
connect(dbb, &QDialogButtonBox::clicked, this, &QDialog::reject);
|
||||
|
||||
QVBoxLayout * vBox = new QVBoxLayout();
|
||||
vBox->addWidget(allSortedList);
|
||||
vBox->addWidget(dbb);
|
||||
setLayout(vBox);
|
||||
|
||||
setWindowTitle("OSM Editor");
|
||||
}
|
||||
|
||||
void CreateFeatureDialog::OnListItemSelected(QModelIndex const & i)
|
||||
{
|
||||
auto const clType = i.data(Qt::DisplayRole).toString().toStdString();
|
||||
m_selectedType = classif().GetTypeByReadableObjectName(clType);
|
||||
ASSERT(m_selectedType != ftype::GetEmptyValue(), ());
|
||||
|
||||
accept();
|
||||
}
|
||||
25
qt/create_feature_dialog.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class QModelIndex;
|
||||
namespace osm
|
||||
{
|
||||
class NewFeatureCategories;
|
||||
} // namespace osm
|
||||
|
||||
class CreateFeatureDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CreateFeatureDialog(QWidget * parent, osm::NewFeatureCategories & cats);
|
||||
/// Valid only if dialog has finished with Accepted code.
|
||||
uint32_t GetSelectedType() const { return m_selectedType; }
|
||||
|
||||
private slots:
|
||||
void OnListItemSelected(QModelIndex const & i);
|
||||
|
||||
private:
|
||||
uint32_t m_selectedType = 0;
|
||||
};
|
||||
773
qt/draw_widget.cpp
Normal file
|
|
@ -0,0 +1,773 @@
|
|||
#include "qt/draw_widget.hpp"
|
||||
|
||||
#include "qt/create_feature_dialog.hpp"
|
||||
#include "qt/editor_dialog.hpp"
|
||||
#include "qt/place_page_dialog_common.hpp"
|
||||
#include "qt/place_page_dialog_developer.hpp"
|
||||
#include "qt/place_page_dialog_user.hpp"
|
||||
#include "qt/qt_common/helpers.hpp"
|
||||
#include "qt/routing_settings_dialog.hpp"
|
||||
#include "qt/screenshoter.hpp"
|
||||
|
||||
#include "generator/borders.hpp"
|
||||
|
||||
#include "map/framework.hpp"
|
||||
|
||||
#include "search/result.hpp"
|
||||
#include "search/reverse_geocoder.hpp"
|
||||
|
||||
#include "routing/following_info.hpp"
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
|
||||
#include "storage/country_decl.hpp"
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
||||
#include "indexer/editable_map_object.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
#include "defines.hpp"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
using namespace qt::common;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<dp::Color> colorList = {
|
||||
dp::Color(255, 0, 0, 255), dp::Color(0, 255, 0, 255), dp::Color(0, 0, 255, 255), dp::Color(255, 255, 0, 255),
|
||||
dp::Color(0, 255, 255, 255), dp::Color(255, 0, 255, 255), dp::Color(100, 0, 0, 255), dp::Color(0, 100, 0, 255),
|
||||
dp::Color(0, 0, 100, 255), dp::Color(100, 100, 0, 255), dp::Color(0, 100, 100, 255), dp::Color(100, 0, 100, 255)};
|
||||
|
||||
void DrawMwmBorder(df::DrapeApi & drapeApi, std::string const & mwmName, std::vector<m2::RegionD> const & regions,
|
||||
bool withVertices)
|
||||
{
|
||||
for (size_t i = 0; i < regions.size(); ++i)
|
||||
{
|
||||
auto const & region = regions[i];
|
||||
auto const & points = region.Data();
|
||||
if (points.empty())
|
||||
return;
|
||||
|
||||
static uint32_t kColorCounter = 0;
|
||||
|
||||
auto lineData = df::DrapeApiLineData(points, colorList[kColorCounter]).Width(4.0f).ShowId();
|
||||
if (withVertices)
|
||||
lineData.ShowPoints(true /* markPoints */);
|
||||
|
||||
auto const & name = i == 0 ? mwmName : mwmName + "_" + std::to_string(i);
|
||||
drapeApi.AddLine(name, lineData);
|
||||
|
||||
kColorCounter = (kColorCounter + 1) % colorList.size();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OMIM_OS_LINUX)
|
||||
df::TouchEvent::ETouchType qtTouchEventTypeToDfTouchEventType(QEvent::Type qEventType)
|
||||
{
|
||||
switch (qEventType)
|
||||
{
|
||||
case QEvent::TouchBegin: return df::TouchEvent::TOUCH_DOWN;
|
||||
case QEvent::TouchEnd: return df::TouchEvent::TOUCH_UP;
|
||||
case QEvent::TouchUpdate: return df::TouchEvent::TOUCH_MOVE;
|
||||
case QEvent::TouchCancel: return df::TouchEvent::TOUCH_CANCEL;
|
||||
default: return df::TouchEvent::TOUCH_NONE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
DrawWidget::DrawWidget(Framework & framework, std::unique_ptr<ScreenshotParams> && screenshotParams, QWidget * parent)
|
||||
: TBase(framework, screenshotParams != nullptr, parent)
|
||||
, m_rubberBand(nullptr)
|
||||
, m_emulatingLocation(false)
|
||||
{
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
m_framework.SetPlacePageListeners([this]() { ShowPlacePage(); }, {} /* onClose */, {} /* onUpdate */,
|
||||
{} /*onSwitchFullScreen */);
|
||||
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
|
||||
routingManager.SetRouteBuildingListener(
|
||||
[&routingManager, this](routing::RouterResultCode, storage::CountriesSet const &)
|
||||
{
|
||||
auto & drapeApi = m_framework.GetDrapeApi();
|
||||
|
||||
m_turnsVisualizer.ClearTurns(drapeApi);
|
||||
|
||||
if (RoutingSettings::TurnsEnabled())
|
||||
m_turnsVisualizer.Visualize(routingManager, drapeApi);
|
||||
|
||||
auto const routerType = routingManager.GetLastUsedRouter();
|
||||
if (routerType == routing::RouterType::Pedestrian || routerType == routing::RouterType::Bicycle)
|
||||
{
|
||||
RoutingManager::DistanceAltitude da;
|
||||
if (!routingManager.GetRouteAltitudesAndDistancesM(da))
|
||||
return;
|
||||
|
||||
for (int iter = 0; iter < 2; ++iter)
|
||||
{
|
||||
LOG(LINFO, ("Altitudes", iter == 0 ? "before" : "after", "simplify:"));
|
||||
LOG_SHORT(LDEBUG, (da));
|
||||
|
||||
uint32_t totalAscent, totalDescent;
|
||||
da.CalculateAscentDescent(totalAscent, totalDescent);
|
||||
LOG_SHORT(LINFO, ("Ascent:", totalAscent, "Descent:", totalDescent));
|
||||
|
||||
da.Simplify();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
routingManager.SetRouteRecommendationListener([this](RoutingManager::Recommendation r) { OnRouteRecommendation(r); });
|
||||
|
||||
if (screenshotParams != nullptr)
|
||||
{
|
||||
m_ratio = screenshotParams->m_dpiScale;
|
||||
m_screenshoter = std::make_unique<Screenshoter>(*screenshotParams, m_framework, this);
|
||||
}
|
||||
}
|
||||
|
||||
DrawWidget::~DrawWidget()
|
||||
{
|
||||
delete m_rubberBand;
|
||||
}
|
||||
|
||||
void DrawWidget::PrepareShutdown()
|
||||
{
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
if (routingManager.IsRoutingActive() && routingManager.IsRoutingFollowing())
|
||||
{
|
||||
routingManager.SaveRoutePoints();
|
||||
|
||||
auto style = m_framework.GetMapStyle();
|
||||
m_framework.MarkMapStyle(MapStyleIsDark(style) ? MapStyle::MapStyleDefaultDark : MapStyle::MapStyleDefaultLight);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::UpdateAfterSettingsChanged()
|
||||
{
|
||||
m_framework.EnterForeground();
|
||||
}
|
||||
|
||||
void DrawWidget::ShowAll()
|
||||
{
|
||||
m_framework.ShowAll();
|
||||
}
|
||||
|
||||
void DrawWidget::ChoosePositionModeEnable()
|
||||
{
|
||||
m_framework.BlockTapEvents(true /* block */);
|
||||
m_framework.EnableChoosePositionMode(true /* enable */, false /* enableBounds */, nullptr /* optionalPosition */);
|
||||
}
|
||||
|
||||
void DrawWidget::ChoosePositionModeDisable()
|
||||
{
|
||||
m_framework.EnableChoosePositionMode(false /* enable */, false /* enableBounds */, nullptr /* optionalPosition */);
|
||||
m_framework.BlockTapEvents(false /* block */);
|
||||
}
|
||||
|
||||
void DrawWidget::initializeGL()
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
m_framework.GetBookmarkManager().EnableTestMode(true);
|
||||
else
|
||||
m_framework.LoadBookmarks();
|
||||
|
||||
MapWidget::initializeGL();
|
||||
|
||||
m_framework.GetRoutingManager().LoadRoutePoints([this](bool success)
|
||||
{
|
||||
if (success)
|
||||
m_framework.GetRoutingManager().BuildRoute();
|
||||
});
|
||||
|
||||
if (m_screenshotMode)
|
||||
m_screenshoter->Start();
|
||||
}
|
||||
|
||||
bool DrawWidget::event(QEvent * event)
|
||||
{
|
||||
#if !defined(OMIM_OS_LINUX)
|
||||
return QOpenGLWidget::event(event);
|
||||
#else
|
||||
// TouchScreen
|
||||
if (auto dfTouchEventType = qtTouchEventTypeToDfTouchEventType(event->type());
|
||||
dfTouchEventType != df::TouchEvent::TOUCH_NONE)
|
||||
{
|
||||
event->accept();
|
||||
QTouchEvent const * qtTouchEvent = dynamic_cast<QTouchEvent const *>(event);
|
||||
df::TouchEvent dfTouchEvent;
|
||||
// The SetTouchType hast to be set even if `qtTouchEvent->points()` is empty
|
||||
// which theoretically can happen in case of `QEvent::TouchCancel`
|
||||
dfTouchEvent.SetTouchType(dfTouchEventType);
|
||||
|
||||
int64_t i = 0;
|
||||
for (auto it = qtTouchEvent->points().cbegin();
|
||||
it != qtTouchEvent->points().cend() && i < 2; /* For now drape_frontend can only handle max 2 touches */
|
||||
++it, ++i)
|
||||
{
|
||||
df::Touch touch;
|
||||
touch.m_id = i;
|
||||
touch.m_location = m2::PointD(L2D(it->position().x()), L2D(it->position().y()));
|
||||
if (i == 0)
|
||||
dfTouchEvent.SetFirstTouch(touch);
|
||||
else
|
||||
dfTouchEvent.SetSecondTouch(touch);
|
||||
}
|
||||
m_framework.TouchEvent(dfTouchEvent);
|
||||
return true;
|
||||
}
|
||||
// TouchPad
|
||||
else if (event->type() == QEvent::NativeGesture)
|
||||
{
|
||||
event->accept();
|
||||
auto qNativeGestureEvent = dynamic_cast<QNativeGestureEvent *>(event);
|
||||
if (qNativeGestureEvent->gestureType() == Qt::ZoomNativeGesture)
|
||||
{
|
||||
QPointF const pos = qNativeGestureEvent->position();
|
||||
double const factor = qNativeGestureEvent->value();
|
||||
m_framework.Scale(exp(factor), m2::PointD(L2D(pos.x()), L2D(pos.y())), false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Everything else
|
||||
return QOpenGLWidget::event(event);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DrawWidget::mousePressEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::mousePressEvent(e);
|
||||
|
||||
m2::PointD const pt = GetDevicePoint(e);
|
||||
|
||||
if (IsLeftButton(e))
|
||||
{
|
||||
if (IsShiftModifier(e))
|
||||
SubmitRoutingPoint(pt, false);
|
||||
else if (m_ruler.IsActive() && IsAltModifier(e))
|
||||
SubmitRulerPoint(pt);
|
||||
else if (IsAltModifier(e))
|
||||
SubmitFakeLocationPoint(pt);
|
||||
else
|
||||
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_DOWN));
|
||||
}
|
||||
else if (IsRightButton(e))
|
||||
{
|
||||
if (IsAltModifier(e))
|
||||
{
|
||||
SubmitBookmark(pt);
|
||||
}
|
||||
else if (!m_selectionMode || IsCommandModifier(e))
|
||||
{
|
||||
ShowInfoPopup(e, pt);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rubberBandOrigin = e->pos();
|
||||
if (m_rubberBand == nullptr)
|
||||
m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
|
||||
m_rubberBand->setGeometry(QRect(m_rubberBandOrigin, QSize()));
|
||||
m_rubberBand->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::mouseMoveEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::mouseMoveEvent(e);
|
||||
|
||||
if (IsLeftButton(e) && !IsAltModifier(e))
|
||||
{
|
||||
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_MOVE));
|
||||
e->accept();
|
||||
}
|
||||
|
||||
if (m_selectionMode && m_rubberBand != nullptr && m_rubberBand->isVisible())
|
||||
m_rubberBand->setGeometry(QRect(m_rubberBandOrigin, e->pos()).normalized());
|
||||
}
|
||||
|
||||
void DrawWidget::VisualizeMwmsBordersInRect(m2::RectD const & rect, bool withVertices, bool fromPackedPolygon,
|
||||
bool boundingBox)
|
||||
{
|
||||
auto const getRegions = [&](std::string const & mwmName)
|
||||
{
|
||||
if (fromPackedPolygon)
|
||||
{
|
||||
std::vector<storage::CountryDef> countries;
|
||||
FilesContainerR reader(base::JoinPath(GetPlatform().ResourcesDir(), PACKED_POLYGONS_FILE));
|
||||
ReaderSource<ModelReaderPtr> src(reader.GetReader(PACKED_POLYGONS_INFO_TAG));
|
||||
rw::Read(src, countries);
|
||||
|
||||
for (size_t id = 0; id < countries.size(); ++id)
|
||||
{
|
||||
if (countries[id].m_countryId != mwmName)
|
||||
continue;
|
||||
|
||||
src = reader.GetReader(std::to_string(id));
|
||||
return borders::ReadPolygonsOfOneBorder(src);
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string const bordersDir = base::JoinPath(GetPlatform().WritableDir(), BORDERS_DIR);
|
||||
std::string const path = base::JoinPath(bordersDir, mwmName + BORDERS_EXTENSION);
|
||||
|
||||
std::vector<m2::RegionD> polygons;
|
||||
borders::LoadBorders(path, polygons);
|
||||
return polygons;
|
||||
}
|
||||
};
|
||||
|
||||
auto mwmNames = m_framework.GetRegionsCountryIdByRect(rect, false /* rough */);
|
||||
|
||||
for (auto & mwmName : mwmNames)
|
||||
{
|
||||
auto regions = getRegions(mwmName);
|
||||
mwmName += fromPackedPolygon ? ".bin" : ".poly";
|
||||
if (boundingBox)
|
||||
{
|
||||
std::vector<m2::RegionD> boxes;
|
||||
for (auto const & region : regions)
|
||||
{
|
||||
auto const r = region.GetRect();
|
||||
boxes.emplace_back(
|
||||
std::vector<m2::PointD>({r.LeftBottom(), r.LeftTop(), r.RightTop(), r.RightBottom(), r.LeftBottom()}));
|
||||
}
|
||||
|
||||
regions = std::move(boxes);
|
||||
mwmName += ".box";
|
||||
}
|
||||
DrawMwmBorder(m_framework.GetDrapeApi(), mwmName, regions, withVertices);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::mouseReleaseEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::mouseReleaseEvent(e);
|
||||
if (IsLeftButton(e) && !IsAltModifier(e))
|
||||
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_UP));
|
||||
else if (m_selectionMode && IsRightButton(e) && m_rubberBand != nullptr && m_rubberBand->isVisible())
|
||||
ProcessSelectionMode();
|
||||
}
|
||||
|
||||
void DrawWidget::ProcessSelectionMode()
|
||||
{
|
||||
QPoint const lt = m_rubberBand->geometry().topLeft();
|
||||
QPoint const rb = m_rubberBand->geometry().bottomRight();
|
||||
m2::RectD rect;
|
||||
rect.Add(m_framework.PtoG(m2::PointD(L2D(lt.x()), L2D(lt.y()))));
|
||||
rect.Add(m_framework.PtoG(m2::PointD(L2D(rb.x()), L2D(rb.y()))));
|
||||
|
||||
switch (*m_selectionMode)
|
||||
{
|
||||
case SelectionMode::Features: m_framework.VisualizeRoadsInRect(rect); break;
|
||||
|
||||
case SelectionMode::CityBoundaries: m_framework.VisualizeCityBoundariesInRect(rect); break;
|
||||
|
||||
case SelectionMode::CityRoads: m_framework.VisualizeCityRoadsInRect(rect); break;
|
||||
|
||||
case SelectionMode::CrossMwmSegments: m_framework.VisualizeCrossMwmTransitionsInRect(rect); break;
|
||||
|
||||
case SelectionMode::MwmsBordersByPolyFiles:
|
||||
VisualizeMwmsBordersInRect(rect, false /* withVertices */, false /* fromPackedPolygon */, false /* boundingBox */);
|
||||
break;
|
||||
|
||||
case SelectionMode::MwmsBordersWithVerticesByPolyFiles:
|
||||
VisualizeMwmsBordersInRect(rect, true /* withVertices */, false /* fromPackedPolygon */, false /* boundingBox */);
|
||||
break;
|
||||
|
||||
case SelectionMode::MwmsBordersByPackedPolygon:
|
||||
VisualizeMwmsBordersInRect(rect, false /* withVertices */, true /* fromPackedPolygon */, false /* boundingBox */);
|
||||
break;
|
||||
|
||||
case SelectionMode::MwmsBordersWithVerticesByPackedPolygon:
|
||||
VisualizeMwmsBordersInRect(rect, true /* withVertices */, true /* fromPackedPolygon */, false /* boundingBox */);
|
||||
break;
|
||||
|
||||
case SelectionMode::BoundingBoxByPolyFiles:
|
||||
VisualizeMwmsBordersInRect(rect, true /* withVertices */, false /* fromPackedPolygon */, true /* boundingBox */);
|
||||
break;
|
||||
|
||||
case SelectionMode::BoundingBoxByPackedPolygon:
|
||||
VisualizeMwmsBordersInRect(rect, true /* withVertices */, true /* fromPackedPolygon */, true /* boundingBox */);
|
||||
break;
|
||||
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
m_rubberBand->hide();
|
||||
}
|
||||
|
||||
void DrawWidget::keyPressEvent(QKeyEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
if (IsLeftButton(QGuiApplication::mouseButtons()) && e->key() == Qt::Key_Control)
|
||||
{
|
||||
df::TouchEvent event;
|
||||
event.SetTouchType(df::TouchEvent::TOUCH_DOWN);
|
||||
df::Touch touch;
|
||||
touch.m_id = 0;
|
||||
touch.m_location = m2::PointD(L2D(QCursor::pos().x()), L2D(QCursor::pos().y()));
|
||||
event.SetFirstTouch(touch);
|
||||
event.SetSecondTouch(GetSymmetrical(touch));
|
||||
|
||||
m_framework.TouchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::keyReleaseEvent(QKeyEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
if (IsLeftButton(QGuiApplication::mouseButtons()) && e->key() == Qt::Key_Control)
|
||||
{
|
||||
df::TouchEvent event;
|
||||
event.SetTouchType(df::TouchEvent::TOUCH_UP);
|
||||
df::Touch touch;
|
||||
touch.m_id = 0;
|
||||
touch.m_location = m2::PointD(L2D(QCursor::pos().x()), L2D(QCursor::pos().y()));
|
||||
event.SetFirstTouch(touch);
|
||||
event.SetSecondTouch(GetSymmetrical(touch));
|
||||
|
||||
m_framework.TouchEvent(event);
|
||||
}
|
||||
else if (e->key() == Qt::Key_Alt)
|
||||
m_emulatingLocation = false;
|
||||
}
|
||||
|
||||
std::string DrawWidget::GetDistance(search::Result const & res) const
|
||||
{
|
||||
platform::Distance dist;
|
||||
if (auto const position = m_framework.GetCurrentPosition())
|
||||
{
|
||||
auto const ll = mercator::ToLatLon(*position);
|
||||
double dummy;
|
||||
(void)m_framework.GetDistanceAndAzimut(res.GetFeatureCenter(), ll.m_lat, ll.m_lon, -1.0, dist, dummy);
|
||||
}
|
||||
return dist.ToString();
|
||||
}
|
||||
|
||||
void DrawWidget::CreateFeature()
|
||||
{
|
||||
auto cats = m_framework.GetEditorCategories();
|
||||
CreateFeatureDialog dlg(this, cats);
|
||||
if (dlg.exec() == QDialog::Accepted)
|
||||
{
|
||||
osm::EditableMapObject emo;
|
||||
if (m_framework.CreateMapObject(m_framework.GetViewportCenter(), dlg.GetSelectedType(), emo))
|
||||
{
|
||||
EditorDialog dlg(this, emo);
|
||||
int const result = dlg.exec();
|
||||
if (result == QDialog::Accepted)
|
||||
m_framework.SaveEditedMapObject(emo);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, ("Error creating new map object."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::OnLocationUpdate(location::GpsInfo const & info)
|
||||
{
|
||||
if (!m_emulatingLocation)
|
||||
m_framework.OnLocationUpdate(info);
|
||||
}
|
||||
|
||||
void DrawWidget::SetMapStyle(MapStyle mapStyle)
|
||||
{
|
||||
m_framework.SetMapStyle(mapStyle);
|
||||
}
|
||||
|
||||
void DrawWidget::SubmitFakeLocationPoint(m2::PointD const & pt)
|
||||
{
|
||||
m_emulatingLocation = true;
|
||||
|
||||
m2::PointD const point = GetCoordsFromSettingsIfExists(true /* start */, pt, false /* pointIsMercator */);
|
||||
|
||||
m_framework.OnLocationUpdate(qt::common::MakeGpsInfo(point));
|
||||
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
if (routingManager.IsRoutingActive())
|
||||
{
|
||||
/// Immediate update of the position in Route to get updated FollowingInfo state for visual debugging.
|
||||
/// m_framework.OnLocationUpdate calls RoutingSession::OnLocationPositionChanged
|
||||
/// with delay several times according to interpolation.
|
||||
/// @todo Write log when the final point will be reached and
|
||||
/// RoutingSession::OnLocationPositionChanged will be called the last time.
|
||||
routingManager.RoutingSession().OnLocationPositionChanged(qt::common::MakeGpsInfo(point));
|
||||
|
||||
routing::FollowingInfo loc;
|
||||
routingManager.GetRouteFollowingInfo(loc);
|
||||
if (routingManager.GetCurrentRouterType() == routing::RouterType::Pedestrian)
|
||||
{
|
||||
LOG(LDEBUG, ("Distance:", loc.m_distToTarget, "Time:", loc.m_time, DebugPrint(loc.m_pedestrianTurn), "in",
|
||||
loc.m_distToTurn.ToString(), loc.m_nextStreetName.empty() ? "" : "to " + loc.m_nextStreetName));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string speed;
|
||||
if (loc.m_speedLimitMps > 0)
|
||||
speed = "SpeedLimit: " +
|
||||
measurement_utils::FormatSpeedNumeric(loc.m_speedLimitMps, measurement_utils::Units::Metric);
|
||||
|
||||
LOG(LDEBUG, ("Distance:", loc.m_distToTarget, "Time:", loc.m_time, speed, GetTurnString(loc.m_turn),
|
||||
(loc.m_exitNum != 0 ? ":" + std::to_string(loc.m_exitNum) : ""), "in", loc.m_distToTurn.ToString(),
|
||||
loc.m_nextStreetName.empty() ? "" : "to " + loc.m_nextStreetName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::SubmitRulerPoint(m2::PointD const & pt)
|
||||
{
|
||||
m_ruler.AddPoint(P2G(pt));
|
||||
m_ruler.DrawLine(m_framework.GetDrapeApi());
|
||||
}
|
||||
|
||||
void DrawWidget::SubmitRoutingPoint(m2::PointD const & pt, bool pointIsMercator)
|
||||
{
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
|
||||
// Check if limit of intermediate points is reached.
|
||||
bool const isIntermediate = m_routePointAddMode == RouteMarkType::Intermediate;
|
||||
if (isIntermediate && !routingManager.CouldAddIntermediatePoint())
|
||||
routingManager.RemoveRoutePoint(RouteMarkType::Intermediate, 0);
|
||||
|
||||
// Insert implicit start point.
|
||||
if (m_routePointAddMode == RouteMarkType::Finish && routingManager.GetRoutePoints().empty())
|
||||
{
|
||||
RouteMarkData startPoint;
|
||||
startPoint.m_pointType = RouteMarkType::Start;
|
||||
startPoint.m_isMyPosition = true;
|
||||
routingManager.AddRoutePoint(std::move(startPoint));
|
||||
}
|
||||
|
||||
RouteMarkData point;
|
||||
point.m_pointType = m_routePointAddMode;
|
||||
point.m_isMyPosition = false;
|
||||
if (!isIntermediate)
|
||||
point.m_position = GetCoordsFromSettingsIfExists(false /* start */, pt, pointIsMercator);
|
||||
else
|
||||
point.m_position = pointIsMercator ? pt : P2G(pt);
|
||||
|
||||
routingManager.AddRoutePoint(std::move(point));
|
||||
|
||||
if (routingManager.GetRoutePoints().size() >= 2)
|
||||
{
|
||||
if (RoutingSettings::UseDebugGuideTrack())
|
||||
{
|
||||
// Like in guides_tests.cpp, GetTestGuides().
|
||||
routing::GuidesTracks guides;
|
||||
guides[10] = {{{mercator::FromLatLon(48.13999, 11.56873), 10},
|
||||
{mercator::FromLatLon(48.14096, 11.57246), 10},
|
||||
{mercator::FromLatLon(48.14487, 11.57259), 10}}};
|
||||
routingManager.RoutingSession().SetGuidesForTests(std::move(guides));
|
||||
}
|
||||
else
|
||||
routingManager.RoutingSession().SetGuidesForTests({});
|
||||
|
||||
routingManager.BuildRoute();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::SubmitBookmark(m2::PointD const & pt)
|
||||
{
|
||||
auto & manager = m_framework.GetBookmarkManager();
|
||||
|
||||
kml::BookmarkData data;
|
||||
data.m_color.m_predefinedColor = kml::PredefinedColor::Red;
|
||||
data.m_point = m_framework.P3dtoG(pt);
|
||||
manager.GetEditSession().CreateBookmark(std::move(data), manager.LastEditedBMCategory());
|
||||
}
|
||||
|
||||
void DrawWidget::FollowRoute()
|
||||
{
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
|
||||
auto const points = routingManager.GetRoutePoints();
|
||||
if (points.size() < 2)
|
||||
return;
|
||||
if (!points.front().m_isMyPosition && !points.back().m_isMyPosition)
|
||||
return;
|
||||
if (routingManager.IsRoutingActive() && !routingManager.IsRoutingFollowing())
|
||||
{
|
||||
routingManager.FollowRoute();
|
||||
SetMapStyleToVehicle();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::ClearRoute()
|
||||
{
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
|
||||
bool const wasActive = routingManager.IsRoutingActive() && routingManager.IsRoutingFollowing();
|
||||
routingManager.CloseRouting(true /* remove route points */);
|
||||
|
||||
if (wasActive)
|
||||
SetMapStyleToDefault();
|
||||
|
||||
m_turnsVisualizer.ClearTurns(m_framework.GetDrapeApi());
|
||||
}
|
||||
|
||||
void DrawWidget::OnRouteRecommendation(RoutingManager::Recommendation recommendation)
|
||||
{
|
||||
if (recommendation == RoutingManager::Recommendation::RebuildAfterPointsLoading)
|
||||
{
|
||||
auto & routingManager = m_framework.GetRoutingManager();
|
||||
|
||||
RouteMarkData startPoint;
|
||||
startPoint.m_pointType = RouteMarkType::Start;
|
||||
startPoint.m_isMyPosition = true;
|
||||
routingManager.AddRoutePoint(std::move(startPoint));
|
||||
|
||||
if (routingManager.GetRoutePoints().size() >= 2)
|
||||
routingManager.BuildRoute();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget::ShowPlacePage()
|
||||
{
|
||||
place_page::Info const & info = m_framework.GetCurrentPlacePageInfo();
|
||||
search::ReverseGeocoder::Address address;
|
||||
if (info.IsFeature())
|
||||
{
|
||||
search::ReverseGeocoder const coder(m_framework.GetDataSource());
|
||||
coder.GetExactAddress(info.GetID(), address);
|
||||
}
|
||||
else
|
||||
{
|
||||
address = m_framework.GetAddressAtPoint(info.GetMercator());
|
||||
}
|
||||
|
||||
std::unique_ptr<QDialog> placePageDialog = nullptr;
|
||||
bool developerMode;
|
||||
if (settings::Get(settings::kDeveloperMode, developerMode) && developerMode)
|
||||
placePageDialog = std::make_unique<PlacePageDialogDeveloper>(this, info, address);
|
||||
else
|
||||
placePageDialog = std::make_unique<PlacePageDialogUser>(this, info, address);
|
||||
|
||||
switch (placePageDialog->exec())
|
||||
{
|
||||
case place_page_dialog::EditPlace:
|
||||
{
|
||||
osm::EditableMapObject emo;
|
||||
if (m_framework.GetEditableMapObject(info.GetID(), emo))
|
||||
{
|
||||
EditorDialog dlg(this, emo);
|
||||
int const result = dlg.exec();
|
||||
if (result == QDialog::Accepted)
|
||||
{
|
||||
m_framework.SaveEditedMapObject(emo);
|
||||
m_framework.UpdatePlacePageInfoForCurrentSelection();
|
||||
}
|
||||
else if (result == QDialogButtonBox::DestructiveRole)
|
||||
{
|
||||
m_framework.DeleteFeature(info.GetID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LERROR, ("Error while trying to edit feature."));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case place_page_dialog::RouteFrom:
|
||||
{
|
||||
SetRoutePointAddMode(RouteMarkType::Start);
|
||||
SubmitRoutingPoint(info.GetMercator(), true);
|
||||
}
|
||||
break;
|
||||
case place_page_dialog::AddStop:
|
||||
{
|
||||
SetRoutePointAddMode(RouteMarkType::Intermediate);
|
||||
SubmitRoutingPoint(info.GetMercator(), true);
|
||||
}
|
||||
break;
|
||||
case place_page_dialog::RouteTo:
|
||||
{
|
||||
SetRoutePointAddMode(RouteMarkType::Finish);
|
||||
SubmitRoutingPoint(info.GetMercator(), true);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
m_framework.DeactivateMapSelection();
|
||||
}
|
||||
|
||||
void DrawWidget::SetRuler(bool enabled)
|
||||
{
|
||||
if (!enabled)
|
||||
m_ruler.EraseLine(m_framework.GetDrapeApi());
|
||||
m_ruler.SetActive(enabled);
|
||||
}
|
||||
|
||||
// static
|
||||
void DrawWidget::RefreshDrawingRules()
|
||||
{
|
||||
SetMapStyle(MapStyleDefaultLight);
|
||||
}
|
||||
|
||||
void DrawWidget::SetMapStyleToDefault()
|
||||
{
|
||||
auto const style = m_framework.GetMapStyle();
|
||||
SetMapStyle(MapStyleIsDark(style) ? MapStyle::MapStyleDefaultDark : MapStyle::MapStyleDefaultLight);
|
||||
}
|
||||
|
||||
void DrawWidget::SetMapStyleToVehicle()
|
||||
{
|
||||
auto const style = m_framework.GetMapStyle();
|
||||
SetMapStyle(MapStyleIsDark(style) ? MapStyle::MapStyleVehicleDark : MapStyle::MapStyleVehicleLight);
|
||||
}
|
||||
|
||||
void DrawWidget::SetMapStyleToOutdoors()
|
||||
{
|
||||
auto const style = m_framework.GetMapStyle();
|
||||
SetMapStyle(MapStyleIsDark(style) ? MapStyle::MapStyleOutdoorsDark : MapStyle::MapStyleOutdoorsLight);
|
||||
}
|
||||
|
||||
m2::PointD DrawWidget::P2G(m2::PointD const & pt) const
|
||||
{
|
||||
return m_framework.P3dtoG(pt);
|
||||
}
|
||||
|
||||
m2::PointD DrawWidget::GetCoordsFromSettingsIfExists(bool start, m2::PointD const & pt, bool pointIsMercator) const
|
||||
{
|
||||
if (auto optional = RoutingSettings::GetCoords(start))
|
||||
return mercator::FromLatLon(*optional);
|
||||
|
||||
return pointIsMercator ? pt : P2G(pt);
|
||||
}
|
||||
} // namespace qt
|
||||
129
qt/draw_widget.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include "qt/qt_common/map_widget.hpp"
|
||||
#include "qt/routing_turns_visualizer.hpp"
|
||||
#include "qt/ruler.hpp"
|
||||
#include "qt/selection.hpp"
|
||||
|
||||
#include "map/routing_manager.hpp"
|
||||
|
||||
#include "search/result.hpp"
|
||||
|
||||
#include "indexer/map_style.hpp"
|
||||
|
||||
#include <QtWidgets/QRubberBand>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
class Framework;
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
class ScaleSlider;
|
||||
}
|
||||
|
||||
class Screenshoter;
|
||||
struct ScreenshotParams;
|
||||
|
||||
class DrawWidget : public qt::common::MapWidget
|
||||
{
|
||||
using TBase = MapWidget;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public Q_SLOTS:
|
||||
void ShowAll();
|
||||
|
||||
void ChoosePositionModeEnable();
|
||||
void ChoosePositionModeDisable();
|
||||
|
||||
public:
|
||||
DrawWidget(Framework & framework, std::unique_ptr<ScreenshotParams> && screenshotParams, QWidget * parent);
|
||||
~DrawWidget() override;
|
||||
|
||||
std::string GetDistance(search::Result const & res) const;
|
||||
|
||||
void CreateFeature();
|
||||
|
||||
void OnLocationUpdate(location::GpsInfo const & info);
|
||||
|
||||
void UpdateAfterSettingsChanged();
|
||||
|
||||
void PrepareShutdown();
|
||||
|
||||
Framework & GetFramework() { return m_framework; }
|
||||
|
||||
void SetMapStyle(MapStyle mapStyle);
|
||||
|
||||
void SetRuler(bool enabled);
|
||||
|
||||
RouteMarkType GetRoutePointAddMode() const { return m_routePointAddMode; }
|
||||
void SetRoutePointAddMode(RouteMarkType mode) { m_routePointAddMode = mode; }
|
||||
void FollowRoute();
|
||||
void ClearRoute();
|
||||
void OnRouteRecommendation(RoutingManager::Recommendation recommendation);
|
||||
|
||||
void RefreshDrawingRules();
|
||||
void SetMapStyleToDefault();
|
||||
void SetMapStyleToVehicle();
|
||||
void SetMapStyleToOutdoors();
|
||||
|
||||
protected:
|
||||
/// @name Overriden from MapWidget.
|
||||
//@{
|
||||
void initializeGL() override;
|
||||
|
||||
// Touch events
|
||||
bool event(QEvent * event) override;
|
||||
|
||||
// Non-touch events
|
||||
void mousePressEvent(QMouseEvent * e) override;
|
||||
void mouseMoveEvent(QMouseEvent * e) override;
|
||||
void mouseReleaseEvent(QMouseEvent * e) override;
|
||||
//@}
|
||||
|
||||
void keyPressEvent(QKeyEvent * e) override;
|
||||
void keyReleaseEvent(QKeyEvent * e) override;
|
||||
|
||||
private:
|
||||
void SubmitFakeLocationPoint(m2::PointD const & pt);
|
||||
void SubmitRulerPoint(m2::PointD const & pt);
|
||||
void SubmitRoutingPoint(m2::PointD const & pt, bool pointIsMercator);
|
||||
void SubmitBookmark(m2::PointD const & pt);
|
||||
void ShowPlacePage();
|
||||
|
||||
void VisualizeMwmsBordersInRect(m2::RectD const & rect, bool withVertices, bool fromPackedPolygon, bool boundingBox);
|
||||
|
||||
m2::PointD P2G(m2::PointD const & pt) const;
|
||||
m2::PointD GetCoordsFromSettingsIfExists(bool start, m2::PointD const & pt, bool pointIsMercator) const;
|
||||
|
||||
QRubberBand * m_rubberBand;
|
||||
QPoint m_rubberBandOrigin;
|
||||
|
||||
bool m_emulatingLocation;
|
||||
|
||||
public:
|
||||
/// Pass empty \a mode to drop selection.
|
||||
void SetSelectionMode(std::optional<SelectionMode> mode) { m_selectionMode = mode; }
|
||||
|
||||
void DropSelectionIfMWMBordersMode()
|
||||
{
|
||||
static_assert(SelectionMode::MWMBorders < SelectionMode::Cancelled, "");
|
||||
if (m_selectionMode && *m_selectionMode > SelectionMode::MWMBorders && *m_selectionMode < SelectionMode::Cancelled)
|
||||
m_selectionMode = {};
|
||||
}
|
||||
|
||||
private:
|
||||
void ProcessSelectionMode();
|
||||
std::optional<SelectionMode> m_selectionMode;
|
||||
RouteMarkType m_routePointAddMode = RouteMarkType::Finish;
|
||||
|
||||
std::unique_ptr<Screenshoter> m_screenshoter;
|
||||
Ruler m_ruler;
|
||||
RoutingTurnsVisualizer m_turnsVisualizer;
|
||||
};
|
||||
} // namespace qt
|
||||
233
qt/editor_dialog.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#include "qt/editor_dialog.hpp"
|
||||
|
||||
#include "indexer/editable_map_object.hpp"
|
||||
#include "indexer/feature_utils.hpp"
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QGridLayout>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
#include <QtCore/QSignalMapper>
|
||||
|
||||
constexpr char const * kStreetObjectName = "addr:street";
|
||||
constexpr char const * kHouseNumberObjectName = "addr:housenumber";
|
||||
constexpr char const * kPostcodeObjectName = "addr:postcode";
|
||||
constexpr char const * kInternetObjectName = "internet_access";
|
||||
|
||||
EditorDialog::EditorDialog(QWidget * parent, osm::EditableMapObject & emo) : QDialog(parent), m_feature(emo)
|
||||
{
|
||||
QGridLayout * grid = new QGridLayout();
|
||||
int row = 0;
|
||||
|
||||
// Coordinates.
|
||||
{
|
||||
ms::LatLon const ll = emo.GetLatLon();
|
||||
grid->addWidget(new QLabel("Latitude/Longitude:"), row, 0);
|
||||
QHBoxLayout * coords = new QHBoxLayout();
|
||||
coords->addWidget(new QLabel(
|
||||
QString::fromStdString(strings::to_string_dac(ll.m_lat, 7) + " " + strings::to_string_dac(ll.m_lon, 7))));
|
||||
grid->addLayout(coords, row++, 1);
|
||||
}
|
||||
|
||||
// Feature types.
|
||||
{
|
||||
grid->addWidget(new QLabel("Type:"), row, 0);
|
||||
|
||||
std::string const raw = DebugPrint(m_feature.GetTypes());
|
||||
QLabel * label = new QLabel(QString::fromStdString(raw));
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
grid->addWidget(label, row++, 1);
|
||||
}
|
||||
|
||||
// Names.
|
||||
if (emo.IsNameEditable())
|
||||
{
|
||||
grid->addWidget(new QLabel(QString("Name:")), row, 0);
|
||||
|
||||
QGridLayout * namesGrid = new QGridLayout();
|
||||
int namesRow = 0;
|
||||
for (auto const & ln : emo.GetNamesDataSource().names)
|
||||
{
|
||||
namesGrid->addWidget(new QLabel(QString::fromUtf8(ln.m_lang.data(), ln.m_lang.size())), namesRow, 0);
|
||||
QLineEdit * lineEditName = new QLineEdit(QString::fromStdString(ln.m_name));
|
||||
lineEditName->setReadOnly(!emo.IsNameEditable());
|
||||
std::string_view const code = StringUtf8Multilang::GetLangByCode(ln.m_code);
|
||||
lineEditName->setObjectName(QString::fromUtf8(code.data(), code.size()));
|
||||
namesGrid->addWidget(lineEditName, namesRow++, 1);
|
||||
}
|
||||
|
||||
grid->addLayout(namesGrid, row++, 1);
|
||||
}
|
||||
|
||||
using PropID = osm::MapObject::MetadataID;
|
||||
|
||||
// Address rows.
|
||||
if (emo.IsAddressEditable())
|
||||
{
|
||||
auto nearbyStreets = emo.GetNearbyStreets();
|
||||
grid->addWidget(new QLabel(kStreetObjectName), row, 0);
|
||||
QComboBox * cmb = new QComboBox();
|
||||
cmb->setEditable(true);
|
||||
|
||||
if (emo.GetStreet().m_defaultName.empty())
|
||||
cmb->addItem("");
|
||||
|
||||
for (size_t i = 0; i < nearbyStreets.size(); ++i)
|
||||
{
|
||||
std::string street = nearbyStreets[i].m_defaultName;
|
||||
if (!nearbyStreets[i].m_localizedName.empty())
|
||||
street += " / " + nearbyStreets[i].m_localizedName;
|
||||
cmb->addItem(street.c_str());
|
||||
if (emo.GetStreet() == nearbyStreets[i])
|
||||
cmb->setCurrentIndex(static_cast<int>(i));
|
||||
}
|
||||
cmb->setObjectName(kStreetObjectName);
|
||||
grid->addWidget(cmb, row++, 1);
|
||||
|
||||
grid->addWidget(new QLabel(kHouseNumberObjectName), row, 0);
|
||||
QLineEdit * houseLineEdit = new QLineEdit(emo.GetHouseNumber().c_str());
|
||||
houseLineEdit->setObjectName(kHouseNumberObjectName);
|
||||
grid->addWidget(houseLineEdit, row++, 1);
|
||||
|
||||
grid->addWidget(new QLabel(kPostcodeObjectName), row, 0);
|
||||
QLineEdit * postcodeEdit = new QLineEdit(QString::fromStdString(std::string(emo.GetPostcode())));
|
||||
postcodeEdit->setObjectName(kPostcodeObjectName);
|
||||
grid->addWidget(postcodeEdit, row++, 1);
|
||||
}
|
||||
|
||||
// Editable metadata rows.
|
||||
for (auto const prop : emo.GetEditableProperties())
|
||||
{
|
||||
std::string v;
|
||||
switch (prop)
|
||||
{
|
||||
case PropID::FMD_INTERNET:
|
||||
{
|
||||
grid->addWidget(new QLabel(kInternetObjectName), row, 0);
|
||||
QComboBox * cmb = new QComboBox();
|
||||
std::string const values[] = {DebugPrint(feature::Internet::Unknown), DebugPrint(feature::Internet::Wlan),
|
||||
DebugPrint(feature::Internet::Wired), DebugPrint(feature::Internet::Terminal),
|
||||
DebugPrint(feature::Internet::Yes), DebugPrint(feature::Internet::No)};
|
||||
for (auto const & v : values)
|
||||
cmb->addItem(v.c_str());
|
||||
cmb->setCurrentText(DebugPrint(emo.GetInternet()).c_str());
|
||||
cmb->setObjectName(kInternetObjectName);
|
||||
grid->addWidget(cmb, row++, 1);
|
||||
}
|
||||
continue;
|
||||
case PropID::FMD_CUISINE: v = strings::JoinStrings(emo.GetLocalizedCuisines(), ", "); break;
|
||||
case PropID::FMD_POSTCODE: // already set above
|
||||
continue;
|
||||
default: v = emo.GetMetadata(prop); break;
|
||||
}
|
||||
|
||||
QString const fieldName = QString::fromStdString(DebugPrint(prop));
|
||||
grid->addWidget(new QLabel(fieldName), row, 0);
|
||||
QLineEdit * lineEdit = new QLineEdit(QString::fromStdString(v));
|
||||
lineEdit->setObjectName(fieldName);
|
||||
grid->addWidget(lineEdit, row++, 1);
|
||||
}
|
||||
|
||||
// Dialog buttons.
|
||||
{
|
||||
QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &EditorDialog::OnSave);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
// Delete button should send custom int return value from dialog.
|
||||
QPushButton * deletePOIButton = new QPushButton("Delete POI");
|
||||
QSignalMapper * signalMapper = new QSignalMapper();
|
||||
connect(deletePOIButton, SIGNAL(clicked()), signalMapper, SLOT(map()));
|
||||
signalMapper->setMapping(deletePOIButton, QDialogButtonBox::DestructiveRole);
|
||||
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(done(int)));
|
||||
buttonBox->addButton(deletePOIButton, QDialogButtonBox::DestructiveRole);
|
||||
grid->addWidget(buttonBox, row++, 1);
|
||||
}
|
||||
|
||||
setLayout(grid);
|
||||
setWindowTitle("OSM Editor");
|
||||
}
|
||||
|
||||
void EditorDialog::OnSave()
|
||||
{
|
||||
// Store names.
|
||||
if (m_feature.IsNameEditable())
|
||||
{
|
||||
StringUtf8Multilang names;
|
||||
for (int8_t langCode = StringUtf8Multilang::kDefaultCode; langCode < StringUtf8Multilang::kMaxSupportedLanguages;
|
||||
++langCode)
|
||||
{
|
||||
std::string_view const lang = StringUtf8Multilang::GetLangByCode(langCode);
|
||||
QLineEdit * le = findChild<QLineEdit *>(QString::fromUtf8(lang.data(), lang.size()));
|
||||
if (!le)
|
||||
continue;
|
||||
|
||||
std::string const name = le->text().toStdString();
|
||||
if (!name.empty())
|
||||
names.AddString(langCode, name);
|
||||
}
|
||||
|
||||
m_feature.SetName(names);
|
||||
}
|
||||
|
||||
using PropID = osm::MapObject::MetadataID;
|
||||
|
||||
// Store address.
|
||||
if (m_feature.IsAddressEditable())
|
||||
{
|
||||
m_feature.SetHouseNumber(findChild<QLineEdit *>(kHouseNumberObjectName)->text().toStdString());
|
||||
QString const editedStreet = findChild<QComboBox *>(kStreetObjectName)->currentText();
|
||||
QStringList const names = editedStreet.split(" / ", Qt::SkipEmptyParts);
|
||||
QString const localized = names.size() > 1 ? names.at(1) : QString();
|
||||
if (!names.empty())
|
||||
m_feature.SetStreet({names.at(0).toStdString(), localized.toStdString()});
|
||||
else
|
||||
m_feature.SetStreet({});
|
||||
|
||||
QLineEdit * editor = findChild<QLineEdit *>(kPostcodeObjectName);
|
||||
std::string v = editor->text().toStdString();
|
||||
if (osm::EditableMapObject::ValidatePostCode(v))
|
||||
m_feature.SetPostcode(v);
|
||||
}
|
||||
|
||||
// Store other props.
|
||||
for (auto const prop : m_feature.GetEditableProperties())
|
||||
{
|
||||
if (prop == PropID::FMD_INTERNET)
|
||||
{
|
||||
QComboBox * cmb = findChild<QComboBox *>(kInternetObjectName);
|
||||
m_feature.SetInternet(feature::InternetFromString(cmb->currentText().toStdString()));
|
||||
continue;
|
||||
}
|
||||
if (prop == PropID::FMD_POSTCODE) // already set above
|
||||
continue;
|
||||
|
||||
QLineEdit * editor = findChild<QLineEdit *>(QString::fromStdString(DebugPrint(prop)));
|
||||
if (!editor)
|
||||
continue;
|
||||
|
||||
std::string v = editor->text().toStdString();
|
||||
switch (prop)
|
||||
{
|
||||
case PropID::FMD_CUISINE: m_feature.SetCuisines(strings::Tokenize(v, ";")); break;
|
||||
default:
|
||||
if (osm::EditableMapObject::IsValidMetadata(prop, v))
|
||||
m_feature.SetMetadata(prop, std::move(v));
|
||||
else
|
||||
{
|
||||
/// @todo Show error popup?
|
||||
editor->setFocus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
||||
21
qt/editor_dialog.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
namespace osm
|
||||
{
|
||||
class EditableMapObject;
|
||||
} // namespace osm
|
||||
|
||||
class EditorDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EditorDialog(QWidget * parent, osm::EditableMapObject & emo);
|
||||
private slots:
|
||||
void OnSave();
|
||||
|
||||
private:
|
||||
osm::EditableMapObject & m_feature;
|
||||
};
|
||||
63
qt/info_dialog.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "qt/info_dialog.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QTextBrowser>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
InfoDialog::InfoDialog(QString const & title, QString const & text, QWidget * parent, QStringList const & buttons)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
|
||||
{
|
||||
QIcon icon(":/ui/logo.png");
|
||||
setWindowIcon(icon);
|
||||
setWindowTitle(title);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
QVBoxLayout * vBox = new QVBoxLayout();
|
||||
QTextBrowser * browser = new QTextBrowser();
|
||||
browser->setReadOnly(true);
|
||||
browser->setOpenLinks(true);
|
||||
browser->setOpenExternalLinks(true);
|
||||
browser->setText(text);
|
||||
vBox->addWidget(browser);
|
||||
|
||||
// this horizontal layout is for buttons
|
||||
QHBoxLayout * hBox = new QHBoxLayout();
|
||||
hBox->addSpacing(static_cast<int>(browser->width() / 4 * (3.5 - buttons.size())));
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
QPushButton * button = new QPushButton(buttons[i], this);
|
||||
switch (i)
|
||||
{
|
||||
case 0: connect(button, &QAbstractButton::clicked, this, &InfoDialog::OnButtonClick1); break;
|
||||
case 1: connect(button, &QAbstractButton::clicked, this, &InfoDialog::OnButtonClick2); break;
|
||||
case 2: connect(button, &QAbstractButton::clicked, this, &InfoDialog::OnButtonClick3); break;
|
||||
default: ASSERT(false, ("Only 3 buttons are currently supported in info dialog"));
|
||||
}
|
||||
hBox->addWidget(button);
|
||||
}
|
||||
|
||||
vBox->addLayout(hBox);
|
||||
setLayout(vBox);
|
||||
}
|
||||
|
||||
void InfoDialog::OnButtonClick1()
|
||||
{
|
||||
done(1);
|
||||
}
|
||||
void InfoDialog::OnButtonClick2()
|
||||
{
|
||||
done(2);
|
||||
}
|
||||
void InfoDialog::OnButtonClick3()
|
||||
{
|
||||
done(3);
|
||||
}
|
||||
} // namespace qt
|
||||
22
qt/info_dialog.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
/// Simple information dialog with scrollable html content
|
||||
/// @note exec() returns 0 if dialog was closed or [1..buttons_count] for any button pressed
|
||||
class InfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InfoDialog(QString const & title, QString const & text, QWidget * parent,
|
||||
QStringList const & buttons = QStringList());
|
||||
public Q_SLOTS:
|
||||
void OnButtonClick1();
|
||||
void OnButtonClick2();
|
||||
void OnButtonClick3();
|
||||
};
|
||||
} // namespace qt
|
||||
270
qt/main.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
#include "qt/info_dialog.hpp"
|
||||
#include "qt/mainwindow.hpp"
|
||||
#include "qt/screenshoter.hpp"
|
||||
|
||||
#include "qt/qt_common/helpers.hpp"
|
||||
|
||||
#include "map/framework.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include "build_style/build_style.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_string(data_path, "", "Path to data directory.");
|
||||
DEFINE_string(log_abort_level, base::ToString(base::GetDefaultLogAbortLevel()),
|
||||
"Log messages severity that causes termination.");
|
||||
DEFINE_string(resources_path, "", "Path to resources directory.");
|
||||
DEFINE_string(kml_path, "",
|
||||
"Activates screenshot mode. Path to a kml file or a directory with kml files to take screenshots.");
|
||||
DEFINE_string(points, "",
|
||||
"Activates screenshot mode. Points on the map and zoom level "
|
||||
"[1..18] in format \"lat,lon,zoom[;lat,lon,zoom]\" or path to a file with points in "
|
||||
"the same format. Each point and zoom define a place on the map to take screenshot.");
|
||||
DEFINE_string(rects, "",
|
||||
"Activates screenshot mode. Rects on the map in format"
|
||||
"\"lat_leftBottom,lon_leftBottom,lat_rightTop,lon_rightTop"
|
||||
"[;lat_leftBottom,lon_leftBottom,lat_rightTop,lon_rightTop]\" or path to a file with "
|
||||
"rects in the same format. Each rect defines a place on the map to take screenshot.");
|
||||
DEFINE_string(dst_path, "", "Path to a directory to save screenshots.");
|
||||
DEFINE_string(lang, "", "Device language.");
|
||||
DEFINE_int32(width, 0, "Screenshot width.");
|
||||
DEFINE_int32(height, 0, "Screenshot height.");
|
||||
DEFINE_double(
|
||||
dpi_scale, 0.0,
|
||||
"Screenshot dpi scale (mdpi = 1.0, hdpi = 1.5, xhdpiScale = 2.0, 6plus = 2.4, xxhdpi = 3.0, xxxhdpi = 3.5).");
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ValidateLogAbortLevel(char const * flagname, std::string const & value)
|
||||
{
|
||||
if (auto level = base::FromString(value); !level)
|
||||
{
|
||||
std::cerr << "Invalid value for --" << flagname << ": " << value << ", must be one of: ";
|
||||
auto const & names = base::GetLogLevelNames();
|
||||
for (size_t i = 0; i < names.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
std::cerr << ", ";
|
||||
std::cerr << names[i];
|
||||
}
|
||||
std::cerr << '\n';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool const g_logAbortLevelDummy = gflags::RegisterFlagValidator(&FLAGS_log_abort_level, &ValidateLogAbortLevel);
|
||||
|
||||
class FinalizeBase
|
||||
{
|
||||
public:
|
||||
~FinalizeBase()
|
||||
{
|
||||
// optional - clean allocated data in protobuf library
|
||||
// useful when using memory and resource leak utilites
|
||||
// google::protobuf::ShutdownProtobufLibrary();
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(OMIM_OS_WINDOWS) //&& defined(PROFILER_COMMON)
|
||||
class InitializeFinalize : public FinalizeBase
|
||||
{
|
||||
FILE * m_errFile;
|
||||
base::ScopedLogLevelChanger const m_debugLog;
|
||||
|
||||
public:
|
||||
InitializeFinalize() : m_debugLog(LDEBUG)
|
||||
{
|
||||
// App runs without error console under win32.
|
||||
m_errFile = ::freopen(".\\mapsme.log", "w", stderr);
|
||||
|
||||
//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF);
|
||||
//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
}
|
||||
~InitializeFinalize() { ::fclose(m_errFile); }
|
||||
};
|
||||
#else
|
||||
typedef FinalizeBase InitializeFinalize;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
// Our double parsing code (base/string_utils.hpp) needs dots as a floating point delimiters, not commas.
|
||||
// TODO: Refactor our doubles parsing code to use locale-independent delimiters.
|
||||
// For example, https://github.com/google/double-conversion can be used.
|
||||
// See http://dbaron.org/log/20121222-locale for more details.
|
||||
(void)::setenv("LC_NUMERIC", "C", 1);
|
||||
|
||||
Platform & platform = GetPlatform();
|
||||
|
||||
LOG(LINFO, ("CoMaps", platform.Version(), "built with QT:", QT_VERSION_STR, "runtime QT:", qVersion(),
|
||||
"detected CPU cores:", platform.CpuCores()));
|
||||
|
||||
gflags::SetUsageMessage("Desktop application.");
|
||||
gflags::SetVersionString(platform.Version());
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
if (!FLAGS_resources_path.empty())
|
||||
platform.SetResourceDir(FLAGS_resources_path);
|
||||
if (!FLAGS_data_path.empty())
|
||||
platform.SetWritableDirForTests(FLAGS_data_path);
|
||||
|
||||
if (auto const logLevel = base::FromString(FLAGS_log_abort_level); logLevel)
|
||||
base::g_LogAbortLevel = *logLevel;
|
||||
else
|
||||
LOG(LCRITICAL, ("Invalid log level:", FLAGS_log_abort_level));
|
||||
|
||||
Q_INIT_RESOURCE(resources_common);
|
||||
|
||||
InitializeFinalize mainGuard;
|
||||
UNUSED_VALUE(mainGuard);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
app.setDesktopFileName("app.comaps.comaps");
|
||||
platform.SetupMeasurementSystem();
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
QApplication::setApplicationName("CoMaps Designer");
|
||||
#else
|
||||
QApplication::setApplicationName("CoMaps");
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool constexpr developerMode = true;
|
||||
#else
|
||||
static bool constexpr developerMode = false;
|
||||
#endif
|
||||
bool outvalue;
|
||||
if (!settings::Get(settings::kDeveloperMode, outvalue))
|
||||
settings::Set(settings::kDeveloperMode, developerMode);
|
||||
|
||||
// Display EULA if needed.
|
||||
char const * settingsEULA = "EulaAccepted";
|
||||
bool eulaAccepted = false;
|
||||
if (!settings::Get(settingsEULA, eulaAccepted) || !eulaAccepted)
|
||||
{
|
||||
std::string buffer;
|
||||
{
|
||||
ReaderPtr<Reader> reader = platform.GetReader("copyright.html");
|
||||
reader.ReadAsString(buffer);
|
||||
}
|
||||
qt::InfoDialog eulaDialog(QCoreApplication::applicationName(), buffer.c_str(), nullptr, {"Accept", "Decline"});
|
||||
eulaAccepted = (eulaDialog.exec() == 1);
|
||||
settings::Set(settingsEULA, eulaAccepted);
|
||||
}
|
||||
|
||||
int returnCode = -1;
|
||||
if (eulaAccepted) // User has accepted EULA
|
||||
{
|
||||
std::unique_ptr<qt::ScreenshotParams> screenshotParams;
|
||||
|
||||
if (!FLAGS_lang.empty())
|
||||
(void)::setenv("LANGUAGE", FLAGS_lang.c_str(), 1);
|
||||
|
||||
if (!FLAGS_kml_path.empty() || !FLAGS_points.empty() || !FLAGS_rects.empty())
|
||||
{
|
||||
screenshotParams = std::make_unique<qt::ScreenshotParams>();
|
||||
if (!FLAGS_kml_path.empty())
|
||||
{
|
||||
screenshotParams->m_kmlPath = FLAGS_kml_path;
|
||||
screenshotParams->m_mode = qt::ScreenshotParams::Mode::KmlFiles;
|
||||
}
|
||||
else if (!FLAGS_points.empty())
|
||||
{
|
||||
screenshotParams->m_points = FLAGS_points;
|
||||
screenshotParams->m_mode = qt::ScreenshotParams::Mode::Points;
|
||||
}
|
||||
else if (!FLAGS_rects.empty())
|
||||
{
|
||||
screenshotParams->m_rects = FLAGS_rects;
|
||||
screenshotParams->m_mode = qt::ScreenshotParams::Mode::Rects;
|
||||
}
|
||||
if (!FLAGS_dst_path.empty())
|
||||
screenshotParams->m_dstPath = FLAGS_dst_path;
|
||||
if (FLAGS_width > 0)
|
||||
screenshotParams->m_width = FLAGS_width;
|
||||
if (FLAGS_height > 0)
|
||||
screenshotParams->m_height = FLAGS_height;
|
||||
if (FLAGS_dpi_scale >= df::VisualParams::kMdpiScale && FLAGS_dpi_scale <= df::VisualParams::kXxxhdpiScale)
|
||||
screenshotParams->m_dpiScale = FLAGS_dpi_scale;
|
||||
}
|
||||
|
||||
qt::common::SetDefaultSurfaceFormat(QApplication::platformName());
|
||||
|
||||
FrameworkParams frameworkParams;
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
QString mapcssFilePath;
|
||||
if (argc >= 2 && platform.IsFileExistsByFullPath(argv[1]))
|
||||
mapcssFilePath = argv[1];
|
||||
if (0 == mapcssFilePath.length())
|
||||
mapcssFilePath = QFileDialog::getOpenFileName(nullptr, "Open style.mapcss file", "~/", "MapCSS Files (*.mapcss)");
|
||||
if (mapcssFilePath.isEmpty())
|
||||
return returnCode;
|
||||
|
||||
try
|
||||
{
|
||||
build_style::BuildIfNecessaryAndApply(mapcssFilePath);
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
#endif // BUILD_DESIGNER
|
||||
|
||||
Framework framework(frameworkParams);
|
||||
qt::MainWindow w(framework, std::move(screenshotParams), QApplication::primaryScreen()->geometry()
|
||||
#ifdef BUILD_DESIGNER
|
||||
,
|
||||
mapcssFilePath
|
||||
#endif // BUILD_DESIGNER
|
||||
);
|
||||
w.show();
|
||||
returnCode = QApplication::exec();
|
||||
}
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
if (build_style::NeedRecalculate && !mapcssFilePath.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
build_style::RunRecalculationGeometryScript(mapcssFilePath);
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
#endif // BUILD_DESIGNER
|
||||
|
||||
LOG_SHORT(LINFO, ("Finished with code", returnCode));
|
||||
return returnCode;
|
||||
}
|
||||
926
qt/mainwindow.cpp
Normal file
|
|
@ -0,0 +1,926 @@
|
|||
#include "qt/mainwindow.hpp"
|
||||
#include "qt/about.hpp"
|
||||
#include "qt/bookmark_dialog.hpp"
|
||||
#include "qt/draw_widget.hpp"
|
||||
#include "qt/mwms_borders_selection.hpp"
|
||||
#include "qt/osm_auth_dialog.hpp"
|
||||
#include "qt/popup_menu_holder.hpp"
|
||||
#include "qt/preferences_dialog.hpp"
|
||||
#include "qt/qt_common/helpers.hpp"
|
||||
#include "qt/qt_common/scale_slider.hpp"
|
||||
#include "qt/routing_settings_dialog.hpp"
|
||||
#include "qt/screenshoter.hpp"
|
||||
#include "qt/search_panel.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include "defines.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
#include "build_style/build_common.h"
|
||||
#include "build_style/build_phone_pack.h"
|
||||
#include "build_style/build_statistics.h"
|
||||
#include "build_style/build_style.h"
|
||||
#include "build_style/run_tests.h"
|
||||
|
||||
#include "drape_frontend/debug_rect_renderer.hpp"
|
||||
#endif // BUILD_DESIGNER
|
||||
|
||||
#include <QtGui/QCloseEvent>
|
||||
#include <QtWidgets/QDockWidget>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QStatusBar>
|
||||
#include <QtWidgets/QToolBar>
|
||||
|
||||
#ifdef OMIM_OS_WINDOWS
|
||||
#include "std/windows.hpp"
|
||||
#define IDM_ABOUT_DIALOG 1001
|
||||
#define IDM_PREFERENCES_DIALOG 1002
|
||||
#endif
|
||||
|
||||
#ifndef NO_DOWNLOADER
|
||||
#include "qt/info_dialog.hpp"
|
||||
#include "qt/update_dialog.hpp"
|
||||
#endif // NO_DOWNLOADER
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void FormatMapSize(uint64_t sizeInBytes, std::string & units, size_t & sizeToDownload)
|
||||
{
|
||||
int const mbInBytes = 1024 * 1024;
|
||||
int const kbInBytes = 1024;
|
||||
if (sizeInBytes > mbInBytes)
|
||||
{
|
||||
sizeToDownload = (sizeInBytes + mbInBytes - 1) / mbInBytes;
|
||||
units = "MB";
|
||||
}
|
||||
else if (sizeInBytes > kbInBytes)
|
||||
{
|
||||
sizeToDownload = (sizeInBytes + kbInBytes - 1) / kbInBytes;
|
||||
units = "KB";
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeToDownload = sizeInBytes;
|
||||
units = "B";
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T * CreateBlackControl(QString const & name)
|
||||
{
|
||||
T * p = new T(name);
|
||||
p->setStyleSheet("color: black;");
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Defined in osm_auth_dialog.cpp.
|
||||
extern char const * kOauthTokenSetting;
|
||||
|
||||
MainWindow::MainWindow(Framework & framework, std::unique_ptr<ScreenshotParams> && screenshotParams,
|
||||
QRect const & screenGeometry
|
||||
#ifdef BUILD_DESIGNER
|
||||
,
|
||||
QString const & mapcssFilePath
|
||||
#endif
|
||||
)
|
||||
: m_locationService(CreateDesktopLocationService(*this))
|
||||
, m_screenshotMode(screenshotParams != nullptr)
|
||||
#ifdef BUILD_DESIGNER
|
||||
, m_mapcssFilePath(mapcssFilePath)
|
||||
#endif
|
||||
{
|
||||
setGeometry(screenGeometry);
|
||||
|
||||
if (m_screenshotMode)
|
||||
{
|
||||
screenshotParams->m_statusChangedFn = [this](std::string const & state, bool finished)
|
||||
{
|
||||
statusBar()->showMessage(QString::fromStdString(state));
|
||||
if (finished)
|
||||
QCoreApplication::quit();
|
||||
};
|
||||
}
|
||||
|
||||
int const width = m_screenshotMode ? static_cast<int>(screenshotParams->m_width) : 0;
|
||||
int const height = m_screenshotMode ? static_cast<int>(screenshotParams->m_height) : 0;
|
||||
m_pDrawWidget = new DrawWidget(framework, std::move(screenshotParams), this);
|
||||
|
||||
setCentralWidget(m_pDrawWidget);
|
||||
|
||||
if (m_screenshotMode)
|
||||
{
|
||||
m_pDrawWidget->setFixedSize(width, height);
|
||||
setFixedSize(width, height + statusBar()->height());
|
||||
}
|
||||
|
||||
connect(m_pDrawWidget, SIGNAL(BeforeEngineCreation()), this, SLOT(OnBeforeEngineCreation()));
|
||||
|
||||
CreateCountryStatusControls();
|
||||
CreateNavigationBar();
|
||||
CreateSearchBarAndPanel();
|
||||
|
||||
QString caption = QCoreApplication::applicationName();
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
if (!m_mapcssFilePath.isEmpty())
|
||||
caption += QString(" - ") + m_mapcssFilePath;
|
||||
#endif
|
||||
|
||||
setWindowTitle(caption);
|
||||
setWindowIcon(QIcon(":/ui/logo.png"));
|
||||
|
||||
#ifndef OMIM_OS_WINDOWS
|
||||
QMenu * helpMenu = new QMenu(tr("Help"), this);
|
||||
menuBar()->addMenu(helpMenu);
|
||||
helpMenu->addAction(tr("OpenStreetMap Login"), QKeySequence(Qt::CTRL | Qt::Key_O), this, SLOT(OnLoginMenuItem()));
|
||||
helpMenu->addAction(tr("Upload Edits"), QKeySequence(Qt::CTRL | Qt::Key_U), this, SLOT(OnUploadEditsMenuItem()));
|
||||
helpMenu->addAction(tr("Preferences"), QKeySequence(Qt::CTRL | Qt::Key_P), this, SLOT(OnPreferences()));
|
||||
helpMenu->addAction(tr("About"), QKeySequence(Qt::Key_F1), this, SLOT(OnAbout()));
|
||||
helpMenu->addAction(tr("Exit"), QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
|
||||
#else
|
||||
{
|
||||
// create items in the system menu
|
||||
HMENU menu = ::GetSystemMenu((HWND)winId(), FALSE);
|
||||
MENUITEMINFOA item;
|
||||
item.cbSize = sizeof(MENUITEMINFOA);
|
||||
item.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
|
||||
item.fType = MFT_STRING;
|
||||
item.wID = IDM_PREFERENCES_DIALOG;
|
||||
QByteArray const prefsStr = tr("Preferences...").toLocal8Bit();
|
||||
item.dwTypeData = const_cast<char *>(prefsStr.data());
|
||||
item.cch = prefsStr.size();
|
||||
::InsertMenuItemA(menu, ::GetMenuItemCount(menu) - 1, TRUE, &item);
|
||||
item.wID = IDM_ABOUT_DIALOG;
|
||||
QByteArray const aboutStr = tr("About...").toLocal8Bit();
|
||||
item.dwTypeData = const_cast<char *>(aboutStr.data());
|
||||
item.cch = aboutStr.size();
|
||||
::InsertMenuItemA(menu, ::GetMenuItemCount(menu) - 1, TRUE, &item);
|
||||
item.fType = MFT_SEPARATOR;
|
||||
::InsertMenuItemA(menu, ::GetMenuItemCount(menu) - 1, TRUE, &item);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Always show on full screen.
|
||||
showMaximized();
|
||||
|
||||
#ifndef NO_DOWNLOADER
|
||||
// Show intro dialog if necessary
|
||||
bool bShow = true;
|
||||
std::string const showWelcome = "ShowWelcome";
|
||||
settings::TryGet(showWelcome, bShow);
|
||||
|
||||
if (bShow)
|
||||
{
|
||||
bool bShowUpdateDialog = true;
|
||||
|
||||
std::string text;
|
||||
try
|
||||
{
|
||||
ReaderPtr<Reader> reader = GetPlatform().GetReader("welcome.html");
|
||||
reader.ReadAsString(text);
|
||||
}
|
||||
catch (...)
|
||||
{}
|
||||
|
||||
if (!text.empty())
|
||||
{
|
||||
InfoDialog welcomeDlg(QString("Welcome to ") + caption, text.c_str(), this, QStringList(tr("Download Maps")));
|
||||
if (welcomeDlg.exec() == QDialog::Rejected)
|
||||
bShowUpdateDialog = false;
|
||||
}
|
||||
settings::Set("ShowWelcome", false);
|
||||
|
||||
if (bShowUpdateDialog)
|
||||
ShowUpdateDialog();
|
||||
}
|
||||
#endif // NO_DOWNLOADER
|
||||
|
||||
m_pDrawWidget->UpdateAfterSettingsChanged();
|
||||
|
||||
RoutingSettings::LoadSession(m_pDrawWidget->GetFramework());
|
||||
}
|
||||
|
||||
#if defined(OMIM_OS_WINDOWS)
|
||||
bool MainWindow::nativeEvent(QByteArray const & eventType, void * message, qintptr * result)
|
||||
{
|
||||
MSG * msg = static_cast<MSG *>(message);
|
||||
if (msg->message == WM_SYSCOMMAND)
|
||||
{
|
||||
switch (msg->wParam)
|
||||
{
|
||||
case IDM_PREFERENCES_DIALOG:
|
||||
OnPreferences();
|
||||
*result = 0;
|
||||
return true;
|
||||
case IDM_ABOUT_DIALOG:
|
||||
OnAbout();
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QMainWindow::nativeEvent(eventType, message, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::LocationStateModeChanged(location::EMyPositionMode mode)
|
||||
{
|
||||
if (mode == location::PendingPosition)
|
||||
{
|
||||
m_locationService->Start();
|
||||
m_pMyPositionAction->setIcon(QIcon(":/navig64/location-search.png"));
|
||||
m_pMyPositionAction->setToolTip(tr("Looking for position..."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_pMyPositionAction->setIcon(QIcon(":/navig64/location.png"));
|
||||
m_pMyPositionAction->setToolTip(tr("My Position"));
|
||||
}
|
||||
|
||||
void MainWindow::CreateNavigationBar()
|
||||
{
|
||||
QToolBar * pToolBar = new QToolBar(tr("Navigation Bar"), this);
|
||||
pToolBar->setOrientation(Qt::Vertical);
|
||||
pToolBar->setIconSize(QSize(32, 32));
|
||||
{
|
||||
m_pDrawWidget->BindHotkeys(*this);
|
||||
|
||||
// Add navigation hot keys.
|
||||
qt::common::Hotkey const hotkeys[] = {{Qt::Key_A, SLOT(ShowAll())},
|
||||
// Use CMD+n (New Item hotkey) to activate Create Feature mode.
|
||||
{Qt::Key_Escape, SLOT(ChoosePositionModeDisable())}};
|
||||
|
||||
for (auto const & hotkey : hotkeys)
|
||||
{
|
||||
QAction * pAct = new QAction(this);
|
||||
pAct->setShortcut(QKeySequence(hotkey.m_key));
|
||||
connect(pAct, SIGNAL(triggered()), m_pDrawWidget, hotkey.m_slot);
|
||||
addAction(pAct);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
m_layers = new PopupMenuHolder(this);
|
||||
|
||||
/// @todo Uncomment when we will integrate a traffic provider.
|
||||
// m_layers->addAction(QIcon(":/navig64/traffic.png"), tr("Traffic"),
|
||||
// std::bind(&MainWindow::OnLayerEnabled, this, LayerType::TRAFFIC), true);
|
||||
// m_layers->setChecked(LayerType::TRAFFIC, m_pDrawWidget->GetFramework().LoadTrafficEnabled());
|
||||
|
||||
m_layers->addAction(QIcon(":/navig64/subway.png"), tr("Public transport"),
|
||||
std::bind(&MainWindow::OnLayerEnabled, this, LayerType::TRANSIT), true);
|
||||
m_layers->setChecked(LayerType::TRANSIT, m_pDrawWidget->GetFramework().LoadTransitSchemeEnabled());
|
||||
|
||||
m_layers->addAction(QIcon(":/navig64/isolines.png"), tr("Isolines"),
|
||||
std::bind(&MainWindow::OnLayerEnabled, this, LayerType::ISOLINES), true);
|
||||
m_layers->setChecked(LayerType::ISOLINES, m_pDrawWidget->GetFramework().LoadIsolinesEnabled());
|
||||
|
||||
m_layers->addAction(QIcon(":/navig64/isolines.png"), tr("Outdoors"),
|
||||
std::bind(&MainWindow::OnLayerEnabled, this, LayerType::OUTDOORS), true);
|
||||
m_layers->setChecked(LayerType::OUTDOORS, m_pDrawWidget->GetFramework().LoadOutdoorsEnabled());
|
||||
|
||||
pToolBar->addWidget(m_layers->create());
|
||||
m_layers->setMainIcon(QIcon(":/navig64/layers.png"));
|
||||
|
||||
pToolBar->addSeparator();
|
||||
|
||||
pToolBar->addAction(QIcon(":/navig64/bookmark.png"),
|
||||
tr("Show bookmarks and tracks; use ALT + RMB to add a bookmark"), this,
|
||||
SLOT(OnBookmarksAction()));
|
||||
pToolBar->addSeparator();
|
||||
|
||||
#ifndef BUILD_DESIGNER
|
||||
m_routing = new PopupMenuHolder(this);
|
||||
|
||||
// The order should be the same as in "enum class RouteMarkType".
|
||||
m_routing->addAction(QIcon(":/navig64/point-start.png"), tr("Start point"),
|
||||
std::bind(&MainWindow::OnRoutePointSelected, this, RouteMarkType::Start), false);
|
||||
m_routing->addAction(QIcon(":/navig64/point-intermediate.png"), tr("Intermediate point"),
|
||||
std::bind(&MainWindow::OnRoutePointSelected, this, RouteMarkType::Intermediate), false);
|
||||
m_routing->addAction(QIcon(":/navig64/point-finish.png"), tr("Finish point"),
|
||||
std::bind(&MainWindow::OnRoutePointSelected, this, RouteMarkType::Finish), false);
|
||||
|
||||
QToolButton * toolBtn = m_routing->create();
|
||||
toolBtn->setToolTip(tr("Select mode and use SHIFT + LMB to set point"));
|
||||
pToolBar->addWidget(toolBtn);
|
||||
m_routing->setCurrent(m_pDrawWidget->GetRoutePointAddMode());
|
||||
|
||||
QAction * act =
|
||||
pToolBar->addAction(QIcon(":/navig64/routing.png"), tr("Follow route"), this, SLOT(OnFollowRoute()));
|
||||
act->setToolTip(tr("Build route and use ALT + LMB to emulate current position"));
|
||||
pToolBar->addAction(QIcon(":/navig64/clear-route.png"), tr("Clear route"), this, SLOT(OnClearRoute()));
|
||||
pToolBar->addAction(QIcon(":/navig64/settings-routing.png"), tr("Routing settings"), this,
|
||||
SLOT(OnRoutingSettings()));
|
||||
|
||||
pToolBar->addSeparator();
|
||||
|
||||
m_pCreateFeatureAction =
|
||||
pToolBar->addAction(QIcon(":/navig64/select.png"), tr("Create Feature"), this, SLOT(OnCreateFeatureClicked()));
|
||||
m_pCreateFeatureAction->setCheckable(true);
|
||||
m_pCreateFeatureAction->setToolTip(tr("Push to select position, next push to create Feature"));
|
||||
m_pCreateFeatureAction->setShortcut(QKeySequence::New);
|
||||
|
||||
pToolBar->addSeparator();
|
||||
|
||||
m_selection = new PopupMenuHolder(this);
|
||||
|
||||
// The order should be the same as in "enum class SelectionMode".
|
||||
m_selection->addAction(QIcon(":/navig64/selectmode.png"), tr("Roads selection mode"),
|
||||
std::bind(&MainWindow::OnSwitchSelectionMode, this, SelectionMode::Features), true);
|
||||
m_selection->addAction(QIcon(":/navig64/city_boundaries.png"), tr("City boundaries selection mode"),
|
||||
std::bind(&MainWindow::OnSwitchSelectionMode, this, SelectionMode::CityBoundaries), true);
|
||||
m_selection->addAction(QIcon(":/navig64/city_roads.png"), tr("City roads selection mode"),
|
||||
std::bind(&MainWindow::OnSwitchSelectionMode, this, SelectionMode::CityRoads), true);
|
||||
m_selection->addAction(QIcon(":/navig64/test.png"), tr("Cross MWM segments selection mode"),
|
||||
std::bind(&MainWindow::OnSwitchSelectionMode, this, SelectionMode::CrossMwmSegments), true);
|
||||
m_selection->addAction(QIcon(":/navig64/borders_selection.png"), tr("MWMs borders selection mode"), this,
|
||||
SLOT(OnSwitchMwmsBordersSelectionMode()), true);
|
||||
|
||||
toolBtn = m_selection->create();
|
||||
toolBtn->setToolTip(tr("Select mode and use RMB to define selection box"));
|
||||
pToolBar->addWidget(toolBtn);
|
||||
|
||||
pToolBar->addAction(QIcon(":/navig64/clear.png"), tr("Clear selection"), this, SLOT(OnClearSelection()));
|
||||
|
||||
pToolBar->addSeparator();
|
||||
|
||||
#endif // NOT BUILD_DESIGNER
|
||||
|
||||
// Add search button with "checked" behavior.
|
||||
m_pSearchAction =
|
||||
pToolBar->addAction(QIcon(":/navig64/search.png"), tr("Offline Search"), this, SLOT(OnSearchButtonClicked()));
|
||||
m_pSearchAction->setCheckable(true);
|
||||
m_pSearchAction->setShortcut(QKeySequence::Find);
|
||||
|
||||
m_rulerAction = pToolBar->addAction(QIcon(":/navig64/ruler.png"), tr("Ruler"), this, SLOT(OnRulerEnabled()));
|
||||
m_rulerAction->setToolTip(tr("Check this button and use ALT + LMB to set points"));
|
||||
m_rulerAction->setCheckable(true);
|
||||
m_rulerAction->setChecked(false);
|
||||
|
||||
pToolBar->addSeparator();
|
||||
|
||||
// add my position button with "checked" behavior
|
||||
|
||||
m_pMyPositionAction =
|
||||
pToolBar->addAction(QIcon(":/navig64/location.png"), tr("My Position"), this, SLOT(OnMyPosition()));
|
||||
m_pMyPositionAction->setCheckable(true);
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
// Add "Build style" button
|
||||
if (!m_mapcssFilePath.isEmpty())
|
||||
{
|
||||
m_pBuildStyleAction =
|
||||
pToolBar->addAction(QIcon(":/navig64/run.png"), tr("Build style"), this, SLOT(OnBuildStyle()));
|
||||
m_pBuildStyleAction->setCheckable(false);
|
||||
m_pBuildStyleAction->setToolTip(tr("Build style"));
|
||||
|
||||
m_pRecalculateGeomIndex = pToolBar->addAction(QIcon(":/navig64/geom.png"), tr("Recalculate geometry index"), this,
|
||||
SLOT(OnRecalculateGeomIndex()));
|
||||
m_pRecalculateGeomIndex->setCheckable(false);
|
||||
m_pRecalculateGeomIndex->setToolTip(tr("Recalculate geometry index"));
|
||||
}
|
||||
|
||||
// Add "Debug style" button
|
||||
m_pDrawDebugRectAction =
|
||||
pToolBar->addAction(QIcon(":/navig64/bug.png"), tr("Debug style"), this, SLOT(OnDebugStyle()));
|
||||
m_pDrawDebugRectAction->setCheckable(true);
|
||||
m_pDrawDebugRectAction->setChecked(false);
|
||||
m_pDrawDebugRectAction->setToolTip(tr("Debug style"));
|
||||
m_pDrawWidget->GetFramework().EnableDebugRectRendering(false);
|
||||
|
||||
// Add "Get statistics" button
|
||||
m_pGetStatisticsAction =
|
||||
pToolBar->addAction(QIcon(":/navig64/chart.png"), tr("Get statistics"), this, SLOT(OnGetStatistics()));
|
||||
m_pGetStatisticsAction->setCheckable(false);
|
||||
m_pGetStatisticsAction->setToolTip(tr("Get statistics"));
|
||||
|
||||
// Add "Run tests" button
|
||||
m_pRunTestsAction = pToolBar->addAction(QIcon(":/navig64/test.png"), tr("Run tests"), this, SLOT(OnRunTests()));
|
||||
m_pRunTestsAction->setCheckable(false);
|
||||
m_pRunTestsAction->setToolTip(tr("Run tests"));
|
||||
|
||||
// Add "Build phone package" button
|
||||
m_pBuildPhonePackAction = pToolBar->addAction(QIcon(":/navig64/phonepack.png"), tr("Build phone package"), this,
|
||||
SLOT(OnBuildPhonePackage()));
|
||||
m_pBuildPhonePackAction->setCheckable(false);
|
||||
m_pBuildPhonePackAction->setToolTip(tr("Build phone package"));
|
||||
#endif // BUILD_DESIGNER
|
||||
}
|
||||
|
||||
pToolBar->addSeparator();
|
||||
qt::common::ScaleSlider::Embed(Qt::Vertical, *pToolBar, *m_pDrawWidget);
|
||||
|
||||
#ifndef NO_DOWNLOADER
|
||||
pToolBar->addSeparator();
|
||||
pToolBar->addAction(QIcon(":/navig64/download.png"), tr("Download Maps"), this, SLOT(ShowUpdateDialog()));
|
||||
#endif // NO_DOWNLOADER
|
||||
|
||||
if (m_screenshotMode)
|
||||
pToolBar->setVisible(false);
|
||||
|
||||
addToolBar(Qt::RightToolBarArea, pToolBar);
|
||||
}
|
||||
|
||||
Framework & MainWindow::GetFramework() const
|
||||
{
|
||||
return m_pDrawWidget->GetFramework();
|
||||
}
|
||||
|
||||
void MainWindow::CreateCountryStatusControls()
|
||||
{
|
||||
QHBoxLayout * mainLayout = new QHBoxLayout();
|
||||
m_downloadButton = CreateBlackControl<QPushButton>("Download");
|
||||
mainLayout->addWidget(m_downloadButton, 0, Qt::AlignHCenter);
|
||||
m_downloadButton->setVisible(false);
|
||||
connect(m_downloadButton, &QAbstractButton::released, this, &MainWindow::OnDownloadClicked);
|
||||
|
||||
m_retryButton = CreateBlackControl<QPushButton>("Retry downloading");
|
||||
mainLayout->addWidget(m_retryButton, 0, Qt::AlignHCenter);
|
||||
m_retryButton->setVisible(false);
|
||||
connect(m_retryButton, &QAbstractButton::released, this, &MainWindow::OnRetryDownloadClicked);
|
||||
|
||||
m_downloadingStatusLabel = CreateBlackControl<QLabel>("Downloading");
|
||||
mainLayout->addWidget(m_downloadingStatusLabel, 0, Qt::AlignHCenter);
|
||||
m_downloadingStatusLabel->setVisible(false);
|
||||
|
||||
m_pDrawWidget->setLayout(mainLayout);
|
||||
|
||||
auto const OnCountryChanged = [this](storage::CountryId const & countryId)
|
||||
{
|
||||
m_downloadButton->setVisible(false);
|
||||
m_retryButton->setVisible(false);
|
||||
m_downloadingStatusLabel->setVisible(false);
|
||||
|
||||
m_lastCountry = countryId;
|
||||
// Called by Framework in World zoom level.
|
||||
if (countryId.empty())
|
||||
return;
|
||||
|
||||
auto const & storage = GetFramework().GetStorage();
|
||||
auto status = storage.CountryStatusEx(countryId);
|
||||
auto const & countryName = countryId;
|
||||
|
||||
if (status == storage::Status::NotDownloaded)
|
||||
{
|
||||
m_downloadButton->setVisible(true);
|
||||
|
||||
std::string units;
|
||||
size_t sizeToDownload = 0;
|
||||
FormatMapSize(storage.CountrySizeInBytes(countryId).second, units, sizeToDownload);
|
||||
std::stringstream str;
|
||||
str << "Download (" << countryName << ") " << sizeToDownload << units;
|
||||
m_downloadButton->setText(str.str().c_str());
|
||||
}
|
||||
else if (status == storage::Status::Downloading)
|
||||
{
|
||||
m_downloadingStatusLabel->setVisible(true);
|
||||
}
|
||||
else if (status == storage::Status::InQueue)
|
||||
{
|
||||
m_downloadingStatusLabel->setVisible(true);
|
||||
|
||||
std::stringstream str;
|
||||
str << countryName << " is waiting for downloading";
|
||||
m_downloadingStatusLabel->setText(str.str().c_str());
|
||||
}
|
||||
else if (status != storage::Status::OnDisk && status != storage::Status::OnDiskOutOfDate)
|
||||
{
|
||||
m_retryButton->setVisible(true);
|
||||
|
||||
std::stringstream str;
|
||||
str << "Retry to download " << countryName;
|
||||
m_retryButton->setText(str.str().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
GetFramework().SetCurrentCountryChangedListener(OnCountryChanged);
|
||||
|
||||
GetFramework().GetStorage().Subscribe(
|
||||
[this, onChanged = std::move(OnCountryChanged)](storage::CountryId const & countryId)
|
||||
{
|
||||
// Storage also calls notifications for parents, but we are interested in leafs only.
|
||||
if (GetFramework().GetStorage().IsLeaf(countryId))
|
||||
onChanged(countryId);
|
||||
}, [this](storage::CountryId const & countryId, downloader::Progress const & progress)
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "Downloading (" << countryId << ") " << progress.m_bytesDownloaded * 100 / progress.m_bytesTotal << "%";
|
||||
m_downloadingStatusLabel->setText(str.str().c_str());
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::OnAbout()
|
||||
{
|
||||
AboutDialog dlg(this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void MainWindow::OnLocationError(location::TLocationError errorCode)
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case location::EDenied: [[fallthrough]];
|
||||
case location::ETimeout: [[fallthrough]];
|
||||
case location::EUnknown:
|
||||
{
|
||||
if (m_pDrawWidget && m_pMyPositionAction)
|
||||
m_pMyPositionAction->setEnabled(false);
|
||||
break;
|
||||
}
|
||||
|
||||
default: ASSERT(false, ("Not handled location notification:", errorCode)); break;
|
||||
}
|
||||
|
||||
if (m_pDrawWidget != nullptr)
|
||||
m_pDrawWidget->GetFramework().OnLocationError(errorCode);
|
||||
}
|
||||
|
||||
void MainWindow::OnLocationUpdated(location::GpsInfo const & info)
|
||||
{
|
||||
m_pDrawWidget->GetFramework().OnLocationUpdate(info);
|
||||
}
|
||||
|
||||
void MainWindow::OnMyPosition()
|
||||
{
|
||||
if (m_pMyPositionAction->isEnabled())
|
||||
m_pDrawWidget->GetFramework().SwitchMyPositionNextMode();
|
||||
}
|
||||
|
||||
void MainWindow::OnCreateFeatureClicked()
|
||||
{
|
||||
if (m_pCreateFeatureAction->isChecked())
|
||||
{
|
||||
m_pDrawWidget->ChoosePositionModeEnable();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pDrawWidget->ChoosePositionModeDisable();
|
||||
m_pDrawWidget->CreateFeature();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnSwitchSelectionMode(SelectionMode mode)
|
||||
{
|
||||
if (m_selection->isChecked(mode))
|
||||
{
|
||||
m_selection->setCurrent(mode);
|
||||
m_pDrawWidget->SetSelectionMode(mode);
|
||||
}
|
||||
else
|
||||
OnClearSelection();
|
||||
}
|
||||
|
||||
void MainWindow::OnSwitchMwmsBordersSelectionMode()
|
||||
{
|
||||
MwmsBordersSelection dlg(this);
|
||||
auto const response = dlg.ShowModal();
|
||||
if (response == SelectionMode::Cancelled)
|
||||
{
|
||||
m_pDrawWidget->DropSelectionIfMWMBordersMode();
|
||||
return;
|
||||
}
|
||||
|
||||
m_selection->setCurrent(SelectionMode::MWMBorders);
|
||||
m_pDrawWidget->SetSelectionMode(response);
|
||||
}
|
||||
|
||||
void MainWindow::OnClearSelection()
|
||||
{
|
||||
m_pDrawWidget->GetFramework().GetDrapeApi().Clear();
|
||||
m_pDrawWidget->SetSelectionMode({});
|
||||
|
||||
m_selection->setMainIcon({});
|
||||
}
|
||||
|
||||
void MainWindow::OnSearchButtonClicked()
|
||||
{
|
||||
if (m_pSearchAction->isChecked())
|
||||
m_Docks[0]->show();
|
||||
else
|
||||
m_Docks[0]->hide();
|
||||
}
|
||||
|
||||
void MainWindow::OnLoginMenuItem()
|
||||
{
|
||||
OsmAuthDialog dlg(this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void MainWindow::OnUploadEditsMenuItem()
|
||||
{
|
||||
std::string token;
|
||||
if (!settings::Get(kOauthTokenSetting, token) || token.empty())
|
||||
{
|
||||
OnLoginMenuItem();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto & editor = osm::Editor::Instance();
|
||||
if (editor.HaveMapEditsOrNotesToUpload())
|
||||
editor.UploadChanges(token, {{"created_by", "CoMaps " OMIM_OS_NAME}});
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnBeforeEngineCreation()
|
||||
{
|
||||
m_pDrawWidget->GetFramework().SetMyPositionModeListener([this](location::EMyPositionMode mode, bool /*routingActive*/)
|
||||
{ LocationStateModeChanged(mode); });
|
||||
}
|
||||
|
||||
void MainWindow::OnPreferences()
|
||||
{
|
||||
Framework & framework = m_pDrawWidget->GetFramework();
|
||||
PreferencesDialog dlg(this, framework);
|
||||
dlg.exec();
|
||||
|
||||
framework.EnterForeground();
|
||||
}
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
void MainWindow::OnBuildStyle()
|
||||
{
|
||||
try
|
||||
{
|
||||
build_style::BuildAndApply(m_mapcssFilePath);
|
||||
m_pDrawWidget->RefreshDrawingRules();
|
||||
|
||||
bool enabled;
|
||||
if (!settings::Get(kEnabledAutoRegenGeomIndex, enabled))
|
||||
enabled = false;
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
build_style::NeedRecalculate = true;
|
||||
QMainWindow::close();
|
||||
}
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnRecalculateGeomIndex()
|
||||
{
|
||||
try
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Warning");
|
||||
msgBox.setText("Geometry index will be regenerated. It can take a while.\nApplication may be closed and reopened!");
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
if (msgBox.exec() == QMessageBox::Yes)
|
||||
{
|
||||
build_style::NeedRecalculate = true;
|
||||
QMainWindow::close();
|
||||
}
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnDebugStyle()
|
||||
{
|
||||
bool const checked = m_pDrawDebugRectAction->isChecked();
|
||||
m_pDrawWidget->GetFramework().EnableDebugRectRendering(checked);
|
||||
m_pDrawWidget->RefreshDrawingRules();
|
||||
}
|
||||
|
||||
void MainWindow::OnGetStatistics()
|
||||
{
|
||||
try
|
||||
{
|
||||
QString text = build_style::GetCurrentStyleStatistics();
|
||||
InfoDialog dlg(QString("Style statistics"), text, NULL);
|
||||
dlg.exec();
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnRunTests()
|
||||
{
|
||||
try
|
||||
{
|
||||
std::pair<bool, QString> res = build_style::RunCurrentStyleTests();
|
||||
InfoDialog dlg(QString("Style tests: ") + (res.first ? "OK" : "FAILED"), res.second, NULL);
|
||||
dlg.exec();
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnBuildPhonePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
char const * const kStylesFolder = "styles";
|
||||
char const * const kClearStyleFolder = "clear";
|
||||
|
||||
QString const targetDir = QFileDialog::getExistingDirectory(nullptr, "Choose output directory");
|
||||
if (targetDir.isEmpty())
|
||||
return;
|
||||
auto outDir = QDir(JoinPathQt({targetDir, kStylesFolder}));
|
||||
if (outDir.exists())
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Warning");
|
||||
msgBox.setText(QString("Folder ") + outDir.absolutePath() + " will be deleted?");
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
auto result = msgBox.exec();
|
||||
if (result == QMessageBox::No)
|
||||
throw std::runtime_error(std::string("Target directory exists: ") + outDir.absolutePath().toStdString());
|
||||
}
|
||||
|
||||
QString const stylesDir = JoinPathQt({m_mapcssFilePath, "..", "..", ".."});
|
||||
if (!QDir(JoinPathQt({stylesDir, kClearStyleFolder})).exists())
|
||||
throw std::runtime_error(std::string("Styles folder is not found in ") + stylesDir.toStdString());
|
||||
|
||||
QString text = build_style::RunBuildingPhonePack(stylesDir, targetDir);
|
||||
text.append("\nMobile device style package is in the directory: ");
|
||||
text.append(JoinPathQt({targetDir, kStylesFolder}));
|
||||
text.append(". Copy it to your mobile device.\n");
|
||||
InfoDialog dlg(QString("Building phone pack"), text, nullptr);
|
||||
dlg.exec();
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(e.what());
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
#endif // BUILD_DESIGNER
|
||||
|
||||
#ifndef NO_DOWNLOADER
|
||||
void MainWindow::ShowUpdateDialog()
|
||||
{
|
||||
UpdateDialog dlg(this, m_pDrawWidget->GetFramework());
|
||||
dlg.ShowModal();
|
||||
m_pDrawWidget->update();
|
||||
}
|
||||
|
||||
#endif // NO_DOWNLOADER
|
||||
|
||||
void MainWindow::CreateSearchBarAndPanel()
|
||||
{
|
||||
CreatePanelImpl(0, Qt::RightDockWidgetArea, tr("Search"), QKeySequence(), 0);
|
||||
|
||||
SearchPanel * panel = new SearchPanel(m_pDrawWidget, m_Docks[0]);
|
||||
m_Docks[0]->setWidget(panel);
|
||||
}
|
||||
|
||||
void MainWindow::CreatePanelImpl(size_t i, Qt::DockWidgetArea area, QString const & name, QKeySequence const & hotkey,
|
||||
char const * slot)
|
||||
{
|
||||
ASSERT_LESS(i, m_Docks.size(), ());
|
||||
m_Docks[i] = new QDockWidget(name, this);
|
||||
|
||||
addDockWidget(area, m_Docks[i]);
|
||||
|
||||
// hide by default
|
||||
m_Docks[i]->hide();
|
||||
|
||||
// register a hotkey to show panel
|
||||
if (slot && !hotkey.isEmpty())
|
||||
{
|
||||
QAction * pAct = new QAction(this);
|
||||
pAct->setShortcut(hotkey);
|
||||
connect(pAct, SIGNAL(triggered()), this, slot);
|
||||
addAction(pAct);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent * e)
|
||||
{
|
||||
m_pDrawWidget->PrepareShutdown();
|
||||
e->accept();
|
||||
}
|
||||
|
||||
void MainWindow::OnDownloadClicked()
|
||||
{
|
||||
GetFramework().GetStorage().DownloadNode(m_lastCountry);
|
||||
}
|
||||
|
||||
void MainWindow::OnRetryDownloadClicked()
|
||||
{
|
||||
GetFramework().GetStorage().RetryDownloadNode(m_lastCountry);
|
||||
}
|
||||
|
||||
void MainWindow::SetLayerEnabled(LayerType type, bool enable)
|
||||
{
|
||||
auto & frm = m_pDrawWidget->GetFramework();
|
||||
switch (type)
|
||||
{
|
||||
// @todo Uncomment when we will integrate a traffic provider.
|
||||
// case LayerType::TRAFFIC:
|
||||
// frm.GetTrafficManager().SetEnabled(enable);
|
||||
// frm.SaveTrafficEnabled(enable);
|
||||
// break;
|
||||
case LayerType::TRANSIT:
|
||||
frm.GetTransitManager().EnableTransitSchemeMode(enable);
|
||||
frm.SaveTransitSchemeEnabled(enable);
|
||||
break;
|
||||
case LayerType::ISOLINES:
|
||||
frm.GetIsolinesManager().SetEnabled(enable);
|
||||
frm.SaveIsolinesEnabled(enable);
|
||||
break;
|
||||
case LayerType::OUTDOORS:
|
||||
frm.SaveOutdoorsEnabled(enable);
|
||||
if (enable)
|
||||
m_pDrawWidget->SetMapStyleToOutdoors();
|
||||
else
|
||||
m_pDrawWidget->SetMapStyleToDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnLayerEnabled(LayerType layer)
|
||||
{
|
||||
SetLayerEnabled(layer, m_layers->isChecked(layer));
|
||||
}
|
||||
|
||||
void MainWindow::OnRulerEnabled()
|
||||
{
|
||||
m_pDrawWidget->SetRuler(m_rulerAction->isChecked());
|
||||
}
|
||||
|
||||
void MainWindow::OnRoutePointSelected(RouteMarkType type)
|
||||
{
|
||||
m_routing->setCurrent(type);
|
||||
m_pDrawWidget->SetRoutePointAddMode(type);
|
||||
}
|
||||
|
||||
void MainWindow::OnFollowRoute()
|
||||
{
|
||||
m_pDrawWidget->FollowRoute();
|
||||
}
|
||||
|
||||
void MainWindow::OnClearRoute()
|
||||
{
|
||||
m_pDrawWidget->ClearRoute();
|
||||
}
|
||||
|
||||
void MainWindow::OnRoutingSettings()
|
||||
{
|
||||
RoutingSettings dlg(this, m_pDrawWidget->GetFramework());
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
void MainWindow::OnBookmarksAction()
|
||||
{
|
||||
BookmarkDialog dlg(this, m_pDrawWidget->GetFramework());
|
||||
dlg.ShowModal();
|
||||
m_pDrawWidget->update();
|
||||
}
|
||||
|
||||
} // namespace qt
|
||||
152
qt/mainwindow.hpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#pragma once
|
||||
#include "qt/selection.hpp"
|
||||
|
||||
#include "map/routing_mark.hpp"
|
||||
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
#include "platform/location_service/location_service.hpp"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Framework;
|
||||
class QDockWidget;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
namespace search
|
||||
{
|
||||
class Result;
|
||||
}
|
||||
|
||||
namespace qt
|
||||
{
|
||||
class DrawWidget;
|
||||
class PopupMenuHolder;
|
||||
struct ScreenshotParams;
|
||||
|
||||
class MainWindow
|
||||
: public QMainWindow
|
||||
, location::LocationObserver
|
||||
{
|
||||
DrawWidget * m_pDrawWidget = nullptr;
|
||||
// TODO(mgsergio): Make indexing more informative.
|
||||
std::array<QDockWidget *, 1> m_Docks;
|
||||
|
||||
QPushButton * m_downloadButton = nullptr;
|
||||
QPushButton * m_retryButton = nullptr;
|
||||
QLabel * m_downloadingStatusLabel = nullptr;
|
||||
|
||||
storage::CountryId m_lastCountry;
|
||||
|
||||
std::unique_ptr<location::LocationService> const m_locationService;
|
||||
bool const m_screenshotMode;
|
||||
|
||||
QAction * m_pMyPositionAction = nullptr;
|
||||
QAction * m_pCreateFeatureAction = nullptr;
|
||||
QAction * m_pSearchAction = nullptr;
|
||||
QAction * m_rulerAction = nullptr;
|
||||
|
||||
enum LayerType : uint8_t
|
||||
{
|
||||
/// @todo Uncomment when we will integrate a traffic provider.
|
||||
// TRAFFIC = 0,
|
||||
TRANSIT = 0, // Metro scheme
|
||||
ISOLINES,
|
||||
OUTDOORS,
|
||||
};
|
||||
PopupMenuHolder * m_layers = nullptr;
|
||||
PopupMenuHolder * m_routing = nullptr;
|
||||
PopupMenuHolder * m_selection = nullptr;
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
QString const m_mapcssFilePath = nullptr;
|
||||
QAction * m_pBuildStyleAction = nullptr;
|
||||
QAction * m_pRecalculateGeomIndex = nullptr;
|
||||
QAction * m_pDrawDebugRectAction = nullptr;
|
||||
QAction * m_pGetStatisticsAction = nullptr;
|
||||
QAction * m_pRunTestsAction = nullptr;
|
||||
QAction * m_pBuildPhonePackAction = nullptr;
|
||||
#endif // BUILD_DESIGNER
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow(Framework & framework, std::unique_ptr<ScreenshotParams> && screenshotParams, QRect const & screenGeometry
|
||||
#ifdef BUILD_DESIGNER
|
||||
,
|
||||
QString const & mapcssFilePath = QString()
|
||||
#endif
|
||||
);
|
||||
|
||||
protected:
|
||||
Framework & GetFramework() const;
|
||||
|
||||
void OnLocationError(location::TLocationError errorCode) override;
|
||||
void OnLocationUpdated(location::GpsInfo const & info) override;
|
||||
void LocationStateModeChanged(location::EMyPositionMode mode);
|
||||
|
||||
void CreatePanelImpl(size_t i, Qt::DockWidgetArea area, QString const & name, QKeySequence const & hotkey,
|
||||
char const * slot);
|
||||
void CreateNavigationBar();
|
||||
void CreateSearchBarAndPanel();
|
||||
void CreateCountryStatusControls();
|
||||
|
||||
void SetLayerEnabled(LayerType type, bool enable);
|
||||
|
||||
#if defined(OMIM_OS_WINDOWS)
|
||||
/// to handle menu messages
|
||||
bool nativeEvent(QByteArray const & eventType, void * message, qintptr * result) override;
|
||||
#endif
|
||||
|
||||
void closeEvent(QCloseEvent * e) override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
#ifndef NO_DOWNLOADER
|
||||
void ShowUpdateDialog();
|
||||
#endif // NO_DOWNLOADER
|
||||
|
||||
void OnPreferences();
|
||||
void OnAbout();
|
||||
void OnMyPosition();
|
||||
void OnCreateFeatureClicked();
|
||||
void OnSearchButtonClicked();
|
||||
void OnLoginMenuItem();
|
||||
void OnUploadEditsMenuItem();
|
||||
|
||||
void OnBeforeEngineCreation();
|
||||
|
||||
void OnDownloadClicked();
|
||||
void OnRetryDownloadClicked();
|
||||
|
||||
void OnSwitchSelectionMode(SelectionMode mode);
|
||||
void OnSwitchMwmsBordersSelectionMode();
|
||||
void OnClearSelection();
|
||||
|
||||
void OnLayerEnabled(LayerType layer);
|
||||
|
||||
void OnRulerEnabled();
|
||||
|
||||
void OnRoutePointSelected(RouteMarkType type);
|
||||
void OnFollowRoute();
|
||||
void OnClearRoute();
|
||||
void OnRoutingSettings();
|
||||
|
||||
void OnBookmarksAction();
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
void OnBuildStyle();
|
||||
void OnRecalculateGeomIndex();
|
||||
void OnDebugStyle();
|
||||
void OnGetStatistics();
|
||||
void OnRunTests();
|
||||
void OnBuildPhonePackage();
|
||||
#endif // BUILD_DESIGNER
|
||||
};
|
||||
} // namespace qt
|
||||
115
qt/mwms_borders_selection.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include "qt/mwms_borders_selection.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QRadioButton>
|
||||
#include <QtWidgets/QSplitter>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
MwmsBordersSelection::MwmsBordersSelection(QWidget * parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle("Mwms borders selection settings");
|
||||
|
||||
auto * grid = new QGridLayout;
|
||||
grid->addWidget(CreateSourceChoosingGroup(), 0, 0);
|
||||
grid->addWidget(CreateViewTypeGroup(), 1, 0);
|
||||
grid->addWidget(CreateButtonBoxGroup(), 2, 0);
|
||||
|
||||
setLayout(grid);
|
||||
}
|
||||
|
||||
QGroupBox * MwmsBordersSelection::CreateButtonBoxGroup()
|
||||
{
|
||||
auto * groupBox = new QGroupBox();
|
||||
|
||||
auto * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||
|
||||
auto * vbox = new QVBoxLayout;
|
||||
|
||||
vbox->addWidget(buttonBox);
|
||||
groupBox->setLayout(vbox);
|
||||
groupBox->setFlat(true);
|
||||
|
||||
QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
return groupBox;
|
||||
}
|
||||
|
||||
SelectionMode MwmsBordersSelection::ShowModal()
|
||||
{
|
||||
if (exec() != QDialog::Accepted)
|
||||
return SelectionMode::Cancelled;
|
||||
|
||||
if (m_radioBordersFromData->isChecked())
|
||||
{
|
||||
if (m_radioJustBorders->isChecked())
|
||||
return SelectionMode::MwmsBordersByPolyFiles;
|
||||
|
||||
if (m_radioWithPoints->isChecked())
|
||||
return SelectionMode::MwmsBordersWithVerticesByPolyFiles;
|
||||
|
||||
if (m_radioBoundingBox->isChecked())
|
||||
return SelectionMode::BoundingBoxByPolyFiles;
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (m_radioBordersFromPackedPolygon->isChecked())
|
||||
{
|
||||
if (m_radioJustBorders->isChecked())
|
||||
return SelectionMode::MwmsBordersByPackedPolygon;
|
||||
|
||||
if (m_radioWithPoints->isChecked())
|
||||
return SelectionMode::MwmsBordersWithVerticesByPackedPolygon;
|
||||
|
||||
if (m_radioBoundingBox->isChecked())
|
||||
return SelectionMode::BoundingBoxByPackedPolygon;
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
QGroupBox * MwmsBordersSelection::CreateSourceChoosingGroup()
|
||||
{
|
||||
auto * groupBox = new QGroupBox();
|
||||
|
||||
m_radioBordersFromPackedPolygon = new QRadioButton(tr("Get borders from packed_polygon.bin"));
|
||||
m_radioBordersFromData = new QRadioButton(tr("Get borders from *.poly files"));
|
||||
|
||||
m_radioBordersFromPackedPolygon->setChecked(true);
|
||||
|
||||
auto * vbox = new QVBoxLayout;
|
||||
|
||||
vbox->addWidget(m_radioBordersFromPackedPolygon);
|
||||
vbox->addWidget(m_radioBordersFromData);
|
||||
groupBox->setLayout(vbox);
|
||||
|
||||
return groupBox;
|
||||
}
|
||||
|
||||
QGroupBox * MwmsBordersSelection::CreateViewTypeGroup()
|
||||
{
|
||||
auto * groupBox = new QGroupBox();
|
||||
|
||||
m_radioWithPoints = new QRadioButton(tr("Show borders with vertices"));
|
||||
m_radioJustBorders = new QRadioButton(tr("Show just borders"));
|
||||
m_radioBoundingBox = new QRadioButton(tr("Show bounding box"));
|
||||
|
||||
m_radioWithPoints->setChecked(true);
|
||||
|
||||
auto * vbox = new QVBoxLayout;
|
||||
|
||||
vbox->addWidget(m_radioWithPoints);
|
||||
vbox->addWidget(m_radioJustBorders);
|
||||
vbox->addWidget(m_radioBoundingBox);
|
||||
groupBox->setLayout(vbox);
|
||||
|
||||
return groupBox;
|
||||
}
|
||||
} // namespace qt
|
||||
36
qt/mwms_borders_selection.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "qt/selection.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
#include <QtWidgets/QGroupBox>
|
||||
#include <QtWidgets/QRadioButton>
|
||||
|
||||
class Framework;
|
||||
|
||||
namespace qt
|
||||
{
|
||||
class MwmsBordersSelection : public QDialog
|
||||
{
|
||||
public:
|
||||
MwmsBordersSelection(QWidget * parent);
|
||||
|
||||
SelectionMode ShowModal();
|
||||
|
||||
private:
|
||||
QGroupBox * CreateSourceChoosingGroup();
|
||||
QGroupBox * CreateViewTypeGroup();
|
||||
QGroupBox * CreateButtonBoxGroup();
|
||||
|
||||
QRadioButton * m_radioBordersFromPackedPolygon;
|
||||
QRadioButton * m_radioBordersFromData;
|
||||
|
||||
QRadioButton * m_radioWithPoints;
|
||||
QRadioButton * m_radioJustBorders;
|
||||
QRadioButton * m_radioBoundingBox;
|
||||
};
|
||||
} // namespace qt
|
||||
131
qt/osm_auth_dialog.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
#include "qt/osm_auth_dialog.hpp"
|
||||
|
||||
#include "editor/osm_auth.hpp"
|
||||
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
using namespace osm;
|
||||
|
||||
namespace qt
|
||||
{
|
||||
char const * kTokenKeySetting = "OsmTokenKey";
|
||||
char const * kTokenSecretSetting = "OsmTokenSecret";
|
||||
char const * kLoginDialogTitle = "OpenStreetMap Login";
|
||||
char const * kOauthTokenSetting = "OsmOauthToken";
|
||||
|
||||
OsmAuthDialog::OsmAuthDialog(QWidget * parent)
|
||||
{
|
||||
std::string token;
|
||||
bool const isLoginDialog = !settings::Get(kOauthTokenSetting, token) || token.empty();
|
||||
|
||||
QVBoxLayout * vLayout = new QVBoxLayout(parent);
|
||||
|
||||
QHBoxLayout * loginRow = new QHBoxLayout();
|
||||
loginRow->addWidget(new QLabel(tr("Username/email:")));
|
||||
QLineEdit * loginLineEdit = new QLineEdit();
|
||||
loginLineEdit->setObjectName("login");
|
||||
if (!isLoginDialog)
|
||||
loginLineEdit->setEnabled(false);
|
||||
loginRow->addWidget(loginLineEdit);
|
||||
vLayout->addLayout(loginRow);
|
||||
|
||||
QHBoxLayout * passwordRow = new QHBoxLayout();
|
||||
passwordRow->addWidget(new QLabel(tr("Password:")));
|
||||
QLineEdit * passwordLineEdit = new QLineEdit();
|
||||
passwordLineEdit->setEchoMode(QLineEdit::Password);
|
||||
passwordLineEdit->setObjectName("password");
|
||||
if (!isLoginDialog)
|
||||
passwordLineEdit->setEnabled(false);
|
||||
passwordRow->addWidget(passwordLineEdit);
|
||||
vLayout->addLayout(passwordRow);
|
||||
|
||||
QPushButton * actionButton = new QPushButton(isLoginDialog ? tr("Login") : tr("Logout"));
|
||||
actionButton->setObjectName("button");
|
||||
connect(actionButton, &QAbstractButton::clicked, this, &OsmAuthDialog::OnAction);
|
||||
|
||||
QPushButton * closeButton = new QPushButton(tr("Close"));
|
||||
connect(closeButton, &QAbstractButton::clicked, this, &QWidget::close);
|
||||
|
||||
QHBoxLayout * buttonsLayout = new QHBoxLayout();
|
||||
buttonsLayout->addWidget(closeButton);
|
||||
buttonsLayout->addWidget(actionButton);
|
||||
vLayout->addLayout(buttonsLayout);
|
||||
|
||||
setLayout(vLayout);
|
||||
setWindowTitle(tr(kLoginDialogTitle));
|
||||
}
|
||||
|
||||
void SwitchToLogin(QDialog * dlg)
|
||||
{
|
||||
dlg->findChild<QLineEdit *>("login")->setEnabled(true);
|
||||
dlg->findChild<QLineEdit *>("password")->setEnabled(true);
|
||||
dlg->findChild<QPushButton *>("button")->setText(dlg->tr("Login"));
|
||||
}
|
||||
|
||||
void SwitchToLogout(QDialog * dlg)
|
||||
{
|
||||
dlg->findChild<QLineEdit *>("login")->setEnabled(false);
|
||||
dlg->findChild<QLineEdit *>("password")->setEnabled(false);
|
||||
dlg->findChild<QPushButton *>("button")->setText(dlg->tr("Logout"));
|
||||
dlg->setWindowTitle(dlg->tr(kLoginDialogTitle));
|
||||
}
|
||||
|
||||
void OsmAuthDialog::OnAction()
|
||||
{
|
||||
bool const isLoginDialog = findChild<QPushButton *>("button")->text() == tr("Login");
|
||||
if (isLoginDialog)
|
||||
{
|
||||
std::string const login = findChild<QLineEdit *>("login")->text().toStdString();
|
||||
std::string const password = findChild<QLineEdit *>("password")->text().toStdString();
|
||||
|
||||
if (login.empty())
|
||||
{
|
||||
setWindowTitle("Please enter Login");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.empty())
|
||||
{
|
||||
setWindowTitle("Please enter Password");
|
||||
return;
|
||||
}
|
||||
|
||||
OsmOAuth auth = osm::OsmOAuth::ServerAuth();
|
||||
try
|
||||
{
|
||||
if (auth.AuthorizePassword(login, password))
|
||||
{
|
||||
auto const token = auth.GetAuthToken();
|
||||
settings::Set(kOauthTokenSetting, token);
|
||||
|
||||
SwitchToLogout(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
setWindowTitle("Auth failed: invalid login or password");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
setWindowTitle((std::string("Auth failed: ") + ex.what()).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
settings::Set(kOauthTokenSetting, std::string());
|
||||
|
||||
SwitchToLogin(this);
|
||||
}
|
||||
}
|
||||
} // namespace qt
|
||||
19
qt/osm_auth_dialog.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
|
||||
class OsmAuthDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OsmAuthDialog(QWidget * parent);
|
||||
|
||||
private slots:
|
||||
void OnAction();
|
||||
};
|
||||
|
||||
} // namespace qt
|
||||
42
qt/place_page_dialog_common.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#include "qt/place_page_dialog_common.hpp"
|
||||
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
namespace place_page_dialog
|
||||
{
|
||||
void addCommonButtons(QDialog * this_, QDialogButtonBox * dbb, bool shouldShowEditPlace)
|
||||
{
|
||||
dbb->setCenterButtons(true);
|
||||
|
||||
QPushButton * fromButton = new QPushButton("Route From");
|
||||
fromButton->setIcon(QIcon(":/navig64/point-start.png"));
|
||||
fromButton->setAutoDefault(false);
|
||||
this_->connect(fromButton, &QAbstractButton::clicked, this_, [this_] { this_->done(RouteFrom); });
|
||||
dbb->addButton(fromButton, QDialogButtonBox::ActionRole);
|
||||
|
||||
QPushButton * addStopButton = new QPushButton("Add Stop");
|
||||
addStopButton->setIcon(QIcon(":/navig64/point-intermediate.png"));
|
||||
addStopButton->setAutoDefault(false);
|
||||
this_->connect(addStopButton, &QAbstractButton::clicked, this_, [this_] { this_->done(AddStop); });
|
||||
dbb->addButton(addStopButton, QDialogButtonBox::ActionRole);
|
||||
|
||||
QPushButton * routeToButton = new QPushButton("Route To");
|
||||
routeToButton->setIcon(QIcon(":/navig64/point-finish.png"));
|
||||
routeToButton->setAutoDefault(false);
|
||||
this_->connect(routeToButton, &QAbstractButton::clicked, this_, [this_] { this_->done(RouteTo); });
|
||||
dbb->addButton(routeToButton, QDialogButtonBox::ActionRole);
|
||||
|
||||
QPushButton * closeButton = new QPushButton("Close");
|
||||
closeButton->setDefault(true);
|
||||
this_->connect(closeButton, &QAbstractButton::clicked, this_, [this_] { this_->done(place_page_dialog::Close); });
|
||||
dbb->addButton(closeButton, QDialogButtonBox::RejectRole);
|
||||
|
||||
if (shouldShowEditPlace)
|
||||
{
|
||||
QPushButton * editButton = new QPushButton("Edit Place");
|
||||
this_->connect(editButton, &QAbstractButton::clicked, this_,
|
||||
[this_] { this_->done(place_page_dialog::EditPlace); });
|
||||
dbb->addButton(editButton, QDialogButtonBox::AcceptRole);
|
||||
}
|
||||
}
|
||||
} // namespace place_page_dialog
|
||||
18
qt/place_page_dialog_common.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
|
||||
namespace place_page_dialog
|
||||
{
|
||||
enum PressedButton : int
|
||||
{
|
||||
Close = QDialog::Rejected,
|
||||
RouteFrom,
|
||||
AddStop,
|
||||
RouteTo,
|
||||
EditPlace
|
||||
};
|
||||
|
||||
void addCommonButtons(QDialog * this_, QDialogButtonBox * dbb, bool shouldShowEditPlace);
|
||||
} // namespace place_page_dialog
|
||||
132
qt/place_page_dialog_developer.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include "qt/place_page_dialog_developer.hpp"
|
||||
#include "qt/place_page_dialog_common.hpp"
|
||||
|
||||
#include "qt/qt_common/text_dialog.hpp"
|
||||
|
||||
#include "map/place_page_info.hpp"
|
||||
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QGridLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
#include <string>
|
||||
|
||||
PlacePageDialogDeveloper::PlacePageDialogDeveloper(QWidget * parent, place_page::Info const & info,
|
||||
search::ReverseGeocoder::Address const & address)
|
||||
: QDialog(parent)
|
||||
{
|
||||
QVBoxLayout * layout = new QVBoxLayout();
|
||||
QGridLayout * grid = new QGridLayout();
|
||||
int row = 0;
|
||||
|
||||
auto const addEntry = [grid, &row](std::string const & key, std::string const & value, bool isLink = false)
|
||||
{
|
||||
grid->addWidget(new QLabel(QString::fromStdString(key)), row, 0);
|
||||
QLabel * label = new QLabel(QString::fromStdString(value));
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
if (isLink)
|
||||
{
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
label->setText(QString::fromStdString("<a href=\"" + value + "\">" + value + "</a>"));
|
||||
}
|
||||
grid->addWidget(label, row++, 1);
|
||||
return label;
|
||||
};
|
||||
|
||||
{
|
||||
ms::LatLon const ll = info.GetLatLon();
|
||||
addEntry("lat, lon", strings::to_string_dac(ll.m_lat, 7) + ", " + strings::to_string_dac(ll.m_lon, 7));
|
||||
}
|
||||
|
||||
addEntry("CountryId", info.GetCountryId());
|
||||
|
||||
auto const & title = info.GetTitle();
|
||||
if (!title.empty())
|
||||
addEntry("Title", title);
|
||||
|
||||
if (auto const & subTitle = info.GetSubtitle(); !subTitle.empty())
|
||||
addEntry("Subtitle", subTitle);
|
||||
|
||||
addEntry("Address", address.FormatAddress());
|
||||
|
||||
if (info.IsBookmark())
|
||||
{
|
||||
grid->addWidget(new QLabel("Bookmark"), row, 0);
|
||||
grid->addWidget(new QLabel("Yes"), row++, 1);
|
||||
}
|
||||
|
||||
if (info.IsMyPosition())
|
||||
{
|
||||
grid->addWidget(new QLabel("MyPosition"), row, 0);
|
||||
grid->addWidget(new QLabel("Yes"), row++, 1);
|
||||
}
|
||||
|
||||
if (info.HasApiUrl())
|
||||
{
|
||||
grid->addWidget(new QLabel("Api URL"), row, 0);
|
||||
grid->addWidget(new QLabel(QString::fromStdString(info.GetApiUrl())), row++, 1);
|
||||
}
|
||||
|
||||
if (info.IsFeature())
|
||||
{
|
||||
addEntry("Feature ID", DebugPrint(info.GetID()));
|
||||
addEntry("Raw Types", DebugPrint(info.GetTypes()));
|
||||
}
|
||||
|
||||
auto const layer = info.GetLayer();
|
||||
if (layer != feature::LAYER_EMPTY)
|
||||
addEntry("Layer", std::to_string(layer));
|
||||
|
||||
using PropID = osm::MapObject::MetadataID;
|
||||
|
||||
if (auto cuisines = info.FormatCuisines(); !cuisines.empty())
|
||||
addEntry(DebugPrint(PropID::FMD_CUISINE), cuisines);
|
||||
|
||||
layout->addLayout(grid);
|
||||
|
||||
QDialogButtonBox * dbb = new QDialogButtonBox();
|
||||
place_page_dialog::addCommonButtons(this, dbb, info.ShouldShowEditPlace());
|
||||
|
||||
if (auto const & descr = info.GetWikiDescription(); !descr.empty())
|
||||
{
|
||||
QPushButton * wikiButton = new QPushButton("Wiki Description");
|
||||
connect(wikiButton, &QAbstractButton::clicked, this, [this, descr, title]()
|
||||
{
|
||||
auto textDialog = TextDialog(this, QString::fromStdString(descr), QString::fromStdString("Wikipedia: " + title));
|
||||
textDialog.exec();
|
||||
});
|
||||
dbb->addButton(wikiButton, QDialogButtonBox::ActionRole);
|
||||
}
|
||||
|
||||
info.ForEachMetadataReadable([&addEntry](PropID id, std::string const & value)
|
||||
{
|
||||
bool isLink = false;
|
||||
switch (id)
|
||||
{
|
||||
case PropID::FMD_EMAIL:
|
||||
case PropID::FMD_WEBSITE:
|
||||
case PropID::FMD_CONTACT_FACEBOOK:
|
||||
case PropID::FMD_CONTACT_INSTAGRAM:
|
||||
case PropID::FMD_CONTACT_TWITTER:
|
||||
case PropID::FMD_CONTACT_VK:
|
||||
case PropID::FMD_CONTACT_LINE:
|
||||
case PropID::FMD_CONTACT_FEDIVERSE:
|
||||
case PropID::FMD_CONTACT_BLUESKY:
|
||||
case PropID::FMD_WIKIPEDIA:
|
||||
case PropID::FMD_WIKIMEDIA_COMMONS:
|
||||
case PropID::FMD_PANORAMAX: isLink = true; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
addEntry(DebugPrint(id), value, isLink);
|
||||
});
|
||||
|
||||
layout->addWidget(dbb);
|
||||
setLayout(layout);
|
||||
|
||||
auto const ppTitle = std::string("Place Page") + (info.IsBookmark() ? " (bookmarked)" : "");
|
||||
setWindowTitle(ppTitle.c_str());
|
||||
}
|
||||
19
qt/place_page_dialog_developer.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "search/reverse_geocoder.hpp"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
namespace place_page
|
||||
{
|
||||
class Info;
|
||||
}
|
||||
|
||||
class PlacePageDialogDeveloper : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlacePageDialogDeveloper(QWidget * parent, place_page::Info const & info,
|
||||
search::ReverseGeocoder::Address const & address);
|
||||
};
|
||||
312
qt/place_page_dialog_user.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
#include "qt/place_page_dialog_user.hpp"
|
||||
#include "qt/place_page_dialog_common.hpp"
|
||||
|
||||
#include "qt/qt_common/text_dialog.hpp"
|
||||
|
||||
#include "indexer/validate_and_format_contacts.hpp"
|
||||
#include "map/place_page_info.hpp"
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QGridLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
static int constexpr kMaxLengthOfPlacePageDescription = 500;
|
||||
static int constexpr kMinWidthOfShortDescription = 390;
|
||||
|
||||
std::string getShortDescription(std::string const & description)
|
||||
{
|
||||
std::string_view view(description);
|
||||
|
||||
auto const paragraphStart = view.find("<p>");
|
||||
auto const paragraphEnd = view.find("</p>");
|
||||
|
||||
if (paragraphStart == 0 && paragraphEnd != std::string::npos)
|
||||
view = view.substr(3, paragraphEnd - 3);
|
||||
|
||||
if (view.length() > kMaxLengthOfPlacePageDescription)
|
||||
return std::string(view.substr(0, kMaxLengthOfPlacePageDescription - 3)) + "...";
|
||||
|
||||
return std::string(view);
|
||||
}
|
||||
|
||||
std::string_view stripSchemeFromURI(std::string_view uri)
|
||||
{
|
||||
for (std::string_view prefix : {"https://", "http://"})
|
||||
if (uri.starts_with(prefix))
|
||||
return uri.substr(prefix.size());
|
||||
return uri;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class QHLine : public QFrame
|
||||
{
|
||||
public:
|
||||
QHLine(QWidget * parent = nullptr) : QFrame(parent)
|
||||
{
|
||||
setFrameShape(QFrame::HLine);
|
||||
setFrameShadow(QFrame::Sunken);
|
||||
}
|
||||
};
|
||||
|
||||
PlacePageDialogUser::PlacePageDialogUser(QWidget * parent, place_page::Info const & info,
|
||||
search::ReverseGeocoder::Address const & address)
|
||||
: QDialog(parent)
|
||||
{
|
||||
auto const & title = info.GetTitle();
|
||||
|
||||
QVBoxLayout * layout = new QVBoxLayout();
|
||||
{
|
||||
QVBoxLayout * header = new QVBoxLayout();
|
||||
|
||||
if (!title.empty())
|
||||
{
|
||||
QLabel * titleLabel = new QLabel(QString::fromStdString("<h1>" + title + "</h1>"));
|
||||
titleLabel->setWordWrap(true);
|
||||
header->addWidget(titleLabel);
|
||||
}
|
||||
|
||||
if (auto const subTitle = info.GetSubtitle(); !subTitle.empty())
|
||||
{
|
||||
QLabel * subtitleLabel = new QLabel(QString::fromStdString(subTitle));
|
||||
subtitleLabel->setWordWrap(true);
|
||||
header->addWidget(subtitleLabel);
|
||||
}
|
||||
|
||||
if (auto const addressFormatted = address.FormatAddress(); !addressFormatted.empty())
|
||||
{
|
||||
QLabel * addressLabel = new QLabel(QString::fromStdString(addressFormatted));
|
||||
addressLabel->setWordWrap(true);
|
||||
header->addWidget(addressLabel);
|
||||
}
|
||||
|
||||
layout->addLayout(header);
|
||||
}
|
||||
|
||||
{
|
||||
QHLine * line = new QHLine();
|
||||
layout->addWidget(line);
|
||||
}
|
||||
|
||||
{
|
||||
QGridLayout * data = new QGridLayout();
|
||||
|
||||
int row = 0;
|
||||
|
||||
auto const addEntry = [data, &row](std::string const & key, std::string const & value, bool isLink = false)
|
||||
{
|
||||
data->addWidget(new QLabel(QString::fromStdString(key)), row, 0);
|
||||
QLabel * label = new QLabel(QString::fromStdString(value));
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
label->setWordWrap(true);
|
||||
if (isLink)
|
||||
{
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
label->setText(QString::fromStdString("<a href=\"" + value + "\">" + value + "</a>"));
|
||||
}
|
||||
data->addWidget(label, row++, 1);
|
||||
return label;
|
||||
};
|
||||
|
||||
if (info.IsBookmark())
|
||||
addEntry("Bookmark", "Yes");
|
||||
|
||||
// Wikipedia fragment
|
||||
if (auto const & wikipedia = info.GetMetadata(feature::Metadata::EType::FMD_WIKIPEDIA); !wikipedia.empty())
|
||||
{
|
||||
QLabel * name = new QLabel("Wikipedia");
|
||||
name->setOpenExternalLinks(true);
|
||||
name->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
name->setText(QString::fromStdString("<a href=\"" + feature::Metadata::ToWikiURL(std::string(wikipedia)) +
|
||||
"\">Wikipedia</a>"));
|
||||
data->addWidget(name, row++, 0);
|
||||
}
|
||||
|
||||
// Description
|
||||
if (auto const & description = info.GetWikiDescription(); !description.empty())
|
||||
{
|
||||
auto descriptionShort = getShortDescription(description);
|
||||
|
||||
QLabel * value = new QLabel(QString::fromStdString(descriptionShort));
|
||||
value->setWordWrap(true);
|
||||
|
||||
data->addWidget(value, row++, 0, 1, 2);
|
||||
|
||||
QPushButton * wikiButton = new QPushButton("More...", value);
|
||||
wikiButton->setAutoDefault(false);
|
||||
connect(wikiButton, &QAbstractButton::clicked, this, [this, description, title]()
|
||||
{
|
||||
auto textDialog =
|
||||
TextDialog(this, QString::fromStdString(description), QString::fromStdString("Wikipedia: " + title));
|
||||
textDialog.exec();
|
||||
});
|
||||
|
||||
data->addWidget(wikiButton, row++, 0, 1, 2, Qt::AlignLeft);
|
||||
}
|
||||
|
||||
// Opening hours fragment
|
||||
if (auto openingHours = info.GetOpeningHours(); !openingHours.empty())
|
||||
addEntry("Opening hours", std::string(openingHours));
|
||||
|
||||
// Cuisine fragment
|
||||
if (auto cuisines = info.FormatCuisines(); !cuisines.empty())
|
||||
addEntry("Cuisine", cuisines);
|
||||
|
||||
// Capacity fragment
|
||||
if (auto capacity = info.GetCapacity(); !capacity.empty())
|
||||
addEntry("Capacity", capacity);
|
||||
|
||||
// Sockets fragment
|
||||
if (auto sockets = info.GetChargeSockets(); !sockets.empty())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
for (auto s : sockets)
|
||||
{
|
||||
oss << s.type;
|
||||
if (s.power > 0)
|
||||
oss << " (" << s.power << "kW)";
|
||||
if (s.count > 0)
|
||||
oss << " × " << s.count;
|
||||
oss << "\n";
|
||||
}
|
||||
addEntry("Charging sockets", oss.str());
|
||||
}
|
||||
|
||||
// Entrance fragment
|
||||
// TODO
|
||||
|
||||
// Phone fragment
|
||||
if (auto phoneNumber = info.GetMetadata(feature::Metadata::EType::FMD_PHONE_NUMBER); !phoneNumber.empty())
|
||||
{
|
||||
data->addWidget(new QLabel("Phone"), row, 0);
|
||||
|
||||
QLabel * value = new QLabel(QString::fromStdString("<a href='tel:" + std::string(phoneNumber) + "'>" +
|
||||
std::string(phoneNumber) + "</a>"));
|
||||
value->setOpenExternalLinks(true);
|
||||
|
||||
data->addWidget(value, row++, 1);
|
||||
}
|
||||
|
||||
// Operator fragment
|
||||
if (auto operatorName = info.GetMetadata(feature::Metadata::EType::FMD_OPERATOR); !operatorName.empty())
|
||||
addEntry("Operator", std::string(operatorName));
|
||||
|
||||
// Wifi fragment
|
||||
if (info.HasWifi())
|
||||
addEntry("Wi-Fi", "Yes");
|
||||
|
||||
// Links fragment
|
||||
if (auto website = info.GetMetadata(feature::Metadata::EType::FMD_WEBSITE); !website.empty())
|
||||
addEntry("Website", std::string(stripSchemeFromURI(website)), true);
|
||||
|
||||
if (auto email = info.GetMetadata(feature::Metadata::EType::FMD_EMAIL); !email.empty())
|
||||
{
|
||||
data->addWidget(new QLabel("Email"), row, 0);
|
||||
|
||||
QLabel * value = new QLabel(
|
||||
QString::fromStdString("<a href='mailto:" + std::string(email) + "'>" + std::string(email) + "</a>"));
|
||||
value->setOpenExternalLinks(true);
|
||||
|
||||
data->addWidget(value, row++, 1);
|
||||
}
|
||||
|
||||
// Social networks
|
||||
{
|
||||
auto addSocialNetworkWidget = [data, &info, &row](std::string const label, feature::Metadata::EType const eType)
|
||||
{
|
||||
if (auto item = info.GetMetadata(eType); !item.empty())
|
||||
{
|
||||
data->addWidget(new QLabel(QString::fromStdString(label)), row, 0);
|
||||
|
||||
QLabel * value = new QLabel(QString::fromStdString(
|
||||
"<a href='" + osm::socialContactToURL(eType, std::string(item)) + "'>" + std::string(item) + "</a>"));
|
||||
value->setOpenExternalLinks(true);
|
||||
value->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
|
||||
data->addWidget(value, row++, 1);
|
||||
}
|
||||
};
|
||||
|
||||
addSocialNetworkWidget("Facebook", feature::Metadata::EType::FMD_CONTACT_FACEBOOK);
|
||||
addSocialNetworkWidget("Instagram", feature::Metadata::EType::FMD_CONTACT_INSTAGRAM);
|
||||
addSocialNetworkWidget("Twitter", feature::Metadata::EType::FMD_CONTACT_TWITTER);
|
||||
addSocialNetworkWidget("VK", feature::Metadata::EType::FMD_CONTACT_VK);
|
||||
addSocialNetworkWidget("Line", feature::Metadata::EType::FMD_CONTACT_LINE);
|
||||
addSocialNetworkWidget("Mastodon", feature::Metadata::EType::FMD_CONTACT_FEDIVERSE);
|
||||
addSocialNetworkWidget("Bluesky", feature::Metadata::EType::FMD_CONTACT_BLUESKY);
|
||||
}
|
||||
|
||||
if (auto wikimedia_commons = info.GetMetadata(feature::Metadata::EType::FMD_WIKIMEDIA_COMMONS);
|
||||
!wikimedia_commons.empty())
|
||||
{
|
||||
data->addWidget(new QLabel("Wikimedia Commons"), row, 0);
|
||||
|
||||
QLabel * value = new QLabel(QString::fromStdString(
|
||||
"<a href='" + feature::Metadata::ToWikimediaCommonsURL(std::string(wikimedia_commons)) +
|
||||
"'>Wikimedia Commons</a>"));
|
||||
value->setOpenExternalLinks(true);
|
||||
value->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
|
||||
data->addWidget(value, row++, 1);
|
||||
}
|
||||
|
||||
if (auto panoramax = info.GetMetadata(feature::Metadata::EType::FMD_PANORAMAX); !panoramax.empty())
|
||||
{
|
||||
data->addWidget(new QLabel("Panoramax Picture"), row, 0);
|
||||
|
||||
QLabel * value = new QLabel(QString::fromStdString(
|
||||
"<a href='https://api.panoramax.xyz/?pic=" + std::string(panoramax) + "'>Panoramax Image</a>"));
|
||||
value->setOpenExternalLinks(true);
|
||||
value->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
|
||||
data->addWidget(value, row++, 1);
|
||||
}
|
||||
|
||||
// Level fragment
|
||||
if (auto level = info.GetMetadata(feature::Metadata::EType::FMD_LEVEL); !level.empty())
|
||||
addEntry("Level", std::string(level));
|
||||
|
||||
// ATM fragment
|
||||
if (info.HasAtm())
|
||||
addEntry("ATM", "Yes");
|
||||
|
||||
// Latlon fragment
|
||||
|
||||
{
|
||||
ms::LatLon const ll = info.GetLatLon();
|
||||
addEntry("Coordinates", strings::to_string_dac(ll.m_lat, 7) + ", " + strings::to_string_dac(ll.m_lon, 7));
|
||||
}
|
||||
|
||||
data->setColumnStretch(0, 0);
|
||||
data->setColumnStretch(1, 1);
|
||||
|
||||
layout->addLayout(data);
|
||||
}
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
{
|
||||
QHLine * line = new QHLine();
|
||||
layout->addWidget(line);
|
||||
}
|
||||
|
||||
{
|
||||
QDialogButtonBox * dbb = new QDialogButtonBox();
|
||||
place_page_dialog::addCommonButtons(this, dbb, info.ShouldShowEditPlace());
|
||||
layout->addWidget(dbb, Qt::AlignCenter);
|
||||
}
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
auto const ppTitle = std::string("Place Page") + (info.IsBookmark() ? " (bookmarked)" : "");
|
||||
setWindowTitle(ppTitle.c_str());
|
||||
}
|
||||
19
qt/place_page_dialog_user.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "search/reverse_geocoder.hpp"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
namespace place_page
|
||||
{
|
||||
class Info;
|
||||
}
|
||||
|
||||
class PlacePageDialogUser : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlacePageDialogUser(QWidget * parent, place_page::Info const & info,
|
||||
search::ReverseGeocoder::Address const & address);
|
||||
};
|
||||
57
qt/popup_menu_holder.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#include "popup_menu_holder.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
|
||||
PopupMenuHolder::PopupMenuHolder(QObject * parent) : QObject(parent) {}
|
||||
|
||||
QAction * PopupMenuHolder::addActionImpl(QIcon const & icon, QString const & text, bool checkable)
|
||||
{
|
||||
QAction * p = new QAction(icon, text, this);
|
||||
p->setCheckable(checkable);
|
||||
m_actions.push_back(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
QToolButton * PopupMenuHolder::create()
|
||||
{
|
||||
QMenu * menu = new QMenu();
|
||||
for (auto * p : m_actions)
|
||||
menu->addAction(p);
|
||||
|
||||
m_toolButton = new QToolButton();
|
||||
m_toolButton->setPopupMode(QToolButton::MenuButtonPopup);
|
||||
m_toolButton->setMenu(menu);
|
||||
return m_toolButton;
|
||||
}
|
||||
|
||||
void PopupMenuHolder::setMainIcon(QIcon const & icon)
|
||||
{
|
||||
m_toolButton->setIcon(icon);
|
||||
}
|
||||
|
||||
void PopupMenuHolder::setCurrent(size_t idx)
|
||||
{
|
||||
CHECK_LESS(idx, m_actions.size(), ());
|
||||
m_toolButton->setIcon(m_actions[idx]->icon());
|
||||
}
|
||||
|
||||
void PopupMenuHolder::setChecked(size_t idx, bool checked)
|
||||
{
|
||||
CHECK_LESS(idx, m_actions.size(), ());
|
||||
ASSERT(m_actions[idx]->isCheckable(), ());
|
||||
m_actions[idx]->setChecked(checked);
|
||||
}
|
||||
|
||||
bool PopupMenuHolder::isChecked(size_t idx)
|
||||
{
|
||||
CHECK_LESS(idx, m_actions.size(), ());
|
||||
ASSERT(m_actions[idx]->isCheckable(), ());
|
||||
return m_actions[idx]->isChecked();
|
||||
}
|
||||
|
||||
} // namespace qt
|
||||
60
qt/popup_menu_holder.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtGui/QAction>
|
||||
#include <QtWidgets/QToolButton>
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
/// Inherit from QObject to assign parents and don't worry about deleting.
|
||||
class PopupMenuHolder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QToolButton * m_toolButton;
|
||||
|
||||
std::vector<QAction *> m_actions;
|
||||
|
||||
QAction * addActionImpl(QIcon const & icon, QString const & text, bool checkable);
|
||||
|
||||
public:
|
||||
explicit PopupMenuHolder(QObject * parent = nullptr);
|
||||
|
||||
QAction * addAction(QIcon const & icon, QString const & text, QObject const * receiver, char const * member,
|
||||
bool checkable)
|
||||
{
|
||||
QAction * p = addActionImpl(icon, text, checkable);
|
||||
connect(p, SIGNAL(triggered()), receiver, member);
|
||||
return p;
|
||||
}
|
||||
|
||||
template <class SlotT>
|
||||
QAction * addAction(QIcon const & icon, QString const & text, SlotT slot, bool checkable)
|
||||
{
|
||||
QAction * p = addActionImpl(icon, text, checkable);
|
||||
connect(p, &QAction::triggered, std::move(slot));
|
||||
return p;
|
||||
}
|
||||
|
||||
QToolButton * create();
|
||||
void setMainIcon(QIcon const & icon);
|
||||
|
||||
void setCurrent(size_t idx);
|
||||
template <class T>
|
||||
typename std::enable_if<std::is_enum<T>::value, void>::type setCurrent(T idx)
|
||||
{
|
||||
setCurrent(static_cast<size_t>(idx));
|
||||
}
|
||||
|
||||
void setChecked(size_t idx, bool checked);
|
||||
|
||||
bool isChecked(size_t idx);
|
||||
template <class T>
|
||||
typename std::enable_if<std::is_enum<T>::value, bool>::type isChecked(T idx)
|
||||
{
|
||||
return isChecked(static_cast<size_t>(idx));
|
||||
}
|
||||
};
|
||||
} // namespace qt
|
||||
216
qt/preferences_dialog.cpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
#include "qt/preferences_dialog.hpp"
|
||||
|
||||
#include "coding/string_utf8_multilang.hpp"
|
||||
#include "indexer/map_style.hpp"
|
||||
#include "map/framework.hpp"
|
||||
|
||||
#include "platform/measurement_utils.hpp"
|
||||
#include "platform/preferred_languages.hpp"
|
||||
#include "platform/settings.hpp"
|
||||
#include "platform/style_utils.hpp"
|
||||
|
||||
#include <QLocale>
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtWidgets/QButtonGroup>
|
||||
#include <QtWidgets/QCheckBox>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QGroupBox>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QRadioButton>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
using namespace measurement_utils;
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
std::string const kEnabledAutoRegenGeomIndex = "EnabledAutoRegenGeomIndex";
|
||||
#endif
|
||||
|
||||
namespace qt
|
||||
{
|
||||
PreferencesDialog::PreferencesDialog(QWidget * parent, Framework & framework)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
|
||||
{
|
||||
QIcon icon(":/ui/logo.png");
|
||||
setWindowIcon(icon);
|
||||
setWindowTitle(tr("Preferences"));
|
||||
|
||||
QButtonGroup * unitsGroup = new QButtonGroup(this);
|
||||
QGroupBox * unitsRadioBox = new QGroupBox("System of measurement");
|
||||
{
|
||||
QHBoxLayout * layout = new QHBoxLayout();
|
||||
|
||||
QRadioButton * radioButton = new QRadioButton("Metric");
|
||||
layout->addWidget(radioButton);
|
||||
unitsGroup->addButton(radioButton, static_cast<int>(Units::Metric));
|
||||
|
||||
radioButton = new QRadioButton("Imperial (foot)");
|
||||
layout->addWidget(radioButton);
|
||||
unitsGroup->addButton(radioButton, static_cast<int>(Units::Imperial));
|
||||
|
||||
unitsRadioBox->setLayout(layout);
|
||||
|
||||
Units u;
|
||||
if (!settings::Get(settings::kMeasurementUnits, u))
|
||||
{
|
||||
// Set default measurement from system locale
|
||||
if (QLocale::system().measurementSystem() == QLocale::MetricSystem)
|
||||
u = Units::Metric;
|
||||
else
|
||||
u = Units::Imperial;
|
||||
}
|
||||
unitsGroup->button(static_cast<int>(u))->setChecked(true);
|
||||
|
||||
// Temporary to pass the address of overloaded function.
|
||||
void (QButtonGroup::*buttonClicked)(int) = &QButtonGroup::idClicked;
|
||||
connect(unitsGroup, buttonClicked, [&framework](int i)
|
||||
{
|
||||
Units u = Units::Metric;
|
||||
switch (i)
|
||||
{
|
||||
case 0: u = Units::Metric; break;
|
||||
case 1: u = Units::Imperial; break;
|
||||
}
|
||||
|
||||
settings::Set(settings::kMeasurementUnits, u);
|
||||
framework.SetupMeasurementSystem();
|
||||
});
|
||||
}
|
||||
|
||||
QCheckBox * largeFontCheckBox = new QCheckBox("Use larger font on the map");
|
||||
{
|
||||
largeFontCheckBox->setChecked(framework.LoadLargeFontsSize());
|
||||
connect(largeFontCheckBox, &QCheckBox::stateChanged,
|
||||
[&framework](int i) { framework.SetLargeFontsSize(static_cast<bool>(i)); });
|
||||
}
|
||||
|
||||
QCheckBox * transliterationCheckBox = new QCheckBox("Transliterate to Latin");
|
||||
{
|
||||
transliterationCheckBox->setChecked(framework.LoadTransliteration());
|
||||
connect(transliterationCheckBox, &QCheckBox::stateChanged, [&framework](int i)
|
||||
{
|
||||
bool const enable = i > 0;
|
||||
framework.SaveTransliteration(enable);
|
||||
framework.AllowTransliteration(enable);
|
||||
});
|
||||
}
|
||||
|
||||
QCheckBox * developerModeCheckBox = new QCheckBox("Developer Mode");
|
||||
{
|
||||
bool developerMode;
|
||||
if (settings::Get(settings::kDeveloperMode, developerMode) && developerMode)
|
||||
developerModeCheckBox->setChecked(developerMode);
|
||||
connect(developerModeCheckBox, &QCheckBox::stateChanged,
|
||||
[](int i) { settings::Set(settings::kDeveloperMode, static_cast<bool>(i)); });
|
||||
}
|
||||
|
||||
QLabel * mapLanguageLabel = new QLabel("Map Language");
|
||||
QComboBox * mapLanguageComboBox = new QComboBox();
|
||||
{
|
||||
// The property maxVisibleItems is ignored for non-editable comboboxes in styles that
|
||||
// return true for `QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style.
|
||||
// So we ensure that it returns false here.
|
||||
mapLanguageComboBox->setStyleSheet("QComboBox { combobox-popup: 0; }");
|
||||
mapLanguageComboBox->setMaxVisibleItems(10);
|
||||
StringUtf8Multilang::Languages const & supportedLanguages =
|
||||
StringUtf8Multilang::GetSupportedLanguages(/* includeServiceLangs */ false);
|
||||
|
||||
// Create a vector of pairs (name, index) and sort by name
|
||||
std::vector<std::pair<std::string, size_t>> languageNameIndexPairs;
|
||||
for (size_t i = 0; i < supportedLanguages.size(); ++i)
|
||||
{
|
||||
languageNameIndexPairs.emplace_back(std::string(supportedLanguages[i].m_name), i);
|
||||
}
|
||||
std::sort(languageNameIndexPairs.begin(), languageNameIndexPairs.end(),
|
||||
[](auto const & a, auto const & b) { return a.first < b.first; });
|
||||
|
||||
QStringList languagesList = QStringList();
|
||||
std::vector<size_t> sortedIndices;
|
||||
for (auto const & pair : languageNameIndexPairs)
|
||||
{
|
||||
languagesList << QString::fromStdString(pair.first);
|
||||
sortedIndices.push_back(pair.second);
|
||||
}
|
||||
|
||||
mapLanguageComboBox->addItems(languagesList);
|
||||
std::string const & mapLanguageCode = framework.GetMapLanguageCode();
|
||||
int8_t languageIndex = StringUtf8Multilang::GetLangIndex(mapLanguageCode);
|
||||
if (languageIndex == StringUtf8Multilang::kUnsupportedLanguageCode)
|
||||
languageIndex = StringUtf8Multilang::kDefaultCode;
|
||||
|
||||
mapLanguageComboBox->setCurrentText(
|
||||
QString::fromStdString(std::string(StringUtf8Multilang::GetLangNameByCode(languageIndex))));
|
||||
connect(mapLanguageComboBox, &QComboBox::activated, [&framework, &supportedLanguages, sortedIndices](int index)
|
||||
{
|
||||
auto const & mapLanguageCode = std::string(supportedLanguages[sortedIndices[index]].m_code);
|
||||
framework.SetMapLanguageCode(mapLanguageCode);
|
||||
});
|
||||
}
|
||||
|
||||
QButtonGroup * nightModeGroup = new QButtonGroup(this);
|
||||
QGroupBox * nightModeRadioBox = new QGroupBox("Night Mode");
|
||||
{
|
||||
using namespace style_utils;
|
||||
QHBoxLayout * layout = new QHBoxLayout();
|
||||
|
||||
QRadioButton * radioButton = new QRadioButton("Off");
|
||||
layout->addWidget(radioButton);
|
||||
nightModeGroup->addButton(radioButton, static_cast<int>(NightMode::Off));
|
||||
|
||||
radioButton = new QRadioButton("On");
|
||||
layout->addWidget(radioButton);
|
||||
nightModeGroup->addButton(radioButton, static_cast<int>(NightMode::On));
|
||||
|
||||
nightModeRadioBox->setLayout(layout);
|
||||
|
||||
int const btn = MapStyleIsDark(framework.GetMapStyle()) ? 1 : 0;
|
||||
nightModeGroup->button(btn)->setChecked(true);
|
||||
|
||||
void (QButtonGroup::*buttonClicked)(int) = &QButtonGroup::idClicked;
|
||||
connect(nightModeGroup, buttonClicked, [&framework](int i)
|
||||
{
|
||||
auto const currStyle = framework.GetMapStyle();
|
||||
framework.SetMapStyle((i == 0) ? GetLightMapStyleVariant(currStyle) : GetDarkMapStyleVariant(currStyle));
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
QCheckBox * indexRegenCheckBox = new QCheckBox("Enable auto regeneration of geometry index");
|
||||
{
|
||||
bool enabled = false;
|
||||
if (!settings::Get(kEnabledAutoRegenGeomIndex, enabled))
|
||||
settings::Set(kEnabledAutoRegenGeomIndex, false);
|
||||
indexRegenCheckBox->setChecked(enabled);
|
||||
connect(indexRegenCheckBox, &QCheckBox::stateChanged,
|
||||
[](int i) { settings::Set(kEnabledAutoRegenGeomIndex, static_cast<bool>(i)) });
|
||||
}
|
||||
#endif
|
||||
|
||||
QHBoxLayout * bottomLayout = new QHBoxLayout();
|
||||
{
|
||||
QPushButton * closeButton = new QPushButton(tr("Close"));
|
||||
closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
closeButton->setDefault(true);
|
||||
connect(closeButton, &QAbstractButton::clicked, [this]() { done(0); });
|
||||
|
||||
bottomLayout->addStretch(1);
|
||||
bottomLayout->setSpacing(0);
|
||||
bottomLayout->addWidget(closeButton);
|
||||
}
|
||||
|
||||
QVBoxLayout * finalLayout = new QVBoxLayout();
|
||||
finalLayout->addWidget(unitsRadioBox);
|
||||
finalLayout->addWidget(largeFontCheckBox);
|
||||
finalLayout->addWidget(transliterationCheckBox);
|
||||
finalLayout->addWidget(developerModeCheckBox);
|
||||
finalLayout->addWidget(mapLanguageLabel);
|
||||
finalLayout->addWidget(mapLanguageComboBox);
|
||||
finalLayout->addWidget(nightModeRadioBox);
|
||||
#ifdef BUILD_DESIGNER
|
||||
finalLayout->addWidget(indexRegenCheckBox);
|
||||
#endif
|
||||
finalLayout->addLayout(bottomLayout);
|
||||
setLayout(finalLayout);
|
||||
}
|
||||
} // namespace qt
|
||||
24
qt/preferences_dialog.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class Framework;
|
||||
|
||||
namespace qt
|
||||
{
|
||||
class PreferencesDialog : public QDialog
|
||||
{
|
||||
typedef QDialog base_t;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PreferencesDialog(QWidget * parent, Framework & framework);
|
||||
};
|
||||
} // namespace qt
|
||||
|
||||
#ifdef BUILD_DESIGNER
|
||||
extern std::string const kEnabledAutoRegenGeomIndex;
|
||||
#endif
|
||||
36
qt/qt_common/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
project(qt_common)
|
||||
|
||||
QT6_ADD_RESOURCES(RESOURCES res/resources_common.qrc)
|
||||
|
||||
set_property(SOURCE qrc_resources_common.cpp PROPERTY SKIP_AUTOGEN ON)
|
||||
|
||||
set(SRC
|
||||
helpers.cpp
|
||||
helpers.hpp
|
||||
map_widget.cpp
|
||||
map_widget.hpp
|
||||
proxy_style.cpp
|
||||
proxy_style.hpp
|
||||
qtoglcontext.cpp
|
||||
qtoglcontext.hpp
|
||||
qtoglcontextfactory.cpp
|
||||
qtoglcontextfactory.hpp
|
||||
scale_slider.cpp
|
||||
scale_slider.hpp
|
||||
spinner.cpp
|
||||
spinner.hpp
|
||||
text_dialog.cpp
|
||||
text_dialog.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC} ${RESOURCES})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC ON)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
map
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
Qt6::OpenGL
|
||||
Qt6::OpenGLWidgets
|
||||
)
|
||||
96
qt/qt_common/helpers.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include "qt/qt_common/helpers.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtGui/QSurfaceFormat>
|
||||
|
||||
namespace qt::common
|
||||
{
|
||||
|
||||
bool IsLeftButton(Qt::MouseButtons buttons)
|
||||
{
|
||||
return buttons & Qt::LeftButton;
|
||||
}
|
||||
|
||||
bool IsLeftButton(QMouseEvent const * const e)
|
||||
{
|
||||
return IsLeftButton(e->button()) || IsLeftButton(e->buttons());
|
||||
}
|
||||
|
||||
bool IsRightButton(Qt::MouseButtons buttons)
|
||||
{
|
||||
return buttons & Qt::RightButton;
|
||||
}
|
||||
|
||||
bool IsRightButton(QMouseEvent const * const e)
|
||||
{
|
||||
return IsRightButton(e->button()) || IsRightButton(e->buttons());
|
||||
}
|
||||
|
||||
bool IsCommandModifier(QMouseEvent const * const e)
|
||||
{
|
||||
return e->modifiers() & Qt::ControlModifier;
|
||||
}
|
||||
|
||||
bool IsShiftModifier(QMouseEvent const * const e)
|
||||
{
|
||||
return e->modifiers() & Qt::ShiftModifier;
|
||||
}
|
||||
|
||||
bool IsAltModifier(QMouseEvent const * const e)
|
||||
{
|
||||
return e->modifiers() & Qt::AltModifier;
|
||||
}
|
||||
|
||||
location::GpsInfo MakeGpsInfo(m2::PointD const & point)
|
||||
{
|
||||
location::GpsInfo info;
|
||||
info.m_source = location::EUser;
|
||||
info.m_latitude = mercator::YToLat(point.y);
|
||||
info.m_longitude = mercator::XToLon(point.x);
|
||||
info.m_horizontalAccuracy = 10;
|
||||
info.m_timestamp = QDateTime::currentMSecsSinceEpoch() / 1000.0;
|
||||
return info;
|
||||
}
|
||||
|
||||
void SetDefaultSurfaceFormat(QString const & platformName)
|
||||
{
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setAlphaBufferSize(8);
|
||||
fmt.setBlueBufferSize(8);
|
||||
fmt.setGreenBufferSize(8);
|
||||
fmt.setRedBufferSize(8);
|
||||
fmt.setStencilBufferSize(0);
|
||||
fmt.setSamples(0);
|
||||
fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
|
||||
fmt.setSwapInterval(1);
|
||||
fmt.setDepthBufferSize(16);
|
||||
#if defined(OMIM_OS_LINUX)
|
||||
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
|
||||
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||
#endif
|
||||
|
||||
// Set proper OGL version now (needed for "cocoa" or "xcb"), but have troubles with "wayland" devices.
|
||||
// It will be resolved later in MapWidget::initializeGL when OGL context is available.
|
||||
if (platformName != QString("wayland"))
|
||||
{
|
||||
#if defined(OMIM_OS_LINUX)
|
||||
LOG(LINFO, ("Set default OpenGL version to ES 3.0"));
|
||||
fmt.setVersion(3, 0);
|
||||
#else
|
||||
LOG(LINFO, ("Set default OGL version to 3.2"));
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
fmt.setVersion(3, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL_DIAGNOSTICS
|
||||
fmt.setOption(QSurfaceFormat::DebugContext);
|
||||
#endif
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
}
|
||||
|
||||
} // namespace qt::common
|
||||
31
qt/qt_common/helpers.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
namespace qt::common
|
||||
{
|
||||
bool IsLeftButton(Qt::MouseButtons buttons);
|
||||
bool IsLeftButton(QMouseEvent const * const e);
|
||||
|
||||
bool IsRightButton(Qt::MouseButtons buttons);
|
||||
bool IsRightButton(QMouseEvent const * const e);
|
||||
|
||||
bool IsCommandModifier(QMouseEvent const * const e);
|
||||
bool IsShiftModifier(QMouseEvent const * const e);
|
||||
bool IsAltModifier(QMouseEvent const * const e);
|
||||
|
||||
struct Hotkey
|
||||
{
|
||||
Hotkey() = default;
|
||||
Hotkey(QKeySequence const & key, char const * slot) : m_key(key), m_slot(slot) {}
|
||||
|
||||
QKeySequence m_key = 0;
|
||||
char const * m_slot = nullptr;
|
||||
};
|
||||
|
||||
location::GpsInfo MakeGpsInfo(m2::PointD const & point);
|
||||
|
||||
void SetDefaultSurfaceFormat(QString const & platformName);
|
||||
} // namespace qt::common
|
||||
522
qt/qt_common/map_widget.cpp
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
#include "qt/qt_common/map_widget.hpp"
|
||||
|
||||
#include "qt/qt_common/helpers.hpp"
|
||||
#include "qt/qt_common/scale_slider.hpp"
|
||||
|
||||
#include "map/framework.hpp"
|
||||
|
||||
#include "routing/maxspeeds.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QTouchEvent>
|
||||
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QOpenGLFunctions>
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
// Fraction of the viewport for a move event
|
||||
static constexpr float kViewportFractionRoughMove = 0.2;
|
||||
|
||||
// Fraction of the viewport for a small move event
|
||||
static constexpr float kViewportFractionSmoothMove = 0.1;
|
||||
|
||||
namespace qt::common
|
||||
{
|
||||
// #define ENABLE_AA_SWITCH
|
||||
|
||||
MapWidget::MapWidget(Framework & framework, bool isScreenshotMode, QWidget * parent)
|
||||
: QOpenGLWidget(parent)
|
||||
, m_framework(framework)
|
||||
, m_screenshotMode(isScreenshotMode)
|
||||
, m_slider(nullptr)
|
||||
, m_sliderState(SliderState::Released)
|
||||
, m_ratio(1.0f)
|
||||
, m_contextFactory(nullptr)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
// Update widget's content frequency is 60 fps.
|
||||
m_updateTimer = std::make_unique<QTimer>(this);
|
||||
VERIFY(connect(m_updateTimer.get(), SIGNAL(timeout()), this, SLOT(update())), ());
|
||||
m_updateTimer->setSingleShot(false);
|
||||
m_updateTimer->start(1000 / 60);
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
}
|
||||
|
||||
MapWidget::~MapWidget()
|
||||
{
|
||||
m_framework.EnterBackground();
|
||||
m_framework.SetRenderingDisabled(true);
|
||||
m_contextFactory->PrepareToShutdown();
|
||||
m_framework.DestroyDrapeEngine();
|
||||
m_contextFactory.reset();
|
||||
}
|
||||
|
||||
void MapWidget::BindHotkeys(QWidget & parent)
|
||||
{
|
||||
Hotkey const hotkeys[] = {
|
||||
{Qt::Key_Equal, SLOT(ScalePlus())},
|
||||
{Qt::Key_Plus, SLOT(ScalePlus())},
|
||||
{Qt::Key_Minus, SLOT(ScaleMinus())},
|
||||
{Qt::Key_Right, SLOT(MoveRight())},
|
||||
{Qt::Key_Left, SLOT(MoveLeft())},
|
||||
{Qt::Key_Up, SLOT(MoveUp())},
|
||||
{Qt::Key_Down, SLOT(MoveDown())},
|
||||
{Qt::ALT | Qt::Key_Equal, SLOT(ScalePlusLight())},
|
||||
{Qt::ALT | Qt::Key_Plus, SLOT(ScalePlusLight())},
|
||||
{Qt::ALT | Qt::Key_Minus, SLOT(ScaleMinusLight())},
|
||||
{Qt::ALT | Qt::Key_Right, SLOT(MoveRightSmooth())},
|
||||
{Qt::ALT | Qt::Key_Left, SLOT(MoveLeftSmooth())},
|
||||
{Qt::ALT | Qt::Key_Up, SLOT(MoveUpSmooth())},
|
||||
{Qt::ALT | Qt::Key_Down, SLOT(MoveDownSmooth())},
|
||||
#ifdef ENABLE_AA_SWITCH
|
||||
{Qt::ALT | Qt::Key_A, SLOT(AntialiasingOn())},
|
||||
{Qt::ALT | Qt::Key_S, SLOT(AntialiasingOff())},
|
||||
#endif
|
||||
};
|
||||
|
||||
for (auto const & hotkey : hotkeys)
|
||||
{
|
||||
auto action = std::make_unique<QAction>(&parent);
|
||||
action->setShortcut(QKeySequence(hotkey.m_key));
|
||||
connect(action.get(), SIGNAL(triggered()), this, hotkey.m_slot);
|
||||
parent.addAction(action.release());
|
||||
}
|
||||
}
|
||||
|
||||
void MapWidget::BindSlider(ScaleSlider & slider)
|
||||
{
|
||||
m_slider = &slider;
|
||||
|
||||
connect(m_slider, &QAbstractSlider::actionTriggered, this, &MapWidget::ScaleChanged);
|
||||
connect(m_slider, &QAbstractSlider::sliderPressed, this, &MapWidget::SliderPressed);
|
||||
connect(m_slider, &QAbstractSlider::sliderReleased, this, &MapWidget::SliderReleased);
|
||||
}
|
||||
|
||||
void MapWidget::CreateEngine()
|
||||
{
|
||||
Framework::DrapeCreationParams p;
|
||||
|
||||
p.m_apiVersion = dp::ApiVersion::OpenGLES3;
|
||||
|
||||
p.m_surfaceWidth = m_screenshotMode ? width() : static_cast<int>(m_ratio * width());
|
||||
p.m_surfaceHeight = m_screenshotMode ? height() : static_cast<int>(m_ratio * height());
|
||||
p.m_visualScale = static_cast<float>(m_ratio);
|
||||
p.m_hints.m_screenshotMode = m_screenshotMode;
|
||||
|
||||
m_skin.reset(new gui::Skin(gui::ResolveGuiSkinFile("default"), m_ratio));
|
||||
m_skin->Resize(p.m_surfaceWidth, p.m_surfaceHeight);
|
||||
m_skin->ForEach([&p](gui::EWidget widget, gui::Position const & pos) { p.m_widgetsInitInfo[widget] = pos; });
|
||||
|
||||
p.m_widgetsInitInfo[gui::WIDGET_SCALE_FPS_LABEL] = gui::Position(dp::LeftTop);
|
||||
|
||||
m_framework.CreateDrapeEngine(make_ref(m_contextFactory), std::move(p));
|
||||
m_framework.SetViewportListener(std::bind(&MapWidget::OnViewportChanged, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void MapWidget::ScalePlus()
|
||||
{
|
||||
m_framework.Scale(Framework::SCALE_MAG, true);
|
||||
}
|
||||
|
||||
void MapWidget::ScaleMinus()
|
||||
{
|
||||
m_framework.Scale(Framework::SCALE_MIN, true);
|
||||
}
|
||||
|
||||
void MapWidget::ScalePlusLight()
|
||||
{
|
||||
m_framework.Scale(Framework::SCALE_MAG_LIGHT, true);
|
||||
}
|
||||
|
||||
void MapWidget::ScaleMinusLight()
|
||||
{
|
||||
m_framework.Scale(Framework::SCALE_MIN_LIGHT, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveRight()
|
||||
{
|
||||
m_framework.Move(-kViewportFractionRoughMove, 0, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveRightSmooth()
|
||||
{
|
||||
m_framework.Move(-kViewportFractionSmoothMove, 0, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveLeft()
|
||||
{
|
||||
m_framework.Move(kViewportFractionRoughMove, 0, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveLeftSmooth()
|
||||
{
|
||||
m_framework.Move(kViewportFractionSmoothMove, 0, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveUp()
|
||||
{
|
||||
m_framework.Move(0, -kViewportFractionRoughMove, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveUpSmooth()
|
||||
{
|
||||
m_framework.Move(0, -kViewportFractionSmoothMove, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveDown()
|
||||
{
|
||||
m_framework.Move(0, kViewportFractionRoughMove, true);
|
||||
}
|
||||
|
||||
void MapWidget::MoveDownSmooth()
|
||||
{
|
||||
m_framework.Move(0, kViewportFractionSmoothMove, true);
|
||||
}
|
||||
|
||||
void MapWidget::AntialiasingOn()
|
||||
{
|
||||
auto engine = m_framework.GetDrapeEngine();
|
||||
if (engine != nullptr)
|
||||
engine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, true);
|
||||
}
|
||||
|
||||
void MapWidget::AntialiasingOff()
|
||||
{
|
||||
auto engine = m_framework.GetDrapeEngine();
|
||||
if (engine != nullptr)
|
||||
engine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, false);
|
||||
}
|
||||
|
||||
void MapWidget::ScaleChanged(int action)
|
||||
{
|
||||
if (!m_slider)
|
||||
return;
|
||||
|
||||
if (action == QAbstractSlider::SliderNoAction)
|
||||
return;
|
||||
|
||||
double const factor = m_slider->GetScaleFactor();
|
||||
if (factor != 1.0)
|
||||
m_framework.Scale(factor, false);
|
||||
}
|
||||
|
||||
void MapWidget::SliderPressed()
|
||||
{
|
||||
m_sliderState = SliderState::Pressed;
|
||||
}
|
||||
|
||||
void MapWidget::SliderReleased()
|
||||
{
|
||||
m_sliderState = SliderState::Released;
|
||||
}
|
||||
|
||||
m2::PointD MapWidget::GetDevicePoint(QMouseEvent * e) const
|
||||
{
|
||||
return m2::PointD(L2D(e->position().x()), L2D(e->position().y()));
|
||||
}
|
||||
|
||||
df::Touch MapWidget::GetDfTouchFromQMouseEvent(QMouseEvent * e) const
|
||||
{
|
||||
df::Touch touch;
|
||||
touch.m_id = 0;
|
||||
touch.m_location = GetDevicePoint(e);
|
||||
return touch;
|
||||
}
|
||||
|
||||
df::TouchEvent MapWidget::GetDfTouchEventFromQMouseEvent(QMouseEvent * e, df::TouchEvent::ETouchType type) const
|
||||
{
|
||||
df::TouchEvent event;
|
||||
event.SetTouchType(type);
|
||||
event.SetFirstTouch(GetDfTouchFromQMouseEvent(e));
|
||||
if (IsCommandModifier(e))
|
||||
event.SetSecondTouch(GetSymmetrical(event.GetFirstTouch()));
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
df::Touch MapWidget::GetSymmetrical(df::Touch const & touch) const
|
||||
{
|
||||
m2::PointD const pixelCenter = m_framework.GetVisiblePixelCenter();
|
||||
m2::PointD const symmetricalLocation = pixelCenter + pixelCenter - m2::PointD(touch.m_location);
|
||||
|
||||
df::Touch result;
|
||||
result.m_id = touch.m_id + 1;
|
||||
result.m_location = symmetricalLocation;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MapWidget::OnViewportChanged(ScreenBase const & screen)
|
||||
{
|
||||
UpdateScaleControl();
|
||||
}
|
||||
|
||||
void MapWidget::UpdateScaleControl()
|
||||
{
|
||||
if (!m_slider || m_sliderState == SliderState::Pressed)
|
||||
return;
|
||||
m_slider->SetPosWithBlockedSignals(m_framework.GetDrawScale());
|
||||
}
|
||||
|
||||
void MapWidget::Build()
|
||||
{
|
||||
std::string_view vertexSrc;
|
||||
std::string_view fragmentSrc;
|
||||
#if defined(OMIM_OS_LINUX)
|
||||
vertexSrc = ":common/shaders/gles_300.vsh.glsl";
|
||||
fragmentSrc = ":common/shaders/gles_300.fsh.glsl";
|
||||
#else
|
||||
vertexSrc = ":common/shaders/gl_150.vsh.glsl";
|
||||
fragmentSrc = ":common/shaders/gl_150.fsh.glsl";
|
||||
#endif
|
||||
|
||||
m_program = std::make_unique<QOpenGLShaderProgram>(this);
|
||||
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexSrc.data());
|
||||
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentSrc.data());
|
||||
m_program->link();
|
||||
|
||||
m_vao = std::make_unique<QOpenGLVertexArrayObject>(this);
|
||||
m_vao->create();
|
||||
m_vao->bind();
|
||||
|
||||
m_vbo = std::make_unique<QOpenGLBuffer>(QOpenGLBuffer::VertexBuffer);
|
||||
m_vbo->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||
m_vbo->create();
|
||||
m_vbo->bind();
|
||||
|
||||
QVector4D vertices[4] = {QVector4D(-1.0, 1.0, 0.0, 1.0), QVector4D(1.0, 1.0, 1.0, 1.0),
|
||||
QVector4D(-1.0, -1.0, 0.0, 0.0), QVector4D(1.0, -1.0, 1.0, 0.0)};
|
||||
m_vbo->allocate(static_cast<void *>(vertices), sizeof(vertices));
|
||||
QOpenGLFunctions * f = QOpenGLContext::currentContext()->functions();
|
||||
// 0-index of the buffer is linked to "a_position" attribute in vertex shader.
|
||||
// Introduced in https://github.com/organicmaps/organicmaps/pull/9814
|
||||
f->glEnableVertexAttribArray(0);
|
||||
f->glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(QVector4D), nullptr);
|
||||
|
||||
m_program->release();
|
||||
m_vao->release();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
search::ReverseGeocoder::Address GetFeatureAddressInfo(Framework const & framework, FeatureType & ft)
|
||||
{
|
||||
search::ReverseGeocoder const coder(framework.GetDataSource());
|
||||
search::ReverseGeocoder::Address address;
|
||||
coder.GetExactAddress(ft, address);
|
||||
return address;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MapWidget::ShowInfoPopup(QMouseEvent * e, m2::PointD const & pt)
|
||||
{
|
||||
// show feature types
|
||||
QMenu menu;
|
||||
auto const addStringFn = [&menu](std::string const & s)
|
||||
{
|
||||
if (!s.empty())
|
||||
menu.addAction(QString::fromUtf8(s.c_str()));
|
||||
};
|
||||
|
||||
m_framework.ForEachFeatureAtPoint([&](FeatureType & ft)
|
||||
{
|
||||
// ID
|
||||
addStringFn(DebugPrint(ft.GetID()));
|
||||
|
||||
// Types
|
||||
std::string concat;
|
||||
auto types = feature::TypesHolder(ft);
|
||||
types.SortBySpec();
|
||||
for (auto const & type : types.ToObjectNames())
|
||||
concat += type + " ";
|
||||
addStringFn(concat);
|
||||
|
||||
// Name
|
||||
addStringFn(std::string(ft.GetReadableName()));
|
||||
|
||||
// Address
|
||||
auto const info = GetFeatureAddressInfo(m_framework, ft);
|
||||
addStringFn(info.FormatAddress());
|
||||
|
||||
if (ft.GetGeomType() == feature::GeomType::Line)
|
||||
{
|
||||
// Maxspeed
|
||||
auto const & dataSource = m_framework.GetDataSource();
|
||||
auto const handle = dataSource.GetMwmHandleById(ft.GetID().m_mwmId);
|
||||
auto const speeds = routing::LoadMaxspeeds(handle);
|
||||
if (speeds)
|
||||
{
|
||||
auto const speed = speeds->GetMaxspeed(ft.GetID().m_index);
|
||||
if (speed.IsValid())
|
||||
addStringFn(DebugPrint(speed));
|
||||
}
|
||||
}
|
||||
|
||||
int const layer = ft.GetLayer();
|
||||
if (layer != feature::LAYER_EMPTY)
|
||||
addStringFn("Layer = " + std::to_string(layer));
|
||||
|
||||
menu.addSeparator();
|
||||
}, m_framework.PtoG(pt));
|
||||
|
||||
menu.exec(e->pos());
|
||||
}
|
||||
|
||||
void MapWidget::initializeGL()
|
||||
{
|
||||
ASSERT(m_contextFactory == nullptr, ());
|
||||
if (!m_screenshotMode)
|
||||
m_ratio = devicePixelRatio();
|
||||
|
||||
#if defined(OMIM_OS_LINUX)
|
||||
{
|
||||
QOpenGLFunctions * funcs = context()->functions();
|
||||
LOG(LINFO, ("Vendor:", funcs->glGetString(GL_VENDOR), "\nRenderer:", funcs->glGetString(GL_RENDERER),
|
||||
"\nVersion:", funcs->glGetString(GL_VERSION), "\nShading language version:\n",
|
||||
funcs->glGetString(GL_SHADING_LANGUAGE_VERSION), "\nExtensions:", funcs->glGetString(GL_EXTENSIONS)));
|
||||
|
||||
if (!context()->isOpenGLES())
|
||||
LOG(LCRITICAL, ("Context is not LibGLES! This shouldn't have happened."));
|
||||
|
||||
auto fmt = context()->format();
|
||||
if (context()->format().version() < qMakePair(3, 0))
|
||||
{
|
||||
CHECK(false, ("OpenGL ES2 is not supported"));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LINFO, ("OpenGL version is at least 3.0, enabling GLSL '#version 300 es'"));
|
||||
fmt.setVersion(3, 0);
|
||||
}
|
||||
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_contextFactory.reset(new QtOGLContextFactory(context()));
|
||||
|
||||
emit BeforeEngineCreation();
|
||||
CreateEngine();
|
||||
m_framework.EnterForeground();
|
||||
}
|
||||
|
||||
void MapWidget::paintGL()
|
||||
{
|
||||
if (m_program == nullptr)
|
||||
Build();
|
||||
|
||||
if (m_contextFactory->AcquireFrame())
|
||||
{
|
||||
m_vao->bind();
|
||||
m_program->bind();
|
||||
|
||||
QOpenGLFunctions * funcs = context()->functions();
|
||||
funcs->glActiveTexture(GL_TEXTURE0);
|
||||
GLuint const image = m_contextFactory->GetTextureHandle();
|
||||
funcs->glBindTexture(GL_TEXTURE_2D, image);
|
||||
|
||||
int const samplerLocation = m_program->uniformLocation("u_sampler");
|
||||
m_program->setUniformValue(samplerLocation, 0);
|
||||
|
||||
QRectF const & texRect = m_contextFactory->GetTexRect();
|
||||
QVector2D const samplerSize(texRect.width(), texRect.height());
|
||||
|
||||
int const samplerSizeLocation = m_program->uniformLocation("u_samplerSize");
|
||||
m_program->setUniformValue(samplerSizeLocation, samplerSize);
|
||||
|
||||
funcs->glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
m_program->release();
|
||||
m_vao->release();
|
||||
}
|
||||
}
|
||||
|
||||
void MapWidget::resizeGL(int width, int height)
|
||||
{
|
||||
int const w = m_screenshotMode ? width : m_ratio * width;
|
||||
int const h = m_screenshotMode ? height : m_ratio * height;
|
||||
m_framework.OnSize(w, h);
|
||||
|
||||
if (m_skin)
|
||||
{
|
||||
m_skin->Resize(w, h);
|
||||
|
||||
gui::TWidgetsLayoutInfo layout;
|
||||
m_skin->ForEach([&layout](gui::EWidget w, gui::Position const & pos) { layout[w] = pos.m_pixelPivot; });
|
||||
|
||||
m_framework.SetWidgetLayout(std::move(layout));
|
||||
}
|
||||
}
|
||||
|
||||
void MapWidget::mouseDoubleClickEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::mouseDoubleClickEvent(e);
|
||||
if (IsLeftButton(e))
|
||||
m_framework.Scale(Framework::SCALE_MAG_LIGHT, GetDevicePoint(e), true);
|
||||
}
|
||||
|
||||
void MapWidget::mousePressEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::mousePressEvent(e);
|
||||
if (IsLeftButton(e))
|
||||
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_DOWN));
|
||||
}
|
||||
|
||||
void MapWidget::mouseMoveEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::mouseMoveEvent(e);
|
||||
if (IsLeftButton(e))
|
||||
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_MOVE));
|
||||
}
|
||||
|
||||
void MapWidget::mouseReleaseEvent(QMouseEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
if (e->button() == Qt::RightButton)
|
||||
emit OnContextMenuRequested(e->globalPosition().toPoint());
|
||||
|
||||
QOpenGLWidget::mouseReleaseEvent(e);
|
||||
if (IsLeftButton(e))
|
||||
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_UP));
|
||||
}
|
||||
|
||||
void MapWidget::wheelEvent(QWheelEvent * e)
|
||||
{
|
||||
if (m_screenshotMode)
|
||||
return;
|
||||
|
||||
QOpenGLWidget::wheelEvent(e);
|
||||
|
||||
QPointF const pos = e->position();
|
||||
|
||||
double const factor = e->angleDelta().y() / 3.0 / 360.0;
|
||||
// https://doc-snapshots.qt.io/qt6-dev/qwheelevent.html#angleDelta, angleDelta() returns in eighths of a degree.
|
||||
/// @todo Here you can tune the speed of zooming.
|
||||
m_framework.Scale(exp(factor), m2::PointD(L2D(pos.x()), L2D(pos.y())), false);
|
||||
}
|
||||
} // namespace qt::common
|
||||
113
qt/qt_common/map_widget.hpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape_frontend/gui/skin.hpp"
|
||||
#include "drape_frontend/user_event_stream.hpp"
|
||||
|
||||
#include "qt/qt_common/qtoglcontextfactory.hpp"
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Framework;
|
||||
class QMouseEvent;
|
||||
class QWidget;
|
||||
class ScreenBase;
|
||||
class QOpenGLShaderProgram;
|
||||
class QOpenGLVertexArrayObject;
|
||||
class QOpenGLBuffer;
|
||||
|
||||
namespace qt::common
|
||||
{
|
||||
class ScaleSlider;
|
||||
|
||||
class MapWidget : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MapWidget(Framework & framework, bool isScreenshotMode, QWidget * parent);
|
||||
~MapWidget() override;
|
||||
|
||||
void BindHotkeys(QWidget & parent);
|
||||
void BindSlider(ScaleSlider & slider);
|
||||
void CreateEngine();
|
||||
void grabGestures(QList<Qt::GestureType> const & gestures);
|
||||
|
||||
signals:
|
||||
void OnContextMenuRequested(QPoint const & p);
|
||||
|
||||
public slots:
|
||||
void ScalePlus();
|
||||
void ScaleMinus();
|
||||
void ScalePlusLight();
|
||||
void ScaleMinusLight();
|
||||
void MoveRight();
|
||||
void MoveRightSmooth();
|
||||
void MoveLeft();
|
||||
void MoveLeftSmooth();
|
||||
void MoveUp();
|
||||
void MoveUpSmooth();
|
||||
void MoveDown();
|
||||
void MoveDownSmooth();
|
||||
|
||||
void ScaleChanged(int action);
|
||||
void SliderPressed();
|
||||
void SliderReleased();
|
||||
|
||||
void AntialiasingOn();
|
||||
void AntialiasingOff();
|
||||
|
||||
public:
|
||||
Q_SIGNAL void BeforeEngineCreation();
|
||||
|
||||
protected:
|
||||
enum class SliderState
|
||||
{
|
||||
Pressed,
|
||||
Released
|
||||
};
|
||||
|
||||
int L2D(int px) const { return px * m_ratio; }
|
||||
m2::PointD GetDevicePoint(QMouseEvent * e) const;
|
||||
df::Touch GetDfTouchFromQMouseEvent(QMouseEvent * e) const;
|
||||
df::TouchEvent GetDfTouchEventFromQMouseEvent(QMouseEvent * e, df::TouchEvent::ETouchType type) const;
|
||||
df::Touch GetSymmetrical(df::Touch const & touch) const;
|
||||
|
||||
void UpdateScaleControl();
|
||||
void Build();
|
||||
void ShowInfoPopup(QMouseEvent * e, m2::PointD const & pt);
|
||||
|
||||
void OnViewportChanged(ScreenBase const & screen);
|
||||
|
||||
// QOpenGLWidget overrides:
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
void resizeGL(int width, int height) override;
|
||||
|
||||
void mouseDoubleClickEvent(QMouseEvent * e) override;
|
||||
void mousePressEvent(QMouseEvent * e) override;
|
||||
void mouseMoveEvent(QMouseEvent * e) override;
|
||||
void mouseReleaseEvent(QMouseEvent * e) override;
|
||||
void wheelEvent(QWheelEvent * e) override;
|
||||
|
||||
Framework & m_framework;
|
||||
bool m_screenshotMode;
|
||||
ScaleSlider * m_slider;
|
||||
SliderState m_sliderState;
|
||||
|
||||
float m_ratio;
|
||||
drape_ptr<QtOGLContextFactory> m_contextFactory;
|
||||
std::unique_ptr<gui::Skin> m_skin;
|
||||
|
||||
std::unique_ptr<QTimer> m_updateTimer;
|
||||
|
||||
std::unique_ptr<QOpenGLShaderProgram> m_program;
|
||||
std::unique_ptr<QOpenGLVertexArrayObject> m_vao;
|
||||
std::unique_ptr<QOpenGLBuffer> m_vbo;
|
||||
};
|
||||
|
||||
} // namespace qt::common
|
||||
135
qt/qt_common/proxy_style.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#include "qt/qt_common/proxy_style.hpp"
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
ProxyStyle::ProxyStyle(QStyle * p) : QStyle(), style(p) {}
|
||||
|
||||
void ProxyStyle::drawComplexControl(ComplexControl control, QStyleOptionComplex const * option, QPainter * painter,
|
||||
QWidget const * widget) const
|
||||
{
|
||||
style->drawComplexControl(control, option, painter, widget);
|
||||
}
|
||||
|
||||
void ProxyStyle::drawControl(ControlElement element, QStyleOption const * option, QPainter * painter,
|
||||
QWidget const * widget) const
|
||||
{
|
||||
style->drawControl(element, option, painter, widget);
|
||||
}
|
||||
|
||||
void ProxyStyle::drawItemPixmap(QPainter * painter, QRect const & rect, int alignment, QPixmap const & pixmap) const
|
||||
{
|
||||
style->drawItemPixmap(painter, rect, alignment, pixmap);
|
||||
}
|
||||
|
||||
void ProxyStyle::drawItemText(QPainter * painter, QRect const & rect, int alignment, QPalette const & pal, bool enabled,
|
||||
QString const & text, QPalette::ColorRole textRole) const
|
||||
{
|
||||
style->drawItemText(painter, rect, alignment, pal, enabled, text, textRole);
|
||||
}
|
||||
|
||||
void ProxyStyle::drawPrimitive(PrimitiveElement elem, QStyleOption const * option, QPainter * painter,
|
||||
QWidget const * widget) const
|
||||
{
|
||||
style->drawPrimitive(elem, option, painter, widget);
|
||||
}
|
||||
|
||||
QPixmap ProxyStyle::generatedIconPixmap(QIcon::Mode iconMode, QPixmap const & pixmap, QStyleOption const * option) const
|
||||
{
|
||||
return style->generatedIconPixmap(iconMode, pixmap, option);
|
||||
}
|
||||
|
||||
QStyle::SubControl ProxyStyle::hitTestComplexControl(ComplexControl control, QStyleOptionComplex const * option,
|
||||
QPoint const & pos, QWidget const * widget) const
|
||||
{
|
||||
return style->hitTestComplexControl(control, option, pos, widget);
|
||||
}
|
||||
|
||||
QRect ProxyStyle::itemPixmapRect(QRect const & rect, int alignment, QPixmap const & pixmap) const
|
||||
{
|
||||
return style->itemPixmapRect(rect, alignment, pixmap);
|
||||
}
|
||||
|
||||
QRect ProxyStyle::itemTextRect(QFontMetrics const & metrics, QRect const & rect, int alignment, bool enabled,
|
||||
QString const & text) const
|
||||
{
|
||||
return style->itemTextRect(metrics, rect, alignment, enabled, text);
|
||||
}
|
||||
|
||||
int ProxyStyle::pixelMetric(PixelMetric metric, QStyleOption const * option, QWidget const * widget) const
|
||||
{
|
||||
return style->pixelMetric(metric, option, widget);
|
||||
}
|
||||
|
||||
void ProxyStyle::polish(QWidget * widget)
|
||||
{
|
||||
style->polish(widget);
|
||||
}
|
||||
|
||||
void ProxyStyle::polish(QApplication * app)
|
||||
{
|
||||
style->polish(app);
|
||||
}
|
||||
|
||||
void ProxyStyle::polish(QPalette & pal)
|
||||
{
|
||||
style->polish(pal);
|
||||
}
|
||||
|
||||
QSize ProxyStyle::sizeFromContents(ContentsType type, QStyleOption const * option, QSize const & contentsSize,
|
||||
QWidget const * widget) const
|
||||
{
|
||||
return style->sizeFromContents(type, option, contentsSize, widget);
|
||||
}
|
||||
|
||||
QIcon ProxyStyle::standardIcon(StandardPixmap standardIcon, QStyleOption const * option, QWidget const * widget) const
|
||||
{
|
||||
return style->standardIcon(standardIcon, option, widget);
|
||||
}
|
||||
|
||||
QPalette ProxyStyle::standardPalette() const
|
||||
{
|
||||
return style->standardPalette();
|
||||
}
|
||||
|
||||
QPixmap ProxyStyle::standardPixmap(StandardPixmap standardPixmap, QStyleOption const * option,
|
||||
QWidget const * widget) const
|
||||
{
|
||||
return style->standardPixmap(standardPixmap, option, widget);
|
||||
}
|
||||
|
||||
int ProxyStyle::styleHint(StyleHint hint, QStyleOption const * option, QWidget const * widget,
|
||||
QStyleHintReturn * returnData) const
|
||||
{
|
||||
return style->styleHint(hint, option, widget, returnData);
|
||||
}
|
||||
|
||||
QRect ProxyStyle::subControlRect(ComplexControl control, QStyleOptionComplex const * option, SubControl subControl,
|
||||
QWidget const * widget) const
|
||||
{
|
||||
return style->subControlRect(control, option, subControl, widget);
|
||||
}
|
||||
|
||||
QRect ProxyStyle::subElementRect(SubElement element, QStyleOption const * option, QWidget const * widget) const
|
||||
{
|
||||
return style->subElementRect(element, option, widget);
|
||||
}
|
||||
|
||||
void ProxyStyle::unpolish(QWidget * widget)
|
||||
{
|
||||
style->unpolish(widget);
|
||||
}
|
||||
|
||||
void ProxyStyle::unpolish(QApplication * app)
|
||||
{
|
||||
style->unpolish(app);
|
||||
}
|
||||
|
||||
int ProxyStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
|
||||
Qt::Orientation orientation, QStyleOption const * option, QWidget const * widget) const
|
||||
{
|
||||
return style->layoutSpacing(control1, control2, orientation, option, widget);
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
55
qt/qt_common/proxy_style.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QStyle>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
class ProxyStyle : public QStyle
|
||||
{
|
||||
public:
|
||||
explicit ProxyStyle(QStyle * p);
|
||||
|
||||
// QStyle overrides:
|
||||
void drawComplexControl(ComplexControl control, QStyleOptionComplex const * option, QPainter * painter,
|
||||
QWidget const * widget = 0) const override;
|
||||
void drawControl(ControlElement element, QStyleOption const * option, QPainter * painter,
|
||||
QWidget const * widget = 0) const override;
|
||||
void drawItemPixmap(QPainter * painter, QRect const & rect, int alignment, QPixmap const & pixmap) const override;
|
||||
void drawItemText(QPainter * painter, QRect const & rect, int alignment, QPalette const & pal, bool enabled,
|
||||
QString const & text, QPalette::ColorRole textRole = QPalette::NoRole) const override;
|
||||
void drawPrimitive(PrimitiveElement elem, QStyleOption const * option, QPainter * painter,
|
||||
QWidget const * widget = 0) const override;
|
||||
QPixmap generatedIconPixmap(QIcon::Mode iconMode, QPixmap const & pixmap, QStyleOption const * option) const override;
|
||||
SubControl hitTestComplexControl(ComplexControl control, QStyleOptionComplex const * option, QPoint const & pos,
|
||||
QWidget const * widget = 0) const override;
|
||||
QRect itemPixmapRect(QRect const & rect, int alignment, QPixmap const & pixmap) const override;
|
||||
QRect itemTextRect(QFontMetrics const & metrics, QRect const & rect, int alignment, bool enabled,
|
||||
QString const & text) const override;
|
||||
int pixelMetric(PixelMetric metric, QStyleOption const * option = 0, QWidget const * widget = 0) const override;
|
||||
void polish(QWidget * widget) override;
|
||||
void polish(QApplication * app) override;
|
||||
void polish(QPalette & pal) override;
|
||||
QSize sizeFromContents(ContentsType type, QStyleOption const * option, QSize const & contentsSize,
|
||||
QWidget const * widget = 0) const override;
|
||||
QIcon standardIcon(StandardPixmap standardIcon, QStyleOption const * option = 0,
|
||||
QWidget const * widget = 0) const override;
|
||||
QPalette standardPalette() const override;
|
||||
QPixmap standardPixmap(StandardPixmap standardPixmap, QStyleOption const * option = 0,
|
||||
QWidget const * widget = 0) const override;
|
||||
int styleHint(StyleHint hint, QStyleOption const * option = 0, QWidget const * widget = 0,
|
||||
QStyleHintReturn * returnData = 0) const override;
|
||||
QRect subControlRect(ComplexControl control, QStyleOptionComplex const * option, SubControl subControl,
|
||||
QWidget const * widget = 0) const override;
|
||||
QRect subElementRect(SubElement element, QStyleOption const * option, QWidget const * widget = 0) const override;
|
||||
void unpolish(QWidget * widget) override;
|
||||
void unpolish(QApplication * app) override;
|
||||
int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation,
|
||||
QStyleOption const * option = 0, QWidget const * widget = 0) const override;
|
||||
|
||||
private:
|
||||
QStyle * style;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
144
qt/qt_common/qtoglcontext.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#include "qt/qt_common/qtoglcontext.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include "drape/gl_functions.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
QtRenderOGLContext::QtRenderOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface)
|
||||
: m_surface(surface)
|
||||
, m_ctx(std::make_unique<QOpenGLContext>())
|
||||
, m_isContextAvailable(false)
|
||||
{
|
||||
m_ctx->setFormat(rootContext->format());
|
||||
m_ctx->setShareContext(rootContext);
|
||||
m_ctx->create();
|
||||
CHECK(m_ctx->isValid(), ());
|
||||
}
|
||||
|
||||
void QtRenderOGLContext::Present()
|
||||
{
|
||||
GLFunctions::glFinish();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_frameMutex);
|
||||
std::swap(m_frontFrame, m_backFrame);
|
||||
m_frameUpdated = true;
|
||||
}
|
||||
|
||||
void QtRenderOGLContext::MakeCurrent()
|
||||
{
|
||||
CHECK(m_ctx->makeCurrent(m_surface), ());
|
||||
m_isContextAvailable = true;
|
||||
}
|
||||
|
||||
void QtRenderOGLContext::DoneCurrent()
|
||||
{
|
||||
m_isContextAvailable = false;
|
||||
m_ctx->doneCurrent();
|
||||
}
|
||||
|
||||
void QtRenderOGLContext::SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer)
|
||||
{
|
||||
if (framebuffer)
|
||||
framebuffer->Bind();
|
||||
else if (m_backFrame != nullptr)
|
||||
m_backFrame->bind();
|
||||
}
|
||||
|
||||
void QtRenderOGLContext::Resize(uint32_t w, uint32_t h)
|
||||
{
|
||||
CHECK(m_isContextAvailable, ());
|
||||
|
||||
// This function can't be called inside BeginRendering - EndRendering.
|
||||
std::lock_guard lock(m_frameMutex);
|
||||
|
||||
auto const nw = static_cast<int>(math::NextPowOf2(w));
|
||||
auto const nh = static_cast<int>(math::NextPowOf2(h));
|
||||
|
||||
if (nw <= m_width && nh <= m_height && m_backFrame != nullptr)
|
||||
{
|
||||
m_frameRect = QRectF(0.0, 0.0, w / static_cast<float>(m_width), h / static_cast<float>(m_height));
|
||||
return;
|
||||
}
|
||||
|
||||
m_width = nw;
|
||||
m_height = nh;
|
||||
m_frameRect = QRectF(0.0, 0.0, w / static_cast<float>(m_width), h / static_cast<float>(m_height));
|
||||
|
||||
m_backFrame = std::make_unique<QOpenGLFramebufferObject>(QSize(m_width, m_height), QOpenGLFramebufferObject::Depth);
|
||||
m_frontFrame = std::make_unique<QOpenGLFramebufferObject>(QSize(m_width, m_height), QOpenGLFramebufferObject::Depth);
|
||||
m_acquiredFrame =
|
||||
std::make_unique<QOpenGLFramebufferObject>(QSize(m_width, m_height), QOpenGLFramebufferObject::Depth);
|
||||
}
|
||||
|
||||
bool QtRenderOGLContext::AcquireFrame()
|
||||
{
|
||||
if (!m_isContextAvailable)
|
||||
return false;
|
||||
|
||||
std::lock_guard lock(m_frameMutex);
|
||||
// Render current acquired frame.
|
||||
if (!m_frameUpdated)
|
||||
return true;
|
||||
|
||||
// Update acquired frame.
|
||||
m_acquiredFrameRect = m_frameRect;
|
||||
std::swap(m_acquiredFrame, m_frontFrame);
|
||||
m_frameUpdated = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QRectF const & QtRenderOGLContext::GetTexRect() const
|
||||
{
|
||||
return m_acquiredFrameRect;
|
||||
}
|
||||
|
||||
GLuint QtRenderOGLContext::GetTextureHandle() const
|
||||
{
|
||||
if (!m_acquiredFrame)
|
||||
return 0;
|
||||
|
||||
CHECK(!m_acquiredFrame->isBound(), ());
|
||||
return m_acquiredFrame->texture();
|
||||
}
|
||||
|
||||
QtUploadOGLContext::QtUploadOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface)
|
||||
: m_surface(surface)
|
||||
, m_ctx(std::make_unique<QOpenGLContext>())
|
||||
{
|
||||
m_ctx->setFormat(rootContext->format());
|
||||
m_ctx->setShareContext(rootContext);
|
||||
m_ctx->create();
|
||||
CHECK(m_ctx->isValid(), ());
|
||||
}
|
||||
|
||||
void QtUploadOGLContext::MakeCurrent()
|
||||
{
|
||||
m_ctx->makeCurrent(m_surface);
|
||||
}
|
||||
|
||||
void QtUploadOGLContext::DoneCurrent()
|
||||
{
|
||||
m_ctx->doneCurrent();
|
||||
}
|
||||
|
||||
void QtUploadOGLContext::Present()
|
||||
{
|
||||
CHECK(false, ());
|
||||
}
|
||||
|
||||
void QtUploadOGLContext::SetFramebuffer(ref_ptr<dp::BaseFramebuffer>)
|
||||
{
|
||||
CHECK(false, ());
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
66
qt/qt_common/qtoglcontext.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/oglcontext.hpp"
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include <QtGui/QOffscreenSurface>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
class QtRenderOGLContext : public dp::OGLContext
|
||||
{
|
||||
public:
|
||||
QtRenderOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface);
|
||||
|
||||
void Present() override;
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override;
|
||||
void Resize(uint32_t w, uint32_t h) override;
|
||||
|
||||
bool AcquireFrame();
|
||||
GLuint GetTextureHandle() const;
|
||||
QRectF const & GetTexRect() const;
|
||||
|
||||
private:
|
||||
QOffscreenSurface * m_surface = nullptr;
|
||||
std::unique_ptr<QOpenGLContext> m_ctx;
|
||||
|
||||
std::unique_ptr<QOpenGLFramebufferObject> m_frontFrame;
|
||||
std::unique_ptr<QOpenGLFramebufferObject> m_backFrame;
|
||||
std::unique_ptr<QOpenGLFramebufferObject> m_acquiredFrame;
|
||||
QRectF m_acquiredFrameRect = QRectF(0.0, 0.0, 0.0, 0.0);
|
||||
QRectF m_frameRect = QRectF(0.0, 0.0, 0.0, 0.0);
|
||||
bool m_frameUpdated = false;
|
||||
|
||||
std::atomic<bool> m_isContextAvailable;
|
||||
int m_width = 0;
|
||||
int m_height = 0;
|
||||
|
||||
std::mutex m_frameMutex;
|
||||
};
|
||||
|
||||
class QtUploadOGLContext : public dp::OGLContext
|
||||
{
|
||||
public:
|
||||
QtUploadOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface);
|
||||
|
||||
void Present() override;
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override;
|
||||
|
||||
private:
|
||||
QOffscreenSurface * m_surface = nullptr; // non-owning ptr
|
||||
std::unique_ptr<QOpenGLContext> m_ctx;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
78
qt/qt_common/qtoglcontextfactory.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#include "qt/qt_common/qtoglcontextfactory.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
QtOGLContextFactory::QtOGLContextFactory(QOpenGLContext * rootContext) : m_rootContext(rootContext)
|
||||
{
|
||||
m_uploadSurface = CreateSurface();
|
||||
m_drawSurface = CreateSurface();
|
||||
}
|
||||
|
||||
QtOGLContextFactory::~QtOGLContextFactory()
|
||||
{
|
||||
m_drawContext.reset();
|
||||
m_uploadContext.reset();
|
||||
|
||||
m_drawSurface->destroy();
|
||||
m_uploadSurface->destroy();
|
||||
}
|
||||
|
||||
void QtOGLContextFactory::PrepareToShutdown()
|
||||
{
|
||||
m_preparedToShutdown = true;
|
||||
}
|
||||
|
||||
bool QtOGLContextFactory::AcquireFrame()
|
||||
{
|
||||
if (m_preparedToShutdown || !m_drawContext)
|
||||
return false;
|
||||
|
||||
return m_drawContext->AcquireFrame();
|
||||
}
|
||||
|
||||
QRectF const & QtOGLContextFactory::GetTexRect() const
|
||||
{
|
||||
ASSERT(m_drawContext != nullptr, ());
|
||||
return m_drawContext->GetTexRect();
|
||||
}
|
||||
|
||||
GLuint QtOGLContextFactory::GetTextureHandle() const
|
||||
{
|
||||
ASSERT(m_drawContext != nullptr, ());
|
||||
return m_drawContext->GetTextureHandle();
|
||||
}
|
||||
|
||||
dp::GraphicsContext * QtOGLContextFactory::GetDrawContext()
|
||||
{
|
||||
if (!m_drawContext)
|
||||
m_drawContext = std::make_unique<QtRenderOGLContext>(m_rootContext, m_drawSurface.get());
|
||||
|
||||
return m_drawContext.get();
|
||||
}
|
||||
|
||||
dp::GraphicsContext * QtOGLContextFactory::GetResourcesUploadContext()
|
||||
{
|
||||
if (!m_uploadContext)
|
||||
m_uploadContext = std::make_unique<QtUploadOGLContext>(m_rootContext, m_uploadSurface.get());
|
||||
|
||||
return m_uploadContext.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<QOffscreenSurface> QtOGLContextFactory::CreateSurface()
|
||||
{
|
||||
QSurfaceFormat format = m_rootContext->format();
|
||||
auto result = std::make_unique<QOffscreenSurface>(m_rootContext->screen());
|
||||
result->setFormat(format);
|
||||
result->create();
|
||||
ASSERT(result->isValid(), ());
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
43
qt/qt_common/qtoglcontextfactory.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "drape/graphics_context_factory.hpp"
|
||||
#include "qt/qt_common/qtoglcontext.hpp"
|
||||
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
class QtOGLContextFactory : public dp::GraphicsContextFactory
|
||||
{
|
||||
public:
|
||||
QtOGLContextFactory(QOpenGLContext * rootContext);
|
||||
~QtOGLContextFactory() override;
|
||||
|
||||
void PrepareToShutdown();
|
||||
|
||||
bool AcquireFrame();
|
||||
GLuint GetTextureHandle() const;
|
||||
QRectF const & GetTexRect() const;
|
||||
|
||||
// dp::GraphicsContextFactory overrides:
|
||||
dp::GraphicsContext * GetDrawContext() override;
|
||||
dp::GraphicsContext * GetResourcesUploadContext() override;
|
||||
bool IsDrawContextCreated() const override { return m_drawContext != nullptr; }
|
||||
bool IsUploadContextCreated() const override { return m_uploadContext != nullptr; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOffscreenSurface> CreateSurface();
|
||||
|
||||
QOpenGLContext * m_rootContext;
|
||||
std::unique_ptr<QtRenderOGLContext> m_drawContext;
|
||||
std::unique_ptr<QOffscreenSurface> m_drawSurface;
|
||||
std::unique_ptr<QtUploadOGLContext> m_uploadContext;
|
||||
std::unique_ptr<QOffscreenSurface> m_uploadSurface;
|
||||
bool m_preparedToShutdown = false;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
BIN
qt/qt_common/res/minus.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
qt/qt_common/res/plus.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
22
qt/qt_common/res/resources_common.qrc
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<RCC>
|
||||
<qresource prefix="/common">
|
||||
<file>minus.png</file>
|
||||
<file>plus.png</file>
|
||||
<file>spinner1.png</file>
|
||||
<file>spinner2.png</file>
|
||||
<file>spinner3.png</file>
|
||||
<file>spinner4.png</file>
|
||||
<file>spinner5.png</file>
|
||||
<file>spinner6.png</file>
|
||||
<file>spinner7.png</file>
|
||||
<file>spinner8.png</file>
|
||||
<file>spinner9.png</file>
|
||||
<file>spinner10.png</file>
|
||||
<file>spinner11.png</file>
|
||||
<file>spinner12.png</file>
|
||||
<file>shaders/gl_150.fsh.glsl</file>
|
||||
<file>shaders/gl_150.vsh.glsl</file>
|
||||
<file>shaders/gles_300.fsh.glsl</file>
|
||||
<file>shaders/gles_300.vsh.glsl</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
10
qt/qt_common/res/shaders/gl_150.fsh.glsl
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#version 150 core
|
||||
|
||||
uniform sampler2D u_sampler;
|
||||
in vec2 v_texCoord;
|
||||
out vec4 v_FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
v_FragColor = vec4(texture(u_sampler, v_texCoord).rgb, 1.0);
|
||||
}
|
||||
11
qt/qt_common/res/shaders/gl_150.vsh.glsl
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#version 150 core
|
||||
|
||||
in vec4 a_position;
|
||||
uniform vec2 u_samplerSize;
|
||||
out vec2 v_texCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
v_texCoord = vec2(a_position.z * u_samplerSize.x, a_position.w * u_samplerSize.y);
|
||||
gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
|
||||
}
|
||||
16
qt/qt_common/res/shaders/gles_300.fsh.glsl
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#version 300 es
|
||||
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform sampler2D u_sampler;
|
||||
in vec2 v_texCoord;
|
||||
out vec4 v_FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
v_FragColor = vec4(texture(u_sampler, v_texCoord).rgb, 1.0);
|
||||
}
|
||||
17
qt/qt_common/res/shaders/gles_300.vsh.glsl
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#version 300 es
|
||||
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
in vec4 a_position;
|
||||
uniform vec2 u_samplerSize;
|
||||
out vec2 v_texCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
v_texCoord = vec2(a_position.z * u_samplerSize.x, a_position.w * u_samplerSize.y);
|
||||
gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
|
||||
}
|
||||
BIN
qt/qt_common/res/spinner1.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner10.png
Normal file
|
After Width: | Height: | Size: 489 B |
BIN
qt/qt_common/res/spinner11.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner12.png
Normal file
|
After Width: | Height: | Size: 491 B |
BIN
qt/qt_common/res/spinner2.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner3.png
Normal file
|
After Width: | Height: | Size: 495 B |
BIN
qt/qt_common/res/spinner4.png
Normal file
|
After Width: | Height: | Size: 486 B |
BIN
qt/qt_common/res/spinner5.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner6.png
Normal file
|
After Width: | Height: | Size: 492 B |
BIN
qt/qt_common/res/spinner7.png
Normal file
|
After Width: | Height: | Size: 490 B |
BIN
qt/qt_common/res/spinner8.png
Normal file
|
After Width: | Height: | Size: 487 B |
BIN
qt/qt_common/res/spinner9.png
Normal file
|
After Width: | Height: | Size: 493 B |
79
qt/qt_common/scale_slider.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include "qt/qt_common/scale_slider.hpp"
|
||||
|
||||
#include "qt/qt_common/map_widget.hpp"
|
||||
#include "qt/qt_common/proxy_style.hpp"
|
||||
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <QtWidgets/QToolBar>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class MyProxyStyle : public ProxyStyle
|
||||
{
|
||||
public:
|
||||
explicit MyProxyStyle(QStyle * parent) : ProxyStyle(parent) {}
|
||||
|
||||
int styleHint(StyleHint hint, QStyleOption const * option, QWidget const * widget,
|
||||
QStyleHintReturn * returnData) const override
|
||||
{
|
||||
if (hint == SH_Slider_AbsoluteSetButtons)
|
||||
return Qt::LeftButton;
|
||||
return ProxyStyle::styleHint(hint, option, widget, returnData);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ScaleSlider::ScaleSlider(Qt::Orientation orient, QWidget * parent) : QSlider(orient, parent), m_factor(20)
|
||||
{
|
||||
setStyle(new MyProxyStyle(style()));
|
||||
SetRange(2, scales::GetUpperScale());
|
||||
setTickPosition(QSlider::TicksRight);
|
||||
}
|
||||
|
||||
// static
|
||||
void ScaleSlider::Embed(Qt::Orientation orient, QToolBar & toolBar, MapWidget & mapWidget)
|
||||
{
|
||||
toolBar.addAction(QIcon(":/common/plus.png"), tr("Scale +"), &mapWidget, SLOT(ScalePlus()));
|
||||
{
|
||||
auto slider = std::make_unique<ScaleSlider>(orient, &toolBar);
|
||||
mapWidget.BindSlider(*slider);
|
||||
toolBar.addWidget(slider.release());
|
||||
}
|
||||
toolBar.addAction(QIcon(":/common/minus.png"), tr("Scale -"), &mapWidget, SLOT(ScaleMinus()));
|
||||
}
|
||||
|
||||
double ScaleSlider::GetScaleFactor() const
|
||||
{
|
||||
double const oldValue = value();
|
||||
double const newValue = sliderPosition();
|
||||
|
||||
if (oldValue == newValue)
|
||||
return 1.0;
|
||||
double const f = pow(2, fabs(oldValue - newValue) / m_factor);
|
||||
return (newValue > oldValue ? f : 1.0 / f);
|
||||
}
|
||||
|
||||
void ScaleSlider::SetPosWithBlockedSignals(double pos)
|
||||
{
|
||||
bool const blocked = signalsBlocked();
|
||||
blockSignals(true);
|
||||
setSliderPosition(std::lround(pos * m_factor));
|
||||
blockSignals(blocked);
|
||||
}
|
||||
|
||||
void ScaleSlider::SetRange(int low, int up)
|
||||
{
|
||||
setRange(low * m_factor, up * m_factor);
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
30
qt/qt_common/scale_slider.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/Qt>
|
||||
#include <QtWidgets/QSlider>
|
||||
|
||||
class QToolBar;
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
class MapWidget;
|
||||
|
||||
class ScaleSlider : public QSlider
|
||||
{
|
||||
public:
|
||||
ScaleSlider(Qt::Orientation orient, QWidget * parent);
|
||||
|
||||
static void Embed(Qt::Orientation orient, QToolBar & toolBar, MapWidget & mapWidget);
|
||||
|
||||
double GetScaleFactor() const;
|
||||
void SetPosWithBlockedSignals(double pos);
|
||||
|
||||
private:
|
||||
void SetRange(int low, int up);
|
||||
|
||||
int m_factor;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace qt
|
||||
52
qt/qt_common/spinner.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "qt/qt_common/spinner.hpp"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/Qt>
|
||||
|
||||
namespace
|
||||
{
|
||||
int constexpr kMinSpinnerPixmap = 1;
|
||||
int constexpr kMaxSpinnerPixmap = 12;
|
||||
int constexpr kTimeoutMs = 100;
|
||||
} // namespace
|
||||
|
||||
Spinner::Spinner()
|
||||
{
|
||||
for (int i = kMinSpinnerPixmap; i <= kMaxSpinnerPixmap; ++i)
|
||||
{
|
||||
auto const path = ":common/spinner" + QString::number(i) + ".png";
|
||||
m_pixmaps.emplace_back(path);
|
||||
}
|
||||
|
||||
setEnabled(false);
|
||||
setAlignment(Qt::AlignCenter);
|
||||
|
||||
QSizePolicy policy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
policy.setRetainSizeWhenHidden(true);
|
||||
setSizePolicy(policy);
|
||||
|
||||
setMinimumSize(m_pixmaps.front().size());
|
||||
setPixmap(m_pixmaps.front());
|
||||
|
||||
m_timer = new QTimer(this /* parent */);
|
||||
connect(m_timer, &QTimer::timeout, [this]()
|
||||
{
|
||||
m_progress = (m_progress + 1) % m_pixmaps.size();
|
||||
setPixmap(m_pixmaps[m_progress]);
|
||||
});
|
||||
}
|
||||
|
||||
void Spinner::Show()
|
||||
{
|
||||
m_progress = 0;
|
||||
setPixmap(m_pixmaps[m_progress]);
|
||||
m_timer->start(kTimeoutMs);
|
||||
show();
|
||||
}
|
||||
|
||||
void Spinner::Hide()
|
||||
{
|
||||
m_timer->stop();
|
||||
hide();
|
||||
}
|
||||
23
qt/qt_common/spinner.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtWidgets/QLabel>
|
||||
|
||||
class Spinner : private QLabel
|
||||
{
|
||||
public:
|
||||
Spinner();
|
||||
|
||||
void Show();
|
||||
void Hide();
|
||||
|
||||
QLabel & AsWidget() { return static_cast<QLabel &>(*this); }
|
||||
|
||||
private:
|
||||
std::vector<QPixmap> m_pixmaps;
|
||||
QTimer * m_timer = nullptr;
|
||||
size_t m_progress = 0;
|
||||
};
|
||||
37
qt/qt_common/text_dialog.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include "qt/qt_common/text_dialog.hpp"
|
||||
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QTextEdit>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
TextDialog::TextDialog(QWidget * parent, QString const & htmlOrText, QString const & title) : QDialog(parent)
|
||||
{
|
||||
auto * textEdit = new QTextEdit(this);
|
||||
textEdit->setReadOnly(true);
|
||||
textEdit->setHtml(htmlOrText);
|
||||
|
||||
auto * closeButton = new QPushButton("Close");
|
||||
closeButton->setDefault(true);
|
||||
connect(closeButton, &QAbstractButton::clicked, this, &TextDialog::OnClose);
|
||||
|
||||
auto * dbb = new QDialogButtonBox();
|
||||
dbb->addButton(closeButton, QDialogButtonBox::RejectRole);
|
||||
|
||||
auto * vBoxLayout = new QVBoxLayout(this);
|
||||
vBoxLayout->addWidget(textEdit);
|
||||
vBoxLayout->addWidget(dbb);
|
||||
setLayout(vBoxLayout);
|
||||
|
||||
setWindowTitle(title);
|
||||
|
||||
if (htmlOrText.size() > 10000)
|
||||
setWindowState(Qt::WindowMaximized);
|
||||
else
|
||||
resize(parent->size());
|
||||
}
|
||||
|
||||
void TextDialog::OnClose()
|
||||
{
|
||||
reject();
|
||||
}
|
||||
15
qt/qt_common/text_dialog.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
/// A reusable dialog that shows text or HTML.
|
||||
class TextDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TextDialog(QWidget * parent, QString const & htmlOrText, QString const & title = "");
|
||||
|
||||
private slots:
|
||||
void OnClose();
|
||||
};
|
||||
BIN
qt/res/bookmark.png
Normal file
|
After Width: | Height: | Size: 960 B |
BIN
qt/res/borders_selection.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
qt/res/bug.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
qt/res/busy.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
qt/res/chart.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
qt/res/city_boundaries.png
Normal file
|
After Width: | Height: | Size: 904 B |
BIN
qt/res/city_roads.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
qt/res/clear-route.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
qt/res/clear.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
qt/res/down.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
qt/res/download.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
qt/res/geom.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
qt/res/guides.png
Normal file
|
After Width: | Height: | Size: 546 B |
BIN
qt/res/isolines.png
Normal file
|
After Width: | Height: | Size: 484 B |
BIN
qt/res/layers.png
Normal file
|
After Width: | Height: | Size: 405 B |
BIN
qt/res/left.png
Normal file
|
After Width: | Height: | Size: 5 KiB |
15
qt/res/linux/app.comaps.comaps.desktop
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Version=1.0
|
||||
Name=CoMaps
|
||||
Comment=Detailed Offline Maps of the World
|
||||
Comment[ast]=Mapes detallaos del mundu ensin conexón
|
||||
Comment[ca]=Mapes detallats del món sense connexió
|
||||
Comment[es]=Mapas detallados del mundo sin conexión
|
||||
Comment[ru]=Подробная оффлайновая карта мира
|
||||
Icon=comaps
|
||||
TryExec=CoMaps
|
||||
Exec=CoMaps
|
||||
Categories=Maps;Education;Geography;Geoscience;Qt;
|
||||
Keywords=Map;Maps;Offline Maps;CoMaps;OSM;OpenStreetMap;
|
||||
X-KDE-FormFactor=desktop;tablet;handset;
|
||||
BIN
qt/res/load.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
qt/res/location-search.png
Normal file
|
After Width: | Height: | Size: 516 B |
BIN
qt/res/location.png
Normal file
|
After Width: | Height: | Size: 363 B |