/* * Copyright 2006 Daniel Silverstone * Copyright 2006 Rob Kendrick * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "content/hlcache.h" #include "gtk/window.h" #include "desktop/browser.h" #include "desktop/mouse.h" #include "desktop/options.h" #include "desktop/searchweb.h" #include "desktop/textinput.h" #include "desktop/selection.h" #include "gtk/compat.h" #include "gtk/gui.h" #include "gtk/options.h" #include "gtk/scaffolding.h" #include "gtk/plotters.h" #include "gtk/schedule.h" #include "gtk/tabs.h" #include "utils/log.h" #include "utils/utils.h" struct gui_window { /** The gtk scaffold object containing menu, buttons, url bar, [tabs], * drawing area, etc that may contain one or more gui_windows. */ nsgtk_scaffolding *scaffold; /** The 'content' window that is rendered in the gui_window */ struct browser_window *bw; /** mouse state and events. */ struct { struct gui_window *gui; struct box *box; gdouble pressed_x; gdouble pressed_y; gboolean waiting; browser_mouse_state state; } mouse; /** caret dimension and location for rendering */ int caretx, carety, careth; /** caret shape for rendering */ gui_pointer_shape current_pointer; /** previous event location */ int last_x, last_y; /** display widget for this page or frame */ GtkLayout *layout; /** frames only; top level of gtk structure of gui_window */ GtkScrolledWindow *scrolledwindow; /** handle to the the visible tab */ GtkWidget *tab; /** statusbar */ GtkLabel *status_bar; /** scrollbar paned */ GtkPaned *paned; /** to allow disactivation / resume of normal window behaviour */ gulong signalhandler[NSGTK_WINDOW_SIGNAL_COUNT]; /** list for cleanup */ struct gui_window *next, *prev; }; struct gui_window *window_list = NULL; /**< first entry in win list*/ int temp_open_background = -1; nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g) { return g->scaffold; } struct browser_window *gui_window_get_browser_window(struct gui_window *g) { return g->bw; } unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i) { return g->signalhandler[i]; } GtkLayout *nsgtk_window_get_layout(struct gui_window *g) { return g->layout; } GtkWidget *nsgtk_window_get_tab(struct gui_window *g) { return g->tab; } void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w) { g->tab = w; } struct gui_window *nsgtk_window_iterate(struct gui_window *g) { return g->next; } float nsgtk_get_scale_for_gui(struct gui_window *g) { return g->bw->scale; } /** Attaches the child gui_window (frame) into the parent. * * The window will be resized later. * * \param parent The parent gui window. * \param child The gui window to attach to the parent. */ static void nsgtk_gui_window_attach_child(struct gui_window *parent, struct gui_window *child) { GtkLayout *parent_layout = parent->layout; GtkWidget *child_widget = GTK_WIDGET(child->scrolledwindow); gtk_layout_put(parent_layout, child_widget, 0, 0); } static gboolean nsgtk_window_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct gui_window *g = data; struct gui_window *z; struct rect clip; assert(g); assert(g->bw); for (z = window_list; z && z != g; z = z->next) continue; assert(z); assert(GTK_WIDGET(g->layout) == widget); current_widget = (GtkWidget *)g->layout; current_drawable = g->layout->bin_window; current_gc = gdk_gc_new(current_drawable); #ifdef CAIRO_VERSION current_cr = gdk_cairo_create(current_drawable); #endif plot = nsgtk_plotters; nsgtk_plot_set_scale(g->bw->scale); current_redraw_browser = g->bw; clip.x0 = event->area.x; clip.y0 = event->area.y; clip.x1 = event->area.x + event->area.width; clip.y1 = event->area.y + event->area.height; browser_window_redraw(g->bw, 0, 0, &clip); current_redraw_browser = NULL; if (g->careth != 0) nsgtk_plot_caret(g->caretx, g->carety, g->careth); current_widget = NULL; g_object_unref(current_gc); #ifdef CAIRO_VERSION cairo_destroy(current_cr); #endif return FALSE; } static gboolean nsgtk_window_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct gui_window *g = data; bool shift = event->state & GDK_SHIFT_MASK; bool ctrl = event->state & GDK_CONTROL_MASK; if ((abs(event->x - g->last_x) < 5) && (abs(event->y - g->last_y) < 5)) { /* Mouse hasn't moved far enough from press coordinate for this * to be considered a drag. */ return FALSE; } else { /* This is a drag, ensure it's always treated as such, even if * we drag back over the press location */ g->last_x = INT_MIN; g->last_y = INT_MIN; } if (g->mouse.state & BROWSER_MOUSE_PRESS_1) { /* Start button 1 drag */ browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_1, g->mouse.pressed_x, g->mouse.pressed_y); /* Replace PRESS with HOLDING and declare drag in progress */ g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_HOLDING_1); g->mouse.state |= BROWSER_MOUSE_DRAG_ON; } else if (g->mouse.state & BROWSER_MOUSE_PRESS_2) { /* Start button 2 drag */ browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_2, g->mouse.pressed_x, g->mouse.pressed_y); /* Replace PRESS with HOLDING and declare drag in progress */ g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_HOLDING_2); g->mouse.state |= BROWSER_MOUSE_DRAG_ON; } /* Handle modifiers being removed */ if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift) g->mouse.state ^= BROWSER_MOUSE_MOD_1; if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl) g->mouse.state ^= BROWSER_MOUSE_MOD_2; browser_window_mouse_track(g->bw, g->mouse.state, event->x / g->bw->scale, event->y / g->bw->scale); return TRUE; } static gboolean nsgtk_window_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct gui_window *g = data; gtk_widget_grab_focus(GTK_WIDGET(g->layout)); gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window( g->scaffold)->window)); g->mouse.pressed_x = event->x / g->bw->scale; g->mouse.pressed_y = event->y / g->bw->scale; switch (event->button) { case 1: /* Left button, usually. Pass to core as BUTTON 1. */ g->mouse.state = BROWSER_MOUSE_PRESS_1; break; case 2: /* Middle button, usually. Pass to core as BUTTON 2 */ g->mouse.state = BROWSER_MOUSE_PRESS_2; break; case 3: /* Right button, usually. Action button, context menu. */ browser_window_remove_caret(g->bw); nsgtk_scaffolding_popup_menu(g->scaffold, g->mouse.pressed_x, g->mouse.pressed_y); return TRUE; default: return FALSE; } /* Handle the modifiers too */ if (event->state & GDK_SHIFT_MASK) g->mouse.state |= BROWSER_MOUSE_MOD_1; if (event->state & GDK_CONTROL_MASK) g->mouse.state |= BROWSER_MOUSE_MOD_2; /* Record where we pressed, for use when determining whether to start * a drag in motion notify events. */ g->last_x = event->x; g->last_y = event->y; browser_window_mouse_click(g->bw, g->mouse.state, g->mouse.pressed_x, g->mouse.pressed_y); return TRUE; } static gboolean nsgtk_window_button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct gui_window *g = data; bool shift = event->state & GDK_SHIFT_MASK; bool ctrl = event->state & GDK_CONTROL_MASK; /* If the mouse state is PRESS then we are waiting for a release to emit * a click event, otherwise just reset the state to nothing */ if (g->mouse.state & BROWSER_MOUSE_PRESS_1) g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1); else if (g->mouse.state & BROWSER_MOUSE_PRESS_2) g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2); /* Handle modifiers being removed */ if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift) g->mouse.state ^= BROWSER_MOUSE_MOD_1; if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl) g->mouse.state ^= BROWSER_MOUSE_MOD_2; if (g->mouse.state & (BROWSER_MOUSE_CLICK_1|BROWSER_MOUSE_CLICK_2)) { browser_window_mouse_click(g->bw, g->mouse.state, event->x / g->bw->scale, event->y / g->bw->scale); } else { browser_window_mouse_drag_end(g->bw, 0, event->x / g->bw->scale, event->y / g->bw->scale); } g->mouse.state = 0; return TRUE; } static gboolean nsgtk_window_scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) { struct gui_window *g = data; double value; GtkAdjustment *vscroll = gtk_layout_get_vadjustment(g->layout); GtkAdjustment *hscroll = gtk_layout_get_hadjustment(g->layout); GtkAdjustment *scroll; const GtkAllocation *const alloc = >K_WIDGET(g->layout)->allocation; switch (event->direction) { case GDK_SCROLL_LEFT: scroll = hscroll; value = gtk_adjustment_get_value(scroll) - (scroll->step_increment * 2); if (value < scroll->lower) value = scroll->lower; break; case GDK_SCROLL_UP: scroll = vscroll; value = gtk_adjustment_get_value(scroll) - (scroll->step_increment * 2); if (value < scroll->lower) value = scroll->lower; break; case GDK_SCROLL_RIGHT: scroll = hscroll; value = gtk_adjustment_get_value(scroll) + (scroll->step_increment * 2); if (value > scroll->upper - alloc->width) value = scroll->upper - alloc->width; break; case GDK_SCROLL_DOWN: scroll = vscroll; value = gtk_adjustment_get_value(scroll) + (scroll->step_increment * 2); if (value > scroll->upper - alloc->height) value = scroll->upper - alloc->height; break; default: return TRUE; } gtk_adjustment_set_value(scroll, value); return TRUE; } static gboolean nsgtk_window_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct gui_window *g = data; uint32_t nskey = gtk_gui_gdkkey_to_nskey(event); if (browser_window_key_press(g->bw, nskey)) return TRUE; if ((event->state & 0x7) == 0) { double value; GtkAdjustment *vscroll = gtk_layout_get_vadjustment(g->layout); GtkAdjustment *hscroll = gtk_layout_get_hadjustment(g->layout); GtkAdjustment *scroll; const GtkAllocation *const alloc = >K_WIDGET(g->layout)->allocation; switch (event->keyval) { default: return TRUE; case GDK_Home: case GDK_KP_Home: scroll = vscroll; value = scroll->lower; break; case GDK_End: case GDK_KP_End: scroll = vscroll; value = scroll->upper - alloc->height; if (value < scroll->lower) value = scroll->lower; break; case GDK_Left: case GDK_KP_Left: scroll = hscroll; value = gtk_adjustment_get_value(scroll) - scroll->step_increment; if (value < scroll->lower) value = scroll->lower; break; case GDK_Up: case GDK_KP_Up: scroll = vscroll; value = gtk_adjustment_get_value(scroll) - scroll->step_increment; if (value < scroll->lower) value = scroll->lower; break; case GDK_Right: case GDK_KP_Right: scroll = hscroll; value = gtk_adjustment_get_value(scroll) + scroll->step_increment; if (value > scroll->upper - alloc->width) value = scroll->upper - alloc->width; break; case GDK_Down: case GDK_KP_Down: scroll = vscroll; value = gtk_adjustment_get_value(scroll) + scroll->step_increment; if (value > scroll->upper - alloc->height) value = scroll->upper - alloc->height; break; case GDK_Page_Up: case GDK_KP_Page_Up: scroll = vscroll; value = gtk_adjustment_get_value(scroll) - scroll->page_increment; if (value < scroll->lower) value = scroll->lower; break; case GDK_Page_Down: case GDK_KP_Page_Down: scroll = vscroll; value = gtk_adjustment_get_value(scroll) + scroll->page_increment; if (value > scroll->upper - alloc->height) value = scroll->upper - alloc->height; break; } gtk_adjustment_set_value(scroll, value); } return TRUE; } static gboolean nsgtk_window_size_allocate_event(GtkWidget *widget, GtkAllocation *allocation, gpointer data) { struct gui_window *g = data; g->bw->reformat_pending = true; browser_reformat_pending = true; if (g->paned != NULL) { /* Set status bar / scroll bar proportion according to * option_toolbar_status_width */ /* TODO: Probably want to detect when the user adjusts the * status bar width, remember that proportion for the * window, and use that here. */ gtk_paned_set_position(g->paned, (option_toolbar_status_width * allocation->width) / 10000); } return TRUE; } /* Core interface docuemnted in desktop/gui.h to create a gui_window */ struct gui_window *gui_create_browser_window(struct browser_window *bw, struct browser_window *clone, bool new_tab) { struct gui_window *g; /**< what we're creating to return */ GtkPolicyType scrollpolicy; g = calloc(1, sizeof(*g)); if (!g) { warn_user("NoMemory", 0); return 0; } LOG(("Creating gui window %p for browser window %p", g, bw)); g->bw = bw; g->paned = NULL; g->mouse.state = 0; g->current_pointer = GUI_POINTER_DEFAULT; if (clone != NULL) bw->scale = clone->scale; else bw->scale = (float) option_scale / 100; g->careth = 0; if (bw->parent != NULL) /* Find our parent's scaffolding */ g->scaffold = bw->parent->window->scaffold; else if (new_tab) g->scaffold = clone->window->scaffold; else /* Now construct and attach a scaffold */ g->scaffold = nsgtk_new_scaffolding(g); if (g->scaffold == NULL) { warn_user("NoMemory", 0); free(g); return NULL; } /* Construct our primary elements */ if (bw->parent == NULL) { /* top-level document (not a frame) => create a new tab */ GladeXML *xml = glade_xml_new(glade_netsurf_file_location, "tabContents", NULL); if (!xml) { warn_user("MiscError", "Failed to create tab contents"); free(g); return 0; } GtkWidget *tab_contents = glade_xml_get_widget(xml, "tabContents"); g->layout = GTK_LAYOUT(glade_xml_get_widget(xml, "layout")); g->status_bar = GTK_LABEL(glade_xml_get_widget(xml, "status_bar")); g->paned = GTK_PANED(glade_xml_get_widget(xml, "hpaned1")); /* connect the scrollbars to the layout widget */ gtk_layout_set_hadjustment(g->layout, gtk_range_get_adjustment(GTK_RANGE( glade_xml_get_widget(xml, "hscrollbar")))); gtk_layout_set_vadjustment(g->layout, gtk_range_get_adjustment(GTK_RANGE( glade_xml_get_widget(xml, "vscrollbar")))); /* add the tab to the scaffold */ bool tempback = true; switch (temp_open_background) { case -1: tempback = !(option_focus_new); break; case 0: tempback = false; break; case 1: tempback = true; break; } g_object_set_data(G_OBJECT(tab_contents), "gui_window", g); nsgtk_tab_add(g, tab_contents, tempback); g_object_unref(xml); } else { /* frame or iframe => create a child layout */ g->layout = GTK_LAYOUT(gtk_layout_new(NULL, NULL)); gtk_container_set_border_width(GTK_CONTAINER(g->layout), 0); g->scrolledwindow = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); g_object_set_data(G_OBJECT(g->scrolledwindow), "gui_window", g); gtk_container_add(GTK_CONTAINER(g->scrolledwindow), GTK_WIDGET(g->layout)); gtk_scrolled_window_set_shadow_type(g->scrolledwindow, GTK_SHADOW_NONE); g->tab = NULL; /* Attach ourselves into our parent at the right point */ nsgtk_gui_window_attach_child(bw->parent->window, g); gtk_widget_show(GTK_WIDGET(g->scrolledwindow)); } switch(bw->scrolling) { case SCROLLING_NO: scrollpolicy = GTK_POLICY_NEVER; break; case SCROLLING_YES: scrollpolicy = GTK_POLICY_ALWAYS; break; case SCROLLING_AUTO: default: scrollpolicy = GTK_POLICY_AUTOMATIC; break; } switch (bw->browser_window_type) { case BROWSER_WINDOW_FRAMESET: if (g->scrolledwindow) gtk_scrolled_window_set_policy(g->scrolledwindow, GTK_POLICY_NEVER, GTK_POLICY_NEVER); break; case BROWSER_WINDOW_FRAME: if (g->scrolledwindow) gtk_scrolled_window_set_policy(g->scrolledwindow, scrollpolicy, scrollpolicy); break; case BROWSER_WINDOW_NORMAL: if (g->scrolledwindow) gtk_scrolled_window_set_policy(g->scrolledwindow, scrollpolicy, scrollpolicy); break; case BROWSER_WINDOW_IFRAME: if (g->scrolledwindow) gtk_scrolled_window_set_policy(g->scrolledwindow, scrollpolicy, scrollpolicy); break; } /* Attach ourselves to the list (push_top) */ if (window_list) window_list->prev = g; g->next = window_list; g->prev = NULL; window_list = g; /* set the events we're interested in receiving from the browser's * drawing area. */ gtk_widget_add_events(GTK_WIDGET(g->layout), GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK); nsgtk_widget_set_can_focus(GTK_WIDGET(g->layout), TRUE); /* set the default background colour of the drawing area to white. */ gtk_widget_modify_bg(GTK_WIDGET(g->layout), GTK_STATE_NORMAL, &((GdkColor) { 0, 0xffff, 0xffff, 0xffff } )); #define CONNECT(obj, sig, callback, ptr) \ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) g->signalhandler[NSGTK_WINDOW_SIGNAL_REDRAW] = CONNECT(g->layout, "expose_event", nsgtk_window_expose_event, g); CONNECT(g->layout, "motion_notify_event", nsgtk_window_motion_notify_event, g); g->signalhandler[NSGTK_WINDOW_SIGNAL_CLICK] = CONNECT(g->layout, "button_press_event", nsgtk_window_button_press_event, g); CONNECT(g->layout, "button_release_event", nsgtk_window_button_release_event, g); CONNECT(g->layout, "key_press_event", nsgtk_window_keypress_event, g); CONNECT(g->layout, "size_allocate", nsgtk_window_size_allocate_event, g); CONNECT(g->layout, "scroll_event", nsgtk_window_scroll_event, g); return g; } void gui_window_position_frame(struct gui_window *g, int x0, int y0, int x1, int y1) { /* g is a child frame, we need to place it relative to its parent */ GtkWidget *w = GTK_WIDGET(g->scrolledwindow); GtkLayout *f = g->bw->parent->window->layout; int width = x1 - x0 + 2, height = y1 - y0 + 2; assert(w); assert(f); if (width < 1) width = 1; if (height < 1) height = 1; LOG(("%s: %d,%d %dx%d", g->bw->name, x0, y0, width, height)); /* if the window has not changed position or size, do not bother * moving/resising it. */ LOG((" current: %d,%d %dx%d", w->allocation.x, w->allocation.y, w->allocation.width, w->allocation.height)); if (w->allocation.x != x0 || w->allocation.y != y0 || w->allocation.width != width || w->allocation.height != height) { LOG((" frame has moved/resized.")); gtk_layout_move(f, w, x0, y0); gtk_widget_set_size_request(w, width, height); } } void nsgtk_reflow_all_windows(void) { for (struct gui_window *g = window_list; g; g = g->next) { nsgtk_tab_options_changed(GTK_WIDGET( nsgtk_scaffolding_notebook(g->scaffold))); g->bw->reformat_pending = true; } browser_reformat_pending = true; } /** * Process pending reformats */ void nsgtk_window_process_reformats(void) { struct gui_window *g; browser_reformat_pending = false; for (g = window_list; g; g = g->next) { GtkWidget *widget = GTK_WIDGET(g->layout); if (!g->bw->reformat_pending) continue; g->bw->reformat_pending = false; browser_window_reformat(g->bw, widget->allocation.width, widget->allocation.height); } } void nsgtk_window_destroy_browser(struct gui_window *g) { browser_window_destroy(g->bw); } void gui_window_destroy(struct gui_window *g) { if (g->prev) g->prev->next = g->next; else window_list = g->next; if (g->next) g->next->prev = g->prev; LOG(("Destroying gui_window %p", g)); assert(g != NULL); assert(g->bw != NULL); LOG((" Scaffolding: %p", g->scaffold)); LOG((" Window name: %s", g->bw->name)); if (g->scrolledwindow == NULL) { /* tab => remove tab */ gtk_widget_destroy(gtk_widget_get_parent(GTK_WIDGET(g->layout))); /* if it was the last tab, destroy scaffold too */ gint numbertabs = gtk_notebook_get_n_pages(nsgtk_scaffolding_notebook(g->scaffold)); if (numbertabs == 0) nsgtk_scaffolding_destroy(g->scaffold); } else { /* frame within a document => destroy frame only */ gtk_widget_destroy(GTK_WIDGET(g->scrolledwindow)); } free(g); } static void nsgtk_redraw_caret(struct gui_window *g) { if (g->careth == 0) return; gui_window_redraw(g, g->caretx, g->carety, g->caretx, g->carety + g->careth); } void gui_window_redraw(struct gui_window *g, int x0, int y0, int x1, int y1) { int sx, sy; gui_window_get_scroll(g, &sx, &sy); gtk_widget_queue_draw_area(GTK_WIDGET(g->layout), x0 - sx, y0 - sy, x1-x0+1, y1-y0+1); } void gui_window_redraw_window(struct gui_window *g) { gtk_widget_queue_draw(GTK_WIDGET(g->layout)); } void gui_window_update_box(struct gui_window *g, const union content_msg_data *data) { int sx, sy; hlcache_handle *c = g->bw->current_content; if (c == NULL) return; gui_window_get_scroll(g, &sx, &sy); gtk_widget_queue_draw_area(GTK_WIDGET(g->layout), data->redraw.x * g->bw->scale - sx, data->redraw.y * g->bw->scale - sy, data->redraw.width * g->bw->scale, data->redraw.height * g->bw->scale); } void gui_window_set_status(struct gui_window *g, const char *text) { assert(g); assert(g->status_bar); gtk_label_set_text(g->status_bar, text); } bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy) { GtkAdjustment *vadj = gtk_layout_get_vadjustment(g->layout); GtkAdjustment *hadj = gtk_layout_get_hadjustment(g->layout); assert(vadj); assert(hadj); *sy = (int)(gtk_adjustment_get_value(vadj)); *sx = (int)(gtk_adjustment_get_value(hadj)); return true; } void gui_window_set_scroll(struct gui_window *g, int sx, int sy) { GtkAdjustment *vadj = gtk_layout_get_vadjustment(g->layout); GtkAdjustment *hadj = gtk_layout_get_hadjustment(g->layout); gdouble vlower, vpage, vupper, hlower, hpage, hupper, x = (double)sx, y = (double)sy; assert(vadj); assert(hadj); g_object_get(vadj, "page-size", &vpage, "lower", &vlower, "upper", &vupper, NULL); g_object_get(hadj, "page-size", &hpage, "lower", &hlower, "upper", &hupper, NULL); if (x < hlower) x = hlower; if (x > (hupper - hpage)) x = hupper - hpage; if (y < vlower) y = vlower; if (y > (vupper - vpage)) y = vupper - vpage; gtk_adjustment_set_value(vadj, y); gtk_adjustment_set_value(hadj, x); } void gui_window_scroll_visible(struct gui_window *g, int x0, int y0, int x1, int y1) { gui_window_set_scroll(g,x0,y0); } /** * Set the scale setting of a window * * \param g gui window * \param scale scale value (1.0 == normal scale) */ void gui_window_set_scale(struct gui_window *g, float scale) { } void gui_window_update_extent(struct gui_window *g) { if (!g->bw->current_content) return; gtk_layout_set_size(g->layout, content_get_width(g->bw->current_content) * g->bw->scale, content_get_height(g->bw->current_content) * g->bw->scale); } static GdkCursor *nsgtk_create_menu_cursor(void) { static char menu_cursor_bits[] = { 0x00, 0x00, 0x80, 0x7F, 0x88, 0x40, 0x9E, 0x5E, 0x88, 0x40, 0x80, 0x56, 0x80, 0x40, 0x80, 0x5A, 0x80, 0x40, 0x80, 0x5E, 0x80, 0x40, 0x80, 0x56, 0x80, 0x40, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, }; static char menu_cursor_mask_bits[] = { 0xC0, 0xFF, 0xC8, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xC8, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0x00, 0x00, }; static GdkCursor *r; static GdkColor fg = { 0, 0, 0, 0 }; static GdkColor bg = { 0, 65535, 65535, 65535 }; GdkPixmap *source, *mask; if (r != NULL) return r; source = gdk_bitmap_create_from_data(NULL, menu_cursor_bits, 16, 16); mask = gdk_bitmap_create_from_data (NULL, menu_cursor_mask_bits, 16, 16); r = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 0, 3); gdk_pixmap_unref(source); gdk_pixmap_unref(mask); return r; } void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) { GdkCursor *cursor = NULL; GdkCursorType cursortype; bool nullcursor = false; if (g->current_pointer == shape) return; g->current_pointer = shape; switch (shape) { case GUI_POINTER_POINT: cursortype = GDK_HAND2; break; case GUI_POINTER_CARET: cursortype = GDK_XTERM; break; case GUI_POINTER_UP: cursortype = GDK_TOP_SIDE; break; case GUI_POINTER_DOWN: cursortype = GDK_BOTTOM_SIDE; break; case GUI_POINTER_LEFT: cursortype = GDK_LEFT_SIDE; break; case GUI_POINTER_RIGHT: cursortype = GDK_RIGHT_SIDE; break; case GUI_POINTER_LD: cursortype = GDK_BOTTOM_LEFT_CORNER; break; case GUI_POINTER_RD: cursortype = GDK_BOTTOM_RIGHT_CORNER; break; case GUI_POINTER_LU: cursortype = GDK_TOP_LEFT_CORNER; break; case GUI_POINTER_RU: cursortype = GDK_TOP_RIGHT_CORNER; break; case GUI_POINTER_CROSS: cursortype = GDK_CROSS; break; case GUI_POINTER_MOVE: cursortype = GDK_FLEUR; break; case GUI_POINTER_WAIT: cursortype = GDK_WATCH; break; case GUI_POINTER_HELP: cursortype = GDK_QUESTION_ARROW; break; case GUI_POINTER_MENU: cursor = nsgtk_create_menu_cursor(); nullcursor = true; break; case GUI_POINTER_PROGRESS: /* In reality, this needs to be the funky left_ptr_watch * which we can't do easily yet. */ cursortype = GDK_WATCH; break; /* The following we're not sure about */ case GUI_POINTER_NO_DROP: case GUI_POINTER_NOT_ALLOWED: case GUI_POINTER_DEFAULT: default: nullcursor = true; } if (!nullcursor) cursor = gdk_cursor_new_for_display( gtk_widget_get_display( GTK_WIDGET(g->layout)), cursortype); gdk_window_set_cursor(GTK_WIDGET(g->layout)->window, cursor); if (!nullcursor) gdk_cursor_unref(cursor); } void gui_window_hide_pointer(struct gui_window *g) { } void gui_window_place_caret(struct gui_window *g, int x, int y, int height) { nsgtk_redraw_caret(g); g->caretx = x; g->carety = y + 1; g->careth = height - 2; nsgtk_redraw_caret(g); gtk_widget_grab_focus(GTK_WIDGET(g->layout)); } void gui_window_remove_caret(struct gui_window *g) { int oh = g->careth; if (oh == 0) return; g->careth = 0; gui_window_redraw(g, g->caretx, g->carety, g->caretx, g->carety + oh); } void gui_window_new_content(struct gui_window *g) { } bool gui_window_scroll_start(struct gui_window *g) { return true; } bool gui_window_box_scroll_start(struct gui_window *g, int x0, int y0, int x1, int y1) { return true; } void gui_drag_save_object(gui_save_type type, hlcache_handle *c, struct gui_window *g) { } void gui_drag_save_selection(struct selection *s, struct gui_window *g) { } void gui_window_get_dimensions(struct gui_window *g, int *width, int *height, bool scaled) { *width = GTK_WIDGET(g->layout)->allocation.width; *height = GTK_WIDGET(g->layout)->allocation.height; if (scaled) { *width /= g->bw->scale; *height /= g->bw->scale; } LOG(("\tWINDOW WIDTH: %i\n", *width)); LOG(("\tWINDOW HEIGHT: %i\n", *height)); } bool gui_window_frame_resize_start(struct gui_window *g) { return true; }