| 1 /* |
|
| 2 * LDForge: LDraw parts authoring CAD |
|
| 3 * Copyright (C) 2013 Santeri Piippo |
|
| 4 * |
|
| 5 * This program is free software: you can redistribute it and/or modify |
|
| 6 * it under the terms of the GNU General Public License as published by |
|
| 7 * the Free Software Foundation, either version 3 of the License, or |
|
| 8 * (at your option) any later version. |
|
| 9 * |
|
| 10 * This program is distributed in the hope that it will be useful, |
|
| 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 13 * GNU General Public License for more details. |
|
| 14 * |
|
| 15 * You should have received a copy of the GNU General Public License |
|
| 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
| 17 */ |
|
| 18 |
|
| 19 #include <qgridlayout.h> |
|
| 20 #include <qradiobutton.h> |
|
| 21 #include <qcheckbox.h> |
|
| 22 #include "gui.h" |
|
| 23 #include "zz_addObjectDialog.h" |
|
| 24 #include "file.h" |
|
| 25 #include "colors.h" |
|
| 26 #include "zz_colorSelectDialog.h" |
|
| 27 #include "history.h" |
|
| 28 #include "zz_setContentsDialog.h" |
|
| 29 |
|
| 30 #define APPLY_COORDS(OBJ, N) \ |
|
| 31 for (short i = 0; i < N; ++i) \ |
|
| 32 for (const Axis ax : g_Axes) \ |
|
| 33 OBJ->vaCoords[i][ax] = dlg.dsb_coords[(i * 3) + ax]->value (); |
|
| 34 |
|
| 35 // ============================================================================= |
|
| 36 class SubfileListItem : public QTreeWidgetItem { |
|
| 37 public: |
|
| 38 SubfileListItem (QTreeWidgetItem* parent, int subfileID) : |
|
| 39 QTreeWidgetItem (parent), subfileID (subfileID) {} |
|
| 40 SubfileListItem (QTreeWidget* parent, int subfileID) : |
|
| 41 QTreeWidgetItem (parent), subfileID (subfileID) {} |
|
| 42 |
|
| 43 int subfileID; |
|
| 44 }; |
|
| 45 |
|
| 46 // ============================================================================= |
|
| 47 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 48 // ============================================================================= |
|
| 49 AddObjectDialog::AddObjectDialog (const LDObject::Type type, LDObject* obj, QWidget* parent) : |
|
| 50 QDialog (parent) |
|
| 51 { |
|
| 52 setlocale (LC_ALL, "C"); |
|
| 53 |
|
| 54 short coordCount = 0; |
|
| 55 |
|
| 56 switch (type) { |
|
| 57 case LDObject::Comment: |
|
| 58 le_comment = new QLineEdit; |
|
| 59 if (obj) |
|
| 60 le_comment->setText (static_cast<LDComment*> (obj)->text); |
|
| 61 break; |
|
| 62 |
|
| 63 case LDObject::Line: |
|
| 64 coordCount = 6; |
|
| 65 break; |
|
| 66 |
|
| 67 case LDObject::Triangle: |
|
| 68 coordCount = 9; |
|
| 69 break; |
|
| 70 |
|
| 71 case LDObject::Quad: |
|
| 72 case LDObject::CondLine: |
|
| 73 coordCount = 12; |
|
| 74 break; |
|
| 75 |
|
| 76 case LDObject::Vertex: |
|
| 77 coordCount = 3; |
|
| 78 break; |
|
| 79 |
|
| 80 case LDObject::BFC: |
|
| 81 rb_bfcType = new RadioBox ("Statement", {}, 0, Qt::Vertical); |
|
| 82 |
|
| 83 for (int i = 0; i < LDBFC::NumStatements; ++i) |
|
| 84 rb_bfcType->addButton (new QRadioButton (LDBFC::statements[i])); |
|
| 85 |
|
| 86 if (obj) |
|
| 87 rb_bfcType->setValue ((int) static_cast<LDBFC*> (obj)->type); |
|
| 88 break; |
|
| 89 |
|
| 90 case LDObject::Subfile: |
|
| 91 coordCount = 3; |
|
| 92 |
|
| 93 enum { |
|
| 94 Parts, |
|
| 95 Subparts, |
|
| 96 Primitives, |
|
| 97 HiRes, |
|
| 98 }; |
|
| 99 |
|
| 100 tw_subfileList = new QTreeWidget (); |
|
| 101 for (int i : vector<int> ({Parts, Subparts, Primitives, HiRes})) { |
|
| 102 SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, -1); |
|
| 103 parentItem->setText (0, (i == Parts) ? "Parts" : |
|
| 104 (i == Subparts) ? "Subparts" : |
|
| 105 (i == Primitives) ? "Primitives" : |
|
| 106 "Hi-Res"); |
|
| 107 |
|
| 108 ulong j = 0; |
|
| 109 for (partListEntry& part : g_PartList) { |
|
| 110 QList<QTreeWidgetItem*> subfileItems; |
|
| 111 |
|
| 112 str fileName = part.sName; |
|
| 113 const bool isSubpart = fileName.substr (0, 2) == "s\\"; |
|
| 114 const bool isPrimitive = str (part.sTitle).substr (0, 9) == "Primitive"; |
|
| 115 const bool isHiRes = fileName.substr (0, 3) == "48\\"; |
|
| 116 |
|
| 117 if ((i == Subparts && isSubpart) || |
|
| 118 (i == Primitives && isPrimitive) || |
|
| 119 (i == HiRes && isHiRes) || |
|
| 120 (i == Parts && !isSubpart && !isPrimitive && !isHiRes)) |
|
| 121 { |
|
| 122 SubfileListItem* item = new SubfileListItem (parentItem, j); |
|
| 123 item->setText (0, fmt ("%s - %s", part.sName, part.sTitle)); |
|
| 124 subfileItems.append (item); |
|
| 125 } |
|
| 126 |
|
| 127 j++; |
|
| 128 } |
|
| 129 |
|
| 130 tw_subfileList->addTopLevelItem (parentItem); |
|
| 131 } |
|
| 132 |
|
| 133 connect (tw_subfileList, SIGNAL (itemSelectionChanged ()), this, SLOT (slot_subfileTypeChanged ())); |
|
| 134 lb_subfileName = new QLabel ("File:"); |
|
| 135 le_subfileName = new QLineEdit; |
|
| 136 le_subfileName->setFocus (); |
|
| 137 |
|
| 138 if (obj) { |
|
| 139 LDSubfile* ref = static_cast<LDSubfile*> (obj); |
|
| 140 le_subfileName->setText (ref->zFileName); |
|
| 141 } |
|
| 142 break; |
|
| 143 |
|
| 144 case LDObject::Radial: |
|
| 145 coordCount = 3; |
|
| 146 |
|
| 147 lb_radType = new QLabel ("Type:"); |
|
| 148 lb_radResolution = new QLabel ("Resolution:"); |
|
| 149 lb_radSegments = new QLabel ("Segments:"); |
|
| 150 lb_radRingNum = new QLabel ("Ring number:"); |
|
| 151 |
|
| 152 rb_radType = new RadioBox ("Type", {}, 0, Qt::Vertical); |
|
| 153 |
|
| 154 for (int i = 0; i < LDRadial::NumTypes; ++i) { |
|
| 155 if (i % (LDRadial::NumTypes / 2) == 0) |
|
| 156 rb_radType->rowBreak (); |
|
| 157 |
|
| 158 rb_radType->addButton (new QRadioButton (LDRadial::radialTypeName ((LDRadial::Type) i))); |
|
| 159 } |
|
| 160 |
|
| 161 connect (rb_radType, SIGNAL (sig_buttonPressed (int)), this, SLOT (slot_radialTypeChanged (int))); |
|
| 162 |
|
| 163 cb_radHiRes = new QCheckBox ("Hi-Res"); |
|
| 164 |
|
| 165 sb_radSegments = new QSpinBox; |
|
| 166 sb_radSegments->setMinimum (1); |
|
| 167 |
|
| 168 sb_radRingNum = new QSpinBox; |
|
| 169 sb_radRingNum->setEnabled (false); |
|
| 170 |
|
| 171 if (obj) { |
|
| 172 LDRadial* rad = static_cast<LDRadial*> (obj); |
|
| 173 |
|
| 174 rb_radType->setValue (rad->eRadialType); |
|
| 175 sb_radSegments->setValue (rad->dSegments); |
|
| 176 cb_radHiRes->setChecked ((rad->dDivisions == 48) ? Qt::Checked : Qt::Unchecked); |
|
| 177 sb_radRingNum->setValue (rad->dRingNum); |
|
| 178 } |
|
| 179 break; |
|
| 180 |
|
| 181 default: |
|
| 182 assert (false); |
|
| 183 return; |
|
| 184 } |
|
| 185 |
|
| 186 QPixmap icon = getIcon (fmt ("add-%s", g_saObjTypeIcons[type])); |
|
| 187 LDObject* defaults = LDObject::getDefault (type); |
|
| 188 |
|
| 189 lb_typeIcon = new QLabel; |
|
| 190 lb_typeIcon->setPixmap (icon); |
|
| 191 |
|
| 192 // Show a color edit dialog for the types that actually use the color |
|
| 193 if (defaults->isColored ()) { |
|
| 194 if (obj != null) |
|
| 195 dColor = obj->dColor; |
|
| 196 else |
|
| 197 dColor = (type == LDObject::CondLine || type == LDObject::Line) ? edgecolor : maincolor; |
|
| 198 |
|
| 199 pb_color = new QPushButton; |
|
| 200 setButtonBackground (pb_color, dColor); |
|
| 201 connect (pb_color, SIGNAL (clicked ()), this, SLOT (slot_colorButtonClicked ())); |
|
| 202 } |
|
| 203 |
|
| 204 for (short i = 0; i < coordCount; ++i) { |
|
| 205 dsb_coords[i] = new QDoubleSpinBox; |
|
| 206 dsb_coords[i]->setDecimals (5); |
|
| 207 dsb_coords[i]->setMinimum (-10000.0); |
|
| 208 dsb_coords[i]->setMaximum (10000.0); |
|
| 209 } |
|
| 210 |
|
| 211 IMPLEMENT_DIALOG_BUTTONS |
|
| 212 |
|
| 213 QGridLayout* const layout = new QGridLayout; |
|
| 214 layout->addWidget (lb_typeIcon, 0, 0); |
|
| 215 |
|
| 216 switch (type) { |
|
| 217 case LDObject::Line: |
|
| 218 case LDObject::CondLine: |
|
| 219 case LDObject::Triangle: |
|
| 220 case LDObject::Quad: |
|
| 221 // Apply coordinates |
|
| 222 if (obj) { |
|
| 223 for (short i = 0; i < coordCount / 3; ++i) |
|
| 224 for (short j = 0; j < 3; ++j) |
|
| 225 dsb_coords[(i * 3) + j]->setValue (obj->vaCoords[i].coord (j)); |
|
| 226 } |
|
| 227 break; |
|
| 228 |
|
| 229 case LDObject::Comment: |
|
| 230 layout->addWidget (le_comment, 0, 1); |
|
| 231 break; |
|
| 232 |
|
| 233 case LDObject::BFC: |
|
| 234 layout->addWidget (rb_bfcType, 0, 1); |
|
| 235 break; |
|
| 236 |
|
| 237 case LDObject::Radial: |
|
| 238 layout->addWidget (rb_radType, 1, 1, 3, 2); |
|
| 239 layout->addWidget (cb_radHiRes, 1, 3); |
|
| 240 layout->addWidget (lb_radSegments, 2, 3); |
|
| 241 layout->addWidget (sb_radSegments, 2, 4); |
|
| 242 layout->addWidget (lb_radRingNum, 3, 3); |
|
| 243 layout->addWidget (sb_radRingNum, 3, 4); |
|
| 244 |
|
| 245 if (obj) |
|
| 246 for (short i = 0; i < 3; ++i) |
|
| 247 dsb_coords[i]->setValue (static_cast<LDRadial*> (obj)->vPosition.coord (i)); |
|
| 248 break; |
|
| 249 |
|
| 250 case LDObject::Subfile: |
|
| 251 layout->addWidget (tw_subfileList, 1, 1, 1, 2); |
|
| 252 layout->addWidget (lb_subfileName, 2, 1); |
|
| 253 layout->addWidget (le_subfileName, 2, 2); |
|
| 254 |
|
| 255 if (obj) |
|
| 256 for (short i = 0; i < 3; ++i) |
|
| 257 dsb_coords[i]->setValue (static_cast<LDSubfile*> (obj)->vPosition.coord (i)); |
|
| 258 break; |
|
| 259 |
|
| 260 default: |
|
| 261 break; |
|
| 262 } |
|
| 263 |
|
| 264 if (type == LDObject::Subfile || type == LDObject::Radial) { |
|
| 265 QLabel* lb_matrix = new QLabel ("Matrix:"); |
|
| 266 le_matrix = new QLineEdit; |
|
| 267 // le_matrix->setValidator (new QDoubleValidator); |
|
| 268 matrix<3> defval = g_identity; |
|
| 269 |
|
| 270 if (obj) { |
|
| 271 if (obj->getType () == LDObject::Subfile) |
|
| 272 defval = static_cast<LDSubfile*> (obj)->mMatrix; |
|
| 273 else |
|
| 274 defval = static_cast<LDRadial*> (obj)->mMatrix; |
|
| 275 } |
|
| 276 |
|
| 277 le_matrix->setText (defval.stringRep ()); |
|
| 278 layout->addWidget (lb_matrix, 4, 1); |
|
| 279 layout->addWidget (le_matrix, 4, 2, 1, 3); |
|
| 280 } |
|
| 281 |
|
| 282 if (defaults->isColored ()) |
|
| 283 layout->addWidget (pb_color, 1, 0); |
|
| 284 |
|
| 285 if (coordCount > 0) { |
|
| 286 QGridLayout* const qCoordLayout = new QGridLayout; |
|
| 287 |
|
| 288 for (short i = 0; i < coordCount; ++i) |
|
| 289 qCoordLayout->addWidget (dsb_coords[i], (i / 3), (i % 3)); |
|
| 290 |
|
| 291 layout->addLayout (qCoordLayout, 0, 1, (coordCount / 3), 3); |
|
| 292 } |
|
| 293 |
|
| 294 layout->addWidget (bbx_buttons, 5, 0, 1, 4); |
|
| 295 setLayout (layout); |
|
| 296 setWindowTitle (fmt (APPNAME ": New %s", |
|
| 297 g_saObjTypeNames[type]).chars()); |
|
| 298 |
|
| 299 setWindowIcon (icon); |
|
| 300 delete defaults; |
|
| 301 } |
|
| 302 |
|
| 303 // ============================================================================= |
|
| 304 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 305 // ============================================================================= |
|
| 306 void AddObjectDialog::setButtonBackground (QPushButton* button, short color) { |
|
| 307 button->setIcon (getIcon ("palette")); |
|
| 308 button->setAutoFillBackground (true); |
|
| 309 button->setStyleSheet ( |
|
| 310 fmt ("background-color: %s", getColor (color)->zColorString.chars()).chars() |
|
| 311 ); |
|
| 312 } |
|
| 313 |
|
| 314 // ============================================================================= |
|
| 315 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 316 // ============================================================================= |
|
| 317 char* AddObjectDialog::currentSubfileName() { |
|
| 318 SubfileListItem* item = static_cast<SubfileListItem*> (tw_subfileList->currentItem ()); |
|
| 319 |
|
| 320 if (item->subfileID == -1) |
|
| 321 return null; // selected a heading |
|
| 322 |
|
| 323 return g_PartList[item->subfileID].sName; |
|
| 324 } |
|
| 325 |
|
| 326 // ============================================================================= |
|
| 327 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 328 // ============================================================================= |
|
| 329 void AddObjectDialog::slot_colorButtonClicked () { |
|
| 330 ColorSelectDialog::staticDialog (dColor, dColor, this); |
|
| 331 setButtonBackground (pb_color, dColor); |
|
| 332 } |
|
| 333 |
|
| 334 // ============================================================================= |
|
| 335 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 336 // ============================================================================= |
|
| 337 void AddObjectDialog::slot_radialTypeChanged (int dType) { |
|
| 338 LDRadial::Type eType = (LDRadial::Type) dType; |
|
| 339 sb_radRingNum->setEnabled (eType == LDRadial::Ring || eType == LDRadial::Cone); |
|
| 340 } |
|
| 341 |
|
| 342 // ============================================================================= |
|
| 343 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 344 // ============================================================================= |
|
| 345 void AddObjectDialog::slot_subfileTypeChanged () { |
|
| 346 char* name = currentSubfileName (); |
|
| 347 |
|
| 348 if (name) |
|
| 349 le_subfileName->setText (name); |
|
| 350 } |
|
| 351 |
|
| 352 // ============================================================================= |
|
| 353 template<class T> T* initObj (LDObject*& obj) { |
|
| 354 if (obj == null) |
|
| 355 obj = new T; |
|
| 356 |
|
| 357 return static_cast<T*> (obj); |
|
| 358 } |
|
| 359 |
|
| 360 // ============================================================================= |
|
| 361 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
| 362 // ============================================================================= |
|
| 363 void AddObjectDialog::staticDialog (const LDObject::Type type, LDObject* obj) { |
|
| 364 setlocale (LC_ALL, "C"); |
|
| 365 |
|
| 366 // Redirect editing of gibberish to the set contents dialog |
|
| 367 if (obj && obj->getType () == LDObject::Gibberish) { |
|
| 368 SetContentsDialog::staticDialog (obj); |
|
| 369 return; |
|
| 370 } |
|
| 371 |
|
| 372 if (type == LDObject::Empty) |
|
| 373 return; // Nothing to edit with empties |
|
| 374 |
|
| 375 const bool newObject = (obj == null); |
|
| 376 AddObjectDialog dlg (type, obj); |
|
| 377 |
|
| 378 if (obj) |
|
| 379 assert (obj->getType () == type); |
|
| 380 |
|
| 381 if (dlg.exec () == false) |
|
| 382 return; |
|
| 383 |
|
| 384 LDObject* backup = null; |
|
| 385 if (!newObject) |
|
| 386 backup = obj->clone (); |
|
| 387 |
|
| 388 matrix<3> transform = g_identity; |
|
| 389 if (type == LDObject::Subfile || type == LDObject::Radial) { |
|
| 390 vector<str> matrixstrvals = str (dlg.le_matrix->text ()).split (" ", true); |
|
| 391 |
|
| 392 if (matrixstrvals.size () == 9) { |
|
| 393 double matrixvals[9]; |
|
| 394 int i = 0; |
|
| 395 |
|
| 396 for (str val : matrixstrvals) |
|
| 397 matrixvals[i++] = atof (val); |
|
| 398 |
|
| 399 transform = matrix<3> (matrixvals); |
|
| 400 } |
|
| 401 } |
|
| 402 |
|
| 403 switch (type) { |
|
| 404 case LDObject::Comment: |
|
| 405 { |
|
| 406 LDComment* comm = initObj<LDComment> (obj); |
|
| 407 comm->text = dlg.le_comment->text (); |
|
| 408 } |
|
| 409 break; |
|
| 410 |
|
| 411 case LDObject::Line: |
|
| 412 { |
|
| 413 LDLine* line = initObj<LDLine> (obj); |
|
| 414 line->dColor = dlg.dColor; |
|
| 415 APPLY_COORDS (line, 2) |
|
| 416 } |
|
| 417 break; |
|
| 418 |
|
| 419 case LDObject::Triangle: |
|
| 420 { |
|
| 421 LDTriangle* tri = initObj<LDTriangle> (obj); |
|
| 422 tri->dColor = dlg.dColor; |
|
| 423 APPLY_COORDS (tri, 3) |
|
| 424 } |
|
| 425 break; |
|
| 426 |
|
| 427 case LDObject::Quad: |
|
| 428 { |
|
| 429 LDQuad* quad = initObj<LDQuad> (obj); |
|
| 430 quad->dColor = dlg.dColor; |
|
| 431 APPLY_COORDS (quad, 4) |
|
| 432 } |
|
| 433 break; |
|
| 434 |
|
| 435 case LDObject::CondLine: |
|
| 436 { |
|
| 437 LDCondLine* line = initObj<LDCondLine> (obj); |
|
| 438 line->dColor = dlg.dColor; |
|
| 439 APPLY_COORDS (line, 4) |
|
| 440 } |
|
| 441 break; |
|
| 442 |
|
| 443 case LDObject::BFC: |
|
| 444 { |
|
| 445 LDBFC* bfc = initObj<LDBFC> (obj); |
|
| 446 bfc->type = (LDBFC::Type) dlg.rb_bfcType->value (); |
|
| 447 } |
|
| 448 break; |
|
| 449 |
|
| 450 case LDObject::Vertex: |
|
| 451 { |
|
| 452 LDVertex* vert = initObj<LDVertex> (obj); |
|
| 453 vert->dColor = dlg.dColor; |
|
| 454 |
|
| 455 for (const Axis ax : g_Axes) |
|
| 456 vert->vPosition[ax] = dlg.dsb_coords[ax]->value (); |
|
| 457 } |
|
| 458 break; |
|
| 459 |
|
| 460 case LDObject::Radial: |
|
| 461 { |
|
| 462 LDRadial* pRad = initObj<LDRadial> (obj); |
|
| 463 pRad->dColor = dlg.dColor; |
|
| 464 |
|
| 465 for (const Axis ax : g_Axes) |
|
| 466 pRad->vPosition[ax] = dlg.dsb_coords[ax]->value (); |
|
| 467 |
|
| 468 pRad->dDivisions = (dlg.cb_radHiRes->checkState () != Qt::Checked) ? 16 : 48; |
|
| 469 pRad->dSegments = min<short> (dlg.sb_radSegments->value (), pRad->dDivisions); |
|
| 470 pRad->eRadialType = (LDRadial::Type) dlg.rb_radType->value (); |
|
| 471 pRad->dRingNum = dlg.sb_radRingNum->value (); |
|
| 472 pRad->mMatrix = transform; |
|
| 473 } |
|
| 474 break; |
|
| 475 |
|
| 476 case LDObject::Subfile: |
|
| 477 { |
|
| 478 str name = dlg.le_subfileName->text (); |
|
| 479 if (~name == 0) |
|
| 480 return; // no subfile filename |
|
| 481 |
|
| 482 OpenFile* file = loadSubfile (name); |
|
| 483 if (!file) |
|
| 484 return; |
|
| 485 |
|
| 486 LDSubfile* ref = initObj<LDSubfile> (obj); |
|
| 487 ref->dColor = dlg.dColor; |
|
| 488 |
|
| 489 for (const Axis ax : g_Axes) |
|
| 490 ref->vPosition[ax] = dlg.dsb_coords[ax]->value (); |
|
| 491 |
|
| 492 ref->zFileName = name; |
|
| 493 ref->mMatrix = transform; |
|
| 494 ref->pFile = file; |
|
| 495 } |
|
| 496 break; |
|
| 497 |
|
| 498 default: |
|
| 499 break; |
|
| 500 } |
|
| 501 |
|
| 502 if (newObject) { |
|
| 503 ulong idx = g_win->getInsertionPoint (); |
|
| 504 g_curfile->insertObj (idx, obj); |
|
| 505 History::addEntry (new AddHistory ({(ulong) idx}, {obj->clone ()})); |
|
| 506 } else { |
|
| 507 History::addEntry (new EditHistory ({(ulong) obj->getIndex (g_curfile)}, {backup}, {obj->clone ()})); |
|
| 508 } |
|
| 509 |
|
| 510 g_win->refresh (); |
|
| 511 } |
|