OpenClonk
C4EditCursor.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Handles viewport editing in console mode */
19 
20 #include "C4Include.h"
21 #include "editor/C4EditCursor.h"
22 
23 #include "editor/C4Console.h"
24 #include "object/C4Def.h"
25 #include "object/C4Object.h"
26 #include "game/C4Application.h"
27 #include "lib/C4Random.h"
28 #include "gui/C4MouseControl.h"
29 #include "landscape/C4Landscape.h"
30 #include "landscape/C4Texture.h"
32 #include "object/C4GameObjects.h"
33 #include "control/C4GameControl.h"
34 #include "script/C4AulExec.h"
35 #ifdef WITH_QT_EDITOR
37 #endif
38 #include "lib/StdMesh.h"
39 
40 #ifdef _WIN32
41 #include "res/resource.h"
42 #endif
43 
44 
46 {
47  StdStrBuf Output;
48  // Compose info text by selected object(s)
49  int32_t obj_count = size();
50  switch (obj_count)
51  {
52  // No selection
53  case 0:
54  Output = LoadResStr("IDS_CNS_NOOBJECT");
55  break;
56  // One selected object
57  case 1:
58  {
59  C4Object *obj = GetObject();
60  if (obj)
61  Output.Take(obj->GetDataString());
62  else
63  Output.Take(front().GetDataString());
64  break;
65  }
66  // Multiple selected objects
67  default:
68  Output.Format(LoadResStr("IDS_CNS_MULTIPLEOBJECTS"), obj_count);
69  break;
70  }
71  return Output;
72 }
73 
75 {
76  // Get indexed C4Object * in list
77  C4Object *obj;
78  for (const C4Value &v : (*this))
79  if ((obj = v.getObj()))
80  if (!index--)
81  return obj;
82  return nullptr;
83 }
84 
86 {
87  C4Object *obj, *last = nullptr;
88  for (const C4Value &v : (*this))
89  if ((obj = v.getObj()))
90  last = obj;
91  return last;
92 }
93 
95 {
96  // remove nullptred entries that may happen because objects got deleted
97  this->remove(C4VNull);
98 }
99 
101 {
102  bool found = false;
103  for (C4Value &v : (*this))
104  if (obj == v.getObj())
105  {
106  found = true;
107  v.Set0();
108  }
109  if (found) ConsolidateEmpty();
110  return found;
111 }
112 
114 {
115  for (const C4Value &v : (*this)) if (obj == v.getPropList()) return true;
116  return false;
117 }
118 
120 {
121  // count only C4Object *
122  int32_t count = 0;
123  for (const C4Value &v : *this) if (v.getObj()) ++count;
124  return count;
125 }
126 
127 
129 #ifdef WITH_QT_EDITOR
130  : shapes(new C4ConsoleQtShapes())
131 #endif
132 {
133  Default();
134 }
135 
137 {
138  Clear();
139 }
140 
142 {
143  // drawing
144  switch (Mode)
145  {
146  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
147  case C4CNS_ModeEdit:
148  // Hold selection
149  if (Hold)
150  EMMoveObject(fShiftWasDown ? EMMO_MoveForced : EMMO_Move, Fix0, Fix0, nullptr, &selection, nullptr, false);
151  break;
152  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
153  case C4CNS_ModeDraw:
154  switch (Console.ToolsDlg.Tool)
155  {
156  case C4TLS_Fill:
157  if (Hold) if (!Game.HaltCount) if (Console.Editing) ApplyToolFill();
158  break;
159  }
160  break;
161  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162  }
164  {
167  }
168 }
169 
171 {
172 
173 #ifdef USE_WIN32_WINDOWS
174  if (!(hMenu = LoadMenu(Application.GetInstance(),MAKEINTRESOURCE(IDR_CONTEXTMENUS))))
175  return false;
176 #endif
178 
179  return true;
180 }
181 
183 {
184  if (Target==pObj) Target=nullptr;
185  if (selection.ClearPointers(pObj))
187 }
188 
189 bool C4EditCursor::Move(float iX, float iY, float iZoom, DWORD dwKeyState)
190 {
191  // alt check
192  bool fAltIsDown = (dwKeyState & MK_ALT) != 0;
193  if (fAltIsDown != fAltWasDown)
194  {
195  if ((fAltWasDown = fAltIsDown))
196  AltDown();
197  else
198  AltUp();
199  }
200 
201  // shift check
202  bool fShiftIsDown = (dwKeyState & MK_SHIFT) != 0;
203  if(fShiftIsDown != fShiftWasDown)
204  fShiftWasDown = fShiftIsDown;
205 
206  // Offset movement
207  float xoff = iX-X; float yoff = iY-Y;
208  X = iX; Y = iY; Zoom = iZoom;
209 
210  // Drag rotation/scale of object
211  if (DragTransform)
212  {
213  C4Object *obj = selection.GetObject();
214  if (obj)
215  {
216  int32_t new_rot = (DragRot0 + int32_t(float(X - X2)*Zoom)) % 360;
217  if (new_rot < 0) new_rot += 360;
218  if (fShiftIsDown) new_rot = (new_rot + 23) / 45 * 45;
219  int32_t new_con = DragCon0 + int32_t(float(Y2 - Y)*Zoom*(FullCon / 200));
220  int32_t con_step = FullCon / 5;
221  if (fShiftIsDown) new_con = (new_con + con_step/2) / con_step * con_step;
222  if (!obj->Def->Oversize) new_con = std::min<int32_t>(new_con, FullCon);
223  new_con = std::max<int32_t>(new_con, fShiftIsDown ? 1 : con_step);
224  bool any_change = false;
225  if (obj->Def->Rotateable)
226  if (new_rot != DragRotLast)
227  any_change = true;
228  if (obj->Def->GrowthType)
229  if (new_con != DragConLast)
230  any_change = true;
231  if (any_change)
232  {
233  EMMoveObject(EMMO_Transform, itofix(new_rot, 1), itofix(new_con, FullCon/100), obj, nullptr);
234  DragRotLast = new_rot;
235  DragConLast = new_con;
236  }
237  }
238 
239  }
240 
241  switch (Mode)
242  {
243  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
244  case C4CNS_ModeEdit:
245 #ifdef WITH_QT_EDITOR
246  shapes->MouseMove(X, Y, Hold, 3.0f/Zoom, !!(dwKeyState & MK_SHIFT), !!(dwKeyState & MK_CONTROL));
247 #endif
248  // Hold
249  if (!DragFrame && Hold && !DragShape && !DragTransform)
250  {
251  MoveSelection(ftofix(xoff),ftofix(yoff), false);
252  UpdateDropTarget(dwKeyState);
253  }
254  // Update target
255  // Shift always indicates a target outside the current selection
256  else
257  {
258  Target = (dwKeyState & MK_SHIFT) ? selection.GetLastObject() : nullptr;
259  do
260  {
261  Target = Game.FindObject(nullptr,X,Y,0,0,OCF_NotContained, Target);
262  }
263  while ((dwKeyState & MK_SHIFT) && Target && selection.IsContained(Target));
264  }
265  break;
266  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
268  // Drop target for contained object creation
269  UpdateDropTarget(dwKeyState);
270  break;
271  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
272  case C4CNS_ModeDraw:
273  switch (Console.ToolsDlg.Tool)
274  {
275  case C4TLS_Brush:
276  if (Hold) ApplyToolBrush();
277  break;
278  case C4TLS_Line: case C4TLS_Rect:
279  break;
280  }
281  break;
282  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
283  }
284 
285  // Update
286  UpdateStatusBar();
287  return true;
288 }
289 
290 bool C4EditCursor::Move(DWORD new_key_state)
291 {
292  // Move at last position with new key state
293  return Move(X, Y, Zoom, new_key_state);
294 }
295 
297 {
298  int32_t X=this->X, Y=this->Y;
299  StdStrBuf str;
300  switch (Mode)
301  {
302  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
303  case C4CNS_ModePlay:
305  break;
306  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
307  case C4CNS_ModeEdit:
308  str.Format("%i/%i (%s)",X,Y,Target ? (Target->GetName()) : LoadResStr("IDS_CNS_NOTHING") );
309  break;
310  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
312  str.Format(LoadResStr("IDS_CNS_CREATESTATUS"), creator_def ? (creator_def->GetName()) : LoadResStr("IDS_CNS_NOTHING"));
313  break;
314  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
315  case C4CNS_ModeDraw:
316  str.Format("%i/%i (fg: %s, bg: %s)",X,Y,
317  MatValid(::Landscape.GetMat(X,Y)) ? ::MaterialMap.Map[::Landscape.GetMat(X,Y)].Name : LoadResStr("IDS_CNS_NOTHING"),
318  MatValid(::Landscape.GetBackMat(X,Y)) ? ::MaterialMap.Map[::Landscape.GetBackMat(X,Y)].Name : LoadResStr("IDS_CNS_NOTHING") );
319  break;
320  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
321  }
323 }
324 
325 void C4EditCursor::OnSelectionChanged(bool by_objectlist)
326 {
328  if (!by_objectlist) ::Console.ObjectListDlg.Update(selection);
329 }
330 
332 {
333  if (!add_proplist) return;
334  C4Object *add_obj = add_proplist->GetObject();
335  if (add_obj && !add_obj->Status) return;
336  if (selection.IsContained(add_proplist)) return;
337  // add object to selection and do script callback
338  selection.push_back(C4VPropList(add_proplist));
339 }
340 
342 {
343  if (!remove_proplist) return false;
344  C4Object *remove_obj = remove_proplist->GetObject();
345  if (remove_obj && !remove_obj->Status) return false;
346  // remove object from selection and do script callback
347  if (!selection.IsContained(remove_proplist)) return false;
348  selection.remove(C4VPropList(remove_proplist));
349  return true;
350 }
351 
353 {
354  // remove everything from selection
355  selection.clear();
356 }
357 
359 {
360 
361  // Hold
362  Hold=true;
363 
364  switch (Mode)
365  {
366  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
367  case C4CNS_ModeEdit:
368  // Click on shape?
369 #ifdef WITH_QT_EDITOR
370  if (shapes->MouseDown(X, Y, 3.0f / Zoom, !!(dwKeyState & MK_SHIFT), !!(dwKeyState & MK_CONTROL)))
371  {
372  DragShape = shapes->IsDragging();
373  break;
374  }
375 #endif
376  if (dwKeyState & MK_CONTROL)
377  {
378  // Toggle target
379  if (Target)
382  }
383  else
384  {
385  // Click rotate/scale marker?
387  {
388  DragTransform = true;
389  X2 = X; Y2 = Y;
390  C4Object *dragged_obj = selection.GetObject();
391  DragRot0 = DragRotLast = dragged_obj->GetR();
392  DragCon0 = DragConLast = dragged_obj->GetCon();
393  break;
394  }
395  // Click on unselected: select single
396  if (Target)
397  {
398  bool found = false;
399  for (C4Value &obj : selection)
400  {
401  if(obj.getObj() && obj.getObj()->At(X, Y))
402  {
403  found = true;
404  break;
405  }
406  }
407  if(!found) // means loop didn't break
408  {
411  }
412  }
413  // Click on nothing: drag frame
414  if (!Target)
415  { ClearSelection(); DragFrame=true; X2=X; Y2=Y; }
416  }
418  break;
419  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
421  ApplyCreateObject(!!(dwKeyState & MK_CONTROL));
422  break;
423  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
424  case C4CNS_ModeDraw:
425  switch (Console.ToolsDlg.Tool)
426  {
427  case C4TLS_Brush: ApplyToolBrush(); break;
428  case C4TLS_Line: DragLine=true; X2=X; Y2=Y; break;
429  case C4TLS_Rect: DragFrame=true; X2=X; Y2=Y; break;
430  case C4TLS_Fill:
431  if (Game.HaltCount)
432  { Hold=false; Console.Message(LoadResStr("IDS_CNS_FILLNOHALT")); return false; }
433  break;
434  case C4TLS_Picker: ApplyToolPicker(); break;
435  }
436  break;
437  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
438  }
439 
440  DropTarget=nullptr;
441 
442  return true;
443 }
444 
446 {
447 
448  switch (Mode)
449  {
450  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
451  case C4CNS_ModeEdit:
452  if ( (dwKeyState & MK_CONTROL) == 0)
453  {
454  // Check whether cursor is on anything in the selection
455  bool fCursorIsOnSelection = false;
456  for (C4Value &obj : selection)
457  {
458  if (obj.getObj() && obj.getObj()->At(X,Y))
459  {
460  fCursorIsOnSelection = true;
461  break;
462  }
463  }
464  if (!fCursorIsOnSelection)
465  {
466  // Click on unselected
468  {
470  }
471  // Click on nothing
472  if (!Target) ClearSelection();
473  }
474  }
476  break;
477  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
478  }
479 
480  return true;
481 }
482 
484 {
485  // Finish edit/tool
486  switch (Mode)
487  {
488  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
489  case C4CNS_ModeEdit:
490  // Finish object drag
491  if (!DragFrame && Hold && !DragShape && !DragTransform)
492  {
493  MoveSelection(Fix0, Fix0, true);
494  }
495  if (DragFrame) FrameSelection();
496  if (DropTarget) PutContents();
497  break;
498  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
499  case C4CNS_ModeDraw:
500  switch (Console.ToolsDlg.Tool)
501  {
502  case C4TLS_Line:
503  if (DragLine) ApplyToolLine();
504  break;
505  case C4TLS_Rect:
506  if (DragFrame) ApplyToolRect();
507  break;
508  }
509  break;
510  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
511  }
512 
513  // Release
514 #ifdef WITH_QT_EDITOR
515  shapes->MouseUp(X, Y, !!(dwKeyState & MK_SHIFT), !!(dwKeyState & MK_CONTROL));
516 #endif
517  Hold=false;
518  DragFrame=false;
519  DragLine=false;
520  DragShape = false;
521  DragTransform = false;
522  DropTarget=nullptr;
523  // Update
524  UpdateStatusBar();
525  return true;
526 }
527 
528 bool C4EditCursor::KeyDown(C4KeyCode KeyCode, DWORD dwKeyState)
529 {
530  // alt check
531  bool fAltIsDown = (dwKeyState & MK_ALT) != 0;
532  fAltIsDown = fAltIsDown || KeyCode == K_ALT_L || KeyCode == K_ALT_R;
533  if (fAltIsDown != fAltWasDown)
534  {
535  if ((fAltWasDown = fAltIsDown))
536  AltDown();
537  else
538  AltUp();
539  }
540 
541  // shift check
542  bool fShiftIsDown = (dwKeyState & MK_SHIFT) != 0;
543  fShiftIsDown = fShiftIsDown || KeyCode == K_SHIFT_L || KeyCode == K_SHIFT_R;
544  if(fShiftIsDown != fShiftWasDown)
545  fShiftWasDown = fShiftIsDown;
546 
547  return true;
548 }
549 
550 bool C4EditCursor::KeyUp(C4KeyCode KeyCode, DWORD dwKeyState)
551 {
552  // alt check
553  bool fAltIsDown = (dwKeyState & MK_ALT) != 0;
554  fAltIsDown = fAltIsDown && !( KeyCode == K_ALT_L || KeyCode == K_ALT_R);
555  if (fAltIsDown != fAltWasDown)
556  {
557  if ((fAltWasDown = fAltIsDown))
558  AltDown();
559  else
560  AltUp();
561  }
562 
563  // shift check
564  bool fShiftIsDown = (dwKeyState & MK_SHIFT) != 0;
565  fShiftIsDown = fShiftIsDown && !(KeyCode == K_SHIFT_L || KeyCode == K_SHIFT_R);
566  if(fShiftIsDown != fShiftWasDown)
567  fShiftWasDown = fShiftIsDown;
568 
569  return true;
570 }
571 
572 #ifdef USE_WIN32_WINDOWS
573 bool SetMenuItemEnable(HMENU hMenu, WORD id, bool fEnable)
574 {
575  return !!EnableMenuItem(hMenu,id,MF_BYCOMMAND | MF_ENABLED | ( fEnable ? 0 : MF_GRAYED));
576 }
577 
578 bool SetMenuItemText(HMENU hMenu, WORD id, const char *szText)
579 {
580  MENUITEMINFOW minfo;
581  ZeroMem(&minfo,sizeof(minfo));
582  minfo.cbSize = sizeof(minfo);
583  minfo.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
584  minfo.fType = MFT_STRING;
585  minfo.wID = id;
586  StdBuf td = GetWideCharBuf(szText);
587  minfo.dwTypeData = getMBufPtr<wchar_t>(td);
588  minfo.cch = wcslen(minfo.dwTypeData);
589  return !!SetMenuItemInfoW(hMenu,id,false,&minfo);
590 }
591 #endif
592 
594 {
595  Target=nullptr;
596 #ifndef WITH_QT_EDITOR
597  DoContextMenu(dwKeyState);
598 #endif
599  // Update
600  UpdateStatusBar();
601 
602  return true;
603 }
604 
606 {
607  if (!EditingOK()) return false;
609  if (::Control.isCtrlHost())
610  {
612  }
613  return true;
614 }
615 
617 {
618  switch (Mode)
619  {
620  case C4CNS_ModeEdit: case C4CNS_ModePlay:
623  break;
624  case C4CNS_ModeDraw:
626  break;
627  }
628  return true;
629 }
630 
632 {
634  return true;
635 }
636 
637 void C4EditCursor::PerformDuplication(int32_t *object_numbers, int32_t object_count, bool local_call)
638 {
639  if (!object_count) return;
640  // Remember last OEI so duplicated objects can be determined
641  int32_t prev_oei = C4PropListNumbered::GetEnumerationIndex();
642  // Get serialized objects
643  C4RefCntPointer<C4ValueArray> object_numbers_c4v = new C4ValueArray();
644  object_numbers_c4v->SetSize(object_count);
645  int32_t total_object_count = 0;
646  for (int32_t i = 0; i < object_count; ++i)
647  {
648  C4Object *obj = ::Objects.SafeObjectPointer(object_numbers[i]);
649  if (!obj) continue;
650  total_object_count = obj->AddObjectAndContentsToArray(object_numbers_c4v, total_object_count);
651  }
652  object_numbers_c4v->SetSize(total_object_count);
653  int32_t objects_file_handle = ::ScriptEngine.CreateUserFile();
654  C4AulParSet pars(C4VInt(objects_file_handle), C4VArray(object_numbers_c4v.Get()));
656  bool result = !!result_c4v;
657  if (result_c4v.GetType() == C4V_Nil)
658  {
659  // Function returned nil: This usually means there was a script error during object writing.
660  // It could also mean the scripter overloaded global func SaveScenarioObjects and returned nil.
661  // In either case, no the objects file will contain garbage so no objects were duplicated.
662  LogF("ERROR: No valid result from global func " PSF_SaveScenarioObjects ". Regular object duplication failed.");
663  }
664  else
665  {
666  // Function completed successfully (returning true or false)
667  C4AulUserFile *file = ::ScriptEngine.GetUserFile(objects_file_handle);
668  if (!result || !file || !file->GetFileLength())
669  {
670  // Nothing written? Then we don't have objects.
671  // That's OK; not an error.
672  }
673  else
674  {
675  // Create copy of objects by executing duplication script
676  StdStrBuf data = file->GrabFileContents();
677  AulExec.DirectExec(&::ScriptEngine, data.getData(), "object duplication", false, nullptr, true);
678  }
679  }
680  ::ScriptEngine.CloseUserFile(objects_file_handle);
681  // Did duplication work?
682  bool any_duplicates = false;
683  for (C4Object *obj : ::Objects)
684  {
685  if (obj->Number > prev_oei)
686  {
687  any_duplicates = true;
688  break;
689  }
690  }
691  // If duplication created no objects, the user probably tried to copy a non-saved object
692  // Just copy the old way then
693  if (!any_duplicates)
694  {
695  PerformDuplicationLegacy(object_numbers, object_count, local_call);
696  return;
697  }
698  // Update local client status: Put new objects into selection
699  if (local_call)
700  {
701  selection.clear();
702  int64_t X_all = 0, Y_all = 0, n_selected = 0;
703  float old_x = X, old_y = Y;
704  for (C4Object *obj : ::Objects)
705  {
706  if (obj->Number > prev_oei)
707  {
708  selection.push_back(C4VObj(obj));
709  X_all += obj->GetX();
710  Y_all += obj->GetY();
711  ++n_selected;
712  }
713  }
714  // Reset EditCursor pos to center of duplicated objects, so they will be dragged along with the cursor
715  if (n_selected)
716  {
717  X = X_all / n_selected;
718  Y = Y_all / n_selected;
719  }
720  SetHold(true);
722  // Ensure duplicated objects moved to the cursor without extra mouse movement
723  // Move with shift key pressed to allow initial shift of HorizontalFixed items
724  DWORD last_key_state = MK_SHIFT;
725  if (fAltWasDown) last_key_state |= MK_ALT;
726  bool shift_was_down = fShiftWasDown;
727  Move(old_x, old_y, Zoom, last_key_state);
728  fShiftWasDown = shift_was_down;
729  }
730 }
731 
732 void C4EditCursor::PerformDuplicationLegacy(int32_t *pObjects, int32_t iObjectNum, bool fLocalCall)
733 {
734  // Old-style object copying: Just create new objects at old object position with same prototype
735  C4Object *pOldObj, *pObj;
736  for (int i = 0; i<iObjectNum; ++i)
737  if ((pOldObj = ::Objects.SafeObjectPointer(pObjects[i])))
738  {
739  pObj = Game.CreateObject(pOldObj->GetPrototype(), pOldObj, pOldObj->Owner, pOldObj->GetX(), pOldObj->GetY());
740  if (pObj && pObj->Status)
741  {
742  // local call? adjust selection then
743  // do callbacks for all clients for sync reasons
744  if (fLocalCall) selection.push_back(C4VObj(pObj));
745  C4AulParSet pars(C4VObj(pObj));
746  if (pOldObj->Status) pOldObj->Call(PSF_EditCursorDeselection, &pars);
747  if (pObj->Status) pObj->Call(PSF_EditCursorSelection);
748  }
749  }
750  // update status
751  if (fLocalCall)
752  {
753  for (int i = 0; i<iObjectNum; ++i)
754  if ((pOldObj = ::Objects.SafeObjectPointer(pObjects[i])))
755  selection.remove(C4VObj(pOldObj));
756  SetHold(true);
758  }
759 }
760 
761 void C4EditCursor::DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight, bool draw_transform_marker)
762 {
763  // target pos (parallax)
764  float line_width = std::max<float>(1.0f, 1.0f / cgo.Zoom);
765  float offX, offY, newzoom;
766  cobj->GetDrawPosition(cgo, offX, offY, newzoom);
767  ZoomDataStackItem zdsi(cgo.X, cgo.Y, newzoom);
768  if (select_mark_color)
769  {
770  FLOAT_RECT frame =
771  {
772  offX + cobj->Shape.x,
773  offX + cobj->Shape.x + cobj->Shape.Wdt,
774  offY + cobj->Shape.y,
775  offY + cobj->Shape.y + cobj->Shape.Hgt
776  };
777  DrawSelectMark(cgo, frame, line_width, select_mark_color);
778  }
779  if (highlight)
780  {
781  uint32_t dwOldMod = cobj->ColorMod;
782  uint32_t dwOldBlitMode = cobj->BlitMode;
783  cobj->ColorMod = 0xffffffff;
785 
786  if (cobj->pMeshInstance)
788 
789  cobj->Draw(cgo, -1);
790  cobj->DrawTopFace(cgo, -1);
791 
792  if (cobj->pMeshInstance)
794 
795  cobj->ColorMod = dwOldMod;
796  cobj->BlitMode = dwOldBlitMode;
797  }
798  // Transformer knob
799  if (draw_transform_marker)
800  {
801  float transform_marker_x = 0.0f, transform_marker_y = 0.0f;
802  if (HasTransformMarker(&transform_marker_x, &transform_marker_y, cgo.Zoom))
803  {
804  transform_marker_x += offX; transform_marker_y += offY;
805  float sz = float(::GraphicsResource.fctTransformKnob.Hgt) / cgo.Zoom;
806  C4Facet transform_target_sfc(cgo.Surface, transform_marker_x-sz/2, transform_marker_y-sz/2, sz, sz);
807  ::GraphicsResource.fctTransformKnob.Draw(transform_target_sfc);
808  // Transform knob while dragging
809  if (DragTransform)
810  {
812  transform_target_sfc.X += X - X2;
813  transform_target_sfc.Y += Y - Y2;
814  ::GraphicsResource.fctTransformKnob.Draw(transform_target_sfc);
815  pDraw->ResetBlitMode();
816  }
817  }
818  }
819 }
820 
821 bool C4EditCursor::HasTransformMarker(float *x, float *y, float zoom) const
822 {
823  // Single selection only (assume obj is in selection)
824  if (selection.size() != 1) return false;
825  C4Object *obj = selection.GetObject();
826  if (!obj) return false;
827  // Show knob only for objects that can be scaled or rotated
828  if (!obj->Def->GrowthType && !obj->Def->Rotateable) return false;
829  // Show knob only if the shape has a certain minimum size in either extent (so small objects can still be moved)
830  float vis_wdt = float(obj->Shape.Wdt) * zoom;
831  float vis_hgt = float(obj->Shape.Wdt) * zoom;
832  if (vis_wdt < ::GraphicsResource.fctTransformKnob.Hgt && vis_hgt < ::GraphicsResource.fctTransformKnob.Hgt) return false;
833  // It's visible: Put it to the bottom of the shape without the shape expansion through rotation
834  *x = 0;
835  *y = float(obj->Def->Shape.y + obj->Def->Shape.Hgt) * obj->GetCon() / FullCon - float(::GraphicsResource.fctTransformKnob.Hgt) / (zoom*2);
836  return true;
837 }
838 
840 {
841  ZoomDataStackItem zdsi(cgo.X, cgo.Y, cgo.Zoom);
842  float line_width = std::max<float>(1.0f, 1.0f / cgo.Zoom);
843 #ifdef WITH_QT_EDITOR
844  // Draw shapes of selection
845  shapes->Draw(cgo);
846 #endif
847  // Draw selection marks
848  for (C4Value &obj : selection)
849  {
850  C4Object *cobj = obj.getObj();
851  if (!cobj) continue;
852  DrawObject(cgo, cobj, 0xffffffff, fShiftWasDown, true); // highlight selection if shift is pressed
853  }
854  // Draw drag frame
855  if (DragFrame)
857  std::min(X, X2) + cgo.X - cgo.TargetX, std::min(Y, Y2) + cgo.Y - cgo.TargetY,
858  std::max(X, X2) + cgo.X - cgo.TargetX, std::max(Y, Y2) + cgo.Y - cgo.TargetY, 0xffffffff, line_width);
859  // Draw drag line
860  if (DragLine)
861  pDraw->DrawLineDw(cgo.Surface,
862  X + cgo.X - cgo.TargetX, Y + cgo.Y - cgo.TargetY,
863  X2 + cgo.X - cgo.TargetX, Y2 + cgo.Y - cgo.TargetY, 0xffffffff, line_width);
864  // Draw drop target
865  if (DropTarget)
867  DropTarget->GetX() + cgo.X - cgo.TargetX - ::GraphicsResource.fctMouseCursor.Wdt / 2 / cgo.Zoom,
869  float(::GraphicsResource.fctMouseCursor.Wdt) / cgo.Zoom,
871  // Draw paint circle
873  {
874  // shadow for recognition on white background/material
875  pDraw->DrawCircleDw(cgo.Surface, X + cgo.X - cgo.TargetX + 1.0f/cgo.Zoom, Y + cgo.Y - cgo.TargetY + 1.0f / cgo.Zoom, ::Console.ToolsDlg.Grade, 0xff000000, line_width);
876  // actual circle
877  pDraw->DrawCircleDw(cgo.Surface, X + cgo.X - cgo.TargetX, Y + cgo.Y - cgo.TargetY, ::Console.ToolsDlg.Grade, 0xffffffff, line_width);
878  }
879  // Draw creator preview
881  {
882  C4TargetFacet cgo_creator;
883  // Add the shape's offset and the shape's width / 2 (or height, resp.).
884  // This does nothing for most objects, where these two sum up to 0.
885  // However, for some objects, this fixes the preview, so it actually lines up with
886  // the position where it is placed.
887  cgo_creator.Set(cgo.Surface,
888  X + cgo.X - cgo.TargetX + creator_def->Shape.x + creator_def->Shape.Wdt / 2,
889  Y + cgo.Y - cgo.TargetY + creator_def->Shape.y + creator_def->Shape.Hgt / 2,
890  creator_def->Shape.Wdt, creator_def->Shape.Hgt, 0, 0, cgo.Zoom, 0, 0);
891  if (!creator_overlay)
892  {
893  creator_overlay = std::make_unique<C4GraphicsOverlay>();
895  }
896  creator_overlay->Draw(cgo_creator, nullptr, NO_OWNER);
897  }
898  // Draw object highlight
899  C4Object *highlight = highlighted_object.getObj();
900  if (highlight) DrawObject(cgo, highlight, 0xffff8000, true, false); // highlight selection if shift is pressed
901 }
902 
903 
904 void C4EditCursor::DrawSelectMark(C4Facet &cgo, FLOAT_RECT frame, float width, uint32_t color)
905 {
906  if ((cgo.Wdt<1) || (cgo.Hgt<1)) return;
907 
908  if (!cgo.Surface) return;
909 
910  const float EDGE_WIDTH = 2.f;
911 
912  unsigned char c[4] = {
913  static_cast<unsigned char>((color >> 16) & 0xff),
914  static_cast<unsigned char>((color >> 8) & 0xff),
915  static_cast<unsigned char>((color >> 0) & 0xff),
916  static_cast<unsigned char>((color >> 24) & 0xff)
917  };
918 
919  const C4BltVertex vertices[] = {
920  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left + EDGE_WIDTH, frame.top, 0.f },
921  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.top, 0.f },
922  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.top, 0.f },
923  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.top+EDGE_WIDTH, 0.f },
924 
925  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left+EDGE_WIDTH, frame.bottom-1, 0.f },
926  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.bottom-1, 0.f },
927  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.bottom-1, 0.f },
928  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.bottom-1-EDGE_WIDTH, 0.f },
929 
930  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1-EDGE_WIDTH, frame.top, 0.f },
931  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.top, 0.f },
932  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.top, 0.f },
933  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.top+EDGE_WIDTH, 0.f },
934 
935  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1-EDGE_WIDTH, frame.bottom-1, 0.f },
936  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.bottom-1, 0.f },
937  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.bottom-1, 0.f },
938  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.bottom-1-EDGE_WIDTH, 0.f },
939  };
940 
941  const unsigned int n_vertices = sizeof(vertices) / sizeof(vertices[0]);
942 
943  pDraw->PerformMultiLines(cgo.Surface, vertices, n_vertices, width, nullptr);
944 }
945 
946 
947 void C4EditCursor::MoveSelection(C4Real XOff, C4Real YOff, bool drag_finished)
948 {
949  EMMoveObject(fShiftWasDown ? EMMO_MoveForced : EMMO_Move, XOff, YOff, nullptr, &selection, nullptr, drag_finished);
950 }
951 
953 {
954  ClearSelection();
955  for (C4Object *cobj : Objects)
956  {
957  if (cobj->Status && cobj->OCF & OCF_NotContained)
958  {
959  if (Inside(cobj->GetX(),std::min(X,X2),std::max(X,X2)) && Inside(cobj->GetY(),std::min(Y,Y2),std::max(Y,Y2)))
960  AddToSelection(cobj);
961  }
962  }
964 }
965 
966 bool C4EditCursor::In(const char *szText)
967 {
969  EMMoveObject(EMMO_Script, Fix0, Fix0, nullptr, &selection, szText);
972  return true;
973 }
974 
976 {
977  fAltWasDown=false;
978  fShiftWasDown=false;
980  X=Y=X2=Y2=0;
981  Target=DropTarget=nullptr;
982 #ifdef USE_WIN32_WINDOWS
983  hMenu=nullptr;
984 #endif
986  selection.clear();
987  creator_def = nullptr;
988  creator_overlay = nullptr;
989  has_mouse_hover = false;
990  selection_invalid = false;
992 }
993 
995 {
996 #ifdef USE_WIN32_WINDOWS
997  if (hMenu) DestroyMenu(hMenu); hMenu=nullptr;
998 #endif
999 #ifdef WITH_DEBUG_MODE
1001 #endif
1002  selection.clear();
1004  creator_overlay.reset(nullptr);
1005 #ifdef WITH_QT_EDITOR
1006  shapes->ClearShapes(); // Should really be empty already
1007 #endif
1008 }
1009 
1010 bool C4EditCursor::SetMode(int32_t iMode)
1011 {
1012  // Store focus
1013 #ifdef USE_WIN32_WINDOWS
1014  HWND hFocus=GetFocus();
1015 #endif
1016  // Update console buttons (always)
1017  Console.UpdateModeCtrls(iMode);
1018  // No change
1019  if (iMode==Mode) return true;
1020  // Set mode
1021  Mode = iMode;
1022  // Update prop tools by mode
1023  switch (Mode)
1024  {
1027  break;
1028  case C4CNS_ModeDraw:
1030  break;
1031  }
1032  if (Mode == C4CNS_ModePlay)
1033  {
1035  }
1036  else
1037  {
1038  OpenPropTools();
1040  }
1041  // Restore focus
1042 #ifdef USE_WIN32_WINDOWS
1043  SetFocus(hFocus);
1044 #endif
1045  // Done
1046  return true;
1047 }
1048 
1050 {
1051 
1052  if (!EditingOK()) return false;
1053 
1054  // Step through modes
1055  int32_t iNewMode;
1056  switch (Mode)
1057  {
1058  case C4CNS_ModePlay: iNewMode=C4CNS_ModeEdit; break;
1059 #ifdef WITH_QT_EDITOR
1060  case C4CNS_ModeEdit: iNewMode=C4CNS_ModeCreateObject; break;
1061 #else
1062  case C4CNS_ModeEdit: iNewMode = C4CNS_ModeDraw; break;
1063 #endif
1064  case C4CNS_ModeCreateObject: iNewMode = C4CNS_ModeDraw; break;
1065  case C4CNS_ModeDraw: iNewMode=C4CNS_ModePlay; break;
1066  default: iNewMode=C4CNS_ModePlay; break;
1067  }
1068 
1069  // Set new mode
1070  SetMode(iNewMode);
1071 
1072  return true;
1073 }
1074 
1076 {
1077  if (!EditingOK()) return;
1078  if (!creator_def) return;
1079  if (container && !DropTarget) return;
1080  // execute/send control
1082 }
1083 
1085 {
1086  if (!EditingOK(true)) return;
1087  C4ToolsDlg *pTools=&Console.ToolsDlg;
1088  // execute/send control
1089  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Brush, ::Landscape.GetMode(), X,Y,0,0, pTools->Grade, pTools->Material, pTools->Texture, pTools->BackMaterial, pTools->BackTexture));
1090 }
1091 
1093 {
1094  if (!EditingOK(true)) return;
1095  C4ToolsDlg *pTools=&Console.ToolsDlg;
1096  // execute/send control
1097  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Line, ::Landscape.GetMode(), X,Y,X2,Y2, pTools->Grade, pTools->Material,pTools->Texture, pTools->BackMaterial, pTools->BackTexture));
1098 }
1099 
1101 {
1102  if (!EditingOK(true)) return;
1103  C4ToolsDlg *pTools=&Console.ToolsDlg;
1104  // execute/send control
1105  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Rect, ::Landscape.GetMode(), X,Y,X2,Y2, pTools->Grade, pTools->Material, pTools->Texture, pTools->BackMaterial, pTools->BackTexture));
1106 }
1107 
1109 {
1110  if (!EditingOK(true)) return;
1111  C4ToolsDlg *pTools=&Console.ToolsDlg;
1112  // execute/send control
1113  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Fill, ::Landscape.GetMode(), X,Y,0,Y2, pTools->Grade, pTools->Material, nullptr, nullptr, nullptr));
1114 }
1115 
1116 void C4EditCursor::AppendMenuItem(int num, const StdStrBuf & label)
1117 {
1118 #ifdef USE_WIN32_WINDOWS
1119  itemsObjselect[num].ItemId = IDM_VPORTDYN_FIRST + num;
1120  if (num)
1121  AppendMenu(GetSubMenu(hMenu,0), MF_STRING, IDM_VPORTDYN_FIRST + num, label.GetWideChar());
1122  else
1123  AppendMenu(GetSubMenu(hMenu,0), MF_SEPARATOR, IDM_VPORTDYN_FIRST, nullptr);
1124 #endif
1125 }
1126 
1128 {
1129 #ifdef USE_WIN32_WINDOWS
1130  bool fObjectSelected = !!selection.GetObject();
1131  POINT point; GetCursorPos(&point);
1132  HMENU hContext = GetSubMenu(hMenu,0);
1133  SetMenuItemEnable(hContext, IDM_VIEWPORT_DELETE, fObjectSelected && Console.Editing);
1134  SetMenuItemEnable(hContext, IDM_VIEWPORT_DUPLICATE, fObjectSelected && Console.Editing);
1135  SetMenuItemEnable(hContext, IDM_VIEWPORT_CONTENTS, fObjectSelected && selection.GetObject()->Contents.ObjectCount() && Console.Editing);
1136  SetMenuItemText(hContext,IDM_VIEWPORT_DELETE,LoadResStr("IDS_MNU_DELETE"));
1137  SetMenuItemText(hContext,IDM_VIEWPORT_DUPLICATE,LoadResStr("IDS_MNU_DUPLICATE"));
1138  SetMenuItemText(hContext,IDM_VIEWPORT_CONTENTS,LoadResStr("IDS_MNU_CONTENTS"));
1139 #endif
1140 
1141  // Add selection and custom command entries for any objects at the cursor
1142  ObjselectDelItems(); // clear previous entries
1143  C4FindObjectAtPoint pFO(X,Y);
1144  C4ValueArray * atcursor; atcursor = pFO.FindMany(::Objects, ::Objects.Sectors); // needs freeing (single object ptr)
1145  int itemcount = atcursor->GetSize();
1146  if(itemcount > 0)
1147  {
1148  // Count required entries for all objects and their custom commands
1149  int entrycount = itemcount;
1150  for (int i_item = 0; i_item < itemcount; ++i_item)
1151  {
1152  C4Object *pObj = (*atcursor)[i_item].getObj(); assert(pObj);
1153  C4ValueArray *custom_commands = pObj->GetPropertyArray(P_EditCursorCommands);
1154  if (custom_commands) entrycount += custom_commands->GetSize();
1155  }
1156 #ifdef USE_WIN32_WINDOWS
1157  // If too many entries would be shown, add a "..." in the end
1158  const int maxentries = 25; // Maximum displayed objects. if you raise it, also change note with IDM_VPORTDYN_FIRST in resource.h
1159  bool has_too_many_entries = (entrycount > maxentries);
1160  if (has_too_many_entries) entrycount = maxentries + 1;
1161 #else
1162  const int maxentries = std::numeric_limits<int>::max();
1163 #endif
1164  itemsObjselect.resize(entrycount + 1); // +1 for a separator
1165  // Add a separator bar
1166  itemsObjselect[0].Object = nullptr;
1167  itemsObjselect[0].Command.Clear();
1168  itemsObjselect[0].EditCursor = this;
1169  AppendMenuItem(0, StdStrBuf());
1170  // Add all objects
1171  int i_entry = 0;
1172  for (int i_item = 0; i_item < itemcount; ++i_item)
1173  {
1174  ++i_entry; if (i_entry >= maxentries) break;
1175  // Add selection entry
1176  C4Object *obj = (*atcursor)[i_item].getObj();
1177  assert(obj);
1178  itemsObjselect[i_entry].Object = obj;
1179  itemsObjselect[i_entry].Command.Clear();
1180  itemsObjselect[i_entry].EditCursor = this;
1181  AppendMenuItem(i_entry, FormatString("%s #%i (%i/%i)", obj->GetName(), obj->Number, obj->GetX(), obj->GetY()));
1182 
1183  // Add custom command entries
1184  C4ValueArray *custom_commands = obj->GetPropertyArray(P_EditCursorCommands);
1185  if (custom_commands) for (int i_cmd = 0; i_cmd < custom_commands->GetSize(); ++i_cmd)
1186  {
1187  ++i_entry; if (i_entry >= maxentries) break;
1188  const C4Value &cmd = custom_commands->GetItem(i_cmd);
1189  StdStrBuf custom_command_szstr; C4AulFunc *custom_command; C4String *custom_command_string;
1190  // Custom command either by string or by function pointer
1191  if ((custom_command = cmd.getFunction()))
1192  custom_command_szstr.Format("%s()", custom_command->GetName());
1193  else if ((custom_command_string = cmd.getStr()))
1194  custom_command_szstr.Copy(custom_command_string->GetData()); // copy just in case script get reloaded inbetween
1195  if (custom_command_szstr.getLength())
1196  {
1197  itemsObjselect[i_entry].Object = obj;
1198  itemsObjselect[i_entry].Command.Take(custom_command_szstr);
1199  itemsObjselect[i_entry].EditCursor = this;
1200  AppendMenuItem(i_entry, FormatString("%s->%s", obj->GetName(), custom_command_szstr.getData()));
1201  }
1202  }
1203  }
1204 #ifdef USE_WIN32_WINDOWS
1205  if (has_too_many_entries)
1206  {
1207  AppendMenu(hContext, MF_GRAYED, IDM_VPORTDYN_FIRST + maxentries + 1, L"...");
1208  itemsObjselect[maxentries + 1].ItemId = IDM_VPORTDYN_FIRST + maxentries + 1;
1209  itemsObjselect[maxentries + 1].Object = nullptr;
1210  itemsObjselect[maxentries + 1].Command.Clear();
1211  }
1212 #endif
1213  }
1214  delete atcursor;
1215 
1216 #ifdef USE_WIN32_WINDOWS
1217  int32_t iItem = TrackPopupMenu(
1218  hContext,
1219  TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NONOTIFY,
1220  point.x,point.y, 0,
1221  Console.hWindow,
1222  nullptr);
1223  switch (iItem)
1224  {
1225  case IDM_VIEWPORT_DELETE: Delete(); break;
1226  case IDM_VIEWPORT_DUPLICATE: Duplicate(); break;
1227  case IDM_VIEWPORT_CONTENTS: GrabContents(); break;
1228  case 0: break;
1229  default:
1230  for(std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin() + 1; it != itemsObjselect.end(); ++it)
1231  if(it->ItemId == iItem)
1232  {
1233  if (it->Command.getLength())
1234  DoContextObjCommand(it->Object, it->Command.getData());
1235  else
1236  DoContextObjsel(it->Object, (dwKeyState & MK_SHIFT) == 0);
1237  break;
1238  }
1239  break;
1240  }
1242 #endif
1243  return true;
1244 }
1245 
1247 {
1248  // Set selection
1249  C4Object *pFrom;
1250  if (!( pFrom = selection.GetObject() )) return;
1251  ClearSelection();
1252  for (C4Object *cont : pFrom->Contents) AddToSelection(cont);
1254  Hold=true;
1255 
1256  // Exit all objects
1257  EMMoveObject(EMMO_Exit, Fix0, Fix0, nullptr, &selection);
1258 }
1259 
1261 {
1262  // A drop target is set if holding down control either while moving an object or in object creation mode
1263  DropTarget=nullptr;
1264 
1265  if (dwKeyState & MK_CONTROL)
1267  for (C4Object *cobj : Objects)
1268  {
1269  if (cobj->Status)
1270  if (!cobj->Contained)
1271  if (Inside<int32_t>(X-(cobj->GetX()+cobj->Shape.x),0,cobj->Shape.Wdt-1))
1272  if (Inside<int32_t>(Y-(cobj->GetY()+cobj->Shape.y),0,cobj->Shape.Hgt-1))
1273  if (!selection.IsContained(cobj))
1274  { DropTarget=cobj; break; }
1275  }
1276 
1277 }
1278 
1280 {
1281  if (!DropTarget) return;
1283 }
1284 
1286 {
1287  return Target;
1288 }
1289 
1290 bool C4EditCursor::EditingOK(bool for_landscape_drawing)
1291 {
1292  if (!Console.Editing)
1293  {
1294  Hold=false;
1295  Console.Message(LoadResStr("IDS_CNS_NONETEDIT"));
1296  return false;
1297  }
1298  return true;
1299 }
1300 
1302 {
1303  return Mode;
1304 }
1305 
1307 {
1308  C4ToolsDlg *pTools=&Console.ToolsDlg;
1309  Hold=false;
1310  Console.Message(FormatString(LoadResStr("IDS_CNS_NOMATDEF"),pTools->Material,pTools->Texture).getData());
1311 }
1312 
1314 {
1315  int32_t iMaterial;
1316  BYTE byIndex;
1317  switch (::Landscape.GetMode())
1318  {
1319  case LandscapeMode::Static:
1320  {
1321  bool material_set = false;
1322  int32_t x = X/::Landscape.GetMapZoom();
1323  int32_t y = Y/::Landscape.GetMapZoom();
1324  // Material-texture from map
1325  if ((byIndex = ::Landscape.GetMapIndex(x, y)))
1326  {
1327  const C4TexMapEntry *pTex = ::TextureMap.GetEntry(byIndex);
1328  if (pTex && pTex->GetMaterialName() && *pTex->GetMaterialName())
1329  {
1330  const BYTE byIndexBkg = Landscape.GetBackMapIndex(x, y);
1333 
1334  // Set background index if GUI backend supports it
1336  {
1337  const C4TexMapEntry *pBgTex = ::TextureMap.GetEntry(byIndexBkg);
1338  if (pBgTex && !pBgTex->isNull())
1339  {
1342  }
1343  else
1344  {
1346  }
1347  }
1348  else
1349  {
1350  Console.ToolsDlg.SetIFT(byIndexBkg != 0);
1351  }
1352 
1353  material_set = true;
1354  }
1355  }
1356  // default to sky, because invalid materials are always rendered as sky
1357  if (!material_set) Console.ToolsDlg.SelectMaterial(C4TLS_MatSky);
1358  break;
1359  }
1360  case LandscapeMode::Exact:
1361  // Material only from landscape
1362  if (MatValid(iMaterial=GBackMat(X,Y)))
1363  {
1366  }
1367  else
1369  break;
1370  }
1371  Hold=false;
1372 }
1373 
1374 void C4EditCursor::EMMoveObject(C4ControlEMObjectAction eAction, C4Real tx, C4Real ty, C4Object *pTargetObj, const C4EditCursorSelection *pObjs, const char *szScript, bool drag_finished)
1375 {
1376  // construct object list
1377  int32_t iObjCnt = 0; int32_t *pObjIDs = nullptr;
1378  if (pObjs && (iObjCnt = pObjs->ObjectCount()))
1379  {
1380  pObjIDs = new int32_t [iObjCnt];
1381  // fill
1382  int32_t i = 0;
1383  for (const C4Value &vobj : *pObjs)
1384  {
1385  C4Object *obj = vobj.getObj();
1386  if (!obj) continue;
1387  if (obj && obj->Status)
1388  pObjIDs[i++] = obj->Number;
1389  else
1390  pObjIDs[i++] = 0;
1391  }
1392  }
1393 
1394  // execute control
1395  EMControl(CID_EMMoveObj, new C4ControlEMMoveObject(eAction, tx, ty, pTargetObj, iObjCnt, pObjIDs, szScript, drag_finished));
1396 
1397 }
1398 
1400 {
1401  ::Control.DoInput(eCtrlType, pCtrl, CDT_Decide);
1402 }
1403 
1404 
1406  if(!itemsObjselect.size()) return;
1407  std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin();
1408  while(it != itemsObjselect.end()) {
1409  #if defined(USE_WIN32_WINDOWS)
1410  if(!it->ItemId) { ++it; continue; }
1411  HMENU hContext = GetSubMenu(hMenu,0);
1412  DeleteMenu(hContext, it->ItemId, MF_BYCOMMAND);
1413  #endif
1414  ++it;
1415  }
1416  itemsObjselect.resize(0);
1417 }
1418 
1420 {
1421  // alt only has an effect in draw mode (picker)
1422  if (Mode == C4CNS_ModeDraw)
1423  {
1425  }
1426  // key not processed - allow further usages of Alt
1427  return false;
1428 }
1429 
1431 {
1432  if (Mode == C4CNS_ModeDraw)
1433  {
1435  }
1436  // key not processed - allow further usages of Alt
1437  return false;
1438 }
1439 
1441 {
1442  if (clear) ClearSelection(obj);
1443  AddToSelection(obj);
1445 }
1446 
1447 void C4EditCursor::DoContextObjCommand(C4Object * obj, const char *cmd)
1448 {
1449  // Command going through queue for sync
1450  if (!obj || !cmd) return;
1451  In(FormatString("Object(%d)->%s", obj->Number, cmd).getData());
1452 }
1453 
1454 bool C4EditCursor::GetCurrentSelectionPosition(int32_t *x, int32_t *y)
1455 {
1456  C4Object *obj = selection.GetObject();
1457  if (!obj || !obj->Status) return false;
1458  *x = obj->GetX();
1459  *y = obj->GetY();
1460  return true;
1461 }
1462 
1464 {
1465  highlighted_object = C4VObj(new_highlight);
1466 }
1467 
1469 {
1470  float trf_marker_x, trf_marker_y;
1471  if (HasTransformMarker(&trf_marker_x, &trf_marker_y, Zoom))
1472  {
1473  C4Object *obj = selection.GetObject();
1474  float dx = (float(X - obj->GetX()) - trf_marker_x) * Zoom;
1475  float dy = (float(Y - obj->GetY()) - trf_marker_y) * Zoom;
1477  return true;
1478  }
1479  return false;
1480 }
C4AulExec AulExec
Definition: C4AulExec.cpp:29
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
bool SetMenuItemText(HMENU hMenu, WORD id, const char *szText)
const int32_t FullCon
Definition: C4Constants.h:181
const uint32_t OCF_NotContained
Definition: C4Constants.h:96
const int NO_OWNER
Definition: C4Constants.h:137
C4ControlEMObjectAction
Definition: C4Control.h:441
@ EMMO_MoveForced
Definition: C4Control.h:443
@ EMMO_Script
Definition: C4Control.h:446
@ EMMO_Duplicate
Definition: C4Control.h:445
@ EMMO_Move
Definition: C4Control.h:442
@ EMMO_Enter
Definition: C4Control.h:444
@ EMMO_Exit
Definition: C4Control.h:448
@ EMMO_Remove
Definition: C4Control.h:447
@ EMMO_Transform
Definition: C4Control.h:450
@ EMDT_Fill
Definition: C4Control.h:479
@ EMDT_Brush
Definition: C4Control.h:478
@ EMDT_Rect
Definition: C4Control.h:481
@ EMDT_Line
Definition: C4Control.h:480
C4Draw * pDraw
Definition: C4Draw.cpp:42
C4GameControl Control
@ CDT_Decide
Definition: C4GameControl.h:39
#define PSF_EditCursorDeselection
Definition: C4GameScript.h:97
#define PSF_SaveScenarioObjects
Definition: C4GameScript.h:101
#define PSF_EditCursorSelection
Definition: C4GameScript.h:96
C4Game Game
Definition: C4Globals.cpp:52
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4Console Console
Definition: C4Globals.cpp:45
C4Application Application
Definition: C4Globals.cpp:44
C4GameObjects Objects
Definition: C4Globals.cpp:48
C4MouseControl MouseControl
Definition: C4Globals.cpp:47
C4GraphicsResource GraphicsResource
unsigned long C4KeyCode
C4Landscape Landscape
int32_t GBackMat(int32_t x, int32_t y)
Definition: C4Landscape.h:219
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
C4MaterialMap MaterialMap
Definition: C4Material.cpp:974
bool MatValid(int32_t mat)
Definition: C4Material.h:210
const int32_t C4MC_Cursor_DropInto
C4PacketType
Definition: C4PacketBase.h:77
@ CID_EMMoveObj
Definition: C4PacketBase.h:169
@ CID_EMDrawTool
Definition: C4PacketBase.h:170
C4Fixed itofix(int32_t x)
Definition: C4Real.h:261
C4Fixed ftofix(float x)
Definition: C4Real.h:258
const C4Real Fix0
Definition: C4Real.h:312
float bottom
Definition: C4Rect.h:25
float top
Definition: C4Rect.h:25
float right
Definition: C4Rect.h:25
float left
Definition: C4Rect.h:25
@ P_EditCursorCommands
#define C4GFXBLIT_ADDITIVE
Definition: C4Surface.h:26
#define C4GFXBLIT_CLRSFC_MOD2
Definition: C4Surface.h:29
C4TextureMap TextureMap
Definition: C4Texture.cpp:576
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_Brush
Definition: C4ToolsDlg.h:28
const int32_t C4TLS_Picker
Definition: C4ToolsDlg.h:32
#define C4TLS_MatSky
Definition: C4ToolsDlg.h:39
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:88
const C4Value C4VNull
Definition: C4Value.cpp:30
C4Value C4VArray(C4ValueArray *pArray)
Definition: C4Value.h:246
@ C4V_Nil
Definition: C4Value.h:25
C4Value C4VInt(int32_t i)
Definition: C4Value.h:239
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:242
StdBuf GetWideCharBuf(const char *utf8)
uint8_t BYTE
uint16_t WORD
uint32_t DWORD
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:60
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors=false, C4AulScriptContext *context=nullptr, bool parse_function=false)
Definition: C4AulExec.cpp:1012
const char * GetName() const
Definition: C4AulFunc.h:56
int32_t CreateUserFile()
Definition: C4Aul.cpp:228
C4PropListStatic * GetPropList()
Definition: C4Aul.h:151
C4AulUserFile * GetUserFile(int32_t handle)
Definition: C4Aul.cpp:252
void CloseUserFile(int32_t handle)
Definition: C4Aul.cpp:241
size_t GetFileLength()
Definition: C4Aul.h:100
StdStrBuf GrabFileContents()
Definition: C4Aul.h:99
bool PropertyDlgOpen()
Definition: C4Console.cpp:685
bool UpdateModeCtrls(int iMode)
Definition: C4Console.cpp:699
void ToolsDlgClose()
Definition: C4Console.cpp:666
void PropertyDlgUpdate(class C4EditCursorSelection &rSelection, bool force_function_update)
Definition: C4Console.cpp:687
void PropertyDlgClose()
Definition: C4Console.cpp:686
void DisplayInfoText(InfoTextType type, StdStrBuf &text)
Definition: C4Console.cpp:679
bool Message(const char *szMessage, bool fQuery=false)
Definition: C4Console.cpp:292
@ MRU_Object
Definition: C4Console.h:99
C4ObjectListDlg ObjectListDlg
Definition: C4Console.h:89
void RegisterRecentInput(const char *input, RecentScriptInputLists section)
Definition: C4Console.cpp:639
C4EditCursor EditCursor
Definition: C4Console.h:90
C4ToolsDlg ToolsDlg
Definition: C4Console.h:88
static C4ControlEMMoveObject * CreateObject(const C4ID &id, C4Real x, C4Real y, C4Object *container)
Definition: C4Control.cpp:1214
C4DefGraphics Graphics
Definition: C4Def.h:191
C4ID id
Definition: C4Def.h:101
int32_t Rotateable
Definition: C4Def.h:119
int32_t Oversize
Definition: C4Def.h:129
int32_t GrowthType
Definition: C4Def.h:111
C4Shape Shape
Definition: C4Def.h:104
void DrawCircleDw(C4Surface *sfcTarget, float cx, float cy, float r, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:618
void DrawFrameDw(C4Surface *sfcDest, int x1, int y1, int x2, int y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:635
void SetBlitMode(DWORD dwBlitMode)
Definition: C4Draw.h:191
virtual void PerformMultiLines(C4Surface *sfcTarget, const C4BltVertex *vertices, unsigned int n_vertices, float width, C4ShaderCall *shader_call)=0
void DrawLineDw(C4Surface *sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:608
void ResetBlitMode()
Definition: C4Draw.h:192
bool EditingOK(bool for_landscape_drawing=false)
int32_t Mode
Definition: C4EditCursor.h:54
bool LeftButtonDown(DWORD dwKeyState)
int32_t GetMode()
bool SetMode(int32_t iMode)
bool KeyDown(C4KeyCode KeyCode, DWORD dwKeyState)
void UpdateDropTarget(DWORD dwKeyState)
void DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight, bool draw_transform_marker)
bool selection_invalid
Definition: C4EditCursor.h:53
void PerformDuplicationLegacy(int32_t *pObjects, int32_t iObjectNum, bool fLocalCall)
bool HasTransformMarker(float *x, float *y, float zoom) const
void Draw(C4TargetFacet &cgo)
void AddToSelection(C4PropList *add_obj)
bool OpenPropTools()
int32_t DragCon0
Definition: C4EditCursor.h:57
bool DragTransform
Definition: C4EditCursor.h:56
bool In(const char *szText)
void PerformDuplication(int32_t *object_numbers, int32_t object_count, bool local_call)
void MoveSelection(C4Real iXOff, C4Real iYOff, bool drag_finished)
bool IsHoveringTransformMarker() const
bool RemoveFromSelection(C4PropList *remove_obj)
void ApplyToolFill()
void EMControl(enum C4PacketType eCtrlType, class C4ControlPacket *pCtrl)
C4Value highlighted_object
Definition: C4EditCursor.h:59
bool GetCurrentSelectionPosition(int32_t *x, int32_t *y)
std::unique_ptr< C4GraphicsOverlay > creator_overlay
Definition: C4EditCursor.h:61
void SetHighlightedObject(C4Object *new_highlight)
void ApplyCreateObject(bool contained)
std::vector< ObjselItemDt > itemsObjselect
Definition: C4EditCursor.h:70
C4Object * Target
Definition: C4EditCursor.h:58
C4Object * DropTarget
Definition: C4EditCursor.h:58
int32_t DragRot0
Definition: C4EditCursor.h:57
void DrawSelectMark(C4Facet &cgo, FLOAT_RECT r, float width, uint32_t color=0xffffffff)
bool RightButtonDown(DWORD dwKeyState)
bool RightButtonUp(DWORD dwKeyState)
bool LeftButtonUp(DWORD dwKeyState)
bool fShiftWasDown
Definition: C4EditCursor.h:51
class C4Def * creator_def
Definition: C4EditCursor.h:60
C4Object * GetTarget()
void DoContextObjsel(C4Object *, bool clear)
void ClearPointers(C4Object *pObj)
void AppendMenuItem(int num, const StdStrBuf &label)
void SetHold(bool fToState)
Definition: C4EditCursor.h:105
void FrameSelection()
void EMMoveObject(enum C4ControlEMObjectAction eAction, C4Real tx, C4Real ty, C4Object *pTargetObj, const C4EditCursorSelection *pObjs=nullptr, const char *szScript=nullptr, bool drag_finished=false)
int32_t DragRotLast
Definition: C4EditCursor.h:57
void UpdateStatusBar()
int32_t DragConLast
Definition: C4EditCursor.h:57
void ObjselectDelItems()
C4EditCursorSelection selection
Definition: C4EditCursor.h:75
bool Move(float iX, float iY, float zoom, DWORD dwKeyState)
bool fAltWasDown
Definition: C4EditCursor.h:50
void OnSelectionChanged(bool by_objectlist=false)
bool DoContextMenu(DWORD dwKeyState)
void ClearSelection(C4PropList *next_selection=nullptr)
void ApplyToolBrush()
bool has_mouse_hover
Definition: C4EditCursor.h:52
bool IsSelectionInvalidated() const
Definition: C4EditCursor.h:154
bool KeyUp(C4KeyCode KeyCode, DWORD dwKeyState)
void DoContextObjCommand(C4Object *, const char *cmd)
void ApplyToolLine()
void ApplyToolRect()
void ApplyToolPicker()
int32_t ObjectCount() const
bool IsContained(C4PropList *obj) const
bool ClearPointers(C4Object *obj)
C4Object * GetLastObject() const
StdStrBuf GetDataString() const
C4Object * GetObject(int32_t index=0) const
C4Surface * Surface
Definition: C4Facet.h:117
float Hgt
Definition: C4Facet.h:118
float Wdt
Definition: C4Facet.h:118
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
void DrawX(C4Surface *sfcTarget, float iX, float iY, float iWdt, float iHgt, int32_t iPhaseX=0, int32_t iPhaseY=0) const
Definition: C4Facet.cpp:358
float Y
Definition: C4Facet.h:118
float X
Definition: C4Facet.h:118
C4ValueArray * FindMany(const C4ObjectList &Objs)
Definition: C4Real.h:59
bool isCtrlHost() const
Definition: C4GameControl.h:99
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
C4Object * FindObject(C4Def *def, int32_t x=0, int32_t y=0, int32_t wdt=0, int32_t hgt=0, DWORD ocf=OCF_All, C4Object *find_next=nullptr)
Definition: C4Game.cpp:1456
int32_t iTick35
Definition: C4Game.h:130
int32_t HaltCount
Definition: C4Game.h:112
C4Object * CreateObject(C4PropList *type, C4Object *creator, int32_t owner=NO_OWNER, int32_t x=50, int32_t y=50, int32_t r=0, bool grow_from_center=false, C4Real xdir=Fix0, C4Real ydir=Fix0, C4Real rdir=Fix0, int32_t controller=NO_OWNER)
Definition: C4Game.cpp:1334
C4LSectors Sectors
Definition: C4GameObjects.h:42
C4Object * SafeObjectPointer(int32_t object_number)
BYTE GetMapIndex(int32_t iX, int32_t iY) const
LandscapeMode GetMode() const
BYTE GetBackPix(int32_t x, int32_t y) const
int32_t GetBackMat(int32_t x, int32_t y) const
int32_t GetMat(int32_t x, int32_t y) const
int32_t GetMapZoom() const
BYTE GetBackMapIndex(int32_t iX, int32_t iY) const
char Name[C4M_MaxName+1]
Definition: C4Material.h:89
C4Material * Map
Definition: C4Material.h:169
const char * GetCaption()
bool GetDrawPosition(const C4TargetFacet &cgo, float &resultx, float &resulty, float &resultzoom) const
void DrawTopFace(C4TargetFacet &cgo, int32_t iByPlayer=-1, DrawMode eDrawMode=ODM_Normal, float offX=0, float offY=0)
int32_t Owner
Definition: C4Object.h:108
int32_t AddObjectAndContentsToArray(C4ValueArray *target_array, int32_t index=0)
StdStrBuf GetDataString()
Definition: C4Object.cpp:729
int32_t GetX() const
Definition: C4Object.h:285
int32_t GetY() const
Definition: C4Object.h:286
StdMeshInstance * pMeshInstance
Definition: C4Object.h:154
C4NotifyingObjectList Contents
Definition: C4Object.h:151
int32_t GetR() const
Definition: C4Object.h:287
int32_t GetCon() const
Definition: C4Object.h:271
void Draw(C4TargetFacet &cgo, int32_t iByPlayer=-1, DrawMode eDrawMode=ODM_Normal, float offX=0, float offY=0)
uint32_t BlitMode
Definition: C4Object.h:161
uint32_t ColorMod
Definition: C4Object.h:160
C4Def * Def
Definition: C4Object.h:141
C4Shape Shape
Definition: C4Object.h:146
void Update(class C4EditCursorSelection &rSelection)
int ObjectCount(C4ID id=C4ID::None) const
virtual C4Object * GetObject()
Definition: C4PropList.cpp:636
virtual const char * GetName() const
Definition: C4PropList.cpp:618
int32_t Status
Definition: C4PropList.h:173
C4ValueArray * GetPropertyArray(C4PropertyName n) const
Definition: C4PropList.cpp:758
C4PropList * GetPrototype() const
Definition: C4PropList.h:85
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:114
static int32_t GetEnumerationIndex()
Definition: C4PropList.h:230
const char * GetName() const override
Definition: C4PropList.cpp:243
int32_t y
Definition: C4Rect.h:30
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
int32_t x
Definition: C4Rect.h:30
T * Get() const
StdStrBuf GetData() const
Definition: C4StringTable.h:50
float TargetY
Definition: C4Facet.h:165
float TargetX
Definition: C4Facet.h:165
float Zoom
Definition: C4Facet.h:165
void Set(const C4Facet &cpy)
Definition: C4Facet.h:182
Definition: C4Texture.h:49
const char * GetTextureName() const
Definition: C4Texture.h:61
const char * GetMaterialName() const
Definition: C4Texture.h:60
bool isNull() const
Definition: C4Texture.h:59
const C4TexMapEntry * GetEntry(int32_t iIndex) const
Definition: C4Texture.h:85
bool ModeBack
Definition: C4ToolsDlg.h:58
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
int32_t Grade
Definition: C4ToolsDlg.h:54
bool SetIFT(bool fIFT)
Definition: C4ToolsDlg.cpp:131
bool SelectBackMaterial(const char *szMaterial, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:277
int32_t Tool
Definition: C4ToolsDlg.h:53
bool SelectBackTexture(const char *szTexture, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:270
char BackTexture[C4M_MaxName+1]
Definition: C4ToolsDlg.h:60
void ResetAlternateTool()
Definition: C4ToolsDlg.cpp:290
char Texture[C4M_MaxName+1]
Definition: C4ToolsDlg.h:57
char BackMaterial[C4M_MaxName+1]
Definition: C4ToolsDlg.h:59
bool IsGradedTool() const
Definition: C4ToolsDlg.h:81
bool Open()
Definition: C4ToolsDlg.cpp:28
void SetAlternateTool()
Definition: C4ToolsDlg.cpp:284
char Material[C4M_MaxName+1]
Definition: C4ToolsDlg.h:56
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
int32_t GetSize() const
Definition: C4ValueArray.h:36
C4Object * getObj() const
Definition: C4Value.cpp:68
C4String * getStr() const
Definition: C4Value.h:117
C4V_Type GetType() const
Definition: C4Value.h:161
C4AulFunc * getFunction() const
Definition: C4Value.h:119
Definition: StdBuf.h:30
void SetFaceOrderingForClrModulation(uint32_t clrmod)
Definition: StdMesh.cpp:1135
void SetFaceOrdering(FaceOrdering ordering)
Definition: StdMesh.cpp:1118
void CopyUntil(const char *szString, char cUntil)
Definition: StdBuf.h:613
const char * getData() const
Definition: StdBuf.h:442
void Copy()
Definition: StdBuf.h:467
size_t getLength() const
Definition: StdBuf.h:445
void Take(char *pnData)
Definition: StdBuf.h:457
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
#define IDM_VPORTDYN_FIRST
Definition: resource.h:94
#define IDM_VIEWPORT_DELETE
Definition: resource.h:91
#define IDM_VIEWPORT_CONTENTS
Definition: resource.h:90
#define IDM_VIEWPORT_DUPLICATE
Definition: resource.h:92
#define IDR_CONTEXTMENUS
Definition: resource.h:97