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"
20 #include "landscape/C4Texture.h"
21 #include "landscape/C4Landscape.h"
22 // Make sure to include landscape/* first. Otherwise, Qt will either include gltypes and that forbids including glew, or, if glew is inlcuded first, QT will undefine glew partially, and then it can't be included again.
23 #include "editor/C4ConsoleQtState.h"
24 #include "editor/C4ConsoleQtDefinitionListViewer.h"
25 #include "editor/C4ConsoleQtObjectListViewer.h"
26 #include "editor/C4ConsoleQtPropListViewer.h"
27 #include "editor/C4ConsoleQtShapes.h"
28 #include "editor/C4ConsoleQtLocalizeOverview.h"
29 #include "editor/C4Console.h"
30 #include "editor/C4ConsoleGUI.h"
31 #include "script/C4AulExec.h"
32 #include "C4Version.h"
33 
34 #include "editor/C4ConsoleQt.h"
35 
OnStartGame()36 void C4ConsoleGUI::OnStartGame()
37 {
38 	// Welcome screen made invisible on first game load
39 	state->HideWelcomeScreen();
40 }
41 
Execute()42 void C4ConsoleGUI::Execute() { state->Execute(); }
43 
SetCursor(C4ConsoleGUI::Cursor cursor)44 void C4ConsoleGUI::SetCursor(C4ConsoleGUI::Cursor cursor)
45 {
46 
47 }
48 
RecordingEnabled()49 void C4ConsoleGUI::RecordingEnabled()
50 {
51 	if (Active) state->SetRecording(true); // TODO this is never reset. Noone uses it anyway...
52 }
53 
ShowAboutWithCopyright(StdStrBuf & copyright)54 void C4ConsoleGUI::ShowAboutWithCopyright(StdStrBuf &copyright)
55 {
56 	QMessageBox::about(state->window.get(), QString(LoadResStr("IDS_MENU_ABOUT")), QString(copyright.getData()));
57 }
58 
UpdateModeCtrls(int iMode)59 bool C4ConsoleGUI::UpdateModeCtrls(int iMode)
60 {
61 	if (!Active) return false;
62 	state->SetEditCursorMode(iMode);
63 	return true;
64 }
65 
AddNetMenu()66 void C4ConsoleGUI::AddNetMenu()
67 {
68 	if (Active) state->SetNetEnabled(true);
69 }
70 
ClearNetMenu()71 void C4ConsoleGUI::ClearNetMenu()
72 {
73 	if (Active) state->ClearNetMenu();
74 }
75 
AddNetMenuItemForPlayer(int32_t client_id,const char * text,C4ConsoleGUI::ClientOperation op)76 void C4ConsoleGUI::AddNetMenuItemForPlayer(int32_t client_id, const char *text, C4ConsoleGUI::ClientOperation op)
77 {
78 	if (Active) state->AddNetMenuItem(client_id, text, op);
79 }
80 
ClearPlayerMenu()81 void C4ConsoleGUI::ClearPlayerMenu()
82 {
83 	if (Active) state->ClearPlayerMenu();
84 }
85 
SetInputFunctions(std::list<const char * > & functions)86 void C4ConsoleGUI::SetInputFunctions(std::list<const char*> &functions)
87 {
88 	if (Active) state->SetInputFunctions(functions);
89 }
90 
CreateConsoleWindow(C4AbstractApp * application)91 bool C4ConsoleGUI::CreateConsoleWindow(C4AbstractApp *application)
92 {
93 	if (!state->CreateConsoleWindow(application)) return false;
94 	Active = true;
95 	EnableControls(fGameOpen);
96 	return true;
97 }
98 
DeleteConsoleWindow()99 void C4ConsoleGUI::DeleteConsoleWindow()
100 {
101 	if (Active)
102 	{
103 		Active = false;
104 		state->DeleteConsoleWindow();
105 	}
106 }
107 
Out(const char * message)108 void C4ConsoleGUI::Out(const char* message)
109 {
110 	// Log text: Add to log window
111 	if (state->window.get())
112 	{
113 		// Append text
114 		state->ui.logView->append(QString(message));
115 		// Scroll to end to display it
116 		QScrollBar *sb = state->ui.logView->verticalScrollBar();
117 		if (sb) sb->setValue(sb->maximum());
118 		state->Redraw();
119 	}
120 }
121 
ClearLog()122 bool C4ConsoleGUI::ClearLog()
123 {
124 	// Empty log window
125 	if (!Active) return false;
126 	state->ui.logView->clear();
127 	return true;
128 }
129 
DisplayInfoText(InfoTextType type,StdStrBuf & text)130 void C4ConsoleGUI::DisplayInfoText(InfoTextType type, StdStrBuf& text)
131 {
132 	QLabel *target = nullptr;
133 	switch (type)
134 	{
135 	case CONSOLE_Cursor: target = state->status_cursor; break;
136 	case CONSOLE_FrameCounter: target = state->status_framecounter; break;
137 	case CONSOLE_TimeFPS: target = state->status_timefps; break;
138 	}
139 	if (!target) return;
140 	target->setText(text.getData());
141 }
142 
SetCaptionToFileName(const char * file_name)143 void C4ConsoleGUI::SetCaptionToFileName(const char* file_name) { /* This is never even called? */ }
144 
FileSelect(StdStrBuf * sFilename,const char * szFilter,DWORD dwFlags,bool fSave)145 bool C4ConsoleGUI::FileSelect(StdStrBuf *sFilename, const char * szFilter, DWORD dwFlags, bool fSave)
146 {
147 	// Prepare filters from double-zero-terminated list to ";;"-separated list in Qt format
148 	QString filter="", selected_filter, filename;
149 	QStringList filenames; bool has_multi = (dwFlags & OpenFileFlags::OFN_ALLOWMULTISELECT);
150 	if (szFilter)
151 	{
152 		while (*szFilter)
153 		{
154 			if (filter.length() > 0) filter.append(";;");
155 			filter.append(szFilter);
156 			szFilter += strlen(szFilter) + 1;
157 			if (*szFilter)
158 			{
159 				filter.append(" (");
160 				filter.append(szFilter);
161 				filter.append(")");
162 				szFilter += strlen(szFilter) + 1;
163 			}
164 			if (selected_filter.length() <= 0) selected_filter = filter;
165 		}
166 	}
167 #ifdef USE_WIN32_WINDOWS
168 	// cwd backup
169 	size_t l = GetCurrentDirectoryW(0, nullptr);
170 	std::unique_ptr<wchar_t []> wd(new wchar_t[l]);
171 	GetCurrentDirectoryW(l, wd.get());
172 #endif
173 	// Show dialogue
174 	if (fSave)
175 		filename = QFileDialog::getSaveFileName(state->window.get(), LoadResStr("IDS_DLG_SAVE"), QString(sFilename->getData()), filter, &selected_filter);
176 	else if (!has_multi)
177 		filename = QFileDialog::getOpenFileName(state->window.get(), LoadResStr("IDS_DLG_OPEN"), QString(), filter);
178 	else
179 		filenames = QFileDialog::getOpenFileNames(state->window.get(), LoadResStr("IDS_DLG_OPEN"), QString(), filter, &selected_filter);
180 #ifdef USE_WIN32_WINDOWS
181 	// Restore cwd; may have been changed in open/save dialogue
182 	SetCurrentDirectoryW(wd.get());
183 #endif
184 	// Process multi vs single file select
185 	if (has_multi)
186 	{
187 		// Multi-select: Return double-zero-terminated string list
188 		if (!filenames.length()) return false;
189 		for (auto fn : filenames)
190 		{
191 			sFilename->Append(fn.toUtf8());
192 			sFilename->AppendChar('\0');
193 		}
194 		return true;
195 	}
196 	// Cancelled?
197 	if (filename.length() <= 0) return false;
198 	// File selected!
199 	sFilename->Copy(filename.toUtf8());
200 	sFilename->AppendChar('\0');
201 	return true;
202 }
203 
AddMenuItemForPlayer(C4Player * player,StdStrBuf & player_text)204 void C4ConsoleGUI::AddMenuItemForPlayer(C4Player  *player, StdStrBuf& player_text)
205 {
206 	// Add "new viewport for X" to window menu
207 	if (Active) state->AddPlayerViewportMenuItem(player->Number, player_text.getData());
208 }
209 
AddKickPlayerMenuItem(C4Player * player,StdStrBuf & player_text,bool enabled)210 void C4ConsoleGUI::AddKickPlayerMenuItem(C4Player *player, StdStrBuf& player_text, bool enabled)
211 {
212 	// Add "kick X" to player menu
213 	if (Active) state->AddKickPlayerMenuItem(player->Number, player_text.getData(), enabled);
214 }
215 
ClearViewportMenu()216 void C4ConsoleGUI::ClearViewportMenu()
217 {
218 	// Remove all "new viewport for X" entries from window menu
219 	if (Active) state->ClearViewportMenu();
220 }
221 
Message(const char * message,bool query)222 bool C4ConsoleGUI::Message(const char *message, bool query)
223 {
224 	// Show a message through Qt
225 	if (query)
226 	{
227 		auto result = QMessageBox::question(state->window.get(), C4ENGINECAPTION, message, QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel);
228 		return (result == QMessageBox::StandardButton::Ok);
229 	}
230 	else
231 	{
232 		QMessageBox::information(state->window.get(), C4ENGINECAPTION, message, QMessageBox::StandardButton::Ok);
233 		return true;
234 	}
235 }
236 
DoEnableControls(bool fEnable)237 void C4ConsoleGUI::DoEnableControls(bool fEnable)
238 {
239 	if (!Active) return;
240 	state->SetEnabled(fEnable);
241 	state->SetLandscapeMode(::Landscape.GetMode(), ::Game.C4S.Landscape.FlatChunkShapes); // initial setting
242 	// If disabling controls, also stop translation editing
243 	if (!fEnable) state->translation_overview_dialogue.reset();
244 }
245 
DoUpdateHaltCtrls(bool fHalt)246 bool C4ConsoleGUI::DoUpdateHaltCtrls(bool fHalt)
247 {
248 	// Reflect halt state in play/pause buttons
249 	if (!Active) return false;
250 	state->ui.actionPlay->setChecked(!fHalt);
251 	state->ui.actionPause->setChecked(fHalt);
252 	return true;
253 }
254 
PropertyDlgOpen()255 bool C4ConsoleGUI::PropertyDlgOpen() { /* Always open */ return true; }
PropertyDlgClose()256 void C4ConsoleGUI::PropertyDlgClose() { /* Always open */ }
257 
PropertyDlgUpdate(C4EditCursorSelection & rSelection,bool force_function_update)258 void C4ConsoleGUI::PropertyDlgUpdate(C4EditCursorSelection &rSelection, bool force_function_update)
259 {
260 	if (Active) state->PropertyDlgUpdate(rSelection, force_function_update);
261 }
262 
ToolsDlgOpen(class C4ToolsDlg * dlg)263 bool C4ConsoleGUI::ToolsDlgOpen(class C4ToolsDlg *dlg) { /* Always open */ return true; }
ToolsDlgClose()264 void C4ConsoleGUI::ToolsDlgClose() { /* Always open */ }
265 
ToolsDlgInitMaterialCtrls(class C4ToolsDlg * dlg)266 void C4ConsoleGUI::ToolsDlgInitMaterialCtrls(class C4ToolsDlg *dlg)
267 {
268 	// All foreground materials
269 	assert(Active);
270 	if (!Active) return;
271 	if (state->ui.foregroundMatTexComboBox->count()) return; // already initialized
272 	state->ui.foregroundMatTexComboBox->clear();
273 	state->ui.foregroundMatTexComboBox->addItem(QString(C4TLS_MatSky));
274 	QStringList items;
275 	const C4TexMapEntry *entry; int32_t i = 0;
276 	while ((entry = ::TextureMap.GetEntry(i++)))
277 	{
278 		if (!entry->isNull())
279 		{
280 			const char *material_name = entry->GetMaterialName();
281 			if (strcmp(material_name, "Vehicle") && strcmp(material_name, "HalfVehicle"))
282 			{
283 				items.append(QString(FormatString("%s-%s", material_name, entry->GetTextureName()).getData()));
284 			}
285 		}
286 	}
287 	items.sort();
288 	for (QString &item : items) state->ui.foregroundMatTexComboBox->addItem(item);
289 	auto width = 130; /* The ToolBar randomly resizes the control */
290 	state->ui.foregroundMatTexComboBox->view()->setMinimumWidth(width);
291 	state->ui.foregroundMatTexComboBox->setFixedWidth(width);
292 	// Background materials: True background materials first; then the "funny" stuff
293 	state->ui.backgroundMatTexComboBox->addItem(QString(C4TLS_MatSky));
294 	items.clear();
295 	i = 0;
296 	while ((entry = ::TextureMap.GetEntry(i++)))
297 	{
298 		if (!entry->isNull())
299 		{
300 			const char *material_name = entry->GetMaterialName();
301 			C4Material *mat = entry->GetMaterial();
302 			if (strcmp(material_name, "Vehicle") && strcmp(material_name, "HalfVehicle") && mat->Density == C4M_Background)
303 			{
304 				items.append(QString(FormatString("%s-%s", material_name, entry->GetTextureName()).getData()));
305 			}
306 		}
307 	}
308 	items.sort();
309 	for (QString &item : items) state->ui.backgroundMatTexComboBox->addItem(item);
310 	state->ui.backgroundMatTexComboBox->addItem(QString("----------"));
311 	items.clear();
312 	i = 0;
313 	while ((entry = ::TextureMap.GetEntry(i++)))
314 	{
315 		if (!entry->isNull())
316 		{
317 			const char *material_name = entry->GetMaterialName();
318 			C4Material *mat = entry->GetMaterial();
319 			if (strcmp(material_name, "Vehicle") && strcmp(material_name, "HalfVehicle") && mat->Density != C4M_Background)
320 			{
321 				items.append(QString(FormatString("%s-%s", material_name, entry->GetTextureName()).getData()));
322 			}
323 		}
324 	}
325 	items.sort();
326 	for (QString &item : items) state->ui.backgroundMatTexComboBox->addItem(item);
327 	state->ui.backgroundMatTexComboBox->view()->setMinimumWidth(width);
328 	state->ui.backgroundMatTexComboBox->setFixedWidth(width);
329 	// Select current materials
330 	state->SetMaterial(dlg->Material);
331 	state->SetTexture(dlg->Texture);
332 	state->SetBackMaterial(dlg->BackMaterial);
333 	state->SetBackTexture(dlg->BackTexture);
334 	state->UpdateMatTex();
335 	state->UpdateBackMatTex();
336 }
337 
ToolsDlgSelectTexture(C4ToolsDlg * dlg,const char * texture)338 void C4ConsoleGUI::ToolsDlgSelectTexture(C4ToolsDlg *dlg, const char *texture) { if (!Active) return; state->SetTexture(texture); }
ToolsDlgSelectMaterial(C4ToolsDlg * dlg,const char * material)339 void C4ConsoleGUI::ToolsDlgSelectMaterial(C4ToolsDlg *dlg, const char *material) { if (!Active) return; state->SetMaterial(material); }
ToolsDlgSelectBackTexture(C4ToolsDlg * dlg,const char * texture)340 void C4ConsoleGUI::ToolsDlgSelectBackTexture(C4ToolsDlg *dlg, const char *texture) { if (!Active) return; state->SetBackTexture(texture); }
ToolsDlgSelectBackMaterial(C4ToolsDlg * dlg,const char * material)341 void C4ConsoleGUI::ToolsDlgSelectBackMaterial(C4ToolsDlg *dlg, const char *material) { if (!Active) return; state->SetBackMaterial(material); }
342 
343 #ifdef USE_WIN32_WINDOWS
Win32KeepDialogsFloating(HWND hwnd)344 void C4ConsoleGUI::Win32KeepDialogsFloating(HWND hwnd) { /* Dialogues float nicely */ }
Win32DialogMessageHandling(MSG * msg)345 bool C4ConsoleGUI::Win32DialogMessageHandling(MSG *msg) { return false; /* message handling done through Qt (somehow?) */ }
UpdateMenuText(HMENU hMenu)346 void C4ConsoleGUI::UpdateMenuText(HMENU hMenu) { /* Translation done through QTranslator */ }
347 #endif
348 
AddViewport(C4ViewportWindow * cvp)349  void C4ConsoleGUI::AddViewport(C4ViewportWindow *cvp)
350 {
351 	// Add surrounding widget for viewport
352 	state->AddViewport(cvp);
353 }
354 
RemoveViewport(C4ViewportWindow * cvp)355  void C4ConsoleGUI::RemoveViewport(C4ViewportWindow *cvp)
356  {
357 	 // Add surrounding widget for viewport
358 	 state->RemoveViewport(cvp);
359  }
360 
CreateNewScenario(StdStrBuf * out_filename,bool * out_host_as_network)361 bool C4ConsoleGUI::CreateNewScenario(StdStrBuf *out_filename, bool *out_host_as_network)
362 {
363 #ifdef WITH_QT_EDITOR
364 	return state->CreateNewScenario(out_filename, out_host_as_network);
365 #else
366 	return false
367 #endif
368 }
369 
OnObjectSelectionChanged(class C4EditCursorSelection & selection)370  void C4ConsoleGUI::OnObjectSelectionChanged(class C4EditCursorSelection &selection)
371  {
372 	// selection changed (through other means than creator or object list view)
373 	// reflect selection change in dialogues
374 	state->SetObjectSelection(selection);
375  }
376 
ClearGamePointers()377 void C4ConsoleGUI::ClearGamePointers()
378 {
379 	state->ClearGamePointers();
380 }
381 
EnsureDefinitionListInitialized()382 void C4ConsoleGUI::EnsureDefinitionListInitialized()
383 {
384 	state->definition_list_model->EnsureInit();
385 }
386 
CloseConsoleWindow()387 void C4ConsoleGUI::CloseConsoleWindow()
388 {
389 	if (state && state->window) state->window->close();
390 }
391 
ClearPointers(class C4Object * obj)392 void C4ConsoleGUI::ClearPointers(class C4Object *obj)
393 {
394 	if (state && state->object_list_model) state->object_list_model->Invalidate();
395 }
396 
EditGraphControl(const class C4ControlEditGraph * control)397 void C4ConsoleGUI::EditGraphControl(const class C4ControlEditGraph *control)
398 {
399 	if (state && state->property_model)
400 	{
401 		const char *path = control->GetPath();
402 		if (path && *path)
403 		{
404 			// Apply control to value: Resolve value
405 			C4Value graph_value = AulExec.DirectExec(::ScriptEngine.GetPropList(), path, "resolve graph edit", false, nullptr);
406 			// ...and apply changes (will check for value validity)
407 			C4ConsoleQtGraph::EditGraphValue(graph_value, control->GetAction(), control->GetIndex(), control->GetX(), control->GetY());
408 			// For remote clients, also update any edited shapes
409 			if (!control->LocalControl())
410 			{
411 				C4ConsoleQtShape *shape = state->property_model->GetShapeByPropertyPath(path);
412 				if (shape)
413 				{
414 					C4ConsoleQtGraph *shape_graph = shape->GetGraphShape();
415 					if (shape_graph)
416 					{
417 						shape_graph->EditGraph(false, control->GetAction(), control->GetIndex(), control->GetX(), control->GetY());
418 					}
419 				}
420 			}
421 		}
422 	}
423 }
424 
UpdateToolCtrls()425 void C4ToolsDlg::UpdateToolCtrls()
426 {
427 	// Set selected drawing tool
428 	if (::Console.Active) ::Console.state->SetDrawingTool(Tool);
429 }
430 
UpdateTextures()431 void C4ToolsDlg::UpdateTextures() { /* Textures are done with materials */ }
NeedPreviewUpdate()432 void C4ToolsDlg::NeedPreviewUpdate() { /* No preview */}
433 
InitGradeCtrl()434 void C4ToolsDlg::InitGradeCtrl()
435 {
436 	// Update current grade
437 	if (::Console.Active) ::Console.state->ui.drawSizeSlider->setValue(Grade);
438 }
439 
PopMaterial()440 bool C4ToolsDlg::PopMaterial()
441 {
442 	// Show material selection
443 	if (!::Console.Active) return false;
444 	::Console.state->ui.foregroundMatTexComboBox->setFocus();
445 	::Console.state->ui.foregroundMatTexComboBox->showPopup();
446 	return true;
447 }
448 
PopTextures()449 bool C4ToolsDlg::PopTextures()
450 {
451 	// Show texture selection
452 	if (!::Console.Active) return false;
453 	::Console.state->ui.foregroundMatTexComboBox->setFocus();
454 	::Console.state->ui.foregroundMatTexComboBox->showPopup();
455 	return true;
456 }
457 
UpdateIFTControls()458 void C4ToolsDlg::UpdateIFTControls() { /* not using IFT */ }
459 
UpdateLandscapeModeCtrls()460 void C4ToolsDlg::UpdateLandscapeModeCtrls()
461 {
462 	// Update button down states for landscape mode
463 	if (::Console.Active) ::Console.state->SetLandscapeMode(::Landscape.GetMode(), ::Game.C4S.Landscape.FlatChunkShapes);
464 }
465 
466 
EnableControls()467 void C4ToolsDlg::EnableControls() { /* Handled internally by tool selection */ }
468 
469 #include "editor/C4ConsoleGUICommon.h"
470