Files
noggit-red/src/noggit/ui/windows/noggitWindow/NoggitWindow.cpp
T1ti c9ed5891a6 - allow to favorite a project and auto load it
- added menu button to exit to project selection
- re ordered settings tabs
2024-04-25 23:42:50 +02:00

546 lines
19 KiB
C++
Executable File

#include <noggit/ui/windows/about/About.h>
#include <noggit/DBC.h>
#include <noggit/DBCFile.h>
#include <noggit/Log.h>
#include <noggit/World.h>
#include <noggit/ContextObject.hpp>
#include <noggit/ui/windows/noggitWindow/NoggitWindow.hpp>
#include <noggit/MapView.h>
#include <noggit/ui/windows/settingsPanel/SettingsPanel.h>
#include <noggit/ui/minimap_widget.hpp>
#include <noggit/ui/UidFixWindow.hpp>
#include <noggit/uid_storage.hpp>
#include <noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.hpp>
#include <noggit/ui/FontAwesome.hpp>
#include <noggit/ui/FramelessWindow.hpp>
#include <noggit/ui/tools/UiCommon/StackedWidget.hpp>
#include <noggit/project/ApplicationProject.h>
#include <BlizzardDatabase.h>
#include <QtGui/QCloseEvent>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
#include <QtWidgets/QStackedWidget>
#include <QtWidgets/QComboBox>
#include <noggit/ui/windows/noggitWindow/widgets/MapListItem.hpp>
#include <noggit/ui/windows/noggitWindow/widgets/MapBookmarkListItem.hpp>
#include <QtNetwork/QTcpSocket>
#include <sstream>
#include <QSysInfo>
#include <QStandardPaths>
#include <QDir>
#include <QIcon>
#include <noggit/ui/windows/noggitWindow/components/BuildMapListComponent.hpp>
#include <noggit/application/Utils.hpp>
#ifdef USE_MYSQL_UID_STORAGE
#include <mysql/mysql.h>
#include <QtCore/QSettings>
#endif
#include "revision.h"
#include "ui_TitleBar.h"
#include <external/framelesshelper/framelesswindowsmanager.h>
#include <noggit/ui/tools/ViewportManager/ViewportManager.hpp>
namespace Noggit::Ui::Windows
{
NoggitWindow::NoggitWindow(std::shared_ptr<Noggit::Application::NoggitApplicationConfiguration> application,
std::shared_ptr<Noggit::Project::NoggitProject> project)
: QMainWindow(nullptr)
, _null_widget(new QWidget(this))
, _applicationConfiguration(application)
, _project(project)
{
std::stringstream title;
title << "Noggit - " << STRPRODUCTVER;
setWindowTitle(QString::fromStdString(title.str()));
setWindowIcon(QIcon(":/icon"));
Log << "Project version : " << Noggit::Project::ClientVersionFactory::MapToStringVersion(project->projectVersion).c_str() << std::endl;
if (project->projectVersion == Project::ProjectVersion::WOTLK)
{
OpenDBs(project->ClientData);
}
setCentralWidget(_null_widget);
// The default value is AnimatedDocks | AllowTabbedDocks.
setDockOptions(AnimatedDocks | AllowNestedDocks | AllowTabbedDocks | GroupedDragging);
_about = new about(this);
_settings = new settings(this);
_menuBar = menuBar();
QSettings settings;
if (!settings.value("systemWindowFrame", true).toBool())
{
QWidget* widget = new QWidget(this);
::Ui::TitleBar* titleBarWidget = setupFramelessWindow(widget, this, minimumSize(), maximumSize(), true);
titleBarWidget->horizontalLayout->insertWidget(2, _menuBar);
setMenuWidget(widget);
}
_menuBar->setNativeMenuBar(settings.value("nativeMenubar", true).toBool());
auto file_menu(_menuBar->addMenu("&Noggit"));
auto settings_action(file_menu->addAction("Settings"));
QObject::connect(settings_action, &QAction::triggered, [&]
{
_settings->show();
}
);
auto about_action(file_menu->addAction("About"));
QObject::connect(about_action, &QAction::triggered, [&]
{
_about->show();
}
);
auto mapmenu_action(file_menu->addAction("Exit"));
QObject::connect(mapmenu_action, &QAction::triggered, [this]
{
close();
}
);
auto proj_selec_action(file_menu->addAction("Exit to Project Selection"));
QObject::connect(proj_selec_action, &QAction::triggered, [this]
{
auto noggit = Noggit::Application::NoggitApplication::instance();
auto project_selection = new Noggit::Ui::Windows::NoggitProjectSelectionWindow(noggit);
project_selection->show();
close();
}
);
_menuBar->adjustSize();
_buildMapListComponent = std::make_unique<Component::BuildMapListComponent>();
buildMenu();
}
void NoggitWindow::check_uid_then_enter_map
(glm::vec3 pos, math::degrees camera_pitch, math::degrees camera_yaw, bool from_bookmark
)
{
QSettings settings;
#ifdef USE_MYSQL_UID_STORAGE
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
bool valid_conn = false;
if (use_mysql)
{
valid_conn = mysql::testConnection(true);
}
if ((valid_conn && mysql::hasMaxUIDStoredDB(_world->getMapID()))
|| uid_storage::hasMaxUIDStored(_world->getMapID())
)
{
_world->mapIndex.loadMaxUID();
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
}
#else
if (uid_storage::hasMaxUIDStored(_world->getMapID()))
{
if (settings.value("uid_startup_check", true).toBool())
{
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::max_uid, from_bookmark);
} else
{
_world->mapIndex.loadMaxUID();
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
}
}
#endif
else
{
auto uid_fix_window(new UidFixWindow(pos, camera_pitch, camera_yaw));
uid_fix_window->show();
connect(uid_fix_window, &Noggit::Ui::UidFixWindow::fix_uid, [this, from_bookmark]
(glm::vec3 pos, math::degrees camera_pitch, math::degrees camera_yaw, uid_fix_mode uid_fix
)
{
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix, from_bookmark);
}
);
}
}
void
NoggitWindow::enterMapAt(glm::vec3 pos, math::degrees camera_pitch, math::degrees camera_yaw, uid_fix_mode uid_fix,
bool from_bookmark
)
{
if (_world->mapIndex.hasAGlobalWMO())
{
// enter at mdoel's position
// pos = glm::vec3(_world->mWmoEntry[0], _world->mWmoEntry.pos[1], _world->mWmoEntry.pos[2]);
// better, enter at model's max extent, facing toward min extent
auto min_extent = glm::vec3(_world->mWmoEntry.extents[0][0], _world->mWmoEntry.extents[0][1], _world->mWmoEntry.extents[0][2]);
auto max_extent = glm::vec3(_world->mWmoEntry.extents[1][0], _world->mWmoEntry.extents[1][1] * 2, _world->mWmoEntry.extents[1][2]);
float dx = min_extent.x - max_extent.x;
float dy = min_extent.z - max_extent.z; // flipping z and y works better for some reason
float dz = min_extent.y - max_extent.y;
pos = max_extent;
camera_yaw = math::degrees(math::radians(std::atan2(dx, dy)));
float distance = std::sqrt(dx * dx + dy * dy + dz * dz);
camera_pitch = math::degrees(math::radians(std::asin(dz / distance)));
}
_map_creation_wizard->destroyFakeWorld();
_map_view = (new MapView(camera_yaw, camera_pitch, pos, this, _project, std::move(_world), uid_fix, from_bookmark));
connect(_map_view, &MapView::uid_fix_failed, [this]()
{ promptUidFixFailure(); });
connect(_settings, &settings::saved, [this]()
{ if (_map_view) _map_view->onSettingsSave(); });
_stack_widget->addWidget(_map_view);
_stack_widget->setCurrentIndex(1);
map_loaded = true;
}
void NoggitWindow::applyFilterSearch(const QString &name, int type, int expansion, bool wmo_maps)
{
for (int i = 0; i < _continents_table->count(); ++i)
{
auto item_widget = _continents_table->item(i);
auto widget = qobject_cast<Noggit::Ui::Widget::MapListItem*>(_continents_table->itemWidget(item_widget));
if (!widget)
continue;
item_widget->setHidden(false);
if (!widget->name().contains(name, Qt::CaseInsensitive))
{
item_widget->setHidden(true);
continue;
}
if (!(widget->type() == (type - 2)) && type != 0)
{
item_widget->setHidden(true);
continue;
}
if (!(widget->expansion() == (expansion - 1)) && expansion != 0)
{
item_widget->setHidden(true);
}
if (!(widget->wmo_map() == wmo_maps))
{
item_widget->setHidden(true);
}
}
}
void NoggitWindow::loadMap(int map_id)
{
_minimap->world(nullptr);
_world.reset();
auto table = _project->ClientDatabase->LoadTable("Map", readFileAsIMemStream);
auto record = table.Record(map_id);
_world = std::make_unique<World>(record.Columns["Directory"].Value, map_id, Noggit::NoggitRenderContext::MAP_VIEW);
_minimap->world(_world.get());
_project->ClientDatabase->UnloadTable("Map");
emit mapSelected(map_id);
}
void NoggitWindow::buildMenu()
{
_stack_widget = new StackedWidget(this);
_stack_widget->setAutoResize(true);
setCentralWidget(_stack_widget);
auto widget(new QWidget(_stack_widget));
_stack_widget->addWidget(widget);
auto layout(new QHBoxLayout(widget));
layout->setAlignment(Qt::AlignLeft);
QListWidget* bookmarks_table(new QListWidget(widget));
_continents_table = new QListWidget(widget);
QObject::connect(_continents_table, &QListWidget::itemClicked, [this](QListWidgetItem* item)
{
loadMap(item->data(Qt::UserRole).toInt());
}
);
QTabWidget* entry_points_tabs(new QTabWidget(widget));
//entry_points_tabs->addTab(_continents_table, "Maps");
auto add_btn = new QPushButton("Add New Map", this);
add_btn->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::plus));
add_btn->setAccessibleName("map_wizard_add_button");
/* set-up widget for seaching etc... through _continents_table */
{
QWidget* _first_tab = new QWidget(this);
QVBoxLayout* _first_tab_layout = new QVBoxLayout();
_first_tab->setLayout(_first_tab_layout);
QGroupBox* _group_search = new QGroupBox(tr("Search"), this);
QLineEdit* _line_edit_search = new QLineEdit(this);
QComboBox* _combo_search = new QComboBox(this);
_combo_search->addItems(QStringList() <<
tr("All") <<
tr("Unknown") <<
tr("Continent") <<
tr("Dungeon") <<
tr("Raid") <<
tr("Battleground") <<
tr("Arena") <<
tr("Scenario"));
_combo_search->setCurrentIndex(0);
QComboBox* _combo_exp_search = new QComboBox(this);
_combo_exp_search->addItem(tr("All"));
_combo_exp_search->addItem(QIcon(":/icon-classic"), tr("Classic"));
_combo_exp_search->addItem(QIcon(":/icon-burning"), tr("Burning Cursade"));
_combo_exp_search->addItem(QIcon(":/icon-wrath"), tr("Wrath of the Lich King"));
_combo_exp_search->addItem(QIcon(":/icon-cata"), tr("Cataclism"));
_combo_exp_search->addItem(QIcon(":/icon-panda"), tr("Mist of Pandaria"));
_combo_exp_search->addItem(QIcon(":/icon-warlords"), tr("Warlords of Draenor"));
_combo_exp_search->addItem(QIcon(":/icon-legion"), tr("Legion"));
_combo_exp_search->addItem(QIcon(":/icon-battle"), tr("Battle for Azeroth"));
_combo_exp_search->addItem(QIcon(":/icon-shadow"), tr("Shadowlands"));
_combo_exp_search->setCurrentIndex(0);
QCheckBox* _wmo_maps_search = new QCheckBox("Display WMO maps (No terrain)", this);
QObject::connect(_line_edit_search, QOverload<const QString&>::of(&QLineEdit::textChanged), [this, _combo_search, _combo_exp_search, _wmo_maps_search](const QString &name)
{
applyFilterSearch(name, _combo_search->currentIndex(), _combo_exp_search->currentIndex(), _wmo_maps_search->isChecked());
});
QObject::connect(_combo_search, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, _line_edit_search, _combo_exp_search, _wmo_maps_search](int index)
{
applyFilterSearch(_line_edit_search->text(), index, _combo_exp_search->currentIndex(), _wmo_maps_search->isChecked());
});
QObject::connect(_combo_exp_search, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, _line_edit_search, _combo_search, _wmo_maps_search](int index)
{
applyFilterSearch(_line_edit_search->text(), _combo_search->currentIndex(), index, _wmo_maps_search->isChecked());
});
QObject::connect(_wmo_maps_search, &QCheckBox::stateChanged, [this, _line_edit_search, _combo_search, _combo_exp_search](bool b)
{
applyFilterSearch(_line_edit_search->text(), _combo_search->currentIndex(), _combo_exp_search->currentIndex(), b);
});
QFormLayout* _group_layout = new QFormLayout();
_group_layout->addRow(tr("Name : "), _line_edit_search);
_group_layout->addRow(tr("Type : "), _combo_search);
_group_layout->addRow(tr("Expansion : "), _combo_exp_search);
_group_layout->addRow( _wmo_maps_search);
_group_search->setLayout(_group_layout);
_first_tab_layout->addWidget(_group_search);
_first_tab_layout->addSpacing(5);
_first_tab_layout->addWidget(_continents_table);
_first_tab_layout->addWidget(add_btn);
entry_points_tabs->addTab(_first_tab, tr("Maps"));
}
entry_points_tabs->addTab(bookmarks_table, "Bookmarks");
entry_points_tabs->setFixedWidth(310);
layout->addWidget(entry_points_tabs);
_buildMapListComponent->buildMapList(this);
qulonglong bookmark_index(0);
for (auto entry: _project->Bookmarks)
{
auto item = new QListWidgetItem(bookmarks_table);
auto bookmark_data = Widget::MapListBookmarkData();
bookmark_data.MapName = QString::fromStdString(entry.name);
bookmark_data.Position = entry.position;
auto map_bookmark_item = new Widget::MapListBookmarkItem(bookmark_data, bookmarks_table);
item->setData(Qt::UserRole, QVariant(bookmark_index++));
item->setSizeHint(map_bookmark_item->minimumSizeHint());
bookmarks_table->setItemWidget(item, map_bookmark_item);
}
QObject::connect(bookmarks_table, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
{
auto& entry(_project->Bookmarks.at(item->data(Qt::UserRole).toInt()));
_world.reset();
for (DBCFile::Iterator it = gMapDB.begin(); it != gMapDB.end(); ++it)
{
if (it->getInt(MapDB::MapID) == entry.map_id)
{
_world = std::make_unique<World>(it->getString(MapDB::InternalName),
entry.map_id, Noggit::NoggitRenderContext::MAP_VIEW);
check_uid_then_enter_map(entry.position, math::degrees(entry.camera_pitch), math::degrees(entry.camera_yaw),
true
);
return;
}
}
}
);
_minimap = new minimap_widget(this);
_minimap->draw_boundaries(true);
//_minimap->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
QObject::connect(_minimap, &minimap_widget::map_clicked, [this](::glm::vec3 const& pos)
{
if (_world->mapIndex.hasAGlobalWMO()) // skip uid check
enterMapAt(pos, math::degrees(30.f), math::degrees(90.f), uid_fix_mode::none, false);
else
check_uid_then_enter_map(pos, math::degrees(30.f), math::degrees(90.f));
}
);
_right_side = new QTabWidget(this);
auto minimap_holder = new QScrollArea(this);
minimap_holder->setWidgetResizable(true);
minimap_holder->setAlignment(Qt::AlignCenter);
minimap_holder->setWidget(_minimap);
_right_side->addTab(minimap_holder, "Enter map");
minimap_holder->setAccessibleName("main_menu_minimap_holder");
_map_creation_wizard = new Noggit::Ui::Tools::MapCreationWizard::Ui::MapCreationWizard(_project, this);
_map_wizard_connection = connect(_map_creation_wizard,
&Noggit::Ui::Tools::MapCreationWizard::Ui::MapCreationWizard::map_dbc_updated, [=]
{
_buildMapListComponent->buildMapList(this);
}
);
_right_side->addTab(_map_creation_wizard, "Edit map");
layout->addWidget(_right_side);
connect(add_btn, &QPushButton::clicked
, [&]()
{
_right_side->setCurrentIndex(1);
_map_creation_wizard->addNewMap();
});
//setCentralWidget (_stack_widget);
_minimap->adjustSize();
}
void NoggitWindow::closeEvent(QCloseEvent* event)
{
if (map_loaded)
{
event->ignore();
promptExit(event);
} else
{
event->accept();
}
}
void NoggitWindow::handleEventMapListContextMenuPinMap(int mapId, std::string MapName)
{
_project->pinMap(mapId, MapName);
_buildMapListComponent->buildMapList(this);
}
void NoggitWindow::handleEventMapListContextMenuUnpinMap(int mapId)
{
_project->unpinMap(mapId);
_buildMapListComponent->buildMapList(this);
}
void NoggitWindow::promptExit(QCloseEvent* event)
{
emit exitPromptOpened();
QMessageBox prompt;
prompt.setModal(true);
prompt.setIcon(QMessageBox::Warning);
prompt.setText("Exit?");
prompt.setInformativeText("Any unsaved changes will be lost.");
prompt.addButton("Exit", QMessageBox::DestructiveRole);
prompt.addButton("Return to menu", QMessageBox::AcceptRole);
prompt.setDefaultButton(prompt.addButton("Cancel", QMessageBox::RejectRole));
prompt.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint);
prompt.exec();
switch (prompt.buttonRole(prompt.clickedButton()))
{
case QMessageBox::AcceptRole:
_stack_widget->setCurrentIndex(0);
_stack_widget->removeLast();
delete _map_view;
_map_view = nullptr;
_minimap->world(nullptr);
map_loaded = false;
break;
case QMessageBox::DestructiveRole:
Noggit::Ui::Tools::ViewportManager::ViewportManager::unloadAll();
setCentralWidget(_null_widget = new QWidget(this));
event->accept();
break;
default:
event->ignore();
break;
}
}
void NoggitWindow::promptUidFixFailure()
{
_stack_widget->setCurrentIndex(0);
QMessageBox::critical
(nullptr, "UID fix failed", "The UID fix couldn't be done because some models were missing or fucked up.\n"
"The models are listed in the log file.", QMessageBox::Ok
);
}
}