Шаг 79.
Библиотека OWL.
Программа для выбора вида курсора

    На этом шаге мы рассмотрим приложение, позволяющее выбирать внешний вид курсора.

    Студент 4 курса (2010-11 уч.год) Войцеховский Егор создал приложение, позволяющее выбирать внешний вид курсора средствами бибилиотеки OWL.

    Приложение представляет собой программу, которая позволяет вам просмотреть системные курсоры Windows. Кроме того, программа позволяет выбирать свой собственный (пользовательский) курсор.

    Внешний вид приложения изображен на рисунке 1:


Рис.1. Внешний вид приложения

    Изменять курсоры можно двумя способами:

В первом случае открывается диалоговое окно, содержащее 11 системных и 3 пользовательских курсора. Вызвать диалог можно через меню Cursor | Change Cursor, либо щелчком по кнопке CURSORS на левой панели окна (рисунок 2) . Кнопки на правой панели переключают между собой 2 пользовательских курсора


Рис.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;
В таблице реакций связываем нажатия на кнопки (CM_MY1, CM_MY2) и вызов диалогового окна (CM_CURSOR) с соответствующими командами.

    Теперь обратимся к конструктору главного окна:

  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;
Таким образом, всем элементам буфера обмена присваивается значение FALSE, однако значение первого (нулевого) элемента устанавливается в 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. Обращаясь к определенному идентификатору, мы можем показать требуемый курсор.

    На следующем шаге мы рассмотрим создание небольшого графического редактора.




Предыдущий шаг Содержание Следующий шаг