quakeforge/ruamoko/qwaq/editor/editor.r

675 lines
14 KiB
R
Raw Normal View History

#include <QF/keys.h>
#include <string.h>
#include "ruamoko/qwaq/qwaq-app.h"
#include "ruamoko/qwaq/editor/editor.h"
#include "ruamoko/qwaq/ui/listener.h"
#include "ruamoko/qwaq/ui/scrollbar.h"
@implementation Editor
static int
center (unsigned v, int len)
{
return v > len / 2 ? v / 2 : 0;
}
static void
trackCursor (Editor *self)
{
unsigned cx = [self.buffer charPos:self.line_index at:self.char_index];
if (self.cursor.x != cx) {
if (self.char_index < [self.buffer getEOT]) {
int c = [self.buffer getChar:self.char_index];
}
}
}
-initWithRect:(Rect) rect file:(string) filename path:(string) filepath
{
if (!(self = [super initWithRect: rect])) {
return nil;
}
self.filename = str_hold (filename);
if (filepath != filename) {
self.filepath = str_hold (filepath);
} else {
self.filepath = filename;
}
buffer = [[EditBuffer withFile:filepath] retain];
line_count = [buffer countLines: {0, [buffer textSize]}];
linebuffer = [[DrawBuffer buffer: { xlen, 1 }] retain];
growMode = gfGrowHi;
options = ofCanFocus | ofRelativeEvents;
[onViewScrolled addListener:self :@selector(onScroll:)];
[self setCursorVisible:1];
return self;
}
+(Editor *)withRect:(Rect)rect file:(string)filename
{
return [[[self alloc] initWithRect:rect
file:filename
path:filename] autorelease];
}
+(Editor *)withRect:(Rect)rect file:(string)filename path:(string)filepath
{
return [[[self alloc] initWithRect:rect
file:filename
path:filepath] autorelease];
}
-(void)dealloc
{
if (filepath != filename) {
str_free (filepath);
}
str_free (filename);
[vScrollBar release];
[buffer release];
[linebuffer release];
[super dealloc];
}
-(string)filename
{
return filename;
}
-(string)filepath
{
return filepath;
}
-(Point)cursor
{
return cursor;
}
-setStatusView:(EditStatus *)status
{
self.status = status;
[status setModified:modified];
[status setCursorMode:cursorMode];
[status redraw];
return self;
}
-trackCursor:(int)fwd
{
unsigned tx = [buffer charPos:line_index at:char_index];
cursorMode &= ~cmVInsert;
if (tx != cursor.x) {
if (char_index < [buffer getEOT]) {
int c = [buffer getChar:char_index];
if (virtualInsert) {
if (c != '\t' || cursorThroughTabs) {
cursorMode |= cmVInsert;
goto done;
}
}
if (c == '\t' && fwd) {
tx = [buffer charPos:line_index at:++char_index];
}
} else if (virtualInsert) {
cursorMode |= cmVInsert;
goto done;
}
cursor.x = tx;
}
done:
[status setCursorMode:cursorMode];
[status redraw];
return self;
}
-draw
{
[super draw];
unsigned lind = base_index;
int *lbuf = [linebuffer buffer];
for (int y = 0; y < ylen; y++) {
lind = [buffer formatLine:lind from:base.x into:lbuf width:xlen
highlight:selection colors: {color_palette[047],
color_palette[007]}];
[textContext blitFromBuffer: linebuffer to: {xpos, ypos + y}
from: [linebuffer rect]];
}
return self;
}
-resize: (Extent) delta
{
[super resize: delta];
[linebuffer resizeTo: {xlen, 1}];
return self;
}
static int
handleEvent (Editor *self, qwaq_event_t *event)
{
if (event.what & qe_mouse) {
if (event.what == qe_mouseclick) {
if (event.mouse.buttons & (1 << 3)) {
[self scrollUp: 1];
return 1;
}
if (event.mouse.buttons & (1 << 4)) {
[self scrollDown: 1];
return 1;
}
if (event.mouse.buttons & (1 << 5)) {
[self scrollLeft: 1];
return 1;
}
if (event.mouse.buttons & (1 << 6)) {
[self scrollRight: 1];
return 1;
}
}
} else if (event.what == qe_keydown) {
switch (event.key.code) {
case QFK_PAGEUP:
if (event.key.shift & qe_control) {
[self moveBOT];
} else {
[self pageUp];
}
return 1;
case QFK_PAGEDOWN:
if (event.key.shift & qe_control) {
[self moveEOT];
} else {
[self pageDown];
}
return 1;
case QFK_UP:
if (event.key.shift & qe_control) {
[self linesUp];
} else {
[self charUp];
}
return 1;
case QFK_DOWN:
if (event.key.shift & qe_control) {
[self linesDown];
} else {
[self charDown];
}
return 1;
case QFK_LEFT:
if (event.key.shift & qe_control) {
[self wordLeft];
} else {
[self charLeft];
}
return 1;
case QFK_RIGHT:
if (event.key.shift & qe_control) {
[self wordRight];
} else {
[self charRight];
}
return 1;
case QFK_HOME:
if (event.key.shift & qe_control) {
[self moveBOS];
} else {
[self moveBOL];
}
return 1;
case QFK_END:
if (event.key.shift & qe_control) {
[self moveEOS];
} else {
[self moveEOL];
}
return 1;
}
}
return 0;
}
-handleEvent:(qwaq_event_t *) event
{
[super handleEvent: event];
if (handleEvent (self, event)) {
event.what = qe_none;
}
return self;
}
-scrollUp:(unsigned) count
{
unsigned index;
unsigned lines;
if (count == 1) {
index = [buffer prevLine: base_index];
} else {
index = [buffer prevLine: base_index :count];
}
lines = [buffer countLines: {index, base_index - index}];
base.y -= lines;
base_index = index;
[self redraw];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-scrollDown:(unsigned) count
{
unsigned index;
unsigned lines;
if (count == 1) {
index = [buffer nextLine: base_index];
} else {
index = [buffer nextLine: base_index :count];
}
lines = [buffer countLines: {base_index, index - base_index}];
base.y += lines;
base_index = index;
[self redraw];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-scrollLeft:(unsigned) count
{
if (base.x > count) {
base.x -= count;
} else {
base.x = 0;
}
[self redraw];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-scrollRight:(unsigned) count
{
if (1024 - base.x > count) {
base.x += count;
} else {
base.x = 1024;
}
[self redraw];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-scrollTo:(unsigned)target
{
if (target > base.y) {
base_index = [buffer nextLine:base_index :target - base.y];
} else if (target < base.y) {
base_index = [buffer prevLine:base_index :base.y - target];
}
base.y = target;
[vScrollBar setIndex:base.y];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-pageUp
{
[self recenter:0];
unsigned count = cursor.y;
if (count > ylen) {
count = ylen;
}
if (count) {
cursor.y -= count;
[vScrollBar setIndex:[vScrollBar index] - count];
line_index = [buffer prevLine:line_index :count];
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
}
return self;
}
-pageDown
{
[self recenter:0];
unsigned count = line_count - cursor.y;
if (count > ylen) {
count = ylen;
}
if (count) {
cursor.y += count;
[vScrollBar setIndex:[vScrollBar index] + count];
line_index = [buffer nextLine:line_index :count];
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
}
return self;
}
-linesUp
{
while (line_index > 0) {
line_index = [buffer prevLine: line_index];
cursor.y--;
unsigned p = [buffer nextNonSpace: line_index];
if ([buffer getChar:p] == '\n') {
continue;
}
int x = [buffer charPos:line_index at:p];
if (x <= cursor.x) {
break;
}
}
char_index = [buffer charPtr:line_index at:cursor.x];
[self recenter:0];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-linesDown
{
unsigned end = [buffer getEOT];
unsigned last = [buffer getBOL:end];
while (line_index < last) {
line_index = [buffer nextLine: line_index];
cursor.y++;
unsigned p = [buffer nextNonSpace: line_index];
if (p >= end || [buffer getChar:p] == '\n') {
continue;
}
int x = [buffer charPos:line_index at:p];
if (x <= cursor.x) {
break;
}
}
char_index = [buffer charPtr:line_index at:cursor.x];
[self recenter:0];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-charUp
{
[self recenter:0];
if (cursor.y < 1) {
return self;
}
cursor.y--;
line_index = [buffer prevLine:line_index :1];
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
if (base.y > cursor.y) {
[vScrollBar setIndex:cursor.y];
}
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-charDown
{
[self recenter:0];
if (cursor.y >= line_count) {
return self;
}
cursor.y++;
line_index = [buffer nextLine:line_index :1];
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
if (base.y + ylen - 1 < cursor.y) {
[vScrollBar setIndex:cursor.y + 1 - ylen];
}
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-charLeft
{
[self recenter:0];
if (cursor.x < 1) {
return self;
}
cursor.x--;
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:0];
if (base.x > cursor.x) {
[hScrollBar setIndex:cursor.x];
}
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-charRight
{
[self recenter:0];
// FIXME handle horizontal scrolling and how to deal with max scroll
cursor.x++;
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
if (base.x + xlen - 1 < cursor.x) {
[hScrollBar setIndex:cursor.x + 1 - xlen];
}
if (base.x + xlen - 1 < cursor.x) {
cursor.x = base.x + xlen - 1;
}
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-wordLeft
{
[self recenter:0];
//unsigned oc = char_index;
Point b = base;
do {
if (char_index && char_index == line_index) {
line_index = [buffer prevLine: line_index];
char_index = [buffer getEOL: line_index];
if (--cursor.y < b.y) {
b.y = cursor.y;
}
}
char_index = [buffer prevWord:char_index];
} while (char_index && char_index < [buffer getEOT]
&& ![buffer isWord:char_index]);
cursor.x = [buffer charPos:line_index at:char_index];
if (cursor.x < b.x) {
b.x = cursor.x;
} else if (cursor.x >= b.x) {
b.x = center (b.x, xlen);
}
base.x = b.x;
[vScrollBar setIndex:b.y];
trackCursor (self);
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-wordRight
{
[self recenter:0];
//unsigned oc = char_index;
Point b = base;
if (char_index >= [buffer getEOT]) {
return self;
}
if ([buffer getChar:char_index] == '\n') {
while ([buffer getChar:char_index] == '\n') {
char_index = line_index = [buffer nextLine:line_index];
if (++cursor.y >= b.y + ylen) {
b.y = cursor.y + 1 - ylen;
}
if (char_index >= [buffer getEOT]) {
break;
}
if (![buffer isWord:char_index]) {
char_index = [buffer nextWord: char_index];
}
}
} else {
char_index = [buffer nextWord: char_index];
}
cursor.x = [buffer charPos:line_index at:char_index];
if (cursor.x < b.x) {
b.x = cursor.x;
} else if (cursor.x >= b.x) {
b.x = center (b.x, xlen);
}
base.x = b.x;
[vScrollBar setIndex:b.y];
trackCursor (self);
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-moveBOT
{
line_index = base_index = char_index = 0;;
cursor = nil;
base.x = 0;
[vScrollBar setIndex:0];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-moveEOT
{
char_index = [buffer getEOT];
line_index = [buffer getBOL:char_index];
cursor.x = [buffer charPos:line_index at:char_index];
cursor.y = line_count;
[self recenter:0];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-moveBOS
{
line_index = base_index;
cursor.y = base.y;
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-moveEOS
{
unsigned count = line_count - base.y;
if (count > ylen - 1) {
count = ylen - 1;
}
line_index = [buffer nextLine:base_index :count];
cursor.y = base.y + count;
char_index = [buffer charPtr:line_index at:cursor.x];
[self trackCursor:1];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-moveBOL
{
char_index = line_index;
cursor.x = 0;
base.x = 0;
[self recenter:0];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-moveEOL
{
char_index = [buffer getEOL:line_index];
cursor.x = [buffer charPos:line_index at:char_index];
[self recenter:0];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
return self;
}
-(void)onScroll:(id)sender
{
base.x = scroll.x;
if (base.y != scroll.y) {
[self scrollTo:scroll.y];
}
}
-setVerticalScrollBar:(ScrollBar *)scrollbar
{
[super setVerticalScrollBar:scrollbar];
[vScrollBar setRange:line_count];
[vScrollBar setPageStep: ylen];
return self;
}
2020-03-24 23:28:12 +00:00
-recenter:(int) force
{
if (force || cursor.y < base.y || cursor.y - base.y >= ylen) {
[self scrollTo: center (cursor.y, ylen)];
2020-03-24 23:28:12 +00:00
}
return self;
}
-gotoLine:(unsigned) line
{
if (line > cursor.y) {
line_index = [buffer nextLine:line_index :line - cursor.y];
} else if (line < cursor.y) {
line_index = [buffer prevLine:line_index :cursor.y - line];
}
cursor.y = line;
char_index = [buffer charPtr:line_index at:cursor.x];
[self recenter:0];
[self moveCursor: {cursor.x - base.x, cursor.y - base.y}];
2020-03-24 23:28:12 +00:00
return self;
}
-highlightLine
{
selection.start = line_index;
selection.length = [buffer nextLine: line_index] - line_index;
if (!selection.length) {
selection.length = [buffer getEOL: line_index] - line_index;
}
return self;
}
-(string)getWordAt:(Point) pos
{
if (pos.x < 0 || pos.y < 0 || pos.x >= xlen || pos.y >= ylen) {
return nil;
}
pos.x += base.x;
unsigned lind = [buffer nextLine:base_index :pos.y];
unsigned cind = [buffer charPtr:lind at:pos.x];
eb_sel_t word = [buffer getWord: cind];
return [buffer readString:word];
}
@end