На этом шаге мы рассмотрим приложение, позволяющее выбирать внешний вид курсора.
Студент 4 курса (2010-11 уч.год) Войцеховский Егор создал приложение, позволяющее выбирать внешний вид курсора средствами бибилиотеки OWL.
Приложение представляет собой программу, которая позволяет вам просмотреть системные курсоры Windows. Кроме того, программа позволяет выбирать свой собственный (пользовательский) курсор.
Внешний вид приложения изображен на рисунке 1:
Рис.1. Внешний вид приложения
Изменять курсоры можно двумя способами:
Рис.2. Окно Выбор курсора
Приведем текст приложения.
#include <owl\applicat.h> #include <owl\framewin.h> #include <owl\decframe.h> #include <owl\dialog.h> #include <owl\radiobut.h> #include <owl\gdiobjec.h> #include <owl\controlb.h> #include <owl\buttonga.h> #include <owl\messageb.h> #include "cur.rc" // Буфер обмена. struct { BOOL buttons [14]; } transBuf; //Класс приложения. class TApp: public TApplication { public: TApp():TApplication() {} void InitMainWindow(); }; // Класс главного окна. class TWndw : public TDecoratedFrame { public: TWndw (TWindow *parent, const char far *title, TWindow *client, BOOL tr); protected: void mycurs(char *cursor); void CmCursor(); void ChangeCursor(); void CmAbout(); //about void CmMy1(){mycurs("IDC_MYCUR1");}; void CmMy2(){mycurs("IDC_MYCUR2");}; DECLARE_RESPONSE_TABLE (TWndw); }; DEFINE_RESPONSE_TABLE1 (TWndw, TDecoratedFrame) EV_COMMAND (CM_CURSOR, CmCursor), EV_COMMAND(CM_ABOUT, CmAbout), EV_COMMAND(CM_MY1, CmMy1), EV_COMMAND(CM_MY2, CmMy2), END_RESPONSE_TABLE; // Класс диалогового окна Select Cursor. class TDlg : public TDialog { public: TDlg(TWindow *parent, TResId resId); }; // TWndw::TWndw() // Rонструктор главного окна. TWndw::TWndw(TWindow *parent, const char far *title,TWindow *client, BOOL tr) :TDecoratedFrame(parent, title, client, tr) { // Определить расположение и размеры окна. Attr.W=GetSystemMetrics(SM_CXSCREEN)/2; Attr.H=GetSystemMetrics(SM_CYSCREEN)/2; Attr.X=GetSystemMetrics(SM_CXSCREEN)/2-Attr.W/2; Attr.Y=GetSystemMetrics(SM_CYSCREEN)/2-Attr.H/2; //++++ TButtonGadget *but; //кнопки TSeparatorGadget *sep; //сепараторы AssignMenu(MENU_1); TControlBar *cntrlbar = new TControlBar(this, TControlBar::Vertical); sep=new TSeparatorGadget(50); //separator cntrlbar->Insert(*sep); but=new TButtonGadget(BMP_CURTYPE, CM_CURSOR); //new cntrlbar->Insert(*but); sep=new TSeparatorGadget(10); cntrlbar->Insert(*sep); but=new TButtonGadget(BMP_ABOUT, CM_ABOUT); //open cntrlbar->Insert(*but); sep=new TSeparatorGadget(10); cntrlbar->Insert(*sep); but=new TButtonGadget(BMP_EXIT, CM_EXIT); //exit cntrlbar->Insert(*but); Insert(*cntrlbar, TDecoratedFrame::Left); cntrlbar->SetHintMode(TGadgetWindow::EnterHints); //hint's TControlBar *cntrlbar2 = new TControlBar(this, TControlBar::Vertical); sep=new TSeparatorGadget(50); //separator cntrlbar2->Insert(*sep); but=new TButtonGadget(BITMAP_1, CM_MY1); //new cntrlbar2->Insert(*but); sep=new TSeparatorGadget(10); cntrlbar2->Insert(*sep); but=new TButtonGadget(BITMAP_1, CM_MY2); //open cntrlbar2->Insert(*but); Insert(*cntrlbar2, TDecoratedFrame::Right); TMessageBar *msgbar = new TMessageBar(this); msgbar->SetText("Сообщения:"); Insert(*msgbar, TDecoratedFrame::Bottom);//status barr //==== // Добавить меню к окну. AssignMenu(MENU_1); // Инициализировать буфер обмена. memset (&transBuf,0,sizeof(transBuf)); transBuf.buttons[0] = TRUE; } // TWndw::CmCursor() // Эта функция реагирует на сообщение CM_CURSOR, // которое посылается, когда пользователь // выбирает команду Select Cursor в меню Cursor. void TWndw::CmCursor() { // Создать диалоговое окно Select Cursor. TDialog *dialog = new TDlg (this, CURSORDLG); // Отобразить окно. int result = dialog->Execute(); // Если пользователь выходит по кнопке ОК... if (result == IDOK) //то переключиться на выбранный курсор. ChangeCursor(); } void TWndw::mycurs(char *cursor) { HINSTANCE instance; HCURSOR hCursor; instance = *GetApplication() ; // Получить ключ выбранного курсора. hCursor = LoadCursor (instance, cursor); // Изменить задаваемый по умолчанию курсор // оконного класса на выбранный. SetClassWord (GCW_HCURSOR, (WORD) hCursor); } void TWndw::CmAbout() { MessageBox("О программе", "О программе", MB_OK); } // TWndw::ChangeCutsor() // Эта функция находит выбранный пользователем тип // курсора по значениям радио-кнопок выбора из // диалогового окна SelectCursor, а затем заменяет // курсор на выбранный. void TWndw::ChangeCursor() { int selection; const char *cursor; HINSTANCE instance; HCURSOR hCursor; // Найти нужную радио-кнопку. for (int x = 0; x < 14; ++x) if (transBuf.buttons[x]) selection = x; // Найти тип курсора, выбранного пользователем. switch (selection) { case 0 : cursor = IDC_ARROW;break; case 1 : cursor = IDC_CROSS;break; case 2 : cursor = IDC_IBEAM;break; case 3 : cursor = IDC_ICON;break; case 4 : cursor = IDC_SIZE;break; case 5 : cursor = IDC_SIZENESW;break; case 6 : cursor = IDC_SIZENS;break; case 7 : cursor = IDC_SIZENWSE; break; case 8 : cursor = IDC_SIZEWE; break; case 9 : cursor = IDC_UPARROW; break; case 10 : cursor = IDC_WAIT; break; case 11 : cursor = "IDC_TARGET"; break; case 12 : cursor = "IDC_MYCUR1"; break; case 13 : cursor = "IDC_MYCUR2"; break; } // Если пользователь выбрал стандартный курсор ... if (selection >= 11) // Получить ссылку на этот экземпляр приложения. instance = *GetApplication() ; else // Иначе присвоить instance значение NULL, чтобы // загрузить системный курсор Windows. instance = NULL; // Получить ключ выбранного курсора. hCursor = LoadCursor (instance, cursor); // Изменить задаваемый по умолчанию курсор // оконного класса на выбранный. SetClassWord (GCW_HCURSOR, (WORD) hCursor); } //************************************** // Реализация класса TDlg. //************************************** // TDlg::TDlg() // Это конструктор диалогового окна SelectCursor. TDlg::TDlg(TWindow *parent, TResId resId): TDialog (parent, resId) { // Создать управляющие элементы OWL для каждого // управляющего элемента диалогового окна, которое // будет принимать участие в передаче данных. new TRadioButton(this, ID_ARROWBUT, 0); new TRadioButton(this, ID_CROSSBUT, 0); new TRadioButton(this, ID_IBEAMBUT, 0); new TRadioButton(this, ID_ICONBUT, 0); new TRadioButton(this, ID_SIZEBUT, 0); new TRadioButton(this, ID_NESWBUT, 0); new TRadioButton(this, ID_NSBUT, 0); new TRadioButton(this, ID_NWSEBUT, 0); new TRadioButton(this, ID_WEBUT, 0); new TRadioButton(this, ID_UPBUT, 0); new TRadioButton(this, ID_WAITBUT, 0) ; new TRadioButton(this, ID_TARGETBUT, 0); new TRadioButton(this, ID_MY1BUT, 0); new TRadioButton(this, ID_MY2BUT, 0); // Установить адрес буфера обмена. TransferBuffer = &transBuf; } void TApp::InitMainWindow() { TWindow *client = new TWindow(0,0,0); TDecoratedFrame *wndw = new TWndw(0, "Main Form", client, TRUE); SetMainWindow(wndw); EnableBWCC(); } int OwlMain(int,char *[]) { return TApp().Run(); }
Файл ресурсов:
#ifndef WORKSHOP_INVOKED #include "windows.h" #endif #define CURSOR_2 2 #define BMP_EXIT 6 #define BMP_ABOUT 4 #define BMP_CURTYPE 5 #define BMP_NEW 3 #define BITMAP_1 1 #define CURSOR_1 1 #define MENU_1 100 #define CURSORDLG 200 #define CM_CURSOR 201 #define CM_EXIT 24310 #define CM_ABOUT 202 #define CM_MY1 203 #define CM_MY2 204 #define ID_ARROWBUT 101 #define ID_CROSSBUT 102 #define ID_IBEAMBUT 103 #define ID_ICONBUT 104 #define ID_SIZEBUT 105 #define ID_NESWBUT 106 #define ID_NSBUT 107 #define ID_NWSEBUT 108 #define ID_WEBUT 109 #define ID_UPBUT 110 #define ID_WAITBUT 111 #define ID_TARGETBUT 112 #define ID_MY1BUT 113 #define ID_MY2BUT 114 #ifdef RC_INVOKED CURSORDLG DIALOG 18, 37, 257, 197 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CLASS "BorDlg" CAPTION "Выбор курсора" FONT 8, "MS Sans Serif" { CONTROL " ", 2, "BorShade", BSS_HDIP | BSS_LEFT | WS_CHILD | WS_VISIBLE, 5, 158, 250, 3 CONTROL " ", IDOK, "BorBtn", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 9, 165, 37, 25 CONTROL " ", IDCANCEL, "BorBtn", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 215, 164, 37, 25 CONTROL " ", -1, "BorShade", BSS_GROUP | BSS_LEFT | WS_CHILD | WS_VISIBLE, 4, 4, 250, 148 CONTROL "Arrow", ID_ARROWBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 15, 20, 48, 12 CONTROL "Crosshair", ID_CROSSBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 14, 35, 48, 12 CONTROL "I-Beam", ID_IBEAMBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 15, 51, 48, 12 CONTROL "Icon", ID_ICONBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 15, 71, 48, 13 CONTROL "Size", ID_SIZEBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 14, 88, 52, 12 CONTROL "NE - SW", ID_NESWBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 14, 104, 52, 12 CONTROL "North - South", ID_NSBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 14, 120, 53, 12 CONTROL "NW - SE", ID_NWSEBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 75, 22, 53, 13 CONTROL "West - East", ID_WEBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 75, 38, 47, 12 CONTROL "Up Arrow", ID_UPBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 74, 56, 47, 12 CONTROL "Wait", ID_WAITBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 74, 74, 47, 12 CONTROL "Target", ID_TARGETBUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 142, 30, 47, 12 CONTROL "MY_CUR1", ID_MY1BUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 142, 46, 47, 12 CONTROL "MY_CUR2", ID_MY2BUT, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 142, 63, 47, 12 LTEXT "Стандартные курсоры Win", -1, 11, 12, 109, 8, WS_DISABLED | WS_GROUP LTEXT "Пользовательские курсоры", -1, 139, 14, 109, 8, WS_DISABLED | WS_GROUP } MENU_1 MENU { POPUP "&File" { MENUITEM "A&bout Program" CM_ABOUT MENUITEM "E&xit", CM_EXIT } POPUP "&Cursor" { MENUITEM "&Change cursor...", CM_CURSOR MENUITEM "My cursor1", CM_MY1 } } IDC_TARGET CURSOR "target.cur" IDC_MYCUR1 CURSOR "my1.cur" IDC_MYCUR2 CURSOR "my2.cur" BITMAP_1 BITMAP "my.bmp" BMP_ABOUT BITMAP "about.bmp" BMP_CURTYPE BITMAP "dialog.bmp" BMP_EXIT BITMAP "exit.bmp" BMP_NEW BITMAP "my.bmp" #endif
Ниже приведено пояснение основных моментов работы программы.
Сначала подключаем все необходимые для нашего проекта библиотеки. Строка #include "cur.rc" подключает к нашему проекту файл-ресурсов cur.rc,в котором содержится описание меню проекта, курсоров, диалогового окна:
#include <owl\applicat.h> #include <owl\framewin.h> #include <owl\decframe.h> #include <owl\dialog.h> #include <owl\radiobut.h> #include <owl\gdiobjec.h> #include <owl\controlb.h> #include <owl\buttonga.h> #include <owl\messageb.h> #include "cur.rc"
Далее нужно создать 14-элементный массив булевых величин, каждая из которых представляет состояние одной из кнопок окна диалога. Через него будет передаваться информация от диалогового окна непосредственно к нашему главному окну:
struct
{
BOOL buttons [14];
} transBuf;
Класс диалогового окна, описанный ниже, содержит только конструктор. Само диалоговое окно было построено предварительно в редакторе ресурсов.
class TDlg : public TDialog { public: TDlg(TWindow *parent, TResId resId); };
В классе главного окна описываем все необходимые функции:
protected: void mycurs(char *cursor); void CmCursor(); void ChangeCursor(); void CmAbout(); //about void CmMy1(){mycurs("IDC_MYCUR1");}; void CmMy2(){mycurs("IDC_MYCUR2");}; DECLARE_RESPONSE_TABLE (TWndw); }; DEFINE_RESPONSE_TABLE1 (TWndw, TDecoratedFrame) EV_COMMAND (CM_CURSOR, CmCursor), EV_COMMAND(CM_ABOUT, CmAbout), EV_COMMAND(CM_MY1, CmMy1), EV_COMMAND(CM_MY2, CmMy2), END_RESPONSE_TABLE;
Теперь обратимся к конструктору главного окна:
Attr.W=GetSystemMetrics(SM_CXSCREEN)/2; Attr.H=GetSystemMetrics(SM_CYSCREEN)/2; Attr.X=GetSystemMetrics(SM_CXSCREEN)/2-Attr.W/2; Attr.Y=GetSystemMetrics(SM_CYSCREEN)/2-Attr.H/2;
Подключаем меню, описываем указатели на классы кнопок, разделителей и панелей:
TButtonGadget *but;
TSeparatorGadget *sep;
AssignMenu(MENU_1);
TControlBar *cntrlbar = new TControlBar(this, TControlBar::Vertical);
После того, как оформление главного окна приложения нами завершено, мы должны инициализировать буфер обмена:
memset (&transBuf,0,sizeof(transBuf)); transBuf.buttons[0] = TRUE;
void TWndw::CmCursor() { // Создать диалоговое окно Select Cursor. TDialog *dialog = new TDlg (this, CURSORDLG); // Отобразить окно. int result = dialog->Execute(); // Если пользователь выходит по кнопке ОК... if (result == IDOK) //то переключиться на выбранный курсор. ChangeCursor(); }
Приведенная выше функция реагирует на сообщение CM_CURSOR, которое посылается, когда пользователь открывает диалоговое окно. Тут мы создаем объект класса диалогового окна и вызываем метод Execute(). В качестве проверки используем условие if (result == IDOK), которое выполнится, если пользователь завершит диалоговое окно нажатием на кнопку ОК. Далее последует непосредственное изменение курсора через функцию ChangeCursor():
int selection; const char *cursor; HINSTANCE instance; HCURSOR hCursor; // Найти нужную радио-кнопку. for (int x = 0; x < 14; ++x) if (transBuf.buttons[x]) selection = x; // Найти тип курсора, выбранного пользователем. switch (selection) { case 0 : cursor = IDC_ARROW;break; case 1 : cursor = IDC_CROSS;break; case 2 : cursor = IDC_IBEAM;break; case 3 : cursor = IDC_ICON;break; case 4 : cursor = IDC_SIZE;break; case 5 : cursor = IDC_SIZENESW;break; case 6 : cursor = IDC_SIZENS;break; case 7 : cursor = IDC_SIZENWSE; break; case 8 : cursor = IDC_SIZEWE; break; case 9 : cursor = IDC_UPARROW; break; case 10 : cursor = IDC_WAIT; break; case 11 : cursor = "IDC_TARGET"; break; case 12 : cursor = "IDC_MYCUR1"; break; case 13 : cursor = "IDC_MYCUR2"; break; } // Если пользователь выбрал стандартный курсор ... if (selection >= 11) // Получить ссылку на этот экземпляр приложения. instance = *GetApplication() ; else // Иначе присвоить instance значение NULL, чтобы // загрузить системный курсор Windows. instance = NULL; // Получить ключ выбранного курсора. hCursor = LoadCursor (instance, cursor); // Изменить задаваемый по умолчанию курсор // оконного класса на выбранный. SetClassWord (GCW_HCURSOR, (WORD) hCursor);
Существует только одна выбранная кнопка и, следовательно, только одно значение в массиве будет равно TRUE. Когда ChangeCursor() находит значение выбранной кнопки, он использует его в операторе switch для нахождения затребованного пользователем курсора.
После того, как ChangeCursor() присвоил имя курсора символьному указателю cursor, можно установить новый курсор. Однако функция LoadCursor() в Windows API требует в качестве первого аргумента дескриптор экземпляра. Если пользователь выбрал собственный курсор, следует использовать дескриптор экземпляра для текущего приложения, в противном случае нуль, определяющий системный курсор, который затем использовать как первый аргумент LoadCursor().
Когда ChangeCursor() определил соответствующий дескриптор экземпляра приложения, он вызывает LoadCursor() с этим дескриптором и указателем на имя курсора, чтобы получить дескриптор курсора, который хранится в hCursor.
Главное окно программы наследует SetClassWord() из TWindow. Эта функция является OWL-версией для функции Windows API того же названия. Вы можете использовать ее для изменения значения любого слова в структуре WNDCLASS для Windows. Первый аргумент представляет собой индекс подлежащего замене слова; Windows имеет заранее предопределенные константы, которые можно использовать для этого аргумента.
Если же мы хотим изменить курсор не через диалоговое окно, а нажатием на кнопку в главном окне программы, вызовется подобная по структуре функция TWndw::mycurs(char *cursor).
Так как нажатие на определенную кнопку устанавливает определенный курсор, то мы можем просто передавать нашей функции в качестве параметра идентификатор курсора из файла ресурсов.
Код функции:
HINSTANCE instance; HCURSOR hCursor; instance = *GetApplication() ; // Получить ключ выбранного курсора. hCursor = LoadCursor (instance, cursor); // Изменить задаваемый по умолчанию курсор // оконного класса на выбранный. SetClassWord (GCW_HCURSOR, (WORD) hCursor);
В исходном коде файла ресурсов отметим лишь эту часть:
IDC_TARGET CURSOR "target.cur" IDC_MYCUR1 CURSOR "my1.cur" IDC_MYCUR2 CURSOR "my2.cur"
После подключения данного файла ресурсов к приложению программа ассоциирует три идентификатора с именами target.cur, my1.cur и my2.cur. Обращаясь к определенному идентификатору, мы можем показать требуемый курсор.
На следующем шаге мы рассмотрим создание небольшого графического редактора.