aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ioview.cpp
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2016-08-16 05:04:54 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2016-08-19 03:29:11 +0200
commit61ff6e97c57f62ee3ad4ffc2166e433bc060e7cb (patch)
tree64c032ebdd040809d1b7e822e627e199a9c2476e /src/ioview.cpp
parent94b041ec331427fd63cdae3e943efe825d1bbf14 (diff)
downloadsciteco-61ff6e97c57f62ee3ad4ffc2166e433bc060e7cb.tar.gz
Integrated clipboard support
* mapped to different registers beginning with "~" * on supported platforms accessing the clipboard is as easy as X~ or G~. Naturally this also allows clipboards to be pasted in string arguments/insertions (^EQ~). * Currently, Gtk+, PDCurses and ncurses/XTerm are supported. For XTerm clipboard support, users must set 0,256ED to enable it since we cannot check for XTerm window ops programmatically (at least without libX11). * When clipboard regs exist, the clipboard can also be deemed functional. This allows macros to fall back to xclip(1) if necessary. * EOL handling has been moved into a new file eol.c and eol.h. EOL translation no longer depends on GIOChannels but can be memory-backed as well.
Diffstat (limited to 'src/ioview.cpp')
-rw-r--r--src/ioview.cpp353
1 files changed, 39 insertions, 314 deletions
diff --git a/src/ioview.cpp b/src/ioview.cpp
index 1643a29..a5685fe 100644
--- a/src/ioview.cpp
+++ b/src/ioview.cpp
@@ -37,6 +37,7 @@
#include "undo.h"
#include "error.h"
#include "qregisters.h"
+#include "eol.h"
#include "ioview.h"
#ifdef HAVE_WINDOWS_H
@@ -86,173 +87,6 @@ set_file_attributes(const gchar *filename, FileAttributes attrs)
#endif /* !G_OS_WIN32 */
/**
- * A wrapper around g_io_channel_read_chars() that also
- * performs automatic EOL translation (if enabled) in a
- * more or less efficient manner.
- * Unlike g_io_channel_read_chars(), this returns an
- * offset and length into the buffer with normalized
- * EOL character.
- * The function must therefore be called iteratively on
- * on the same buffer while it returns G_IO_STATUS_NORMAL.
- *
- * @param channel The GIOChannel to read from.
- ' @param buffer Used to store blocks.
- * @param buffer_len Size of buffer.
- * @param read_len Total number of bytes read into buffer.
- * Must be provided over the lifetime of buffer
- * and initialized with 0.
- * @param offset If a block could be read (G_IO_STATUS_NORMAL),
- * this will be set to indicate its beginning in
- * buffer. Should be initialized to 0.
- * @param block_len Will be set to the block length.
- * Should be initialized to 0.
- * @param state Opaque state that must persist for the lifetime
- * of the channel. Must be initialized with 0.
- * @param eol_style Will be set to the EOL style guessed from
- * the data in channel (if the data allows it).
- * Should be initialized with -1 (unknown).
- * @param eol_style_inconsistent Will be set to TRUE if
- * inconsistent EOL styles are detected.
- * @param error If not NULL and an error occurred, it is set to
- * the error. It should be initialized to -1.
- * @return A GIOStatus as returned by g_io_channel_read_chars()
- */
-GIOStatus
-IOView::channel_read_with_eol(GIOChannel *channel,
- gchar *buffer, gsize buffer_len,
- gsize &read_len,
- guint &offset, gsize &block_len,
- gint &state, gint &eol_style,
- gboolean &eol_style_inconsistent,
- GError **error)
-{
- GIOStatus status;
-
- if (state < 0) {
- /* a CRLF was last translated */
- block_len++;
- state = '\n';
- }
- offset += block_len;
-
- if (offset == read_len) {
- offset = 0;
-
- status = g_io_channel_read_chars(channel, buffer, buffer_len,
- &read_len, error);
- if (status == G_IO_STATUS_EOF && state == '\r') {
- /*
- * Very last character read is CR.
- * If this is the only EOL so far, the
- * EOL style is MAC.
- * This is also executed if auto-eol is disabled
- * but it doesn't hurt.
- */
- if (eol_style < 0)
- eol_style = SC_EOL_CR;
- else if (eol_style != SC_EOL_CR)
- eol_style_inconsistent = TRUE;
- }
- if (status != G_IO_STATUS_NORMAL)
- return status;
-
- if (!(Flags::ed & Flags::ED_AUTOEOL)) {
- /*
- * No EOL translation - always return entire
- * buffer
- */
- block_len = read_len;
- return G_IO_STATUS_NORMAL;
- }
- }
-
- /*
- * Return data with automatic EOL translation.
- * Every EOL sequence is normalized to LF and
- * the first sequence determines the documents
- * EOL style.
- * This loop is executed for every byte of the
- * file/stream, so it was important to optimize
- * it. Specifically, the number of returns
- * is minimized by keeping a pointer to
- * the beginning of a block of data in the buffer
- * which already has LFs (offset).
- * Mac EOLs can be converted to UNIX EOLs directly
- * in the buffer.
- * So if their EOLs are consistent, the function
- * will return one block for the entire buffer.
- * When reading a file with DOS EOLs, there will
- * be one call per line which is significantly slower.
- */
- for (guint i = offset; i < read_len; i++) {
- switch (buffer[i]) {
- case '\n':
- if (state == '\r') {
- if (eol_style < 0)
- eol_style = SC_EOL_CRLF;
- else if (eol_style != SC_EOL_CRLF)
- eol_style_inconsistent = TRUE;
-
- /*
- * Return block. CR has already
- * been made LF in `buffer`.
- */
- block_len = i-offset;
- /* next call will skip the CR */
- state = -1;
- return G_IO_STATUS_NORMAL;
- }
-
- if (eol_style < 0)
- eol_style = SC_EOL_LF;
- else if (eol_style != SC_EOL_LF)
- eol_style_inconsistent = TRUE;
- /*
- * No conversion necessary and no need to
- * return block yet.
- */
- state = '\n';
- break;
-
- case '\r':
- if (state == '\r') {
- if (eol_style < 0)
- eol_style = SC_EOL_CR;
- else if (eol_style != SC_EOL_CR)
- eol_style_inconsistent = TRUE;
- }
-
- /*
- * Convert CR to LF in `buffer`.
- * This way more than one line using
- * Mac EOLs can be returned at once.
- */
- buffer[i] = '\n';
- state = '\r';
- break;
-
- default:
- if (state == '\r') {
- if (eol_style < 0)
- eol_style = SC_EOL_CR;
- else if (eol_style != SC_EOL_CR)
- eol_style_inconsistent = TRUE;
- }
- state = buffer[i];
- break;
- }
- }
-
- /*
- * Return remaining block.
- * With UNIX/MAC EOLs, this will usually be the
- * entire `buffer`
- */
- block_len = read_len-offset;
- return G_IO_STATUS_NORMAL;
-}
-
-/**
* Loads the view's document by reading all data from
* a GIOChannel.
* The EOL style is guessed from the channel's data
@@ -261,23 +95,17 @@ IOView::channel_read_with_eol(GIOChannel *channel,
* Also it tries to guess the size of the file behind
* channel in order to preallocate memory in Scintilla.
*
+ * Any error reading the GIOChannel is propagated as
+ * an exception.
+ *
* @param channel Channel to read from.
- * @param error Glib error or NULL.
- * @returns A GIOStatus as returned by g_io_channel_read_chars()
*/
-GIOStatus
-IOView::load(GIOChannel *channel, GError **error)
+void
+IOView::load(GIOChannel *channel)
{
- GIOStatus status;
GStatBuf stat_buf;
- gchar buffer[1024];
- gsize read_len = 0;
- guint offset = 0;
- gsize block_len = 0;
- gint state = 0; /* opaque state */
- gint eol_style = -1; /* yet unknown */
- gboolean eol_style_inconsistent = FALSE;
+ EOLReaderGIO reader(channel);
ssm(SCI_BEGINUNDOACTION);
ssm(SCI_CLEARALL);
@@ -294,17 +122,15 @@ IOView::load(GIOChannel *channel, GError **error)
stat_buf.st_size > 0)
ssm(SCI_ALLOCATE, stat_buf.st_size);
- for (;;) {
- status = channel_read_with_eol(
- channel, buffer, sizeof(buffer),
- read_len, offset, block_len, state,
- eol_style, eol_style_inconsistent,
- error
- );
- if (status != G_IO_STATUS_NORMAL)
- break;
-
- ssm(SCI_APPENDTEXT, block_len, (sptr_t)(buffer+offset));
+ try {
+ const gchar *data;
+ gsize data_len;
+
+ while ((data = reader.convert(data_len)))
+ ssm(SCI_APPENDTEXT, data_len, (sptr_t)data);
+ } catch (...) {
+ ssm(SCI_ENDUNDOACTION);
+ throw; /* forward */
}
/*
@@ -317,15 +143,14 @@ IOView::load(GIOChannel *channel, GError **error)
* If it is enabled but the stream does not contain any
* EOL characters, the platform default is still assumed.
*/
- if (eol_style >= 0)
- ssm(SCI_SETEOLMODE, eol_style);
+ if (reader.eol_style >= 0)
+ ssm(SCI_SETEOLMODE, reader.eol_style);
- if (eol_style_inconsistent)
+ if (reader.eol_style_inconsistent)
interface.msg(InterfaceCurrent::MSG_WARNING,
"Inconsistent EOL styles normalized");
ssm(SCI_ENDUNDOACTION);
- return status;
}
/**
@@ -336,7 +161,6 @@ IOView::load(const gchar *filename)
{
GError *error = NULL;
GIOChannel *channel;
- GIOStatus status;
channel = g_io_channel_new_file(filename, "r", &error);
if (!channel) {
@@ -354,15 +178,17 @@ IOView::load(const gchar *filename)
g_io_channel_set_encoding(channel, NULL, NULL);
g_io_channel_set_buffered(channel, FALSE);
- status = load(channel, &error);
- /* also closes file: */
- g_io_channel_unref(channel);
- if (status == G_IO_STATUS_ERROR) {
+ try {
+ load(channel);
+ } catch (Error &e) {
Error err("Error reading file \"%s\": %s",
- filename, error->message);
- g_error_free(error);
+ filename, e.description);
+ g_io_channel_unref(channel);
throw err;
}
+
+ /* also closes file: */
+ g_io_channel_unref(channel);
}
#if 0
@@ -471,132 +297,30 @@ make_savepoint(const gchar *filename)
#endif
-GIOStatus
-IOView::save(GIOChannel *channel, guint position, gsize len,
- gsize *bytes_written, gint &state, GError **error)
-{
- const gchar *buffer;
- const gchar *eol_seq;
- gchar last_c;
- guint i = 0;
- guint block_start;
- gsize block_written;
-
- GIOStatus status;
-
- enum {
- SAVE_STATE_START = 0,
- SAVE_STATE_WRITE_LF
- };
-
- buffer = (const gchar *)ssm(SCI_GETRANGEPOINTER,
- position, (sptr_t)len);
-
- if (!(Flags::ed & Flags::ED_AUTOEOL))
- /*
- * Write without EOL-translation:
- * `state` is not required
- */
- return g_io_channel_write_chars(channel, buffer, len,
- bytes_written, error);
-
- /*
- * Write to stream with EOL-translation.
- * The document's EOL mode tells us what was guessed
- * when its content was read in (presumably from a file)
- * but might have been changed manually by the user.
- * NOTE: This code assumes that the output stream is
- * buffered, since otherwise it would be slower
- * (has been benchmarked).
- * NOTE: The loop is executed for every character
- * in `buffer` and has been optimized for minimal
- * function (i.e. GIOChannel) calls.
- */
- *bytes_written = 0;
- if (state == SAVE_STATE_WRITE_LF) {
- /* complete writing a CRLF sequence */
- status = g_io_channel_write_chars(channel, "\n", 1, NULL, error);
- if (status != G_IO_STATUS_NORMAL)
- return status;
- state = SAVE_STATE_START;
- (*bytes_written)++;
- i++;
- }
-
- eol_seq = get_eol_seq(ssm(SCI_GETEOLMODE));
- last_c = ssm(SCI_GETCHARAT, position-1);
-
- block_start = i;
- while (i < len) {
- switch (buffer[i]) {
- case '\n':
- if (last_c == '\r') {
- /* EOL sequence already written */
- (*bytes_written)++;
- block_start = i+1;
- break;
- }
- /* fall through */
- case '\r':
- status = g_io_channel_write_chars(channel, buffer+block_start,
- i-block_start, &block_written, error);
- *bytes_written += block_written;
- if (status != G_IO_STATUS_NORMAL ||
- block_written < i-block_start)
- return status;
-
- status = g_io_channel_write_chars(channel, eol_seq,
- -1, &block_written, error);
- if (status != G_IO_STATUS_NORMAL)
- return status;
- if (eol_seq[block_written]) {
- /* incomplete EOL seq - we have written CR of CRLF */
- state = SAVE_STATE_WRITE_LF;
- return G_IO_STATUS_NORMAL;
- }
- (*bytes_written)++;
-
- block_start = i+1;
- break;
- }
-
- last_c = buffer[i++];
- }
-
- /*
- * Write out remaining block (i.e. line)
- */
- status = g_io_channel_write_chars(channel, buffer+block_start,
- len-block_start, &block_written, error);
- *bytes_written += block_written;
- return status;
-}
-
-gboolean
-IOView::save(GIOChannel *channel, GError **error)
+void
+IOView::save(GIOChannel *channel)
{
+ EOLWriterGIO writer(channel, ssm(SCI_GETEOLMODE));
sptr_t gap;
gsize size;
+ const gchar *buffer;
gsize bytes_written;
- gint state = 0;
/* write part of buffer before gap */
gap = ssm(SCI_GETGAPPOSITION);
if (gap > 0) {
- if (save(channel, 0, gap, &bytes_written, state, error) == G_IO_STATUS_ERROR)
- return FALSE;
+ buffer = (const gchar *)ssm(SCI_GETRANGEPOINTER, 0, gap);
+ bytes_written = writer.convert(buffer, gap);
g_assert(bytes_written == (gsize)gap);
}
/* write part of buffer after gap */
size = ssm(SCI_GETLENGTH) - gap;
if (size > 0) {
- if (save(channel, gap, size, &bytes_written, state, error) == G_IO_STATUS_ERROR)
- return FALSE;
+ buffer = (const gchar *)ssm(SCI_GETRANGEPOINTER, gap, (sptr_t)size);
+ bytes_written = writer.convert(buffer, size);
g_assert(bytes_written == size);
}
-
- return TRUE;
}
void
@@ -636,9 +360,10 @@ IOView::save(const gchar *filename)
g_io_channel_set_encoding(channel, NULL, NULL);
g_io_channel_set_buffered(channel, TRUE);
- if (!save(channel, &error)) {
- Error err("Error writing file \"%s\": %s", filename, error->message);
- g_error_free(error);
+ try {
+ save(channel);
+ } catch (Error &e) {
+ Error err("Error writing file \"%s\": %s", filename, e.description);
g_io_channel_unref(channel);
throw err;
}