wg_application.cpp

00001 // wg_application.cpp
00002 //
00003 // CApplication interface
00004 //
00005 //
00006 // Copyright (c) 2002-2004 Rob Wiskow
00007 // rob-dev@boxedchaos.com
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00022 //
00023 
00024 
00025 #include "wgui_include_config.h"
00026 #include "wg_application.h"
00027 #include "wg_error.h"
00028 #include "wutil_debug.h"
00029 #include <iostream>
00030 #include <fstream>
00031 
00032 
00033 namespace wGui
00034 {
00035 
00036 // Static members
00037 CApplication* CApplication::m_pInstance = 0;
00038 
00039 
00040 void CApplication::HandleSDLEvent(SDL_Event Event)
00041 {
00042      // this will turn an SDL event into a wGui message
00043      switch (Event.type)
00044      {
00045      case SDL_VIDEORESIZE:
00046           CMessageServer::Instance().QueueMessage(new TPointMessage(
00047                CMessage::CTRL_RESIZE, 0, this, CPoint(Event.resize.w, Event.resize.h)));
00048           break;
00049      case SDL_KEYDOWN:
00050           CMessageServer::Instance().QueueMessage(new CKeyboardMessage(
00051                CMessage::KEYBOARD_KEYDOWN, CApplication::Instance()->GetKeyFocus(), this,
00052                Event.key.keysym.scancode, Event.key.keysym.mod,
00053                Event.key.keysym.sym, Event.key.keysym.unicode));
00054           break;
00055      case SDL_KEYUP:
00056           CMessageServer::Instance().QueueMessage(new CKeyboardMessage(
00057                CMessage::KEYBOARD_KEYUP, CApplication::Instance()->GetKeyFocus(), this,
00058                Event.key.keysym.scancode, Event.key.keysym.mod,
00059                Event.key.keysym.sym, Event.key.keysym.unicode));
00060           break;
00061      case SDL_MOUSEBUTTONDOWN:
00062           CMessageServer::Instance().QueueMessage(new CMouseMessage(
00063                CMessage::MOUSE_BUTTONDOWN, CApplication::Instance()->GetMouseFocus(), this,
00064                CPoint(Event.button.x, Event.button.y), CPoint(),
00065                CMouseMessage::TranslateSDLButton(Event.button.button)));
00066           break;
00067      case SDL_MOUSEBUTTONUP:
00068           CMessageServer::Instance().QueueMessage(new CMouseMessage(
00069                CMessage::MOUSE_BUTTONUP, CApplication::Instance()->GetMouseFocus(), this,
00070                CPoint(Event.button.x, Event.button.y), CPoint(),
00071                CMouseMessage::TranslateSDLButton(Event.button.button)));
00072           break;
00073      case SDL_MOUSEMOTION:
00074           CMessageServer::Instance().QueueMessage(new CMouseMessage(
00075                CMessage::MOUSE_MOVE, CApplication::Instance()->GetMouseFocus(), this,
00076                CPoint(Event.motion.x, Event.motion.y), CPoint(Event.motion.xrel, Event.motion.yrel),
00077                CMouseMessage::TranslateSDLButtonState(Event.motion.state)));
00078           break;
00079      case SDL_QUIT:
00080           CMessageServer::Instance().QueueMessage(new CMessage(CMessage::APP_EXIT, 0, this));
00081           break;
00082      default:
00083           CMessageServer::Instance().QueueMessage(new CSDLMessage(CMessage::SDL, 0, this, Event));
00084           break;
00085      }
00086 }
00087 
00088 
00089 CApplication::CApplication(int argc, char** argv, std::wstring sFontFileName, bool bHandleExceptionsInternally) :
00090      m_argc(argc),
00091      m_argv(argv),
00092      m_sFontFileName(sFontFileName),
00093      m_iExitCode(EXIT_FAILURE),
00094      m_bRunning(false),
00095      m_bInited(false),
00096      m_pKeyFocusWindow(0),
00097      m_pMouseFocusWindow(0),
00098      m_pDefaultFontEngine(0),
00099      m_iBitsPerPixel(DEFAULT_BPP),
00100      m_DefaultBackgroundColor(DEFAULT_BACKGROUND_COLOR),
00101      m_DefaultForegroundColor(DEFAULT_FOREGROUND_COLOR),
00102      m_DefaultSelectionColor(DEFAULT_BACKGROUND_COLOR),
00103      m_bHandleExceptionsInternally(bHandleExceptionsInternally),
00104      m_bResourcePoolEnabled(true),
00105      m_pCurrentCursorResourceHandle(0),
00106      m_pSystemDefaultCursor(0)
00107 {
00108      if (m_pInstance)
00109      {
00110           throw(Wg_Ex_App(L"CApplication::CApplication : An instance of the CApplication already exists."));
00111      }
00112 
00113      m_pInstance = this;
00114 
00115      if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) == -1)
00116      {
00117           throw(Wg_Ex_SDL(L"CApplication::CApplication : Could not initialize SDL: " + stdex::ToWString(SDL_GetError())));
00118      }
00119      //Setting the keyboard repeat rate using the SDL Default rates
00120      if(SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) == -1)
00121      {
00122           throw(Wg_Ex_SDL(L"CApplication::Capplication : Error setting SDL keyboard repeat rate."));
00123      }
00124 
00125      m_pSystemDefaultCursor = SDL_GetCursor();
00126      m_AppLog.AddLogEntry(L"wGui Application starting", APP_LOG_INFO);
00127 
00128      atexit(SDL_Quit);
00129 }
00130 
00131 
00132 CApplication::~CApplication(void)
00133 {
00134      if (m_pInstance == this)
00135      {
00136           m_pInstance = 0;
00137      }
00138 
00139      for(std::map<std::pair<std::wstring, unsigned char>, CFontEngine*>::iterator iter = m_FontEngines.begin(); iter != m_FontEngines.end(); ++iter)
00140      {
00141           delete iter->second;
00142           iter->second = 0;
00143      }
00144 
00145      m_AppLog.AddLogEntry(L"wGui Application closing", APP_LOG_INFO);
00146      m_AppLog.WriteToFile(L"wGui.log", false, std::wstring(L"wGui Application Log (version ") + stdex::ToWString(VERSION) + L")\nSeverity Levels : [1] Critical, [3] Error, [5] Warning, [8] Info");
00147 }
00148 
00149 
00150 void CApplication::SetKeyFocus(CWindow* pWindow)
00151 {
00152      if (m_pKeyFocusWindow != pWindow)
00153      {
00154           // notify the window that's losing focus to repaint itself
00155           if (m_pKeyFocusWindow)
00156           {
00157                CMessageServer::Instance().QueueMessage(new CMessage(CMessage::CTRL_LOSINGKEYFOCUS, m_pKeyFocusWindow, this));
00158           }
00159           m_pKeyFocusWindow = pWindow;
00160           CMessageServer::Instance().QueueMessage(new CMessage(CMessage::CTRL_GAININGKEYFOCUS, m_pKeyFocusWindow, this));
00161      }
00162 }
00163 
00164 
00165 void CApplication::SetMouseFocus(CWindow* pWindow)
00166 {
00167      if (m_pMouseFocusWindow != pWindow)
00168      {
00169           // notify the window that's losing focus to repaint itself
00170           if (m_pMouseFocusWindow)
00171           {
00172                CMessageServer::Instance().QueueMessage(new CMessage(CMessage::CTRL_LOSINGMOUSEFOCUS, m_pMouseFocusWindow, this));
00173           }
00174           m_pMouseFocusWindow = pWindow;
00175           CMessageServer::Instance().QueueMessage(new CMessage(CMessage::CTRL_GAININGMOUSEFOCUS, m_pMouseFocusWindow, this));
00176      }
00177 }
00178 
00179 
00180 
00181 void CApplication::Init(void)
00182 {
00183      CMessageServer::Instance().RegisterMessageClient(this, CMessage::APP_EXIT, CMessageServer::PRIORITY_LAST);
00184      SDL_EnableUNICODE(1);
00185 
00186      // Now we have to find the wGui.conf file and load it into the Global Config
00187      // Our ordered search path is (so the current directory will overwrite any common settings with the other directories):
00188      // <global directory>
00189      // <home directory>
00190      // <current directory>
00191      bool bConfigLoaded = false;
00192 
00193      // global directory first
00194      if (m_GlobalConfig.ReadFromFile(GLOBAL_CONFIG_PATH + L"wgui.conf"))
00195      {
00196           m_AppLog.AddLogEntry(L"wGui Application config read from Global directory.", APP_LOG_INFO);
00197           bConfigLoaded = true;
00198      }
00199 
00200      // now the home directory
00201      if (m_GlobalConfig.ReadFromFile(USER_CONFIG_PATH + L"wgui.conf"))
00202      {
00203           m_AppLog.AddLogEntry(L"wGui Application config read from User directory.", APP_LOG_INFO);
00204           bConfigLoaded = true;
00205      }
00206 
00207      // and finally the current directory
00208      if (m_GlobalConfig.ReadFromFile(L"wgui.conf"))
00209      {
00210           m_AppLog.AddLogEntry(L"wGui Application config read from Local directory.", APP_LOG_INFO);
00211           bConfigLoaded = true;
00212      }
00213 
00214      if (!bConfigLoaded)
00215      {
00216           m_AppLog.AddLogEntry(L"wGui Application config was not found (see Global Config section of the docs).", APP_LOG_WARNING);
00217      }
00218 
00219      if (m_GlobalConfig.EntryExists(L"DEFAULTFONT"))
00220      {
00221           if (m_GlobalConfig.EntryExists(L"DEFAULTFONTSIZE"))
00222           {
00223                m_pDefaultFontEngine = GetFontEngine(m_GlobalConfig.GetStringEntry(L"DEFAULTFONT").second,
00224                     stdex::safe_static_cast<unsigned char>(m_GlobalConfig.GetLongIntEntry(L"DEFAULTFONTSIZE").second));
00225           }
00226           else
00227           {
00228                m_pDefaultFontEngine = GetFontEngine(m_GlobalConfig.GetStringEntry(L"DEFAULTFONT").second);
00229           }
00230      }
00231 
00232      if (!m_pDefaultFontEngine)
00233      {
00234           m_AppLog.AddLogEntry(L"wGui Application was unable to load a default font from the global config.  Check to see that the specified paths are correct.", APP_LOG_WARNING);
00235      }
00236 
00237      if (m_GlobalConfig.EntryExists(L"BACKGROUNDCOLOR"))
00238      {
00239           m_DefaultBackgroundColor = CRGBColor(m_GlobalConfig.GetStringEntry(L"BACKGROUNDCOLOR").second);
00240      }
00241 
00242      if (m_GlobalConfig.EntryExists(L"FOREGROUNDCOLOR"))
00243      {
00244           m_DefaultForegroundColor = CRGBColor(m_GlobalConfig.GetStringEntry(L"FOREGROUNDCOLOR").second);
00245      }
00246 
00247      if (m_GlobalConfig.EntryExists(L"SELECTIONCOLOR"))
00248      {
00249           m_DefaultSelectionColor = CRGBColor(m_GlobalConfig.GetStringEntry(L"SELECTIONCOLOR").second);
00250      }
00251 
00252      m_bInited = true;
00253 }
00254 
00255 
00256 void CApplication::Exec(void)
00257 {
00258      try
00259      {
00260           if (!m_bInited)
00261           {
00262                throw(Wg_Ex_App(L"CApplication::Exec : Application Init() was not called!"));
00263           }
00264 
00265           m_bRunning = true;
00266           SDL_Event event;
00267           CMessageServer::Instance().IgnoreAllNewMessages(false);
00268           CMessageServer::Instance().QueueMessage(new CMessage(CMessage::APP_PAINT, 0, this));
00269           m_AppLog.AddLogEntry(L"wGui Application entering Exec loop", APP_LOG_INFO);
00270           while (m_bRunning)
00271           {
00272                while (SDL_PollEvent(&event))
00273                {
00274                     HandleSDLEvent(event);
00275                }
00276                while (!CMessageServer::Instance().MessageAvailable())
00277                {
00278                     while (SDL_PollEvent(&event))
00279                     {
00280                          HandleSDLEvent(event);
00281                     }
00282                     SDL_Delay(5);
00283                }
00284                try
00285                {
00286                     CMessageServer::Instance().DeliverMessage();
00287                }
00288                catch (Wg_Ex_Base& e)
00289                {
00290                     m_AppLog.AddLogEntry(L"Exception (wGui) : " + e.std_what(), APP_LOG_CRITICAL);
00291                     if (!m_bHandleExceptionsInternally)
00292                     {
00293                          throw;
00294                     }
00295                     // Since we're handling exceptions internally, and it's one of our own exceptions, we're just going to
00296                     // send something to the debug output and then continue processing the message queue
00297                     wUtil::Trace(L"wGui exception while delivering message : " + e.std_what());
00298                }
00299           }
00300      }
00301      catch (Wg_Ex_Base& e)
00302      {
00303           m_AppLog.AddLogEntry(L"Exception (wGui) : " + e.std_what(), APP_LOG_CRITICAL);
00304           m_AppLog.AddLogEntry(L"SDL Last Error = " + stdex::ToWString(SDL_GetError()), APP_LOG_ERROR);
00305           if (!m_bHandleExceptionsInternally)
00306           {
00307                throw;
00308           }
00309           // We are supposed to handle all exceptions internally, but something hasn't been handled at this point, so we need to exit gracefully
00310           wUtil::Trace(L"Unhandled wGui exception : " + e.std_what());
00311      }
00312      catch (std::exception& e)
00313      {
00314           m_AppLog.AddLogEntry(L"Exception (std) : " + stdex::ToWString(e.what()), APP_LOG_CRITICAL);
00315           m_AppLog.AddLogEntry(L"SDL Last Error = " + stdex::ToWString(SDL_GetError()), APP_LOG_ERROR);
00316           if (!m_bHandleExceptionsInternally)
00317           {
00318                throw;
00319           }
00320           // We are supposed to handle all exceptions internally, but something hasn't been handled at this point, so we need to exit gracefully
00321           wUtil::Trace(L"Unhandled std exception : " + stdex::ToWString(e.what()));
00322      }
00323      catch (...)
00324      {
00325           m_AppLog.AddLogEntry(L"Exception (non std)", APP_LOG_CRITICAL);
00326           m_AppLog.AddLogEntry(L"SDL Last Error = " + stdex::ToWString(SDL_GetError()), APP_LOG_ERROR);
00327           if (!m_bHandleExceptionsInternally)
00328           {
00329                throw;
00330           }
00331           // We are supposed to handle all exceptions internally, but something hasn't been handled at this point, so we need to exit gracefully
00332           wUtil::Trace(L"Unhandled exception");
00333      }
00334 }
00335 
00336 
00337 void CApplication::ApplicationExit(int iExitCode)
00338 {
00339      // push an event into the SDL queue so the SDLEventLoopThread can exit
00340      // the actual contents of the event are not a concern as it serves just to trigger the SDL_WaitEvent
00341      SDL_Event user_event;
00342      user_event.type=SDL_USEREVENT;
00343      user_event.user.code=0;
00344      user_event.user.data1=0;
00345      user_event.user.data2=0;
00346      int iResult = SDL_PushEvent(&user_event);
00347      wUtil::TraceIf(iResult == -1, L"CApplication::ApplicationExit - Unable to push SDL user_event.");
00348      m_iExitCode = iExitCode;
00349      m_bRunning = false;
00350 }
00351 
00352 
00353 CFontEngine* CApplication::GetFontEngine(std::wstring sFontFileName, unsigned char iFontSize)
00354 {
00355      // First search to see if the requested font engine already exists
00356      t_FontEngineMap::iterator iterFontEngine = m_FontEngines.find(std::make_pair(sFontFileName, iFontSize));
00357      CFontEngine* pFontEngine = 0;
00358 
00359      if (iterFontEngine == m_FontEngines.end())
00360      {
00361           // Requested font engine doesn't exist, so create one and add it to the map
00362           try
00363           {
00364                if (sFontFileName.find_first_of(L"\\/") != std::string::npos)
00365                {
00366                     // if the Font File name that was passed in includes a path, just check there
00367                     std::wifstream FileTest(stdex::ToMbString(sFontFileName).c_str());
00368                     bool bFileExists = FileTest.is_open();
00369                     FileTest.close();
00370                     if (bFileExists)
00371                     {
00372                          pFontEngine = new CFontEngine(sFontFileName, iFontSize);
00373                     }
00374                }
00375                else
00376                {
00377                     // otherwise check the Font Path from the global config
00378                     std::vector<std::wstring> FontPaths = stdex::DetokenizeString(m_GlobalConfig.GetStringEntry(L"FONTPATH").second, L";");
00379                     for(std::vector<std::wstring>::iterator iter = FontPaths.begin(); iter != FontPaths.end(); ++iter)
00380                     {
00381                          std::wstring sFullPath = *iter;
00382                          if (!iter->empty()  && (*iter)[iter->length()] != L'\\' && (*iter)[iter->length()] != L'/')
00383                          {
00384                               sFullPath += L"/";
00385                          }
00386                          sFullPath += sFontFileName;
00387                          std::wifstream FileTest(stdex::ToMbString(sFullPath).c_str());
00388                          bool bFileExists = FileTest.is_open();
00389                          FileTest.close();
00390                          if (bFileExists)
00391                          {
00392                               pFontEngine = new CFontEngine(sFullPath, iFontSize);
00393                          }
00394                     }
00395                }
00396                if (pFontEngine)
00397                {
00398                     m_FontEngines.insert(std::make_pair(std::make_pair(sFontFileName, iFontSize), pFontEngine));
00399                }
00400           }
00401           catch (Wg_Ex_FreeType& e)
00402           {
00403                m_AppLog.AddLogEntry(L"CApplication::GetFontEngine - Exception thrown while creating Font Engine : " + e.std_what(), APP_LOG_ERROR);
00404                if (!m_bHandleExceptionsInternally)
00405                {
00406                     throw;
00407                }
00408           }
00409      }
00410      else
00411      {
00412           pFontEngine = iterFontEngine->second;
00413      }
00414 
00415      return pFontEngine;
00416 }
00417 
00418 
00419 void CApplication::EnableResourcePool(bool bEnable)
00420 {
00421      m_bResourcePoolEnabled = bEnable;
00422      if (bEnable == false)
00423      {
00424           m_ResourceHandlePool.clear();
00425      }
00426 }
00427 
00428 
00429 bool CApplication::AddToResourcePool(CResourceHandle& ResourceHandle)
00430 {
00431      bool bSuccess = false;
00432 
00433      if (m_bResourcePoolEnabled)
00434      {
00435           m_ResourceHandlePool.push_back(ResourceHandle);
00436           bSuccess = true;
00437      }
00438 
00439      return bSuccess;
00440 }
00441 
00442 
00443 void CApplication::SetMouseCursor(CCursorResourceHandle* pCursorResourceHandle)
00444 {
00445      // The auto pointer is used to make sure that the cursor handle is valid until we're done using the cursor
00446      if (pCursorResourceHandle && pCursorResourceHandle != m_pCurrentCursorResourceHandle.get())
00447      {
00448           std::auto_ptr<CCursorResourceHandle> pNewCursorResourceHandle(new CCursorResourceHandle(*pCursorResourceHandle));
00449           m_pCurrentCursorResourceHandle = pNewCursorResourceHandle;
00450           SDL_SetCursor(m_pCurrentCursorResourceHandle->Cursor());
00451      }
00452      else
00453      {
00454           if( m_pCurrentCursorResourceHandle.get() )
00455           {
00456                #ifdef MSVC6  // VC6's auto pointers are really broken
00457                     delete m_pCurrentCursorResourceHandle.release();
00458                     m_pCurrentCursorResourceHandle = std::auto_ptr<CCursorResourceHandle>(0);
00459                #else
00460                     m_pCurrentCursorResourceHandle.reset(0);
00461                #endif // MSVC6
00462                SDL_SetCursor(m_pSystemDefaultCursor);
00463           }
00464      }
00465 }
00466 
00467 
00468 bool CApplication::HandleMessage(CMessage* pMessage)
00469 {
00470      bool bHandled = false;
00471 
00472      if (pMessage)
00473      {
00474           switch (pMessage->MessageType())
00475           {
00476           case CMessage::APP_EXIT :
00477                ApplicationExit();
00478                bHandled = true;
00479                break;
00480           default:
00481                break;
00482           }
00483      }
00484 
00485      return bHandled;
00486 }
00487 
00488 }

Generated on Wed May 16 23:11:25 2007 for wGui by  doxygen 1.5.1