nautilus/libnautilus-private/nautilus-list-column-title.c
Darin Adler 63d4a318cb A little more desktop window work.
* src/nautilus-desktop-window.c:
	(nautilus_desktop_window_initialize_class),
	(nautilus_desktop_window_initialize),
	(nautilus_desktop_window_new):
	* src/nautilus-desktop-window.h:
	* src/ntl-app.c: (nautilus_app_startup), (nautilus_app_quit),
	(nautilus_app_create_window):
	* src/ntl-app.h:
	* src/ntl-main.c: (main):
	Made a desktop window that's created when you start the program
	if you pass the --desktop option. At the moment it's a normal
	window and not set up to take over the desktop.

	* libnautilus-extensions/nautilus-glib-extensions.c:
	(nautilus_self_check_glib_extensions): Fixed some broken self-
	checks. Now we have one really broken self-check in the
	nautilus-directory.c file. I might turn that one off soon so we
	can at least run the others.

	* libnautilus-extensions/nautilus-gtk-macros.h:
	Changed how some of the macros work. Few of these macros were used
	in Nautilus. I hope they aren't used in the Mozilla component,
	because then I broke it.

	* libnautilus-extensions/nautilus-list-column-title.c:
	(nautilus_list_column_title_motion),
	(nautilus_list_column_title_button_press),
	(nautilus_list_column_title_button_release):
	Updated to use NAUTILUS_INVOKE_METHOD instead of NAUTILUS_KLASS,
	because I renamed it to NAUTILUS_CLASS and had to change these
	anyway.

	A little sweep for code that should be using
	CORBA_Object_is_nil instead of direct compares with
	CORBA_OBJECT_NIL.

	* components/html/main.c: (url_requested_cb):
	* libnautilus-extensions/bonobo-stream-vfs.c:
	(bonobo_stream_create):
	* libnautilus/nautilus-undo-manager.c:
	(set_up_bonobo_control):
	* libnautilus/nautilus-undoable.c: (nautilus_undo_register_full),
	(nautilus_undo_unregister), (nautilus_undo):
	* src/file-manager/dfos.c: (dfos_new):
	Switch to use CORBA_Object_is_nil.

	* libnautilus/nautilus-undo-manager.c: (undo_manager_unref),
	(nautilus_attach_undo_manager), (nautilus_share_undo_manager),
	(set_up_bonobo_control):
	Do some missing duplicate and release calls.

	* libnautilus/nautilus-undoable.c: (nautilus_undo_unregister):
	Fix a place where we'd keep a reference to a freed list.
	Change O(n^2) walk of list by index to O(n) walk by pointer.

	* src/ntl-index-panel.c: (nautilus_index_panel_remove_meta_view):
	Changed g_warning to a g_return_if_fail, which was designed for
	cases just like this one.

	* src/ntl-view-bonobo-subdoc.c:
	(bonobo_subdoc_notify_location_change):
	Put in a better version of the FIXME.

	* RENAMING: More updates.
2000-05-27 02:17:41 +00:00

933 lines
29 KiB
C

/* -*- 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 <config.h>
#include "nautilus-list-column-title.h"
#include "nautilus-gtk-macros.h"
#include "nautilus-gdk-extensions.h"
#include "nautilus-list.h"
#include <gdk/gdk.h>
#include <gtk/gtkclist.h>
#include <gtk/gtkmain.h>
#include <libgnomeui/gnome-pixmap.h>
#include <string.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",
" .. ",
" ",
" .... ",
" ",
"......"
};
#define COLUMN_TITLE_THEME_STYLE_NAME "menu"
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;
/* offscreen drawing support */
/* FIXME bugzilla.eazel.com 614:
* consolidate this into it's own class once I figure out all the details.
*/
GtkWidget *offscreen_widget;
GdkPixmap *offscreen_pixmap;
GdkGC *offscreen_blitting_gc;
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, GtkWidget *draw_target, GdkDrawable *target_drawable, 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->offscreen_widget = NULL;
column_title->details->offscreen_pixmap = NULL;
column_title->details->offscreen_blitting_gc = 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 (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));
}
if (column_title->details->offscreen_widget != NULL) {
gdk_pixmap_unref (column_title->details->offscreen_pixmap);
gtk_widget_unref (column_title->details->offscreen_widget);
gdk_gc_destroy (column_title->details->offscreen_blitting_gc);
}
if (column_title->details->copy_area_gc != NULL) {
gdk_gc_destroy (column_title->details->copy_area_gc);
}
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;
}
}
/* Add more truncation modes, optimize for performance, move to nautilus-gdk-extensions */
static char *
truncate_string (const char *string, GdkFont *font, int width, int *final_width)
{
int current_width;
int ellipsis_width;
int length;
int trimmed_length;
char *result;
length = strlen (string);
current_width = gdk_text_width (font, string, length);
if (current_width <= width) {
/* trivial case, already fits fine */
if (final_width != NULL) {
*final_width = current_width;
}
return g_strdup (string);
}
ellipsis_width = gdk_string_width (font, "...");
if (ellipsis_width > width) {
/* we can't fit anything */
if (final_width != NULL) {
*final_width = 0;
}
return g_strdup ("");
}
width -= ellipsis_width;
for (trimmed_length = length - 1; trimmed_length >= 0; trimmed_length--) {
current_width = gdk_text_width (font, string, trimmed_length);
if (current_width <= width)
break;
}
result = (char *) g_malloc (trimmed_length + 3 + 1);
strncpy (result, string, trimmed_length);
strcpy (result + trimmed_length, "...");
if (final_width != NULL) {
*final_width = current_width + ellipsis_width;
}
return result;
}
/* FIXME bugzilla.eazel.com 615:
* Some of these magic numbers could be replaced with some more dynamic values
*/
enum {
CELL_TITLE_INSET = 3,
TITLE_BASELINE_OFFSET = 6,
SORT_ORDER_INDICATOR_WIDTH = 10,
SORT_INDICATOR_X_OFFSET = 6,
SORT_INDICATOR_Y_OFFSET = 3
};
static void
nautilus_list_column_title_paint (GtkWidget *widget, GtkWidget *draw_target,
GdkDrawable *target_drawable, 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 text_x_offset;
int text_x_available_end;
int sort_indicator_x_offset;
GnomePixmap *sort_indicator;
gboolean right_justified;
sort_indicator_x_offset = 0;
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 bugzilla.eazel.com 616:
* add support for center justification
*/
text_x_offset = cell_rectangle.x + CELL_TITLE_INSET;
text_x_available_end = cell_rectangle.x + cell_rectangle.width - 2 * CELL_TITLE_INSET;
/* Paint the column tiles as rectangles using "menu" (COLUMN_TITLE_THEME_STYLE_NAME).
* 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, target_drawable,
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, draw_target, COLUMN_TITLE_THEME_STYLE_NAME,
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;
if (right_justified) {
sort_indicator_x_offset = cell_rectangle.x + SORT_INDICATOR_X_OFFSET;
text_x_offset = sort_indicator_x_offset + CELL_TITLE_INSET
+ SORT_ORDER_INDICATOR_WIDTH ;
} else {
sort_indicator_x_offset = cell_rectangle.x + cell_rectangle.width
- SORT_INDICATOR_X_OFFSET - SORT_ORDER_INDICATOR_WIDTH;
text_x_available_end = sort_indicator_x_offset - CELL_TITLE_INSET;
}
y_offset = cell_rectangle.y + cell_rectangle.height / 2
- SORT_INDICATOR_Y_OFFSET;
/* 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, sort_indicator_x_offset, y_offset);
gdk_draw_pixmap (target_drawable, column_title->details->copy_area_gc,
sort_indicator->pixmap, 0, 0, sort_indicator_x_offset, y_offset,
-1, -1);
}
if (cell_label) {
char *truncated_label;
int truncanted_width;
/* Extend the redraw area vertically to contain the entire cell
* -- seems like if I don't do this, for short exposed areas no text
* will get drawn.
* This happens when the title is half off-screen and you move it up by a pixel or two.
* If you move it up faster, it gets redrawn properly.
*/
cell_redraw_area.y = cell_rectangle.y;
cell_redraw_area.height = cell_rectangle.height;
/* Clip a little more than the cell rectangle to
* not have the text draw over the cell broder.
*/
nautilus_rectangle_inset (&cell_redraw_area, 2, 2);
truncated_label = truncate_string (cell_label, widget->style->font,
text_x_available_end - text_x_offset, &truncanted_width);
if (right_justified) {
text_x_offset = text_x_available_end - truncanted_width;
}
gtk_paint_string (widget->style, target_drawable, GTK_STATE_NORMAL,
&cell_redraw_area, draw_target, "label",
text_x_offset,
cell_rectangle.y + cell_rectangle.height - TITLE_BASELINE_OFFSET,
truncated_label);
g_free (truncated_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, widget, widget->window, area);
}
static void
nautilus_list_column_title_buffered_draw (GtkWidget *widget)
{
/* draw using an offscreen_widget bitmap */
GdkRectangle redraw_area;
NautilusListColumnTitle *column_title;
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
redraw_area.x = widget->allocation.x;
redraw_area.y = widget->allocation.y;
redraw_area.width = widget->allocation.width;
redraw_area.height = widget->allocation.height;
if (column_title->details->offscreen_widget != NULL
&& (column_title->details->offscreen_widget->allocation.x < redraw_area.x
|| column_title->details->offscreen_widget->allocation.y < redraw_area.y
|| column_title->details->offscreen_widget->allocation.width > redraw_area.width
|| column_title->details->offscreen_widget->allocation.height > redraw_area.height)) {
/* existing offscreen_widget not large enough, need to
* allocate a new one, get rid of the old one first
*/
gdk_pixmap_unref (column_title->details->offscreen_pixmap);
gtk_widget_unref (column_title->details->offscreen_widget);
column_title->details->offscreen_widget = NULL;
}
if (column_title->details->offscreen_widget == NULL) {
/* allocate a new offscreen_widget */
column_title->details->offscreen_pixmap = gdk_pixmap_new (widget->window,
redraw_area.width,
redraw_area.height, -1);
column_title->details->offscreen_widget =
gtk_type_new (gtk_widget_get_type ());
gdk_window_set_user_data (column_title->details->offscreen_pixmap,
column_title->details->offscreen_widget);
gtk_widget_show (column_title->details->offscreen_widget);
}
/* Erase the offscreen background.
* We are using the GtkStyle call to draw the background - this is a tiny bit
* less efficient but gives us the convenience of setting up the right colors and
* gc for the style we are using to blit the column titles.
*/
gtk_paint_box (widget->style, column_title->details->offscreen_pixmap,
GTK_STATE_NORMAL, GTK_SHADOW_OUT,
&redraw_area, column_title->details->offscreen_widget,
COLUMN_TITLE_THEME_STYLE_NAME,
redraw_area.x, redraw_area.y,
redraw_area.width, redraw_area.height);
/* render the column titles into the offscreen */
nautilus_list_column_title_paint (widget, column_title->details->offscreen_widget,
column_title->details->offscreen_pixmap, &redraw_area);
if (column_title->details->offscreen_blitting_gc == NULL)
/* allocate a gc to blit the offscreen if needed */
column_title->details->offscreen_blitting_gc = gdk_gc_new (widget->window);
/* blit the offscreen into the real view */
gdk_draw_pixmap (widget->window, column_title->details->offscreen_blitting_gc,
column_title->details->offscreen_pixmap,
0, 0, 0, 0, -1, -1);
}
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, widget, widget->window, &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;
gboolean title_update_needed;
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
g_assert (NAUTILUS_IS_LIST (widget->parent));
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
parent_list = GTK_WIDGET (widget->parent);
title_update_needed = FALSE;
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;
NAUTILUS_INVOKE_METHOD
(NAUTILUS_LIST_CLASS, parent_list,
column_resize_track,
(parent_list, column_title->details->tracking_column_resize));
title_update_needed = TRUE;
}
} 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 */
title_update_needed |= track_prelight (widget, mouse_x, mouse_y);
if (title_update_needed) {
nautilus_list_column_title_buffered_draw (widget);
}
return TRUE;
}
static gboolean
nautilus_list_column_title_leave (GtkWidget *widget, GdkEventCrossing *event)
{
NautilusListColumnTitle *column_title;
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
g_assert (NAUTILUS_IS_LIST (widget->parent));
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
/* see if we need to update the prelight state of a column */
if (column_title->details->tracking_column_prelight != -1) {
column_title->details->tracking_column_prelight = -1;
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;
int grab_result;
g_assert (event != NULL);
g_assert (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
g_assert (NAUTILUS_IS_LIST (widget->parent));
g_assert (event->type != GDK_BUTTON_PRESS
|| 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;
/* 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 bugzilla.eazel.com 617:
* use a "resized" state here ?
*/
gtk_widget_set_state (widget, GTK_STATE_NORMAL);
/* start column resize tracking */
NAUTILUS_INVOKE_METHOD
(NAUTILUS_LIST_CLASS, 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);
/* grab the pointer events so that we get release events even when
* the mouse tracks out of the widget window
*/
grab_result = gdk_pointer_grab (widget->window, FALSE,
GDK_BUTTON_RELEASE_MASK,
NULL, NULL, event->time);
if (grab_result != 0) {
/* failed to grab the pointer, give up */
return FALSE;
}
nautilus_list_column_title_buffered_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 (NAUTILUS_IS_LIST_COLUMN_TITLE (widget));
g_assert (NAUTILUS_IS_LIST (widget->parent));
column_title = NAUTILUS_LIST_COLUMN_TITLE(widget);
parent_list = GTK_WIDGET (widget->parent);
/* let go of all the pointer events */
if ((column_title->details->tracking_column_resize != -1
|| column_title->details->tracking_column_press != -1)
&& gdk_pointer_is_grabbed ())
gdk_pointer_ungrab (event->time);
if (column_title->details->tracking_column_resize != -1) {
/* end column resize tracking */
NAUTILUS_INVOKE_METHOD
(NAUTILUS_LIST_CLASS, 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);
nautilus_list_column_title_buffered_draw (widget);
return FALSE;
}