Шаг 42.
Библиотека OWL.
Добавление панелей инструментов

    На этом шаге мы рассмотрим алгоритм создания панели инструментов.

    Ничто не делает программу такой легкой в использовании, как быстрый доступ к важным командам. За долгие годы программисты, работающие в Windows, открыли, что самый простой способ обеспечения быстрого доступа к командам - это панели инструментов, которые являются специальными окнами, содержащими кнопки для представления определенных команд меню. Для того, чтобы выбрать команду, пользователь нажимает соответствующую кнопку, вместо того, чтобы выбрать меню, а затем выбрать команду из этого меню.

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

    Библиотека OWL обеспечивает как класс TDecoratedFrame для обработки панелей инструментов и других элементов оформления, так и класс TControlBar, представляющий собой панель инструментов библиотеки OWL. Класс TControlBar происходит из TGadgetWindow, который, в свою очередь, происходит из TWindow. Класс TGadgetWindow предлагает несложное включение и управление приспособлениями, которые являются специальными управляющими элементами класса TGadget, включая кнопки, надписи и разделители. Приспособления в библиотеке OWL представлены классами TButtonGadget, TTextGadget, TBitmapGadget, TSeparatorGadget и TControlGadget.

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

#include <owl\applicat.h>
#include <owl\decframe.h>
#include <owl\controlb.h>
#include <owl\buttonga.h>
#include "pr42_1.rc"

//Класс приложения
class TApp: public TApplication
{
  public:
	 TApp():TApplication(){}
	 void InitMainWindow();
};

//Класс основного окна
class TWndw : public TDecoratedFrame
{
  public:
	  TWndw(TWindow *parent, const char far *title, TWindow *client);
  protected:
	  void CmFileNew();
	  void CmFileOpen();
	  void CmFileSave();
	  void CmMax();
	  void CmRestore();
	  // Переключатели доступности команд.
	  void CmEnableMax (TCommandEnabler &commandEnabler);
	  void CmEnableRestore (TCommandEnabler &commandEnabler);

	  DECLARE_RESPONSE_TABLE(TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TDecoratedFrame)
	EV_COMMAND(CM_FILENEW, CmFileNew),
	EV_COMMAND(CM_FILEOPEN, CmFileOpen),
	EV_COMMAND(CM_FILESAVE, CmFileSave),
	EV_COMMAND(CM_MAX, CmMax),
	EV_COMMAND(CM_RESTORE, CmRestore),
	EV_COMMAND_ENABLE(CM_MAX, CmEnableMax),
	EV_COMMAND_ENABLE(CM_RESTORE, CmEnableRestore),
END_RESPONSE_TABLE;

TWndw::TWndw(TWindow *parent, const char far *title, TWindow *clientWnd):
		  TDecoratedFrame(parent,title, clientWnd)
{
  TButtonGadget *b;
  TSeparatorGadget *s;
  // Добавить меню к главному окну.
  AssignMenu("MENU_1");
  TControlBar *cntrlBar = new TControlBar(this);
  // Добавить приспособления к инструментальной линейке.
  b = new TButtonGadget(BMP_NEW, CM_FILENEW);
  cntrlBar->Insert(*b);
  b = new TButtonGadget(BMP_OPEN, CM_FILEOPEN);
  cntrlBar->Insert(*b);
  b = new TButtonGadget (BMP_SAVE, CM_FILESAVE);
  cntrlBar->Insert(*b);
  s = new TSeparatorGadget(10);
  cntrlBar->Insert(*s);
  b = new TButtonGadget(BMP_MAX, CM_MAX);
  cntrlBar->Insert(*b);
  b = new TButtonGadget(BMP_RESTORE, CM_RESTORE);
  cntrlBar->Insert(*b);
  s = new TSeparatorGadget(10);
  cntrlBar->Insert (*s);
  b = new TButtonGadget(BMP_EXIT, CM_EXIT);
  cntrlBar->Insert(*b);
  // Вставить инструментальную линейку в окно.
  Insert(*cntrlBar, TDecoratedFrame::Top);
  // Определить расположение и размеры окна.
  Attr.X = 50;
  Attr.Y = 50;
  Attr.W = GetSystemMetrics(SM_CXSCREEN) / 1.5;
  Attr.H = GetSystemMetrics(SM_CYSCREEN) / 1.5;
}

// TWndw::CmFileNew()
// Эта функция реагирует на сообщение CM_FILENEW,
// которое генерируется командой New из файлового
// меню или кнопкой NEW.
void TWndw::CmFileNew()
{
  MessageBox ("Выбран пункт FILENEW", "Сообщение",MB_OK);
}

// TWndw::CmFileOpen()
// Эта функция реагирует на сообщение CM_FILEOPEN,
// которое генерируется командой Open из файлового
// меню или кнопкой OPEN.
void TWndw::CmFileOpen()
{
  MessageBox ("Выбран пункт FILEOPEN", "Сообщение",MB_OK);
}

// TWndw::CmFileSave()
// Эта функция реагирует на сообщение CM_FILESAVE,
// которое генерируется командой Save из файлового
// меню или кнопкой SAVE.
void TWndw::CmFileSave()
{
  MessageBox ("Выбран пункт FILESAVE", "Сообщение",MB_OK);
}

//TWndw::CmRestore()
// Эта функция реагирует на сообщение СМ_RESTORE, которое
// генерируется кнопкой RES панели управления.
void TWndw::CmRestore()
{
  // Послать системное сообщение для восстановления размеров окна.
  SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0);
}


//TWndw::CmMax()
// Эта функция реагирует на сообщение СМ_МAХ, которое
// генерируется кнопкой МАХ панели управления.
void TWndw::CmMax()
{
  // Послать системное сообщение для максимизации окна.
  SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0);
}

//TWndw::CmEnableMax()
// Эта функция является переключателем доступности
// кнопки MAX панели управления.
void TWndw::CmEnableMax (TCommandEnabler &commandEnabler)
{
  // Разблокировать кнопку MAX, если окно не
  // максимизировано; в противном случае
  // заблокировать кнопку MAX.
  commandEnabler.Enable(!IsZoomed());
}

//TWndw::CmEnableRestore()
// Эта функция является переключателем доступности
// кнопки RES панели управления.
void TWndw::CmEnableRestore(TCommandEnabler &commandEnabler)
{
  // Разблокировать кнопку RES, если окно
  // максимизировано; в противном случае
  // заблокировать кнопку RES.
  commandEnabler.Enable(IsZoomed());
}

void TApp::InitMainWindow()
{
  // Окно TDecoratedFrame должно иметь окно-клиент.
  TWindow *client = new TWindow (0,0,0);
  // Сконструировать главное окно.
  TDecoratedFrame *wndw= new TWndw(0,"Создание панелей инструментов",client);
  // Установить значение указателя MainWindow приложения.
  SetMainWindow(wndw);
}

int OwlMain(int,char *[])
{
  return TApp().Run();
}

    Файл ресурсов:

#ifndef WORKSHOP_INVOKED
#include "windows.h"
#endif

#define BMP_NEW       1
#define BMP_OPEN      2
#define BMP_SAVE      3
#define BMP_MAX       4
#define BMP_RESTORE   5
#define BMP_EXIT      6
#define CM_FILESAVE   24333
#define CM_EXIT       24310
#define CM_FILEOPEN   24332
#define CM_FILENEW    24331
#define CM_MAX        201
#define CM_RESTORE    202

#ifdef RC_INVOKED

MENU_1 MENU
{
  POPUP "&File"
  {
	  MENUITEM "&New...",  CM_FILENEW
	  MENUITEM "&Open...", CM_FILEOPEN
	  MENUITEM "&Save...", CM_FILESAVE
	  MENUITEM SEPARATOR
	  MENUITEM "E&xit",    CM_EXIT
  }
}


BMP_NEW  BITMAP  "100131.bmp"
BMP_OPEN BITMAP "100143.bmp"
BMP_SAVE BITMAP "100144.bmp"
BMP_MAX BITMAP "100156.bmp"
BMP_RESTORE BITMAP "100130.bmp"
BMP_EXIT BITMAP "100139.bmp"

#endif
Текст этого приложения вместе с файлами рисунков можно взять здесь.

    Результат работы приложения:


Рис.1. Результат работы приложения

    При запуске этой программы вы увидите окно, показанное на рисунке 1. Программа имеет меню File с командами New, Open, Save и Exit. Программа содержит также панель инструментов, которая является не только "зеркалом" команд из меню, но добавляет также кнопки МАХ и RES, которые соответственно максимизируют и восстанавливают окно.

    Если вы выбрали команду New, Open или Save (из меню или панели инструментов), вы увидите окно сообщения, подтверждающее получение программой сообщения команды. Если вы щелкнули по кнопке МАХ, окно программы максимизируется, кнопка МАХ блокируется, а кнопка RES разблокируется. Щелчок по кнопке RES вернет окно к первоначальным размерам, блокирует кнопку RES и разблокирует кнопку МАХ.

    Опишем работу программы.

    В начале примера находятся следующие строки:

#include <owl\applicat.h>
#include <owl\decframe.h>
#include <owl\controlb.h>
#include <owl\buttonga.h>

    Это заголовочные файлы, которые должны быть включены, чтобы использовать в программе соответствующие классы библиотеки OWL. Вы уже ранее видели APPLICAT.H, но другие заголовочные файлы для вас являются незнакомыми. DECFRAME.H, CONTROLB.H, BUTTONGA.H - это заголовочные файлы для классов TDecoratedFrame, TControlBar и TButtonGadget.

    Посмотрите теперь на класс главного окна:

class TWndw : public TDecoratedFrame
{
  public:
	  TWndw(TWindow *parent, const char far *title, TWindow *client);
  protected:
	  void CmFileNew();
	  void CmFileOpen();
	  void CmFileSave();
	  void CmMax();
	  void CmRestore();
	  // Переключатели доступности команд.
	  void CmEnableMax (TCommandEnabler &commandEnabler);
	  void CmEnableRestore (TCommandEnabler &commandEnabler);

	  DECLARE_RESPONSE_TABLE(TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TDecoratedFrame)
	EV_COMMAND(CM_FILENEW, CmFileNew),
	EV_COMMAND(CM_FILEOPEN, CmFileOpen),
	EV_COMMAND(CM_FILESAVE, CmFileSave),
	EV_COMMAND(CM_MAX, CmMax),
	EV_COMMAND(CM_RESTORE, CmRestore),
	EV_COMMAND_ENABLE(CM_MAX, CmEnableMax),
	EV_COMMAND_ENABLE(CM_RESTORE, CmEnableRestore),
END_RESPONSE_TABLE;

    Класс главного окна программы, TWndw, происходит из класса TDecoratedFrame библиотеки OWL. Кроме обычного конструктора, данный класс содержит функции для каждой из команд меню, так же как и переключатели доступности команд для кнопок МАХ и RES. Как вы уже знаете, переключатели доступности команд выполняют блокировку, разблокировку и проверку пунктов команд меню. Переключатели доступности команд действуют также и для панелей инструментов, которые в действительности являются расширением строки меню приложения.

    Панель инструментов создается в конструкторе класса TWndw. Конструктор сначала назначает меню окна, после чего он создает новую панель инструментов, вызывая конструктор класса TControlBar:

    TControlBar   *cntrlBar  =  new  TControlBar(this);

    Конструктор класса TControlBar получает в качестве параметров указатель на родительское окно, ориентацию панели инструментов, указатель TGadgetWindowFont и указатель на TModule. Все эти параметры имеют значения, задаваемые по умолчанию. В этой программе задается только указатель на родительское окно панели инструментов как параметр для конструктора. Если вы захотите изменить ориентацию панели управления, то сможете сделать это, передав конструктору значение TControlBar::Vertical в качестве второго параметра. Значение этого параметра по умолчанию TControlBar::Horizontal.

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

  b = new TButtonGadget(BMP_NEW, CM_FILENEW);
  cntrlBar->Insert(*b);
  b = new TButtonGadget(BMP_OPEN, CM_FILEOPEN);
  cntrlBar->Insert(*b);
  b = new TButtonGadget (BMP_SAVE, CM_FILESAVE);
  cntrlBar->Insert(*b);
  s = new TSeparatorGadget(10);
  cntrlBar->Insert(*s);
  b = new TButtonGadget(BMP_MAX, CM_MAX);
  cntrlBar->Insert(*b);
  b = new TButtonGadget(BMP_RESTORE, CM_RESTORE);
  cntrlBar->Insert(*b);
  s = new TSeparatorGadget(10);
  cntrlBar->Insert (*s);
  b = new TButtonGadget(BMP_EXIT, CM_EXIT);
  cntrlBar->Insert(*b);

    Эта инструментальная линейка содержит приспособления-кнопки и приспособления-разделители. Приспособления-кнопки представляют собой кнопки, которые вы видите на панели управления. Приспособления-разделители обеспечивают наличие промежутков между наборами кнопок. Если вы посмотрите на рисунок 1, то вы увидите такую пропущенную область между кнопками SAVE и МАХ (третьей и четвертой), а также между кнопками RES и EXIT (пятой и шестой). Эти области и являются приспособлениями-разделителями.

    Чтобы добавить приспособления-кнопки в панель инструментов, программа сначала конструирует приспособление-кнопку, вызывая конструктор класса TButtonGadget, имеющий в качестве параметров идентификатор ресурса растрового изображения для изображения лицевой стороны кнопки, идентификатор команды меню, реализуемой кнопкой, тип кнопки, переменную, показывающую, разблокирована ли кнопка, состояние кнопки и булеву переменную, определяющую повторное нажатие.

    Все эти параметры, кроме первых двух, имеют задаваемые по умолчанию значения, так что их можно обычно проигнорировать. В данной программе конструктору передаются только два первых параметра. Идентификатор ресурса картинки определяет, как будет выглядеть кнопка. Вы должны использовать Resource Workshop (или другие графические программы) для создания картинки, представляющей внешний вид кнопки. Библиотека OWL берет на себя заботу об остальном, генерируя образ, который видит пользователь при нажатии или блокировке кнопки.

    После того, как программа сконструирует приспособление, она добавляет его в панель инструментов с помощью вызова функции Insert() панели инструментов (унаследованной из класса TGadgetWindow). В рассмотренном ранее фрагменте программы Insert() принимает единственный параметр, который являлся указателем на новое приспособление. Однако, Insert() имеет еще два других аргумента, имеющих задаваемые по умолчанию значения. Второй аргумент определяет, где будет расположено приспособление в панели инструментов, и может иметь вид либо TControlBar::After, либо TControlBar::Before. Третий аргумент, связанный со вторым, представляет собой указатель на то приспособление, рядом с которым будет помещено новое приспособление. Если эта величина равна нулю, новое приспособление появится в начале или в конце существующего приспособления в зависимости от второго аргумента (Before или After). Значения по умолчанию для второго и третьего аргументов равны соответственно After и 0.

    В рассмотренном выше фрагменте программы она разделяет группы кнопок приспособлениями-разделителями, сконструированными при вызове конструктора TSeparatorGadget. Единственным аргументом этого конструктора является размер приспособления-разделителя. Вы можете пропустить этот аргумент, приняв для конструктора значение по умолчанию, равное 6, или создать разделитель любого размера. Так же, как и для TButtonGadget программа добавляет TSeparatorGadget к инструментальной линейке, вызывая функцию Insert() панели инструментов. После того, как панель инструментов сформирована, программа добавляет ее к родительскому окну, обращаясь к функции-члену Insert() родительского окна:

    Insert(*cntrlBar,   TDecoratedFrame::Top);

    Данная версия Insert() имеет два аргумента: указатель включаемую инструментальную линейку и ее расположение. Вторым аргументом могут быть TDecoratedFrame::Тор (по умолчанию), TDecoratedFrame::Bottom, TDecoratedFrame::Left или TDecoratedFrame::Right.


    Замечания.
  1. Для того, чтобы послать сообщение окнам приложения, можно использовать либо функцию SendMessage(), либо функцию PostMeasage(). Однако, если SendMessage() посылает сообщение непосредственно в окно, то PostMeasage() посылает сообщение в очередь оконных сообщений. Это означает, что сообщения SendMessage() обрабатываются так быстро, как только возможно, в то время как сообщения от PostMeasage() ожидают своей очереди в очереди сообщений. Обе функции имеют одинаковые аргументы.
  2. Определяя местоположение панели инструментов, вы должны создать эту панель, указав ее ориентацию с помощью TControlBar::Horizontal или TControlBar::Vertical и передать соответствующее расположение (TDecoratedFrame::Top, TDecoratedFrame::Bottom, TDecoratedFrame::Left или TDecoratedFrame::Right) в качестве второго аргумента в вызове функции-члена Insert() родительского окна. Очевидно, нет никакого смысла устанавливать одновременно TControlBar::Horizontal или TDecoratedFrame::Left, ибо это может привести к весьма странным результатам.

    Наконец, после того, как инструментальная линейка добавлена к окну, конструктор TWndw устанавливает положение и размер окна на экране:

  Attr.X = 50;
  Attr.Y = 50;
  Attr.W = GetSystemMetrics(SM_CXSCREEN) / 1.5;
  Attr.H = GetSystemMetrics(SM_CYSCREEN) / 1.5;

    Функции CmFileNew(), CmFileOpen() и CmFileSave() не выполняют никаких других действий, кроме отображения окна сообщений, когда пользователь выбирает команду New, Open или Save из меню File или панели управления. Но если пользователь выбрал кнопку МАХ на панели управления, библиотека вызывает СmМах():

void TWndw::CmMax()
{
  // Послать системное сообщение для максимизации окна.
  SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0);
}

    Эта функция вызывает SendMessage(), чтобы послать сообщение WM_SYSCOMMAND с SC_MAXIMIZE wParam главному окну. В этом случае третий аргумент (параметр сообщения lParam) игнорируется и устанавливается в 0. Хотя SendMessage() представляет собой функцию, наследуемую из класса TWindow, в действительности это OWL-версия API-функции Windows с тем же именем.

    Функция CmRestore() действует аналогично СmМах(). Различие состоит в том, что wParam для WM_SYSCOMMAND равен SC_RESTORE, а не SC_MAXIMIZE:

void TWndw::CmRestore()
{
  // Послать системное сообщение для восстановления размеров окна.
  SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0);
}

    Наконец, кнопки МАХ и RES разблокируются и блокируются соответствующим образом при помощи их переключателей доступности команд. Например, CmEnableMax() управляет кнопкой МАХ:

void TWndw::CmEnableMax (TCommandEnabler &commandEnabler)
{
  // Разблокировать кнопку MAX, если окно не
  // максимизировано; в противном случае
  // заблокировать кнопку MAX.
  commandEnabler.Enable(!IsZoomed());
}

    Эта функция разблокирует кнопку, если IsZoomed() возвращает значение FALSE, и блокирует кнопку, если IsZoomed() возвращает значение TRUE. IsZoomed() является функцией, наследуемой из класса TWindow, и она возвращает значение булевой переменной, указывая, произошла ли максимизация окна.

    Переключатель доступности команды CmEnableRestore() действует противоположно CmEnableMax(), разблокируя кнопку RES, если окно максимизировано, и блокирует кнопку RES, если окно не максимизировано:

void TWndw::CmEnableRestore(TCommandEnabler &commandEnabler)
{
  // Разблокировать кнопку RES, если окно
  // максимизировано; в противном случае
  // заблокировать кнопку RES.
  commandEnabler.Enable(IsZoomed());
}

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

    На следующем шаге мы рассмотрим добавление строки сообщения.




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