]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CGUIEnvironment.cpp
e970d64061c6989028e9e16cdd01c4f7310ff11e
[irrlicht.git] / source / Irrlicht / CGUIEnvironment.cpp
1 \r
2 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
3 // This file is part of the "Irrlicht Engine".\r
4 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
5 \r
6 #include "CGUIEnvironment.h"\r
7 \r
8 #ifdef _IRR_COMPILE_WITH_GUI_\r
9 \r
10 #include "IVideoDriver.h"\r
11 \r
12 #include "CGUISkin.h"\r
13 #include "CGUIButton.h"\r
14 #include "CGUIWindow.h"\r
15 #include "CGUIScrollBar.h"\r
16 #include "CGUIFont.h"\r
17 #include "CGUISpriteBank.h"\r
18 #include "CGUIImage.h"\r
19 #include "CGUIMeshViewer.h"\r
20 #include "CGUICheckBox.h"\r
21 #include "CGUIListBox.h"\r
22 #include "CGUITreeView.h"\r
23 #include "CGUIImageList.h"\r
24 #include "CGUIFileOpenDialog.h"\r
25 #include "CGUIColorSelectDialog.h"\r
26 #include "CGUIStaticText.h"\r
27 #include "CGUIEditBox.h"\r
28 #include "CGUISpinBox.h"\r
29 #include "CGUIInOutFader.h"\r
30 #include "CGUIMessageBox.h"\r
31 #include "CGUIModalScreen.h"\r
32 #include "CGUITabControl.h"\r
33 #include "CGUIContextMenu.h"\r
34 #include "CGUIComboBox.h"\r
35 #include "CGUIMenu.h"\r
36 #include "CGUIToolBar.h"\r
37 #include "CGUITable.h"\r
38 #include "CGUIProfiler.h"\r
39 \r
40 #include "CDefaultGUIElementFactory.h"\r
41 #include "IWriteFile.h"\r
42 #include "IXMLWriter.h"\r
43 \r
44 #include "BuiltInFont.h"\r
45 #include "os.h"\r
46 \r
47 namespace irr\r
48 {\r
49 namespace gui\r
50 {\r
51 \r
52 const wchar_t IRR_XML_FORMAT_GUI_ENV[]                  = L"irr_gui";\r
53 const wchar_t IRR_XML_FORMAT_GUI_ELEMENT[]              = L"element";\r
54 const wchar_t IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE[]    = L"type";\r
55 \r
56 const io::path CGUIEnvironment::DefaultFontName = "#DefaultFont";\r
57 \r
58 //! constructor\r
59 CGUIEnvironment::CGUIEnvironment(io::IFileSystem* fs, video::IVideoDriver* driver, IOSOperator* op)\r
60 : IGUIElement(EGUIET_ROOT, 0, 0, 0, core::rect<s32>(driver ? core::dimension2d<s32>(driver->getScreenSize()) : core::dimension2d<s32>(0,0))),\r
61         Driver(driver), Hovered(0), HoveredNoSubelement(0), Focus(0), LastHoveredMousePos(0,0), CurrentSkin(0),\r
62         FileSystem(fs), UserReceiver(0), Operator(op), FocusFlags(EFF_SET_ON_LMOUSE_DOWN|EFF_SET_ON_TAB)\r
63 {\r
64         if (Driver)\r
65                 Driver->grab();\r
66 \r
67         if (FileSystem)\r
68                 FileSystem->grab();\r
69 \r
70         if (Operator)\r
71                 Operator->grab();\r
72 \r
73         #ifdef _DEBUG\r
74         IGUIEnvironment::setDebugName("CGUIEnvironment");\r
75         #endif\r
76 \r
77         // gui factory\r
78         IGUIElementFactory* factory = new CDefaultGUIElementFactory(this);\r
79         registerGUIElementFactory(factory);\r
80         factory->drop();\r
81 \r
82         loadBuiltInFont();\r
83 \r
84         IGUISkin* skin = createSkin( gui::EGST_WINDOWS_METALLIC );\r
85         setSkin(skin);\r
86         skin->drop();\r
87 \r
88         //set tooltip default\r
89         ToolTip.LastTime = 0;\r
90         ToolTip.EnterTime = 0;\r
91         ToolTip.LaunchTime = 1000;\r
92         ToolTip.RelaunchTime = 500;\r
93         ToolTip.Element = 0;\r
94 \r
95         // environment is root tab group\r
96         Environment = this;\r
97         setTabGroup(true);\r
98 }\r
99 \r
100 \r
101 //! destructor\r
102 CGUIEnvironment::~CGUIEnvironment()\r
103 {\r
104         clearDeletionQueue();\r
105 \r
106         if ( HoveredNoSubelement && HoveredNoSubelement != this )\r
107         {\r
108                 HoveredNoSubelement->drop();\r
109                 HoveredNoSubelement = 0;\r
110         }\r
111 \r
112         if (Hovered && Hovered != this)\r
113         {\r
114                 Hovered->drop();\r
115                 Hovered = 0;\r
116         }\r
117 \r
118         if (Focus)\r
119         {\r
120                 Focus->drop();\r
121                 Focus = 0;\r
122         }\r
123 \r
124         if (ToolTip.Element)\r
125         {\r
126                 ToolTip.Element->drop();\r
127                 ToolTip.Element = 0;\r
128         }\r
129 \r
130         // drop skin\r
131         if (CurrentSkin)\r
132         {\r
133                 CurrentSkin->drop();\r
134                 CurrentSkin = 0;\r
135         }\r
136 \r
137         u32 i;\r
138 \r
139         // delete all sprite banks\r
140         for (i=0; i<Banks.size(); ++i)\r
141                 if (Banks[i].Bank)\r
142                         Banks[i].Bank->drop();\r
143 \r
144         // delete all fonts\r
145         for (i=0; i<Fonts.size(); ++i)\r
146                 Fonts[i].Font->drop();\r
147 \r
148         // remove all factories\r
149         for (i=0; i<GUIElementFactoryList.size(); ++i)\r
150                 GUIElementFactoryList[i]->drop();\r
151 \r
152         if (Operator)\r
153         {\r
154                 Operator->drop();\r
155                 Operator = 0;\r
156         }\r
157 \r
158         if (FileSystem)\r
159         {\r
160                 FileSystem->drop();\r
161                 FileSystem = 0;\r
162         }\r
163 \r
164         if (Driver)\r
165         {\r
166                 Driver->drop();\r
167                 Driver = 0;\r
168         }\r
169 }\r
170 \r
171 \r
172 void CGUIEnvironment::loadBuiltInFont()\r
173 {\r
174         io::IReadFile* file = FileSystem->createMemoryReadFile(BuiltInFontData,\r
175                                 BuiltInFontDataSize, DefaultFontName, false);\r
176 \r
177         CGUIFont* font = new CGUIFont(this, DefaultFontName );\r
178         if (!font->load(file))\r
179         {\r
180                 os::Printer::log("Error: Could not load built-in Font. Did you compile without the BMP loader?", ELL_ERROR);\r
181                 font->drop();\r
182                 file->drop();\r
183                 return;\r
184         }\r
185 \r
186         SFont f;\r
187         f.NamedPath.setPath(DefaultFontName);\r
188         f.Font = font;\r
189         Fonts.push_back(f);\r
190 \r
191         file->drop();\r
192 }\r
193 \r
194 \r
195 //! draws all gui elements\r
196 void CGUIEnvironment::drawAll(bool useScreenSize)\r
197 {\r
198         if (useScreenSize && Driver)\r
199         {\r
200                 core::dimension2d<s32> dim(Driver->getScreenSize());\r
201                 if (AbsoluteRect.LowerRightCorner.X != dim.Width ||\r
202                         AbsoluteRect.UpperLeftCorner.X != 0 ||\r
203                         AbsoluteRect.LowerRightCorner.Y != dim.Height ||\r
204                         AbsoluteRect.UpperLeftCorner.Y != 0\r
205                         )\r
206                 {\r
207                         setRelativePosition(core::recti(0,0,dim.Width, dim.Height));\r
208                 }\r
209         }\r
210 \r
211         // make sure tooltip is always on top\r
212         if (ToolTip.Element)\r
213                 bringToFront(ToolTip.Element);\r
214 \r
215         draw();\r
216         OnPostRender ( os::Timer::getTime () );\r
217 \r
218         clearDeletionQueue();\r
219 }\r
220 \r
221 \r
222 //! sets the focus to an element\r
223 bool CGUIEnvironment::setFocus(IGUIElement* element)\r
224 {\r
225         if (Focus == element)\r
226         {\r
227                 return false;\r
228         }\r
229 \r
230         // GUI Environment should just reset the focus to 0\r
231         if (element == this)\r
232                 element = 0;\r
233 \r
234         // stop element from being deleted\r
235         if (element)\r
236                 element->grab();\r
237 \r
238         // focus may change or be removed in this call\r
239         IGUIElement *currentFocus = 0;\r
240         if (Focus)\r
241         {\r
242                 currentFocus = Focus;\r
243                 currentFocus->grab();\r
244                 SEvent e;\r
245                 e.EventType = EET_GUI_EVENT;\r
246                 e.GUIEvent.Caller = Focus;\r
247                 e.GUIEvent.Element = element;\r
248                 e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;\r
249                 if (Focus->OnEvent(e))\r
250                 {\r
251                         if (element)\r
252                                 element->drop();\r
253                         currentFocus->drop();\r
254                         return false;\r
255                 }\r
256                 currentFocus->drop();\r
257                 currentFocus = 0;\r
258         }\r
259 \r
260         if (element)\r
261         {\r
262                 currentFocus = Focus;\r
263                 if (currentFocus)\r
264                         currentFocus->grab();\r
265 \r
266                 // send focused event\r
267                 SEvent e;\r
268                 e.EventType = EET_GUI_EVENT;\r
269                 e.GUIEvent.Caller = element;\r
270                 e.GUIEvent.Element = Focus;\r
271                 e.GUIEvent.EventType = EGET_ELEMENT_FOCUSED;\r
272                 if (element->OnEvent(e))\r
273                 {\r
274                         if (element)\r
275                                 element->drop();\r
276                         if (currentFocus)\r
277                                 currentFocus->drop();\r
278                         return false;\r
279                 }\r
280         }\r
281 \r
282         if (currentFocus)\r
283                 currentFocus->drop();\r
284 \r
285         if (Focus)\r
286                 Focus->drop();\r
287 \r
288         // element is the new focus so it doesn't have to be dropped\r
289         Focus = element;\r
290 \r
291         return true;\r
292 }\r
293 \r
294 \r
295 //! returns the element with the focus\r
296 IGUIElement* CGUIEnvironment::getFocus() const\r
297 {\r
298         return Focus;\r
299 }\r
300 \r
301 //! returns the element last known to be under the mouse cursor\r
302 IGUIElement* CGUIEnvironment::getHovered() const\r
303 {\r
304         return Hovered;\r
305 }\r
306 \r
307 \r
308 //! removes the focus from an element\r
309 bool CGUIEnvironment::removeFocus(IGUIElement* element)\r
310 {\r
311         if (Focus && Focus==element)\r
312         {\r
313                 SEvent e;\r
314                 e.EventType = EET_GUI_EVENT;\r
315                 e.GUIEvent.Caller = Focus;\r
316                 e.GUIEvent.Element = 0;\r
317                 e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;\r
318                 if (Focus->OnEvent(e))\r
319                 {\r
320                         return false;\r
321                 }\r
322         }\r
323         if (Focus)\r
324         {\r
325                 Focus->drop();\r
326                 Focus = 0;\r
327         }\r
328 \r
329         return true;\r
330 }\r
331 \r
332 \r
333 //! Returns whether the element has focus\r
334 bool CGUIEnvironment::hasFocus(const IGUIElement* element, bool checkSubElements) const\r
335 {\r
336         if (element == Focus)\r
337                 return true;\r
338 \r
339         if ( !checkSubElements || !element )\r
340                 return false;\r
341 \r
342         IGUIElement* f = Focus;\r
343         while ( f && f->isSubElement() )\r
344         {\r
345                 f = f->getParent();\r
346                 if ( f == element )\r
347                         return true;\r
348         }\r
349         return false;\r
350 }\r
351 \r
352 \r
353 //! returns the current video driver\r
354 video::IVideoDriver* CGUIEnvironment::getVideoDriver() const\r
355 {\r
356         return Driver;\r
357 }\r
358 \r
359 \r
360 //! returns the current file system\r
361 io::IFileSystem* CGUIEnvironment::getFileSystem() const\r
362 {\r
363         return FileSystem;\r
364 }\r
365 \r
366 \r
367 //! returns a pointer to the OS operator\r
368 IOSOperator* CGUIEnvironment::getOSOperator() const\r
369 {\r
370         return Operator;\r
371 }\r
372 \r
373 \r
374 //! clear all GUI elements\r
375 void CGUIEnvironment::clear()\r
376 {\r
377         // Remove the focus\r
378         if (Focus)\r
379         {\r
380                 Focus->drop();\r
381                 Focus = 0;\r
382         }\r
383 \r
384         if (Hovered && Hovered != this)\r
385         {\r
386                 Hovered->drop();\r
387                 Hovered = 0;\r
388         }\r
389         if ( HoveredNoSubelement && HoveredNoSubelement != this)\r
390         {\r
391                 HoveredNoSubelement->drop();\r
392                 HoveredNoSubelement = 0;\r
393         }\r
394 \r
395         // get the root's children in case the root changes in future\r
396         const core::list<IGUIElement*>& children = getRootGUIElement()->getChildren();\r
397 \r
398         while (!children.empty())\r
399                 (*children.getLast())->remove();\r
400 }\r
401 \r
402 \r
403 //! called by ui if an event happened.\r
404 bool CGUIEnvironment::OnEvent(const SEvent& event)\r
405 {\r
406 \r
407         bool ret = false;\r
408         if (UserReceiver\r
409                 && (event.EventType != EET_MOUSE_INPUT_EVENT)\r
410                 && (event.EventType != EET_KEY_INPUT_EVENT)\r
411                 && (event.EventType != EET_GUI_EVENT || event.GUIEvent.Caller != this))\r
412         {\r
413                 ret = UserReceiver->OnEvent(event);\r
414         }\r
415 \r
416         return ret;\r
417 }\r
418 \r
419 //\r
420 void CGUIEnvironment::OnPostRender( u32 time )\r
421 {\r
422         // launch tooltip\r
423         if ( ToolTip.Element == 0 &&\r
424                 HoveredNoSubelement && HoveredNoSubelement != this &&\r
425                 (time - ToolTip.EnterTime >= ToolTip.LaunchTime\r
426                 || (time - ToolTip.LastTime >= ToolTip.RelaunchTime && time - ToolTip.LastTime < ToolTip.LaunchTime)) &&\r
427                 HoveredNoSubelement->getToolTipText().size() &&\r
428                 getSkin() &&\r
429                 getSkin()->getFont(EGDF_TOOLTIP)\r
430                 )\r
431         {\r
432                 core::rect<s32> pos;\r
433 \r
434                 pos.UpperLeftCorner = LastHoveredMousePos;\r
435                 core::dimension2du dim = getSkin()->getFont(EGDF_TOOLTIP)->getDimension(HoveredNoSubelement->getToolTipText().c_str());\r
436                 dim.Width += getSkin()->getSize(EGDS_TEXT_DISTANCE_X)*2;\r
437                 dim.Height += getSkin()->getSize(EGDS_TEXT_DISTANCE_Y)*2;\r
438 \r
439                 pos.UpperLeftCorner.Y -= dim.Height+1;\r
440                 pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + dim.Height-1;\r
441                 pos.LowerRightCorner.X = pos.UpperLeftCorner.X + dim.Width;\r
442 \r
443                 pos.constrainTo(getAbsolutePosition());\r
444 \r
445                 ToolTip.Element = addStaticText(HoveredNoSubelement->getToolTipText().c_str(), pos, true, true, this, -1, true);\r
446                 ToolTip.Element->setOverrideColor(getSkin()->getColor(EGDC_TOOLTIP));\r
447                 ToolTip.Element->setBackgroundColor(getSkin()->getColor(EGDC_TOOLTIP_BACKGROUND));\r
448                 ToolTip.Element->setOverrideFont(getSkin()->getFont(EGDF_TOOLTIP));\r
449                 ToolTip.Element->setSubElement(true);\r
450                 ToolTip.Element->grab();\r
451 \r
452                 s32 textHeight = ToolTip.Element->getTextHeight();\r
453                 pos = ToolTip.Element->getRelativePosition();\r
454                 pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + textHeight;\r
455                 ToolTip.Element->setRelativePosition(pos);\r
456         }\r
457 \r
458         if (ToolTip.Element && ToolTip.Element->isVisible() )   // (isVisible() check only because we might use visibility for ToolTip one day)\r
459         {\r
460                 ToolTip.LastTime = time;\r
461 \r
462                 // got invisible or removed in the meantime?\r
463                 if ( !HoveredNoSubelement ||\r
464                         !HoveredNoSubelement->isVisible() ||\r
465                         !HoveredNoSubelement->getParent()\r
466                         )       // got invisible or removed in the meantime?\r
467                 {\r
468                         ToolTip.Element->remove();\r
469                         ToolTip.Element->drop();\r
470                         ToolTip.Element = 0;\r
471                 }\r
472         }\r
473 \r
474         IGUIElement::OnPostRender ( time );\r
475 }\r
476 \r
477 void CGUIEnvironment::addToDeletionQueue(IGUIElement* element)\r
478 {\r
479         if (!element)\r
480                 return;\r
481 \r
482         element->grab();\r
483         DeletionQueue.push_back(element);\r
484 }\r
485 \r
486 void CGUIEnvironment::clearDeletionQueue()\r
487 {\r
488         if (DeletionQueue.empty())\r
489                 return;\r
490 \r
491         for (u32 i=0; i<DeletionQueue.size(); ++i)\r
492         {\r
493                 DeletionQueue[i]->remove();\r
494                 DeletionQueue[i]->drop();\r
495         }\r
496 \r
497         DeletionQueue.clear();\r
498 }\r
499 \r
500 //\r
501 void CGUIEnvironment::updateHoveredElement(core::position2d<s32> mousePos)\r
502 {\r
503         IGUIElement* lastHovered = Hovered;\r
504         IGUIElement* lastHoveredNoSubelement = HoveredNoSubelement;\r
505         LastHoveredMousePos = mousePos;\r
506 \r
507         Hovered = getElementFromPoint(mousePos);\r
508 \r
509         if ( ToolTip.Element && Hovered == ToolTip.Element )\r
510         {\r
511                 // When the mouse is over the ToolTip we remove that so it will be re-created at a new position.\r
512                 // Note that ToolTip.EnterTime does not get changed here, so it will be re-created at once.\r
513                 ToolTip.Element->remove();\r
514                 ToolTip.Element->drop();\r
515                 ToolTip.Element = 0;\r
516 \r
517                 // Get the real Hovered\r
518                 Hovered = getElementFromPoint(mousePos);\r
519         }\r
520 \r
521         // for tooltips we want the element itself and not some of it's subelements\r
522         HoveredNoSubelement = Hovered;\r
523         while ( HoveredNoSubelement && HoveredNoSubelement->isSubElement() )\r
524         {\r
525                 HoveredNoSubelement = HoveredNoSubelement->getParent();\r
526         }\r
527 \r
528         if (Hovered && Hovered != this)\r
529                 Hovered->grab();\r
530         if ( HoveredNoSubelement && HoveredNoSubelement != this)\r
531                 HoveredNoSubelement->grab();\r
532 \r
533         if (Hovered != lastHovered)\r
534         {\r
535                 SEvent event;\r
536                 event.EventType = EET_GUI_EVENT;\r
537 \r
538                 if (lastHovered)\r
539                 {\r
540                         event.GUIEvent.Caller = lastHovered;\r
541                         event.GUIEvent.Element = 0;\r
542                         event.GUIEvent.EventType = EGET_ELEMENT_LEFT;\r
543                         lastHovered->OnEvent(event);\r
544                 }\r
545 \r
546                 if ( Hovered )\r
547                 {\r
548                         event.GUIEvent.Caller  = Hovered;\r
549                         event.GUIEvent.Element = Hovered;\r
550                         event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;\r
551                         Hovered->OnEvent(event);\r
552                 }\r
553         }\r
554 \r
555         if ( lastHoveredNoSubelement != HoveredNoSubelement )\r
556         {\r
557                 if (ToolTip.Element)\r
558                 {\r
559                         ToolTip.Element->remove();\r
560                         ToolTip.Element->drop();\r
561                         ToolTip.Element = 0;\r
562                 }\r
563 \r
564                 if ( HoveredNoSubelement )\r
565                 {\r
566                         u32 now = os::Timer::getTime();\r
567                         ToolTip.EnterTime = now;\r
568                 }\r
569         }\r
570 \r
571         if (lastHovered && lastHovered != this)\r
572                 lastHovered->drop();\r
573         if (lastHoveredNoSubelement && lastHoveredNoSubelement != this)\r
574                 lastHoveredNoSubelement->drop();\r
575 }\r
576 \r
577 \r
578 //! This sets a new event receiver for gui events. Usually you do not have to\r
579 //! use this method, it is used by the internal engine.\r
580 void CGUIEnvironment::setUserEventReceiver(IEventReceiver* evr)\r
581 {\r
582         UserReceiver = evr;\r
583 }\r
584 \r
585 \r
586 //! posts an input event to the environment\r
587 bool CGUIEnvironment::postEventFromUser(const SEvent& event)\r
588 {\r
589         switch(event.EventType)\r
590         {\r
591         case EET_GUI_EVENT:\r
592                 {\r
593                         // hey, why is the user sending gui events..?\r
594                 }\r
595 \r
596                 break;\r
597         case EET_MOUSE_INPUT_EVENT:\r
598 \r
599                 updateHoveredElement(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));\r
600 \r
601                 if ( Hovered != Focus )\r
602                 {\r
603                         IGUIElement * focusCandidate = Hovered;\r
604 \r
605                         // Only allow enabled elements to be focused (unless EFF_CAN_FOCUS_DISABLED is set)\r
606                         if ( Hovered && !Hovered->isEnabled() && !(FocusFlags & EFF_CAN_FOCUS_DISABLED))\r
607                                 focusCandidate = NULL;  // we still remove focus from the active element\r
608 \r
609                         // Please don't merge this into a single if clause, it's easier to debug the way it is\r
610                         if (FocusFlags & EFF_SET_ON_LMOUSE_DOWN &&\r
611                                 event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN )\r
612                         {\r
613                                 setFocus(focusCandidate);\r
614                         }\r
615                         else if ( FocusFlags & EFF_SET_ON_RMOUSE_DOWN &&\r
616                                 event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN )\r
617                         {\r
618                                 setFocus(focusCandidate);\r
619                         }\r
620                         else if ( FocusFlags & EFF_SET_ON_MOUSE_OVER &&\r
621                                 event.MouseInput.Event == EMIE_MOUSE_MOVED )\r
622                         {\r
623                                 setFocus(focusCandidate);\r
624                         }\r
625                 }\r
626 \r
627                 // sending input to focus\r
628                 if (Focus && Focus->OnEvent(event))\r
629                         return true;\r
630 \r
631                 // focus could have died in last call\r
632                 if (!Focus && Hovered)\r
633                 {\r
634                         return Hovered->OnEvent(event);\r
635                 }\r
636 \r
637                 break;\r
638         case EET_KEY_INPUT_EVENT:\r
639                 {\r
640                         if (Focus && Focus->OnEvent(event))\r
641                                 return true;\r
642 \r
643                         // For keys we handle the event before changing focus to give elements the chance for catching the TAB\r
644                         // Send focus changing event\r
645                         if (FocusFlags & EFF_SET_ON_TAB &&\r
646                                 event.EventType == EET_KEY_INPUT_EVENT &&\r
647                                 event.KeyInput.PressedDown &&\r
648                                 event.KeyInput.Key == KEY_TAB)\r
649                         {\r
650                                 IGUIElement *next = getNextElement(event.KeyInput.Shift, event.KeyInput.Control);\r
651                                 if (next && next != Focus)\r
652                                 {\r
653                                         if (setFocus(next))\r
654                                                 return true;\r
655                                 }\r
656                         }\r
657                 }\r
658                 break;\r
659         default:\r
660                 break;\r
661         } // end switch\r
662 \r
663         return false;\r
664 }\r
665 \r
666 \r
667 //! returns the current gui skin\r
668 IGUISkin* CGUIEnvironment::getSkin() const\r
669 {\r
670         return CurrentSkin;\r
671 }\r
672 \r
673 \r
674 //! Sets a new GUI Skin\r
675 void CGUIEnvironment::setSkin(IGUISkin* skin)\r
676 {\r
677         if (CurrentSkin==skin)\r
678                 return;\r
679 \r
680         if (CurrentSkin)\r
681                 CurrentSkin->drop();\r
682 \r
683         CurrentSkin = skin;\r
684 \r
685         if (CurrentSkin)\r
686                 CurrentSkin->grab();\r
687 }\r
688 \r
689 \r
690 //! Creates a new GUI Skin based on a template.\r
691 /** \return Returns a pointer to the created skin.\r
692 If you no longer need the skin, you should call IGUISkin::drop().\r
693 See IReferenceCounted::drop() for more information. */\r
694 IGUISkin* CGUIEnvironment::createSkin(EGUI_SKIN_TYPE type)\r
695 {\r
696         IGUISkin* skin = new CGUISkin(type, Driver);\r
697 \r
698         IGUIFont* builtinfont = getBuiltInFont();\r
699         IGUIFontBitmap* bitfont = 0;\r
700         if (builtinfont && builtinfont->getType() == EGFT_BITMAP)\r
701                 bitfont = (IGUIFontBitmap*)builtinfont;\r
702 \r
703         IGUISpriteBank* bank = 0;\r
704         skin->setFont(builtinfont);\r
705 \r
706         if (bitfont)\r
707                 bank = bitfont->getSpriteBank();\r
708 \r
709         skin->setSpriteBank(bank);\r
710 \r
711         return skin;\r
712 }\r
713 \r
714 \r
715 //! Returns the default element factory which can create all built in elements\r
716 IGUIElementFactory* CGUIEnvironment::getDefaultGUIElementFactory() const\r
717 {\r
718         return getGUIElementFactory(0);\r
719 }\r
720 \r
721 \r
722 //! Adds an element factory to the gui environment.\r
723 /** Use this to extend the gui environment with new element types which it should be\r
724 able to create automatically, for example when loading data from xml files. */\r
725 void CGUIEnvironment::registerGUIElementFactory(IGUIElementFactory* factoryToAdd)\r
726 {\r
727         if (factoryToAdd)\r
728         {\r
729                 factoryToAdd->grab();\r
730                 GUIElementFactoryList.push_back(factoryToAdd);\r
731         }\r
732 }\r
733 \r
734 \r
735 //! Returns amount of registered scene node factories.\r
736 u32 CGUIEnvironment::getRegisteredGUIElementFactoryCount() const\r
737 {\r
738         return GUIElementFactoryList.size();\r
739 }\r
740 \r
741 \r
742 //! Returns a scene node factory by index\r
743 IGUIElementFactory* CGUIEnvironment::getGUIElementFactory(u32 index) const\r
744 {\r
745         if (index < GUIElementFactoryList.size())\r
746                 return GUIElementFactoryList[index];\r
747         else\r
748                 return 0;\r
749 }\r
750 \r
751 \r
752 //! adds a GUI Element using its name\r
753 IGUIElement* CGUIEnvironment::addGUIElement(const c8* elementName, IGUIElement* parent)\r
754 {\r
755         IGUIElement* node=0;\r
756 \r
757         if (!parent)\r
758                 parent = this;\r
759 \r
760         for (s32 i=GUIElementFactoryList.size()-1; i>=0 && !node; --i)\r
761                 node = GUIElementFactoryList[i]->addGUIElement(elementName, parent);\r
762 \r
763 \r
764         return node;\r
765 }\r
766 \r
767 \r
768 //! Saves the current gui into a file.\r
769 //! \param filename: Name of the file .\r
770 bool CGUIEnvironment::saveGUI(const io::path& filename, IGUIElement* start)\r
771 {\r
772         io::IWriteFile* file = FileSystem->createAndWriteFile(filename);\r
773         if (!file)\r
774         {\r
775                 return false;\r
776         }\r
777 \r
778         bool ret = saveGUI(file, start);\r
779         file->drop();\r
780         return ret;\r
781 }\r
782 \r
783 \r
784 //! Saves the current gui into a file.\r
785 bool CGUIEnvironment::saveGUI(io::IWriteFile* file, IGUIElement* start)\r
786 {\r
787         if (!file)\r
788         {\r
789                 return false;\r
790         }\r
791 \r
792         io::IXMLWriter* writer = FileSystem->createXMLWriter(file);\r
793         if (!writer)\r
794         {\r
795                 return false;\r
796         }\r
797 \r
798         writer->writeXMLHeader();\r
799         writeGUIElement(writer, start ? start : this);\r
800         writer->drop();\r
801 \r
802         return true;\r
803 }\r
804 \r
805 \r
806 //! Loads the gui. Note that the current gui is not cleared before.\r
807 //! \param filename: Name of the file.\r
808 bool CGUIEnvironment::loadGUI(const io::path& filename, IGUIElement* parent)\r
809 {\r
810         io::IReadFile* read = FileSystem->createAndOpenFile(filename);\r
811         if (!read)\r
812         {\r
813                 os::Printer::log("Unable to open gui file", filename, ELL_ERROR);\r
814                 return false;\r
815         }\r
816 \r
817         bool ret = loadGUI(read, parent);\r
818         read->drop();\r
819 \r
820         return ret;\r
821 }\r
822 \r
823 \r
824 //! Loads the gui. Note that the current gui is not cleared before.\r
825 bool CGUIEnvironment::loadGUI(io::IReadFile* file, IGUIElement* parent)\r
826 {\r
827         if (!file)\r
828         {\r
829                 os::Printer::log("Unable to open GUI file", ELL_ERROR);\r
830                 return false;\r
831         }\r
832 \r
833         io::IXMLReader* reader = FileSystem->createXMLReader(file);\r
834         if (!reader)\r
835         {\r
836                 os::Printer::log("GUI is not a valid XML file", file->getFileName(), ELL_ERROR);\r
837                 return false;\r
838         }\r
839 \r
840         // read file\r
841         while(reader->read())\r
842         {\r
843                 readGUIElement(reader, parent);\r
844         }\r
845 \r
846         // finish up\r
847 \r
848         reader->drop();\r
849         return true;\r
850 }\r
851 \r
852 \r
853 //! reads an element\r
854 void CGUIEnvironment::readGUIElement(io::IXMLReader* reader, IGUIElement* node)\r
855 {\r
856         if (!reader)\r
857                 return;\r
858 \r
859         io::EXML_NODE nodeType = reader->getNodeType();\r
860 \r
861         if (nodeType == io::EXN_NONE || nodeType == io::EXN_UNKNOWN || nodeType == io::EXN_ELEMENT_END)\r
862                 return;\r
863 \r
864         IGUIElement* deferedNode = 0;\r
865         if (!wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))\r
866         {\r
867                 // GuiEnvironment always must be this as it would serialize into a wrong element otherwise.\r
868                 // So we use the given node next time\r
869                 if ( node && node != this )\r
870                         deferedNode = node;\r
871                 node = this; // root\r
872         }\r
873         else if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()))\r
874         {\r
875                 // find node type and create it\r
876                 const core::stringc attrName = reader->getAttributeValue(IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE);\r
877 \r
878                 node = addGUIElement(attrName.c_str(), node);\r
879 \r
880                 if (!node)\r
881                         os::Printer::log("Could not create GUI element of unknown type", attrName.c_str());\r
882         }\r
883 \r
884         // read attributes\r
885 \r
886         while(reader->read())\r
887         {\r
888                 bool endreached = false;\r
889 \r
890                 switch (reader->getNodeType())\r
891                 {\r
892                 case io::EXN_ELEMENT_END:\r
893                         if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT,  reader->getNodeName()) ||\r
894                                 !wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))\r
895                         {\r
896                                 endreached = true;\r
897                         }\r
898                         break;\r
899                 case io::EXN_ELEMENT:\r
900                         if (!wcscmp(L"attributes", reader->getNodeName()))\r
901                         {\r
902                                 // read attributes\r
903                                 io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver);\r
904                                 attr->read(reader, true);\r
905 \r
906                                 if (node)\r
907                                         node->deserializeAttributes(attr);\r
908 \r
909                                 attr->drop();\r
910                         }\r
911                         else\r
912                         if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()) ||\r
913                                 !wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))\r
914                         {\r
915                                 if ( deferedNode )\r
916                                         readGUIElement(reader, deferedNode);\r
917                                 else\r
918                                         readGUIElement(reader, node);\r
919                         }\r
920                         else\r
921                         {\r
922                                 os::Printer::log("Found unknown element in irrlicht GUI file",\r
923                                                 core::stringc(reader->getNodeName()).c_str());\r
924                         }\r
925 \r
926                         break;\r
927                 default:\r
928                         break;\r
929                 }\r
930 \r
931                 if (endreached)\r
932                         break;\r
933         }\r
934 }\r
935 \r
936 \r
937 //! writes an element\r
938 void CGUIEnvironment::writeGUIElement(io::IXMLWriter* writer, IGUIElement* node)\r
939 {\r
940         if (!writer || !node )\r
941                 return;\r
942 \r
943         const wchar_t* name = 0;\r
944 \r
945         // write properties\r
946 \r
947         io::IAttributes* attr = FileSystem->createEmptyAttributes();\r
948         node->serializeAttributes(attr);\r
949 \r
950         // all gui elements must have at least one attribute\r
951         // if they have nothing then we ignore them.\r
952         if (attr->getAttributeCount() != 0)\r
953         {\r
954                 if (node == this)\r
955                 {\r
956                         name = IRR_XML_FORMAT_GUI_ENV;\r
957                         writer->writeElement(name, false);\r
958                 }\r
959                 else\r
960                 {\r
961                         name = IRR_XML_FORMAT_GUI_ELEMENT;\r
962                         writer->writeElement(name, false, IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE,\r
963                                 core::stringw(node->getTypeName()).c_str());\r
964                 }\r
965 \r
966                 writer->writeLineBreak();\r
967 \r
968                 attr->write(writer);\r
969         }\r
970 \r
971         // write children\r
972 \r
973         core::list<IGUIElement*>::ConstIterator it = node->getChildren().begin();\r
974         for (; it != node->getChildren().end(); ++it)\r
975         {\r
976                 if (!(*it)->isSubElement())\r
977                 {\r
978                         writer->writeLineBreak();\r
979                         writeGUIElement(writer, (*it));\r
980                 }\r
981         }\r
982 \r
983         // write closing brace if required\r
984         if (attr->getAttributeCount() != 0)\r
985         {\r
986                 writer->writeClosingTag(name);\r
987                 writer->writeLineBreak();\r
988         }\r
989 \r
990         attr->drop();\r
991 }\r
992 \r
993 \r
994 //! Writes attributes of the environment\r
995 void CGUIEnvironment::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const\r
996 {\r
997         IGUISkin* skin = getSkin();\r
998 \r
999         if (skin)\r
1000         {\r
1001                 out->addEnum("Skin", getSkin()->getType(), GUISkinTypeNames);\r
1002                 skin->serializeAttributes(out, options);\r
1003         }\r
1004 }\r
1005 \r
1006 \r
1007 //! Reads attributes of the environment\r
1008 void CGUIEnvironment::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)\r
1009 {\r
1010         if (in->existsAttribute("Skin"))\r
1011         {\r
1012                 IGUISkin *skin = getSkin();\r
1013 \r
1014                 EGUI_SKIN_TYPE t = (EGUI_SKIN_TYPE) in->getAttributeAsEnumeration("Skin",GUISkinTypeNames);\r
1015                 if ( !skin || t != skin->getType())\r
1016                 {\r
1017                         skin = createSkin(t);\r
1018                         setSkin(skin);\r
1019                         skin->drop();\r
1020                 }\r
1021 \r
1022                 skin = getSkin();\r
1023 \r
1024                 if (skin)\r
1025                 {\r
1026                         skin->deserializeAttributes(in, options);\r
1027                 }\r
1028 \r
1029         }\r
1030 \r
1031         RelativeRect = AbsoluteRect =\r
1032                         core::rect<s32>(Driver ? core::dimension2di(Driver->getScreenSize()) : core::dimension2d<s32>(0,0));\r
1033 }\r
1034 \r
1035 \r
1036 //! adds a button. The returned pointer must not be dropped.\r
1037 IGUIButton* CGUIEnvironment::addButton(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext)\r
1038 {\r
1039         IGUIButton* button = new CGUIButton(this, parent ? parent : this, id, rectangle);\r
1040         if (text)\r
1041                 button->setText(text);\r
1042 \r
1043         if ( tooltiptext )\r
1044                 button->setToolTipText ( tooltiptext );\r
1045 \r
1046         button->drop();\r
1047         return button;\r
1048 }\r
1049 \r
1050 \r
1051 //! adds a window. The returned pointer must not be dropped.\r
1052 IGUIWindow* CGUIEnvironment::addWindow(const core::rect<s32>& rectangle, bool modal,\r
1053                 const wchar_t* text, IGUIElement* parent, s32 id)\r
1054 {\r
1055         parent = parent ? parent : this;\r
1056 \r
1057         IGUIWindow* win = new CGUIWindow(this, parent, id, rectangle);\r
1058         if (text)\r
1059                 win->setText(text);\r
1060         win->drop();\r
1061 \r
1062         if (modal)\r
1063         {\r
1064                 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very\r
1065                 // careful not to get virtual function call, like OnEvent, in the window.\r
1066                 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);\r
1067                 modalScreen->drop();\r
1068                 modalScreen->addChild(win);\r
1069         }\r
1070 \r
1071         return win;\r
1072 }\r
1073 \r
1074 \r
1075 //! adds a modal screen. The returned pointer must not be dropped.\r
1076 IGUIElement* CGUIEnvironment::addModalScreen(IGUIElement* parent)\r
1077 {\r
1078         parent = parent ? parent : this;\r
1079 \r
1080         IGUIElement *win = new CGUIModalScreen(this, parent, -1);\r
1081         win->drop();\r
1082 \r
1083         return win;\r
1084 }\r
1085 \r
1086 \r
1087 //! Adds a message box.\r
1088 IGUIWindow* CGUIEnvironment::addMessageBox(const wchar_t* caption, const wchar_t* text,\r
1089         bool modal, s32 flag, IGUIElement* parent, s32 id, video::ITexture* image)\r
1090 {\r
1091         if (!CurrentSkin)\r
1092                 return 0;\r
1093 \r
1094         parent = parent ? parent : this;\r
1095 \r
1096         core::rect<s32> rect;\r
1097         core::dimension2d<u32> screenDim, msgBoxDim;\r
1098 \r
1099         screenDim.Width = parent->getAbsolutePosition().getWidth();\r
1100         screenDim.Height = parent->getAbsolutePosition().getHeight();\r
1101         msgBoxDim.Width = 2;\r
1102         msgBoxDim.Height = 2;\r
1103 \r
1104         rect.UpperLeftCorner.X = (screenDim.Width - msgBoxDim.Width) / 2;\r
1105         rect.UpperLeftCorner.Y = (screenDim.Height - msgBoxDim.Height) / 2;\r
1106         rect.LowerRightCorner.X = rect.UpperLeftCorner.X + msgBoxDim.Width;\r
1107         rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + msgBoxDim.Height;\r
1108 \r
1109         IGUIWindow* win = new CGUIMessageBox(this, caption, text, flag,\r
1110                 parent, id, rect, image);\r
1111         win->drop();\r
1112 \r
1113         if (modal)\r
1114         {\r
1115                 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very\r
1116                 // careful not to get virtual function call, like OnEvent, in the CGUIMessageBox.\r
1117                 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);\r
1118                 modalScreen->drop();\r
1119                 modalScreen->addChild( win );\r
1120         }\r
1121 \r
1122 \r
1123         return win;\r
1124 }\r
1125 \r
1126 \r
1127 //! adds a scrollbar. The returned pointer must not be dropped.\r
1128 IGUIScrollBar* CGUIEnvironment::addScrollBar(bool horizontal, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id)\r
1129 {\r
1130         IGUIScrollBar* bar = new CGUIScrollBar(horizontal, this, parent ? parent : this, id, rectangle);\r
1131         bar->drop();\r
1132         return bar;\r
1133 }\r
1134 \r
1135 //! Adds a table to the environment\r
1136 IGUITable* CGUIEnvironment::addTable(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, bool drawBackground)\r
1137 {\r
1138         CGUITable* b = new CGUITable(this, parent ? parent : this, id, rectangle, true, drawBackground, false);\r
1139         b->drop();\r
1140         return b;\r
1141 }\r
1142 \r
1143         //! Adds an element to display the information from the Irrlicht profiler\r
1144 IGUIProfiler* CGUIEnvironment::addProfilerDisplay(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id)\r
1145 {\r
1146         CGUIProfiler* p = new CGUIProfiler(this, parent ? parent : this, id, rectangle, NULL);\r
1147         p->drop();\r
1148         return p;\r
1149 }\r
1150 \r
1151 //! Adds an image element.\r
1152 IGUIImage* CGUIEnvironment::addImage(video::ITexture* image, core::position2d<s32> pos,\r
1153         bool useAlphaChannel, IGUIElement* parent, s32 id, const wchar_t* text)\r
1154 {\r
1155         core::dimension2d<s32> sz(0,0);\r
1156         if (image)\r
1157                 sz = core::dimension2d<s32>(image->getOriginalSize());\r
1158 \r
1159         IGUIImage* img = new CGUIImage(this, parent ? parent : this,\r
1160                 id, core::rect<s32>(pos, sz));\r
1161 \r
1162         if (text)\r
1163                 img->setText(text);\r
1164 \r
1165         if (useAlphaChannel)\r
1166                 img->setUseAlphaChannel(true);\r
1167 \r
1168         if (image)\r
1169                 img->setImage(image);\r
1170 \r
1171         img->drop();\r
1172         return img;\r
1173 }\r
1174 \r
1175 \r
1176 //! adds an image. The returned pointer must not be dropped.\r
1177 IGUIImage* CGUIEnvironment::addImage(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text, bool useAlphaChannel)\r
1178 {\r
1179         IGUIImage* img = new CGUIImage(this, parent ? parent : this,\r
1180                 id, rectangle);\r
1181 \r
1182         if (text)\r
1183                 img->setText(text);\r
1184 \r
1185         if ( useAlphaChannel )\r
1186                 img->setUseAlphaChannel(true);\r
1187 \r
1188         img->drop();\r
1189         return img;\r
1190 }\r
1191 \r
1192 \r
1193 //! adds an mesh viewer. The returned pointer must not be dropped.\r
1194 IGUIMeshViewer* CGUIEnvironment::addMeshViewer(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text)\r
1195 {\r
1196         IGUIMeshViewer* v = new CGUIMeshViewer(this, parent ? parent : this,\r
1197                 id, rectangle);\r
1198 \r
1199         if (text)\r
1200                 v->setText(text);\r
1201 \r
1202         v->drop();\r
1203         return v;\r
1204 }\r
1205 \r
1206 \r
1207 //! adds a checkbox\r
1208 IGUICheckBox* CGUIEnvironment::addCheckBox(bool checked, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text)\r
1209 {\r
1210         IGUICheckBox* b = new CGUICheckBox(checked, this,\r
1211                 parent ? parent : this , id , rectangle);\r
1212 \r
1213         if (text)\r
1214                 b->setText(text);\r
1215 \r
1216         b->drop();\r
1217         return b;\r
1218 }\r
1219 \r
1220 \r
1221 //! adds a list box\r
1222 IGUIListBox* CGUIEnvironment::addListBox(const core::rect<s32>& rectangle,\r
1223                                         IGUIElement* parent, s32 id, bool drawBackground)\r
1224 {\r
1225         IGUIListBox* b = new CGUIListBox(this, parent ? parent : this, id, rectangle,\r
1226                 true, drawBackground, false);\r
1227 \r
1228         if (CurrentSkin && CurrentSkin->getSpriteBank())\r
1229         {\r
1230                 b->setSpriteBank(CurrentSkin->getSpriteBank());\r
1231         }\r
1232         else if (getBuiltInFont() && getBuiltInFont()->getType() == EGFT_BITMAP)\r
1233         {\r
1234                 b->setSpriteBank( ((IGUIFontBitmap*)getBuiltInFont())->getSpriteBank());\r
1235         }\r
1236 \r
1237         b->drop();\r
1238         return b;\r
1239 }\r
1240 \r
1241 //! adds a tree view\r
1242 IGUITreeView* CGUIEnvironment::addTreeView(const core::rect<s32>& rectangle,\r
1243                                          IGUIElement* parent, s32 id,\r
1244                                          bool drawBackground,\r
1245                                          bool scrollBarVertical, bool scrollBarHorizontal)\r
1246 {\r
1247         IGUITreeView* b = new CGUITreeView(this, parent ? parent : this, id, rectangle,\r
1248                 true, drawBackground, scrollBarVertical, scrollBarHorizontal);\r
1249 \r
1250         b->setIconFont ( getBuiltInFont () );\r
1251         b->drop();\r
1252         return b;\r
1253 }\r
1254 \r
1255 //! adds a file open dialog. The returned pointer must not be dropped.\r
1256 IGUIFileOpenDialog* CGUIEnvironment::addFileOpenDialog(const wchar_t* title,\r
1257                                 bool modal, IGUIElement* parent, s32 id,\r
1258                                 bool restoreCWD, io::path::char_type* startDir)\r
1259 {\r
1260         parent = parent ? parent : this;\r
1261 \r
1262         IGUIFileOpenDialog* d = new CGUIFileOpenDialog(title, this, parent, id,\r
1263                         restoreCWD, startDir);\r
1264         d->drop();\r
1265 \r
1266         if (modal)\r
1267         {\r
1268                 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very\r
1269                 // careful not to get virtual function call, like OnEvent, in the window.\r
1270                 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);\r
1271                 modalScreen->drop();\r
1272                 modalScreen->addChild(d);\r
1273         }\r
1274 \r
1275         return d;\r
1276 }\r
1277 \r
1278 \r
1279 //! adds a color select dialog. The returned pointer must not be dropped.\r
1280 IGUIColorSelectDialog* CGUIEnvironment::addColorSelectDialog(const wchar_t* title,\r
1281                                 bool modal, IGUIElement* parent, s32 id)\r
1282 {\r
1283         parent = parent ? parent : this;\r
1284 \r
1285         IGUIColorSelectDialog* d = new CGUIColorSelectDialog( title,\r
1286                         this, parent, id);\r
1287         d->drop();\r
1288 \r
1289         if (modal)\r
1290         {\r
1291                 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very\r
1292                 // careful not to get virtual function call, like OnEvent, in the window.\r
1293                 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);\r
1294                 modalScreen->drop();\r
1295                 modalScreen->addChild(d);\r
1296         }\r
1297 \r
1298         return d;\r
1299 }\r
1300 \r
1301 \r
1302 //! adds a static text. The returned pointer must not be dropped.\r
1303 IGUIStaticText* CGUIEnvironment::addStaticText(const wchar_t* text,\r
1304                                 const core::rect<s32>& rectangle,\r
1305                                 bool border, bool wordWrap,\r
1306                                 IGUIElement* parent, s32 id, bool background)\r
1307 {\r
1308         IGUIStaticText* d = new CGUIStaticText(text, border, this,\r
1309                         parent ? parent : this, id, rectangle, background);\r
1310 \r
1311         d->setWordWrap(wordWrap);\r
1312         d->drop();\r
1313 \r
1314         return d;\r
1315 }\r
1316 \r
1317 \r
1318 //! Adds an edit box. The returned pointer must not be dropped.\r
1319 IGUIEditBox* CGUIEnvironment::addEditBox(const wchar_t* text,\r
1320                         const core::rect<s32>& rectangle, bool border,\r
1321                         IGUIElement* parent, s32 id)\r
1322 {\r
1323         IGUIEditBox* d = new CGUIEditBox(text, border, this,\r
1324                         parent ? parent : this, id, rectangle);\r
1325 \r
1326         d->drop();\r
1327         return d;\r
1328 }\r
1329 \r
1330 \r
1331 //! Adds a spin box to the environment\r
1332 IGUISpinBox* CGUIEnvironment::addSpinBox(const wchar_t* text,\r
1333                                          const core::rect<s32> &rectangle,\r
1334                                          bool border,IGUIElement* parent, s32 id)\r
1335 {\r
1336         IGUISpinBox* d = new CGUISpinBox(text, border,this,\r
1337                 parent ? parent : this, id, rectangle);\r
1338 \r
1339         d->drop();\r
1340         return d;\r
1341 }\r
1342 \r
1343 \r
1344 //! Adds a tab control to the environment.\r
1345 IGUITabControl* CGUIEnvironment::addTabControl(const core::rect<s32>& rectangle,\r
1346         IGUIElement* parent, bool fillbackground, bool border, s32 id)\r
1347 {\r
1348         IGUITabControl* t = new CGUITabControl(this, parent ? parent : this,\r
1349                 rectangle, fillbackground, border, id);\r
1350         t->drop();\r
1351         return t;\r
1352 }\r
1353 \r
1354 \r
1355 //! Adds tab to the environment.\r
1356 IGUITab* CGUIEnvironment::addTab(const core::rect<s32>& rectangle,\r
1357         IGUIElement* parent, s32 id)\r
1358 {\r
1359         IGUITab* t = new CGUITab(this, parent ? parent : this,\r
1360                 rectangle, id);\r
1361         t->drop();\r
1362         return t;\r
1363 }\r
1364 \r
1365 \r
1366 //! Adds a context menu to the environment.\r
1367 IGUIContextMenu* CGUIEnvironment::addContextMenu(const core::rect<s32>& rectangle,\r
1368         IGUIElement* parent, s32 id)\r
1369 {\r
1370         IGUIContextMenu* c = new CGUIContextMenu(this,\r
1371                 parent ? parent : this, id, rectangle, true);\r
1372         c->drop();\r
1373         return c;\r
1374 }\r
1375 \r
1376 \r
1377 //! Adds a menu to the environment.\r
1378 IGUIContextMenu* CGUIEnvironment::addMenu(IGUIElement* parent, s32 id)\r
1379 {\r
1380         if (!parent)\r
1381                 parent = this;\r
1382 \r
1383         IGUIContextMenu* c = new CGUIMenu(this,\r
1384                 parent, id, core::rect<s32>(0,0,\r
1385                                 parent->getAbsolutePosition().getWidth(),\r
1386                                 parent->getAbsolutePosition().getHeight()));\r
1387 \r
1388         c->drop();\r
1389         return c;\r
1390 }\r
1391 \r
1392 \r
1393 //! Adds a toolbar to the environment. It is like a menu is always placed on top\r
1394 //! in its parent, and contains buttons.\r
1395 IGUIToolBar* CGUIEnvironment::addToolBar(IGUIElement* parent, s32 id)\r
1396 {\r
1397         if (!parent)\r
1398                 parent = this;\r
1399 \r
1400         IGUIToolBar* b = new CGUIToolBar(this, parent, id, core::rect<s32>(0,0,10,10));\r
1401         b->drop();\r
1402         return b;\r
1403 }\r
1404 \r
1405 \r
1406 //! Adds an element for fading in or out.\r
1407 IGUIInOutFader* CGUIEnvironment::addInOutFader(const core::rect<s32>* rectangle, IGUIElement* parent, s32 id)\r
1408 {\r
1409         core::rect<s32> rect;\r
1410 \r
1411         if (rectangle)\r
1412                 rect = *rectangle;\r
1413         else if (Driver)\r
1414                 rect = core::rect<s32>(core::dimension2di(Driver->getScreenSize()));\r
1415 \r
1416         if (!parent)\r
1417                 parent = this;\r
1418 \r
1419         IGUIInOutFader* fader = new CGUIInOutFader(this, parent, id, rect);\r
1420         fader->drop();\r
1421         return fader;\r
1422 }\r
1423 \r
1424 \r
1425 //! Adds a combo box to the environment.\r
1426 IGUIComboBox* CGUIEnvironment::addComboBox(const core::rect<s32>& rectangle,\r
1427         IGUIElement* parent, s32 id)\r
1428 {\r
1429         IGUIComboBox* t = new CGUIComboBox(this, parent ? parent : this,\r
1430                 id, rectangle);\r
1431         t->drop();\r
1432         return t;\r
1433 }\r
1434 \r
1435 \r
1436 //! returns the font\r
1437 IGUIFont* CGUIEnvironment::getFont(const io::path& filename)\r
1438 {\r
1439         // search existing font\r
1440 \r
1441         SFont f;\r
1442         f.NamedPath.setPath(filename);\r
1443 \r
1444         s32 index = Fonts.binary_search(f);\r
1445         if (index != -1)\r
1446                 return Fonts[index].Font;\r
1447 \r
1448         // font doesn't exist, attempt to load it\r
1449 \r
1450         // does the file exist?\r
1451 \r
1452         if (!FileSystem->existFile(filename))\r
1453         {\r
1454                 os::Printer::log("Could not load font because the file does not exist", f.NamedPath.getPath(), ELL_ERROR);\r
1455                 return 0;\r
1456         }\r
1457 \r
1458         IGUIFont* ifont=0;\r
1459         io::IXMLReader *xml = FileSystem->createXMLReader(filename );\r
1460         if (xml)\r
1461         {\r
1462                 // this is an XML font, but we need to know what type\r
1463                 EGUI_FONT_TYPE t = EGFT_CUSTOM;\r
1464 \r
1465                 bool found=false;\r
1466                 while(!found && xml->read())\r
1467                 {\r
1468                         if (xml->getNodeType() == io::EXN_ELEMENT)\r
1469                         {\r
1470                                 if (core::stringw(L"font") == xml->getNodeName())\r
1471                                 {\r
1472                                         if (core::stringw(L"vector") == xml->getAttributeValue(L"type"))\r
1473                                         {\r
1474                                                 t = EGFT_VECTOR;\r
1475                                                 found=true;\r
1476                                         }\r
1477                                         else if (core::stringw(L"bitmap") == xml->getAttributeValue(L"type"))\r
1478                                         {\r
1479                                                 t = EGFT_BITMAP;\r
1480                                                 found=true;\r
1481                                         }\r
1482                                         else found=true;\r
1483                                 }\r
1484                         }\r
1485                 }\r
1486 \r
1487                 if (t==EGFT_BITMAP)\r
1488                 {\r
1489                         CGUIFont* font = new CGUIFont(this, filename);\r
1490                         ifont = (IGUIFont*)font;\r
1491 \r
1492                         // load the font\r
1493                         io::path directory;\r
1494                         core::splitFilename(filename, &directory);\r
1495                         if (!font->load(xml, directory))\r
1496                         {\r
1497                                 font->drop();\r
1498                                 font  = 0;\r
1499                                 ifont = 0;\r
1500                         }\r
1501                 }\r
1502                 else if (t==EGFT_VECTOR)\r
1503                 {\r
1504                         // todo: vector fonts\r
1505                         os::Printer::log("Unable to load font, XML vector fonts are not supported yet", f.NamedPath, ELL_ERROR);\r
1506 \r
1507                         //CGUIFontVector* font = new CGUIFontVector(Driver);\r
1508                         //ifont = (IGUIFont*)font;\r
1509                         //if (!font->load(xml))\r
1510                 }\r
1511                 xml->drop();\r
1512         }\r
1513 \r
1514 \r
1515         if (!ifont)\r
1516         {\r
1517 \r
1518                 CGUIFont* font = new CGUIFont(this, f.NamedPath.getPath() );\r
1519                 ifont = (IGUIFont*)font;\r
1520                 if (!font->load(f.NamedPath.getPath()))\r
1521                 {\r
1522                         font->drop();\r
1523                         return 0;\r
1524                 }\r
1525         }\r
1526 \r
1527         // add to fonts.\r
1528 \r
1529         f.Font = ifont;\r
1530         Fonts.push_back(f);\r
1531 \r
1532         return ifont;\r
1533 }\r
1534 \r
1535 \r
1536 //! add an externally loaded font\r
1537 IGUIFont* CGUIEnvironment::addFont(const io::path& name, IGUIFont* font)\r
1538 {\r
1539         if (font)\r
1540         {\r
1541                 SFont f;\r
1542                 f.NamedPath.setPath(name);\r
1543                 s32 index = Fonts.binary_search(f);\r
1544                 if (index != -1)\r
1545                         return Fonts[index].Font;\r
1546                 f.Font = font;\r
1547                 Fonts.push_back(f);\r
1548                 font->grab();\r
1549         }\r
1550         return font;\r
1551 }\r
1552 \r
1553 //! remove loaded font\r
1554 void CGUIEnvironment::removeFont(IGUIFont* font)\r
1555 {\r
1556         if ( !font )\r
1557                 return;\r
1558         for ( u32 i=0; i<Fonts.size(); ++i )\r
1559         {\r
1560                 if ( Fonts[i].Font == font )\r
1561                 {\r
1562                         Fonts[i].Font->drop();\r
1563                         Fonts.erase(i);\r
1564                         return;\r
1565                 }\r
1566         }\r
1567 }\r
1568 \r
1569 //! returns default font\r
1570 IGUIFont* CGUIEnvironment::getBuiltInFont() const\r
1571 {\r
1572         if (Fonts.empty())\r
1573                 return 0;\r
1574 \r
1575         return Fonts[0].Font;\r
1576 }\r
1577 \r
1578 \r
1579 IGUISpriteBank* CGUIEnvironment::getSpriteBank(const io::path& filename)\r
1580 {\r
1581         // search for the file name\r
1582 \r
1583         SSpriteBank b;\r
1584         b.NamedPath.setPath(filename);\r
1585 \r
1586         s32 index = Banks.binary_search(b);\r
1587         if (index != -1)\r
1588                 return Banks[index].Bank;\r
1589 \r
1590         // we don't have this sprite bank, we should load it\r
1591         if (!FileSystem->existFile(b.NamedPath.getPath()))\r
1592         {\r
1593                 if ( filename != DefaultFontName )\r
1594                 {\r
1595                         os::Printer::log("Could not load sprite bank because the file does not exist", b.NamedPath.getPath(), ELL_DEBUG);\r
1596                 }\r
1597                 return 0;\r
1598         }\r
1599 \r
1600         // todo: load it!\r
1601 \r
1602         return 0;\r
1603 }\r
1604 \r
1605 \r
1606 IGUISpriteBank* CGUIEnvironment::addEmptySpriteBank(const io::path& name)\r
1607 {\r
1608         // no duplicate names allowed\r
1609 \r
1610         SSpriteBank b;\r
1611         b.NamedPath.setPath(name);\r
1612 \r
1613         const s32 index = Banks.binary_search(b);\r
1614         if (index != -1)\r
1615                 return 0;\r
1616 \r
1617         // create a new sprite bank\r
1618 \r
1619         b.Bank = new CGUISpriteBank(this);\r
1620         Banks.push_back(b);\r
1621 \r
1622         return b.Bank;\r
1623 }\r
1624 \r
1625 \r
1626 //! Creates the image list from the given texture.\r
1627 IGUIImageList* CGUIEnvironment::createImageList(  video::ITexture* texture,\r
1628                                         core::dimension2d<s32>  imageSize, bool useAlphaChannel )\r
1629 {\r
1630         CGUIImageList* imageList = new CGUIImageList( Driver );\r
1631         if( !imageList->createImageList( texture, imageSize, useAlphaChannel ) )\r
1632         {\r
1633                 imageList->drop();\r
1634                 return 0;\r
1635         }\r
1636 \r
1637         return imageList;\r
1638 }\r
1639 \r
1640 //! Returns the root gui element.\r
1641 IGUIElement* CGUIEnvironment::getRootGUIElement()\r
1642 {\r
1643         return this;\r
1644 }\r
1645 \r
1646 \r
1647 //! Returns the next element in the tab group starting at the focused element\r
1648 IGUIElement* CGUIEnvironment::getNextElement(bool reverse, bool group)\r
1649 {\r
1650         // start the search at the root of the current tab group\r
1651         IGUIElement *startPos = Focus ? Focus->getTabGroup() : 0;\r
1652         s32 startOrder = -1;\r
1653 \r
1654         // if we're searching for a group\r
1655         if (group && startPos)\r
1656         {\r
1657                 startOrder = startPos->getTabOrder();\r
1658         }\r
1659         else\r
1660         if (!group && Focus && !Focus->isTabGroup())\r
1661         {\r
1662                 startOrder = Focus->getTabOrder();\r
1663                 if (startOrder == -1)\r
1664                 {\r
1665                         // this element is not part of the tab cycle,\r
1666                         // but its parent might be...\r
1667                         IGUIElement *el = Focus;\r
1668                         while (el && el->getParent() && startOrder == -1)\r
1669                         {\r
1670                                 el = el->getParent();\r
1671                                 startOrder = el->getTabOrder();\r
1672                         }\r
1673 \r
1674                 }\r
1675         }\r
1676 \r
1677         if (group || !startPos)\r
1678                 startPos = this; // start at the root\r
1679 \r
1680         // find the element\r
1681         IGUIElement *closest = 0;\r
1682         IGUIElement *first = 0;\r
1683         startPos->getNextElement(startOrder, reverse, group, first, closest, false, (FocusFlags & EFF_CAN_FOCUS_DISABLED) != 0);\r
1684 \r
1685         if (closest)\r
1686                 return closest; // we found an element\r
1687         else if (first)\r
1688                 return first; // go to the end or the start\r
1689         else if (group)\r
1690                 return this; // no group found? root group\r
1691         else\r
1692                 return 0;\r
1693 }\r
1694 \r
1695 void CGUIEnvironment::setFocusBehavior(u32 flags)\r
1696 {\r
1697         FocusFlags = flags;\r
1698 }\r
1699 \r
1700 u32 CGUIEnvironment::getFocusBehavior() const\r
1701 {\r
1702         return FocusFlags;\r
1703 }\r
1704 \r
1705 //! creates an GUI Environment\r
1706 IGUIEnvironment* createGUIEnvironment(io::IFileSystem* fs,\r
1707                                         video::IVideoDriver* Driver,\r
1708                                         IOSOperator* op)\r
1709 {\r
1710         return new CGUIEnvironment(fs, Driver, op);\r
1711 }\r
1712 \r
1713 \r
1714 } // end namespace gui\r
1715 } // end namespace irr\r
1716 \r
1717 #endif // _IRR_COMPILE_WITH_GUI_\r
1718 \r