37 |
(PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)) |
(PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)) |
38 |
|
|
39 |
|
|
|
/* Key to save a struct xg_last_sb_pos in the scroll bars. */ |
|
|
#define XG_LAST_SB_POS "emacs_last_sb_pos" |
|
|
|
|
|
|
|
|
/* Use this in scroll bars to keep track of when redraw is really needed. */ |
|
|
struct xg_last_sb_pos |
|
|
{ |
|
|
int portion, position, whole; |
|
|
}; |
|
|
|
|
|
|
|
40 |
|
|
41 |
/*********************************************************************** |
/*********************************************************************** |
42 |
Utility functions |
Utility functions |
287 |
gdk_window_process_all_updates (); |
gdk_window_process_all_updates (); |
288 |
} |
} |
289 |
|
|
|
/* When the Emacs frame is resized, we must call this function for each |
|
|
child of our GtkFixed widget. The children are scroll bars and |
|
|
we invalidate the last position data here so it will be properly |
|
|
redrawn later when xg_update_scrollbar_pos is called. |
|
|
W is the child widget. |
|
|
CLIENT_DATA is not used. */ |
|
|
static handle_fixed_child (w, client_data) |
|
|
GtkWidget *w; |
|
|
gpointer client_data; |
|
|
{ |
|
|
struct xg_last_sb_pos *last_pos |
|
|
= g_object_get_data (G_OBJECT (w), XG_LAST_SB_POS); |
|
|
if (last_pos) |
|
|
{ |
|
|
last_pos->portion = last_pos->position = last_pos->whole = .1; |
|
|
} |
|
|
} |
|
|
|
|
290 |
/* This gets called after the frame F has been cleared. Since that is |
/* This gets called after the frame F has been cleared. Since that is |
291 |
done with X calls, we need to redraw GTK widget (scroll bars). */ |
done with X calls, we need to redraw GTK widget (scroll bars). */ |
292 |
void |
void |
297 |
|
|
298 |
if (wfixed) |
if (wfixed) |
299 |
{ |
{ |
|
gtk_container_foreach (GTK_CONTAINER (wfixed), |
|
|
(GtkCallback) handle_fixed_child, |
|
|
NULL); |
|
300 |
gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE); |
gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE); |
301 |
gdk_window_process_all_updates (); |
gdk_window_process_all_updates (); |
302 |
} |
} |
2245 |
to indicate that callback should do nothing. */ |
to indicate that callback should do nothing. */ |
2246 |
int xg_ignore_gtk_scrollbar; |
int xg_ignore_gtk_scrollbar; |
2247 |
|
|
|
/* After we send a scroll bar event, x_set_toolkit_scroll_bar_thumb will |
|
|
be called. For some reason that needs to be debugged, it gets called |
|
|
with bad values. Thus, we set this variable to ignore those calls. */ |
|
|
int xg_ignore_next_thumb; |
|
|
|
|
2248 |
/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in |
/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in |
2249 |
32 bits. But we want to store pointers, and they may be larger |
32 bits. But we want to store pointers, and they may be larger |
2250 |
than 32 bits. Keep a mapping from integer index to widget pointers |
than 32 bits. Keep a mapping from integer index to widget pointers |
2354 |
|
|
2355 |
p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA); |
p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA); |
2356 |
if (p) xfree (p); |
if (p) xfree (p); |
|
p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_POS); |
|
|
if (p) xfree (p); |
|
2357 |
xg_remove_widget_from_map (id); |
xg_remove_widget_from_map (id); |
2358 |
} |
} |
2359 |
|
|
2360 |
/* Callback for button press/release events. Used to start timer so that |
/* Callback for button press/release events. Used to start timer so that |
2361 |
the scroll bar repetition timer in GTK gets handeled. |
the scroll bar repetition timer in GTK gets handeled. |
2362 |
|
Also, sets bar->dragging to Qnil when dragging (button release) is done. |
2363 |
WIDGET is the scroll bar widget the event is for (not used). |
WIDGET is the scroll bar widget the event is for (not used). |
2364 |
EVENT contains the event. |
EVENT contains the event. |
2365 |
USER_DATA is 0 (not used). |
USER_DATA points to the struct scrollbar structure. |
2366 |
|
|
2367 |
Returns FALSE to tell GTK that it shall continue propagate the event |
Returns FALSE to tell GTK that it shall continue propagate the event |
2368 |
to widgets. */ |
to widgets. */ |
2374 |
{ |
{ |
2375 |
if (event->type == GDK_BUTTON_PRESS && ! xg_timer) |
if (event->type == GDK_BUTTON_PRESS && ! xg_timer) |
2376 |
xg_start_timer (); |
xg_start_timer (); |
2377 |
else if (event->type == GDK_BUTTON_RELEASE && xg_timer) |
else if (event->type == GDK_BUTTON_RELEASE) |
2378 |
xg_stop_timer (); |
{ |
2379 |
|
struct scroll_bar *bar = (struct scroll_bar *) user_data; |
2380 |
|
if (xg_timer) xg_stop_timer (); |
2381 |
|
bar->dragging = Qnil; |
2382 |
|
} |
2383 |
|
|
2384 |
return FALSE; |
return FALSE; |
2385 |
} |
} |
2386 |
|
|
2426 |
g_signal_connect (G_OBJECT (wscroll), |
g_signal_connect (G_OBJECT (wscroll), |
2427 |
"button-press-event", |
"button-press-event", |
2428 |
G_CALLBACK (scroll_bar_button_cb), |
G_CALLBACK (scroll_bar_button_cb), |
2429 |
0); |
(gpointer)bar); |
2430 |
g_signal_connect (G_OBJECT (wscroll), |
g_signal_connect (G_OBJECT (wscroll), |
2431 |
"button-release-event", |
"button-release-event", |
2432 |
G_CALLBACK (scroll_bar_button_cb), |
G_CALLBACK (scroll_bar_button_cb), |
2433 |
0); |
(gpointer)bar); |
2434 |
|
|
2435 |
gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), |
gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), |
2436 |
wscroll, 0, 0); |
wscroll, -1, -1); |
2437 |
|
|
2438 |
/* Set the cursor to an arrow. */ |
/* Set the cursor to an arrow. */ |
2439 |
xg_set_cursor (wscroll, &xg_left_ptr_cursor); |
xg_set_cursor (wscroll, &xg_left_ptr_cursor); |
2440 |
|
|
|
/* Allocate a place to hold the last scollbar size. GTK redraw for |
|
|
scroll bars is basically clear all, and then redraw. This flickers |
|
|
a lot since xg_update_scrollbar_pos gets called on every cursor move |
|
|
and a lot more places. So we have this to check if a redraw really |
|
|
is needed. */ |
|
|
struct xg_last_sb_pos *last_pos |
|
|
= (struct xg_last_sb_pos *) xmalloc (sizeof (struct xg_last_sb_pos)); |
|
|
last_pos->portion = last_pos->position = last_pos->whole = -1; |
|
|
g_object_set_data (G_OBJECT (wscroll), XG_LAST_SB_POS, last_pos); |
|
|
|
|
2441 |
SET_SCROLL_BAR_X_WINDOW (bar, scroll_id); |
SET_SCROLL_BAR_X_WINDOW (bar, scroll_id); |
2442 |
} |
} |
2443 |
|
|
2465 |
} |
} |
2466 |
} |
} |
2467 |
|
|
2468 |
|
/* Find left/top for widget W in GtkFixed widget WFIXED. */ |
2469 |
|
static void |
2470 |
|
xg_find_top_left_in_fixed (w, wfixed, left, top) |
2471 |
|
GtkWidget *w, *wfixed; |
2472 |
|
int *left, *top; |
2473 |
|
{ |
2474 |
|
GList *iter; |
2475 |
|
|
2476 |
|
for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter)) |
2477 |
|
{ |
2478 |
|
GtkFixedChild *child = (GtkFixedChild *) iter->data; |
2479 |
|
|
2480 |
|
if (child->widget == w) |
2481 |
|
{ |
2482 |
|
*left = child->x; |
2483 |
|
*top = child->y; |
2484 |
|
return; |
2485 |
|
} |
2486 |
|
} |
2487 |
|
|
2488 |
|
/* Shall never end up here. */ |
2489 |
|
abort (); |
2490 |
|
} |
2491 |
|
|
2492 |
/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID |
/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID |
2493 |
in frame F. |
in frame F. |
2504 |
{ |
{ |
2505 |
|
|
2506 |
GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id); |
GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id); |
2507 |
|
|
2508 |
if (wscroll) |
if (wscroll) |
2509 |
{ |
{ |
|
int gheight = max (height, 1); |
|
|
struct xg_last_sb_pos *last_pos |
|
|
= g_object_get_data (G_OBJECT (wscroll), XG_LAST_SB_POS); |
|
2510 |
GtkWidget *wfixed = f->output_data.x->edit_widget; |
GtkWidget *wfixed = f->output_data.x->edit_widget; |
2511 |
|
int gheight = max (height, 1); |
2512 |
|
int canon_width = FRAME_SCROLL_BAR_COLS (f) * CANON_X_UNIT (f); |
2513 |
|
int winextra = canon_width > width ? (canon_width - width) / 2 : 0; |
2514 |
|
int bottom = top + gheight; |
2515 |
|
|
2516 |
last_pos->portion = last_pos->position = last_pos->whole = -1; |
gint slider_width; |
2517 |
xg_ignore_next_thumb = 0; |
int oldtop, oldleft, oldbottom; |
2518 |
|
GtkRequisition req; |
2519 |
|
|
2520 |
|
/* Get old values. */ |
2521 |
|
xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop); |
2522 |
|
gtk_widget_size_request (wscroll, &req); |
2523 |
|
oldbottom = oldtop + req.height; |
2524 |
|
|
2525 |
|
/* Scroll bars in GTK has a fixed width, so if we say width 16, it |
2526 |
|
will only be its fixed width (14 is default) anyway, the rest is |
2527 |
|
blank. We are drawing the mode line across scroll bars when |
2528 |
|
the frame is split: |
2529 |
|
|bar| |fringe| |
2530 |
|
---------------- |
2531 |
|
mode line |
2532 |
|
---------------- |
2533 |
|
|bar| |fringe| |
2534 |
|
|
2535 |
|
When we "unsplit" the frame: |
2536 |
|
|
2537 |
|
|bar| |fringe| |
2538 |
|
-| |-| | |
2539 |
|
m¦ |i| | |
2540 |
|
-| |-| | |
2541 |
|
| | | | |
2542 |
|
|
2543 |
|
|
2544 |
|
the remains of the mode line can be seen in these blank spaces. |
2545 |
|
So we must clear them explicitly. |
2546 |
|
GTK scroll bars should do that, but they don't. |
2547 |
|
Also, the scroll bar canonical width may be wider than the width |
2548 |
|
passed in here. */ |
2549 |
|
|
2550 |
|
if (oldtop != -1 && oldleft != -1) |
2551 |
|
{ |
2552 |
|
int gtkextra; |
2553 |
|
int xl, xr, wblank; |
2554 |
|
int bottomdiff, topdiff; |
2555 |
|
|
2556 |
|
gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL); |
2557 |
|
gtkextra = width > slider_width ? (width - slider_width) / 2 : 0; |
2558 |
|
|
2559 |
|
xl = left - winextra; |
2560 |
|
wblank = gtkextra + winextra; |
2561 |
|
xr = left + gtkextra + slider_width; |
2562 |
|
bottomdiff = abs (oldbottom - bottom); |
2563 |
|
topdiff = abs (oldtop - top); |
2564 |
|
|
2565 |
|
if (oldtop > top) |
2566 |
|
{ |
2567 |
|
gdk_window_clear_area (wfixed->window, xl, top, wblank, topdiff); |
2568 |
|
gdk_window_clear_area (wfixed->window, xr, top, wblank, topdiff); |
2569 |
|
} |
2570 |
|
else if (oldtop < top) |
2571 |
|
{ |
2572 |
|
gdk_window_clear_area (wfixed->window, xl, oldtop, wblank, |
2573 |
|
topdiff); |
2574 |
|
gdk_window_clear_area (wfixed->window, xr, oldtop, wblank, |
2575 |
|
topdiff); |
2576 |
|
} |
2577 |
|
|
2578 |
|
if (oldbottom > bottom) |
2579 |
|
{ |
2580 |
|
gdk_window_clear_area (wfixed->window, xl, bottom, wblank, |
2581 |
|
bottomdiff); |
2582 |
|
gdk_window_clear_area (wfixed->window, xr, bottom, wblank, |
2583 |
|
bottomdiff); |
2584 |
|
} |
2585 |
|
else if (oldbottom < bottom) |
2586 |
|
{ |
2587 |
|
gdk_window_clear_area (wfixed->window, xl, oldbottom, wblank, |
2588 |
|
bottomdiff); |
2589 |
|
gdk_window_clear_area (wfixed->window, xr, oldbottom, wblank, |
2590 |
|
bottomdiff); |
2591 |
|
} |
2592 |
|
} |
2593 |
|
|
2594 |
|
/* Move and resize to new values. */ |
2595 |
gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top); |
gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top); |
2596 |
gtk_widget_set_size_request (wscroll, width, gheight); |
gtk_widget_set_size_request (wscroll, width, gheight); |
2597 |
|
|
2598 |
gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE); |
gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE); |
2599 |
|
|
2600 |
/* Must force out update so wscroll gets the resize. |
/* Make GTK draw the new sizes. We are not using a pure GTK event |
2601 |
Otherwise, the gdk_window_clear clears the old window size. */ |
loop so we need to do this. */ |
|
gdk_window_process_all_updates (); |
|
|
|
|
|
/* The scroll bar doesn't explicitly redraw the whole window |
|
|
when a resize occurs. Since the scroll bar seems to be fixed |
|
|
in width it doesn't fill the space reserved, so we must clear |
|
|
the whole window. */ |
|
|
gdk_window_clear (wscroll->window); |
|
|
|
|
|
/* Since we are not using a pure gtk event loop, we must force out |
|
|
pending update events with this call. */ |
|
2602 |
gdk_window_process_all_updates (); |
gdk_window_process_all_updates (); |
2603 |
|
|
2604 |
SET_FRAME_GARBAGED (f); |
SET_FRAME_GARBAGED (f); |
2616 |
GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar)); |
GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar)); |
2617 |
|
|
2618 |
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); |
FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); |
|
struct xg_last_sb_pos *last_pos |
|
|
= (wscroll ? g_object_get_data (G_OBJECT (wscroll), XG_LAST_SB_POS) : 0); |
|
2619 |
|
|
2620 |
BLOCK_INPUT; |
BLOCK_INPUT; |
2621 |
if (wscroll |
|
2622 |
&& ! xg_ignore_next_thumb |
if (wscroll && NILP (bar->dragging)) |
|
&& last_pos |
|
|
&& (last_pos->portion != portion |
|
|
|| last_pos->position != position |
|
|
|| last_pos->whole != whole)) |
|
2623 |
{ |
{ |
2624 |
GtkAdjustment *adj; |
GtkAdjustment *adj; |
2625 |
gdouble shown; |
gdouble shown; |
2628 |
|
|
2629 |
adj = gtk_range_get_adjustment (GTK_RANGE (wscroll)); |
adj = gtk_range_get_adjustment (GTK_RANGE (wscroll)); |
2630 |
|
|
2631 |
|
/* We do the same as for MOTIF in xterm.c, assume 30 chars per line |
2632 |
|
rather than the real portion value. This makes the thumb less likely |
2633 |
|
to resize and that looks better. */ |
2634 |
|
portion = XFASTINT (XWINDOW (bar->window)->height) * 30; |
2635 |
|
/* When the thumb is at the bottom, position == whole. |
2636 |
|
So we need to increase `whole' to make space for the thumb. */ |
2637 |
|
whole += portion; |
2638 |
|
|
2639 |
if (whole <= 0) |
if (whole <= 0) |
2640 |
top = 0, shown = 1; |
top = 0, shown = 1; |
2641 |
else |
else |
2652 |
value = min (value, whole - size); |
value = min (value, whole - size); |
2653 |
value = max (value, XG_SB_MIN); |
value = max (value, XG_SB_MIN); |
2654 |
|
|
|
adj->upper = max (whole, size); |
|
2655 |
adj->page_size = (int)size; |
adj->page_size = (int)size; |
2656 |
|
|
|
/* Assume a page increment is about 95% of the page size */ |
|
|
adj->page_increment = (int) (0.95*adj->page_size); |
|
|
|
|
|
/* Assume all lines are equal. */ |
|
|
adj->step_increment = portion / max (1, FRAME_HEIGHT (f)); |
|
|
|
|
|
if (last_pos) |
|
|
{ |
|
|
last_pos->portion = portion; |
|
|
last_pos->position = position; |
|
|
last_pos->whole = whole; |
|
|
} |
|
|
|
|
2657 |
/* gtk_range_set_value invokes the callback. Set |
/* gtk_range_set_value invokes the callback. Set |
2658 |
ignore_gtk_scrollbar to make the callback do nothing */ |
ignore_gtk_scrollbar to make the callback do nothing */ |
2659 |
xg_ignore_gtk_scrollbar = 1; |
xg_ignore_gtk_scrollbar = 1; |
2660 |
|
|
2661 |
|
gtk_range_set_range (GTK_RANGE (wscroll), adj->lower, max (whole, size)); |
2662 |
|
|
2663 |
|
/* Assume all lines are of equal size. */ |
2664 |
|
/* Assume a page increment is about 95% of the page size */ |
2665 |
|
gtk_range_set_increments (GTK_RANGE (wscroll), |
2666 |
|
portion / max (1, FRAME_HEIGHT (f)), |
2667 |
|
(int) (0.95*adj->page_size)); |
2668 |
|
|
2669 |
gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value); |
gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value); |
2670 |
xg_ignore_gtk_scrollbar = 0; |
xg_ignore_gtk_scrollbar = 0; |
2671 |
|
|
2672 |
/* Make sure the scroll bar is redrawn with new thumb */ |
/* Make GTK draw the new thumb. We are not using a pure GTK event |
2673 |
gtk_widget_queue_draw (wscroll); |
loop so we need to do this. */ |
2674 |
|
gdk_window_process_all_updates (); |
2675 |
} |
} |
2676 |
|
|
|
gdk_window_process_all_updates (); |
|
|
xg_ignore_next_thumb = 0; |
|
2677 |
UNBLOCK_INPUT; |
UNBLOCK_INPUT; |
2678 |
} |
} |
2679 |
|
|
3121 |
xg_initialize () |
xg_initialize () |
3122 |
{ |
{ |
3123 |
xg_ignore_gtk_scrollbar = 0; |
xg_ignore_gtk_scrollbar = 0; |
|
xg_ignore_next_thumb = 0; |
|
3124 |
xg_left_ptr_cursor = 0; |
xg_left_ptr_cursor = 0; |
3125 |
xg_did_tearoff = 0; |
xg_did_tearoff = 0; |
3126 |
|
|