root/cafu/trunk/CaWE/ParentFrame.cpp

Revision 505, 46.6 KB (checked in by Carsten, 2 months ago)

CaWE: Added a "hidden" menu item for setting the size of the parent frame.

This can be useful for taking screen-shots that need specific dimensions, e.g. for documentation.

Line 
1/*
2=================================================================================
3This file is part of Cafu, the open-source game engine and graphics engine
4for multiplayer, cross-platform, real-time 3D action.
5Copyright (C) 2002-2012 Carsten Fuchs Software.
6
7Cafu is free software: you can redistribute it and/or modify it under the terms
8of the GNU General Public License as published by the Free Software Foundation,
9either version 3 of the License, or (at your option) any later version.
10
11Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with Cafu. If not, see <http://www.gnu.org/licenses/>.
17
18For support and more information about Cafu, visit us at <http://www.cafu.de>.
19=================================================================================
20*/
21
22#include "ChildFrame.hpp"
23#include "GameConfig.hpp"
24#include "ParentFrame.hpp"
25#include "MapDocument.hpp"
26#include "Options.hpp"
27#include "DialogOptions.hpp"
28
29#include "GuiEditor/ChildFrame.hpp"
30#include "GuiEditor/GuiDocument.hpp"
31#include "FontWizard/FontWizard.hpp"
32#include "ModelEditor/ChildFrame.hpp"
33#include "ModelEditor/ModelDocument.hpp"
34
35#include "ConsoleCommands/Console.hpp"
36#include "FileSys/FileManImpl.hpp"
37#include "GuiSys/GuiImpl.hpp"   // Needed to catch InitErrorT if GUI document creation fails.
38#include "MaterialSystem/MapComposition.hpp"
39#include "MaterialSystem/Renderer.hpp"
40#include "MaterialSystem/TextureMap.hpp"
41#include "Models/Loader.hpp"    // Needed for the ModelLoaderT::LoadErrorT exception that must be caught when model loading failed.
42#include "TextParser/TextParser.hpp"
43#include "PlatformAux.hpp"
44
45#include "wx/wx.h"
46#include "wx/aboutdlg.h"
47#include "wx/busyinfo.h"
48#include "wx/cmdline.h"
49#include "wx/confbase.h"
50#include "wx/dir.h"
51#include "wx/filename.h"
52#include "wx/glcanvas.h"
53#include "wx/numdlg.h"
54#include "wx/progdlg.h"
55#include "wx/stdpaths.h"
56#include "wx/utils.h"
57
58#include <fstream>
59
60#ifdef _WIN32
61#define WIN32_LEAN_AND_MEAN
62#include <windows.h>
63#include <direct.h>
64#elif __linux__
65#include <dirent.h>
66#include <dlfcn.h>
67#define __stdcall
68#define GetProcAddress dlsym
69#define FreeLibrary dlclose
70#endif
71
72
73BEGIN_EVENT_TABLE(ParentFrameT, wxMDIParentFrame)
74#ifdef __WXGTK__
75    EVT_SIZE(ParentFrameT::OnSize)
76#endif
77    EVT_SHOW(ParentFrameT::OnShow)
78    EVT_CLOSE(ParentFrameT::OnClose)
79    EVT_MENU_RANGE(ID_MENU_FILE_NEW_MAP,  ID_MENU_FILE_EXIT,  ParentFrameT::OnMenuFile)
80    EVT_MENU_RANGE(wxID_FILE1,            wxID_FILE9,         ParentFrameT::OnMenuFile)
81    EVT_MENU_RANGE(ID_MENU_HELP_CONTENTS, ID_MENU_HELP_ABOUT, ParentFrameT::OnMenuHelp)
82END_EVENT_TABLE()
83
84
85int ParentFrameT::OpenGLAttributeList[]=
86{
87    WX_GL_RGBA,
88    WX_GL_DOUBLEBUFFER,
89    WX_GL_MIN_RED,       8,
90    WX_GL_MIN_GREEN,     8,
91    WX_GL_MIN_BLUE,      8,
92    WX_GL_MIN_ALPHA,     8,
93    WX_GL_DEPTH_SIZE,   16,
94    WX_GL_STENCIL_SIZE,  8,
95    0   // Zero indicates the end of the array.
96};
97
98
99ParentFrameT::ParentFrameT(wxCmdLineParser& Parser)
100#ifdef DEBUG
101      // We need the wxMAXIMIZE window style here, or else the window is not properly maximized (looks like a bug?).
102    : wxMDIParentFrame(NULL /*parent*/, -1 /*id*/, wxString("Cafu World Editor ") + "[DEBUG] - " + __DATE__, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxMAXIMIZE),
103#else
104      // We need the wxMAXIMIZE window style here, or else the window is not properly maximized (looks like a bug?).
105    : wxMDIParentFrame(NULL /*parent*/, -1 /*id*/, wxString("Cafu World Editor - ") + __DATE__, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxMAXIMIZE),
106#endif
107      m_GLCanvas(NULL),
108      m_GLContext(NULL),
109      m_WhiteTexture(NULL),
110      m_CmdLineParser(Parser),
111      m_RendererDLL(NULL)
112{
113    wxMenuBar *item0 = new wxMenuBar;
114
115    wxMenu* item1 = new wxMenu;
116
117    wxMenu* NewMenu = new wxMenu;
118
119    NewMenu->Append(ID_MENU_FILE_NEW_MAP,   wxT("New &Map\tCtrl+N"), wxT(""));
120    NewMenu->Append(ID_MENU_FILE_NEW_MODEL, wxT("New M&odel\tCtrl+Shift+N"), wxT(""));
121    NewMenu->Append(ID_MENU_FILE_NEW_GUI,   wxT("New &GUI\tCtrl+Alt+N"), wxT(""));
122    NewMenu->Append(ID_MENU_FILE_NEW_FONT,  wxT("New &Font"), wxT(""));
123
124    item1->AppendSubMenu(NewMenu, wxT("&New"));
125    item1->Append(ID_MENU_FILE_OPEN, wxT("&Open...\tCtrl+O"), wxT(""));
126
127    item1->AppendSeparator();
128    item1->Append(ID_MENU_FILE_CONFIGURE, wxT("Conf&igure CaWE..."), wxT("") );
129    item1->Append(ID_MENU_FILE_EXIT, wxT("E&xit"), wxT("") );
130    m_FileHistory.Load(*wxConfigBase::Get());
131    m_FileHistory.UseMenu(item1);
132    m_FileHistory.AddFilesToMenu(item1);
133    item0->Append(item1, wxT("&File") );
134
135    wxMenu* item2 = new wxMenu;
136    item2->Append(ID_MENU_HELP_CONTENTS, wxT("&CaWE Help\tF1"), wxT("") );
137    item2->AppendSeparator();
138    item2->Append(ID_MENU_HELP_CAFU_WEBSITE, wxT("Cafu &Website"), wxT("") );
139    item2->Append(ID_MENU_HELP_CAFU_FORUM, wxT("Cafu &Forum"), wxT("") );
140    item2->AppendSeparator();
141    if (wxConfigBase::Get()->Read("General/Activate Hidden", 0L)==0x1978)
142    {
143        // "Secret, hidden" CaWE functions...
144        item2->Append(ID_MENU_HELP_SET_FRAME_SIZE, wxT("Set frame size..."), wxT("Set the size of the parent frame. Useful for taking screen-shots.") );
145        item2->Append(ID_MENU_HELP_D3_MTR_CONVERTER, wxT("Doom3 materials converter"), wxT("") );
146        item2->AppendSeparator();
147    }
148    item2->Append(ID_MENU_HELP_ABOUT, wxT("&About..."), wxT("") );
149    item0->Append(item2, wxT("&Help") );
150
151    SetMenuBar(item0);      // According to the documentation of wxFrame::SetMenuBar(), this generates a call to OnSize()...
152
153
154#ifdef __WXMSW__
155#if 0
156    wxIconBundle Icons;
157
158    Icons.AddIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE, 48, 48));
159    Icons.AddIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE, 32, 32));
160    Icons.AddIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE, 16, 16));
161
162    SetIcons(Icons);
163#else
164    SetIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE));
165#endif
166#endif
167
168
169    // Create the parent GL canvas and a GL context.
170    m_GLCanvas =new wxGLCanvas(this, -1, OpenGLAttributeList, wxPoint(600, 5), wxSize(10, 10), 0, "ParentGLCanvas");
171    m_GLContext=new wxGLContext(m_GLCanvas);
172
173    Maximize();     // Under wxGTK, the wxMAXIMIZE frame style does not seem to suffice...
174    Show();         // Without this, the parent frame is not shown...
175
176#ifdef __WXMSW__
177    // ARGH! See my message "wx 2.9.0, Showing parent frame during app init" to wx-users
178    // at http://article.gmane.org/gmane.comp.lib.wxwindows.general/68490 for details.
179    wxShowEvent SE(0, true);
180    OnShow(SE);
181#endif
182}
183
184
185ParentFrameT::~ParentFrameT()
186{
187    m_FileHistory.Save(*wxConfigBase::Get());
188
189    // Release the resources in the game configs before releasing the material system below.
190    Options.DeleteGameConfigs();
191
192    // Release the Cafu Material System.
193    if (MatSys::TextureMapManager!=NULL)
194    {
195        MatSys::TextureMapManager->FreeTextureMap(m_WhiteTexture);
196        MatSys::TextureMapManager=NULL;
197    }
198
199    if (MatSys::Renderer!=NULL)
200    {
201        MatSys::Renderer->Release();
202        MatSys::Renderer=NULL;
203    }
204
205    if (m_RendererDLL!=NULL)
206    {
207        FreeLibrary(m_RendererDLL);
208        m_RendererDLL=NULL;
209    }
210}
211
212
213ChildFrameT* ParentFrameT::GetActiveMapChildFrame() const
214{
215    wxMDIChildFrame* ActiveChildFrame=GetActiveChild();
216    if (ActiveChildFrame==NULL) return NULL;
217
218    ChildFrameT* ActiveChildFrameCast=dynamic_cast<ChildFrameT*>(ActiveChildFrame);
219
220    return ActiveChildFrameCast;
221}
222
223
224MapDocumentT* ParentFrameT::GetActiveMapDoc() const
225{
226    ChildFrameT* ACF=GetActiveMapChildFrame();
227
228    if (ACF==NULL) return NULL;
229
230    return ACF->GetDoc();
231}
232
233
234/**********************/
235/*** Event Handlers ***/
236/**********************/
237
238#ifdef __WXGTK__
239// This is the default behaviour under WXMSW, but apparently not under WXGTK...
240void ParentFrameT::OnSize(wxSizeEvent& SE)
241{
242    if (GetClientWindow())
243    {
244        GetClientWindow()->SetSize(GetClientSize());
245    }
246}
247#endif
248
249
250void ParentFrameT::OnShow(wxShowEvent& SE)
251{
252    if (SE.IsShown() && m_RendererDLL==NULL)
253    {
254        // Initialize the Material System.
255        // This code is in this place due to a few peculiarities of OpenGL under GTK that do not exist under MSW:
256        //   - First, an OpenGL context can only be made current with a canvas that is shown on the screen.
257        //   - Second, calling Show() in the ctor above doesn't show the frame immediately - that requires
258        //     getting back to the main event loop first.
259        // Consequently, the first and best opportunity for initializing the MatSys is here.
260        wxASSERT(m_GLCanvas->IsShownOnScreen());
261
262        // If this was in the ctor, it would trigger an assertion in debug build and yield an invalid (unusable)
263        // OpenGL context in release builds (the GL code in the MatSys::Renderer->IsSupported() methods would fail).
264        m_GLCanvas->SetCurrent(*m_GLContext);
265
266
267        // Obtain the specified MatSys renderer (or if none is specified, automatically find the "best").
268        const wxString RendererName=wxConfigBase::Get()->Read("Global/Renderer", "").Trim();
269
270        if (RendererName!="" && !RendererName.StartsWith("#"))
271            MatSys::Renderer=PlatformAux::GetRenderer(std::string(RendererName), m_RendererDLL);
272
273        if (MatSys::Renderer==NULL || m_RendererDLL==NULL)
274            MatSys::Renderer=PlatformAux::GetBestRenderer(m_RendererDLL);
275
276        if (MatSys::Renderer==NULL || m_RendererDLL==NULL)
277        {
278            wxMessageBox("Could not find a renderer that is supported on your system.", "Material System Error", wxOK | wxICON_ERROR);
279            Destroy();
280            return;
281        }
282
283        MatSys::Renderer->Initialize();
284
285
286        // Obtain the texture manager for the previously loaded renderer.
287        MatSys::TextureMapManager=PlatformAux::GetTextureMapManager(m_RendererDLL);
288
289        if (MatSys::TextureMapManager==NULL)
290        {
291            wxMessageBox("No TextureMapManager obtained.", "ERROR");
292            Destroy();
293            return;
294        }
295
296
297        // Create a very simple lightmap for the materials that need one, and register it with the renderer.
298        char Data[]={ 255, 255, 255, 255, 255, 255, 0, 0,
299                      255, 255, 255, 255, 255, 255, 0, 0 };
300
301        m_WhiteTexture=MatSys::TextureMapManager->GetTextureMap2D(Data, 2, 2, 3, true, MapCompositionT(MapCompositionT::Linear, MapCompositionT::Linear));
302
303        MatSys::Renderer->SetCurrentLightMap(m_WhiteTexture);
304        MatSys::Renderer->SetCurrentLightDirMap(NULL);  // The MatSys provides a default for LightDirMaps when NULL is set.
305
306
307        // The files specified at the command line can only be loaded after both
308        //   - our m_ParentFrame member is set, i.e. the ParentFrameT() ctor returned,
309        //   - and the MatSys is inited, i.e. ParentFrameT::OnShow() has been called.
310        // Due to the different times things happen on Windows vs. Linux, putting this
311        // as an event into the command queue seems to be the only way to get it right.
312        GetEventHandler()->QueueEvent(new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, ID_MENU_FILE_OPEN_CMDLINE));
313    }
314}
315
316
317void ParentFrameT::OnClose(wxCloseEvent& CE)
318{
319    if (!CE.CanVeto())
320    {
321        Destroy();
322        return;
323    }
324
325
326    // Have to work with a copy of the list, because child frames remove themselves from the original list
327    // when they are successfully closed during the following process.
328    ArrayT<ChildFrameT*> ChildFrames=m_ChildFrames;
329
330    for (unsigned long ChildNr=0; ChildNr<ChildFrames.Size(); ChildNr++)
331        if (!ChildFrames[ChildNr]->Close())
332        {
333            // If the child was not closed, e.g. because the user vetoed against it,
334            // veto in turn against the close of the parent frame and stop trying to close the remaining children.
335            CE.Veto();
336            return;
337        }
338
339
340    // See comment above.
341    ArrayT<ModelEditor::ChildFrameT*> MdlChildFrames=m_MdlChildFrames;
342
343    for (unsigned long ChildNr=0; ChildNr<MdlChildFrames.Size(); ChildNr++)
344        if (!MdlChildFrames[ChildNr]->Close())
345        {
346            // If the child was not closed, e.g. because the user vetoed against it,
347            // veto in turn against the close of the parent frame and stop trying to close the remaining children.
348            CE.Veto();
349            return;
350        }
351
352
353    // See comment above.
354    ArrayT<GuiEditor::ChildFrameT*> GuiChildFrames=m_GuiChildFrames;
355
356    for (unsigned long ChildNr=0; ChildNr<GuiChildFrames.Size(); ChildNr++)
357        if (!GuiChildFrames[ChildNr]->Close())
358        {
359            // If the child was not closed, e.g. because the user vetoed against it,
360            // veto in turn against the close of the parent frame and stop trying to close the remaining children.
361            CE.Veto();
362            return;
363        }
364
365
366    // All child frames were successfully closed.
367    // It's safe now to also close this parent window, which will gracefully exit the application.
368    Destroy();
369}
370
371
372GameConfigT* ParentFrameT::AskUserForGameConfig(const wxFileName& DocumentPath) const
373{
374    if (Options.GameConfigs.Size()==0)
375    {
376        wxMessageBox("I scanned the Games subdirectory for game configurations,\n"
377                     "but didn't find any.\n"
378                     "You need to have at least one game configuration inside your\n"
379                     "Games directory for the editor to work.\n"
380                     "Please re-install Cafu or contact the Cafu forums for help.", "No game configurations found", wxOK | wxICON_ERROR);
381
382        return NULL;
383    }
384
385
386    // If there is only exactly one game configuration, take it.
387    if (Options.GameConfigs.Size()==1) return Options.GameConfigs[0];
388
389
390    // If there is more than one game configuration, try to choose one based on the DocumentPath.
391    ArrayT<GameConfigT*> Candidates;
392
393    for (unsigned int i=0; i+1<DocumentPath.GetDirCount(); i++)
394    {
395        if (DocumentPath.GetDirs()[i]=="Games")
396        {
397            for (unsigned long CfgNr=0; CfgNr<Options.GameConfigs.Size(); CfgNr++)
398            {
399                if (DocumentPath.GetDirs()[i+1]==Options.GameConfigs[CfgNr]->Name)
400                    Candidates.PushBack(Options.GameConfigs[CfgNr]);
401            }
402        }
403    }
404
405    // Only if there is exactly one candidate can we unambiguously pick one.
406    if (Candidates.Size()==1) return Candidates[0];
407
408
409    // If there is more than one game configuration, prompt the user to select one.
410    ArrayT<wxString> Choices;
411
412    for (unsigned long CfgNr=0; CfgNr<Options.GameConfigs.Size(); CfgNr++)
413        Choices.PushBack(wxString(Options.GameConfigs[CfgNr]->Name));
414
415    const int SelectionNr=wxGetSingleChoiceIndex("Available configurations:", wxString("Please select a game for ")+DocumentPath.GetFullName(), Choices.Size(), &Choices[0]);
416
417    if (SelectionNr==-1) return NULL;
418    return Options.GameConfigs[SelectionNr];
419}
420
421
422namespace
423{
424    /// This class is a wrapper around a wxProgressDialog.
425    /// It is necessary because the wxProgressDialog has several bugs, see code below for details.
426    class ProgDlgT
427    {
428        public:
429
430        ProgDlgT(wxWindow* Parent, const wxString& Msg)
431            : m_ProgDlg(NULL),
432              m_BusyInfo(NULL)
433        {
434            #if defined(__WXGTK__)
435                // Under wxGTK, the wxProgressDialog cannot be used at all, see http://trac.wxwidgets.org/ticket/12500 for details.
436                // To make matters worse, the wxBusyCursor and the wxBusyInfo don't work as well:
437                //   - wxBusyCursor: http://trac.wxwidgets.org/ticket/13390
438                //   - wxBusyInfo:   http://trac.wxwidgets.org/ticket/13262
439            #elif defined(__WXMSW__)
440                /// Under wxMSW, the wxProgressDialog sometimes hangs when a 32-bit program is run under WOW64,
441                /// see http://trac.cafu.de/ticket/47 for details.
442                if (sizeof(void*)==4 && wxIsPlatform64Bit())
443                    m_BusyInfo=new wxBusyInfo(Msg);
444                else
445                    m_ProgDlg=new wxProgressDialog(Msg, "Almost done...", 100, Parent, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_SMOOTH);
446            #else
447                m_ProgDlg=new wxProgressDialog(Msg, "Almost done...", 100, Parent, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_SMOOTH);
448            #endif
449        }
450
451        ~ProgDlgT()
452        {
453            delete m_ProgDlg;
454            delete m_BusyInfo;
455        }
456
457        wxProgressDialog* GetPtr()
458        {
459            return m_ProgDlg;
460        }
461
462
463        private:
464
465        ProgDlgT(const ProgDlgT&);          // Use of the Copy Constructor    is not allowed.
466        void operator = (const ProgDlgT&);  // Use of the Assignment Operator is not allowed.
467
468        wxProgressDialog* m_ProgDlg;
469        wxBusyInfo*       m_BusyInfo;
470    };
471}
472
473
474wxMDIChildFrame* ParentFrameT::OpenFile(GameConfigT* GameConfig, wxString FileName)
475{
476    static const char*         SPECIFIER_LIST_C[]={ "D3", "HL1", "HL2" };
477    static const wxArrayString SPECIFIER_LIST(3, SPECIFIER_LIST_C);
478
479    // Don't attempt to open the file if the game config is not given.
480    if (GameConfig==NULL) return NULL;
481
482    // Separate the filename and the disambiguation specifier.
483    wxString Specifier="";
484
485    for (size_t i=0; i<SPECIFIER_LIST.GetCount(); i++)
486    {
487        wxString Rest;
488
489        if (FileName.EndsWith(" ("+SPECIFIER_LIST[i]+")", &Rest))
490        {
491            FileName =Rest;
492            Specifier=SPECIFIER_LIST[i];
493            break;
494        }
495    }
496
497    // Load the specified file.
498    try
499    {
500        if (FileName.EndsWith(".cmap"))
501        {
502            ProgDlgT ProgDlg(this, "Loading Cafu Map File");
503
504            return new ChildFrameT(this, FileName, new MapDocumentT(GameConfig, ProgDlg.GetPtr(), FileName));
505        }
506
507        if (FileName.EndsWith(".cmdl"))
508        {
509            wxBusyInfo BusyInfo("Loading...");
510
511            return new ModelEditor::ChildFrameT(this, FileName, new ModelEditor::ModelDocumentT(GameConfig, FileName));
512        }
513
514        if (FileName.EndsWith(".cgui"))
515        {
516            if (FileName.EndsWith("_init.cgui"))
517            {
518                wxBusyInfo BusyInfo("Loading...");
519
520                return new GuiEditor::ChildFrameT(this, FileName, new GuiEditor::GuiDocumentT(GameConfig, FileName));
521            }
522
523            wxMessageBox("In order to load this GUI, please open the related file whose name ends with _init.cgui\n"
524                         "(instead of "+FileName+").\n\n"
525                         "CaWE always deals with the *_init.cgui files, everything else is for your customizations (hand-written script code).\n"
526                         "This way the files never overwrite each other.", "*_init.gui file expected");
527            return NULL;
528        }
529
530        if (FileName.EndsWith(".map"))
531        {
532            if (Specifier=="")
533            {
534                const int Choice=wxGetSingleChoiceIndex("Please select this map files source format:", "Import map file", SPECIFIER_LIST);
535
536                if (Choice<0) return NULL;  // The user pressed Cancel.
537                Specifier=SPECIFIER_LIST[Choice];
538            }
539
540            ProgDlgT ProgDlg(this, "Importing "+Specifier+" map file");
541
542            if (Specifier=="HL1")
543            {
544                return new ChildFrameT(this, FileName, MapDocumentT::ImportHalfLife1Map(GameConfig, ProgDlg.GetPtr(), FileName));
545            }
546
547            if (Specifier=="D3")
548            {
549                return new ChildFrameT(this, FileName, MapDocumentT::ImportDoom3Map(GameConfig, ProgDlg.GetPtr(), FileName));
550            }
551        }
552
553        if (FileName.EndsWith(".vmf"))
554        {
555            ProgDlgT ProgDlg(this, "Importing HL2 vmf file");
556
557            return new ChildFrameT(this, FileName, MapDocumentT::ImportHalfLife2Vmf(GameConfig, ProgDlg.GetPtr(), FileName));
558        }
559
560        // We've tried all known filename suffixes. Now assume that FileName specifies a model file and try that.
561        wxBusyInfo BusyInfo("Loading...");
562
563        return new ModelEditor::ChildFrameT(this, FileName, new ModelEditor::ModelDocumentT(GameConfig, FileName));
564    }
565    catch (const MapDocumentT::LoadErrorT& /*E*/)
566    {
567        wxMessageBox(wxString("The map file \"")+FileName+"\" could not be loaded!", "Couldn't load the map");
568    }
569    catch (const ModelLoaderT::LoadErrorT& LE)
570    {
571        wxMessageBox(wxString("The model file \"")+FileName+"\" could not be loaded:\n"+LE.what(), "Couldn't load or import model");
572    }
573    catch (const cf::GuiSys::GuiImplT::InitErrorT& IE)
574    {
575        wxMessageBox(wxString("The GUI script \"")+FileName+"\" could not be loaded:\n"+IE.what(), "Couldn't load GUI script");
576    }
577
578    return NULL;
579}
580
581
582void ParentFrameT::OnMenuFile(wxCommandEvent& CE)
583{
584    wxASSERT(m_RendererDLL!=NULL && MatSys::Renderer!=NULL);
585
586    switch (CE.GetId())
587    {
588        case ID_MENU_FILE_NEW_MAP:
589        {
590            GameConfigT* GameConfig=AskUserForGameConfig(wxFileName("the new map"));
591            if (GameConfig==NULL) break;
592
593            // Create the new, empty document.
594            MapDocumentT* NewDocument=new MapDocumentT(GameConfig);
595
596            // Create the child frame and give the NewDocument to it (the child frame becomes the owner of the NewDocument).
597            new ChildFrameT(this, "New Document", NewDocument);
598            break;
599        }
600
601        case ID_MENU_FILE_NEW_MODEL:
602        {
603            wxFileDialog FileDialog(this, "Open File", "", "", "Model files (*.*)|*.*", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
604
605            if (FileDialog.ShowModal()!=wxID_OK) break;
606
607            GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileDialog.GetPath()));
608            wxString     FileName  =FileDialog.GetPath();
609
610            if (OpenFile(GameConfig, FileName))
611            {
612                // The file was successfully opened, now add it to the MRU list.
613                m_FileHistory.AddFileToHistory(FileName);
614            }
615            break;
616        }
617
618        case ID_MENU_FILE_NEW_GUI:
619        {
620            GameConfigT* GameConfig=AskUserForGameConfig(wxFileName("the new gui"));
621            if (GameConfig==NULL) break;
622
623            try
624            {
625                new GuiEditor::ChildFrameT(this, "New GUI", new GuiEditor::GuiDocumentT(GameConfig));
626            }
627            catch (const cf::GuiSys::GuiImplT::InitErrorT& /*E*/)
628            {
629                wxMessageBox(wxString("An error occured during GUI creation, no GUI has been created!", "Couldn't create GUI"));
630            }
631            break;
632        }
633
634        case ID_MENU_FILE_NEW_FONT:
635        {
636            FontWizardT* FontWizard=new FontWizardT(this);
637
638            FontWizard->Run();
639            FontWizard->Destroy();
640            break;
641        }
642
643        case ID_MENU_FILE_OPEN:
644        {
645            wxFileDialog FileDialog(this,               // The window parent.
646                                    "Open File",        // Message.
647                                    "",                 // The default directory.
648                                    "",                 // The default file name.
649                                    "All files (*.*)|*.*"       // The wildcard.
650                                    "|Map files|*.cmap;*.map;*.vmf"
651                                    "|Model files|*.*"
652                                    "|GUI files|*.cgui"
653                                    "|-----------------|*.*"
654                                    "|Cafu map (*.cmap)|*.cmap"
655                                    "|Doom3 map (*.map)|*.map"
656                                    "|Hammer 3.x (HL1) map (*.map)|*.map"
657                                    "|Hammer 4.x (HL2) map (*.vmf)|*.vmf",
658                                    wxFD_OPEN | wxFD_FILE_MUST_EXIST);
659
660            if (FileDialog.ShowModal()!=wxID_OK) break;
661
662            GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileDialog.GetPath()));
663            wxString     FileName  =FileDialog.GetPath();
664
665            switch (FileDialog.GetFilterIndex())
666            {
667                case 6: FileName+=" (D3)"; break;
668                case 7: FileName+=" (HL1)"; break;
669                case 8: FileName+=" (HL2)"; break;
670            }
671
672            if (OpenFile(GameConfig, FileName))
673            {
674                // The file was successfully opened, now add it to the MRU list (with the Specifier, if present).
675                // We do this both for "native" Cafu files as well as for "foreign" imported files - it's useful either way.
676                m_FileHistory.AddFileToHistory(FileName);
677            }
678            break;
679        }
680
681        case ID_MENU_FILE_OPEN_CMDLINE:
682        {
683            // Open the files specified at the command line.
684            for (size_t ParamNr=0; ParamNr<m_CmdLineParser.GetParamCount(); ParamNr++)
685            {
686                wxString FileName=m_CmdLineParser.GetParam(ParamNr);
687
688                if (!wxFileExists(FileName))
689                {
690                    wxMessageBox("File not found\n\n" + FileName, "Open File", wxOK | wxICON_ERROR);
691                    continue;
692                }
693
694                GameConfigT*     GameConfig=AskUserForGameConfig(wxFileName(FileName));
695                wxMDIChildFrame* ChildFrame=OpenFile(GameConfig, FileName);
696
697                if (ChildFrame)
698                {
699                    // The file was successfully opened, now add it to the MRU list (with the Specifier, if present).
700                    // We do this both for "native" Cafu files as well as for "foreign" imported files - it's useful either way.
701                    m_FileHistory.AddFileToHistory(FileName);
702                }
703            }
704
705            break;
706        }
707
708        case ID_MENU_FILE_CONFIGURE:
709        {
710            OptionsDialogT OptionsDialog(this);
711
712            if (OptionsDialog.ShowModal()==wxID_OK) Options.Write();
713            break;
714        }
715
716        case ID_MENU_FILE_EXIT:
717        {
718            // Close() generates a EVT_CLOSE event which is handled by our OnClose() handler.
719            // See wx Window Deletion Overview for more details.
720            Close();
721            break;
722        }
723
724        default:
725        {
726            if (CE.GetId()>=wxID_FILE1 && CE.GetId()<=wxID_FILE9)
727            {
728                // Handle a selection from the file history (MRU list).
729                wxString     FileName  =m_FileHistory.GetHistoryFile(CE.GetId()-wxID_FILE1);
730                GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileName));
731
732                // If the user cancelled the "select game configuration" dialog,
733                // don't remove the file from the file history.
734                if (!GameConfig) break;
735
736                // We remove the file from the file history beforehand.
737                // It is added back below when it could be successfully opened, and left out otherwise.
738                m_FileHistory.RemoveFileFromHistory(CE.GetId()-wxID_FILE1);
739
740                if (OpenFile(GameConfig, FileName))
741                {
742                    // The file was successfully opened, now add it back to the MRU list.
743                    m_FileHistory.AddFileToHistory(FileName);
744                }
745            }
746
747            break;
748        }
749    }
750}
751
752
753/// Skips a block in TP, e.g. a { ... } or ( ... ) block. The block can contain nested blocks.
754/// It can (and must) be stated if the caller has already read the opening token.
755static void SkipBlock(TextParserT& TP, const std::string& OpeningToken, const std::string& ClosingToken, bool CallerAlreadyReadOpeningToken)
756{
757    if (!CallerAlreadyReadOpeningToken) TP.GetNextToken();
758
759    unsigned long NestedLevel=1;
760
761    while (true)
762    {
763        const std::string Token=TP.GetNextToken();
764
765        if (Token==OpeningToken)
766        {
767            NestedLevel++;
768        }
769        else if (Token==ClosingToken)
770        {
771            NestedLevel--;
772            if (NestedLevel==0) break;
773        }
774    }
775}
776
777
778/// Replaces the "textures/" prefix of Doom3 texture image paths with "texturesD3/",
779/// and "models/" with "modelsD3/". For all other texture names, simply "D3/" is prepended.
780static wxString ReplaceTexturePrefix(const wxString& D3TexPath)
781{
782    if (D3TexPath.StartsWith("textures/") || D3TexPath.StartsWith("textures\\"))
783    {
784        wxString FixedString=D3TexPath;
785        FixedString.insert(8, "D3");
786        return FixedString;
787    }
788    else if (D3TexPath.StartsWith("models/") || D3TexPath.StartsWith("models\\"))
789    {
790        wxString FixedString=D3TexPath;
791        FixedString.insert(6, "D3");
792        return FixedString;
793    }
794    else
795    {
796        // There are countless other prefixes, and a few apparently built-in "textures":
797        // _scratch _fog _cubicLight _spotlight _pointLight1,2,3 _black _quadratic _default _flat
798        return wxString("D3/")+D3TexPath;
799    }
800}
801
802
803static wxString ParseD3MapComposition(TextParserT& TP)
804{
805    std::string Token=TP.GetNextToken();
806
807    if (Token=="addnormals")
808    {
809        TP.AssertAndSkipToken("(");
810        wxString NMap1=ParseD3MapComposition(TP);
811        TP.AssertAndSkipToken(",");
812        wxString NMap2=ParseD3MapComposition(TP);
813        TP.AssertAndSkipToken(")");
814
815        return wxString("combineNMs(")+NMap1+", "+NMap2+")";
816    }
817    else if (Token=="heightmap")
818    {
819        TP.AssertAndSkipToken("(");
820        wxString HeightMap=ParseD3MapComposition(TP);
821        TP.AssertAndSkipToken(",");
822        wxString Scale=TP.GetNextToken().c_str();
823        TP.AssertAndSkipToken(")");
824
825        return wxString("hm2nm(")+HeightMap+", "+Scale+")";
826    }
827    else if (Token=="makealpha")
828    {
829        TP.AssertAndSkipToken("(");
830        wxString Map=ParseD3MapComposition(TP);
831        TP.AssertAndSkipToken(")");
832
833        return wxString("blue2alpha(")+Map+")";
834    }
835    else if (Token=="makeintensity")
836    {
837        TP.AssertAndSkipToken("(");
838        wxString Map=ParseD3MapComposition(TP);
839        TP.AssertAndSkipToken(")");
840
841        // Don't know exactly what makeintensity means (for a normal texture).
842        return Map;
843    }
844    else
845    {
846        // It's a texture name.
847        wxString TexName=ReplaceTexturePrefix(Token.c_str());
848
849        if (TexName.Right(4)!=".tga") TexName+=".tga";
850
851        return TexName;
852    }
853}
854
855
856static wxString ParseD3RenderStage(TextParserT& TP, char& MapID)
857{
858    MapID=0;
859    wxString TexName="";
860
861    while (true)
862    {
863        std::string Token=TP.GetNextToken();
864
865        if (Token=="}")
866        {
867            return TexName;
868        }
869        else if (Token=="{")
870        {
871            SkipBlock(TP, "{", "}", true);
872        }
873        else if (Token=="if")
874        {
875            if (TP.PeekNextToken()=="(") SkipBlock(TP, "(", ")", false);
876                                    else TP.GetNextToken();
877        }
878        else if (Token=="blend")
879        {
880            wxString MapToken=wxString(TP.GetNextToken().c_str()).Lower();
881
882                 if (MapToken=="diffusemap" ) MapID='d';
883            else if (MapToken=="bumpmap"    ) MapID='b';
884            else if (MapToken=="specularmap") MapID='s';
885            else                              MapID='c';    // Custom blend mode.
886        }
887        else if (Token=="map")
888        {
889            TexName=ParseD3MapComposition(TP);
890            if (MapID==0) MapID='c';
891        }
892    }
893}
894
895
896void ParentFrameT::OnMenuHelp(wxCommandEvent& CE)
897{
898    wxASSERT(m_RendererDLL!=NULL && MatSys::Renderer!=NULL);
899
900    switch (CE.GetId())
901    {
902        case ID_MENU_HELP_CONTENTS:
903            if (!wxLaunchDefaultBrowser("http://www.cafu.de/wiki"))
904                wxMessageBox("Sorry, I could not open www.cafu.de/wiki in your default browser automatically.");
905            break;
906
907        case ID_MENU_HELP_CAFU_WEBSITE:
908            if (!wxLaunchDefaultBrowser("http://www.cafu.de"))
909                wxMessageBox("Sorry, I could not open www.cafu.de in your default browser automatically.");
910            break;
911
912        case ID_MENU_HELP_CAFU_FORUM:
913            if (!wxLaunchDefaultBrowser("http://www.cafu.de/forum"))
914                wxMessageBox("Sorry, I could not open www.cafu.de/forum in your default browser automatically.");
915            break;
916
917        case ID_MENU_HELP_SET_FRAME_SIZE:
918        {
919            const long w=wxGetNumberFromUser("Enter the new width for the parent frame, 0 for no change.",  "new width:",  "Set frame width",  1024, 20, 4096, this);
920            const long h=wxGetNumberFromUser("Enter the new height for the parent frame, 0 for no change.", "new height:", "Set frame height",  768, 20, 4096, this);
921
922            if (w>=20 || h>=20)
923                SetSize(w, h);
924            break;
925        }
926
927        case ID_MENU_HELP_D3_MTR_CONVERTER:
928        {
929            /// This method converts Doom3 *.mtr materials to Cafu *.cmat materials.
930            /// The source and destination folders can be chosen.
931            /// The file names are kept, just the extention is replaced (e.g. "senetemp.mtr" in the source folder becomes "senetemp.cmat" in the dest folder).
932            /// For a Doom3 *.mtr material name of the form "textures/xy/z", the Cafu *.cmat material name "D3conv/textures/xy/z" is generated,
933            /// i.e. "D3conv/" is prepended.
934            /// For a Doom3 texture name of the form "textures/xy/z.tga", the texture name "texturesD3/xy/z.tga" is written into the Cafu *.cmat file,
935            /// i.e. "D3" is inserted.
936            wxString SourceMtrDirName=wxDirSelector("Please choose the folder with the Doom3 *.mtr source material scripts."); if (SourceMtrDirName=="") break;
937            wxString DestCmatDirName =wxDirSelector("Please choose the folder for the Cafu *.cmat destination material scripts."); if (DestCmatDirName=="") break;
938
939            // This sets the cursor to the busy cursor in its ctor, and back to the default cursor in the dtor.
940            wxBusyCursor BusyCursor;
941
942            wxDir SourceMtrDir(SourceMtrDirName);
943            if (!SourceMtrDir.IsOpened()) break;
944
945            unsigned long CountMaterialFiles            =0;
946            unsigned long CountMaterialDefs             =0;
947            unsigned long CountMatsWithNoDiffuseMap     =0;
948            unsigned long CountMatsThatStartWithTextures=0;
949
950            wxString MtrName;
951            for (bool more=SourceMtrDir.GetFirst(&MtrName, "*.mtr", wxDIR_FILES); more; more=SourceMtrDir.GetNext(&MtrName))
952            {
953                wxString MtrPathName =SourceMtrDirName+"/"+MtrName;
954                wxString CMatName    =MtrName; CMatName.replace(MtrName.length()-3, 3, "cmat");
955                wxString CMatPathName=DestCmatDirName+"/"+CMatName;
956
957                TextParserT TP(MtrPathName.c_str(), "({,})");
958                std::ofstream CMatFile(CMatPathName.fn_str());
959                CMatFile << "// Cafu material definition file.\n";
960                CMatFile << "// Created by the CaWE built-in D3 mtr converter.\n\n";
961                CountMaterialFiles++;
962
963                try
964                {
965                    while (!TP.IsAtEOF())
966                    {
967                        if (TP.PeekNextToken()=="table")
968                        {
969                            // Overread the table.
970                            TP.AssertAndSkipToken("table");
971                            TP.GetNextToken();  // Table name.
972                            SkipBlock(TP, "{", "}", false);
973                        }
974                        else if (TP.PeekNextToken()=="/*"  ) SkipBlock(TP, "/*",     "*/", false);
975                        else if (TP.PeekNextToken()=="/**" ) SkipBlock(TP, "/**",   "**/", false);
976                        else if (TP.PeekNextToken()=="/***") SkipBlock(TP, "/***", "***/", false);
977                        else if (TP.PeekNextToken()=="skin")
978                        {
979                            TP.GetNextToken();
980                            TP.GetNextToken();
981                            SkipBlock(TP, "{", "}", false);
982                        }
983                        else if (TP.PeekNextToken()=="particle")
984                        {
985                            TP.GetNextToken();
986                            TP.GetNextToken();
987                            SkipBlock(TP, "{", "}", false);
988                        }
989                        else
990                        {
991                            // It's a material definition.
992                            CountMaterialDefs++;
993                            wxString MaterialName=TP.GetNextToken().c_str();
994                            if (MaterialName=="material") MaterialName=TP.GetNextToken().c_str();
995                            if (MaterialName.StartsWith("textures")) CountMatsThatStartWithTextures++;
996
997                            // It seems that some material names in D3 end with ".tga", which they shouldn't.
998                            if (MaterialName.Right(4)==".tga") MaterialName.erase(MaterialName.length()-4);
999
1000                            TP.AssertAndSkipToken("{");
1001
1002                            CMatFile << "D3conv/" << MaterialName.c_str() << "\n";
1003                            CMatFile << "{\n";
1004
1005                            wxString diffuseMap    ="";
1006                            wxString normalMap     ="";
1007                            wxString specularMap   ="";
1008                            wxString customBlendMap="";
1009                            wxString editorImage   ="";
1010
1011                            while (true)
1012                            {
1013                                std::string Token=TP.GetNextToken();
1014
1015                                if (Token=="}")
1016                                {
1017                                    break;
1018                                }
1019                                else if (Token=="/*" ) SkipBlock(TP, "/*",   "*/", true);
1020                                else if (Token=="/**") SkipBlock(TP, "/**", "**/", true);
1021                                else if (Token=="{")
1022                                {
1023                                    // See if this is a stage / renderpass definition of a map that we have not (yet) found by the simple keywords.
1024                                    char     MapID =0;
1025                                    wxString Result=ParseD3RenderStage(TP, MapID);
1026
1027                                    if (Result!="" && MapID!=0)
1028                                    {
1029                                        switch (MapID)
1030                                        {
1031                                            // The "if (...=="")" is there for two purposes:
1032                                            // a) the simple keywords should have priority over the stage definitions, and
1033                                            // b) if there are multiple stage definitions for the same map, always stay with the first.
1034                                            case 'd': if (diffuseMap    =="") diffuseMap    =Result; break;
1035                                            case 'b': if (normalMap     =="") normalMap     =Result; break;
1036                                            case 's': if (specularMap   =="") specularMap   =Result; break;
1037                                            case 'c': if (customBlendMap=="") customBlendMap=Result; break;
1038                                            default: break;
1039                                        }
1040                                    }
1041                                }
1042                                else if (wxString(Token.c_str()).Lower()=="diffusemap"     ) diffuseMap =ParseD3MapComposition(TP);
1043                                else if (wxString(Token.c_str()).Lower()=="bumpmap"        ) normalMap  =ParseD3MapComposition(TP);
1044                                else if (wxString(Token.c_str()).Lower()=="specularmap"    ) specularMap=ParseD3MapComposition(TP);
1045                                else if (wxString(Token.c_str()).Lower()=="qer_editorimage") editorImage=ParseD3MapComposition(TP);
1046                                else
1047                                {
1048                                    // Ignore everything else.
1049                                }
1050                            }
1051
1052                            if (diffuseMap=="") diffuseMap=editorImage;
1053                            if (diffuseMap=="") diffuseMap=customBlendMap;
1054
1055
1056                            if (diffuseMap=="")
1057                            {
1058                                // wxMessageBox(wxString("No diffusemap found for mtr material\n")+MaterialName+"\nin file\n"+MtrPathName);
1059                                CountMatsWithNoDiffuseMap++;
1060                            }
1061
1062                            if (diffuseMap !="") CMatFile << "    diffusemap  " << diffuseMap .c_str() << "\n";
1063                            if (diffuseMap !="") CMatFile << "    lightmap    " << "$lightmap"         << "\n";
1064                            if (normalMap  !="") CMatFile << "    normalmap   " << normalMap  .c_str() << "\n";
1065                            if (specularMap!="") CMatFile << "    specularmap " << specularMap.c_str() << "\n";
1066
1067                            CMatFile << "}\n\n";
1068                        }
1069                    }
1070                }
1071                catch (const TextParserT::ParseError&)
1072                {
1073                    wxMessageBox(wxString("There was an error parsing the file\n")+MtrPathName+wxString::Format("\nnear byte %lu.", TP.GetReadPosByte()));
1074                }
1075            }
1076
1077            wxMessageBox(wxString("The material conversion is complete.\n")+
1078                         wxString::Format("Converted %lu mtr files to cmat files which contained a total of %lu materials.\n", CountMaterialFiles, CountMaterialDefs)+
1079                         wxString::Format("For %lu materials no diffusemap was found (e.g. video materials etc.).\n", CountMatsWithNoDiffuseMap)+
1080                         wxString::Format("The names of %lu materials start with \"textures/\".\n\n", CountMatsThatStartWithTextures)+
1081                         "ALL materials with a diffusemap also got a \"lightmap $lightmap\" line\n"+
1082                         "(they are intended to be *temporarily* used in *maps*, after all).\n\n"+
1083                         "Note that you must restart CaWE in order to use the new materials.");
1084            break;
1085        }
1086
1087        case ID_MENU_HELP_ABOUT:
1088        {
1089            wxString TextTop=
1090                "CaWE, the Cafu World Editor.\n"
1091                "Copyright (C) 2002-"+wxString(__DATE__+7)+" Carsten Fuchs Software.\n"
1092                "\n"
1093                "Please report all encountered bugs.\n";
1094
1095            wxString TextBottom=
1096                "Build date: "+wxString(__DATE__)+wxString::Format(". CaWE is installed since %lu days.", Options.DaysSinceInstalled)
1097                +wxString("\n\nYour CaWE configuration files are located at:\n")+wxStandardPaths::Get().GetUserDataDir();
1098
1099            wxDialog  AboutDialog(this, -1, "CaWE, the Cafu World Editor - "+wxString(__DATE__), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
1100            wxWindow* parent=&AboutDialog;
1101
1102            // ### BEGIN of code generated by and copied from wxDesigner.
1103            wxBoxSizer *item0 = new wxBoxSizer( wxHORIZONTAL );
1104
1105            wxStaticBitmap *item1 = new wxStaticBitmap( parent, -1, GetIcon() /*Icon of parent frame.*/, wxDefaultPosition, wxDefaultSize );
1106            item0->Add( item1, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
1107
1108            wxBoxSizer *item2 = new wxBoxSizer( wxVERTICAL );
1109
1110            wxStaticText *item3 = new wxStaticText( parent, -1, TextTop, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
1111            item2->Add( item3, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
1112
1113            wxStaticBox *item5 = new wxStaticBox( parent, -1, wxT("External Libraries and Artwork") );
1114            wxStaticBoxSizer *item4 = new wxStaticBoxSizer( item5, wxVERTICAL );
1115
1116            class AutoUrlTextCtrlT : public wxTextCtrl
1117            {
1118                public:
1119
1120                AutoUrlTextCtrlT(wxWindow* Parent, wxWindowID ID, const wxString& Value="", const wxPoint& Pos=wxDefaultPosition, const wxSize& Size=wxDefaultSize, long Style=0)
1121                    : wxTextCtrl(Parent, ID, Value, Pos, Size, Style)
1122                {
1123                    Connect(wxEVT_COMMAND_TEXT_URL, wxTextUrlEventHandler(AutoUrlTextCtrlT::OnAboutUrl));
1124                }
1125
1126                /// Event handler for "text URL" events.
1127                void OnAboutUrl(wxTextUrlEvent& TUE)
1128                {
1129                    const wxMouseEvent& ME=TUE.GetMouseEvent();
1130
1131                    // Filter out mouse moves, too many of them.
1132                    if (ME.Moving()) return;
1133                    if (!ME.LeftDown()) return;
1134
1135                    const int UrlStart=TUE.GetURLStart();
1136                    const int UrlEnd  =TUE.GetURLEnd();
1137
1138                    wxLaunchDefaultBrowser(GetValue().Mid(UrlStart, UrlEnd-UrlStart));
1139                }
1140            };
1141
1142            wxTextCtrl *item6 = new AutoUrlTextCtrlT(parent, -1, wxT(""), wxDefaultPosition, wxSize(400, 200),
1143                wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxTE_AUTO_URL | wxBORDER_SUNKEN);
1144
1145            item6->LoadFile("CaWE/res/AboutExtLibs.txt");           // This is much simpler and more flexible than any string constant.
1146         // item6->SetInsertionPoint(item6->GetLastPosition());     // Scrolls to the bottom of the text.
1147
1148            item4->Add( item6, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
1149
1150            item2->Add( item4, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
1151
1152            wxStaticText *item7 = new wxStaticText( parent, -1, TextBottom, wxDefaultPosition, wxDefaultSize, 0 );
1153            item2->Add( item7, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
1154
1155            wxButton *item8 = new wxButton( parent, wxID_OK, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0 );
1156            item8->SetDefault();
1157            item2->Add( item8, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
1158
1159            item0->Add( item2, 1, wxGROW|wxALIGN_CENTER_HORIZONTAL, 5 );
1160
1161            parent->SetSizer( item0 );
1162            item0->SetSizeHints( parent );
1163            // ### END of code generated by and copied from wxDesigner.
1164
1165            AboutDialog.CenterOnParent();
1166            AboutDialog.ShowModal();
1167            break;
1168        }
1169    }
1170}
Note: See TracBrowser for help on using the browser.