mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-09-12 20:41:34 +00:00
First pass on making list column resizing live.
2000-03-16 Pavel Cisler <pavel@eazel.com> First pass on making list column resizing live. * libnautilus/Makefile.am * libnautilus/nautilus-list-column-title.c * libnautilus/nautilus-list-column-title.h * libnautilus/gtkflist.h * libnautilus/gtkflist.c * manager/fm-directory-view-list.c * libnautilus/nautilus-gdk-extensions.c * libnautilus/nautilus-gdk-extensions.h
This commit is contained in:
parent
6d921f15ff
commit
53aa5a6308
|
@ -1,3 +1,53 @@
|
|||
2000-03-16 Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
First pass on making list column resizing live.
|
||||
|
||||
* libnautilus/Makefile.am
|
||||
* libnautilus/nautilus-list-column-title.c
|
||||
* libnautilus/nautilus-list-column-title.h
|
||||
New list column title widget that makes it easier to hook into
|
||||
the mouse tracking and allows using flicker free redraw. Currently
|
||||
it doesn't use an offscreen buffer yet, comming soon.
|
||||
|
||||
* libnautilus/gtkflist.c
|
||||
(gtk_flist_initialize_class), (gtk_flist_initialize),
|
||||
(gtk_flist_realize), (list_requisition_width),
|
||||
(gtk_flist_size_request), (new_column_width), (size_allocate_columns),
|
||||
(size_allocate_title_buttons), (get_cell_style), (draw_cell_pixmap),
|
||||
(draw_row), (draw_rows),
|
||||
Made it possible to replace the existing column list view titles
|
||||
from GtkCList view to be replaced with the new widget. In order
|
||||
to do that I had to pull in a number of calls form GtkCList and
|
||||
work around dependencies on column titles being buttons.
|
||||
Prepared code some for smart column truncation.
|
||||
|
||||
* libnautilus/gtkflist.h
|
||||
* libnautilus/gtkflist.c
|
||||
(gtk_flist_track_new_column_width), (gtk_flist_column_resize_track_start),
|
||||
(gtk_flist_column_resize_track), (gtk_flist_column_resize_track_end):
|
||||
Added new calls that hook up with the mouse tracking code that
|
||||
resize the tracker column.
|
||||
|
||||
* manager/fm-directory-view-list.c
|
||||
(get_sort_indicator), (hide_sort_indicator), (show_sort_indicator),
|
||||
(create_flist):
|
||||
Removed code that insert the sort order indicator into the list
|
||||
column - this is now done in the new column title widget.
|
||||
|
||||
* manager/fm-directory-view-list.c
|
||||
(create_flist):
|
||||
Added min and max column width values.
|
||||
|
||||
* libnautilus/gtkflist.c
|
||||
(gtk_flist_initialize_class)
|
||||
Fixe a bug where the "selection_changed" was mistakenly connected
|
||||
to the start_drag callback.
|
||||
|
||||
* libnautilus/nautilus-gdk-extensions.c
|
||||
* libnautilus/nautilus-gdk-extensions.h
|
||||
(nautilus_rectangle_contains), (nautilus_rectangle_inset):
|
||||
Added GdkRectangle utility calls.
|
||||
|
||||
2000-03-16 John Sullivan <sullivan@eazel.com>
|
||||
|
||||
* src/nautilus-index-title.c:
|
||||
|
|
|
@ -47,6 +47,7 @@ libnautilusinclude_HEADERS= \
|
|||
nautilus-gtk-extensions.h \
|
||||
nautilus-icon-factory.h \
|
||||
nautilus-icons-view-icon-item.h \
|
||||
nautilus-list-column-title.h \
|
||||
nautilus-metadata.h \
|
||||
nautilus-mime-type.h \
|
||||
nautilus-self-checks.h \
|
||||
|
@ -85,6 +86,7 @@ libnautilus_la_SOURCES=$(nautilus_idl_sources) \
|
|||
nautilus-icon-factory.c \
|
||||
nautilus-icons-view-icon-item.c \
|
||||
nautilus-lib-self-check-functions.c \
|
||||
nautilus-list-column-title.c \
|
||||
nautilus-mime-type.c \
|
||||
nautilus-self-checks.c \
|
||||
nautilus-string-list.c \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nautilus-glib-extensions.h"
|
||||
#include "nautilus-gtk-macros.h"
|
||||
#include "nautilus-background.h"
|
||||
#include "nautilus-list-column-title.h"
|
||||
|
||||
struct _GtkFListDetails
|
||||
{
|
||||
|
@ -37,7 +38,8 @@ struct _GtkFListDetails
|
|||
/* Delayed selection information */
|
||||
int dnd_select_pending;
|
||||
guint dnd_select_pending_state;
|
||||
|
||||
|
||||
GtkWidget *title;
|
||||
};
|
||||
|
||||
/* maximum amount of milliseconds the mouse button is allowed to stay down and still be considered a click */
|
||||
|
@ -81,8 +83,18 @@ static gboolean gtk_flist_drag_drop (GtkWidget *widget, GdkDragContext *context,
|
|||
static void gtk_flist_drag_data_received (GtkWidget *widget, GdkDragContext *context,
|
||||
gint x, gint y, GtkSelectionData *data,
|
||||
guint info, guint time);
|
||||
static void select_or_unselect_row_cb (GtkCList *clist, gint row, gint column,
|
||||
GdkEvent *event);
|
||||
|
||||
static void gtk_flist_clear (GtkCList *clist);
|
||||
static void draw_row (GtkCList *flist, GdkRectangle *area, gint row, GtkCListRow *clist_row);
|
||||
|
||||
static void gtk_flist_realize (GtkWidget *widget);
|
||||
static void gtk_flist_size_request (GtkWidget *widget, GtkRequisition *requisition);
|
||||
|
||||
static void gtk_flist_column_resize_track_start (GtkWidget *widget, int column);
|
||||
static void gtk_flist_column_resize_track (GtkWidget *widget, int column);
|
||||
static void gtk_flist_column_resize_track_end (GtkWidget *widget, int column);
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (GtkFList, gtk_flist, GTK_TYPE_CLIST)
|
||||
|
||||
|
@ -90,15 +102,17 @@ static guint flist_signals[LAST_SIGNAL];
|
|||
|
||||
/* Standard class initialization function */
|
||||
static void
|
||||
gtk_flist_initialize_class (GtkFListClass *class)
|
||||
gtk_flist_initialize_class (GtkFListClass *klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
GtkCListClass *clist_class;
|
||||
GtkFListClass *flist_class;
|
||||
|
||||
object_class = (GtkObjectClass *) class;
|
||||
widget_class = (GtkWidgetClass *) class;
|
||||
clist_class = (GtkCListClass *) class;
|
||||
object_class = (GtkObjectClass *) klass;
|
||||
widget_class = (GtkWidgetClass *) klass;
|
||||
clist_class = (GtkCListClass *) klass;
|
||||
flist_class = (GtkFListClass *) klass;
|
||||
|
||||
flist_signals[CONTEXT_CLICK_SELECTION] =
|
||||
gtk_signal_new ("context_click_selection",
|
||||
|
@ -135,13 +149,18 @@ gtk_flist_initialize_class (GtkFListClass *class)
|
|||
gtk_signal_new ("selection_changed",
|
||||
GTK_RUN_FIRST,
|
||||
object_class->type,
|
||||
GTK_SIGNAL_OFFSET (GtkFListClass, start_drag),
|
||||
GTK_SIGNAL_OFFSET (GtkFListClass, selection_changed),
|
||||
gtk_marshal_NONE__NONE,
|
||||
GTK_TYPE_NONE, 0);
|
||||
|
||||
gtk_object_class_add_signals (object_class, flist_signals, LAST_SIGNAL);
|
||||
|
||||
flist_class->column_resize_track_start = gtk_flist_column_resize_track_start;
|
||||
flist_class->column_resize_track = gtk_flist_column_resize_track;
|
||||
flist_class->column_resize_track_end = gtk_flist_column_resize_track_end;
|
||||
|
||||
clist_class->clear = gtk_flist_clear;
|
||||
clist_class->draw_row = draw_row;
|
||||
|
||||
widget_class->button_press_event = gtk_flist_button_press;
|
||||
widget_class->button_release_event = gtk_flist_button_release;
|
||||
|
@ -155,20 +174,14 @@ gtk_flist_initialize_class (GtkFListClass *class)
|
|||
widget_class->drag_motion = gtk_flist_drag_motion;
|
||||
widget_class->drag_drop = gtk_flist_drag_drop;
|
||||
widget_class->drag_data_received = gtk_flist_drag_data_received;
|
||||
}
|
||||
|
||||
static void
|
||||
select_or_unselect_row_cb (GtkCList *clist, gint row, gint column, GdkEvent *event)
|
||||
{
|
||||
/* This is the one bottleneck for all selection changes */
|
||||
gtk_signal_emit (GTK_OBJECT (clist), flist_signals[SELECTION_CHANGED]);
|
||||
widget_class->realize = gtk_flist_realize;
|
||||
widget_class->size_request = gtk_flist_size_request;
|
||||
}
|
||||
|
||||
/* Standard object initialization function */
|
||||
static void
|
||||
gtk_flist_initialize (GtkFList *flist)
|
||||
{
|
||||
|
||||
{
|
||||
flist->details = g_new0 (GtkFListDetails, 1);
|
||||
flist->details->anchor_row = -1;
|
||||
|
||||
|
@ -194,6 +207,16 @@ gtk_flist_initialize (GtkFList *flist)
|
|||
"unselect_row",
|
||||
select_or_unselect_row_cb,
|
||||
flist);
|
||||
|
||||
flist->details->title = GTK_WIDGET (nautilus_list_column_title_new());
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
select_or_unselect_row_cb (GtkCList *clist, gint row, gint column, GdkEvent *event)
|
||||
{
|
||||
/* This is the one bottleneck for all selection changes */
|
||||
gtk_signal_emit (GTK_OBJECT (clist), flist_signals[SELECTION_CHANGED]);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -429,6 +452,717 @@ gtk_flist_button_release (GtkWidget *widget, GdkEventButton *event)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* stolen from gtkclist.c for now */
|
||||
|
||||
/* minimum allowed width of a column */
|
||||
#define COLUMN_MIN_WIDTH 5
|
||||
|
||||
/* this defines the base grid spacing */
|
||||
#define CELL_SPACING 1
|
||||
|
||||
/* added the horizontal space at the beginning and end of a row */
|
||||
#define COLUMN_INSET 3
|
||||
|
||||
/* the width of the column resize windows */
|
||||
#define DRAG_WIDTH 6
|
||||
|
||||
/* gives the left pixel of the given column in context of
|
||||
* the clist's hoffset */
|
||||
#define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \
|
||||
(clist)->hoffset)
|
||||
|
||||
/* gives the top pixel of the given row in context of
|
||||
* the clist's voffset */
|
||||
#define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
|
||||
(((row) + 1) * CELL_SPACING) + \
|
||||
(clist)->voffset)
|
||||
|
||||
/* returns the row index from a y pixel location in the
|
||||
* context of the clist's voffset */
|
||||
#define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
|
||||
((clist)->row_height + CELL_SPACING))
|
||||
|
||||
/* returns the GList item for the nth row */
|
||||
#define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
|
||||
(clist)->row_list_end : \
|
||||
g_list_nth ((clist)->row_list, (row)))
|
||||
|
||||
/* returns the total height of the list */
|
||||
#define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \
|
||||
(CELL_SPACING * ((clist)->rows + 1)))
|
||||
|
||||
static void
|
||||
gtk_flist_realize (GtkWidget *widget)
|
||||
{
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
flist = GTK_FLIST (widget);
|
||||
clist = GTK_CLIST (widget);
|
||||
|
||||
clist->column[0].button = flist->details->title;
|
||||
|
||||
NAUTILUS_CALL_PARENT_CLASS (GTK_WIDGET_CLASS, realize, (widget));
|
||||
|
||||
if (clist->title_window) {
|
||||
gtk_widget_set_parent_window (flist->details->title, clist->title_window);
|
||||
}
|
||||
gtk_widget_set_parent (flist->details->title, GTK_WIDGET (clist));
|
||||
gtk_widget_show (flist->details->title);
|
||||
|
||||
GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
|
||||
}
|
||||
|
||||
/* this is here just temporarily */
|
||||
static gint
|
||||
list_requisition_width (GtkCList *clist)
|
||||
{
|
||||
gint width = CELL_SPACING;
|
||||
gint i;
|
||||
|
||||
for (i = clist->columns - 1; i >= 0; i--) {
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
if (clist->column[i].width_set)
|
||||
width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
|
||||
else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
|
||||
width += clist->column[i].button->requisition.width;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_flist_size_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
/* stolen from gtk_clist
|
||||
* make sure the proper title ammount is allocated for the column
|
||||
* title view -- this would not otherwise be done because
|
||||
* GtkFList depends the buttons being there when doing a size calculation
|
||||
*/
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
g_return_if_fail (requisition != NULL);
|
||||
|
||||
clist = GTK_CLIST (widget);
|
||||
flist = GTK_FLIST (widget);
|
||||
|
||||
requisition->width = 0;
|
||||
requisition->height = 0;
|
||||
|
||||
/* compute the size of the column title (title) area */
|
||||
clist->column_title_area.height = 0;
|
||||
if (GTK_CLIST_SHOW_TITLES(clist) && flist->details->title) {
|
||||
GtkRequisition child_requisition;
|
||||
|
||||
gtk_widget_size_request (flist->details->title,
|
||||
&child_requisition);
|
||||
|
||||
child_requisition.height = 20;
|
||||
/* for now */
|
||||
|
||||
clist->column_title_area.height =
|
||||
MAX (clist->column_title_area.height,
|
||||
child_requisition.height);
|
||||
}
|
||||
|
||||
requisition->width += (widget->style->klass->xthickness +
|
||||
GTK_CONTAINER (widget)->border_width) * 2;
|
||||
requisition->height += (clist->column_title_area.height +
|
||||
(widget->style->klass->ythickness +
|
||||
GTK_CONTAINER (widget)->border_width) * 2);
|
||||
|
||||
|
||||
requisition->width += list_requisition_width (clist);
|
||||
requisition->height += LIST_HEIGHT (clist);
|
||||
}
|
||||
|
||||
static gint
|
||||
new_column_width (GtkCList *clist, gint column, gint *x)
|
||||
{
|
||||
gint xthickness = GTK_WIDGET (clist)->style->klass->xthickness;
|
||||
gint width;
|
||||
gint cx;
|
||||
gint dx;
|
||||
gint last_column;
|
||||
|
||||
/* first translate the x position from widget->window
|
||||
* to clist->clist_window */
|
||||
cx = *x - xthickness;
|
||||
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--);
|
||||
|
||||
/* calculate new column width making sure it doesn't end up
|
||||
* less than the minimum width */
|
||||
dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
|
||||
(column < last_column) * CELL_SPACING);
|
||||
width = cx - dx;
|
||||
|
||||
if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width)) {
|
||||
width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
|
||||
cx = dx + width;
|
||||
*x = cx + xthickness;
|
||||
} else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
|
||||
width > clist->column[column].max_width) {
|
||||
width = clist->column[column].max_width;
|
||||
cx = dx + clist->column[column].max_width;
|
||||
*x = cx + xthickness;
|
||||
}
|
||||
|
||||
if (cx < 0 || cx > clist->clist_window_width)
|
||||
*x = -1;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static void
|
||||
size_allocate_columns (GtkCList *clist, gboolean block_resize)
|
||||
{
|
||||
int xoffset = CELL_SPACING + COLUMN_INSET;
|
||||
int last_column;
|
||||
int i;
|
||||
|
||||
/* find last visible column and calculate correct column width */
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--)
|
||||
;
|
||||
|
||||
if (last_column < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i <= last_column; i++) {
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
clist->column[i].area.x = xoffset;
|
||||
if (clist->column[i].width_set) {
|
||||
if (!block_resize && GTK_CLIST_SHOW_TITLES(clist) &&
|
||||
clist->column[i].auto_resize && clist->column[i].button) {
|
||||
gint width;
|
||||
|
||||
width = (clist->column[i].button->requisition.width -
|
||||
(CELL_SPACING + (2 * COLUMN_INSET)));
|
||||
|
||||
if (width > clist->column[i].width)
|
||||
gtk_clist_set_column_width (clist, i, width);
|
||||
}
|
||||
|
||||
clist->column[i].area.width = clist->column[i].width;
|
||||
xoffset += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
|
||||
} else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button) {
|
||||
clist->column[i].area.width =
|
||||
clist->column[i].button->requisition.width -
|
||||
(CELL_SPACING + (2 * COLUMN_INSET));
|
||||
xoffset += clist->column[i].button->requisition.width;
|
||||
}
|
||||
}
|
||||
|
||||
clist->column[last_column].area.width += MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
|
||||
}
|
||||
|
||||
static void
|
||||
size_allocate_title_buttons (GtkCList *clist)
|
||||
{
|
||||
GtkAllocation button_allocation;
|
||||
int last_column;
|
||||
int last_button = 0;
|
||||
int i;
|
||||
|
||||
button_allocation.x = clist->hoffset;
|
||||
button_allocation.y = 0;
|
||||
button_allocation.width = 0;
|
||||
button_allocation.height = clist->column_title_area.height;
|
||||
|
||||
/* find last visible column */
|
||||
for (last_column = clist->columns - 1; last_column >= 0; last_column--)
|
||||
if (clist->column[last_column].visible)
|
||||
break;
|
||||
|
||||
for (i = 0; i < last_column; i++) {
|
||||
if (!clist->column[i].visible) {
|
||||
last_button = i + 1;
|
||||
gdk_window_hide (clist->column[i].window);
|
||||
continue;
|
||||
}
|
||||
|
||||
button_allocation.width += (clist->column[i].area.width +
|
||||
CELL_SPACING + 2 * COLUMN_INSET);
|
||||
|
||||
if (!clist->column[i + 1].button) {
|
||||
gdk_window_hide (clist->column[i].window);
|
||||
continue;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (clist->column[last_button].button,
|
||||
&button_allocation);
|
||||
button_allocation.x += button_allocation.width;
|
||||
button_allocation.width = 0;
|
||||
|
||||
last_button = i + 1;
|
||||
}
|
||||
|
||||
button_allocation.width += (clist->column[last_column].area.width +
|
||||
2 * (CELL_SPACING + COLUMN_INSET));
|
||||
gtk_widget_size_allocate (clist->column[last_button].button,
|
||||
&button_allocation);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
get_cell_style (GtkCList *clist, GtkCListRow *clist_row,
|
||||
gint state, gint column, GtkStyle **style,
|
||||
GdkGC **fg_gc, GdkGC **bg_gc)
|
||||
{
|
||||
gint fg_state;
|
||||
|
||||
if ((state == GTK_STATE_NORMAL) &&
|
||||
(GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
|
||||
fg_state = GTK_STATE_INSENSITIVE;
|
||||
else
|
||||
fg_state = state;
|
||||
|
||||
if (clist_row->cell[column].style) {
|
||||
if (style)
|
||||
*style = clist_row->cell[column].style;
|
||||
if (fg_gc)
|
||||
*fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = clist_row->cell[column].style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = clist_row->cell[column].style->base_gc[state];
|
||||
}
|
||||
} else if (clist_row->style) {
|
||||
if (style)
|
||||
*style = clist_row->style;
|
||||
if (fg_gc)
|
||||
*fg_gc = clist_row->style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = clist_row->style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = clist_row->style->base_gc[state];
|
||||
}
|
||||
} else {
|
||||
if (style)
|
||||
*style = GTK_WIDGET (clist)->style;
|
||||
if (fg_gc)
|
||||
*fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
|
||||
}
|
||||
|
||||
if (state != GTK_STATE_SELECTED) {
|
||||
if (fg_gc && clist_row->fg_set)
|
||||
*fg_gc = clist->fg_gc;
|
||||
if (bg_gc && clist_row->bg_set)
|
||||
*bg_gc = clist->bg_gc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
draw_cell_pixmap (GdkWindow *window, GdkRectangle *clip_rectangle, GdkGC *fg_gc,
|
||||
GdkPixmap *pixmap, GdkBitmap *mask,
|
||||
gint x, gint y, gint width, gint height)
|
||||
{
|
||||
gint xsrc = 0;
|
||||
gint ysrc = 0;
|
||||
|
||||
if (mask) {
|
||||
gdk_gc_set_clip_mask (fg_gc, mask);
|
||||
gdk_gc_set_clip_origin (fg_gc, x, y);
|
||||
}
|
||||
|
||||
if (x < clip_rectangle->x) {
|
||||
xsrc = clip_rectangle->x - x;
|
||||
width -= xsrc;
|
||||
x = clip_rectangle->x;
|
||||
}
|
||||
|
||||
if (x + width > clip_rectangle->x + clip_rectangle->width)
|
||||
width = clip_rectangle->x + clip_rectangle->width - x;
|
||||
|
||||
if (y < clip_rectangle->y) {
|
||||
ysrc = clip_rectangle->y - y;
|
||||
height -= ysrc;
|
||||
y = clip_rectangle->y;
|
||||
}
|
||||
if (y + height > clip_rectangle->y + clip_rectangle->height)
|
||||
height = clip_rectangle->y + clip_rectangle->height - y;
|
||||
|
||||
gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
|
||||
gdk_gc_set_clip_origin (fg_gc, 0, 0);
|
||||
if (mask)
|
||||
gdk_gc_set_clip_mask (fg_gc, NULL);
|
||||
|
||||
return x + MAX (width, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_row (GtkCList *clist,
|
||||
GdkRectangle *area,
|
||||
gint row,
|
||||
GtkCListRow *clist_row)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkRectangle *rect;
|
||||
GdkRectangle row_rectangle;
|
||||
GdkRectangle cell_rectangle;
|
||||
GdkRectangle clip_rectangle;
|
||||
GdkRectangle intersect_rectangle;
|
||||
gint last_column;
|
||||
gint state;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (clist != NULL);
|
||||
|
||||
/* bail now if we arn't drawable yet */
|
||||
if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
|
||||
return;
|
||||
|
||||
widget = GTK_WIDGET (clist);
|
||||
|
||||
/* if the function is passed the pointer to the row instead of null,
|
||||
* it avoids this expensive lookup */
|
||||
if (!clist_row)
|
||||
clist_row = ROW_ELEMENT (clist, row)->data;
|
||||
|
||||
/* rectangle of the entire row */
|
||||
row_rectangle.x = 0;
|
||||
row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
|
||||
row_rectangle.width = clist->clist_window_width;
|
||||
row_rectangle.height = clist->row_height;
|
||||
|
||||
/* rectangle of the cell spacing above the row */
|
||||
cell_rectangle.x = 0;
|
||||
cell_rectangle.y = row_rectangle.y - CELL_SPACING;
|
||||
cell_rectangle.width = row_rectangle.width;
|
||||
cell_rectangle.height = CELL_SPACING;
|
||||
|
||||
/* rectangle used to clip drawing operations, its y and height
|
||||
* positions only need to be set once, so we set them once here.
|
||||
* the x and width are set withing the drawing loop below once per
|
||||
* column */
|
||||
clip_rectangle.y = row_rectangle.y;
|
||||
clip_rectangle.height = row_rectangle.height;
|
||||
|
||||
if (clist_row->state == GTK_STATE_NORMAL)
|
||||
{
|
||||
if (clist_row->fg_set)
|
||||
gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
|
||||
if (clist_row->bg_set)
|
||||
gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
|
||||
}
|
||||
|
||||
state = clist_row->state;
|
||||
|
||||
/* draw the cell borders and background */
|
||||
if (area)
|
||||
{
|
||||
rect = &intersect_rectangle;
|
||||
if (gdk_rectangle_intersect (area, &cell_rectangle,
|
||||
&intersect_rectangle))
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
intersect_rectangle.x,
|
||||
intersect_rectangle.y,
|
||||
intersect_rectangle.width,
|
||||
intersect_rectangle.height);
|
||||
|
||||
/* the last row has to clear its bottom cell spacing too */
|
||||
if (clist_row == clist->row_list_end->data)
|
||||
{
|
||||
cell_rectangle.y += clist->row_height + CELL_SPACING;
|
||||
|
||||
if (gdk_rectangle_intersect (area, &cell_rectangle,
|
||||
&intersect_rectangle))
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
intersect_rectangle.x,
|
||||
intersect_rectangle.y,
|
||||
intersect_rectangle.width,
|
||||
intersect_rectangle.height);
|
||||
}
|
||||
|
||||
if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
|
||||
return;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = &clip_rectangle;
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
cell_rectangle.x,
|
||||
cell_rectangle.y,
|
||||
cell_rectangle.width,
|
||||
cell_rectangle.height);
|
||||
|
||||
/* the last row has to clear its bottom cell spacing too */
|
||||
if (clist_row == clist->row_list_end->data)
|
||||
{
|
||||
cell_rectangle.y += clist->row_height + CELL_SPACING;
|
||||
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
cell_rectangle.x,
|
||||
cell_rectangle.y,
|
||||
cell_rectangle.width,
|
||||
cell_rectangle.height);
|
||||
}
|
||||
}
|
||||
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--)
|
||||
;
|
||||
|
||||
/* iterate and draw all the columns (row cells) and draw their contents */
|
||||
for (i = 0; i < clist->columns; i++)
|
||||
{
|
||||
GtkStyle *style;
|
||||
GdkGC *fg_gc;
|
||||
GdkGC *bg_gc;
|
||||
|
||||
gint width;
|
||||
gint height;
|
||||
gint pixmap_width;
|
||||
gint offset = 0;
|
||||
gint row_center_offset;
|
||||
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
|
||||
|
||||
clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
|
||||
clip_rectangle.width = clist->column[i].area.width;
|
||||
|
||||
/* calculate clipping region clipping region */
|
||||
clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
|
||||
clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
|
||||
(i == last_column) * CELL_SPACING);
|
||||
|
||||
if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
|
||||
&intersect_rectangle))
|
||||
continue;
|
||||
|
||||
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
|
||||
rect->x, rect->y, rect->width, rect->height);
|
||||
|
||||
clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
|
||||
clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
|
||||
(i == last_column) * CELL_SPACING);
|
||||
|
||||
/* calculate real width for column justification */
|
||||
pixmap_width = 0;
|
||||
offset = 0;
|
||||
switch (clist_row->cell[i].type)
|
||||
{
|
||||
case GTK_CELL_TEXT:
|
||||
width = gdk_string_width (style->font,
|
||||
GTK_CELL_TEXT (clist_row->cell[i])->text);
|
||||
break;
|
||||
case GTK_CELL_PIXMAP:
|
||||
gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
|
||||
&pixmap_width, &height);
|
||||
width = pixmap_width;
|
||||
break;
|
||||
case GTK_CELL_PIXTEXT:
|
||||
gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
|
||||
&pixmap_width, &height);
|
||||
width = (pixmap_width +
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
|
||||
gdk_string_width (style->font,
|
||||
GTK_CELL_PIXTEXT
|
||||
(clist_row->cell[i])->text));
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (clist->column[i].justification)
|
||||
{
|
||||
case GTK_JUSTIFY_LEFT:
|
||||
offset = clip_rectangle.x + clist_row->cell[i].horizontal;
|
||||
break;
|
||||
case GTK_JUSTIFY_RIGHT:
|
||||
offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
|
||||
clip_rectangle.width - width);
|
||||
break;
|
||||
case GTK_JUSTIFY_CENTER:
|
||||
case GTK_JUSTIFY_FILL:
|
||||
offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
|
||||
(clip_rectangle.width / 2) - (width / 2));
|
||||
break;
|
||||
};
|
||||
|
||||
/* Draw Text and/or Pixmap */
|
||||
switch (clist_row->cell[i].type)
|
||||
{
|
||||
case GTK_CELL_PIXMAP:
|
||||
draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
|
||||
GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
|
||||
GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
|
||||
offset,
|
||||
clip_rectangle.y + clist_row->cell[i].vertical +
|
||||
(clip_rectangle.height - height) / 2,
|
||||
pixmap_width, height);
|
||||
break;
|
||||
case GTK_CELL_PIXTEXT:
|
||||
offset =
|
||||
draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
|
||||
offset,
|
||||
clip_rectangle.y + clist_row->cell[i].vertical+
|
||||
(clip_rectangle.height - height) / 2,
|
||||
pixmap_width, height);
|
||||
offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
|
||||
case GTK_CELL_TEXT:
|
||||
if (style != GTK_WIDGET (clist)->style)
|
||||
row_center_offset = (((clist->row_height - style->font->ascent -
|
||||
style->font->descent - 1) / 2) + 1.5 +
|
||||
style->font->ascent);
|
||||
else
|
||||
row_center_offset = clist->row_center_offset;
|
||||
|
||||
gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
|
||||
gdk_draw_string (clist->clist_window, style->font, fg_gc,
|
||||
offset,
|
||||
row_rectangle.y + row_center_offset +
|
||||
clist_row->cell[i].vertical,
|
||||
(clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
|
||||
GTK_CELL_TEXT (clist_row->cell[i])->text);
|
||||
gdk_gc_set_clip_rectangle (fg_gc, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* draw focus rectangle */
|
||||
if (clist->focus_row == row &&
|
||||
GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS(widget))
|
||||
{
|
||||
if (!area)
|
||||
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
|
||||
row_rectangle.x, row_rectangle.y,
|
||||
row_rectangle.width - 1, row_rectangle.height - 1);
|
||||
else if (gdk_rectangle_intersect (area, &row_rectangle,
|
||||
&intersect_rectangle))
|
||||
{
|
||||
gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
|
||||
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
|
||||
row_rectangle.x, row_rectangle.y,
|
||||
row_rectangle.width - 1,
|
||||
row_rectangle.height - 1);
|
||||
gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_rows (GtkCList *clist, GdkRectangle *area)
|
||||
{
|
||||
GList *list;
|
||||
gint i;
|
||||
gint first_row;
|
||||
gint last_row;
|
||||
|
||||
if (clist->row_height == 0 || !GTK_WIDGET_DRAWABLE (clist))
|
||||
return;
|
||||
|
||||
first_row = ROW_FROM_YPIXEL (clist, area->y);
|
||||
last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
|
||||
|
||||
/* this is a small special case which exposes the bottom cell line
|
||||
* on the last row -- it might go away if I change the wall the cell
|
||||
* spacings are drawn
|
||||
*/
|
||||
if (clist->rows == first_row)
|
||||
first_row--;
|
||||
|
||||
list = ROW_ELEMENT (clist, first_row);
|
||||
for (i = first_row; i <= last_row ; i++) {
|
||||
if (list == NULL)
|
||||
break;
|
||||
|
||||
GTK_CLIST_CLASS ((GTK_OBJECT (clist))->klass)->draw_row (clist, area, i,
|
||||
list->data);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flist_track_new_column_width (GtkCList *clist, int column, int new_width)
|
||||
{
|
||||
GtkFList *flist;
|
||||
GdkRectangle title_redraw_area;
|
||||
|
||||
flist = GTK_FLIST (clist);
|
||||
|
||||
/* pin new_width to min and max values */
|
||||
if (new_width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
|
||||
new_width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
|
||||
if (clist->column[column].max_width >= 0 &&
|
||||
new_width > clist->column[column].max_width)
|
||||
new_width = clist->column[column].max_width;
|
||||
|
||||
/* check to see if the pinned value is still different */
|
||||
if (clist->column[column].width == new_width)
|
||||
return;
|
||||
|
||||
/* set the new width */
|
||||
clist->column[column].width = new_width;
|
||||
clist->column[column].width_set = TRUE;
|
||||
|
||||
size_allocate_columns (clist, TRUE);
|
||||
size_allocate_title_buttons (clist);
|
||||
|
||||
/* redraw the invalid columns */
|
||||
if (clist->freeze_count == 0) {
|
||||
|
||||
GdkRectangle area;
|
||||
|
||||
area = clist->column_title_area;
|
||||
area.x = clist->column[column].area.x;
|
||||
area.height += clist->clist_window_height;
|
||||
|
||||
draw_rows (clist, &area);
|
||||
}
|
||||
|
||||
/* draw the column title
|
||||
* ToDo:
|
||||
* fix this up
|
||||
*/
|
||||
title_redraw_area.x = GTK_WIDGET (flist->details->title)->allocation.x;
|
||||
title_redraw_area.y = GTK_WIDGET (flist->details->title)->allocation.y;
|
||||
title_redraw_area.width = GTK_WIDGET (flist->details->title)->allocation.width;
|
||||
title_redraw_area.height = GTK_WIDGET (flist->details->title)->allocation.height;
|
||||
|
||||
(GTK_WIDGET_CLASS (NAUTILUS_KLASS (flist->details->title)))->
|
||||
draw (flist->details->title, &title_redraw_area);
|
||||
}
|
||||
|
||||
/* Our handler for motion_notify events. We override all of GtkCList's broken
|
||||
* behavior.
|
||||
*/
|
||||
|
@ -476,6 +1210,45 @@ gtk_flist_motion (GtkWidget *widget, GdkEventMotion *event)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track_start (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist->drag_pos = column;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
int x;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist = GTK_CLIST (widget);
|
||||
|
||||
gtk_widget_get_pointer (widget, &x, NULL);
|
||||
gtk_flist_track_new_column_width (clist, column,
|
||||
new_column_width (clist, column, &x));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track_end (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist->drag_pos = -1;
|
||||
}
|
||||
|
||||
/* Our handler for key_press and key_release events. We do nothing, and we do
|
||||
* this to avoid GtkCList's broken behavior.
|
||||
*/
|
||||
|
@ -578,7 +1351,17 @@ gtk_flist_new_with_titles (int columns, char **titles)
|
|||
GtkFList *flist;
|
||||
|
||||
flist = gtk_type_new (gtk_flist_get_type ());
|
||||
gtk_clist_construct (GTK_CLIST (flist), columns, titles);
|
||||
gtk_clist_construct (GTK_CLIST (flist), columns, NULL);
|
||||
if (titles) {
|
||||
GtkCList *clist;
|
||||
int index;
|
||||
|
||||
clist = GTK_CLIST(flist);
|
||||
|
||||
for (index = 0; index < columns; index++) {
|
||||
clist->column[index].title = g_strdup (titles[index]);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_clist_set_selection_mode (GTK_CLIST (flist),
|
||||
GTK_SELECTION_MULTIPLE);
|
||||
|
|
|
@ -36,7 +36,7 @@ struct _GtkFListClass {
|
|||
GtkCListClass parent_class;
|
||||
|
||||
/* Signal: invoke the popup menu for selected items */
|
||||
void (* context_click_selection) (GtkFList *flist, gint row);
|
||||
void (* context_click_selection) (GtkFList *flist, int row);
|
||||
|
||||
/* Signal: invoke the popup menu for empty areas */
|
||||
void (* context_click_background) (GtkFList *flist);
|
||||
|
@ -45,10 +45,15 @@ struct _GtkFListClass {
|
|||
void (* activate) (GtkFList *flist, gpointer data);
|
||||
|
||||
/* Signal: initiate a drag and drop operation */
|
||||
void (* start_drag) (GtkFList *flist, gint button, GdkEvent *event);
|
||||
void (* start_drag) (GtkFList *flist, int button, GdkEvent *event);
|
||||
|
||||
/* Signal: selection has changed */
|
||||
void (* selection_changed) (GtkFList *flist);
|
||||
|
||||
/* column resize tracking calls */
|
||||
void (* column_resize_track_start) (GtkWidget *widget, int column);
|
||||
void (* column_resize_track) (GtkWidget *widget, int column);
|
||||
void (* column_resize_track_end) (GtkWidget *widget, int column);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -148,6 +148,47 @@ nautilus_fill_rectangle_with_gradient (GdkDrawable *drawable,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_rectangle_contains:
|
||||
* @rectangle: Rectangle possibly containing a point.
|
||||
* @x: X coordinate of a point.
|
||||
* @y: Y coordinate of a point.
|
||||
*
|
||||
* Retun TRUE if point is contained inside a rectangle
|
||||
*/
|
||||
gboolean
|
||||
nautilus_rectangle_contains (const GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
g_return_val_if_fail (rectangle != NULL, FALSE);
|
||||
return rectangle->x <= x && rectangle->x + rectangle->width >= x
|
||||
&& rectangle->y <= y && rectangle->y + rectangle->height >= y;
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_rectangle_inset:
|
||||
* @rectangle: Rectangle we are insetting.
|
||||
* @x: Horizontal ammount to inset by.
|
||||
* @y: Vertical ammount to inset by.
|
||||
*
|
||||
* Inset a rectangle by a given horizontal and vertical ammount.
|
||||
* Pass in negative inset values to grow the rectangle, positive to
|
||||
* shrink it.
|
||||
*/
|
||||
void
|
||||
nautilus_rectangle_inset (GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
g_return_if_fail (rectangle != NULL);
|
||||
|
||||
rectangle->x += x;
|
||||
rectangle->width -= 2 * x;
|
||||
rectangle->y += y;
|
||||
rectangle->height -= 2 * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_interpolate_color:
|
||||
* @ratio: Place on line between colors to interpolate.
|
||||
|
|
|
@ -75,6 +75,15 @@ void nautilus_fill_rectangle_with_gradient (GdkDrawable *drawab
|
|||
const GdkColor *end_color,
|
||||
gboolean horizontal_gradient);
|
||||
|
||||
/* Misc GdkRectangle helper functions */
|
||||
gboolean nautilus_rectangle_contains (const GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
void nautilus_rectangle_inset (GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
/* A basic operation we use for drawing gradients is interpolating two colors.*/
|
||||
void nautilus_interpolate_color (gdouble ratio,
|
||||
const GdkColor *start_color,
|
||||
|
|
763
libnautilus-extensions/nautilus-list-column-title.c
Normal file
763
libnautilus-extensions/nautilus-list-column-title.c
Normal file
|
@ -0,0 +1,763 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
||||
|
||||
nautilus-list-column-title.c: List column title widget for interacting with list columns
|
||||
|
||||
Copyright (C) 2000 Eazel, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
Authors: Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
*/
|
||||
|
||||
#include "nautilus-list-column-title.h"
|
||||
#include "nautilus-gtk-macros.h"
|
||||
#include "nautilus-gdk-extensions.h"
|
||||
|
||||
/* for now we need to know about GtkFList */
|
||||
#include "gtkflist.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
|
||||
#include <libgnomeui/gnome-pixmap.h>
|
||||
|
||||
/* these are from GtkCList, for now we need to copy them here
|
||||
* eventually the target list should be able to describe the values
|
||||
*/
|
||||
|
||||
enum {
|
||||
/* this defines the base grid spacing */
|
||||
CELL_SPACING = 1,
|
||||
|
||||
/* added the horizontal space at the beginning and end of a row */
|
||||
COLUMN_INSET = 3,
|
||||
|
||||
/* from GtkButton */
|
||||
CHILD_SPACING = 1,
|
||||
|
||||
/* the width of the column resize windows */
|
||||
DRAG_WIDTH = 6
|
||||
};
|
||||
|
||||
static char * down_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"......",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
" .. "
|
||||
};
|
||||
|
||||
static char * up_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
" .. ",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
"......"
|
||||
};
|
||||
|
||||
struct NautilusListColumnTitleDetails
|
||||
{
|
||||
/* gc for blitting sort order pixmaps, lazily allocated */
|
||||
GdkGC *copy_area_gc;
|
||||
|
||||
/* sort order indicator pixmaps, lazily allocated */
|
||||
GnomePixmap *up_indicator;
|
||||
GnomePixmap *down_indicator;
|
||||
|
||||
int tracking_column_resize;
|
||||
/* index of the column we are currently tracking or -1 */
|
||||
int tracking_column_prelight;
|
||||
/* index of the column we are currently rolling over or -1 */
|
||||
int tracking_column_press;
|
||||
/* index of the column we are currently pressing or -1 */
|
||||
|
||||
int last_tracking_x;
|
||||
/* last horizontal track point so we can only resize when needed */
|
||||
gboolean resize_cursor_on;
|
||||
};
|
||||
|
||||
static void nautilus_list_column_title_initialize_class (gpointer klass);
|
||||
static void nautilus_list_column_title_initialize (gpointer object, gpointer klass);
|
||||
static void nautilus_list_column_title_paint (GtkWidget *widget, GdkRectangle *area);
|
||||
static void nautilus_list_column_title_draw (GtkWidget *widget, GdkRectangle *box);
|
||||
static void nautilus_list_column_title_buffered_draw (GtkWidget *widget);
|
||||
static gboolean nautilus_list_column_title_expose (GtkWidget *widget, GdkEventExpose *event);
|
||||
static void nautilus_list_column_title_realize (GtkWidget *widget);
|
||||
static void nautilus_list_column_title_finalize (GtkObject *object);
|
||||
static void nautilus_list_column_title_request (GtkWidget *widget, GtkRequisition *requisition);
|
||||
|
||||
static gboolean nautilus_list_column_title_motion (GtkWidget *widget, GdkEventMotion *event);
|
||||
static gboolean nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event);
|
||||
|
||||
static gboolean nautilus_list_column_title_button_press (GtkWidget *widget, GdkEventButton *event);
|
||||
static gboolean nautilus_list_column_title_button_release (GtkWidget *widget, GdkEventButton *event);
|
||||
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusListColumnTitle, nautilus_list_column_title, GTK_TYPE_BIN)
|
||||
/* generates nautilus_list_column_title_get_type */
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_initialize_class (gpointer klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
|
||||
object_class = GTK_OBJECT_CLASS (klass);
|
||||
widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = nautilus_list_column_title_finalize;
|
||||
widget_class->draw = nautilus_list_column_title_draw;
|
||||
widget_class->expose_event = nautilus_list_column_title_expose;
|
||||
widget_class->realize = nautilus_list_column_title_realize;
|
||||
widget_class->size_request = nautilus_list_column_title_request;
|
||||
widget_class->motion_notify_event = nautilus_list_column_title_motion;
|
||||
widget_class->leave_notify_event = nautilus_list_column_title_leave;
|
||||
widget_class->button_press_event = nautilus_list_column_title_button_press;
|
||||
widget_class->button_release_event = nautilus_list_column_title_button_release;
|
||||
}
|
||||
|
||||
NautilusListColumnTitle *
|
||||
nautilus_list_column_title_new (void)
|
||||
{
|
||||
return gtk_type_new (nautilus_list_column_title_get_type ());
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_initialize (gpointer object, gpointer klass)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(object);
|
||||
column_title->details = g_new0 (NautilusListColumnTitleDetails, 1);
|
||||
|
||||
/* copy_gc, up/down indicators get allocated lazily when needed */
|
||||
column_title->details->copy_area_gc = NULL;
|
||||
column_title->details->up_indicator = NULL;
|
||||
column_title->details->down_indicator = NULL;
|
||||
|
||||
column_title->details->resize_cursor_on = FALSE;
|
||||
column_title->details->tracking_column_resize = -1;
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
column_title->details->tracking_column_press = -1;
|
||||
column_title->details->last_tracking_x = -1;
|
||||
|
||||
GTK_WIDGET_UNSET_FLAGS (object, GTK_NO_WINDOW);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_realize (GtkWidget *widget)
|
||||
{
|
||||
GdkWindowAttr attributes;
|
||||
int attributes_mask;
|
||||
|
||||
g_assert (widget != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
||||
|
||||
/* ask for expose events */
|
||||
attributes.window_type = GDK_WINDOW_CHILD;
|
||||
attributes.x = widget->allocation.x + GTK_CONTAINER (widget)->border_width;
|
||||
attributes.y = widget->allocation.y + GTK_CONTAINER (widget)->border_width;
|
||||
attributes.width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
|
||||
attributes.height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.visual = gtk_widget_get_visual (widget);
|
||||
attributes.colormap = gtk_widget_get_colormap (widget);
|
||||
attributes.event_mask = gtk_widget_get_events (widget);
|
||||
attributes.event_mask |= GDK_EXPOSURE_MASK
|
||||
| GDK_ENTER_NOTIFY_MASK
|
||||
| GDK_LEAVE_NOTIFY_MASK
|
||||
| GDK_BUTTON_PRESS_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK
|
||||
| GDK_POINTER_MOTION_MASK
|
||||
| GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_KEY_PRESS_MASK;
|
||||
|
||||
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
||||
|
||||
/* give ourselves a background window */
|
||||
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
|
||||
&attributes, attributes_mask);
|
||||
gdk_window_set_user_data (widget->window, widget);
|
||||
|
||||
widget->style = gtk_style_attach (widget->style, widget->window);
|
||||
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_finalize (GtkObject *object)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(object);
|
||||
|
||||
if (column_title->details->up_indicator != NULL)
|
||||
gtk_widget_destroy (GTK_WIDGET (column_title->details->up_indicator));
|
||||
if (column_title->details->down_indicator != NULL)
|
||||
gtk_widget_destroy (GTK_WIDGET (column_title->details->down_indicator));
|
||||
|
||||
/* FIXME:
|
||||
* figure out if we need to delete the copy_area_gc here
|
||||
*/
|
||||
|
||||
g_free (column_title->details);
|
||||
|
||||
NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
/* size requisition: make sure we have at least a minimal height */
|
||||
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (requisition != NULL);
|
||||
|
||||
requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
|
||||
widget->style->klass->xthickness) * 2;
|
||||
requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
|
||||
widget->style->klass->ythickness) * 2;
|
||||
|
||||
|
||||
if (GTK_BIN (widget)->child && GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child)) {
|
||||
GtkRequisition child_requisition;
|
||||
|
||||
gtk_widget_size_request (GTK_BIN (widget)->child, &child_requisition);
|
||||
|
||||
requisition->width += child_requisition.width;
|
||||
requisition->height += child_requisition.height;
|
||||
|
||||
requisition->height = MIN (requisition->height, 10);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_column_label_at (GtkWidget *column_title, int index)
|
||||
{
|
||||
GtkCList *parent_clist;
|
||||
|
||||
parent_clist = GTK_CLIST (column_title->parent);
|
||||
|
||||
return parent_clist->column[index].title;
|
||||
}
|
||||
|
||||
static void
|
||||
get_column_frame_at(GtkWidget *column_title, int index, GdkRectangle *result)
|
||||
{
|
||||
GtkCList *parent_clist;
|
||||
parent_clist = GTK_CLIST (column_title->parent);
|
||||
|
||||
*result = parent_clist->column_title_area;
|
||||
result->x = parent_clist->hoffset + parent_clist->column[index].area.x - COLUMN_INSET;
|
||||
result->y = 0;
|
||||
result->width = parent_clist->column[index].area.width
|
||||
+ CELL_SPACING + 2 * COLUMN_INSET - 1;
|
||||
}
|
||||
|
||||
static GnomePixmap *
|
||||
get_sort_indicator (GtkWidget *widget, gboolean ascending)
|
||||
{
|
||||
/* return the sort order pixmap for a given sort direction
|
||||
* allocate the pixmap first time around
|
||||
*/
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
if (ascending) {
|
||||
if (column_title->details->up_indicator == NULL) {
|
||||
column_title->details->up_indicator =
|
||||
GNOME_PIXMAP (gnome_pixmap_new_from_xpm_d (up_xpm));
|
||||
}
|
||||
return column_title->details->up_indicator;
|
||||
} else {
|
||||
if (column_title->details->down_indicator == NULL) {
|
||||
column_title->details->down_indicator =
|
||||
GNOME_PIXMAP (gnome_pixmap_new_from_xpm_d (down_xpm));
|
||||
}
|
||||
return column_title->details->down_indicator;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME:
|
||||
* Some of these magic numbers could be replaced with some more dynamic values
|
||||
*/
|
||||
enum {
|
||||
CELL_TITLE_INSET = 6,
|
||||
TITLE_BASELINE_OFFSET = 6,
|
||||
SORT_ORDER_INDICATOR_WIDTH = 10
|
||||
};
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_paint (GtkWidget *widget, GdkRectangle *area)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
g_assert (GTK_CLIST (widget->parent) != NULL);
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
GdkRectangle cell_rectangle;
|
||||
GdkRectangle cell_redraw_area;
|
||||
const char *cell_label;
|
||||
int x_offset;
|
||||
GnomePixmap *sort_indicator;
|
||||
gboolean right_justified;
|
||||
|
||||
sort_indicator = NULL;
|
||||
right_justified = (parent_clist->column[index].justification == GTK_JUSTIFY_RIGHT);
|
||||
|
||||
/* pick the ascending/descending sort indicator if needed */
|
||||
if (index == parent_clist->sort_column) {
|
||||
sort_indicator = get_sort_indicator (widget,
|
||||
parent_clist->sort_type == GTK_SORT_ASCENDING);
|
||||
}
|
||||
|
||||
get_column_frame_at (widget, index, &cell_rectangle);
|
||||
gdk_rectangle_intersect (&cell_rectangle, area, &cell_redraw_area);
|
||||
|
||||
if (cell_redraw_area.width == 0 || cell_redraw_area.height == 0) {
|
||||
/* no work, go on to the next */
|
||||
continue;
|
||||
}
|
||||
|
||||
cell_label = get_column_label_at (widget, index);
|
||||
|
||||
/* FIXME:
|
||||
* add support for center justification
|
||||
*/
|
||||
|
||||
if (right_justified) {
|
||||
x_offset = cell_rectangle.x + cell_rectangle.width - CELL_TITLE_INSET;
|
||||
} else {
|
||||
x_offset = cell_rectangle.x + CELL_TITLE_INSET;
|
||||
}
|
||||
|
||||
/* Paint the column title tiles as rectangles using "menu" style
|
||||
* buttons as used by GtkCList produce round corners in some themes.
|
||||
* Eventually we might consider having a separate style for column titles
|
||||
*/
|
||||
gtk_paint_box (widget->style, widget->window,
|
||||
column_title->details->tracking_column_prelight == index ?
|
||||
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
|
||||
column_title->details->tracking_column_press == index ?
|
||||
GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
||||
area, widget, "menu",
|
||||
cell_rectangle.x, cell_rectangle.y,
|
||||
cell_rectangle.width, cell_rectangle.height);
|
||||
|
||||
|
||||
/* Draw the sort indicator if needed */
|
||||
if (sort_indicator != NULL) {
|
||||
int y_offset = TITLE_BASELINE_OFFSET + 2;
|
||||
|
||||
if (right_justified) {
|
||||
x_offset -= SORT_ORDER_INDICATOR_WIDTH;
|
||||
}
|
||||
|
||||
/* allocate the sort indicator copy gc first time around */
|
||||
if (column_title->details->copy_area_gc == NULL) {
|
||||
column_title->details->copy_area_gc = gdk_gc_new (widget->window);
|
||||
gdk_gc_set_function (column_title->details->copy_area_gc, GDK_COPY);
|
||||
}
|
||||
/* move the pixmap clip mask and origin to the right spot in the gc */
|
||||
gdk_gc_set_clip_mask (column_title->details->copy_area_gc,
|
||||
sort_indicator->mask);
|
||||
gdk_gc_set_clip_origin (column_title->details->copy_area_gc, x_offset, y_offset);
|
||||
|
||||
|
||||
gdk_draw_pixmap (widget->window, column_title->details->copy_area_gc,
|
||||
sort_indicator->pixmap, 0, 0, x_offset, y_offset,
|
||||
-1, -1);
|
||||
|
||||
if (!right_justified) {
|
||||
x_offset += SORT_ORDER_INDICATOR_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell_label) {
|
||||
/* clip a little more than the cell rectangle to
|
||||
* not have the text draw over the cell broder
|
||||
* (this might no longer be needed when the
|
||||
* title gets truncated properly, as opposed to
|
||||
* getting chopped of
|
||||
*/
|
||||
nautilus_rectangle_inset (&cell_redraw_area, 2, 2);
|
||||
if (right_justified) {
|
||||
x_offset -= gdk_string_width (widget->style->font, cell_label) + 4;
|
||||
}
|
||||
|
||||
gtk_paint_string (widget->style, widget->window, GTK_STATE_NORMAL,
|
||||
&cell_redraw_area, widget, "label",
|
||||
x_offset,
|
||||
cell_rectangle.y + cell_rectangle.height - TITLE_BASELINE_OFFSET,
|
||||
cell_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_draw (GtkWidget *widget, GdkRectangle *area)
|
||||
{
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (area != NULL);
|
||||
|
||||
if (!GTK_WIDGET_DRAWABLE (widget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nautilus_list_column_title_paint (widget, area);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_buffered_draw (GtkWidget *widget)
|
||||
{
|
||||
/* draw using an offscreen bitmap
|
||||
* for now just draw directly
|
||||
* add code here to lazily allocate an offscreen and pass it to the paint call
|
||||
* without the offscreen you can now see a funny artifact when part of the
|
||||
* old column border does not get erased properly when shrinking a column
|
||||
*/
|
||||
GdkRectangle redraw_area;
|
||||
redraw_area.x = widget->allocation.x;
|
||||
redraw_area.y = widget->allocation.y;
|
||||
redraw_area.width = widget->allocation.width;
|
||||
redraw_area.height = widget->allocation.height;
|
||||
|
||||
nautilus_list_column_title_draw (widget, &redraw_area);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_expose (GtkWidget *widget, GdkEventExpose *event)
|
||||
{
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (event != NULL);
|
||||
|
||||
if (!GTK_WIDGET_DRAWABLE (widget)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nautilus_list_column_title_paint (widget, &event->area);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
in_column_rect (GtkWidget *widget, int x, int y)
|
||||
{
|
||||
/* return the index of the column we hit or -1 */
|
||||
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
/* hit testing for column resizing */
|
||||
GdkRectangle cell_rectangle;
|
||||
|
||||
get_column_frame_at (widget, index, &cell_rectangle);
|
||||
|
||||
/* inset by a pixel so that you have to move past the border
|
||||
* to be considered inside the rect
|
||||
* nautilus_list_column_title_leave depends on this
|
||||
*/
|
||||
nautilus_rectangle_inset (&cell_rectangle, 1, 0);
|
||||
|
||||
|
||||
if (nautilus_rectangle_contains (&cell_rectangle, x, y))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
in_resize_rect (GtkWidget *widget, int x, int y)
|
||||
{
|
||||
/* return the index of the resize rect of a column we hit or -1 */
|
||||
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
/* hit testing for column resizing */
|
||||
GdkRectangle resize_rectangle;
|
||||
|
||||
get_column_frame_at (widget, index, &resize_rectangle);
|
||||
|
||||
nautilus_rectangle_inset (&resize_rectangle, 1, 0);
|
||||
|
||||
resize_rectangle.x = resize_rectangle.x + resize_rectangle.width - DRAG_WIDTH / 2;
|
||||
resize_rectangle.width = DRAG_WIDTH;
|
||||
|
||||
if (nautilus_rectangle_contains (&resize_rectangle, x, y))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
show_hide_resize_cursor_if_needed (GtkWidget *widget, gboolean on)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
if (on == column_title->details->resize_cursor_on)
|
||||
/* already set right */
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
/* switch to a resize cursor */
|
||||
GdkCursor *cursor;
|
||||
|
||||
cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
||||
gdk_window_set_cursor (widget->window, cursor);
|
||||
gdk_cursor_destroy (cursor);
|
||||
} else
|
||||
/* restore to old cursor */
|
||||
gdk_window_set_cursor (widget->window, NULL);
|
||||
|
||||
column_title->details->resize_cursor_on = on;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
track_prelight (GtkWidget *widget, int mouse_x, int mouse_y)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
int over_column;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
over_column = in_column_rect (widget, mouse_x, mouse_y);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
/* resizing a column, don't prelight */
|
||||
over_column = -1;
|
||||
}
|
||||
|
||||
if (column_title->details->tracking_column_press != -1) {
|
||||
/* pressing a column, don't prelight */
|
||||
over_column = -1;
|
||||
}
|
||||
|
||||
if (column_title->details->tracking_column_prelight == over_column) {
|
||||
/* no change */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* update state and tell callers to redraw */
|
||||
column_title->details->tracking_column_prelight = over_column;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_motion (GtkWidget *widget, GdkEventMotion *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
int mouse_x, mouse_y;
|
||||
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
gdk_window_get_pointer (widget->window, &mouse_x, &mouse_y, NULL);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
/* we are currently tracking a column */
|
||||
if (column_title->details->last_tracking_x != mouse_x) {
|
||||
/* mouse did move horizontally since last time */
|
||||
column_title->details->last_tracking_x = mouse_x;
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track (parent_list,
|
||||
column_title->details->tracking_column_resize);
|
||||
}
|
||||
} else {
|
||||
/* make sure we are showing the right cursor */
|
||||
show_hide_resize_cursor_if_needed (widget,
|
||||
in_resize_rect (widget, mouse_x, mouse_y) != -1);
|
||||
}
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
if (track_prelight (widget, mouse_x, mouse_y)) {
|
||||
nautilus_list_column_title_buffered_draw (widget);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event)
|
||||
{
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
if (track_prelight (widget, (int)event->x, (int)event->y)) {
|
||||
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
|
||||
nautilus_list_column_title_buffered_draw (widget);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_button_press (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
|
||||
g_assert (event != NULL);
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (NAUTILUS_LIST_COLUMN_TITLE(widget)->details->tracking_column_resize == -1);
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS) {
|
||||
int resized_column;
|
||||
int clicked_column;
|
||||
|
||||
resized_column = in_resize_rect (widget, (int)event->x, (int)event->y);
|
||||
clicked_column = in_column_rect (widget, (int)event->x, (int)event->y);
|
||||
|
||||
if (resized_column != -1) {
|
||||
GdkCursor *cursor;
|
||||
int grab_result;
|
||||
|
||||
/* during the drag, use the resize cursor */
|
||||
cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
||||
|
||||
/* grab the pointer events so that we get them even when
|
||||
* the mouse tracks out of the widget window
|
||||
*/
|
||||
grab_result = gdk_pointer_grab (widget->window, FALSE,
|
||||
GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_BUTTON1_MOTION_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK,
|
||||
NULL, cursor, event->time);
|
||||
gdk_cursor_destroy (cursor);
|
||||
|
||||
if (grab_result != 0)
|
||||
/* failed to grab the pointer, give up
|
||||
*
|
||||
* The grab results are not very well documented
|
||||
* looks like they may be Success, GrabSuccess, AlreadyGrabbed
|
||||
* or anything else the low level X calls in gdk_pointer_grab
|
||||
* decide to return.
|
||||
*/
|
||||
return FALSE;
|
||||
|
||||
/* set up new state */
|
||||
column_title->details->tracking_column_resize = resized_column;
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
|
||||
/* FIXME:
|
||||
* use a "resized" state here ?
|
||||
*/
|
||||
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
|
||||
|
||||
/* start column resize tracking */
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track_start (parent_list, resized_column);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
if (clicked_column != -1) {
|
||||
/* clicked a column, draw the pressed column title */
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
column_title->details->tracking_column_press = clicked_column;
|
||||
gtk_widget_set_state (widget, GTK_STATE_ACTIVE);
|
||||
/* FIXME:
|
||||
* buffered draw may be better here
|
||||
*/
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_button_release (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
|
||||
g_assert (event != NULL);
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
|
||||
/* let go of all the pointer events */
|
||||
if (gdk_pointer_is_grabbed ())
|
||||
gdk_pointer_ungrab (event->time);
|
||||
|
||||
/* end column resize tracking */
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track_end (parent_list,
|
||||
column_title->details->tracking_column_resize);
|
||||
column_title->details->tracking_column_resize = -1;
|
||||
|
||||
} else if (column_title->details->tracking_column_press != -1) {
|
||||
|
||||
/* column title press -- change the sort order */
|
||||
gtk_signal_emit_by_name (GTK_OBJECT (parent_list), "click_column",
|
||||
column_title->details->tracking_column_press);
|
||||
/* end press tracking */
|
||||
column_title->details->tracking_column_press = -1;
|
||||
}
|
||||
|
||||
track_prelight (widget, (int)event->x, (int)event->y);
|
||||
gtk_widget_set_state (widget,
|
||||
column_title->details->tracking_column_prelight != -1 ?
|
||||
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
|
||||
|
||||
/* FIXME:
|
||||
* buffered draw may be better here
|
||||
*/
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return FALSE;
|
||||
}
|
73
libnautilus-extensions/nautilus-list-column-title.h
Normal file
73
libnautilus-extensions/nautilus-list-column-title.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
||||
|
||||
nautilus-list-column-title.h: List column title widget for interacting with list columns
|
||||
|
||||
Copyright (C) 2000 Eazel, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
Authors: Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __NAUTILUS_LIST_COLUMN_TITLE__
|
||||
#define __NAUTILUS_LIST_COLUMN_TITLE__
|
||||
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkbin.h>
|
||||
#include <gtk/gtkenums.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define NAUTILUS_TYPE_LIST_COLUMN_TITLE \
|
||||
(nautilus_list_column_title_get_type ())
|
||||
#define NAUTILUS_LIST_COLUMN_TITLE(obj) \
|
||||
(GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_LIST_COLUMN_TITLE, NautilusListColumnTitle))
|
||||
#define NAUTILUS_LIST_COLUMN_TITLE_CLASS(klass) \
|
||||
(GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_LIST_COLUMN_TITLE, NautilusListColumnTitleClass))
|
||||
#define NAUTILUS_IS_LIST_COLUMN_TITLE(obj) \
|
||||
(GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_LIST_COLUMN_TITLE))
|
||||
#define NAUTILUS_IS_LIST_COLUMN_TITLE_CLASS(klass) \
|
||||
(GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_LIST_COLUMN_TITLE))
|
||||
|
||||
typedef struct NautilusListColumnTitle NautilusListColumnTitle;
|
||||
typedef struct NautilusListColumnTitleClass NautilusListColumnTitleClass;
|
||||
typedef struct NautilusListColumnTitleDetails NautilusListColumnTitleDetails;
|
||||
|
||||
|
||||
struct NautilusListColumnTitle
|
||||
{
|
||||
GtkBin bin;
|
||||
NautilusListColumnTitleDetails *details;
|
||||
};
|
||||
|
||||
struct NautilusListColumnTitleClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
};
|
||||
|
||||
GtkType nautilus_list_column_title_get_type (void);
|
||||
NautilusListColumnTitle *nautilus_list_column_title_new (void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* __NAUTILUS_LIST_COLUMN_TITLE__ */
|
|
@ -47,6 +47,7 @@ libnautilusinclude_HEADERS= \
|
|||
nautilus-gtk-extensions.h \
|
||||
nautilus-icon-factory.h \
|
||||
nautilus-icons-view-icon-item.h \
|
||||
nautilus-list-column-title.h \
|
||||
nautilus-metadata.h \
|
||||
nautilus-mime-type.h \
|
||||
nautilus-self-checks.h \
|
||||
|
@ -85,6 +86,7 @@ libnautilus_la_SOURCES=$(nautilus_idl_sources) \
|
|||
nautilus-icon-factory.c \
|
||||
nautilus-icons-view-icon-item.c \
|
||||
nautilus-lib-self-check-functions.c \
|
||||
nautilus-list-column-title.c \
|
||||
nautilus-mime-type.c \
|
||||
nautilus-self-checks.c \
|
||||
nautilus-string-list.c \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nautilus-glib-extensions.h"
|
||||
#include "nautilus-gtk-macros.h"
|
||||
#include "nautilus-background.h"
|
||||
#include "nautilus-list-column-title.h"
|
||||
|
||||
struct _GtkFListDetails
|
||||
{
|
||||
|
@ -37,7 +38,8 @@ struct _GtkFListDetails
|
|||
/* Delayed selection information */
|
||||
int dnd_select_pending;
|
||||
guint dnd_select_pending_state;
|
||||
|
||||
|
||||
GtkWidget *title;
|
||||
};
|
||||
|
||||
/* maximum amount of milliseconds the mouse button is allowed to stay down and still be considered a click */
|
||||
|
@ -81,8 +83,18 @@ static gboolean gtk_flist_drag_drop (GtkWidget *widget, GdkDragContext *context,
|
|||
static void gtk_flist_drag_data_received (GtkWidget *widget, GdkDragContext *context,
|
||||
gint x, gint y, GtkSelectionData *data,
|
||||
guint info, guint time);
|
||||
static void select_or_unselect_row_cb (GtkCList *clist, gint row, gint column,
|
||||
GdkEvent *event);
|
||||
|
||||
static void gtk_flist_clear (GtkCList *clist);
|
||||
static void draw_row (GtkCList *flist, GdkRectangle *area, gint row, GtkCListRow *clist_row);
|
||||
|
||||
static void gtk_flist_realize (GtkWidget *widget);
|
||||
static void gtk_flist_size_request (GtkWidget *widget, GtkRequisition *requisition);
|
||||
|
||||
static void gtk_flist_column_resize_track_start (GtkWidget *widget, int column);
|
||||
static void gtk_flist_column_resize_track (GtkWidget *widget, int column);
|
||||
static void gtk_flist_column_resize_track_end (GtkWidget *widget, int column);
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (GtkFList, gtk_flist, GTK_TYPE_CLIST)
|
||||
|
||||
|
@ -90,15 +102,17 @@ static guint flist_signals[LAST_SIGNAL];
|
|||
|
||||
/* Standard class initialization function */
|
||||
static void
|
||||
gtk_flist_initialize_class (GtkFListClass *class)
|
||||
gtk_flist_initialize_class (GtkFListClass *klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
GtkCListClass *clist_class;
|
||||
GtkFListClass *flist_class;
|
||||
|
||||
object_class = (GtkObjectClass *) class;
|
||||
widget_class = (GtkWidgetClass *) class;
|
||||
clist_class = (GtkCListClass *) class;
|
||||
object_class = (GtkObjectClass *) klass;
|
||||
widget_class = (GtkWidgetClass *) klass;
|
||||
clist_class = (GtkCListClass *) klass;
|
||||
flist_class = (GtkFListClass *) klass;
|
||||
|
||||
flist_signals[CONTEXT_CLICK_SELECTION] =
|
||||
gtk_signal_new ("context_click_selection",
|
||||
|
@ -135,13 +149,18 @@ gtk_flist_initialize_class (GtkFListClass *class)
|
|||
gtk_signal_new ("selection_changed",
|
||||
GTK_RUN_FIRST,
|
||||
object_class->type,
|
||||
GTK_SIGNAL_OFFSET (GtkFListClass, start_drag),
|
||||
GTK_SIGNAL_OFFSET (GtkFListClass, selection_changed),
|
||||
gtk_marshal_NONE__NONE,
|
||||
GTK_TYPE_NONE, 0);
|
||||
|
||||
gtk_object_class_add_signals (object_class, flist_signals, LAST_SIGNAL);
|
||||
|
||||
flist_class->column_resize_track_start = gtk_flist_column_resize_track_start;
|
||||
flist_class->column_resize_track = gtk_flist_column_resize_track;
|
||||
flist_class->column_resize_track_end = gtk_flist_column_resize_track_end;
|
||||
|
||||
clist_class->clear = gtk_flist_clear;
|
||||
clist_class->draw_row = draw_row;
|
||||
|
||||
widget_class->button_press_event = gtk_flist_button_press;
|
||||
widget_class->button_release_event = gtk_flist_button_release;
|
||||
|
@ -155,20 +174,14 @@ gtk_flist_initialize_class (GtkFListClass *class)
|
|||
widget_class->drag_motion = gtk_flist_drag_motion;
|
||||
widget_class->drag_drop = gtk_flist_drag_drop;
|
||||
widget_class->drag_data_received = gtk_flist_drag_data_received;
|
||||
}
|
||||
|
||||
static void
|
||||
select_or_unselect_row_cb (GtkCList *clist, gint row, gint column, GdkEvent *event)
|
||||
{
|
||||
/* This is the one bottleneck for all selection changes */
|
||||
gtk_signal_emit (GTK_OBJECT (clist), flist_signals[SELECTION_CHANGED]);
|
||||
widget_class->realize = gtk_flist_realize;
|
||||
widget_class->size_request = gtk_flist_size_request;
|
||||
}
|
||||
|
||||
/* Standard object initialization function */
|
||||
static void
|
||||
gtk_flist_initialize (GtkFList *flist)
|
||||
{
|
||||
|
||||
{
|
||||
flist->details = g_new0 (GtkFListDetails, 1);
|
||||
flist->details->anchor_row = -1;
|
||||
|
||||
|
@ -194,6 +207,16 @@ gtk_flist_initialize (GtkFList *flist)
|
|||
"unselect_row",
|
||||
select_or_unselect_row_cb,
|
||||
flist);
|
||||
|
||||
flist->details->title = GTK_WIDGET (nautilus_list_column_title_new());
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
select_or_unselect_row_cb (GtkCList *clist, gint row, gint column, GdkEvent *event)
|
||||
{
|
||||
/* This is the one bottleneck for all selection changes */
|
||||
gtk_signal_emit (GTK_OBJECT (clist), flist_signals[SELECTION_CHANGED]);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -429,6 +452,717 @@ gtk_flist_button_release (GtkWidget *widget, GdkEventButton *event)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* stolen from gtkclist.c for now */
|
||||
|
||||
/* minimum allowed width of a column */
|
||||
#define COLUMN_MIN_WIDTH 5
|
||||
|
||||
/* this defines the base grid spacing */
|
||||
#define CELL_SPACING 1
|
||||
|
||||
/* added the horizontal space at the beginning and end of a row */
|
||||
#define COLUMN_INSET 3
|
||||
|
||||
/* the width of the column resize windows */
|
||||
#define DRAG_WIDTH 6
|
||||
|
||||
/* gives the left pixel of the given column in context of
|
||||
* the clist's hoffset */
|
||||
#define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \
|
||||
(clist)->hoffset)
|
||||
|
||||
/* gives the top pixel of the given row in context of
|
||||
* the clist's voffset */
|
||||
#define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
|
||||
(((row) + 1) * CELL_SPACING) + \
|
||||
(clist)->voffset)
|
||||
|
||||
/* returns the row index from a y pixel location in the
|
||||
* context of the clist's voffset */
|
||||
#define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
|
||||
((clist)->row_height + CELL_SPACING))
|
||||
|
||||
/* returns the GList item for the nth row */
|
||||
#define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
|
||||
(clist)->row_list_end : \
|
||||
g_list_nth ((clist)->row_list, (row)))
|
||||
|
||||
/* returns the total height of the list */
|
||||
#define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \
|
||||
(CELL_SPACING * ((clist)->rows + 1)))
|
||||
|
||||
static void
|
||||
gtk_flist_realize (GtkWidget *widget)
|
||||
{
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
flist = GTK_FLIST (widget);
|
||||
clist = GTK_CLIST (widget);
|
||||
|
||||
clist->column[0].button = flist->details->title;
|
||||
|
||||
NAUTILUS_CALL_PARENT_CLASS (GTK_WIDGET_CLASS, realize, (widget));
|
||||
|
||||
if (clist->title_window) {
|
||||
gtk_widget_set_parent_window (flist->details->title, clist->title_window);
|
||||
}
|
||||
gtk_widget_set_parent (flist->details->title, GTK_WIDGET (clist));
|
||||
gtk_widget_show (flist->details->title);
|
||||
|
||||
GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
|
||||
}
|
||||
|
||||
/* this is here just temporarily */
|
||||
static gint
|
||||
list_requisition_width (GtkCList *clist)
|
||||
{
|
||||
gint width = CELL_SPACING;
|
||||
gint i;
|
||||
|
||||
for (i = clist->columns - 1; i >= 0; i--) {
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
if (clist->column[i].width_set)
|
||||
width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
|
||||
else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
|
||||
width += clist->column[i].button->requisition.width;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_flist_size_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
/* stolen from gtk_clist
|
||||
* make sure the proper title ammount is allocated for the column
|
||||
* title view -- this would not otherwise be done because
|
||||
* GtkFList depends the buttons being there when doing a size calculation
|
||||
*/
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
g_return_if_fail (requisition != NULL);
|
||||
|
||||
clist = GTK_CLIST (widget);
|
||||
flist = GTK_FLIST (widget);
|
||||
|
||||
requisition->width = 0;
|
||||
requisition->height = 0;
|
||||
|
||||
/* compute the size of the column title (title) area */
|
||||
clist->column_title_area.height = 0;
|
||||
if (GTK_CLIST_SHOW_TITLES(clist) && flist->details->title) {
|
||||
GtkRequisition child_requisition;
|
||||
|
||||
gtk_widget_size_request (flist->details->title,
|
||||
&child_requisition);
|
||||
|
||||
child_requisition.height = 20;
|
||||
/* for now */
|
||||
|
||||
clist->column_title_area.height =
|
||||
MAX (clist->column_title_area.height,
|
||||
child_requisition.height);
|
||||
}
|
||||
|
||||
requisition->width += (widget->style->klass->xthickness +
|
||||
GTK_CONTAINER (widget)->border_width) * 2;
|
||||
requisition->height += (clist->column_title_area.height +
|
||||
(widget->style->klass->ythickness +
|
||||
GTK_CONTAINER (widget)->border_width) * 2);
|
||||
|
||||
|
||||
requisition->width += list_requisition_width (clist);
|
||||
requisition->height += LIST_HEIGHT (clist);
|
||||
}
|
||||
|
||||
static gint
|
||||
new_column_width (GtkCList *clist, gint column, gint *x)
|
||||
{
|
||||
gint xthickness = GTK_WIDGET (clist)->style->klass->xthickness;
|
||||
gint width;
|
||||
gint cx;
|
||||
gint dx;
|
||||
gint last_column;
|
||||
|
||||
/* first translate the x position from widget->window
|
||||
* to clist->clist_window */
|
||||
cx = *x - xthickness;
|
||||
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--);
|
||||
|
||||
/* calculate new column width making sure it doesn't end up
|
||||
* less than the minimum width */
|
||||
dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
|
||||
(column < last_column) * CELL_SPACING);
|
||||
width = cx - dx;
|
||||
|
||||
if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width)) {
|
||||
width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
|
||||
cx = dx + width;
|
||||
*x = cx + xthickness;
|
||||
} else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
|
||||
width > clist->column[column].max_width) {
|
||||
width = clist->column[column].max_width;
|
||||
cx = dx + clist->column[column].max_width;
|
||||
*x = cx + xthickness;
|
||||
}
|
||||
|
||||
if (cx < 0 || cx > clist->clist_window_width)
|
||||
*x = -1;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static void
|
||||
size_allocate_columns (GtkCList *clist, gboolean block_resize)
|
||||
{
|
||||
int xoffset = CELL_SPACING + COLUMN_INSET;
|
||||
int last_column;
|
||||
int i;
|
||||
|
||||
/* find last visible column and calculate correct column width */
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--)
|
||||
;
|
||||
|
||||
if (last_column < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i <= last_column; i++) {
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
clist->column[i].area.x = xoffset;
|
||||
if (clist->column[i].width_set) {
|
||||
if (!block_resize && GTK_CLIST_SHOW_TITLES(clist) &&
|
||||
clist->column[i].auto_resize && clist->column[i].button) {
|
||||
gint width;
|
||||
|
||||
width = (clist->column[i].button->requisition.width -
|
||||
(CELL_SPACING + (2 * COLUMN_INSET)));
|
||||
|
||||
if (width > clist->column[i].width)
|
||||
gtk_clist_set_column_width (clist, i, width);
|
||||
}
|
||||
|
||||
clist->column[i].area.width = clist->column[i].width;
|
||||
xoffset += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
|
||||
} else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button) {
|
||||
clist->column[i].area.width =
|
||||
clist->column[i].button->requisition.width -
|
||||
(CELL_SPACING + (2 * COLUMN_INSET));
|
||||
xoffset += clist->column[i].button->requisition.width;
|
||||
}
|
||||
}
|
||||
|
||||
clist->column[last_column].area.width += MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
|
||||
}
|
||||
|
||||
static void
|
||||
size_allocate_title_buttons (GtkCList *clist)
|
||||
{
|
||||
GtkAllocation button_allocation;
|
||||
int last_column;
|
||||
int last_button = 0;
|
||||
int i;
|
||||
|
||||
button_allocation.x = clist->hoffset;
|
||||
button_allocation.y = 0;
|
||||
button_allocation.width = 0;
|
||||
button_allocation.height = clist->column_title_area.height;
|
||||
|
||||
/* find last visible column */
|
||||
for (last_column = clist->columns - 1; last_column >= 0; last_column--)
|
||||
if (clist->column[last_column].visible)
|
||||
break;
|
||||
|
||||
for (i = 0; i < last_column; i++) {
|
||||
if (!clist->column[i].visible) {
|
||||
last_button = i + 1;
|
||||
gdk_window_hide (clist->column[i].window);
|
||||
continue;
|
||||
}
|
||||
|
||||
button_allocation.width += (clist->column[i].area.width +
|
||||
CELL_SPACING + 2 * COLUMN_INSET);
|
||||
|
||||
if (!clist->column[i + 1].button) {
|
||||
gdk_window_hide (clist->column[i].window);
|
||||
continue;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (clist->column[last_button].button,
|
||||
&button_allocation);
|
||||
button_allocation.x += button_allocation.width;
|
||||
button_allocation.width = 0;
|
||||
|
||||
last_button = i + 1;
|
||||
}
|
||||
|
||||
button_allocation.width += (clist->column[last_column].area.width +
|
||||
2 * (CELL_SPACING + COLUMN_INSET));
|
||||
gtk_widget_size_allocate (clist->column[last_button].button,
|
||||
&button_allocation);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
get_cell_style (GtkCList *clist, GtkCListRow *clist_row,
|
||||
gint state, gint column, GtkStyle **style,
|
||||
GdkGC **fg_gc, GdkGC **bg_gc)
|
||||
{
|
||||
gint fg_state;
|
||||
|
||||
if ((state == GTK_STATE_NORMAL) &&
|
||||
(GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
|
||||
fg_state = GTK_STATE_INSENSITIVE;
|
||||
else
|
||||
fg_state = state;
|
||||
|
||||
if (clist_row->cell[column].style) {
|
||||
if (style)
|
||||
*style = clist_row->cell[column].style;
|
||||
if (fg_gc)
|
||||
*fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = clist_row->cell[column].style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = clist_row->cell[column].style->base_gc[state];
|
||||
}
|
||||
} else if (clist_row->style) {
|
||||
if (style)
|
||||
*style = clist_row->style;
|
||||
if (fg_gc)
|
||||
*fg_gc = clist_row->style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = clist_row->style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = clist_row->style->base_gc[state];
|
||||
}
|
||||
} else {
|
||||
if (style)
|
||||
*style = GTK_WIDGET (clist)->style;
|
||||
if (fg_gc)
|
||||
*fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
|
||||
}
|
||||
|
||||
if (state != GTK_STATE_SELECTED) {
|
||||
if (fg_gc && clist_row->fg_set)
|
||||
*fg_gc = clist->fg_gc;
|
||||
if (bg_gc && clist_row->bg_set)
|
||||
*bg_gc = clist->bg_gc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
draw_cell_pixmap (GdkWindow *window, GdkRectangle *clip_rectangle, GdkGC *fg_gc,
|
||||
GdkPixmap *pixmap, GdkBitmap *mask,
|
||||
gint x, gint y, gint width, gint height)
|
||||
{
|
||||
gint xsrc = 0;
|
||||
gint ysrc = 0;
|
||||
|
||||
if (mask) {
|
||||
gdk_gc_set_clip_mask (fg_gc, mask);
|
||||
gdk_gc_set_clip_origin (fg_gc, x, y);
|
||||
}
|
||||
|
||||
if (x < clip_rectangle->x) {
|
||||
xsrc = clip_rectangle->x - x;
|
||||
width -= xsrc;
|
||||
x = clip_rectangle->x;
|
||||
}
|
||||
|
||||
if (x + width > clip_rectangle->x + clip_rectangle->width)
|
||||
width = clip_rectangle->x + clip_rectangle->width - x;
|
||||
|
||||
if (y < clip_rectangle->y) {
|
||||
ysrc = clip_rectangle->y - y;
|
||||
height -= ysrc;
|
||||
y = clip_rectangle->y;
|
||||
}
|
||||
if (y + height > clip_rectangle->y + clip_rectangle->height)
|
||||
height = clip_rectangle->y + clip_rectangle->height - y;
|
||||
|
||||
gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
|
||||
gdk_gc_set_clip_origin (fg_gc, 0, 0);
|
||||
if (mask)
|
||||
gdk_gc_set_clip_mask (fg_gc, NULL);
|
||||
|
||||
return x + MAX (width, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_row (GtkCList *clist,
|
||||
GdkRectangle *area,
|
||||
gint row,
|
||||
GtkCListRow *clist_row)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkRectangle *rect;
|
||||
GdkRectangle row_rectangle;
|
||||
GdkRectangle cell_rectangle;
|
||||
GdkRectangle clip_rectangle;
|
||||
GdkRectangle intersect_rectangle;
|
||||
gint last_column;
|
||||
gint state;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (clist != NULL);
|
||||
|
||||
/* bail now if we arn't drawable yet */
|
||||
if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
|
||||
return;
|
||||
|
||||
widget = GTK_WIDGET (clist);
|
||||
|
||||
/* if the function is passed the pointer to the row instead of null,
|
||||
* it avoids this expensive lookup */
|
||||
if (!clist_row)
|
||||
clist_row = ROW_ELEMENT (clist, row)->data;
|
||||
|
||||
/* rectangle of the entire row */
|
||||
row_rectangle.x = 0;
|
||||
row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
|
||||
row_rectangle.width = clist->clist_window_width;
|
||||
row_rectangle.height = clist->row_height;
|
||||
|
||||
/* rectangle of the cell spacing above the row */
|
||||
cell_rectangle.x = 0;
|
||||
cell_rectangle.y = row_rectangle.y - CELL_SPACING;
|
||||
cell_rectangle.width = row_rectangle.width;
|
||||
cell_rectangle.height = CELL_SPACING;
|
||||
|
||||
/* rectangle used to clip drawing operations, its y and height
|
||||
* positions only need to be set once, so we set them once here.
|
||||
* the x and width are set withing the drawing loop below once per
|
||||
* column */
|
||||
clip_rectangle.y = row_rectangle.y;
|
||||
clip_rectangle.height = row_rectangle.height;
|
||||
|
||||
if (clist_row->state == GTK_STATE_NORMAL)
|
||||
{
|
||||
if (clist_row->fg_set)
|
||||
gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
|
||||
if (clist_row->bg_set)
|
||||
gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
|
||||
}
|
||||
|
||||
state = clist_row->state;
|
||||
|
||||
/* draw the cell borders and background */
|
||||
if (area)
|
||||
{
|
||||
rect = &intersect_rectangle;
|
||||
if (gdk_rectangle_intersect (area, &cell_rectangle,
|
||||
&intersect_rectangle))
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
intersect_rectangle.x,
|
||||
intersect_rectangle.y,
|
||||
intersect_rectangle.width,
|
||||
intersect_rectangle.height);
|
||||
|
||||
/* the last row has to clear its bottom cell spacing too */
|
||||
if (clist_row == clist->row_list_end->data)
|
||||
{
|
||||
cell_rectangle.y += clist->row_height + CELL_SPACING;
|
||||
|
||||
if (gdk_rectangle_intersect (area, &cell_rectangle,
|
||||
&intersect_rectangle))
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
intersect_rectangle.x,
|
||||
intersect_rectangle.y,
|
||||
intersect_rectangle.width,
|
||||
intersect_rectangle.height);
|
||||
}
|
||||
|
||||
if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
|
||||
return;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = &clip_rectangle;
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
cell_rectangle.x,
|
||||
cell_rectangle.y,
|
||||
cell_rectangle.width,
|
||||
cell_rectangle.height);
|
||||
|
||||
/* the last row has to clear its bottom cell spacing too */
|
||||
if (clist_row == clist->row_list_end->data)
|
||||
{
|
||||
cell_rectangle.y += clist->row_height + CELL_SPACING;
|
||||
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
cell_rectangle.x,
|
||||
cell_rectangle.y,
|
||||
cell_rectangle.width,
|
||||
cell_rectangle.height);
|
||||
}
|
||||
}
|
||||
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--)
|
||||
;
|
||||
|
||||
/* iterate and draw all the columns (row cells) and draw their contents */
|
||||
for (i = 0; i < clist->columns; i++)
|
||||
{
|
||||
GtkStyle *style;
|
||||
GdkGC *fg_gc;
|
||||
GdkGC *bg_gc;
|
||||
|
||||
gint width;
|
||||
gint height;
|
||||
gint pixmap_width;
|
||||
gint offset = 0;
|
||||
gint row_center_offset;
|
||||
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
|
||||
|
||||
clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
|
||||
clip_rectangle.width = clist->column[i].area.width;
|
||||
|
||||
/* calculate clipping region clipping region */
|
||||
clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
|
||||
clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
|
||||
(i == last_column) * CELL_SPACING);
|
||||
|
||||
if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
|
||||
&intersect_rectangle))
|
||||
continue;
|
||||
|
||||
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
|
||||
rect->x, rect->y, rect->width, rect->height);
|
||||
|
||||
clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
|
||||
clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
|
||||
(i == last_column) * CELL_SPACING);
|
||||
|
||||
/* calculate real width for column justification */
|
||||
pixmap_width = 0;
|
||||
offset = 0;
|
||||
switch (clist_row->cell[i].type)
|
||||
{
|
||||
case GTK_CELL_TEXT:
|
||||
width = gdk_string_width (style->font,
|
||||
GTK_CELL_TEXT (clist_row->cell[i])->text);
|
||||
break;
|
||||
case GTK_CELL_PIXMAP:
|
||||
gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
|
||||
&pixmap_width, &height);
|
||||
width = pixmap_width;
|
||||
break;
|
||||
case GTK_CELL_PIXTEXT:
|
||||
gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
|
||||
&pixmap_width, &height);
|
||||
width = (pixmap_width +
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
|
||||
gdk_string_width (style->font,
|
||||
GTK_CELL_PIXTEXT
|
||||
(clist_row->cell[i])->text));
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (clist->column[i].justification)
|
||||
{
|
||||
case GTK_JUSTIFY_LEFT:
|
||||
offset = clip_rectangle.x + clist_row->cell[i].horizontal;
|
||||
break;
|
||||
case GTK_JUSTIFY_RIGHT:
|
||||
offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
|
||||
clip_rectangle.width - width);
|
||||
break;
|
||||
case GTK_JUSTIFY_CENTER:
|
||||
case GTK_JUSTIFY_FILL:
|
||||
offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
|
||||
(clip_rectangle.width / 2) - (width / 2));
|
||||
break;
|
||||
};
|
||||
|
||||
/* Draw Text and/or Pixmap */
|
||||
switch (clist_row->cell[i].type)
|
||||
{
|
||||
case GTK_CELL_PIXMAP:
|
||||
draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
|
||||
GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
|
||||
GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
|
||||
offset,
|
||||
clip_rectangle.y + clist_row->cell[i].vertical +
|
||||
(clip_rectangle.height - height) / 2,
|
||||
pixmap_width, height);
|
||||
break;
|
||||
case GTK_CELL_PIXTEXT:
|
||||
offset =
|
||||
draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
|
||||
offset,
|
||||
clip_rectangle.y + clist_row->cell[i].vertical+
|
||||
(clip_rectangle.height - height) / 2,
|
||||
pixmap_width, height);
|
||||
offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
|
||||
case GTK_CELL_TEXT:
|
||||
if (style != GTK_WIDGET (clist)->style)
|
||||
row_center_offset = (((clist->row_height - style->font->ascent -
|
||||
style->font->descent - 1) / 2) + 1.5 +
|
||||
style->font->ascent);
|
||||
else
|
||||
row_center_offset = clist->row_center_offset;
|
||||
|
||||
gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
|
||||
gdk_draw_string (clist->clist_window, style->font, fg_gc,
|
||||
offset,
|
||||
row_rectangle.y + row_center_offset +
|
||||
clist_row->cell[i].vertical,
|
||||
(clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
|
||||
GTK_CELL_TEXT (clist_row->cell[i])->text);
|
||||
gdk_gc_set_clip_rectangle (fg_gc, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* draw focus rectangle */
|
||||
if (clist->focus_row == row &&
|
||||
GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS(widget))
|
||||
{
|
||||
if (!area)
|
||||
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
|
||||
row_rectangle.x, row_rectangle.y,
|
||||
row_rectangle.width - 1, row_rectangle.height - 1);
|
||||
else if (gdk_rectangle_intersect (area, &row_rectangle,
|
||||
&intersect_rectangle))
|
||||
{
|
||||
gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
|
||||
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
|
||||
row_rectangle.x, row_rectangle.y,
|
||||
row_rectangle.width - 1,
|
||||
row_rectangle.height - 1);
|
||||
gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_rows (GtkCList *clist, GdkRectangle *area)
|
||||
{
|
||||
GList *list;
|
||||
gint i;
|
||||
gint first_row;
|
||||
gint last_row;
|
||||
|
||||
if (clist->row_height == 0 || !GTK_WIDGET_DRAWABLE (clist))
|
||||
return;
|
||||
|
||||
first_row = ROW_FROM_YPIXEL (clist, area->y);
|
||||
last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
|
||||
|
||||
/* this is a small special case which exposes the bottom cell line
|
||||
* on the last row -- it might go away if I change the wall the cell
|
||||
* spacings are drawn
|
||||
*/
|
||||
if (clist->rows == first_row)
|
||||
first_row--;
|
||||
|
||||
list = ROW_ELEMENT (clist, first_row);
|
||||
for (i = first_row; i <= last_row ; i++) {
|
||||
if (list == NULL)
|
||||
break;
|
||||
|
||||
GTK_CLIST_CLASS ((GTK_OBJECT (clist))->klass)->draw_row (clist, area, i,
|
||||
list->data);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flist_track_new_column_width (GtkCList *clist, int column, int new_width)
|
||||
{
|
||||
GtkFList *flist;
|
||||
GdkRectangle title_redraw_area;
|
||||
|
||||
flist = GTK_FLIST (clist);
|
||||
|
||||
/* pin new_width to min and max values */
|
||||
if (new_width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
|
||||
new_width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
|
||||
if (clist->column[column].max_width >= 0 &&
|
||||
new_width > clist->column[column].max_width)
|
||||
new_width = clist->column[column].max_width;
|
||||
|
||||
/* check to see if the pinned value is still different */
|
||||
if (clist->column[column].width == new_width)
|
||||
return;
|
||||
|
||||
/* set the new width */
|
||||
clist->column[column].width = new_width;
|
||||
clist->column[column].width_set = TRUE;
|
||||
|
||||
size_allocate_columns (clist, TRUE);
|
||||
size_allocate_title_buttons (clist);
|
||||
|
||||
/* redraw the invalid columns */
|
||||
if (clist->freeze_count == 0) {
|
||||
|
||||
GdkRectangle area;
|
||||
|
||||
area = clist->column_title_area;
|
||||
area.x = clist->column[column].area.x;
|
||||
area.height += clist->clist_window_height;
|
||||
|
||||
draw_rows (clist, &area);
|
||||
}
|
||||
|
||||
/* draw the column title
|
||||
* ToDo:
|
||||
* fix this up
|
||||
*/
|
||||
title_redraw_area.x = GTK_WIDGET (flist->details->title)->allocation.x;
|
||||
title_redraw_area.y = GTK_WIDGET (flist->details->title)->allocation.y;
|
||||
title_redraw_area.width = GTK_WIDGET (flist->details->title)->allocation.width;
|
||||
title_redraw_area.height = GTK_WIDGET (flist->details->title)->allocation.height;
|
||||
|
||||
(GTK_WIDGET_CLASS (NAUTILUS_KLASS (flist->details->title)))->
|
||||
draw (flist->details->title, &title_redraw_area);
|
||||
}
|
||||
|
||||
/* Our handler for motion_notify events. We override all of GtkCList's broken
|
||||
* behavior.
|
||||
*/
|
||||
|
@ -476,6 +1210,45 @@ gtk_flist_motion (GtkWidget *widget, GdkEventMotion *event)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track_start (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist->drag_pos = column;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
int x;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist = GTK_CLIST (widget);
|
||||
|
||||
gtk_widget_get_pointer (widget, &x, NULL);
|
||||
gtk_flist_track_new_column_width (clist, column,
|
||||
new_column_width (clist, column, &x));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track_end (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist->drag_pos = -1;
|
||||
}
|
||||
|
||||
/* Our handler for key_press and key_release events. We do nothing, and we do
|
||||
* this to avoid GtkCList's broken behavior.
|
||||
*/
|
||||
|
@ -578,7 +1351,17 @@ gtk_flist_new_with_titles (int columns, char **titles)
|
|||
GtkFList *flist;
|
||||
|
||||
flist = gtk_type_new (gtk_flist_get_type ());
|
||||
gtk_clist_construct (GTK_CLIST (flist), columns, titles);
|
||||
gtk_clist_construct (GTK_CLIST (flist), columns, NULL);
|
||||
if (titles) {
|
||||
GtkCList *clist;
|
||||
int index;
|
||||
|
||||
clist = GTK_CLIST(flist);
|
||||
|
||||
for (index = 0; index < columns; index++) {
|
||||
clist->column[index].title = g_strdup (titles[index]);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_clist_set_selection_mode (GTK_CLIST (flist),
|
||||
GTK_SELECTION_MULTIPLE);
|
||||
|
|
|
@ -36,7 +36,7 @@ struct _GtkFListClass {
|
|||
GtkCListClass parent_class;
|
||||
|
||||
/* Signal: invoke the popup menu for selected items */
|
||||
void (* context_click_selection) (GtkFList *flist, gint row);
|
||||
void (* context_click_selection) (GtkFList *flist, int row);
|
||||
|
||||
/* Signal: invoke the popup menu for empty areas */
|
||||
void (* context_click_background) (GtkFList *flist);
|
||||
|
@ -45,10 +45,15 @@ struct _GtkFListClass {
|
|||
void (* activate) (GtkFList *flist, gpointer data);
|
||||
|
||||
/* Signal: initiate a drag and drop operation */
|
||||
void (* start_drag) (GtkFList *flist, gint button, GdkEvent *event);
|
||||
void (* start_drag) (GtkFList *flist, int button, GdkEvent *event);
|
||||
|
||||
/* Signal: selection has changed */
|
||||
void (* selection_changed) (GtkFList *flist);
|
||||
|
||||
/* column resize tracking calls */
|
||||
void (* column_resize_track_start) (GtkWidget *widget, int column);
|
||||
void (* column_resize_track) (GtkWidget *widget, int column);
|
||||
void (* column_resize_track_end) (GtkWidget *widget, int column);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -148,6 +148,47 @@ nautilus_fill_rectangle_with_gradient (GdkDrawable *drawable,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_rectangle_contains:
|
||||
* @rectangle: Rectangle possibly containing a point.
|
||||
* @x: X coordinate of a point.
|
||||
* @y: Y coordinate of a point.
|
||||
*
|
||||
* Retun TRUE if point is contained inside a rectangle
|
||||
*/
|
||||
gboolean
|
||||
nautilus_rectangle_contains (const GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
g_return_val_if_fail (rectangle != NULL, FALSE);
|
||||
return rectangle->x <= x && rectangle->x + rectangle->width >= x
|
||||
&& rectangle->y <= y && rectangle->y + rectangle->height >= y;
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_rectangle_inset:
|
||||
* @rectangle: Rectangle we are insetting.
|
||||
* @x: Horizontal ammount to inset by.
|
||||
* @y: Vertical ammount to inset by.
|
||||
*
|
||||
* Inset a rectangle by a given horizontal and vertical ammount.
|
||||
* Pass in negative inset values to grow the rectangle, positive to
|
||||
* shrink it.
|
||||
*/
|
||||
void
|
||||
nautilus_rectangle_inset (GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
g_return_if_fail (rectangle != NULL);
|
||||
|
||||
rectangle->x += x;
|
||||
rectangle->width -= 2 * x;
|
||||
rectangle->y += y;
|
||||
rectangle->height -= 2 * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_interpolate_color:
|
||||
* @ratio: Place on line between colors to interpolate.
|
||||
|
|
|
@ -75,6 +75,15 @@ void nautilus_fill_rectangle_with_gradient (GdkDrawable *drawab
|
|||
const GdkColor *end_color,
|
||||
gboolean horizontal_gradient);
|
||||
|
||||
/* Misc GdkRectangle helper functions */
|
||||
gboolean nautilus_rectangle_contains (const GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
void nautilus_rectangle_inset (GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
/* A basic operation we use for drawing gradients is interpolating two colors.*/
|
||||
void nautilus_interpolate_color (gdouble ratio,
|
||||
const GdkColor *start_color,
|
||||
|
|
763
libnautilus-private/nautilus-list-column-title.c
Normal file
763
libnautilus-private/nautilus-list-column-title.c
Normal file
|
@ -0,0 +1,763 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
||||
|
||||
nautilus-list-column-title.c: List column title widget for interacting with list columns
|
||||
|
||||
Copyright (C) 2000 Eazel, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
Authors: Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
*/
|
||||
|
||||
#include "nautilus-list-column-title.h"
|
||||
#include "nautilus-gtk-macros.h"
|
||||
#include "nautilus-gdk-extensions.h"
|
||||
|
||||
/* for now we need to know about GtkFList */
|
||||
#include "gtkflist.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
|
||||
#include <libgnomeui/gnome-pixmap.h>
|
||||
|
||||
/* these are from GtkCList, for now we need to copy them here
|
||||
* eventually the target list should be able to describe the values
|
||||
*/
|
||||
|
||||
enum {
|
||||
/* this defines the base grid spacing */
|
||||
CELL_SPACING = 1,
|
||||
|
||||
/* added the horizontal space at the beginning and end of a row */
|
||||
COLUMN_INSET = 3,
|
||||
|
||||
/* from GtkButton */
|
||||
CHILD_SPACING = 1,
|
||||
|
||||
/* the width of the column resize windows */
|
||||
DRAG_WIDTH = 6
|
||||
};
|
||||
|
||||
static char * down_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"......",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
" .. "
|
||||
};
|
||||
|
||||
static char * up_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
" .. ",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
"......"
|
||||
};
|
||||
|
||||
struct NautilusListColumnTitleDetails
|
||||
{
|
||||
/* gc for blitting sort order pixmaps, lazily allocated */
|
||||
GdkGC *copy_area_gc;
|
||||
|
||||
/* sort order indicator pixmaps, lazily allocated */
|
||||
GnomePixmap *up_indicator;
|
||||
GnomePixmap *down_indicator;
|
||||
|
||||
int tracking_column_resize;
|
||||
/* index of the column we are currently tracking or -1 */
|
||||
int tracking_column_prelight;
|
||||
/* index of the column we are currently rolling over or -1 */
|
||||
int tracking_column_press;
|
||||
/* index of the column we are currently pressing or -1 */
|
||||
|
||||
int last_tracking_x;
|
||||
/* last horizontal track point so we can only resize when needed */
|
||||
gboolean resize_cursor_on;
|
||||
};
|
||||
|
||||
static void nautilus_list_column_title_initialize_class (gpointer klass);
|
||||
static void nautilus_list_column_title_initialize (gpointer object, gpointer klass);
|
||||
static void nautilus_list_column_title_paint (GtkWidget *widget, GdkRectangle *area);
|
||||
static void nautilus_list_column_title_draw (GtkWidget *widget, GdkRectangle *box);
|
||||
static void nautilus_list_column_title_buffered_draw (GtkWidget *widget);
|
||||
static gboolean nautilus_list_column_title_expose (GtkWidget *widget, GdkEventExpose *event);
|
||||
static void nautilus_list_column_title_realize (GtkWidget *widget);
|
||||
static void nautilus_list_column_title_finalize (GtkObject *object);
|
||||
static void nautilus_list_column_title_request (GtkWidget *widget, GtkRequisition *requisition);
|
||||
|
||||
static gboolean nautilus_list_column_title_motion (GtkWidget *widget, GdkEventMotion *event);
|
||||
static gboolean nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event);
|
||||
|
||||
static gboolean nautilus_list_column_title_button_press (GtkWidget *widget, GdkEventButton *event);
|
||||
static gboolean nautilus_list_column_title_button_release (GtkWidget *widget, GdkEventButton *event);
|
||||
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusListColumnTitle, nautilus_list_column_title, GTK_TYPE_BIN)
|
||||
/* generates nautilus_list_column_title_get_type */
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_initialize_class (gpointer klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
|
||||
object_class = GTK_OBJECT_CLASS (klass);
|
||||
widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = nautilus_list_column_title_finalize;
|
||||
widget_class->draw = nautilus_list_column_title_draw;
|
||||
widget_class->expose_event = nautilus_list_column_title_expose;
|
||||
widget_class->realize = nautilus_list_column_title_realize;
|
||||
widget_class->size_request = nautilus_list_column_title_request;
|
||||
widget_class->motion_notify_event = nautilus_list_column_title_motion;
|
||||
widget_class->leave_notify_event = nautilus_list_column_title_leave;
|
||||
widget_class->button_press_event = nautilus_list_column_title_button_press;
|
||||
widget_class->button_release_event = nautilus_list_column_title_button_release;
|
||||
}
|
||||
|
||||
NautilusListColumnTitle *
|
||||
nautilus_list_column_title_new (void)
|
||||
{
|
||||
return gtk_type_new (nautilus_list_column_title_get_type ());
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_initialize (gpointer object, gpointer klass)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(object);
|
||||
column_title->details = g_new0 (NautilusListColumnTitleDetails, 1);
|
||||
|
||||
/* copy_gc, up/down indicators get allocated lazily when needed */
|
||||
column_title->details->copy_area_gc = NULL;
|
||||
column_title->details->up_indicator = NULL;
|
||||
column_title->details->down_indicator = NULL;
|
||||
|
||||
column_title->details->resize_cursor_on = FALSE;
|
||||
column_title->details->tracking_column_resize = -1;
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
column_title->details->tracking_column_press = -1;
|
||||
column_title->details->last_tracking_x = -1;
|
||||
|
||||
GTK_WIDGET_UNSET_FLAGS (object, GTK_NO_WINDOW);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_realize (GtkWidget *widget)
|
||||
{
|
||||
GdkWindowAttr attributes;
|
||||
int attributes_mask;
|
||||
|
||||
g_assert (widget != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
||||
|
||||
/* ask for expose events */
|
||||
attributes.window_type = GDK_WINDOW_CHILD;
|
||||
attributes.x = widget->allocation.x + GTK_CONTAINER (widget)->border_width;
|
||||
attributes.y = widget->allocation.y + GTK_CONTAINER (widget)->border_width;
|
||||
attributes.width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
|
||||
attributes.height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.visual = gtk_widget_get_visual (widget);
|
||||
attributes.colormap = gtk_widget_get_colormap (widget);
|
||||
attributes.event_mask = gtk_widget_get_events (widget);
|
||||
attributes.event_mask |= GDK_EXPOSURE_MASK
|
||||
| GDK_ENTER_NOTIFY_MASK
|
||||
| GDK_LEAVE_NOTIFY_MASK
|
||||
| GDK_BUTTON_PRESS_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK
|
||||
| GDK_POINTER_MOTION_MASK
|
||||
| GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_KEY_PRESS_MASK;
|
||||
|
||||
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
||||
|
||||
/* give ourselves a background window */
|
||||
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
|
||||
&attributes, attributes_mask);
|
||||
gdk_window_set_user_data (widget->window, widget);
|
||||
|
||||
widget->style = gtk_style_attach (widget->style, widget->window);
|
||||
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_finalize (GtkObject *object)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(object);
|
||||
|
||||
if (column_title->details->up_indicator != NULL)
|
||||
gtk_widget_destroy (GTK_WIDGET (column_title->details->up_indicator));
|
||||
if (column_title->details->down_indicator != NULL)
|
||||
gtk_widget_destroy (GTK_WIDGET (column_title->details->down_indicator));
|
||||
|
||||
/* FIXME:
|
||||
* figure out if we need to delete the copy_area_gc here
|
||||
*/
|
||||
|
||||
g_free (column_title->details);
|
||||
|
||||
NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
/* size requisition: make sure we have at least a minimal height */
|
||||
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (requisition != NULL);
|
||||
|
||||
requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
|
||||
widget->style->klass->xthickness) * 2;
|
||||
requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
|
||||
widget->style->klass->ythickness) * 2;
|
||||
|
||||
|
||||
if (GTK_BIN (widget)->child && GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child)) {
|
||||
GtkRequisition child_requisition;
|
||||
|
||||
gtk_widget_size_request (GTK_BIN (widget)->child, &child_requisition);
|
||||
|
||||
requisition->width += child_requisition.width;
|
||||
requisition->height += child_requisition.height;
|
||||
|
||||
requisition->height = MIN (requisition->height, 10);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_column_label_at (GtkWidget *column_title, int index)
|
||||
{
|
||||
GtkCList *parent_clist;
|
||||
|
||||
parent_clist = GTK_CLIST (column_title->parent);
|
||||
|
||||
return parent_clist->column[index].title;
|
||||
}
|
||||
|
||||
static void
|
||||
get_column_frame_at(GtkWidget *column_title, int index, GdkRectangle *result)
|
||||
{
|
||||
GtkCList *parent_clist;
|
||||
parent_clist = GTK_CLIST (column_title->parent);
|
||||
|
||||
*result = parent_clist->column_title_area;
|
||||
result->x = parent_clist->hoffset + parent_clist->column[index].area.x - COLUMN_INSET;
|
||||
result->y = 0;
|
||||
result->width = parent_clist->column[index].area.width
|
||||
+ CELL_SPACING + 2 * COLUMN_INSET - 1;
|
||||
}
|
||||
|
||||
static GnomePixmap *
|
||||
get_sort_indicator (GtkWidget *widget, gboolean ascending)
|
||||
{
|
||||
/* return the sort order pixmap for a given sort direction
|
||||
* allocate the pixmap first time around
|
||||
*/
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
if (ascending) {
|
||||
if (column_title->details->up_indicator == NULL) {
|
||||
column_title->details->up_indicator =
|
||||
GNOME_PIXMAP (gnome_pixmap_new_from_xpm_d (up_xpm));
|
||||
}
|
||||
return column_title->details->up_indicator;
|
||||
} else {
|
||||
if (column_title->details->down_indicator == NULL) {
|
||||
column_title->details->down_indicator =
|
||||
GNOME_PIXMAP (gnome_pixmap_new_from_xpm_d (down_xpm));
|
||||
}
|
||||
return column_title->details->down_indicator;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME:
|
||||
* Some of these magic numbers could be replaced with some more dynamic values
|
||||
*/
|
||||
enum {
|
||||
CELL_TITLE_INSET = 6,
|
||||
TITLE_BASELINE_OFFSET = 6,
|
||||
SORT_ORDER_INDICATOR_WIDTH = 10
|
||||
};
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_paint (GtkWidget *widget, GdkRectangle *area)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
g_assert (GTK_CLIST (widget->parent) != NULL);
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
GdkRectangle cell_rectangle;
|
||||
GdkRectangle cell_redraw_area;
|
||||
const char *cell_label;
|
||||
int x_offset;
|
||||
GnomePixmap *sort_indicator;
|
||||
gboolean right_justified;
|
||||
|
||||
sort_indicator = NULL;
|
||||
right_justified = (parent_clist->column[index].justification == GTK_JUSTIFY_RIGHT);
|
||||
|
||||
/* pick the ascending/descending sort indicator if needed */
|
||||
if (index == parent_clist->sort_column) {
|
||||
sort_indicator = get_sort_indicator (widget,
|
||||
parent_clist->sort_type == GTK_SORT_ASCENDING);
|
||||
}
|
||||
|
||||
get_column_frame_at (widget, index, &cell_rectangle);
|
||||
gdk_rectangle_intersect (&cell_rectangle, area, &cell_redraw_area);
|
||||
|
||||
if (cell_redraw_area.width == 0 || cell_redraw_area.height == 0) {
|
||||
/* no work, go on to the next */
|
||||
continue;
|
||||
}
|
||||
|
||||
cell_label = get_column_label_at (widget, index);
|
||||
|
||||
/* FIXME:
|
||||
* add support for center justification
|
||||
*/
|
||||
|
||||
if (right_justified) {
|
||||
x_offset = cell_rectangle.x + cell_rectangle.width - CELL_TITLE_INSET;
|
||||
} else {
|
||||
x_offset = cell_rectangle.x + CELL_TITLE_INSET;
|
||||
}
|
||||
|
||||
/* Paint the column title tiles as rectangles using "menu" style
|
||||
* buttons as used by GtkCList produce round corners in some themes.
|
||||
* Eventually we might consider having a separate style for column titles
|
||||
*/
|
||||
gtk_paint_box (widget->style, widget->window,
|
||||
column_title->details->tracking_column_prelight == index ?
|
||||
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
|
||||
column_title->details->tracking_column_press == index ?
|
||||
GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
||||
area, widget, "menu",
|
||||
cell_rectangle.x, cell_rectangle.y,
|
||||
cell_rectangle.width, cell_rectangle.height);
|
||||
|
||||
|
||||
/* Draw the sort indicator if needed */
|
||||
if (sort_indicator != NULL) {
|
||||
int y_offset = TITLE_BASELINE_OFFSET + 2;
|
||||
|
||||
if (right_justified) {
|
||||
x_offset -= SORT_ORDER_INDICATOR_WIDTH;
|
||||
}
|
||||
|
||||
/* allocate the sort indicator copy gc first time around */
|
||||
if (column_title->details->copy_area_gc == NULL) {
|
||||
column_title->details->copy_area_gc = gdk_gc_new (widget->window);
|
||||
gdk_gc_set_function (column_title->details->copy_area_gc, GDK_COPY);
|
||||
}
|
||||
/* move the pixmap clip mask and origin to the right spot in the gc */
|
||||
gdk_gc_set_clip_mask (column_title->details->copy_area_gc,
|
||||
sort_indicator->mask);
|
||||
gdk_gc_set_clip_origin (column_title->details->copy_area_gc, x_offset, y_offset);
|
||||
|
||||
|
||||
gdk_draw_pixmap (widget->window, column_title->details->copy_area_gc,
|
||||
sort_indicator->pixmap, 0, 0, x_offset, y_offset,
|
||||
-1, -1);
|
||||
|
||||
if (!right_justified) {
|
||||
x_offset += SORT_ORDER_INDICATOR_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell_label) {
|
||||
/* clip a little more than the cell rectangle to
|
||||
* not have the text draw over the cell broder
|
||||
* (this might no longer be needed when the
|
||||
* title gets truncated properly, as opposed to
|
||||
* getting chopped of
|
||||
*/
|
||||
nautilus_rectangle_inset (&cell_redraw_area, 2, 2);
|
||||
if (right_justified) {
|
||||
x_offset -= gdk_string_width (widget->style->font, cell_label) + 4;
|
||||
}
|
||||
|
||||
gtk_paint_string (widget->style, widget->window, GTK_STATE_NORMAL,
|
||||
&cell_redraw_area, widget, "label",
|
||||
x_offset,
|
||||
cell_rectangle.y + cell_rectangle.height - TITLE_BASELINE_OFFSET,
|
||||
cell_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_draw (GtkWidget *widget, GdkRectangle *area)
|
||||
{
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (area != NULL);
|
||||
|
||||
if (!GTK_WIDGET_DRAWABLE (widget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nautilus_list_column_title_paint (widget, area);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_buffered_draw (GtkWidget *widget)
|
||||
{
|
||||
/* draw using an offscreen bitmap
|
||||
* for now just draw directly
|
||||
* add code here to lazily allocate an offscreen and pass it to the paint call
|
||||
* without the offscreen you can now see a funny artifact when part of the
|
||||
* old column border does not get erased properly when shrinking a column
|
||||
*/
|
||||
GdkRectangle redraw_area;
|
||||
redraw_area.x = widget->allocation.x;
|
||||
redraw_area.y = widget->allocation.y;
|
||||
redraw_area.width = widget->allocation.width;
|
||||
redraw_area.height = widget->allocation.height;
|
||||
|
||||
nautilus_list_column_title_draw (widget, &redraw_area);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_expose (GtkWidget *widget, GdkEventExpose *event)
|
||||
{
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (event != NULL);
|
||||
|
||||
if (!GTK_WIDGET_DRAWABLE (widget)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nautilus_list_column_title_paint (widget, &event->area);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
in_column_rect (GtkWidget *widget, int x, int y)
|
||||
{
|
||||
/* return the index of the column we hit or -1 */
|
||||
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
/* hit testing for column resizing */
|
||||
GdkRectangle cell_rectangle;
|
||||
|
||||
get_column_frame_at (widget, index, &cell_rectangle);
|
||||
|
||||
/* inset by a pixel so that you have to move past the border
|
||||
* to be considered inside the rect
|
||||
* nautilus_list_column_title_leave depends on this
|
||||
*/
|
||||
nautilus_rectangle_inset (&cell_rectangle, 1, 0);
|
||||
|
||||
|
||||
if (nautilus_rectangle_contains (&cell_rectangle, x, y))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
in_resize_rect (GtkWidget *widget, int x, int y)
|
||||
{
|
||||
/* return the index of the resize rect of a column we hit or -1 */
|
||||
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
/* hit testing for column resizing */
|
||||
GdkRectangle resize_rectangle;
|
||||
|
||||
get_column_frame_at (widget, index, &resize_rectangle);
|
||||
|
||||
nautilus_rectangle_inset (&resize_rectangle, 1, 0);
|
||||
|
||||
resize_rectangle.x = resize_rectangle.x + resize_rectangle.width - DRAG_WIDTH / 2;
|
||||
resize_rectangle.width = DRAG_WIDTH;
|
||||
|
||||
if (nautilus_rectangle_contains (&resize_rectangle, x, y))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
show_hide_resize_cursor_if_needed (GtkWidget *widget, gboolean on)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
if (on == column_title->details->resize_cursor_on)
|
||||
/* already set right */
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
/* switch to a resize cursor */
|
||||
GdkCursor *cursor;
|
||||
|
||||
cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
||||
gdk_window_set_cursor (widget->window, cursor);
|
||||
gdk_cursor_destroy (cursor);
|
||||
} else
|
||||
/* restore to old cursor */
|
||||
gdk_window_set_cursor (widget->window, NULL);
|
||||
|
||||
column_title->details->resize_cursor_on = on;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
track_prelight (GtkWidget *widget, int mouse_x, int mouse_y)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
int over_column;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
over_column = in_column_rect (widget, mouse_x, mouse_y);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
/* resizing a column, don't prelight */
|
||||
over_column = -1;
|
||||
}
|
||||
|
||||
if (column_title->details->tracking_column_press != -1) {
|
||||
/* pressing a column, don't prelight */
|
||||
over_column = -1;
|
||||
}
|
||||
|
||||
if (column_title->details->tracking_column_prelight == over_column) {
|
||||
/* no change */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* update state and tell callers to redraw */
|
||||
column_title->details->tracking_column_prelight = over_column;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_motion (GtkWidget *widget, GdkEventMotion *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
int mouse_x, mouse_y;
|
||||
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
gdk_window_get_pointer (widget->window, &mouse_x, &mouse_y, NULL);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
/* we are currently tracking a column */
|
||||
if (column_title->details->last_tracking_x != mouse_x) {
|
||||
/* mouse did move horizontally since last time */
|
||||
column_title->details->last_tracking_x = mouse_x;
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track (parent_list,
|
||||
column_title->details->tracking_column_resize);
|
||||
}
|
||||
} else {
|
||||
/* make sure we are showing the right cursor */
|
||||
show_hide_resize_cursor_if_needed (widget,
|
||||
in_resize_rect (widget, mouse_x, mouse_y) != -1);
|
||||
}
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
if (track_prelight (widget, mouse_x, mouse_y)) {
|
||||
nautilus_list_column_title_buffered_draw (widget);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event)
|
||||
{
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
if (track_prelight (widget, (int)event->x, (int)event->y)) {
|
||||
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
|
||||
nautilus_list_column_title_buffered_draw (widget);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_button_press (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
|
||||
g_assert (event != NULL);
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (NAUTILUS_LIST_COLUMN_TITLE(widget)->details->tracking_column_resize == -1);
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS) {
|
||||
int resized_column;
|
||||
int clicked_column;
|
||||
|
||||
resized_column = in_resize_rect (widget, (int)event->x, (int)event->y);
|
||||
clicked_column = in_column_rect (widget, (int)event->x, (int)event->y);
|
||||
|
||||
if (resized_column != -1) {
|
||||
GdkCursor *cursor;
|
||||
int grab_result;
|
||||
|
||||
/* during the drag, use the resize cursor */
|
||||
cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
||||
|
||||
/* grab the pointer events so that we get them even when
|
||||
* the mouse tracks out of the widget window
|
||||
*/
|
||||
grab_result = gdk_pointer_grab (widget->window, FALSE,
|
||||
GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_BUTTON1_MOTION_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK,
|
||||
NULL, cursor, event->time);
|
||||
gdk_cursor_destroy (cursor);
|
||||
|
||||
if (grab_result != 0)
|
||||
/* failed to grab the pointer, give up
|
||||
*
|
||||
* The grab results are not very well documented
|
||||
* looks like they may be Success, GrabSuccess, AlreadyGrabbed
|
||||
* or anything else the low level X calls in gdk_pointer_grab
|
||||
* decide to return.
|
||||
*/
|
||||
return FALSE;
|
||||
|
||||
/* set up new state */
|
||||
column_title->details->tracking_column_resize = resized_column;
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
|
||||
/* FIXME:
|
||||
* use a "resized" state here ?
|
||||
*/
|
||||
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
|
||||
|
||||
/* start column resize tracking */
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track_start (parent_list, resized_column);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
if (clicked_column != -1) {
|
||||
/* clicked a column, draw the pressed column title */
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
column_title->details->tracking_column_press = clicked_column;
|
||||
gtk_widget_set_state (widget, GTK_STATE_ACTIVE);
|
||||
/* FIXME:
|
||||
* buffered draw may be better here
|
||||
*/
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_button_release (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
|
||||
g_assert (event != NULL);
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
|
||||
/* let go of all the pointer events */
|
||||
if (gdk_pointer_is_grabbed ())
|
||||
gdk_pointer_ungrab (event->time);
|
||||
|
||||
/* end column resize tracking */
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track_end (parent_list,
|
||||
column_title->details->tracking_column_resize);
|
||||
column_title->details->tracking_column_resize = -1;
|
||||
|
||||
} else if (column_title->details->tracking_column_press != -1) {
|
||||
|
||||
/* column title press -- change the sort order */
|
||||
gtk_signal_emit_by_name (GTK_OBJECT (parent_list), "click_column",
|
||||
column_title->details->tracking_column_press);
|
||||
/* end press tracking */
|
||||
column_title->details->tracking_column_press = -1;
|
||||
}
|
||||
|
||||
track_prelight (widget, (int)event->x, (int)event->y);
|
||||
gtk_widget_set_state (widget,
|
||||
column_title->details->tracking_column_prelight != -1 ?
|
||||
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
|
||||
|
||||
/* FIXME:
|
||||
* buffered draw may be better here
|
||||
*/
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return FALSE;
|
||||
}
|
73
libnautilus-private/nautilus-list-column-title.h
Normal file
73
libnautilus-private/nautilus-list-column-title.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
||||
|
||||
nautilus-list-column-title.h: List column title widget for interacting with list columns
|
||||
|
||||
Copyright (C) 2000 Eazel, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
Authors: Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __NAUTILUS_LIST_COLUMN_TITLE__
|
||||
#define __NAUTILUS_LIST_COLUMN_TITLE__
|
||||
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkbin.h>
|
||||
#include <gtk/gtkenums.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define NAUTILUS_TYPE_LIST_COLUMN_TITLE \
|
||||
(nautilus_list_column_title_get_type ())
|
||||
#define NAUTILUS_LIST_COLUMN_TITLE(obj) \
|
||||
(GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_LIST_COLUMN_TITLE, NautilusListColumnTitle))
|
||||
#define NAUTILUS_LIST_COLUMN_TITLE_CLASS(klass) \
|
||||
(GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_LIST_COLUMN_TITLE, NautilusListColumnTitleClass))
|
||||
#define NAUTILUS_IS_LIST_COLUMN_TITLE(obj) \
|
||||
(GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_LIST_COLUMN_TITLE))
|
||||
#define NAUTILUS_IS_LIST_COLUMN_TITLE_CLASS(klass) \
|
||||
(GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_LIST_COLUMN_TITLE))
|
||||
|
||||
typedef struct NautilusListColumnTitle NautilusListColumnTitle;
|
||||
typedef struct NautilusListColumnTitleClass NautilusListColumnTitleClass;
|
||||
typedef struct NautilusListColumnTitleDetails NautilusListColumnTitleDetails;
|
||||
|
||||
|
||||
struct NautilusListColumnTitle
|
||||
{
|
||||
GtkBin bin;
|
||||
NautilusListColumnTitleDetails *details;
|
||||
};
|
||||
|
||||
struct NautilusListColumnTitleClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
};
|
||||
|
||||
GtkType nautilus_list_column_title_get_type (void);
|
||||
NautilusListColumnTitle *nautilus_list_column_title_new (void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* __NAUTILUS_LIST_COLUMN_TITLE__ */
|
|
@ -47,6 +47,7 @@ libnautilusinclude_HEADERS= \
|
|||
nautilus-gtk-extensions.h \
|
||||
nautilus-icon-factory.h \
|
||||
nautilus-icons-view-icon-item.h \
|
||||
nautilus-list-column-title.h \
|
||||
nautilus-metadata.h \
|
||||
nautilus-mime-type.h \
|
||||
nautilus-self-checks.h \
|
||||
|
@ -85,6 +86,7 @@ libnautilus_la_SOURCES=$(nautilus_idl_sources) \
|
|||
nautilus-icon-factory.c \
|
||||
nautilus-icons-view-icon-item.c \
|
||||
nautilus-lib-self-check-functions.c \
|
||||
nautilus-list-column-title.c \
|
||||
nautilus-mime-type.c \
|
||||
nautilus-self-checks.c \
|
||||
nautilus-string-list.c \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nautilus-glib-extensions.h"
|
||||
#include "nautilus-gtk-macros.h"
|
||||
#include "nautilus-background.h"
|
||||
#include "nautilus-list-column-title.h"
|
||||
|
||||
struct _GtkFListDetails
|
||||
{
|
||||
|
@ -37,7 +38,8 @@ struct _GtkFListDetails
|
|||
/* Delayed selection information */
|
||||
int dnd_select_pending;
|
||||
guint dnd_select_pending_state;
|
||||
|
||||
|
||||
GtkWidget *title;
|
||||
};
|
||||
|
||||
/* maximum amount of milliseconds the mouse button is allowed to stay down and still be considered a click */
|
||||
|
@ -81,8 +83,18 @@ static gboolean gtk_flist_drag_drop (GtkWidget *widget, GdkDragContext *context,
|
|||
static void gtk_flist_drag_data_received (GtkWidget *widget, GdkDragContext *context,
|
||||
gint x, gint y, GtkSelectionData *data,
|
||||
guint info, guint time);
|
||||
static void select_or_unselect_row_cb (GtkCList *clist, gint row, gint column,
|
||||
GdkEvent *event);
|
||||
|
||||
static void gtk_flist_clear (GtkCList *clist);
|
||||
static void draw_row (GtkCList *flist, GdkRectangle *area, gint row, GtkCListRow *clist_row);
|
||||
|
||||
static void gtk_flist_realize (GtkWidget *widget);
|
||||
static void gtk_flist_size_request (GtkWidget *widget, GtkRequisition *requisition);
|
||||
|
||||
static void gtk_flist_column_resize_track_start (GtkWidget *widget, int column);
|
||||
static void gtk_flist_column_resize_track (GtkWidget *widget, int column);
|
||||
static void gtk_flist_column_resize_track_end (GtkWidget *widget, int column);
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (GtkFList, gtk_flist, GTK_TYPE_CLIST)
|
||||
|
||||
|
@ -90,15 +102,17 @@ static guint flist_signals[LAST_SIGNAL];
|
|||
|
||||
/* Standard class initialization function */
|
||||
static void
|
||||
gtk_flist_initialize_class (GtkFListClass *class)
|
||||
gtk_flist_initialize_class (GtkFListClass *klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
GtkCListClass *clist_class;
|
||||
GtkFListClass *flist_class;
|
||||
|
||||
object_class = (GtkObjectClass *) class;
|
||||
widget_class = (GtkWidgetClass *) class;
|
||||
clist_class = (GtkCListClass *) class;
|
||||
object_class = (GtkObjectClass *) klass;
|
||||
widget_class = (GtkWidgetClass *) klass;
|
||||
clist_class = (GtkCListClass *) klass;
|
||||
flist_class = (GtkFListClass *) klass;
|
||||
|
||||
flist_signals[CONTEXT_CLICK_SELECTION] =
|
||||
gtk_signal_new ("context_click_selection",
|
||||
|
@ -135,13 +149,18 @@ gtk_flist_initialize_class (GtkFListClass *class)
|
|||
gtk_signal_new ("selection_changed",
|
||||
GTK_RUN_FIRST,
|
||||
object_class->type,
|
||||
GTK_SIGNAL_OFFSET (GtkFListClass, start_drag),
|
||||
GTK_SIGNAL_OFFSET (GtkFListClass, selection_changed),
|
||||
gtk_marshal_NONE__NONE,
|
||||
GTK_TYPE_NONE, 0);
|
||||
|
||||
gtk_object_class_add_signals (object_class, flist_signals, LAST_SIGNAL);
|
||||
|
||||
flist_class->column_resize_track_start = gtk_flist_column_resize_track_start;
|
||||
flist_class->column_resize_track = gtk_flist_column_resize_track;
|
||||
flist_class->column_resize_track_end = gtk_flist_column_resize_track_end;
|
||||
|
||||
clist_class->clear = gtk_flist_clear;
|
||||
clist_class->draw_row = draw_row;
|
||||
|
||||
widget_class->button_press_event = gtk_flist_button_press;
|
||||
widget_class->button_release_event = gtk_flist_button_release;
|
||||
|
@ -155,20 +174,14 @@ gtk_flist_initialize_class (GtkFListClass *class)
|
|||
widget_class->drag_motion = gtk_flist_drag_motion;
|
||||
widget_class->drag_drop = gtk_flist_drag_drop;
|
||||
widget_class->drag_data_received = gtk_flist_drag_data_received;
|
||||
}
|
||||
|
||||
static void
|
||||
select_or_unselect_row_cb (GtkCList *clist, gint row, gint column, GdkEvent *event)
|
||||
{
|
||||
/* This is the one bottleneck for all selection changes */
|
||||
gtk_signal_emit (GTK_OBJECT (clist), flist_signals[SELECTION_CHANGED]);
|
||||
widget_class->realize = gtk_flist_realize;
|
||||
widget_class->size_request = gtk_flist_size_request;
|
||||
}
|
||||
|
||||
/* Standard object initialization function */
|
||||
static void
|
||||
gtk_flist_initialize (GtkFList *flist)
|
||||
{
|
||||
|
||||
{
|
||||
flist->details = g_new0 (GtkFListDetails, 1);
|
||||
flist->details->anchor_row = -1;
|
||||
|
||||
|
@ -194,6 +207,16 @@ gtk_flist_initialize (GtkFList *flist)
|
|||
"unselect_row",
|
||||
select_or_unselect_row_cb,
|
||||
flist);
|
||||
|
||||
flist->details->title = GTK_WIDGET (nautilus_list_column_title_new());
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
select_or_unselect_row_cb (GtkCList *clist, gint row, gint column, GdkEvent *event)
|
||||
{
|
||||
/* This is the one bottleneck for all selection changes */
|
||||
gtk_signal_emit (GTK_OBJECT (clist), flist_signals[SELECTION_CHANGED]);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -429,6 +452,717 @@ gtk_flist_button_release (GtkWidget *widget, GdkEventButton *event)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* stolen from gtkclist.c for now */
|
||||
|
||||
/* minimum allowed width of a column */
|
||||
#define COLUMN_MIN_WIDTH 5
|
||||
|
||||
/* this defines the base grid spacing */
|
||||
#define CELL_SPACING 1
|
||||
|
||||
/* added the horizontal space at the beginning and end of a row */
|
||||
#define COLUMN_INSET 3
|
||||
|
||||
/* the width of the column resize windows */
|
||||
#define DRAG_WIDTH 6
|
||||
|
||||
/* gives the left pixel of the given column in context of
|
||||
* the clist's hoffset */
|
||||
#define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \
|
||||
(clist)->hoffset)
|
||||
|
||||
/* gives the top pixel of the given row in context of
|
||||
* the clist's voffset */
|
||||
#define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
|
||||
(((row) + 1) * CELL_SPACING) + \
|
||||
(clist)->voffset)
|
||||
|
||||
/* returns the row index from a y pixel location in the
|
||||
* context of the clist's voffset */
|
||||
#define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
|
||||
((clist)->row_height + CELL_SPACING))
|
||||
|
||||
/* returns the GList item for the nth row */
|
||||
#define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
|
||||
(clist)->row_list_end : \
|
||||
g_list_nth ((clist)->row_list, (row)))
|
||||
|
||||
/* returns the total height of the list */
|
||||
#define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \
|
||||
(CELL_SPACING * ((clist)->rows + 1)))
|
||||
|
||||
static void
|
||||
gtk_flist_realize (GtkWidget *widget)
|
||||
{
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
flist = GTK_FLIST (widget);
|
||||
clist = GTK_CLIST (widget);
|
||||
|
||||
clist->column[0].button = flist->details->title;
|
||||
|
||||
NAUTILUS_CALL_PARENT_CLASS (GTK_WIDGET_CLASS, realize, (widget));
|
||||
|
||||
if (clist->title_window) {
|
||||
gtk_widget_set_parent_window (flist->details->title, clist->title_window);
|
||||
}
|
||||
gtk_widget_set_parent (flist->details->title, GTK_WIDGET (clist));
|
||||
gtk_widget_show (flist->details->title);
|
||||
|
||||
GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
|
||||
}
|
||||
|
||||
/* this is here just temporarily */
|
||||
static gint
|
||||
list_requisition_width (GtkCList *clist)
|
||||
{
|
||||
gint width = CELL_SPACING;
|
||||
gint i;
|
||||
|
||||
for (i = clist->columns - 1; i >= 0; i--) {
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
if (clist->column[i].width_set)
|
||||
width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
|
||||
else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
|
||||
width += clist->column[i].button->requisition.width;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_flist_size_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
/* stolen from gtk_clist
|
||||
* make sure the proper title ammount is allocated for the column
|
||||
* title view -- this would not otherwise be done because
|
||||
* GtkFList depends the buttons being there when doing a size calculation
|
||||
*/
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
g_return_if_fail (requisition != NULL);
|
||||
|
||||
clist = GTK_CLIST (widget);
|
||||
flist = GTK_FLIST (widget);
|
||||
|
||||
requisition->width = 0;
|
||||
requisition->height = 0;
|
||||
|
||||
/* compute the size of the column title (title) area */
|
||||
clist->column_title_area.height = 0;
|
||||
if (GTK_CLIST_SHOW_TITLES(clist) && flist->details->title) {
|
||||
GtkRequisition child_requisition;
|
||||
|
||||
gtk_widget_size_request (flist->details->title,
|
||||
&child_requisition);
|
||||
|
||||
child_requisition.height = 20;
|
||||
/* for now */
|
||||
|
||||
clist->column_title_area.height =
|
||||
MAX (clist->column_title_area.height,
|
||||
child_requisition.height);
|
||||
}
|
||||
|
||||
requisition->width += (widget->style->klass->xthickness +
|
||||
GTK_CONTAINER (widget)->border_width) * 2;
|
||||
requisition->height += (clist->column_title_area.height +
|
||||
(widget->style->klass->ythickness +
|
||||
GTK_CONTAINER (widget)->border_width) * 2);
|
||||
|
||||
|
||||
requisition->width += list_requisition_width (clist);
|
||||
requisition->height += LIST_HEIGHT (clist);
|
||||
}
|
||||
|
||||
static gint
|
||||
new_column_width (GtkCList *clist, gint column, gint *x)
|
||||
{
|
||||
gint xthickness = GTK_WIDGET (clist)->style->klass->xthickness;
|
||||
gint width;
|
||||
gint cx;
|
||||
gint dx;
|
||||
gint last_column;
|
||||
|
||||
/* first translate the x position from widget->window
|
||||
* to clist->clist_window */
|
||||
cx = *x - xthickness;
|
||||
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--);
|
||||
|
||||
/* calculate new column width making sure it doesn't end up
|
||||
* less than the minimum width */
|
||||
dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
|
||||
(column < last_column) * CELL_SPACING);
|
||||
width = cx - dx;
|
||||
|
||||
if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width)) {
|
||||
width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
|
||||
cx = dx + width;
|
||||
*x = cx + xthickness;
|
||||
} else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
|
||||
width > clist->column[column].max_width) {
|
||||
width = clist->column[column].max_width;
|
||||
cx = dx + clist->column[column].max_width;
|
||||
*x = cx + xthickness;
|
||||
}
|
||||
|
||||
if (cx < 0 || cx > clist->clist_window_width)
|
||||
*x = -1;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static void
|
||||
size_allocate_columns (GtkCList *clist, gboolean block_resize)
|
||||
{
|
||||
int xoffset = CELL_SPACING + COLUMN_INSET;
|
||||
int last_column;
|
||||
int i;
|
||||
|
||||
/* find last visible column and calculate correct column width */
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--)
|
||||
;
|
||||
|
||||
if (last_column < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i <= last_column; i++) {
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
clist->column[i].area.x = xoffset;
|
||||
if (clist->column[i].width_set) {
|
||||
if (!block_resize && GTK_CLIST_SHOW_TITLES(clist) &&
|
||||
clist->column[i].auto_resize && clist->column[i].button) {
|
||||
gint width;
|
||||
|
||||
width = (clist->column[i].button->requisition.width -
|
||||
(CELL_SPACING + (2 * COLUMN_INSET)));
|
||||
|
||||
if (width > clist->column[i].width)
|
||||
gtk_clist_set_column_width (clist, i, width);
|
||||
}
|
||||
|
||||
clist->column[i].area.width = clist->column[i].width;
|
||||
xoffset += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
|
||||
} else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button) {
|
||||
clist->column[i].area.width =
|
||||
clist->column[i].button->requisition.width -
|
||||
(CELL_SPACING + (2 * COLUMN_INSET));
|
||||
xoffset += clist->column[i].button->requisition.width;
|
||||
}
|
||||
}
|
||||
|
||||
clist->column[last_column].area.width += MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
|
||||
}
|
||||
|
||||
static void
|
||||
size_allocate_title_buttons (GtkCList *clist)
|
||||
{
|
||||
GtkAllocation button_allocation;
|
||||
int last_column;
|
||||
int last_button = 0;
|
||||
int i;
|
||||
|
||||
button_allocation.x = clist->hoffset;
|
||||
button_allocation.y = 0;
|
||||
button_allocation.width = 0;
|
||||
button_allocation.height = clist->column_title_area.height;
|
||||
|
||||
/* find last visible column */
|
||||
for (last_column = clist->columns - 1; last_column >= 0; last_column--)
|
||||
if (clist->column[last_column].visible)
|
||||
break;
|
||||
|
||||
for (i = 0; i < last_column; i++) {
|
||||
if (!clist->column[i].visible) {
|
||||
last_button = i + 1;
|
||||
gdk_window_hide (clist->column[i].window);
|
||||
continue;
|
||||
}
|
||||
|
||||
button_allocation.width += (clist->column[i].area.width +
|
||||
CELL_SPACING + 2 * COLUMN_INSET);
|
||||
|
||||
if (!clist->column[i + 1].button) {
|
||||
gdk_window_hide (clist->column[i].window);
|
||||
continue;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (clist->column[last_button].button,
|
||||
&button_allocation);
|
||||
button_allocation.x += button_allocation.width;
|
||||
button_allocation.width = 0;
|
||||
|
||||
last_button = i + 1;
|
||||
}
|
||||
|
||||
button_allocation.width += (clist->column[last_column].area.width +
|
||||
2 * (CELL_SPACING + COLUMN_INSET));
|
||||
gtk_widget_size_allocate (clist->column[last_button].button,
|
||||
&button_allocation);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
get_cell_style (GtkCList *clist, GtkCListRow *clist_row,
|
||||
gint state, gint column, GtkStyle **style,
|
||||
GdkGC **fg_gc, GdkGC **bg_gc)
|
||||
{
|
||||
gint fg_state;
|
||||
|
||||
if ((state == GTK_STATE_NORMAL) &&
|
||||
(GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
|
||||
fg_state = GTK_STATE_INSENSITIVE;
|
||||
else
|
||||
fg_state = state;
|
||||
|
||||
if (clist_row->cell[column].style) {
|
||||
if (style)
|
||||
*style = clist_row->cell[column].style;
|
||||
if (fg_gc)
|
||||
*fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = clist_row->cell[column].style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = clist_row->cell[column].style->base_gc[state];
|
||||
}
|
||||
} else if (clist_row->style) {
|
||||
if (style)
|
||||
*style = clist_row->style;
|
||||
if (fg_gc)
|
||||
*fg_gc = clist_row->style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = clist_row->style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = clist_row->style->base_gc[state];
|
||||
}
|
||||
} else {
|
||||
if (style)
|
||||
*style = GTK_WIDGET (clist)->style;
|
||||
if (fg_gc)
|
||||
*fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
|
||||
if (bg_gc) {
|
||||
if (state == GTK_STATE_SELECTED)
|
||||
*bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
|
||||
else
|
||||
*bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
|
||||
}
|
||||
|
||||
if (state != GTK_STATE_SELECTED) {
|
||||
if (fg_gc && clist_row->fg_set)
|
||||
*fg_gc = clist->fg_gc;
|
||||
if (bg_gc && clist_row->bg_set)
|
||||
*bg_gc = clist->bg_gc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
draw_cell_pixmap (GdkWindow *window, GdkRectangle *clip_rectangle, GdkGC *fg_gc,
|
||||
GdkPixmap *pixmap, GdkBitmap *mask,
|
||||
gint x, gint y, gint width, gint height)
|
||||
{
|
||||
gint xsrc = 0;
|
||||
gint ysrc = 0;
|
||||
|
||||
if (mask) {
|
||||
gdk_gc_set_clip_mask (fg_gc, mask);
|
||||
gdk_gc_set_clip_origin (fg_gc, x, y);
|
||||
}
|
||||
|
||||
if (x < clip_rectangle->x) {
|
||||
xsrc = clip_rectangle->x - x;
|
||||
width -= xsrc;
|
||||
x = clip_rectangle->x;
|
||||
}
|
||||
|
||||
if (x + width > clip_rectangle->x + clip_rectangle->width)
|
||||
width = clip_rectangle->x + clip_rectangle->width - x;
|
||||
|
||||
if (y < clip_rectangle->y) {
|
||||
ysrc = clip_rectangle->y - y;
|
||||
height -= ysrc;
|
||||
y = clip_rectangle->y;
|
||||
}
|
||||
if (y + height > clip_rectangle->y + clip_rectangle->height)
|
||||
height = clip_rectangle->y + clip_rectangle->height - y;
|
||||
|
||||
gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
|
||||
gdk_gc_set_clip_origin (fg_gc, 0, 0);
|
||||
if (mask)
|
||||
gdk_gc_set_clip_mask (fg_gc, NULL);
|
||||
|
||||
return x + MAX (width, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_row (GtkCList *clist,
|
||||
GdkRectangle *area,
|
||||
gint row,
|
||||
GtkCListRow *clist_row)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkRectangle *rect;
|
||||
GdkRectangle row_rectangle;
|
||||
GdkRectangle cell_rectangle;
|
||||
GdkRectangle clip_rectangle;
|
||||
GdkRectangle intersect_rectangle;
|
||||
gint last_column;
|
||||
gint state;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (clist != NULL);
|
||||
|
||||
/* bail now if we arn't drawable yet */
|
||||
if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
|
||||
return;
|
||||
|
||||
widget = GTK_WIDGET (clist);
|
||||
|
||||
/* if the function is passed the pointer to the row instead of null,
|
||||
* it avoids this expensive lookup */
|
||||
if (!clist_row)
|
||||
clist_row = ROW_ELEMENT (clist, row)->data;
|
||||
|
||||
/* rectangle of the entire row */
|
||||
row_rectangle.x = 0;
|
||||
row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
|
||||
row_rectangle.width = clist->clist_window_width;
|
||||
row_rectangle.height = clist->row_height;
|
||||
|
||||
/* rectangle of the cell spacing above the row */
|
||||
cell_rectangle.x = 0;
|
||||
cell_rectangle.y = row_rectangle.y - CELL_SPACING;
|
||||
cell_rectangle.width = row_rectangle.width;
|
||||
cell_rectangle.height = CELL_SPACING;
|
||||
|
||||
/* rectangle used to clip drawing operations, its y and height
|
||||
* positions only need to be set once, so we set them once here.
|
||||
* the x and width are set withing the drawing loop below once per
|
||||
* column */
|
||||
clip_rectangle.y = row_rectangle.y;
|
||||
clip_rectangle.height = row_rectangle.height;
|
||||
|
||||
if (clist_row->state == GTK_STATE_NORMAL)
|
||||
{
|
||||
if (clist_row->fg_set)
|
||||
gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
|
||||
if (clist_row->bg_set)
|
||||
gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
|
||||
}
|
||||
|
||||
state = clist_row->state;
|
||||
|
||||
/* draw the cell borders and background */
|
||||
if (area)
|
||||
{
|
||||
rect = &intersect_rectangle;
|
||||
if (gdk_rectangle_intersect (area, &cell_rectangle,
|
||||
&intersect_rectangle))
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
intersect_rectangle.x,
|
||||
intersect_rectangle.y,
|
||||
intersect_rectangle.width,
|
||||
intersect_rectangle.height);
|
||||
|
||||
/* the last row has to clear its bottom cell spacing too */
|
||||
if (clist_row == clist->row_list_end->data)
|
||||
{
|
||||
cell_rectangle.y += clist->row_height + CELL_SPACING;
|
||||
|
||||
if (gdk_rectangle_intersect (area, &cell_rectangle,
|
||||
&intersect_rectangle))
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
intersect_rectangle.x,
|
||||
intersect_rectangle.y,
|
||||
intersect_rectangle.width,
|
||||
intersect_rectangle.height);
|
||||
}
|
||||
|
||||
if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
|
||||
return;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = &clip_rectangle;
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
cell_rectangle.x,
|
||||
cell_rectangle.y,
|
||||
cell_rectangle.width,
|
||||
cell_rectangle.height);
|
||||
|
||||
/* the last row has to clear its bottom cell spacing too */
|
||||
if (clist_row == clist->row_list_end->data)
|
||||
{
|
||||
cell_rectangle.y += clist->row_height + CELL_SPACING;
|
||||
|
||||
gdk_draw_rectangle (clist->clist_window,
|
||||
widget->style->base_gc[GTK_STATE_ACTIVE],
|
||||
TRUE,
|
||||
cell_rectangle.x,
|
||||
cell_rectangle.y,
|
||||
cell_rectangle.width,
|
||||
cell_rectangle.height);
|
||||
}
|
||||
}
|
||||
|
||||
for (last_column = clist->columns - 1;
|
||||
last_column >= 0 && !clist->column[last_column].visible; last_column--)
|
||||
;
|
||||
|
||||
/* iterate and draw all the columns (row cells) and draw their contents */
|
||||
for (i = 0; i < clist->columns; i++)
|
||||
{
|
||||
GtkStyle *style;
|
||||
GdkGC *fg_gc;
|
||||
GdkGC *bg_gc;
|
||||
|
||||
gint width;
|
||||
gint height;
|
||||
gint pixmap_width;
|
||||
gint offset = 0;
|
||||
gint row_center_offset;
|
||||
|
||||
if (!clist->column[i].visible)
|
||||
continue;
|
||||
|
||||
get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
|
||||
|
||||
clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
|
||||
clip_rectangle.width = clist->column[i].area.width;
|
||||
|
||||
/* calculate clipping region clipping region */
|
||||
clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
|
||||
clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
|
||||
(i == last_column) * CELL_SPACING);
|
||||
|
||||
if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
|
||||
&intersect_rectangle))
|
||||
continue;
|
||||
|
||||
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
|
||||
rect->x, rect->y, rect->width, rect->height);
|
||||
|
||||
clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
|
||||
clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
|
||||
(i == last_column) * CELL_SPACING);
|
||||
|
||||
/* calculate real width for column justification */
|
||||
pixmap_width = 0;
|
||||
offset = 0;
|
||||
switch (clist_row->cell[i].type)
|
||||
{
|
||||
case GTK_CELL_TEXT:
|
||||
width = gdk_string_width (style->font,
|
||||
GTK_CELL_TEXT (clist_row->cell[i])->text);
|
||||
break;
|
||||
case GTK_CELL_PIXMAP:
|
||||
gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
|
||||
&pixmap_width, &height);
|
||||
width = pixmap_width;
|
||||
break;
|
||||
case GTK_CELL_PIXTEXT:
|
||||
gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
|
||||
&pixmap_width, &height);
|
||||
width = (pixmap_width +
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
|
||||
gdk_string_width (style->font,
|
||||
GTK_CELL_PIXTEXT
|
||||
(clist_row->cell[i])->text));
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (clist->column[i].justification)
|
||||
{
|
||||
case GTK_JUSTIFY_LEFT:
|
||||
offset = clip_rectangle.x + clist_row->cell[i].horizontal;
|
||||
break;
|
||||
case GTK_JUSTIFY_RIGHT:
|
||||
offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
|
||||
clip_rectangle.width - width);
|
||||
break;
|
||||
case GTK_JUSTIFY_CENTER:
|
||||
case GTK_JUSTIFY_FILL:
|
||||
offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
|
||||
(clip_rectangle.width / 2) - (width / 2));
|
||||
break;
|
||||
};
|
||||
|
||||
/* Draw Text and/or Pixmap */
|
||||
switch (clist_row->cell[i].type)
|
||||
{
|
||||
case GTK_CELL_PIXMAP:
|
||||
draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
|
||||
GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
|
||||
GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
|
||||
offset,
|
||||
clip_rectangle.y + clist_row->cell[i].vertical +
|
||||
(clip_rectangle.height - height) / 2,
|
||||
pixmap_width, height);
|
||||
break;
|
||||
case GTK_CELL_PIXTEXT:
|
||||
offset =
|
||||
draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
|
||||
offset,
|
||||
clip_rectangle.y + clist_row->cell[i].vertical+
|
||||
(clip_rectangle.height - height) / 2,
|
||||
pixmap_width, height);
|
||||
offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
|
||||
case GTK_CELL_TEXT:
|
||||
if (style != GTK_WIDGET (clist)->style)
|
||||
row_center_offset = (((clist->row_height - style->font->ascent -
|
||||
style->font->descent - 1) / 2) + 1.5 +
|
||||
style->font->ascent);
|
||||
else
|
||||
row_center_offset = clist->row_center_offset;
|
||||
|
||||
gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
|
||||
gdk_draw_string (clist->clist_window, style->font, fg_gc,
|
||||
offset,
|
||||
row_rectangle.y + row_center_offset +
|
||||
clist_row->cell[i].vertical,
|
||||
(clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
|
||||
GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
|
||||
GTK_CELL_TEXT (clist_row->cell[i])->text);
|
||||
gdk_gc_set_clip_rectangle (fg_gc, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* draw focus rectangle */
|
||||
if (clist->focus_row == row &&
|
||||
GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS(widget))
|
||||
{
|
||||
if (!area)
|
||||
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
|
||||
row_rectangle.x, row_rectangle.y,
|
||||
row_rectangle.width - 1, row_rectangle.height - 1);
|
||||
else if (gdk_rectangle_intersect (area, &row_rectangle,
|
||||
&intersect_rectangle))
|
||||
{
|
||||
gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
|
||||
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
|
||||
row_rectangle.x, row_rectangle.y,
|
||||
row_rectangle.width - 1,
|
||||
row_rectangle.height - 1);
|
||||
gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_rows (GtkCList *clist, GdkRectangle *area)
|
||||
{
|
||||
GList *list;
|
||||
gint i;
|
||||
gint first_row;
|
||||
gint last_row;
|
||||
|
||||
if (clist->row_height == 0 || !GTK_WIDGET_DRAWABLE (clist))
|
||||
return;
|
||||
|
||||
first_row = ROW_FROM_YPIXEL (clist, area->y);
|
||||
last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
|
||||
|
||||
/* this is a small special case which exposes the bottom cell line
|
||||
* on the last row -- it might go away if I change the wall the cell
|
||||
* spacings are drawn
|
||||
*/
|
||||
if (clist->rows == first_row)
|
||||
first_row--;
|
||||
|
||||
list = ROW_ELEMENT (clist, first_row);
|
||||
for (i = first_row; i <= last_row ; i++) {
|
||||
if (list == NULL)
|
||||
break;
|
||||
|
||||
GTK_CLIST_CLASS ((GTK_OBJECT (clist))->klass)->draw_row (clist, area, i,
|
||||
list->data);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flist_track_new_column_width (GtkCList *clist, int column, int new_width)
|
||||
{
|
||||
GtkFList *flist;
|
||||
GdkRectangle title_redraw_area;
|
||||
|
||||
flist = GTK_FLIST (clist);
|
||||
|
||||
/* pin new_width to min and max values */
|
||||
if (new_width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
|
||||
new_width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
|
||||
if (clist->column[column].max_width >= 0 &&
|
||||
new_width > clist->column[column].max_width)
|
||||
new_width = clist->column[column].max_width;
|
||||
|
||||
/* check to see if the pinned value is still different */
|
||||
if (clist->column[column].width == new_width)
|
||||
return;
|
||||
|
||||
/* set the new width */
|
||||
clist->column[column].width = new_width;
|
||||
clist->column[column].width_set = TRUE;
|
||||
|
||||
size_allocate_columns (clist, TRUE);
|
||||
size_allocate_title_buttons (clist);
|
||||
|
||||
/* redraw the invalid columns */
|
||||
if (clist->freeze_count == 0) {
|
||||
|
||||
GdkRectangle area;
|
||||
|
||||
area = clist->column_title_area;
|
||||
area.x = clist->column[column].area.x;
|
||||
area.height += clist->clist_window_height;
|
||||
|
||||
draw_rows (clist, &area);
|
||||
}
|
||||
|
||||
/* draw the column title
|
||||
* ToDo:
|
||||
* fix this up
|
||||
*/
|
||||
title_redraw_area.x = GTK_WIDGET (flist->details->title)->allocation.x;
|
||||
title_redraw_area.y = GTK_WIDGET (flist->details->title)->allocation.y;
|
||||
title_redraw_area.width = GTK_WIDGET (flist->details->title)->allocation.width;
|
||||
title_redraw_area.height = GTK_WIDGET (flist->details->title)->allocation.height;
|
||||
|
||||
(GTK_WIDGET_CLASS (NAUTILUS_KLASS (flist->details->title)))->
|
||||
draw (flist->details->title, &title_redraw_area);
|
||||
}
|
||||
|
||||
/* Our handler for motion_notify events. We override all of GtkCList's broken
|
||||
* behavior.
|
||||
*/
|
||||
|
@ -476,6 +1210,45 @@ gtk_flist_motion (GtkWidget *widget, GdkEventMotion *event)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track_start (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist->drag_pos = column;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
int x;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist = GTK_CLIST (widget);
|
||||
|
||||
gtk_widget_get_pointer (widget, &x, NULL);
|
||||
gtk_flist_track_new_column_width (clist, column,
|
||||
new_column_width (clist, column, &x));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
gtk_flist_column_resize_track_end (GtkWidget *widget, int column)
|
||||
{
|
||||
GtkCList *clist;
|
||||
|
||||
g_return_if_fail (widget != NULL);
|
||||
g_return_if_fail (GTK_IS_FLIST (widget));
|
||||
|
||||
clist->drag_pos = -1;
|
||||
}
|
||||
|
||||
/* Our handler for key_press and key_release events. We do nothing, and we do
|
||||
* this to avoid GtkCList's broken behavior.
|
||||
*/
|
||||
|
@ -578,7 +1351,17 @@ gtk_flist_new_with_titles (int columns, char **titles)
|
|||
GtkFList *flist;
|
||||
|
||||
flist = gtk_type_new (gtk_flist_get_type ());
|
||||
gtk_clist_construct (GTK_CLIST (flist), columns, titles);
|
||||
gtk_clist_construct (GTK_CLIST (flist), columns, NULL);
|
||||
if (titles) {
|
||||
GtkCList *clist;
|
||||
int index;
|
||||
|
||||
clist = GTK_CLIST(flist);
|
||||
|
||||
for (index = 0; index < columns; index++) {
|
||||
clist->column[index].title = g_strdup (titles[index]);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_clist_set_selection_mode (GTK_CLIST (flist),
|
||||
GTK_SELECTION_MULTIPLE);
|
||||
|
|
|
@ -36,7 +36,7 @@ struct _GtkFListClass {
|
|||
GtkCListClass parent_class;
|
||||
|
||||
/* Signal: invoke the popup menu for selected items */
|
||||
void (* context_click_selection) (GtkFList *flist, gint row);
|
||||
void (* context_click_selection) (GtkFList *flist, int row);
|
||||
|
||||
/* Signal: invoke the popup menu for empty areas */
|
||||
void (* context_click_background) (GtkFList *flist);
|
||||
|
@ -45,10 +45,15 @@ struct _GtkFListClass {
|
|||
void (* activate) (GtkFList *flist, gpointer data);
|
||||
|
||||
/* Signal: initiate a drag and drop operation */
|
||||
void (* start_drag) (GtkFList *flist, gint button, GdkEvent *event);
|
||||
void (* start_drag) (GtkFList *flist, int button, GdkEvent *event);
|
||||
|
||||
/* Signal: selection has changed */
|
||||
void (* selection_changed) (GtkFList *flist);
|
||||
|
||||
/* column resize tracking calls */
|
||||
void (* column_resize_track_start) (GtkWidget *widget, int column);
|
||||
void (* column_resize_track) (GtkWidget *widget, int column);
|
||||
void (* column_resize_track_end) (GtkWidget *widget, int column);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -148,6 +148,47 @@ nautilus_fill_rectangle_with_gradient (GdkDrawable *drawable,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_rectangle_contains:
|
||||
* @rectangle: Rectangle possibly containing a point.
|
||||
* @x: X coordinate of a point.
|
||||
* @y: Y coordinate of a point.
|
||||
*
|
||||
* Retun TRUE if point is contained inside a rectangle
|
||||
*/
|
||||
gboolean
|
||||
nautilus_rectangle_contains (const GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
g_return_val_if_fail (rectangle != NULL, FALSE);
|
||||
return rectangle->x <= x && rectangle->x + rectangle->width >= x
|
||||
&& rectangle->y <= y && rectangle->y + rectangle->height >= y;
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_rectangle_inset:
|
||||
* @rectangle: Rectangle we are insetting.
|
||||
* @x: Horizontal ammount to inset by.
|
||||
* @y: Vertical ammount to inset by.
|
||||
*
|
||||
* Inset a rectangle by a given horizontal and vertical ammount.
|
||||
* Pass in negative inset values to grow the rectangle, positive to
|
||||
* shrink it.
|
||||
*/
|
||||
void
|
||||
nautilus_rectangle_inset (GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
g_return_if_fail (rectangle != NULL);
|
||||
|
||||
rectangle->x += x;
|
||||
rectangle->width -= 2 * x;
|
||||
rectangle->y += y;
|
||||
rectangle->height -= 2 * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* nautilus_interpolate_color:
|
||||
* @ratio: Place on line between colors to interpolate.
|
||||
|
|
|
@ -75,6 +75,15 @@ void nautilus_fill_rectangle_with_gradient (GdkDrawable *drawab
|
|||
const GdkColor *end_color,
|
||||
gboolean horizontal_gradient);
|
||||
|
||||
/* Misc GdkRectangle helper functions */
|
||||
gboolean nautilus_rectangle_contains (const GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
void nautilus_rectangle_inset (GdkRectangle *rectangle,
|
||||
int x,
|
||||
int y);
|
||||
|
||||
/* A basic operation we use for drawing gradients is interpolating two colors.*/
|
||||
void nautilus_interpolate_color (gdouble ratio,
|
||||
const GdkColor *start_color,
|
||||
|
|
763
libnautilus/nautilus-list-column-title.c
Normal file
763
libnautilus/nautilus-list-column-title.c
Normal file
|
@ -0,0 +1,763 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
||||
|
||||
nautilus-list-column-title.c: List column title widget for interacting with list columns
|
||||
|
||||
Copyright (C) 2000 Eazel, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
Authors: Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
*/
|
||||
|
||||
#include "nautilus-list-column-title.h"
|
||||
#include "nautilus-gtk-macros.h"
|
||||
#include "nautilus-gdk-extensions.h"
|
||||
|
||||
/* for now we need to know about GtkFList */
|
||||
#include "gtkflist.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
|
||||
#include <libgnomeui/gnome-pixmap.h>
|
||||
|
||||
/* these are from GtkCList, for now we need to copy them here
|
||||
* eventually the target list should be able to describe the values
|
||||
*/
|
||||
|
||||
enum {
|
||||
/* this defines the base grid spacing */
|
||||
CELL_SPACING = 1,
|
||||
|
||||
/* added the horizontal space at the beginning and end of a row */
|
||||
COLUMN_INSET = 3,
|
||||
|
||||
/* from GtkButton */
|
||||
CHILD_SPACING = 1,
|
||||
|
||||
/* the width of the column resize windows */
|
||||
DRAG_WIDTH = 6
|
||||
};
|
||||
|
||||
static char * down_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"......",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
" .. "
|
||||
};
|
||||
|
||||
static char * up_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
" .. ",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
"......"
|
||||
};
|
||||
|
||||
struct NautilusListColumnTitleDetails
|
||||
{
|
||||
/* gc for blitting sort order pixmaps, lazily allocated */
|
||||
GdkGC *copy_area_gc;
|
||||
|
||||
/* sort order indicator pixmaps, lazily allocated */
|
||||
GnomePixmap *up_indicator;
|
||||
GnomePixmap *down_indicator;
|
||||
|
||||
int tracking_column_resize;
|
||||
/* index of the column we are currently tracking or -1 */
|
||||
int tracking_column_prelight;
|
||||
/* index of the column we are currently rolling over or -1 */
|
||||
int tracking_column_press;
|
||||
/* index of the column we are currently pressing or -1 */
|
||||
|
||||
int last_tracking_x;
|
||||
/* last horizontal track point so we can only resize when needed */
|
||||
gboolean resize_cursor_on;
|
||||
};
|
||||
|
||||
static void nautilus_list_column_title_initialize_class (gpointer klass);
|
||||
static void nautilus_list_column_title_initialize (gpointer object, gpointer klass);
|
||||
static void nautilus_list_column_title_paint (GtkWidget *widget, GdkRectangle *area);
|
||||
static void nautilus_list_column_title_draw (GtkWidget *widget, GdkRectangle *box);
|
||||
static void nautilus_list_column_title_buffered_draw (GtkWidget *widget);
|
||||
static gboolean nautilus_list_column_title_expose (GtkWidget *widget, GdkEventExpose *event);
|
||||
static void nautilus_list_column_title_realize (GtkWidget *widget);
|
||||
static void nautilus_list_column_title_finalize (GtkObject *object);
|
||||
static void nautilus_list_column_title_request (GtkWidget *widget, GtkRequisition *requisition);
|
||||
|
||||
static gboolean nautilus_list_column_title_motion (GtkWidget *widget, GdkEventMotion *event);
|
||||
static gboolean nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event);
|
||||
|
||||
static gboolean nautilus_list_column_title_button_press (GtkWidget *widget, GdkEventButton *event);
|
||||
static gboolean nautilus_list_column_title_button_release (GtkWidget *widget, GdkEventButton *event);
|
||||
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusListColumnTitle, nautilus_list_column_title, GTK_TYPE_BIN)
|
||||
/* generates nautilus_list_column_title_get_type */
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_initialize_class (gpointer klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
|
||||
object_class = GTK_OBJECT_CLASS (klass);
|
||||
widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = nautilus_list_column_title_finalize;
|
||||
widget_class->draw = nautilus_list_column_title_draw;
|
||||
widget_class->expose_event = nautilus_list_column_title_expose;
|
||||
widget_class->realize = nautilus_list_column_title_realize;
|
||||
widget_class->size_request = nautilus_list_column_title_request;
|
||||
widget_class->motion_notify_event = nautilus_list_column_title_motion;
|
||||
widget_class->leave_notify_event = nautilus_list_column_title_leave;
|
||||
widget_class->button_press_event = nautilus_list_column_title_button_press;
|
||||
widget_class->button_release_event = nautilus_list_column_title_button_release;
|
||||
}
|
||||
|
||||
NautilusListColumnTitle *
|
||||
nautilus_list_column_title_new (void)
|
||||
{
|
||||
return gtk_type_new (nautilus_list_column_title_get_type ());
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_initialize (gpointer object, gpointer klass)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(object);
|
||||
column_title->details = g_new0 (NautilusListColumnTitleDetails, 1);
|
||||
|
||||
/* copy_gc, up/down indicators get allocated lazily when needed */
|
||||
column_title->details->copy_area_gc = NULL;
|
||||
column_title->details->up_indicator = NULL;
|
||||
column_title->details->down_indicator = NULL;
|
||||
|
||||
column_title->details->resize_cursor_on = FALSE;
|
||||
column_title->details->tracking_column_resize = -1;
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
column_title->details->tracking_column_press = -1;
|
||||
column_title->details->last_tracking_x = -1;
|
||||
|
||||
GTK_WIDGET_UNSET_FLAGS (object, GTK_NO_WINDOW);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_realize (GtkWidget *widget)
|
||||
{
|
||||
GdkWindowAttr attributes;
|
||||
int attributes_mask;
|
||||
|
||||
g_assert (widget != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
||||
|
||||
/* ask for expose events */
|
||||
attributes.window_type = GDK_WINDOW_CHILD;
|
||||
attributes.x = widget->allocation.x + GTK_CONTAINER (widget)->border_width;
|
||||
attributes.y = widget->allocation.y + GTK_CONTAINER (widget)->border_width;
|
||||
attributes.width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
|
||||
attributes.height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.visual = gtk_widget_get_visual (widget);
|
||||
attributes.colormap = gtk_widget_get_colormap (widget);
|
||||
attributes.event_mask = gtk_widget_get_events (widget);
|
||||
attributes.event_mask |= GDK_EXPOSURE_MASK
|
||||
| GDK_ENTER_NOTIFY_MASK
|
||||
| GDK_LEAVE_NOTIFY_MASK
|
||||
| GDK_BUTTON_PRESS_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK
|
||||
| GDK_POINTER_MOTION_MASK
|
||||
| GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_KEY_PRESS_MASK;
|
||||
|
||||
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
||||
|
||||
/* give ourselves a background window */
|
||||
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
|
||||
&attributes, attributes_mask);
|
||||
gdk_window_set_user_data (widget->window, widget);
|
||||
|
||||
widget->style = gtk_style_attach (widget->style, widget->window);
|
||||
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_finalize (GtkObject *object)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(object);
|
||||
|
||||
if (column_title->details->up_indicator != NULL)
|
||||
gtk_widget_destroy (GTK_WIDGET (column_title->details->up_indicator));
|
||||
if (column_title->details->down_indicator != NULL)
|
||||
gtk_widget_destroy (GTK_WIDGET (column_title->details->down_indicator));
|
||||
|
||||
/* FIXME:
|
||||
* figure out if we need to delete the copy_area_gc here
|
||||
*/
|
||||
|
||||
g_free (column_title->details);
|
||||
|
||||
NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
/* size requisition: make sure we have at least a minimal height */
|
||||
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (requisition != NULL);
|
||||
|
||||
requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
|
||||
widget->style->klass->xthickness) * 2;
|
||||
requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
|
||||
widget->style->klass->ythickness) * 2;
|
||||
|
||||
|
||||
if (GTK_BIN (widget)->child && GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child)) {
|
||||
GtkRequisition child_requisition;
|
||||
|
||||
gtk_widget_size_request (GTK_BIN (widget)->child, &child_requisition);
|
||||
|
||||
requisition->width += child_requisition.width;
|
||||
requisition->height += child_requisition.height;
|
||||
|
||||
requisition->height = MIN (requisition->height, 10);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_column_label_at (GtkWidget *column_title, int index)
|
||||
{
|
||||
GtkCList *parent_clist;
|
||||
|
||||
parent_clist = GTK_CLIST (column_title->parent);
|
||||
|
||||
return parent_clist->column[index].title;
|
||||
}
|
||||
|
||||
static void
|
||||
get_column_frame_at(GtkWidget *column_title, int index, GdkRectangle *result)
|
||||
{
|
||||
GtkCList *parent_clist;
|
||||
parent_clist = GTK_CLIST (column_title->parent);
|
||||
|
||||
*result = parent_clist->column_title_area;
|
||||
result->x = parent_clist->hoffset + parent_clist->column[index].area.x - COLUMN_INSET;
|
||||
result->y = 0;
|
||||
result->width = parent_clist->column[index].area.width
|
||||
+ CELL_SPACING + 2 * COLUMN_INSET - 1;
|
||||
}
|
||||
|
||||
static GnomePixmap *
|
||||
get_sort_indicator (GtkWidget *widget, gboolean ascending)
|
||||
{
|
||||
/* return the sort order pixmap for a given sort direction
|
||||
* allocate the pixmap first time around
|
||||
*/
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
if (ascending) {
|
||||
if (column_title->details->up_indicator == NULL) {
|
||||
column_title->details->up_indicator =
|
||||
GNOME_PIXMAP (gnome_pixmap_new_from_xpm_d (up_xpm));
|
||||
}
|
||||
return column_title->details->up_indicator;
|
||||
} else {
|
||||
if (column_title->details->down_indicator == NULL) {
|
||||
column_title->details->down_indicator =
|
||||
GNOME_PIXMAP (gnome_pixmap_new_from_xpm_d (down_xpm));
|
||||
}
|
||||
return column_title->details->down_indicator;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME:
|
||||
* Some of these magic numbers could be replaced with some more dynamic values
|
||||
*/
|
||||
enum {
|
||||
CELL_TITLE_INSET = 6,
|
||||
TITLE_BASELINE_OFFSET = 6,
|
||||
SORT_ORDER_INDICATOR_WIDTH = 10
|
||||
};
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_paint (GtkWidget *widget, GdkRectangle *area)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
g_assert (GTK_CLIST (widget->parent) != NULL);
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
GdkRectangle cell_rectangle;
|
||||
GdkRectangle cell_redraw_area;
|
||||
const char *cell_label;
|
||||
int x_offset;
|
||||
GnomePixmap *sort_indicator;
|
||||
gboolean right_justified;
|
||||
|
||||
sort_indicator = NULL;
|
||||
right_justified = (parent_clist->column[index].justification == GTK_JUSTIFY_RIGHT);
|
||||
|
||||
/* pick the ascending/descending sort indicator if needed */
|
||||
if (index == parent_clist->sort_column) {
|
||||
sort_indicator = get_sort_indicator (widget,
|
||||
parent_clist->sort_type == GTK_SORT_ASCENDING);
|
||||
}
|
||||
|
||||
get_column_frame_at (widget, index, &cell_rectangle);
|
||||
gdk_rectangle_intersect (&cell_rectangle, area, &cell_redraw_area);
|
||||
|
||||
if (cell_redraw_area.width == 0 || cell_redraw_area.height == 0) {
|
||||
/* no work, go on to the next */
|
||||
continue;
|
||||
}
|
||||
|
||||
cell_label = get_column_label_at (widget, index);
|
||||
|
||||
/* FIXME:
|
||||
* add support for center justification
|
||||
*/
|
||||
|
||||
if (right_justified) {
|
||||
x_offset = cell_rectangle.x + cell_rectangle.width - CELL_TITLE_INSET;
|
||||
} else {
|
||||
x_offset = cell_rectangle.x + CELL_TITLE_INSET;
|
||||
}
|
||||
|
||||
/* Paint the column title tiles as rectangles using "menu" style
|
||||
* buttons as used by GtkCList produce round corners in some themes.
|
||||
* Eventually we might consider having a separate style for column titles
|
||||
*/
|
||||
gtk_paint_box (widget->style, widget->window,
|
||||
column_title->details->tracking_column_prelight == index ?
|
||||
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
|
||||
column_title->details->tracking_column_press == index ?
|
||||
GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
||||
area, widget, "menu",
|
||||
cell_rectangle.x, cell_rectangle.y,
|
||||
cell_rectangle.width, cell_rectangle.height);
|
||||
|
||||
|
||||
/* Draw the sort indicator if needed */
|
||||
if (sort_indicator != NULL) {
|
||||
int y_offset = TITLE_BASELINE_OFFSET + 2;
|
||||
|
||||
if (right_justified) {
|
||||
x_offset -= SORT_ORDER_INDICATOR_WIDTH;
|
||||
}
|
||||
|
||||
/* allocate the sort indicator copy gc first time around */
|
||||
if (column_title->details->copy_area_gc == NULL) {
|
||||
column_title->details->copy_area_gc = gdk_gc_new (widget->window);
|
||||
gdk_gc_set_function (column_title->details->copy_area_gc, GDK_COPY);
|
||||
}
|
||||
/* move the pixmap clip mask and origin to the right spot in the gc */
|
||||
gdk_gc_set_clip_mask (column_title->details->copy_area_gc,
|
||||
sort_indicator->mask);
|
||||
gdk_gc_set_clip_origin (column_title->details->copy_area_gc, x_offset, y_offset);
|
||||
|
||||
|
||||
gdk_draw_pixmap (widget->window, column_title->details->copy_area_gc,
|
||||
sort_indicator->pixmap, 0, 0, x_offset, y_offset,
|
||||
-1, -1);
|
||||
|
||||
if (!right_justified) {
|
||||
x_offset += SORT_ORDER_INDICATOR_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell_label) {
|
||||
/* clip a little more than the cell rectangle to
|
||||
* not have the text draw over the cell broder
|
||||
* (this might no longer be needed when the
|
||||
* title gets truncated properly, as opposed to
|
||||
* getting chopped of
|
||||
*/
|
||||
nautilus_rectangle_inset (&cell_redraw_area, 2, 2);
|
||||
if (right_justified) {
|
||||
x_offset -= gdk_string_width (widget->style->font, cell_label) + 4;
|
||||
}
|
||||
|
||||
gtk_paint_string (widget->style, widget->window, GTK_STATE_NORMAL,
|
||||
&cell_redraw_area, widget, "label",
|
||||
x_offset,
|
||||
cell_rectangle.y + cell_rectangle.height - TITLE_BASELINE_OFFSET,
|
||||
cell_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_draw (GtkWidget *widget, GdkRectangle *area)
|
||||
{
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (area != NULL);
|
||||
|
||||
if (!GTK_WIDGET_DRAWABLE (widget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nautilus_list_column_title_paint (widget, area);
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_list_column_title_buffered_draw (GtkWidget *widget)
|
||||
{
|
||||
/* draw using an offscreen bitmap
|
||||
* for now just draw directly
|
||||
* add code here to lazily allocate an offscreen and pass it to the paint call
|
||||
* without the offscreen you can now see a funny artifact when part of the
|
||||
* old column border does not get erased properly when shrinking a column
|
||||
*/
|
||||
GdkRectangle redraw_area;
|
||||
redraw_area.x = widget->allocation.x;
|
||||
redraw_area.y = widget->allocation.y;
|
||||
redraw_area.width = widget->allocation.width;
|
||||
redraw_area.height = widget->allocation.height;
|
||||
|
||||
nautilus_list_column_title_draw (widget, &redraw_area);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_expose (GtkWidget *widget, GdkEventExpose *event)
|
||||
{
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (event != NULL);
|
||||
|
||||
if (!GTK_WIDGET_DRAWABLE (widget)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nautilus_list_column_title_paint (widget, &event->area);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
in_column_rect (GtkWidget *widget, int x, int y)
|
||||
{
|
||||
/* return the index of the column we hit or -1 */
|
||||
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
/* hit testing for column resizing */
|
||||
GdkRectangle cell_rectangle;
|
||||
|
||||
get_column_frame_at (widget, index, &cell_rectangle);
|
||||
|
||||
/* inset by a pixel so that you have to move past the border
|
||||
* to be considered inside the rect
|
||||
* nautilus_list_column_title_leave depends on this
|
||||
*/
|
||||
nautilus_rectangle_inset (&cell_rectangle, 1, 0);
|
||||
|
||||
|
||||
if (nautilus_rectangle_contains (&cell_rectangle, x, y))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
in_resize_rect (GtkWidget *widget, int x, int y)
|
||||
{
|
||||
/* return the index of the resize rect of a column we hit or -1 */
|
||||
|
||||
GtkCList *parent_clist;
|
||||
int index;
|
||||
|
||||
parent_clist = GTK_CLIST (widget->parent);
|
||||
|
||||
for (index = 0; index < parent_clist->columns; index++) {
|
||||
/* hit testing for column resizing */
|
||||
GdkRectangle resize_rectangle;
|
||||
|
||||
get_column_frame_at (widget, index, &resize_rectangle);
|
||||
|
||||
nautilus_rectangle_inset (&resize_rectangle, 1, 0);
|
||||
|
||||
resize_rectangle.x = resize_rectangle.x + resize_rectangle.width - DRAG_WIDTH / 2;
|
||||
resize_rectangle.width = DRAG_WIDTH;
|
||||
|
||||
if (nautilus_rectangle_contains (&resize_rectangle, x, y))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
show_hide_resize_cursor_if_needed (GtkWidget *widget, gboolean on)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
if (on == column_title->details->resize_cursor_on)
|
||||
/* already set right */
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
/* switch to a resize cursor */
|
||||
GdkCursor *cursor;
|
||||
|
||||
cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
||||
gdk_window_set_cursor (widget->window, cursor);
|
||||
gdk_cursor_destroy (cursor);
|
||||
} else
|
||||
/* restore to old cursor */
|
||||
gdk_window_set_cursor (widget->window, NULL);
|
||||
|
||||
column_title->details->resize_cursor_on = on;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
track_prelight (GtkWidget *widget, int mouse_x, int mouse_y)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
int over_column;
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
over_column = in_column_rect (widget, mouse_x, mouse_y);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
/* resizing a column, don't prelight */
|
||||
over_column = -1;
|
||||
}
|
||||
|
||||
if (column_title->details->tracking_column_press != -1) {
|
||||
/* pressing a column, don't prelight */
|
||||
over_column = -1;
|
||||
}
|
||||
|
||||
if (column_title->details->tracking_column_prelight == over_column) {
|
||||
/* no change */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* update state and tell callers to redraw */
|
||||
column_title->details->tracking_column_prelight = over_column;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_motion (GtkWidget *widget, GdkEventMotion *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
int mouse_x, mouse_y;
|
||||
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
gdk_window_get_pointer (widget->window, &mouse_x, &mouse_y, NULL);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
/* we are currently tracking a column */
|
||||
if (column_title->details->last_tracking_x != mouse_x) {
|
||||
/* mouse did move horizontally since last time */
|
||||
column_title->details->last_tracking_x = mouse_x;
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track (parent_list,
|
||||
column_title->details->tracking_column_resize);
|
||||
}
|
||||
} else {
|
||||
/* make sure we are showing the right cursor */
|
||||
show_hide_resize_cursor_if_needed (widget,
|
||||
in_resize_rect (widget, mouse_x, mouse_y) != -1);
|
||||
}
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
if (track_prelight (widget, mouse_x, mouse_y)) {
|
||||
nautilus_list_column_title_buffered_draw (widget);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event)
|
||||
{
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
/* see if we need to update the prelight state of a column */
|
||||
if (track_prelight (widget, (int)event->x, (int)event->y)) {
|
||||
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
|
||||
nautilus_list_column_title_buffered_draw (widget);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_button_press (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
|
||||
g_assert (event != NULL);
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
g_assert (NAUTILUS_LIST_COLUMN_TITLE(widget)->details->tracking_column_resize == -1);
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS) {
|
||||
int resized_column;
|
||||
int clicked_column;
|
||||
|
||||
resized_column = in_resize_rect (widget, (int)event->x, (int)event->y);
|
||||
clicked_column = in_column_rect (widget, (int)event->x, (int)event->y);
|
||||
|
||||
if (resized_column != -1) {
|
||||
GdkCursor *cursor;
|
||||
int grab_result;
|
||||
|
||||
/* during the drag, use the resize cursor */
|
||||
cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
|
||||
|
||||
/* grab the pointer events so that we get them even when
|
||||
* the mouse tracks out of the widget window
|
||||
*/
|
||||
grab_result = gdk_pointer_grab (widget->window, FALSE,
|
||||
GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_BUTTON1_MOTION_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK,
|
||||
NULL, cursor, event->time);
|
||||
gdk_cursor_destroy (cursor);
|
||||
|
||||
if (grab_result != 0)
|
||||
/* failed to grab the pointer, give up
|
||||
*
|
||||
* The grab results are not very well documented
|
||||
* looks like they may be Success, GrabSuccess, AlreadyGrabbed
|
||||
* or anything else the low level X calls in gdk_pointer_grab
|
||||
* decide to return.
|
||||
*/
|
||||
return FALSE;
|
||||
|
||||
/* set up new state */
|
||||
column_title->details->tracking_column_resize = resized_column;
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
|
||||
/* FIXME:
|
||||
* use a "resized" state here ?
|
||||
*/
|
||||
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
|
||||
|
||||
/* start column resize tracking */
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track_start (parent_list, resized_column);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
if (clicked_column != -1) {
|
||||
/* clicked a column, draw the pressed column title */
|
||||
column_title->details->tracking_column_prelight = -1;
|
||||
column_title->details->tracking_column_press = clicked_column;
|
||||
gtk_widget_set_state (widget, GTK_STATE_ACTIVE);
|
||||
/* FIXME:
|
||||
* buffered draw may be better here
|
||||
*/
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
nautilus_list_column_title_button_release (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
NautilusListColumnTitle *column_title;
|
||||
GtkWidget *parent_list;
|
||||
|
||||
g_assert (event != NULL);
|
||||
g_assert (GTK_FLIST (widget->parent) != NULL);
|
||||
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
|
||||
|
||||
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
|
||||
parent_list = GTK_WIDGET (widget->parent);
|
||||
|
||||
if (column_title->details->tracking_column_resize != -1) {
|
||||
|
||||
/* let go of all the pointer events */
|
||||
if (gdk_pointer_is_grabbed ())
|
||||
gdk_pointer_ungrab (event->time);
|
||||
|
||||
/* end column resize tracking */
|
||||
(GTK_FLIST_CLASS (NAUTILUS_KLASS (parent_list)))->
|
||||
column_resize_track_end (parent_list,
|
||||
column_title->details->tracking_column_resize);
|
||||
column_title->details->tracking_column_resize = -1;
|
||||
|
||||
} else if (column_title->details->tracking_column_press != -1) {
|
||||
|
||||
/* column title press -- change the sort order */
|
||||
gtk_signal_emit_by_name (GTK_OBJECT (parent_list), "click_column",
|
||||
column_title->details->tracking_column_press);
|
||||
/* end press tracking */
|
||||
column_title->details->tracking_column_press = -1;
|
||||
}
|
||||
|
||||
track_prelight (widget, (int)event->x, (int)event->y);
|
||||
gtk_widget_set_state (widget,
|
||||
column_title->details->tracking_column_prelight != -1 ?
|
||||
GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
|
||||
|
||||
/* FIXME:
|
||||
* buffered draw may be better here
|
||||
*/
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return FALSE;
|
||||
}
|
73
libnautilus/nautilus-list-column-title.h
Normal file
73
libnautilus/nautilus-list-column-title.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
||||
|
||||
nautilus-list-column-title.h: List column title widget for interacting with list columns
|
||||
|
||||
Copyright (C) 2000 Eazel, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
Authors: Pavel Cisler <pavel@eazel.com>
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __NAUTILUS_LIST_COLUMN_TITLE__
|
||||
#define __NAUTILUS_LIST_COLUMN_TITLE__
|
||||
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkbin.h>
|
||||
#include <gtk/gtkenums.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define NAUTILUS_TYPE_LIST_COLUMN_TITLE \
|
||||
(nautilus_list_column_title_get_type ())
|
||||
#define NAUTILUS_LIST_COLUMN_TITLE(obj) \
|
||||
(GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_LIST_COLUMN_TITLE, NautilusListColumnTitle))
|
||||
#define NAUTILUS_LIST_COLUMN_TITLE_CLASS(klass) \
|
||||
(GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_LIST_COLUMN_TITLE, NautilusListColumnTitleClass))
|
||||
#define NAUTILUS_IS_LIST_COLUMN_TITLE(obj) \
|
||||
(GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_LIST_COLUMN_TITLE))
|
||||
#define NAUTILUS_IS_LIST_COLUMN_TITLE_CLASS(klass) \
|
||||
(GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_LIST_COLUMN_TITLE))
|
||||
|
||||
typedef struct NautilusListColumnTitle NautilusListColumnTitle;
|
||||
typedef struct NautilusListColumnTitleClass NautilusListColumnTitleClass;
|
||||
typedef struct NautilusListColumnTitleDetails NautilusListColumnTitleDetails;
|
||||
|
||||
|
||||
struct NautilusListColumnTitle
|
||||
{
|
||||
GtkBin bin;
|
||||
NautilusListColumnTitleDetails *details;
|
||||
};
|
||||
|
||||
struct NautilusListColumnTitleClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
};
|
||||
|
||||
GtkType nautilus_list_column_title_get_type (void);
|
||||
NautilusListColumnTitle *nautilus_list_column_title_new (void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* __NAUTILUS_LIST_COLUMN_TITLE__ */
|
|
@ -126,41 +126,11 @@ const char * get_attribute_from_column (int
|
|||
int get_column_from_attribute (const char *value);
|
||||
int get_sort_column_from_attribute (const char *value);
|
||||
static GtkFList * get_flist (FMDirectoryViewList *list_view);
|
||||
static GtkWidget * get_sort_indicator (GtkFList *flist,
|
||||
int column,
|
||||
gboolean reverse);
|
||||
static void hide_sort_indicator (GtkFList *flist,
|
||||
int column);
|
||||
static void install_icon (FMDirectoryViewList *list_view,
|
||||
guint row);
|
||||
static void show_sort_indicator (GtkFList *flist,
|
||||
int column,
|
||||
gboolean sort_reversed);
|
||||
static int sort_criterion_from_column (int column);
|
||||
static void update_icons (FMDirectoryViewList *list_view);
|
||||
|
||||
static char * down_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"......",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
" .. "
|
||||
};
|
||||
|
||||
static char * up_xpm[] = {
|
||||
"6 5 2 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
" .. ",
|
||||
" ",
|
||||
" .... ",
|
||||
" ",
|
||||
"......"
|
||||
};
|
||||
|
||||
|
||||
NAUTILUS_DEFINE_CLASS_BOILERPLATE (FMDirectoryViewList, fm_directory_view_list, FM_TYPE_DIRECTORY_VIEW);
|
||||
|
||||
|
@ -315,7 +285,12 @@ create_flist (FMDirectoryViewList *list_view)
|
|||
{
|
||||
GtkFList *flist;
|
||||
GtkCList *clist;
|
||||
|
||||
|
||||
/* title setup should allow for columns not being resizable at all,
|
||||
* justification, editable or not, type/format,
|
||||
* not being usable as a sort order criteria, etc.
|
||||
* for now just set up name, min, max and current width
|
||||
*/
|
||||
char *titles[] = {
|
||||
NULL,
|
||||
_("Name"),
|
||||
|
@ -323,6 +298,7 @@ create_flist (FMDirectoryViewList *list_view)
|
|||
_("Type"),
|
||||
_("Date Modified"),
|
||||
};
|
||||
|
||||
guint widths[] = {
|
||||
fm_directory_view_list_get_icon_size (list_view), /* Icon */
|
||||
130, /* Name */
|
||||
|
@ -330,6 +306,23 @@ create_flist (FMDirectoryViewList *list_view)
|
|||
95, /* Type */
|
||||
100, /* Modified */
|
||||
};
|
||||
|
||||
guint min_widths[] = {
|
||||
fm_directory_view_list_get_icon_size (list_view), /* Icon */
|
||||
30, /* Name */
|
||||
20, /* Size */
|
||||
20, /* Type */
|
||||
30, /* Modified */
|
||||
};
|
||||
|
||||
guint max_widths[] = {
|
||||
fm_directory_view_list_get_icon_size (list_view), /* Icon */
|
||||
300, /* Name */
|
||||
80, /* Size */
|
||||
200, /* Type */
|
||||
200, /* Modified */
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (FM_IS_DIRECTORY_VIEW_LIST (list_view), NULL);
|
||||
|
@ -339,54 +332,28 @@ create_flist (FMDirectoryViewList *list_view)
|
|||
|
||||
for (i = 0; i < LIST_VIEW_COLUMN_COUNT; ++i)
|
||||
{
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *label;
|
||||
GtkWidget *sort_up_indicator;
|
||||
GtkWidget *sort_down_indicator;
|
||||
gboolean right_justified;
|
||||
|
||||
right_justified = (i == LIST_VIEW_COLUMN_SIZE);
|
||||
|
||||
gtk_clist_set_column_width (clist, i, widths[i]);
|
||||
gtk_clist_set_column_min_width (clist, i, min_widths[i]);
|
||||
gtk_clist_set_column_max_width (clist, i, max_widths[i]);
|
||||
|
||||
/* Column header button contains three views, a title,
|
||||
* a "sort downward" indicator, and a "sort upward" indicator.
|
||||
* Only one sort indicator (for all columns) is shown at once.
|
||||
*/
|
||||
hbox = gtk_hbox_new (FALSE, GNOME_PAD_SMALL);
|
||||
gtk_widget_show (GTK_WIDGET (hbox));
|
||||
label = gtk_label_new (titles[i]);
|
||||
gtk_widget_show (GTK_WIDGET (label));
|
||||
|
||||
/* Sort indicators are initially hidden. They're marked with
|
||||
* special data so they can be located later in each column.
|
||||
*/
|
||||
sort_up_indicator = gnome_pixmap_new_from_xpm_d (up_xpm);
|
||||
gtk_object_set_data(GTK_OBJECT(sort_up_indicator),
|
||||
SORT_INDICATOR_KEY,
|
||||
GINT_TO_POINTER (UP_INDICATOR_VALUE));
|
||||
|
||||
sort_down_indicator = gnome_pixmap_new_from_xpm_d (down_xpm);
|
||||
gtk_object_set_data(GTK_OBJECT(sort_down_indicator),
|
||||
SORT_INDICATOR_KEY,
|
||||
GINT_TO_POINTER (DOWN_INDICATOR_VALUE));
|
||||
|
||||
if (right_justified)
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (hbox), sort_up_indicator, FALSE, FALSE, GNOME_PAD);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), sort_down_indicator, FALSE, FALSE, GNOME_PAD);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
||||
|
||||
if (right_justified) {
|
||||
/* hack around a problem where gtk_clist_set_column_justification
|
||||
* crashes if there is a column title but now
|
||||
* column button (it should really be checking if it has a button instead)
|
||||
* this is an easy, dirty fix for now, will get straightened out
|
||||
* with a replacement list view (alternatively, we'd fix this in GtkCList)
|
||||
*/
|
||||
char *tmp_title = clist->column[i].title;
|
||||
clist->column[i].title = NULL;
|
||||
gtk_clist_set_column_justification (clist, i, GTK_JUSTIFY_RIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), sort_up_indicator, FALSE, FALSE, GNOME_PAD);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), sort_down_indicator, FALSE, FALSE, GNOME_PAD);
|
||||
clist->column[i].title = tmp_title;
|
||||
}
|
||||
|
||||
gtk_clist_set_column_widget (clist, i, hbox);
|
||||
}
|
||||
|
||||
gtk_clist_set_auto_sort (clist, TRUE);
|
||||
|
@ -771,9 +738,7 @@ fm_directory_view_list_sort_items (FMDirectoryViewList *list_view,
|
|||
|
||||
flist = get_flist (list_view);
|
||||
clist = GTK_CLIST (flist);
|
||||
hide_sort_indicator (flist, list_view->details->sort_column);
|
||||
show_sort_indicator (flist, column, reversed);
|
||||
|
||||
|
||||
list_view->details->sort_column = column;
|
||||
|
||||
if (reversed != list_view->details->sort_reversed)
|
||||
|
@ -892,60 +857,6 @@ get_sort_column_from_attribute (const char *value)
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
static GtkWidget *
|
||||
get_sort_indicator (GtkFList *flist, int column, gboolean reverse)
|
||||
{
|
||||
GtkWidget *column_widget;
|
||||
GtkWidget *result;
|
||||
GList *children;
|
||||
GList *iter;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_FLIST (flist), NULL);
|
||||
g_return_val_if_fail (column >= 0, NULL);
|
||||
|
||||
column_widget = gtk_clist_get_column_widget (GTK_CLIST (flist), column);
|
||||
g_assert (GTK_IS_HBOX (column_widget));
|
||||
|
||||
children = gtk_container_children (GTK_CONTAINER (column_widget));
|
||||
|
||||
iter = children;
|
||||
result = NULL;
|
||||
while (iter != NULL)
|
||||
{
|
||||
int indicator_int = GPOINTER_TO_INT (
|
||||
gtk_object_get_data (GTK_OBJECT (iter->data), SORT_INDICATOR_KEY));
|
||||
|
||||
if ((reverse && indicator_int == DOWN_INDICATOR_VALUE) ||
|
||||
(!reverse && indicator_int == UP_INDICATOR_VALUE))
|
||||
{
|
||||
result = GTK_WIDGET (iter->data);
|
||||
break;
|
||||
}
|
||||
|
||||
iter = g_list_next(iter);
|
||||
}
|
||||
|
||||
g_assert (result != NULL);
|
||||
|
||||
g_list_free (children);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
hide_sort_indicator (GtkFList *flist, int column)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_FLIST (flist));
|
||||
|
||||
if (column == LIST_VIEW_COLUMN_NONE)
|
||||
return;
|
||||
|
||||
gtk_widget_hide (get_sort_indicator (flist, column, FALSE));
|
||||
gtk_widget_hide (get_sort_indicator (flist, column, TRUE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* install_icon:
|
||||
*
|
||||
|
@ -976,17 +887,6 @@ install_icon (FMDirectoryViewList *list_view, guint row)
|
|||
gtk_clist_set_pixmap (clist, row, LIST_VIEW_COLUMN_ICON, pixmap, bitmap);
|
||||
}
|
||||
|
||||
static void
|
||||
show_sort_indicator (GtkFList *flist, int column, gboolean sort_reversed)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_FLIST (flist));
|
||||
|
||||
if (column == LIST_VIEW_COLUMN_NONE)
|
||||
return;
|
||||
|
||||
gtk_widget_show (get_sort_indicator (flist, column, sort_reversed));
|
||||
}
|
||||
|
||||
static int
|
||||
sort_criterion_from_column (int column)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue