1 // Copyright (C) 2002-2012 Nikolaus Gebhardt / Gaz Davidson
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 // Thanks to Midnight for all his testing, bug fixes and patches :)
6 
7 #include "CGUIEditWorkspace.h"
8 #include "IGUIEnvironment.h"
9 #include "IVideoDriver.h"
10 #include "IOSOperator.h"
11 #include "IReadFile.h"
12 #include "IFileSystem.h"
13 #include "IXMLWriter.h"
14 #include "IGUISkin.h"
15 #include "IGUIElementFactory.h"
16 #include "CGUIEditWindow.h"
17 #include "IGUIContextMenu.h"
18 #include "IGUIFileOpenDialog.h"
19 #include "IGUITreeView.h"
20 #include "CGUIAttribute.h"
21 #include "CMemoryReadWriteFile.h"
22 
23 namespace irr
24 {
25 namespace gui
26 {
27 
28 //! constructor
CGUIEditWorkspace(IGUIEnvironment * environment,s32 id,IGUIElement * parent)29 CGUIEditWorkspace::CGUIEditWorkspace(IGUIEnvironment* environment, s32 id, IGUIElement *parent)
30 : IGUIElement(EGUIET_ELEMENT, environment, parent ? parent : environment->getRootGUIElement(), id, environment->getRootGUIElement()->getAbsolutePosition()),
31 	CurrentMode(EGUIEDM_SELECT), MouseOverMode(EGUIEDM_SELECT),
32 	GridSize(10,10), MenuCommandStart(0x3D17), DrawGrid(false), UseGrid(true),
33 	MouseOverElement(0), SelectedElement(0), EditorWindow(0)
34 {
35 	#ifdef _DEBUG
36 	setDebugName("CGUIEditWorkspace");
37 	#endif
38 
39 	// this element is never saved.
40 	setSubElement(true);
41 
42 	// it resizes to fit a resizing window
43 	setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
44 
45 	EditorWindow = (CGUIEditWindow*) Environment->addGUIElement("GUIEditWindow", this);
46 	if (EditorWindow)
47 	{
48 		EditorWindow->grab();
49 		EditorWindow->setSubElement(true);
50 
51 		Environment->setFocus(EditorWindow);
52 		serializeAttributes(EditorWindow->getOptionEditor()->getAttribs());
53 		EditorWindow->getOptionEditor()->refreshAttribs();
54 
55 		if (EditorWindow->getEnvironmentEditor())
56 		{
57 			Environment->serializeAttributes(EditorWindow->getEnvironmentEditor()->getAttribs());
58 			EditorWindow->getEnvironmentEditor()->refreshAttribs();
59 		}
60 	}
61 }
62 
63 
64 //! destructor
~CGUIEditWorkspace()65 CGUIEditWorkspace::~CGUIEditWorkspace()
66 {
67 	if (EditorWindow)
68 		EditorWindow->drop();
69 }
70 
71 
setMenuCommandIDStart(s32 id)72 void CGUIEditWorkspace::setMenuCommandIDStart(s32 id)
73 {
74 	MenuCommandStart = id;
75 }
76 
getModeFromPos(core::position2di p)77 CGUIEditWorkspace::EGUIEDIT_MODE CGUIEditWorkspace::getModeFromPos(core::position2di p)
78 {
79 	if (SelectedElement)
80 	{
81 		core::rect<s32>		r = SelectedElement->getAbsolutePosition();
82 
83 		if		(TLRect.isPointInside(p))
84 			return EGUIEDM_RESIZE_TL;
85 
86 		else if (TRRect.isPointInside(p))
87 			return EGUIEDM_RESIZE_TR;
88 
89 		else if (BLRect.isPointInside(p) )
90 			return EGUIEDM_RESIZE_BL;
91 
92 		else if (BRRect.isPointInside(p))
93 			return EGUIEDM_RESIZE_BR;
94 
95 		else if (TopRect.isPointInside(p))
96 			return EGUIEDM_RESIZE_T;
97 
98 		else if (BRect.isPointInside(p))
99 			return EGUIEDM_RESIZE_B;
100 
101 		else if (LRect.isPointInside(p))
102 			return EGUIEDM_RESIZE_L;
103 
104 		else if (RRect.isPointInside(p))
105 			return EGUIEDM_RESIZE_R;
106 
107 		else if (getEditableElementFromPoint(SelectedElement, p) == SelectedElement)
108 			return EGUIEDM_MOVE;
109 
110 		else
111 			return EGUIEDM_SELECT;
112 	}
113 
114 	return EGUIEDM_SELECT;
115 
116 }
117 
getEditableElementFromPoint(IGUIElement * start,const core::position2di & point,s32 index)118 IGUIElement* CGUIEditWorkspace::getEditableElementFromPoint(IGUIElement *start, const core::position2di &point, s32 index )
119 {
120 	IGUIElement* target = 0;
121 
122 	// we have to search from back to front.
123 
124 	core::list<IGUIElement*>::ConstIterator it = start->getChildren().getLast();
125 	s32 count=0;
126 	while(it != start->getChildren().end())
127 	{
128 		target = getEditableElementFromPoint((*it),point);
129 		if (target)
130 		{
131 			if (!target->isSubElement() && !isMyChild(target) && target != this)
132 			{
133 				if (index == count)
134 					return target;
135 				else
136 					count++;
137 			}
138 			else
139 				target = 0;
140 		}
141 		--it;
142 	}
143 
144 	if (start->getAbsolutePosition().isPointInside(point))
145 		target = start;
146 
147 	return target;
148 }
149 
setSelectedElement(IGUIElement * sel)150 void CGUIEditWorkspace::setSelectedElement(IGUIElement *sel)
151 {
152 	IGUIElement* focus = Environment->getFocus();
153 	// we only give focus back to children
154 	if (!isMyChild(focus))
155 		focus = 0;
156 
157 	if (SelectedElement != Parent)
158 	{
159 		if (SelectedElement != sel && EditorWindow)
160 		{
161 			EditorWindow->setSelectedElement(sel);
162 			SelectedElement = sel;
163 		}
164 	}
165 	else
166 		SelectedElement = 0;
167 
168 	if (focus)
169 		Environment->setFocus(focus);
170 	else
171 		Environment->setFocus(this);
172 }
173 
getSelectedElement()174 IGUIElement* CGUIEditWorkspace::getSelectedElement()
175 {
176 	return SelectedElement;
177 }
selectNextSibling()178 void CGUIEditWorkspace::selectNextSibling()
179 {
180 	IGUIElement* p=0;
181 
182 	if (SelectedElement && SelectedElement->getParent())
183 		p = SelectedElement->getParent();
184 	else
185 		p = Parent;
186 
187 	core::list<IGUIElement*>::ConstIterator it = p->getChildren().begin();
188 	// find selected element
189 	if (SelectedElement)
190 		while (*it != SelectedElement)
191 			++it;
192 	if (it !=p->getChildren().end())
193 		++it;
194 	// find next non sub-element
195 	while (it != p->getChildren().end() && (*it)->isSubElement())
196 		++it;
197 
198 	if (it != p->getChildren().end())
199 		setSelectedElement(*it);
200 }
selectPreviousSibling()201 void CGUIEditWorkspace::selectPreviousSibling()
202 {
203 	IGUIElement* p=0;
204 
205 	if (SelectedElement && SelectedElement->getParent())
206 		p = SelectedElement->getParent();
207 	else
208 		p = Parent;
209 
210 	core::list<IGUIElement*>::ConstIterator it = p->getChildren().getLast();
211 	// find selected element
212 	if (SelectedElement)
213 		while (*it != SelectedElement)
214 			--it;
215 	if (it != p->getChildren().end())
216 		--it;
217 	// find next non sub-element
218 	while (it != p->getChildren().end() && (*it)->isSubElement())
219 		--it;
220 
221 	if (it != p->getChildren().end())
222 		setSelectedElement(*it);
223 }
224 
225 //! called if an event happened.
OnEvent(const SEvent & e)226 bool CGUIEditWorkspace::OnEvent(const SEvent &e)
227 {
228 	IGUIFileOpenDialog* dialog=0;
229 	switch(e.EventType)
230 	{
231 	case ATTRIBEDIT_ATTRIB_CHANGED:
232 		{
233 			switch (e.UserEvent.UserData1)
234 			{
235 			case EGUIEDCE_ATTRIB_EDITOR:
236 				{
237 					// update selected items attributes
238 					if (SelectedElement)
239 					{
240 						SelectedElement->deserializeAttributes(EditorWindow->getAttributeEditor()->getAttribs());
241 						EditorWindow->updateTree();
242 					}
243 					return true;
244 				}
245 			case EGUIEDCE_OPTION_EDITOR:
246 				{
247 					// update editor options
248 					deserializeAttributes(EditorWindow->getOptionEditor()->getAttribs());
249 					return true;
250 				}
251 			case EGUIEDCE_ENV_EDITOR:
252 				{
253 					// update environment
254 					Environment->deserializeAttributes(EditorWindow->getEnvironmentEditor()->getAttribs());
255 					return true;
256 				}
257 			}
258 		}
259 		break;
260 
261 	case EET_KEY_INPUT_EVENT:
262 		if (!e.KeyInput.PressedDown)
263 		{
264 			switch (e.KeyInput.Key)
265 			{
266 			case KEY_DELETE:
267 				if (SelectedElement)
268 				{
269 					IGUIElement* el = SelectedElement;
270 					setSelectedElement(0);
271 					MouseOverElement = 0;
272 					el->remove();
273 					EditorWindow->updateTree();
274 				}
275 				break;
276 			case KEY_KEY_X:
277 				if (e.KeyInput.Control && SelectedElement)
278 				{
279 					// cut
280 					CopySelectedElementXML();
281 					// delete element
282 					IGUIElement *el = SelectedElement;
283 					setSelectedElement(0);
284 					MouseOverElement = 0;
285 					el->remove();
286 				}
287 				break;
288 			case KEY_KEY_C:
289 				// copy
290 				if (e.KeyInput.Control && SelectedElement)
291 				{
292 					CopySelectedElementXML();
293 				}
294 				break;
295 			case KEY_KEY_V:
296 				// paste
297 				if (e.KeyInput.Control)
298 				{
299 					PasteXMLToSelectedElement();
300 				}
301 				break;
302 			default:
303 				break;
304 			}
305 
306 			return true;
307 		}
308 		break;
309 
310 	case EET_MOUSE_INPUT_EVENT:
311 
312 		switch(e.MouseInput.Event)
313 		{
314 		case EMIE_MOUSE_WHEEL:
315 			{
316 				f32 wheel = e.MouseInput.Wheel;
317 
318 				if (wheel > 0)
319 					selectPreviousSibling();
320 				else
321 					selectNextSibling();
322 			}
323 			break;
324 		case EMIE_LMOUSE_PRESSED_DOWN:
325 		{
326 			core::position2di p = core::position2di(e.MouseInput.X,e.MouseInput.Y);
327 
328 			IGUIElement* newSelection = getElementFromPoint(p);
329 
330 			if (newSelection != this && isMyChild(newSelection) ) // redirect event
331 			{
332 				Environment->setFocus(newSelection);
333 				return true;
334 			}
335 
336 			// hide the gui editor
337 			if (EditorWindow)
338 				EditorWindow->setVisible(false);
339 
340 			if (CurrentMode == EGUIEDM_SELECT)
341 			{
342 				if (SelectedElement)
343 				{
344 					// start moving or dragging
345 					CurrentMode = getModeFromPos(p);
346 
347 					if (CurrentMode == EGUIEDM_MOVE)
348 						StartMovePos = SelectedElement->getAbsolutePosition().UpperLeftCorner;
349 
350 					DragStart = p;
351 					SelectedArea = SelectedElement->getAbsolutePosition();
352 				}
353 
354 				if (CurrentMode < EGUIEDM_MOVE)
355 				{
356 					// selecting an element...
357 					MouseOverElement = getEditableElementFromPoint(Parent, p);
358 
359 					if (MouseOverElement == Parent)
360 						MouseOverElement = 0;
361 
362 					setSelectedElement(MouseOverElement);
363 				}
364 			}
365 
366 			break;
367 		}
368 		case EMIE_RMOUSE_PRESSED_DOWN:
369 			if (CurrentMode == EGUIEDM_SELECT_NEW_PARENT || CurrentMode >= EGUIEDM_MOVE)
370 			{
371 				// cancel dragging
372 				CurrentMode = EGUIEDM_SELECT;
373 			}
374 			else
375 			{
376 				DragStart = core::position2di(e.MouseInput.X,e.MouseInput.Y);
377 				// root menu
378 				IGUIContextMenu* mnu = Environment->addContextMenu(
379 					core::rect<s32>(e.MouseInput.X, e.MouseInput.Y, e.MouseInput.Y+100, e.MouseInput.Y+100),this);
380 				mnu->addItem(L"File",-1,true,true);
381 				mnu->addItem(L"Edit",-1,true,true);
382 				mnu->addItem(L"View",-1,true,true);
383 				mnu->addItem(SelectedElement ? L"Add child" : L"Add" ,-1,true,true);
384 
385 				// file menu
386 				IGUIContextMenu* sub = mnu->getSubMenu(0);
387 				IGUIContextMenu* sub2 =0;
388 
389 				sub->addItem(L"New",	MenuCommandStart + EGUIEDMC_FILE_NEW );
390 				sub->addItem(L"Load...",MenuCommandStart + EGUIEDMC_FILE_LOAD);
391 				sub->addItem(L"Save...",MenuCommandStart + EGUIEDMC_FILE_SAVE);
392 
393 				// edit menu
394 				sub = mnu->getSubMenu(1);
395 				sub->addItem(L"Cut (ctrl+x)", MenuCommandStart + EGUIEDMC_CUT_ELEMENT,	(SelectedElement != 0));
396 				sub->addItem(L"Copy (ctrl+c)", MenuCommandStart + EGUIEDMC_COPY_ELEMENT,	(SelectedElement != 0));
397 				sub->addItem(L"Paste (ctrl+v)", MenuCommandStart + EGUIEDMC_PASTE_ELEMENT,
398 					(core::stringc(Environment->getOSOperator()->getTextFromClipboard()) != ""));
399 				sub->addItem(L"Delete (del)", MenuCommandStart + EGUIEDMC_DELETE_ELEMENT, (SelectedElement != 0));
400 				sub->addSeparator();
401 				sub->addItem(L"Set parent",		MenuCommandStart + EGUIEDMC_SET_PARENT,		(SelectedElement != 0));
402 				sub->addItem(L"Bring to front", MenuCommandStart + EGUIEDMC_BRING_TO_FRONT, (SelectedElement != 0));
403 				sub->addSeparator();
404 				sub->addItem(L"Save to XML...", MenuCommandStart + EGUIEDMC_SAVE_ELEMENT,	(SelectedElement != 0));
405 
406 				sub = mnu->getSubMenu(2);
407 				// view menu
408 				if (EditorWindow)
409 					sub->addItem(EditorWindow->isVisible() ? L"Hide window" : L"Show window", MenuCommandStart + EGUIEDMC_TOGGLE_EDITOR);
410 
411 				sub = mnu->getSubMenu(3);
412 
413 				s32 i,j,c=0;
414 				sub->addItem(L"Default factory",-1,true, true);
415 
416 				// add elements from each factory
417 				for (i=0; u32(i) < Environment->getRegisteredGUIElementFactoryCount(); ++i)
418 				{
419 					sub2 = sub->getSubMenu(i);
420 
421 					IGUIElementFactory *f = Environment->getGUIElementFactory(i);
422 
423 					for (j=0; j< f->getCreatableGUIElementTypeCount(); ++j)
424 					{
425 						sub2->addItem(core::stringw(f->getCreateableGUIElementTypeName(j)).c_str(), MenuCommandStart + EGUIEDMC_COUNT + c);
426 						c++;
427 					}
428 
429 					if (u32(i+1) < Environment->getRegisteredGUIElementFactoryCount())
430 					{
431 						core::stringw strFact;
432 						strFact = L"Factory ";
433 						strFact += i+1;
434 						sub->addItem(strFact.c_str(),-1, true, true);
435 					}
436 				}
437 				sub->addSeparator();
438 				sub->addItem(L"From XML...", MenuCommandStart + EGUIEDMC_INSERT_XML);
439 
440 				// set focus to menu
441 				Environment->setFocus(mnu);
442 
443 			}
444 			break;
445 		case EMIE_LMOUSE_LEFT_UP:
446 
447 			// make window visible again
448 			if (EditorWindow)
449 				EditorWindow->setVisible(true);
450 			if (CurrentMode == EGUIEDM_SELECT_NEW_PARENT)
451 			{
452 				if (SelectedElement)
453 				{
454 					MouseOverElement = getEditableElementFromPoint(Parent,
455 						core::position2di(e.MouseInput.X,e.MouseInput.Y));
456 					if (MouseOverElement)
457 					{
458 						MouseOverElement->addChild(SelectedElement);
459 						setSelectedElement(0);
460 						setSelectedElement(SelectedElement);
461 					}
462 				}
463 				CurrentMode = EGUIEDM_SELECT;
464 			}
465 			else if (CurrentMode >= EGUIEDM_MOVE)
466 			{
467 				IGUIElement *sel = SelectedElement;
468 				// unselect
469 				setSelectedElement(0);
470 
471 				// move
472 				core::position2d<s32> p(0,0);
473 				if (sel->getParent())
474 					p = sel->getParent()->getAbsolutePosition().UpperLeftCorner;
475 
476 				sel->setRelativePosition(SelectedArea - p);
477 
478 				// select
479 				setSelectedElement(sel);
480 
481 				// reset selection mode...
482 				CurrentMode = EGUIEDM_SELECT;
483 			}
484 			break;
485 		case EMIE_MOUSE_MOVED:
486 			// always on top
487 			Parent->bringToFront(this);
488 
489 			// if selecting
490 			if (CurrentMode == EGUIEDM_SELECT || CurrentMode == EGUIEDM_SELECT_NEW_PARENT)
491 			{
492 
493 				core::position2di p = core::position2di(e.MouseInput.X,e.MouseInput.Y);
494 
495 				// highlight the element that the mouse is over
496 				MouseOverElement = getEditableElementFromPoint(Parent, p);
497 				if (MouseOverElement == Parent)
498 				{
499 					MouseOverElement = 0;
500 				}
501 
502 				if (CurrentMode == EGUIEDM_SELECT)
503 				{
504 					MouseOverMode = getModeFromPos(p);
505 					if (MouseOverMode > EGUIEDM_MOVE)
506 					{
507 						MouseOverElement = SelectedElement;
508 					}
509 				}
510 			}
511 			else if (CurrentMode == EGUIEDM_MOVE)
512 			{
513 				// get difference
514 				core::position2di p = core::position2di(e.MouseInput.X,e.MouseInput.Y);
515 				p -= DragStart;
516 
517 				// apply to top corner
518 				p = StartMovePos + p;
519 				if (UseGrid)
520 				{
521 					p.X = (p.X/GridSize.Width)*GridSize.Width;
522 					p.Y = (p.Y/GridSize.Height)*GridSize.Height;
523 				}
524 
525 				SelectedArea += p - SelectedArea.UpperLeftCorner;
526 			}
527 			else if (CurrentMode > EGUIEDM_MOVE)
528 			{
529 				// get difference from start position
530 				core::position2di p = core::position2di(e.MouseInput.X,e.MouseInput.Y);
531 				if (UseGrid)
532 				{
533 					p.X = (p.X/GridSize.Width)*GridSize.Width;
534 					p.Y = (p.Y/GridSize.Height)*GridSize.Height;
535 				}
536 
537 				switch(CurrentMode)
538 				{
539 					case EGUIEDM_RESIZE_T:
540 						SelectedArea.UpperLeftCorner.Y = p.Y;
541 						break;
542 					case EGUIEDM_RESIZE_B:
543 						SelectedArea.LowerRightCorner.Y = p.Y;
544 						break;
545 					case EGUIEDM_RESIZE_L:
546 						SelectedArea.UpperLeftCorner.X = p.X;
547 						break;
548 					case EGUIEDM_RESIZE_R:
549 						SelectedArea.LowerRightCorner.X = p.X;
550 						break;
551 					case EGUIEDM_RESIZE_TL:
552 						SelectedArea.UpperLeftCorner = p;
553 						break;
554 					case EGUIEDM_RESIZE_TR:
555 						SelectedArea.UpperLeftCorner.Y = p.Y;
556 						SelectedArea.LowerRightCorner.X = p.X;
557 						break;
558 					case EGUIEDM_RESIZE_BL:
559 						SelectedArea.UpperLeftCorner.X = p.X;
560 						SelectedArea.LowerRightCorner.Y = p.Y;
561 						break;
562 					case EGUIEDM_RESIZE_BR:
563 						SelectedArea.LowerRightCorner = p;
564 						break;
565 					default:
566 						break;
567 				}
568 			}
569 
570 			break;
571 		default:
572 			break;
573 		}
574 		break;
575 
576 	case EET_GUI_EVENT:
577 		switch(e.GUIEvent.EventType)
578 		{
579         case EGET_TREEVIEW_NODE_SELECT:
580         {
581             IGUITreeViewNode* eventnode = ((IGUITreeView*)e.GUIEvent.Caller)->getLastEventNode();
582             if(!eventnode->isRoot())
583                 setSelectedElement((IGUIElement*)(eventnode->getData()));
584             break;
585         }
586 		// load a gui file
587 		case EGET_FILE_SELECTED:
588 			dialog = (IGUIFileOpenDialog*)e.GUIEvent.Caller;
589 			Environment->loadGUI(core::stringc(dialog->getFileName()).c_str());
590 			break;
591 
592 		case EGET_MENU_ITEM_SELECTED:
593 		{
594 			IGUIContextMenu *menu = (IGUIContextMenu*)e.GUIEvent.Caller;
595 			s32 cmdID = menu->getItemCommandId(menu->getSelectedItem()) - MenuCommandStart;
596 
597 			IGUIElement* el;
598 
599 			switch(cmdID)
600 			{
601 
602 				//! file commands
603 				case EGUIEDMC_FILE_NEW:
604 					// clear all elements belonging to our parent
605 					setSelectedElement(0);
606 					MouseOverElement = 0;
607 					el = Parent;
608 					grab();
609 					// remove all children
610 					while(Children.end() != el->getChildren().begin())
611 						el->removeChild(*(el->getChildren().begin()));
612 					// attach to parent again
613 					el->addChild(this);
614 					drop();
615 
616 					break;
617 				case EGUIEDMC_FILE_LOAD:
618 					Environment->addFileOpenDialog(L"Please select a GUI file to open", false, this);
619 					break;
620 				case EGUIEDMC_FILE_SAVE:
621 					Environment->saveGUI("guiTest.xml");
622 					break;
623 
624 				//! edit menu
625 				case EGUIEDMC_CUT_ELEMENT:
626 				{
627 					CopySelectedElementXML();
628 					// delete element
629 					el = SelectedElement;
630 					setSelectedElement(0);
631 					MouseOverElement = 0;
632 					el->remove();
633 					break;
634 				}
635 				case EGUIEDMC_COPY_ELEMENT:
636 					CopySelectedElementXML();
637 					break;
638 				case EGUIEDMC_PASTE_ELEMENT:
639 					PasteXMLToSelectedElement();
640 					break;
641 				case EGUIEDMC_DELETE_ELEMENT:
642 					el = SelectedElement;
643 					setSelectedElement(0);
644 					MouseOverElement = 0;
645 					el->remove();
646 					break;
647 				case EGUIEDMC_SET_PARENT:
648 					CurrentMode = EGUIEDM_SELECT_NEW_PARENT;
649 					break;
650 				case EGUIEDMC_BRING_TO_FRONT:
651 					if (SelectedElement->getParent())
652 						SelectedElement->getParent()->bringToFront(SelectedElement);
653 					break;
654 
655 				case EGUIEDMC_SAVE_ELEMENT:
656                     //TODO: add 'save' dialog.
657 					Environment->saveGUI("guiTest.xml", SelectedElement ? SelectedElement : Environment->getRootGUIElement() );
658 					break;
659 
660 				//! toggle edit window
661 				case EGUIEDMC_TOGGLE_EDITOR:
662 					break;
663 
664 				case EGUIEDMC_INSERT_XML:
665 					Environment->loadGUI("guiTest.xml", SelectedElement ? SelectedElement : Environment->getRootGUIElement() );
666 					break;
667 
668 				default:
669 					// create element from factory?
670 					if (cmdID >= EGUIEDMC_COUNT)
671 					{
672 
673 						s32 num = cmdID - EGUIEDMC_COUNT; // get index
674 						// loop through all factories
675 						s32 i, c=Environment->getRegisteredGUIElementFactoryCount();
676 						for (i=0; i<c && num > Environment->getGUIElementFactory(i)->getCreatableGUIElementTypeCount(); ++i)
677 						{
678 							num -= Environment->getGUIElementFactory(i)->getCreatableGUIElementTypeCount();
679 						}
680 						if (num < Environment->getGUIElementFactory(i)->getCreatableGUIElementTypeCount() )
681 						{
682 							core::stringc name = Environment->getGUIElementFactory(i)->getCreateableGUIElementTypeName(num);
683 							IGUIElement *parentElement = SelectedElement ? SelectedElement : Environment->getRootGUIElement();
684 							// add it
685 							IGUIElement *newElement = Environment->getGUIElementFactory(i)->addGUIElement(name.c_str(),parentElement);
686 							if (newElement)
687 							{
688 								core::position2di p = DragStart - parentElement->getAbsolutePosition().UpperLeftCorner;
689 								newElement->setRelativePosition(core::rect<s32>(p,p+core::position2di(100,100)));
690 								//Environment->removeFocus(newElement);
691 							}
692 						}
693 					}
694 					break;
695 				}
696 				EditorWindow->updateTree();
697 			}
698 			return true;
699 		default:
700 			break;
701 		}
702 		break;
703 
704 	default:
705 		break;
706 	}
707 
708 	// even if we didn't absorb the event,
709 	// we never pass events back to the GUI we're editing!
710 	return false;
711 }
712 
713 
714 //! draws the element and its children
draw()715 void CGUIEditWorkspace::draw()
716 {
717 	video::IVideoDriver *driver = Environment->getVideoDriver();
718 
719 	if (DrawGrid)
720 	{
721 		// draw the grid
722 
723 		core::rect<s32> r = getAbsolutePosition();
724 
725 		s32 cy = r.UpperLeftCorner.Y;
726 		while (cy < r.LowerRightCorner.Y)
727 		{
728 			s32 cx = r.UpperLeftCorner.X;
729 			while (cx < r.LowerRightCorner.X)
730 			{
731 				driver->draw2DRectangle(video::SColor(40,0,0,90),core::rect<s32>(cx+1,cy+1,GridSize.Width+cx,GridSize.Height+cy));
732 				cx += GridSize.Width;
733 			}
734 			cy += GridSize.Height;
735 		}
736 	}
737 	if (MouseOverElement &&
738 		MouseOverElement != SelectedElement &&
739 		MouseOverElement != Parent)
740 	{
741 		core::rect<s32> r = MouseOverElement->getAbsolutePosition();
742 		driver->draw2DRectangle(video::SColor(100,0,0,255), r);
743 	}
744 	if (SelectedElement && CurrentMode == EGUIEDM_SELECT)
745 	{
746 		driver->draw2DRectangle(video::SColor(100,0,255,0),SelectedElement->getAbsolutePosition());
747 	}
748 	if (CurrentMode >= EGUIEDM_MOVE)
749 	{
750 		driver->draw2DRectangle(video::SColor(100,255,0,0),SelectedArea);
751 	}
752 
753 	if ( (SelectedElement && CurrentMode >= EGUIEDM_MOVE) ||
754 		(SelectedElement && MouseOverElement == SelectedElement && MouseOverMode >= EGUIEDM_MOVE) )
755 	{
756 		// draw handles for moving
757 		EGUIEDIT_MODE m = CurrentMode;
758 		core::rect<s32> r = SelectedArea;
759 		if (m < EGUIEDM_MOVE)
760 		{
761 			m = MouseOverMode;
762 			r = SelectedElement->getAbsolutePosition();
763 		}
764 
765 		core::position2di d = core::position2di(4,4);
766 
767 		TLRect = core::rect<s32>(r.UpperLeftCorner, r.UpperLeftCorner + d );
768 		TRRect = core::rect<s32>(r.LowerRightCorner.X-4, r.UpperLeftCorner.Y, r.LowerRightCorner.X, r.UpperLeftCorner.Y+4);
769 		TopRect = core::rect<s32>(r.getCenter().X-2, r.UpperLeftCorner.Y,r.getCenter().X+2, r.UpperLeftCorner.Y+4 );
770 		BLRect = core::rect<s32>(r.UpperLeftCorner.X, r.LowerRightCorner.Y-4, r.UpperLeftCorner.X+4, r.LowerRightCorner.Y);
771 		LRect = core::rect<s32>(r.UpperLeftCorner.X,r.getCenter().Y-2, r.UpperLeftCorner.X+4, r.getCenter().Y+2 );
772 		RRect = core::rect<s32>(r.LowerRightCorner.X-4,r.getCenter().Y-2, r.LowerRightCorner.X, r.getCenter().Y+2 );
773 		BRRect = core::rect<s32>(r.LowerRightCorner-d, r.LowerRightCorner);
774 		BRect = core::rect<s32>(r.getCenter().X-2, r.LowerRightCorner.Y-4,r.getCenter().X+2, r.LowerRightCorner.Y );
775 
776 		// top left
777 		if (m == EGUIEDM_RESIZE_T || m == EGUIEDM_RESIZE_L || m == EGUIEDM_RESIZE_TL || m == EGUIEDM_MOVE )
778 			driver->draw2DRectangle(video::SColor(100,255,255,255), TLRect);
779 
780 		if (m == EGUIEDM_RESIZE_T || m == EGUIEDM_RESIZE_R || m == EGUIEDM_RESIZE_TR || m == EGUIEDM_MOVE )
781 			driver->draw2DRectangle(video::SColor(100,255,255,255), TRRect);
782 
783 		if (m == EGUIEDM_RESIZE_T || m == EGUIEDM_MOVE )
784 			driver->draw2DRectangle(video::SColor(100,255,255,255), TopRect);
785 
786 		if (m == EGUIEDM_RESIZE_L || m == EGUIEDM_RESIZE_BL || m == EGUIEDM_RESIZE_B || m == EGUIEDM_MOVE )
787 			driver->draw2DRectangle(video::SColor(100,255,255,255), BLRect);
788 
789 		if (m == EGUIEDM_RESIZE_L || m == EGUIEDM_MOVE )
790 			driver->draw2DRectangle(video::SColor(100,255,255,255), LRect);
791 
792 		if (m == EGUIEDM_RESIZE_R || m == EGUIEDM_MOVE )
793 			driver->draw2DRectangle(video::SColor(100,255,255,255), RRect);
794 
795 		if (m == EGUIEDM_RESIZE_R || m == EGUIEDM_RESIZE_BR || m == EGUIEDM_RESIZE_B || m == EGUIEDM_MOVE )
796 			driver->draw2DRectangle(video::SColor(100,255,255,255), BRRect );
797 
798 		if (m == EGUIEDM_RESIZE_B || m == EGUIEDM_MOVE )
799 			driver->draw2DRectangle(video::SColor(100,255,255,255), BRect);
800 
801 
802 	}
803 
804 	IGUIElement::draw();
805 }
806 
807 
setDrawGrid(bool drawGrid)808 void CGUIEditWorkspace::setDrawGrid(bool drawGrid)
809 {
810 	DrawGrid = drawGrid;
811 }
812 
setGridSize(const core::dimension2di & gridSize)813 void CGUIEditWorkspace::setGridSize(const core::dimension2di& gridSize)
814 {
815 	GridSize = gridSize;
816 	if (GridSize.Width < 2)
817 		GridSize.Width = 2;
818 	if (GridSize.Height < 2)
819 		GridSize.Height = 2;
820 }
821 
setUseGrid(bool useGrid)822 void CGUIEditWorkspace::setUseGrid(bool useGrid)
823 {
824 	UseGrid = useGrid;
825 }
826 
827 
828 //! Removes a child.
removeChild(IGUIElement * child)829 void CGUIEditWorkspace::removeChild(IGUIElement* child)
830 {
831 	IGUIElement::removeChild(child);
832 
833 	if (Children.empty())
834 		remove();
835 }
836 
837 
updateAbsolutePosition()838 void CGUIEditWorkspace::updateAbsolutePosition()
839 {
840 	core::rect<s32> parentRect(0,0,0,0);
841 
842 	if (Parent)
843 	{
844 		parentRect = Parent->getAbsolutePosition();
845 		RelativeRect.UpperLeftCorner.X = 0;
846 		RelativeRect.UpperLeftCorner.Y = 0;
847 		RelativeRect.LowerRightCorner.X = parentRect.getWidth();
848 		RelativeRect.LowerRightCorner.Y = parentRect.getHeight();
849 	}
850 
851 	IGUIElement::updateAbsolutePosition();
852 }
853 
CopySelectedElementXML()854 void CGUIEditWorkspace::CopySelectedElementXML()
855 {
856 	core::stringc XMLText;
857 	core::stringw wXMLText;
858 	// create memory write file
859 	io::CMemoryReadWriteFile* memWrite = new io::CMemoryReadWriteFile("#Clipboard#");
860 	// save gui to mem file
861 	io::IXMLWriter* xml = Environment->getFileSystem()->createXMLWriter(memWrite);
862 	Environment->writeGUIElement(xml, SelectedElement);
863 
864 	// copy to clipboard- wide chars not supported yet :(
865 	wXMLText = (wchar_t*)&memWrite->getData()[0];
866 	u32 i = memWrite->getData().size()/sizeof(wchar_t);
867 	if (wXMLText.size() > i)
868 		wXMLText[i] = L'\0';
869 	XMLText = wXMLText.c_str();
870 	memWrite->drop();
871 	xml->drop();
872 	Environment->getOSOperator()->copyToClipboard(XMLText.c_str());
873 }
874 
PasteXMLToSelectedElement()875 void CGUIEditWorkspace::PasteXMLToSelectedElement()
876 {
877 	// get clipboard data
878 	const char * p = Environment->getOSOperator()->getTextFromClipboard();
879 
880 	// convert to stringw
881 	// TODO: we should have such a function in core::string
882 	size_t lenOld = strlen(p);
883 	wchar_t *ws = new wchar_t[lenOld + 1];
884 	size_t len = mbstowcs(ws,p,lenOld);
885 	ws[len] = 0;
886 	irr::core::stringw wXMLText(ws);
887 	delete[] ws;
888 
889 	io::CMemoryReadWriteFile* memWrite = new io::CMemoryReadWriteFile("#Clipboard#");
890 
891 	io::IXMLWriter* xmlw = Environment->getFileSystem()->createXMLWriter(memWrite);
892 	xmlw->writeXMLHeader(); // it needs one of those
893 	xmlw->drop();
894 
895 	// write clipboard data
896 	memWrite->write((void*)&wXMLText[0], wXMLText.size() * sizeof(wchar_t));
897 
898 	// rewind file
899 	memWrite->seek(0, false);
900 
901 	// read xml
902 	Environment->loadGUI(memWrite, SelectedElement);
903 
904 	// reset focus
905 	Environment->setFocus(this);
906 
907 	// drop the read file
908 	memWrite->drop();
909 }
910 
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options)911 void CGUIEditWorkspace::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options)
912 {
913 	out->addBool("DrawGrid", DrawGrid);
914 	out->addBool("UseGrid", UseGrid);
915 	out->addPosition2d("GridSize", core::position2di(GridSize.Width, GridSize.Height));
916 	out->addInt("MenuCommandStart", MenuCommandStart);
917 }
918 
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options)919 void CGUIEditWorkspace::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
920 {
921 	setDrawGrid(in->getAttributeAsBool("DrawGrid"));
922 	setUseGrid(in->getAttributeAsBool("UseGrid"));
923 
924 	core::position2di tmpp = in->getAttributeAsPosition2d("GridSize");
925 	core::dimension2di tmpd(tmpp.X, tmpp.Y);
926 	setGridSize(tmpd);
927 	setMenuCommandIDStart(in->getAttributeAsInt("MenuCommandStart"));
928 }
929 
930 
931 } // end namespace gui
932 } // end namespace irr
933 
934 
935