UTF8 string fix

use pango for text layout

git-svn-id: svn://svn.code.sf.net/p/xournal/svn/trunk@25 9fe2bcd3-a095-4d8b-a836-9b85dc8d7627
presentation
andreasb123 15 years ago
parent 1d0bb7f106
commit a4a06661fa
  1. 6
      src/Xournalpp.cpp
  2. 3
      src/control/ClipboardHandler.cpp
  3. 7
      src/control/Cursor.cpp
  4. 9
      src/control/ZoomControl.cpp
  5. 330
      src/gui/TextEditor.cpp
  6. 10
      src/gui/TextEditor.h
  7. 2
      src/gui/toolbarMenubar/ToolMenuHandler.cpp
  8. 190
      src/model/String.cpp
  9. 29
      src/model/String.h
  10. 2
      src/model/StringBuffer.cpp
  11. 10
      src/pdf/poppler/XojPopplerDocument.cpp
  12. 2
      src/util/ObjectStream.cpp
  13. 2
      src/util/OutputStream.cpp
  14. 188
      src/view/TextView.cpp
  15. 3
      src/view/TextView.h

@ -178,7 +178,7 @@ int main(int argc, char *argv[]) {
if (special) {
special = false;
String t = tmp;
if (t.length() > 1) {
if (t.size() > 1) {
char x[2] = { 0 };
x[0] = hexValue(tmp[0]) << 4 + hexValue(tmp[1]);
path += x;
@ -201,12 +201,12 @@ int main(int argc, char *argv[]) {
}
}
win->show();
if (!opened) {
control->newFile();
}
win->show();
gtk_main();
ev_metadata_manager_shutdown();

@ -83,7 +83,7 @@ public:
gtk_selection_data_set_pixbuf(selection, contents->image);
} else if (atomSvg1 == selection->target || atomSvg2 == selection->target) {
gtk_selection_data_set(selection, selection->target, 8, (guchar *) contents->svg.c_str(),
contents->svg.length());
contents->svg.size());
} else if (atomXournal == selection->target) {
gtk_selection_data_set(selection, selection->target, 8, (guchar *) contents->str->str, contents->str->len);
}
@ -199,7 +199,6 @@ void ClipboardHandler::copy() {
GtkTargetList * list = gtk_target_list_new(NULL, 0);
GtkTargetEntry *targets;
int n_targets;
int len = text.length();
// if we have text elements...
if (!text.isEmpty()) {

@ -127,9 +127,9 @@ void Cursor::updateCursor() {
} else if (type == TOOL_TEXT) {
if (this->invisible) {
cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
} else {
cursor = gdk_cursor_new(GDK_XTERM);
}
cursor = gdk_cursor_new(GDK_XTERM);
} else if (type == TOOL_IMAGE) {
// No special cursor needed
} else if (type == TOOL_SELECT_RECT || type == TOOL_SELECT_REGION || type == TOOL_SELECT_OBJECT) {
@ -163,7 +163,7 @@ void Cursor::updateCursor() {
cursor = gdk_cursor_new(GDK_TCROSS);
}
} else if (type == TOOL_VERTICAL_SPACE) {
if(this->mouseDown) {
if (this->mouseDown) {
cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
}
} else if (type == TOOL_HAND) {
@ -180,6 +180,7 @@ void Cursor::updateCursor() {
while (it.hasNext()) {
PageView * p = it.next();
if (GDK_IS_WINDOW(p->getWidget()->window)) {
gdk_window_set_cursor(p->getWidget()->window, cursor);

@ -97,11 +97,14 @@ void ZoomControl::zoomOut() {
}
bool ZoomControl::onScrolledwindowMainScrollEvent(GtkWidget *widget, GdkEventScroll *event, ZoomControl * zoom) {
guint state;
guint state = event->state & gtk_accelerator_get_default_mod_mask();
state = event->state & gtk_accelerator_get_default_mod_mask();
// do not handle e.g. ALT + Scroll (e.g. Compiz use this shortcut for setting transparency...)
if (state != 0 && state & ~(GDK_CONTROL_MASK | GDK_SHIFT_MASK)) {
return true;
}
if (state == GDK_CONTROL_MASK) {
if (state & GDK_CONTROL_MASK) {
if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) {
zoom->zoomIn();
} else {

@ -9,38 +9,7 @@
#include "TextEditorWidget.h"
class CharContents {
public:
char c;
double width;
};
class LineConentents {
public:
LineConentents() {
this->charContents = NULL;
}
~LineConentents() {
for (GList * l = this->charContents; l != NULL; l = l->next) {
CharContents * c = (CharContents *) l->data;
delete c;
}
g_list_free(this->charContents);
this->charContents = NULL;
}
void insertChar(double width, char ch) {
CharContents * c = new CharContents();
c->width = width;
c->c = ch;
this->charContents = g_list_append(this->charContents, c);
}
double y;
double height;
GList * charContents;
};
// TODO: implement drag & drop
TextEditor::TextEditor(PageView * gui, Text * text, bool ownText) {
this->gui = gui;
@ -52,7 +21,7 @@ TextEditor::TextEditor(PageView * gui, Text * text, bool ownText) {
this->cursorOverwrite = false;
this->needImReset = false;
this->textWidget = gtk_xoj_int_txt_new(this);
this->contents = NULL;
this->layout = NULL;
this->virtualCursor = 0;
this->markPosX = 0;
this->markPosY = 0;
@ -60,7 +29,6 @@ TextEditor::TextEditor(PageView * gui, Text * text, bool ownText) {
this->markPosQueue = false;
this->mouseDown = 0;
this->undoActions = NULL;
this->fContentsChanged = false;
this->lastText = text->getText();
this->buffer = gtk_text_buffer_new(NULL);
@ -68,6 +36,7 @@ TextEditor::TextEditor(PageView * gui, Text * text, bool ownText) {
if (txt == NULL) {
txt = "";
}
gtk_text_buffer_set_text(this->buffer, txt, -1);
GtkTextIter first = { 0 };
@ -78,16 +47,16 @@ TextEditor::TextEditor(PageView * gui, Text * text, bool ownText) {
g_object_get(settings, "gtk-cursor-blink-time", &this->cursorBlinkTime, NULL);
g_object_get(settings, "gtk-cursor-blink-timeout", &this->cursorBlinkTimeout, NULL);
this->im_context = gtk_im_context_simple_new();
gtk_im_context_focus_in(this->im_context);
this->imContext = gtk_im_context_simple_new();
gtk_im_context_focus_in(this->imContext);
g_signal_connect (this->im_context, "commit",
g_signal_connect (this->imContext, "commit",
G_CALLBACK (iMCommitCallback), this);
g_signal_connect (this->im_context, "preedit-changed",
g_signal_connect (this->imContext, "preedit-changed",
G_CALLBACK (iMPreeditChangedCallback), this);
g_signal_connect (this->im_context, "retrieve-surrounding",
g_signal_connect (this->imContext, "retrieve-surrounding",
G_CALLBACK (iMRetrieveSurroundingCallback), this);
g_signal_connect (this->im_context, "delete-surrounding",
g_signal_connect (this->imContext, "delete-surrounding",
G_CALLBACK (imDeleteSurroundingCallback), this);
blinkCallback(this);
@ -130,7 +99,7 @@ TextEditor::~TextEditor() {
g_source_remove(this->blinkTimeout);
}
g_object_unref(this->im_context);
g_object_unref(this->imContext);
this->text = NULL;
}
@ -228,13 +197,12 @@ void TextEditor::iMPreeditChangedCallback(GtkIMContext *context, TextEditor * te
bool TextEditor::iMRetrieveSurroundingCallback(GtkIMContext *context, TextEditor * te) {
GtkTextIter start;
GtkTextIter end;
gint pos;
gchar *text;
gtk_text_buffer_get_iter_at_mark(te->buffer, &start, gtk_text_buffer_get_insert(te->buffer));
end = start;
pos = gtk_text_iter_get_line_index(&start);
gint pos = gtk_text_iter_get_line_index(&start);
gtk_text_iter_set_line_offset(&start, 0);
gtk_text_iter_forward_to_line_end(&end);
@ -271,13 +239,13 @@ bool TextEditor::onKeyPressEvent(GdkEventKey *event) {
}
bool retval = false;
gboolean obscure = false;
bool obscure = false;
GtkTextIter iter;
GtkTextMark* insert = gtk_text_buffer_get_insert(this->buffer);
gtk_text_buffer_get_iter_at_mark(this->buffer, &iter, insert);
bool canInsert = gtk_text_iter_can_insert(&iter, true);
if (gtk_im_context_filter_keypress(this->im_context, event)) {
if (gtk_im_context_filter_keypress(this->imContext, event)) {
this->needImReset = true;
if (!canInsert) {
this->resetImContext();
@ -315,7 +283,7 @@ bool TextEditor::onKeyReleaseEvent(GdkEventKey *event) {
GtkTextMark *insert = gtk_text_buffer_get_insert(this->buffer);
gtk_text_buffer_get_iter_at_mark(this->buffer, &iter, insert);
if (gtk_text_iter_can_insert(&iter, true) && gtk_im_context_filter_keypress(this->im_context, event)) {
if (gtk_text_iter_can_insert(&iter, true) && gtk_im_context_filter_keypress(this->imContext, event)) {
this->needImReset = true;
return true;
}
@ -447,43 +415,19 @@ void TextEditor::moveCursor(GtkMovementStep step, int count, bool extend_selecti
}
void TextEditor::findPos(GtkTextIter * iter, double xPos, double yPos) {
int line = 0;
for (GList * l = this->contents; l != NULL; l = l->next, line++) {
LineConentents * lc = (LineConentents *) l->data;
if (lc->y > yPos) {
break;
}
}
gtk_text_iter_set_line(iter, line);
LineConentents * lc = (LineConentents *) g_list_nth_data(this->contents, line);
if (!lc) {
if (!this->layout) {
return;
}
double x = 0;
double lastPadding = ABS(xPos - x);
int id = 0;
for (GList * l = lc->charContents; l != NULL; l = l->next, id++) {
CharContents * c = (CharContents *) l->data;
x += c->width;
double padding = ABS(xPos - x);
if (padding > lastPadding) {
break;
} else {
lastPadding = padding;
}
int index = 0;
if(!pango_layout_xy_to_index(this->layout, xPos * PANGO_SCALE, yPos * PANGO_SCALE, &index, NULL)) {
index++;
}
gtk_text_iter_set_line_offset(iter, id);
gtk_text_iter_set_offset(iter, index);
}
void TextEditor::contentsChanged(bool forceCreateUndoAction) {
this->fContentsChanged = true;
String currentText = getText()->getText();
if (forceCreateUndoAction || ABS(lastText.length()-currentText.length()) > 100) {
@ -507,7 +451,7 @@ UndoAction * TextEditor::getFirstUndoAction() {
}
void TextEditor::markPos(double x, double y, bool extendSelection) {
if (this->contents == NULL) {
if (this->layout == NULL) {
this->markPosX = x;
this->markPosY = y;
this->markPosExtendSelection = extendSelection;
@ -548,28 +492,19 @@ void TextEditor::mouseReleased() {
void TextEditor::jumpALine(GtkTextIter * textIter, int count) {
int cursorLine = gtk_text_iter_get_line(textIter);
LineConentents * lc = (LineConentents *) g_list_nth_data(this->contents, cursorLine + count);
if (!lc) {
if(cursorLine + count < 0) {
return;
}
double x = 0;
double lastPadding = ABS(this->virtualCursor-x);
int id = 0;
for (GList * l = lc->charContents; l != NULL; l = l->next, id++) {
CharContents * c = (CharContents *) l->data;
x += c->width;
double padding = ABS(this->virtualCursor-x);
if (padding > lastPadding) {
break;
} else {
lastPadding = padding;
}
PangoLayoutLine * line = pango_layout_get_line(this->layout, cursorLine + count);
if(line == NULL) {
return;
}
gtk_text_iter_set_line(textIter, cursorLine + count);
gtk_text_iter_set_line_offset(textIter, id);
int index = 0;
pango_layout_line_x_to_index(line, this->virtualCursor * PANGO_SCALE, &index, NULL);
gtk_text_iter_set_offset(textIter, index);
}
void TextEditor::calcVirtualCursor() {
@ -577,19 +512,12 @@ void TextEditor::calcVirtualCursor() {
GtkTextIter cursorIter = { 0 };
GtkTextMark * cursor = gtk_text_buffer_get_insert(this->buffer);
gtk_text_buffer_get_iter_at_mark(this->buffer, &cursorIter, cursor);
int cursorLine = gtk_text_iter_get_line(&cursorIter);
int cursorPos = gtk_text_iter_get_line_offset(&cursorIter);
LineConentents * lc = (LineConentents *) g_list_nth_data(this->contents, cursorLine);
if (!lc) {
return;
}
int offset = gtk_text_iter_get_offset(&cursorIter);
int id = 0;
for (GList * l = lc->charContents; l != NULL && id < cursorPos; l = l->next, id++) {
CharContents * c = (CharContents *) l->data;
this->virtualCursor += c->width;
}
PangoRectangle rect = { 0 };
pango_layout_index_to_pos(this->layout, offset, &rect);
this->virtualCursor = ((double) rect.x) / PANGO_SCALE;
}
void TextEditor::moveCursor(const GtkTextIter *new_location, gboolean extend_selection) {
@ -800,7 +728,7 @@ void TextEditor::pasteFromClipboard() {
void TextEditor::resetImContext() {
if (this->needImReset) {
this->needImReset = false;
gtk_im_context_reset(this->im_context);
gtk_im_context_reset(this->imContext);
}
}
@ -841,7 +769,7 @@ void TextEditor::redrawEditor() {
gtk_widget_queue_draw(gui->getWidget());
}
void TextEditor::drawCursor(cairo_t * cr, double & x0, double & x, double & y, cairo_font_extents_t & fe, double zoom) {
void TextEditor::drawCursor(cairo_t * cr, double x, double y, double height, double zoom) {
double cw = 2 / zoom;
double dX = 0;
if (this->cursorOverwrite) {
@ -853,182 +781,114 @@ void TextEditor::drawCursor(cairo_t * cr, double & x0, double & x, double & y, c
// Not draw cursor if a move is pending
if (!this->markPosQueue) {
cairo_rectangle(cr, x0 + x + dX, y - fe.height + fe.descent, cw, fe.height);
if (this->cursorVisible) {
cairo_set_source_rgb(cr, 0, 0, 0);
} else {
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, x + dX, y, cw, height);
cairo_fill(cr);
}
cairo_fill(cr);
}
DocumentView::applyColor(cr, this->text);
if (!preeditString.isEmpty()) {
cairo_text_extents_t ex = { 0 };
cairo_text_extents(cr, this->preeditString.c_str(), &ex);
cairo_move_to(cr, x0 + x, y);
cairo_show_text(cr, this->preeditString.c_str());
cairo_set_line_width(cr, 1 / zoom);
cairo_move_to(cr, x0 + x, y + 2);
x += ex.x_advance;
cairo_line_to(cr, x0 + x, y + 2);
cairo_stroke(cr);
}
}
void TextEditor::paint(cairo_t * cr, GdkEventExpose *event, double zoom) {
double x0 = this->text->getX();
double y0 = this->text->getY();
double y = y0;
double width = 0;
GdkColor selectionColor = gui->getSelectionColor();
DocumentView::applyColor(cr, this->text);
TextView::initCairo(cr, this->text);
cairo_save(cr);
cairo_text_extents_t extents = { 0 };
cairo_font_extents_t fe = { 0 };
DocumentView::applyColor(cr, this->text);
// This need to be here, why...? I don't know, the size should be calculated anyway if t->getX() is called...
this->text->getElementWidth();
y += 3;
cairo_font_extents(cr, &fe);
GtkTextIter iter = { 0 };
gtk_text_buffer_get_iter_at_offset(this->buffer, &iter, 0);
gunichar c = 1;
int line = 0;
int pos = 0;
GtkTextIter cursorIter = { 0 };
GtkTextMark * cursor = gtk_text_buffer_get_insert(this->buffer);
gtk_text_buffer_get_iter_at_mark(this->buffer, &cursorIter, cursor);
int cursorLine = gtk_text_iter_get_line(&cursorIter);
int cursorPos = gtk_text_iter_get_line_offset(&cursorIter);
GtkTextIter start;
GtkTextIter end;
bool hasSelection = gtk_text_buffer_get_selection_bounds(this->buffer, &start, &end);
bool inSelection = false;
if (this->fContentsChanged) {
for (GList * l = this->contents; l != NULL; l = l->next) {
LineConentents * contents = (LineConentents *) l->data;
delete contents;
}
double x0 = this->text->getX();
double y0 = this->text->getY();
cairo_translate(cr, x0, y0);
g_list_free(this->contents);
this->contents = NULL;
this->fContentsChanged = false;
if (this->layout) {
g_object_unref(this->layout);
}
this->layout = TextView::initPango(cr, this->text);
// per line
do {
LineConentents * lc = new LineConentents();
this->contents = g_list_append(this->contents, lc);
y += fe.height - fe.descent;
lc->y = y - y0;
lc->height = fe.height - fe.descent;
double x = 0;
pos = 0;
// per char
while (!gtk_text_iter_ends_line(&iter) && c) {
c = gtk_text_iter_get_char(&iter);
if (hasSelection) {
if (inSelection && gtk_text_iter_compare(&iter, &end) >= 0) {
inSelection = false;
// do not process selection again
hasSelection = false;
} else if (!inSelection && gtk_text_iter_compare(&start, &iter) <= 0) {
inSelection = true;
}
}
gunichar tmp[2] = { 0, 0 };
tmp[0] = c;
cairo_text_extents_t ex = { 0 };
cairo_text_extents(cr, (const char *) tmp, &ex);
if (inSelection) {
cairo_set_source_rgb(cr, selectionColor.red / 65536.0, selectionColor.green / 65536.0,
selectionColor.blue / 65536.0);
if (!preeditString.isEmpty()) {
String text = this->text->getText();
int pos = gtk_text_iter_get_offset(&cursorIter);
String txt = text.substring(0, pos);
txt += preeditString;
txt += text.substring(pos);
double width = ex.x_advance;
if (c == '\t') {
int tab = x / TextView::TAB_INDEX + 1;
width = tab * TextView::TAB_INDEX - x;
}
PangoAttribute * attrib = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
PangoAttrList * list = pango_layout_get_attributes(this->layout);
cairo_rectangle(cr, x + x0 - 0.5, y - fe.height + fe.descent, width + 1, fe.height);
cairo_fill(cr);
attrib->start_index = pos;
attrib->end_index = pos + preeditString.size();
DocumentView::applyColor(cr, this->text);
}
if (list == NULL) {
list = pango_attr_list_new();
pango_layout_set_attributes(this->layout, list);
}
pango_attr_list_insert(list, attrib);
if (cursorLine == line && cursorPos == pos) {
drawCursor(cr, x0, x, y, fe, zoom);
}
pango_layout_set_text(layout, txt.c_str(), txt.size());
} else {
String txt = this->text->getText();
pango_layout_set_text(layout, txt.c_str(), txt.size());
}
if (c == '\t') {
double lastX = x;
int tab = x / TextView::TAB_INDEX + 1;
x = tab * TextView::TAB_INDEX;
lc->insertChar(x - lastX, '\t');
} else {
cairo_move_to(cr, x + x0, y);
cairo_show_text(cr, (const char *) tmp);
GtkTextIter start;
GtkTextIter end;
bool hasSelection = gtk_text_buffer_get_selection_bounds(this->buffer, &start, &end);
x += ex.x_advance;
lc->insertChar(ex.x_advance, (char) c);
}
if (hasSelection) {
PangoAttribute * attrib = pango_attr_background_new(selectionColor.red, selectionColor.green,
selectionColor.blue);
PangoAttrList * list = pango_layout_get_attributes(this->layout);
if (!gtk_text_iter_forward_char(&iter)) {
c = 0;
}
pos++;
}
attrib->start_index = gtk_text_iter_get_offset(&start);
attrib->end_index = gtk_text_iter_get_offset(&end);
if (cursorLine == line && cursorPos == pos) {
drawCursor(cr, x0, x, y, fe, zoom);
if (list == NULL) {
list = pango_attr_list_new();
pango_layout_set_attributes(this->layout, list);
}
pango_attr_list_insert(list, attrib);
}
y += fe.height * 0.25;
// printf("\n");
pango_cairo_show_layout(cr, layout);
int w = 0;
int h = 0;
pango_layout_get_size(this->layout, &w, &h);
double width = ((double) w) / PANGO_SCALE;
double height = ((double) h) / PANGO_SCALE;
width = MAX(width, x);
int offset = gtk_text_iter_get_offset(&cursorIter);
PangoRectangle rect = { 0 };
pango_layout_index_to_pos(layout, offset + preeditString.size(), &rect);
double cX = ((double) rect.x) / PANGO_SCALE;
double cY = ((double) rect.y) / PANGO_SCALE;
double cHeight = ((double) rect.height) / PANGO_SCALE;
if (gtk_text_iter_is_end(&iter)) {
break;
}
drawCursor(cr, cX, cY, cHeight, zoom);
gtk_text_iter_forward_line(&iter);
line++;
} while (c);
cairo_restore(cr);
// set the line always the same size on display
cairo_set_line_width(cr, 1 / zoom);
cairo_set_source_rgb(cr, selectionColor.red / 65536.0, selectionColor.green / 65536.0, selectionColor.blue
/ 65536.0);
cairo_rectangle(cr, x0 - 5 / zoom, y0 - 5 / zoom, width + 10 / zoom, y - y0 + 10 / zoom);
cairo_rectangle(cr, x0 - 5 / zoom, y0 - 5 / zoom, width + 10 / zoom, height + 10 / zoom);
cairo_stroke(cr);
this->text->setWidth(width);
this->text->setHeight(y - y0);
this->text->setHeight(height);
if (this->markPosQueue) {
this->markPosQueue = false;
markPos(this->markPosX, this->markPosY, this->markPosExtendSelection);
}
}

@ -39,7 +39,6 @@ public:
void pasteFromClipboard();
String getSelection();
Text * getText();
void textCopyed();
@ -53,7 +52,7 @@ public:
void setFont(XojFont font);
private:
void redrawEditor();
void drawCursor(cairo_t * cr, double & x0, double & x, double & y, cairo_font_extents_t & fe, double zoom);
void drawCursor(cairo_t * cr, double x, double y, double height, double zoom);
void redrawCursor();
void resetImContext();
@ -81,17 +80,14 @@ private:
GtkWidget *textWidget;
GtkIMContext *im_context;
GtkIMContext *imContext;
String preeditString;
bool needImReset;
GtkTextBuffer * buffer;
double virtualCursor;
bool fContentsChanged;
GList * undoActions;
GList * contents;
double markPosX;
double markPosY;
bool markPosExtendSelection;
@ -103,6 +99,8 @@ private:
String lastText;
PangoLayout * layout;
int cursorBlinkTime;
int cursorBlinkTimeout;
bool cursorVisible;

@ -223,7 +223,7 @@ void ToolMenuHandler::load(ToolbarData * d, GtkWidget * toolbar, const char * to
continue;
}
if (name.startsWith("COLOR(") && name.length() == 15) {
if (name.startsWith("COLOR(") && name.size() == 15) {
String color = name.substring(6, 8);
if (!color.startsWith("0x")) {
g_warning("Toolbar:COLOR(...) has to start with 0x, get color: %s", color.c_str());

@ -13,14 +13,27 @@
#include <string.h>
#include <ctype.h>
/**
* This code is a modified version of what SQLite uses.
*/
#define SKIP_MULTI_BYTE_SEQUENCE(input) { \
if( (*(input++)) >= 0xc0 ) { \
while( (*input & 0xc0) == 0x80 ){ input++; } \
} \
}
#define utf8 unsigned char *
class _RefStrInternal {
public:
_RefStrInternal(gchar * str) {
_RefStrInternal(char * str) {
this->s = str;
if (str == NULL) {
this->length = 0;
this->size = 0;
this->len = 0;
} else {
this->length = strlen(str);
this->size = strlen(str);
this->len = length((utf8)str);
}
}
@ -39,12 +52,26 @@ public:
}
}
gchar * c_str() {
int length(const utf8 string) {
int len;
len = 0;
while (*string) {
++len;
SKIP_MULTI_BYTE_SEQUENCE(string);
}
return len;
}
char * c_str() {
return s;
}
int getLength() const {
return length;
return this->len;
}
int getSize() const {
return this->size;
}
private:
_RefStrInternal(const _RefStrInternal & str) {
@ -55,8 +82,9 @@ private:
private:
int nref;
gchar *s;
int length;
char * s;
int size;
int len;
};
String::String() {
@ -73,7 +101,7 @@ String::String(const char * str) {
this->data->reference();
}
String::String(char * str, gboolean freeAutomatically) {
String::String(char * str, bool freeAutomatically) {
if (freeAutomatically) {
this->data = new _RefStrInternal(str);
} else {
@ -92,8 +120,8 @@ int String::indexOfCaseInsensitiv(String substr, int fromIndex) const {
if (source == NULL || target == NULL) {
return -1;
}
int sourceCount = length();
int targetCount = substr.length();
int sourceCount = size();
int targetCount = substr.size();
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
@ -140,8 +168,8 @@ int String::indexOf(String substr, int fromIndex) const {
if (source == NULL || target == NULL) {
return -1;
}
int sourceCount = length();
int targetCount = substr.length();
int sourceCount = size();
int targetCount = substr.size();
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
@ -183,7 +211,7 @@ int String::indexOf(String substr, int fromIndex) const {
}
int String::lastIndexOf(String substr) const {
return lastIndexOf(substr, length() - 1);
return lastIndexOf(substr, size() - 1);
}
int String::lastIndexOf(String substr, int fromIndex) const {
@ -192,8 +220,8 @@ int String::lastIndexOf(String substr, int fromIndex) const {
if (source == NULL || target == NULL) {
return -1;
}
int sourceCount = length();
int targetCount = substr.length();
int sourceCount = size();
int targetCount = substr.size();
if (fromIndex <= 0) {
return (targetCount == 0 ? 0 : -1);
@ -230,14 +258,14 @@ int String::lastIndexOf(String substr, int fromIndex) const {
return -1;
}
gboolean String::contains(const gchar * substr) const {
bool String::contains(const char * substr) const {
if (c_str() == NULL || substr == NULL) {
return false;
}
return g_strrstr(c_str(), substr) != NULL;
}
gboolean String::equals(const gchar * other) const {
bool String::equals(const char * other) const {
if (other == c_str()) {
return true;
}
@ -247,7 +275,7 @@ gboolean String::equals(const gchar * other) const {
return strcmp(c_str(), other) == 0;
}
gboolean String::equals(const String & s) const {
bool String::equals(const String & s) const {
return equals(s.c_str());
}
@ -257,16 +285,16 @@ String& String::operator=(const char * str) {
this->data->reference();
}
String& String::operator=(const String &str) {
String& String::operator=(const String & str) {
this->data = str.data;
this->data->reference();
}
gboolean String::operator==(const String & str) const {
bool String::operator==(const String & str) const {
return equals(str.c_str());
}
gboolean String::operator!=(const String & str) const {
bool String::operator!=(const String & str) const {
return !equals(str.c_str());
}
@ -287,7 +315,7 @@ void String::operator +=(double d) {
}
void String::operator +=(const char * str) {
gchar * data = g_strconcat(c_str(), str, NULL);
char * data = g_strconcat(c_str(), str, NULL);
this->data->unreference();
this->data = new _RefStrInternal(data);
this->data->reference();
@ -321,18 +349,29 @@ bool String::operator >(const String & str) const {
return strcmp(c_str(), str.c_str()) > 0;
}
const gchar * String::c_str() const {
const char * String::c_str() const {
return data->c_str();
}
gboolean String::isEmpty() const {
bool String::isEmpty() const {
return this->c_str() == NULL || *c_str() == 0;
}
/**
* the lengths is the count of chars which this string contains,
* this is may smaller than the size, if there are multibyte UTF8 chars
*/
int String::length() const {
return this->data->getLength();
}
/**
* The size is the count of bytes which this string contains
*/
int String::size() const {
return this->data->getSize();
}
String String::substring(int start) const {
if (start < 0) {
start = length() + start;
@ -345,12 +384,35 @@ String String::substring(int start) const {
return substring(start, length() - start);
}
//String oldSubstring(String str, int start, int length) {
// if (length < 0) {
// length = str.length() - start + length;
// }
//
// if(start < 0) {
// start = str.length() - start;
// }
//
// if (start + length > str.length() || start < 0 || length < 0) {
// g_critical("substring \"%s\" (%i, %i) out of bounds", str.c_str(), start, length);
// return "";
// }
//
// const char * orig = str.c_str();
// char * d = (char *) g_malloc(length + 1);
// strncpy(d, &orig[start], length);
// d[length] = 0;
//
// String substr(d, true);
// return substr;
//}
String String::substring(int start, int length) const {
if (length < 0) {
length = this->length() - start + length;
}
if(start < 0) {
if (start < 0) {
start = this->length() - start;
}
@ -359,12 +421,40 @@ String String::substring(int start, int length) const {
return "";
}
const char * orig = c_str();
char * d = (char *) g_malloc(length + 1);
strncpy(d, &orig[start], length);
d[length] = 0;
const utf8 string = (utf8)c_str();
int s = start;
while (*string && s) {
SKIP_MULTI_BYTE_SEQUENCE(string);
--s;
}
const utf8 str2;
int l = length;
for (str2 = string; *str2 && l; l--) {
SKIP_MULTI_BYTE_SEQUENCE(str2);
}
int bytes = (int) (str2 - string);
char * substring = (char *) g_malloc(bytes + 1);
char * output = substring;
String substr(d, true);
for (int i = 0; i < bytes; i++) {
*output++ = *string++;
}
*output = '\0';
// String old = oldSubstring(*this, start, length);
//
// const char * error = "";
//
// if(old != substring) {
// error = "\nMISSMATCH!";
// }
//
// printf("substring(\"%s\", %i, %i) =\n\"%s\"\n\"%s\"%s\n\n", this->c_str(), start, length, substring, old.c_str(),error);
String substr(substring, true);
return substr;
}
@ -382,7 +472,7 @@ String String::trim() {
}
}
len = length() - start;
len = size() - start;
while (len != 0) {
char tmp = s[start + len - 1];
@ -393,30 +483,30 @@ String String::trim() {
}
}
if (start == 0 && len == length()) {
if (start == 0 && len == size()) {
return *this;
}
return substring(start, len);
}
gboolean String::startsWith(const String & s) const {
bool String::startsWith(const String & s) const {
return startsWith(s.c_str());
}
gboolean String::startsWith(const char * s) const {
bool String::startsWith(const char * s) const {
return g_str_has_prefix(c_str(), s);
}
gboolean String::endsWith(const String & s) const {
bool String::endsWith(const String & s) const {
return endsWith(s.c_str());
}
gboolean String::equalsIgnorCase(const String & s) {
bool String::equalsIgnorCase(const String & s) {
return this->toLowerCase().equals(s.toLowerCase());
}
gboolean String::endsWith(const char * s) const {
bool String::endsWith(const char * s) const {
return g_str_has_suffix(c_str(), s);
}
@ -425,7 +515,7 @@ String String::toLowerCase() const {
char * data = s.data->c_str();
for (int i = 0; i < s.length(); i++) {
for (int i = 0; i < s.size(); i++) {
data[i] = tolower(data[i]);
}
@ -437,22 +527,25 @@ String String::toUpperCase() const {
char * data = s.data->c_str();
for (int i = 0; i < s.length(); i++) {
for (int i = 0; i < s.size(); i++) {
data[i] = toupper(data[i]);
}
return s;
}
/**
* TODO: this has some bugs!!
*/
String String::replace(String search, String replace) const {
char const * const original = c_str();
char const * const pattern = search.c_str();
char const * const replacement = replace.c_str();
size_t const replen = replace.length();
size_t const patlen = search.length();
size_t const orilen = length();
int const replen = replace.size();
int const patlen = search.size();
int const orilen = size();
size_t patcnt = 0;
int patcnt = 0;
const char * oriptr;
const char * patloc;
@ -466,7 +559,7 @@ String String::replace(String search, String replace) const {
}
// allocate memory for the new string
size_t const retlen = orilen + patcnt * (replen - patlen);
int const retlen = orilen + patcnt * (replen - patlen);
char * const returned = (char *) g_malloc(sizeof(char) * (retlen + 1));
if (returned != NULL) {
@ -474,7 +567,7 @@ String String::replace(String search, String replace) const {
// replacing all the instances of the pattern
char * retptr = returned;
for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen) {
size_t const skplen = patloc - oriptr;
int const skplen = patloc - oriptr;
// copy the section until the occurence of the pattern
strncpy(retptr, oriptr, skplen);
retptr += skplen;
@ -489,6 +582,11 @@ String String::replace(String search, String replace) const {
return String(returned, true);
}
/**
* String tokenizer
*
* THIS CLASS IS NOT UTF8 SAVE
*/
StringTokenizer::StringTokenizer(String s, char token, bool returnToken) {
this->str = g_strdup(s.c_str());
this->token = token;
@ -497,7 +595,7 @@ StringTokenizer::StringTokenizer(String s, char token, bool returnToken) {
this->returnToken = returnToken;
this->lastWasToken = false;
this->x = 0;
this->len = s.length();
this->len = s.size();
}
StringTokenizer::~StringTokenizer() {

@ -20,14 +20,14 @@ public:
String();
String(const String & str);
String(const char * data);
String(char * data, gboolean freeAutomatically);
String(char * data, bool freeAutomatically);
~String();
String& operator=(const String &str);
String& operator=(const String & str);
String& operator=(const char * str);
gboolean operator ==(const String & str) const;
gboolean operator !=(const String & str) const;
bool operator ==(const String & str) const;
bool operator !=(const String & str) const;
bool operator <(const String & str) const;
bool operator >(const String & str) const;
@ -38,7 +38,7 @@ public:
String replace(String search, String replace) const;
const gchar * c_str() const;
const char * c_str() const;
// gchar * reserveBuffer(int buffer);
@ -47,19 +47,20 @@ public:
int lastIndexOf(String substr) const;
int lastIndexOf(String substr, int fromIndex) const;
gboolean contains(const gchar * substr) const;
bool contains(const char * substr) const;
gboolean equals(const gchar * other) const;
gboolean equals(const String & s) const;
gboolean isEmpty() const;
gboolean startsWith(const String & s) const;
gboolean startsWith(const char * s) const;
gboolean endsWith(const String & s) const;
gboolean endsWith(const char * s) const;
bool equals(const char * other) const;
bool equals(const String & s) const;
bool isEmpty() const;
bool startsWith(const String & s) const;
bool startsWith(const char * s) const;
bool endsWith(const String & s) const;
bool endsWith(const char * s) const;
gboolean equalsIgnorCase(const String & s);
bool equalsIgnorCase(const String & s);
int length() const;
int size() const;
String substring(int start, int length) const;
String substring(int start) const;
String trim();

@ -14,7 +14,7 @@ StringBuffer::~StringBuffer() {
}
void StringBuffer::append(String s) {
append(s.c_str(), s.length());
append(s.c_str(), s.size());
}
void StringBuffer::append(const char * str) {

@ -225,17 +225,17 @@ bool XojPopplerDocument::load(const char *uri, const char *password, GError **er
#ifdef G_OS_WIN32
wchar_t *filenameW;
int length;
int xxxxxxxxxxxx;
length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
xxxxxxxxxxxx = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
filenameW = new WCHAR[length];
filenameW = new WCHAR[xxxxxxxxxxxx];
if (!filenameW)
return NULL;
length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, length);
xxxxxxxxxxxx = MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, xxxxxxxxxxxx);
newDoc = new PDFDoc(filenameW, length, password_g, password_g);
newDoc = new PDFDoc(filenameW, xxxxxxxxxxxx, password_g, password_g);
delete filenameW;
#else
filename_g = new GooString(filename);

@ -47,7 +47,7 @@ void ObjectOutputStream::writeString(const char * str) {
void ObjectOutputStream::writeString(const String & s) {
g_string_append(this->data, "_s");
int len = s.length();
int len = s.size();
char * c = (char *) &len;
g_string_append_len(this->data, c, sizeof(int));
g_string_append_len(this->data, s.c_str(), len);

@ -9,7 +9,7 @@ OutputStream::~OutputStream() {
}
void OutputStream::write(const String & str) {
write(str.c_str(), str.length());
write(str.c_str(), str.size());
}
void OutputStream::write(const char * str) {

@ -9,145 +9,71 @@ TextView::TextView() {
TextView::~TextView() {
}
void TextView::initCairo(cairo_t *cr, Text * t) {
XojFont & font = t->getFont();
PangoFontDescription * desc = pango_font_description_from_string(font.getName().c_str());
pango_font_description_set_size(desc, font.getSize());
bool italic = pango_font_description_get_style(desc) != PANGO_STYLE_NORMAL ;
bool bold = pango_font_description_get_weight(desc) == PANGO_WEIGHT_BOLD;
cairo_select_font_face(cr, pango_font_description_get_family(desc), italic? CAIRO_FONT_SLANT_ITALIC
: CAIRO_FONT_SLANT_NORMAL, bold ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, font.getSize());
PangoLayout * TextView::initPango(cairo_t *cr, Text * t) {
PangoLayout * layout = pango_cairo_create_layout(cr);
PangoFontDescription * desc = pango_font_description_from_string(t->getFont().getName().c_str());
pango_font_description_set_absolute_size(desc, t->getFont().getSize() * PANGO_SCALE);
pango_layout_set_font_description(layout, desc);
pango_font_description_free(desc);
// TODO LOW PRIO: add autowrap and text field size for the next xournal release (with new fileformat...)
//pango_layout_set_wrap
// TODO: use http://library.gnome.org/devel/pango/stable/pango-Cairo-Rendering.html
// double size = font.getSize();
// cairo_matrix_t m;
// cairo_matrix_init_scale(&m, size, size);
//
// printf("size: %lf, x0 = %lf, xx = %lf, xy = %lf, y0 = %lf, yx = %lf, yy = %lf\n", size, m.x0, m.xx, m.xy, m.y0, m.yx, m.yy);
//
// cairo_set_font_matrix(cr, &m);
return layout;
}
void TextView::drawText(cairo_t *cr, Text * t) {
initCairo(cr, t);
cairo_text_extents_t extents = { 0 };
cairo_font_extents_t fe = { 0 };
// This need to be here, why...? I don't know, the size should be calculated anyway if t->getX() is called...
t->getElementWidth();
double y = 3 + t->getY();
StringTokenizer token(t->getText(), '\n');
const char * str = token.next();
void TextView::drawText(cairo_t * cr, Text * t) {
cairo_save(cr);
cairo_font_extents(cr, &fe);
cairo_translate(cr, t->getX(), t->getY());
// TODO: with "Hallo Test X" this went wrong on some zoom factors...
// printf("fe height: %lf\n", fe.height);
// this is because cairo fonts are not always the same size on different zoom levels!!
PangoLayout * layout = initPango(cr, t);
String str = t->getText();
pango_layout_set_text(layout, str.c_str(), str.size());
pango_cairo_show_layout(cr, layout);
while (str) {
y += fe.height - fe.descent;
g_object_unref(layout);
StringTokenizer tokenTab(str, '\t', true);
const char * strTab = tokenTab.next();
double x = 0;
while (strTab) {
if (strcmp(strTab, "\t") == 0) {
int tab = x / TextView::TAB_INDEX + 1;
x = tab * TextView::TAB_INDEX;
} else {
cairo_move_to(cr, t->getX() + x, y);
cairo_show_text(cr, strTab);
cairo_text_extents(cr, strTab, &extents);
x += extents.x_advance;
}
strTab = tokenTab.next();
}
y += fe.height * 0.25;
// double x = t->getX();
// for (char * c = str; *c; c++) {
// char tmp[2] = { 0, 0 };
// tmp[0] = *c;
// cairo_text_extents_t ex = { 0 };
// cairo_text_extents(cr, tmp, &ex);
//
// printf("->\"%s\": w:%lf h:%lf xa:%lf ya:%lf xb:%lf yb:%lf\n", tmp, ex.width, ex.height, ex.x_advance,
// ex.y_advance, ex.x_bearing, ex.y_bearing);
// cairo_rectangle(cr, x + ex.x_bearing, y + ex.y_bearing, ex.width, ex.height);
// x += ex.x_advance;
//
// cairo_stroke(cr);
// }
// printf("--------\n\n");
// printf("->\"%s\"\n", str);
str = token.next();
}
cairo_restore(cr);
}
GList * TextView::findText(Text * t, const char * search) {
GList * list = NULL;
cairo_surface_t * surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
cairo_t * cr = cairo_create(surface);
initCairo(cr, t);
cairo_text_extents_t extents = { 0 };
cairo_font_extents_t fe = { 0 };
StringTokenizer token(t->getText(), '\n');
const char * str = token.next();
double y = 3 + t->getY();
double x = t->getX();
int searchlen = strlen(search);
GList * list = NULL;
cairo_font_extents(cr, &fe);
PangoLayout * layout = initPango(cr, t);
String str = t->getText();
pango_layout_set_text(layout, str.c_str(), str.size());
while (str) {
String line = str;
int pos = -1;
y += fe.height - fe.descent;
do {
pos = line.indexOfCaseInsensitiv(search, pos + 1);
if (pos != -1) {
cairo_text_extents(cr, line.substring(0, pos).c_str(), &extents);
XojPopplerRectangle * rect = new XojPopplerRectangle();
rect->x1 = x + extents.x_advance;
rect->y1 = y;
int pos = -1;
// because of case insensitive search, TEST and test may have different sizes (depending of the font)
cairo_text_extents(cr, line.substring(pos, searchlen).c_str(), &extents);
String text = t->getText();
rect->x1 += extents.x_bearing;
rect->y1 += extents.y_bearing;
rect->x2 = rect->x1 + extents.x_advance;
rect->y2 = rect->y1 + extents.height;
String srch = search;
list = g_list_append(list, rect);
}
do {
pos = text.indexOfCaseInsensitiv(srch, pos + 1);
if (pos != -1) {
XojPopplerRectangle * mark = new XojPopplerRectangle();
PangoRectangle rect = { 0 };
pango_layout_index_to_pos(layout, pos, &rect);
mark->x1 = ((double)rect.x) / PANGO_SCALE + t->getX();
mark->y1 = ((double)rect.y) / PANGO_SCALE + t->getY();
} while (pos != -1);
pango_layout_index_to_pos(layout, pos + srch.length(), &rect);
mark->x2 = ((double)rect.x + rect.width) / PANGO_SCALE + t->getX();
mark->y2 = ((double)rect.y + rect.height) / PANGO_SCALE + t->getY();
str = token.next();
y += fe.height * 0.25;
}
list = g_list_append(list, mark);
}
} while (pos != -1);
g_object_unref(layout);
cairo_surface_destroy(surface);
cairo_destroy(cr);
@ -156,33 +82,17 @@ GList * TextView::findText(Text * t, const char * search) {
void TextView::calcSize(Text * t, double & width, double & height) {
cairo_surface_t * surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
cairo_t *cr;
cr = cairo_create(surface);
initCairo(cr, t);
cairo_text_extents_t extents = { 0 };
cairo_font_extents_t fe = { 0 };
cairo_font_extents(cr, &fe);
height = 3;
width = 0;
StringTokenizer token(t->getText(), '\n');
const char * str = token.next();
while (str) {
cairo_text_extents(cr, str, &extents);
height += fe.height * 1.25 - fe.descent;
width = MAX(width, extents.x_advance);
cairo_t * cr = cairo_create(surface);
str = token.next();
if (!str) {
height -= fe.height * 0.25;
}
}
PangoLayout * layout = initPango(cr, t);
String str = t->getText();
pango_layout_set_text(layout, str.c_str(), str.size());
int w = 0;
int h = 0;
pango_layout_get_size(layout, &w, &h);
width = ((double) w) / PANGO_SCALE;
height = ((double) h) / PANGO_SCALE;
g_object_unref(layout);
cairo_surface_destroy(surface);
cairo_destroy(cr);

@ -25,8 +25,7 @@ public:
static void calcSize(Text * t, double & width, double & height);
static void drawText(cairo_t *cr, Text * t);
static GList * findText(Text * t, const char * text);
static void initCairo(cairo_t *cr, Text * t);
static const int TAB_INDEX = 30;
static PangoLayout * initPango(cairo_t *cr, Text * t);
};
#endif /* __TEXTVIEW_H__ */

Loading…
Cancel
Save