2004 г.
Помещение репликанта на Shelf Deskbar'а
Алексей Сариков,
avix.pp.ru
Статья опубликована на сайте www.qube.ru
Статья рассматривает процесс создания репликанта Deskbar'а - приложения для BeOS, помещающегося на Shelf'е Deskbar'а.
Определим некоторые термины, используемые в изложении.
Deskbar - системная программа, содержащая меню доступа к приложениям и настройкам системы (меню BeOS), а также список запущенных задач.
Shelf Deskbar'а - прямоугольная область темно-серого цвета в окне Deskbar'а, на которой расположены часы. В этой области размещают свои иконки приложения. Как правило, это системные утилиты, стартующие при запуске системы и выполняющие какие-либо управляющие функции все время работы системы.
Репликант Deskbar'а - собственно то приложение, которое помещает (встраивает) свою иконку на Shelf Deskbar'а. В более общем случае репликант - приложение, встраиваемое в другое приложение. Примерами репликантов Deskbar'а могут служить утилиты Pulse, Switcher.
Создаваемый нами репликант будет выполнять следующие действия. При нажатии левой клавиши мыши на иконке репликанта выдается окно с информацией о программе (окно About). При нажатии на иконке правой клавиши мыши появляется контекстное меню, содержащее пункты About и Quit. Выбор пункта About вызывает окно About, выбор пункта Quit убирает репликант из Shelf'а.
Приступим к разработке программы. Запустим исполняемый файл среды разработки BeIDE (/boot/develop/BeIDE/BeIDE). Выберем в меню File/New Project. В появившемся окне выбираем тип приложения BeApp, и нажимаем кнопку Create. Во вновь появившемся окне указываем желаемое размещение проекта и имя (в нашем случае пишем имя DeskbarReplicantDemo.proj). Мы создали файл проекта для нашего репликанта. В проект уже включены библиотеки libbe.so и libroot.so, необходимые для работы программы.
Выбираем в меню File/New Text. Создается файл, в котором будем записывать непосредственно текст программы. Сохраняем этот файл (меню File/Save As) под именем DeskbarReplicantDemo.cpp в каталоге с файлом проекта. В окне сохранения ставим галочку возле надписи Add to Project - в этом случае файл будет добавлен в окно проекта.
Зададим имя исполняемому файлу. Для этого в окне проекта выбираем меню Edit/Project Settings. В появившемся окне указываем элемент списка x86 ELF Project, а в правой части окна в графе FileName пишем DeskbarReplicantDemo. Нажимаем кнопку Save и даем утвердительный ответ на вопрос о необходимости повторной линковки проекта.
Текст программы начинаем писать с функции main - главной функции любой C/C++-программы:
int main(void)
{
DRDAppClass DRDApp;
return 1;
}
DRDAppClass является классом приложения
- наследником класса BApplication:
class DRDAppClass:public BApplication
{
public:
DRDAppClass();
};
Для возможности использования этого класса следует включить в текст программы заголовочный файл
#include < Application.h>
Класс DRDAppClass содержит единственную функцию - конструктор класса, поскольку его задачей является всего лишь инициализация объекта класса-наследника BView, помещаемого на Shelf Deskbar'а. После чего приложение завершает свою работу (функция main не содержит вызова функции Run() объекта класса DRDAppClass).
DRDAppClass::DRDAppClass():BApplication(APP_NAME)
{
BDeskbar d;
if(d.HasItem(VIEW_NAME)) return;
DRDAppViewClass *DRDAppView;
DRDAppView=new DRDAppViewClass();
status_t err=d.AddItem(DRDAppView);
if(err!=B_OK)
{
char temp[255];
sprintf(temp, "Error starting: %s", strerror(err));
BAlert *alert = new BAlert("", temp, "OK");
alert->Go();
}
}
Сигнатуру приложения APP_NAME и имя VIEW_NAME объекта, помещаемого на Shelf, задаем следующим образом:
#define APP_NAME "application/x-vnd.DeskbarReplicantDemo"
#define VIEW_NAME "DRDAppView"
В первых двух строках конструктора класса DRDAppClass выполняется проверка наличия на Shelf'е объекта с именем VIEW_NAME и выход из конструктора, если такой объект уже существует. Таким образом предотвращается помещение на Shelf нескольких экземпляров одного и того же объекта.
Для того, чтобы иметь доступ к классу BDeskbar, включаем заголовочный файл
#include < Deskbar.h>
Далее в конструкторе происходит создание объекта класса DRDAppViewClass, наследованного от класса BView, и попытка помещения его на Shelf. В случае неудачи выводится окно с сообщением об ошибке. Для возможности вывода сообщения и работы с классом BView включаем заголовочные файлы
#include < Alert.h>
#include < String.h>
#include < stdio.h>
#include < View.h>
Класс DRDAppViewClass объявляется следующим образом:
class DRDAppViewClass:public BView
{
public:
DRDAppViewClass();
DRDAppViewClass(BMessage *msg);
virtual status_t Archive(BMessage
*data, bool deep=true) const;
virtual void AttachedToWindow();
virtual void Draw(BRect p);
static DRDAppViewClass *
Instantiate(BMessage *data);
virtual void MouseDown(BPoint p);
virtual bool QuitRequested(void);
private:
BPopUpMenu *m;
BBitmap *bg;
};
Опишем последовательно функции и структуры данных, которые содержатся в классе DRDAppViewClass.
Конструктор класса без параметров создает первоначальный объект - область размером 16x16 пикселов с именем VIEW_NAME, определенным ранее.
DRDAppViewClass::DRDAppViewClass()
:BView(BRect(0,0,15,15),
VIEW_NAME,
B_FOLLOW_LEFT,
B_WILL_DRAW)
{
return;
}
Класс DRDAppViewClass, объект которого помещается на Shelf Deskbar'a, должен быть архивируемым (archivable). Размещение выполняется посредством архивации и последующей разархивации созданного объекта класса. Поэтому в описание класса включены функции Archive, Instantiate и конструктор DRDAppViewClass с параметром BMessage *msg. Функция Archive осуществляет упаковку объекта, созданного конструктором без параметров, в переменную-объект класса BMessage*:
status_t DRDAppViewClass::Archive
(BMessage *data, bool deep=true) const
{
BView::Archive(data, deep);
data->AddString("add_on",APP_NAME);
data->AddString("class", VIEW_NAME);
return B_OK;
}
В архив - объект класса BMessage* - добавляются имя объекта класса DRDAppViewClass для контроля при последующей распаковке и сигнатура приложения. Параметр deep отвечает за включение в архив всех объектов, принадлежащих архивируемому (true - включать, false - нет). Для нашего случая значение deep не играет роли.
Функция Instantiate распаковывает объект класса BMessage*, восстанавливая ранее созданный объект класса DRDAppViewClass. В случае, если переданный параметр (объект класса BMessage*) не является архивом объекта класса с именем VIEW_NAME, возвращается NULL.
DRDAppViewClass *DRDAppViewClass::
Instantiate(BMessage *data)
{
if(!validate_instantiation
(data, VIEW_NAME)) return NULL;
return new DRDAppViewClass(data);
};
Функции Archive и Instantiate явно в программе не вызываются. По-видимому, их вызов происходит в функции AddItem класса BDeskbar.
Вызов конструктора класса DRDAppViewClass(BMessage* msg) в функции Instantiate создает непосредственно тот объект, который будет помещен на Shelf Deskbar'а. Именно в этом конструкторе мы создадим и инициализируем все необходимые для работы объекты и переменные:
DRDAppViewClass::DRDAppViewClass
(BMessage *msg):BView(msg)
{
m=new BPopUpMenu("PopUpMenu",false,false);
m->AddItem(new BMenuItem("About",
new BMessage(B_ABOUT_REQUESTED)));
m->AddSeparatorItem();
m->AddItem(new BMenuItem("Quit",
new BMessage(B_QUIT_REQUESTED)));
m->SetTargetForItems(this);
bg=new BBitmap(BRect(0,0,15,15),B_CMAP8);
bg->SetBits(bmpdata,256,0,B_CMAP8);
return;
};
Необходимыми объектами являются выпадающее меню и иконка, отображаемая на Shelf'е. Меню создается конструктором BPopUpMenu и содержит два пункта. Выбор пункта About приводит к появлению информации о программе, выбор пункта Quit приводит к удалению репликанта из Shelf'а. Для обеспечения возможности работы с меню включаем в текст программы соответствующие заголовочные файлы:
#include < MenuItem.h>
#include < PopUpMenu.h>
Иконка, отображаемая на Shelf'е, занимает квадрат размером 16x16 пикселов и представляет собой объект класса BBitmap. Рисунок иконки является 256-цветным. Данные для его инициализации берутся из массива bmpdata размером 256 байт:
unsigned char bmpdata[256]={......};
// в фигурных скобках находятся 256 чисел, каждое в интервале от 0 до 255.
Установить соответствия между цветами и их числовыми значениями для режима B_CMAP8 можно с помощью программы 256. Также можно преобразовать уже готовый рисунок в заголовочный файл с помощью программы ImageToHeader.
Вывод рисунка иконки осуществляется функцией Draw(), которая устанавливает иконку в качестве фона для объекта класса DRDAppViewClass:
void DRDAppViewClass::Draw(BRect p)
{
SetViewBitmap(bg);
Invalidate(BRect(0,0,15,15));
}
Функция Draw() вызывается системой каждый раз при необходимости перерисовать изображение, а программой - при присоединении объекта класса DRDAppViewClass к Shelf'у в функции AttachedToWindow:
void DRDAppViewClass::AttachedToWindow()
{
Draw(BRect(0,0,15,15));
}
Осталось рассмотреть две функции класса DRDAppViewClass. Функция MouseDown() вызывается системой при нажатии на иконке клавиши мыши. В ней производится обработка нажатий:
void DRDAppViewClass::MouseDown(BPoint p)
{
uint32 b;
BPoint p1;
GetMouse(&p1, &b);
if(b==B_SECONDARY_MOUSE_BUTTON)
{
p1.Set(0,0);
ConvertToScreen(&p1);
BRect r=BRect(p1.x,p1.y,p1.x+15,p1.y+15);
ConvertToScreen(&p);
BMenuItem *mi=m->Go(p, false, true, r);
if(mi)
switch (mi->Message()->what)
{
case B_ABOUT_REQUESTED:
(new BAlert("","Deskbar Replicant Demo
\nby Al.V. Sarikov.\nKherson, Ukraine, 2004.
\nE-mail: avix@ukrpost.net.\nHome page:
http://avix.pp.ru.","OK"))->Go();
break;
case B_QUIT_REQUESTED:
QuitRequested();
break;
}
}
else if(b==B_PRIMARY_MOUSE_BUTTON)
(new BAlert("","Deskbar Replicant Demo
\nby Al.V. Sarikov.\nKherson, Ukraine, 2004.
\nE-mail: avix@ukrpost.net.\nHome page:
http://avix.pp.ru.","OK"))->Go();
}
При нажатии левой клавиши мыши (Primary Mouse Button) выводится информация о программе и авторе. При нажатии правой клавиши мыши (Secondary Mouse Button) вызывается выпадающее меню функцией Go(). Параметры функции задают вызов меню в точке, в которой находится курсор мыши (первый параметр). Меню остается на экране после отпускания клавиши мыши (третий и четвертый параметры) и при выборе пункта меню не передает сообщения (второй параметр). Координаты прямоугольника, в который заключена иконка репликанта, и координаты курсора мыши пересчитываются относительно экрана функцией ConvertToScreen().
Функция Go() возвращает выбранный пункт меню. В данной программе пункты меню различаются по назначенным им сообщениям. Если выбран пункт About (сообщение B_ABOUT_REQUESTED), то выводится информация о программе. Если выбран пункт Quit (сообщение B_QUIT_REQUESTED), то вызывается функция QuitRequested(), которая удаляет выпадающее меню и репликант из Shelf'а. При невозможности удалить репликант выводится сообщение об ошибке:
bool DRDAppViewClass::QuitRequested(void)
{
BDeskbar d;
status_t err=d.RemoveItem(VIEW_NAME);
if (err!=B_OK)
{
char temp[255];
sprintf(temp, "Error removing:
%s", strerror(err));
BAlert *alert=new BAlert("", temp, "OK");
alert->Go();
}
delete m;
return true;
}
Замечание: такой способ выхода из программы-репликанта Deskbar'а не приводит к полной потере Deskbar'ом информации о репликанте. Это можно проверить следующим образом: поместим репликант на Shelf и выгрузим его. Затем изменим что-либо в тексте программы, перекомпилируем и поместим на Shelf опять. Мы увидим, что на Shelf был помещен старый вариант репликанта, несмотря на то, что исполняемый файл, запущенный нами, уже изменен. Для того, чтобы на Shelf поместить измененный репликант, нужно перегрузить Deskbar (нажать Ctrl-Alt-Del, удалить Deskbar посредством Kill, затем опять нажать Ctrl-Alt-Del, а затем - появившуюся кнопку Restart the Desktop и Cancel для выхода из окна со списком задач).
Теперь программа полностью написана. Но, откомпилировав и запустив ее, мы увидим сообщение об ошибке: "Error starting: application could not be found". Дело в том, что mime-тип приложения APP_NAME не зарегистрирован в системе. Наиболее простой способ зарегистрировать mime-тип в любой системе, где запускается программа - включить его описание в виде ресурса в саму программу. Для этого откомпилированный файл DeskbarReplicantDemo открываем с помощью приложения FileTypes. В появившемся окне в графе Signature пишем: application/x-vnd.DeskbarReplicantDemo (т.е. аналогично APP_NAME). В меню выбираем File/Save. Теперь при запуске mime-тип будет зарегистрирован и приложение запустится нормально.
Но еще лучше сохранить описание mime-типа в файле ресурсов и подключить этот файл в проект, чтобы описание mime-типа автоматически добавлялось в исполняемый файл при перекомпиляции проекта. Для этого сделаем все, как описано в предыдущем абзаце, но вместо File/Save выберем File/Save into Resource File. Введем имя файла DeskbarReplicantDemo.rsrc и сохраним его в каталог с проектом. Чтобы подключить файл ресурсов к проекту, просто перетащим его на окно проекта. Далее следует пересобрать (Link) проект.
Теперь, имея файл ресурсов, можно, например, создать для приложения свою иконку. Как это сделать - описано в этой статье.
Файлы проекта, описанного в статье, можно загрузить здесь (вместе с текстом этой статьи).