gtkcmclist.c (210224B)
1 /* GTK - The GIMP Toolkit 2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald, 3 * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the 17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 * Boston, MA 02111-1307, USA. 19 */ 20 21 /* 22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS 23 * file for a list of people on the GTK+ Team. See the ChangeLog 24 * files for a list of changes. These files are distributed with 25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 26 */ 27 28 #include <stdlib.h> 29 #include <string.h> 30 31 #include <gtk/gtk.h> 32 #include "claws-marshal.h" 33 #include "gtkcmclist.h" 34 #include <gdk/gdkkeysyms.h> 35 #include "utils.h" 36 #include "gtkutils.h" 37 38 /* length of button_actions array */ 39 #define MAX_BUTTON 5 40 41 /* the number rows memchunk expands at a time */ 42 #define CMCLIST_OPTIMUM_SIZE 64 43 44 /* the width of the column resize windows */ 45 #define DRAG_WIDTH 6 46 47 /* minimum allowed width of a column */ 48 #define COLUMN_MIN_WIDTH 5 49 50 /* this defigns the base grid spacing */ 51 #define CELL_SPACING 1 52 53 /* added the horizontal space at the beginning and end of a row*/ 54 #define COLUMN_INSET 3 55 56 /* used for auto-scrolling */ 57 #define SCROLL_TIME 100 58 59 /* gives the top pixel of the given row in context of 60 * the clist's voffset */ 61 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \ 62 (((row) + 1) * CELL_SPACING) + \ 63 (clist)->voffset) 64 65 /* returns the row index from a y pixel location in the 66 * context of the clist's voffset */ 67 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \ 68 ((clist)->row_height + CELL_SPACING)) 69 70 /* gives the left pixel of the given column in context of 71 * the clist's hoffset */ 72 #define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \ 73 (clist)->hoffset) 74 75 static void gtk_cmclist_scrollable_init (GtkScrollableInterface *iface); 76 77 /* returns the column index from a x pixel location in the 78 * context of the clist's hoffset */ 79 static inline gint 80 COLUMN_FROM_XPIXEL (GtkCMCList * clist, 81 gint x) 82 { 83 gint i, cx; 84 85 for (i = 0; i < clist->columns; i++) 86 if (clist->column[i].visible) 87 { 88 cx = clist->column[i].area.x + clist->hoffset; 89 90 if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) && 91 x <= (cx + clist->column[i].area.width + COLUMN_INSET)) 92 return i; 93 } 94 95 /* no match */ 96 return -1; 97 } 98 99 /* returns the top pixel of the given row in the context of 100 * the list height */ 101 #define ROW_TOP(clist, row) (((clist)->row_height + CELL_SPACING) * (row)) 102 103 /* returns the left pixel of the given column in the context of 104 * the list width */ 105 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x) 106 107 /* returns the total height of the list */ 108 #define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \ 109 (CELL_SPACING * ((clist)->rows + 1))) 110 111 112 /* returns the total width of the list */ 113 static inline gint 114 LIST_WIDTH (GtkCMCList * clist) 115 { 116 gint last_column; 117 118 for (last_column = clist->columns - 1; 119 last_column >= 0 && !clist->column[last_column].visible; last_column--); 120 121 if (last_column >= 0) 122 return (clist->column[last_column].area.x + 123 clist->column[last_column].area.width + 124 COLUMN_INSET + CELL_SPACING); 125 return 0; 126 } 127 128 /* returns the GList item for the nth row */ 129 #define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \ 130 (clist)->row_list_end : \ 131 g_list_nth ((clist)->row_list, (row))) 132 133 134 /* redraw the list if it's not frozen */ 135 #define CLIST_UNFROZEN(clist) (((GtkCMCList*) (clist))->freeze_count == 0) 136 #define CLIST_REFRESH(clist) G_STMT_START { \ 137 if (CLIST_UNFROZEN (clist)) \ 138 GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \ 139 } G_STMT_END 140 141 142 /* Signals */ 143 enum { 144 SELECT_ROW, 145 UNSELECT_ROW, 146 ROW_MOVE, 147 CLICK_COLUMN, 148 RESIZE_COLUMN, 149 TOGGLE_FOCUS_ROW, 150 SELECT_ALL, 151 UNSELECT_ALL, 152 UNDO_SELECTION, 153 START_SELECTION, 154 END_SELECTION, 155 TOGGLE_ADD_MODE, 156 EXTEND_SELECTION, 157 SCROLL_VERTICAL, 158 SCROLL_HORIZONTAL, 159 ABORT_COLUMN_RESIZE, 160 LAST_SIGNAL 161 }; 162 163 enum { 164 SYNC_REMOVE, 165 SYNC_INSERT 166 }; 167 168 enum { 169 ARG_0, 170 ARG_N_COLUMNS, 171 ARG_SHADOW_TYPE, 172 ARG_SELECTION_MODE, 173 ARG_ROW_HEIGHT, 174 ARG_TITLES_ACTIVE, 175 ARG_REORDERABLE, 176 ARG_USE_DRAG_ICONS, 177 ARG_SORT_TYPE, 178 ARG_HADJUSTMENT, 179 ARG_VADJUSTMENT, 180 ARG_HSCROLL_POLICY, 181 ARG_VSCROLL_POLICY 182 }; 183 184 /* GtkCMCList Methods */ 185 static void gtk_cmclist_class_init (GtkCMCListClass *klass); 186 static void gtk_cmclist_init (GtkCMCList *clist); 187 static GObject* gtk_cmclist_constructor (GType type, 188 guint n_construct_properties, 189 GObjectConstructParam *construct_params); 190 191 /* GtkObject Methods */ 192 static void gtk_cmclist_destroy (GtkWidget *object); 193 static void gtk_cmclist_finalize (GObject *object); 194 static void gtk_cmclist_set_arg (GObject *object, 195 guint arg_id, 196 const GValue *value, 197 GParamSpec *spec); 198 static void gtk_cmclist_get_arg (GObject *object, 199 guint arg_id, 200 GValue *value, 201 GParamSpec *spec); 202 203 /* GtkWidget Methods */ 204 static void gtk_cmclist_realize (GtkWidget *widget); 205 static void gtk_cmclist_unrealize (GtkWidget *widget); 206 static void gtk_cmclist_map (GtkWidget *widget); 207 static void gtk_cmclist_unmap (GtkWidget *widget); 208 static gint gtk_cmclist_draw (GtkWidget *widget, 209 cairo_t *event); 210 static gint gtk_cmclist_button_press (GtkWidget *widget, 211 GdkEventButton *event); 212 static gint gtk_cmclist_button_release (GtkWidget *widget, 213 GdkEventButton *event); 214 static gint gtk_cmclist_motion (GtkWidget *widget, 215 GdkEventMotion *event); 216 static void gtk_cmclist_get_preferred_height (GtkWidget *widget, 217 gint *minimal_height, 218 gint *natural_height); 219 static void gtk_cmclist_get_preferred_width (GtkWidget *widget, 220 gint *minimal_width, 221 gint *natural_width); 222 static void gtk_cmclist_size_request (GtkWidget *widget, 223 GtkRequisition *requisition); 224 static void gtk_cmclist_size_allocate (GtkWidget *widget, 225 GtkAllocation *allocation); 226 static void gtk_cmclist_undraw_focus (GtkWidget *widget); 227 static void gtk_cmclist_draw_focus (GtkWidget *widget); 228 static gint gtk_cmclist_focus_in (GtkWidget *widget, 229 GdkEventFocus *event); 230 static gint gtk_cmclist_focus_out (GtkWidget *widget, 231 GdkEventFocus *event); 232 static gint gtk_cmclist_focus (GtkWidget *widget, 233 GtkDirectionType direction); 234 static void gtk_cmclist_set_focus_child (GtkContainer *container, 235 GtkWidget *child); 236 static void gtk_cmclist_style_set (GtkWidget *widget, 237 GtkStyle *previous_style); 238 static void gtk_cmclist_drag_begin (GtkWidget *widget, 239 GdkDragContext *context); 240 static gint gtk_cmclist_drag_motion (GtkWidget *widget, 241 GdkDragContext *context, 242 gint x, 243 gint y, 244 guint time); 245 static void gtk_cmclist_drag_leave (GtkWidget *widget, 246 GdkDragContext *context, 247 guint time); 248 static void gtk_cmclist_drag_end (GtkWidget *widget, 249 GdkDragContext *context); 250 static gboolean gtk_cmclist_drag_drop (GtkWidget *widget, 251 GdkDragContext *context, 252 gint x, 253 gint y, 254 guint time); 255 static void gtk_cmclist_drag_data_get (GtkWidget *widget, 256 GdkDragContext *context, 257 GtkSelectionData *selection_data, 258 guint info, 259 guint time); 260 static void gtk_cmclist_drag_data_received (GtkWidget *widget, 261 GdkDragContext *context, 262 gint x, 263 gint y, 264 GtkSelectionData *selection_data, 265 guint info, 266 guint time); 267 268 /* GtkContainer Methods */ 269 static void gtk_cmclist_forall (GtkContainer *container, 270 gboolean include_internals, 271 GtkCallback callback, 272 gpointer callback_data); 273 274 /* Selection */ 275 static void toggle_row (GtkCMCList *clist, 276 gint row, 277 gint column, 278 GdkEvent *event); 279 static void real_select_row (GtkCMCList *clist, 280 gint row, 281 gint column, 282 GdkEvent *event); 283 static void real_unselect_row (GtkCMCList *clist, 284 gint row, 285 gint column, 286 GdkEvent *event); 287 static void update_extended_selection (GtkCMCList *clist, 288 gint row); 289 static GList *selection_find (GtkCMCList *clist, 290 gint row_number, 291 GList *row_list_element); 292 static void real_select_all (GtkCMCList *clist); 293 static void real_unselect_all (GtkCMCList *clist); 294 static void move_vertical (GtkCMCList *clist, 295 gint row, 296 gfloat align); 297 static void move_horizontal (GtkCMCList *clist, 298 gint diff); 299 static void real_undo_selection (GtkCMCList *clist); 300 static void fake_unselect_all (GtkCMCList *clist, 301 gint row); 302 static void fake_toggle_row (GtkCMCList *clist, 303 gint row); 304 static void resync_selection (GtkCMCList *clist, 305 GdkEvent *event); 306 static void sync_selection (GtkCMCList *clist, 307 gint row, 308 gint mode); 309 static void set_anchor (GtkCMCList *clist, 310 gboolean add_mode, 311 gint anchor, 312 gint undo_anchor); 313 static void start_selection (GtkCMCList *clist); 314 static void end_selection (GtkCMCList *clist); 315 static void toggle_add_mode (GtkCMCList *clist); 316 static void toggle_focus_row (GtkCMCList *clist); 317 static void extend_selection (GtkCMCList *clist, 318 GtkScrollType scroll_type, 319 gfloat position, 320 gboolean auto_start_selection); 321 static gint get_selection_info (GtkCMCList *clist, 322 gint x, 323 gint y, 324 gint *row, 325 gint *column); 326 327 /* Scrolling */ 328 static void move_focus_row (GtkCMCList *clist, 329 GtkScrollType scroll_type, 330 gfloat position); 331 static void scroll_horizontal (GtkCMCList *clist, 332 GtkScrollType scroll_type, 333 gfloat position); 334 static void scroll_vertical (GtkCMCList *clist, 335 GtkScrollType scroll_type, 336 gfloat position); 337 static void move_horizontal (GtkCMCList *clist, 338 gint diff); 339 static void move_vertical (GtkCMCList *clist, 340 gint row, 341 gfloat align); 342 static gint horizontal_timeout (GtkCMCList *clist); 343 static gint vertical_timeout (GtkCMCList *clist); 344 static void remove_grab (GtkCMCList *clist); 345 346 347 /* Resize Columns */ 348 static void draw_xor_line (GtkCMCList *clist); 349 static gint new_column_width (GtkCMCList *clist, 350 gint column, 351 gint *x); 352 static void column_auto_resize (GtkCMCList *clist, 353 GtkCMCListRow *clist_row, 354 gint column, 355 gint old_width); 356 static void real_resize_column (GtkCMCList *clist, 357 gint column, 358 gint width); 359 static void abort_column_resize (GtkCMCList *clist); 360 static void cell_size_request (GtkCMCList *clist, 361 GtkCMCListRow *clist_row, 362 gint column, 363 GtkRequisition *requisition); 364 365 /* Buttons */ 366 static void column_button_create (GtkCMCList *clist, 367 gint column); 368 static void column_button_clicked (GtkWidget *widget, 369 gpointer data); 370 371 /* Adjustments */ 372 static void adjust_adjustments (GtkCMCList *clist, 373 gboolean block_resize); 374 static void vadjustment_value_changed (GtkAdjustment *adjustment, 375 gpointer data); 376 static void hadjustment_value_changed (GtkAdjustment *adjustment, 377 gpointer data); 378 379 /* Drawing */ 380 static void get_cell_style (GtkCMCList *clist, 381 GtkCMCListRow *clist_row, 382 gint state, 383 gint column, 384 GtkStyle **style); 385 static gint draw_cell_pixbuf (GdkWindow *window, 386 GdkRectangle *clip_rectangle, 387 cairo_t *cr, 388 GdkPixbuf *pixbuf, 389 gint x, 390 gint y, 391 gint width, 392 gint height); 393 static void draw_row (GtkCMCList *clist, 394 GdkRectangle *area, 395 gint row, 396 GtkCMCListRow *clist_row); 397 static void draw_rows (GtkCMCList *clist, 398 GdkRectangle *area); 399 static void clist_refresh (GtkCMCList *clist); 400 401 /* Size Allocation / Requisition */ 402 static void size_allocate_title_buttons (GtkCMCList *clist); 403 static void size_allocate_columns (GtkCMCList *clist, 404 gboolean block_resize); 405 static gint list_requisition_width (GtkCMCList *clist); 406 407 /* Memory Allocation/Distruction Routines */ 408 static GtkCMCListColumn *columns_new (GtkCMCList *clist); 409 static void column_title_new (GtkCMCList *clist, 410 gint column, 411 const gchar *title); 412 static void columns_delete (GtkCMCList *clist); 413 static GtkCMCListRow *row_new (GtkCMCList *clist); 414 static void row_delete (GtkCMCList *clist, 415 GtkCMCListRow *clist_row); 416 static void set_cell_contents (GtkCMCList *clist, 417 GtkCMCListRow *clist_row, 418 gint column, 419 GtkCMCellType type, 420 const gchar *text, 421 guint8 spacing, 422 GdkPixbuf *pixbuf); 423 static gint real_insert_row (GtkCMCList *clist, 424 gint row, 425 gchar *text[]); 426 static void real_remove_row (GtkCMCList *clist, 427 gint row); 428 static void real_clear (GtkCMCList *clist); 429 430 /* Sorting */ 431 static gint default_compare (GtkCMCList *clist, 432 gconstpointer row1, 433 gconstpointer row2); 434 static void real_sort_list (GtkCMCList *clist); 435 static GList *gtk_cmclist_merge (GtkCMCList *clist, 436 GList *a, 437 GList *b); 438 static GList *gtk_cmclist_mergesort (GtkCMCList *clist, 439 GList *list, 440 gint num); 441 /* Misc */ 442 static gboolean title_focus_in (GtkCMCList *clist, 443 gint dir); 444 static gboolean title_focus_move (GtkCMCList *clist, 445 gint dir); 446 447 static void real_row_move (GtkCMCList *clist, 448 gint source_row, 449 gint dest_row); 450 static gint column_title_passive_func (GtkWidget *widget, 451 GdkEvent *event, 452 gpointer data); 453 static void drag_dest_cell (GtkCMCList *clist, 454 gint x, 455 gint y, 456 GtkCMCListDestInfo *dest_info); 457 458 459 460 static guint clist_signals[LAST_SIGNAL] = {0}; 461 462 static const GtkTargetEntry clist_target_table = { "gtk-clist-drag-reorder", 0, 0}; 463 464 G_DEFINE_TYPE_WITH_CODE (GtkCMCList, gtk_cmclist, GTK_TYPE_CONTAINER, 465 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, 466 gtk_cmclist_scrollable_init)) 467 468 static void 469 gtk_cmclist_class_init (GtkCMCListClass *klass) 470 { 471 GObjectClass *object_class = G_OBJECT_CLASS (klass); 472 473 GtkWidgetClass *widget_class; 474 GtkContainerClass *container_class; 475 GtkBindingSet *binding_set; 476 477 object_class->constructor = gtk_cmclist_constructor; 478 479 widget_class = (GtkWidgetClass *) klass; 480 container_class = (GtkContainerClass *) klass; 481 482 object_class->finalize = gtk_cmclist_finalize; 483 widget_class->destroy = gtk_cmclist_destroy; 484 object_class->set_property = gtk_cmclist_set_arg; 485 object_class->get_property = gtk_cmclist_get_arg; 486 487 488 widget_class->realize = gtk_cmclist_realize; 489 widget_class->unrealize = gtk_cmclist_unrealize; 490 widget_class->map = gtk_cmclist_map; 491 widget_class->unmap = gtk_cmclist_unmap; 492 widget_class->button_press_event = gtk_cmclist_button_press; 493 widget_class->button_release_event = gtk_cmclist_button_release; 494 widget_class->motion_notify_event = gtk_cmclist_motion; 495 widget_class->draw = gtk_cmclist_draw; 496 497 widget_class->get_preferred_width = gtk_cmclist_get_preferred_width; 498 widget_class->get_preferred_height = gtk_cmclist_get_preferred_height; 499 widget_class->size_allocate = gtk_cmclist_size_allocate; 500 widget_class->focus_in_event = gtk_cmclist_focus_in; 501 widget_class->focus_out_event = gtk_cmclist_focus_out; 502 widget_class->style_set = gtk_cmclist_style_set; 503 widget_class->drag_begin = gtk_cmclist_drag_begin; 504 widget_class->drag_end = gtk_cmclist_drag_end; 505 widget_class->drag_motion = gtk_cmclist_drag_motion; 506 widget_class->drag_leave = gtk_cmclist_drag_leave; 507 widget_class->drag_drop = gtk_cmclist_drag_drop; 508 widget_class->drag_data_get = gtk_cmclist_drag_data_get; 509 widget_class->drag_data_received = gtk_cmclist_drag_data_received; 510 widget_class->focus = gtk_cmclist_focus; 511 512 /* container_class->add = NULL; use the default GtkContainerClass warning */ 513 /* container_class->remove=NULL; use the default GtkContainerClass warning */ 514 515 container_class->forall = gtk_cmclist_forall; 516 container_class->set_focus_child = gtk_cmclist_set_focus_child; 517 518 klass->refresh = clist_refresh; 519 klass->select_row = real_select_row; 520 klass->unselect_row = real_unselect_row; 521 klass->row_move = real_row_move; 522 klass->undo_selection = real_undo_selection; 523 klass->resync_selection = resync_selection; 524 klass->selection_find = selection_find; 525 klass->click_column = NULL; 526 klass->resize_column = real_resize_column; 527 klass->draw_row = draw_row; 528 klass->insert_row = real_insert_row; 529 klass->remove_row = real_remove_row; 530 klass->clear = real_clear; 531 klass->sort_list = real_sort_list; 532 klass->select_all = real_select_all; 533 klass->unselect_all = real_unselect_all; 534 klass->fake_unselect_all = fake_unselect_all; 535 klass->scroll_horizontal = scroll_horizontal; 536 klass->scroll_vertical = scroll_vertical; 537 klass->extend_selection = extend_selection; 538 klass->toggle_focus_row = toggle_focus_row; 539 klass->toggle_add_mode = toggle_add_mode; 540 klass->start_selection = start_selection; 541 klass->end_selection = end_selection; 542 klass->abort_column_resize = abort_column_resize; 543 klass->set_cell_contents = set_cell_contents; 544 klass->cell_size_request = cell_size_request; 545 546 g_object_class_install_property (object_class, 547 ARG_N_COLUMNS, 548 g_param_spec_uint ("n-columns", 549 "N-Columns", 550 "N-Columns", 551 1, 552 G_MAXINT, 553 1, 554 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); 555 g_object_class_install_property (object_class, 556 ARG_SHADOW_TYPE, 557 g_param_spec_enum ("shadow-type", 558 "shadow-type", 559 "shadow-type", 560 GTK_TYPE_SHADOW_TYPE, 0, 561 G_PARAM_READWRITE)); 562 g_object_class_install_property (object_class, 563 ARG_SELECTION_MODE, 564 g_param_spec_enum ("selection-mode", 565 "selection-mode", 566 "selection-mode", 567 GTK_TYPE_SELECTION_MODE, 0, 568 G_PARAM_READWRITE)); 569 g_object_class_install_property (object_class, 570 ARG_ROW_HEIGHT, 571 g_param_spec_uint ("row-height", 572 "row-height", 573 "row-height", 574 0, 575 G_MAXINT, 576 0, 577 G_PARAM_READWRITE)); 578 g_object_class_install_property (object_class, 579 ARG_REORDERABLE, 580 g_param_spec_boolean ("reorderable", 581 "reorderable", 582 "reorderable", 583 TRUE, 584 G_PARAM_READWRITE)); 585 g_object_class_install_property (object_class, 586 ARG_TITLES_ACTIVE, 587 g_param_spec_boolean ("titles-active", 588 "titles-active", 589 "titles-active", 590 TRUE, 591 G_PARAM_READWRITE)); 592 g_object_class_install_property (object_class, 593 ARG_USE_DRAG_ICONS, 594 g_param_spec_boolean ("use-drag-icons", 595 "use-drag-icons", 596 "use-drag-icons", 597 TRUE, 598 G_PARAM_READWRITE)); 599 g_object_class_install_property (object_class, 600 ARG_SORT_TYPE, 601 g_param_spec_enum ("sort-type", 602 "sort-type", 603 "sort-type", 604 GTK_TYPE_SORT_TYPE, 0, 605 G_PARAM_READWRITE)); 606 /* Scrollable interface properties */ 607 g_object_class_override_property (object_class, ARG_HADJUSTMENT, "hadjustment"); 608 g_object_class_override_property (object_class, ARG_VADJUSTMENT, "vadjustment"); 609 g_object_class_override_property (object_class, ARG_HSCROLL_POLICY, "hscroll-policy"); 610 g_object_class_override_property (object_class, ARG_VSCROLL_POLICY, "vscroll-policy"); 611 612 clist_signals[SELECT_ROW] = 613 g_signal_new ("select_row", 614 G_TYPE_FROM_CLASS (object_class), 615 G_SIGNAL_RUN_FIRST, 616 G_STRUCT_OFFSET (GtkCMCListClass, select_row), 617 NULL, NULL, 618 claws_marshal_VOID__INT_INT_BOXED, 619 G_TYPE_NONE, 3, 620 G_TYPE_INT, 621 G_TYPE_INT, 622 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); 623 clist_signals[UNSELECT_ROW] = 624 g_signal_new ("unselect_row", 625 G_TYPE_FROM_CLASS (object_class), 626 G_SIGNAL_RUN_FIRST, 627 G_STRUCT_OFFSET (GtkCMCListClass, unselect_row), 628 NULL, NULL, 629 claws_marshal_VOID__INT_INT_BOXED, 630 G_TYPE_NONE, 3, 631 G_TYPE_INT, 632 G_TYPE_INT, 633 GDK_TYPE_EVENT); 634 clist_signals[ROW_MOVE] = 635 g_signal_new ("row_move", 636 G_TYPE_FROM_CLASS (object_class), 637 G_SIGNAL_RUN_LAST, 638 G_STRUCT_OFFSET (GtkCMCListClass, row_move), 639 NULL, NULL, 640 claws_marshal_VOID__INT_INT, 641 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); 642 clist_signals[CLICK_COLUMN] = 643 g_signal_new ("click_column", 644 G_TYPE_FROM_CLASS (object_class), 645 G_SIGNAL_RUN_FIRST, 646 G_STRUCT_OFFSET (GtkCMCListClass, click_column), 647 NULL, NULL, 648 claws_marshal_VOID__INT, 649 G_TYPE_NONE, 1, G_TYPE_INT); 650 clist_signals[RESIZE_COLUMN] = 651 g_signal_new ("resize_column", 652 G_TYPE_FROM_CLASS (object_class), 653 G_SIGNAL_RUN_LAST, 654 G_STRUCT_OFFSET (GtkCMCListClass, resize_column), 655 NULL, NULL, 656 claws_marshal_VOID__INT_INT, 657 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); 658 659 clist_signals[TOGGLE_FOCUS_ROW] = 660 g_signal_new ("toggle_focus_row", 661 G_TYPE_FROM_CLASS (object_class), 662 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 663 G_STRUCT_OFFSET (GtkCMCListClass, toggle_focus_row), 664 NULL, NULL, 665 claws_marshal_VOID__VOID, 666 G_TYPE_NONE, 0); 667 clist_signals[SELECT_ALL] = 668 g_signal_new ("select_all", 669 G_TYPE_FROM_CLASS (object_class), 670 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 671 G_STRUCT_OFFSET (GtkCMCListClass, select_all), 672 NULL, NULL, 673 claws_marshal_VOID__VOID, 674 G_TYPE_NONE, 0); 675 clist_signals[UNSELECT_ALL] = 676 g_signal_new ("unselect_all", 677 G_TYPE_FROM_CLASS (object_class), 678 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 679 G_STRUCT_OFFSET (GtkCMCListClass, unselect_all), 680 NULL, NULL, 681 claws_marshal_VOID__VOID, 682 G_TYPE_NONE, 0); 683 clist_signals[UNDO_SELECTION] = 684 g_signal_new ("undo_selection", 685 G_TYPE_FROM_CLASS (object_class), 686 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 687 G_STRUCT_OFFSET (GtkCMCListClass, undo_selection), 688 NULL, NULL, 689 claws_marshal_VOID__VOID, 690 G_TYPE_NONE, 0); 691 clist_signals[START_SELECTION] = 692 g_signal_new ("start_selection", 693 G_TYPE_FROM_CLASS (object_class), 694 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 695 G_STRUCT_OFFSET (GtkCMCListClass, start_selection), 696 NULL, NULL, 697 claws_marshal_VOID__VOID, 698 G_TYPE_NONE, 0); 699 clist_signals[END_SELECTION] = 700 g_signal_new ("end_selection", 701 G_TYPE_FROM_CLASS (object_class), 702 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 703 G_STRUCT_OFFSET (GtkCMCListClass, end_selection), 704 NULL, NULL, 705 claws_marshal_VOID__VOID, 706 G_TYPE_NONE, 0); 707 clist_signals[TOGGLE_ADD_MODE] = 708 g_signal_new ("toggle_add_mode", 709 G_TYPE_FROM_CLASS (object_class), 710 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 711 G_STRUCT_OFFSET (GtkCMCListClass, toggle_add_mode), 712 NULL, NULL, 713 claws_marshal_VOID__VOID, 714 G_TYPE_NONE, 0); 715 clist_signals[EXTEND_SELECTION] = 716 g_signal_new ("extend_selection", 717 G_TYPE_FROM_CLASS (object_class), 718 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 719 G_STRUCT_OFFSET (GtkCMCListClass, extend_selection), 720 NULL, NULL, 721 claws_marshal_VOID__ENUM_FLOAT_BOOLEAN, 722 G_TYPE_NONE, 3, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT, G_TYPE_BOOLEAN); 723 clist_signals[SCROLL_VERTICAL] = 724 g_signal_new ("scroll_vertical", 725 G_TYPE_FROM_CLASS (object_class), 726 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 727 G_STRUCT_OFFSET (GtkCMCListClass, scroll_vertical), 728 NULL, NULL, 729 claws_marshal_VOID__ENUM_FLOAT, 730 G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT); 731 clist_signals[SCROLL_HORIZONTAL] = 732 g_signal_new ("scroll_horizontal", 733 G_TYPE_FROM_CLASS (object_class), 734 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 735 G_STRUCT_OFFSET (GtkCMCListClass, scroll_horizontal), 736 NULL, NULL, 737 claws_marshal_VOID__ENUM_FLOAT, 738 G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT); 739 clist_signals[ABORT_COLUMN_RESIZE] = 740 g_signal_new ("abort_column_resize", 741 G_TYPE_FROM_CLASS (object_class), 742 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 743 G_STRUCT_OFFSET (GtkCMCListClass, abort_column_resize), 744 NULL, NULL, 745 claws_marshal_VOID__VOID, 746 G_TYPE_NONE, 0); 747 748 binding_set = gtk_binding_set_by_class (klass); 749 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0, 750 "scroll_vertical", 2, 751 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, 752 G_TYPE_FLOAT, 0.0); 753 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0, 754 "scroll_vertical", 2, 755 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, 756 G_TYPE_FLOAT, 0.0); 757 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0, 758 "scroll_vertical", 2, 759 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, 760 G_TYPE_FLOAT, 0.0); 761 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0, 762 "scroll_vertical", 2, 763 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, 764 G_TYPE_FLOAT, 0.0); 765 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0, 766 "scroll_vertical", 2, 767 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD, 768 G_TYPE_FLOAT, 0.0); 769 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0, 770 "scroll_vertical", 2, 771 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD, 772 G_TYPE_FLOAT, 0.0); 773 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0, 774 "scroll_vertical", 2, 775 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD, 776 G_TYPE_FLOAT, 0.0); 777 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0, 778 "scroll_vertical", 2, 779 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD, 780 G_TYPE_FLOAT, 0.0); 781 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, 782 "scroll_vertical", 2, 783 G_TYPE_ENUM, GTK_SCROLL_JUMP, 784 G_TYPE_FLOAT, 0.0); 785 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK, 786 "scroll_vertical", 2, 787 G_TYPE_ENUM, GTK_SCROLL_JUMP, 788 G_TYPE_FLOAT, 0.0); 789 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, 790 "scroll_vertical", 2, 791 G_TYPE_ENUM, GTK_SCROLL_JUMP, 792 G_TYPE_FLOAT, 1.0); 793 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK, 794 "scroll_vertical", 2, 795 G_TYPE_ENUM, GTK_SCROLL_JUMP, 796 G_TYPE_FLOAT, 1.0); 797 798 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_SHIFT_MASK, 799 "extend_selection", 3, 800 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, 801 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 802 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_SHIFT_MASK, 803 "extend_selection", 3, 804 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, 805 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 806 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_SHIFT_MASK, 807 "extend_selection", 3, 808 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, 809 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 810 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_SHIFT_MASK, 811 "extend_selection", 3, 812 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, 813 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 814 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, GDK_SHIFT_MASK, 815 "extend_selection", 3, 816 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD, 817 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 818 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, GDK_SHIFT_MASK, 819 "extend_selection", 3, 820 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD, 821 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 822 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, GDK_SHIFT_MASK, 823 "extend_selection", 3, 824 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD, 825 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 826 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, GDK_SHIFT_MASK, 827 "extend_selection", 3, 828 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD, 829 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 830 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 831 GDK_SHIFT_MASK | GDK_CONTROL_MASK, 832 "extend_selection", 3, 833 G_TYPE_ENUM, GTK_SCROLL_JUMP, 834 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 835 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 836 GDK_SHIFT_MASK | GDK_CONTROL_MASK, 837 "extend_selection", 3, 838 G_TYPE_ENUM, GTK_SCROLL_JUMP, 839 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE); 840 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 841 GDK_SHIFT_MASK | GDK_CONTROL_MASK, 842 "extend_selection", 3, 843 G_TYPE_ENUM, GTK_SCROLL_JUMP, 844 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE); 845 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 846 GDK_SHIFT_MASK | GDK_CONTROL_MASK, 847 "extend_selection", 3, 848 G_TYPE_ENUM, GTK_SCROLL_JUMP, 849 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE); 850 851 852 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0, 853 "scroll_horizontal", 2, 854 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, 855 G_TYPE_FLOAT, 0.0); 856 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0, 857 "scroll_horizontal", 2, 858 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD, 859 G_TYPE_FLOAT, 0.0); 860 861 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0, 862 "scroll_horizontal", 2, 863 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, 864 G_TYPE_FLOAT, 0.0); 865 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0, 866 "scroll_horizontal", 2, 867 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD, 868 G_TYPE_FLOAT, 0.0); 869 870 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0, 871 "scroll_horizontal", 2, 872 G_TYPE_ENUM, GTK_SCROLL_JUMP, 873 G_TYPE_FLOAT, 0.0); 874 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0, 875 "scroll_horizontal", 2, 876 G_TYPE_ENUM, GTK_SCROLL_JUMP, 877 G_TYPE_FLOAT, 0.0); 878 879 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0, 880 "scroll_horizontal", 2, 881 G_TYPE_ENUM, GTK_SCROLL_JUMP, 882 G_TYPE_FLOAT, 1.0); 883 884 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0, 885 "scroll_horizontal", 2, 886 G_TYPE_ENUM, GTK_SCROLL_JUMP, 887 G_TYPE_FLOAT, 1.0); 888 889 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, 890 "undo_selection", 0); 891 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, 892 "abort_column_resize", 0); 893 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, 894 "toggle_focus_row", 0); 895 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, 896 "toggle_focus_row", 0); 897 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, 898 "toggle_add_mode", 0); 899 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, 900 "toggle_add_mode", 0); 901 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, 902 "select_all", 0); 903 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Divide, GDK_CONTROL_MASK, 904 "select_all", 0); 905 gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK, 906 "unselect_all", 0); 907 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L, 908 GDK_RELEASE_MASK | GDK_SHIFT_MASK, 909 "end_selection", 0); 910 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R, 911 GDK_RELEASE_MASK | GDK_SHIFT_MASK, 912 "end_selection", 0); 913 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L, 914 GDK_RELEASE_MASK | GDK_SHIFT_MASK | 915 GDK_CONTROL_MASK, 916 "end_selection", 0); 917 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R, 918 GDK_RELEASE_MASK | GDK_SHIFT_MASK | 919 GDK_CONTROL_MASK, 920 "end_selection", 0); 921 } 922 923 static void 924 gtk_cmclist_set_arg (GObject *object, 925 guint arg_id, 926 const GValue *value, 927 GParamSpec *spec) 928 { 929 GtkCMCList *clist; 930 931 clist = GTK_CMCLIST (object); 932 933 switch (arg_id) 934 { 935 case ARG_N_COLUMNS: /* only set at construction time */ 936 clist->columns = MAX (1, g_value_get_uint (value)); 937 break; 938 case ARG_SHADOW_TYPE: 939 gtk_cmclist_set_shadow_type (clist, g_value_get_enum (value)); 940 break; 941 case ARG_SELECTION_MODE: 942 gtk_cmclist_set_selection_mode (clist, g_value_get_enum (value)); 943 break; 944 case ARG_ROW_HEIGHT: 945 gtk_cmclist_set_row_height (clist, g_value_get_uint (value)); 946 break; 947 case ARG_REORDERABLE: 948 gtk_cmclist_set_reorderable (clist, g_value_get_boolean (value)); 949 break; 950 case ARG_TITLES_ACTIVE: 951 if (g_value_get_boolean (value)) 952 gtk_cmclist_column_titles_active (clist); 953 else 954 gtk_cmclist_column_titles_passive (clist); 955 break; 956 case ARG_USE_DRAG_ICONS: 957 gtk_cmclist_set_use_drag_icons (clist, g_value_get_boolean (value)); 958 break; 959 case ARG_SORT_TYPE: 960 gtk_cmclist_set_sort_type (clist, g_value_get_enum (value)); 961 break; 962 case ARG_HADJUSTMENT: 963 gtk_cmclist_set_hadjustment (clist, g_value_get_object (value)); 964 break; 965 case ARG_VADJUSTMENT: 966 gtk_cmclist_set_vadjustment (clist, g_value_get_object (value)); 967 break; 968 case ARG_HSCROLL_POLICY: 969 case ARG_VSCROLL_POLICY: 970 break; 971 } 972 } 973 974 static void 975 gtk_cmclist_get_arg (GObject *object, 976 guint arg_id, 977 GValue *value, 978 GParamSpec *spec) 979 { 980 GtkCMCList *clist; 981 982 clist = GTK_CMCLIST (object); 983 984 switch (arg_id) 985 { 986 guint i; 987 988 case ARG_N_COLUMNS: 989 g_value_set_uint(value, clist->columns); 990 break; 991 case ARG_SHADOW_TYPE: 992 g_value_set_enum(value, clist->shadow_type); 993 break; 994 case ARG_SELECTION_MODE: 995 g_value_set_enum(value, clist->selection_mode); 996 break; 997 case ARG_ROW_HEIGHT: 998 g_value_set_uint(value, GTK_CMCLIST_ROW_HEIGHT_SET(clist) ? clist->row_height : 0); 999 break; 1000 case ARG_REORDERABLE: 1001 g_value_set_boolean(value, GTK_CMCLIST_REORDERABLE (clist)); 1002 break; 1003 case ARG_TITLES_ACTIVE: 1004 g_value_set_boolean(value, TRUE); 1005 for (i = 0; i < clist->columns; i++) 1006 if (clist->column[i].button && 1007 !gtk_widget_get_sensitive (clist->column[i].button)) 1008 { 1009 g_value_set_boolean(value, FALSE); 1010 break; 1011 } 1012 break; 1013 case ARG_USE_DRAG_ICONS: 1014 g_value_set_boolean(value, GTK_CMCLIST_USE_DRAG_ICONS (clist)); 1015 break; 1016 case ARG_SORT_TYPE: 1017 g_value_set_enum(value, clist->sort_type); 1018 break; 1019 case ARG_HADJUSTMENT: 1020 g_value_set_object(value, gtk_cmclist_get_hadjustment(clist)); 1021 break; 1022 case ARG_VADJUSTMENT: 1023 g_value_set_object(value, gtk_cmclist_get_vadjustment(clist)); 1024 break; 1025 case ARG_HSCROLL_POLICY: 1026 case ARG_VSCROLL_POLICY: 1027 g_value_set_enum(value, GTK_SCROLL_NATURAL); 1028 break; 1029 default: 1030 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec); 1031 break; 1032 } 1033 } 1034 1035 static void 1036 gtk_cmclist_init (GtkCMCList *clist) 1037 { 1038 clist->flags = 0; 1039 1040 gtk_widget_set_has_window (GTK_WIDGET(clist), TRUE); 1041 gtk_widget_set_can_focus (GTK_WIDGET(clist), TRUE); 1042 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_DRAW_DRAG_LINE); 1043 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS); 1044 1045 clist->freeze_count = 0; 1046 1047 clist->rows = 0; 1048 clist->row_height = 0; 1049 clist->row_list = NULL; 1050 clist->row_list_end = NULL; 1051 1052 clist->columns = 0; 1053 1054 clist->title_window = NULL; 1055 clist->column_title_area.x = 0; 1056 clist->column_title_area.y = 0; 1057 clist->column_title_area.width = 1; 1058 clist->column_title_area.height = 1; 1059 1060 clist->clist_window = NULL; 1061 clist->clist_window_width = 1; 1062 clist->clist_window_height = 1; 1063 1064 clist->hoffset = 0; 1065 clist->voffset = 0; 1066 1067 clist->shadow_type = GTK_SHADOW_IN; 1068 clist->vadjustment = NULL; 1069 clist->hadjustment = NULL; 1070 1071 clist->button_actions[0] = GTK_CMBUTTON_SELECTS | GTK_CMBUTTON_DRAGS; 1072 clist->button_actions[1] = GTK_CMBUTTON_IGNORED; 1073 clist->button_actions[2] = GTK_CMBUTTON_IGNORED; 1074 clist->button_actions[3] = GTK_CMBUTTON_IGNORED; 1075 clist->button_actions[4] = GTK_CMBUTTON_IGNORED; 1076 1077 clist->cursor_drag = NULL; 1078 clist->x_drag = 0; 1079 1080 clist->selection_mode = GTK_SELECTION_SINGLE; 1081 clist->selection = NULL; 1082 clist->selection_end = NULL; 1083 clist->undo_selection = NULL; 1084 clist->undo_unselection = NULL; 1085 1086 clist->focus_row = -1; 1087 clist->focus_header_column = -1; 1088 clist->undo_anchor = -1; 1089 1090 clist->anchor = -1; 1091 clist->anchor_state = GTK_STATE_SELECTED; 1092 clist->drag_pos = -1; 1093 clist->htimer = 0; 1094 clist->vtimer = 0; 1095 1096 clist->click_cell.row = -1; 1097 clist->click_cell.column = -1; 1098 1099 clist->compare = default_compare; 1100 clist->sort_type = GTK_SORT_ASCENDING; 1101 clist->sort_column = 0; 1102 1103 clist->drag_highlight_row = -1; 1104 } 1105 1106 /* Constructor */ 1107 static GObject* 1108 gtk_cmclist_constructor (GType type, 1109 guint n_construct_properties, 1110 GObjectConstructParam *construct_properties) 1111 { 1112 GObject *object = G_OBJECT_CLASS (gtk_cmclist_parent_class)->constructor (type, 1113 n_construct_properties, 1114 construct_properties); 1115 GtkCMCList *clist = GTK_CMCLIST (object); 1116 1117 /* allocate memory for columns */ 1118 clist->column = columns_new (clist); 1119 1120 /* there needs to be at least one column button 1121 * because there is alot of code that will break if it 1122 * isn't there 1123 */ 1124 column_button_create (clist, 0); 1125 1126 clist->draw_now = 1; 1127 1128 return object; 1129 } 1130 1131 /* GTKCLIST PUBLIC INTERFACE 1132 * gtk_cmclist_new 1133 * gtk_cmclist_new_with_titles 1134 * gtk_cmclist_set_hadjustment 1135 * gtk_cmclist_set_vadjustment 1136 * gtk_cmclist_get_hadjustment 1137 * gtk_cmclist_get_vadjustment 1138 * gtk_cmclist_set_shadow_type 1139 * gtk_cmclist_set_selection_mode 1140 * gtk_cmclist_freeze 1141 * gtk_cmclist_thaw 1142 */ 1143 GtkWidget* 1144 gtk_cmclist_new (gint columns) 1145 { 1146 return gtk_cmclist_new_with_titles (columns, NULL); 1147 } 1148 1149 GtkWidget* 1150 gtk_cmclist_new_with_titles (gint columns, 1151 gchar *titles[]) 1152 { 1153 GtkCMCList *clist; 1154 1155 clist = g_object_new (GTK_TYPE_CMCLIST, 1156 "n_columns", columns, 1157 NULL); 1158 if (titles) 1159 { 1160 guint i; 1161 1162 for (i = 0; i < clist->columns; i++) 1163 gtk_cmclist_set_column_title (clist, i, titles[i]); 1164 gtk_cmclist_column_titles_show (clist); 1165 } 1166 else 1167 gtk_cmclist_column_titles_hide (clist); 1168 1169 return GTK_WIDGET (clist); 1170 } 1171 1172 void 1173 gtk_cmclist_set_hadjustment (GtkCMCList *clist, 1174 GtkAdjustment *adjustment) 1175 { 1176 GtkAdjustment *old_adjustment; 1177 1178 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1179 if (adjustment) 1180 cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); 1181 1182 if (clist->hadjustment == adjustment) 1183 return; 1184 1185 old_adjustment = clist->hadjustment; 1186 1187 if (clist->hadjustment) 1188 { 1189 g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA, 1190 0, 0, 0, 0, clist); 1191 1192 g_object_unref (G_OBJECT (clist->hadjustment)); 1193 } 1194 1195 clist->hadjustment = adjustment; 1196 1197 if (clist->hadjustment) 1198 { 1199 g_object_ref_sink (clist->hadjustment); 1200 g_signal_connect (G_OBJECT (clist->hadjustment), "value_changed", 1201 G_CALLBACK( hadjustment_value_changed), 1202 (gpointer) clist); 1203 } 1204 1205 if (!clist->hadjustment || !old_adjustment) 1206 gtk_widget_queue_resize (GTK_WIDGET (clist)); 1207 } 1208 1209 GtkAdjustment * 1210 gtk_cmclist_get_hadjustment (GtkCMCList *clist) 1211 { 1212 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 1213 1214 return clist->hadjustment; 1215 } 1216 1217 void 1218 gtk_cmclist_set_vadjustment (GtkCMCList *clist, 1219 GtkAdjustment *adjustment) 1220 { 1221 GtkAdjustment *old_adjustment; 1222 1223 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1224 if (adjustment) 1225 cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); 1226 1227 if (clist->vadjustment == adjustment) 1228 return; 1229 1230 old_adjustment = clist->vadjustment; 1231 1232 if (clist->vadjustment) 1233 { 1234 g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA, 1235 0, 0, 0, 0, clist); 1236 g_object_unref (G_OBJECT (clist->vadjustment)); 1237 } 1238 1239 clist->vadjustment = adjustment; 1240 1241 if (clist->vadjustment) 1242 { 1243 g_object_ref_sink (clist->vadjustment); 1244 1245 g_signal_connect (G_OBJECT (clist->vadjustment), "value_changed", 1246 G_CALLBACK(vadjustment_value_changed), 1247 (gpointer) clist); 1248 } 1249 1250 if (!clist->vadjustment || !old_adjustment) 1251 gtk_widget_queue_resize (GTK_WIDGET (clist)); 1252 } 1253 1254 GtkAdjustment * 1255 gtk_cmclist_get_vadjustment (GtkCMCList *clist) 1256 { 1257 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 1258 1259 return clist->vadjustment; 1260 } 1261 1262 void 1263 gtk_cmclist_set_shadow_type (GtkCMCList *clist, 1264 GtkShadowType type) 1265 { 1266 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1267 1268 clist->shadow_type = type; 1269 1270 if (gtk_widget_get_visible (GTK_WIDGET(clist))) 1271 gtk_widget_queue_resize (GTK_WIDGET (clist)); 1272 } 1273 1274 void 1275 gtk_cmclist_set_selection_mode (GtkCMCList *clist, 1276 GtkSelectionMode mode) 1277 { 1278 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1279 cm_return_if_fail (mode != GTK_SELECTION_NONE); 1280 1281 if (mode == clist->selection_mode) 1282 return; 1283 1284 clist->selection_mode = mode; 1285 clist->anchor = -1; 1286 clist->anchor_state = GTK_STATE_SELECTED; 1287 clist->drag_pos = -1; 1288 clist->undo_anchor = clist->focus_row; 1289 1290 g_list_free (clist->undo_selection); 1291 g_list_free (clist->undo_unselection); 1292 clist->undo_selection = NULL; 1293 clist->undo_unselection = NULL; 1294 1295 switch (mode) 1296 { 1297 case GTK_SELECTION_MULTIPLE: 1298 return; 1299 case GTK_SELECTION_BROWSE: 1300 case GTK_SELECTION_SINGLE: 1301 gtk_cmclist_unselect_all (clist); 1302 break; 1303 default: 1304 /* Someone set it by hand */ 1305 g_assert_not_reached (); 1306 } 1307 } 1308 1309 void 1310 gtk_cmclist_freeze (GtkCMCList *clist) 1311 { 1312 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1313 1314 clist->freeze_count++; 1315 } 1316 1317 void 1318 gtk_cmclist_thaw (GtkCMCList *clist) 1319 { 1320 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1321 1322 if (clist->freeze_count) 1323 { 1324 clist->freeze_count--; 1325 CLIST_REFRESH (clist); 1326 } 1327 } 1328 1329 /* PUBLIC COLUMN FUNCTIONS 1330 * gtk_cmclist_column_titles_show 1331 * gtk_cmclist_column_titles_hide 1332 * gtk_cmclist_column_title_active 1333 * gtk_cmclist_column_title_passive 1334 * gtk_cmclist_column_titles_active 1335 * gtk_cmclist_column_titles_passive 1336 * gtk_cmclist_set_column_title 1337 * gtk_cmclist_get_column_title 1338 * gtk_cmclist_set_column_widget 1339 * gtk_cmclist_set_column_justification 1340 * gtk_cmclist_set_column_visibility 1341 * gtk_cmclist_set_column_resizeable 1342 * gtk_cmclist_set_column_auto_resize 1343 * gtk_cmclist_optimal_column_width 1344 * gtk_cmclist_set_column_width 1345 * gtk_cmclist_set_column_min_width 1346 * gtk_cmclist_set_column_max_width 1347 */ 1348 void 1349 gtk_cmclist_column_titles_show (GtkCMCList *clist) 1350 { 1351 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1352 1353 if (!GTK_CMCLIST_SHOW_TITLES(clist)) 1354 { 1355 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_SHOW_TITLES); 1356 if (clist->title_window) 1357 gdk_window_show (clist->title_window); 1358 gtk_widget_queue_resize (GTK_WIDGET (clist)); 1359 } 1360 } 1361 1362 void 1363 gtk_cmclist_column_titles_hide (GtkCMCList *clist) 1364 { 1365 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1366 1367 if (GTK_CMCLIST_SHOW_TITLES(clist)) 1368 { 1369 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_SHOW_TITLES); 1370 if (clist->title_window) 1371 gdk_window_hide (clist->title_window); 1372 gtk_widget_queue_resize (GTK_WIDGET (clist)); 1373 } 1374 } 1375 1376 void 1377 gtk_cmclist_column_title_active (GtkCMCList *clist, 1378 gint column) 1379 { 1380 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1381 1382 if (column < 0 || column >= clist->columns) 1383 return; 1384 if (!clist->column[column].button || !clist->column[column].button_passive) 1385 return; 1386 1387 clist->column[column].button_passive = FALSE; 1388 1389 g_signal_handlers_disconnect_matched(G_OBJECT (clist->column[column].button), G_SIGNAL_MATCH_FUNC, 1390 0, 0, 0, column_title_passive_func, 0); 1391 1392 gtk_widget_set_can_focus (clist->column[column].button, TRUE); 1393 if (gtk_widget_get_visible (GTK_WIDGET(clist))) 1394 gtk_widget_queue_draw (clist->column[column].button); 1395 } 1396 1397 void 1398 gtk_cmclist_column_title_passive (GtkCMCList *clist, 1399 gint column) 1400 { 1401 GtkToggleButton *button; 1402 1403 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1404 1405 if (column < 0 || column >= clist->columns) 1406 return; 1407 if (!clist->column[column].button || clist->column[column].button_passive) 1408 return; 1409 1410 button = GTK_TOGGLE_BUTTON (clist->column[column].button); 1411 1412 clist->column[column].button_passive = TRUE; 1413 1414 if (gtk_toggle_button_get_active(button)) 1415 g_signal_connect(G_OBJECT (clist->column[column].button), 1416 "button-release-event", 1417 G_CALLBACK(column_title_passive_func), 1418 NULL); 1419 if (gtk_widget_is_focus(gtk_bin_get_child(GTK_BIN(button)))) 1420 g_signal_connect(G_OBJECT (clist->column[column].button), 1421 "leave-notify-event", 1422 G_CALLBACK(column_title_passive_func), 1423 NULL); 1424 1425 g_signal_connect (G_OBJECT (clist->column[column].button), "event", 1426 G_CALLBACK(column_title_passive_func), NULL); 1427 1428 gtk_widget_set_can_focus (clist->column[column].button, FALSE); 1429 if (gtk_widget_get_visible (GTK_WIDGET(clist))) 1430 gtk_widget_queue_draw (clist->column[column].button); 1431 } 1432 1433 void 1434 gtk_cmclist_column_titles_active (GtkCMCList *clist) 1435 { 1436 gint i; 1437 1438 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1439 1440 for (i = 0; i < clist->columns; i++) 1441 gtk_cmclist_column_title_active (clist, i); 1442 } 1443 1444 void 1445 gtk_cmclist_column_titles_passive (GtkCMCList *clist) 1446 { 1447 gint i; 1448 1449 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1450 1451 for (i = 0; i < clist->columns; i++) 1452 gtk_cmclist_column_title_passive (clist, i); 1453 } 1454 1455 void 1456 gtk_cmclist_set_column_title (GtkCMCList *clist, 1457 gint column, 1458 const gchar *title) 1459 { 1460 gint new_button = 0; 1461 GtkWidget *old_widget; 1462 GtkWidget *alignment = NULL; 1463 GtkWidget *label; 1464 1465 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1466 1467 if (column < 0 || column >= clist->columns) 1468 return; 1469 1470 /* if the column button doesn't currently exist, 1471 * it has to be created first */ 1472 if (!clist->column[column].button) 1473 { 1474 column_button_create (clist, column); 1475 new_button = 1; 1476 } 1477 1478 column_title_new (clist, column, title); 1479 1480 /* remove and destroy the old widget */ 1481 old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button)); 1482 if (old_widget) 1483 gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget); 1484 1485 /* create new alignment based no column justification */ 1486 switch (clist->column[column].justification) 1487 { 1488 case GTK_JUSTIFY_LEFT: 1489 alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0); 1490 break; 1491 1492 case GTK_JUSTIFY_RIGHT: 1493 alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0); 1494 break; 1495 1496 case GTK_JUSTIFY_CENTER: 1497 alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); 1498 break; 1499 1500 case GTK_JUSTIFY_FILL: 1501 alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); 1502 break; 1503 } 1504 1505 gtk_widget_push_composite_child (); 1506 label = gtk_label_new (clist->column[column].title); 1507 gtk_widget_pop_composite_child (); 1508 gtk_container_add (GTK_CONTAINER (alignment), label); 1509 gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment); 1510 gtk_widget_show (label); 1511 gtk_widget_show (alignment); 1512 1513 /* if this button didn't previously exist, then the 1514 * column button positions have to be re-computed */ 1515 if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button) 1516 size_allocate_title_buttons (clist); 1517 } 1518 1519 gchar * 1520 gtk_cmclist_get_column_title (GtkCMCList *clist, 1521 gint column) 1522 { 1523 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 1524 1525 if (column < 0 || column >= clist->columns) 1526 return NULL; 1527 1528 return clist->column[column].title; 1529 } 1530 1531 void 1532 gtk_cmclist_set_column_widget (GtkCMCList *clist, 1533 gint column, 1534 GtkWidget *widget) 1535 { 1536 gint new_button = 0; 1537 GtkWidget *old_widget; 1538 1539 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1540 1541 if (column < 0 || column >= clist->columns) 1542 return; 1543 1544 /* if the column button doesn't currently exist, 1545 * it has to be created first */ 1546 if (!clist->column[column].button) 1547 { 1548 column_button_create (clist, column); 1549 new_button = 1; 1550 } 1551 1552 column_title_new (clist, column, NULL); 1553 1554 /* remove and destroy the old widget */ 1555 old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button)); 1556 if (old_widget) 1557 gtk_container_remove (GTK_CONTAINER (clist->column[column].button), 1558 old_widget); 1559 1560 /* add and show the widget */ 1561 if (widget) 1562 { 1563 gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget); 1564 gtk_widget_show (widget); 1565 } 1566 1567 /* if this button didn't previously exist, then the 1568 * column button positions have to be re-computed */ 1569 if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button) 1570 size_allocate_title_buttons (clist); 1571 } 1572 1573 GtkWidget * 1574 gtk_cmclist_get_column_widget (GtkCMCList *clist, 1575 gint column) 1576 { 1577 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 1578 1579 if (column < 0 || column >= clist->columns) 1580 return NULL; 1581 1582 if (clist->column[column].button) 1583 return gtk_bin_get_child (GTK_BIN (clist->column[column].button)); 1584 1585 return NULL; 1586 } 1587 1588 void 1589 gtk_cmclist_set_column_justification (GtkCMCList *clist, 1590 gint column, 1591 GtkJustification justification) 1592 { 1593 GtkWidget *alignment; 1594 1595 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1596 1597 if (column < 0 || column >= clist->columns) 1598 return; 1599 1600 clist->column[column].justification = justification; 1601 1602 /* change the alinment of the button title if it's not a 1603 * custom widget */ 1604 if (clist->column[column].title) 1605 { 1606 alignment = gtk_bin_get_child (GTK_BIN (clist->column[column].button)); 1607 1608 switch (clist->column[column].justification) 1609 { 1610 case GTK_JUSTIFY_LEFT: 1611 gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0); 1612 break; 1613 1614 case GTK_JUSTIFY_RIGHT: 1615 gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0); 1616 break; 1617 1618 case GTK_JUSTIFY_CENTER: 1619 gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0); 1620 break; 1621 1622 case GTK_JUSTIFY_FILL: 1623 gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0); 1624 break; 1625 1626 default: 1627 break; 1628 } 1629 } 1630 1631 if (CLIST_UNFROZEN (clist)) 1632 draw_rows (clist, NULL); 1633 } 1634 1635 void 1636 gtk_cmclist_set_column_visibility (GtkCMCList *clist, 1637 gint column, 1638 gboolean visible) 1639 { 1640 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1641 1642 if (column < 0 || column >= clist->columns) 1643 return; 1644 if (clist->column[column].visible == visible) 1645 return; 1646 1647 /* don't hide last visible column */ 1648 if (!visible) 1649 { 1650 gint i; 1651 gint vis_columns = 0; 1652 1653 for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++) 1654 if (clist->column[i].visible) 1655 vis_columns++; 1656 1657 if (vis_columns < 2) 1658 return; 1659 } 1660 1661 clist->column[column].visible = visible; 1662 1663 if (clist->column[column].button) 1664 { 1665 if (visible) 1666 gtk_widget_show (clist->column[column].button); 1667 else 1668 gtk_widget_hide (clist->column[column].button); 1669 } 1670 1671 gtk_widget_queue_resize (GTK_WIDGET(clist)); 1672 } 1673 1674 void 1675 gtk_cmclist_set_column_resizeable (GtkCMCList *clist, 1676 gint column, 1677 gboolean resizeable) 1678 { 1679 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1680 1681 if (column < 0 || column >= clist->columns) 1682 return; 1683 if (clist->column[column].resizeable == resizeable) 1684 return; 1685 1686 clist->column[column].resizeable = resizeable; 1687 if (resizeable) 1688 clist->column[column].auto_resize = FALSE; 1689 1690 if (gtk_widget_get_visible (GTK_WIDGET(clist))) 1691 size_allocate_title_buttons (clist); 1692 } 1693 1694 void 1695 gtk_cmclist_set_column_auto_resize (GtkCMCList *clist, 1696 gint column, 1697 gboolean auto_resize) 1698 { 1699 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1700 1701 if (column < 0 || column >= clist->columns) 1702 return; 1703 if (clist->column[column].auto_resize == auto_resize) 1704 return; 1705 1706 clist->column[column].auto_resize = auto_resize; 1707 if (auto_resize) 1708 { 1709 clist->column[column].resizeable = FALSE; 1710 if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 1711 { 1712 gint width; 1713 1714 width = gtk_cmclist_optimal_column_width (clist, column); 1715 gtk_cmclist_set_column_width (clist, column, width); 1716 } 1717 } 1718 1719 if (gtk_widget_get_visible (GTK_WIDGET(clist))) 1720 size_allocate_title_buttons (clist); 1721 } 1722 1723 gint 1724 gtk_cmclist_columns_autosize (GtkCMCList *clist) 1725 { 1726 gint i; 1727 gint width; 1728 1729 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 1730 1731 gtk_cmclist_freeze (clist); 1732 width = 0; 1733 for (i = 0; i < clist->columns; i++) 1734 { 1735 gtk_cmclist_set_column_width (clist, i, 1736 gtk_cmclist_optimal_column_width (clist, i)); 1737 1738 width += clist->column[i].width; 1739 } 1740 1741 gtk_cmclist_thaw (clist); 1742 return width; 1743 } 1744 1745 gint 1746 gtk_cmclist_optimal_column_width (GtkCMCList *clist, 1747 gint column) 1748 { 1749 GtkRequisition requisition; 1750 GList *list; 1751 gint width; 1752 1753 cm_return_val_if_fail (GTK_CMCLIST (clist), 0); 1754 1755 if (column < 0 || column >= clist->columns) 1756 return 0; 1757 1758 if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button) 1759 { 1760 gtk_widget_get_requisition (clist->column[column].button, &requisition); 1761 width = requisition.width 1762 #if 0 1763 (CELL_SPACING + (2 * COLUMN_INSET))) 1764 #endif 1765 ; 1766 } 1767 else 1768 width = 0; 1769 1770 for (list = clist->row_list; list; list = list->next) 1771 { 1772 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request 1773 (clist, GTK_CMCLIST_ROW (list), column, &requisition); 1774 width = MAX (width, requisition.width); 1775 } 1776 1777 return width; 1778 } 1779 1780 void 1781 gtk_cmclist_set_column_width (GtkCMCList *clist, 1782 gint column, 1783 gint width) 1784 { 1785 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1786 1787 if (column < 0 || column >= clist->columns) 1788 return; 1789 1790 g_signal_emit (G_OBJECT (clist), clist_signals[RESIZE_COLUMN], 0, 1791 column, width); 1792 } 1793 1794 void 1795 gtk_cmclist_set_column_min_width (GtkCMCList *clist, 1796 gint column, 1797 gint min_width) 1798 { 1799 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1800 1801 if (column < 0 || column >= clist->columns) 1802 return; 1803 if (clist->column[column].min_width == min_width) 1804 return; 1805 1806 if (clist->column[column].max_width >= 0 && 1807 clist->column[column].max_width < min_width) 1808 clist->column[column].min_width = clist->column[column].max_width; 1809 else 1810 clist->column[column].min_width = min_width; 1811 1812 if (clist->column[column].area.width < clist->column[column].min_width) 1813 gtk_cmclist_set_column_width (clist, column,clist->column[column].min_width); 1814 } 1815 1816 void 1817 gtk_cmclist_set_column_max_width (GtkCMCList *clist, 1818 gint column, 1819 gint max_width) 1820 { 1821 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1822 1823 if (column < 0 || column >= clist->columns) 1824 return; 1825 if (clist->column[column].max_width == max_width) 1826 return; 1827 1828 if (clist->column[column].min_width >= 0 && max_width >= 0 && 1829 clist->column[column].min_width > max_width) 1830 clist->column[column].max_width = clist->column[column].min_width; 1831 else 1832 clist->column[column].max_width = max_width; 1833 1834 if (clist->column[column].area.width > clist->column[column].max_width) 1835 gtk_cmclist_set_column_width (clist, column,clist->column[column].max_width); 1836 } 1837 1838 /* PRIVATE COLUMN FUNCTIONS 1839 * column_auto_resize 1840 * real_resize_column 1841 * abort_column_resize 1842 * size_allocate_title_buttons 1843 * size_allocate_columns 1844 * list_requisition_width 1845 * new_column_width 1846 * column_button_create 1847 * column_button_clicked 1848 * column_title_passive_func 1849 */ 1850 static void 1851 column_auto_resize (GtkCMCList *clist, 1852 GtkCMCListRow *clist_row, 1853 gint column, 1854 gint old_width) 1855 { 1856 /* resize column if needed for auto_resize */ 1857 GtkRequisition requisition; 1858 1859 if (!clist->column[column].auto_resize || 1860 GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 1861 return; 1862 1863 if (clist_row) 1864 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row, 1865 column, &requisition); 1866 else 1867 requisition.width = 0; 1868 1869 if (requisition.width > clist->column[column].width) 1870 gtk_cmclist_set_column_width (clist, column, requisition.width); 1871 else if (requisition.width < old_width && 1872 old_width == clist->column[column].width) 1873 { 1874 GList *list; 1875 gint new_width = 0; 1876 1877 /* run a "gtk_cmclist_optimal_column_width" but break, if 1878 * the column doesn't shrink */ 1879 if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button) 1880 { 1881 gtk_widget_get_requisition (clist->column[column].button, &requisition); 1882 new_width = (requisition.width - 1883 (CELL_SPACING + (2 * COLUMN_INSET))); 1884 } 1885 else 1886 new_width = 0; 1887 1888 for (list = clist->row_list; list; list = list->next) 1889 { 1890 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request 1891 (clist, GTK_CMCLIST_ROW (list), column, &requisition); 1892 new_width = MAX (new_width, requisition.width); 1893 if (new_width == clist->column[column].width) 1894 break; 1895 } 1896 if (new_width < clist->column[column].width) 1897 gtk_cmclist_set_column_width 1898 (clist, column, MAX (new_width, clist->column[column].min_width)); 1899 } 1900 } 1901 1902 static void 1903 real_resize_column (GtkCMCList *clist, 1904 gint column, 1905 gint width) 1906 { 1907 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1908 1909 if (column < 0 || column >= clist->columns) 1910 return; 1911 1912 if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width)) 1913 width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width); 1914 if (clist->column[column].max_width >= 0 && 1915 width > clist->column[column].max_width) 1916 width = clist->column[column].max_width; 1917 1918 clist->column[column].width = width; 1919 clist->column[column].width_set = TRUE; 1920 1921 /* FIXME: this is quite expensive to do if the widget hasn't 1922 * been size_allocated yet, and pointless. Should 1923 * a flag be kept 1924 */ 1925 size_allocate_columns (clist, TRUE); 1926 size_allocate_title_buttons (clist); 1927 1928 CLIST_REFRESH (clist); 1929 } 1930 1931 static void 1932 abort_column_resize (GtkCMCList *clist) 1933 { 1934 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 1935 1936 if (!GTK_CMCLIST_IN_DRAG(clist)) 1937 return; 1938 1939 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG); 1940 gtk_grab_remove (GTK_WIDGET (clist)); 1941 gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)), 1942 GDK_CURRENT_TIME); 1943 clist->drag_pos = -1; 1944 1945 if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1) 1946 clist_refresh(clist); 1947 } 1948 1949 static void 1950 size_allocate_title_buttons (GtkCMCList *clist) 1951 { 1952 GtkAllocation button_allocation; 1953 gint last_column; 1954 gint last_button = 0; 1955 gint i; 1956 1957 if (!gtk_widget_get_realized (GTK_WIDGET(clist))) 1958 return; 1959 1960 /* we're too early, the widget is not yet ready */ 1961 if (clist->column_title_area.height <= 1) 1962 return; 1963 1964 button_allocation.x = clist->hoffset; 1965 button_allocation.y = 0; 1966 button_allocation.width = 0; 1967 button_allocation.height = clist->column_title_area.height; 1968 1969 /* find last visible column */ 1970 for (last_column = clist->columns - 1; last_column >= 0; last_column--) 1971 if (clist->column[last_column].visible) 1972 break; 1973 1974 for (i = 0; i < last_column; i++) 1975 { 1976 if (!clist->column[i].visible) 1977 { 1978 last_button = i + 1; 1979 gdk_window_hide (clist->column[i].window); 1980 continue; 1981 } 1982 1983 button_allocation.width += (clist->column[i].area.width + 1984 CELL_SPACING + 2 * COLUMN_INSET); 1985 1986 if (!clist->column[i + 1].button) 1987 { 1988 gdk_window_hide (clist->column[i].window); 1989 continue; 1990 } 1991 1992 gtk_widget_size_allocate (clist->column[last_button].button, 1993 &button_allocation); 1994 button_allocation.x += button_allocation.width; 1995 button_allocation.width = 0; 1996 1997 if (clist->column[last_button].resizeable) 1998 { 1999 gdk_window_show (clist->column[last_button].window); 2000 gdk_window_move_resize (clist->column[last_button].window, 2001 button_allocation.x - (DRAG_WIDTH / 2), 2002 0, DRAG_WIDTH, 2003 clist->column_title_area.height); 2004 } 2005 else 2006 gdk_window_hide (clist->column[last_button].window); 2007 2008 last_button = i + 1; 2009 } 2010 2011 button_allocation.width += (clist->column[last_column].area.width + 2012 2 * (CELL_SPACING + COLUMN_INSET)); 2013 gtk_widget_size_allocate (clist->column[last_button].button, 2014 &button_allocation); 2015 2016 if (clist->column[last_button].resizeable) 2017 { 2018 button_allocation.x += button_allocation.width; 2019 2020 gdk_window_show (clist->column[last_button].window); 2021 gdk_window_move_resize (clist->column[last_button].window, 2022 button_allocation.x - (DRAG_WIDTH / 2), 2023 0, DRAG_WIDTH, clist->column_title_area.height); 2024 } 2025 else 2026 gdk_window_hide (clist->column[last_button].window); 2027 } 2028 2029 static void 2030 size_allocate_columns (GtkCMCList *clist, 2031 gboolean block_resize) 2032 { 2033 GtkRequisition requisition; 2034 gint xoffset = CELL_SPACING + COLUMN_INSET; 2035 gint last_column; 2036 gint i; 2037 2038 /* find last visible column and calculate correct column width */ 2039 for (last_column = clist->columns - 1; 2040 last_column >= 0 && !clist->column[last_column].visible; last_column--); 2041 2042 if (last_column < 0) 2043 return; 2044 2045 for (i = 0; i <= last_column; i++) 2046 { 2047 if (!clist->column[i].visible) 2048 continue; 2049 clist->column[i].area.x = xoffset; 2050 if (clist->column[i].width_set) 2051 { 2052 if (!block_resize && GTK_CMCLIST_SHOW_TITLES(clist) && 2053 clist->column[i].auto_resize && clist->column[i].button) 2054 { 2055 gint width; 2056 2057 gtk_widget_get_requisition (clist->column[i].button, &requisition); 2058 width = (requisition.width - 2059 (CELL_SPACING + (2 * COLUMN_INSET))); 2060 2061 if (width > clist->column[i].width) 2062 gtk_cmclist_set_column_width (clist, i, width); 2063 } 2064 2065 clist->column[i].area.width = clist->column[i].width; 2066 xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET); 2067 } 2068 else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button) 2069 { 2070 gtk_widget_get_requisition (clist->column[i].button, &requisition); 2071 clist->column[i].area.width = 2072 requisition.width - 2073 (CELL_SPACING + (2 * COLUMN_INSET)); 2074 xoffset += requisition.width; 2075 } 2076 } 2077 2078 clist->column[last_column].area.width = clist->column[last_column].area.width 2079 + MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset); 2080 } 2081 2082 static gint 2083 list_requisition_width (GtkCMCList *clist) 2084 { 2085 GtkRequisition requisition; 2086 gint width = CELL_SPACING; 2087 gint i; 2088 2089 for (i = clist->columns - 1; i >= 0; i--) 2090 { 2091 if (!clist->column[i].visible) 2092 continue; 2093 2094 if (clist->column[i].width_set) 2095 width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET); 2096 else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button) 2097 { 2098 gtk_widget_get_requisition (clist->column[i].button, &requisition); 2099 width += requisition.width; 2100 } 2101 } 2102 2103 return width; 2104 } 2105 2106 /* this function returns the new width of the column being resized given 2107 * the column and x position of the cursor; the x cursor position is passed 2108 * in as a pointer and automagicly corrected if it's beyond min/max limits */ 2109 static gint 2110 new_column_width (GtkCMCList *clist, 2111 gint column, 2112 gint *x) 2113 { 2114 gint xthickness = gtk_widget_get_style (GTK_WIDGET (clist))->xthickness; 2115 gint width; 2116 gint cx; 2117 gint dx; 2118 gint last_column; 2119 2120 /* first translate the x position from widget->window 2121 * to clist->clist_window */ 2122 cx = *x - xthickness; 2123 2124 for (last_column = clist->columns - 1; 2125 last_column >= 0 && !clist->column[last_column].visible; last_column--); 2126 2127 /* calculate new column width making sure it doesn't end up 2128 * less than the minimum width */ 2129 dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET + 2130 (column < last_column) * CELL_SPACING); 2131 width = cx - dx; 2132 2133 if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width)) 2134 { 2135 width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width); 2136 cx = dx + width; 2137 *x = cx + xthickness; 2138 } 2139 else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH && 2140 width > clist->column[column].max_width) 2141 { 2142 width = clist->column[column].max_width; 2143 cx = dx + clist->column[column].max_width; 2144 *x = cx + xthickness; 2145 } 2146 2147 if (cx < 0 || cx > clist->clist_window_width) 2148 *x = -1; 2149 2150 return width; 2151 } 2152 2153 static void 2154 column_button_create (GtkCMCList *clist, 2155 gint column) 2156 { 2157 GtkWidget *button; 2158 2159 gtk_widget_push_composite_child (); 2160 button = clist->column[column].button = gtk_button_new (); 2161 GtkRcStyle *style = gtk_rc_style_new(); 2162 style->ythickness = 0; 2163 gtk_widget_modify_style(clist->column[column].button, style); 2164 g_object_unref(style); 2165 gtk_container_set_border_width(GTK_CONTAINER(button), 0); 2166 gtk_widget_pop_composite_child (); 2167 2168 if (gtk_widget_get_realized (GTK_WIDGET(clist)) && clist->title_window) 2169 gtk_widget_set_parent_window (clist->column[column].button, 2170 clist->title_window); 2171 gtk_widget_set_parent (button, GTK_WIDGET (clist)); 2172 2173 g_signal_connect (G_OBJECT (button), "clicked", 2174 G_CALLBACK(column_button_clicked), 2175 (gpointer) clist); 2176 gtk_widget_show (button); 2177 } 2178 2179 static void 2180 column_button_clicked (GtkWidget *widget, 2181 gpointer data) 2182 { 2183 gint i; 2184 GtkCMCList *clist; 2185 2186 cm_return_if_fail (widget != NULL); 2187 cm_return_if_fail (GTK_IS_CMCLIST (data)); 2188 2189 clist = GTK_CMCLIST (data); 2190 2191 /* find the column who's button was pressed */ 2192 for (i = 0; i < clist->columns; i++) 2193 if (clist->column[i].button == widget) 2194 break; 2195 2196 g_signal_emit (G_OBJECT (clist), clist_signals[CLICK_COLUMN], 0, i); 2197 } 2198 2199 static gint 2200 column_title_passive_func (GtkWidget *widget, 2201 GdkEvent *event, 2202 gpointer data) 2203 { 2204 cm_return_val_if_fail (event != NULL, FALSE); 2205 2206 switch (event->type) 2207 { 2208 case GDK_MOTION_NOTIFY: 2209 case GDK_BUTTON_PRESS: 2210 case GDK_2BUTTON_PRESS: 2211 case GDK_3BUTTON_PRESS: 2212 case GDK_BUTTON_RELEASE: 2213 case GDK_ENTER_NOTIFY: 2214 case GDK_LEAVE_NOTIFY: 2215 return TRUE; 2216 default: 2217 break; 2218 } 2219 return FALSE; 2220 } 2221 2222 2223 /* PUBLIC CELL FUNCTIONS 2224 * gtk_cmclist_get_cell_type 2225 * gtk_cmclist_set_text 2226 * gtk_cmclist_get_text 2227 * gtk_cmclist_set_pixbuf 2228 * gtk_cmclist_get_pixbuf 2229 * gtk_cmclist_set_pixtext 2230 * gtk_cmclist_get_pixtext 2231 * gtk_cmclist_set_shift 2232 */ 2233 GtkCMCellType 2234 gtk_cmclist_get_cell_type (GtkCMCList *clist, 2235 gint row, 2236 gint column) 2237 { 2238 GtkCMCListRow *clist_row; 2239 2240 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1); 2241 2242 if (row < 0 || row >= clist->rows) 2243 return -1; 2244 if (column < 0 || column >= clist->columns) 2245 return -1; 2246 2247 clist_row = ROW_ELEMENT (clist, row)->data; 2248 2249 return clist_row->cell[column].type; 2250 } 2251 2252 void 2253 gtk_cmclist_set_text (GtkCMCList *clist, 2254 gint row, 2255 gint column, 2256 const gchar *text) 2257 { 2258 GtkCMCListRow *clist_row; 2259 2260 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2261 2262 if (row < 0 || row >= clist->rows) 2263 return; 2264 if (column < 0 || column >= clist->columns) 2265 return; 2266 2267 clist_row = ROW_ELEMENT (clist, row)->data; 2268 2269 /* if text is null, then the cell is empty */ 2270 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents 2271 (clist, clist_row, column, GTK_CMCELL_TEXT, text, 0, NULL); 2272 2273 /* redraw the list if it's not frozen */ 2274 if (CLIST_UNFROZEN (clist)) 2275 { 2276 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 2277 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 2278 } 2279 } 2280 2281 gint 2282 gtk_cmclist_get_text (GtkCMCList *clist, 2283 gint row, 2284 gint column, 2285 gchar **text) 2286 { 2287 GtkCMCListRow *clist_row; 2288 2289 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 2290 2291 if (row < 0 || row >= clist->rows) 2292 return 0; 2293 if (column < 0 || column >= clist->columns) 2294 return 0; 2295 2296 clist_row = ROW_ELEMENT (clist, row)->data; 2297 2298 if (clist_row->cell[column].type != GTK_CMCELL_TEXT) 2299 return 0; 2300 2301 if (text) 2302 *text = GTK_CMCELL_TEXT (clist_row->cell[column])->text; 2303 2304 return 1; 2305 } 2306 2307 void 2308 gtk_cmclist_set_pixbuf (GtkCMCList *clist, 2309 gint row, 2310 gint column, 2311 GdkPixbuf *pixbuf) 2312 { 2313 GtkCMCListRow *clist_row; 2314 2315 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2316 2317 if (row < 0 || row >= clist->rows) 2318 return; 2319 if (column < 0 || column >= clist->columns) 2320 return; 2321 2322 clist_row = ROW_ELEMENT (clist, row)->data; 2323 2324 g_object_ref (pixbuf); 2325 2326 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents 2327 (clist, clist_row, column, GTK_CMCELL_PIXBUF, NULL, 0, pixbuf); 2328 2329 /* redraw the list if it's not frozen */ 2330 if (CLIST_UNFROZEN (clist)) 2331 { 2332 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 2333 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 2334 } 2335 } 2336 2337 gint 2338 gtk_cmclist_get_pixbuf (GtkCMCList *clist, 2339 gint row, 2340 gint column, 2341 GdkPixbuf **pixbuf) 2342 { 2343 GtkCMCListRow *clist_row; 2344 2345 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 2346 2347 if (row < 0 || row >= clist->rows) 2348 return 0; 2349 if (column < 0 || column >= clist->columns) 2350 return 0; 2351 2352 clist_row = ROW_ELEMENT (clist, row)->data; 2353 2354 if (clist_row->cell[column].type != GTK_CMCELL_PIXBUF) 2355 return 0; 2356 2357 if (pixbuf) 2358 { 2359 *pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf; 2360 } 2361 2362 return 1; 2363 } 2364 2365 void 2366 gtk_cmclist_set_pixtext (GtkCMCList *clist, 2367 gint row, 2368 gint column, 2369 const gchar *text, 2370 guint8 spacing, 2371 GdkPixbuf *pixbuf) 2372 { 2373 GtkCMCListRow *clist_row; 2374 2375 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2376 2377 if (row < 0 || row >= clist->rows) 2378 return; 2379 if (column < 0 || column >= clist->columns) 2380 return; 2381 2382 clist_row = ROW_ELEMENT (clist, row)->data; 2383 2384 g_object_ref (pixbuf); 2385 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents 2386 (clist, clist_row, column, GTK_CMCELL_PIXTEXT, text, spacing, pixbuf); 2387 2388 /* redraw the list if it's not frozen */ 2389 if (CLIST_UNFROZEN (clist)) 2390 { 2391 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 2392 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 2393 } 2394 } 2395 2396 gint 2397 gtk_cmclist_get_pixtext (GtkCMCList *clist, 2398 gint row, 2399 gint column, 2400 gchar **text, 2401 guint8 *spacing, 2402 GdkPixbuf **pixbuf) 2403 { 2404 GtkCMCListRow *clist_row; 2405 2406 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 2407 2408 if (row < 0 || row >= clist->rows) 2409 return 0; 2410 if (column < 0 || column >= clist->columns) 2411 return 0; 2412 2413 clist_row = ROW_ELEMENT (clist, row)->data; 2414 2415 if (clist_row->cell[column].type != GTK_CMCELL_PIXTEXT) 2416 return 0; 2417 2418 if (text) 2419 *text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text; 2420 if (spacing) 2421 *spacing = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing; 2422 if (pixbuf) 2423 *pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf; 2424 2425 return 1; 2426 } 2427 2428 void 2429 gtk_cmclist_set_shift (GtkCMCList *clist, 2430 gint row, 2431 gint column, 2432 gint vertical, 2433 gint horizontal) 2434 { 2435 GtkRequisition requisition = { 0 }; 2436 GtkCMCListRow *clist_row; 2437 2438 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2439 2440 if (row < 0 || row >= clist->rows) 2441 return; 2442 if (column < 0 || column >= clist->columns) 2443 return; 2444 2445 clist_row = ROW_ELEMENT (clist, row)->data; 2446 2447 if (clist->column[column].auto_resize && 2448 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 2449 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row, 2450 column, &requisition); 2451 2452 clist_row->cell[column].vertical = vertical; 2453 clist_row->cell[column].horizontal = horizontal; 2454 2455 column_auto_resize (clist, clist_row, column, requisition.width); 2456 2457 if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 2458 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 2459 } 2460 2461 /* PRIVATE CELL FUNCTIONS 2462 * set_cell_contents 2463 * cell_size_request 2464 */ 2465 static void 2466 set_cell_contents (GtkCMCList *clist, 2467 GtkCMCListRow *clist_row, 2468 gint column, 2469 GtkCMCellType type, 2470 const gchar *text, 2471 guint8 spacing, 2472 GdkPixbuf *pixbuf) 2473 { 2474 GtkRequisition requisition; 2475 gchar *old_text = NULL; 2476 GdkPixbuf *old_pixbuf = NULL; 2477 2478 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2479 cm_return_if_fail (clist_row != NULL); 2480 2481 if (clist->column[column].auto_resize && 2482 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 2483 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row, 2484 column, &requisition); 2485 2486 switch (clist_row->cell[column].type) 2487 { 2488 case GTK_CMCELL_EMPTY: 2489 break; 2490 case GTK_CMCELL_TEXT: 2491 old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text; 2492 break; 2493 case GTK_CMCELL_PIXBUF: 2494 old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf; 2495 break; 2496 case GTK_CMCELL_PIXTEXT: 2497 old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text; 2498 old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf; 2499 break; 2500 case GTK_CMCELL_WIDGET: 2501 /* unimplemented */ 2502 break; 2503 default: 2504 break; 2505 } 2506 2507 clist_row->cell[column].type = GTK_CMCELL_EMPTY; 2508 2509 /* Note that pixbuf and mask were already ref'ed by the caller 2510 */ 2511 switch (type) 2512 { 2513 case GTK_CMCELL_TEXT: 2514 if (text) 2515 { 2516 clist_row->cell[column].type = GTK_CMCELL_TEXT; 2517 GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text); 2518 } 2519 break; 2520 case GTK_CMCELL_PIXBUF: 2521 if (pixbuf) 2522 { 2523 clist_row->cell[column].type = GTK_CMCELL_PIXBUF; 2524 GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf; 2525 } 2526 break; 2527 case GTK_CMCELL_PIXTEXT: 2528 if (text && pixbuf) 2529 { 2530 clist_row->cell[column].type = GTK_CMCELL_PIXTEXT; 2531 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text); 2532 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing; 2533 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf; 2534 } 2535 break; 2536 default: 2537 break; 2538 } 2539 2540 if (clist->column[column].auto_resize && 2541 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 2542 column_auto_resize (clist, clist_row, column, requisition.width); 2543 2544 g_free (old_text); 2545 if (old_pixbuf) 2546 g_object_unref (old_pixbuf); 2547 } 2548 2549 PangoLayout * 2550 _gtk_cmclist_create_cell_layout (GtkCMCList *clist, 2551 GtkCMCListRow *clist_row, 2552 gint column) 2553 { 2554 PangoLayout *layout; 2555 GtkStyle *style; 2556 GtkCMCell *cell; 2557 gchar *text; 2558 2559 get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style); 2560 2561 2562 cell = &clist_row->cell[column]; 2563 switch (cell->type) 2564 { 2565 case GTK_CMCELL_TEXT: 2566 case GTK_CMCELL_PIXTEXT: 2567 text = ((cell->type == GTK_CMCELL_PIXTEXT) ? 2568 GTK_CMCELL_PIXTEXT (*cell)->text : 2569 GTK_CMCELL_TEXT (*cell)->text); 2570 2571 if (!text) 2572 return NULL; 2573 2574 layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist), 2575 ((cell->type == GTK_CMCELL_PIXTEXT) ? 2576 GTK_CMCELL_PIXTEXT (*cell)->text : 2577 GTK_CMCELL_TEXT (*cell)->text)); 2578 pango_layout_set_font_description (layout, style->font_desc); 2579 2580 return layout; 2581 2582 default: 2583 return NULL; 2584 } 2585 } 2586 2587 static void 2588 cell_size_request (GtkCMCList *clist, 2589 GtkCMCListRow *clist_row, 2590 gint column, 2591 GtkRequisition *requisition) 2592 { 2593 gint width; 2594 gint height; 2595 PangoLayout *layout; 2596 PangoRectangle logical_rect; 2597 2598 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2599 cm_return_if_fail (requisition != NULL); 2600 2601 layout = _gtk_cmclist_create_cell_layout (clist, clist_row, column); 2602 if (layout) 2603 { 2604 pango_layout_get_pixel_extents (layout, NULL, &logical_rect); 2605 2606 requisition->width = logical_rect.width; 2607 requisition->height = logical_rect.height; 2608 2609 g_object_unref (G_OBJECT (layout)); 2610 } 2611 else 2612 { 2613 requisition->width = 0; 2614 requisition->height = 0; 2615 } 2616 2617 if (layout && clist_row->cell[column].type == GTK_CMCELL_PIXTEXT) 2618 requisition->width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing; 2619 2620 switch (clist_row->cell[column].type) 2621 { 2622 case GTK_CMCELL_PIXTEXT: 2623 width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf); 2624 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf); 2625 requisition->width += width; 2626 requisition->height = MAX (requisition->height, height); 2627 break; 2628 case GTK_CMCELL_PIXBUF: 2629 width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf); 2630 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf); 2631 requisition->width += width; 2632 requisition->height = MAX (requisition->height, height); 2633 break; 2634 2635 default: 2636 break; 2637 } 2638 2639 requisition->width += clist_row->cell[column].horizontal; 2640 requisition->height += clist_row->cell[column].vertical; 2641 } 2642 2643 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS 2644 * gtk_cmclist_prepend 2645 * gtk_cmclist_append 2646 * gtk_cmclist_insert 2647 * gtk_cmclist_remove 2648 * gtk_cmclist_clear 2649 */ 2650 gint 2651 gtk_cmclist_prepend (GtkCMCList *clist, 2652 gchar *text[]) 2653 { 2654 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1); 2655 cm_return_val_if_fail (text != NULL, -1); 2656 2657 return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, 0, text); 2658 } 2659 2660 gint 2661 gtk_cmclist_append (GtkCMCList *clist, 2662 gchar *text[]) 2663 { 2664 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1); 2665 cm_return_val_if_fail (text != NULL, -1); 2666 2667 return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text); 2668 } 2669 2670 gint 2671 gtk_cmclist_insert (GtkCMCList *clist, 2672 gint row, 2673 gchar *text[]) 2674 { 2675 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1); 2676 cm_return_val_if_fail (text != NULL, -1); 2677 2678 if (row < 0 || row > clist->rows) 2679 row = clist->rows; 2680 2681 return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, row, text); 2682 } 2683 2684 void 2685 gtk_cmclist_remove (GtkCMCList *clist, 2686 gint row) 2687 { 2688 GTK_CMCLIST_GET_CLASS (clist)->remove_row (clist, row); 2689 } 2690 2691 void 2692 gtk_cmclist_clear (GtkCMCList *clist) 2693 { 2694 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2695 2696 GTK_CMCLIST_GET_CLASS (clist)->clear (clist); 2697 } 2698 2699 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS 2700 * real_insert_row 2701 * real_remove_row 2702 * real_clear 2703 * real_row_move 2704 */ 2705 static gint 2706 real_insert_row (GtkCMCList *clist, 2707 gint row, 2708 gchar *text[]) 2709 { 2710 gint i; 2711 GtkCMCListRow *clist_row; 2712 2713 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1); 2714 cm_return_val_if_fail (text != NULL, -1); 2715 2716 /* return if out of bounds */ 2717 if (row < 0 || row > clist->rows) 2718 return -1; 2719 2720 /* create the row */ 2721 clist_row = row_new (clist); 2722 2723 /* set the text in the row's columns */ 2724 for (i = 0; i < clist->columns; i++) 2725 if (text[i]) 2726 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents 2727 (clist, clist_row, i, GTK_CMCELL_TEXT, text[i], 0, NULL); 2728 2729 if (!clist->rows) 2730 { 2731 clist->row_list = g_list_append (clist->row_list, clist_row); 2732 clist->row_list_end = clist->row_list; 2733 } 2734 else 2735 { 2736 if (GTK_CMCLIST_AUTO_SORT(clist)) /* override insertion pos */ 2737 { 2738 GList *work; 2739 2740 row = 0; 2741 work = clist->row_list; 2742 2743 if (clist->sort_type == GTK_SORT_ASCENDING) 2744 { 2745 while (row < clist->rows && 2746 clist->compare (clist, clist_row, 2747 GTK_CMCLIST_ROW (work)) > 0) 2748 { 2749 row++; 2750 work = work->next; 2751 } 2752 } 2753 else 2754 { 2755 while (row < clist->rows && 2756 clist->compare (clist, clist_row, 2757 GTK_CMCLIST_ROW (work)) < 0) 2758 { 2759 row++; 2760 work = work->next; 2761 } 2762 } 2763 } 2764 2765 /* reset the row end pointer if we're inserting at the end of the list */ 2766 if (row == clist->rows) 2767 clist->row_list_end = (g_list_append (clist->row_list_end, 2768 clist_row))->next; 2769 else 2770 clist->row_list = g_list_insert (clist->row_list, clist_row, row); 2771 2772 } 2773 clist->rows++; 2774 2775 if (row < ROW_FROM_YPIXEL (clist, 0)) 2776 clist->voffset -= (clist->row_height + CELL_SPACING); 2777 2778 /* syncronize the selection list */ 2779 sync_selection (clist, row, SYNC_INSERT); 2780 2781 if (clist->rows == 1) 2782 { 2783 clist->focus_row = 0; 2784 if (clist->selection_mode == GTK_SELECTION_BROWSE) 2785 gtk_cmclist_select_row (clist, 0, -1); 2786 } 2787 2788 /* redraw the list if it isn't frozen */ 2789 if (CLIST_UNFROZEN (clist)) 2790 { 2791 adjust_adjustments (clist, FALSE); 2792 2793 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 2794 draw_rows (clist, NULL); 2795 } 2796 2797 return row; 2798 } 2799 2800 static void 2801 real_remove_row (GtkCMCList *clist, 2802 gint row) 2803 { 2804 gint was_visible; 2805 GList *list; 2806 GtkCMCListRow *clist_row; 2807 2808 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2809 2810 /* return if out of bounds */ 2811 if (row < 0 || row > (clist->rows - 1)) 2812 return; 2813 2814 was_visible = (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE); 2815 2816 /* get the row we're going to delete */ 2817 list = ROW_ELEMENT (clist, row); 2818 g_assert (list != NULL); 2819 clist_row = list->data; 2820 2821 /* if we're removing a selected row, we have to make sure 2822 * it's properly unselected, and then sync up the clist->selected 2823 * list to reflect the deincrimented indexies of rows after the 2824 * removal */ 2825 if (clist_row->state == GTK_STATE_SELECTED) 2826 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 2827 row, -1, NULL); 2828 2829 sync_selection (clist, row, SYNC_REMOVE); 2830 2831 /* reset the row end pointer if we're removing at the end of the list */ 2832 clist->rows--; 2833 if (clist->row_list == list) 2834 clist->row_list = g_list_next (list); 2835 if (clist->row_list_end == list) 2836 clist->row_list_end = g_list_previous (list); 2837 list = g_list_remove (list, clist_row); 2838 2839 if (row < ROW_FROM_YPIXEL (clist, 0)) 2840 clist->voffset += clist->row_height + CELL_SPACING; 2841 2842 if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection && 2843 clist->focus_row >= 0) 2844 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 2845 clist->focus_row, -1, NULL); 2846 2847 /* toast the row */ 2848 row_delete (clist, clist_row); 2849 2850 /* redraw the row if it isn't frozen */ 2851 if (CLIST_UNFROZEN (clist)) 2852 { 2853 adjust_adjustments (clist, FALSE); 2854 2855 if (was_visible) 2856 draw_rows (clist, NULL); 2857 } 2858 } 2859 2860 static void 2861 real_clear (GtkCMCList *clist) 2862 { 2863 GList *list; 2864 GList *free_list; 2865 GtkRequisition requisition; 2866 gint i; 2867 2868 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2869 2870 /* free up the selection list */ 2871 g_list_free (clist->selection); 2872 g_list_free (clist->undo_selection); 2873 g_list_free (clist->undo_unselection); 2874 2875 clist->selection = NULL; 2876 clist->selection_end = NULL; 2877 clist->undo_selection = NULL; 2878 clist->undo_unselection = NULL; 2879 clist->voffset = 0; 2880 clist->focus_row = -1; 2881 clist->anchor = -1; 2882 clist->undo_anchor = -1; 2883 clist->anchor_state = GTK_STATE_SELECTED; 2884 clist->drag_pos = -1; 2885 2886 /* remove all the rows */ 2887 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED); 2888 free_list = clist->row_list; 2889 clist->row_list = NULL; 2890 clist->row_list_end = NULL; 2891 clist->rows = 0; 2892 for (list = free_list; list; list = list->next) 2893 row_delete (clist, GTK_CMCLIST_ROW (list)); 2894 g_list_free (free_list); 2895 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED); 2896 for (i = 0; i < clist->columns; i++) 2897 if (clist->column[i].auto_resize) 2898 { 2899 if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button) 2900 { 2901 gtk_widget_get_requisition (clist->column[i].button, &requisition); 2902 gtk_cmclist_set_column_width 2903 (clist, i, (requisition.width - 2904 (CELL_SPACING + (2 * COLUMN_INSET)))); 2905 } 2906 else 2907 gtk_cmclist_set_column_width (clist, i, 0); 2908 } 2909 /* zero-out the scrollbars */ 2910 if (clist->vadjustment) 2911 { 2912 gtk_adjustment_set_value (clist->vadjustment, 0.0); 2913 CLIST_REFRESH (clist); 2914 } 2915 else 2916 gtk_widget_queue_resize (GTK_WIDGET (clist)); 2917 } 2918 2919 static void 2920 real_row_move (GtkCMCList *clist, 2921 gint source_row, 2922 gint dest_row) 2923 { 2924 GtkCMCListRow *clist_row; 2925 GList *list; 2926 gint first, last; 2927 gint d; 2928 2929 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 2930 2931 if (GTK_CMCLIST_AUTO_SORT(clist)) 2932 return; 2933 2934 if (source_row < 0 || source_row >= clist->rows || 2935 dest_row < 0 || dest_row >= clist->rows || 2936 source_row == dest_row) 2937 return; 2938 2939 gtk_cmclist_freeze (clist); 2940 2941 /* unlink source row */ 2942 clist_row = ROW_ELEMENT (clist, source_row)->data; 2943 if (source_row == clist->rows - 1) 2944 clist->row_list_end = clist->row_list_end->prev; 2945 clist->row_list = g_list_remove (clist->row_list, clist_row); 2946 clist->rows--; 2947 2948 /* relink source row */ 2949 clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row); 2950 if (dest_row == clist->rows) 2951 clist->row_list_end = clist->row_list_end->next; 2952 clist->rows++; 2953 2954 /* sync selection */ 2955 if (source_row > dest_row) 2956 { 2957 first = dest_row; 2958 last = source_row; 2959 d = 1; 2960 } 2961 else 2962 { 2963 first = source_row; 2964 last = dest_row; 2965 d = -1; 2966 } 2967 2968 for (list = clist->selection; list; list = list->next) 2969 { 2970 if (list->data == GINT_TO_POINTER (source_row)) 2971 list->data = GINT_TO_POINTER (dest_row); 2972 else if (first <= GPOINTER_TO_INT (list->data) && 2973 last >= GPOINTER_TO_INT (list->data)) 2974 list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d); 2975 } 2976 2977 if (clist->focus_row == source_row) 2978 clist->focus_row = dest_row; 2979 else if (clist->focus_row > first) 2980 clist->focus_row += d; 2981 2982 gtk_cmclist_thaw (clist); 2983 } 2984 2985 /* PUBLIC ROW FUNCTIONS 2986 * gtk_cmclist_moveto 2987 * gtk_cmclist_set_row_height 2988 * gtk_cmclist_set_row_data 2989 * gtk_cmclist_set_row_data_full 2990 * gtk_cmclist_get_row_data 2991 * gtk_cmclist_find_row_from_data 2992 * gtk_cmclist_swap_rows 2993 * gtk_cmclist_row_move 2994 * gtk_cmclist_row_is_visible 2995 * gtk_cmclist_set_foreground 2996 * gtk_cmclist_set_background 2997 */ 2998 void 2999 gtk_cmclist_moveto (GtkCMCList *clist, 3000 gint row, 3001 gint column, 3002 gfloat row_align, 3003 gfloat col_align) 3004 { 3005 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3006 3007 if (row < -1 || row >= clist->rows) 3008 return; 3009 if (column < -1 || column >= clist->columns) 3010 return; 3011 3012 row_align = CLAMP (row_align, 0, 1); 3013 col_align = CLAMP (col_align, 0, 1); 3014 3015 /* adjust horizontal scrollbar */ 3016 if (clist->hadjustment && column >= 0) 3017 { 3018 gint x; 3019 3020 x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET - 3021 (col_align * (clist->clist_window_width - 2 * COLUMN_INSET - 3022 CELL_SPACING - clist->column[column].area.width))); 3023 if (x < 0) 3024 gtk_adjustment_set_value (clist->hadjustment, 0.0); 3025 else if (x > LIST_WIDTH (clist) - clist->clist_window_width) 3026 gtk_adjustment_set_value 3027 (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width); 3028 else 3029 gtk_adjustment_set_value (clist->hadjustment, x); 3030 } 3031 3032 /* adjust vertical scrollbar */ 3033 if (clist->vadjustment && row >= 0) 3034 move_vertical (clist, row, row_align); 3035 } 3036 3037 void 3038 gtk_cmclist_set_row_height (GtkCMCList *clist, 3039 guint height) 3040 { 3041 GtkStyle *style; 3042 GtkWidget *widget; 3043 3044 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3045 3046 widget = GTK_WIDGET (clist); 3047 3048 style = gtk_widget_get_style (widget); 3049 3050 if (height > 0) 3051 { 3052 clist->row_height = height; 3053 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET); 3054 } 3055 else 3056 { 3057 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET); 3058 clist->row_height = 0; 3059 } 3060 3061 if (style->font_desc) 3062 { 3063 PangoContext *context = gtk_widget_get_pango_context (widget); 3064 PangoFontMetrics *metrics; 3065 3066 metrics = pango_context_get_metrics (context, 3067 style->font_desc, 3068 pango_context_get_language (context)); 3069 3070 if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist)) 3071 { 3072 clist->row_height = (pango_font_metrics_get_ascent (metrics) + 3073 pango_font_metrics_get_descent (metrics)); 3074 clist->row_height = PANGO_PIXELS (clist->row_height) + 1; 3075 } 3076 3077 pango_font_metrics_unref (metrics); 3078 } 3079 3080 CLIST_REFRESH (clist); 3081 } 3082 3083 void 3084 gtk_cmclist_set_row_data (GtkCMCList *clist, 3085 gint row, 3086 gpointer data) 3087 { 3088 gtk_cmclist_set_row_data_full (clist, row, data, NULL); 3089 } 3090 3091 void 3092 gtk_cmclist_set_row_data_full (GtkCMCList *clist, 3093 gint row, 3094 gpointer data, 3095 GDestroyNotify destroy) 3096 { 3097 GtkCMCListRow *clist_row; 3098 3099 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3100 3101 if (row < 0 || row > (clist->rows - 1)) 3102 return; 3103 3104 clist_row = ROW_ELEMENT (clist, row)->data; 3105 3106 if (clist_row->destroy) 3107 clist_row->destroy (clist_row->data); 3108 3109 clist_row->data = data; 3110 clist_row->destroy = destroy; 3111 } 3112 3113 gpointer 3114 gtk_cmclist_get_row_data (GtkCMCList *clist, 3115 gint row) 3116 { 3117 GtkCMCListRow *clist_row; 3118 3119 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 3120 3121 if (row < 0 || row > (clist->rows - 1)) 3122 return NULL; 3123 3124 clist_row = ROW_ELEMENT (clist, row)->data; 3125 return clist_row->data; 3126 } 3127 3128 gint 3129 gtk_cmclist_find_row_from_data (GtkCMCList *clist, 3130 gpointer data) 3131 { 3132 GList *list; 3133 gint n; 3134 3135 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1); 3136 3137 for (n = 0, list = clist->row_list; list; n++, list = list->next) 3138 if (GTK_CMCLIST_ROW (list)->data == data) 3139 return n; 3140 3141 return -1; 3142 } 3143 3144 void 3145 gtk_cmclist_swap_rows (GtkCMCList *clist, 3146 gint row1, 3147 gint row2) 3148 { 3149 gint first, last; 3150 3151 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3152 cm_return_if_fail (row1 != row2); 3153 3154 if (GTK_CMCLIST_AUTO_SORT(clist)) 3155 return; 3156 3157 gtk_cmclist_freeze (clist); 3158 3159 first = MIN (row1, row2); 3160 last = MAX (row1, row2); 3161 3162 gtk_cmclist_row_move (clist, last, first); 3163 gtk_cmclist_row_move (clist, first + 1, last); 3164 3165 gtk_cmclist_thaw (clist); 3166 } 3167 3168 void 3169 gtk_cmclist_row_move (GtkCMCList *clist, 3170 gint source_row, 3171 gint dest_row) 3172 { 3173 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3174 3175 if (GTK_CMCLIST_AUTO_SORT(clist)) 3176 return; 3177 3178 if (source_row < 0 || source_row >= clist->rows || 3179 dest_row < 0 || dest_row >= clist->rows || 3180 source_row == dest_row) 3181 return; 3182 3183 g_signal_emit (G_OBJECT (clist), clist_signals[ROW_MOVE], 0, 3184 source_row, dest_row); 3185 } 3186 3187 GtkVisibility 3188 gtk_cmclist_row_is_visible (GtkCMCList *clist, 3189 gint row) 3190 { 3191 gint top; 3192 3193 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 3194 3195 if (row < 0 || row >= clist->rows) 3196 return GTK_VISIBILITY_NONE; 3197 3198 if (clist->row_height == 0) 3199 return GTK_VISIBILITY_NONE; 3200 3201 if (row < ROW_FROM_YPIXEL (clist, 0)) 3202 return GTK_VISIBILITY_NONE; 3203 3204 if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height)) 3205 return GTK_VISIBILITY_NONE; 3206 3207 top = ROW_TOP_YPIXEL (clist, row); 3208 3209 if ((top < 0) 3210 || ((top + clist->row_height) >= clist->clist_window_height)) 3211 return GTK_VISIBILITY_PARTIAL; 3212 3213 return GTK_VISIBILITY_FULL; 3214 } 3215 3216 gboolean 3217 gtk_cmclist_row_is_above_viewport (GtkCMCList *clist, 3218 gint row) 3219 { 3220 cm_return_val_if_fail(GTK_IS_CMCLIST (clist), 0); 3221 3222 if (row < 0 || row >= clist->rows) 3223 return FALSE; 3224 3225 if (clist->row_height == 0) 3226 return FALSE; 3227 3228 if (row < ROW_FROM_YPIXEL (clist, 0)) 3229 return TRUE; 3230 3231 return FALSE; 3232 } 3233 3234 gboolean 3235 gtk_cmclist_row_is_below_viewport (GtkCMCList *clist, 3236 gint row) 3237 { 3238 cm_return_val_if_fail(GTK_IS_CMCLIST (clist), 0); 3239 3240 if (row < 0 || row >= clist->rows) 3241 return FALSE; 3242 3243 if (clist->row_height == 0) 3244 return FALSE; 3245 3246 if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height)) 3247 return TRUE; 3248 3249 return FALSE; 3250 } 3251 3252 void 3253 gtk_cmclist_set_foreground (GtkCMCList *clist, 3254 gint row, 3255 const GdkColor *color) 3256 { 3257 GtkCMCListRow *clist_row; 3258 3259 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3260 3261 if (row < 0 || row >= clist->rows) 3262 return; 3263 3264 clist_row = ROW_ELEMENT (clist, row)->data; 3265 3266 if (color) 3267 { 3268 clist_row->foreground = *color; 3269 clist_row->fg_set = TRUE; 3270 } 3271 else 3272 clist_row->fg_set = FALSE; 3273 3274 if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 3275 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 3276 } 3277 3278 void 3279 gtk_cmclist_set_background (GtkCMCList *clist, 3280 gint row, 3281 const GdkColor *color) 3282 { 3283 GtkCMCListRow *clist_row; 3284 3285 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3286 3287 if (row < 0 || row >= clist->rows) 3288 return; 3289 3290 clist_row = ROW_ELEMENT (clist, row)->data; 3291 3292 if (color) 3293 { 3294 clist_row->background = *color; 3295 clist_row->bg_set = TRUE; 3296 } 3297 else 3298 clist_row->bg_set = FALSE; 3299 3300 if (CLIST_UNFROZEN (clist) 3301 && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)) 3302 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 3303 } 3304 3305 /* PUBLIC ROW/CELL STYLE FUNCTIONS 3306 * gtk_cmclist_set_cell_style 3307 * gtk_cmclist_get_cell_style 3308 * gtk_cmclist_set_row_style 3309 * gtk_cmclist_get_row_style 3310 */ 3311 void 3312 gtk_cmclist_set_cell_style (GtkCMCList *clist, 3313 gint row, 3314 gint column, 3315 GtkStyle *style) 3316 { 3317 GtkRequisition requisition = { 0 }; 3318 GtkCMCListRow *clist_row; 3319 3320 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3321 3322 if (row < 0 || row >= clist->rows) 3323 return; 3324 if (column < 0 || column >= clist->columns) 3325 return; 3326 3327 clist_row = ROW_ELEMENT (clist, row)->data; 3328 3329 if (clist_row->cell[column].style == style) 3330 return; 3331 3332 if (clist->column[column].auto_resize && 3333 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 3334 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row, 3335 column, &requisition); 3336 3337 if (clist_row->cell[column].style) 3338 { 3339 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 3340 gtk_style_detach (clist_row->cell[column].style); 3341 g_object_unref (clist_row->cell[column].style); 3342 } 3343 3344 clist_row->cell[column].style = style; 3345 3346 if (clist_row->cell[column].style) 3347 { 3348 g_object_ref (clist_row->cell[column].style); 3349 3350 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 3351 clist_row->cell[column].style = 3352 gtk_style_attach (clist_row->cell[column].style, 3353 clist->clist_window); 3354 } 3355 3356 column_auto_resize (clist, clist_row, column, requisition.width); 3357 3358 /* redraw the list if it's not frozen */ 3359 if (CLIST_UNFROZEN (clist)) 3360 { 3361 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 3362 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 3363 } 3364 } 3365 3366 GtkStyle * 3367 gtk_cmclist_get_cell_style (GtkCMCList *clist, 3368 gint row, 3369 gint column) 3370 { 3371 GtkCMCListRow *clist_row; 3372 3373 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 3374 3375 if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns) 3376 return NULL; 3377 3378 clist_row = ROW_ELEMENT (clist, row)->data; 3379 3380 return clist_row->cell[column].style; 3381 } 3382 3383 void 3384 gtk_cmclist_set_row_style (GtkCMCList *clist, 3385 gint row, 3386 GtkStyle *style) 3387 { 3388 GtkRequisition requisition; 3389 GtkCMCListRow *clist_row; 3390 gint *old_width; 3391 gint i; 3392 3393 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3394 3395 if (row < 0 || row >= clist->rows) 3396 return; 3397 3398 clist_row = ROW_ELEMENT (clist, row)->data; 3399 3400 if (clist_row->style == style) 3401 return; 3402 3403 old_width = g_new (gint, clist->columns); 3404 3405 if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 3406 { 3407 for (i = 0; i < clist->columns; i++) 3408 if (clist->column[i].auto_resize) 3409 { 3410 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row, 3411 i, &requisition); 3412 old_width[i] = requisition.width; 3413 } 3414 } 3415 3416 if (clist_row->style) 3417 { 3418 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 3419 gtk_style_detach (clist_row->style); 3420 g_object_unref (clist_row->style); 3421 } 3422 3423 clist_row->style = style; 3424 3425 if (clist_row->style) 3426 { 3427 g_object_ref (clist_row->style); 3428 3429 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 3430 clist_row->style = gtk_style_attach (clist_row->style, 3431 clist->clist_window); 3432 } 3433 3434 if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 3435 for (i = 0; i < clist->columns; i++) 3436 column_auto_resize (clist, clist_row, i, old_width[i]); 3437 3438 g_free (old_width); 3439 3440 /* redraw the list if it's not frozen */ 3441 if (CLIST_UNFROZEN (clist)) 3442 { 3443 if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 3444 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 3445 } 3446 } 3447 3448 GtkStyle * 3449 gtk_cmclist_get_row_style (GtkCMCList *clist, 3450 gint row) 3451 { 3452 GtkCMCListRow *clist_row; 3453 3454 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL); 3455 3456 if (row < 0 || row >= clist->rows) 3457 return NULL; 3458 3459 clist_row = ROW_ELEMENT (clist, row)->data; 3460 3461 return clist_row->style; 3462 } 3463 3464 /* PUBLIC SELECTION FUNCTIONS 3465 * gtk_cmclist_set_selectable 3466 * gtk_cmclist_get_selectable 3467 * gtk_cmclist_select_row 3468 * gtk_cmclist_unselect_row 3469 * gtk_cmclist_select_all 3470 * gtk_cmclist_unselect_all 3471 * gtk_cmclist_undo_selection 3472 */ 3473 void 3474 gtk_cmclist_set_selectable (GtkCMCList *clist, 3475 gint row, 3476 gboolean selectable) 3477 { 3478 GtkCMCListRow *clist_row; 3479 3480 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3481 3482 if (row < 0 || row >= clist->rows) 3483 return; 3484 3485 clist_row = ROW_ELEMENT (clist, row)->data; 3486 3487 if (selectable == clist_row->selectable) 3488 return; 3489 3490 clist_row->selectable = selectable; 3491 3492 if (!selectable && clist_row->state == GTK_STATE_SELECTED) 3493 { 3494 if (clist->anchor >= 0 && 3495 clist->selection_mode == GTK_SELECTION_MULTIPLE) 3496 { 3497 clist->drag_button = 0; 3498 remove_grab (clist); 3499 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 3500 } 3501 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 3502 row, -1, NULL); 3503 } 3504 } 3505 3506 gboolean 3507 gtk_cmclist_get_selectable (GtkCMCList *clist, 3508 gint row) 3509 { 3510 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), FALSE); 3511 3512 if (row < 0 || row >= clist->rows) 3513 return FALSE; 3514 3515 return GTK_CMCLIST_ROW (ROW_ELEMENT (clist, row))->selectable; 3516 } 3517 3518 void 3519 gtk_cmclist_select_row (GtkCMCList *clist, 3520 gint row, 3521 gint column) 3522 { 3523 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3524 3525 if (row < 0 || row >= clist->rows) 3526 return; 3527 if (column < -1 || column >= clist->columns) 3528 return; 3529 3530 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 3531 row, column, NULL); 3532 } 3533 3534 void 3535 gtk_cmclist_unselect_row (GtkCMCList *clist, 3536 gint row, 3537 gint column) 3538 { 3539 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3540 3541 if (row < 0 || row >= clist->rows) 3542 return; 3543 if (column < -1 || column >= clist->columns) 3544 return; 3545 3546 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 3547 row, column, NULL); 3548 } 3549 3550 void 3551 gtk_cmclist_select_all (GtkCMCList *clist) 3552 { 3553 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3554 3555 GTK_CMCLIST_GET_CLASS (clist)->select_all (clist); 3556 } 3557 3558 void 3559 gtk_cmclist_unselect_all (GtkCMCList *clist) 3560 { 3561 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3562 3563 GTK_CMCLIST_GET_CLASS (clist)->unselect_all (clist); 3564 } 3565 3566 void 3567 gtk_cmclist_undo_selection (GtkCMCList *clist) 3568 { 3569 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3570 3571 if (clist->selection_mode == GTK_SELECTION_MULTIPLE && 3572 (clist->undo_selection || clist->undo_unselection)) 3573 g_signal_emit (G_OBJECT (clist), clist_signals[UNDO_SELECTION], 0); 3574 } 3575 3576 /* PRIVATE SELECTION FUNCTIONS 3577 * selection_find 3578 * toggle_row 3579 * fake_toggle_row 3580 * toggle_focus_row 3581 * toggle_add_mode 3582 * real_select_row 3583 * real_unselect_row 3584 * real_select_all 3585 * real_unselect_all 3586 * fake_unselect_all 3587 * real_undo_selection 3588 * set_anchor 3589 * resync_selection 3590 * update_extended_selection 3591 * start_selection 3592 * end_selection 3593 * extend_selection 3594 * sync_selection 3595 */ 3596 static GList * 3597 selection_find (GtkCMCList *clist, 3598 gint row_number, 3599 GList *row_list_element) 3600 { 3601 return g_list_find (clist->selection, GINT_TO_POINTER (row_number)); 3602 } 3603 3604 static void 3605 toggle_row (GtkCMCList *clist, 3606 gint row, 3607 gint column, 3608 GdkEvent *event) 3609 { 3610 GtkCMCListRow *clist_row; 3611 3612 switch (clist->selection_mode) 3613 { 3614 case GTK_SELECTION_MULTIPLE: 3615 case GTK_SELECTION_SINGLE: 3616 clist_row = ROW_ELEMENT (clist, row)->data; 3617 3618 if (!clist_row) 3619 return; 3620 3621 if (clist_row->state == GTK_STATE_SELECTED) 3622 { 3623 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 3624 row, column, event); 3625 return; 3626 } 3627 break; 3628 case GTK_SELECTION_BROWSE: 3629 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 3630 row, column, event); 3631 break; 3632 default: 3633 g_assert_not_reached (); 3634 } 3635 } 3636 3637 static void 3638 fake_toggle_row (GtkCMCList *clist, 3639 gint row) 3640 { 3641 GList *work; 3642 3643 work = ROW_ELEMENT (clist, row); 3644 3645 if (!work || !GTK_CMCLIST_ROW (work)->selectable) 3646 return; 3647 3648 if (GTK_CMCLIST_ROW (work)->state == GTK_STATE_NORMAL) 3649 clist->anchor_state = GTK_CMCLIST_ROW (work)->state = GTK_STATE_SELECTED; 3650 else 3651 clist->anchor_state = GTK_CMCLIST_ROW (work)->state = GTK_STATE_NORMAL; 3652 3653 if (CLIST_UNFROZEN (clist) && 3654 gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 3655 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, 3656 GTK_CMCLIST_ROW (work)); 3657 } 3658 3659 static gboolean 3660 clist_has_grab (GtkCMCList *clist) 3661 { 3662 return (gtk_widget_has_grab (GTK_WIDGET(clist)) && 3663 gtkut_pointer_is_grabbed(GTK_WIDGET(clist))); 3664 } 3665 3666 static void 3667 toggle_focus_row (GtkCMCList *clist) 3668 { 3669 cm_return_if_fail (clist != 0); 3670 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3671 3672 if (clist_has_grab (clist) || 3673 clist->focus_row < 0 || clist->focus_row >= clist->rows) 3674 return; 3675 3676 switch (clist->selection_mode) 3677 { 3678 case GTK_SELECTION_SINGLE: 3679 toggle_row (clist, clist->focus_row, 0, NULL); 3680 break; 3681 case GTK_SELECTION_MULTIPLE: 3682 g_list_free (clist->undo_selection); 3683 g_list_free (clist->undo_unselection); 3684 clist->undo_selection = NULL; 3685 clist->undo_unselection = NULL; 3686 3687 clist->anchor = clist->focus_row; 3688 clist->drag_pos = clist->focus_row; 3689 clist->undo_anchor = clist->focus_row; 3690 3691 if (GTK_CMCLIST_ADD_MODE(clist)) 3692 fake_toggle_row (clist, clist->focus_row); 3693 else 3694 GTK_CMCLIST_GET_CLASS (clist)->fake_unselect_all (clist,clist->focus_row); 3695 3696 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 3697 break; 3698 default: 3699 break; 3700 } 3701 } 3702 3703 static void 3704 toggle_add_mode (GtkCMCList *clist) 3705 { 3706 cm_return_if_fail (clist != 0); 3707 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3708 3709 if (clist_has_grab (clist) || 3710 clist->selection_mode != GTK_SELECTION_MULTIPLE) 3711 return; 3712 3713 gtk_cmclist_undraw_focus (GTK_WIDGET (clist)); 3714 if (!GTK_CMCLIST_ADD_MODE(clist)) 3715 { 3716 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ADD_MODE); 3717 } 3718 else 3719 { 3720 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ADD_MODE); 3721 clist->anchor_state = GTK_STATE_SELECTED; 3722 } 3723 gtk_cmclist_draw_focus (GTK_WIDGET (clist)); 3724 } 3725 3726 static void 3727 real_select_row (GtkCMCList *clist, 3728 gint row, 3729 gint column, 3730 GdkEvent *event) 3731 { 3732 GtkCMCListRow *clist_row; 3733 GList *list; 3734 gint sel_row; 3735 gboolean row_selected; 3736 3737 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3738 3739 if (row < 0 || row > (clist->rows - 1)) 3740 return; 3741 3742 switch (clist->selection_mode) 3743 { 3744 case GTK_SELECTION_SINGLE: 3745 case GTK_SELECTION_BROWSE: 3746 3747 row_selected = FALSE; 3748 list = clist->selection; 3749 3750 while (list) 3751 { 3752 sel_row = GPOINTER_TO_INT (list->data); 3753 list = list->next; 3754 3755 if (row == sel_row) 3756 row_selected = TRUE; 3757 else 3758 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 3759 sel_row, column, event); 3760 } 3761 3762 if (row_selected) 3763 return; 3764 3765 default: 3766 break; 3767 } 3768 3769 clist_row = ROW_ELEMENT (clist, row)->data; 3770 3771 if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable) 3772 return; 3773 3774 clist_row->state = GTK_STATE_SELECTED; 3775 if (!clist->selection) 3776 { 3777 clist->selection = g_list_append (clist->selection, 3778 GINT_TO_POINTER (row)); 3779 clist->selection_end = clist->selection; 3780 } 3781 else 3782 clist->selection_end = 3783 g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next; 3784 3785 if (CLIST_UNFROZEN (clist) 3786 && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)) 3787 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 3788 } 3789 3790 static void 3791 real_unselect_row (GtkCMCList *clist, 3792 gint row, 3793 gint column, 3794 GdkEvent *event) 3795 { 3796 GtkCMCListRow *clist_row; 3797 3798 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3799 3800 if (row < 0 || row > (clist->rows - 1)) 3801 return; 3802 3803 clist_row = ROW_ELEMENT (clist, row)->data; 3804 3805 if (clist_row->state == GTK_STATE_SELECTED) 3806 { 3807 clist_row->state = GTK_STATE_NORMAL; 3808 3809 if (clist->selection_end && 3810 clist->selection_end->data == GINT_TO_POINTER (row)) 3811 clist->selection_end = clist->selection_end->prev; 3812 3813 clist->selection = g_list_remove (clist->selection, 3814 GINT_TO_POINTER (row)); 3815 3816 if (CLIST_UNFROZEN (clist) 3817 && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)) 3818 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row); 3819 } 3820 } 3821 3822 static void 3823 real_select_all (GtkCMCList *clist) 3824 { 3825 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3826 3827 if (clist_has_grab (clist)) 3828 return; 3829 3830 switch (clist->selection_mode) 3831 { 3832 case GTK_SELECTION_SINGLE: 3833 case GTK_SELECTION_BROWSE: 3834 return; 3835 3836 case GTK_SELECTION_MULTIPLE: 3837 g_list_free (clist->undo_selection); 3838 g_list_free (clist->undo_unselection); 3839 clist->undo_selection = NULL; 3840 clist->undo_unselection = NULL; 3841 3842 if (clist->rows && 3843 ((GtkCMCListRow *) (clist->row_list->data))->state != 3844 GTK_STATE_SELECTED) 3845 fake_toggle_row (clist, 0); 3846 3847 clist->anchor_state = GTK_STATE_SELECTED; 3848 clist->anchor = 0; 3849 clist->drag_pos = 0; 3850 clist->undo_anchor = clist->focus_row; 3851 update_extended_selection (clist, clist->rows); 3852 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 3853 return; 3854 default: 3855 g_assert_not_reached (); 3856 } 3857 } 3858 3859 static void 3860 real_unselect_all (GtkCMCList *clist) 3861 { 3862 GList *list; 3863 gint i; 3864 3865 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3866 3867 if (clist_has_grab (clist)) 3868 return; 3869 3870 switch (clist->selection_mode) 3871 { 3872 case GTK_SELECTION_BROWSE: 3873 if (clist->focus_row >= 0) 3874 { 3875 g_signal_emit (G_OBJECT (clist), 3876 clist_signals[SELECT_ROW], 0, 3877 clist->focus_row, -1, NULL); 3878 return; 3879 } 3880 break; 3881 case GTK_SELECTION_MULTIPLE: 3882 g_list_free (clist->undo_selection); 3883 g_list_free (clist->undo_unselection); 3884 clist->undo_selection = NULL; 3885 clist->undo_unselection = NULL; 3886 3887 clist->anchor = -1; 3888 clist->drag_pos = -1; 3889 clist->undo_anchor = clist->focus_row; 3890 break; 3891 default: 3892 break; 3893 } 3894 3895 list = clist->selection; 3896 while (list) 3897 { 3898 i = GPOINTER_TO_INT (list->data); 3899 list = list->next; 3900 g_signal_emit (G_OBJECT (clist), 3901 clist_signals[UNSELECT_ROW], 0, i, -1, NULL); 3902 } 3903 } 3904 3905 static void 3906 fake_unselect_all (GtkCMCList *clist, 3907 gint row) 3908 { 3909 GList *list; 3910 GList *work; 3911 gint i; 3912 3913 if (row >= 0 && (work = ROW_ELEMENT (clist, row))) 3914 { 3915 if (GTK_CMCLIST_ROW (work)->state == GTK_STATE_NORMAL && 3916 GTK_CMCLIST_ROW (work)->selectable) 3917 { 3918 GTK_CMCLIST_ROW (work)->state = GTK_STATE_SELECTED; 3919 3920 if (CLIST_UNFROZEN (clist) && 3921 gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE) 3922 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, 3923 GTK_CMCLIST_ROW (work)); 3924 } 3925 } 3926 3927 clist->undo_selection = clist->selection; 3928 clist->selection = NULL; 3929 clist->selection_end = NULL; 3930 3931 for (list = clist->undo_selection; list; list = list->next) 3932 { 3933 if ((i = GPOINTER_TO_INT (list->data)) == row || 3934 !(work = g_list_nth (clist->row_list, i))) 3935 continue; 3936 3937 GTK_CMCLIST_ROW (work)->state = GTK_STATE_NORMAL; 3938 if (CLIST_UNFROZEN (clist) && 3939 gtk_cmclist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE) 3940 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, i, 3941 GTK_CMCLIST_ROW (work)); 3942 } 3943 } 3944 3945 static void 3946 real_undo_selection (GtkCMCList *clist) 3947 { 3948 GList *work; 3949 3950 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 3951 3952 if (clist_has_grab (clist) || 3953 clist->selection_mode != GTK_SELECTION_MULTIPLE) 3954 return; 3955 3956 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 3957 3958 if (!(clist->undo_selection || clist->undo_unselection)) 3959 { 3960 gtk_cmclist_unselect_all (clist); 3961 return; 3962 } 3963 3964 for (work = clist->undo_selection; work; work = work->next) 3965 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 3966 GPOINTER_TO_INT (work->data), -1, NULL); 3967 3968 for (work = clist->undo_unselection; work; work = work->next) 3969 { 3970 /* g_print ("unselect %d\n",GPOINTER_TO_INT (work->data)); */ 3971 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 3972 GPOINTER_TO_INT (work->data), -1, NULL); 3973 } 3974 3975 if (gtk_widget_has_focus(GTK_WIDGET(clist)) && clist->focus_row != clist->undo_anchor) 3976 { 3977 gtk_cmclist_undraw_focus (GTK_WIDGET (clist)); 3978 clist->focus_row = clist->undo_anchor; 3979 gtk_cmclist_draw_focus (GTK_WIDGET (clist)); 3980 } 3981 else 3982 clist->focus_row = clist->undo_anchor; 3983 3984 clist->undo_anchor = -1; 3985 3986 g_list_free (clist->undo_selection); 3987 g_list_free (clist->undo_unselection); 3988 clist->undo_selection = NULL; 3989 clist->undo_unselection = NULL; 3990 3991 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height > 3992 clist->clist_window_height) 3993 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 3994 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0) 3995 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 3996 } 3997 3998 static void 3999 set_anchor (GtkCMCList *clist, 4000 gboolean add_mode, 4001 gint anchor, 4002 gint undo_anchor) 4003 { 4004 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 4005 4006 if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor >= 0) 4007 return; 4008 4009 g_list_free (clist->undo_selection); 4010 g_list_free (clist->undo_unselection); 4011 clist->undo_selection = NULL; 4012 clist->undo_unselection = NULL; 4013 4014 if (add_mode) 4015 fake_toggle_row (clist, anchor); 4016 else 4017 { 4018 GTK_CMCLIST_GET_CLASS (clist)->fake_unselect_all (clist, anchor); 4019 clist->anchor_state = GTK_STATE_SELECTED; 4020 } 4021 4022 clist->anchor = anchor; 4023 clist->drag_pos = anchor; 4024 clist->undo_anchor = undo_anchor; 4025 } 4026 4027 static void 4028 resync_selection (GtkCMCList *clist, 4029 GdkEvent *event) 4030 { 4031 gint i; 4032 gint e; 4033 gint row; 4034 GList *list; 4035 GtkCMCListRow *clist_row; 4036 4037 if (clist->selection_mode != GTK_SELECTION_MULTIPLE) 4038 return; 4039 4040 if (clist->anchor < 0 || clist->drag_pos < 0) 4041 return; 4042 4043 gtk_cmclist_freeze (clist); 4044 4045 i = MIN (clist->anchor, clist->drag_pos); 4046 e = MAX (clist->anchor, clist->drag_pos); 4047 4048 if (clist->undo_selection) 4049 { 4050 list = clist->selection; 4051 clist->selection = clist->undo_selection; 4052 clist->selection_end = g_list_last (clist->selection); 4053 clist->undo_selection = list; 4054 list = clist->selection; 4055 while (list) 4056 { 4057 row = GPOINTER_TO_INT (list->data); 4058 list = list->next; 4059 if (row < i || row > e) 4060 { 4061 clist_row = g_list_nth (clist->row_list, row)->data; 4062 if (clist_row->selectable) 4063 { 4064 clist_row->state = GTK_STATE_SELECTED; 4065 g_signal_emit (G_OBJECT (clist), 4066 clist_signals[UNSELECT_ROW], 0, 4067 row, -1, event); 4068 clist->undo_selection = g_list_prepend 4069 (clist->undo_selection, GINT_TO_POINTER (row)); 4070 } 4071 } 4072 } 4073 } 4074 4075 if (clist->anchor < clist->drag_pos) 4076 { 4077 for (list = g_list_nth (clist->row_list, i); i <= e; 4078 i++, list = list->next) 4079 if (GTK_CMCLIST_ROW (list)->selectable) 4080 { 4081 if (g_list_find (clist->selection, GINT_TO_POINTER(i))) 4082 { 4083 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_NORMAL) 4084 { 4085 GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED; 4086 g_signal_emit (G_OBJECT (clist), 4087 clist_signals[UNSELECT_ROW], 0, 4088 i, -1, event); 4089 clist->undo_selection = 4090 g_list_prepend (clist->undo_selection, 4091 GINT_TO_POINTER (i)); 4092 } 4093 } 4094 else if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED) 4095 { 4096 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL; 4097 clist->undo_unselection = 4098 g_list_prepend (clist->undo_unselection, 4099 GINT_TO_POINTER (i)); 4100 } 4101 } 4102 } 4103 else 4104 { 4105 for (list = g_list_nth (clist->row_list, e); i <= e; 4106 e--, list = list->prev) 4107 if (GTK_CMCLIST_ROW (list)->selectable) 4108 { 4109 if (g_list_find (clist->selection, GINT_TO_POINTER(e))) 4110 { 4111 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_NORMAL) 4112 { 4113 GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED; 4114 g_signal_emit (G_OBJECT (clist), 4115 clist_signals[UNSELECT_ROW], 0, 4116 e, -1, event); 4117 clist->undo_selection = 4118 g_list_prepend (clist->undo_selection, 4119 GINT_TO_POINTER (e)); 4120 } 4121 } 4122 else if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED) 4123 { 4124 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL; 4125 clist->undo_unselection = 4126 g_list_prepend (clist->undo_unselection, 4127 GINT_TO_POINTER (e)); 4128 } 4129 } 4130 } 4131 4132 clist->undo_unselection = g_list_reverse (clist->undo_unselection); 4133 for (list = clist->undo_unselection; list; list = list->next) 4134 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 4135 GPOINTER_TO_INT (list->data), -1, event); 4136 4137 clist->anchor = -1; 4138 clist->drag_pos = -1; 4139 4140 gtk_cmclist_thaw (clist); 4141 } 4142 4143 static void 4144 update_extended_selection (GtkCMCList *clist, 4145 gint row) 4146 { 4147 gint i; 4148 GList *list; 4149 GdkRectangle area; 4150 gint s1 = -1; 4151 gint s2 = -1; 4152 gint e1 = -1; 4153 gint e2 = -1; 4154 gint y1 = clist->clist_window_height; 4155 gint y2 = clist->clist_window_height; 4156 gint h1 = 0; 4157 gint h2 = 0; 4158 gint top; 4159 4160 if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor == -1) 4161 return; 4162 4163 if (row < 0) 4164 row = 0; 4165 if (row >= clist->rows) 4166 row = clist->rows - 1; 4167 4168 /* extending downwards */ 4169 if (row > clist->drag_pos && clist->anchor <= clist->drag_pos) 4170 { 4171 s2 = clist->drag_pos + 1; 4172 e2 = row; 4173 } 4174 /* extending upwards */ 4175 else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos) 4176 { 4177 s2 = row; 4178 e2 = clist->drag_pos - 1; 4179 } 4180 else if (row < clist->drag_pos && clist->anchor < clist->drag_pos) 4181 { 4182 e1 = clist->drag_pos; 4183 /* row and drag_pos on different sides of anchor : 4184 take back the selection between anchor and drag_pos, 4185 select between anchor and row */ 4186 if (row < clist->anchor) 4187 { 4188 s1 = clist->anchor + 1; 4189 s2 = row; 4190 e2 = clist->anchor - 1; 4191 } 4192 /* take back the selection between anchor and drag_pos */ 4193 else 4194 s1 = row + 1; 4195 } 4196 else if (row > clist->drag_pos && clist->anchor > clist->drag_pos) 4197 { 4198 s1 = clist->drag_pos; 4199 /* row and drag_pos on different sides of anchor : 4200 take back the selection between anchor and drag_pos, 4201 select between anchor and row */ 4202 if (row > clist->anchor) 4203 { 4204 e1 = clist->anchor - 1; 4205 s2 = clist->anchor + 1; 4206 e2 = row; 4207 } 4208 /* take back the selection between anchor and drag_pos */ 4209 else 4210 e1 = row - 1; 4211 } 4212 4213 clist->drag_pos = row; 4214 4215 area.x = 0; 4216 area.width = clist->clist_window_width; 4217 4218 /* restore the elements between s1 and e1 */ 4219 if (s1 >= 0) 4220 { 4221 for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1; 4222 i++, list = list->next) 4223 if (GTK_CMCLIST_ROW (list)->selectable) 4224 { 4225 if (GTK_CMCLIST_GET_CLASS (clist)->selection_find (clist, i, list)) 4226 GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED; 4227 else 4228 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL; 4229 } 4230 4231 top = ROW_TOP_YPIXEL (clist, clist->focus_row); 4232 4233 if (top + clist->row_height <= 0) 4234 { 4235 area.y = 0; 4236 area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height; 4237 draw_rows (clist, &area); 4238 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 4239 } 4240 else if (top >= clist->clist_window_height) 4241 { 4242 area.y = ROW_TOP_YPIXEL (clist, s1) - 1; 4243 area.height = clist->clist_window_height - area.y; 4244 draw_rows (clist, &area); 4245 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 4246 } 4247 else if (top < 0) 4248 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 4249 else if (top + clist->row_height > clist->clist_window_height) 4250 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 4251 4252 y1 = ROW_TOP_YPIXEL (clist, s1) - 1; 4253 h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING); 4254 } 4255 4256 /* extend the selection between s2 and e2 */ 4257 if (s2 >= 0) 4258 { 4259 for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2; 4260 i++, list = list->next) 4261 if (GTK_CMCLIST_ROW (list)->selectable && 4262 GTK_CMCLIST_ROW (list)->state != clist->anchor_state) 4263 GTK_CMCLIST_ROW (list)->state = clist->anchor_state; 4264 4265 top = ROW_TOP_YPIXEL (clist, clist->focus_row); 4266 4267 if (top + clist->row_height <= 0) 4268 { 4269 area.y = 0; 4270 area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height; 4271 draw_rows (clist, &area); 4272 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 4273 } 4274 else if (top >= clist->clist_window_height) 4275 { 4276 area.y = ROW_TOP_YPIXEL (clist, s2) - 1; 4277 area.height = clist->clist_window_height - area.y; 4278 draw_rows (clist, &area); 4279 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 4280 } 4281 else if (top < 0) 4282 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 4283 else if (top + clist->row_height > clist->clist_window_height) 4284 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 4285 4286 y2 = ROW_TOP_YPIXEL (clist, s2) - 1; 4287 h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING); 4288 } 4289 4290 area.y = MAX (0, MIN (y1, y2)); 4291 if (area.y > clist->clist_window_height) 4292 area.y = 0; 4293 area.height = MIN (clist->clist_window_height, h1 + h2); 4294 if (s1 >= 0 && s2 >= 0) 4295 area.height += (clist->row_height + CELL_SPACING); 4296 draw_rows (clist, &area); 4297 } 4298 4299 static void 4300 start_selection (GtkCMCList *clist) 4301 { 4302 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 4303 4304 if (clist_has_grab (clist)) 4305 return; 4306 4307 set_anchor (clist, GTK_CMCLIST_ADD_MODE(clist), clist->focus_row, 4308 clist->focus_row); 4309 } 4310 4311 static void 4312 end_selection (GtkCMCList *clist) 4313 { 4314 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 4315 4316 if (gtkut_pointer_is_grabbed (GTK_WIDGET (clist)) && 4317 gtk_widget_has_focus (GTK_WIDGET(clist))) 4318 return; 4319 4320 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 4321 } 4322 4323 static void 4324 extend_selection (GtkCMCList *clist, 4325 GtkScrollType scroll_type, 4326 gfloat position, 4327 gboolean auto_start_selection) 4328 { 4329 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 4330 4331 if (clist_has_grab (clist) || 4332 clist->selection_mode != GTK_SELECTION_MULTIPLE) 4333 return; 4334 4335 if (auto_start_selection) 4336 set_anchor (clist, GTK_CMCLIST_ADD_MODE(clist), clist->focus_row, 4337 clist->focus_row); 4338 else if (clist->anchor == -1) 4339 return; 4340 4341 move_focus_row (clist, scroll_type, position); 4342 4343 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height > 4344 clist->clist_window_height) 4345 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 4346 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0) 4347 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 4348 4349 update_extended_selection (clist, clist->focus_row); 4350 } 4351 4352 static void 4353 sync_selection (GtkCMCList *clist, 4354 gint row, 4355 gint mode) 4356 { 4357 GList *list; 4358 gint d; 4359 4360 if (mode == SYNC_INSERT) 4361 d = 1; 4362 else 4363 d = -1; 4364 4365 if (clist->focus_row >= row) 4366 { 4367 if (d > 0 || clist->focus_row > row) 4368 clist->focus_row += d; 4369 if (clist->focus_row == -1 && clist->rows >= 1) 4370 clist->focus_row = 0; 4371 else if (d < 0 && clist->focus_row >= clist->rows - 1) 4372 clist->focus_row = clist->rows - 2; 4373 else if (clist->focus_row >= clist->rows) /* Paranoia */ 4374 clist->focus_row = clist->rows - 1; 4375 } 4376 4377 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 4378 4379 g_list_free (clist->undo_selection); 4380 g_list_free (clist->undo_unselection); 4381 clist->undo_selection = NULL; 4382 clist->undo_unselection = NULL; 4383 4384 clist->anchor = -1; 4385 clist->drag_pos = -1; 4386 clist->undo_anchor = clist->focus_row; 4387 4388 list = clist->selection; 4389 4390 while (list) 4391 { 4392 if (GPOINTER_TO_INT (list->data) >= row) 4393 list->data = ((gchar*) list->data) + d; 4394 list = list->next; 4395 } 4396 } 4397 4398 /* GTKOBJECT 4399 * gtk_cmclist_destroy 4400 * gtk_cmclist_finalize 4401 */ 4402 static void gtk_cmclist_destroy (GtkWidget *object) 4403 4404 { 4405 gint i; 4406 GtkCMCList *clist; 4407 4408 cm_return_if_fail (GTK_IS_CMCLIST (object)); 4409 4410 clist = GTK_CMCLIST (object); 4411 4412 /* freeze the list */ 4413 clist->freeze_count++; 4414 4415 /* get rid of all the rows */ 4416 gtk_cmclist_clear (clist); 4417 4418 /* Since we don't have a _remove method, unparent the children 4419 * instead of destroying them so the focus will be unset properly. 4420 * (For other containers, the _remove method takes care of the 4421 * unparent) The destroy will happen when the refcount drops 4422 * to zero. 4423 */ 4424 4425 /* unref adjustments */ 4426 if (clist->hadjustment) 4427 { 4428 g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA, 4429 0, 0, 0, 0, clist); 4430 g_object_unref (G_OBJECT (clist->hadjustment)); 4431 clist->hadjustment = NULL; 4432 } 4433 if (clist->vadjustment) 4434 { 4435 g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA, 4436 0, 0, 0, 0, clist); 4437 g_object_unref (G_OBJECT (clist->vadjustment)); 4438 clist->vadjustment = NULL; 4439 } 4440 4441 remove_grab (clist); 4442 4443 /* destroy the column buttons */ 4444 for (i = 0; i < clist->columns; i++) 4445 if (clist->column[i].button) 4446 { 4447 gtk_widget_unparent (clist->column[i].button); 4448 clist->column[i].button = NULL; 4449 } 4450 4451 if (GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->destroy) 4452 (*GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->destroy) (object); 4453 } 4454 4455 static void 4456 gtk_cmclist_finalize (GObject *object) 4457 { 4458 GtkCMCList *clist; 4459 4460 cm_return_if_fail (GTK_IS_CMCLIST (object)); 4461 4462 clist = GTK_CMCLIST (object); 4463 4464 columns_delete (clist); 4465 4466 G_OBJECT_CLASS (gtk_cmclist_parent_class)->finalize (object); 4467 } 4468 4469 /* GTKWIDGET 4470 * gtk_cmclist_realize 4471 * gtk_cmclist_unrealize 4472 * gtk_cmclist_map 4473 * gtk_cmclist_unmap 4474 * gtk_cmclist_draw 4475 * gtk_cmclist_style_set 4476 * gtk_cmclist_button_press 4477 * gtk_cmclist_button_release 4478 * gtk_cmclist_motion 4479 * gtk_cmclist_size_request 4480 * gtk_cmclist_size_allocate 4481 */ 4482 static void 4483 gtk_cmclist_realize (GtkWidget *widget) 4484 { 4485 GtkAllocation allocation; 4486 GtkCMCList *clist; 4487 GtkStyleContext *style_context; 4488 GtkStateFlags state; 4489 GtkBorder padding; 4490 GdkWindow *window; 4491 GdkWindowAttr attributes; 4492 GtkCMCListRow *clist_row; 4493 GList *list; 4494 gint attributes_mask; 4495 gint event_mask; 4496 gint i; 4497 gint j; 4498 4499 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 4500 4501 clist = GTK_CMCLIST (widget); 4502 4503 gtk_widget_set_realized (widget, TRUE); 4504 4505 gtk_widget_get_allocation (widget, &allocation); 4506 4507 attributes.window_type = GDK_WINDOW_CHILD; 4508 attributes.x = allocation.x; 4509 attributes.y = allocation.y; 4510 attributes.width = allocation.width; 4511 attributes.height = allocation.height; 4512 attributes.wclass = GDK_INPUT_OUTPUT; 4513 attributes.visual = gtk_widget_get_visual (widget); 4514 4515 event_mask = gtk_widget_get_events (widget); 4516 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; 4517 4518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK; 4519 4520 /* main window */ 4521 window = gdk_window_new (gtk_widget_get_parent_window (widget), 4522 &attributes, attributes_mask); 4523 gtk_widget_set_window (widget, window); 4524 gtk_widget_register_window (widget, window); 4525 4526 style_context = gtk_widget_get_style_context (widget); 4527 state = gtk_widget_get_state_flags(widget); 4528 gtk_style_context_set_background (style_context, window); 4529 4530 /* column-title window */ 4531 4532 attributes.x = clist->column_title_area.x; 4533 attributes.y = clist->column_title_area.y; 4534 attributes.width = clist->column_title_area.width; 4535 attributes.height = clist->column_title_area.height; 4536 4537 clist->title_window = gdk_window_new (window, &attributes, 4538 attributes_mask); 4539 gtk_widget_register_window (widget, clist->title_window); 4540 4541 gtk_style_context_set_background (style_context, clist->title_window); 4542 gdk_window_show (clist->title_window); 4543 4544 /* set things up so column buttons are drawn in title window */ 4545 for (i = 0; i < clist->columns; i++) 4546 if (clist->column[i].button) 4547 gtk_widget_set_parent_window (clist->column[i].button, 4548 clist->title_window); 4549 4550 /* clist-window */ 4551 gtk_style_context_get_padding(style_context, state, &padding); 4552 attributes.x = (clist->internal_allocation.x + 4553 padding.left); 4554 attributes.y = (clist->internal_allocation.y + 4555 padding.top + 4556 clist->column_title_area.height); 4557 attributes.width = clist->clist_window_width; 4558 attributes.height = clist->clist_window_height; 4559 attributes.event_mask = event_mask | 4560 GDK_SCROLL_MASK | 4561 GDK_SMOOTH_SCROLL_MASK | 4562 GDK_POINTER_MOTION_MASK | 4563 GDK_KEY_RELEASE_MASK | 4564 GDK_BUTTON_PRESS_MASK | 4565 GDK_BUTTON_RELEASE_MASK; 4566 4567 clist->clist_window = gdk_window_new (window, &attributes, 4568 attributes_mask); 4569 gtk_widget_register_window (widget, clist->clist_window); 4570 4571 gdk_window_show (clist->clist_window); 4572 clist->clist_window_width = gdk_window_get_width(clist->clist_window); 4573 clist->clist_window_height = gdk_window_get_height(clist->clist_window); 4574 4575 /* create resize windows */ 4576 attributes.wclass = GDK_INPUT_ONLY; 4577 attributes_mask = GDK_WA_CURSOR; 4578 attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), 4579 GDK_SB_H_DOUBLE_ARROW); 4580 clist->cursor_drag = attributes.cursor; 4581 4582 attributes.x = LIST_WIDTH (clist) + 1; 4583 attributes.y = 0; 4584 attributes.width = 0; 4585 attributes.height = 0; 4586 attributes.event_mask = event_mask | 4587 GDK_BUTTON_PRESS_MASK | 4588 GDK_BUTTON_RELEASE_MASK | 4589 GDK_POINTER_MOTION_MASK; 4590 4591 for (i = 0; i < clist->columns; i++) 4592 { 4593 clist->column[i].window = gdk_window_new (clist->title_window, 4594 &attributes, attributes_mask); 4595 gtk_widget_register_window (widget, clist->column[i].window); 4596 } 4597 4598 /* This is slightly less efficient than creating them with the 4599 * right size to begin with, but easier 4600 */ 4601 size_allocate_title_buttons (clist); 4602 4603 /* attach optional row/cell styles, allocate foreground/background colors */ 4604 list = clist->row_list; 4605 for (i = 0; i < clist->rows; i++) 4606 { 4607 clist_row = list->data; 4608 list = list->next; 4609 4610 if (clist_row->style) 4611 clist_row->style = gtk_style_attach (clist_row->style, 4612 clist->clist_window); 4613 4614 for (j = 0; j < clist->columns; j++) 4615 if (clist_row->cell[j].style) 4616 clist_row->cell[j].style = 4617 gtk_style_attach (clist_row->cell[j].style, clist->clist_window); 4618 } 4619 } 4620 4621 static void 4622 gtk_cmclist_unrealize (GtkWidget *widget) 4623 { 4624 gint i; 4625 GtkCMCList *clist; 4626 4627 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 4628 4629 clist = GTK_CMCLIST (widget); 4630 4631 /* freeze the list */ 4632 clist->freeze_count++; 4633 4634 if (gtk_widget_get_mapped (widget)) 4635 gtk_cmclist_unmap (widget); 4636 4637 gtk_widget_set_mapped (widget, FALSE); 4638 4639 /* detach optional row/cell styles */ 4640 if (gtk_widget_get_realized (widget)) 4641 { 4642 GtkCMCListRow *clist_row; 4643 GList *list; 4644 gint j; 4645 4646 list = clist->row_list; 4647 for (i = 0; i < clist->rows; i++) 4648 { 4649 clist_row = list->data; 4650 list = list->next; 4651 4652 if (clist_row->style) 4653 gtk_style_detach (clist_row->style); 4654 for (j = 0; j < clist->columns; j++) 4655 if (clist_row->cell[j].style) 4656 gtk_style_detach (clist_row->cell[j].style); 4657 } 4658 } 4659 4660 gdk_cursor_unref (clist->cursor_drag); 4661 4662 for (i = 0; i < clist->columns; i++) 4663 { 4664 if (clist->column[i].button) 4665 gtk_widget_unrealize (clist->column[i].button); 4666 if (clist->column[i].window) 4667 { 4668 gtk_widget_unregister_window (widget, clist->column[i].window); 4669 gdk_window_destroy (clist->column[i].window); 4670 clist->column[i].window = NULL; 4671 } 4672 } 4673 4674 gtk_widget_unregister_window (widget, clist->clist_window); 4675 gdk_window_destroy (clist->clist_window); 4676 clist->clist_window = NULL; 4677 4678 gtk_widget_unregister_window (widget, clist->title_window); 4679 gdk_window_destroy (clist->title_window); 4680 clist->title_window = NULL; 4681 4682 clist->cursor_drag = NULL; 4683 4684 if (GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->unrealize) 4685 (* GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->unrealize) (widget); 4686 } 4687 4688 static void 4689 gtk_cmclist_map (GtkWidget *widget) 4690 { 4691 gint i; 4692 GtkCMCList *clist; 4693 4694 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 4695 4696 clist = GTK_CMCLIST (widget); 4697 4698 if (!gtk_widget_get_mapped (widget)) 4699 { 4700 gtk_widget_set_mapped (widget, TRUE); 4701 4702 /* map column buttons */ 4703 for (i = 0; i < clist->columns; i++) 4704 { 4705 if (clist->column[i].button && 4706 gtk_widget_get_visible (clist->column[i].button) && 4707 !gtk_widget_get_mapped (clist->column[i].button)) 4708 gtk_widget_map (clist->column[i].button); 4709 } 4710 4711 for (i = 0; i < clist->columns; i++) 4712 if (clist->column[i].window && clist->column[i].button) 4713 { 4714 gdk_window_raise (clist->column[i].window); 4715 gdk_window_show (clist->column[i].window); 4716 } 4717 4718 gdk_window_show (clist->title_window); 4719 gdk_window_show (clist->clist_window); 4720 gdk_window_show (gtk_widget_get_window (widget)); 4721 4722 /* unfreeze the list */ 4723 clist->freeze_count = 0; 4724 } 4725 } 4726 4727 static void 4728 gtk_cmclist_unmap (GtkWidget *widget) 4729 { 4730 gint i; 4731 GtkCMCList *clist; 4732 4733 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 4734 4735 clist = GTK_CMCLIST (widget); 4736 4737 if (gtk_widget_get_mapped (widget)) 4738 { 4739 gtk_widget_set_mapped (widget, FALSE); 4740 4741 if (clist_has_grab (clist)) 4742 { 4743 remove_grab (clist); 4744 4745 GTK_CMCLIST_GET_CLASS (widget)->resync_selection (clist, NULL); 4746 4747 clist->click_cell.row = -1; 4748 clist->click_cell.column = -1; 4749 clist->drag_button = 0; 4750 4751 if (GTK_CMCLIST_IN_DRAG(clist)) 4752 { 4753 gpointer drag_data; 4754 4755 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG); 4756 drag_data = g_object_get_data (G_OBJECT (clist), 4757 "gtk-site-data"); 4758 if (drag_data) 4759 g_signal_handlers_unblock_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA, 4760 0, 0, 0, 0, drag_data); 4761 } 4762 } 4763 4764 for (i = 0; i < clist->columns; i++) 4765 if (clist->column[i].window) 4766 gdk_window_hide (clist->column[i].window); 4767 4768 gdk_window_hide (clist->clist_window); 4769 gdk_window_hide (clist->title_window); 4770 gdk_window_hide (gtk_widget_get_window (widget)); 4771 4772 /* unmap column buttons */ 4773 for (i = 0; i < clist->columns; i++) 4774 if (clist->column[i].button && 4775 gtk_widget_get_mapped (clist->column[i].button)) 4776 gtk_widget_unmap (clist->column[i].button); 4777 4778 /* freeze the list */ 4779 clist->freeze_count++; 4780 } 4781 } 4782 4783 static gint 4784 gtk_cmclist_draw (GtkWidget *widget, 4785 cairo_t *cr) 4786 { 4787 GtkCMCList *clist; 4788 4789 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE); 4790 cm_return_val_if_fail (cr != NULL, FALSE); 4791 4792 if (gtk_widget_is_drawable (widget)) 4793 { 4794 clist = GTK_CMCLIST (widget); 4795 clist->draw_now = 0; 4796 4797 /* Draw clist_window */ 4798 if (gtk_cairo_should_draw_window (cr, clist->clist_window)) 4799 { 4800 GdkRectangle area; 4801 4802 /* The painting area is currently relative to GdkWindow 4803 * of the entire widget, we're only interested in the 4804 * part that is inside clist_window. */ 4805 /* First, get geometry of clist_window in coordinates 4806 * relative to the parent window. */ 4807 gdk_window_get_position(clist->clist_window, &area.x, &area.y); 4808 area.height = gdk_window_get_height(clist->clist_window); 4809 area.width = gdk_window_get_width(clist->clist_window); 4810 4811 /* Store current state of the painting area, as we will 4812 * want to use it for title_window later. */ 4813 cairo_save(cr); 4814 4815 /* Now clip the painting area to just the part that is inside 4816 * clist_window, and call draw_rows() with a GdkRectangle 4817 * corresponding to that. */ 4818 gdk_cairo_rectangle(cr, &area); 4819 cairo_clip(cr); 4820 4821 if (gdk_cairo_get_clip_rectangle (cr, &area)) 4822 { 4823 gdouble x, y; 4824 4825 /* Before we pass the area to draw_rows(), we need to 4826 * transform it to coordinates relative to clist_window. 4827 * We already made sure that it is entirely inside 4828 * this window, so no further checks have to be made. */ 4829 gdk_window_coords_from_parent(clist->clist_window, area.x, area.y, &x, &y); 4830 area.x = x; 4831 area.y = y; 4832 4833 draw_rows (clist, &area); 4834 } 4835 4836 /* Restore the original painting area for further use. */ 4837 cairo_restore(cr); 4838 } 4839 4840 /* Draw title_window - just propagate the draw event 4841 * to the individual button widgets, they can draw 4842 * themselves. */ 4843 if (gtk_cairo_should_draw_window (cr, clist->title_window)) 4844 { 4845 gint i; 4846 4847 for (i = 0; i < clist->columns; i++) 4848 { 4849 if (clist->column[i].button) { 4850 gtk_container_propagate_draw (GTK_CONTAINER (clist), clist->column[i].button, cr); 4851 } 4852 } 4853 } 4854 clist->draw_now = 1; 4855 } 4856 return FALSE; 4857 } 4858 4859 static void 4860 gtk_cmclist_style_set (GtkWidget *widget, 4861 GtkStyle *previous_style) 4862 { 4863 GtkCMCList *clist; 4864 GtkStyleContext *style_context; 4865 4866 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 4867 4868 if (GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->style_set) 4869 (*GTK_WIDGET_CLASS (gtk_cmclist_parent_class)->style_set) (widget, previous_style); 4870 4871 clist = GTK_CMCLIST (widget); 4872 4873 if (gtk_widget_get_realized (widget)) 4874 { 4875 style_context = gtk_widget_get_style_context (widget); 4876 gtk_style_context_set_background (style_context, gtk_widget_get_window (widget)); 4877 gtk_style_context_set_background (style_context, clist->title_window); 4878 } 4879 4880 /* Fill in data after widget has correct style */ 4881 4882 /* text properties */ 4883 if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist)) 4884 /* Reset clist->row_height */ 4885 gtk_cmclist_set_row_height (clist, 0); 4886 4887 /* Column widths */ 4888 if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist)) 4889 { 4890 gint width; 4891 gint i; 4892 4893 for (i = 0; i < clist->columns; i++) 4894 if (clist->column[i].auto_resize) 4895 { 4896 width = gtk_cmclist_optimal_column_width (clist, i); 4897 if (width != clist->column[i].width) 4898 gtk_cmclist_set_column_width (clist, i, width); 4899 } 4900 } 4901 } 4902 4903 static gint 4904 gtk_cmclist_button_press (GtkWidget *widget, 4905 GdkEventButton *event) 4906 { 4907 gint i; 4908 GtkCMCList *clist; 4909 gint x; 4910 gint y; 4911 gint row; 4912 gint column; 4913 gint button_actions; 4914 4915 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE); 4916 cm_return_val_if_fail (event != NULL, FALSE); 4917 4918 clist = GTK_CMCLIST (widget); 4919 4920 button_actions = clist->button_actions[event->button - 1]; 4921 4922 if (button_actions == GTK_CMBUTTON_IGNORED) 4923 return FALSE; 4924 4925 /* selections on the list */ 4926 if (event->window == clist->clist_window) 4927 { 4928 x = event->x; 4929 y = event->y; 4930 4931 if (get_selection_info (clist, x, y, &row, &column)) 4932 { 4933 gint old_row = clist->focus_row; 4934 4935 if (clist->focus_row == -1) 4936 old_row = row; 4937 4938 if (event->type == GDK_BUTTON_PRESS) 4939 { 4940 GdkEventMask mask = ((1 << (4 + event->button)) | 4941 GDK_POINTER_MOTION_HINT_MASK | 4942 GDK_BUTTON_RELEASE_MASK); 4943 4944 if (gdk_pointer_grab (clist->clist_window, FALSE, mask, 4945 NULL, NULL, event->time)) 4946 return FALSE; 4947 gtk_grab_add (widget); 4948 4949 clist->click_cell.row = row; 4950 clist->click_cell.column = column; 4951 clist->drag_button = event->button; 4952 } 4953 else 4954 { 4955 clist->click_cell.row = -1; 4956 clist->click_cell.column = -1; 4957 4958 clist->drag_button = 0; 4959 remove_grab (clist); 4960 } 4961 4962 if (button_actions & GTK_CMBUTTON_SELECTS) 4963 { 4964 if (GTK_CMCLIST_ADD_MODE(clist)) 4965 { 4966 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ADD_MODE); 4967 if (gtk_widget_has_focus(widget)) 4968 { 4969 gtk_cmclist_undraw_focus (widget); 4970 clist->focus_row = row; 4971 gtk_cmclist_draw_focus (widget); 4972 } 4973 else 4974 { 4975 clist->focus_row = row; 4976 } 4977 } 4978 else if (row != clist->focus_row) 4979 { 4980 if (gtk_widget_has_focus(widget)) 4981 { 4982 gtk_cmclist_undraw_focus (widget); 4983 clist->focus_row = row; 4984 gtk_cmclist_draw_focus (widget); 4985 } 4986 else 4987 clist->focus_row = row; 4988 } 4989 } 4990 4991 if (!gtk_widget_has_focus(widget)) 4992 gtk_widget_grab_focus (widget); 4993 4994 if (button_actions & GTK_CMBUTTON_SELECTS) 4995 { 4996 switch (clist->selection_mode) 4997 { 4998 case GTK_SELECTION_SINGLE: 4999 if (event->type != GDK_BUTTON_PRESS) 5000 { 5001 g_signal_emit (G_OBJECT (clist), 5002 clist_signals[SELECT_ROW], 0, 5003 row, column, event); 5004 clist->anchor = -1; 5005 } 5006 else 5007 clist->anchor = row; 5008 break; 5009 case GTK_SELECTION_BROWSE: 5010 g_signal_emit (G_OBJECT (clist), 5011 clist_signals[SELECT_ROW], 0, 5012 row, column, event); 5013 break; 5014 case GTK_SELECTION_MULTIPLE: 5015 if (event->type != GDK_BUTTON_PRESS) 5016 { 5017 if (clist->anchor != -1) 5018 { 5019 update_extended_selection (clist, clist->focus_row); 5020 GTK_CMCLIST_GET_CLASS (clist)->resync_selection 5021 (clist, (GdkEvent *) event); 5022 } 5023 g_signal_emit (G_OBJECT (clist), 5024 clist_signals[SELECT_ROW], 0, 5025 row, column, event); 5026 break; 5027 } 5028 5029 if (event->state & GDK_CONTROL_MASK) 5030 { 5031 if (event->state & GDK_SHIFT_MASK) 5032 { 5033 if (clist->anchor < 0) 5034 { 5035 g_list_free (clist->undo_selection); 5036 g_list_free (clist->undo_unselection); 5037 clist->undo_selection = NULL; 5038 clist->undo_unselection = NULL; 5039 clist->anchor = old_row; 5040 clist->drag_pos = old_row; 5041 clist->undo_anchor = old_row; 5042 } 5043 update_extended_selection (clist, clist->focus_row); 5044 } 5045 else 5046 { 5047 if (clist->anchor == -1) 5048 set_anchor (clist, TRUE, row, old_row); 5049 else 5050 update_extended_selection (clist, 5051 clist->focus_row); 5052 } 5053 break; 5054 } 5055 5056 if (event->state & GDK_SHIFT_MASK) 5057 { 5058 set_anchor (clist, FALSE, old_row, old_row); 5059 update_extended_selection (clist, clist->focus_row); 5060 break; 5061 } 5062 5063 if (clist->anchor == -1) 5064 set_anchor (clist, FALSE, row, old_row); 5065 else 5066 update_extended_selection (clist, clist->focus_row); 5067 break; 5068 default: 5069 break; 5070 } 5071 } 5072 } 5073 return TRUE; 5074 } 5075 5076 /* press on resize windows */ 5077 for (i = 0; i < clist->columns; i++) 5078 if (clist->column[i].resizeable && clist->column[i].window && 5079 event->window == clist->column[i].window) 5080 { 5081 gpointer drag_data; 5082 5083 if (gdk_pointer_grab (clist->column[i].window, FALSE, 5084 GDK_POINTER_MOTION_HINT_MASK | 5085 GDK_BUTTON1_MOTION_MASK | 5086 GDK_BUTTON_RELEASE_MASK, 5087 NULL, NULL, event->time)) 5088 return FALSE; 5089 5090 gtk_grab_add (widget); 5091 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_IN_DRAG); 5092 5093 /* block attached dnd signal handler */ 5094 drag_data = g_object_get_data (G_OBJECT (clist), "gtk-site-data"); 5095 if (drag_data) 5096 g_signal_handlers_block_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA, 5097 0, 0, 0, 0, drag_data); 5098 5099 if (!gtk_widget_has_focus(widget)) 5100 gtk_widget_grab_focus (widget); 5101 5102 clist->drag_pos = i; 5103 clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET + 5104 clist->column[i].area.width + CELL_SPACING); 5105 5106 return TRUE; 5107 } 5108 5109 return FALSE; 5110 } 5111 5112 static gint 5113 gtk_cmclist_button_release (GtkWidget *widget, 5114 GdkEventButton *event) 5115 { 5116 GtkCMCList *clist; 5117 gint button_actions; 5118 5119 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE); 5120 cm_return_val_if_fail (event != NULL, FALSE); 5121 5122 clist = GTK_CMCLIST (widget); 5123 5124 button_actions = clist->button_actions[event->button - 1]; 5125 if (button_actions == GTK_CMBUTTON_IGNORED) 5126 return FALSE; 5127 5128 /* release on resize windows */ 5129 if (GTK_CMCLIST_IN_DRAG(clist)) 5130 { 5131 gpointer drag_data; 5132 gint width; 5133 gint x; 5134 gint i; 5135 5136 i = clist->drag_pos; 5137 clist->drag_pos = -1; 5138 5139 /* unblock attached dnd signal handler */ 5140 drag_data = g_object_get_data (G_OBJECT (clist), "gtk-site-data"); 5141 if (drag_data) 5142 g_signal_handlers_unblock_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA, 5143 0, 0, 0, 0, drag_data); 5144 5145 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG); 5146 gtk_widget_get_pointer (widget, &x, NULL); 5147 gtk_grab_remove (widget); 5148 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), event->time); 5149 5150 if (clist->x_drag >= 0) 5151 clist_refresh(clist); 5152 5153 width = new_column_width (clist, i, &x); 5154 gtk_cmclist_set_column_width (clist, i, width); 5155 5156 return TRUE; 5157 } 5158 5159 if (clist->drag_button == event->button) 5160 { 5161 gint row; 5162 gint column; 5163 5164 clist->drag_button = 0; 5165 clist->click_cell.row = -1; 5166 clist->click_cell.column = -1; 5167 5168 remove_grab (clist); 5169 5170 if (button_actions & GTK_CMBUTTON_SELECTS) 5171 { 5172 switch (clist->selection_mode) 5173 { 5174 case GTK_SELECTION_MULTIPLE: 5175 if (!(event->state & GDK_SHIFT_MASK) || 5176 !gtk_widget_get_can_focus (widget) || 5177 event->x < 0 || event->x >= clist->clist_window_width || 5178 event->y < 0 || event->y >= clist->clist_window_height) 5179 GTK_CMCLIST_GET_CLASS (clist)->resync_selection 5180 (clist, (GdkEvent *) event); 5181 break; 5182 case GTK_SELECTION_SINGLE: 5183 if (get_selection_info (clist, event->x, event->y, 5184 &row, &column)) 5185 { 5186 if (row >= 0 && row < clist->rows && clist->anchor == row) 5187 toggle_row (clist, row, column, (GdkEvent *) event); 5188 } 5189 clist->anchor = -1; 5190 break; 5191 default: 5192 break; 5193 } 5194 } 5195 5196 return TRUE; 5197 } 5198 5199 return FALSE; 5200 } 5201 5202 static gint 5203 gtk_cmclist_motion (GtkWidget *widget, 5204 GdkEventMotion *event) 5205 { 5206 GtkCMCList *clist; 5207 gint x; 5208 gint y; 5209 gint row; 5210 gint new_width; 5211 gint button_actions = 0; 5212 guint value; 5213 5214 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE); 5215 5216 clist = GTK_CMCLIST (widget); 5217 if (!clist_has_grab (clist)) 5218 return FALSE; 5219 5220 if (clist->drag_button > 0) 5221 button_actions = clist->button_actions[clist->drag_button - 1]; 5222 5223 if (GTK_CMCLIST_IN_DRAG(clist)) 5224 { 5225 if (event->is_hint || event->window != gtk_widget_get_window (widget)) 5226 gtk_widget_get_pointer (widget, &x, NULL); 5227 else 5228 x = event->x; 5229 5230 new_width = new_column_width (clist, clist->drag_pos, &x); 5231 if (x != clist->x_drag) 5232 { 5233 /* x_drag < 0 indicates that the xor line is already invisible */ 5234 if (clist->x_drag >= 0) 5235 clist_refresh(clist); 5236 5237 clist->x_drag = x; 5238 5239 if (clist->x_drag >= 0) 5240 draw_xor_line (clist); 5241 } 5242 5243 if (new_width <= MAX (COLUMN_MIN_WIDTH + 1, 5244 clist->column[clist->drag_pos].min_width + 1)) 5245 { 5246 if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0) 5247 gtk_cmclist_moveto (clist, -1, clist->drag_pos, 0, 0); 5248 return FALSE; 5249 } 5250 if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH && 5251 new_width >= clist->column[clist->drag_pos].max_width) 5252 { 5253 if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width > 5254 clist->clist_window_width && x < 0) 5255 move_horizontal (clist, 5256 COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + 5257 new_width - clist->clist_window_width + 5258 COLUMN_INSET + CELL_SPACING); 5259 return FALSE; 5260 } 5261 } 5262 5263 if (event->is_hint || event->window != clist->clist_window) { 5264 GdkDisplay *display; 5265 GdkSeat *seat; 5266 5267 display = gdk_window_get_display(event->window); 5268 seat = gdk_display_get_default_seat(display); 5269 gdk_device_get_position(gdk_seat_get_pointer(seat), 5270 NULL, &x, &y); 5271 } 5272 else 5273 { 5274 x = event->x; 5275 y = event->y; 5276 } 5277 5278 if (GTK_CMCLIST_REORDERABLE(clist) && button_actions & GTK_CMBUTTON_DRAGS) 5279 { 5280 /* delayed drag start */ 5281 if (event->window == clist->clist_window && 5282 clist->click_cell.row >= 0 && clist->click_cell.column >= 0 && 5283 (y < 0 || y >= clist->clist_window_height || 5284 x < 0 || x >= clist->clist_window_width || 5285 y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) || 5286 y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) + 5287 clist->row_height) || 5288 x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) || 5289 x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) + 5290 clist->column[clist->click_cell.column].area.width))) 5291 { 5292 GtkTargetList *target_list; 5293 5294 target_list = gtk_target_list_new (&clist_target_table, 1); 5295 gtk_drag_begin_with_coordinates(widget, target_list, GDK_ACTION_MOVE, 5296 clist->drag_button, (GdkEvent *)event, -1, -1); 5297 5298 } 5299 return TRUE; 5300 } 5301 5302 /* horizontal autoscrolling */ 5303 if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width && 5304 (x < 0 || x >= clist->clist_window_width)) 5305 { 5306 if (clist->htimer) 5307 return FALSE; 5308 5309 clist->htimer = gdk_threads_add_timeout 5310 (SCROLL_TIME, (GSourceFunc) horizontal_timeout, clist); 5311 value = gtk_adjustment_get_value (clist->hadjustment); 5312 if (!((x < 0 && value == 0) || 5313 (x >= clist->clist_window_width && 5314 value == 5315 LIST_WIDTH (clist) - clist->clist_window_width))) 5316 { 5317 if (x < 0) 5318 move_horizontal (clist, -1 + (x/2)); 5319 else 5320 move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2); 5321 } 5322 } 5323 5324 if (GTK_CMCLIST_IN_DRAG(clist)) 5325 return FALSE; 5326 5327 /* vertical autoscrolling */ 5328 row = ROW_FROM_YPIXEL (clist, y); 5329 5330 /* don't scroll on last pixel row if it's a cell spacing */ 5331 if (y == clist->clist_window_height - 1 && 5332 y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height) 5333 return FALSE; 5334 5335 if (LIST_HEIGHT (clist) > clist->clist_window_height && 5336 (y < 0 || y >= clist->clist_window_height)) 5337 { 5338 if (clist->vtimer) 5339 return FALSE; 5340 clist->vtimer = gdk_threads_add_timeout (SCROLL_TIME, 5341 (GSourceFunc) vertical_timeout, clist); 5342 if (clist->drag_button && 5343 ((y < 0 && clist->focus_row == 0) || 5344 (y >= clist->clist_window_height && 5345 clist->focus_row == clist->rows - 1))) 5346 return FALSE; 5347 } 5348 5349 row = CLAMP (row, 0, clist->rows - 1); 5350 5351 if (button_actions & GTK_CMBUTTON_SELECTS && 5352 !g_object_get_data (G_OBJECT (widget), "gtk-site-data")) 5353 { 5354 if (row == clist->focus_row) 5355 return FALSE; 5356 5357 gtk_cmclist_undraw_focus (widget); 5358 clist->focus_row = row; 5359 gtk_cmclist_draw_focus (widget); 5360 5361 switch (clist->selection_mode) 5362 { 5363 case GTK_SELECTION_BROWSE: 5364 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 5365 clist->focus_row, -1, event); 5366 break; 5367 case GTK_SELECTION_MULTIPLE: 5368 update_extended_selection (clist, clist->focus_row); 5369 break; 5370 default: 5371 break; 5372 } 5373 } 5374 5375 if (ROW_TOP_YPIXEL(clist, row) < 0) 5376 move_vertical (clist, row, 0); 5377 else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height > 5378 clist->clist_window_height) 5379 move_vertical (clist, row, 1); 5380 5381 return FALSE; 5382 } 5383 5384 static void 5385 gtk_cmclist_get_preferred_width (GtkWidget *widget, 5386 gint *minimal_width, 5387 gint *natural_width) 5388 { 5389 GtkRequisition requisition; 5390 5391 gtk_cmclist_size_request (widget, &requisition); 5392 5393 *minimal_width = *natural_width = requisition.width; 5394 } 5395 5396 static void 5397 gtk_cmclist_get_preferred_height (GtkWidget *widget, 5398 gint *minimal_height, 5399 gint *natural_height) 5400 { 5401 GtkRequisition requisition; 5402 5403 gtk_cmclist_size_request (widget, &requisition); 5404 5405 *minimal_height = *natural_height = requisition.height; 5406 } 5407 5408 static void 5409 gtk_cmclist_size_request (GtkWidget *widget, 5410 GtkRequisition *requisition) 5411 { 5412 GtkCMCList *clist; 5413 GtkStyle *style; 5414 gint i; 5415 guint border_width; 5416 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 5417 cm_return_if_fail (requisition != NULL); 5418 5419 clist = GTK_CMCLIST (widget); 5420 style = gtk_widget_get_style (widget); 5421 5422 requisition->width = 0; 5423 requisition->height = 0; 5424 5425 /* compute the size of the column title (title) area */ 5426 clist->column_title_area.height = 0; 5427 if (GTK_CMCLIST_SHOW_TITLES(clist)) { 5428 for (i = 0; i < clist->columns; i++) 5429 if (clist->column[i].button) 5430 { 5431 GtkRequisition child_requisition; 5432 5433 gtk_widget_get_preferred_size(clist->column[i].button, 5434 &child_requisition, NULL); 5435 clist->column_title_area.height = 5436 MAX (clist->column_title_area.height, 5437 child_requisition.height); 5438 } 5439 //clist->column_title_area.height = font_height; 5440 } 5441 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); 5442 requisition->width += (style->xthickness + 5443 border_width) * 2; 5444 requisition->height += (clist->column_title_area.height + 5445 (style->ythickness + 5446 border_width) * 2); 5447 5448 /* if (!clist->hadjustment) */ 5449 requisition->width += list_requisition_width (clist); 5450 /* if (!clist->vadjustment) */ 5451 requisition->height += LIST_HEIGHT (clist); 5452 } 5453 5454 static void 5455 gtk_cmclist_size_allocate (GtkWidget *widget, 5456 GtkAllocation *allocation) 5457 { 5458 GtkStyle *style; 5459 GtkCMCList *clist; 5460 GtkAllocation clist_allocation; 5461 gint border_width; 5462 5463 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 5464 cm_return_if_fail (allocation != NULL); 5465 5466 style = gtk_widget_get_style (widget); 5467 clist = GTK_CMCLIST (widget); 5468 gtk_widget_set_allocation (widget, allocation); 5469 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); 5470 5471 if (gtk_widget_get_realized (widget)) 5472 { 5473 gdk_window_move_resize (gtk_widget_get_window (widget), 5474 allocation->x + border_width, 5475 allocation->y + border_width, 5476 allocation->width - border_width * 2, 5477 allocation->height - border_width * 2); 5478 } 5479 5480 /* use internal allocation structure for all the math 5481 * because it's easier than always subtracting the container 5482 * border width */ 5483 clist->internal_allocation.x = 0; 5484 clist->internal_allocation.y = 0; 5485 clist->internal_allocation.width = MAX (1, (gint)allocation->width - 5486 border_width * 2); 5487 clist->internal_allocation.height = MAX (1, (gint)allocation->height - 5488 border_width * 2); 5489 5490 /* allocate clist window assuming no scrollbars */ 5491 clist_allocation.x = (clist->internal_allocation.x + 5492 style->xthickness); 5493 clist_allocation.y = (clist->internal_allocation.y + 5494 style->ythickness + 5495 clist->column_title_area.height); 5496 clist_allocation.width = MAX (1, (gint)clist->internal_allocation.width - 5497 (2 * (gint)style->xthickness)); 5498 clist_allocation.height = MAX (1, (gint)clist->internal_allocation.height - 5499 (2 * (gint)style->ythickness) - 5500 (gint)clist->column_title_area.height); 5501 5502 clist->clist_window_width = clist_allocation.width; 5503 clist->clist_window_height = clist_allocation.height; 5504 5505 if (gtk_widget_get_realized (widget)) 5506 { 5507 gdk_window_move_resize (clist->clist_window, 5508 clist_allocation.x, 5509 clist_allocation.y, 5510 clist_allocation.width, 5511 clist_allocation.height); 5512 } 5513 5514 /* position the window which holds the column title buttons */ 5515 clist->column_title_area.x = style->xthickness; 5516 clist->column_title_area.y = style->ythickness; 5517 clist->column_title_area.width = clist_allocation.width; 5518 5519 if (gtk_widget_get_realized (widget)) 5520 { 5521 gdk_window_move_resize (clist->title_window, 5522 clist->column_title_area.x, 5523 clist->column_title_area.y, 5524 clist->column_title_area.width, 5525 clist->column_title_area.height); 5526 } 5527 5528 /* column button allocation */ 5529 size_allocate_columns (clist, FALSE); 5530 size_allocate_title_buttons (clist); 5531 5532 adjust_adjustments (clist, TRUE); 5533 } 5534 5535 /* GTKCONTAINER 5536 * gtk_cmclist_forall 5537 */ 5538 static void 5539 gtk_cmclist_forall (GtkContainer *container, 5540 gboolean include_internals, 5541 GtkCallback callback, 5542 gpointer callback_data) 5543 { 5544 GtkCMCList *clist; 5545 guint i; 5546 5547 cm_return_if_fail (GTK_IS_CMCLIST (container)); 5548 cm_return_if_fail (callback != NULL); 5549 5550 if (!include_internals) 5551 return; 5552 5553 clist = GTK_CMCLIST (container); 5554 5555 /* callback for the column buttons */ 5556 for (i = 0; i < clist->columns; i++) 5557 if (clist->column[i].button) 5558 (*callback) (clist->column[i].button, callback_data); 5559 } 5560 5561 /* PRIVATE DRAWING FUNCTIONS 5562 * get_cell_style 5563 * draw_cell_pixbuf 5564 * draw_row 5565 * draw_rows 5566 * draw_xor_line 5567 * clist_refresh 5568 */ 5569 static void 5570 get_cell_style (GtkCMCList *clist, 5571 GtkCMCListRow *clist_row, 5572 gint state, 5573 gint column, 5574 GtkStyle **style) 5575 { 5576 GtkStyle *gtkstyle; 5577 5578 if (clist_row->cell[column].style) 5579 { 5580 if (style) 5581 *style = clist_row->cell[column].style; 5582 } 5583 else if (clist_row->style) 5584 { 5585 if (style) 5586 *style = clist_row->style; 5587 } 5588 else 5589 { 5590 gtkstyle = gtk_widget_get_style (GTK_WIDGET (clist)); 5591 if (style) 5592 *style = gtkstyle; 5593 } 5594 } 5595 5596 static gint 5597 draw_cell_pixbuf (GdkWindow *window, 5598 GdkRectangle *clip_rectangle, 5599 cairo_t *cr, 5600 GdkPixbuf *pixbuf, 5601 gint x, 5602 gint y, 5603 gint width, 5604 gint height) 5605 { 5606 gint xsrc = 0; 5607 gint ysrc = 0; 5608 5609 if (!pixbuf || (width == 0 && height == 0)) 5610 return x; 5611 5612 if (x < clip_rectangle->x) 5613 { 5614 xsrc = clip_rectangle->x - x; 5615 width -= xsrc; 5616 x = clip_rectangle->x; 5617 } 5618 if (x + width > clip_rectangle->x + clip_rectangle->width) 5619 width = clip_rectangle->x + clip_rectangle->width - x; 5620 5621 if (y < clip_rectangle->y) 5622 { 5623 ysrc = clip_rectangle->y - y; 5624 height -= ysrc; 5625 y = clip_rectangle->y; 5626 } 5627 5628 if (y + height > clip_rectangle->y + clip_rectangle->height) 5629 height = clip_rectangle->y + clip_rectangle->height - y; 5630 5631 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y); 5632 cairo_paint(cr); 5633 5634 return x + MAX (width, 0); 5635 } 5636 5637 static void cairo_dash_from_add_mode(GtkCMCList *clist, cairo_t *cr) 5638 { 5639 const double dashes[] = { 4.0, 4.0 }; 5640 if (GTK_CMCLIST_ADD_MODE(clist)) 5641 cairo_set_dash(cr, dashes, 2, 0); 5642 else 5643 cairo_set_dash(cr, NULL, 0, 0); 5644 } 5645 5646 static void 5647 draw_row (GtkCMCList *clist, 5648 GdkRectangle *area, 5649 gint row, 5650 GtkCMCListRow *clist_row) 5651 { 5652 GtkStyle *style; 5653 GtkWidget *widget; 5654 GdkRectangle *rect; 5655 GdkRectangle row_rectangle; 5656 GdkRectangle cell_rectangle; 5657 GdkRectangle clip_rectangle; 5658 GdkRectangle intersect_rectangle; 5659 gint last_column; 5660 gint state; 5661 gint i; 5662 cairo_t *cr; 5663 cm_return_if_fail (clist != NULL); 5664 5665 if (clist->draw_now) { 5666 gtk_widget_queue_draw(GTK_WIDGET(clist)); 5667 return; 5668 } 5669 5670 /* bail now if we arn't drawable yet */ 5671 if (!gtk_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows) 5672 return; 5673 5674 widget = GTK_WIDGET (clist); 5675 5676 /* if the function is passed the pointer to the row instead of null, 5677 * it avoids this expensive lookup */ 5678 if (!clist_row) 5679 clist_row = ROW_ELEMENT (clist, row)->data; 5680 5681 style = clist_row->style ? clist_row->style : gtk_widget_get_style (widget); 5682 5683 /* rectangle of the entire row */ 5684 row_rectangle.x = 0; 5685 row_rectangle.y = ROW_TOP_YPIXEL (clist, row); 5686 row_rectangle.width = clist->clist_window_width; 5687 row_rectangle.height = clist->row_height; 5688 5689 /* rectangle of the cell spacing above the row */ 5690 cell_rectangle.x = 0; 5691 cell_rectangle.y = row_rectangle.y - CELL_SPACING; 5692 cell_rectangle.width = row_rectangle.width; 5693 cell_rectangle.height = CELL_SPACING; 5694 5695 /* rectangle used to clip drawing operations, its y and height 5696 * positions only need to be set once, so we set them once here. 5697 * the x and width are set withing the drawing loop below once per 5698 * column */ 5699 clip_rectangle.y = row_rectangle.y; 5700 clip_rectangle.height = row_rectangle.height; 5701 5702 state = clist_row->state; 5703 cr = gdk_cairo_create(clist->clist_window); 5704 5705 /* draw the cell borders and background */ 5706 if (area) 5707 { 5708 rect = &intersect_rectangle; 5709 if (gdk_rectangle_intersect (area, &cell_rectangle, 5710 &intersect_rectangle)) { 5711 gdk_cairo_rectangle(cr, &intersect_rectangle); 5712 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]); 5713 cairo_fill(cr); 5714 } 5715 5716 /* the last row has to clear its bottom cell spacing too */ 5717 if (clist_row == clist->row_list_end->data) 5718 { 5719 cell_rectangle.y += clist->row_height + CELL_SPACING; 5720 5721 if (gdk_rectangle_intersect (area, &cell_rectangle, 5722 &intersect_rectangle)) { 5723 gdk_cairo_rectangle(cr, &intersect_rectangle); 5724 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]); 5725 cairo_fill(cr); 5726 } 5727 } 5728 5729 if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle)) 5730 return; 5731 5732 } 5733 else 5734 { 5735 rect = &clip_rectangle; 5736 gdk_cairo_rectangle(cr, &cell_rectangle); 5737 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]); 5738 cairo_fill(cr); 5739 5740 /* the last row has to clear its bottom cell spacing too */ 5741 if (clist_row == clist->row_list_end->data) 5742 { 5743 cell_rectangle.y += clist->row_height + CELL_SPACING; 5744 gdk_cairo_rectangle(cr, &cell_rectangle); 5745 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]); 5746 cairo_fill(cr); 5747 } 5748 } 5749 5750 for (last_column = clist->columns - 1; 5751 last_column >= 0 && !clist->column[last_column].visible; last_column--) 5752 ; 5753 5754 /* iterate and draw all the columns (row cells) and draw their contents */ 5755 for (i = 0; i < clist->columns; i++) 5756 { 5757 GtkStyle *style; 5758 PangoLayout *layout; 5759 PangoRectangle logical_rect; 5760 5761 gint width; 5762 gint height; 5763 gint pixbuf_width; 5764 gint offset = 0; 5765 5766 if (!clist->column[i].visible) 5767 continue; 5768 5769 get_cell_style (clist, clist_row, state, i, &style); 5770 5771 clip_rectangle.x = clist->column[i].area.x + clist->hoffset; 5772 clip_rectangle.width = clist->column[i].area.width; 5773 5774 /* calculate clipping region clipping region */ 5775 clip_rectangle.x -= COLUMN_INSET + CELL_SPACING; 5776 clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING + 5777 (i == last_column) * CELL_SPACING); 5778 5779 if (area && !gdk_rectangle_intersect (area, &clip_rectangle, 5780 &intersect_rectangle)) 5781 continue; 5782 5783 gdk_cairo_rectangle(cr, rect); 5784 gdk_cairo_set_source_color(cr, &style->base[state]); 5785 cairo_fill(cr); 5786 5787 clip_rectangle.x += COLUMN_INSET + CELL_SPACING; 5788 clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING + 5789 (i == last_column) * CELL_SPACING); 5790 5791 5792 /* calculate real width for column justification */ 5793 5794 layout = _gtk_cmclist_create_cell_layout (clist, clist_row, i); 5795 if (layout) 5796 { 5797 pango_layout_get_pixel_extents (layout, NULL, &logical_rect); 5798 width = logical_rect.width; 5799 } 5800 else 5801 width = 0; 5802 5803 pixbuf_width = 0; 5804 height = 0; 5805 offset = 0; 5806 switch (clist_row->cell[i].type) 5807 { 5808 case GTK_CMCELL_PIXBUF: 5809 pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf); 5810 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf); 5811 width += pixbuf_width; 5812 break; 5813 case GTK_CMCELL_PIXTEXT: 5814 pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf); 5815 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf); 5816 width += pixbuf_width + GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing; 5817 break; 5818 default: 5819 break; 5820 } 5821 5822 switch (clist->column[i].justification) 5823 { 5824 case GTK_JUSTIFY_LEFT: 5825 offset = clip_rectangle.x + clist_row->cell[i].horizontal; 5826 break; 5827 case GTK_JUSTIFY_RIGHT: 5828 offset = (clip_rectangle.x + clist_row->cell[i].horizontal + 5829 clip_rectangle.width - width); 5830 break; 5831 case GTK_JUSTIFY_CENTER: 5832 case GTK_JUSTIFY_FILL: 5833 offset = (clip_rectangle.x + clist_row->cell[i].horizontal + 5834 (clip_rectangle.width / 2) - (width / 2)); 5835 break; 5836 }; 5837 5838 /* Draw Text and/or Pixbuf */ 5839 switch (clist_row->cell[i].type) 5840 { 5841 case GTK_CMCELL_PIXBUF: 5842 draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr, 5843 GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf, 5844 offset, 5845 clip_rectangle.y + clist_row->cell[i].vertical + 5846 (clip_rectangle.height - height) / 2, 5847 pixbuf_width, height); 5848 break; 5849 case GTK_CMCELL_PIXTEXT: 5850 offset = 5851 draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr, 5852 GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf, 5853 offset, 5854 clip_rectangle.y + clist_row->cell[i].vertical+ 5855 (clip_rectangle.height - height) / 2, 5856 pixbuf_width, height); 5857 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing; 5858 5859 /* Fall through */ 5860 case GTK_CMCELL_TEXT: 5861 if (layout) 5862 { 5863 gint row_center_offset = (clist->row_height - logical_rect.height - 1) / 2; 5864 gdk_cairo_set_source_color(cr, clist_row->fg_set ? &clist_row->foreground : &style->text[state]); 5865 cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical); 5866 pango_cairo_show_layout(cr, layout); 5867 g_object_unref (G_OBJECT (layout)); 5868 } 5869 break; 5870 default: 5871 break; 5872 } 5873 } 5874 5875 /* draw focus rectangle */ 5876 cairo_dash_from_add_mode(clist, cr); 5877 cairo_set_line_width(cr, 1.0); 5878 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); 5879 if (clist->focus_row == row && 5880 gtk_widget_get_can_focus (widget) && gtk_widget_has_focus(widget)) 5881 { 5882 if (!area) { 5883 cairo_rectangle(cr, row_rectangle.x, row_rectangle.y, 5884 row_rectangle.width + 1, row_rectangle.height); 5885 gdk_cairo_set_source_color(cr, &style->text[GTK_STATE_NORMAL]); 5886 cairo_stroke(cr); 5887 } 5888 else if (gdk_rectangle_intersect (area, &row_rectangle, 5889 &intersect_rectangle)) 5890 { 5891 cairo_rectangle(cr, row_rectangle.x, row_rectangle.y, 5892 row_rectangle.width + 1, row_rectangle.height); 5893 gdk_cairo_set_source_color(cr, &style->text[GTK_STATE_NORMAL]); 5894 cairo_stroke(cr); 5895 } 5896 } 5897 cairo_destroy(cr); 5898 5899 } 5900 5901 static void 5902 draw_rows (GtkCMCList *clist, 5903 GdkRectangle *area) 5904 { 5905 GList *list; 5906 GtkCMCListRow *clist_row; 5907 gint i; 5908 gint first_row; 5909 gint last_row; 5910 5911 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 5912 5913 if (clist->row_height == 0 || 5914 !gtk_widget_is_drawable (GTK_WIDGET(clist))) 5915 return; 5916 5917 if (area) 5918 { 5919 first_row = ROW_FROM_YPIXEL (clist, area->y); 5920 last_row = ROW_FROM_YPIXEL (clist, area->y + area->height); 5921 } 5922 else 5923 { 5924 first_row = ROW_FROM_YPIXEL (clist, 0); 5925 last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height); 5926 } 5927 5928 /* this is a small special case which exposes the bottom cell line 5929 * on the last row -- it might go away if I change the wall the cell 5930 * spacings are drawn 5931 */ 5932 if (clist->rows == first_row) 5933 first_row--; 5934 5935 list = ROW_ELEMENT (clist, first_row); 5936 i = first_row; 5937 while (list) 5938 { 5939 clist_row = list->data; 5940 list = list->next; 5941 5942 if (i > last_row) 5943 return; 5944 5945 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, i, clist_row); 5946 i++; 5947 } 5948 5949 if (!area) { 5950 if (!clist->draw_now) { 5951 int w, h, y; 5952 cairo_t *cr; 5953 w = gdk_window_get_width(clist->clist_window); 5954 h = gdk_window_get_height(clist->clist_window); 5955 cr = gdk_cairo_create(clist->clist_window); 5956 y = ROW_TOP_YPIXEL (clist, i); 5957 gdk_cairo_set_source_color(cr, >k_widget_get_style(GTK_WIDGET(clist))->base[GTK_STATE_NORMAL]); 5958 cairo_rectangle(cr, 0, y, w, h - y); 5959 cairo_fill(cr); 5960 cairo_destroy(cr); 5961 } else { 5962 gtk_widget_queue_draw(GTK_WIDGET(clist)); 5963 } 5964 } 5965 } 5966 5967 static void 5968 draw_xor_line (GtkCMCList *clist) 5969 { 5970 cairo_t *cr; 5971 cr = gdk_cairo_create(clist->clist_window); 5972 cairo_set_line_width(cr, 1.0); 5973 cairo_move_to(cr, clist->x_drag, 5974 gtk_widget_get_style (GTK_WIDGET(clist))->ythickness); 5975 cairo_line_to(cr, clist->x_drag, 5976 clist->column_title_area.height + 5977 clist->clist_window_height + 1); 5978 cairo_stroke(cr); 5979 cairo_destroy(cr); 5980 } 5981 5982 static void 5983 clist_refresh (GtkCMCList *clist) 5984 { 5985 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 5986 5987 if (CLIST_UNFROZEN (clist)) 5988 { 5989 adjust_adjustments (clist, FALSE); 5990 draw_rows (clist, NULL); 5991 } 5992 } 5993 5994 /* get cell from coordinates 5995 * get_selection_info 5996 * gtk_cmclist_get_selection_info 5997 */ 5998 static gint 5999 get_selection_info (GtkCMCList *clist, 6000 gint x, 6001 gint y, 6002 gint *row, 6003 gint *column) 6004 { 6005 gint trow, tcol; 6006 6007 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 6008 6009 /* bounds checking, return false if the user clicked 6010 * on a blank area */ 6011 trow = ROW_FROM_YPIXEL (clist, y); 6012 if (trow >= clist->rows) 6013 return 0; 6014 6015 if (row) 6016 *row = trow; 6017 6018 tcol = COLUMN_FROM_XPIXEL (clist, x); 6019 if (tcol >= clist->columns) 6020 return 0; 6021 6022 if (column) 6023 *column = tcol; 6024 6025 return 1; 6026 } 6027 6028 gint 6029 gtk_cmclist_get_selection_info (GtkCMCList *clist, 6030 gint x, 6031 gint y, 6032 gint *row, 6033 gint *column) 6034 { 6035 cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0); 6036 return get_selection_info (clist, x, y, row, column); 6037 } 6038 6039 /* PRIVATE ADJUSTMENT FUNCTIONS 6040 * adjust_adjustments 6041 * vadjustment_changed 6042 * hadjustment_changed 6043 * vadjustment_value_changed 6044 * hadjustment_value_changed 6045 * check_exposures 6046 */ 6047 static void 6048 adjust_adjustments (GtkCMCList *clist, 6049 gboolean block_resize) 6050 { 6051 if (clist->vadjustment) 6052 { 6053 g_object_freeze_notify(G_OBJECT(clist->vadjustment)); 6054 gtk_adjustment_set_page_size (clist->vadjustment, clist->clist_window_height); 6055 gtk_adjustment_set_step_increment (clist->vadjustment, clist->row_height); 6056 gtk_adjustment_set_page_increment (clist->vadjustment, 6057 MAX (clist->clist_window_height - clist->row_height, 6058 clist->clist_window_height / 2)); 6059 gtk_adjustment_set_lower (clist->vadjustment, 0); 6060 gtk_adjustment_set_upper (clist->vadjustment, LIST_HEIGHT (clist)); 6061 g_object_thaw_notify(G_OBJECT(clist->vadjustment)); 6062 6063 if ((clist->clist_window_height - clist->voffset) > LIST_HEIGHT (clist) || 6064 (clist->voffset + (gint)gtk_adjustment_get_value (clist->vadjustment)) != 0) 6065 { 6066 gtk_adjustment_set_value (clist->vadjustment, 6067 MAX (0, (LIST_HEIGHT (clist) - clist->clist_window_height))); 6068 g_signal_emit_by_name (G_OBJECT (clist->vadjustment), 6069 "value_changed"); 6070 } 6071 g_signal_emit_by_name (G_OBJECT (clist->vadjustment), "changed"); 6072 } 6073 6074 if (clist->hadjustment) 6075 { 6076 g_object_freeze_notify(G_OBJECT(clist->hadjustment)); 6077 gtk_adjustment_set_page_size (clist->hadjustment, clist->clist_window_width); 6078 gtk_adjustment_set_step_increment (clist->hadjustment, 10); 6079 gtk_adjustment_set_page_increment (clist->hadjustment, 6080 MAX (clist->clist_window_width - 6081 gtk_adjustment_get_step_increment (clist->hadjustment), 6082 clist->clist_window_width / 2)); 6083 gtk_adjustment_set_lower (clist->hadjustment, 0); 6084 gtk_adjustment_set_upper (clist->hadjustment, LIST_WIDTH (clist)); 6085 g_object_thaw_notify(G_OBJECT(clist->hadjustment)); 6086 6087 if ((clist->clist_window_width - clist->hoffset) > LIST_WIDTH (clist) || 6088 (clist->hoffset + (gint)gtk_adjustment_get_value (clist->hadjustment)) != 0) 6089 { 6090 gtk_adjustment_set_value (clist->hadjustment, MAX (0, (LIST_WIDTH (clist) - 6091 clist->clist_window_width))); 6092 g_signal_emit_by_name (G_OBJECT (clist->hadjustment), 6093 "value_changed"); 6094 } 6095 g_signal_emit_by_name (G_OBJECT (clist->hadjustment), "changed"); 6096 } 6097 6098 if (!block_resize && (!clist->vadjustment || !clist->hadjustment)) 6099 { 6100 GtkWidget *widget; 6101 GtkRequisition requisition; 6102 GtkAllocation allocation; 6103 6104 widget = GTK_WIDGET (clist); 6105 gtk_widget_get_preferred_size(widget, &requisition, NULL); 6106 gtk_widget_get_allocation (widget, &allocation); 6107 6108 if ((!clist->hadjustment && 6109 requisition.width != allocation.width) || 6110 (!clist->vadjustment && 6111 requisition.height != allocation.height)) 6112 gtk_widget_queue_resize (widget); 6113 } 6114 } 6115 6116 static void 6117 vadjustment_value_changed (GtkAdjustment *adjustment, 6118 gpointer data) 6119 { 6120 GtkCMCList *clist; 6121 gint dy, value; 6122 6123 cm_return_if_fail (adjustment != NULL); 6124 cm_return_if_fail (GTK_IS_CMCLIST (data)); 6125 6126 clist = GTK_CMCLIST (data); 6127 6128 if (adjustment != clist->vadjustment) 6129 return; 6130 6131 value = -gtk_adjustment_get_value (adjustment); 6132 dy = value - clist->voffset; 6133 clist->voffset = value; 6134 6135 if (gtk_widget_is_drawable (GTK_WIDGET(clist))) 6136 { 6137 gdk_window_scroll (clist->clist_window, 0, dy); 6138 } 6139 6140 return; 6141 } 6142 6143 typedef struct 6144 { 6145 GdkWindow *window; 6146 gint dx; 6147 } ScrollData; 6148 6149 /* The window to which widget->window is relative */ 6150 #define ALLOCATION_WINDOW(widget) \ 6151 (!gtk_widget_get_has_window (widget) ? \ 6152 gtk_widget_get_window (widget) : \ 6153 gdk_window_get_parent (gtk_widget_get_window(widget))) 6154 6155 static void 6156 adjust_allocation_recurse (GtkWidget *widget, 6157 gpointer data) 6158 { 6159 GtkAllocation allocation; 6160 ScrollData *scroll_data = data; 6161 6162 gtk_widget_get_allocation (widget, &allocation); 6163 6164 if (!gtk_widget_get_realized (widget)) 6165 { 6166 if (gtk_widget_get_visible (widget)) 6167 { 6168 GdkRectangle tmp_rectangle = allocation; 6169 tmp_rectangle.x += scroll_data->dx; 6170 6171 gtk_widget_size_allocate (widget, &tmp_rectangle); 6172 } 6173 } 6174 else 6175 { 6176 if (ALLOCATION_WINDOW (widget) == scroll_data->window) 6177 { 6178 allocation.x += scroll_data->dx; 6179 gtk_widget_set_allocation (widget, &allocation); 6180 6181 if (GTK_IS_CONTAINER (widget)) 6182 gtk_container_forall (GTK_CONTAINER (widget), 6183 adjust_allocation_recurse, 6184 data); 6185 } 6186 } 6187 } 6188 6189 static void 6190 adjust_allocation (GtkWidget *widget, 6191 gint dx) 6192 { 6193 ScrollData scroll_data; 6194 6195 if (gtk_widget_get_realized (widget)) 6196 scroll_data.window = ALLOCATION_WINDOW (widget); 6197 else 6198 scroll_data.window = NULL; 6199 6200 scroll_data.dx = dx; 6201 6202 adjust_allocation_recurse (widget, &scroll_data); 6203 } 6204 6205 static void 6206 hadjustment_value_changed (GtkAdjustment *adjustment, 6207 gpointer data) 6208 { 6209 GtkCMCList *clist; 6210 GtkContainer *container; 6211 gint i; 6212 gint y = 0; 6213 gint value; 6214 gint dx; 6215 cairo_t *cr; 6216 6217 cm_return_if_fail (adjustment != NULL); 6218 cm_return_if_fail (GTK_IS_CMCLIST (data)); 6219 6220 clist = GTK_CMCLIST (data); 6221 container = GTK_CONTAINER (data); 6222 6223 if (adjustment != clist->hadjustment) 6224 return; 6225 6226 value = gtk_adjustment_get_value (adjustment); 6227 6228 dx = -value - clist->hoffset; 6229 6230 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 6231 gdk_window_scroll (clist->title_window, dx, 0); 6232 6233 /* adjust the column button's allocations */ 6234 for (i = 0; i < clist->columns; i++) 6235 if (clist->column[i].button) 6236 adjust_allocation (clist->column[i].button, dx); 6237 6238 clist->hoffset = -value; 6239 6240 cr = gdk_cairo_create(clist->clist_window); 6241 cairo_dash_from_add_mode(clist, cr); 6242 cairo_set_line_width(cr, 1.0); 6243 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); 6244 if (gtk_widget_is_drawable (GTK_WIDGET(clist))) 6245 { 6246 GtkWidget *focus_child = gtk_container_get_focus_child (container); 6247 6248 gdk_window_scroll (clist->clist_window, dx, 0); 6249 6250 if (gtk_widget_get_can_focus(GTK_WIDGET(clist)) && 6251 gtk_widget_has_focus(GTK_WIDGET(clist)) && 6252 !focus_child && GTK_CMCLIST_ADD_MODE(clist)) 6253 { 6254 y = ROW_TOP_YPIXEL (clist, clist->focus_row); 6255 cairo_rectangle(cr, 0, y, clist->clist_window_width + 1, 6256 clist->row_height); 6257 cairo_stroke(cr); 6258 } 6259 6260 if (gtk_widget_get_can_focus(GTK_WIDGET(clist)) && 6261 gtk_widget_has_focus(GTK_WIDGET(clist)) && 6262 !focus_child) 6263 { 6264 if (GTK_CMCLIST_ADD_MODE(clist)) 6265 { 6266 gint focus_row; 6267 6268 focus_row = clist->focus_row; 6269 clist->focus_row = -1; 6270 draw_rows (clist, NULL); 6271 clist->focus_row = focus_row; 6272 6273 cairo_rectangle(cr, 0, y, clist->clist_window_width + 1, 6274 clist->row_height); 6275 cairo_stroke(cr); 6276 return; 6277 } 6278 } 6279 } 6280 cairo_destroy(cr); 6281 } 6282 6283 /* PRIVATE 6284 * Memory Allocation/Distruction Routines for GtkCMCList stuctures 6285 * 6286 * functions: 6287 * columns_new 6288 * column_title_new 6289 * columns_delete 6290 * row_new 6291 * row_delete 6292 */ 6293 static GtkCMCListColumn * 6294 columns_new (GtkCMCList *clist) 6295 { 6296 GtkCMCListColumn *column; 6297 gint i; 6298 6299 column = g_new (GtkCMCListColumn, clist->columns); 6300 6301 for (i = 0; i < clist->columns; i++) 6302 { 6303 column[i].area.x = 0; 6304 column[i].area.y = 0; 6305 column[i].area.width = 0; 6306 column[i].area.height = 0; 6307 column[i].title = NULL; 6308 column[i].button = NULL; 6309 column[i].window = NULL; 6310 column[i].width = 0; 6311 column[i].min_width = -1; 6312 column[i].max_width = -1; 6313 column[i].visible = TRUE; 6314 column[i].width_set = FALSE; 6315 column[i].resizeable = TRUE; 6316 column[i].auto_resize = FALSE; 6317 column[i].button_passive = FALSE; 6318 column[i].justification = GTK_JUSTIFY_LEFT; 6319 } 6320 6321 return column; 6322 } 6323 6324 static void 6325 column_title_new (GtkCMCList *clist, 6326 gint column, 6327 const gchar *title) 6328 { 6329 g_free (clist->column[column].title); 6330 6331 clist->column[column].title = g_strdup (title); 6332 } 6333 6334 static void 6335 columns_delete (GtkCMCList *clist) 6336 { 6337 gint i; 6338 6339 for (i = 0; i < clist->columns; i++) 6340 g_free (clist->column[i].title); 6341 6342 g_free (clist->column); 6343 } 6344 6345 static GtkCMCListRow * 6346 row_new (GtkCMCList *clist) 6347 { 6348 int i; 6349 GtkCMCListRow *clist_row; 6350 6351 clist_row = g_slice_new (GtkCMCListRow); 6352 clist_row->cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns); 6353 6354 for (i = 0; i < clist->columns; i++) 6355 { 6356 clist_row->cell[i].type = GTK_CMCELL_EMPTY; 6357 clist_row->cell[i].vertical = 0; 6358 clist_row->cell[i].horizontal = 0; 6359 clist_row->cell[i].style = NULL; 6360 } 6361 6362 clist_row->fg_set = FALSE; 6363 clist_row->bg_set = FALSE; 6364 clist_row->style = NULL; 6365 clist_row->selectable = TRUE; 6366 clist_row->state = GTK_STATE_NORMAL; 6367 clist_row->data = NULL; 6368 clist_row->destroy = NULL; 6369 6370 return clist_row; 6371 } 6372 6373 static void 6374 row_delete (GtkCMCList *clist, 6375 GtkCMCListRow *clist_row) 6376 { 6377 gint i; 6378 6379 for (i = 0; i < clist->columns; i++) 6380 { 6381 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents 6382 (clist, clist_row, i, GTK_CMCELL_EMPTY, NULL, 0, NULL); 6383 if (clist_row->cell[i].style) 6384 { 6385 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 6386 gtk_style_detach (clist_row->cell[i].style); 6387 g_object_unref (clist_row->cell[i].style); 6388 } 6389 } 6390 6391 if (clist_row->style) 6392 { 6393 if (gtk_widget_get_realized (GTK_WIDGET(clist))) 6394 gtk_style_detach (clist_row->style); 6395 g_object_unref (clist_row->style); 6396 } 6397 6398 if (clist_row->destroy) 6399 clist_row->destroy (clist_row->data); 6400 6401 g_slice_free1 (sizeof (GtkCMCell) * clist->columns, clist_row->cell); 6402 g_slice_free (GtkCMCListRow, clist_row); 6403 } 6404 6405 /* FOCUS FUNCTIONS 6406 * gtk_cmclist_focus_content_area 6407 * gtk_cmclist_focus 6408 * gtk_cmclist_draw_focus 6409 * gtk_cmclist_focus_in 6410 * gtk_cmclist_focus_out 6411 * title_focus 6412 */ 6413 static void 6414 gtk_cmclist_focus_content_area (GtkCMCList *clist) 6415 { 6416 if (clist->focus_row < 0) 6417 { 6418 clist->focus_row = 0; 6419 6420 if ((clist->selection_mode == GTK_SELECTION_BROWSE || 6421 clist->selection_mode == GTK_SELECTION_MULTIPLE) && 6422 !clist->selection) 6423 g_signal_emit (G_OBJECT (clist), 6424 clist_signals[SELECT_ROW], 0, 6425 clist->focus_row, -1, NULL); 6426 } 6427 gtk_widget_grab_focus (GTK_WIDGET (clist)); 6428 } 6429 6430 static gboolean 6431 gtk_cmclist_focus (GtkWidget *widget, 6432 GtkDirectionType direction) 6433 { 6434 GtkCMCList *clist = GTK_CMCLIST (widget); 6435 GtkWidget *focus_child; 6436 gboolean is_current_focus; 6437 6438 if (!gtk_widget_is_sensitive (widget)) 6439 return FALSE; 6440 6441 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget)); 6442 6443 is_current_focus = gtk_widget_is_focus (GTK_WIDGET (clist)); 6444 6445 if (focus_child && 6446 gtk_widget_child_focus (focus_child, direction)) 6447 return TRUE; 6448 6449 switch (direction) 6450 { 6451 case GTK_DIR_LEFT: 6452 case GTK_DIR_RIGHT: 6453 if (focus_child) 6454 { 6455 if (title_focus_move (clist, direction)) 6456 return TRUE; 6457 } 6458 else if (!is_current_focus) 6459 { 6460 gtk_cmclist_focus_content_area (clist); 6461 return TRUE; 6462 } 6463 break; 6464 case GTK_DIR_DOWN: 6465 case GTK_DIR_TAB_FORWARD: 6466 if (!focus_child && !is_current_focus) 6467 { 6468 if (title_focus_in (clist, direction)) 6469 return TRUE; 6470 } 6471 6472 if (!is_current_focus && clist->rows) 6473 { 6474 gtk_cmclist_focus_content_area (clist); 6475 return TRUE; 6476 } 6477 break; 6478 case GTK_DIR_UP: 6479 case GTK_DIR_TAB_BACKWARD: 6480 if (!focus_child && is_current_focus) 6481 { 6482 if (title_focus_in (clist, direction)) 6483 return TRUE; 6484 } 6485 6486 if (!is_current_focus && !focus_child && clist->rows) 6487 { 6488 gtk_cmclist_focus_content_area (clist); 6489 return TRUE; 6490 } 6491 break; 6492 default: 6493 break; 6494 } 6495 6496 return FALSE; 6497 } 6498 6499 static void 6500 gtk_cmclist_set_focus_child (GtkContainer *container, 6501 GtkWidget *child) 6502 { 6503 GtkCMCList *clist = GTK_CMCLIST (container); 6504 gint i; 6505 6506 for (i = 0; i < clist->columns; i++) 6507 if (clist->column[i].button == child) 6508 clist->focus_header_column = i; 6509 6510 if (GTK_CONTAINER_CLASS (gtk_cmclist_parent_class)->set_focus_child) 6511 (*GTK_CONTAINER_CLASS (gtk_cmclist_parent_class)->set_focus_child) (container, child); 6512 } 6513 6514 static void 6515 gtk_cmclist_draw_focus (GtkWidget *widget) 6516 { 6517 GtkCMCList *clist; 6518 cairo_t *cr; 6519 6520 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 6521 6522 if (!gtk_widget_is_drawable (widget) || !gtk_widget_get_can_focus (widget)) 6523 return; 6524 6525 clist = GTK_CMCLIST (widget); 6526 if (clist->focus_row >= 0) { 6527 if (!clist->draw_now) { 6528 cr = gdk_cairo_create(clist->clist_window); 6529 cairo_dash_from_add_mode(clist, cr); 6530 cairo_set_line_width(cr, 1.0); 6531 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); 6532 cairo_rectangle(cr, 0, ROW_TOP_YPIXEL(clist, clist->focus_row) + 0.5, 6533 clist->clist_window_width + 1, 6534 clist->row_height - 0.5); 6535 cairo_stroke(cr); 6536 cairo_destroy(cr); 6537 } else { 6538 gtk_widget_queue_draw(GTK_WIDGET(clist)); 6539 } 6540 } 6541 } 6542 6543 static void 6544 gtk_cmclist_undraw_focus (GtkWidget *widget) 6545 { 6546 GtkCMCList *clist; 6547 int row; 6548 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 6549 6550 clist = GTK_CMCLIST(widget); 6551 6552 if (clist->focus_row < 0) 6553 return; 6554 6555 if (!gtk_widget_is_drawable (widget) || !gtk_widget_get_can_focus (widget)) 6556 return; 6557 6558 clist = GTK_CMCLIST (widget); 6559 if (clist->focus_row >= 0) { 6560 if (!clist->draw_now) { 6561 cairo_t *cr = gdk_cairo_create(clist->clist_window); 6562 cairo_set_line_width(cr, 1.0); 6563 gdk_cairo_set_source_color(cr, >k_widget_get_style(widget)->base[GTK_STATE_NORMAL]); 6564 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); 6565 cairo_rectangle(cr, 0, ROW_TOP_YPIXEL(clist, clist->focus_row) + 0.5, 6566 clist->clist_window_width + 1, 6567 clist->row_height - 0.5); 6568 cairo_stroke(cr); 6569 cairo_destroy(cr); 6570 } else { 6571 gtk_widget_queue_draw(GTK_WIDGET(clist)); 6572 } 6573 } 6574 6575 row = clist->focus_row; 6576 GTK_CMCLIST_GET_CLASS(GTK_CMCLIST(widget))->draw_row(clist, NULL, row, ROW_ELEMENT (clist, row)->data); 6577 } 6578 6579 static gint 6580 gtk_cmclist_focus_in (GtkWidget *widget, 6581 GdkEventFocus *event) 6582 { 6583 GtkCMCList *clist = GTK_CMCLIST (widget); 6584 6585 if (clist->selection_mode == GTK_SELECTION_BROWSE && 6586 clist->selection == NULL && clist->focus_row > -1) 6587 { 6588 GList *list; 6589 6590 list = g_list_nth (clist->row_list, clist->focus_row); 6591 if (list && GTK_CMCLIST_ROW (list)->selectable) 6592 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 6593 clist->focus_row, -1, event); 6594 else 6595 gtk_cmclist_draw_focus (widget); 6596 } 6597 else 6598 gtk_cmclist_undraw_focus (widget); 6599 6600 return FALSE; 6601 } 6602 6603 static gint 6604 gtk_cmclist_focus_out (GtkWidget *widget, 6605 GdkEventFocus *event) 6606 { 6607 GtkCMCList *clist = GTK_CMCLIST (widget); 6608 6609 gtk_cmclist_undraw_focus (widget); 6610 6611 GTK_CMCLIST_GET_CLASS (widget)->resync_selection (clist, (GdkEvent *) event); 6612 6613 return FALSE; 6614 } 6615 6616 static gboolean 6617 focus_column (GtkCMCList *clist, gint column, gint dir) 6618 { 6619 GtkWidget *child = clist->column[column].button; 6620 6621 if (gtk_widget_child_focus (child, dir)) 6622 { 6623 return TRUE; 6624 } 6625 else if (gtk_widget_get_can_focus (child)) 6626 { 6627 gtk_widget_grab_focus (child); 6628 return TRUE; 6629 } 6630 6631 return FALSE; 6632 } 6633 6634 /* Focus moved onto the headers. Focus first focusable and visible child. 6635 * (FIXME: focus the last focused child if visible) 6636 */ 6637 static gboolean 6638 title_focus_in (GtkCMCList *clist, gint dir) 6639 { 6640 gint i; 6641 gint left, right; 6642 6643 if (!GTK_CMCLIST_SHOW_TITLES (clist)) 6644 return FALSE; 6645 6646 /* Check last focused column */ 6647 if (clist->focus_header_column != -1) 6648 { 6649 i = clist->focus_header_column; 6650 6651 left = COLUMN_LEFT_XPIXEL (clist, i); 6652 right = left + clist->column[i].area.width; 6653 6654 if (left >= 0 && right <= clist->clist_window_width) 6655 { 6656 if (focus_column (clist, i, dir)) 6657 return TRUE; 6658 } 6659 } 6660 6661 /* Check fully visible columns */ 6662 for (i = 0 ; i < clist->columns ; i++) 6663 { 6664 left = COLUMN_LEFT_XPIXEL (clist, i); 6665 right = left + clist->column[i].area.width; 6666 6667 if (left >= 0 && right <= clist->clist_window_width) 6668 { 6669 if (focus_column (clist, i, dir)) 6670 return TRUE; 6671 } 6672 } 6673 6674 /* Check partially visible columns */ 6675 for (i = 0 ; i < clist->columns ; i++) 6676 { 6677 left = COLUMN_LEFT_XPIXEL (clist, i); 6678 right = left + clist->column[i].area.width; 6679 6680 if ((left < 0 && right > 0) || 6681 (left < clist->clist_window_width && right > clist->clist_window_width)) 6682 { 6683 if (focus_column (clist, i, dir)) 6684 return TRUE; 6685 } 6686 } 6687 6688 return FALSE; 6689 } 6690 6691 /* Move the focus right or left within the title buttons, scrolling 6692 * as necessary to keep the focused child visible. 6693 */ 6694 static gboolean 6695 title_focus_move (GtkCMCList *clist, 6696 gint dir) 6697 { 6698 GtkWidget *focus_child; 6699 gboolean return_val = FALSE; 6700 gint d = 0; 6701 gint i = -1; 6702 gint j; 6703 6704 if (!GTK_CMCLIST_SHOW_TITLES(clist)) 6705 return FALSE; 6706 6707 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (clist)); 6708 g_assert (focus_child); 6709 6710 /* Movement direction within headers 6711 */ 6712 switch (dir) 6713 { 6714 case GTK_DIR_RIGHT: 6715 d = 1; 6716 break; 6717 case GTK_DIR_LEFT: 6718 d = -1; 6719 break; 6720 } 6721 6722 for (i = 0; i < clist->columns; i++) 6723 if (clist->column[i].button == focus_child) 6724 break; 6725 6726 g_assert (i != -1); /* Have a starting column */ 6727 6728 j = i + d; 6729 while (!return_val && j >= 0 && j < clist->columns) 6730 { 6731 if (clist->column[j].button && 6732 gtk_widget_get_visible (clist->column[j].button)) 6733 { 6734 if (focus_column (clist, j, dir)) 6735 { 6736 return_val = TRUE; 6737 break; 6738 } 6739 } 6740 j += d; 6741 } 6742 6743 /* If we didn't find it, wrap around and keep looking 6744 */ 6745 if (!return_val) 6746 { 6747 j = d > 0 ? 0 : clist->columns - 1; 6748 6749 while (!return_val && j != i) 6750 { 6751 if (clist->column[j].button && 6752 gtk_widget_get_visible (clist->column[j].button)) 6753 { 6754 if (focus_column (clist, j, dir)) 6755 { 6756 return_val = TRUE; 6757 break; 6758 } 6759 } 6760 j += d; 6761 } 6762 } 6763 6764 /* Scroll horizontally so focused column is visible 6765 */ 6766 if (return_val) 6767 { 6768 if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET) 6769 gtk_cmclist_moveto (clist, -1, j, 0, 0); 6770 else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width > 6771 clist->clist_window_width) 6772 { 6773 gint last_column; 6774 6775 for (last_column = clist->columns - 1; 6776 last_column >= 0 && !clist->column[last_column].visible; last_column--); 6777 6778 if (j == last_column) 6779 gtk_cmclist_moveto (clist, -1, j, 0, 0); 6780 else 6781 gtk_cmclist_moveto (clist, -1, j, 0, 1); 6782 } 6783 } 6784 return TRUE; /* Even if we didn't find a new one, we can keep the 6785 * focus in the same place. 6786 */ 6787 } 6788 6789 /* PRIVATE SCROLLING FUNCTIONS 6790 * move_focus_row 6791 * scroll_horizontal 6792 * scroll_vertical 6793 * move_horizontal 6794 * move_vertical 6795 * horizontal_timeout 6796 * vertical_timeout 6797 * remove_grab 6798 */ 6799 static void 6800 move_focus_row (GtkCMCList *clist, 6801 GtkScrollType scroll_type, 6802 gfloat position) 6803 { 6804 GtkWidget *widget; 6805 6806 cm_return_if_fail (clist != 0); 6807 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 6808 6809 widget = GTK_WIDGET (clist); 6810 6811 switch (scroll_type) 6812 { 6813 case GTK_SCROLL_STEP_UP: 6814 case GTK_SCROLL_STEP_BACKWARD: 6815 if (clist->focus_row <= 0) 6816 return; 6817 gtk_cmclist_undraw_focus (widget); 6818 clist->focus_row--; 6819 gtk_cmclist_draw_focus (widget); 6820 break; 6821 6822 case GTK_SCROLL_STEP_DOWN: 6823 case GTK_SCROLL_STEP_FORWARD: 6824 if (clist->focus_row >= clist->rows - 1) 6825 return; 6826 gtk_cmclist_undraw_focus (widget); 6827 clist->focus_row++; 6828 gtk_cmclist_draw_focus (widget); 6829 break; 6830 case GTK_SCROLL_PAGE_UP: 6831 case GTK_SCROLL_PAGE_BACKWARD: 6832 if (clist->focus_row <= 0) 6833 return; 6834 gtk_cmclist_undraw_focus (widget); 6835 clist->focus_row = MAX (0, clist->focus_row - 6836 (2 * clist->clist_window_height - 6837 clist->row_height - CELL_SPACING) / 6838 (2 * (clist->row_height + CELL_SPACING))); 6839 gtk_cmclist_draw_focus (widget); 6840 break; 6841 case GTK_SCROLL_PAGE_DOWN: 6842 case GTK_SCROLL_PAGE_FORWARD: 6843 if (clist->focus_row >= clist->rows - 1) 6844 return; 6845 gtk_cmclist_undraw_focus (widget); 6846 clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 6847 (2 * clist->clist_window_height - 6848 clist->row_height - CELL_SPACING) / 6849 (2 * (clist->row_height + CELL_SPACING))); 6850 gtk_cmclist_draw_focus (widget); 6851 break; 6852 case GTK_SCROLL_JUMP: 6853 if (position >= 0 && position <= 1) 6854 { 6855 gint row = position * (clist->rows - 1); 6856 6857 if (row == clist->focus_row) 6858 return; 6859 6860 gtk_cmclist_undraw_focus (widget); 6861 clist->focus_row = row; 6862 gtk_cmclist_draw_focus (widget); 6863 } 6864 break; 6865 default: 6866 break; 6867 } 6868 } 6869 6870 static void 6871 scroll_horizontal (GtkCMCList *clist, 6872 GtkScrollType scroll_type, 6873 gfloat position) 6874 { 6875 gint column = 0; 6876 gint last_column; 6877 6878 cm_return_if_fail (clist != 0); 6879 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 6880 6881 if (clist_has_grab (clist)) 6882 return; 6883 6884 for (last_column = clist->columns - 1; 6885 last_column >= 0 && !clist->column[last_column].visible; last_column--) 6886 ; 6887 6888 switch (scroll_type) 6889 { 6890 case GTK_SCROLL_STEP_BACKWARD: 6891 column = COLUMN_FROM_XPIXEL (clist, 0); 6892 if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0 6893 && column > 0) 6894 column--; 6895 break; 6896 case GTK_SCROLL_STEP_FORWARD: 6897 column = COLUMN_FROM_XPIXEL (clist, clist->clist_window_width); 6898 if (column < 0) 6899 return; 6900 if (COLUMN_LEFT_XPIXEL (clist, column) + 6901 clist->column[column].area.width + 6902 CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width && 6903 column < last_column) 6904 column++; 6905 break; 6906 case GTK_SCROLL_PAGE_BACKWARD: 6907 case GTK_SCROLL_PAGE_FORWARD: 6908 return; 6909 case GTK_SCROLL_JUMP: 6910 if (position >= 0 && position <= 1) 6911 { 6912 gint vis_columns = 0; 6913 gint i; 6914 6915 for (i = 0; i <= last_column; i++) 6916 if (clist->column[i].visible) 6917 vis_columns++; 6918 6919 column = position * vis_columns; 6920 6921 for (i = 0; i <= last_column && column > 0; i++) 6922 if (clist->column[i].visible) 6923 column--; 6924 6925 column = i; 6926 } 6927 else 6928 return; 6929 break; 6930 default: 6931 break; 6932 } 6933 6934 if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET) 6935 gtk_cmclist_moveto (clist, -1, column, 0, 0); 6936 else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1 6937 + clist->column[column].area.width > clist->clist_window_width) 6938 { 6939 if (column == last_column) 6940 gtk_cmclist_moveto (clist, -1, column, 0, 0); 6941 else 6942 gtk_cmclist_moveto (clist, -1, column, 0, 1); 6943 } 6944 } 6945 6946 static void 6947 scroll_vertical (GtkCMCList *clist, 6948 GtkScrollType scroll_type, 6949 gfloat position) 6950 { 6951 gint old_focus_row; 6952 6953 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 6954 6955 if (clist_has_grab (clist)) 6956 return; 6957 6958 switch (clist->selection_mode) 6959 { 6960 case GTK_SELECTION_MULTIPLE: 6961 if (clist->anchor >= 0) 6962 return; 6963 case GTK_SELECTION_BROWSE: 6964 6965 old_focus_row = clist->focus_row; 6966 move_focus_row (clist, scroll_type, position); 6967 6968 if (old_focus_row != clist->focus_row) 6969 { 6970 if (clist->selection_mode == GTK_SELECTION_BROWSE) 6971 g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0, 6972 old_focus_row, -1, NULL); 6973 else if (!GTK_CMCLIST_ADD_MODE(clist)) 6974 { 6975 gtk_cmclist_unselect_all (clist); 6976 clist->undo_anchor = old_focus_row; 6977 } 6978 } 6979 6980 switch (gtk_cmclist_row_is_visible (clist, clist->focus_row)) 6981 { 6982 case GTK_VISIBILITY_NONE: 6983 if (old_focus_row != clist->focus_row && 6984 !(clist->selection_mode == GTK_SELECTION_MULTIPLE && 6985 GTK_CMCLIST_ADD_MODE(clist))) 6986 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 6987 clist->focus_row, -1, NULL); 6988 switch (scroll_type) 6989 { 6990 case GTK_SCROLL_PAGE_UP: 6991 case GTK_SCROLL_STEP_UP: 6992 case GTK_SCROLL_STEP_BACKWARD: 6993 case GTK_SCROLL_PAGE_BACKWARD: 6994 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 6995 break; 6996 case GTK_SCROLL_PAGE_DOWN: 6997 case GTK_SCROLL_STEP_DOWN: 6998 case GTK_SCROLL_STEP_FORWARD: 6999 case GTK_SCROLL_PAGE_FORWARD: 7000 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 7001 break; 7002 case GTK_SCROLL_JUMP: 7003 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0.5, 0); 7004 break; 7005 default: 7006 break; 7007 } 7008 break; 7009 case GTK_VISIBILITY_PARTIAL: 7010 switch (scroll_type) 7011 { 7012 case GTK_SCROLL_STEP_BACKWARD: 7013 case GTK_SCROLL_PAGE_BACKWARD: 7014 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 7015 break; 7016 case GTK_SCROLL_STEP_FORWARD: 7017 case GTK_SCROLL_PAGE_FORWARD: 7018 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 7019 break; 7020 case GTK_SCROLL_JUMP: 7021 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0.5, 0); 7022 break; 7023 default: 7024 break; 7025 } 7026 /* fallback is intentional */ 7027 default: 7028 if (old_focus_row != clist->focus_row && 7029 !(clist->selection_mode == GTK_SELECTION_MULTIPLE && 7030 GTK_CMCLIST_ADD_MODE(clist))) 7031 g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0, 7032 clist->focus_row, -1, NULL); 7033 break; 7034 } 7035 break; 7036 default: 7037 move_focus_row (clist, scroll_type, position); 7038 7039 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height > 7040 clist->clist_window_height) 7041 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0); 7042 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0) 7043 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0); 7044 break; 7045 } 7046 } 7047 7048 static void 7049 move_horizontal (GtkCMCList *clist, 7050 gint diff) 7051 { 7052 gdouble value; 7053 7054 if (!clist->hadjustment) 7055 return; 7056 7057 value = CLAMP (gtk_adjustment_get_value (clist->hadjustment) + diff, 0.0, 7058 gtk_adjustment_get_upper (clist->hadjustment) - 7059 gtk_adjustment_get_page_size (clist->hadjustment)); 7060 gtk_adjustment_set_value (clist->hadjustment, value); 7061 } 7062 7063 static void 7064 move_vertical (GtkCMCList *clist, 7065 gint row, 7066 gfloat align) 7067 { 7068 gdouble value; 7069 gdouble upper; 7070 gdouble page_size; 7071 7072 if (!clist->vadjustment) 7073 return; 7074 7075 value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset - 7076 align * (clist->clist_window_height - clist->row_height) + 7077 (2 * align - 1) * CELL_SPACING); 7078 7079 upper = gtk_adjustment_get_upper (clist->vadjustment); 7080 page_size = gtk_adjustment_get_page_size (clist->vadjustment); 7081 if ((value + page_size) > upper) 7082 value = upper - page_size; 7083 7084 gtk_adjustment_set_value (clist->vadjustment, value); 7085 } 7086 7087 static void 7088 do_fake_motion (GtkWidget *widget) 7089 { 7090 GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY); 7091 7092 event->motion.send_event = TRUE; 7093 7094 gtk_cmclist_motion (widget, (GdkEventMotion *)event); 7095 gdk_event_free (event); 7096 } 7097 7098 static gint 7099 horizontal_timeout (GtkCMCList *clist) 7100 { 7101 clist->htimer = 0; 7102 do_fake_motion (GTK_WIDGET (clist)); 7103 7104 return FALSE; 7105 } 7106 7107 static gint 7108 vertical_timeout (GtkCMCList *clist) 7109 { 7110 clist->vtimer = 0; 7111 do_fake_motion (GTK_WIDGET (clist)); 7112 7113 return FALSE; 7114 } 7115 7116 static void 7117 remove_grab (GtkCMCList *clist) 7118 { 7119 GtkWidget *widget = GTK_WIDGET (clist); 7120 7121 if (gtk_widget_has_grab (widget)) 7122 { 7123 GdkDisplay *display = gtk_widget_get_display (widget); 7124 7125 gtk_grab_remove (widget); 7126 if (gtkut_pointer_is_grabbed (widget)) 7127 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); 7128 } 7129 7130 if (clist->htimer) 7131 { 7132 g_source_remove (clist->htimer); 7133 clist->htimer = 0; 7134 } 7135 7136 if (clist->vtimer) 7137 { 7138 g_source_remove (clist->vtimer); 7139 clist->vtimer = 0; 7140 } 7141 } 7142 7143 /* PUBLIC SORTING FUNCTIONS 7144 * gtk_cmclist_sort 7145 * gtk_cmclist_set_compare_func 7146 * gtk_cmclist_set_auto_sort 7147 * gtk_cmclist_set_sort_type 7148 * gtk_cmclist_set_sort_column 7149 */ 7150 void 7151 gtk_cmclist_sort (GtkCMCList *clist) 7152 { 7153 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7154 7155 GTK_CMCLIST_GET_CLASS (clist)->sort_list (clist); 7156 } 7157 7158 void 7159 gtk_cmclist_set_compare_func (GtkCMCList *clist, 7160 GtkCMCListCompareFunc cmp_func) 7161 { 7162 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7163 7164 clist->compare = (cmp_func) ? cmp_func : default_compare; 7165 } 7166 7167 void 7168 gtk_cmclist_set_auto_sort (GtkCMCList *clist, 7169 gboolean auto_sort) 7170 { 7171 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7172 7173 if (GTK_CMCLIST_AUTO_SORT(clist) && !auto_sort) 7174 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_SORT); 7175 else if (!GTK_CMCLIST_AUTO_SORT(clist) && auto_sort) 7176 { 7177 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_SORT); 7178 gtk_cmclist_sort (clist); 7179 } 7180 } 7181 7182 void 7183 gtk_cmclist_set_sort_type (GtkCMCList *clist, 7184 GtkSortType sort_type) 7185 { 7186 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7187 7188 clist->sort_type = sort_type; 7189 } 7190 7191 void 7192 gtk_cmclist_set_sort_column (GtkCMCList *clist, 7193 gint column) 7194 { 7195 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7196 7197 if (column < 0 || column >= clist->columns) 7198 return; 7199 7200 clist->sort_column = column; 7201 } 7202 7203 /* PRIVATE SORTING FUNCTIONS 7204 * default_compare 7205 * real_sort_list 7206 * gtk_cmclist_merge 7207 * gtk_cmclist_mergesort 7208 */ 7209 static gint 7210 default_compare (GtkCMCList *clist, 7211 gconstpointer ptr1, 7212 gconstpointer ptr2) 7213 { 7214 char *text1 = NULL; 7215 char *text2 = NULL; 7216 7217 GtkCMCListRow *row1 = (GtkCMCListRow *) ptr1; 7218 GtkCMCListRow *row2 = (GtkCMCListRow *) ptr2; 7219 7220 switch (row1->cell[clist->sort_column].type) 7221 { 7222 case GTK_CMCELL_TEXT: 7223 text1 = GTK_CMCELL_TEXT (row1->cell[clist->sort_column])->text; 7224 break; 7225 case GTK_CMCELL_PIXTEXT: 7226 text1 = GTK_CMCELL_PIXTEXT (row1->cell[clist->sort_column])->text; 7227 break; 7228 default: 7229 break; 7230 } 7231 7232 switch (row2->cell[clist->sort_column].type) 7233 { 7234 case GTK_CMCELL_TEXT: 7235 text2 = GTK_CMCELL_TEXT (row2->cell[clist->sort_column])->text; 7236 break; 7237 case GTK_CMCELL_PIXTEXT: 7238 text2 = GTK_CMCELL_PIXTEXT (row2->cell[clist->sort_column])->text; 7239 break; 7240 default: 7241 break; 7242 } 7243 7244 if (!text2) 7245 return (text1 != NULL); 7246 7247 if (!text1) 7248 return -1; 7249 7250 return strcmp (text1, text2); 7251 } 7252 7253 static void 7254 real_sort_list (GtkCMCList *clist) 7255 { 7256 GList *list; 7257 GList *work; 7258 gint i; 7259 7260 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7261 7262 if (clist->rows <= 1) 7263 return; 7264 7265 if (clist_has_grab (clist)) 7266 return; 7267 7268 gtk_cmclist_freeze (clist); 7269 7270 if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_MULTIPLE) 7271 { 7272 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 7273 g_list_free (clist->undo_selection); 7274 g_list_free (clist->undo_unselection); 7275 clist->undo_selection = NULL; 7276 clist->undo_unselection = NULL; 7277 } 7278 7279 clist->row_list = gtk_cmclist_mergesort (clist, clist->row_list, clist->rows); 7280 7281 work = clist->selection; 7282 7283 for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next) 7284 { 7285 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED) 7286 { 7287 work->data = GINT_TO_POINTER (i); 7288 work = work->next; 7289 } 7290 7291 if (i == clist->rows - 1) 7292 clist->row_list_end = list; 7293 } 7294 7295 gtk_cmclist_thaw (clist); 7296 } 7297 7298 static GList * 7299 gtk_cmclist_merge (GtkCMCList *clist, 7300 GList *a, /* first list to merge */ 7301 GList *b) /* second list to merge */ 7302 { 7303 GList z = { 0 }; /* auxiliary node */ 7304 GList *c; 7305 gint cmp; 7306 7307 c = &z; 7308 7309 if (!a && !b) 7310 return NULL; 7311 7312 while (a || b) 7313 { 7314 if (a && !b) 7315 { 7316 c->next = a; 7317 a->prev = c; 7318 c = a; 7319 a = a->next; 7320 break; 7321 } 7322 else if (!a && b) 7323 { 7324 c->next = b; 7325 b->prev = c; 7326 c = b; 7327 b = b->next; 7328 break; 7329 } 7330 else /* a && b */ 7331 { 7332 cmp = clist->compare (clist, GTK_CMCLIST_ROW (a), GTK_CMCLIST_ROW (b)); 7333 if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) || 7334 (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING)) 7335 { 7336 c->next = a; 7337 a->prev = c; 7338 c = a; 7339 a = a->next; 7340 } 7341 else 7342 { 7343 c->next = b; 7344 b->prev = c; 7345 c = b; 7346 b = b->next; 7347 } 7348 } 7349 } 7350 7351 if (z.next) 7352 z.next->prev = NULL; 7353 7354 return z.next; 7355 } 7356 7357 static GList * 7358 gtk_cmclist_mergesort (GtkCMCList *clist, 7359 GList *list, /* the list to sort */ 7360 gint num) /* the list's length */ 7361 { 7362 GList *half; 7363 gint i; 7364 7365 if (num <= 1) 7366 { 7367 return list; 7368 } 7369 else 7370 { 7371 /* move "half" to the middle */ 7372 half = list; 7373 for (i = 0; i < num / 2; i++) 7374 half = half->next; 7375 7376 /* cut the list in two */ 7377 half->prev->next = NULL; 7378 half->prev = NULL; 7379 7380 /* recursively sort both lists */ 7381 return gtk_cmclist_merge (clist, 7382 gtk_cmclist_mergesort (clist, list, num / 2), 7383 gtk_cmclist_mergesort (clist, half, num - num / 2)); 7384 } 7385 } 7386 7387 /************************/ 7388 7389 static void 7390 drag_source_info_destroy (gpointer data) 7391 { 7392 GtkCMCListCellInfo *info = data; 7393 7394 g_free (info); 7395 } 7396 7397 static void 7398 drag_dest_info_destroy (gpointer data) 7399 { 7400 GtkCMCListDestInfo *info = data; 7401 7402 g_free (info); 7403 } 7404 7405 static void 7406 drag_dest_cell (GtkCMCList *clist, 7407 gint x, 7408 gint y, 7409 GtkCMCListDestInfo *dest_info) 7410 { 7411 GtkWidget *widget; 7412 GtkStyle *style; 7413 guint border_width; 7414 7415 widget = GTK_WIDGET (clist); 7416 style = gtk_widget_get_style (widget); 7417 7418 dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE; 7419 7420 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); 7421 y -= (border_width + 7422 style->ythickness + 7423 clist->column_title_area.height); 7424 7425 dest_info->cell.row = ROW_FROM_YPIXEL (clist, y); 7426 if (dest_info->cell.row >= clist->rows) 7427 { 7428 dest_info->cell.row = clist->rows - 1; 7429 y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height; 7430 } 7431 if (dest_info->cell.row < -1) 7432 dest_info->cell.row = -1; 7433 7434 x -= border_width + style->xthickness; 7435 7436 dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x); 7437 7438 if (dest_info->cell.row >= 0) 7439 { 7440 gint y_delta; 7441 gint h = 0; 7442 7443 y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row); 7444 7445 if (GTK_CMCLIST_DRAW_DRAG_RECT(clist)) 7446 { 7447 dest_info->insert_pos = GTK_CMCLIST_DRAG_INTO; 7448 h = clist->row_height / 4; 7449 } 7450 else if (GTK_CMCLIST_DRAW_DRAG_LINE(clist)) 7451 { 7452 dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE; 7453 h = clist->row_height / 2; 7454 } 7455 7456 if (GTK_CMCLIST_DRAW_DRAG_LINE(clist)) 7457 { 7458 if (y_delta < h) 7459 dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE; 7460 else if (clist->row_height - y_delta < h) 7461 dest_info->insert_pos = GTK_CMCLIST_DRAG_AFTER; 7462 } 7463 } 7464 } 7465 7466 static void 7467 gtk_cmclist_drag_begin (GtkWidget *widget, 7468 GdkDragContext *context) 7469 { 7470 GtkCMCList *clist; 7471 GtkCMCListCellInfo *info; 7472 7473 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 7474 cm_return_if_fail (context != NULL); 7475 7476 clist = GTK_CMCLIST (widget); 7477 7478 clist->drag_button = 0; 7479 remove_grab (clist); 7480 7481 switch (clist->selection_mode) 7482 { 7483 case GTK_SELECTION_MULTIPLE: 7484 update_extended_selection (clist, clist->focus_row); 7485 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 7486 break; 7487 case GTK_SELECTION_SINGLE: 7488 clist->anchor = -1; 7489 case GTK_SELECTION_BROWSE: 7490 break; 7491 default: 7492 g_assert_not_reached (); 7493 } 7494 7495 info = g_dataset_get_data (context, "gtk-clist-drag-source"); 7496 7497 if (!info) 7498 { 7499 info = g_new (GtkCMCListCellInfo, 1); 7500 7501 if (clist->click_cell.row < 0) 7502 clist->click_cell.row = 0; 7503 else if (clist->click_cell.row >= clist->rows) 7504 clist->click_cell.row = clist->rows - 1; 7505 info->row = clist->click_cell.row; 7506 info->column = clist->click_cell.column; 7507 7508 g_dataset_set_data_full (context, "gtk-clist-drag-source", info, 7509 drag_source_info_destroy); 7510 } 7511 7512 if (GTK_CMCLIST_USE_DRAG_ICONS (clist)) 7513 gtk_drag_set_icon_default (context); 7514 } 7515 7516 static void 7517 gtk_cmclist_drag_end (GtkWidget *widget, 7518 GdkDragContext *context) 7519 { 7520 GtkCMCList *clist; 7521 7522 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 7523 cm_return_if_fail (context != NULL); 7524 7525 clist = GTK_CMCLIST (widget); 7526 7527 clist->click_cell.row = -1; 7528 clist->click_cell.column = -1; 7529 } 7530 7531 static void 7532 gtk_cmclist_drag_leave (GtkWidget *widget, 7533 GdkDragContext *context, 7534 guint time) 7535 { 7536 GtkCMCList *clist; 7537 GtkCMCListDestInfo *dest_info; 7538 7539 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 7540 cm_return_if_fail (context != NULL); 7541 7542 clist = GTK_CMCLIST (widget); 7543 7544 dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest"); 7545 7546 if (dest_info) 7547 { 7548 if (dest_info->cell.row >= 0 && 7549 GTK_CMCLIST_REORDERABLE(clist) && 7550 gtk_drag_get_source_widget (context) == widget) 7551 { 7552 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder"); 7553 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL); 7554 7555 if (atom == found) 7556 { 7557 clist->drag_highlight_row = -1; 7558 } 7559 } 7560 g_dataset_remove_data (context, "gtk-clist-drag-dest"); 7561 } 7562 } 7563 7564 static gint 7565 gtk_cmclist_drag_motion (GtkWidget *widget, 7566 GdkDragContext *context, 7567 gint x, 7568 gint y, 7569 guint time) 7570 { 7571 GtkCMCList *clist; 7572 GtkCMCListDestInfo new_info; 7573 GtkCMCListDestInfo *dest_info; 7574 7575 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE); 7576 7577 clist = GTK_CMCLIST (widget); 7578 7579 dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest"); 7580 7581 if (!dest_info) 7582 { 7583 dest_info = g_new (GtkCMCListDestInfo, 1); 7584 7585 dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE; 7586 dest_info->cell.row = -1; 7587 dest_info->cell.column = -1; 7588 7589 g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info, 7590 drag_dest_info_destroy); 7591 } 7592 7593 drag_dest_cell (clist, x, y, &new_info); 7594 7595 if (GTK_CMCLIST_REORDERABLE (clist)) 7596 { 7597 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder"); 7598 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL); 7599 7600 if (atom == found) 7601 { 7602 if (gtk_drag_get_source_widget (context) != widget || 7603 new_info.insert_pos == GTK_CMCLIST_DRAG_NONE || 7604 new_info.cell.row == clist->click_cell.row || 7605 (new_info.cell.row == clist->click_cell.row - 1 && 7606 new_info.insert_pos == GTK_CMCLIST_DRAG_AFTER) || 7607 (new_info.cell.row == clist->click_cell.row + 1 && 7608 new_info.insert_pos == GTK_CMCLIST_DRAG_BEFORE)) 7609 { 7610 if (dest_info->cell.row < 0) 7611 { 7612 gdk_drag_status (context, GDK_ACTION_DEFAULT, time); 7613 return FALSE; 7614 } 7615 return TRUE; 7616 } 7617 7618 if (new_info.cell.row != dest_info->cell.row || 7619 (new_info.cell.row == dest_info->cell.row && 7620 dest_info->insert_pos != new_info.insert_pos)) 7621 { 7622 7623 dest_info->insert_pos = new_info.insert_pos; 7624 dest_info->cell.row = new_info.cell.row; 7625 dest_info->cell.column = new_info.cell.column; 7626 7627 clist->drag_highlight_row = dest_info->cell.row; 7628 clist->drag_highlight_pos = dest_info->insert_pos; 7629 7630 gdk_drag_status (context, 7631 gdk_drag_context_get_suggested_action(context), time); 7632 } 7633 return TRUE; 7634 } 7635 } 7636 7637 dest_info->insert_pos = new_info.insert_pos; 7638 dest_info->cell.row = new_info.cell.row; 7639 dest_info->cell.column = new_info.cell.column; 7640 return TRUE; 7641 } 7642 7643 static gboolean 7644 gtk_cmclist_drag_drop (GtkWidget *widget, 7645 GdkDragContext *context, 7646 gint x, 7647 gint y, 7648 guint time) 7649 { 7650 cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE); 7651 cm_return_val_if_fail (context != NULL, FALSE); 7652 7653 if (GTK_CMCLIST_REORDERABLE (widget) && 7654 gtk_drag_get_source_widget (context) == widget) 7655 { 7656 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder"); 7657 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL); 7658 7659 if (atom == found) 7660 return TRUE; 7661 } 7662 return FALSE; 7663 } 7664 7665 static void 7666 gtk_cmclist_drag_data_received (GtkWidget *widget, 7667 GdkDragContext *context, 7668 gint x, 7669 gint y, 7670 GtkSelectionData *selection_data, 7671 guint info, 7672 guint time) 7673 { 7674 GtkCMCList *clist; 7675 7676 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 7677 cm_return_if_fail (context != NULL); 7678 cm_return_if_fail (selection_data != NULL); 7679 7680 clist = GTK_CMCLIST (widget); 7681 7682 if (GTK_CMCLIST_REORDERABLE (clist) && 7683 gtk_drag_get_source_widget (context) == widget && 7684 gtk_selection_data_get_target (selection_data) == 7685 gdk_atom_intern_static_string ("gtk-clist-drag-reorder") && 7686 gtk_selection_data_get_format (selection_data) == 8 && 7687 gtk_selection_data_get_length (selection_data) == sizeof (GtkCMCListCellInfo)) 7688 { 7689 GtkCMCListCellInfo *source_info; 7690 7691 source_info = (GtkCMCListCellInfo *)(gtk_selection_data_get_data (selection_data)); 7692 if (source_info) 7693 { 7694 GtkCMCListDestInfo dest_info; 7695 7696 drag_dest_cell (clist, x, y, &dest_info); 7697 7698 if (dest_info.insert_pos == GTK_CMCLIST_DRAG_AFTER) 7699 dest_info.cell.row++; 7700 if (source_info->row < dest_info.cell.row) 7701 dest_info.cell.row--; 7702 if (dest_info.cell.row != source_info->row) 7703 gtk_cmclist_row_move (clist, source_info->row, dest_info.cell.row); 7704 7705 g_dataset_remove_data (context, "gtk-clist-drag-dest"); 7706 } 7707 } 7708 } 7709 7710 static void 7711 gtk_cmclist_drag_data_get (GtkWidget *widget, 7712 GdkDragContext *context, 7713 GtkSelectionData *selection_data, 7714 guint info, 7715 guint time) 7716 { 7717 GdkAtom target; 7718 cm_return_if_fail (GTK_IS_CMCLIST (widget)); 7719 cm_return_if_fail (context != NULL); 7720 cm_return_if_fail (selection_data != NULL); 7721 7722 target = gtk_selection_data_get_target (selection_data); 7723 if (target == gdk_atom_intern_static_string ("gtk-clist-drag-reorder")) 7724 { 7725 GtkCMCListCellInfo *info; 7726 7727 info = g_dataset_get_data (context, "gtk-clist-drag-source"); 7728 7729 if (info) 7730 { 7731 GtkCMCListCellInfo ret_info; 7732 7733 ret_info.row = info->row; 7734 ret_info.column = info->column; 7735 7736 gtk_selection_data_set (selection_data, target, 7737 8, (guchar *) &ret_info, 7738 sizeof (GtkCMCListCellInfo)); 7739 } 7740 } 7741 } 7742 7743 void 7744 gtk_cmclist_set_reorderable (GtkCMCList *clist, 7745 gboolean reorderable) 7746 { 7747 GtkWidget *widget; 7748 7749 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7750 7751 if ((GTK_CMCLIST_REORDERABLE(clist) != 0) == reorderable) 7752 return; 7753 7754 widget = GTK_WIDGET (clist); 7755 7756 if (reorderable) 7757 { 7758 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_REORDERABLE); 7759 gtk_drag_dest_set (widget, 7760 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, 7761 &clist_target_table, 1, GDK_ACTION_MOVE); 7762 } 7763 else 7764 { 7765 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_REORDERABLE); 7766 gtk_drag_dest_unset (GTK_WIDGET (clist)); 7767 } 7768 } 7769 7770 void 7771 gtk_cmclist_set_use_drag_icons (GtkCMCList *clist, 7772 gboolean use_icons) 7773 { 7774 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7775 7776 if (use_icons != 0) 7777 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS); 7778 else 7779 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_USE_DRAG_ICONS); 7780 } 7781 7782 void 7783 gtk_cmclist_set_button_actions (GtkCMCList *clist, 7784 guint button, 7785 guint8 button_actions) 7786 { 7787 cm_return_if_fail (GTK_IS_CMCLIST (clist)); 7788 7789 if (button < MAX_BUTTON) 7790 { 7791 if (gtkut_pointer_is_grabbed (GTK_WIDGET(clist)) || 7792 gtk_widget_has_grab (GTK_WIDGET(clist))) 7793 { 7794 remove_grab (clist); 7795 clist->drag_button = 0; 7796 } 7797 7798 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL); 7799 7800 clist->button_actions[button] = button_actions; 7801 } 7802 } 7803 7804 static gboolean 7805 gtk_cmclist_get_border (GtkScrollable *scrollable, 7806 GtkBorder *border) 7807 { 7808 GtkCMCList *cmclist = GTK_CMCLIST(scrollable); 7809 7810 border->top = cmclist->column_title_area.height; 7811 return TRUE; 7812 } 7813 7814 static void 7815 gtk_cmclist_scrollable_init (GtkScrollableInterface *iface) 7816 { 7817 iface->get_border = gtk_cmclist_get_border; 7818 }