2001-03-19 Christopher James Lahey <clahey@ximian.com> * e-tree-sorted.c (ets_proxy_node_changed): Deal with proxy_node_changed being called on a different root node than the one we have in our tree. * e-tree-table-adapter.c: Did some general clean up here. * Merged branch: 2001-03-19 Christopher James Lahey <clahey@ximian.com> * e-tree-sorted.c (find_child_path): Added this function to allow us to find paths that have been removed from the source. (ets_proxy_node_removed): Fixed the memmove here a bit. Call find_child_path. * e-tree-table-adapter.c (find_node): Check that the passed in path isn't NULL. 2001-03-19 Christopher James Lahey <clahey@ximian.com> * e-table-item.c (eti_reflow): Get width from header object instead of calculating it ourselves. * e-table-selection-model.c: Turn off selection saving since it's so slow. * e-table.c (e_table_set_state_object): Set the width of the newly created header object. * e-tree.c (e_tree_set_state_object): Set the width of the newly created header object. (tree_canvas_size_allocate): Don't bother setting the dimensions of the white background twice. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-table-selection-model.c, e-table-selection-model.h: Made ETableSelectionModel save the cursor properly across changed signals if has_save_id is true. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-table-selection-model.c, e-table-selection-model.h: Made ETableSelectionModel save selection properly across changed signals if has_save_id is true. * e-tree-memory.c: A couple of typos. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-tree-memory.c, e-tree-sorted.c: Send pre_changes properly. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-tree-table-adapter.c: Send pre_changes when performing set_expanded or root_node_set_visible. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-tree-sorted.c (ets_is_expandable): When the API requests whether the object is expandable and it isn't, make sure to send a signal when it becomes expandable. * e-tree-table-adapter.c: Made it so that in a number of cases where it doesn't need to create an empty hash table node if the current tree node has no children, it doesn't. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-tree-memory-callbacks.c, e-tree-memory-callbacks.h (etmc_has_save_id, etmc_get_save_id): Added has_save_id and get_save_id to the list of methods supported by e_tree_memory_callbacks. * e-tree-table-adapter.c, e-tree-table-adapter.h: Added saving of expanded nodes. 2001-03-18 Christopher James Lahey <clahey@ximian.com> * e-table-model.c, e-table-model.h (e_table_model_get_save_id): Changed row_save_id to get_save_id to be consistent with ETree. * e-tree-model.c, e-tree-model.h: Added "pre_change" signal. Added has_save_id and get_save_id methods. * e-tree-sorted.c: Proxy pre_change signal. Implemented has_save_id and get_save_id. If the base model doesn't provide has_save_id then we g_strdup_printf the pointer of the base model ETreePath as the save_id. * e-tree-table-adapter.c: Proxy pre_change signal. If base model has_save_id, then use the results of get_save_id as the key for the hash table of node attributes. Otherwise use the pointer as before. 2001-03-17 Christopher James Lahey <clahey@ximian.com> * e-tree-sorted.c (ets_sort_idle): Fixed it so that all nodes get sorted properly instead of just the top node. 2001-03-17 Christopher James Lahey <clahey@ximian.com> * e-table-sorting-utils.c (e_table_sorting_utils_tree_sort): Made tree sorting faster by using a cache. * e-tree-sorted.c: Added commented out debugging g_prints. 2001-03-17 Christopher James Lahey <clahey@ximian.com> * e-tree-sorted.c: Switched to using GMemChunks. 2001-03-17 Christopher James Lahey <clahey@ximian.com> * e-tree-sorted.c (resort_node): Made it so that children of a node that's being sorted don't send changed signals. 2001-03-17 Christopher James Lahey <clahey@ximian.com> * e-table-sorting-utils.c, e-table-sorting-utils.h: Switched to using e_sort and e_search instead of qsort and a linear search. Added the tree functions e_table_sorting_utils_tree_sort, e_table_sorting_utils_tree_check_position, and e_table_sorting_utils_tree_insert. * e-tree-sorted.c: Made this actually do sorting. * e-tree-table-adapter.c (etta_proxy_node_changed): The old_size needs to be the number of visible children + 1 to include the top node. * e-tree.c (e_tree_set_state_object): Set the sort_info on the ETreeSorted when you get a new sort_info. 2001-03-16 Christopher James Lahey <clahey@ximian.com> * Makefile.am: Added e-tree-sorted.c and e-tree-sorted.h. * e-table-item.c (eti_realize_cell_views): Only realize the cells if they're not realized already and if the canvas is realized. * e-table-sorted.c (ets_proxy_model_cell_changed): Matched the change to the signature of e_table_sorting_utils_affects_sort. * e-table-sorting-utils.c, e-table-sorting-utils.h (e_table_sorting_utils_affects_sort): Changed the signature of this function to not take the ETableModel source since it doesn't use it and we need to use this function for ETreeSorted which doesn't have an ETableModel. * e-tree-memory.c (etmm_get_expanded_default): Actually implement the get_expanded_default for this tree. * e-tree-model.h: Cleaned up the indentation here. * e-tree-sorted.c, e-tree-sorted.h: New class meant to be used for sorting trees. It doesn't actually sort yet. It simply acts as an ETreeProxy which is the hardest part of making ETreeSorted. * e-tree.c, e-tree.h: Made this use an ETreeSorted. 2001-03-14 Christopher James Lahey <clahey@ximian.com> * e-table-item.c, e-table-item.h, e-table-selection-model.c, e-table-selection-model.h, e-table-sorted.c, e-table-sorted.h, e-table-subset.c, e-table-subset.h, e-table.c, e-table.h: Switch to handling e_table_model_rows_inserted instead of e_table_model_row_inserted and e_table_model_rows_deleted instead of e_table_model_row_deleted. * e-table-model.c, e-table-model.h: Replaced the signals "model_row_inserted" and "model_row_deleted" with "model_rows_inserted" and "model_rows_deleted" so that when multiple rows are inserted or deleted at the same time they can be handled properly. * e-tree-table-adapter.c: Call "model_rows_inserted" and "model_rows_deleted" instead of "model_changed" when inserting or deleting multiple rows. 2001-03-14 Christopher James Lahey <clahey@ximian.com> * e-table-item.c (e_table_item_row_diff): Made this not count the pixel between rows if it isn't there. 2001-03-14 Christopher James Lahey <clahey@ximian.com> * e-table-item.c (eti_header_structure_changed): Properly attach & realize cell views here. 2001-03-13 Christopher James Lahey <clahey@ximian.com> * e-tree-table-adapter.c (etta_proxy_node_removed): Check that parent_node and parent aren't NULL before making function calls on them. 2001-03-13 Christopher James Lahey <clahey@ximian.com> * e-table-item.c (confirm_height_cache): Fixed a height cache miscalculation. * e-tree-table-adapter.c (find_first_child_node): Made the semantics of this mean that find_first_child_node(adapter, -1) means return the first node in the tree. 2001-03-13 Christopher James Lahey <clahey@ximian.com> * e-table-extras.c: Added a "string-integer" comparison function to the default %ETableExtras so you can do comparisons based on integer value even if you using strings for the data (this lets you do editable numbers, for instance.) * e-table-item.c: Rearranged it a bit so that if you have draw_grid off it doesn't add space for the horizontal lines, nor leave them the background color. * e-table-model.c, e-table-model.h: Added the row_save_id and has_save_id methods to %ETableModel. * e-tree.c, e-tree.h: Replaced e_tree_compute_location with e_tree_get_cell_at. 2001-03-08 Christopher James Lahey <clahey@ximian.com> * Makefile.am: Added e-table/e-table-utils.c, e-table/e-tree-memory-callbacks.c, e-table/e-tree-memory.c, e-table/e-tree-scrolled.c, e-table/e-tree-table-adapter.c, and e-table/e-tree.c. Removed e-table/e-tree-simple.c. Added e-table/e-table-utils.h, e-table/e-tree-memory-callbacks.h, e-table/e-tree-memory.h, e-table/e-tree-scrolled.h, e-table/e-tree-table-adapter.h, and e-table/e-tree.h. Removed e-table/e-tree-simple.h. * e-cell-tree.c: Updated this for the new tree. * e-table-item.c: Added some redraw requests where appropriate. * e-table-item.h: Fixed an incorrect class method declaration. * e-table-model.c, e-table-model.h: Removed e_table_model_has_sort_group and e_table_model_row_sort_group. * e-table-scrolled.h: Removed unused headers. * e-table-simple.c, e-table-simple.h: Rearranged this a bit. * e-table-sorter.c, e-table-sorting-utils.c, e-table-sorting-utils.h: Removed sort group stuff. Added the function e_table_sorting_utils_check_position. * e-table-utils.c, e-table-utils.h: Utility functions for ETable and ETree. * e-table.c: Moved some of the functionality from here to e-table-utils.c so that it can be reused by ETree. * e-tree-memory-callbacks.c, e-tree-memory-callbacks.h: Class to implement an ETreeMemory as callbacks instead of overriding the class. * e-tree-memory.c, e-tree-memory.h: ETreeModel that stores a tree of physical nodes. * e-tree-model.c, e-tree-model.h: Removed most of the functionality from here to the classes ETreeMemory and ETreeTableAdapter. This is now just a simple virtualized tree class. * e-tree-scrolled.c, e-tree-scrolled.h: New class. An ETree in an EScrollFrame. * e-tree-simple.c: Small change. This is no longer used. * e-tree-table-adapter.c, e-tree-table-adapter.h: ETableModel that represents an ETreeModel as a table. * e-tree.c, e-tree.h: New super class kind of like ETable but for trees. End of branch svn path=/trunk/; revision=8837
721 lines
20 KiB
C
721 lines
20 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/* e-cell-tree.c - Tree cell renderer
|
|
* Copyright (C) 2000 Ximian, Inc.
|
|
*
|
|
* Author: Chris Toshok <toshok@ximian.com>
|
|
*
|
|
* A majority of code taken from:
|
|
*
|
|
* the ECellText renderer.
|
|
*
|
|
* Copyright (C) 1998 The Free Software Foundation
|
|
* Copyright (C) 1999, 2000 Ximian, Inc.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "e-cell-tree.h"
|
|
|
|
#include <gtk/gtkenums.h>
|
|
#include <gtk/gtkentry.h>
|
|
#include <gtk/gtkwindow.h>
|
|
#include <gtk/gtkinvisible.h>
|
|
#include <gtk/gtksignal.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <libgnomeui/gnome-canvas.h>
|
|
#include <stdio.h>
|
|
|
|
#include "e-tree-table-adapter.h"
|
|
#include "e-tree-model.h"
|
|
#include "gal/util/e-util.h"
|
|
#include "e-table-item.h"
|
|
|
|
#include <gdk/gdkx.h> /* for BlackPixel */
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "tree-expanded.xpm"
|
|
#include "tree-unexpanded.xpm"
|
|
|
|
#define PARENT_TYPE e_cell_get_type ()
|
|
|
|
typedef struct {
|
|
ECellView cell_view;
|
|
ECellView *subcell_view;
|
|
GdkGC *gc;
|
|
|
|
GnomeCanvas *canvas;
|
|
|
|
} ECellTreeView;
|
|
|
|
static ECellClass *parent_class;
|
|
|
|
#define INDENT_AMOUNT 16
|
|
|
|
static ETreePath
|
|
e_cell_tree_get_node (ETableModel *table_model, int row)
|
|
{
|
|
return e_table_model_value_at (table_model, -1, row);
|
|
}
|
|
|
|
static ETreeModel*
|
|
e_cell_tree_get_tree_model (ETableModel *table_model, int row)
|
|
{
|
|
return e_table_model_value_at (table_model, -2, row);
|
|
}
|
|
|
|
static ETreeTableAdapter *
|
|
e_cell_tree_get_tree_table_adapter (ETableModel *table_model, int row)
|
|
{
|
|
return e_table_model_value_at (table_model, -3, row);
|
|
}
|
|
|
|
static int
|
|
visible_depth_of_node (ETableModel *model, int row)
|
|
{
|
|
ETreeModel *tree_model = e_cell_tree_get_tree_model(model, row);
|
|
ETreeTableAdapter *adapter = e_cell_tree_get_tree_table_adapter(model, row);
|
|
ETreePath path = e_cell_tree_get_node(model, row);
|
|
return (e_tree_model_node_depth (tree_model, path)
|
|
- (e_tree_table_adapter_root_node_is_visible (adapter) ? 0 : 1));
|
|
}
|
|
|
|
static gint
|
|
offset_of_node (ETableModel *table_model, int row)
|
|
{
|
|
return (visible_depth_of_node(table_model, row) + 1) * INDENT_AMOUNT;
|
|
}
|
|
|
|
/*
|
|
* ECell::new_view method
|
|
*/
|
|
static ECellView *
|
|
ect_new_view (ECell *ecell, ETableModel *table_model, void *e_table_item_view)
|
|
{
|
|
ECellTree *ect = E_CELL_TREE (ecell);
|
|
ECellTreeView *tree_view = g_new0 (ECellTreeView, 1);
|
|
GnomeCanvas *canvas = GNOME_CANVAS_ITEM (e_table_item_view)->canvas;
|
|
|
|
tree_view->cell_view.ecell = ecell;
|
|
tree_view->cell_view.e_table_model = table_model;
|
|
tree_view->cell_view.e_table_item_view = e_table_item_view;
|
|
|
|
/* create our subcell view */
|
|
tree_view->subcell_view = e_cell_new_view (ect->subcell, table_model, e_table_item_view /* XXX */);
|
|
|
|
tree_view->canvas = canvas;
|
|
|
|
return (ECellView *)tree_view;
|
|
}
|
|
|
|
/*
|
|
* ECell::kill_view method
|
|
*/
|
|
static void
|
|
ect_kill_view (ECellView *ecv)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecv;
|
|
|
|
/* kill our subcell view */
|
|
e_cell_kill_view (tree_view->subcell_view);
|
|
|
|
g_free (tree_view);
|
|
}
|
|
|
|
/*
|
|
* ECell::realize method
|
|
*/
|
|
static void
|
|
ect_realize (ECellView *ecell_view)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
|
|
/* realize our subcell view */
|
|
e_cell_realize (tree_view->subcell_view);
|
|
|
|
tree_view->gc = gdk_gc_new (GTK_WIDGET (tree_view->canvas)->window);
|
|
|
|
gdk_gc_set_line_attributes (tree_view->gc, 1,
|
|
GDK_LINE_ON_OFF_DASH, None, None);
|
|
gdk_gc_set_dashes (tree_view->gc, 0, "\1\1", 2);
|
|
|
|
if (parent_class->realize)
|
|
(* parent_class->realize) (ecell_view);
|
|
}
|
|
|
|
/*
|
|
* ECell::unrealize method
|
|
*/
|
|
static void
|
|
ect_unrealize (ECellView *ecv)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecv;
|
|
|
|
/* unrealize our subcell view. */
|
|
e_cell_unrealize (tree_view->subcell_view);
|
|
|
|
gdk_gc_unref (tree_view->gc);
|
|
tree_view->gc = NULL;
|
|
|
|
if (parent_class->unrealize)
|
|
(* parent_class->unrealize) (ecv);
|
|
}
|
|
|
|
/*
|
|
* ECell::draw method
|
|
*/
|
|
static void
|
|
ect_draw (ECellView *ecell_view, GdkDrawable *drawable,
|
|
int model_col, int view_col, int row, ECellFlags flags,
|
|
int x1, int y1, int x2, int y2)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *)ecell_view;
|
|
ETreeModel *tree_model = e_cell_tree_get_tree_model(ecell_view->e_table_model, row);
|
|
ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
|
|
ETreePath node;
|
|
GdkRectangle rect, *clip_rect;
|
|
GtkWidget *canvas = GTK_WIDGET (tree_view->canvas);
|
|
GdkGC *fg_gc = canvas->style->fg_gc[GTK_STATE_ACTIVE];
|
|
GdkColor *foreground;
|
|
gboolean selected;
|
|
|
|
int offset, subcell_offset;
|
|
gboolean expanded, expandable;
|
|
|
|
selected = flags & E_CELL_SELECTED;
|
|
|
|
/* only draw the tree effects if we're the active sort */
|
|
if (/* XXX */ TRUE) {
|
|
GdkPixbuf *node_image;
|
|
int node_image_width = 0, node_image_height = 0;
|
|
ETreePath parent_node;
|
|
|
|
node = e_cell_tree_get_node (ecell_view->e_table_model, row);
|
|
|
|
expandable = e_tree_model_node_is_expandable (tree_model, node);
|
|
expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
|
|
|
|
if (visible_depth_of_node (ecell_view->e_table_model, row) > 0 || expandable) {
|
|
offset = offset_of_node (ecell_view->e_table_model, row);
|
|
} else {
|
|
offset = 0;
|
|
}
|
|
subcell_offset = offset;
|
|
|
|
node_image = e_tree_model_icon_at (tree_model, node);
|
|
|
|
if (node_image) {
|
|
node_image_width = gdk_pixbuf_get_width (node_image);
|
|
node_image_height = gdk_pixbuf_get_height (node_image);
|
|
}
|
|
|
|
/*
|
|
* Be a nice citizen: clip to the region we are supposed to draw on
|
|
*/
|
|
rect.x = x1;
|
|
rect.y = y1;
|
|
rect.width = subcell_offset + node_image_width;
|
|
rect.height = y2 - y1;
|
|
|
|
gdk_gc_set_clip_rectangle (tree_view->gc, &rect);
|
|
gdk_gc_set_clip_rectangle (fg_gc, &rect);
|
|
clip_rect = ▭
|
|
|
|
if (selected) {
|
|
foreground = &canvas->style->text [GTK_STATE_SELECTED];
|
|
} else {
|
|
foreground = &canvas->style->text [GTK_STATE_NORMAL];
|
|
}
|
|
|
|
gdk_gc_set_foreground (tree_view->gc, foreground);
|
|
|
|
/* draw our lines */
|
|
if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_lines) {
|
|
|
|
int depth;
|
|
|
|
if (visible_depth_of_node (ecell_view->e_table_model, row) > 0
|
|
|| e_tree_model_node_get_children (tree_model, node, NULL) > 0)
|
|
gdk_draw_line (drawable, tree_view->gc,
|
|
rect.x + offset - INDENT_AMOUNT / 2 + 1,
|
|
rect.y + rect.height / 2,
|
|
rect.x + offset,
|
|
rect.y + rect.height / 2);
|
|
|
|
if (visible_depth_of_node (ecell_view->e_table_model, row) != 0) {
|
|
gdk_draw_line (drawable, tree_view->gc,
|
|
rect.x + offset - INDENT_AMOUNT / 2,
|
|
rect.y,
|
|
rect.x + offset - INDENT_AMOUNT / 2,
|
|
(e_tree_model_node_get_next (tree_model, node)
|
|
? rect.y + rect.height
|
|
: rect.y + rect.height / 2));
|
|
}
|
|
|
|
/* now traverse back up to the root of the tree, checking at
|
|
each level if the node has siblings, and drawing the
|
|
correct vertical pipe for it's configuration. */
|
|
parent_node = e_tree_model_node_get_parent (tree_model, node);
|
|
offset -= INDENT_AMOUNT;
|
|
depth = visible_depth_of_node (ecell_view->e_table_model, row) - 1;
|
|
while (parent_node && depth != 0) {
|
|
if (e_tree_model_node_get_next(tree_model, parent_node)) {
|
|
gdk_draw_line (drawable, tree_view->gc,
|
|
rect.x + offset - INDENT_AMOUNT / 2,
|
|
rect.y,
|
|
rect.x + offset - INDENT_AMOUNT / 2,
|
|
rect.y + rect.height);
|
|
}
|
|
parent_node = e_tree_model_node_get_parent (tree_model, parent_node);
|
|
depth --;
|
|
offset -= INDENT_AMOUNT;
|
|
}
|
|
}
|
|
|
|
/* now draw our icon if we're expandable */
|
|
if (expandable) {
|
|
GdkPixbuf *image;
|
|
int image_width, image_height;
|
|
|
|
image = (expanded
|
|
? E_CELL_TREE(tree_view->cell_view.ecell)->open_pixbuf
|
|
: E_CELL_TREE(tree_view->cell_view.ecell)->closed_pixbuf);
|
|
|
|
image_width = gdk_pixbuf_get_width(image);
|
|
image_height = gdk_pixbuf_get_height(image);
|
|
|
|
gdk_pixbuf_render_to_drawable_alpha (image,
|
|
drawable,
|
|
0, 0,
|
|
x1 + subcell_offset - INDENT_AMOUNT / 2 - image_width / 2,
|
|
y1 + (y2 - y1) / 2 - image_height / 2,
|
|
image_width, image_height,
|
|
GDK_PIXBUF_ALPHA_BILEVEL,
|
|
128,
|
|
GDK_RGB_DITHER_NORMAL,
|
|
image_width, 0);
|
|
}
|
|
|
|
if (node_image) {
|
|
gdk_pixbuf_render_to_drawable_alpha (node_image,
|
|
drawable,
|
|
0, 0,
|
|
x1 + subcell_offset,
|
|
y1 + (y2 - y1) / 2 - node_image_height / 2,
|
|
node_image_width, node_image_height,
|
|
GDK_PIXBUF_ALPHA_BILEVEL,
|
|
128,
|
|
GDK_RGB_DITHER_NORMAL,
|
|
node_image_width, 0);
|
|
subcell_offset += node_image_width;
|
|
}
|
|
}
|
|
|
|
/* Now cause our subcell to draw its contents, shifted by
|
|
subcell_offset pixels */
|
|
e_cell_draw (tree_view->subcell_view, drawable,
|
|
model_col, view_col, row, flags,
|
|
x1 + subcell_offset, y1, x2, y2);
|
|
}
|
|
|
|
/*
|
|
* ECell::event method
|
|
*/
|
|
static gint
|
|
ect_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, int row, ECellFlags flags, ECellActions *actions)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
|
|
ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
|
|
ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
|
|
int offset = offset_of_node (ecell_view->e_table_model, row);
|
|
|
|
switch (event->type) {
|
|
case GDK_BUTTON_PRESS: {
|
|
/* if the event happened in our area of control (and
|
|
we care about it), handle it. */
|
|
|
|
/* only activate the tree control if the click/release happens in the icon's area. */
|
|
if (event->button.x > (offset - INDENT_AMOUNT) && event->button.x < offset) {
|
|
if (e_tree_model_node_is_expandable (tree_model, node)) {
|
|
e_tree_table_adapter_node_set_expanded (tree_table_adapter,
|
|
node,
|
|
!e_tree_table_adapter_node_is_expanded(tree_table_adapter, node));
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (event->button.x < (offset - INDENT_AMOUNT))
|
|
return FALSE;
|
|
}
|
|
default: {
|
|
gint return_value;
|
|
|
|
/* modify the event and pass it off to our subcell_view */
|
|
switch (event->type) {
|
|
case GDK_BUTTON_PRESS:
|
|
case GDK_BUTTON_RELEASE:
|
|
case GDK_2BUTTON_PRESS:
|
|
case GDK_3BUTTON_PRESS:
|
|
event->button.x -= offset;
|
|
break;
|
|
case GDK_MOTION_NOTIFY:
|
|
event->motion.x -= offset;
|
|
break;
|
|
default:
|
|
/* nada */
|
|
}
|
|
|
|
return_value = e_cell_event(tree_view->subcell_view, event, model_col, view_col, row, flags, actions);
|
|
|
|
/* modify the event and pass it off to our subcell_view */
|
|
switch (event->type) {
|
|
case GDK_BUTTON_PRESS:
|
|
case GDK_BUTTON_RELEASE:
|
|
case GDK_2BUTTON_PRESS:
|
|
case GDK_3BUTTON_PRESS:
|
|
event->button.x += offset;
|
|
break;
|
|
case GDK_MOTION_NOTIFY:
|
|
event->motion.x += offset;
|
|
break;
|
|
default:
|
|
/* nada */
|
|
}
|
|
|
|
return return_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ECell::height method
|
|
*/
|
|
static int
|
|
ect_height (ECellView *ecell_view, int model_col, int view_col, int row)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
|
|
return e_cell_height (tree_view->subcell_view, model_col, view_col, row);
|
|
}
|
|
|
|
/*
|
|
* ECell::max_width method
|
|
*/
|
|
static int
|
|
ect_max_width (ECellView *ecell_view, int model_col, int view_col)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
int row;
|
|
int number_of_rows;
|
|
int max_width = 0;
|
|
int width = 0;
|
|
int subcell_max_width;
|
|
|
|
number_of_rows = e_table_model_row_count (ecell_view->e_table_model);
|
|
|
|
subcell_max_width = e_cell_max_width (tree_view->subcell_view, model_col, view_col);
|
|
|
|
for (row = 0; row < number_of_rows; row++) {
|
|
ETreeModel *tree_model = e_cell_tree_get_tree_model(ecell_view->e_table_model, row);
|
|
ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
|
|
ETreePath node;
|
|
GdkPixbuf *node_image;
|
|
int node_image_width = 0, node_image_height = 0;
|
|
|
|
int offset, subcell_offset;
|
|
gboolean expanded, expandable;
|
|
|
|
node = e_cell_tree_get_node (ecell_view->e_table_model, row);
|
|
|
|
offset = offset_of_node (ecell_view->e_table_model, row);
|
|
expandable = e_tree_model_node_is_expandable (tree_model, node);
|
|
expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
|
|
subcell_offset = offset;
|
|
|
|
node_image = e_tree_model_icon_at (tree_model, node);
|
|
|
|
if (node_image) {
|
|
node_image_width = gdk_pixbuf_get_width (node_image);
|
|
node_image_height = gdk_pixbuf_get_height (node_image);
|
|
}
|
|
|
|
width = subcell_max_width + subcell_offset + node_image_width;
|
|
|
|
if (expandable) {
|
|
GdkPixbuf *image;
|
|
|
|
image = (expanded
|
|
? E_CELL_TREE(tree_view->cell_view.ecell)->open_pixbuf
|
|
: E_CELL_TREE(tree_view->cell_view.ecell)->closed_pixbuf);
|
|
|
|
width += gdk_pixbuf_get_width(image);
|
|
}
|
|
|
|
max_width = MAX (max_width, width);
|
|
}
|
|
|
|
return max_width;
|
|
}
|
|
|
|
/*
|
|
* ECellView::show_tooltip method
|
|
*/
|
|
static void
|
|
ect_show_tooltip (ECellView *ecell_view, int model_col, int view_col, int row,
|
|
int col_width, ETableTooltip *tooltip)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
|
|
ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
|
|
int offset = offset_of_node (ecell_view->e_table_model, row);
|
|
GdkPixbuf *node_image;
|
|
|
|
node_image = e_tree_model_icon_at (tree_model, node);
|
|
if (node_image)
|
|
offset += gdk_pixbuf_get_width (node_image);
|
|
|
|
tooltip->x += offset;
|
|
e_cell_show_tooltip (tree_view->subcell_view, model_col, view_col, row, col_width - offset, tooltip);
|
|
}
|
|
|
|
/*
|
|
* ECellView::enter_edit method
|
|
*/
|
|
static void *
|
|
ect_enter_edit (ECellView *ecell_view, int model_col, int view_col, int row)
|
|
{
|
|
/* just defer to our subcell's view */
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
|
|
return e_cell_enter_edit (tree_view->subcell_view, model_col, view_col, row);
|
|
}
|
|
|
|
/*
|
|
* ECellView::leave_edit method
|
|
*/
|
|
static void
|
|
ect_leave_edit (ECellView *ecell_view, int model_col, int view_col, int row, void *edit_context)
|
|
{
|
|
/* just defer to our subcell's view */
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
|
|
e_cell_leave_edit (tree_view->subcell_view, model_col, view_col, row, edit_context);
|
|
}
|
|
|
|
static void
|
|
ect_print (ECellView *ecell_view, GnomePrintContext *context,
|
|
int model_col, int view_col, int row,
|
|
double width, double height)
|
|
{
|
|
ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
|
|
|
|
if (/* XXX only if we're the active sort */ TRUE) {
|
|
ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
|
|
ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter(ecell_view->e_table_model, row);
|
|
ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
|
|
int offset = offset_of_node (ecell_view->e_table_model, row);
|
|
int subcell_offset = offset;
|
|
gboolean expandable = e_tree_model_node_is_expandable (tree_model, node);
|
|
gboolean expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
|
|
|
|
/* draw our lines */
|
|
if (E_CELL_TREE(tree_view->cell_view.ecell)->draw_lines) {
|
|
int depth;
|
|
|
|
if (!e_tree_model_node_is_root (tree_model, node)
|
|
|| e_tree_model_node_get_children (tree_model, node, NULL) > 0) {
|
|
gnome_print_moveto (context,
|
|
offset - INDENT_AMOUNT / 2,
|
|
height / 2);
|
|
|
|
gnome_print_lineto (context,
|
|
offset,
|
|
height / 2);
|
|
}
|
|
|
|
if (visible_depth_of_node (ecell_view->e_table_model, row) != 0) {
|
|
gnome_print_moveto (context,
|
|
offset - INDENT_AMOUNT / 2,
|
|
height);
|
|
gnome_print_lineto (context,
|
|
offset - INDENT_AMOUNT / 2,
|
|
(e_tree_model_node_get_next (tree_model, node)
|
|
? 0
|
|
: height / 2));
|
|
}
|
|
|
|
/* now traverse back up to the root of the tree, checking at
|
|
each level if the node has siblings, and drawing the
|
|
correct vertical pipe for it's configuration. */
|
|
node = e_tree_model_node_get_parent (tree_model, node);
|
|
depth = visible_depth_of_node (ecell_view->e_table_model, row) - 1;
|
|
offset -= INDENT_AMOUNT;
|
|
while (node && depth != 0) {
|
|
if (e_tree_model_node_get_next(tree_model, node)) {
|
|
gnome_print_moveto (context,
|
|
offset - INDENT_AMOUNT / 2,
|
|
height);
|
|
gnome_print_lineto (context,
|
|
offset - INDENT_AMOUNT / 2,
|
|
0);
|
|
}
|
|
node = e_tree_model_node_get_parent (tree_model, node);
|
|
depth --;
|
|
offset -= INDENT_AMOUNT;
|
|
}
|
|
}
|
|
|
|
/* now draw our icon if we're expandable */
|
|
if (expandable) {
|
|
double image_matrix [6] = {16, 0, 0, 16, 0, 0};
|
|
GdkPixbuf *image = (expanded
|
|
? E_CELL_TREE(tree_view->cell_view.ecell)->open_pixbuf
|
|
: E_CELL_TREE(tree_view->cell_view.ecell)->closed_pixbuf);
|
|
int image_width, image_height, image_rowstride;
|
|
guchar *image_pixels;
|
|
|
|
image_width = gdk_pixbuf_get_width(image);
|
|
image_height = gdk_pixbuf_get_height(image);
|
|
image_pixels = gdk_pixbuf_get_pixels(image);
|
|
image_rowstride = gdk_pixbuf_get_rowstride(image);
|
|
|
|
image_matrix [4] = subcell_offset - INDENT_AMOUNT / 2 - image_width / 2;
|
|
image_matrix [5] = height / 2 - image_height / 2;
|
|
|
|
gnome_print_gsave (context);
|
|
gnome_print_concat (context, image_matrix);
|
|
|
|
gnome_print_rgbaimage (context, image_pixels, image_width, image_height, image_rowstride);
|
|
gnome_print_grestore (context);
|
|
}
|
|
|
|
gnome_print_stroke (context);
|
|
|
|
if (gnome_print_translate(context, subcell_offset, 0) == -1)
|
|
/* FIXME */;
|
|
width -= subcell_offset;
|
|
}
|
|
|
|
|
|
e_cell_print (tree_view->subcell_view, context, model_col, view_col, row, width, height);
|
|
}
|
|
|
|
static gdouble
|
|
ect_print_height (ECellView *ecell_view, GnomePrintContext *context,
|
|
int model_col, int view_col, int row,
|
|
double width)
|
|
{
|
|
return 12; /* XXX */
|
|
}
|
|
|
|
/*
|
|
* GtkObject::destroy method
|
|
*/
|
|
static void
|
|
ect_destroy (GtkObject *object)
|
|
{
|
|
ECellTree *ect = E_CELL_TREE (object);
|
|
|
|
/* destroy our subcell */
|
|
if (ect->subcell)
|
|
gtk_object_unref (GTK_OBJECT (ect->subcell));
|
|
ect->subcell = NULL;
|
|
|
|
gdk_pixbuf_unref (ect->open_pixbuf);
|
|
gdk_pixbuf_unref (ect->closed_pixbuf);
|
|
|
|
GTK_OBJECT_CLASS (parent_class)->destroy (object);
|
|
}
|
|
|
|
static void
|
|
e_cell_tree_class_init (GtkObjectClass *object_class)
|
|
{
|
|
ECellClass *ecc = (ECellClass *) object_class;
|
|
|
|
object_class->destroy = ect_destroy;
|
|
|
|
ecc->new_view = ect_new_view;
|
|
ecc->kill_view = ect_kill_view;
|
|
ecc->realize = ect_realize;
|
|
ecc->unrealize = ect_unrealize;
|
|
ecc->draw = ect_draw;
|
|
ecc->event = ect_event;
|
|
ecc->height = ect_height;
|
|
ecc->enter_edit = ect_enter_edit;
|
|
ecc->leave_edit = ect_leave_edit;
|
|
ecc->print = ect_print;
|
|
ecc->print_height = ect_print_height;
|
|
ecc->max_width = ect_max_width;
|
|
ecc->show_tooltip = ect_show_tooltip;
|
|
|
|
parent_class = gtk_type_class (PARENT_TYPE);
|
|
}
|
|
|
|
E_MAKE_TYPE(e_cell_tree, "ECellTree", ECellTree, e_cell_tree_class_init, NULL, PARENT_TYPE);
|
|
|
|
/**
|
|
* e_cell_tree_construct:
|
|
* @ect: the ECellTree we're constructing.
|
|
* @open_pixbuf: pixbuf to be used instead of the '-' icon.
|
|
* @closed_pixbuf: pixbuf to be used instead of the '+' icon.
|
|
* @draw_lines: whether or not to draw the lines between parents/children/siblings.
|
|
* @subcell: the ECell to render to the right of the tree effects.
|
|
*
|
|
* Constructs an ECellTree. used by subclasses that need to
|
|
* initialize a nested ECellTree. See e_cell_tree_new() for more info.
|
|
*
|
|
**/
|
|
void
|
|
e_cell_tree_construct (ECellTree *ect,
|
|
GdkPixbuf *open_pixbuf,
|
|
GdkPixbuf *closed_pixbuf,
|
|
gboolean draw_lines,
|
|
ECell *subcell)
|
|
{
|
|
ect->subcell = subcell;
|
|
if (subcell) {
|
|
gtk_object_ref (GTK_OBJECT (subcell));
|
|
gtk_object_sink (GTK_OBJECT (subcell));
|
|
}
|
|
if (open_pixbuf)
|
|
ect->open_pixbuf = open_pixbuf;
|
|
else
|
|
ect->open_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)tree_expanded_xpm);
|
|
if (closed_pixbuf)
|
|
ect->closed_pixbuf = closed_pixbuf;
|
|
else
|
|
ect->closed_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)tree_unexpanded_xpm);
|
|
|
|
ect->draw_lines = draw_lines;
|
|
}
|
|
|
|
|
|
/**
|
|
* e_cell_tree_new:
|
|
* @open_pixbuf: pixbuf to be used instead of the '-' icon.
|
|
* @closed_pixbuf: pixbuf to be used instead of the '+' icon.
|
|
* @draw_lines: whether or not to draw the lines between parents/children/siblings.
|
|
* @subcell: the ECell to render to the right of the tree effects.
|
|
*
|
|
* Creates a new ECell renderer that can be used to render tree
|
|
* effects that come from an ETreeModel. Various assumptions are made
|
|
* as to the fact that the ETableModel the ETable this cell is
|
|
* associated with is in fact an ETreeModel. The cell uses special
|
|
* columns to get at structural information (needed to draw the
|
|
* lines/icons.
|
|
*
|
|
* Return value: an ECell object that can be used to render trees.
|
|
**/
|
|
ECell *
|
|
e_cell_tree_new (GdkPixbuf *open_pixbuf,
|
|
GdkPixbuf *closed_pixbuf,
|
|
gboolean draw_lines,
|
|
ECell *subcell)
|
|
{
|
|
ECellTree *ect = gtk_type_new (e_cell_tree_get_type ());
|
|
|
|
e_cell_tree_construct (ect, open_pixbuf, closed_pixbuf, draw_lines, subcell);
|
|
|
|
return (ECell *) ect;
|
|
}
|