diff --git a/common/lc_application.cpp b/common/lc_application.cpp index 4ece3db3..d891a9b3 100644 --- a/common/lc_application.cpp +++ b/common/lc_application.cpp @@ -375,10 +375,6 @@ bool lcApplication::Initialize(int argc, char* argv[], const char* LibraryInstal { return false; } - else - { - mProject->NewModel(); - } lcLoadDefaultKeyboardShortcuts(); diff --git a/common/lc_commands.cpp b/common/lc_commands.cpp index 6a880b5c..68fe3041 100644 --- a/common/lc_commands.cpp +++ b/common/lc_commands.cpp @@ -1127,7 +1127,7 @@ lcCommand gCommands[LC_NUM_COMMANDS] = // LC_MODEL_LIST { "Model.List", - QT_TRANSLATE_NOOP("Menu", "List..."), + QT_TRANSLATE_NOOP("Menu", "Models..."), QT_TRANSLATE_NOOP("Status", "Show a list of all models in this project"), QT_TRANSLATE_NOOP("Shortcut", "") }, diff --git a/common/lc_mainwindow.cpp b/common/lc_mainwindow.cpp index 8b3ecc79..c12862d8 100644 --- a/common/lc_mainwindow.cpp +++ b/common/lc_mainwindow.cpp @@ -163,7 +163,6 @@ void lcMainWindow::NewProject() return; Project* NewProject = new Project(); - NewProject->NewModel(); g_App->SetProject(NewProject); } @@ -829,6 +828,33 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) UpdateAllViews(); } break; */ + case LC_MODEL_NEW: + lcGetActiveProject()->CreateNewModel(); + break; + + case LC_MODEL_LIST: + lcGetActiveProject()->ShowModelListDialog(); + break; + + case LC_MODEL_01: + case LC_MODEL_02: + case LC_MODEL_03: + case LC_MODEL_04: + case LC_MODEL_05: + case LC_MODEL_06: + case LC_MODEL_07: + case LC_MODEL_08: + case LC_MODEL_09: + case LC_MODEL_10: + case LC_MODEL_11: + case LC_MODEL_12: + case LC_MODEL_13: + case LC_MODEL_14: + case LC_MODEL_15: + case LC_MODEL_16: + lcGetActiveProject()->SetActiveModel(CommandId - LC_MODEL_01); + break; + case LC_HELP_HOMEPAGE: g_App->OpenURL("http://www.leocad.org/"); break; diff --git a/common/lc_model.h b/common/lc_model.h index b9362fb1..69fbd942 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -143,6 +143,11 @@ public: return mProperties; } + void SetName(const QString& Name) + { + mProperties.mName = Name; + } + lcStep GetLastStep() const; lcStep GetCurrentStep() const diff --git a/common/project.cpp b/common/project.cpp index da98f62d..27b4c43e 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -17,11 +17,13 @@ #include "lc_application.h" #include "lc_profile.h" #include "preview.h" +#include "lc_qmodellistdialog.h" Project::Project() { mModified = false; - mActiveModel = NULL; + mActiveModel = new lcModel(tr("Model #1")); + mModels.Add(mActiveModel); } Project::~Project() @@ -29,11 +31,148 @@ Project::~Project() mModels.DeleteAll(); } -void Project::NewModel() +bool Project::IsModified() const { - //todo: add dialog to get the model name and check if the name is unique - mActiveModel = new lcModel(tr("Model%1").arg(QString::number(mModels.GetSize() + 1))); - mModels.Add(mActiveModel); + if (mModified) + return true; + + for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++) + if (mModels[ModelIdx]->IsModified()) + return true; + + return false; +} + +QString Project::GetTitle() const +{ + return mFileName.isEmpty() ? tr("New Project.ldr") : QFileInfo(mFileName).fileName(); +} + +void Project::SetActiveModel(int ModelIndex) +{ + if (ModelIndex < 0 || ModelIndex >= mModels.GetSize()) + return; + + mActiveModel = mModels[ModelIndex]; + mActiveModel->UpdateInterface(); + gMainWindow->UpdateModels(); + + const lcArray& Views = gMainWindow->GetViews(); + for (int ViewIdx = 0; ViewIdx < Views.GetSize(); ViewIdx++) + Views[ViewIdx]->SetModel(lcGetActiveModel()); +} + +bool Project::IsModelNameValid(const QString& Name) const +{ + if (Name.isEmpty()) + return false; + + for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++) + if (mModels[ModelIdx]->GetProperties().mName == Name) + return false; + + return true; +} + +void Project::CreateNewModel() +{ + const QString Prefix = tr("Model #"); + int Max = 0; + + for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++) + { + QString Name = mModels[ModelIdx]->GetProperties().mName; + + if (Name.startsWith(Prefix)) + { + QString NumberString = Name.mid(Prefix.length()); + QTextStream Stream(&NumberString); + int Number; + Stream >> Number; + Max = qMax(Max, Number); + } + } + + QString Name = Prefix + QString::number(Max + 1); + + for (;;) + { + bool Ok = false; + + Name = QInputDialog::getText(gMainWindow->mHandle, tr("New Model"), tr("Name:"), QLineEdit::Normal, Name, &Ok); + + if (!Ok) + return; + + if (IsModelNameValid(Name)) + break; + + if (Name.isEmpty()) + QMessageBox::information(gMainWindow->mHandle, tr("Empty Name"), tr("The model name cannot be empty.")); + else + QMessageBox::information(gMainWindow->mHandle, tr("Duplicate Model"), tr("A model named '%1' already exists in this project, please enter an unique name.").arg(Name)); + } + + if (!Name.isEmpty()) + { + mModified = true; + mModels.Add(new lcModel(Name)); + SetActiveModel(mModels.GetSize() - 1); + gMainWindow->UpdateTitle(); + } +} + +void Project::ShowModelListDialog() +{ + QList> Models; + Models.reserve(mModels.GetSize()); + + for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++) + { + lcModel* Model = mModels[ModelIdx]; + Models.append(QPair(Model->GetProperties().mName, Model)); + } + + lcQModelListDialog Dialog(gMainWindow->mHandle, Models); + + if (Dialog.exec() != QDialog::Accepted || Models.isEmpty()) + return; + + lcArray NewModels; + + for (QList>::iterator it = Models.begin(); it != Models.end(); it++) + { + lcModel* Model = it->second; + + if (!Model) + { + Model = new lcModel(it->first); + mModified = true; + } + else if (Model->GetProperties().mName != it->first) + { + Model->SetName(it->first); + mModified = true; + } + + NewModels.Add(Model); + } + + for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++) + { + lcModel* Model = mModels[ModelIdx]; + + if (NewModels.FindIndex(Model) == -1) + { + delete Model; + mModified = true; + } + } + + mModels = NewModels; + + SetActiveModel(Dialog.mActiveModel); + gMainWindow->UpdateTitle(); } bool Project::Load(const QString& FileName) @@ -46,6 +185,7 @@ bool Project::Load(const QString& FileName) return false; } + mModels.DeleteAll(); QString Extension = QFileInfo(FileName).suffix().toLower(); if (Extension == QLatin1String("dat") || Extension == QLatin1String("ldr") || Extension == QLatin1String("mpd")) @@ -75,6 +215,8 @@ bool Project::Load(const QString& FileName) delete Model; } + // todo: validate model names + if (mModels.IsEmpty()) return false; @@ -132,6 +274,7 @@ bool Project::Load(const QString& FileName) */ mFileName = FileName; + mModified = false; return true; } @@ -169,17 +312,6 @@ bool Project::Save(const QString& FileName) return true; } -bool Project::IsModified() const -{ - if (mModified) - return true; - - for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++) - if (mModels[ModelIdx]->IsModified()) - return true; - - return false; -} /* void Project::LoadDefaults() // todo: Change the interface in SetProject() instead { @@ -205,10 +337,6 @@ void Project::LoadDefaults() // todo: Change the interface in SetProject() inste gMainWindow->UpdateFocusObject(NULL); } */ -QString Project::GetTitle() const -{ - return mFileName.isEmpty() ? tr("New Project.ldr") : QFileInfo(mFileName).fileName(); -} void Project::SaveImage() { diff --git a/common/project.h b/common/project.h index 502f3a96..8cb10d87 100644 --- a/common/project.h +++ b/common/project.h @@ -2,11 +2,7 @@ #define _PROJECT_H_ #include "object.h" -#include "opengl.h" #include "lc_array.h" -#include "lc_math.h" -#include "lc_commands.h" -#include "str.h" #include "lc_application.h" #define LC_SCENE_FOG 0x004 // Enable fog @@ -50,18 +46,22 @@ public: return mActiveModel; } + bool IsModified() const; QString GetTitle() const; + QString GetFileName() const { return mFileName; } - void NewModel(); + void SetActiveModel(int ModelIndex); + bool IsModelNameValid(const QString& Name) const; + + void CreateNewModel(); + void ShowModelListDialog(); bool Load(const QString& FileName); bool Save(const QString& FileName); - bool IsModified() const; - protected: bool mModified; QString mFileName; diff --git a/leocad.pro b/leocad.pro index 9fa68826..823f8195 100644 --- a/leocad.pro +++ b/leocad.pro @@ -160,7 +160,8 @@ SOURCES += common/view.cpp \ qt/lc_qimage.cpp \ qt/lc_qglwidget.cpp \ qt/lc_qcolorlist.cpp \ - qt/lc_qfinddialog.cpp + qt/lc_qfinddialog.cpp \ + qt/lc_qmodellistdialog.cpp HEADERS += \ common/view.h \ common/tr.h \ @@ -219,7 +220,8 @@ HEADERS += \ qt/lc_qcolorpicker.h \ qt/lc_qglwidget.h \ qt/lc_qcolorlist.h \ - qt/lc_qfinddialog.h + qt/lc_qfinddialog.h \ + qt/lc_qmodellistdialog.h FORMS += \ qt/lc_qpovraydialog.ui \ qt/lc_qarraydialog.ui \ @@ -234,7 +236,8 @@ FORMS += \ qt/lc_qcategorydialog.ui \ qt/lc_qimagedialog.ui \ qt/lc_qupdatedialog.ui \ - qt/lc_qfinddialog.ui + qt/lc_qfinddialog.ui \ + qt/lc_qmodellistdialog.ui OTHER_FILES += RESOURCES += leocad.qrc diff --git a/qt/lc_qmodellistdialog.cpp b/qt/lc_qmodellistdialog.cpp new file mode 100644 index 00000000..bc1c9c4e --- /dev/null +++ b/qt/lc_qmodellistdialog.cpp @@ -0,0 +1,147 @@ +#include "lc_qmodellistdialog.h" +#include "ui_lc_qmodellistdialog.h" +#include "project.h" + +lcQModelListDialog::lcQModelListDialog(QWidget* Parent, QList>& Models) + : QDialog(Parent), mModels(Models), ui(new Ui::lcQModelListDialog) +{ + ui->setupUi(this); + + for (QList>::iterator it = Models.begin(); it != Models.end(); it++) + { + QListWidgetItem* Item = new QListWidgetItem(it->first); + Item->setData(Qt::UserRole, qVariantFromValue((uintptr_t)it->second)); + ui->ModelList->addItem(Item); + } + ui->ModelList->setCurrentRow(0); +} + +lcQModelListDialog::~lcQModelListDialog() +{ + delete ui; +} + +void lcQModelListDialog::accept() +{ + mActiveModel = ui->ModelList->currentRow(); + if (mActiveModel < 0) + mActiveModel = 0; + + QDialog::accept(); +} + +void lcQModelListDialog::on_NewModel_clicked() +{ + const QString Prefix = tr("Model #"); + int Max = 0; + + for (int ItemIdx = 0; ItemIdx < ui->ModelList->count(); ItemIdx++) + { + QString Name = ui->ModelList->item(ItemIdx)->text(); + + if (Name.startsWith(Prefix)) + { + QString NumberString = Name.mid(Prefix.length()); + QTextStream Stream(&NumberString); + int Number; + Stream >> Number; + Max = qMax(Max, Number); + } + } + + QString Name = Prefix + QString::number(Max + 1); + + if (Name.isEmpty()) + return; + + QListWidgetItem* Item = new QListWidgetItem(Name); + Item->setData(Qt::UserRole, qVariantFromValue(0)); + ui->ModelList->addItem(Item); +} + +void lcQModelListDialog::on_DeleteModel_clicked() +{ + if (ui->ModelList->count() == 1) + { + QMessageBox::information(this, tr("Error"), tr("The project must have at least 1 model.")); + return; + } + + QList SelectedItems = ui->ModelList->selectedItems(); + + if (SelectedItems.isEmpty()) + return; + + QString Prompt = tr("Are you sure you want to delete the model '%1'?").arg(SelectedItems[0]->text()); + if (QMessageBox::question(this, tr("Delete Model"), Prompt, QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return; + + delete SelectedItems[0]; +} + +void lcQModelListDialog::on_RenameModel_clicked() +{ + QList SelectedItems = ui->ModelList->selectedItems(); + + if (SelectedItems.isEmpty()) + return; + + QString Name = SelectedItems[0]->text(); + bool Ok = false; + + for (;;) + { + Name = QInputDialog::getText(this, tr("Rename Model"), tr("Name:"), QLineEdit::Normal, Name, &Ok); + + if (!Ok) + return; + + if (Name.isEmpty()) + { + QMessageBox::information(this, tr("Empty Name"), tr("The model name cannot be empty.")); + continue; + } + + QList MatchedItems = ui->ModelList->findItems(Name, Qt::MatchFixedString); + + if (MatchedItems.isEmpty() || (MatchedItems.size() == 1 && MatchedItems[0] == SelectedItems[0])) + break; + + QMessageBox::information(this, tr("Duplicate Model"), tr("A model named '%1' already exists in this project, please enter an unique name.").arg(Name)); + } + + SelectedItems[0]->setText(Name); +} + +void lcQModelListDialog::on_MoveUp_clicked() +{ + QList SelectedItems = ui->ModelList->selectedItems(); + + if (SelectedItems.isEmpty()) + return; + + QListWidgetItem* Item = SelectedItems[0]; + int Row = ui->ModelList->row(Item); + + if (Row == 0) + return; + + ui->ModelList->takeItem(Row); + ui->ModelList->insertItem(Row - 1, Item); + ui->ModelList->setCurrentItem(Item); +} + +void lcQModelListDialog::on_MoveDown_clicked() +{ + QList SelectedItems = ui->ModelList->selectedItems(); + + if (SelectedItems.isEmpty()) + return; + + QListWidgetItem* Item = SelectedItems[0]; + int Row = ui->ModelList->row(Item); + + ui->ModelList->takeItem(Row); + ui->ModelList->insertItem(Row + 1, Item); + ui->ModelList->setCurrentItem(Item); +} diff --git a/qt/lc_qmodellistdialog.h b/qt/lc_qmodellistdialog.h new file mode 100644 index 00000000..aea28395 --- /dev/null +++ b/qt/lc_qmodellistdialog.h @@ -0,0 +1,33 @@ +#ifndef _LC_QMODELLISTDIALOG_H_ +#define _LC_QMODELLISTDIALOG_H_ + +#include + +namespace Ui { +class lcQModelListDialog; +} + +class lcQModelListDialog : public QDialog +{ + Q_OBJECT + +public: + lcQModelListDialog(QWidget* Parent, QList>& Models); + ~lcQModelListDialog(); + + int mActiveModel; + QList>& mModels; + +public slots: + void accept(); + void on_NewModel_clicked(); + void on_DeleteModel_clicked(); + void on_RenameModel_clicked(); + void on_MoveUp_clicked(); + void on_MoveDown_clicked(); + +private: + Ui::lcQModelListDialog* ui; +}; + +#endif // _LC_QMODELLISTDIALOG_H_ diff --git a/qt/lc_qmodellistdialog.ui b/qt/lc_qmodellistdialog.ui new file mode 100644 index 00000000..cc7834e2 --- /dev/null +++ b/qt/lc_qmodellistdialog.ui @@ -0,0 +1,142 @@ + + + lcQModelListDialog + + + + 0 + 0 + 400 + 300 + + + + Models + + + + + + + + + + + + + New... + + + + + + + Delete... + + + + + + + Rename... + + + + + + + Move Up + + + + + + + Move Down + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QFrame::HLine + + + QFrame::Raised + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + ModelList + NewModel + DeleteModel + RenameModel + MoveUp + MoveDown + buttonBox + + + + + buttonBox + accepted() + lcQModelListDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + lcQModelListDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +