+
+// update the ordering of components in the main vbox
+
+const char *vbox_component_names[VBOX_MAIN_NITEMS]=
+ {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
+
+void update_vbox_order(int *order)
+{
+ int i, j;
+ GtkWidget *child;
+ GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
+ gboolean present[VBOX_MAIN_NITEMS];
+
+ for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
+ j=0;
+ for (i=0; i<VBOX_MAIN_NITEMS; i++) {
+ if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
+ present[order[i]] = TRUE;
+ child = GET_COMPONENT(vbox_component_names[order[i]]);
+ gtk_box_reorder_child(vboxMain, child, j++);
+ gtk_widget_show(child);
+ }
+ for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
+ if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
+}
+
+gchar *make_cur_font_name(void)
+{
+ gchar *str;
+ struct Item *it;
+
+ if (ui.cur_item_type == ITEM_TEXT)
+ str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
+ else if (ui.selection!=NULL && ui.selection->items!=NULL &&
+ ui.selection->items->next==NULL &&
+ (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
+ str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
+ else
+ str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
+ return str;
+}
+
+void update_font_button(void)
+{
+ gchar *str;
+
+ str = make_cur_font_name();
+ gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
+ g_free(str);
+}
+
+gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
+{
+ return GTK_WIDGET_SENSITIVE(widget);
+}
+
+gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
+{
+ if (ui.cur_item_type == ITEM_TEXT) {
+ g_signal_stop_emission_by_name(widget, "can-activate-accel");
+ return FALSE;
+ }
+ return GTK_WIDGET_SENSITIVE(widget);
+}
+
+void allow_all_accels(void)
+{
+ g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("editCut"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
+ "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
+ "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+/* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL); */
+ g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+ g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
+ "can-activate-accel", G_CALLBACK(can_accel), NULL);
+}
+
+void add_scroll_bindings(void)
+{
+ GtkBindingSet *binding_set;
+
+ binding_set = gtk_binding_set_by_class(
+ G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
+ gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
+ G_TYPE_BOOLEAN, TRUE);
+ gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
+ G_TYPE_BOOLEAN, TRUE);
+ gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
+ G_TYPE_BOOLEAN, TRUE);
+ gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
+ "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
+ G_TYPE_BOOLEAN, TRUE);
+}
+
+gboolean is_event_within_textview(GdkEventButton *event)
+{
+ double pt[2];
+
+ if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
+ get_pointer_coords((GdkEvent *)event, pt);
+ if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
+ if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
+ return TRUE;
+}
+
+void hide_unimplemented(void)
+{
+ gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
+ gtk_widget_hide(GET_COMPONENT("journalFlatten"));
+ gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
+ gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
+ gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
+ gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
+ gtk_widget_hide(GET_COMPONENT("helpIndex"));
+
+ /* config file only works with glib 2.6 and beyond */
+ if (glib_minor_version<6) {
+ gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
+ gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
+ }
+ /* gtkprint only works with gtk+ 2.10 and beyond */
+ if (gtk_check_version(2, 10, 0)) {
+ gtk_widget_hide(GET_COMPONENT("filePrint"));
+ }
+
+ /* screenshot feature doesn't work yet in Win32 */
+#ifdef WIN32
+ gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
+#endif
+}
+
+// toggle fullscreen mode
+void do_fullscreen(gboolean active)
+{
+ end_text();
+ ui.fullscreen = active;
+ gtk_check_menu_item_set_active(
+ GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
+ gtk_toggle_tool_button_set_active(
+ GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
+
+ if (ui.fullscreen) {
+#ifdef WIN32
+ gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
+ gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
+ gdk_screen_height());
+#endif
+ gtk_window_fullscreen(GTK_WINDOW(winMain));
+ }
+ else {
+#ifdef WIN32
+ gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
+ gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
+ ui.pre_fullscreen_height);
+#endif
+ gtk_window_unfullscreen(GTK_WINDOW(winMain));
+ }
+
+ update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
+}
+
+/* attempt to work around GTK+ 2.16/2.17 bugs where random interface
+ elements receive XInput events that they can't handle properly */
+
+// prevent interface items from getting bogus XInput events
+
+gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
+{
+ if (event->type == GDK_MOTION_NOTIFY &&
+ event->motion.device != gdk_device_get_core_pointer())
+ return TRUE;
+ if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
+ event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
+ event->button.device != gdk_device_get_core_pointer())
+ return TRUE;
+ return FALSE;
+}
+
+/* Code to turn an extended input event into a core event and send it to
+ a different GdkWindow -- e.g. could be used when a click in a text edit box
+ gets sent to the canvas instead due to incorrect event translation.
+ We now turn off xinput altogether while editing text under GTK+ 2.17, so
+ this isn't needed any more... but could become useful again someday!
+*/
+
+/*
+gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
+{
+ int ix, iy;
+ GdkWindow *window;
+
+ if (user_data) window = (GdkWindow *)user_data;
+ else window = widget->window;
+
+ if (event->type == GDK_MOTION_NOTIFY &&
+ event->motion.device != gdk_device_get_core_pointer()) {
+// printf("fixing motion\n");
+ gdk_window_get_pointer(window, &ix, &iy, NULL);
+ event->motion.x = ix; event->motion.y = iy;
+ event->motion.device = gdk_device_get_core_pointer();
+ g_object_unref(event->motion.window);
+ event->motion.window = g_object_ref(window);
+ gtk_widget_event(widget, event);
+ return TRUE;
+ }
+ if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
+ event->button.device != gdk_device_get_core_pointer()) {
+// printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
+ gdk_window_get_pointer(window, &ix, &iy, NULL);
+ event->button.x = ix; event->button.y = iy;
+ event->button.device = gdk_device_get_core_pointer();
+ g_object_unref(event->button.window);
+ event->button.window = g_object_ref(window);
+// printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
+ gtk_widget_event(widget, event);
+ return TRUE;
+ }
+ return FALSE;
+}
+*/
+
+
+/* When enter is pressed into page spinbox, send focus back to canvas. */
+
+gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
+{
+ reset_focus();
+ return FALSE;
+}
+
+/* recursively unset widget flags */
+
+void unset_flags(GtkWidget *w, gpointer flag)
+{
+ GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
+ if(GTK_IS_CONTAINER(w))
+ gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
+}
+
+/* reset focus when a key or button press event reaches someone, or when the
+ page-number spin button should relinquish control... */
+
+gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
+{
+ if (w == GET_COMPONENT("hbox1")) {
+ /* the event won't be processed since the hbox1 doesn't know what to do with it,
+ so we might as well kill it and avoid confusing ourselves when it gets
+ propagated further ... */
+ return TRUE;
+ }
+ if (w == GET_COMPONENT("spinPageNo")) {
+ /* we let the spin button take care of itself, and don't steal its focus,
+ unless the user presses Esc or Tab (in those cases we intervene) */
+ if (ev->type != GDK_KEY_PRESS) return FALSE;
+ if (ev->key.keyval == GDK_Escape)
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
+ else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
+ return FALSE; // let the spin button process it
+ }
+
+ // otherwise, we want to make sure the canvas or text item gets focus back...
+ reset_focus();
+ return FALSE;
+}
+
+void install_focus_hooks(GtkWidget *w, gpointer data)
+{
+ if (w == NULL) return;
+ g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
+ g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
+ if (GTK_IS_MENU_ITEM(w)) {
+ g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
+ install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
+ }
+ if(GTK_IS_CONTAINER(w))
+ gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
+}
+
+// wrapper for missing poppler functions (defunct poppler-gdk api)
+
+static void
+wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
+ GdkPixbuf *pixbuf)
+{
+ int cairo_width, cairo_height, cairo_rowstride;
+ unsigned char *pixbuf_data, *dst, *cairo_data;
+ int pixbuf_rowstride, pixbuf_n_channels;
+ unsigned int *src;
+ int x, y;
+
+ cairo_width = cairo_image_surface_get_width (surface);
+ cairo_height = cairo_image_surface_get_height (surface);
+ cairo_rowstride = cairo_image_surface_get_stride (surface);
+ cairo_data = cairo_image_surface_get_data (surface);
+
+ pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
+ pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ if (cairo_width > gdk_pixbuf_get_width (pixbuf))
+ cairo_width = gdk_pixbuf_get_width (pixbuf);
+ if (cairo_height > gdk_pixbuf_get_height (pixbuf))
+ cairo_height = gdk_pixbuf_get_height (pixbuf);
+ for (y = 0; y < cairo_height; y++)
+ {
+ src = (unsigned int *) (cairo_data + y * cairo_rowstride);
+ dst = pixbuf_data + y * pixbuf_rowstride;
+ for (x = 0; x < cairo_width; x++)
+ {
+ dst[0] = (*src >> 16) & 0xff;
+ dst[1] = (*src >> 8) & 0xff;
+ dst[2] = (*src >> 0) & 0xff;
+ if (pixbuf_n_channels == 4)
+ dst[3] = (*src >> 24) & 0xff;
+ dst += pixbuf_n_channels;
+ src++;
+ }
+ }
+}
+
+void
+wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ double scale,
+ int rotation,
+ GdkPixbuf *pixbuf)
+{
+ cairo_t *cr;
+ cairo_surface_t *surface;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ src_width, src_height);
+ cr = cairo_create (surface);
+ cairo_save (cr);
+ switch (rotation) {
+ case 90:
+ cairo_translate (cr, src_x + src_width, -src_y);
+ break;
+ case 180:
+ cairo_translate (cr, src_x + src_width, src_y + src_height);
+ break;
+ case 270:
+ cairo_translate (cr, -src_x, src_y + src_height);
+ break;
+ default:
+ cairo_translate (cr, -src_x, -src_y);
+ }
+
+ if (scale != 1.0)
+ cairo_scale (cr, scale, scale);
+
+ if (rotation != 0)
+ cairo_rotate (cr, rotation * G_PI / 180.0);
+
+ poppler_page_render (page, cr);
+ cairo_restore (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
+ cairo_surface_destroy (surface);
+}