| 64 #define act(N) extern_cfg (KeySequence, key_##N); |
65 #define act(N) extern_cfg (KeySequence, key_##N); |
| 65 #include "actions.h" |
66 #include "actions.h" |
| 66 |
67 |
| 67 // ============================================================================= |
68 // ============================================================================= |
| 68 // ----------------------------------------------------------------------------- |
69 // ----------------------------------------------------------------------------- |
| 69 ForgeWindow::ForgeWindow() { |
70 ForgeWindow::ForgeWindow() |
| 70 g_win = this; |
71 { g_win = this; |
| 71 m_renderer = new GLRenderer; |
72 m_renderer = new GLRenderer; |
| 72 |
73 |
| 73 ui = new Ui_LDForgeUI; |
74 ui = new Ui_LDForgeUI; |
| 74 ui->setupUi (this); |
75 ui->setupUi (this); |
| 75 |
76 |
| 76 // Stuff the renderer into its frame |
77 // Stuff the renderer into its frame |
| 77 QVBoxLayout* rendererLayout = new QVBoxLayout (ui->rendererFrame); |
78 QVBoxLayout* rendererLayout = new QVBoxLayout (ui->rendererFrame); |
| 78 rendererLayout->addWidget (R()); |
79 rendererLayout->addWidget (R()); |
| 79 |
80 |
| 80 connect (ui->objectList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_selectionChanged())); |
81 connect (ui->objectList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_selectionChanged())); |
| 81 connect (ui->objectList, SIGNAL (itemDoubleClicked (QListWidgetItem*)), this, SLOT (slot_editObject (QListWidgetItem*))); |
82 connect (ui->objectList, SIGNAL (itemDoubleClicked (QListWidgetItem*)), this, SLOT (slot_editObject (QListWidgetItem*))); |
| 82 connect (ui->fileList, SIGNAL (currentItemChanged (QListWidgetItem*, QListWidgetItem*)), this, SLOT (changeCurrentFile())); |
83 connect (ui->fileList, SIGNAL (currentItemChanged (QListWidgetItem*, QListWidgetItem*)), this, SLOT (changeCurrentFile())); |
| 83 |
84 |
| 84 // Init message log manager |
85 // Init message log manager |
| 85 m_msglog = new MessageManager; |
86 m_msglog = new MessageManager; |
| 86 m_msglog->setRenderer (R()); |
87 m_msglog->setRenderer (R()); |
| 87 m_renderer->setMessageLog (m_msglog); |
88 m_renderer->setMessageLog (m_msglog); |
| 88 m_quickColors = quickColorsFromConfig(); |
89 m_quickColors = quickColorsFromConfig(); |
| 89 slot_selectionChanged(); |
90 slot_selectionChanged(); |
| 90 setStatusBar (new QStatusBar); |
91 setStatusBar (new QStatusBar); |
| 91 |
92 |
| 92 // Init primitive loader task stuff |
93 // Init primitive loader task stuff |
| 93 m_primLoaderBar = new QProgressBar; |
94 m_primLoaderBar = new QProgressBar; |
| 94 m_primLoaderWidget = new QWidget; |
95 m_primLoaderWidget = new QWidget; |
| 95 QHBoxLayout* primLoaderLayout = new QHBoxLayout (m_primLoaderWidget); |
96 QHBoxLayout* primLoaderLayout = new QHBoxLayout (m_primLoaderWidget); |
| 96 primLoaderLayout->addWidget (new QLabel ("Loading primitives:")); |
97 primLoaderLayout->addWidget (new QLabel ("Loading primitives:")); |
| 97 primLoaderLayout->addWidget (m_primLoaderBar); |
98 primLoaderLayout->addWidget (m_primLoaderBar); |
| 98 statusBar()->addPermanentWidget (m_primLoaderWidget); |
99 statusBar()->addPermanentWidget (m_primLoaderWidget); |
| 99 m_primLoaderWidget->hide(); |
100 m_primLoaderWidget->hide(); |
| 100 |
101 |
| 101 // Make certain actions checkable |
102 // Make certain actions checkable |
| 102 ui->actionAxes->setChecked (gl_axes); |
103 ui->actionAxes->setChecked (gl_axes); |
| 103 ui->actionWireframe->setChecked (gl_wireframe); |
104 ui->actionWireframe->setChecked (gl_wireframe); |
| 104 ui->actionBFCView->setChecked (gl_colorbfc); |
105 ui->actionBFCView->setChecked (gl_colorbfc); |
| 105 updateGridToolBar(); |
106 updateGridToolBar(); |
| 106 updateEditModeActions(); |
107 updateEditModeActions(); |
| 107 updateRecentFilesMenu(); |
108 updateRecentFilesMenu(); |
| 108 updateToolBars(); |
109 updateToolBars(); |
| 109 updateTitle(); |
110 updateTitle(); |
| 110 |
111 |
| 111 setMinimumSize (300, 200); |
112 setMinimumSize (300, 200); |
| 112 |
113 |
| 113 connect (qApp, SIGNAL (aboutToQuit()), this, SLOT (slot_lastSecondCleanup())); |
114 connect (qApp, SIGNAL (aboutToQuit()), this, SLOT (slot_lastSecondCleanup())); |
| 114 |
115 |
| 115 // Connect all actions and set shortcuts |
116 // Connect all actions and set shortcuts |
| 116 #define act(N) \ |
117 #define act(N) \ |
| 117 connect (ui->action##N, SIGNAL (triggered()), this, SLOT (slot_action())); \ |
118 connect (ui->action##N, SIGNAL (triggered()), this, SLOT (slot_action())); \ |
| 118 ui->action##N->setShortcut (key_##N); |
119 ui->action##N->setShortcut (key_##N); |
| 119 #include "actions.h" |
120 #include "actions.h" |
| 120 } |
121 } |
| 121 |
122 |
| 122 // ============================================================================= |
123 // ============================================================================= |
| 123 // ----------------------------------------------------------------------------- |
124 // ----------------------------------------------------------------------------- |
| 124 void ForgeWindow::slot_action() { |
125 void ForgeWindow::slot_action() |
| 125 // Find out which action triggered this |
126 { // Find out which action triggered this |
| 126 #define act(N) if (sender() == ui->action##N) invokeAction (ui->action##N, &actiondef_##N); |
127 #define act(N) if (sender() == ui->action##N) invokeAction (ui->action##N, &actiondef_##N); |
| 127 #include "actions.h" |
128 #include "actions.h" |
| 128 } |
129 } |
| 129 |
130 |
| 130 // ============================================================================= |
131 // ============================================================================= |
| 131 // ----------------------------------------------------------------------------- |
132 // ----------------------------------------------------------------------------- |
| 132 void ForgeWindow::invokeAction (QAction* act, void (*func)()) { |
133 void ForgeWindow::invokeAction (QAction* act, void (*func) ()) |
| 133 beginAction (act); |
134 { beginAction (act); |
| 134 (*func)(); |
135 (*func) (); |
| 135 endAction(); |
136 endAction(); |
| 136 } |
137 } |
| 137 |
138 |
| 138 // ============================================================================= |
139 // ============================================================================= |
| 139 // ----------------------------------------------------------------------------- |
140 // ----------------------------------------------------------------------------- |
| 140 void ForgeWindow::slot_lastSecondCleanup() { |
141 void ForgeWindow::slot_lastSecondCleanup() |
| 141 delete m_renderer; |
142 { delete m_renderer; |
| 142 delete ui; |
143 delete ui; |
| 143 } |
144 } |
| 144 |
145 |
| 145 // ============================================================================= |
146 // ============================================================================= |
| 146 // ----------------------------------------------------------------------------- |
147 // ----------------------------------------------------------------------------- |
| 147 void ForgeWindow::updateRecentFilesMenu() { |
148 void ForgeWindow::updateRecentFilesMenu() |
| 148 // First, clear any items in the recent files menu |
149 { // First, clear any items in the recent files menu |
| 149 for (QAction* recent : m_recentFiles) |
150 for (QAction * recent : m_recentFiles) |
| 150 delete recent; |
151 delete recent; |
| |
152 |
| 151 m_recentFiles.clear(); |
153 m_recentFiles.clear(); |
| 152 |
154 |
| 153 QAction* first = null; |
155 QAction* first = null; |
| 154 |
156 |
| 155 for (const QVariant& it : io_recentfiles) { |
157 for (const QVariant & it : io_recentfiles) |
| 156 str file = it.toString(); |
158 { str file = it.toString(); |
| 157 QAction* recent = new QAction (getIcon ("open-recent"), file, this); |
159 QAction* recent = new QAction (getIcon ("open-recent"), file, this); |
| 158 |
160 |
| 159 connect (recent, SIGNAL (triggered()), this, SLOT (slot_recentFile())); |
161 connect (recent, SIGNAL (triggered()), this, SLOT (slot_recentFile())); |
| 160 ui->menuOpenRecent->insertAction (first, recent); |
162 ui->menuOpenRecent->insertAction (first, recent); |
| 161 m_recentFiles << recent; |
163 m_recentFiles << recent; |
| 162 first = recent; |
164 first = recent; |
| 163 } |
165 } |
| 164 } |
166 } |
| 165 |
167 |
| 166 // ============================================================================= |
168 // ============================================================================= |
| 167 // ----------------------------------------------------------------------------- |
169 // ----------------------------------------------------------------------------- |
| 168 List<LDQuickColor> quickColorsFromConfig() { |
170 List<LDQuickColor> quickColorsFromConfig() |
| 169 List<LDQuickColor> colors; |
171 { List<LDQuickColor> colors; |
| 170 |
172 |
| 171 for (str colorname : gui_colortoolbar.value.split (":")) { |
173 for (str colorname : gui_colortoolbar.value.split (":")) |
| 172 if (colorname == "|") |
174 { if (colorname == "|") |
| 173 colors << LDQuickColor::getSeparator(); |
175 colors << LDQuickColor::getSeparator(); |
| 174 else { |
176 else |
| 175 LDColor* col = getColor (colorname.toLong()); |
177 { LDColor* col = getColor (colorname.toLong()); |
| 176 |
178 |
| 177 if (col != null) |
179 if (col != null) |
| 178 colors << LDQuickColor (col, null); |
180 colors << LDQuickColor (col, null); |
| 179 } |
181 } |
| 180 } |
182 } |
| 181 |
183 |
| 182 return colors; |
184 return colors; |
| 183 } |
185 } |
| 184 |
186 |
| 185 // ============================================================================= |
187 // ============================================================================= |
| 186 // ----------------------------------------------------------------------------- |
188 // ----------------------------------------------------------------------------- |
| 187 void ForgeWindow::updateToolBars() { |
189 void ForgeWindow::updateToolBars() |
| 188 m_colorButtons.clear(); |
190 { m_colorButtons.clear(); |
| 189 ui->colorToolbar->clear(); |
191 ui->colorToolbar->clear(); |
| 190 |
192 |
| 191 for (LDQuickColor& entry : m_quickColors) { |
193 for (LDQuickColor & entry : m_quickColors) |
| 192 if (entry.isSeparator()) |
194 { if (entry.isSeparator()) |
| 193 ui->colorToolbar->addSeparator(); |
195 ui->colorToolbar->addSeparator(); |
| 194 else { |
196 else |
| 195 QToolButton* colorButton = new QToolButton; |
197 { QToolButton* colorButton = new QToolButton; |
| 196 colorButton->setIcon (makeColorIcon (entry.color(), 22)); |
198 colorButton->setIcon (makeColorIcon (entry.color(), 22)); |
| 197 colorButton->setIconSize (QSize (22, 22)); |
199 colorButton->setIconSize (QSize (22, 22)); |
| 198 colorButton->setToolTip (entry.color()->name); |
200 colorButton->setToolTip (entry.color()->name); |
| 199 |
201 |
| 200 connect (colorButton, SIGNAL (clicked()), this, SLOT (slot_quickColor())); |
202 connect (colorButton, SIGNAL (clicked()), this, SLOT (slot_quickColor())); |
| 201 ui->colorToolbar->addWidget (colorButton); |
203 ui->colorToolbar->addWidget (colorButton); |
| 202 m_colorButtons << colorButton; |
204 m_colorButtons << colorButton; |
| 203 |
205 |
| 204 entry.setToolButton (colorButton); |
206 entry.setToolButton (colorButton); |
| 205 } |
207 } |
| 206 } |
208 } |
| 207 |
209 |
| 208 updateGridToolBar(); |
210 updateGridToolBar(); |
| 209 } |
211 } |
| 210 |
212 |
| 211 // ============================================================================= |
213 // ============================================================================= |
| 212 // ----------------------------------------------------------------------------- |
214 // ----------------------------------------------------------------------------- |
| 213 void ForgeWindow::updateGridToolBar() { |
215 void ForgeWindow::updateGridToolBar() |
| 214 // Ensure that the current grid - and only the current grid - is selected. |
216 { // Ensure that the current grid - and only the current grid - is selected. |
| 215 ui->actionGridCoarse->setChecked (grid == Grid::Coarse); |
217 ui->actionGridCoarse->setChecked (grid == Grid::Coarse); |
| 216 ui->actionGridMedium->setChecked (grid == Grid::Medium); |
218 ui->actionGridMedium->setChecked (grid == Grid::Medium); |
| 217 ui->actionGridFine->setChecked (grid == Grid::Fine); |
219 ui->actionGridFine->setChecked (grid == Grid::Fine); |
| 218 } |
220 } |
| 219 |
221 |
| 220 // ============================================================================= |
222 // ============================================================================= |
| 221 // ----------------------------------------------------------------------------- |
223 // ----------------------------------------------------------------------------- |
| 222 void ForgeWindow::updateTitle() { |
224 void ForgeWindow::updateTitle() |
| 223 str title = fmt (APPNAME " %1", fullVersionString()); |
225 { str title = fmt (APPNAME " %1", fullVersionString()); |
| 224 |
226 |
| 225 // Append our current file if we have one |
227 // Append our current file if we have one |
| 226 if (LDFile::current()) { |
228 if (LDFile::current()) |
| 227 if (LDFile::current()->name().length() > 0) |
229 { if (LDFile::current()->name().length() > 0) |
| 228 title += fmt (": %1", basename (LDFile::current()->name())); |
230 title += fmt (": %1", basename (LDFile::current()->name())); |
| 229 else |
231 else |
| 230 title += fmt (": <anonymous>"); |
232 title += fmt (": <anonymous>"); |
| 231 |
233 |
| 232 if (LDFile::current()->numObjs() > 0 && |
234 if (LDFile::current()->numObjs() > 0 && |
| 233 LDFile::current()->obj (0)->getType() == LDObject::Comment) |
235 LDFile::current()->obj (0)->getType() == LDObject::Comment) |
| 234 { |
236 { // Append title |
| 235 // Append title |
|
| 236 LDComment* comm = static_cast<LDComment*> (LDFile::current()->obj (0)); |
237 LDComment* comm = static_cast<LDComment*> (LDFile::current()->obj (0)); |
| 237 title += fmt (": %1", comm->text); |
238 title += fmt (": %1", comm->text); |
| 238 } |
239 } |
| 239 |
240 |
| 240 if (LDFile::current()->history().pos() != LDFile::current()->savePos()) |
241 if (LDFile::current()->history().pos() != LDFile::current()->savePos()) |
| 241 title += '*'; |
242 title += '*'; |
| 242 } |
243 } |
| 243 |
244 |
| 244 setWindowTitle (title); |
245 setWindowTitle (title); |
| 245 } |
246 } |
| 246 |
247 |
| 247 // ============================================================================= |
248 // ============================================================================= |
| 248 // ----------------------------------------------------------------------------- |
249 // ----------------------------------------------------------------------------- |
| 249 int ForgeWindow::deleteSelection() { |
250 int ForgeWindow::deleteSelection() |
| 250 if (m_sel.size() == 0) |
251 { if (m_sel.size() == 0) |
| 251 return 0; |
252 return 0; |
| 252 |
253 |
| 253 List<LDObject*> selCopy = m_sel; |
254 List<LDObject*> selCopy = m_sel; |
| 254 int num = 0; |
255 int num = 0; |
| 255 |
256 |
| 256 // Delete the objects that were being selected |
257 // Delete the objects that were being selected |
| 257 for (LDObject* obj : selCopy) { |
258 for (LDObject * obj : selCopy) |
| 258 LDFile::current()->forgetObject (obj); |
259 { LDFile::current()->forgetObject (obj); |
| 259 ++num; |
260 ++num; |
| 260 delete obj; |
261 delete obj; |
| 261 } |
262 } |
| 262 |
263 |
| 263 refresh(); |
264 refresh(); |
| 264 return num; |
265 return num; |
| 265 } |
266 } |
| 266 |
267 |
| 267 // ============================================================================= |
268 // ============================================================================= |
| 268 // ----------------------------------------------------------------------------- |
269 // ----------------------------------------------------------------------------- |
| 269 void ForgeWindow::buildObjList() { |
270 void ForgeWindow::buildObjList() |
| 270 if (!LDFile::current()) |
271 { if (!LDFile::current()) |
| 271 return; |
272 return; |
| 272 |
273 |
| 273 // Lock the selection while we do this so that refreshing the object list |
274 // Lock the selection while we do this so that refreshing the object list |
| 274 // doesn't trigger selection updating so that the selection doesn't get lost |
275 // doesn't trigger selection updating so that the selection doesn't get lost |
| 275 // while this is done. |
276 // while this is done. |
| 276 g_bSelectionLocked = true; |
277 g_bSelectionLocked = true; |
| 277 |
278 |
| 278 for (int i = 0; i < ui->objectList->count(); ++i) |
279 for (int i = 0; i < ui->objectList->count(); ++i) |
| 279 delete ui->objectList->item (i); |
280 delete ui->objectList->item (i); |
| 280 |
281 |
| 281 ui->objectList->clear(); |
282 ui->objectList->clear(); |
| 282 |
283 |
| 283 for (LDObject* obj : LDFile::current()->objects()) { |
284 for (LDObject * obj : LDFile::current()->objects()) |
| 284 str descr; |
285 { str descr; |
| 285 |
286 |
| 286 switch (obj->getType()) { |
287 switch (obj->getType()) |
| 287 case LDObject::Comment: |
288 { case LDObject::Comment: |
| 288 descr = static_cast<LDComment*> (obj)->text; |
289 descr = static_cast<LDComment*> (obj)->text; |
| 289 |
290 |
| 290 // Remove leading whitespace |
291 // Remove leading whitespace |
| 291 while (descr[0] == ' ') |
292 while (descr[0] == ' ') |
| 292 descr.remove (0, 1); |
293 descr.remove (0, 1); |
| 293 break; |
294 |
| 294 |
295 break; |
| 295 case LDObject::Empty: |
296 |
| 296 break; // leave it empty |
297 case LDObject::Empty: |
| 297 |
298 break; // leave it empty |
| 298 case LDObject::Line: |
299 |
| 299 case LDObject::Triangle: |
300 case LDObject::Line: |
| 300 case LDObject::Quad: |
301 case LDObject::Triangle: |
| 301 case LDObject::CndLine: |
302 case LDObject::Quad: |
| 302 for (short i = 0; i < obj->vertices(); ++i) { |
303 case LDObject::CndLine: |
| 303 if (i != 0) |
304 |
| 304 descr += ", "; |
305 for (short i = 0; i < obj->vertices(); ++i) |
| 305 |
306 { if (i != 0) |
| 306 descr += obj->getVertex (i).stringRep (true); |
307 descr += ", "; |
| 307 } |
308 |
| 308 break; |
309 descr += obj->getVertex (i).stringRep (true); |
| 309 |
310 } |
| 310 case LDObject::Error: |
311 |
| 311 descr = fmt ("ERROR: %1", obj->raw()); |
312 break; |
| 312 break; |
313 |
| 313 |
314 case LDObject::Error: |
| 314 case LDObject::Vertex: |
315 descr = fmt ("ERROR: %1", obj->raw()); |
| 315 descr = static_cast<LDVertex*> (obj)->pos.stringRep (true); |
316 break; |
| 316 break; |
317 |
| 317 |
318 case LDObject::Vertex: |
| 318 case LDObject::Subfile: |
319 descr = static_cast<LDVertex*> (obj)->pos.stringRep (true); |
| 319 { |
320 break; |
| 320 LDSubfile* ref = static_cast<LDSubfile*> (obj); |
321 |
| 321 |
322 case LDObject::Subfile: |
| |
323 { LDSubfile* ref = static_cast<LDSubfile*> (obj); |
| |
324 |
| 322 descr = fmt ("%1 %2, (", ref->fileInfo()->name(), |
325 descr = fmt ("%1 %2, (", ref->fileInfo()->name(), |
| 323 ref->position().stringRep (true)); |
326 ref->position().stringRep (true)); |
| 324 |
327 |
| 325 for (short i = 0; i < 9; ++i) |
328 for (short i = 0; i < 9; ++i) |
| 326 descr += fmt ("%1%2", ftoa (ref->transform()[i]), |
329 descr += fmt ("%1%2", ftoa (ref->transform() [i]), |
| 327 (i != 8) ? " " : ""); |
330 (i != 8) ? " " : ""); |
| 328 |
331 |
| 329 descr += ')'; |
332 descr += ')'; |
| 330 } |
333 } |
| 331 break; |
334 break; |
| 332 |
335 |
| 333 case LDObject::BFC: |
336 case LDObject::BFC: |
| 334 descr = LDBFC::statements[static_cast<LDBFC*> (obj)->type]; |
337 descr = LDBFC::statements[static_cast<LDBFC*> (obj)->type]; |
| 335 break; |
338 break; |
| 336 |
339 |
| 337 case LDObject::Overlay: |
340 case LDObject::Overlay: |
| 338 { |
341 { LDOverlay* ovl = static_cast<LDOverlay*> (obj); |
| 339 LDOverlay* ovl = static_cast<LDOverlay*> (obj); |
|
| 340 descr = fmt ("[%1] %2 (%3, %4), %5 x %6", g_CameraNames[ovl->camera()], |
342 descr = fmt ("[%1] %2 (%3, %4), %5 x %6", g_CameraNames[ovl->camera()], |
| 341 basename (ovl->filename()), ovl->x(), ovl->y(), ovl->width(), ovl->height()); |
343 basename (ovl->filename()), ovl->x(), ovl->y(), ovl->width(), ovl->height()); |
| 342 } |
344 } |
| 343 break; |
345 break; |
| 344 |
346 |
| 345 default: |
347 default: |
| 346 descr = obj->typeName(); |
348 descr = obj->typeName(); |
| 347 break; |
349 break; |
| 348 } |
350 } |
| 349 |
351 |
| 350 // Put it into brackets if it's hidden |
352 // Put it into brackets if it's hidden |
| 351 if (obj->hidden()) |
353 if (obj->hidden()) |
| 352 descr = fmt ("[[ %1 ]]", descr); |
354 descr = fmt ("[[ %1 ]]", descr); |
| 353 |
355 |
| 354 QListWidgetItem* item = new QListWidgetItem (descr); |
356 QListWidgetItem* item = new QListWidgetItem (descr); |
| 355 item->setIcon (getIcon (obj->typeName())); |
357 item->setIcon (getIcon (obj->typeName())); |
| 356 |
358 |
| 357 // Color gibberish orange on red so it stands out. |
359 // Color gibberish orange on red so it stands out. |
| 358 if (obj->getType() == LDObject::Error) { |
360 if (obj->getType() == LDObject::Error) |
| 359 item->setBackground (QColor ("#AA0000")); |
361 { item->setBackground (QColor ("#AA0000")); |
| 360 item->setForeground (QColor ("#FFAA00")); |
362 item->setForeground (QColor ("#FFAA00")); |
| 361 } elif (lv_colorize && obj->isColored() && |
363 } elif (lv_colorize && obj->isColored() && |
| 362 obj->color() != maincolor && obj->color() != edgecolor) |
364 |
| 363 { |
365 obj->color() != maincolor && obj->color() != edgecolor) |
| 364 // If the object isn't in the main or edge color, draw this |
366 { // If the object isn't in the main or edge color, draw this |
| 365 // list entry in said color. |
367 // list entry in said color. |
| 366 LDColor* col = getColor (obj->color()); |
368 LDColor* col = getColor (obj->color()); |
| |
369 |
| 367 if (col) |
370 if (col) |
| 368 item->setForeground (col->faceColor); |
371 item->setForeground (col->faceColor); |
| 369 } |
372 } |
| 370 |
373 |
| 371 obj->qObjListEntry = item; |
374 obj->qObjListEntry = item; |
| 372 ui->objectList->insertItem (ui->objectList->count(), item); |
375 ui->objectList->insertItem (ui->objectList->count(), item); |
| 373 } |
376 } |
| 374 |
377 |
| 375 g_bSelectionLocked = false; |
378 g_bSelectionLocked = false; |
| 376 updateSelection(); |
379 updateSelection(); |
| 377 scrollToSelection(); |
380 scrollToSelection(); |
| 378 } |
381 } |
| 379 |
382 |
| 380 // ============================================================================= |
383 // ============================================================================= |
| 381 // ----------------------------------------------------------------------------- |
384 // ----------------------------------------------------------------------------- |
| 382 void ForgeWindow::scrollToSelection() { |
385 void ForgeWindow::scrollToSelection() |
| 383 if (m_sel.size() == 0) |
386 { if (m_sel.size() == 0) |
| 384 return; |
387 return; |
| 385 |
388 |
| 386 LDObject* obj = m_sel[m_sel.size() - 1]; |
389 LDObject* obj = m_sel[m_sel.size() - 1]; |
| 387 ui->objectList->scrollToItem (obj->qObjListEntry); |
390 ui->objectList->scrollToItem (obj->qObjListEntry); |
| 388 } |
391 } |
| 389 |
392 |
| 390 // ============================================================================= |
393 // ============================================================================= |
| 391 // ----------------------------------------------------------------------------- |
394 // ----------------------------------------------------------------------------- |
| 392 void ForgeWindow::slot_selectionChanged() { |
395 void ForgeWindow::slot_selectionChanged() |
| 393 if (g_bSelectionLocked == true || LDFile::current() == null) |
396 { if (g_bSelectionLocked == true || LDFile::current() == null) |
| 394 return; |
397 return; |
| 395 |
398 |
| 396 // Update the shared selection array, though don't do this if this was |
399 // Update the shared selection array, though don't do this if this was |
| 397 // called during GL picking, in which case the GL renderer takes care |
400 // called during GL picking, in which case the GL renderer takes care |
| 398 // of the selection. |
401 // of the selection. |
| 399 if (m_renderer->picking()) |
402 if (m_renderer->picking()) |
| 400 return; |
403 return; |
| 401 |
404 |
| 402 List<LDObject*> priorSelection = m_sel; |
405 List<LDObject*> priorSelection = m_sel; |
| 403 |
406 |
| 404 // Get the objects from the object list selection |
407 // Get the objects from the object list selection |
| 405 m_sel.clear(); |
408 m_sel.clear(); |
| 406 const QList<QListWidgetItem*> items = ui->objectList->selectedItems(); |
409 const QList<QListWidgetItem*> items = ui->objectList->selectedItems(); |
| 407 |
410 |
| 408 for (LDObject* obj : LDFile::current()->objects()) |
411 for (LDObject * obj : LDFile::current()->objects()) |
| 409 for (QListWidgetItem* item : items) { |
412 for (QListWidgetItem * item : items) |
| 410 if (item == obj->qObjListEntry) { |
413 { if (item == obj->qObjListEntry) |
| 411 m_sel << obj; |
414 { m_sel << obj; |
| |
415 break; |
| |
416 } |
| |
417 } |
| |
418 |
| |
419 // Update the GL renderer |
| |
420 for (LDObject * obj : priorSelection) |
| |
421 { obj->setSelected (false); |
| |
422 m_renderer->compileObject (obj); |
| |
423 } |
| |
424 |
| |
425 for (LDObject * obj : m_sel) |
| |
426 { obj->setSelected (true); |
| |
427 m_renderer->compileObject (obj); |
| |
428 } |
| |
429 |
| |
430 m_renderer->update(); |
| |
431 } |
| |
432 |
| |
433 // ============================================================================= |
| |
434 // ----------------------------------------------------------------------------- |
| |
435 void ForgeWindow::slot_recentFile() |
| |
436 { QAction* qAct = static_cast<QAction*> (sender()); |
| |
437 openMainFile (qAct->text()); |
| |
438 } |
| |
439 |
| |
440 // ============================================================================= |
| |
441 // ----------------------------------------------------------------------------- |
| |
442 void ForgeWindow::slot_quickColor() |
| |
443 { beginAction (null); |
| |
444 QToolButton* button = static_cast<QToolButton*> (sender()); |
| |
445 LDColor* col = null; |
| |
446 |
| |
447 for (const LDQuickColor & entry : m_quickColors) |
| |
448 { if (entry.toolButton() == button) |
| |
449 { col = entry.color(); |
| 412 break; |
450 break; |
| 413 } |
451 } |
| 414 } |
452 } |
| 415 |
453 |
| 416 // Update the GL renderer |
|
| 417 for (LDObject* obj : priorSelection) { |
|
| 418 obj->setSelected (false); |
|
| 419 m_renderer->compileObject (obj); |
|
| 420 } |
|
| 421 |
|
| 422 for (LDObject* obj : m_sel) { |
|
| 423 obj->setSelected (true); |
|
| 424 m_renderer->compileObject (obj); |
|
| 425 } |
|
| 426 |
|
| 427 m_renderer->update(); |
|
| 428 } |
|
| 429 |
|
| 430 // ============================================================================= |
|
| 431 // ----------------------------------------------------------------------------- |
|
| 432 void ForgeWindow::slot_recentFile() { |
|
| 433 QAction* qAct = static_cast<QAction*> (sender()); |
|
| 434 openMainFile (qAct->text()); |
|
| 435 } |
|
| 436 |
|
| 437 // ============================================================================= |
|
| 438 // ----------------------------------------------------------------------------- |
|
| 439 void ForgeWindow::slot_quickColor() { |
|
| 440 beginAction (null); |
|
| 441 QToolButton* button = static_cast<QToolButton*> (sender()); |
|
| 442 LDColor* col = null; |
|
| 443 |
|
| 444 for (const LDQuickColor& entry : m_quickColors) { |
|
| 445 if (entry.toolButton() == button) { |
|
| 446 col = entry.color(); |
|
| 447 break; |
|
| 448 } |
|
| 449 } |
|
| 450 |
|
| 451 if (col == null) |
454 if (col == null) |
| 452 return; |
455 return; |
| 453 |
456 |
| 454 short newColor = col->index; |
457 short newColor = col->index; |
| 455 |
458 |
| 456 for (LDObject* obj : m_sel) { |
459 for (LDObject * obj : m_sel) |
| 457 if (obj->isColored() == false) |
460 { if (obj->isColored() == false) |
| 458 continue; // uncolored object |
461 continue; // uncolored object |
| 459 |
462 |
| 460 obj->setColor (newColor); |
463 obj->setColor (newColor); |
| 461 R()->compileObject (obj); |
464 R()->compileObject (obj); |
| 462 } |
465 } |
| 463 |
466 |
| 464 refresh(); |
467 refresh(); |
| 465 endAction(); |
468 endAction(); |
| 466 } |
469 } |
| 467 |
470 |
| 468 // ============================================================================= |
471 // ============================================================================= |
| 469 // ----------------------------------------------------------------------------- |
472 // ----------------------------------------------------------------------------- |
| 470 ulong ForgeWindow::getInsertionPoint() { |
473 ulong ForgeWindow::getInsertionPoint() |
| 471 if (m_sel.size() > 0) { |
474 { if (m_sel.size() > 0) |
| 472 // If we have a selection, put the item after it. |
475 { // If we have a selection, put the item after it. |
| 473 return (m_sel[m_sel.size() - 1]->getIndex()) + 1; |
476 return (m_sel[m_sel.size() - 1]->getIndex()) + 1; |
| 474 } |
477 } |
| 475 |
478 |
| 476 // Otherwise place the object at the end. |
479 // Otherwise place the object at the end. |
| 477 return LDFile::current()->numObjs(); |
480 return LDFile::current()->numObjs(); |
| 478 } |
481 } |
| 479 |
482 |
| 480 // ============================================================================= |
483 // ============================================================================= |
| 481 // ----------------------------------------------------------------------------- |
484 // ----------------------------------------------------------------------------- |
| 482 void ForgeWindow::fullRefresh() { |
485 void ForgeWindow::fullRefresh() |
| 483 buildObjList(); |
486 { buildObjList(); |
| 484 m_renderer->hardRefresh(); |
487 m_renderer->hardRefresh(); |
| 485 } |
488 } |
| 486 |
489 |
| 487 // ============================================================================= |
490 // ============================================================================= |
| 488 // ----------------------------------------------------------------------------- |
491 // ----------------------------------------------------------------------------- |
| 489 void ForgeWindow::refresh() { |
492 void ForgeWindow::refresh() |
| 490 buildObjList(); |
493 { buildObjList(); |
| 491 m_renderer->update(); |
494 m_renderer->update(); |
| 492 } |
495 } |
| 493 |
496 |
| 494 // ============================================================================= |
497 // ============================================================================= |
| 495 // ----------------------------------------------------------------------------- |
498 // ----------------------------------------------------------------------------- |
| 496 void ForgeWindow::updateSelection() { |
499 void ForgeWindow::updateSelection() |
| 497 g_bSelectionLocked = true; |
500 { g_bSelectionLocked = true; |
| 498 |
501 |
| 499 for (LDObject* obj : LDFile::current()->objects()) |
502 for (LDObject * obj : LDFile::current()->objects()) |
| 500 obj->setSelected (false); |
503 obj->setSelected (false); |
| 501 |
504 |
| 502 ui->objectList->clearSelection(); |
505 ui->objectList->clearSelection(); |
| 503 for (LDObject* obj : m_sel) { |
506 |
| 504 if (obj->qObjListEntry == null) |
507 for (LDObject * obj : m_sel) |
| |
508 { if (obj->qObjListEntry == null) |
| 505 continue; |
509 continue; |
| 506 |
510 |
| 507 obj->qObjListEntry->setSelected (true); |
511 obj->qObjListEntry->setSelected (true); |
| 508 obj->setSelected (true); |
512 obj->setSelected (true); |
| 509 } |
513 } |
| 510 |
514 |
| 511 g_bSelectionLocked = false; |
515 g_bSelectionLocked = false; |
| 512 slot_selectionChanged(); |
516 slot_selectionChanged(); |
| 513 } |
517 } |
| 514 |
518 |
| 515 // ============================================================================= |
519 // ============================================================================= |
| 516 // ----------------------------------------------------------------------------- |
520 // ----------------------------------------------------------------------------- |
| 517 bool ForgeWindow::isSelected (LDObject* obj) { |
521 bool ForgeWindow::isSelected (LDObject* obj) |
| 518 LDObject* needle = obj->topLevelParent(); |
522 { LDObject* needle = obj->topLevelParent(); |
| 519 |
523 |
| 520 for (LDObject* hay : m_sel) |
524 for (LDObject * hay : m_sel) |
| 521 if (hay == needle) |
525 if (hay == needle) |
| 522 return true; |
526 return true; |
| 523 |
527 |
| 524 return false; |
528 return false; |
| 525 } |
529 } |
| 526 |
530 |
| 527 short ForgeWindow::getSelectedColor() { |
531 short ForgeWindow::getSelectedColor() |
| 528 short result = -1; |
532 { short result = -1; |
| 529 |
533 |
| 530 for (LDObject* obj : m_sel) { |
534 for (LDObject * obj : m_sel) |
| 531 if (obj->isColored() == false) |
535 { if (obj->isColored() == false) |
| 532 continue; // doesn't use color |
536 continue; // doesn't use color |
| 533 |
537 |
| 534 if (result != -1 && obj->color() != result) |
538 if (result != -1 && obj->color() != result) |
| 535 return -1; // No consensus in object color |
539 return -1; // No consensus in object color |
| 536 |
540 |
| 537 if (result == -1) |
541 if (result == -1) |
| 538 result = obj->color(); |
542 result = obj->color(); |
| 539 } |
543 } |
| 540 |
544 |
| 541 return result; |
545 return result; |
| 542 } |
546 } |
| 543 |
547 |
| 544 // ============================================================================= |
548 // ============================================================================= |
| 545 // ----------------------------------------------------------------------------- |
549 // ----------------------------------------------------------------------------- |
| 546 LDObject::Type ForgeWindow::uniformSelectedType() { |
550 LDObject::Type ForgeWindow::uniformSelectedType() |
| 547 LDObject::Type result = LDObject::Unidentified; |
551 { LDObject::Type result = LDObject::Unidentified; |
| 548 |
552 |
| 549 for (LDObject* obj : m_sel) { |
553 for (LDObject * obj : m_sel) |
| 550 if (result != LDObject::Unidentified && obj->color() != result) |
554 { if (result != LDObject::Unidentified && obj->color() != result) |
| 551 return LDObject::Unidentified; |
555 return LDObject::Unidentified; |
| 552 |
556 |
| 553 if (result == LDObject::Unidentified) |
557 if (result == LDObject::Unidentified) |
| 554 result = obj->getType(); |
558 result = obj->getType(); |
| 555 } |
559 } |
| 556 |
560 |
| 557 return result; |
561 return result; |
| 558 } |
562 } |
| 559 |
563 |
| 560 // ============================================================================= |
564 // ============================================================================= |
| 561 // ----------------------------------------------------------------------------- |
565 // ----------------------------------------------------------------------------- |
| 562 void ForgeWindow::closeEvent (QCloseEvent* ev) { |
566 void ForgeWindow::closeEvent (QCloseEvent* ev) |
| 563 // Check whether it's safe to close all files. |
567 { // Check whether it's safe to close all files. |
| 564 if (!safeToCloseAll()) { |
568 if (!safeToCloseAll()) |
| 565 ev->ignore(); |
569 { ev->ignore(); |
| 566 return; |
570 return; |
| 567 } |
571 } |
| 568 |
572 |
| 569 // Save the configuration before leaving so that, for instance, grid choice |
573 // Save the configuration before leaving so that, for instance, grid choice |
| 570 // is preserved across instances. |
574 // is preserved across instances. |
| 571 Config::save(); |
575 Config::save(); |
| 572 |
576 |
| 573 ev->accept(); |
577 ev->accept(); |
| 574 } |
578 } |
| 575 |
579 |
| 576 // ============================================================================= |
580 // ============================================================================= |
| 577 // ----------------------------------------------------------------------------- |
581 // ----------------------------------------------------------------------------- |
| 578 void ForgeWindow::spawnContextMenu (const QPoint pos) { |
582 void ForgeWindow::spawnContextMenu (const QPoint pos) |
| 579 const bool single = (g_win->sel().size() == 1); |
583 { const bool single = (g_win->sel().size() == 1); |
| 580 LDObject* singleObj = (single) ? g_win->sel()[0] : null; |
584 LDObject* singleObj = (single) ? g_win->sel() [0] : null; |
| 581 |
585 |
| 582 QMenu* contextMenu = new QMenu; |
586 QMenu* contextMenu = new QMenu; |
| 583 |
587 |
| 584 if (single && singleObj->getType() != LDObject::Empty) { |
588 if (single && singleObj->getType() != LDObject::Empty) |
| 585 contextMenu->addAction (ACTION (Edit)); |
589 { contextMenu->addAction (ACTION (Edit)); |
| 586 contextMenu->addSeparator(); |
590 contextMenu->addSeparator(); |
| 587 } |
591 } |
| 588 |
592 |
| 589 contextMenu->addAction (ACTION (Cut)); |
593 contextMenu->addAction (ACTION (Cut)); |
| 590 contextMenu->addAction (ACTION (Copy)); |
594 contextMenu->addAction (ACTION (Copy)); |
| 591 contextMenu->addAction (ACTION (Paste)); |
595 contextMenu->addAction (ACTION (Paste)); |
| 592 contextMenu->addAction (ACTION (Delete)); |
596 contextMenu->addAction (ACTION (Delete)); |
| 593 contextMenu->addSeparator(); |
597 contextMenu->addSeparator(); |
| 594 contextMenu->addAction (ACTION (SetColor)); |
598 contextMenu->addAction (ACTION (SetColor)); |
| 595 |
599 |
| 596 if (single) |
600 if (single) |
| 597 contextMenu->addAction (ACTION (EditRaw)); |
601 contextMenu->addAction (ACTION (EditRaw)); |
| 598 |
602 |
| 599 contextMenu->addAction (ACTION (Borders)); |
603 contextMenu->addAction (ACTION (Borders)); |
| 600 contextMenu->addAction (ACTION (SetOverlay)); |
604 contextMenu->addAction (ACTION (SetOverlay)); |
| 601 contextMenu->addAction (ACTION (ClearOverlay)); |
605 contextMenu->addAction (ACTION (ClearOverlay)); |
| 602 contextMenu->addAction (ACTION (ModeSelect)); |
606 contextMenu->addAction (ACTION (ModeSelect)); |
| 603 contextMenu->addAction (ACTION (ModeDraw)); |
607 contextMenu->addAction (ACTION (ModeDraw)); |
| 604 contextMenu->addAction (ACTION (ModeCircle)); |
608 contextMenu->addAction (ACTION (ModeCircle)); |
| 605 |
609 |
| 606 if (R()->camera() != GL::Free) { |
610 if (R()->camera() != GL::Free) |
| 607 contextMenu->addSeparator(); |
611 { contextMenu->addSeparator(); |
| 608 contextMenu->addAction (ACTION (SetDrawDepth)); |
612 contextMenu->addAction (ACTION (SetDrawDepth)); |
| 609 } |
613 } |
| 610 |
614 |
| 611 contextMenu->exec (pos); |
615 contextMenu->exec (pos); |
| 612 } |
616 } |
| 613 |
617 |
| 614 // ============================================================================= |
618 // ============================================================================= |
| 615 // ----------------------------------------------------------------------------- |
619 // ----------------------------------------------------------------------------- |
| 616 void ForgeWindow::deleteObjVector (List<LDObject*> objs) { |
620 void ForgeWindow::deleteObjVector (List<LDObject*> objs) |
| 617 for (LDObject* obj : objs) { |
621 { for (LDObject * obj : objs) |
| 618 LDFile::current()->forgetObject (obj); |
622 { LDFile::current()->forgetObject (obj); |
| 619 delete obj; |
623 delete obj; |
| 620 } |
624 } |
| 621 } |
625 } |
| 622 |
626 |
| 623 // ============================================================================= |
627 // ============================================================================= |
| 624 // ----------------------------------------------------------------------------- |
628 // ----------------------------------------------------------------------------- |
| 625 void ForgeWindow::deleteByColor (const short colnum) { |
629 void ForgeWindow::deleteByColor (const short colnum) |
| 626 List<LDObject*> objs; |
630 { List<LDObject*> objs; |
| 627 for (LDObject* obj : LDFile::current()->objects()) { |
631 |
| 628 if (!obj->isColored() || obj->color() != colnum) |
632 for (LDObject * obj : LDFile::current()->objects()) |
| |
633 { if (!obj->isColored() || obj->color() != colnum) |
| 629 continue; |
634 continue; |
| 630 |
635 |
| 631 objs << obj; |
636 objs << obj; |
| 632 } |
637 } |
| 633 |
638 |
| 634 deleteObjVector (objs); |
639 deleteObjVector (objs); |
| 635 } |
640 } |
| 636 |
641 |
| 637 // ============================================================================= |
642 // ============================================================================= |
| 638 // ----------------------------------------------------------------------------- |
643 // ----------------------------------------------------------------------------- |
| 639 void ForgeWindow::updateEditModeActions() { |
644 void ForgeWindow::updateEditModeActions() |
| 640 const EditMode mode = R()->editMode(); |
645 { const EditMode mode = R()->editMode(); |
| 641 ACTION (ModeSelect)->setChecked (mode == Select); |
646 ACTION (ModeSelect)->setChecked (mode == Select); |
| 642 ACTION (ModeDraw)->setChecked (mode == Draw); |
647 ACTION (ModeDraw)->setChecked (mode == Draw); |
| 643 ACTION (ModeCircle)->setChecked (mode == CircleMode); |
648 ACTION (ModeCircle)->setChecked (mode == CircleMode); |
| 644 } |
649 } |
| 645 |
650 |
| 646 // ============================================================================= |
651 // ============================================================================= |
| 647 // ----------------------------------------------------------------------------- |
652 // ----------------------------------------------------------------------------- |
| 648 void ForgeWindow::slot_editObject (QListWidgetItem* listitem) { |
653 void ForgeWindow::slot_editObject (QListWidgetItem* listitem) |
| 649 LDObject* obj = null; |
654 { LDObject* obj = null; |
| 650 for (LDObject* it : LDFile::current()->objects()) { |
655 |
| 651 if (it->qObjListEntry == listitem) { |
656 for (LDObject * it : LDFile::current()->objects()) |
| 652 obj = it; |
657 { if (it->qObjListEntry == listitem) |
| |
658 { obj = it; |
| 653 break; |
659 break; |
| 654 } |
660 } |
| 655 } |
661 } |
| 656 |
662 |
| 657 AddObjectDialog::staticDialog (obj->getType(), obj); |
663 AddObjectDialog::staticDialog (obj->getType(), obj); |
| 658 } |
664 } |
| 659 |
665 |
| 660 // ============================================================================= |
666 // ============================================================================= |
| 661 // ----------------------------------------------------------------------------- |
667 // ----------------------------------------------------------------------------- |
| 662 void ForgeWindow::primitiveLoaderStart (ulong max) { |
668 void ForgeWindow::primitiveLoaderStart (ulong max) |
| 663 m_primLoaderWidget->show(); |
669 { m_primLoaderWidget->show(); |
| 664 m_primLoaderBar->setRange (0, max); |
670 m_primLoaderBar->setRange (0, max); |
| 665 m_primLoaderBar->setValue (0); |
671 m_primLoaderBar->setValue (0); |
| 666 m_primLoaderBar->setFormat ("%p%"); |
672 m_primLoaderBar->setFormat ("%p%"); |
| 667 } |
673 } |
| 668 |
674 |
| 669 // ============================================================================= |
675 // ============================================================================= |
| 670 // ----------------------------------------------------------------------------- |
676 // ----------------------------------------------------------------------------- |
| 671 void ForgeWindow::primitiveLoaderUpdate (ulong prog) { |
677 void ForgeWindow::primitiveLoaderUpdate (ulong prog) |
| 672 m_primLoaderBar->setValue (prog); |
678 { m_primLoaderBar->setValue (prog); |
| 673 } |
679 } |
| 674 |
680 |
| 675 // ============================================================================= |
681 // ============================================================================= |
| 676 // ----------------------------------------------------------------------------- |
682 // ----------------------------------------------------------------------------- |
| 677 void ForgeWindow::primitiveLoaderEnd() { |
683 void ForgeWindow::primitiveLoaderEnd() |
| 678 QTimer* hidetimer = new QTimer; |
684 { QTimer* hidetimer = new QTimer; |
| 679 connect (hidetimer, SIGNAL (timeout()), m_primLoaderWidget, SLOT (hide())); |
685 connect (hidetimer, SIGNAL (timeout()), m_primLoaderWidget, SLOT (hide())); |
| 680 hidetimer->setSingleShot (true); |
686 hidetimer->setSingleShot (true); |
| 681 hidetimer->start (1500); |
687 hidetimer->start (1500); |
| 682 m_primLoaderBar->setFormat (tr ("Done")); |
688 m_primLoaderBar->setFormat (tr ("Done")); |
| 683 log (tr ("Primitives scanned: %1 primitives listed"), m_primLoaderBar->value()); |
689 log (tr ("Primitives scanned: %1 primitives listed"), m_primLoaderBar->value()); |
| 684 } |
690 } |
| 685 |
691 |
| 686 // ============================================================================= |
692 // ============================================================================= |
| 687 // ----------------------------------------------------------------------------- |
693 // ----------------------------------------------------------------------------- |
| 688 void ForgeWindow::save (LDFile* f, bool saveAs) { |
694 void ForgeWindow::save (LDFile* f, bool saveAs) |
| 689 str path = f->name(); |
695 { str path = f->name(); |
| 690 |
696 |
| 691 if (path.length() == 0 || saveAs) { |
697 if (path.length() == 0 || saveAs) |
| 692 path = QFileDialog::getSaveFileName (g_win, tr ("Save As"), |
698 { path = QFileDialog::getSaveFileName (g_win, tr ("Save As"), |
| 693 LDFile::current()->name(), tr ("LDraw files (*.dat *.ldr)")); |
699 LDFile::current()->name(), tr ("LDraw files (*.dat *.ldr)")); |
| 694 |
700 |
| 695 if (path.length() == 0) { |
701 if (path.length() == 0) |
| 696 // User didn't give a file name. This happens if the user cancelled |
702 { // User didn't give a file name. This happens if the user cancelled |
| 697 // saving in the save file dialog. Abort. |
703 // saving in the save file dialog. Abort. |
| 698 return; |
704 return; |
| 699 } |
705 } |
| 700 } |
706 } |
| 701 |
707 |
| 702 if (f->save (path)) { |
708 if (f->save (path)) |
| 703 f->setName (path); |
709 { f->setName (path); |
| 704 |
710 |
| 705 if (f == LDFile::current()) |
711 if (f == LDFile::current()) |
| 706 g_win->updateTitle(); |
712 g_win->updateTitle(); |
| 707 |
713 |
| 708 log ("Saved to %1.", path); |
714 log ("Saved to %1.", path); |
| 709 |
715 |
| 710 // Add it to recent files |
716 // Add it to recent files |
| 711 addRecentFile (path); |
717 addRecentFile (path); |
| 712 } else { |
718 } |
| 713 str message = fmt (tr ("Failed to save to %1: %2"), path, strerror (errno)); |
719 else |
| 714 |
720 { str message = fmt (tr ("Failed to save to %1: %2"), path, strerror (errno)); |
| |
721 |
| 715 // Tell the user the save failed, and give the option for saving as with it. |
722 // Tell the user the save failed, and give the option for saving as with it. |
| 716 QMessageBox dlg (QMessageBox::Critical, tr ("Save Failure"), message, QMessageBox::Close, g_win); |
723 QMessageBox dlg (QMessageBox::Critical, tr ("Save Failure"), message, QMessageBox::Close, g_win); |
| 717 |
724 |
| 718 // Add a save-as button |
725 // Add a save-as button |
| 719 QPushButton* saveAsBtn = new QPushButton (tr ("Save As")); |
726 QPushButton* saveAsBtn = new QPushButton (tr ("Save As")); |
| 720 saveAsBtn->setIcon (getIcon ("file-save-as")); |
727 saveAsBtn->setIcon (getIcon ("file-save-as")); |
| 721 dlg.addButton (saveAsBtn, QMessageBox::ActionRole); |
728 dlg.addButton (saveAsBtn, QMessageBox::ActionRole); |
| 722 dlg.setDefaultButton (QMessageBox::Close); |
729 dlg.setDefaultButton (QMessageBox::Close); |
| 723 dlg.exec(); |
730 dlg.exec(); |
| 724 |
731 |
| 725 if (dlg.clickedButton() == saveAsBtn) |
732 if (dlg.clickedButton() == saveAsBtn) |
| 726 save (f, true); // yay recursion! |
733 save (f, true); // yay recursion! |
| 727 } |
734 } |
| 728 } |
735 } |
| 729 |
736 |
| 730 void ForgeWindow::addMessage (str msg) { |
737 void ForgeWindow::addMessage (str msg) |
| 731 m_msglog->addLine (msg); |
738 { m_msglog->addLine (msg); |
| 732 } |
739 } |
| 733 |
740 |
| 734 // ============================================================================ |
741 // ============================================================================ |
| 735 void ObjectList::contextMenuEvent (QContextMenuEvent* ev) { |
742 void ObjectList::contextMenuEvent (QContextMenuEvent* ev) |
| 736 g_win->spawnContextMenu (ev->globalPos()); |
743 { g_win->spawnContextMenu (ev->globalPos()); |
| 737 } |
744 } |
| 738 |
745 |
| 739 // ============================================================================= |
746 // ============================================================================= |
| 740 // ----------------------------------------------------------------------------- |
747 // ----------------------------------------------------------------------------- |
| 741 QPixmap getIcon (str iconName) { |
748 QPixmap getIcon (str iconName) |
| 742 return (QPixmap (fmt (":/icons/%1.png", iconName))); |
749 { return (QPixmap (fmt (":/icons/%1.png", iconName))); |
| 743 } |
750 } |
| 744 |
751 |
| 745 // ============================================================================= |
752 // ============================================================================= |
| 746 bool confirm (str msg) { |
753 bool confirm (str msg) |
| 747 return confirm (ForgeWindow::tr ("Confirm"), msg); |
754 { return confirm (ForgeWindow::tr ("Confirm"), msg); |
| 748 } |
755 } |
| 749 |
756 |
| 750 bool confirm (str title, str msg) { |
757 bool confirm (str title, str msg) |
| 751 return QMessageBox::question (g_win, title, msg, |
758 { return QMessageBox::question (g_win, title, msg, |
| 752 (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes; |
759 (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes; |
| 753 } |
760 } |
| 754 |
761 |
| 755 // ============================================================================= |
762 // ============================================================================= |
| 756 void critical (str msg) { |
763 void critical (str msg) |
| 757 QMessageBox::critical (g_win, ForgeWindow::tr("Error"), msg, |
764 { QMessageBox::critical (g_win, ForgeWindow::tr ("Error"), msg, |
| 758 (QMessageBox::Close), QMessageBox::Close); |
765 (QMessageBox::Close), QMessageBox::Close); |
| 759 } |
766 } |
| 760 |
767 |
| 761 // ============================================================================= |
768 // ============================================================================= |
| 762 QIcon makeColorIcon (LDColor* colinfo, const ushort size) { |
769 QIcon makeColorIcon (LDColor* colinfo, const ushort size) |
| 763 // Create an image object and link a painter to it. |
770 { // Create an image object and link a painter to it. |
| 764 QImage img (size, size, QImage::Format_ARGB32); |
771 QImage img (size, size, QImage::Format_ARGB32); |
| 765 QPainter paint (&img); |
772 QPainter paint (&img); |
| 766 |
773 |
| 767 QColor col = colinfo->faceColor; |
774 QColor col = colinfo->faceColor; |
| 768 if (colinfo->index == maincolor) { |
775 |
| 769 // Use the user preferences for main color here |
776 if (colinfo->index == maincolor) |
| |
777 { // Use the user preferences for main color here |
| 770 col = gl_maincolor.value; |
778 col = gl_maincolor.value; |
| 771 col.setAlphaF (gl_maincolor_alpha); |
779 col.setAlphaF (gl_maincolor_alpha); |
| 772 } |
780 } |
| 773 |
781 |
| 774 // Paint the icon |
782 // Paint the icon |
| 775 paint.fillRect (QRect (0, 0, size, size), colinfo->edgeColor); |
783 paint.fillRect (QRect (0, 0, size, size), colinfo->edgeColor); |
| 776 paint.drawPixmap (QRect (1, 1, size - 2, size - 2), getIcon ("checkerboard"), QRect (0, 0, 8, 8)); |
784 paint.drawPixmap (QRect (1, 1, size - 2, size - 2), getIcon ("checkerboard"), QRect (0, 0, 8, 8)); |
| 777 paint.fillRect (QRect (1, 1, size - 2, size - 2), col); |
785 paint.fillRect (QRect (1, 1, size - 2, size - 2), col); |
| 778 return QIcon (QPixmap::fromImage (img)); |
786 return QIcon (QPixmap::fromImage (img)); |
| 779 } |
787 } |
| 780 |
788 |
| 781 // ============================================================================= |
789 // ============================================================================= |
| 782 void makeColorSelector (QComboBox* box) { |
790 void makeColorSelector (QComboBox* box) |
| 783 std::map<short, ulong> counts; |
791 { std::map<short, ulong> counts; |
| 784 |
792 |
| 785 for (LDObject* obj : LDFile::current()->objects()) { |
793 for (LDObject * obj : LDFile::current()->objects()) |
| 786 if (!obj->isColored()) |
794 { if (!obj->isColored()) |
| 787 continue; |
795 continue; |
| 788 |
796 |
| 789 if (counts.find (obj->color()) == counts.end()) |
797 if (counts.find (obj->color()) == counts.end()) |
| 790 counts[obj->color()] = 1; |
798 counts[obj->color()] = 1; |
| 791 else |
799 else |
| 792 counts[obj->color()]++; |
800 counts[obj->color()]++; |
| 793 } |
801 } |
| 794 |
802 |
| 795 box->clear(); |
803 box->clear(); |
| 796 ulong row = 0; |
804 ulong row = 0; |
| 797 for (const auto& pair : counts) { |
805 |
| 798 LDColor* col = getColor (pair.first); |
806 for (const auto & pair : counts) |
| |
807 { LDColor* col = getColor (pair.first); |
| 799 assert (col != null); |
808 assert (col != null); |
| 800 |
809 |
| 801 QIcon ico = makeColorIcon (col, 16); |
810 QIcon ico = makeColorIcon (col, 16); |
| 802 box->addItem (ico, fmt ("[%1] %2 (%3 object%4)", |
811 box->addItem (ico, fmt ("[%1] %2 (%3 object%4)", |
| 803 pair.first, col->name, pair.second, plural (pair.second))); |
812 pair.first, col->name, pair.second, plural (pair.second))); |
| 804 box->setItemData (row, pair.first); |
813 box->setItemData (row, pair.first); |
| 805 |
814 |
| 806 ++row; |
815 ++row; |
| 807 } |
816 } |
| 808 } |
817 } |
| 809 |
818 |
| 810 void ForgeWindow::setStatusBarText (str text) { |
819 void ForgeWindow::setStatusBarText (str text) |
| 811 statusBar()->showMessage (text); |
820 { statusBar()->showMessage (text); |
| 812 } |
821 } |
| 813 |
822 |
| 814 void ForgeWindow::clearSelection() { |
823 void ForgeWindow::clearSelection() |
| 815 m_sel.clear(); |
824 { m_sel.clear(); |
| 816 } |
825 } |
| 817 |
826 |
| 818 Ui_LDForgeUI* ForgeWindow::interface() const { |
827 Ui_LDForgeUI* ForgeWindow::interface() const |
| 819 return ui; |
828 { return ui; |
| 820 } |
829 } |
| 821 |
830 |
| 822 #define act(N) QAction* ForgeWindow::action##N() { return ui->action##N; } |
831 #define act(N) QAction* ForgeWindow::action##N() { return ui->action##N; } |
| 823 #include "actions.h" |
832 #include "actions.h" |
| 824 |
833 |
| 825 void ForgeWindow::updateFileList() { |
834 void ForgeWindow::updateFileList() |
| 826 ui->fileList->clear(); |
835 { ui->fileList->clear(); |
| 827 |
836 |
| 828 for (LDFile* f : g_loadedFiles) { |
837 for (LDFile * f : g_loadedFiles) |
| 829 // Don't list implicit files unless explicitly desired. |
838 { // Don't list implicit files unless explicitly desired. |
| 830 if (f->implicit() && !gui_implicitfiles) |
839 if (f->implicit() && !gui_implicitfiles) |
| 831 continue; |
840 continue; |
| 832 |
841 |
| 833 // Add an item to the list for this file and store a pointer to it in |
842 // Add an item to the list for this file and store a pointer to it in |
| 834 // the file, so we can find files by the list item. |
843 // the file, so we can find files by the list item. |
| 835 ui->fileList->addItem (""); |
844 ui->fileList->addItem (""); |
| 836 QListWidgetItem* item = ui->fileList->item (ui->fileList->count() - 1); |
845 QListWidgetItem* item = ui->fileList->item (ui->fileList->count() - 1); |
| 837 f->setListItem (item); |
846 f->setListItem (item); |
| 838 |
847 |
| 839 updateFileListItem (f); |
848 updateFileListItem (f); |
| 840 } |
849 } |
| 841 } |
850 } |
| 842 |
851 |
| 843 void ForgeWindow::updateFileListItem (LDFile* f) { |
852 void ForgeWindow::updateFileListItem (LDFile* f) |
| 844 if (f->listItem() == null) { |
853 { if (f->listItem() == null) |
| 845 // We don't have a list item for this file, so the list either doesn't |
854 { // We don't have a list item for this file, so the list either doesn't |
| 846 // exist yet or is out of date. Build the list now. |
855 // exist yet or is out of date. Build the list now. |
| 847 updateFileList(); |
856 updateFileList(); |
| 848 return; |
857 return; |
| 849 } |
858 } |
| 850 |
859 |
| 851 // If this is the current file, it also needs to be the selected item on |
860 // If this is the current file, it also needs to be the selected item on |
| 852 // the list. |
861 // the list. |
| 853 if (f == LDFile::current()) |
862 if (f == LDFile::current()) |
| 854 ui->fileList->setCurrentItem (f->listItem()); |
863 ui->fileList->setCurrentItem (f->listItem()); |
| 855 |
864 |
| 856 // If we list implicit files, draw them with a shade of gray to make them |
865 // If we list implicit files, draw them with a shade of gray to make them |
| 857 // distinct. |
866 // distinct. |
| 858 if (f->implicit()) |
867 if (f->implicit()) |
| 859 f->listItem()->setForeground (QColor (96, 96, 96)); |
868 f->listItem()->setForeground (QColor (96, 96, 96)); |
| 860 |
869 |
| 861 f->listItem()->setText (f->getShortName()); |
870 f->listItem()->setText (f->getShortName()); |
| 862 |
871 |
| 863 // If the file has unsaved changes, draw a little icon next to it to mark that. |
872 // If the file has unsaved changes, draw a little icon next to it to mark that. |
| 864 f->listItem()->setIcon (f->hasUnsavedChanges() ? getIcon ("file-save") : QIcon()); |
873 f->listItem()->setIcon (f->hasUnsavedChanges() ? getIcon ("file-save") : QIcon()); |
| 865 } |
874 } |
| 866 |
875 |
| 867 void ForgeWindow::beginAction (QAction* act) { |
876 void ForgeWindow::beginAction (QAction* act) |
| 868 // Open the history so we can record the edits done during this action. |
877 { // Open the history so we can record the edits done during this action. |
| 869 if (act != ACTION (Undo) && act != ACTION (Redo) && act != ACTION (Open)) |
878 if (act != ACTION (Undo) && act != ACTION (Redo) && act != ACTION (Open)) |
| 870 LDFile::current()->openHistory(); |
879 LDFile::current()->openHistory(); |
| 871 } |
880 } |
| 872 |
881 |
| 873 void ForgeWindow::endAction() { |
882 void ForgeWindow::endAction() |
| 874 // Close the history now. |
883 { // Close the history now. |
| 875 LDFile::current()->closeHistory(); |
884 LDFile::current()->closeHistory(); |
| 876 |
885 |
| 877 // Update the list item of the current file - we may need to draw an icon |
886 // Update the list item of the current file - we may need to draw an icon |
| 878 // now that marks it as having unsaved changes. |
887 // now that marks it as having unsaved changes. |
| 879 updateFileListItem (LDFile::current()); |
888 updateFileListItem (LDFile::current()); |
| 880 } |
889 } |
| 881 |
890 |
| 882 // ============================================================================= |
891 // ============================================================================= |
| 883 // A file is selected from the list of files on the left of the screen. Find out |
892 // A file is selected from the list of files on the left of the screen. Find out |
| 884 // which file was picked and change to it. |
893 // which file was picked and change to it. |
| 885 void ForgeWindow::changeCurrentFile() { |
894 void ForgeWindow::changeCurrentFile() |
| 886 LDFile* f = null; |
895 { LDFile* f = null; |
| 887 QListWidgetItem* item = ui->fileList->currentItem(); |
896 QListWidgetItem* item = ui->fileList->currentItem(); |
| 888 |
897 |
| 889 // Find the file pointer of the item that was selected. |
898 // Find the file pointer of the item that was selected. |
| 890 for (LDFile* it : g_loadedFiles) { |
899 for (LDFile * it : g_loadedFiles) |
| 891 if (it->listItem() == item) { |
900 { if (it->listItem() == item) |
| 892 f = it; |
901 { f = it; |
| 893 break; |
902 break; |
| 894 } |
903 } |
| 895 } |
904 } |
| 896 |
905 |
| 897 // If we picked the same file we're currently on, we don't need to do |
906 // If we picked the same file we're currently on, we don't need to do |
| 898 // anything. |
907 // anything. |
| 899 if (!f || f == LDFile::current()) |
908 if (!f || f == LDFile::current()) |
| 900 return; |
909 return; |
| 901 |
910 |
| 902 LDFile::setCurrent (f); |
911 LDFile::setCurrent (f); |
| 903 } |
912 } |
| 904 |
913 |
| 905 void ForgeWindow::refreshObjectList() { |
914 void ForgeWindow::refreshObjectList() |
| |
915 { |
| 906 #if 0 |
916 #if 0 |
| 907 ui->objectList->clear(); |
917 ui->objectList->clear(); |
| 908 LDFile* f = LDFile::current(); |
918 LDFile* f = LDFile::current(); |
| 909 |
919 |
| 910 for (LDObject* obj : *f) |
920 for (LDObject * obj : *f) |
| 911 ui->objectList->addItem (obj->qObjListEntry); |
921 ui->objectList->addItem (obj->qObjListEntry); |
| |
922 |
| 912 #endif |
923 #endif |
| 913 |
924 |
| 914 buildObjList(); |
925 buildObjList(); |
| 915 } |
926 } |
| 916 |
927 |
| 917 QImage imageFromScreencap (uchar* data, ushort w, ushort h) { |
928 QImage imageFromScreencap (uchar* data, ushort w, ushort h) |
| 918 // GL and Qt formats have R and B swapped. Also, GL flips Y - correct it as well. |
929 { // GL and Qt formats have R and B swapped. Also, GL flips Y - correct it as well. |
| 919 return QImage (data, w, h, QImage::Format_ARGB32).rgbSwapped().mirrored(); |
930 return QImage (data, w, h, QImage::Format_ARGB32).rgbSwapped().mirrored(); |
| 920 } |
931 } |
| 921 |
932 |
| 922 // ============================================================================= |
933 // ============================================================================= |
| 923 // ----------------------------------------------------------------------------- |
934 // ----------------------------------------------------------------------------- |
| 924 LDQuickColor::LDQuickColor (LDColor* color, QToolButton* toolButton) : |
935 LDQuickColor::LDQuickColor (LDColor* color, QToolButton* toolButton) : |
| 925 m_color (color), |
936 m_color (color), |
| 926 m_toolButton (toolButton) {} |
937 m_toolButton (toolButton) {} |
| 927 |
938 |
| 928 LDQuickColor LDQuickColor::getSeparator() { |
939 LDQuickColor LDQuickColor::getSeparator() |
| 929 return LDQuickColor (null, null); |
940 { return LDQuickColor (null, null); |
| 930 } |
941 } |
| 931 |
942 |
| 932 bool LDQuickColor::isSeparator() const { |
943 bool LDQuickColor::isSeparator() const |
| 933 return color() == null; |
944 { return color() == null; |
| 934 } |
945 } |
| 935 #include "moc_gui.cpp" |
|