Add a recent files submenu.

master
poikilos 2021-02-18 09:31:03 -05:00
parent 98ad5d1457
commit 62d3620a16
8 changed files with 248 additions and 31 deletions

View File

@ -1,12 +1,12 @@
#include <string>
#include <filesystem>
// See https://stackoverflow.com/questions/22201663/find-and-move-files-in-c
#include "Engine.h"
#include "UserInterface.h"
#include "Utility.h"
#include "View.h"
// #include <filesystem>
// See https://stackoverflow.com/questions/22201663/find-and-move-files-in-c
#include <cerrno>
// _chdir (not chdir--see):
#include <unistd.h>
@ -70,6 +70,47 @@ void Engine::setEnableTextureInterpolation(bool EnableTextureInterpolation)
}
}
void Engine::addRecent(std::string path)
{
int count = this->countRecent();
std::string name = "recent" + std::to_string(count);
this->settings.set(name, path);
}
void Engine::addRecentPaths(std::vector<std::string> paths)
{
for (std::vector<std::string>::iterator it = paths.begin() ; it != paths.end(); ++it) {
this->addRecent(*it);
}
}
int Engine::countRecent()
{
int count = 0;
while (this->settings.exists("recent" + std::to_string(count))) {
count++;
}
return count;
}
std::vector<std::string> Engine::recentPaths()
{
std::vector<std::string> results;
int count = 0;
while (true) {
bool found;
std::string value = this->settings.get("recent" + std::to_string(count), found);
if (found) {
results.push_back(value);
count++;
}
else {
break;
}
}
return results;
}
void Engine::setupScene()
{
// Setup Light
@ -280,7 +321,29 @@ s32 Engine::getNumberOfVertices()
Engine::Engine()
{
settings.set_int("max_recent", 10);
// For monitoring single press: see
std::string profile = std::getenv("HOME");
std::string appdataParent;
std::string appdatas;
std::string myAppData;
if (profile.length() == 0) {
profile = std::getenv("USERPROFILE");
appdataParent = profile + path_separator_s + "AppData";
appdatas = appdataParent + path_separator_s + "Local";
}
else {
appdataParent = profile;
appdatas = appdataParent + path_separator_s + ".config";
}
if (appdatas.length() > 0) {
myAppData = appdatas + path_separator_s + std::string("b3view");
}
std::string settingsName = "settings.conf";
std::string settingsPath = settingsName;
if (myAppData.length() > 0) {
settingsPath = myAppData + path_separator_s + settingsName;
}
settings.load(settingsPath);
// For monitoring single press: See
// <http://irrlicht.sourceforge.net/forum/viewtopic.php?p=210744>
for (u32 i = 0; i < KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;
@ -827,3 +890,10 @@ void Engine::run()
m_Device->sleep(sleepTime, false);
}
}
bool Engine::loadScene(const std::wstring &fileName)
{
scene::ISceneManager* smgr = this->m_Device->getSceneManager();
bool result = smgr->loadScene(fileName.c_str());
return result;
}

View File

@ -79,6 +79,7 @@ public:
irr::core::vector3df camTarget();
void run();
bool loadScene(const std::wstring& fileName);
bool loadMesh(const std::wstring& fileName);
bool reloadMesh();
std::wstring saveMesh(const irr::io::path path, const std::string& nameOrBlank, const std::string& extension);
@ -99,6 +100,10 @@ public:
void setEnableWireframe(bool EnableWireframe);
void setEnableLighting(bool EnableLighting);
void setEnableTextureInterpolation(bool EnableTextureInterpolation);
void addRecent(std::string path);
void addRecentPaths(std::vector<std::string> paths);
int countRecent();
std::vector<std::string> recentPaths();
};
#endif // ENGINE_H

View File

@ -37,6 +37,9 @@ void UserInterface::setupUserInterface()
// File Menu
fileMenu = menu->getSubMenu(0);
fileMenu->addItem(L"Open", UIC_FILE_OPEN);
this->fileRecentIdx = fileMenu->addItem(L"Open Recent", UIC_FILE_RECENT, true, true);
std::vector<std::string> recentPaths = this->m_Engine->recentPaths();
this->addRecentPaths(recentPaths);
fileMenu->addItem(L"Change Texture", UIC_FILE_OPEN_TEXTURE);
fileMenu->addItem(L"Previous Texture Shift F3", UIC_FILE_PREVIOUS_TEXTURE);
fileMenu->addItem(L"Next Texture F3", UIC_FILE_NEXT_TEXTURE);
@ -47,6 +50,16 @@ void UserInterface::setupUserInterface()
fileMenu->addItem(L"Export STL (stereolithography)", UIC_FILE_EXPORT_STL);
fileMenu->addItem(L"Quit", UIC_FILE_QUIT);
// File, Open Recent submenu
this->recentMenu = fileMenu->getSubMenu(this->fileRecentIdx);
this->recentMenu->addItem(L"Clear Recent", UIC_FILE_RECENT_CLEAR);
this->uic_file_recent_first = UIC_FILE_RECENT_CLEAR + 1;
this->uic_file_recent_next = this->uic_file_recent_first;
this->m_file_recent_first_idx = -1;
this->m_file_recent_last_idx = -1;
// Playback Menu
playbackMenu = menu->getSubMenu(1);
playbackMenu->addItem(L"Previous Frame Left",
@ -319,6 +332,10 @@ void UserInterface::handleMenuItemPressed(IGUIContextMenu* menu)
displayLoadFileDialog();
break;
case UIC_FILE_RECENT_CLEAR:
clearRecent();
break;
case UIC_FILE_EXPORT_DAE:
exportMeshToHome("dae");
break;
@ -757,6 +774,62 @@ void UserInterface::exportMeshToHome(std::string extension)
}
}
void UserInterface::clearRecent()
{
// for (int idx=this->uic_file_recent_next-1; idx>=this->uic_file_recent_first; idx--) {
for (std::vector<u32>::iterator idxIt = this->recentIndices.begin(); idxIt != this->recentIndices.end(); ++idxIt) {
this->recentMenu->removeItem(*idxIt);
}
this->recentIndices.clear();
this->uic_file_recent_next = this->uic_file_recent_first;
this->m_file_recent_first_idx = -1;
this->m_file_recent_last_idx = -1;
}
void UserInterface::addRecent(std::string path)
{
if (!this->hasRecent(path)) {
u32 newI = this->recentMenu->addItem(Utility::toWstring(path).c_str(), this->uic_file_recent_next);
IGUIContextMenu* menu = this->recentMenu->getSubMenu(newI);
this->recentIndices.push_back(newI);
this->uic_file_recent_next += 1;
/*
if (this->m_file_recent_first_idx < 0) {
this->m_file_recent_first_idx = menu->getID(); // SIGSEGV crash
}
this->m_file_recent_last_idx = menu->getID();
*/
this->m_Engine->addRecent(path);
}
}
void UserInterface::addRecentPaths(std::vector<std::string> paths)
{
for (std::vector<std::string>::iterator it = paths.begin() ; it != paths.end(); ++it) {
this->addRecent(*it);
}
}
bool UserInterface::hasRecent(std::string path)
{
for (std::vector<u32>::iterator uiIt = this->recentIndices.begin() ; uiIt != this->recentIndices.end(); ++uiIt) {
IGUIContextMenu* menu = this->recentMenu->getSubMenu(*uiIt);
if (Utility::toString(menu->getText()) == path) {
return true;
}
}
return false;
}
void UserInterface::openRecent(s32 menuID, std::wstring menuText)
{
IGUIElement* menu = this->recentMenu->getElementFromId(menuID);
std::string path = Utility::toString(menu->getText());
cerr << "path: " << path << endl;
cerr << "menuID: " << menuID << endl;
cerr << "menuText: " << Utility::toString(menuText) << endl;
}
// IEventReceiver
bool UserInterface::OnEvent(const SEvent& event)
{
@ -775,14 +848,14 @@ bool UserInterface::OnEvent(const SEvent& event)
// debug() << "EET_GUI_EVENT..." << endl;
handled = true; // set to false below if not handled
const SEvent::SGUIEvent* ge = &(event.GUIEvent);
switch (ge->Caller->getID()) {
s32 callerID = ge->Caller->getID();
switch (callerID) {
case UIE_FILEMENU:
case UIE_PLAYBACKMENU:
case UIE_VIEWMENU:
// call handler for all menu related actions
handleMenuItemPressed(static_cast<IGUIContextMenu*>(ge->Caller));
break;
case UIE_LOADFILEDIALOG:
if (ge->EventType == EGET_FILE_SELECTED) {
IGUIFileOpenDialog* fileOpenDialog = static_cast<IGUIFileOpenDialog*>(ge->Caller);
@ -790,12 +863,13 @@ bool UserInterface::OnEvent(const SEvent& event)
bool result = false;
wstring extension = Utility::extensionOf(path);
if (Utility::toLower(Utility::toString(extension)) == "irr") {
scene::ISceneManager* smgr = m_Engine->m_Device->getSceneManager();
result = smgr->loadScene(fileOpenDialog->getFileName());
result = m_Engine->loadScene(fileOpenDialog->getFileName());
}
else {
result = m_Engine->loadMesh(fileOpenDialog->getFileName());
}
this->addRecent(Utility::toString(path));
if (!result) {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Load Mesh", L"The model is inaccessible or not in a compatible format.");
@ -806,7 +880,7 @@ bool UserInterface::OnEvent(const SEvent& event)
if (ge->EventType == EGET_FILE_SELECTED) {
if (m_Engine->m_LoadedMesh != nullptr) {
IGUIFileOpenDialog* fileOpenDialog = static_cast<IGUIFileOpenDialog*>(ge->Caller);
///fileOpenDialog->getFileName()
// fileOpenDialog->getFileName()
m_Engine->saveMesh(fileOpenDialog->getDirectoryName(), "", "dae");
}
else {
@ -893,10 +967,19 @@ bool UserInterface::OnEvent(const SEvent& event)
);
}
break;
case UIE_RECENTMENU:
break;
default:
// break;
handled = false;
// if ((ge->Caller->getID() >= this->m_file_recent_first_idx)
// && (ge->Caller->getID() <= m_file_recent_last_idx)) {
if (std::find(this->recentIndices.begin(), this->recentIndices.end(), ge->Caller->getID()) != this->recentIndices.end()) {
cerr << "Recent item id: " << callerID << endl;
this->openRecent(callerID, ge->Caller->getText());
}
else {
cerr << "Unknown caller id: " << callerID << endl;
handled = false;
}
}
} else if (event.EventType == EET_KEY_INPUT_EVENT) {
// debug() << "EET_KEY_INPUT_EVENT..." << endl;

View File

@ -12,10 +12,13 @@ class Engine;
enum UserInterfaceElements {
UIE_FILEMENU = 1003,
UIE_LOADFILEDIALOG = 1100,
UIE_RECENTMENU = 1100, // this whole range (1100-1198) must stay free for generated submenus
UIE_RECENTMENU_LAST = 1198,
UIE_RECENTMENU_CLEAR = 1199,
UIE_LOADFILEDIALOG = 1200,
// UIE_LOADBUTTON = 1101,
UIE_LOADTEXTUREDIALOG = 1200,
UIE_SAVEFILEDIALOG = 1300,
UIE_LOADTEXTUREDIALOG = 1300,
UIE_SAVEFILEDIALOG = 1400,
UIE_PLAYBACKMENU = 2000,
@ -39,15 +42,17 @@ enum UserInterfaceElements {
enum UserInterfaceCommands {
UIC_FILE_OPEN = 1000,
UIC_FILE_QUIT = 1001,
UIC_FILE_OPEN_TEXTURE = 1002,
UIC_FILE_NEXT_TEXTURE = 1003,
UIC_FILE_PREVIOUS_TEXTURE = 1004,
UIC_FILE_EXPORT_DAE = 1005,
UIC_FILE_EXPORT_IRR = 1006,
UIC_FILE_EXPORT_IRRMESH = 1007,
UIC_FILE_EXPORT_OBJ = 1008,
UIC_FILE_EXPORT_STL = 1009,
UIC_FILE_RECENT = 1100, // this whole range (1100-1198) must stay free for generated submenus
UIC_FILE_RECENT_CLEAR = 1199,
UIC_FILE_QUIT = 1002,
UIC_FILE_OPEN_TEXTURE = 1003,
UIC_FILE_NEXT_TEXTURE = 1004,
UIC_FILE_PREVIOUS_TEXTURE = 1005,
UIC_FILE_EXPORT_DAE = 1006,
UIC_FILE_EXPORT_IRR = 1007,
UIC_FILE_EXPORT_IRRMESH = 1008,
UIC_FILE_EXPORT_OBJ = 1009,
UIC_FILE_EXPORT_STL = 1010,
UIC_PLAYBACK_PREVIOUS = 2001,
UIC_PLAYBACK_NEXT = 2002,
UIC_PLAYBACK_SLOWER = 2003,
@ -64,6 +69,10 @@ enum UserInterfaceCommands {
class UserInterface : public irr::IEventReceiver {
private:
irr::s32 spacing_y;
irr::u32 uic_file_recent_first;
irr::u32 uic_file_recent_next;
irr::s32 m_file_recent_first_idx;
irr::s32 m_file_recent_last_idx;
Engine* m_Engine;
irr::gui::IGUIEnvironment* m_Gui;
irr::gui::CGUITTFont* m_GuiFont;
@ -84,6 +93,7 @@ private:
public:
irr::gui::IGUIContextMenu* menu;
irr::gui::IGUIContextMenu* fileMenu;
irr::gui::IGUIContextMenu* recentMenu;
irr::gui::IGUIContextMenu* playbackMenu;
irr::gui::IGUIContextMenu* viewMenu;
irr::gui::IGUIStaticText* playbackStartFrameStaticText;
@ -99,6 +109,8 @@ public:
irr::gui::IGUIEditBox* texturePathEditBox;
irr::gui::IGUIStaticText* axisSizeStaticText;
irr::gui::IGUIEditBox* axisSizeEditBox;
irr::u32 fileRecentIdx;
std::vector<irr::u32> recentIndices;
irr::u32 viewTextureInterpolationIdx;
irr::u32 viewWireframeIdx;
irr::u32 viewAxisWidgetIdx;
@ -115,6 +127,11 @@ public:
void drawStatusLine() const;
bool loadNextTexture(int direction);
void exportMeshToHome(std::string extension);
void clearRecent();
void addRecent(std::string path);
void addRecentPaths(std::vector<std::string> paths);
bool hasRecent(std::string path);
void openRecent(irr::s32 menuID, std::wstring menuText);
bool OnSelectMesh();
void setPlaybackText(irr::s32 id, const wchar_t* str);

View File

@ -471,18 +471,21 @@ std::string Utility::toString(irr::f32 val)
std::string Utility::ltrim(const std::string& s)
{
// based on https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
size_t start = s.find_first_not_of(Utility::WHITESPACE);
return (start == std::string::npos) ? "" : s.substr(start);
}
std::string Utility::rtrim(const std::string& s)
{
// based on https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
size_t end = s.find_last_not_of(Utility::WHITESPACE);
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
}
std::string Utility::trim(const std::string& s)
{
// based on https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
return rtrim(ltrim(s));
}
@ -500,6 +503,14 @@ TestUtility::TestUtility() {
testReplaceAll(L"***water_dragon***", L"***", L"", L"water_dragon");
testReplaceAll(L"***water_dragon***", L"", L"***", L"***water_dragon***"); // do nothing
testLTrim("pear ", "pear");
testLTrim(" pear ", "pear ");
testRTrim(" pear ", " pear");
testRTrim("pear ", "pear");
testTrim(" pear ", "pear");
testTrim("pear ", "pear");
testTrim(" pear", "pear");
testTrim("pear ", "pear");
std::cerr << "OK" << std::endl;
}
@ -511,7 +522,22 @@ void TestUtility::testReplaceAll(const std::string &subject, const std::string &
{
std::string result = Utility::replaceAll(subject, from, to);
this->assertEqual(result, expectedResult);
};
}
void TestUtility::testTrim(const std::string &subject, const std::string &expectedResult)
{
assertEqual(Utility::trim(subject), expectedResult);
}
void TestUtility::testLTrim(const std::string &subject, const std::string &expectedResult)
{
assertEqual(Utility::ltrim(subject), expectedResult);
}
void TestUtility::testRTrim(const std::string &subject, const std::string &expectedResult)
{
assertEqual(Utility::rtrim(subject), expectedResult);
}
void TestUtility::assertEqual(const wstring& subject, const wstring& expectedResult)
{

View File

@ -7,6 +7,19 @@
#include <string>
#include <vector>
const char path_separator_s =
#ifdef _WIN32
'\\';
#else
'/';
#endif
const wchar_t path_separator_ws =
#ifdef _WIN32
L'\\';
#else
L'/';
#endif
class Utility {
public:
static const std::string WHITESPACE;
@ -67,6 +80,9 @@ public:
void assertEqual(const std::string subject, const std::string expectedResult);
void testReplaceAll(const std::wstring& subject, const std::wstring& from, const std::wstring& to, const std::wstring& expectedResult);
void testReplaceAll(const std::string& subject, const std::string& from, const std::string& to, const std::string& expectedResult);
void testTrim(const std::string& subject, const std::string &expectedResult);
void testRTrim(const std::string& subject, const std::string &expectedResult);
void testLTrim(const std::string& subject, const std::string &expectedResult);
};
#endif // UTILS_H

View File

@ -15,10 +15,10 @@ void Settings::init_default_symbols() {
this->init(" = ", "# ");
}
void Settings::init(std::string assignmentOperator, std::string commentMark)
void Settings::init(std::string assignmentOperatorAndSpacing, std::string commentMarkAndSpacing)
{
this->ao_and_spacing = assignmentOperator;
this->cm_and_spacing = commentMark;
this->ao_and_spacing = assignmentOperatorAndSpacing;
this->cm_and_spacing = commentMarkAndSpacing;
this->enable_autosave = true;
}
@ -52,13 +52,13 @@ bool Settings::load(std::string path)
this->path = path;
fstream newfile;
this->pre = "";
newfile.open(path,ios::in);
std::string ao = this->ao_trimmed();
std::string cm = this->cm_trimmed();
if (newfile.is_open()){
newfile.open(path, ios::in);
if (newfile.is_open()) {
std::string line;
int lineN = 0; // Set this to 1 before use.
while(getline(newfile, line)) {
while (getline(newfile, line)) {
lineN += 1;
this->pre = this->path + ":" + std::to_string(lineN) + ": "; // must end with space for outputinspector
line = Utility::trim(line);

View File

@ -16,7 +16,7 @@ private:
std::string ao_and_spacing; // assignment operator
std::string cm_and_spacing; // comment mark
std::string pre; // debug prefix such as "filename:lineNumber: " (must end with space for outputinspector)
void init(std::string assignmentOperator, std::string commentMark);
void init(std::string assignmentOperatorAndSpacing, std::string commentMarkAndSpacing);
void init_default_symbols();
public: