00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <fs/rap/fsConWin.h>
00015
00016
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
00048
00049 #include <map>
00050 #include <vector>
00051 #include <string>
00052 #include <windows.h>
00053
00054
00055 #if defined(FS_HAS_FSMEMMGR)
00056 # include <fs/sys/fsMemMgr.h>
00057 #endif
00058
00059 using namespace fs::rap;
00060
00061
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
00071 typedef std::pair<HWND, ConWinImpl_t *> THwnd2ConPair;
00072 typedef std::map<HWND, ConWinImpl_t *> THwnd2ConMap;
00073 THwnd2ConMap g_Hwnd2ConMap;
00074
00075
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:
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
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
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
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
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
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
00206 FS_ASSERT(m_hInWnd);
00207 ::SetFocus(m_hInWnd);
00208
00209
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
00232 Send(pszText, bCmd);
00233
00234
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);
00241 ::SendMessageA(m_hOutWnd, EM_REPLACESEL, 0, (LPARAM)"");
00242 }
00243
00244
00245 ::SendMessage(m_hOutWnd, EM_SETSEL, 65535 , 65535);
00246
00247
00248 if(::SendMessage(m_hOutWnd, WM_GETTEXTLENGTH, 0, 0) > 0)
00249 ::SendMessageA(m_hOutWnd, EM_REPLACESEL, 0, (LPARAM)"\r\n");
00250
00251
00252 if(bCmd)
00253 SendMessageA(m_hOutWnd, EM_REPLACESEL, 0 , (LPARAM)"> ");
00254
00255
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
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
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
00294 Push(szBuf, true);
00295
00296
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;
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;
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
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 }
00521
00522
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