📄 xo-misc.c
字号:
#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <math.h>#include <string.h>#include <gtk/gtk.h>#include <libgnomecanvas/libgnomecanvas.h>#include <gdk/gdkkeysyms.h>#include "xournal.h"#include "xo-interface.h"#include "xo-support.h"#include "xo-callbacks.h"#include "xo-misc.h"#include "xo-file.h"#include "xo-paint.h"// some global constantsguint predef_colors_rgba[COLOR_MAX] = { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff, 0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff, 0xff8000ff, 0xffff00ff, 0xffffffff };guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff, 0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff, 0xffc080ff, 0xffff80ff, 0xffffffff };double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] = { { 0.42, 0.85, 1.41, 2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 3, 7 mm { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 3, 7 mm };// some manipulation functionsstruct Page *new_page(struct Page *template){ struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page)); struct Layer *l = g_new(struct Layer, 1); l->items = NULL; l->nitems = 0; pg->layers = g_list_append(NULL, l); pg->nlayers = 1; pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background)); pg->bg->canvas_item = NULL; if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) { gdk_pixbuf_ref(pg->bg->pixbuf); refstring_ref(pg->bg->filename); } pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new( gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL); make_page_clipbox(pg); update_canvas_bg(pg); l->group = (GnomeCanvasGroup *) gnome_canvas_item_new( pg->group, gnome_canvas_group_get_type(), NULL); return pg;}/* Create a page from a background. Note: bg should be an UNREFERENCED background. If needed, first duplicate it and increase the refcount of the pixbuf.*/struct Page *new_page_with_bg(struct Background *bg, double width, double height){ struct Page *pg = g_new(struct Page, 1); struct Layer *l = g_new(struct Layer, 1); l->items = NULL; l->nitems = 0; pg->layers = g_list_append(NULL, l); pg->nlayers = 1; pg->bg = bg; pg->bg->canvas_item = NULL; pg->height = height; pg->width = width; pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new( gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL); make_page_clipbox(pg); update_canvas_bg(pg); l->group = (GnomeCanvasGroup *) gnome_canvas_item_new( pg->group, gnome_canvas_group_get_type(), NULL); return pg;}void realloc_cur_path(int n){ if (n <= ui.cur_path_storage_alloc) return; ui.cur_path_storage_alloc = n+10; ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+10)*sizeof(double));}// undo utility functionsvoid prepare_new_undo(void){ struct UndoItem *u; // add a new UndoItem on the stack u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem)); u->next = undo; u->multiop = 0; undo = u; ui.saved = FALSE; clear_redo_stack();}void clear_redo_stack(void){ struct UndoItem *u; GList *list, *repl; struct UndoErasureData *erasure; struct Item *it; /* Warning: the redo items might reference items from past redo entries, which have been destroyed before them. Be careful! As a rule, it's safe to destroy data which has been created at the current history step, it's unsafe to refer to any data from previous history steps */ while (redo!=NULL) { if (redo->type == ITEM_STROKE) { gnome_canvas_points_free(redo->item->path); g_free(redo->item); /* the strokes are unmapped, so there are no associated canvas items */ } else if (redo->type == ITEM_TEXT) { g_free(redo->item->text); g_free(redo->item->font_name); g_free(redo->item); } else if (redo->type == ITEM_ERASURE) { for (list = redo->erasurelist; list!=NULL; list=list->next) { erasure = (struct UndoErasureData *)list->data; for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) { it = (struct Item *)repl->data; gnome_canvas_points_free(it->path); g_free(it); } g_list_free(erasure->replacement_items); g_free(erasure); } g_list_free(redo->erasurelist); } else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE || redo->type == ITEM_NEW_DEFAULT_BG) { if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) { if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf); refstring_unref(redo->bg->filename); } g_free(redo->bg); } else if (redo->type == ITEM_NEW_PAGE) { redo->page->group = NULL; delete_page(redo->page); } else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) { g_list_free(redo->itemlist); g_list_free(redo->auxlist); } else if (redo->type == ITEM_PASTE) { for (list = redo->itemlist; list!=NULL; list=list->next) { it = (struct Item *)list->data; if (it->type == ITEM_STROKE) gnome_canvas_points_free(it->path); g_free(it); } g_list_free(redo->itemlist); } else if (redo->type == ITEM_NEW_LAYER) { g_free(redo->layer); } else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) { g_free(redo->str); if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush); } u = redo; redo = redo->next; g_free(u); } update_undo_redo_enabled();}void clear_undo_stack(void){ struct UndoItem *u; GList *list; struct UndoErasureData *erasure; while (undo!=NULL) { // for strokes, items are already in the journal, so we don't free them // for erasures, we need to free the dead items if (undo->type == ITEM_ERASURE) { for (list = undo->erasurelist; list!=NULL; list=list->next) { erasure = (struct UndoErasureData *)list->data; if (erasure->item->type == ITEM_STROKE) gnome_canvas_points_free(erasure->item->path); if (erasure->item->type == ITEM_TEXT) { g_free(erasure->item->text); g_free(erasure->item->font_name); } g_free(erasure->item); g_list_free(erasure->replacement_items); g_free(erasure); } g_list_free(undo->erasurelist); } else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE || undo->type == ITEM_NEW_DEFAULT_BG) { if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) { if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf); refstring_unref(undo->bg->filename); } g_free(undo->bg); } else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) { g_list_free(undo->itemlist); g_list_free(undo->auxlist); } else if (undo->type == ITEM_PASTE) { g_list_free(undo->itemlist); } else if (undo->type == ITEM_DELETE_LAYER) { undo->layer->group = NULL; delete_layer(undo->layer); } else if (undo->type == ITEM_DELETE_PAGE) { undo->page->group = NULL; delete_page(undo->page); } else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) { g_free(undo->str); if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush); } u = undo; undo = undo->next; g_free(u); } update_undo_redo_enabled();}// free data structures void delete_journal(struct Journal *j){ while (j->pages!=NULL) { delete_page((struct Page *)j->pages->data); j->pages = g_list_delete_link(j->pages, j->pages); }}void delete_page(struct Page *pg){ struct Layer *l; while (pg->layers!=NULL) { l = (struct Layer *)pg->layers->data; l->group = NULL; delete_layer(l); pg->layers = g_list_delete_link(pg->layers, pg->layers); } if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group)); // this also destroys the background's canvas items if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) { if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf); if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename); } g_free(pg->bg); g_free(pg);}void delete_layer(struct Layer *l){ struct Item *item; while (l->items!=NULL) { item = (struct Item *)l->items->data; if (item->type == ITEM_STROKE && item->path != NULL) gnome_canvas_points_free(item->path); if (item->type == ITEM_TEXT) { g_free(item->font_name); g_free(item->text); } // don't need to delete the canvas_item, as it's part of the group destroyed below g_free(item); l->items = g_list_delete_link(l->items, l->items); } if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group)); g_free(l);}// referenced stringsstruct Refstring *new_refstring(const char *s){ struct Refstring *rs = g_new(struct Refstring, 1); rs->nref = 1; if (s!=NULL) rs->s = g_strdup(s); else rs->s = NULL; rs->aux = NULL; return rs;}struct Refstring *refstring_ref(struct Refstring *rs){ rs->nref++; return rs;}void refstring_unref(struct Refstring *rs){ rs->nref--; if (rs->nref == 0) { if (rs->s!=NULL) g_free(rs->s); if (rs->aux!=NULL) g_free(rs->aux); g_free(rs); }}// some helper functionsvoid get_pointer_coords(GdkEvent *event, gdouble *ret){ double x, y; gdk_event_get_coords(event, &x, &y); gnome_canvas_window_to_world(canvas, x, y, ret, ret+1); ret[0] -= ui.cur_page->hoffset; ret[1] -= ui.cur_page->voffset;}void fix_xinput_coords(GdkEvent *event){#ifdef ENABLE_XINPUT_BUGFIX double *axes, *px, *py, axis_width; GdkDevice *device; int wx, wy, sx, sy; if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) { axes = event->button.axes; px = &(event->button.x); py = &(event->button.y); device = event->button.device; } else if (event->type == GDK_MOTION_NOTIFY) { axes = event->motion.axes; px = &(event->motion.x); py = &(event->motion.y); device = event->motion.device; } else return; // nothing we know how to do gdk_window_get_origin(event->any.window, &wx, &wy); // somehow, behavior changed starting with GTK+ 2.11.0 if (!gtk_check_version(2, 11, 0)) sx = sy = 0; else gnome_canvas_get_scroll_offsets(canvas, &sx, &sy); axis_width = device->axes[0].max - device->axes[0].min; if (axis_width>EPSILON) *px = (axes[0]/axis_width)*ui.screen_width + sx - wx; axis_width = device->axes[1].max - device->axes[1].min; if (axis_width>EPSILON) *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -