Win32學習筆記 第六章 程式6-3 Typer 說明 (轉)

amyz發表於2007-11-16
Win32學習筆記 第六章 程式6-3 Typer 說明 (轉)[@more@]

『學習筆記』

作者: 姜學哲(tosail0@163.net">netsail0@163.net)

教材: 設計(第五版)北京大學出版社
 [美]Charles Petzold 著
 北京博彥科技發展有限公司 譯  ¥:160

環境:  Pro + Visual C++ 6.0

圖們江程式編制小組 版權所有,轉載請說明出處
--------------------------------------------------------------------
第六章 程式6-5 Typer

看這本書越到後來越難。才第六章就這樣了。書裡面對程式沒有太多的解釋。全靠自己理解。好累啊。這個程式可費了我不少腦筋。所以我想給這個程式寫個說明。剛學完就寫,所以水平不限。有什麼不妥的地方就罵我吧。

Typer程式使用了本章討論的所有內容。我們可以認為TYPER是一個特別簡單的文字編輯器。在視窗中可以輸入字元,用游標移動鍵移動游標,按下Escape鍵擦除視窗內的內容。

WinMain部分就不用講了。從第三章開始從來沒有變過,我想您應該已經背得滾瓜爛熟了。What?還沒有背?還等什麼?趕緊背啊!

WM_INPUTLANGCHANGE:

書裡面沒有說明這個訊息。但是我們一看就能明白,每當我們改變鍵盤佈局的時候就會收到這個訊息。因為裡面有這麼一句:

dwCharSet = wParam ;

dwCharSet裡的是字符集ID --&gt DEFAULT_CHARSET。給該變數重新付值的唯一可能的原因就是鍵盤佈局變了。而且我們可以看出該訊息的wParam欄位裡有新的字符集ID。

WM_CREATE:

然後就是為已選定的鍵盤佈局設定字型了。這是邏輯字型。

(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;

然後就是獲取字元的高度的寬度了,儲存在 cxChar  cyChar 裡面。

這裡請注意一個細節。從WM_INPUTLANGCHANGE訊息開始一直往下直到WM_SIZE訊息結束,都沒有 break 或者 return 語句。這意味著即使程式收到的是WM_INPUTLANGCHANGE訊息,它也會一直到WM_SIZE訊息。

cxBuffer 和 cyBuffer 裡面儲存著客戶區中可顯示字元的行數和列數。

然後根據行數和列數建立 cxBuffer * cyBuffer 個 動態字元陣列。

當客戶區尺寸改變或者改變了鍵盤佈局的時候程式會釋放該動態陣列,然後重新分配陣列。但是這裡有一個問題,因為

free(pBuffer)

在malloc前面,所以最終會導致該動態陣列無法被釋放。我聽說過野指標,不知道是不是指這種情況。所以我在

WM_DESTROY

訊息里加入了一句

free(pBuffer) ;

就在

PostQuitMessage(0) ;

後面。也可能是我錯了,歡迎討論。

程式最開始處有一個宏定義:

#define BUFFER(x, y) *(pBuffer + y * cxBuffer + x)

這個宏給出的是第 y 行 第 x 列的位置。

for (y = 0 ; y < cyBuffer ; y++) 
  for (x = 0 ; x < cxBuffer ; x++)
  BUFFER(x,y) = ' ' ;

上面的語句把客戶區用空格填滿。我們可以把空格改成其它的什麼,比如:

BUFFER(x,y) = 'A' ;

看看會有什麼結果。

xCaret yCaret是游標的位置。函式GetFocus返回擁有輸入焦點的視窗控制程式碼。

SetCaretPos(xCaret*cxChar, yCaret*cyChar) ;

上面的語句把游標移到了客戶區左上角。接著的函式就是重新整理整個客戶區了:

InvalidateRect(hwnd, NULL, TRUE) ;

return 0 ;

當我們改變了鍵盤佈局(WM_INPUTLANGCHANGE),或是建立視窗(WM_CREATE),或是改變視窗尺寸(WM_SIZE)的時候我們對視窗客戶區所做的改動都會消失。視窗會變成剛開始時候的樣子,也就是一大片空格。

WM_SETFOCUS WM_KILLFOCUS訊息在書中寫得很明白。所以不說了。

WM_KEYDOWN 部分只有VK_DELETE要說一下。

這個DELETE跟記事本程式中DELETE的功能一樣。即刪除掉當前位置的字元,然後後繼列左移一個單位,來填補空出來的位置。最後一列補一空格。

for (x = xCaret ; x < cxBuffer - 1 ; x++)
  BUFFER (x, yCaret) = BUFFER (x + 1, yCaret) ;

BUFFER (cxBuffer - 1, yCaret) = ' ' ;

雖然已經改變了變數的內容,但是客戶區的內容還是沒有變,所以要用TextOut()顯示出來。

TextOut (hdc, xCaret * cxChar, yCaret * cyChar,
  & BUFFER (xCaret, yCaret),
  cxBuffer - xCaret) ;

WM_CHAR:
for (i = 0 ; i < (int) LO (lParam) ; i++)

LOWORD (lParam) 是按鍵的重複次數。

case 'b':

先把插入符左移一個單位,然後就是 WM_KEYDOWS 的 VK_DELETE部分了。

case 't':

do
{
  SendMessage (hwnd, WM_CHAR, ' ', 1) ;
}while (xCaret % 8 != 0) ;
break ;

現在,開啟程式Typer.exe。剛開啟的時候插入符的位置是 (0,0)。當我們按了一次 tab 鍵後位置變成了 (8,0),也就是向右移動了8個字元位。這麼說 tab 鍵的功能是使用插入符向右移動8個字元位嗎?末必。

我們現在使用游標移動鍵把插入符移到(2,0),現在再按一次 tab 鍵,這個時候插入符的位置還是(8,0)。按下 tab 鍵後插入符可能的位置是:(i*8,0)。

SendMessage (hwnd, WM_CHAR, ' ', 1) ;

使得程式執行 WM_CHAR 的default部分。

BUFFER(xCaret, yCaret) = (TCHAR)wParam ;

SendMessage()傳遞的是一個空格,所以 yCaret 行 xCaret 列的內容變成了空格。然後就是用TextOut()把變化後的內容顯示出來了。

TextOut (hdc, xCaret * cxChar, yCaret * cyChar, & BUFFER (xCaret, yCaret), 1) ;

直到現在只改變了一個字元位。

然後插入符前進一位了。

++xCaret

這個 ++xCaret 包含在一個 if 裡面:

if (++xCaret == cxBuffer)
{
  xCaret = 0 ;
 
  if (++yCaret == cyBuffer)
  yCaret = 0 ;
}

++xCaret == cxBuffer 的作用是當插入符已經到達了客戶區的最右邊不能再右移的時候把插入符移到下一行首列。本程式使用兩個語句做到了這一點:

xCaret = 0 ;
++yCaret

這個 ++yCaret 又包含在一個 if 裡面。當插入符已經到達了最後一行的時候,程式會把插入符移到(0,0)。

WM_PAINT:

for (y = 0 ; y < cyBuffer ; y++)
 TextOut (hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer) ;

上面的程式碼把所有動態陣列中的內容都輸出到客戶區上。如果沒有這一段程式碼,WM_SIZE 期間的

  for (y = 0 ; y < cyBuffer ; y++)
  for (x = 0 ; x < cxBuffer ; x++)
  BUFFER(x,y) = ' ' ;

就沒有用了。不過我實在是不明白上面的兩層 for 語句到底有什麼用。是不是多此一舉啊?或者是我沒有看明白?


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-982738/,如需轉載,請註明出處,否則將追究法律責任。

相關文章