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