/*
* tkTreeNotify.c --
*
* This module implements "qebind.c" events for treectrl widgets.
*
* Copyright (c) 2002-2011 Tim Baker
*/
#include "tkTreeCtrl.h"
/* Even though these are static globals and initialized for each new
* treectrl widget, the values are exactly the same for every widget. */
static int EVENT_EXPAND,
DETAIL_EXPAND_BEFORE,
DETAIL_EXPAND_AFTER;
static int EVENT_COLLAPSE,
DETAIL_COLLAPSE_BEFORE,
DETAIL_COLLAPSE_AFTER;
static int EVENT_SELECTION;
static int EVENT_ACTIVEITEM;
static int EVENT_SCROLL,
DETAIL_SCROLL_X,
DETAIL_SCROLL_Y;
static int EVENT_ITEM_DELETE;
static int EVENT_ITEM_VISIBILITY;
/*
*----------------------------------------------------------------------
*
* ExpandItem --
*
* Append an item ID to a dynamic string.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
ExpandItem(
TreeCtrl *tree, /* Widget info. */
int id, /* Item ID. */
Tcl_DString *result /* Gets appended. Caller must initialize. */
)
{
char buf[10 + TCL_INTEGER_SPACE];
(void) sprintf(buf, "%s%d",
(tree->itemPrefixLen ? tree->itemPrefix : ""), id);
Tcl_DStringAppend(result, buf, -1);
}
/*
*----------------------------------------------------------------------
*
* ExpandItemList --
*
* Append a list of item IDs to a dynamic string.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
ExpandItemList(
TreeCtrl *tree, /* Widget info. */
TreeItemList *itemList, /* list of item IDs */
Tcl_DString *result /* Gets appended. Caller must initialize. */
)
{
if (itemList == NULL) {
Tcl_DStringAppend(result, "{}", 2);
} else {
int i, count;
char buf[10 + TCL_INTEGER_SPACE];
Tcl_DStringStartSublist(result);
count = TreeItemList_Count(itemList);
for (i = 0; i < count; i++) {
(void) sprintf(buf, "%s%d",
(tree->itemPrefixLen ? tree->itemPrefix : ""),
TreeItem_GetID(tree, TreeItemList_Nth(itemList, i)));
Tcl_DStringAppendElement(result, buf);
}
Tcl_DStringEndSublist(result);
}
}
/*
*----------------------------------------------------------------------
*
* DumpPercents --
*
* Appends a sublist to a dynamic string. The sublist contains
* %-char,value pairs. This is to handle the %? substitution.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
DumpPercents(
QE_ExpandArgs *args, /* %-substitution args. */
QE_ExpandProc proc, /* Function to return value for a given
* %-char. */
CONST char *chars /* NULL-terminated list of %-chars. */
)
{
char which = args->which;
char buf[2];
int i;
buf[1] = '\0';
Tcl_DStringStartSublist(args->result);
for (i = 0; chars[i]; i++) {
args->which = chars[i];
buf[0] = chars[i];
Tcl_DStringAppendElement(args->result, buf);
Tcl_DStringAppend(args->result, " ", 1);
(*proc)(args);
}
Tcl_DStringEndSublist(args->result);
args->which = which;
}
/*
*----------------------------------------------------------------------
*
* Percents_Any --
*
* Append a value for a single %-char to a dynamic string. This
* function handles the default %-chars (d,e,P,W,T, and ?) and
* calls QE_ExpandUnknown() for any unrecognized %-char.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_Any(
QE_ExpandArgs *args, /* %-substitution args. */
QE_ExpandProc proc, /* Function to return value for a given
* %-char. */
CONST char *chars /* NULL-terminated list of %-chars. */
)
{
struct {
TreeCtrl *tree;
} *data = args->clientData;
char chars2[64];
switch (args->which) {
case 'd': /* detail */
QE_ExpandDetail(args->bindingTable, args->event, args->detail,
args->result);
break;
case 'e': /* event */
QE_ExpandEvent(args->bindingTable, args->event, args->result);
break;
case 'P': /* pattern */
QE_ExpandPattern(args->bindingTable, args->event, args->detail, args->result);
break;
case 'W': /* object */
QE_ExpandString((char *) args->object, args->result);
break;
case 'T': /* tree */
QE_ExpandString(Tk_PathName(data->tree->tkwin), args->result);
break;
case '?':
strcpy(chars2, "TWPed");
strcat(chars2, chars);
DumpPercents(args, proc, chars2);
break;
default:
QE_ExpandUnknown(args->which, args->result);
break;
}
}
/*
*----------------------------------------------------------------------
*
* Percents_Expand --
*
* %-substitution callback for <Expand>.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_Expand(
QE_ExpandArgs *args /* %-substitution args. */
)
{
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
int id;
} *data = args->clientData;
switch (args->which) {
case 'I':
ExpandItem(data->tree, data->id, args->result);
break;
default:
Percents_Any(args, Percents_Expand, "I");
break;
}
}
/*
*----------------------------------------------------------------------
*
* Percents_ItemVisibility --
*
* %-substitution callback for <ItemVisibility>.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_ItemVisibility(
QE_ExpandArgs *args /* %-substitution args. */
)
{
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
TreeItemList *v;
TreeItemList *h;
} *data = args->clientData;
TreeItemList *table;
switch (args->which) {
case 'v':
case 'h':
table = (args->which == 'v') ? data->v : data->h;
ExpandItemList(data->tree, table, args->result);
break;
default:
Percents_Any(args, Percents_ItemVisibility, "vh");
break;
}
}
/*
*----------------------------------------------------------------------
*
* Percents_Selection --
*
* %-substitution callback for <Selection>.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_Selection(
QE_ExpandArgs *args /* %-substitution args. */
)
{
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
TreeItemList *select;
TreeItemList *deselect;
int count;
} *data = args->clientData;
TreeItemList *itemList;
switch (args->which) {
case 'c':
QE_ExpandNumber(data->count, args->result);
break;
case 'D':
case 'S':
itemList = (args->which == 'D') ? data->deselect : data->select;
ExpandItemList(data->tree, itemList, args->result);
break;
default:
Percents_Any(args, Percents_Selection, "cSD");
break;
}
}
/*
*----------------------------------------------------------------------
*
* Percents_ActiveItem --
*
* %-substitution callback for <ActiveItem>.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_ActiveItem(
QE_ExpandArgs *args /* %-substitution args. */
)
{
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
int prev;
int current;
} *data = args->clientData;
switch (args->which) {
case 'c':
ExpandItem(data->tree, data->current, args->result);
break;
case 'p':
ExpandItem(data->tree, data->prev, args->result);
break;
default:
Percents_Any(args, Percents_ActiveItem, "cp");
break;
}
}
/*
*----------------------------------------------------------------------
*
* Percents_Scroll --
*
* %-substitution callback for <Scroll>.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_Scroll(
QE_ExpandArgs *args /* %-substitution args. */
)
{
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
double lower;
double upper;
} *data = args->clientData;
switch (args->which) {
case 'l':
QE_ExpandDouble(data->lower, args->result);
break;
case 'u':
QE_ExpandDouble(data->upper, args->result);
break;
default:
Percents_Any(args, Percents_Scroll, "lu");
break;
}
}
/*
*----------------------------------------------------------------------
*
* TreeNotifyCmd --
*
* This procedure is invoked to process the [notify] widget
* command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeNotifyCmd(
ClientData clientData, /* Widget info. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *CONST objv[] /* Argument values. */
)
{
TreeCtrl *tree = clientData;
static CONST char *commandName[] = { "bind", "configure", "detailnames",
"eventnames", "generate", "install", "linkage", "unbind", "uninstall",
(char *) NULL };
enum {
COMMAND_BIND, COMMAND_CONFIGURE, COMMAND_DETAILNAMES,
COMMAND_EVENTNAMES, COMMAND_GENERATE, COMMAND_INSTALL,
COMMAND_LINKAGE, COMMAND_UNBIND, COMMAND_UNINSTALL
};
int index;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], commandName, "command", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch (index) {
case COMMAND_BIND: {
return QE_BindCmd(tree->bindingTable, 2, objc, objv);
}
case COMMAND_CONFIGURE: {
return QE_ConfigureCmd(tree->bindingTable, 2, objc, objv);
}
/* T notify detailnames $eventName */
case COMMAND_DETAILNAMES: {
char *eventName;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "eventName");
return TCL_ERROR;
}
eventName = Tcl_GetString(objv[3]);
return QE_GetDetailNames(tree->bindingTable, eventName);
}
/* T notify eventnames */
case COMMAND_EVENTNAMES: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
return TCL_ERROR;
}
return QE_GetEventNames(tree->bindingTable);
}
case COMMAND_GENERATE: {
return QE_GenerateCmd(tree->bindingTable, 2, objc, objv);
}
case COMMAND_INSTALL: {
return QE_InstallCmd(tree->bindingTable, 2, objc, objv);
}
case COMMAND_LINKAGE: {
return QE_LinkageCmd(tree->bindingTable, 2, objc, objv);
}
case COMMAND_UNBIND: {
return QE_UnbindCmd(tree->bindingTable, 2, objc, objv);
}
case COMMAND_UNINSTALL: {
return QE_UninstallCmd(tree->bindingTable, 2, objc, objv);
}
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_OpenClose --
*
* Generate an <Expand> or <Collapse> event.
*
* Results:
* Any scripts bound to the event are evaluated.
*
* Side effects:
* Whatever binding scripts do.
*
*----------------------------------------------------------------------
*/
void
TreeNotify_OpenClose(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item token. */
int state, /* STATE_ITEM_OPEN or 0 */
int before /* TRUE for <xxx-before> event, FALSE for
* <xxx-after> event. */
)
{
QE_Event event;
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
int id;
} data;
data.tree = tree;
data.id = TreeItem_GetID(tree, item);
if (state & STATE_ITEM_OPEN) {
event.type = EVENT_EXPAND;
event.detail = before ? DETAIL_EXPAND_BEFORE : DETAIL_EXPAND_AFTER;
} else {
event.type = EVENT_COLLAPSE;
event.detail = before ? DETAIL_COLLAPSE_BEFORE : DETAIL_COLLAPSE_AFTER;
}
event.clientData = (ClientData) &data;
(void) QE_BindEvent(tree->bindingTable, &event);
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_Selection --
*
* Generate a <Selection> event.
*
* Results:
* Any scripts bound to the event are evaluated.
*
* Side effects:
* Whatever binding scripts do.
*
*----------------------------------------------------------------------
*/
void
TreeNotify_Selection(
TreeCtrl *tree, /* Widget info. */
TreeItemList *select, /* List of items or NULL. */
TreeItemList *deselect /* List of items or NULL. */
)
{
QE_Event event;
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
TreeItemList *select;
TreeItemList *deselect;
int count;
} data;
data.tree = tree;
data.select = select;
data.deselect = deselect;
data.count = tree->selectCount;
event.type = EVENT_SELECTION;
event.detail = 0;
event.clientData = (ClientData) &data;
(void) QE_BindEvent(tree->bindingTable, &event);
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_ActiveItem --
*
* Generate an <ActiveItem> event.
*
* Results:
* Any scripts bound to the event are evaluated.
*
* Side effects:
* Whatever binding scripts do.
*
*----------------------------------------------------------------------
*/
void
TreeNotify_ActiveItem(
TreeCtrl *tree, /* Widget info. */
TreeItem itemPrev, /* Previous active item. */
TreeItem itemCur /* Current active item. */
)
{
QE_Event event;
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
int prev;
int current;
} data;
data.tree = tree;
data.prev = TreeItem_GetID(tree, itemPrev);
data.current = TreeItem_GetID(tree, itemCur);
event.type = EVENT_ACTIVEITEM;
event.detail = 0;
event.clientData = (ClientData) &data;
(void) QE_BindEvent(tree->bindingTable, &event);
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_Scroll --
*
* Generate a <Scroll> event.
*
* Results:
* Any scripts bound to the event are evaluated.
*
* Side effects:
* Whatever binding scripts do.
*
*----------------------------------------------------------------------
*/
void
TreeNotify_Scroll(
TreeCtrl *tree, /* Widget info. */
double fractions[2], /* Fractions suitable for a scrollbar's
* [set] command. */
int vertical /* TRUE for <Scroll-y>, FALSE for
* <Scroll-x>. */
)
{
QE_Event event;
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
double lower;
double upper;
} data;
data.tree = tree;
data.lower = fractions[0];
data.upper = fractions[1];
event.type = EVENT_SCROLL;
event.detail = vertical ? DETAIL_SCROLL_Y : DETAIL_SCROLL_X;
event.clientData = (ClientData) &data;
(void) QE_BindEvent(tree->bindingTable, &event);
}
/*
*----------------------------------------------------------------------
*
* Percents_ItemDelete --
*
* %-substitution callback for <ItemDelete>.
*
* Results:
* DString gets longer.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
static void
Percents_ItemDelete(
QE_ExpandArgs *args /* %-substitution args. */
)
{
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
TreeItemList *items;
} *data = args->clientData;
switch (args->which) {
case 'i':
ExpandItemList(data->tree, data->items, args->result);
break;
default:
Percents_Any(args, Percents_ItemDelete, "i");
break;
}
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_ItemDeleted --
*
* Generate an <ItemDelete> event.
*
* Results:
* Any scripts bound to the event are evaluated.
*
* Side effects:
* Whatever binding scripts do.
*
*----------------------------------------------------------------------
*/
void
TreeNotify_ItemDeleted(
TreeCtrl *tree, /* Widget info. */
TreeItemList *items /* List of items. */
)
{
QE_Event event;
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
TreeItemList *items;
} data;
data.tree = tree;
data.items = items;
event.type = EVENT_ITEM_DELETE;
event.detail = 0;
event.clientData = (ClientData) &data;
(void) QE_BindEvent(tree->bindingTable, &event);
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_ItemVisibility --
*
* Generate an <ItemVisibility> event.
*
* Results:
* Any scripts bound to the event are evaluated.
*
* Side effects:
* Whatever binding scripts do.
*
*----------------------------------------------------------------------
*/
void
TreeNotify_ItemVisibility(
TreeCtrl *tree, /* Widget info. */
TreeItemList *v, /* List of newly-visible items. */
TreeItemList *h /* List of newly-hidden items. */
)
{
QE_Event event;
struct {
TreeCtrl *tree; /* Must be first. See Percents_Any(). */
TreeItemList *v;
TreeItemList *h;
} data;
data.tree = tree;
data.v = v;
data.h = h;
event.type = EVENT_ITEM_VISIBILITY;
event.detail = 0;
event.clientData = (ClientData) &data;
(void) QE_BindEvent(tree->bindingTable, &event);
}
/*
*----------------------------------------------------------------------
*
* TreeNotify_InitWidget --
*
* Perform event-related initialization when a new TreeCtrl is
* created.
*
* Results:
* Installs all the static events and details.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
int
TreeNotify_InitWidget(
TreeCtrl *tree /* Widget info. */
)
{
tree->bindingTable = QE_CreateBindingTable(tree->interp);
EVENT_EXPAND = QE_InstallEvent(tree->bindingTable, "Expand", Percents_Expand);
DETAIL_EXPAND_BEFORE = QE_InstallDetail(tree->bindingTable, "before", EVENT_EXPAND, NULL);
DETAIL_EXPAND_AFTER = QE_InstallDetail(tree->bindingTable, "after", EVENT_EXPAND, NULL);
EVENT_COLLAPSE = QE_InstallEvent(tree->bindingTable, "Collapse", Percents_Expand);
DETAIL_COLLAPSE_BEFORE = QE_InstallDetail(tree->bindingTable, "before", EVENT_COLLAPSE, NULL);
DETAIL_COLLAPSE_AFTER = QE_InstallDetail(tree->bindingTable, "after", EVENT_COLLAPSE, NULL);
EVENT_SELECTION = QE_InstallEvent(tree->bindingTable, "Selection", Percents_Selection);
EVENT_ACTIVEITEM = QE_InstallEvent(tree->bindingTable, "ActiveItem", Percents_ActiveItem);
EVENT_SCROLL = QE_InstallEvent(tree->bindingTable, "Scroll", Percents_Scroll);
DETAIL_SCROLL_X = QE_InstallDetail(tree->bindingTable, "x", EVENT_SCROLL, NULL);
DETAIL_SCROLL_Y = QE_InstallDetail(tree->bindingTable, "y", EVENT_SCROLL, NULL);
EVENT_ITEM_DELETE = QE_InstallEvent(tree->bindingTable, "ItemDelete", Percents_ItemDelete);
EVENT_ITEM_VISIBILITY = QE_InstallEvent(tree->bindingTable, "ItemVisibility", Percents_ItemVisibility);
return TCL_OK;
}