Шаг 50.
Библиотека OWL.
Управление линейками прокрутки в Windows

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

    Во многих Windows-программах данные, представляющие текущий документ, не соответствуют размерам отображения на экране. Для решения этой проблемы Windows использует линейки прокрутки, которые позволяют пользователю устанавливать положение отображаемой на экран области внутри текущего документа. Добавление линеек прокрутки состоит в добавлении типов окон WS_VSCROLL и WS_HSCROLL к окну, которое вы хотите прокручивать. Однако именно вы занимаетесь инициализацией и соответствующей корректировкой линеек прокрутки. К счастью, ObjectWindows включает класс TScroller, который инкапсулирует функции и данные для управления линейками прокрутки окна.

    Следующий пример демонстрирует использование класса OWL TScroller.

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dialog.h>
#include <owl\scroller.h>
#include <owl\edit.h>
#include <owl\dc.h>
#include "pr50_1.rc"

// Буфер обмена.
struct
{
  char unitsEdit[3];
  char lineEdit[3];
  char hRangeEdit[4];
  char vRangeEdit[4]; 
} transBuf;
// Структура для сохранения данных изображения.
struct rec
{
  int color;
  int x;
  int y;
};

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

// Класс главного окна.
class TWndw : public  TFrameWindow
{
  public:
	 TWndw (TWindow *parent, const char far *title);
  protected:
         rec  recArray[100];
         void Paint(TDC &dc, BOOL, TRect&); 
         void CmSetScroll(); 
         void SetScroller();

	 DECLARE_RESPONSE_TABLE (TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	EV_COMMAND (CM_SETSCROLL, CmSetScroll),
END_RESPONSE_TABLE;

// Класс диалога Scroll Values.
class TDlg : public TDialog
{
  public:
	 TDlg(TWindow *parent, TResId resId);
};

// TWndw::TWndw()
// Это конструктор главного окна.
TWndw::TWndw(TWindow *parent, const char far *title):
			 TFrameWindow (parent, title)
{
  // Добавить вертикальную и горизонтальную линейки
  // прокрутки к основному окну.
  Attr.Style |= WS_VSCROLL | WS_HSCROLL;
  // Определить расположение и размеры окна.
  Attr.X = 20;
  Attr.Y = 20;
  Attr.W = 400;
  Attr.H = 300;
  // Создать OWL-объект прокрутки для окна.
  Scroller = new TScroller (this, 1, 1, 640, 480);
  // Добавить меню к окну.
  AssignMenu(MENU_1);
  // Инициализировать массив изображений данными,
  // необходимыми для отображения 100 цветных прямоугольников.
  randomize();
  for (int i = 0; i < 100; ++i)
  {
    recArray[i].color = random(16); 
    recArray[i].x = random(619); 
    recArray[i].y = random(459);
  }
}

// TWndw::Paint()
// Это функция, которая переопределяет функцию
// Paint() из TWindow, отвечая на сообщение
// WM_PAINT, посылаемое системой Windows окну,
// если оно должно перерисоваться.
void TWndw::Paint (TDC& dc, BOOL, TRect&)
{
  // Нарисовать 100 прямоугольников. 
  for (int i = 0; i < 100; ++i)
  {
    // Создать кисть соответствующего цвета для
    // текущего прямоугольника.
    TBrush *brush = new TBrush (recArray[i].color);
    // Выбрать новую кисть в контекст устройства. 
    dc.SelectObject (*brush);
    // Нарисовать прямоугольник.
    dc.Rectangle (recArray[i].x, recArray[i].y,
           recArray[i].x + 20, recArray[i].y + 20);
    // Избавиться от новой кисти. 
    delete brush;
  }
}

// TWndw::CmSetScroll()
// Эта функция реагирует на сообщение CM_SETSCROLL,
// посылаемое окну, если пользователь
// выбрал команду SetScroller из меню Scroller.
void TWndw::CmSetScroll() 
{
  // Создать диалоговое окно ScrollValues.
  TDialog *dialog = new TDlg(this, SCROLLDLG);
  // Скопировать текущие установки прокрутки в
  // буфер обмена диалогового окна ScrollValues.
  char s[10];
  itoa(Scroller->XUnit, s, 10); 
  strcpy(transBuf.unitsEdit, s); 
  itoa(Scroller->XLine, s, 10); 
  strcpy(transBuf.lineEdit, s); 
  itoa(Scroller->XRange, s, 10); 
  strcpy(transBuf.hRangeEdit, s); 
  itoa(Scroller->YRange, s, 10); 
  strcpy(transBuf.vRangeEdit, s);
  // Выполнить диалоговое окно. 
  int result = dialog->Execute();
  // Если пользователь выходит из диалогового окна по 
  // кнопке ОК, установить новые значения прокрутки. 
  if (result == IDOK)
    SetScroller();
}

// TWndw::SetScroller()
// Эта функция назначает объекту TScroller значения,
// введенные пользователем в диалоговом окне Scroll Values.
void TWndw::SetScroller()
{
  // Установить новые значения XUnit и YUnit.
  int units = atoi (transBuf.unitsEdit);
  if (units > 0) Scroller->SetUnits(units,units);
  // Установить новые значения XLine и YLine.
  int line = atoi (transBuf.lineEdit);
  if (line > 0) 
  {
    Scroller->XLine = line;
    Scroller->YLine = line;
  }
  // Получить новые диапазоны прокрутки.
  long hRange = atoi (transBuf.hRangeEdit);
  long vRange = atoi (transBuf.vRangeEdit);
  // Установить новые диапазоны прокрутки.
  Scroller->SetRange(hRange, vRange);
}

//**************************************
// Реализация класса TDlg.
//**************************************
// TDlg::TDlg()
// Это конструктор диалогового окна ScrollValues.
TDlg::TDlg(TWindow *parent, TResId resId): TDialog (parent, resId)
{
  // Создать OWL-объекты TEDIT для каждой области 
  // ввода диалогового окна.
  new TEdit(this, ID_UNITS, sizeof(transBuf.unitsEdit));
  new TEdit(this, ID_LINE, sizeof(transBuf.lineEdit));
  new TEdit(this, ID_HRANGE, sizeof(transBuf.hRangeEdit));
  new TEdit(this, ID_VRANGE, sizeof(transBuf.vRangeEdit));
  // Установить адрес буфера обмена.
  TransferBuffer = &transBuf;
}

void TApp::InitMainWindow()
{
  TFrameWindow *wndw = new TWndw (0,"Полосы прокрутки");
  SetMainWindow(wndw);
}

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

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

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

#define MENU_1          100
#define CM_EXIT         24310
#define CM_SETSCROLL    200
#define SCROLLDLG       201
#define ID_UNITS        202
#define ID_LINE         203
#define ID_HRANGE       204
#define ID_VRANGE       205


#ifdef RC_INVOKED

SCROLLDLG DIALOG 25, 68, 127, 104
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
      WS_CAPTION | WS_SYSMENU 
CAPTION "Scroll Values" 
FONT 8, "MS Sans Serif"
{
  EDITTEXT ID_UNITS, 41, 13, 16, 12
  EDITTEXT ID_LINE, 41, 35, 16, 12
  EDITTEXT ID_HRANGE, 41, 57, 23, 12
  EDITTEXT ID_VRANGE, 41, 79, 23, 12
  DEFPUSHBUTTON "Ok", IDOK, 69, 12, 50, 14
  PUSHBUTTON "Cancel", IDCANCEL, 69, 30, 50, 14
  LTEXT "Units:", -1, 18, 16, 19, 8
  LTEXT "Line:", -1, 19, 38, 21, 8
  LTEXT "H-Range:", -1, 7, 60, 31, 8
  LTEXT "V-Range:", -1, 7, 81, 31, 8
}

MENU_1 MENU
{
  POPUP "&File"
  {
	  MENUITEM "E&xit", CM_EXIT
  }
  POPUP "&Scroll"
  {
	  MENUITEM "&Set Scroller...", CM_SETSCROLL
  }
}

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

    После запуска данной программы вы увидите окно, приведенное на рисунке 1.


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

    Это окно содержит изображение размером 640*480 пикселей, составленное из 100 цветных прямоугольников. Используйте линейки прокрутки для просмотра частей изображения. Если вы знакомы с работой линеек прокрутки в отдельном окне, выберите команду Set Scroller из меню Scroll. После этого появится окно диалога Scroll Values, показанное на рисунке 2.


Рис.2. Настройка параметров полос прокрутки

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

    Атрибут Units (который в действительности представляет как горизонтальные, так и вертикальные единицы измерения) - определяет наименьшее расстояние, которое может прокрутить линейка. При запуске программы значение Units устанавливается равным 1, т.е. окно может прокрутить за один раз один пиксель.

    Атрибут Line (в данном диалоговом окне представляет как горизонтальные, так и вертикальные строки) задает количество пикселей - единиц измерения, прокручиваемых окном при нажатии пользователем кнопки со стрелкой. При старте программы эта величина устанавливается равной 1, что означает прокрутку в 1 пиксель для каждой линии. Если вы измените значение, задаваемое в позиции Units, на 10, окно прокрутит 10 пикселей для каждой строки. Другими словами, для вычисления числа прокручиваемых окном пикселей для каждой строки необходимо умножить значение в Units на значение, записанное в пункте Line.

    Атрибуты H-Range и V-Range представляют самые большие расстояния по горизонтали и по вертикали, которые может прокрутить окно. Так как отображаемое этой программой на экране изображение имеет размеры 640*480 пикселей, то 640 и 480 используются соответственно для горизонтального и вертикального диапазонов. Величина диапазонов измеряется в условных единицах измерения, поэтому для получения ожидаемого результата вы должны модифицировать диапазон при изменении единиц измерения прокрутки.

    Например, если вы изменили Units на 10, вам следует также изменить Н-диапазон на 64, а V-диапазон на 48. Если этого не сделать, диапазоны будут вычислены, как и линии, умножением единиц измерения на значение диапазона. Другими словами, если вы изменили Units на 10 и оставили прежние диапазоны как 640 и 480, то тогда границы прокрутки будут определяться как 6400 и 4800 пикселей, так как значение единицы измерения стало в 10 раз больше.


    Замечания.
  1. Так как программа примера выполняется в режиме отображения ММ_ТЕХТ, единицами измерения для линейки прокрутки являются пиксели. Изменяя режим отображения, вы также изменяете единицы измерения для линеек прокрутки.
  2. Вы можете отключить линейку прокрутки, установив значение диапазона, равное 0. Например, запустите приведенную программуи установите область редактирования H-Scroll равной 0. Горизонтальная полоса прокрутки исчезнет. Вернуть лининейку можно, установив значение ее горизонтального диапазона большим, чем 0.

    Когда вы нажимаете кнопку мыши на линейке прокрутки, окно прокрутит фрагмент величиной в страницу, определяемый высотой и шириной окна в зависимости от того, какая выбрана прокрутка - горизонтальная или вертикальная. Линейки прокрутки имеют также вертикальные и горизонтальные атрибуты страницы. Они не появляются в окне диалога, так как OWL отрабатывает их автоматически, модифицируя при изменении размера окна.

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

struct
{
  char unitsEdit[3];
  char lineEdit[3];
  char hRangeEdit[4];
  char vRangeEdit[4]; 
} transBuf;

    Структура буфера обмена содержит символьный массив для каждой области ввода в диалоговом окне. Программа связывает области редактирования с OWL-объектами в конструкторе диалогового окна:

TDlg::TDlg(TWindow *parent, TResId resId): TDialog (parent, resId)
{
  // Создать OWL-объекты TEDIT для каждой области 
  // ввода диалогового окна.
  new TEdit(this, ID_UNITS, sizeof(transBuf.unitsEdit));
  new TEdit(this, ID_LINE, sizeof(transBuf.lineEdit));
  new TEdit(this, ID_HRANGE, sizeof(transBuf.hRangeEdit));
  new TEdit(this, ID_VRANGE, sizeof(transBuf.vRangeEdit));
  // Установить адрес буфера обмена.
  TransferBuffer = &transBuf;
}

    Конструктор создает OWL-объекты областей ввода в том же самом порядке, в каком они появляются в буфере обмена, после чего программа присваивает данному полю TransferBuffer диалогового окна значение адреса буфера обмена.

    В начале текста программы объявляется структура, хранящая информацию о прямоугольниках, отображаемых в окне:

struct rec
{
  int color;
  int x;
  int y;
};

    Член-данное color - это цвет прямоугольника, а х и у - координаты прямоугольника в окне. Класс главного окна определяет массив из 100 таких структур прямоугольников:

         rec  recArray[100];

    Конструктору главного окна в этой программе приходится выполнять большую работу:

TWndw::TWndw(TWindow *parent, const char far *title):
			 TFrameWindow (parent, title)
{
  // Добавить вертикальную и горизонтальную линейки
  // прокрутки к основному окну.
  Attr.Style |= WS_VSCROLL | WS_HSCROLL;
  // Определить расположение и размеры окна.
  Attr.X = 20;
  Attr.Y = 20;
  Attr.W = 400;
  Attr.H = 300;
  // Создать OWL-объект прокрутки для окна.
  Scroller = new TScroller (this, 1, 1, 640, 480);
  // Добавить меню к окну.
  AssignMenu(MENU_1);
  // Инициализировать массив изображений данными,
  // необходимыми для отображения 100 цветных прямоугольников.
  randomize();
  for (int i = 0; i < 100; ++i)
  {
    recArray[i].color = random(16); 
    recArray[i].x = random(619); 
    recArray[i].y = random(459);
  }
}

    Конструктор сначала добавляет признаки типа WS_VSCROLL и WS_HSCROLL к флагам стиля главного окна. Это все, что вам необходимо сделать, чтобы в окне появились вертикальная и горизонтальная линейки прокрутки. Затем после установления положения и размера окна конструктор создает OWL-объект TScroller, представляющий как горизонтальную, так и вертикальную линейки прокрутки. Конструктор TScroller принимает в качестве аргументов указатель на родительское окно и значения для полей XUnit, YUnit, XRange и YRange. Эти члены-данные не что иное, как единицы измерения (Units) и диапазоны по горизонтали и вертикали для линеек прокрутки. Указатель на новый объект TScroller помещается в член-данное Scroller главного окна. Все OWL-окна имеют член-данное Scroller, который содержит указатель на объект TScroller окна.

    После установки линеек прокрутки конструктор заполняет массив прямоугольников 100 прямоугольниками со случайными координатами и цветами. Это именно те прямоугольники, которые отображает оконная функция Paint():

void TWndw::Paint (TDC& dc, BOOL, TRect&)
{
  // Нарисовать 100 прямоугольников. 
  for (int i = 0; i < 100; ++i)
  {
    // Создать кисть соответствующего цвета для
    // текущего прямоугольника.
    TBrush *brush = new TBrush (recArray[i].color);
    // Выбрать новую кисть в контекст устройства. 
    dc.SelectObject (*brush);
    // Нарисовать прямоугольник.
    dc.Rectangle (recArray[i].x, recArray[i].y,
           recArray[i].x + 20, recArray[i].y + 20);
    // Избавиться от новой кисти. 
    delete brush;
  }
}

    Paint() использует цикл for для работы с массивом recArray[], который содержит информацию, необходимую для отображения 100 оконных прямоугольников. При каждой итерации цикла Paint() создает новый объект ТВrush с цветом, находящимся в recArray[i].color, выбирает кисть в контекст устройства для раскрашивания, рисует прямоугольник с координатами, находящимися в recArray[i].x и recArray[i].у, и, наконец, удаляет новый объект кисти.

    Заметим, что Paint() предполагает, что рисует все изображение размером 640*480, составленное из всех 100 прямоугольников. Однако OWL гарантирует, что в окне появится только часть экранного изображения, которая определена текущим положением линеек прокрутки. Больше от вас ничего не требуется.

    Если пользователь выбрал в меню Scroll команду Set Scroller, OWL вызывает функцию-член главного окна с именем CmSetScroll():

void TWndw::CmSetScroll() 
{
  // Создать диалоговое окно ScrollValues.
  TDialog *dialog = new TDlg(this, SCROLLDLG);
  // Скопировать текущие установки прокрутки в
  // буфер обмена диалогового окна ScrollValues.
  char s[10];
  itoa(Scroller->XUnit, s, 10); 
  strcpy(transBuf.unitsEdit, s); 
  itoa(Scroller->XLine, s, 10); 
  strcpy(transBuf.lineEdit, s); 
  itoa(Scroller->XRange, s, 10); 
  strcpy(transBuf.hRangeEdit, s); 
  itoa(Scroller->YRange, s, 10); 
  strcpy(transBuf.vRangeEdit, s);
  // Выполнить диалоговое окно. 
  int result = dialog->Execute();
  // Если пользователь выходит из диалогового окна по 
  // кнопке ОК, установить новые значения прокрутки. 
  if (result == IDOK)
    SetScroller();
}

    Сначала функция конструирует диалоговое окно, после чего она использует библиотечную функцию itoa() для преобразования поля прокрутки XUnit, YUnit, XLine, YLine, XRange, YRange в строки, а затем с помощью strcopy() копирует эти строки в буфер обмена диалогового окна transBuf и отображает диалоговое окно, вызывая функцию-член Execute() диалогового окна.

    Если пользователь выходит из диалогового окна по кнопке ОК, CmSetScroller() вызывает функцию SetScroller(), чтобы установить соответствующие поля объекта скроллера:

void TWndw::SetScroller()
{
  // Установить новые значения XUnit и YUnit.
  int units = atoi (transBuf.unitsEdit);
  if (units > 0) Scroller->SetUnits(units,units);
  // Установить новые значения XLine и YLine.
  int line = atoi (transBuf.lineEdit);
  if (line > 0) 
  {
    Scroller->XLine = line;
    Scroller->YLine = line;
  }
  // Получить новые диапазоны прокрутки.
  long hRange = atoi (transBuf.hRangeEdit);
  long vRange = atoi (transBuf.vRangeEdit);
  // Установить новые диапазоны прокрутки.
  Scroller->SetRange(hRange, vRange);
}

    Эта функция сначала вызывает библиотечную функцию atoi() для преобразования содержимого поля ввода Units в переменную целого типа. Если Units больше 0, программа вызывает функцию SetUnits() из TScroller для установки новых значений полей прокрутки XUnit и YUnit. Затем функция аналогичным образом обрабатывает содержимое области ввода Line. Однако, из-за того, что класс TScroller не имеет функции-члена для модификации своих членов-данных XLine и YLine, программа получает доступ к XLine и YLine непосредственно через указатель Scroller.

    Наконец, SetScroller() устанавливает вертикальный и горизонтальный диапазоны линеек прокрутки с помощью преобразования содержимого полей ввода H-Range и V-Range к длинному целому. Затем вызывается функция-член SetRange() класса TScroller с результатами. SetScroller() позволяет пользователю установить эти величины в ноль, поскольку это отключает соответствующую линейку прокрутки. Для включения линеек прокрутки пользователь должен задать значение диапазона линеек прокрутки больше нуля.

    Со следующего шага мы начнем рассматривать более подробно класс TDC.




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