You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
841 lines
21 KiB
841 lines
21 KiB
#include "XournalWidget.h" |
|
|
|
#include "control/Control.h" |
|
#include "control/tools/EditSelection.h" |
|
#include "control/settings/ButtonConfig.h" |
|
#include "control/settings/Settings.h" |
|
#include "gui/Cursor.h" |
|
#include "gui/Layout.h" |
|
#include "gui/pageposition/PagePositionCache.h" |
|
#include "gui/pageposition/PagePositionHandler.h" |
|
#include "gui/Shadow.h" |
|
#include "gui/XournalView.h" |
|
#include "util/DeviceListHelper.h" |
|
|
|
#include <config-debug.h> |
|
#include <Rectangle.h> |
|
#include <Util.h> |
|
#include <XInputUtils.h> |
|
|
|
#include <gdk/gdk.h> |
|
|
|
#include <gdk/gdkkeysyms.h> |
|
#include <math.h> |
|
#include <iostream> |
|
using std::cout; |
|
using std::endl; |
|
|
|
static void gtk_xournal_class_init(GtkXournalClass* klass); |
|
static void gtk_xournal_init(GtkXournal* xournal); |
|
static void gtk_xournal_get_preferred_width(GtkWidget *widget, |
|
gint *minimal_width, |
|
gint *natural_width); |
|
static void gtk_xournal_get_preferred_height(GtkWidget *widget, |
|
gint *minimal_height, |
|
gint *natural_height); |
|
static void gtk_xournal_size_allocate(GtkWidget* widget, |
|
GtkAllocation* allocation); |
|
static void gtk_xournal_realize(GtkWidget* widget); |
|
static gboolean gtk_xournal_draw(GtkWidget* widget, cairo_t* cr); |
|
static void gtk_xournal_destroy(GtkWidget* object); |
|
|
|
static gboolean gtk_xournal_button_press_event(GtkWidget* widget, GdkEventButton* event); |
|
static gboolean gtk_xournal_button_release_event(GtkWidget* widget, GdkEventButton* event); |
|
static gboolean gtk_xournal_motion_notify_event(GtkWidget* widget, GdkEventMotion* event); |
|
|
|
static gboolean gtk_xournal_touch_event(GtkWidget* widget, GdkEventTouch* event); |
|
|
|
static gboolean gtk_xournal_key_press_event(GtkWidget* widget, GdkEventKey* event); |
|
static gboolean gtk_xournal_key_release_event(GtkWidget* widget, GdkEventKey* event); |
|
|
|
static void gtk_xournal_scroll_mouse_event(GtkXournal* xournal, GdkEventMotion* event); |
|
|
|
XojPageView *current_view; |
|
|
|
GType gtk_xournal_get_type(void) |
|
{ |
|
static GType gtk_xournal_type = 0; |
|
|
|
if(!gtk_xournal_type) |
|
{ |
|
static const GTypeInfo gtk_xournal_info = |
|
{ |
|
sizeof(GtkXournalClass), |
|
// base initialize |
|
NULL, |
|
// base finalize |
|
NULL, |
|
// class initialize |
|
(GClassInitFunc) gtk_xournal_class_init, |
|
// class finalize |
|
NULL, |
|
// class data, |
|
NULL, |
|
// instance size |
|
sizeof(GtkXournal), |
|
// n_preallocs |
|
0, |
|
// instance init |
|
(GInstanceInitFunc) gtk_xournal_init, |
|
// value table |
|
(const GTypeValueTable*) NULL |
|
}; |
|
|
|
gtk_xournal_type = g_type_register_static(GTK_TYPE_WIDGET, |
|
"GtkXournal", |
|
>k_xournal_info, |
|
(GTypeFlags) 0); |
|
} |
|
|
|
return gtk_xournal_type; |
|
} |
|
|
|
static void gtk_xournal_init_touch_handling(GtkXournal* xournal) |
|
{ |
|
// Currently does not work, needs further testing |
|
// Settings* settings = xournal->view->getControl()->getSettings(); |
|
// ButtonConfig* cfg = settings->getTouchButtonConfig(); |
|
// |
|
// if (cfg->getDisableDrawing()) |
|
// { |
|
// DeviceListHelper devList; |
|
// for (InputDevice& dev : devList.getDeviceList()) |
|
// { |
|
// if (cfg->device == dev.getName()) |
|
// { |
|
// printf("Disable device for drawing: %s\n", dev.getName().c_str()); |
|
//// gtk_widget_set_device_enabled(GTK_WIDGET(xournal), dev.getDevice(), false); |
|
// gtk_widget_set_device_events(GTK_WIDGET(xournal), dev.getDevice(), (GdkEventMask) 0); |
|
// return; |
|
// } |
|
// } |
|
// |
|
// printf("Could NOT disable device for drawing!\n"); |
|
// } |
|
} |
|
|
|
GtkWidget* gtk_xournal_new(XournalView* view, GtkScrollable* parent) |
|
{ |
|
GtkXournal* xoj = GTK_XOURNAL(g_object_new(gtk_xournal_get_type(), NULL)); |
|
xoj->view = view; |
|
xoj->scrollX = 0; |
|
xoj->scrollY = 0; |
|
xoj->x = 0; |
|
xoj->y = 0; |
|
xoj->layout = new Layout(view, |
|
gtk_scrollable_get_hadjustment(parent), |
|
gtk_scrollable_get_vadjustment(parent)); |
|
xoj->currentInputPage = NULL; |
|
xoj->pagePositionCache = new PagePositionCache(); |
|
|
|
xoj->lastMousePositionX = 0; |
|
xoj->lastMousePositionY = 0; |
|
xoj->scrollOffsetX = 0; |
|
xoj->scrollOffsetY = 0; |
|
xoj->inScrolling = false; |
|
|
|
xoj->selection = NULL; |
|
xoj->shiftDown = false; |
|
|
|
gtk_xournal_init_touch_handling(xoj); |
|
|
|
return GTK_WIDGET(xoj); |
|
} |
|
|
|
static void gtk_xournal_class_init(GtkXournalClass* klass) |
|
{ |
|
GtkWidgetClass* widget_class; |
|
|
|
widget_class = (GtkWidgetClass*) klass; |
|
|
|
widget_class->realize = gtk_xournal_realize; |
|
widget_class->get_preferred_width = gtk_xournal_get_preferred_width; |
|
widget_class->get_preferred_height = gtk_xournal_get_preferred_height; |
|
widget_class->size_allocate = gtk_xournal_size_allocate; |
|
|
|
widget_class->button_press_event = gtk_xournal_button_press_event; |
|
widget_class->button_release_event = gtk_xournal_button_release_event; |
|
widget_class->motion_notify_event = gtk_xournal_motion_notify_event; |
|
|
|
widget_class->touch_event = gtk_xournal_touch_event; |
|
|
|
widget_class->key_press_event = gtk_xournal_key_press_event; |
|
widget_class->key_release_event = gtk_xournal_key_release_event; |
|
|
|
widget_class->draw = gtk_xournal_draw; |
|
|
|
widget_class->destroy = gtk_xournal_destroy; |
|
} |
|
|
|
static gboolean gtk_xournal_key_press_event(GtkWidget* widget, GdkEventKey* event) |
|
{ |
|
g_return_val_if_fail(widget != NULL, FALSE); |
|
g_return_val_if_fail(GTK_IS_XOURNAL(widget), FALSE); |
|
g_return_val_if_fail(event != NULL, FALSE); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
// Shift alone pressed. Is there a constant? |
|
if (event->is_modifier && (event->keyval == 0xFFE1)) |
|
{ |
|
xournal->shiftDown = true; |
|
} |
|
|
|
EditSelection* selection = xournal->selection; |
|
if (selection) |
|
{ |
|
int d = 3; |
|
|
|
if ((event->state & GDK_MOD1_MASK) || (event->state & GDK_SHIFT_MASK)) |
|
{ |
|
if (event->state & GDK_MOD1_MASK) |
|
{ |
|
d = 1; |
|
} |
|
else |
|
{ |
|
d = 20; |
|
} |
|
} |
|
|
|
if (event->keyval == GDK_KEY_Left) |
|
{ |
|
selection->moveSelection(d, 0); |
|
return true; |
|
} |
|
else if (event->keyval == GDK_KEY_Up) |
|
{ |
|
selection->moveSelection(0, d); |
|
return true; |
|
} |
|
else if (event->keyval == GDK_KEY_Right) |
|
{ |
|
selection->moveSelection(-d, 0); |
|
return true; |
|
} |
|
else if (event->keyval == GDK_KEY_Down) |
|
{ |
|
selection->moveSelection(0, -d); |
|
return true; |
|
} |
|
} |
|
|
|
return xournal->view->onKeyPressEvent(event); |
|
} |
|
|
|
static gboolean gtk_xournal_key_release_event(GtkWidget* widget, GdkEventKey* event) |
|
{ |
|
g_return_val_if_fail(widget != NULL, false); |
|
g_return_val_if_fail(GTK_IS_XOURNAL(widget), false); |
|
g_return_val_if_fail(event != NULL, false); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
if (event->is_modifier && (event->state & GDK_SHIFT_MASK)) |
|
{ |
|
xournal->shiftDown = false; |
|
} |
|
|
|
return xournal->view->onKeyReleaseEvent(event); |
|
} |
|
|
|
Rectangle* gtk_xournal_get_visible_area(GtkWidget* widget, XojPageView* p) |
|
{ |
|
g_return_val_if_fail(widget != NULL, NULL); |
|
g_return_val_if_fail(GTK_IS_XOURNAL(widget), NULL); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
GtkAllocation allocation = { 0 }; |
|
gtk_widget_get_allocation(widget, &allocation); |
|
int viewHeight = allocation.height; |
|
int viewWidth = allocation.width; |
|
|
|
GdkRectangle r1; |
|
GdkRectangle r2; |
|
GdkRectangle r3 = { 0, 0, 0, 0 }; |
|
|
|
r1.x = p->getX(); |
|
r1.y = p->getY(); |
|
r1.width = p->getDisplayWidth(); |
|
r1.height = p->getDisplayHeight(); |
|
|
|
r2.x = xournal->x; |
|
r2.y = xournal->y; |
|
r2.width = viewWidth; |
|
r2.height = viewHeight; |
|
|
|
gdk_rectangle_intersect(&r1, &r2, &r3); |
|
|
|
if (r3.width == 0 && r3.height == 0) |
|
{ |
|
return NULL; |
|
} |
|
|
|
double zoom = xournal->view->getZoom(); |
|
|
|
return new Rectangle(MAX(r3.x, 0) / zoom, MAX(r3.y, 0) / zoom, r3.width / zoom, r3.height / zoom); |
|
} |
|
|
|
bool gtk_xournal_scroll_callback(GtkXournal* xournal) |
|
{ |
|
xournal->layout->scrollRelativ(xournal->scrollOffsetX, xournal->scrollOffsetY); |
|
|
|
// Scrolling done, so reset our counters |
|
xournal->scrollOffsetX = 0; |
|
xournal->scrollOffsetY = 0; |
|
|
|
return false; |
|
} |
|
|
|
static void gtk_xournal_scroll_mouse_event(GtkXournal* xournal, GdkEventMotion* event) |
|
{ |
|
// use root coordinates as reference point because |
|
// scrolling changes window relative coordinates |
|
// see github Gnome/evince@1adce5486b10e763bed869 |
|
int x_root = event->x_root; |
|
int y_root = event->y_root; |
|
|
|
if (xournal->lastMousePositionX - x_root == 0 && xournal->lastMousePositionY - y_root == 0) |
|
{ |
|
return; |
|
} |
|
|
|
if (xournal->scrollOffsetX == 0 && xournal->scrollOffsetY == 0) |
|
{ |
|
xournal->scrollOffsetX = xournal->lastMousePositionX - x_root; |
|
xournal->scrollOffsetY = xournal->lastMousePositionY - y_root; |
|
|
|
g_idle_add((GSourceFunc) gtk_xournal_scroll_callback, xournal); |
|
//gtk_xournal_scroll_callback(xournal); |
|
xournal->lastMousePositionX = x_root; |
|
xournal->lastMousePositionY = y_root; |
|
} |
|
} |
|
|
|
XojPageView* gtk_xournal_get_page_view_for_pos_cached(GtkXournal* xournal, int x, int y) |
|
{ |
|
x += xournal->x; |
|
y += xournal->y; |
|
|
|
PagePositionHandler* pph = xournal->view->getPagePositionHandler(); |
|
|
|
return pph->getViewAt(x, y, xournal->pagePositionCache); |
|
} |
|
|
|
Layout* gtk_xournal_get_layout(GtkWidget* widget) |
|
{ |
|
g_return_val_if_fail(widget != NULL, NULL); |
|
g_return_val_if_fail(GTK_IS_XOURNAL(widget), NULL); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
return xournal->layout; |
|
} |
|
|
|
static bool change_tool(Settings* settings, GdkEventButton* event, GtkXournal* xournal) |
|
{ |
|
ButtonConfig* cfg = NULL; |
|
ButtonConfig* cfgTouch = settings->getTouchButtonConfig(); |
|
ToolHandler* h = xournal->view->getControl()->getToolHandler(); |
|
|
|
GdkEvent* rawEvent = (GdkEvent*) event; |
|
GdkDevice* device = gdk_event_get_source_device(rawEvent); |
|
|
|
if (gdk_device_get_source(device) == GDK_SOURCE_PEN) |
|
{ |
|
if (event->button == 2) |
|
{ |
|
cfg = settings->getStylusButton1Config(); |
|
} |
|
else if (event->button == 3) |
|
{ |
|
cfg = settings->getStylusButton2Config(); |
|
} |
|
} |
|
else if (event->button == 2) // Middle Button |
|
{ |
|
cfg = settings->getMiddleButtonConfig(); |
|
} |
|
else if (event->button == 3 && !xournal->selection) // Right Button |
|
{ |
|
cfg = settings->getRightButtonConfig(); |
|
} |
|
else if (gdk_device_get_source(device) == GDK_SOURCE_ERASER) |
|
{ |
|
cfg = settings->getEraserButtonConfig(); |
|
} |
|
else if (cfgTouch->device == gdk_device_get_name(device)) |
|
{ |
|
cfg = cfgTouch; |
|
|
|
// If an action is defined we do it, even if it's a drawing action... |
|
if (cfg->getDisableDrawing() && cfg->getAction() == TOOL_NONE) |
|
{ |
|
ToolType tool = h->getToolType(); |
|
if (tool == TOOL_PEN || tool == TOOL_ERASER || tool == TOOL_HILIGHTER) |
|
{ |
|
printf("ignore touchscreen for drawing!\n"); |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
if (cfg && cfg->getAction() != TOOL_NONE) |
|
{ |
|
h->copyCurrentConfig(); |
|
cfg->acceptActions(h); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
gboolean gtk_xournal_button_press_event(GtkWidget* widget, GdkEventButton* event) |
|
{ |
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
Settings* settings = xournal->view->getControl()->getSettings(); |
|
//gtk_gesture_is_recognized is always false (bug, programming error?) |
|
//workaround with additional variable zoom_gesture_active |
|
if (xournal->view->zoom_gesture_active) |
|
{ |
|
return TRUE; |
|
} |
|
if (event->type != GDK_BUTTON_PRESS) |
|
{ |
|
return FALSE; // this event is not handled here |
|
} |
|
|
|
if (event->button > 3) // scroll wheel events |
|
{ |
|
return FALSE; |
|
} |
|
|
|
gtk_widget_grab_focus(widget); |
|
|
|
// none button release event was sent, send one now |
|
if (xournal->currentInputPage) |
|
{ |
|
GdkEventButton ev = *event; |
|
xournal->currentInputPage->translateEvent((GdkEvent*) &ev, xournal->x, xournal->y); |
|
xournal->currentInputPage->onButtonReleaseEvent(widget, &ev); |
|
} |
|
|
|
ToolHandler* h = xournal->view->getControl()->getToolHandler(); |
|
|
|
// Change the tool depending on the key or device |
|
if (change_tool(settings, event, xournal)) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
// hand tool don't change the selection, so you can scroll e.g. |
|
// with your touchscreen without remove the selection |
|
if (h->getToolType() == TOOL_HAND) |
|
{ |
|
Cursor* cursor = xournal->view->getCursor(); |
|
cursor->setMouseDown(true); |
|
xournal->inScrolling = true; |
|
//set reference |
|
xournal->lastMousePositionX = event->x_root; |
|
xournal->lastMousePositionY = event->y_root; |
|
|
|
return TRUE; |
|
} |
|
else if (xournal->selection) |
|
{ |
|
EditSelection* selection = xournal->selection; |
|
|
|
XojPageView* view = selection->getView(); |
|
GdkEventButton ev = *event; |
|
view->translateEvent((GdkEvent*) &ev, xournal->x, xournal->y); |
|
CursorSelectionType selType = selection->getSelectionTypeForPos(ev.x, ev.y, xournal->view->getZoom()); |
|
if (selType) |
|
{ |
|
|
|
if (selType == CURSOR_SELECTION_MOVE && event->button == 3) |
|
{ |
|
selection->copySelection(); |
|
} |
|
|
|
xournal->view->getCursor()->setMouseDown(true); |
|
xournal->selection->mouseDown(selType, ev.x, ev.y); |
|
return TRUE; |
|
} |
|
else |
|
{ |
|
xournal->view->clearSelection(); |
|
if (change_tool(settings, event, xournal)) |
|
{ |
|
return TRUE; |
|
} |
|
} |
|
} |
|
|
|
XojPageView* pv = gtk_xournal_get_page_view_for_pos_cached(xournal, event->x, event->y); |
|
|
|
current_view = pv; |
|
|
|
if (pv) |
|
{ |
|
xournal->currentInputPage = pv; |
|
pv->translateEvent((GdkEvent*) event, xournal->x, xournal->y); |
|
|
|
xournal->view->getDocument()->indexOf(pv->getPage()); |
|
return pv->onButtonPressEvent(widget, event); |
|
} |
|
|
|
return FALSE; // not handled |
|
} |
|
|
|
gboolean gtk_xournal_button_release_event(GtkWidget* widget, GdkEventButton* event) |
|
{ |
|
if (event->button > 3) // scroll wheel events |
|
{ |
|
return TRUE; |
|
} |
|
|
|
current_view = NULL; |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
Cursor* cursor = xournal->view->getCursor(); |
|
ToolHandler* h = xournal->view->getControl()->getToolHandler(); |
|
if (xournal->view->zoom_gesture_active) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
cursor->setMouseDown(false); |
|
|
|
xournal->inScrolling = false; |
|
|
|
EditSelection* sel = xournal->view->getSelection(); |
|
if (sel) |
|
{ |
|
sel->mouseUp(); |
|
} |
|
|
|
bool res = false; |
|
if (xournal->currentInputPage) |
|
{ |
|
xournal->currentInputPage->translateEvent((GdkEvent*) event, xournal->x, xournal->y); |
|
res = xournal->currentInputPage->onButtonReleaseEvent(widget, event); |
|
xournal->currentInputPage = NULL; |
|
} |
|
|
|
EditSelection* tmpSelection = xournal->selection; |
|
xournal->selection = NULL; |
|
|
|
h->restoreLastConfig(); |
|
|
|
// we need this workaround so it's possible to select something with the middle button |
|
if (tmpSelection) |
|
{ |
|
xournal->view->setSelection(tmpSelection); |
|
} |
|
|
|
return res; |
|
} |
|
|
|
gboolean gtk_xournal_motion_notify_event(GtkWidget* widget, GdkEventMotion* event) |
|
{ |
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
ToolHandler* h = xournal->view->getControl()->getToolHandler(); |
|
|
|
if (xournal->view->zoom_gesture_active) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
if (h->getToolType() == TOOL_HAND) |
|
{ |
|
if (xournal->inScrolling) |
|
{ |
|
gtk_xournal_scroll_mouse_event(xournal, event); |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
else if (xournal->selection) |
|
{ |
|
EditSelection* selection = xournal->selection; |
|
|
|
XojPageView* view = selection->getView(); |
|
GdkEventMotion ev = *event; |
|
view->translateEvent((GdkEvent*) &ev, xournal->x, xournal->y); |
|
|
|
if (xournal->selection->isMoving()) |
|
{ |
|
selection->mouseMove(ev.x, ev.y); |
|
} |
|
else |
|
{ |
|
CursorSelectionType selType = selection->getSelectionTypeForPos(ev.x, ev.y, xournal->view->getZoom()); |
|
xournal->view->getCursor()->setMouseSelectionType(selType); |
|
} |
|
return true; |
|
} |
|
|
|
XojPageView* pv = NULL; |
|
|
|
if (current_view) |
|
{ |
|
pv = current_view; |
|
} |
|
else |
|
{ |
|
pv = gtk_xournal_get_page_view_for_pos_cached(xournal, event->x, event->y); |
|
} |
|
|
|
xournal->view->getCursor()->setInsidePage(pv != NULL); |
|
|
|
if (pv) |
|
{ |
|
// allow events only to a single page! |
|
if (xournal->currentInputPage == NULL || pv == xournal->currentInputPage) |
|
{ |
|
pv->translateEvent((GdkEvent*) event, xournal->x, xournal->y); |
|
return pv->onMotionNotifyEvent(widget, event, xournal->shiftDown); |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
gboolean gtk_xournal_touch_event(GtkWidget* widget, GdkEventTouch* event) |
|
{ |
|
g_return_val_if_fail(widget != NULL, FALSE); |
|
g_return_val_if_fail(GTK_IS_XOURNAL(widget), FALSE); |
|
g_return_val_if_fail(event != NULL, FALSE); |
|
|
|
// GtkXournal* xournal = GTK_XOURNAL(widget); |
|
// printf("Touch event!\n"); |
|
// Currently does not really work |
|
// It's getting called, but the events |
|
// are not consumed! |
|
|
|
// Consume event |
|
return true; |
|
} |
|
|
|
static void gtk_xournal_init(GtkXournal* xournal) |
|
{ |
|
GtkWidget* widget = GTK_WIDGET(xournal); |
|
|
|
gtk_widget_set_can_focus(widget, TRUE); |
|
|
|
int events = GDK_EXPOSURE_MASK; |
|
events |= GDK_POINTER_MOTION_MASK; |
|
events |= GDK_EXPOSURE_MASK; |
|
events |= GDK_BUTTON_MOTION_MASK; |
|
|
|
// See documentation: https://developer.gnome.org/gtk3/stable/chap-input-handling.html |
|
events |= GDK_TOUCH_MASK; |
|
events |= GDK_BUTTON_PRESS_MASK; |
|
events |= GDK_BUTTON_RELEASE_MASK; |
|
events |= GDK_ENTER_NOTIFY_MASK; |
|
events |= GDK_LEAVE_NOTIFY_MASK; |
|
events |= GDK_KEY_PRESS_MASK; |
|
events |= GDK_SCROLL_MASK; |
|
|
|
gtk_widget_set_events(widget, events); |
|
} |
|
|
|
static void |
|
gtk_xournal_get_preferred_width(GtkWidget *widget, |
|
gint *minimal_width, |
|
gint *natural_width) |
|
{ |
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
*minimal_width = *natural_width = xournal->layout->getLayoutWidth(); |
|
} |
|
|
|
static void |
|
gtk_xournal_get_preferred_height(GtkWidget *widget, |
|
gint *minimal_height, |
|
gint *natural_height) |
|
{ |
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
*minimal_height = *natural_height = xournal->layout->getLayoutHeight(); |
|
} |
|
|
|
static void gtk_xournal_size_allocate(GtkWidget* widget, GtkAllocation* allocation) |
|
{ |
|
g_return_if_fail(widget != NULL); |
|
g_return_if_fail(GTK_IS_XOURNAL(widget)); |
|
g_return_if_fail(allocation != NULL); |
|
|
|
gtk_widget_set_allocation(widget, allocation); |
|
|
|
if (gtk_widget_get_realized(widget)) |
|
{ |
|
gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height); |
|
} |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
xournal->layout->setSize(allocation->width, allocation->height); |
|
} |
|
|
|
static void gtk_xournal_realize(GtkWidget* widget) |
|
{ |
|
GdkWindowAttr attributes; |
|
guint attributes_mask; |
|
|
|
g_return_if_fail(widget != NULL); |
|
g_return_if_fail(GTK_IS_XOURNAL(widget)); |
|
|
|
gtk_widget_set_realized(widget, TRUE); |
|
|
|
gtk_widget_set_hexpand(widget, TRUE); |
|
gtk_widget_set_vexpand(widget, TRUE); |
|
|
|
GtkAllocation allocation; |
|
gtk_widget_get_allocation(widget, &allocation); |
|
attributes.window_type = GDK_WINDOW_CHILD; |
|
attributes.x = allocation.x; |
|
attributes.y = allocation.y; |
|
attributes.width = allocation.width; |
|
attributes.height = allocation.height; |
|
|
|
attributes.wclass = GDK_INPUT_OUTPUT; |
|
attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK; |
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y; |
|
|
|
gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask)); |
|
gdk_window_set_user_data(gtk_widget_get_window(widget), widget); |
|
} |
|
|
|
static void gtk_xournal_draw_shadow(GtkXournal* xournal, cairo_t* cr, int left, |
|
int top, int width, int height, bool selected) |
|
{ |
|
if (selected) |
|
{ |
|
Shadow::drawShadow(cr, left - 2, top - 2, width + 4, height + 4); |
|
|
|
Settings* settings = xournal->view->getControl()->getSettings(); |
|
|
|
// Draw border |
|
Util::cairo_set_source_rgbi(cr, settings->getSelectionColor()); |
|
cairo_set_line_width(cr, 4.0); |
|
cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); |
|
cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL); |
|
|
|
cairo_move_to(cr, left, top); |
|
cairo_line_to(cr, left, top + height); |
|
cairo_line_to(cr, left + width, top + height); |
|
cairo_line_to(cr, left + width, top); |
|
cairo_close_path(cr); |
|
|
|
cairo_stroke(cr); |
|
} |
|
else |
|
{ |
|
Shadow::drawShadow(cr, left, top, width, height); |
|
} |
|
} |
|
|
|
void gtk_xournal_repaint_area(GtkWidget* widget, int x1, int y1, int x2, |
|
int y2) |
|
{ |
|
g_return_if_fail(widget != NULL); |
|
g_return_if_fail(GTK_IS_XOURNAL(widget)); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
x1 -= xournal->x; |
|
x2 -= xournal->x; |
|
y1 -= xournal->y; |
|
y2 -= xournal->y; |
|
|
|
if (x2 < 0 || y2 < 0) |
|
{ |
|
return; // outside visible area |
|
} |
|
|
|
GtkAllocation alloc = { 0 }; |
|
gtk_widget_get_allocation(widget, &alloc); |
|
|
|
if (x1 > alloc.width || y1 > alloc.height) |
|
{ |
|
return; // outside visible area |
|
} |
|
|
|
gtk_widget_queue_draw_area(widget, x1, y1, x2 - x1, y2 - y1); |
|
} |
|
|
|
static gboolean gtk_xournal_draw(GtkWidget* widget, cairo_t* cr) |
|
{ |
|
g_return_val_if_fail(widget != NULL, FALSE); |
|
g_return_val_if_fail(GTK_IS_XOURNAL(widget), FALSE); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(widget); |
|
|
|
ArrayIterator<XojPageView*> it = xournal->view->pageViewIterator(); |
|
|
|
double x1, x2, y1, y2; |
|
|
|
cairo_clip_extents(cr, &x1, &y1, &x2, &y2); |
|
|
|
// Draw background |
|
Settings* settings = xournal->view->getControl()->getSettings(); |
|
Util::cairo_set_source_rgbi(cr, settings->getBackgroundColor()); |
|
cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1); |
|
cairo_fill(cr); |
|
|
|
Rectangle clippingRect(x1 - 10, y1 - 10, x2 - x1 + 20, y2 - y1 + 20); |
|
|
|
while (it.hasNext()) |
|
{ |
|
XojPageView* pv = it.next(); |
|
|
|
int px = pv->getX(); |
|
int py = pv->getY(); |
|
int pw = pv->getDisplayWidth(); |
|
int ph = pv->getDisplayHeight(); |
|
|
|
if (!clippingRect.intersects(pv->getRect())) |
|
{ |
|
continue; |
|
} |
|
|
|
gtk_xournal_draw_shadow(xournal, cr, px, py, pw, ph, pv->isSelected()); |
|
|
|
cairo_save(cr); |
|
cairo_translate(cr, px, py); |
|
|
|
pv->paintPage(cr, NULL); |
|
cairo_restore(cr); |
|
} |
|
|
|
if (xournal->selection) |
|
{ |
|
double zoom = xournal->view->getZoom(); |
|
|
|
Redrawable* red = xournal->selection->getView(); |
|
cairo_translate(cr, red->getX(), red->getY()); |
|
|
|
xournal->selection->paint(cr, zoom); |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
static void gtk_xournal_destroy(GtkWidget* object) |
|
{ |
|
g_return_if_fail(object != NULL); |
|
g_return_if_fail(GTK_IS_XOURNAL(object)); |
|
|
|
GtkXournal* xournal = GTK_XOURNAL(object); |
|
delete xournal->pagePositionCache; |
|
xournal->pagePositionCache = NULL; |
|
|
|
delete xournal->selection; |
|
xournal->selection = NULL; |
|
|
|
delete xournal->layout; |
|
xournal->layout = NULL; |
|
} |
|
|
|
|