fs/rap/fsConWin.cpp

Go to the documentation of this file.
00001 /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
00002 /*                          _______      ______    ______          __      */
00003 /*  ~ ~ ~ ~ ~ ~ ~ ~ ~ ~    / ____(_)___ / ___ /)  / ____/___  ____/ /__    */
00004 /*     [fsConWin]         / /_  / / __ \\__ \|/  / /   / __ \/ __  / _ \   */
00005 /*       rev. 4          / __/ / / /_/ /__/ /   / /___/ /_/ / /_/ /  __/   */
00006 /*    8th Feb 2007      /_/   /_/ ,___/____/    \____/\____/\__,_/\___/    */
00007 /*     [x] stable              /_/ (c) 2006-7 Filip STOKLAS (FipS)         */
00008 /*  ~ ~ ~ ~ ~ ~ ~ ~ ~ ~       http://HOLE.4FipS.com/fips_code.php          */
00009 /*                                                                         */
00010 /* This code is free for personal and commercial use. You may redistribute */
00011 /* it by any means. If you use the code for your own projects please give  */
00012 /* me credit. Please send a bug report. Don't alter or remove this header! */
00013 /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
00014 #include <fs/rap/fsConWin.h>
00015 
00016 // >>> COMMON SUPPORT BLOCK (rev. 6)
00017 #if defined(FS_HAS_FSASSERT)
00018 #   include <fs/sys/fsAssert.h>
00019 #else
00020 #   include <cassert>
00021 #   if !defined(FS_ASSERT)
00022 #       define FS_ASSERT(exp) assert(exp)
00023 #   endif
00024 #   if !defined(FS_ASSERT_MSG)
00025 #       define FS_ASSERT_MSG(exp, msg) assert(exp && msg)
00026 #   endif
00027 #   if !defined(FS_VERIFY)
00028 #       define FS_VERIFY(exp) \
00029         { bool bExp = !!(exp); assert(bExp && #exp); bExp; }
00030 #   endif
00031 #   if !defined(FS_VERIFY_MSG)
00032 #       define FS_VERIFY_MSG(exp, msg) \
00033         { bool bExp = !!(exp); assert(bExp && #exp && msg); bExp; }
00034 #   endif
00035 #   if !defined(FS_STATIC_ASSERT)
00036 #       define FS_STATIC_ASSERT(exp) { char error[(exp) ? 1 : 0]; error; }
00037 #   endif
00038 #endif
00039 #if !defined(FS_VERIFY_RETURN)
00040 #   define FS_VERIFY_RETURN(exp) \
00041     if(!(exp)) { FS_ASSERT_MSG(0, #exp "<return>"); return; }
00042 #endif
00043 #if !defined(FS_VERIFY_RETURN_VAL)
00044 #   define FS_VERIFY_RETURN_VAL(exp, ret) \
00045     if(!(exp)) { FS_ASSERT_MSG(0, #exp "<returns:>" #ret); return (ret); }
00046 #endif
00047 // <<< COMMON SUPPORT BLOCK (rev. 6)
00048 
00049 #include <map>
00050 #include <vector>
00051 #include <string>
00052 #include <windows.h>
00053 
00054 // provide detailed memory tracking information
00055 #if defined(FS_HAS_FSMEMMGR)
00056 #   include <fs/sys/fsMemMgr.h>
00057 #endif
00058 //---------------------------------------------------------------------------
00059 using namespace fs::rap;
00060 //---------------------------------------------------------------------------
00061 //--- ANONYMOUS NAMESPACE >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
00062 //---------------------------------------------------------------------------
00063 namespace {
00064 //---------------------------------------------------------------------------
00065 class ConWinImpl_t;
00066 
00067 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wPrm, LPARAM lPrm);
00068 LRESULT CALLBACK InWndProc(HWND hWnd, UINT msg, WPARAM wPrm, LPARAM lPrm);
00069 
00070 // replacement for the Get/SetWindowLongPtr ugly technique
00071 typedef std::pair<HWND, ConWinImpl_t *> THwnd2ConPair;
00072 typedef std::map<HWND, ConWinImpl_t *> THwnd2ConMap;
00073 THwnd2ConMap g_Hwnd2ConMap; 
00074 //---------------------------------------------------------------------------
00075 //--- CLASS ConWinImpl_t >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
00076 //---------------------------------------------------------------------------
00077 class ConWinImpl_t
00078 {
00079  public:
00080  
00081     explicit ConWinImpl_t(const char *pszTitle);
00082     ~ConWinImpl_t();
00083     bool IsValid() const;
00084     
00085     void Show(bool bShow);
00086     void SetPos(int nX, int nY, int nW, int nH);
00087     void Push(const char *pszText, bool bCmd = false);
00088     void RegTxtRcvr(ConWin_t::TxtRcvr_i *pRcvr);
00089     
00090  public: // WndProc
00091  
00092     void OnSize(int nW, int nH);
00093     void OnReturn();
00094     void OnPrevCmd();
00095     void OnNextCmd();
00096     bool IsInWin(HWND hWnd) const;
00097     WNDPROC GetOrigInWndProc() const;
00098                 
00099  private:
00100 
00101     ConWinImpl_t(const ConWinImpl_t &); 
00102     ConWinImpl_t & operator = (const ConWinImpl_t &); 
00103 
00104     HWND InitMainWin();
00105     HFONT InitOutFnt();
00106     HWND InitOutWin(HWND hParWnd);
00107     HWND InitInWin(HWND hParWnd);
00108     void Send(const char *pszText, bool bCmd);
00109     
00110     //---
00111     
00112     bool m_bInited;
00113     HFONT m_hOutFnt;
00114     HWND m_hMainWnd;
00115     HWND m_hOutWnd;
00116     HWND m_hInWnd;
00117     WNDPROC m_pOrigInWndProc;
00118         
00119     typedef std::vector<ConWin_t::TxtRcvr_i *> TTxtRcvrs;
00120     TTxtRcvrs m_TxtRcvrs;
00121     
00122     typedef std::vector<std::string> THist;
00123     THist m_Hist;
00124     int m_nHistPos;
00125 };
00126 //---------------------------------------------------------------------------
00127 ConWinImpl_t::ConWinImpl_t(const char *pszTitle):
00128 m_hOutFnt(0),
00129 m_bInited(false),
00130 m_hMainWnd(0),
00131 m_hOutWnd(0),
00132 m_hInWnd(0),
00133 m_nHistPos(-1),
00134 m_pOrigInWndProc(0)
00135 {
00136     //--- MAIN WINDOW ---
00137     
00138     HWND hMainWnd = InitMainWin();
00139     FS_ASSERT(hMainWnd);
00140     g_Hwnd2ConMap.insert(THwnd2ConPair(hMainWnd, this));
00141     
00142     FS_ASSERT(pszTitle);
00143     ::SetWindowTextA(hMainWnd, pszTitle);
00144     
00145     //--- OUTPUT AREA ---
00146     
00147     HFONT hOutFnt = InitOutFnt();
00148     FS_ASSERT(hOutFnt);
00149         
00150     HWND hOutWnd = InitOutWin(hMainWnd);
00151     FS_ASSERT(hOutWnd);
00152     ::SendMessage(hOutWnd, WM_SETFONT, reinterpret_cast<WPARAM>(hOutFnt),
00153      FALSE);
00154      
00155     //--- COMMAND-LINE ---
00156     
00157     HWND hInWnd = InitInWin(hMainWnd);
00158     FS_ASSERT(hInWnd);
00159     g_Hwnd2ConMap.insert(THwnd2ConPair(hInWnd, this));
00160     
00161 #if defined(_MSC_VER) // MSVC - weird headers???
00162 #   pragma warning(push)
00163 #   pragma warning(disable: 4312)
00164 #   pragma warning(disable: 4244)
00165 #endif
00166 
00167     // subclass the input control
00168     m_pOrigInWndProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtr(hInWnd,
00169      GWL_WNDPROC));
00170     FS_ASSERT(m_pOrigInWndProc);
00171     ::SetWindowLongPtr(hInWnd, GWL_WNDPROC, reinterpret_cast<LONG_PTR>
00172      (InWndProc));
00173      
00174 #if defined(_MSC_VER) // MSVC
00175 #   pragma warning(pop)
00176 #endif
00177     
00178     //--- TRANSFER STATE ---
00179         
00180     m_hOutFnt = hOutFnt;
00181     m_hMainWnd = hMainWnd;
00182     m_hOutWnd = hOutWnd;
00183     m_hInWnd = hInWnd;
00184     m_bInited = true;
00185 }
00186 //---------------------------------------------------------------------------
00187 ConWinImpl_t::~ConWinImpl_t()
00188 {
00189     ::DestroyWindow(m_hMainWnd);
00190     ::DeleteObject(m_hOutFnt);
00191         
00192     g_Hwnd2ConMap.erase(m_hMainWnd);
00193     g_Hwnd2ConMap.erase(m_hInWnd);
00194 }
00195 //---------------------------------------------------------------------------
00196 bool ConWinImpl_t::IsValid() const
00197 {
00198     return m_bInited;
00199 }
00200 //---------------------------------------------------------------------------
00201 void ConWinImpl_t::Show(bool bShow)
00202 {
00203     if(bShow)
00204     {
00205         // set window focus to the command-line
00206         FS_ASSERT(m_hInWnd);
00207         ::SetFocus(m_hInWnd);
00208     
00209         // scroll to the end of the output area
00210         FS_ASSERT(m_hOutWnd);
00211         int nCount = static_cast<int>(::SendMessage(m_hOutWnd,
00212          EM_GETLINECOUNT, 0, 0));
00213         ::SendMessage(m_hOutWnd, EM_LINESCROLL, 0, nCount);
00214     }
00215     
00216     FS_ASSERT(m_hMainWnd);
00217     ::ShowWindow(m_hMainWnd, bShow ? SW_SHOW : SW_HIDE);
00218 }
00219 //---------------------------------------------------------------------------
00220 void ConWinImpl_t::SetPos(int nX, int nY, int nW, int nH)
00221 {
00222     FS_ASSERT(m_hMainWnd);
00223     ::SetWindowPos(m_hMainWnd, 0, nX, nY, nW, nH,
00224      SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
00225 }
00226 //---------------------------------------------------------------------------
00227 void ConWinImpl_t::Push(const char *pszText,  bool bCmd)
00228 {
00229     FS_ASSERT(m_hOutWnd);
00230     
00231     // inform listeners
00232     Send(pszText, bCmd);
00233 
00234     // keep 300 lines at max
00235     while(::SendMessage(m_hOutWnd, EM_GETLINECOUNT, 0, 0) > 300)
00236     {
00237         const int nLen = static_cast<int>(::SendMessage(m_hOutWnd,
00238          EM_LINELENGTH, 0, 0));
00239          
00240         ::SendMessage(m_hOutWnd, EM_SETSEL, 0, nLen + 2); // 2 means \r\n
00241         ::SendMessageA(m_hOutWnd, EM_REPLACESEL, 0, (LPARAM)"");
00242     }
00243     
00244     // reset the selection
00245     ::SendMessage(m_hOutWnd, EM_SETSEL, 65535 , 65535);
00246 
00247     // the first line => \r\n is not needed
00248     if(::SendMessage(m_hOutWnd, WM_GETTEXTLENGTH, 0, 0) > 0)
00249         ::SendMessageA(m_hOutWnd, EM_REPLACESEL, 0, (LPARAM)"\r\n");
00250 
00251     // write the type symbol
00252     if(bCmd)
00253         SendMessageA(m_hOutWnd, EM_REPLACESEL, 0 , (LPARAM)"> ");
00254 
00255     // write the text
00256     ::SendMessageA(m_hOutWnd, EM_REPLACESEL, 0, (LPARAM)pszText);
00257 }
00258 //---------------------------------------------------------------------------
00259 void ConWinImpl_t::RegTxtRcvr(ConWin_t::TxtRcvr_i *pRcvr)
00260 {
00261     FS_ASSERT(pRcvr);
00262     m_TxtRcvrs.push_back(pRcvr);
00263 }
00264 //---------------------------------------------------------------------------
00265 void ConWinImpl_t::OnSize(int nW, int nH)
00266 {
00267     // resize the output area
00268     FS_ASSERT(m_hOutWnd);
00269     ::SetWindowPos(m_hOutWnd, 0,
00270      0, 0, nW, nH - 20,
00271      SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE
00272     );
00273     
00274     // resize the command-line
00275     FS_ASSERT(m_hInWnd);
00276     ::SetWindowPos(
00277      m_hInWnd, 0,
00278      0, nH - 20, nW, 20,
00279      SWP_NOACTIVATE | SWP_NOZORDER
00280     );
00281 }
00282 //---------------------------------------------------------------------------
00283 void ConWinImpl_t::OnReturn()
00284 {
00285     FS_ASSERT(m_hInWnd);
00286     
00287     char szBuf[256];
00288     ::GetWindowTextA(m_hInWnd, szBuf, sizeof(szBuf) - 1);
00289     
00290     if(::strlen(szBuf) <= 0)
00291         return;
00292         
00293     // print the command
00294     Push(szBuf, true);
00295             
00296     // append the command into the history list
00297     while(m_Hist.size() > 50)
00298         m_Hist.erase(m_Hist.begin());
00299                 
00300     m_Hist.push_back(szBuf);
00301     m_nHistPos = -1;
00302     
00303     ::SetWindowText(m_hInWnd, TEXT(""));
00304 }
00305 //---------------------------------------------------------------------------
00306 void ConWinImpl_t::OnPrevCmd()
00307 {
00308     int nHistCount = static_cast<int>(m_Hist.size());
00309     
00310     if(nHistCount <= 0)
00311         return;
00312 
00313     if(m_nHistPos == -1)
00314         m_nHistPos = nHistCount; // also compensates '--'
00315         
00316     m_nHistPos--;
00317     
00318     if(m_nHistPos < 0)
00319         m_nHistPos = 0;
00320      
00321     FS_ASSERT(m_hInWnd);
00322     ::SetWindowTextA(m_hInWnd, m_Hist[m_nHistPos].c_str());
00323     ::SendMessage(m_hInWnd, EM_SETSEL, 65535 , 65535);
00324 }
00325 //---------------------------------------------------------------------------
00326 void ConWinImpl_t::OnNextCmd()
00327 {
00328     int nHistCount = static_cast<int>(m_Hist.size());
00329     
00330     if(nHistCount <= 0)
00331         return;
00332 
00333     if(m_nHistPos == -1)
00334         m_nHistPos = nHistCount - 2; // also compensates '++'
00335         
00336     m_nHistPos++;
00337     
00338     if(m_nHistPos > nHistCount - 1)
00339      m_nHistPos = nHistCount - 1;
00340      
00341     FS_ASSERT(m_hInWnd);
00342     ::SetWindowTextA(m_hInWnd, m_Hist[m_nHistPos].c_str());
00343     ::SendMessage(m_hInWnd, EM_SETSEL, 65535 , 65535);
00344 }
00345 //---------------------------------------------------------------------------
00346 bool ConWinImpl_t::IsInWin(HWND hWnd) const
00347 {
00348     return hWnd == m_hInWnd;
00349 }
00350 //---------------------------------------------------------------------------
00351 WNDPROC ConWinImpl_t::GetOrigInWndProc() const
00352 {
00353     return m_pOrigInWndProc;
00354 }
00355 //---------------------------------------------------------------------------
00356 HWND ConWinImpl_t::InitMainWin()
00357 {
00358     HINSTANCE hInst = ::GetModuleHandle(0);
00359     FS_ASSERT(hInst);
00360     
00361     WNDCLASSEX wc;
00362     if(!::GetClassInfoEx(hInst, TEXT("FS_RAP_CONWIN"), &wc))
00363     {
00364         wc.cbSize = sizeof(WNDCLASSEX);
00365         wc.style = 0;
00366         wc.lpfnWndProc = WndProc;
00367         wc.cbClsExtra = 0;
00368         wc.cbWndExtra = 0;
00369         wc.hInstance = hInst;
00370         wc.hIcon = 0;
00371         wc.hCursor = 0;
00372         wc.hbrBackground = 0;
00373         wc.lpszMenuName = 0;
00374         wc.lpszClassName = TEXT("FS_RAP_CONWIN");
00375         wc.hIconSm = 0;
00376         
00377         if(!::RegisterClassEx(&wc))
00378         {
00379             FS_ASSERT_MSG(0, "Could not register the window class!");
00380             return 0;
00381         }
00382     }
00383         
00384     HWND hWnd = ::CreateWindowEx(
00385      0,
00386      TEXT("FS_RAP_CONWIN"),
00387      TEXT(""),
00388      WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME|WS_MAXIMIZEBOX,
00389      CW_USEDEFAULT, CW_USEDEFAULT,
00390      700, 450,
00391      0,
00392      0,
00393      hInst,
00394      0
00395     );
00396     
00397     return hWnd;
00398 }
00399 //---------------------------------------------------------------------------
00400 HFONT ConWinImpl_t::InitOutFnt()
00401 {
00402     HFONT hFnt = ::CreateFont(
00403      14,
00404      0,
00405      0,
00406      0,
00407      FW_NORMAL,
00408      FALSE,
00409      FALSE,
00410      FALSE,
00411      DEFAULT_CHARSET,
00412      OUT_CHARACTER_PRECIS,
00413      CLIP_CHARACTER_PRECIS,
00414      DEFAULT_QUALITY,
00415      DEFAULT_PITCH | FF_DONTCARE,
00416      TEXT("Courier")
00417     );
00418     
00419     return hFnt;
00420 }
00421 //---------------------------------------------------------------------------
00422 HWND ConWinImpl_t::InitOutWin(HWND hParWnd)
00423 {
00424     HINSTANCE hInst = ::GetModuleHandle(0);
00425     FS_ASSERT(hInst);
00426 
00427     HWND hWnd = ::CreateWindowEx(
00428      WS_EX_STATICEDGE,
00429      TEXT("EDIT"),
00430      TEXT(""),
00431      WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_READONLY | WS_VSCROLL,
00432      0, 0,
00433      0, 0,
00434      hParWnd,
00435      0,
00436      hInst,
00437      0
00438     );
00439     
00440     return hWnd;
00441 }
00442 //---------------------------------------------------------------------------
00443 HWND ConWinImpl_t::InitInWin(HWND hParWnd)
00444 {
00445     HINSTANCE hInst = ::GetModuleHandle(0);
00446     FS_ASSERT(hInst);
00447 
00448     HWND hWnd = ::CreateWindowEx(
00449      WS_EX_STATICEDGE,
00450      TEXT("EDIT"),
00451      TEXT(""),
00452      WS_CHILD | WS_VISIBLE | WS_VSCROLL,
00453      0, 0,
00454      0, 0,
00455      hParWnd,
00456      0,
00457      hInst,
00458      0
00459     );
00460             
00461     return hWnd;
00462 }
00463 //---------------------------------------------------------------------------
00464 void ConWinImpl_t::Send(const char *pszText, bool bCmd)
00465 {
00466     for(size_t i = 0, n = m_TxtRcvrs.size(); i < n; ++i)
00467     {
00468         m_TxtRcvrs[i]->Receive(pszText, bCmd);
00469     }
00470 }
00471 //---------------------------------------------------------------------------
00472 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wPrm, LPARAM lPrm)
00473 {
00474     switch(msg)
00475     {
00476         case WM_SIZE:
00477         {
00478             THwnd2ConMap::iterator it = g_Hwnd2ConMap.find(hWnd);
00479             ConWinImpl_t *pCW = it != g_Hwnd2ConMap.end() ? it->second : 0;
00480             FS_ASSERT(pCW);
00481             
00482             pCW->OnSize(LOWORD(lPrm), HIWORD(lPrm));
00483             
00484             break;
00485         }
00486         default:
00487             return DefWindowProc(hWnd, msg, wPrm, lPrm);
00488     }
00489     
00490     return 0;
00491 }
00492 //---------------------------------------------------------------------------
00493 LRESULT CALLBACK InWndProc(HWND hWnd, UINT msg, WPARAM wPrm, LPARAM lPrm)
00494 {
00495     // find the associated ConWinImpl_t object
00496     THwnd2ConMap::iterator it = g_Hwnd2ConMap.find(hWnd);
00497     ConWinImpl_t *pCW = it != g_Hwnd2ConMap.end() ? it->second : 0;
00498     FS_ASSERT(pCW);
00499     
00500     WNDPROC pOrigWndProc = pCW->GetOrigInWndProc();
00501     FS_ASSERT(pOrigWndProc);
00502     
00503     switch(msg)
00504     {
00505         case WM_KEYDOWN:
00506         {
00507             switch(wPrm)
00508             {
00509                 case VK_RETURN: pCW->OnReturn(); break;
00510                 case VK_UP: pCW->OnPrevCmd(); return 0;
00511                 case VK_DOWN: pCW->OnNextCmd(); return 0;
00512             }
00513             break;
00514         }
00515     }
00516     
00517     return ::CallWindowProc(pOrigWndProc, hWnd, msg, wPrm, lPrm);
00518 }
00519 //---------------------------------------------------------------------------
00520 } // anonymous namespace
00521 //---------------------------------------------------------------------------
00522 //--- CLASS ConWin_t >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
00523 //---------------------------------------------------------------------------
00524 ConWin_t::ConWin_t(const char *pszTitle):
00525 m_pImpl(new ConWinImpl_t(pszTitle))
00526 {
00527     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00528 }
00529 //---------------------------------------------------------------------------
00530 ConWin_t::~ConWin_t()
00531 {
00532     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00533     delete static_cast<ConWinImpl_t *>(m_pImpl);
00534 }
00535 //---------------------------------------------------------------------------
00536 bool ConWin_t::IsValid() const
00537 {
00538     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00539     return static_cast<ConWinImpl_t *>(m_pImpl)->IsValid();
00540 }
00541 //---------------------------------------------------------------------------
00542 void ConWin_t::Show(bool bShow)
00543 {
00544     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00545     static_cast<ConWinImpl_t *>(m_pImpl)->Show(bShow);
00546 }
00547 //---------------------------------------------------------------------------
00548 void ConWin_t::SetPos(int nX, int nY, int nW, int nH)
00549 {
00550     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00551     static_cast<ConWinImpl_t *>(m_pImpl)->SetPos(nX, nY, nW, nH);
00552 }
00553 //---------------------------------------------------------------------------
00554 void ConWin_t::Push(const char *pszText)
00555 {
00556     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00557     static_cast<ConWinImpl_t *>(m_pImpl)->Push(pszText);
00558 }
00559 //---------------------------------------------------------------------------
00560 void ConWin_t::RegTxtRcvr(TxtRcvr_i *pRcvr)
00561 {
00562     FS_ASSERT_MSG(m_pImpl, "Invalid implementation pointer!");
00563     static_cast<ConWinImpl_t *>(m_pImpl)->RegTxtRcvr(pRcvr);
00564 }
00565 //---------------------------------------------------------------------------

FipS' Code (Thu Feb 15 22:43:35 2007) - (c) Filip STOKLAS (FipS) - [ www ] [ Guest Book ]