Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[175] 팁! 사용자정의 커서 사용하기
박지훈.임프 [cbuilder] 16220 읽음    1999-06-04 00:00
임프랍니다.
오늘의 팁을 너무 오랫동안 쉬었죠? 한 일주일 쉬었나봐요..
그동안 너무너무 바빠서.. 물론 지금도 좀 바쁘긴 합니다만.

오늘은 비교적 많이 알려진 팁으로, 직접 디자인한 마우스커서를 사용하는
방법을 알아봅시다. 초보분도 한번만 들으면 바로 직접 해볼 수 있도록,
단계별로 차근차근 해봅시다. 하지만 너무 쉬운 일반적인 것만 설명하면
고수분들이 실망하실까봐.. 군데군데 사족을 좀 많이 달아보겠습니다.
(쓰다보니 넘 길어져서... 몇개 섹션으로 나눴습니다.)


Win32환경에서의 마우스 커서
──────────────

본격적으로 들어가기 전에.. 델파이와 C++빌더에서의 마우스 커서에 대한 일반적인
사항들을 한번 간단히 되짚고 넘어갑시다. 아래 단락은 api상에서의 커서를 설명한
것이므로 관심이 없으시거나 어느정도 아시는 분은 넘어가셔도 상관없습니다.

원래 윈도우즈 환경에서 커서는 (win16이든 win32이든) HCURSOR형의 핸들
객체입니다. api 문법대로 요넘을 사용하려면, 가장 먼저 CreateCursor() 함수를
사용하여 커서의 AND 마스크와 XOR 마스크, 그리고 핫스팟(커서 이미지에서 실제
커서의 위치로 인식하게 될 포인트) 등등을 설정해주고 생성한후, 리턴받은
HCURSOR 핸들을 SetCursor()함수로 세팅합니다. 커서를 코드로 생성하기보다
리소스로부터 읽어들이려면 LoadCursor()함수를 사용하는데, 여기서 넘겨주는
인자에 따라 윈도우즈의 디폴트 커서를 읽어올수도 있습니다. (인스턴스 핸들을
NULL로 넘겨주면서 디폴트커서의 이름을 함께 넘겨주면 됩니다.)


리소스로서의 마우스커서
────────────

이번엔.. 커서의 다른 면을 살펴봅시다. 여기서 설명할 것은, 리소스로서의
커서의 성격입니다. 간단하게만 살펴보겠지만, 역시 관심이 없으시거나
어느정도 알고 계신 분은 넘어가셔서 다음 단락을 바로 보시면 되겠네요.

커서의 또다른 면은.. 실제의 커서이미지 리소스는 윈도우즈의 기본 아이콘과
동일한 포맷을 사용한다는 것입니다. 오래전에(92년쯤이었던것 같네요)
강력한 아이콘 에디터를 만들면서 관련된 모든 화일들의 포맷을 분석했었는데,
커서리소스의 포맷은 아이콘리소스와 완전히 동일하며, 단지 열번째였던가...
바이트에서 두바이트가 차이가 났던 기억이 나는군요.
아이콘과 커서 리소스가 같은 포맷을 쓸수밖에 없는 이유는, 윈도우 탐색기나
바탕화면(이것도 탐색기지만)에서 파일 아이콘을 드래그할때 파일 아이콘이
투명하게 비치면서 커서처럼 되는 것을 본 걸 상기해보면 간단히 이해가
되실 겁니다.
물론 커서가 디폴트로는 16x16사이즈를 쓰기 때문에 아이콘의 기본 사이즈인
32x32와는 차이가 나지만, 이것은 정해진 고정값이 아니라 레지스트리에 기록된
그냥 디폴트 값일 뿐입니다. 플러스 테마등을 깔아보신 분은 커서가 아이콘처럼
화려한 색깔의 32x32 크기 이미지가 되는 것을 보셨을 겁니다. 32x32 크기, 혹은
48x46이나 64x64 크기의 커서도 얼마든지 볼 수 있습니다.


VCL에서의 커서
───────

위에 쓴 것은 그냥 커서에 대한 일반적인 사항을 줄줄이 늘어본거구요..
이제 본격적으로 델파이와 C++빌더에서의 커서에 대하여 알아봅시다.하지만 vcl상에서의
커서를 또 주절주절 설명할거니까 역시 관심이 없으시면 넘어가셔도 상관없습니다.
하지만 알아두시면 당연히 내공이 상당히 증진되겠지요.
제법 길게 주절거릴 생각이니.. 당장 사용자 커서 세팅하는 것이 급하신 분은
좀 많이 스크롤해야 겠습니다. ^^

아시다시피... C++빌더나 델파이에서 사용하는 vcl 라이브러리는 당연히 api에 대한
래핑(wrapping) 일 뿐입니다. 실제로는 모든 것이 약간의 코드를 거쳐서 api 함수
를 통해 실행됩니다. 커서에 관련한 부분들도 역시 마찬가지죠.
컨트롤들은 (컴퍼넌트가 아닌) vcl 상속단계에서 TControl을 상속받고, 화면에
보이는 모든 컨트롤들은 당연히 그 컨트롤에서 나타날 커서를 정해줄 수 있어야
하기 때문에 Cursor 프로퍼티는 TControl의 멤버입니다.
이 Cursor 프로퍼티는 vcl 소스의 Controls.pas에 보면 다음과 같이 선언되어
있습니다.

property Cursor: TCursor read FCursor write SetCursor default crDefault;

파스칼 소스라 갑갑한 C++ 개발자라면, C++ 버전인 Controls.hpp를 보면 됩니다.

__property TCursor Cursor = {read=FCursor, write=SetCursor, default=0};

컴퍼넌트를 만드는데 관심이 있으시거나 혹은 VCL을 좀이라도 깊이 공부해보신
분은 다 아시다시피, 이 한줄의 문장은 Cursor라는 프로퍼티를 억세스하려고
했을때 실제로 억세스할 함수(혹은 변수)를 지정한 문장이죠. 여기서는,
Cursor 프로퍼티에 읽으려고 할때 리턴할 값으로 FCursor 값을, 그리고
쓰려고 할때 호출할 함수로 SetCursor() 함수를 지정하고 있습니다.

이 함수 SetCursor()의 정의를 찾기 위해 해당 헤더의 vcl 소스인 controls.pas를
뒤져보면, CM_CURSORCHANGED라는 vcl에서 정의한 메시지를 자체 컨트롤에 보내는
코드로 되어있습니다. 그리고 당연히 이 메시지에 핸들러가 있는데.. (핸들러가 없는
메시지는 의미가 없죠..) 다시 이 핸들러 함수를 찾아보면.. WM_SETCURSOR 메시지를
보내고 있습니다.
(왜 한번에 WM_SETCURSOR를 보내고 말지 구질구질하게 CM_CURSORCHANGED를 거쳐서
보내냐 하는 것은... CM_CURSORCHANGED의 핸들러 함수에서 WM_SETCURSOR를 보내기
전에 실행하는 약간의 코드 때문입니다. 아주 약간 복잡한 이유로 WM_SETCURSOR를
바로 보내지 않고 약간더 단계를 거치는 이유가 있습니다.)

자아.. 여기서 끝일까요? 죄송하게도.. 아닙니다. vcl은 상당히 복잡하고 잘 짜여진
구조적인 클래스 라이브러리입니다. 더욱이 모든 컴퍼넌트의 경우 런타임이 아닌
디자인모드에서도 실제 객체가 존재하기 때문에 그에 대한 처리까지 하면 상당히
복잡해집니다.
그래서 이 WM_SETCURSOR 메시지에 대한 핸들러를 자체적으로 가지고 있습니다.
어쨌든.. 이 핸들러 내부에는 약간의 구질구질한(사실은 필수적인) 코드들 속에서
다음과 같은 눈에 번쩍 띄는 라인을 발견할 수 있습니다.

Windows.SetCursor(Screen.Cursors[Cursor]);

이게 뭘까요? C++식으로 말하자면, Windows::SetCursor(Screen->Cursors[Cursor]);
이렇게 되죠. 그럼, 스크린 클래스는 알겠는데, Windows라는 것은 뭐당가?
여기서 Windows는 API 콜 함수들이 대부분 모여있는 소스 파일 이름입니다.
파스칼에서의 유닛 이름은 C++에서는 네임스페이스가 됩니다. 이 Windows 유닛을
열어서 SetCursor 함수를 찾아보면 다음의 라인을 찾을 수 있습니다.

function SetCursor; external user32 name 'SetCursor';

파스칼에서는 이렇게 사용하지만, C++빌더는 C++ 컴파일러이기 때문에 좀 다릅니다.
C++에서의 API 함수들의 선언은 헤더파일들에 있고, SetCursor는 winuser.h에 있습니다.

자아... 여기까지.. 참 길고도 길게 설명했습니다. C++빌더에서 우리가 쉽게
아무생각없이 쓰는 MyLabel->Cursor = crHandPoint; 이런 문장이 실제로는
어떻게 실행이 되는지요. 그래도 그렇게 필요가 없는 부분은 구질구질하니까
다 빼고 설명했는데도 이렇게 길어지네요.. 관심도 없는데 억지로 보신 분은
없으시겠지요? (미리 경고했습니다... --;;;;)


메인 : 사용자정의 커서 사용하기
────────────────

자...아.... 기지개 한번 켜고.. 이제 이 팁의 주제인 사용자 정의 커서를
델파이나 C++빌더에서 사용하는 방법을 알아봅시다. 그러니까.. 기나긴 서론끝의 본론
쬐금입니다. 사족은 위에서 엄청나게 달아봤으니까.. 여기서는 무지무지 간단하게~!
절차 위주로 설명해보겠습니다.

먼저 사용자 정의 커서를 만들어봅시다. 말그대로 '사용자정의'니까 직접 만들어야
됩니다. 멀로 만드냐고요? 델파이, C++빌더에 포함된 이미지 에디터는 그냥 폼인가요?
이럴때 써먹지. ^^
먼저 이미지에디터를 실행합니다. 그리고 다음과 같이 해봅시다.

1. File 메뉴의 New 항목중 'Resource files (.res)' 을 선택해서 새
   리소스화일을 시작한다.
   (그림을 그릴수 있는 그리드 모양이 바로 튀어나오지 않는데 대해 황당해하지
    않도록 주의한다. 여기서 황당해 하면 나중에 무식하단 말을 듣는수가 있다.)

2. 화면에 나온 창에서(트리뷰임) Contents라고 된 글자에서 마우스 왼쪽키를 눌러
   나온 팝업 메뉴에서 다시 New->Cursor 항목을 선택해서 커서를 만든다.

3. 먼저 이름을 정하자. 모든 리소스는 각각 이름을 붙여줘야 억세스가 되는데,
   디폴트로 나온 Cursor1이란 이름은 짱난다. 나중에 코딩할때 헷갈린다.
   그러니까 Cursor1이라고 되어있는 아이템을 천천히 두번 클릭해서 에디트 상태가
   되도록 한 후, 이름을 적어본다. 이름을 맘대로 정해도 되지만, 여기서는
   학습의 효과를 높이기 위해 IMPCURSOR라고 지어보자.
   (학습의 효과랑 정말 상관이 있냐고? 따지지 말자. 지면 모자란다.)

4. 그림은 언제 그리냐고? 안그래도 바쁜시간 쪼개서 팁 쓰는 불쌍한 임프
   재촉하지 말자. 이제부터 그린다.
   정해준 이름 항목을 더블클릭한다. 그럼 이제 무식한 임프가 좋아하는 그림그리는
   판때기가 나온다. 열심히 그리자. 여기서도 아무거나 그려도 되지만, 학습의
   효과를 높이기 위해... 임프 얼굴을 그려보자. 임프 얼굴이 잘 기억이 안나는
   분은 크리스 슬레이터나.. 정우성 등의 얼굴을 연상하면 된다.
   (여기서 송강호 얼굴 사진을 갖다놓고 그린 사람은 들통났을 경우 장수에
    지장이 올수도 있다.)

5. 핫스팟을 정한다. 핫스팟이 뭐냐고? (수업시간에 졸았군.) 아까 설명했다.
   다 스킵해도 된다고 한거 뻥이었다. 다시 스크롤해서 찾아봐라. 재방송 안한다.
   이미지에디터에서 핫스팟을 정하려면 메인메뉴의 Cursor 항목에서 Set Hot
   spot을 선택하면 된다. x, y 좌표로 지정해준후 오케바리! 하고 빠져나오자.

6. 저장한다. 이름은 맘대로 지어도 된다. 그렇다고 프로젝트의 메인 리소스화일
   (프로젝트이름.res)을 덮어쓰면 안된다. 이 경고를 무시하면 나중에 프로젝트
   링크 과정에서 델파이나 C++빌더에게서 궁시렁거리는 욕을 듣게된다. 프로젝트
   화일이름만 피하자.

7. 여기까지 해서 사용자 정의 커서가 포함된 리소스화일을 만들었다.
   끝이냐고? 델파이나 C++빌더가 이 화일이 프로젝트의 일부인지 어떻게 아냐.
   그러면 리소스 파일을 참조하도록 만들어야 할 텐데, 이 부분에서 델파이와
   C++빌더는 조금 다르다.
   델파이의 경우. implementation 섹션 시작한 바로 아래에 다음과 같이 한줄을
   추가해준다.
   {$R 리소스파일.res}
   C++빌더의 경우. 바로 위의 델파이처럼 소스에서 참조시키려면 cpp 파일의
   #include들 바로 다음에 다음과 같이 추가해준다.
   #pragma resource "리소스파일.res"
   혹은, C++빌더에서는 리소스 파일을 프로젝트 매니저를 이용해서 바로 프로젝트에
   추가시킬 수도 있다. C++빌더의 메인 메뉴에 있는 Project 항목에서 Add to
   Project 를 클릭해서 나온 파일 다이얼로그 박스에서 방금 만든 리소스 화일을
   찾아 오케바리! 하고 나오면 된다.
   (괜히 무식한척, 디폴트로 설정된 .cpp 필터가 그대로 되어있는 데서 방금
    만든 리소스화일을 찾지 말자. 리소스화일은 확장자가 .res다.)

여기까지... 이렇게 해서 코딩만 남았군요. 자.. 그럼 코딩은 빨리빨리 진도를
빼 봅시다.

1. 당연히 어느 컨트롤에서 커서가 바뀔건지부터 생각해야 된다.
   이것도 생각하기 귀찮은 분들을 위해 방법을 제시한다. 폼에 label을 하나
   떨어뜨리고 캡션을 "임프 만세~!" 라고 해보자. label은 이정도 문자열로는
   크기가 너무 작으므로 그 다음에 임프를 사랑하는 마음을 듬뿍 담아서 작문을
   하는 것도 좋다.

2. 폼을 더블클릭한다. 빈폼을 더블클릭하면 폼의 OnCreate 이벤트 핸들러가 자동
   생성되는 것도 모르는 분은 여기까지 헛고생하셨다. 첨부터 다시 공부하자.
   그리고 코드는 다음과 같이 집어넣는다.

// C++ 코드
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    TCursor crImp = TCursor(5);
    Screen->Cursors[crImp] = LoadCursor(HInstance, "IMPCURSOR");
    Label1->Cursor = crImp;
}


// 델파이 코드
procedure TForm1.FormCreate(Sender: TObject);
var
  crImp: TCursor;
begin
  crImp := TCursor(5);
  Screen.Cursors[crImp] := LoadCursor(HInstance, 'IMPCURSOR');
  Label1.Cursor := crImp;
end;

   Label1이 뭐냐고 묻지 말자. 아까 만든 임프만세 label 이름을 적어주면 된다.

3. ??
   뭘 기다리남? F9 눌러서 실행해보자. 끝났다.
   아마도 label에 많은 내용을 적었을수록 더 좋은 결과를 보게 될 것이다.
   label에 커서를 가져갔을때 나타나는 멋진 얼굴이 보이는가?


헥..헥...~
힘들군요.. 물론 읽으시는 분도 힘드셨겠지만. 중간중간에 장난을 좀 과하게
친거 같은데.. 이해해주세요.. 재미있게 쓰려고 주제에 재롱을 한번 부려봤습니다.

이번 팁에선, 그동안 팁을 소홀히 한 사죄의 의미로 좀 깊숙히 파봤습니다.
(엥? 아무 돔도 안된다구용? -.-;;;;)

내일도 팁은 꼬옥꼭 올리겠습니다. 하지만... 이번같이 길게는 못쓰겠네요.. 쩝..
(다행이라구요? 천벌받을겨...)
그럼.. 내일 다시 알찬 팁으로 찾아뵐 것을 약속드리면서.. 이만...
^_________________^


독립문에서 임펠리테리였습니다.
얼큰 [cityguy2]   2002-05-16 11:32 X
재밌고 유용한 정보 잘 봤습니다.

+ -

관련 글 리스트
175 팁! 사용자정의 커서 사용하기 박지훈.임프 16220 1999/06/04
(링크)     C++Builder Tip'N Tricks > 팁! 사용자정의 커서 사용하기
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.