OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4ConsoleQtState.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2013, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 /* Editor windows using Qt*/
18 
19 #include "C4Include.h"
27 #include "editor/C4Console.h"
28 #include "platform/StdRegistry.h"
29 #include "landscape/C4Landscape.h"
30 #include "player/C4PlayerList.h"
31 #include "object/C4Def.h"
32 #include "object/C4Object.h"
33 #include "game/C4Viewport.h"
34 #include "config/C4Config.h"
35 
36 #ifdef USE_WIN32_WINDOWS
37 #include <shellapi.h>
38 #endif
39 
40 /* String translation */
41 
42 QString C4ConsoleQtTranslator::translate(const char * context, const char * sourceText, const char * disambiguation, int n) const
43 {
44  // Map to LoadResStr for all QStrings marked as res
45  if (disambiguation && !strcmp(disambiguation, "res"))
46  return QString(LoadResStr(sourceText));
47  else
48  return QString(sourceText);
49 }
50 
51 C4ConsoleQtTranslator qt_translator;
52 
53 /* Kick client action */
54 
55 C4ConsoleClientAction::C4ConsoleClientAction(int32_t client_id, const char *text, QObject *parent, C4ConsoleGUI::ClientOperation op)
56  : QAction(text, parent), client_id(client_id), op(op)
57 {
58  connect(this, SIGNAL(triggered()), this, SLOT(Execute()));
59 }
60 
61 void C4ConsoleClientAction::Execute()
62 {
63  if (!::Control.isCtrlHost()) return;
64  switch (op)
65  {
68  break;
71  break;
73  ::Game.Clients.CtrlRemove(Game.Clients.getClientByID(client_id), LoadResStr("IDS_MSG_KICKBYMENU"));
74  break;
75  }
76 }
77 
78 
79 /* Remove player action */
80 
81 C4ConsoleRemovePlayerAction::C4ConsoleRemovePlayerAction(int32_t player_num, const char *text, QObject *parent)
82  : QAction(text, parent), player_num(player_num)
83 {
84  connect(this, SIGNAL(triggered()), this, SLOT(Execute()));
85 }
86 
87 void C4ConsoleRemovePlayerAction::Execute()
88 {
89  C4Player *plr = ::Players.Get(player_num);
90  if (!plr) return;
92 }
93 
94 
95 /* Add viewport for player action */
96 
97 C4ConsoleOpenViewportAction::C4ConsoleOpenViewportAction(int32_t player_num, const char *text, QObject *parent)
98  : QAction(text, parent), player_num(player_num)
99 {
100  connect(this, SIGNAL(triggered()), this, SLOT(Execute()));
101 }
102 
103 void C4ConsoleOpenViewportAction::Execute()
104 {
105  ::Viewports.CreateViewport(player_num);
106 }
107 
108 
109 /* Recursion check to avoid some crashing Qt re-entry */
110 
112 {
113  static int counter;
114 public:
115  ExecRecursionCheck() { ++counter; }
116  ~ExecRecursionCheck() { --counter; }
117 
118  bool IsRecursion() const { return counter > 1; }
119 };
120 
121 int ExecRecursionCheck::counter = 0;
122 
123 
124 /* Console main window */
125 
126 C4ConsoleQtMainWindow::C4ConsoleQtMainWindow(C4AbstractApp *app, C4ConsoleGUIState *state)
127  : QMainWindow(nullptr), state(state)
128 {
129 }
130 
131 void C4ConsoleQtMainWindow::keyPressEvent(QKeyEvent * event)
132 {
133  if (HandleEditorKeyDown(event)) event->setAccepted(true);
134  QMainWindow::keyPressEvent(event);
135 }
136 
137 void C4ConsoleQtMainWindow::keyReleaseEvent(QKeyEvent * event)
138 {
139  if (HandleEditorKeyUp(event)) event->setAccepted(true);
140  QMainWindow::keyPressEvent(event);
141 }
142 
143 // Editor window state config file
145 {
147 
149  {
150  comp->Value(geometry);
151  comp->Value(window_state);
152  }
153 };
154 
155 void C4ConsoleQtMainWindow::closeEvent(QCloseEvent *event)
156 {
157  // Store window settings
159  QByteArray geometry = saveGeometry(), window_state = saveState();
160  ws.geometry.Copy(geometry.constData(), geometry.size());
161  ws.window_state.Copy(window_state.constData(), window_state.size());
162  StdBuf ws_contents = DecompileToBuf<StdCompilerBinWrite>(ws);
164  // Perform close
165  QMainWindow::closeEvent(event);
166  ::Console.Close();
167 }
168 
169 void C4ConsoleQtMainWindow::LoadGeometry()
170 {
171  // Restore window settings from file
172  StdBuf ws_contents;
174  {
175  try
176  {
178  CompileFromBuf<StdCompilerBinRead>(ws, ws_contents);
179  QByteArray geometry(static_cast<const char *>(ws.geometry.getData()), ws.geometry.getSize()),
180  window_state(static_cast<const char *>(ws.window_state.getData()), ws.window_state.getSize());
181  restoreGeometry(geometry);
182  restoreState(window_state);
183  }
184  catch (StdCompiler::Exception *e)
185  {
186  Log("Editor: Could not restore window settings");
187  delete e;
188  }
189  }
190 }
191 
192 void C4ConsoleQtMainWindow::PlayPressed(bool down)
193 {
194  if (down)
195  ::Console.DoPlay();
196  else // cannot un-check by pressing again
197  state->ui.actionPlay->setChecked(true);
198 }
199 
200 void C4ConsoleQtMainWindow::PausePressed(bool down)
201 {
202  if (down)
203  ::Console.DoHalt();
204  else // can un-check by pressing again!
205  ::Console.DoPlay();
206 }
207 
208 void C4ConsoleQtMainWindow::CursorGamePressed(bool down)
209 {
210  if (down)
211  {
213  }
214  else
215  {
216  // cannot un-check by pressing again
217  state->ui.actionCursorGame->setChecked(true);
218  }
219 }
220 
221 void C4ConsoleQtMainWindow::CursorSelectPressed(bool down)
222 {
223  if (down)
224  {
226  // When the select cursor is activated, always show either the property dock
227  state->ui.propertyDockWidget->raise();
228  }
229  else
230  {
231  // cannot un-check by pressing again
232  state->ui.actionCursorSelect->setChecked(true);
233  }
234 }
235 
236 void C4ConsoleQtMainWindow::CursorCreateObjPressed(bool down)
237 {
238  if (down)
239  {
241  // When the creator cursor is activated, always show the defintiion list
242  state->ui.creatorDockWidget->raise();
243  }
244  else
245  {
246  // cannot un-check by pressing again
247  state->ui.actionCursorCreateObj->setChecked(true);
248  }
249 }
250 
251 void C4ConsoleQtMainWindow::CursorDrawPenPressed(bool down)
252 {
253  if (down)
254  {
257  }
258  else // cannot un-check by pressing again
259  state->ui.actionCursorDrawPen->setChecked(true);
260 }
261 
262 void C4ConsoleQtMainWindow::CursorDrawLinePressed(bool down)
263 {
264  if (down)
265  {
268  }
269  else // cannot un-check by pressing again
270  state->ui.actionCursorDrawLine->setChecked(true);
271 }
272 
273 void C4ConsoleQtMainWindow::CursorDrawRectPressed(bool down)
274 {
275  if (down)
276  {
279  }
280  else // cannot un-check by pressing again
281  state->ui.actionCursorDrawRect->setChecked(true);
282 }
283 
284 void C4ConsoleQtMainWindow::CursorFillPressed(bool down)
285 {
286  if (down)
287  {
290  }
291  else // cannot un-check by pressing again
292  state->ui.actionCursorFill->setChecked(true);
293 }
294 
295 
296 void C4ConsoleQtMainWindow::CursorPickerPressed(bool down)
297 {
298  if (down)
299  {
302  }
303  else // cannot un-check by pressing again
304  state->ui.actionCursorPicker->setChecked(true);
305 }
306 
307 void C4ConsoleQtMainWindow::DynamicLandscapePressed(bool down)
308 {
309  if (down)
311  else // cannot un-check by pressing again
312  state->ui.actionDynamicLandscape->setChecked(true);
313 }
314 
315 void C4ConsoleQtMainWindow::StaticLandscapePressed(bool down)
316 {
317  if (down)
319  else // cannot un-check by pressing again
320  state->ui.actionStaticLandscape->setChecked(true);
321 }
322 
323 void C4ConsoleQtMainWindow::StaticFlatLandscapePressed(bool down)
324 {
325  if (down)
327  else // cannot un-check by pressing again
328  state->ui.actionStaticFlatLandscape->setChecked(true);
329 }
330 
331 void C4ConsoleQtMainWindow::ExactLandscapePressed(bool down)
332 {
333  if (down)
335  else // cannot un-check by pressing again
336  state->ui.actionExactLandscape->setChecked(true);
337 }
338 
339 void C4ConsoleQtMainWindow::DrawSizeChanged(int newval)
340 {
341  ::Console.ToolsDlg.SetGrade(newval);
342 }
343 
344 // File menu
345 void C4ConsoleQtMainWindow::FileNew() { ::Console.FileNew(); }
346 void C4ConsoleQtMainWindow::FileOpen() { ::Console.FileOpen(nullptr, false); }
347 void C4ConsoleQtMainWindow::FileOpenInNetwork() { ::Console.FileOpen(nullptr, true); }
348 void C4ConsoleQtMainWindow::FileOpenWithPlayers() { Console.FileOpenWPlrs(); }
349 void C4ConsoleQtMainWindow::FileRecord() { ::Console.FileRecord(); }
350 void C4ConsoleQtMainWindow::FileSave() { ::Console.FileSave(); }
351 void C4ConsoleQtMainWindow::FileSaveAs() { ::Console.FileSaveAs(false); }
352 void C4ConsoleQtMainWindow::FileSaveGameAs() { ::Console.FileSaveAs(true); }
353 void C4ConsoleQtMainWindow::FileExportPacked() { ::Console.FileSaveAs(false, true); }
354 void C4ConsoleQtMainWindow::FileClose() { ::Console.FileClose(); }
355 void C4ConsoleQtMainWindow::FileQuit() { ::Console.FileQuit(); }
356 
357 void C4ConsoleQtMainWindow::FileReInitScenario()
358 {
360 }
361 
362 // Player menu
363 void C4ConsoleQtMainWindow::PlayerJoin() { ::Console.PlayerJoin(); }
364 // Window menu
365 void C4ConsoleQtMainWindow::ViewportNew() { ::Console.ViewportNew(); }
366 // Help menu
367 void C4ConsoleQtMainWindow::HelpAbout() { ::Console.HelpAbout(); }
368 
369 void C4ConsoleQtMainWindow::HelpToggle(bool enabled)
370 {
371  ::Config.Developer.ShowHelp = enabled;
373  repaint();
374 }
375 
376 // Script enter
377 void C4ConsoleQtMainWindow::MainConsoleEditEnter()
378 {
379  QLineEdit *main_console_edit = state->ui.consoleInputBox->lineEdit();
380  ::Console.RegisterRecentInput(main_console_edit->text().toUtf8(), C4Console::MRU_Scenario);
381  ::Console.In(main_console_edit->text().toUtf8());
382 }
383 
384 void C4ConsoleQtMainWindow::PropertyConsoleEditEnter()
385 {
386  QLineEdit *property_console_edit = state->ui.propertyInputBox->lineEdit();
387  ::Console.EditCursor.In(property_console_edit->text().toUtf8());
388 }
389 
390 // View selection changes
391 void C4ConsoleQtMainWindow::OnCreatorSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
392 {
393  state->OnCreatorSelectionChanged(selected, deselected);
394 }
395 
396 void C4ConsoleQtMainWindow::OnCreatorCurrentChanged(const QModelIndex & current, const QModelIndex & previous)
397 {
398  state->OnCreatorCurrentChanged(current, previous);
399 }
400 
401 void C4ConsoleQtMainWindow::OnObjectListSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
402 {
403  state->OnObjectListSelectionChanged(selected, deselected);
404 }
405 
406 void C4ConsoleQtMainWindow::AscendPropertyPath()
407 {
408  state->property_model->AscendPath();
410 }
411 
412 void C4ConsoleQtMainWindow::AddArrayElement()
413 {
414  if (state->property_model) state->property_model->AddArrayElement();
415 }
416 
417 void C4ConsoleQtMainWindow::RemoveArrayElement()
418 {
419  if (state->property_model) state->property_model->RemoveArrayElement();
420 }
421 
422 
423 bool C4ConsoleQtMainWindow::HandleEditorKeyDown(QKeyEvent *event)
424 {
425  switch (event->key())
426  {
427  case Qt::Key_Delete:
429  return true;
430  case Qt::Key_F2:
432  return true;
433  }
434  uint32_t shift = 0;
435  if (event->modifiers() & Qt::AltModifier) shift |= MK_ALT;
436  if (event->modifiers() & Qt::ControlModifier) shift |= MK_CONTROL;
437  if (event->modifiers() & Qt::ShiftModifier) shift |= MK_SHIFT;
438  ::Console.EditCursor.KeyDown(event->nativeScanCode(), shift);
439  // key not handled (ignore shift handling done in EditCursor)
440  return false;
441 }
442 
443 bool C4ConsoleQtMainWindow::HandleEditorKeyUp(QKeyEvent *event)
444 {
445  uint32_t shift = 0;
446  if (event->modifiers() & Qt::AltModifier) shift |= MK_ALT;
447  if (event->modifiers() & Qt::ControlModifier) shift |= MK_CONTROL;
448  if (event->modifiers() & Qt::ShiftModifier) shift |= MK_SHIFT;
449  ::Console.EditCursor.KeyUp(event->nativeScanCode(), shift);
450  // key not handled (ignore shift handling done in EditCursor)
451  return false;
452 }
453 
454 void SplitMaterialTexture(const QString &mat_tex, QString *mat, QString *tex)
455 {
456  int sep = mat_tex.indexOf('-');
457  if (sep < 0)
458  {
459  *mat = mat_tex;
460  *tex = QString();
461  }
462  else
463  {
464  *mat = mat_tex.mid(0, sep);
465  *tex = mat_tex.mid(sep + 1);
466  }
467 }
468 
469 void C4ConsoleQtMainWindow::ForegroundMaterialChanged(const QString &new_selection)
470 {
471  QString mat, tex;
472  SplitMaterialTexture(new_selection, &mat, &tex);
473  if (mat.size() > 0) ::Console.ToolsDlg.SelectMaterial(mat.toUtf8(), true);
474  if (tex.size() > 0) ::Console.ToolsDlg.SelectTexture(tex.toUtf8(), true);
475 }
476 
477 void C4ConsoleQtMainWindow::BackgroundMaterialChanged(const QString &new_selection)
478 {
479  QString mat, tex;
480  SplitMaterialTexture(new_selection, &mat, &tex);
481  if (mat.size() > 0) ::Console.ToolsDlg.SelectBackMaterial(mat.toUtf8(), true);
482  if (tex.size() > 0) ::Console.ToolsDlg.SelectBackTexture(tex.toUtf8(), true);
483 }
484 
485 void C4ConsoleQtMainWindow::WelcomeLinkActivated(const QString &link)
486 {
487  // Default links
488  if (link == "new") FileNew();
489  else if (link == "open") FileOpen();
490  else if (link == "exploreuserpath")
491  {
492  bool success = false;
493 #ifdef USE_WIN32_WINDOWS
495  intptr_t iError = (intptr_t) ::ShellExecute(nullptr, L"open", path.GetWideChar(), nullptr, path.GetWideChar(), SW_SHOW);
496  if (iError > 32) success = true;
497 #else
498  success = QDesktopServices::openUrl(QUrl::fromLocalFile(::Config.General.UserDataPath));
499 #endif
500  if (!success)
501  QMessageBox::critical(this, LoadResStr("IDS_MNU_EXPLOREUSERPATH"), LoadResStr("IDS_ERR_EXPLOREUSERPATH"));
502  }
503  // Open recent link
504  else if (link.startsWith("open:"))
505  {
506  QString open_file = link.mid(5);
507  ::Console.FileOpen(open_file.toUtf8());
508  }
509 }
510 
511 void C4ConsoleQtMainWindow::SelectionDelete()
512 {
514 }
515 
516 void C4ConsoleQtMainWindow::SelectionDuplicate()
517 {
519 }
520 
521 void C4ConsoleQtMainWindow::SelectionEjectContents()
522 {
524 }
525 
526 void C4ConsoleQtMainWindow::FocusGlobalScriptBox()
527 {
528  state->ui.logDockWidget->show();
529  state->ui.logDockWidget->raise();
530  state->ui.consoleInputBox->setFocus();
531 }
532 
533 void C4ConsoleQtMainWindow::FocusObjectScriptBox()
534 {
535  state->ui.propertyDockWidget->show();
536  state->ui.propertyDockWidget->raise();
537  state->ui.propertyInputBox->setFocus();
538 }
539 
540 void C4ConsoleQtMainWindow::OpenMaterialSelection()
541 {
542  if (state->ui.foregroundMatTexComboBox->isEnabled())
543  {
544  state->ui.foregroundMatTexComboBox->setFocus();
545  state->ui.foregroundMatTexComboBox->showPopup();
546  }
547 }
548 
549 void C4ConsoleQtMainWindow::FocusNextViewport()
550 {
551  // Focus viewport after the one that has focus
552  bool has_focus_vp = false;
553  for (C4ConsoleQtViewportDockWidget *vp : state->viewports)
554  {
555  if (has_focus_vp)
556  {
557  vp->SetFocus();
558  return;
559  }
560  else if (vp->HasFocus())
561  {
562  has_focus_vp = true;
563  }
564  }
565  // No focus or last viewport was focused? Focus first.
566  if (state->viewports.size())
567  {
568  state->viewports.front()->SetFocus();
569  }
570 }
571 
572 void C4ConsoleQtMainWindow::GradeUp()
573 {
574  if (state->ui.drawSizeSlider->isEnabled())
575  {
576  state->ui.drawSizeSlider->setValue(state->ui.drawSizeSlider->value() + state->ui.drawSizeSlider->singleStep());
577  }
578 }
579 
580 void C4ConsoleQtMainWindow::GradeDown()
581 {
582  if (state->ui.drawSizeSlider->isEnabled())
583  {
584  state->ui.drawSizeSlider->setValue(state->ui.drawSizeSlider->value() - state->ui.drawSizeSlider->singleStep());
585  }
586 }
587 
588 
589 /* Common C4ConsoleGUI interface */
590 
591 C4ConsoleGUIState::C4ConsoleGUIState(C4ConsoleGUI *console) : viewport_area(nullptr),
592  enabled(false), recording(false), net_enabled(false), landscape_mode(LandscapeMode::Dynamic), flat_chunk_shapes(false),
593  editcursor_mode(C4CNS_ModePlay), drawing_tool(C4TLS_Brush), is_object_selection_updating(0), disable_shortcut_filter(new C4DisableShortcutFilter(nullptr))
594 {
595 }
596 
597 C4ConsoleGUIState::~C4ConsoleGUIState()
598 {
599 }
600 
601 void C4ConsoleGUIState::AddToolbarSpacer(int space)
602 {
603  auto spacer = new QWidget();
604  spacer->setFixedWidth(space);
605  ui.toolBar->addWidget(spacer);
606 }
607 
608 bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
609 {
610  // No Qt main loop execution during console creation
611  ExecRecursionCheck no_qt_recursion;
612 
613  // Initialize OpenGL.
614  QSurfaceFormat format;
615  format.setMajorVersion(/*REQUESTED_GL_CTX_MAJOR*/ 3);
616  format.setMinorVersion(/*REQUESTED_GL_CTX_MINOR*/ 2);
617  format.setRedBufferSize(8);
618  format.setGreenBufferSize(8);
619  format.setBlueBufferSize(8);
620  format.setDepthBufferSize(8);
621  format.setProfile(QSurfaceFormat::CoreProfile);
622  format.setSwapInterval(0); // turn off vsync because otherwise each viewport causes an extra 1/(refesh rate) delay
624  format.setOption(QSurfaceFormat::DebugContext);
625  QSurfaceFormat::setDefaultFormat(format);
626  QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
627 
628 
629  // Basic Qt+Main window setup from .ui file
630  // Note that QApplication needs at least one valid argument which must
631  // stay valid over the lifetime of the application.
632  static int fake_argc = 1;
633  static const char *fake_argv[] = { "openclonk" };
634  application.reset(new QApplication(fake_argc, const_cast<char **>(fake_argv)));
635  application->installTranslator(&qt_translator);
636  window.reset(new C4ConsoleQtMainWindow(app, this));
637  ui.setupUi(window.get());
638 
639  // Setup some extra stuff that cannot be done easily in the designer
640  // Divide status bar
641  status_cursor = new QLabel("", window.get());
642  status_framecounter = new QLabel("", window.get());
643  status_timefps = new QLabel("", window.get());
644  ui.statusbar->addPermanentWidget(status_cursor, 3);
645  ui.statusbar->addPermanentWidget(status_framecounter, 1);
646  ui.statusbar->addPermanentWidget(status_timefps, 1);
647  // Move drawing tools into toolbar
648  ui.toolBar->addWidget(ui.foregroundMatTexComboBox);
649  ui.toolBar->addWidget(ui.backgroundMatTexComboBox);
650  AddToolbarSpacer(5);
651  ui.toolBar->addWidget(ui.drawSizeLabel);
652  AddToolbarSpacer(5);
653  ui.toolBar->addWidget(ui.drawSizeSlider);
654  ui.drawSizeSlider->setMaximum(C4TLS_GradeMax);
655  ui.drawSizeSlider->setMinimum(C4TLS_GradeMin);
656  ui.drawSizeSlider->setValue(C4TLS_GradeDefault);
657  ui.drawSizeSlider->setSingleStep(1);
658  // Console input box signal
659  QLineEdit *main_console_edit = ui.consoleInputBox->lineEdit();
660  main_console_edit->completer()->setCaseSensitivity(Qt::CaseSensitivity::CaseSensitive);
661  main_console_edit->connect(main_console_edit, SIGNAL(returnPressed()), window.get(), SLOT(MainConsoleEditEnter()));
662  QLineEdit *property_console_edit = ui.propertyInputBox->lineEdit();
663  property_console_edit->connect(property_console_edit, SIGNAL(returnPressed()), window.get(), SLOT(PropertyConsoleEditEnter()));
664  property_console_edit->completer()->setCaseSensitivity(Qt::CaseSensitivity::CaseSensitive);
665  // Add window menu actions
666  window_menu_separator = ui.menuWindows->addSeparator();
667  QAction *dock_action = ui.creatorDockWidget->toggleViewAction();
668  dock_action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_1));
669  ui.menuWindows->addAction(dock_action);
670  dock_action = ui.objectListDockWidget->toggleViewAction();
671  dock_action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_2));
672  ui.menuWindows->addAction(dock_action);
673  dock_action = ui.propertyDockWidget->toggleViewAction();
674  dock_action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_3));
675  ui.menuWindows->addAction(dock_action);
676  dock_action = ui.logDockWidget->toggleViewAction();
677  dock_action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_4));
678  ui.menuWindows->addAction(dock_action);
679  // Viewport area setup
680  viewport_area = new QMainWindow();
681  viewport_area->setWindowFlags(Qt::Widget);
682  window->setCentralWidget(viewport_area);
683  window->setDockNestingEnabled(true);
684  viewport_area->setDockNestingEnabled(true);
685  QWidget *foo = new QWidget(viewport_area);
686  viewport_area->setCentralWidget(foo);
687  foo->hide();
688  // Default action state
689  ui.actionHelp->setChecked(::Config.Developer.ShowHelp);
690 
691  // Disable some shortcuts on actions that are handled internally
692  // (none right now)
693 
694  // Property editor
695  property_delegate_factory.reset(new C4PropertyDelegateFactory());
696  ui.propertyTable->setItemDelegateForColumn(1, property_delegate_factory.get());
697  ui.propertyEditAscendPathButton->setMaximumWidth(ui.propertyEditAscendPathButton->fontMetrics().boundingRect(ui.propertyEditAscendPathButton->text()).width() + 10);
698  ui.propertyTable->setDropIndicatorShown(true);
699  ui.propertyTable->setAcceptDrops(true);
700  property_name_delegate.reset(new C4PropertyNameDelegate());
701  ui.propertyTable->setItemDelegateForColumn(0, property_name_delegate.get());
702  ui.propertyTable->setMouseTracking(true);
703 
704  // View models
705  property_model.reset(new C4ConsoleQtPropListModel(property_delegate_factory.get()));
706  property_delegate_factory->SetPropertyModel(property_model.get());
707  property_name_delegate->SetPropertyModel(property_model.get());
708  QItemSelectionModel *m = ui.propertyTable->selectionModel();
709  ui.propertyTable->setModel(property_model.get());
710  delete m;
711  property_model->SetSelectionModel(ui.propertyTable->selectionModel());
712  object_list_model.reset(new C4ConsoleQtObjectListModel());
713  m = ui.objectListView->selectionModel();
714  ui.objectListView->setModel(object_list_model.get());
715  delete m;
716  window->connect(ui.objectListView->selectionModel(), &QItemSelectionModel::selectionChanged, window.get(), &C4ConsoleQtMainWindow::OnObjectListSelectionChanged);
717  definition_list_model.reset(new C4ConsoleQtDefinitionListModel());
718  property_delegate_factory->SetDefinitionListModel(definition_list_model.get());
719  m = ui.creatorTreeView->selectionModel();
720  ui.creatorTreeView->setModel(definition_list_model.get());
721  delete m;
722  window->connect(ui.creatorTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, window.get(), &C4ConsoleQtMainWindow::OnCreatorSelectionChanged);
723  window->connect(ui.creatorTreeView->selectionModel(), &QItemSelectionModel::currentChanged, window.get(), &C4ConsoleQtMainWindow::OnCreatorCurrentChanged);
724  window->connect(ui.propertyTable->selectionModel(), &QItemSelectionModel::currentChanged, window.get(), [this]() {
725  this->ui.arrayRemoveButton->setDisabled(this->property_model->IsTargetReadonly() || this->ui.propertyTable->selectionModel()->selectedRows().empty());
726  });
727 
728  // Double-clicking an item in the object list focuses and raises the property window
729  window->connect(ui.objectListView, &QTreeView::doubleClicked, window.get(), [this](const QModelIndex &index) {
730  window->FocusObjectScriptBox();
731  });
732 
733  // Initial layout is tabified (somehow I cannot do this in the designer)
734  window->tabifyDockWidget(ui.objectListDockWidget, ui.propertyDockWidget);
735  window->tabifyDockWidget(ui.objectListDockWidget, ui.creatorDockWidget);
736  ui.propertyDockWidget->raise();
737 
738  // Welcome page
739  InitWelcomeScreen();
740  ShowWelcomeScreen();
741 
742  // Initial empty property page
743  auto sel = C4EditCursorSelection();
744  PropertyDlgUpdate(sel, true);
745 
746  // Restore layout & show!
747  window->LoadGeometry();
748  window->show();
749  return true;
750 }
751 
752 void C4ConsoleGUIState::DeleteConsoleWindow()
753 {
754  // Reset to a state before CreateConsoleWindow was called
755  action_object = C4VNull;
756  is_object_selection_updating = false;
757 
758  editcursor_mode = C4CNS_ModePlay;
759  drawing_tool = C4TLS_Brush;
760  landscape_mode = LandscapeMode::Dynamic;
761  net_enabled = false;
762  recording = false;
763  enabled = false;
764 
765  window_menu_separator = nullptr;
766  status_cursor = status_framecounter = status_timefps = nullptr;
767 
768  while (!viewports.empty())
769  {
770  auto vp = viewports.front();
771  viewports.erase(viewports.begin());
772 
773  viewport_area->removeDockWidget(vp);
774  delete vp;
775  }
776 
777  client_actions.clear();
778  player_actions.clear();
779  viewport_actions.clear();
780  viewport_area = nullptr;
781 
782  disable_shortcut_filter.reset(nullptr);
783  definition_list_model.reset(nullptr);
784  object_list_model.reset(nullptr);
785  property_name_delegate.reset(nullptr);
786  property_delegate_factory.reset(nullptr);
787  property_model.reset(nullptr);
788  window.reset(nullptr);
789  application.reset(nullptr);
790 }
791 
792 void C4ConsoleGUIState::Execute(bool redraw_only)
793 {
794  // Nothing to do - Qt's event loop is handling everything.
795 }
796 
797 // Set action pressed/checked and enabled states
798 void C4ConsoleGUIState::UpdateActionStates()
799 {
800  // Enabled states
801  bool has_draw_tools = enabled && landscape_mode != LandscapeMode::Dynamic;
802  bool has_exact_draw_tools = enabled && landscape_mode == LandscapeMode::Exact;
803  bool is_drawing = has_draw_tools && editcursor_mode == C4CNS_ModeDraw;
804  bool is_lobby = ::Network.isLobbyActive();
805  ui.actionFileNew->setEnabled(!enabled);
806  ui.actionFileReInitScenario->setEnabled(enabled);
807  ui.actionPlay->setEnabled(enabled || is_lobby);
808  ui.actionPause->setEnabled(enabled);
809  ui.actionCursorGame->setEnabled(enabled);
810  ui.actionCursorSelect->setEnabled(enabled);
811  ui.actionCursorCreateObj->setEnabled(enabled);
812  ui.actionCursorDrawPen->setEnabled(has_draw_tools);
813  ui.actionCursorDrawLine->setEnabled(has_draw_tools);
814  ui.actionCursorDrawRect->setEnabled(has_draw_tools);
815  ui.actionCursorPicker->setEnabled(has_draw_tools);
816  ui.actionCursorFill->setEnabled(has_exact_draw_tools);
817  ui.actionDynamicLandscape->setEnabled(enabled);
818  ui.actionStaticLandscape->setEnabled(enabled);
819  ui.actionStaticFlatLandscape->setEnabled(enabled);
820  ui.actionExactLandscape->setEnabled(enabled);
821  ui.foregroundMatTexComboBox->setEnabled(is_drawing);
822  ui.backgroundMatTexComboBox->setEnabled(is_drawing);
823  ui.drawSizeSlider->setEnabled(is_drawing);
824  ui.actionFileClose->setEnabled(enabled);
825  ui.actionFileRecord->setEnabled(enabled && !recording);
826  ui.actionFileSaveGameAs->setEnabled(enabled);
827  ui.actionFileSaveScenario->setEnabled(enabled);
828  ui.actionFileSaveScenarioAs->setEnabled(enabled);
829  ui.actionFileExportScenarioPacked->setEnabled(enabled);
830  ui.actionViewportNew->setEnabled(enabled);
831  ui.actionPlayerJoin->setEnabled(enabled);
832  ui.menuNet->setEnabled(net_enabled);
833 
834  // Checked states
835  ui.actionCursorGame->setChecked(editcursor_mode == C4CNS_ModePlay);
836  ui.actionCursorSelect->setChecked(editcursor_mode == C4CNS_ModeEdit);
837  ui.actionCursorCreateObj->setChecked(editcursor_mode == C4CNS_ModeCreateObject);
838  ui.actionCursorDrawPen->setChecked((editcursor_mode == C4CNS_ModeDraw) && (drawing_tool == C4TLS_Brush));
839  ui.actionCursorDrawLine->setChecked((editcursor_mode == C4CNS_ModeDraw) && (drawing_tool == C4TLS_Line));
840  ui.actionCursorDrawRect->setChecked((editcursor_mode == C4CNS_ModeDraw) && (drawing_tool == C4TLS_Rect));
841  ui.actionCursorFill->setChecked((editcursor_mode == C4CNS_ModeDraw) && (drawing_tool == C4TLS_Fill));
842  ui.actionCursorPicker->setChecked((editcursor_mode == C4CNS_ModeDraw) && (drawing_tool == C4TLS_Picker));
843  ui.actionDynamicLandscape->setChecked(landscape_mode == LandscapeMode::Dynamic);
844  ui.actionStaticLandscape->setChecked(landscape_mode == LandscapeMode::Static && !flat_chunk_shapes);
845  ui.actionStaticFlatLandscape->setChecked(landscape_mode == LandscapeMode::Static && flat_chunk_shapes);
846  ui.actionExactLandscape->setChecked(landscape_mode == LandscapeMode::Exact);
847  ui.actionFileRecord->setChecked(recording);
848 }
849 
850 // Put function list into combo box selectable items
851 static void SetComboItems(QComboBox *box, std::list<const char*> &items)
852 {
853  QString text = box->lineEdit()->text(); // remember and restore current text
854  box->clear();
855  for (std::list<const char*>::iterator it = items.begin(); it != items.end(); it++)
856  {
857  if (!*it)
858  box->addItem("----------");
859  else
860  box->addItem(*it);
861  }
862  box->lineEdit()->setText(text);
863 }
864 
865 void C4ConsoleGUIState::UpdateMatTex()
866 {
867  // Update selection of mattex in combo box
868  int new_index = 0;
869  if (material != C4TLS_MatSky) new_index = ui.foregroundMatTexComboBox->findText(QString(FormatString("%s-%s", material.getData(), texture.getData()).getData()));
870  if (new_index >= 0) ui.foregroundMatTexComboBox->setCurrentIndex(new_index);
871 }
872 
873 void C4ConsoleGUIState::UpdateBackMatTex()
874 {
875  // Update selection of mattex in combo box
876  int new_index = 0;
877  if (back_material != C4TLS_MatSky) new_index = ui.backgroundMatTexComboBox->findText(QString(FormatString("%s-%s", back_material.getData(), back_texture.getData()).getData()));
878  if (new_index >= 0) ui.backgroundMatTexComboBox->setCurrentIndex(new_index);
879 }
880 
881 void C4ConsoleGUIState::AddNetMenuItem(int32_t index, const char *text, C4ConsoleGUI::ClientOperation op)
882 {
883  auto *kick_action = new C4ConsoleClientAction(index, text, ui.menuNet, op);
884  if (op == C4ConsoleGUI::CO_None) kick_action->setDisabled(true);
885  client_actions.emplace_back(kick_action);
886  ui.menuNet->addAction(kick_action);
887 }
888 
889 void C4ConsoleGUIState::ClearNetMenu()
890 {
891  for (auto &action : client_actions) ui.menuNet->removeAction(action.get());
892  client_actions.clear();
893 }
894 
895 void C4ConsoleGUIState::AddKickPlayerMenuItem(int32_t plr, const char *text, bool item_enabled)
896 {
897  auto *kick_action = new C4ConsoleRemovePlayerAction(plr, text, ui.menuPlayers);
898  kick_action->setEnabled(item_enabled);
899  player_actions.emplace_back(kick_action);
900  ui.menuPlayers->addAction(kick_action);
901 }
902 
903 void C4ConsoleGUIState::ClearPlayerMenu()
904 {
905  for (auto &action : player_actions) ui.menuPlayers->removeAction(action.get());
906  player_actions.clear();
907 }
908 
909 void C4ConsoleGUIState::AddPlayerViewportMenuItem(int32_t plr, const char *text)
910 {
911  auto *action = new C4ConsoleOpenViewportAction(plr, text, ui.menuWindows);
912  viewport_actions.emplace_back(action);
913  ui.menuWindows->insertAction(window_menu_separator, action);
914 }
915 
916 void C4ConsoleGUIState::ClearViewportMenu()
917 {
918  for (auto &action : viewport_actions) ui.menuWindows->removeAction(action.get());
919  viewport_actions.clear();
920 }
921 
922 void C4ConsoleGUIState::AddViewport(C4ViewportWindow *cvp)
923 {
924  if (!viewport_area) return;
925  C4ConsoleQtViewportDockWidget *new_viewport = new C4ConsoleQtViewportDockWidget(window.get(), viewport_area, cvp);
926  viewport_area->addDockWidget(Qt::BottomDockWidgetArea, new_viewport);
927  viewports.push_back(new_viewport);
928  new_viewport->SetFocus();
929 }
930 
931 void C4ConsoleGUIState::RemoveViewport(C4ViewportWindow *cvp)
932 {
933  if (!viewport_area) return;
934 
935  for (auto iter = viewports.begin(); iter != viewports.end(); )
936  {
937  auto vp = *iter;
938  if (vp->GetViewportWindow() == cvp)
939  {
940  viewport_area->removeDockWidget(vp);
941  iter = viewports.erase(iter);
942 
943  // cannot use deleteLater here because Qt will then
944  // still select/deselect the viewport's GL context
945  // behind the scenes, leaving us with an unselected
946  // GL context.
947  // Documented at http://doc.qt.io/qt-5/qopenglwidget.html
948  // Instead, delete the viewport widget directly.
949  delete vp;
950  }
951  else
952  {
953  ++iter;
954  }
955  }
956 }
957 
958 void C4ConsoleGUIState::SetInputFunctions(std::list<const char*> &functions)
959 {
960  SetComboItems(ui.consoleInputBox, functions);
961 }
962 
963 void C4ConsoleGUIState::PropertyDlgUpdate(C4EditCursorSelection &rSelection, bool force_function_update)
964 {
965  int sel_count = rSelection.size();
966  bool is_array = false;
967  if (sel_count != 1)
968  {
969  // Multi object selection: Hide property view; show info label
970  property_model->SetBasePropList(nullptr);
971  ui.propertyTable->setEnabled(false);
972  ui.selectionInfoLabel->setText(rSelection.GetDataString().getData());
973  ui.propertyEditAscendPathButton->hide();
974  UpdateActionObject(nullptr);
975  ui.selectionHelpLabel->hide();
976  }
977  else
978  {
979  // Single object selection: Show property view + Object info in label
980  C4PropList *prev_list = property_model->GetBasePropList(), *new_list = rSelection.front().getPropList();
981  if (prev_list != new_list)
982  {
983  property_model->SetBasePropList(new_list);
984  ui.propertyTable->setFirstColumnSpanned(0, QModelIndex(), true);
985  ui.propertyTable->setFirstColumnSpanned(1, QModelIndex(), true);
986  ui.propertyTable->expand(property_model->index(0, 0, QModelIndex()));
987  UpdateActionObject(new_list->GetObject());
988  }
990  {
991  property_model->UpdateValue(false);
992  }
993  ui.selectionInfoLabel->setText(property_model->GetTargetPathText());
994  QString help_text = property_model->GetTargetPathHelp();
995  if (!help_text.isEmpty() && ::Config.Developer.ShowHelp)
996  {
997  const char *help_label = property_model->GetTargetPathName();
998  if (!help_label) help_label = LoadResStr("IDS_CNS_DESCRIPTION");
999  ui.selectionHelpLabel->setText(QString("%1: %2").arg(help_label).arg(help_text));
1000  ui.selectionHelpLabel->show();
1001  }
1002  else
1003  {
1004  ui.selectionHelpLabel->hide();
1005  }
1006  ui.propertyEditAscendPathButton->setVisible(property_model->GetTargetPathStackSize() >= 1);
1007  is_array = property_model->IsArray();
1008  if (is_array)
1009  {
1010  bool is_readonly = property_model->IsTargetReadonly();
1011  ui.arrayAddButton->setDisabled(is_readonly);
1012  ui.arrayRemoveButton->setDisabled(is_readonly || ui.propertyTable->selectionModel()->selectedRows().empty());
1013  }
1014  ui.propertyTable->setEnabled(true);
1016  }
1017  ui.arrayAddButton->setVisible(is_array);
1018  ui.arrayRemoveButton->setVisible(is_array);
1019  // Function update in script combo box
1020  if (force_function_update)
1021  {
1022  auto suggestions = ::Console.GetScriptSuggestions(rSelection.GetObject(), C4Console::MRU_Object);
1023  SetComboItems(ui.propertyInputBox, suggestions);
1024  }
1025 }
1026 
1027 void C4ConsoleGUIState::ReInitDefinitions()
1028 {
1029  if (definition_list_model) definition_list_model->ReInit();
1030  // This also affects the object list
1031  if (object_list_model) object_list_model->Invalidate();
1032 }
1033 
1034 void C4ConsoleGUIState::OnCreatorSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
1035 {
1036  if (is_object_selection_updating || !definition_list_model) return; // only process callbacks from users interacting with widget
1037  // Forward to EditCursor
1038  C4Def *def;
1039  auto deselected_indexes = deselected.indexes();
1040  for (const QModelIndex &item : deselected_indexes)
1041  if ((def = definition_list_model->GetDefByModelIndex(item)))
1043  auto selected_indexes = selected.indexes();
1044  for (const QModelIndex &item : selected_indexes)
1045  if ((def = definition_list_model->GetDefByModelIndex(item)))
1048  // Switching to def selection mode: Remove any non-defs from selection
1049  if (!selected.empty())
1050  {
1051  ui.objectListView->selectionModel()->clearSelection();
1052  // ...and switch to creator mode
1054  }
1055 }
1056 
1057 void C4ConsoleGUIState::OnObjectListSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
1058 {
1059  if (is_object_selection_updating) return; // only process callbacks from users interacting with widget
1060  // Forward to EditCursor
1061  C4PropList *p;
1062  for (const QModelIndex &item : deselected.indexes())
1063  if ((p = object_list_model->GetItemByModelIndex(item)))
1065  for (const QModelIndex &item : selected.indexes())
1066  if ((p = object_list_model->GetItemByModelIndex(item)))
1069  // Switching to object/effect selection mode: Remove any non-objects/effects from selection
1070  if (!selected.empty())
1071  {
1072  ui.creatorTreeView->selectionModel()->clearSelection();
1073  // ...and switch to editing mode
1075  }
1076 }
1077 
1078 void C4ConsoleGUIState::SetObjectSelection(class C4EditCursorSelection &rSelection)
1079 {
1080  if (!window.get()) return;
1081  // Callback from EditCursor when selection was changed e.g. from viewport
1082  // Reflect selection change in object and definition view
1083  C4Def *creator_def = ::Console.EditCursor.GetCreatorDef();
1084  ++is_object_selection_updating;
1085  ui.objectListView->selectionModel()->clearSelection();
1086  ui.creatorTreeView->selectionModel()->clearSelection();
1087  QModelIndex last_idx_obj, last_idx_def, creator_idx;
1088  for (C4Value &v : rSelection)
1089  {
1090  C4PropList *p = v.getPropList();
1091  if (!p) continue;
1092  C4Def *def = p->GetDef();
1093  if (def && !p->GetObject())
1094  {
1095 
1096  QModelIndex idx = definition_list_model->GetModelIndexByItem(def);
1097  if (idx.isValid())
1098  {
1099  ui.creatorTreeView->selectionModel()->select(idx, QItemSelectionModel::Select);
1100  last_idx_def = idx;
1101  if (def == creator_def) creator_idx = idx;
1102  }
1103  }
1104  else
1105  {
1106  QModelIndex idx = object_list_model->GetModelIndexByItem(v.getPropList());
1107  if (idx.isValid())
1108  {
1109  ui.objectListView->selectionModel()->select(idx, QItemSelectionModel::Select);
1110  last_idx_obj = idx;
1111  }
1112  }
1113  }
1114  if (last_idx_obj.isValid()) ui.objectListView->scrollTo(last_idx_obj);
1115  if (last_idx_def.isValid()) ui.creatorTreeView->scrollTo(last_idx_def);
1117  {
1118  // Switch away from creator tool if user selected a non-definition
1120  }
1121  // Sync creator selection
1122  if (creator_idx.isValid()) ui.creatorTreeView->selectionModel()->select(creator_idx, QItemSelectionModel::Current);
1123  --is_object_selection_updating;
1124 }
1125 
1126 void C4ConsoleGUIState::OnCreatorCurrentChanged(const QModelIndex & current, const QModelIndex & previous)
1127 {
1128  // A new definition was selected from the creator definition view
1129  // Reflect in selection and auto-switch to creation mode if necessery
1130  if (!definition_list_model) return;
1131  C4Def *new_def = definition_list_model->GetDefByModelIndex(current);
1132  //if (new_def) ::Console.EditCursor.SetMode(C4CNS_ModeCreateObject); - done by selection change
1133  ::Console.EditCursor.SetCreatorDef(new_def); // set or clear def in EditCursor
1134 }
1135 
1136 bool C4ConsoleGUIState::CreateNewScenario(StdStrBuf *out_filename, bool *out_host_as_network)
1137 {
1138  // Show dialogue
1139  std::unique_ptr<C4ConsoleQtNewScenarioDlg> dlg(new C4ConsoleQtNewScenarioDlg(window.get()));
1140  if (!dlg->exec()) return false;
1141  // Dlg said OK! Scenario created
1142  out_filename->Copy(dlg->GetFilename());
1143  *out_host_as_network = dlg->IsHostAsNetwork();
1144  return true;
1145 }
1146 
1147 void C4ConsoleGUIState::InitWelcomeScreen()
1148 {
1149  // Init links
1150  ui.welcomeNewLabel->setText(QString("<a href=\"new\">%1</a>").arg(ui.welcomeNewLabel->text()));
1151  ui.welcomeOpenLabel->setText(QString("<a href=\"open\">%1</a>").arg(ui.welcomeOpenLabel->text()));
1152  ui.welcomeExploreUserPathLabel->setText(QString("<a href=\"exploreuserpath\">%1</a>").arg(ui.welcomeExploreUserPathLabel->text()));
1153  // Recently opened scenarios
1154  bool any_file = false;
1155  int recent_idx = ui.welcomeScrollLayout->indexOf(ui.welcomeRecentLabel);
1156  for (int32_t i = 0; i < CFG_MaxEditorMRU; ++i)
1157  {
1158  const char *filename = ::Config.Developer.RecentlyEditedSzenarios[i];
1159  if (*filename && ::ItemExists(filename))
1160  {
1161  StdStrBuf basename(GetFilename(filename), true);
1162  if (basename == C4CFN_ScenarioCore)
1163  {
1164  // If a Scenario.txt was opened, use the enclosing .ocs name
1165  basename.Copy(filename, strlen(filename) - basename.getLength());
1166  int32_t len = basename.getLength();
1167  while (len && (basename.getData()[len - 1] == DirectorySeparator || basename.getData()[len - 1] == AltDirectorySeparator))
1168  basename.SetLength(--len);
1169  StdStrBuf base_folder_name(GetFilename(basename.getData()), true);
1170  basename.Take(base_folder_name);
1171  }
1172  RemoveExtension(&basename);
1173  QLabel *link = new QLabel(ui.welcomeScrollAreaWidgetContents);
1174  ui.welcomeScrollLayout->insertWidget(++recent_idx, link);
1175  link->setIndent(ui.welcomeOpenLabel->indent());
1176  link->setTextInteractionFlags(ui.welcomeOpenLabel->textInteractionFlags());
1177  link->setText(QString("<a href=\"open:%1\">%2</a>").arg(filename).arg(basename.getData())); // let's hope file names never contain "
1178  any_file = true;
1179  window->connect(link, SIGNAL(linkActivated(QString)), window.get(), SLOT(WelcomeLinkActivated(QString)));
1180  }
1181  }
1182  if (!any_file) ui.welcomeRecentLabel->hide();
1183 }
1184 
1185 void C4ConsoleGUIState::ShowWelcomeScreen()
1186 {
1187  viewport_area->addDockWidget(Qt::BottomDockWidgetArea, ui.welcomeDockWidget);
1188 }
1189 
1190 void C4ConsoleGUIState::HideWelcomeScreen()
1191 {
1192  ui.welcomeDockWidget->close();
1193 }
1194 
1195 void C4ConsoleGUIState::ClearGamePointers()
1196 {
1197  if (property_delegate_factory) property_delegate_factory->ClearDelegates();
1198 }
1199 
1200 void C4ConsoleGUIState::UpdateActionObject(C4Object *new_action_object)
1201 {
1202  // No change? Do not recreate buttons then because it may interfere with their usage
1203  C4Object *prev_object = action_object.getObj();
1204  if (new_action_object && prev_object == new_action_object) return;
1205  action_object = C4VObj(new_action_object);
1206  // Clear old action buttons
1207  int32_t i = ui.objectActionPanel->count();
1208  while (i--)
1209  {
1210  ui.objectActionPanel->itemAt(i)->widget()->deleteLater();
1211  }
1212  // Create new buttons
1213  // Actions are defined as properties in a local proplist called EditorActions
1214  if (!new_action_object) return;
1215  C4PropList *editor_actions_list = new_action_object->GetPropertyPropList(P_EditorActions);
1216  if (!editor_actions_list) return;
1217  auto new_properties = editor_actions_list->GetSortedProperties(nullptr);
1218  int row = 0, column = 0;
1219  for (C4String *action_def_id : new_properties)
1220  {
1221  // Get action definition proplist
1222  C4Value action_def_val;
1223  if (!editor_actions_list->GetPropertyByS(action_def_id, &action_def_val))
1224  {
1225  // property disappeared (cannot happen)
1226  continue;
1227  }
1228  C4PropList *action_def = action_def_val.getPropList();
1229  if (!action_def)
1230  {
1231  // property is of wrong type (can happen; scripter error)
1232  continue;
1233  }
1234  // Get action name
1235  C4String *action_name = action_def->GetPropertyStr(P_Name);
1236  if (!action_name)
1237  {
1238  // Fallback to identifier for unnamed actions
1239  action_name = action_def_id;
1240  }
1241  // Get action help
1242  QString action_help;
1243  C4String *action_help_s = action_def->GetPropertyStr(P_EditorHelp);
1244  if (action_help_s)
1245  {
1246  action_help = QString(action_help_s->GetCStr()).replace('|', '\n');
1247  }
1248  // Script command to execute
1249  C4RefCntPointer<C4String> script_command = action_def->GetPropertyStr(P_Command);
1250  int32_t object_number = new_action_object->Number;
1251  // Create action button
1252  QPushButton *btn = new QPushButton(action_name->GetCStr(), window.get());
1253  if (!action_help.isEmpty()) btn->setToolTip(action_help);
1254  if (script_command)
1255  {
1256  bool select_returned_object = action_def->GetPropertyBool(P_Select);
1257  btn->connect(btn, &QPushButton::pressed, btn, [script_command, object_number, select_returned_object]()
1258  {
1259  // Action execution. Replace %player% by first local player.
1260  StdStrBuf script_command_cpy(script_command->GetData(), true);
1261  C4Player *local_player = ::Players.GetLocalByIndex(0);
1262  int32_t local_player_number = local_player ? local_player->Number : NO_OWNER;
1263  script_command_cpy.Replace("%player%", FormatString("%d", (int)local_player_number).getData());
1264  ::Console.EditCursor.EMControl(CID_Script, new C4ControlScript(script_command_cpy.getData(), object_number, false, select_returned_object));
1265  });
1266  }
1267  ui.objectActionPanel->addWidget(btn, row, column);
1268  if (++column >= 3)
1269  {
1270  column = 0;
1271  ++row;
1272  }
1273  }
1274 }
bool FileSaveAs(bool fSaveGame, bool export_packed=false)
Definition: C4Console.cpp:274
char * GetFilename(char *szPath)
Definition: StdFile.cpp:55
const char * getData() const
Definition: StdBuf.h:450
C4EditCursor EditCursor
Definition: C4Console.h:90
#define C4CFN_EditorGeometry
Definition: C4Components.h:148
StdStrBuf GetData() const
Definition: C4StringTable.h:50
int32_t DebugOpenGL
Definition: C4Config.h:118
C4Config Config
Definition: C4Config.cpp:837
Definition: StdBuf.h:37
bool SetGrade(int32_t iGrade)
Definition: C4ToolsDlg.cpp:165
bool SetMode(int32_t iMode)
bool In(const char *szText)
C4Console Console
Definition: C4Globals.cpp:45
bool isCtrlHost() const
Definition: C4GameControl.h:99
C4Game Game
Definition: C4Globals.cpp:52
int32_t Number
Definition: C4Player.h:88
void HelpAbout()
Definition: C4Console.cpp:454
C4ConfigGeneral General
Definition: C4Config.h:252
bool isLobbyActive() const
Definition: C4Network2.h:204
C4ToolsDlg ToolsDlg
Definition: C4Console.h:88
const char * GetCStr() const
Definition: C4StringTable.h:49
bool LoadFromFile(const char *szFile)
Definition: StdBuf.cpp:39
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void RegisterRecentInput(const char *input, RecentScriptInputLists section)
Definition: C4Console.cpp:644
void DoHalt()
Definition: C4Console.cpp:105
bool FileClose()
Definition: C4Console.cpp:387
void CompileFunc(StdCompiler *comp)
const int C4CNS_ModeEdit
Definition: C4Console.h:31
void CtrlRemove(const C4Client *pClient, const char *szReason)
Definition: C4Client.cpp:350
bool KeyDown(C4KeyCode KeyCode, DWORD dwKeyState)
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:90
bool IsSelectionInvalidated() const
Definition: C4EditCursor.h:155
virtual C4Object * GetObject()
Definition: C4PropList.cpp:644
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
virtual C4Def const * GetDef() const
Definition: C4PropList.cpp:662
#define AltDirectorySeparator
const int32_t C4TLS_Rect
Definition: C4ToolsDlg.h:30
C4ConfigGraphics Graphics
Definition: C4Config.h:254
C4Object * GetObject(int32_t index=0) const
int32_t GetPropertyBool(C4PropertyName n, bool default_val=false) const
Definition: C4PropList.cpp:849
C4Player * Get(int iPlayer) const
C4Def * GetCreatorDef()
Definition: C4EditCursor.h:150
void EMControl(enum C4PacketType eCtrlType, class C4ControlPacket *pCtrl)
C4Network2 Network
Definition: C4Globals.cpp:53
const int32_t C4TLS_Picker
Definition: C4ToolsDlg.h:32
void OnSelectionChanged(bool by_objectlist=false)
char UserDataPath[CFG_MaxString+1]
Definition: C4Config.h:55
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const
Definition: C4PropList.cpp:734
void DoPlay()
Definition: C4Console.cpp:100
C4GameControl Control
void Copy(size_t inSize)
Definition: StdBuf.h:233
static C4ControlPlayerAction * Eliminate(const C4Player *source)
Definition: C4Control.cpp:574
const int C4CNS_ModePlay
Definition: C4Console.h:30
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
C4Control Input
Definition: C4GameControl.h:66
C4PlayerList Players
const int32_t C4TLS_GradeMin
Definition: C4ToolsDlg.h:36
void ViewportNew()
Definition: C4Console.cpp:461
std::vector< C4String * > GetSortedProperties(const char *prefix, C4PropList *ignore_parent=nullptr) const
Definition: C4PropList.cpp:613
C4ConfigDeveloper Developer
Definition: C4Config.h:253
C4Player * GetLocalByIndex(int iIndex) const
Definition: C4Def.h:100
const int NO_OWNER
Definition: C4Constants.h:138
const int C4CNS_ModeDraw
Definition: C4Console.h:33
C4ConsoleQtTranslator qt_translator
const int32_t C4TLS_GradeMax
Definition: C4ToolsDlg.h:35
bool In(const char *szText)
Definition: C4Console.cpp:70
void Value(const T &rStruct)
Definition: StdCompiler.h:171
bool CreateViewport(int32_t iPlayer, bool fSilent=false)
Definition: C4Viewport.cpp:920
void RemoveExtension(char *szFilename)
Definition: StdFile.cpp:316
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:60
std::list< const char * > GetScriptSuggestions(class C4PropList *target, RecentScriptInputLists section) const
Definition: C4Console.cpp:627
bool KeyUp(C4KeyCode KeyCode, DWORD dwKeyState)
bool FileOpenWPlrs()
Definition: C4Console.cpp:348
char RecentlyEditedSzenarios[CFG_MaxEditorMRU][CFG_MaxString+1]
Definition: C4Config.h:90
bool FileRecord()
Definition: C4Console.cpp:399
bool SelectBackTexture(const char *szTexture, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:269
const C4Value C4VNull
Definition: C4Value.cpp:32
bool SetLandscapeMode(LandscapeMode iMode, bool flat_chunk_shapes, bool fThroughControl=false)
Definition: C4ToolsDlg.cpp:180
LandscapeMode
Definition: C4Landscape.h:30
bool FileOpen(const char *filename=nullptr, bool host_in_network=false)
Definition: C4Console.cpp:324
bool RemoveFromSelection(C4PropList *remove_obj)
C4ViewportList Viewports
Definition: C4Viewport.cpp:841
bool SelectBackMaterial(const char *szMaterial, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:276
const int32_t C4TLS_Brush
Definition: C4ToolsDlg.h:28
C4Client * getClientByID(int32_t iID) const
Definition: C4Client.cpp:217
#define C4CFN_ScenarioCore
Definition: C4Components.h:42
const int32_t C4TLS_Fill
Definition: C4ToolsDlg.h:31
const int C4CNS_ModeCreateObject
Definition: C4Console.h:32
bool FileNew()
Definition: C4Console.cpp:303
const char * AtUserDataPath(const char *szFilename)
Definition: C4Config.cpp:530
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
bool FileSave()
Definition: C4Console.cpp:267
C4ClientList & Clients
Definition: C4Game.h:71
const int32_t C4TLS_GradeDefault
Definition: C4ToolsDlg.h:37
bool FileQuit()
Definition: C4Console.cpp:448
#define C4TLS_MatSky
Definition: C4ToolsDlg.h:39
void AddToSelection(C4PropList *add_obj)
bool SelectMaterial(const char *szMaterial, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:262
bool IsRecursion() const
bool SelectTexture(const char *szTexture, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:255
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
void InvalidateSelection()
Definition: C4EditCursor.h:153
void SplitMaterialTexture(const QString &mat_tex, QString *mat, QString *tex)
void SetCreatorDef(C4Def *new_def)
Definition: C4EditCursor.h:149
virtual void Close()
Definition: C4Console.cpp:442
const int32_t C4TLS_Line
Definition: C4ToolsDlg.h:29
#define DirectorySeparator
bool SetTool(int32_t iTool, bool fTemp)
Definition: C4ToolsDlg.cpp:42
void Copy()
Definition: StdBuf.h:475
C4String * GetPropertyStr(C4PropertyName k) const
Definition: C4PropList.cpp:752
C4PropList * GetPropertyPropList(C4PropertyName k) const
Definition: C4PropList.cpp:877
StdStrBuf GetDataString() const
void ValidateSelection()
Definition: C4EditCursor.h:154
int32_t GetMode()
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
void PlayerJoin()
Definition: C4Console.cpp:518
C4PropList * getPropList() const
Definition: C4Value.h:116