Commit 99904b10 authored by Robert Sprowson's avatar Robert Sprowson
Browse files

Switch from ArcEdit style model to clipboard cut & paste model

The txt library part of RISC_OSLib was essentially contrary to everything Acorn was telling its developers, to use the global clipboard.
Additionally, support is added for swap case within a selection.
txt.h:
 Add a new charoption type 'txt_READONLY' to inform txt that the buffer is notionally read only, this is used to supress 'Paste' in the selection menu when appropriate.
 Correct some typos in the comments.
txt1.h:
 Kill off modula 2 dummy structure member.
txtar.h:
 Remove unused function export.
txtfile.h:
 Rename basicimport to be consistent with the text version of the same function. We use 'import' to denote RAM transfers and 'insert' to denote file/scrap transfers throughout.
txtmisc.h:
 Redundant internal functions removed.
txtundo.h:
 Add a new undo operation type 't' for swap case, to avoid polluting the undo buffer with an entire copy of the text where only the case changed.
 Kill off modula 2 double pointer requirement.
txtedit.h:
 Unused structure member 'selectctl' removed.
txt.c:
 Kill off modula 2 double pointer requirement.
txtar.c:
 Refactor message despatch with a switch statement so the save/load/open operations are explicitly checked for, in case the application enables other messages.
txtedit.c:
 Adopt MOVERWRITE, been enabled since 1988 so is probably good to keep.
 Dynamically generate the Select menu by first sending a clipboard request, and fading Paste if no reply comes back.
 Implement changed mouse selection logic.
 Implement different hotkeys and caret navigation.
 Implement copy and paste/replace operations.
 Implement swap case operation.
txtfile.c:
 Type corrections and function rename admin.
 Implement copy and paste/replace operation for detokenised BASIC.
txtmisc.c:
 Supporting functions for clipboard added, supporting functions for ArcEdit removed.
txtscrap.c:
 Make sure the caret is visible for programmatical zero sized selections.
txtundo.c:
 Allow suspension of undo during known complex operations, such as import via RAM transmit. This was a longstanding bug where the import buffer was grabbed in ~4k chunks (even if only 1 byte was being transferred) which in turn resulted in a +4000 undo insertion and a -3999 removal, which given the default undo buffer is only 5k would result in it failing the reversibility test, so no undo was possible.
 Now, during a RAM transmit undo is suspended until the total transfer size is known, and only that data is placed in the undo buffer (subject to the same 5k reversibility limit).
 Add new undo type 't' for swap case operation.
rlibdata.s:
 4 new ints and 1 new BOOL, so RlibSpace increases by 5.

Version 5.85. Tagged as 'RISC_OSLib-5_85'
parent 97c92ee5
......@@ -11,13 +11,13 @@
GBLS Module_HelpVersion
GBLS Module_ComponentName
GBLS Module_ComponentPath
Module_MajorVersion SETS "5.84"
Module_Version SETA 584
Module_MajorVersion SETS "5.85"
Module_Version SETA 585
Module_MinorVersion SETS ""
Module_Date SETS "03 Apr 2015"
Module_ApplicationDate SETS "03-Apr-15"
Module_ComponentName SETS "RISC_OSLib"
Module_ComponentPath SETS "castle/RiscOS/Sources/Lib/RISC_OSLib"
Module_FullVersion SETS "5.84"
Module_HelpVersion SETS "5.84 (03 Apr 2015)"
Module_FullVersion SETS "5.85"
Module_HelpVersion SETS "5.85 (03 Apr 2015)"
END
/* (5.84)
/* (5.85)
*
* This file is automatically maintained by srccommit, do not edit manually.
* Last processed by srccommit version: 1.1.
*
*/
#define Module_MajorVersion_CMHG 5.84
#define Module_MajorVersion_CMHG 5.85
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 03 Apr 2015
#define Module_MajorVersion "5.84"
#define Module_Version 584
#define Module_MajorVersion "5.85"
#define Module_Version 585
#define Module_MinorVersion ""
#define Module_Date "03 Apr 2015"
......@@ -18,6 +18,6 @@
#define Module_ComponentName "RISC_OSLib"
#define Module_ComponentPath "castle/RiscOS/Sources/Lib/RISC_OSLib"
#define Module_FullVersion "5.84"
#define Module_HelpVersion "5.84 (03 Apr 2015)"
#define Module_LibraryVersionInfo "5:84"
#define Module_FullVersion "5.85"
#define Module_HelpVersion "5.85 (03 Apr 2015)"
#define Module_LibraryVersionInfo "5:85"
......@@ -191,8 +191,6 @@ typedef struct txt1_window {
typedef struct txt1_str { /* the overall state object for a text */
int dud; /* >>>> for M2 compatibility */
/* The main character array */
char *buf; /* the character buffer */
txt1_bufindex top; /* no of chars in buffer - 1 */
......
......@@ -40,9 +40,6 @@ space. */
/* Some of these are not too Arthur-specific. The general format should be
followed by the matching interface for other systems. */
void txtar_movetoback(txt);
/* Functionality of the move-to-back mouse button. Gives away the cursor. */
void txtar_clone_current_window(txt t);
/* The current primary window on t is a txtar one. Clone it, using the
txt3 facilities, resulting in two windows. They should divide up between
......
......@@ -37,11 +37,11 @@ int txtfile_saverange(txt t, char *filename, typdat ty, txt_index from, txt_inde
/* Save the given range of text, using the given type/date stamp. */
#if BASIC
BOOL txtfile_dobasicimport(txtedit_state *s, int estsize, int detokenise, BOOL strip);
BOOL txtfile_basicimport(txtedit_state *s, BOOL insrep, int detokenise, BOOL strip);
/* import some BASIC into the txt. */
BOOL txtfile_basicinsert(txt t, char *filename, int l, typdat *ty, int detokenise, BOOL strip);
/* import a BASIC file into the txt. */
/* insert a BASIC file into the txt. */
BOOL txtfile_basicsenderproc(txt t, int *maxbuf, txt_index from, txt_index to, int tokenise, int increment);
/* export some BASIC from the txt. */
......
......@@ -14,7 +14,7 @@
*/
/* -> h.txtmisc
* Title: txtmisc.h
* Purpose: Search text for string.
* Purpose: Various facilities.
* Author: AFP
* Status: system-independent
* Requires:
......@@ -92,25 +92,6 @@ void txtmisc_selectpointandline(txt t, txt_index point, txt_index line);
void txtmisc_select3(txt t, txt_index a1, txt_index a2, txt_index a3);
/* Set the selection to contain all of a1-a3. */
/* -------- Operations on Selections -------- */
void txtmisc_clearselection(void);
/* Clear any selection. */
void txtmisc_deleteselection(int parawidth);
void txtmisc_moveselection(txt t, int parawidthsrc, int parawidthdst);
/* The selection is copied to the caret, then deleted at the source. The new
version is now the selection. t contains the Caret, a shared location is used
for the selection. */
void txtmisc_copyselection(txt t, int parawidth);
/* The selection is copied to the caret. The old version is still the
selection. */
int txtmisc_textinsertfromflexistore(txt t, flex_ptr anchor, unsigned n);
/* Return FALSE, with situation unchanged, if out of store. */
/* --------- Wordwrap ------------------------ */
#if WORDWRAP
......
......@@ -37,11 +37,18 @@ void txtundo_separate_major_edits(txt);
units that the user understands, rather than single moves, deletes and
inserts. Adjacent separators, without intermediate edits, will be ignored. */
void txtundo_prevent_undo(txt);
void txtundo_purge_undo(txt);
/* Prevent undoing from going back beyond this point. This is useful if
the text object has been edited by direct access to txt_arraysegment, or if
for any other reason going back beyond this point is not reasonable. */
BOOL txtundo_suspend_undo(txt, BOOL);
/* Temporarily suspend (or resume) puts into the undo buffer. This is useful if
lots of edits are being implicitly being made by calling txt's internal routines
to achieve some overall much more simply expressed edit. The more simple
version can then be inserted manually rather than polluting the undo buffer
with all the intermediate steps. */
typedef enum {
txtundo_MINOR, /* successfully reversed a move/copy/delete */
txtundo_MAJOR, /* encountered a major edit separator */
......@@ -53,12 +60,12 @@ void txtundo_init(txt);
/* Prepare to start a sequence of undo operations. */
txtundo_result txtundo_undo(txt);
/* undo the last insert, move or delete operation. If successful the inverse
operation will be invoked on the text, further adding to the undo buffer. A
pointer to "current position" within the undo buffer is maintained, so that
subsequent calls to undo go further back into history. The pointer is reset
by a call to txtundo_init. No other edits to the text should occur between a
txtundo_init and subsequent undo/redo calls. */
/* undo the last insert, delete, select or move operation. If successful the
inverse operation will be invoked on the text, further adding to the undo
buffer. A pointer to "current position" within the undo buffer is maintained,
so that subsequent calls to undo go further back into history. The pointer is
reset by a call to txtundo_init. No other edits to the text should occur
between a txtundo_init and subsequent undo/redo calls. */
txtundo_result txtundo_redo(txt);
/* Redo an operation caused by undo. The result concerns the operation that
......@@ -85,8 +92,7 @@ typedef struct txtundo__state *txtundo; /* abstract undo buffer state. */
txtundo txtundo_new(void); /* with small undo buffer. Will return
0 if not enough store. */
void txtundo_dispose(txtundo*);
/* >>>> The extra * is for M2-compatibility: it will change. */
void txtundo_dispose(txtundo);
void txtundo_putbytes(txtundo, char*, int);
void txtundo_putnumber(txtundo, int);
......@@ -106,6 +112,8 @@ void txtundo_putcode(txtundo, char);
putnumber index-of-old-start-of-selection
putnumber index-of-old-end-of-selection
putcode 'l'
when toggling the case of the selection:
putcode 't'
This can be done without regard for overflow etc.
Calling txtundo_putcode when not nested within any undo op on that
......
......@@ -122,7 +122,7 @@ void txt_dispose(txt *t)
if ((*t)->eventnest == 0) {
txt1_disposetextbuffer(*t);
txt3_disposeallwindows(*t);
txtundo_dispose(&((*t)->undostate));
txtundo_dispose((*t)->undostate);
free(*t);
} else {
(*t)->disposepending = TRUE;
......
......@@ -1961,7 +1961,6 @@ static void txtar__performredraw(txt t)
#if FALSE
static msgref = 1;
#endif
void txtar_movetoback(txt t)
{
......@@ -1973,6 +1972,7 @@ void txtar_movetoback(txt t)
wimpt_noerr(wimp_open_wind(&state.o));
win_give_away_caret();
}
#endif
#define DELAY_BEFORE_HOURGLASS 200
......@@ -2097,15 +2097,18 @@ static void txtar__textwimpevent(wimp_eventstr *e, void *handle)
case wimp_ESEND:
case wimp_ESENDWANTACK:
{
char *name; /* dud */
int estsize; /* dud */
if (xferrecv_checkinsert(&name) != -1
|| xferrecv_checkimport(&estsize) != -1) {
txtar__docharevent(t, txt_EXTRACODE + akbd_Sh + akbd_Fn + 2);
} else if (e->data.msg.hdr.action == wimp_MHELPREQUEST) {
txtar__docharevent(t, txt_EXTRACODE + akbd_Fn + 1);
} else {
tracef0("strange wimp message arrived at txtar, ignored.\n");
switch (e->data.msg.hdr.action) {
case wimp_MDATASAVE:
case wimp_MDATALOAD:
case wimp_MDATAOPEN:
txtar__docharevent(t, txt_EXTRACODE + akbd_Fn + akbd_Sh + 2);
break;
case wimp_MHELPREQUEST:
txtar__docharevent(t, txt_EXTRACODE + akbd_Fn + 1);
break;
default:
tracef0("strange wimp message arrived at txtar, ignored.\n");
break;
};
};
break;
......@@ -2268,7 +2271,9 @@ static void txtar__doopenevent(txt t, wimp_openstr *o /*in*/)
txtar__sysdata *s = (txtar__sysdata*) t->w->syshandle;
BOOL sizechange;
BOOL move;
#if FALSE
BOOL wasfull = (s->d->info.flags & wimp_WFULL) != 0;
#endif
tracef4("open event: to (%i,%i,%i,%i).\n",
o->box.x0, o->box.y0, o->box.x1, o->box.y1);
......@@ -2748,7 +2753,7 @@ BOOL txtar_initwindow(txt t, char *title)
er = wimp_create_wind(&s->d->info, &s->w);
if (er != 0) {
tracef0("create window failed.\n");
werr(FALSE, &er->errmess[0]);
werr(FALSE, er->errmess);
free(s->d);
free(s);
return FALSE;
......
This diff is collapsed.
......@@ -46,8 +46,11 @@
#include "flex.h"
#include "swis.h"
#include "wimpt.h"
#include "txt.h"
#include "EditIntern/txtundo.h"
#include "EditIntern/txt1.h"
#include "txtedit.h"
#include "txtscrap.h"
#include "xfersend.h"
#include "xferrecv.h"
#endif
......@@ -80,7 +83,7 @@ else
}
int txtfile_insert(txt t, char *filename, int l, typdat *ty)
BOOL txtfile_insert(txt t, char *filename, int l, typdat *ty)
/* The insert operation works in two phases. First, find out the size of the
file and insert that much junk into it. Then, actually read the contents of
the file directly into the text object memory. It is important to take this
......@@ -112,7 +115,7 @@ if (l < 0) {
er = os_file(&file);
tracef0("the os_file 5 returned.\n");
if (er != 0) { /* read info on pathname */
werr(FALSE, &er->errmess[0]);
werr(FALSE, er->errmess);
return(0);
};
......@@ -180,10 +183,10 @@ if (l < 0) {
er = os_file(&file);
if (er != 0) { /* load into buffer */
werr(FALSE, &er->errmess[0]);
werr(FALSE, er->errmess);
txt_delete(t, txt_size(t) - size);
if (! wasupdated) txt_setcharoptions(t, txt_UPDATED, 0);
result = 0;
result = FALSE;
} else {
ty->ld = file.loadaddr; /* return type to client */
ty->ex = file.execaddr;
......@@ -241,7 +244,7 @@ else
file.end = (int) &a[n];
er = os_file(&file);
if (er != 0) {
werr(FALSE, &er->errmess[0]);
werr(FALSE, er->errmess);
};
if (oldattribs != -1) {
......@@ -385,41 +388,64 @@ static BOOL txtfile__basicimportbufferprocessor(char **buffer, int *size)
return(result);
}
BOOL txtfile_dobasicimport(txtedit_state *s, int estsize, int detokenise, BOOL strip)
BOOL txtfile_basicimport(txtedit_state *s, BOOL insrep, int detokenise, BOOL strip)
{
/* Plan of action is:
1) import everything into a flex buffer
2) detokenise and insert!
*/
int last;
BOOL result;
BOOL result, undo;
txt_index dot;
int size, tsize;
char *import_buffer = malloc(4096); /* do imports in 4K chunks */
estsize = estsize;
if (import_buffer == NULL) {
werr(FALSE, msgs_lookup(MSGS_txt3));
return(FALSE);
}
undo = txtundo_suspend_undo(s->t, TRUE);
tsize = txt_size(s->t);
dot = txt_dot(s->t);
receiver_anchor = NULL; /* no imported data yet */
receiver_size = 0;
if (import_buffer) {
last = xferrecv_doimport(import_buffer, 4096, txtfile__basicimportbufferprocessor);
if (last == -1) {
result = FALSE;
last = xferrecv_doimport(import_buffer, 4096, txtfile__basicimportbufferprocessor);
if (last == -1) {
result = FALSE;
} else {
if (txtfile__basicimportbufferprocessor(&import_buffer, &last)) {
txtfile__basicimportdata(s->t, &receiver_anchor, receiver_size, detokenise, strip);
result = TRUE;
} else result = FALSE;
}
free(import_buffer);
if (receiver_anchor != NULL)
flex_free((flex_ptr)&receiver_anchor);
if (result) {
size = txt_size(s->t) - tsize; /* Delta */
txt_setdot(s->t, dot);
txtundo_suspend_undo(s->t, undo);
txtundo_putnumber(s->t->undostate, size);
txtundo_putcode(s->t->undostate, 'd');
if (size == 0) {
/* Nothing happened overall */
} else {
if (txtfile__basicimportbufferprocessor(&import_buffer, &last)) {
txtfile__basicimportdata(s->t, &receiver_anchor, receiver_size, detokenise, strip);
result = TRUE;
} else result = FALSE;
/* When insertion replaced a selection, must select the new
insertion in its place. */
if (insrep) {
txtscrap_setselect(s->t, dot, dot + size);
} else {
txt_movedot(s->t, size);
txt_show(s->t); /* Force a redraw so caret x/y is recalculated */
}
}
free(import_buffer);
if (receiver_anchor != NULL)
flex_free((flex_ptr)&receiver_anchor);
txtedit_settexttitle(s);
txtundo_separate_major_edits(s->t);
return(result);
} else {
werr(FALSE, msgs_lookup(MSGS_txt3));
return(FALSE);
}
txtedit_settexttitle(s);
txtundo_separate_major_edits(s->t);
return(result);
}
BOOL txtfile_basicinsert(txt t, char *filename, int l, typdat *ty, int detokenise, BOOL strip)
......@@ -447,7 +473,7 @@ BOOL txtfile_basicinsert(txt t, char *filename, int l, typdat *ty, int detokenis
er = os_file(&file);
if (er != 0) {
werr(FALSE, &er->errmess[0]);
werr(FALSE, er->errmess);
result = FALSE;
} else {
result = txtfile__basicimportdata(t, &anchor, file.start, detokenise, strip);
......@@ -644,7 +670,7 @@ BOOL txtfile_savebasicrange(txt t, char *filename, typdat ty, txt_index from, tx
file.end = (int)sender_anchor + end_ptr;
er = os_file(&file);
if (er != 0) {
werr(FALSE, &er->errmess[0]);
werr(FALSE, er->errmess);
}
result = er == 0;
flex_free((flex_ptr)&sender_anchor);
......
......@@ -460,7 +460,7 @@ while (at != size)
}
txt_setcharoptions(t, txt_DISPLAY, txt_DISPLAY);
if (updated) txt_setcharoptions(t, txt_UPDATED, txt_UPDATED);
txtundo_prevent_undo(t);
txtundo_purge_undo(t);
}
......@@ -664,15 +664,6 @@ txtmisc_select3(t, point, begin, end);
}
void txtmisc_clearselection(void)
/* Clear any selection. */
{
txtscrap_setselect(NULL, 0, 0);
}
#if EDIT2
/* -------- Wordwrap -------- */
......@@ -683,192 +674,6 @@ void txtmisc_normalisepara(
);
#endif
/* -------- Operations on Selections -------- */
void txtmisc_deleteselection(int parawidth)
{
txt_marker m;
txt owner;
owner = txtscrap_selectowner();
if (owner != NULL && txt_selectset(owner))
{
txt_newmarker(owner, &m);
txt_setdot(owner, txt_selectstart(owner));
txt_delete(owner, txt_selectend(owner) - txt_selectstart(owner));
txtmisc_normalisepara(owner, parawidth);
txt_movedottomarker(owner, &m);
txt_disposemarker(owner, &m);
txtscrap_setselect(owner, 0, 0);
}
}
#define BIG 256
static void movecopyselection(txt t, int deletesource, int parawidthsrc, int parawidthdst)
/* Two different techniques are used for small and for big operations. Small
ones are quicker to repaint. Bigger ones use FlexiStore as the transitory
space, this is tricky because inserting from FlexiStore might cause the
source to move under your feet if the object expands. */
{
unsigned size;
int n, stored, segsize;
txt owner;
char *a;
void *scrap;
owner = txtscrap_selectowner();
if (owner != NULL)
{
/* determine size */
size = txt_selectend(owner) - txt_selectstart(owner);
if (t == owner && deletesource &&
txt_dot(t) >= txt_selectstart(t) && txt_dot(t) < txt_selectend(t))
/* move with caret in the middle of select, do nothing */
txt_setdot(t, txt_selectend(t));
else
{
/* get temporary space */
stored = TRUE;
if (size <= BIG) {
scrap = malloc(BIG);
if (scrap == 0) {
werr(FALSE, msgs_lookup(MSGS_txt48));
return;
};
} else
if (!flex_alloc(&scrap, size))
{
werr(NULL, msgs_lookup(MSGS_txt48));
stored = FALSE;
};
tracef2("\nRequired size is %d, stored = %d.\n", size, stored);
if (stored)
{
/* Copy the selection into the temporary store */
n = 0;
while (n != size)
{
txt_arrayseg(owner, txt_selectstart(owner) + n, &a, &segsize);
segsize = (segsize < size - n) ? segsize : size - n;
memcpy((char *)scrap + n, a, segsize);
n += segsize;
}
/* Delete the source. */
if (deletesource)
txtmisc_deleteselection(parawidthsrc);
/* >>>> dangerous? If t=owner, definitely a good move. */
{
int prevsize = txt_size(t);
/* Insert the selection into the target */
if (size <= BIG)
{
txt_replacechars(t, 0, scrap, size);
tracef0("small MoveCopy.\n");
}
else
{
tracef0("large MoveCopy.\n");
txtmisc_textinsertfromflexistore(t, &scrap, size);
}
if (txt_size(t) == prevsize + size) {
/* managed to insert: not out of space. */
/* The result becomes the selection */
if (deletesource)
txtscrap_setselect(t, txt_dot(t), txt_dot(t) + size);
txtmisc_normalisepara(t, parawidthdst);
/* NB we're relying on this not changing the size of what we've
inserted: otherwise, moving past the insertion needs a marker. */
/* Move past the result. */
txt_movedot(t, size);
};
};
/* deallocate temporary space */
if (size <= BIG)
free(scrap);
else
flex_free(&scrap);
}
}
}
}
void txtmisc_moveselection(txt t, int parawidthsrc, int parawidthdst)
/* The selection is copied to the caret, then deleted at the source.
The new version is now the selection. */
{
movecopyselection(t, 1, parawidthsrc, parawidthdst);
}
void txtmisc_copyselection(txt t, int parawidth)
/* The selection is copied to the caret. The new version is now the
selection. */
{
movecopyselection(t, NULL, parawidth, parawidth);
}
int txtmisc_textinsertfromflexistore(txt t, flex_ptr anchor, unsigned n)
/* If we just did an insert and this caused an expand and we got moved, the
text would be taken from the wrong place. So, we must turn off display for
this one.
Return FALSE, with situation unchanged, if out of store. */
{
void *prevad;
int prevsize;
prevad = *anchor;
prevsize = txt_size(t);
txt_setcharoptions(t, txt_DISPLAY, 0);
txt_replacechars(t, 0, *anchor, n);
if (txt_size(t) != n + prevsize) {
tracef0("not enough space.\n");
txt_delete(t, txt_size(t) - prevsize);
txt_setcharoptions(t, txt_DISPLAY, txt_DISPLAY);
/* werr(FALSE, "Not enough space."); */
/* 21-Nov-88 WRS: Failure will already have produced an error message. */
return FALSE;
};
if (*anchor != prevad)
{
tracef0("Replace caused movement.\n");
txt_replacechars(t, n, *anchor, n);
}
txt_setcharoptions(t, txt_DISPLAY, txt_DISPLAY);
return(1);
}
/* >>>> do out of store stuff. */
/* -------- Wordwrap. -------- */
#if WORDWRAP
......
......@@ -49,6 +49,7 @@ if (from == to || t == (txt) NULL)
else
{
txt_setselect(owner, 0, 0);
txt_setcharoptions(owner, txt_CARET, txt_CARET);
owner