832 lines
15 KiB
C
832 lines
15 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/*
|
|
* $XConsortium: vwr.c /main/3 1995/11/06 17:57:10 rswiston $
|
|
*
|
|
* @(#)vwr.c 1.21 16 Aug 1994
|
|
*
|
|
* RESTRICTED CONFIDENTIAL INFORMATION:
|
|
*
|
|
* The information in this document is subject to special
|
|
* restrictions in a confidential disclosure agreement between
|
|
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
|
|
* document outside HP, IBM, Sun, USL, SCO, or Univel without
|
|
* Sun's specific written approval. This document and all copies
|
|
* and derivative works thereof must be returned or destroyed at
|
|
* Sun's request.
|
|
*
|
|
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* browser.c
|
|
* This file contains code to implement the generic object browser.
|
|
* It should be standalone - it should not contain dependencies on
|
|
* the App Builder.
|
|
*
|
|
* Currently, this is a direct port of the Devguide browser.
|
|
*
|
|
* This file will be modified (soon) for the new browser for the CDE
|
|
* App Builder.
|
|
*/
|
|
#ifndef _POSIX_SOURCE
|
|
#define _POSIX_SOURCE 1
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/X.h>
|
|
#include <Xm/Xm.h>
|
|
#include <ab_private/vwr.h>
|
|
|
|
/*
|
|
* Some internal functions.
|
|
static ViewerNode *r_locate_node();
|
|
*/
|
|
static void erase_node();
|
|
static int child_of_selected_node();
|
|
static void draw_icon();
|
|
static void draw_arrow();
|
|
|
|
/*************************************
|
|
ADT of the browser structure
|
|
*************************************/
|
|
|
|
/*
|
|
* Create the browser object.
|
|
*/
|
|
Vwr
|
|
vwr_create(
|
|
VMethods m
|
|
)
|
|
{
|
|
Vwr v;
|
|
|
|
v = (Viewer *) calloc(1, sizeof(Viewer));
|
|
|
|
v->obj_data = NULL;
|
|
v->methods = m;
|
|
v->ui_handle = NULL;
|
|
v->properties = NULL;
|
|
v->next = NULL;
|
|
v->previous = NULL;
|
|
|
|
/*
|
|
* v->tree == root
|
|
* v->current_tree == 'current' subtree
|
|
*/
|
|
v->tree = v->current_tree = NULL;
|
|
|
|
if (m)
|
|
{
|
|
(*m->init_prop)(v);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroy the browser object.
|
|
*/
|
|
void
|
|
vwr_destroy(
|
|
Vwr v
|
|
)
|
|
{
|
|
VMethods m;
|
|
|
|
m = v->methods;
|
|
|
|
if (m && m->free_prop)
|
|
(*m->free_prop)(v);
|
|
|
|
free(v);
|
|
}
|
|
|
|
|
|
/*
|
|
* Recursively destroy a tree and whatever below it.
|
|
*/
|
|
void
|
|
vwr_destroy_tree
|
|
(
|
|
ViewerNode *tree
|
|
)
|
|
{
|
|
Viewer *b;
|
|
ViewerMethods *m;
|
|
ViewerNode *child;
|
|
ViewerNode *next_child;
|
|
int i, num_child;
|
|
|
|
if (!tree)
|
|
return;
|
|
|
|
b = tree->browser;
|
|
m = b->methods;
|
|
|
|
num_child = (*m->get_num_children)(tree);
|
|
|
|
/*
|
|
* Recursively destroy children subtrees
|
|
* Destroy procs should check for (child == NULL)
|
|
*/
|
|
for (i=0, child = (*m->get_child)(tree, 0);
|
|
(i < num_child);
|
|
child = (*m->get_child)(tree, ++i))
|
|
vwr_destroy_tree(child);
|
|
|
|
if (tree == b->current_tree)
|
|
b->current_tree = b->tree;
|
|
|
|
vwr_destroy_node(tree);
|
|
}
|
|
|
|
VNode
|
|
vwr_create_node(void)
|
|
{
|
|
VNode bnode;
|
|
|
|
bnode = (VNode) malloc(sizeof(ViewerNode));
|
|
|
|
bnode->browser = NULL;
|
|
bnode->obj_data = NULL;
|
|
bnode->elm_methods = NULL;
|
|
bnode->elements = NULL;
|
|
bnode->num_elements = 0;
|
|
bnode->boundbox_shown = TRUE;
|
|
bnode->x = bnode->y = bnode->width = bnode->height = -1;
|
|
bnode->state = 0;
|
|
bnode->next = NULL;
|
|
bnode->previous = NULL;
|
|
|
|
return (bnode);
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroy one browser node.
|
|
* To avoid mulitply destroying the same node, use this
|
|
* function.
|
|
*/
|
|
void
|
|
vwr_destroy_node
|
|
(
|
|
ViewerNode *node
|
|
)
|
|
{
|
|
free(node->elements);
|
|
free(node);
|
|
}
|
|
|
|
void
|
|
vwr_init_elements(
|
|
VNode vnode
|
|
)
|
|
{
|
|
Vwr v;
|
|
VMethods m;
|
|
|
|
if (!vnode)
|
|
return;
|
|
|
|
if (!(v = vnode->browser))
|
|
return;
|
|
|
|
if (!(m = v->methods))
|
|
return;
|
|
|
|
(*m->init_elements)(vnode);
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the current tree to be the root of the tree.
|
|
*/
|
|
void
|
|
vwr_reset(Viewer *b)
|
|
{
|
|
b->current_tree = b->tree;
|
|
}
|
|
|
|
|
|
/*
|
|
* Delete a tree node from a given tree.
|
|
*/
|
|
void
|
|
vwr_delete_tree(Vwr v, VNode node)
|
|
{
|
|
}
|
|
|
|
|
|
/*
|
|
* Traverse the given tree and apply the user-given function
|
|
* to each of the nodes in the tree.
|
|
* If the user-function (*fn) returns FALSE, the traversal
|
|
* aborts.
|
|
*/
|
|
ViewerNode *
|
|
vwr_traverse_tree
|
|
(
|
|
ViewerNode *tree,
|
|
int (*fn)(ViewerNode *)
|
|
)
|
|
{
|
|
ViewerMethods *m;
|
|
ViewerNode *child,
|
|
*rtn_node = NULL,
|
|
*tmp_node = NULL;
|
|
int i, num_child;
|
|
|
|
if (!tree)
|
|
return (NULL);
|
|
|
|
m = BNODE_METHODS(tree);
|
|
|
|
num_child = (*m->get_num_children)(tree);
|
|
|
|
/*
|
|
* Recursively traverse child subtrees
|
|
* The traverse proc should check for (child == NULL)
|
|
*/
|
|
for (i=0, child = (*m->get_child)(tree, 0);
|
|
(i < num_child);
|
|
child = (*m->get_child)(tree, ++i))
|
|
{
|
|
if (tmp_node = vwr_traverse_tree(child, fn))
|
|
rtn_node = tmp_node;
|
|
}
|
|
|
|
if ((*fn) (tree))
|
|
return tree;
|
|
else
|
|
return rtn_node;
|
|
}
|
|
|
|
/**************************************************
|
|
Drawing routines for the browser objects.
|
|
PUBLIC FUNCTIONS.
|
|
**************************************************/
|
|
|
|
#ifdef MATRIX
|
|
/*
|
|
* Draw the tree in a matrix form.
|
|
*/
|
|
void
|
|
draw_matrix(b)
|
|
Viewer *b;
|
|
{
|
|
ViewerNode *node;
|
|
|
|
if (!b->current_tree || !b->current_tree->first)
|
|
return;
|
|
|
|
recalc_matrix(b);
|
|
for (node = b->current_tree->first; node; node = node->next) {
|
|
render_matrix(b, node, XtWindow(b->canvas), b->select_fn);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/**************************************************
|
|
Drawing routines for the browser objects.
|
|
PRIVATE FUNCTIONS.
|
|
**************************************************/
|
|
|
|
#ifdef OLD
|
|
/*
|
|
* Erase a node.
|
|
*/
|
|
static void
|
|
erase_node
|
|
(
|
|
Viewer *b,
|
|
ViewerNode *node,
|
|
Pixmap pixmap
|
|
)
|
|
{
|
|
Widget draw_area;
|
|
|
|
draw_area = aob_ui_draw_area(b);
|
|
|
|
XClearArea(XtDisplay(draw_area), XtWindow(draw_area), node->x, node->y,
|
|
node->width, node->height, FALSE);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef MATRIX
|
|
/*
|
|
* Recalculate the coordinates of the nodes in a matrix.
|
|
* b->select_fn is applied to each
|
|
* of the nodes in the tree to determine if the node is to be
|
|
* drawn.
|
|
* After this function is called, some of the coordinates stored
|
|
* in the tree node will be changed. So to switch back to drawing
|
|
* a tree, all the coordinates have to be recalculated.
|
|
* A temporary list is derived to temporarily link up all the
|
|
* nodes that will be shown.
|
|
*/
|
|
static void
|
|
recalc_matrix(b)
|
|
Viewer *b;
|
|
{
|
|
ViewerNode *head = NULL;
|
|
int max_y, width;
|
|
ViewerNode *node;
|
|
|
|
for (node = b->current_tree->last; node; node = node->previous)
|
|
line_up_nodes(b, node, b->select_fn, &head);
|
|
|
|
if (!head)
|
|
return;
|
|
|
|
XtVaGetValues(b->canvas, XtNwidth, &width, NULL);
|
|
|
|
compute_matrix(b, head, X_ORIGIN, Y_ORIGIN, width, &max_y);
|
|
reallocate_canvas(b->canvas, width, max_y);
|
|
}
|
|
|
|
|
|
/*
|
|
* Line up the nodes in a linear list. The next_in_line
|
|
* pointer is used here.
|
|
*/
|
|
static void
|
|
line_up_nodes(b, tree, fn, next)
|
|
Viewer *b;
|
|
ViewerNode *tree;
|
|
int (*fn)();
|
|
ViewerNode **next;
|
|
{
|
|
ViewerNode *child;
|
|
|
|
for (child = tree->last; child; child = child->previous)
|
|
line_up_nodes(b, child, fn, next);
|
|
|
|
if (!fn || (*fn) (b, tree)) {
|
|
/*
|
|
* This is a node of concern.
|
|
*/
|
|
tree->next_in_line = *next;
|
|
*next = tree;
|
|
}
|
|
else {
|
|
tree->next_in_line = NULL;
|
|
/*
|
|
* This is a node of no concern. Put
|
|
* some irrelevant x, y coordinates in
|
|
* the node.
|
|
*/
|
|
tree->icon_x = -1 - tree->icon_width;
|
|
tree->icon_y = -1 - tree->icon_height;
|
|
tree->str_width = -1;
|
|
tree->str_height = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute the coordinates of the nodes if drawn in the
|
|
* matrix form.
|
|
* The tree data structure is good for drawing trees, but
|
|
* bad for drawing matrices. Well, what can I do?
|
|
*/
|
|
static void
|
|
compute_matrix(b, head, start_x, start_y, x_limit, end_y)
|
|
Viewer *b;
|
|
ViewerNode *head;
|
|
int start_x;
|
|
int start_y;
|
|
int x_limit;
|
|
int *end_y;
|
|
{
|
|
ViewerNode *node;
|
|
int x = start_x;
|
|
int y = start_y;
|
|
int row_max_height = 0;
|
|
|
|
for (node = head; node; node = node->next_in_line)
|
|
{
|
|
browser_node_compute(node, 0, 0);
|
|
if (x + node->width > x_limit)
|
|
{
|
|
x = start_x;
|
|
y = y + row_max_height + NODE_Y_GAP;
|
|
row_max_height = 0;
|
|
}
|
|
node->x = x;
|
|
node->y = y;
|
|
node->icon_x = x + node->icon_x;
|
|
node->icon_y = y + node->icon_y;
|
|
node->str_x = x + node->str_x;
|
|
node->str_y = y + node->str_y;
|
|
x = x + node->width + NODE_X_GAP;
|
|
if (node->height > row_max_height)
|
|
row_max_height = node->height;
|
|
}
|
|
*end_y = y + row_max_height + NODE_Y_GAP;
|
|
}
|
|
|
|
|
|
/*
|
|
* Actually draw the matrix of nodes on the screen.
|
|
*/
|
|
static void
|
|
render_matrix(b, tree, pixmap, fn)
|
|
Viewer *b;
|
|
ViewerNode *tree;
|
|
Pixmap pixmap;
|
|
int (*fn)();
|
|
{
|
|
ViewerNode *child;
|
|
|
|
if (!fn || (*fn) (b, tree))
|
|
{
|
|
if (tree && tree->selected)
|
|
browser_node_render(tree, pixmap, TRUE);
|
|
else
|
|
browser_node_render(tree, pixmap, FALSE);
|
|
}
|
|
|
|
for (child = tree->first; child; child = child->next)
|
|
render_matrix(b, child, pixmap, fn);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Given x and y, determine which node the point is in.
|
|
*/
|
|
VNode
|
|
vwr_locate_node
|
|
(
|
|
Vwr b,
|
|
int x,
|
|
int y
|
|
)
|
|
{
|
|
VNode selected_node = NULL;
|
|
ViewerMethods *m;
|
|
|
|
if (!b)
|
|
return(NULL);
|
|
|
|
m = b->methods;
|
|
|
|
/*
|
|
selected_node = r_locate_node(b->current_tree, x, y);
|
|
*/
|
|
selected_node = (*m->locate_node)(b->current_tree, x, y);
|
|
|
|
return selected_node;
|
|
}
|
|
|
|
#ifdef OLD
|
|
/*
|
|
* Draw the selection box for the selected icon.
|
|
*/
|
|
static void
|
|
draw_icon(pixmap, x, y, node)
|
|
Pixmap pixmap;
|
|
int x;
|
|
int y;
|
|
ViewerNode *node;
|
|
{
|
|
|
|
int x_delta = x - node->icon_x;
|
|
int y_delta = y - node->icon_y;
|
|
XDrawRectangle(dpy, pixmap, xor_gc,
|
|
node->icon_x + x_delta + ICON_OFFSET,
|
|
node->icon_y + y_delta,
|
|
node->icon_width - 2 * ICON_OFFSET,
|
|
node->icon_height);
|
|
}
|
|
|
|
|
|
/*
|
|
* Draw an up-pointing arrow at the given point.
|
|
*/
|
|
static void
|
|
draw_arrow(pixmap, x, y)
|
|
Pixmap pixmap;
|
|
int x;
|
|
int y;
|
|
{
|
|
#define pcount 7
|
|
|
|
int i;
|
|
XPoint plist[pcount];
|
|
static XPoint list[] = {
|
|
{-2, 10}, {-2, 6}, {-6, 6}, {0, 0}, {6, 6}, {2, 6}, {2, 10},
|
|
};
|
|
|
|
for (i = 0; i < pcount; i++)
|
|
{
|
|
plist[i] = list[i];
|
|
plist[i].x += x;
|
|
plist[i].y += y;
|
|
}
|
|
|
|
XFillPolygon(dpy, pixmap, xor_gc, plist, pcount, Convex, CoordModeOrigin);
|
|
|
|
#undef pcount
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* vwr_num_cond()
|
|
* Returns:
|
|
* - the nuber of nodes satisfying the passed
|
|
* condition/function
|
|
*/
|
|
void
|
|
vwr_num_cond(
|
|
VNode tree,
|
|
int *num, /* RETURN */
|
|
int (*cond)(VNode)
|
|
)
|
|
{
|
|
VMethods m;
|
|
VNode child;
|
|
int i,
|
|
num_child;
|
|
|
|
if (!tree || !num)
|
|
return;
|
|
|
|
m = BNODE_METHODS(tree);
|
|
|
|
num_child = (*m->get_num_children)(tree);
|
|
|
|
/*
|
|
* Recursively count selected nodes of child subtree
|
|
* This proc should check for (child == NULL)
|
|
*/
|
|
for (i=0, child = (*m->get_child)(tree, 0);
|
|
(i < num_child);
|
|
child = (*m->get_child)(tree, ++i))
|
|
{
|
|
vwr_num_cond(child, num, cond);
|
|
}
|
|
|
|
/*
|
|
* Increment running total if:
|
|
* - there is no condition function
|
|
* - if the condition function returns a non zero
|
|
* result
|
|
*/
|
|
if (!cond || (*cond)(tree))
|
|
{
|
|
++(*num);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* vwr_get_cond()
|
|
* Returns the nodes satisfying the passed condition/function
|
|
* in the passed array of nodes.
|
|
* Returns the number of nodes satisfying the condition in
|
|
* 'num'.
|
|
* Space for the array is allocated. This space must be free'd
|
|
* by the caller of this function.
|
|
*/
|
|
void
|
|
vwr_get_cond(
|
|
VNode tree,
|
|
VNode **array, /* RETURN */
|
|
int *num, /* RETURN */
|
|
int (*cond)(VNode)
|
|
)
|
|
{
|
|
VMethods m;
|
|
VNode child;
|
|
VNode *cur_array = NULL,
|
|
*child_array;
|
|
int child_num,
|
|
cur_num = 0;
|
|
int i,
|
|
num_child;
|
|
|
|
/*
|
|
* Return immediately if a NULL root is passed or
|
|
* the count pointer is NULL
|
|
*/
|
|
if (!tree || !num)
|
|
return;
|
|
|
|
/*
|
|
* Get the methods vector.
|
|
* Return if it is NULL
|
|
*/
|
|
if (!(m = BNODE_METHODS(tree)))
|
|
return;
|
|
|
|
/*
|
|
* Get child count
|
|
*/
|
|
num_child = (*m->get_num_children)(tree);
|
|
|
|
/*
|
|
* Search for child nodes satisfying condition first
|
|
*/
|
|
for (i=0, child = (*m->get_child)(tree, 0);
|
|
(i < num_child);
|
|
child = (*m->get_child)(tree, ++i))
|
|
{
|
|
/*
|
|
* Reset child count, array
|
|
*/
|
|
child_num = 0;
|
|
child_array = NULL;
|
|
|
|
/*
|
|
* Get nodes from current child subtree satisfying
|
|
* condition
|
|
*/
|
|
vwr_get_cond(child, &child_array, &child_num, cond);
|
|
|
|
/*
|
|
* Any nodes satisfied condition ?
|
|
*/
|
|
if (child_num > 0)
|
|
{
|
|
/*
|
|
* Have we found any nodes yet ?
|
|
* If we have,
|
|
* cur_array = the array containing the nodes we
|
|
* have found so far
|
|
* cur_num = the current total
|
|
*/
|
|
if (cur_num > 0)
|
|
{
|
|
VNode *new_array;
|
|
|
|
/*
|
|
* We need to allocate a bigger array
|
|
* and copy everything into it
|
|
*/
|
|
new_array = (VNode *)calloc(sizeof(VNode),
|
|
cur_num + child_num);
|
|
|
|
/*
|
|
* Copying the current nodes list
|
|
*/
|
|
memcpy(new_array, cur_array,
|
|
(cur_num * sizeof(VNode)));
|
|
|
|
/*
|
|
* Copying the child nodes list
|
|
*/
|
|
memcpy(&new_array[cur_num], child_array,
|
|
(child_num * sizeof(VNode)));
|
|
|
|
/*
|
|
* Free the old array AND
|
|
* the child array
|
|
*/
|
|
free((char *)cur_array);
|
|
free((char *)child_array);
|
|
|
|
/*
|
|
* Point current array to this new,
|
|
* bigger array
|
|
*/
|
|
cur_array = new_array;
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If we have not found any nodes yet, make the
|
|
* returned node the current node
|
|
*/
|
|
cur_array = child_array;
|
|
|
|
/*
|
|
* Paranoia - make sure cur_num is not < 0
|
|
*/
|
|
cur_num = 0;
|
|
}
|
|
|
|
/*
|
|
* Increment current node count
|
|
*/
|
|
cur_num += child_num;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Check if parent node satisfies condition
|
|
*/
|
|
if (!cond || (*cond)(tree))
|
|
{
|
|
VNode *new_array;
|
|
|
|
/*
|
|
* Allocate new array to add one extra node
|
|
*/
|
|
new_array = (VNode *)calloc(sizeof(VNode),
|
|
cur_num + 1);
|
|
|
|
/*
|
|
* If we have nodes accumulated from before,
|
|
* copy those guys into this new array.
|
|
*/
|
|
if (cur_num > 0)
|
|
{
|
|
memcpy(new_array, cur_array,
|
|
(cur_num * sizeof(VNode)));
|
|
|
|
/*
|
|
* Copy the parent node
|
|
*/
|
|
new_array[cur_num] = tree;
|
|
|
|
/*
|
|
* Free old array
|
|
*/
|
|
free((char *)cur_array);
|
|
|
|
/*
|
|
* Point current array to this new one
|
|
*/
|
|
cur_array = new_array;
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* No nodes accumulated so far
|
|
* Just copy parent node
|
|
*/
|
|
new_array[0] = tree;
|
|
|
|
cur_array = new_array;
|
|
}
|
|
|
|
/*
|
|
* Increment count
|
|
*/
|
|
++cur_num;
|
|
}
|
|
|
|
/*
|
|
* Return total number of nodes found and the array
|
|
* of nodes
|
|
*/
|
|
*num = cur_num;
|
|
*array = cur_array;
|
|
}
|
|
|
|
/*
|
|
* Repaint the viewer
|
|
*/
|
|
void
|
|
vwr_repaint
|
|
(
|
|
Viewer *v
|
|
)
|
|
{
|
|
VMethods m;
|
|
|
|
if (!v || !v->current_tree)
|
|
return;
|
|
|
|
m = v->methods;
|
|
|
|
(*m->render_tree)(v->current_tree);
|
|
}
|
|
|