Руководство программиста в Photon (953920), страница 57
Текст из файла (страница 57)
#include <Ap.h>
/* Локальные хеадеры */
#include "abimport.h"
#include "proto.h"
#define TEXT_GROUP 0
#define IMAGE_GROUP 1
PtTransportReqDataCB_t request_callback;
int start_dnd( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) {
char *widget_text = NULL;
char *label_type;
PhImage_t * image = NULL;
PtRequestables_t *req;
PtTransportCtrl_t *tctrl = PtCreateTransportCtrl();
int ret;
/* предотвращает предупреждения (варнинги) об отсутствии ссылок */
widget = widget, apinfo = apinfo;
cbinfo = cbinfo;
/* Получает тип надписи, так что мы можем определить, какие данные паковать */
PtGetResource( widget, Pt_ARG_LABEL_TYPE, &label_type, 0);
if ((*label_type = = Pt_Z_STRING) || (*label_type = = Pt_TEXT_IMAGE)) {
/* Получает текст виджета и пакует его в поток */
PtGetResource( widget, Pt_ARG_TEXT_STRING, &widget_text, 0);
PtTransportType( tctrl, "text", "plain", TEXT_GROUP, Ph_TRANSPORT_INLINE, "string",
widget_text, 0, 0);
}
/* Если это образ, добавляем его как данные, доступные по запросу.
Готовим запрашиваемые данные (позволяя автоматический запрос)
*/
if ((*label_type = = Pt_IMAGE) || (*label_type = = Pt_TEXT_IMAGE)) {
PtGetResource( widget, Pt_ARG_LABEL_IMAGE, &image, 0);
if (image) {
req = PtTransportRequestable ( tctrl, "image", "an image", IMAGE_GROUP,
Ph_TRANSPORT_INLINE, "PhImage", NULL, NULL );
PtAddResponseType( tctrl, req, "image", "an image", Ph_TRANSPORT_INLINE, "PhImage",
image, 0, 0);
}
}
/* Добавляем доступную по запросу строку, которая будет
предоставлена по ответной реакции
*/
PtTransportRequestable( tctrl, "text", "image description", IMAGE_GROUP,
Ph_TRANSPORT_INLINE, "string",
(PtTransportReqDataCB_t *) &request_callback, "This was requested");
/* Инициализируем операцию "тащи и бросай" */
ret = PtInitDnd( tctrl, widget, cbinfo->event, NULL, 0);
return( Pt_CONTINUE );
} // функции start_dnd()
int unsigned request_callback( int unsigned type, PtReqResponseHdr_t *req_hdr,
PtRequestables_t *req) {
if (type = = Pt_DND_REQUEST_DATA) {
/* Ответить на запрос строкой из req->rq_callback_data,
последнего аргумента в PtTransportRequestable()
*/
PtAddResponseType( req->ctrl, req, "text", "request", Ph_TRANSPORT_INLINE, "string",
req->rq_callback_data, 0, 0);
return Pt_CONTINUE;
}
/* Отвергнуть запрос */
return Pt_END;
} // функции request_callback()
Получение событий "тащи и бросай"
Чтобы виджет был в состоянии получать события "тащи и бросай", прикрепите к нему ответную реакцию Pt_CB_DND (см. описание PtWidget в "Справочнике виджетов Photon'а").
Чтобы у виджета вызывались его ответные реакции Pt_CB_DND, в его флагах Pt_ARG_FLAGS не требуется устанавливать флаг Pt_SELECTABLE.
Всякий раз, когда каким-либо образом виджет вовлекается в событие "тащи и бросай", вызывается его ответная реакция Pt_CB_DND. В ответной реакции cbinfo–>reason_subtype указывает тип произошедшего действия "тащи и бросай".
В нижеследующем разделе описаны события операции "Тащи и бросай", интересующие виджет-источник и виджет-адресат. Конечно, если виджет может быть и источником, и адресатом в (отдельных) операциях "тащи и бросай", его ответные реакции Pt_CB_DND должны иметь оба набора событий. Более подробно информация о событиях дана в описании типа PhEvent_t "Справочника библиотечных функций Photon'а".
Виджет-источник
Виджет-источник операции "тащи и бросай" может получать события, описывающие состояние операции. Если Вам не нужны эти события, установите флаг Pt_DND_SILENT в аргументе flags функции PtInitDnd().
Не устанавливайте флаг Pt_DND_SILENT, если Вы включаете доступные по запросу данные в структуру управления.
Подтипами события операции "тащи и бросай", представляющими интерес для источника операции, являются:
Ph_EV_DND_INIT | Операция успешно запущена |
Ph_EV_DND_CANCEL | Операция была отменена (например, если сбрасывание произошло не над зоной, где возможен сброс, или адресат прервал операцию до получения сброса, или до того, как завершил выборку запрашиваемых данных). Если операция отменена таким образом, библиотека автоматически очищает структуры данных. |
Ph_EV_DND_COMPLETE | Событие операции "тащи и бросай" поставлено адресатом в очередь (адресат пока что его не рассматривает). |
Ph_EV_DND_DELIVERED | Адресат удалил событие операции "тащи и бросай" из очереди. |
Виджет-адресат
Подтипами события "тащи и бросай", представляющими интерес для адресата операции, являются:
Ph_EV_DND_ENTER | Кто-то перетащил некие данные в область виджета, но ещё не сбросил их. Этот подтип события (reason_subtype) является первоначальной причиной вызова ответной реакции операции "тащи и бросай". В этот момент приложение принимает решение, примет ли оно сброшенные данные. Оно должно построить некий массив структур типа PtDndFetch_t и передать его функции PtDndSelect(). Это массив описывает допустимые типы, описания и транспортные методы для данных, участвующих в операциях "тащи и бросай", доступные для виджета. Функция PtDndSelect() возвращает число выбранных элементов из массива. Если событие содержит данные или ссылки на данные, в допустимом формате, выбираются и эти фрагменты событий. Если никакие данные не допустимы, этот виджет никакими другими событиями текущей операции "тащи и бросай" не модифицируется. |
Ph_EV_DND_MOTION | Указатель перемещается внутри зоны выиджета. Этот тип событий генерируется только в случае, когда для фрагмента выбранных данных в члене select_flags структуры PtDndFetch_t установлен бит Pt_DND_SELECT_MOTION. |
Ph_EV_DND_DROP | Пользователь сбросил данные. Для этого подтипа события ответная реакция получает из события выбранные данные. Это может включать в себя какую-то автоматическую, неблокируемую связь с источником данных – чтобы не допустить какую-либо связь с источником, задайте Ph_TRANSPORT_INLINE в качестве единственно допустимого транспортного протокола. Если сброс данных выполнен успешно, память, использовавшаяся транспортным механизмом, автоматически освобождается. |
Ph_EV_DND_LEAVE | Указатель вышел за пределы зоны виджета, но пользователь не сбросил данные. |
Вот пример, работающий с ответной реакцией, приведённой выше для виджета PtLabel. Эта ответная реакция допускает в качестве данных операции "тащи и бросай" следующие типы:
-
текст
-
образ
-
альтернативная строка, если в данных отсутствует образ (в элементе PtDndFetch_t установлен Pt_DND_SELECT_DUP_DATA).
Виджет-источник пакует образ и альтернативную строку как данные, доступные по запросу, но адресат не предпринимает ничего, чтобы их затребовать; механизм транспортировки делает это автоматически.
/* Стандартные хеадеры */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Инструментальные хеадеры */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Локальные хеадеры */
#include "abimport.h"
#include "proto.h"
static PtDndFetch_t stuff_i_want[] = {
{"text", "plain", Ph_TRANSPORT_INLINE, },
{"image", NULL, Ph_TRANSPORT_INLINE, },
{"text", "image description", Ph_TRANSPORT_INLINE, Pt_DND_SELECT_DUP_DATA, },
};
enum {
PLAIN_TEXT = 0,
IMAGE,
IMAGE_TEXT,
};
int dnd_callback( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) {
PtDndCallbackInfo_t *dndcb = cbinfo->cbdata;
int deep_free = 1, num_matches;
/* предотвращает предупреждения (варнинги) об отсутствии ссылок */
widget = widget, apinfo = apinfo;
cbinfo = cbinfo;
switch (cbinfo->reason_subtype) {
case Ph_EV_DND_ENTER:
num_matches = PtDndSelect (widget, stuff_i_want, ARRAY_SIZE( stuff_i_want ),
NULL, NULL, cbinfo );
break;
case Ph_EV_DND_DROP:
switch (dndcb->fetch_index) {
case PLAIN_TEXT:
PtSetResource (widget, Pt_ARG_TEXT_STRING, dndcb->data, strlen(dndcb->data));
break;
case IMAGE:
PtSetResource (widget, Pt_ARG_LABEL_IMAGE, dndcb->data, 0);
free (dndcb->data);
deep_free = 0;
break;
case IMAGE_TEXT:
printf ("Образ отсутствует; альтернативный текст: %s\n", (char *)dndcb->data);
break;
}
if (deep_free) {
PhFreeTransportType (dndcb->data, dndcb->trans_hdr->packing_type);
}
break;
} // switch по подтипу вызвавшего события
return( Pt_CONTINUE );
}
Отмена операции "тащи и бросай"
Виджет-источник может отменить операцию "тащи и бросай", вызвав PtCancelDnd().
Виджет должен затем очистить структуры управления транспортом и запакованные данные, вызвав функцию PtReleaseTransportCtrl(). (Если сброс был выполнен успешно, управляющие структуры очищаются автоматически. Адресат решает, когда ему освобождать сброшенные данные).
Регистрация новых транспортных типов
В этом разделе обсуждается:
-
Простая структура данных
-
Более сложная структура
-
Транспортные функции
Чтобы транспортировать данные типов, отличных от тех, которые автоматически определяются Photon'ом, Вы должны определить тип и зарегистрировать его в транспортном реестре – наборе описаний типов, каждое из которых включает:
-
имя типа в виде строки, например, PhImage
-
метод упаковки, который будет использоваться (один из следующих: Ph_PACK_RAW, Ph_PACK_STRING или Ph_PACK_STRUCT)
-
список членов внутри типа, которые ссылаются на данные в запросе вне базового размера типа (ссылка или члены типа указателя)
-
список членов, которые чувствительны к способу передачи данных (вперёд старшим байтом или назад) (эти члены являются корректными при передаче, даже если распаковываются на машине, способ передачи данных у которой отличается от способа на той машине, где данные были упакованы)
-
список членов, которые должны быть очищены, когда тип распакован (например, указатель на данные – такие как пароль – которые Вы не хотите транспортировать).
Перед тем, как данные могут быть успешно транспортированы, тип данных должен быть определён в реестрах транспортировки и в приложении-источнике, и в приложении-адресате.
Простая структура данных
Давайте рассмотрим простую структуру данных:
typedef struct simp1 {
int num;
int nums_10[10];
char name[10];
short vals_5[5];
} Simp1_t;
Эту структуру можно легко запаковать, используя тип raw, поскольку она не содержит каких-либо внешних ссылок (т.е. не имеет членов-указателей). Но это не защищает транспортируемые данные от различий между источником и адресатом, заключающихся в способе передачи данных (вперёд старшим или младшим байтом). [Так, достало меня это писать! Дальше по тексту этот "способ передачи данных вперёд старшим или младшим байтом" "первожу" как эндиан. Прим. пер.] Так что даже для такой простой структуры полезно описание типа, детализирующее его эндиан-чувствительность.
Описание типа начинается с массива элементов типа init unsigned, которые описывают эндиан-чувствительность для каждого члена:
static const int unsigned Simp1Endians[] = {
Tr_ENDIAN( Simp1_t, num ),
Tr_ENDIAN_ARRAY( Simp1_t, nums_10 ),
Tr_ENDIAN_ARRAY( Simp1_t, vals_5 ),
0 /* Конец эндиан-списка */
};