|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T
Length: 26768 (0x6890)
Types: TextFile
Names: »TextView.c«
└─⟦8648bda34⟧ Bits:30007244 EUUGD5_II: X11R5
└─⟦87c3ac0e0⟧ »./contrib-3/contrib-3.00«
└─⟦de8ce1454⟧
└─⟦this⟧ »contrib/lib/iv/src/bin/doc/TextView.c«
/*
* Copyright (c) 1991 Stanford University
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Stanford not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Stanford makes no representations about
* the suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* TextView
*/
#include "TextView.h"
#include "Application.h"
#include "Document.h"
#include "DocViewer.h"
#include "TextItem.h"
#include "codes.h"
#include "properties.h"
#include <InterViews/event.h>
#include <InterViews/world.h>
#include <OS/math.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <strstream.h>
#include <fstream.h>
class LigatureInfo {
public:
long _character;
long _preceding;
long _ligature;
};
LigatureInfo ligatures[] = {
{wordspace, wordspace, sentencespace},
{wordspace, sentencespace, sentencespace},
{wordspace, parbreak, parbreak},
{wordspace, linebreak, linebreak},
{wordspace, quadspace, quadspace},
{linebreak, wordspace, linebreak},
{linebreak, sentencespace, linebreak},
{linebreak, linebreak, linebreak},
{parbreak, wordspace, parbreak},
{parbreak, sentencespace, parbreak},
{parbreak, parbreak, parbreak},
{visiblehyphen, visiblehyphen, endash},
{visiblehyphen, endash, emdash},
{visiblehyphen, emdash, emdash},
{smallskip, wordspace, smallskip},
{smallskip, parbreak, smallskip},
{smallskip, smallskip, medskip},
{smallskip, medskip, bigskip},
{smallskip, bigskip, bigskip},
{hfil, hfil, hfill},
{hfil, hfill, hfill},
{'`', '`', 0252},
{'`', 0252, 0252},
{'\'', '\'', 0272},
{'\'', 0272, 0272},
{'.', '.', 0267},
{'.', 0267, 0267},
/*
{'i', 'f', 0256},
{'l', 'f', 0257},
*/
{0, 0, 0}
};
TextView::TextView (
DocumentViewer* viewer, ItemView* parent, TextItem* text
) : ItemView(viewer, parent) {
_text = text;
_text->ref();
_text->attach(this);
_active = false;
_dot = 0;
_mark = 0;
_style = text->style();
_source = text->source();
_menubar = nil;
_encoded_keymap = nil;
_verbatim_keymap = nil;
}
TextView::~TextView () {
delete _encoded_keymap;
delete _verbatim_keymap;
delete _menubar;
if (_active) {
_viewer->focus(nil);
}
_text->detach(this);
_text->unref();
}
void TextView::repair () {
_text->notify();
}
void TextView::update () { }
void TextView::item_changed (long, long) { }
void TextView::item_replaced (long, long) { }
void TextView::item_inserted (long index, long count) {
if (index < _dot) {
_dot += count;
}
if (index < _mark) {
_mark += count;
}
}
void TextView::item_removed (long index, long count) {
if (index + count < _dot) {
_dot -= count;
} else if (index < _dot) {
_dot = index;
}
if (index + count < _mark) {
_mark -= count;
} else if (index < _mark) {
_mark = index;
}
}
long TextView::dot () {
return _dot;
}
void TextView::dot (long dot) {
if (dot >= 0L) {
_dot = Math::min(Math::max(dot, 0L), _text->item_count());
_mark = _dot;
}
}
long TextView::mark () {
return _mark;
}
void TextView::mark (long mark) {
if (mark >= 0L) {
_mark = Math::min(Math::max(mark, 0L), _text->item_count());
}
}
void TextView::mark_selection () { }
boolean TextView::safe_to_edit (long d, long m) {
Document* document = _text->document();
if (d == m) {
return document->text_source(_source)._editable;
} else {
TextSource& outer = document->text_source(
document->common_source(
_text->item_source(d-1), _text->item_source(m)
)
);
TextSource& inner = document->text_source(
document->common_source(
_text->item_source(d), _text->item_source(m-1)
)
);
TextSource& begin = document->text_source(
document->common_source(
_text->item_source(d-1), _text->item_source(d)
)
);
TextSource& end = document->text_source(
document->common_source(
_text->item_source(m-1), _text->item_source(m)
)
);
return (
inner._depth >= begin._depth
&& inner._depth >= end._depth
&& (
begin._depth == end._depth
? outer._editable||inner._editable : inner._editable
)
);
}
}
boolean TextView::insert_text (long character) {
long d = Math::min(_dot, _mark);
long m = Math::max(_dot, _mark);
if (!safe_to_edit(d, m)) {
_viewer->application()->complain(_viewer, "Can't edit mixed text");
return false;
} else {
if (d != m) {
dot(_text->remove(d, m - d));
}
if (character != 0 && _dot > 0 && safe_to_edit(_dot - 1, _dot)) {
for (long i = 0; ligatures[i]._character != 0; ++i) {
LigatureInfo& l = ligatures[i];
if (
l._character == character
&& l._preceding == _text->item_code(_dot - 1)
) {
dot(_text->remove(_dot - 1, 1));
dot(_text->insert(_dot, l._ligature,_style,_source,nil));
return true;
}
}
}
if (character != 0) {
dot(_text->insert(_dot, character, _style, _source, nil));
}
return true;
}
}
static boolean interword_code (long code) {
int int_code = int(code);
return (
isascii(int_code) && (isspace(int_code) || ispunct(int_code))
|| code == hfil
|| code == vfil
|| code == hrule
|| code == smallskip
|| code == medskip
|| code == bigskip
|| code == smallvspace
|| code == medvspace
|| code == bigvspace
|| code == newpage
|| code == pagebreak
|| code == parbreak
|| code == linebreak
|| code == nobreakspace
|| code == wordspace
|| code == sentencespace
|| code == quadspace
|| code == endash
|| code == emdash
);
}
static boolean interpar_code (long code) {
return (
code == vfil
|| code == hrule
|| code == smallskip
|| code == medskip
|| code == bigskip
|| code == smallvspace
|| code == medvspace
|| code == bigvspace
|| code == newpage
|| code == pagebreak
|| code == parbreak
);
}
boolean TextView::command (const char* command) {
Document* document = _text->document();
if (strncmp(command, "control", 7) == 0) {
return false;
} else if (strncmp(command, "select", 6) == 0) {
long d = Math::min(_dot, _mark);
long m = Math::max(_dot, _mark);
long count = _text->item_count();
if (strcmp(command+7, "all") == 0) {
d = 0;
m = count;
} else if (strcmp(command+7, "word") == 0) {
while (d > 0 && interword_code(_text->item_code(d - 1))) { --d; }
while (d > 0 && !interword_code(_text->item_code(d - 1))) { --d; }
while (m < count && !interword_code(_text->item_code(m))) { ++m; }
} else if (strcmp(command+7, "paragraph") == 0) {
while (d > 0 && interpar_code(_text->item_code(d - 1))) { --d; }
while (d > 0 && !interpar_code(_text->item_code(d - 1))) { --d; }
while (m < count && !interpar_code(_text->item_code(m))) { ++m; }
}
dot(d);
mark(m);
mark_selection();
selection_changed(true);
return false;
} else if (strncmp(command, "delete", 6) == 0) {
if (strcmp(command+7, "following") == 0) {
if (_dot == _mark) mark(_dot + 1);
insert_text(0);
} else if (strcmp(command+7, "preceding") == 0) {
if (_dot == _mark) mark(_dot - 1);
insert_text(0);
}
return true;
} else if (strncmp(command, "go", 2) == 0) {
long d = Math::min(_dot, _mark);
long m = Math::max(_dot, _mark);
if (strcmp(command+3, "forward_character") == 0) {
dot(m + ((d == m) ? 1 : 0));
} else if (strcmp(command+3, "backward_character") == 0) {
dot(d - ((d == m) ? 1 : 0));
} else if (strcmp(command+3, "forward_paragraph") == 0) {
long count = _text->item_count();
d = m + 1;
while (d < count && !interpar_code(_text->item_code(d))) { ++d; }
dot(d);
} else if (strcmp(command+3, "backward_paragraph") == 0) {
d = d - 1;
while (d > 0 && !interpar_code(_text->item_code(d - 1))) { --d; }
dot(d);
} else if (strcmp(command+3, "beginning") == 0) {
dot(0);
} else if (strcmp(command+3, "end") == 0) {
dot(_text->item_count());
}
mark_selection();
selection_changed(true);
return true;
} else if (strncmp(command, "font", 4) == 0) {
long dot = Math::min(_dot, _mark);
long mark = Math::max(_dot, _mark);
if (!safe_to_edit(dot, mark)) {
_viewer->application()->complain(_viewer, "Can't edit mixed text");
return false;
} else {
const char* font;
if (strlen(command) == 4) {
font = _viewer->application()->ask(
_viewer, "Font name:", ""
);
} else {
font = command + 5;
}
if (font == nil) {
return false;
} else {
char style[100];
sprintf(style, "\\font{%s}", font);
long old_style = 0;
long new_style = 0;
do {
if (_text->item_style(dot) != old_style) {
old_style = _text->item_style(dot);
new_style = document->parse_style(
style, old_style
);
}
if (dot < mark && new_style != old_style) {
_text->replace(dot, 1, new_style);
}
++dot;
} while (dot < mark);
_style = document->parse_style(style, _style);
selection_changed(false);
return true;
}
}
} else if (strncmp(command, "color", 5) == 0) {
long dot = Math::min(_dot, _mark);
long mark = Math::max(_dot, _mark);
if (!safe_to_edit(dot, mark)) {
_viewer->application()->complain(_viewer, "Can't edit mixed text");
return false;
} else {
const char* color;
if (strlen(command) == 5) {
color = _viewer->application()->ask(
_viewer, "Color name:", ""
);
} else {
color = command + 6;
}
if (color == nil) {
return false;
} else {
char style[100];
sprintf(style, "\\color{%s}", color);
long old_style = 0;
long new_style = 0;
do {
if (_text->item_style(dot) != old_style) {
old_style = _text->item_style(dot);
new_style = document->parse_style(
style, old_style
);
}
if (dot < mark && new_style != old_style) {
_text->replace(dot, 1, new_style);
}
++dot;
} while (dot < mark);
_style = document->parse_style(style, _style);
selection_changed(false);
return true;
}
}
} else if (strncmp(command, "size", 4) == 0) {
long dot = Math::min(_dot, _mark);
long mark = Math::max(_dot, _mark);
if (!safe_to_edit(dot, mark)) {
_viewer->application()->complain(_viewer, "Can't edit mixed text");
return false;
} else {
const char* size;
if (strlen(command) == 4) {
size = _viewer->application()->ask(
_viewer, "Point size:", ""
);
} else {
size = command + 5;
}
if (size == nil) {
return false;
} else {
char style[100];
sprintf(style, "\\size{%s}", size);
long old_style = 0;
long new_style = 0;
do {
if (_text->item_style(dot) != old_style) {
old_style = _text->item_style(dot);
new_style = document->parse_style(
style, old_style
);
}
if (dot < mark && new_style != old_style) {
_text->replace(dot, 1, new_style);
}
++dot;
} while (dot < mark);
_style = document->parse_style(style, _style);
selection_changed(false);
return true;
}
}
} else if (strncmp(command, "align", 5) == 0) {
long dot = Math::min(_dot, _mark);
long mark = Math::max(_dot, _mark);
if (!safe_to_edit(dot, mark)) {
_viewer->application()->complain(_viewer, "Can't edit mixed text");
return false;
} else {
const char* align = command + 6;
if (align == nil) {
return false;
} else {
char style[100];
sprintf(style, "\\align{%s}", align);
long old_style = 0;
long new_style = 0;
do {
if (_text->item_style(dot) != old_style) {
old_style = _text->item_style(dot);
new_style = document->parse_style(
style, old_style
);
}
if (dot < mark && new_style != old_style) {
_text->replace(dot, 1, new_style);
}
++dot;
} while (dot < mark);
_style = document->parse_style(style, _style);
selection_changed(false);
return true;
}
}
} else if (strncmp(command, "character", 9) == 0) {
long c = document->parse_text(command + 10);
if (c == 0) {
c = document->parse_text(
_viewer->application()->ask(
_viewer, "Octal character code:", ""
)
);
}
if (c > 0) {
insert_text(c);
selection_changed(true);
return true;
} else {
return false;
}
} else if (strncmp(command, "macro", 5) == 0) {
const char* macro;
boolean cancelled = false;
strstream scratch;
if (strlen(command) == 5) {
macro = _viewer->application()->ask(
_viewer, "Macro name:", ""
);
} else {
macro = command + 6;
}
if (macro == nil) {
cancelled = true;
}
if (!cancelled) {
scratch << "\\" << macro << "{";
long d = Math::min(_dot, _mark);
long m = Math::max(_dot, _mark);
if (m > d) {
document->copy(scratch, _text, d, m-d, _style, _source);
} else {
scratch << "<" << macro << ">";
}
scratch << "}";
scratch.seekg(0);
boolean safe = insert_text(0);
if (safe) {
dot(document->paste(scratch, _text, _dot, _style, _source));
selection_changed(true);
}
}
return !cancelled;
} else if (strncmp(command, "item", 4) == 0) {
boolean cancelled = false;
strstream scratch;
const char* keyword;
if (strlen(command) == 4) {
keyword = _viewer->application()->ask(
_viewer, "Item:", ""
);
} else {
keyword = command + 5;
}
if (keyword == nil) {
cancelled = true;
} else if (strcmp(keyword, "import") == 0) {
const char* file = _viewer->application()->choose(
_viewer, "Import text from file:", nil
);
if (file == nil) {
cancelled = true;
} else {
scratch << "\\import{" << file << "}";
}
} else if (strcmp(keyword, "float") == 0) {
const char* params = _viewer->application()->ask(
_viewer, "Float context:", "figure"
);
if (params == nil) {
cancelled = true;
} else {
scratch << "\\float{" << params << "}{<Float>}";
}
} else if (strcmp(keyword, "tabular") == 0) {
scratch << "\\begin{tabular}{|l|}\n";
scratch << "\\hline\n";
scratch << "<Cell>\\\\\n";
scratch << "\\hline\n";
scratch << "\\end{tabular}";
} else if (strcmp(keyword, "parbox") == 0) {
const char* params = _viewer->application()->ask(
_viewer, "Parbox width:", ""
);
if (params == nil) {
cancelled = true;
} else {
scratch << "\\parbox{" << params << "}{<Parbox>}";
}
} else if (strcmp(keyword, "psfig") == 0) {
const char* file = _viewer->application()->choose(
_viewer, "Import graphic from file:", nil
);
const char* params = nil;
if (file != nil) {
params = _viewer->application()->ask(
_viewer, "PSFig parameter list:", ""
);
}
if (file == nil || params == nil) {
cancelled = true;
} else {
scratch << "\\psfig{figure=" << file;
if (strlen(params) > 0) {
scratch << "," << params;
}
scratch << "}";
}
} else if (strcmp(keyword, "label") == 0) {
const char* label = _viewer->application()->ask(
_viewer, "Label tag:", ""
);
if (label == nil) {
cancelled = true;
} else {
scratch << "\\label{" << label << "}";
}
} else if (strcmp(keyword, "ref") == 0) {
const char* ref = _viewer->application()->ask(
_viewer, "Reference to what label tag:", ""
);
if (ref == nil) {
cancelled = true;
} else {
scratch << "\\ref{" << ref << "}";
}
} else {
scratch << keyword;
}
if (!cancelled) {
scratch.seekg(0);
boolean safe = insert_text(0);
if (safe) {
dot(document->paste(scratch, _text, _dot, _style, _source));
selection_changed(true);
} else {
cancelled = true;
}
}
return !cancelled;
} else if (strncmp(command, "clip", 4) == 0) {
long d = Math::min(_dot, _mark);
long m = Math::max(_dot, _mark);
if (!safe_to_edit(d, m)) {
_viewer->application()->complain(_viewer, "Can't edit mixed text");
return false;
} else {
const char* keyword = command + 5;
_viewer->highlight(keyword, true);
if (strcmp(keyword, "cut") == 0 || strcmp(keyword, "copy") == 0) {
ofstream clip(".clipboard");
if (clip) {
document->copy(clip, _text, d, m-d, _style, _source);
}
}
if (strcmp(keyword, "cut") == 0 || strcmp(keyword, "paste") == 0) {
insert_text(0);
}
if (strcmp(keyword, "paste") == 0) {
ifstream clip(".clipboard");
if (clip) {
dot(document->paste(clip, _text, _dot, _style, _source));
}
}
selection_changed(true);
_viewer->highlight(keyword, false);
return true;
}
} else {
return ItemView::command(command);
}
}
void TextView::activate (boolean active) {
if (_active != active) {
if (active) {
World* w = World::current();
if (_menubar == nil) {
const char* menubar = w->property_value(
DEFAULT_TEXT_MENUBAR
);
_menubar = strcpy(new char[strlen(menubar)+1], menubar);
}
if (_encoded_keymap == nil) {
const char* keymap = w->property_value(
DEFAULT_ENCODED_KEYMAP
);
delete _encoded_keymap;
_encoded_keymap = strcpy(new char[strlen(keymap)+1], keymap);
}
if (_verbatim_keymap == nil) {
const char* keymap = w->property_value(
DEFAULT_VERBATIM_KEYMAP
);
delete _verbatim_keymap;
_verbatim_keymap = strcpy(new char[strlen(keymap)+1], keymap);
}
w->flush();
_viewer->menubar(_menubar);
w->flush();
_source = -1;
_style = -1;
}
_active = active;
mark_selection();
selection_changed(true);
}
}
boolean TextView::handle_char (long c) {
if (!iscntrl(char(c & 0x7f))) {
return insert_text(c);
} else {
return false; // undefined mapping;
}
}
long TextView::index (Coord, Coord) {
return -1;
}
void TextView::selection_changed (boolean compute_style) {
Document* document = _text->document();
long new_style = _style;
long new_source = _source;
if (compute_style) {
long pre_source = _text->item_source(_dot - 1);
long post_source = _text->item_source(_dot);
if (pre_source == post_source) {
new_source = pre_source;
long pre_code = _text->item_code(_dot - 1);
long post_code = _text->item_code(_dot);
if (interword_code(pre_code) && !interword_code(post_code)) {
new_style = _text->item_style(_dot - 1);
} else {
new_style = _text->item_style(_dot);
}
} else if (
_source == -1 ||
document->common_source(pre_source, _source) != _source
&& document->common_source(post_source, _source) != _source
) {
new_source = document->common_source(pre_source, post_source);
new_style = _text->style();
}
}
if (_mark != _dot || new_source != _source || new_style != _style) {
_viewer->choose(nil, false);
TextSource& source = document->text_source(new_source);
if (strcmp(source._source, "document") == 0) {
_viewer->keymap(_encoded_keymap);
} else if (strcmp(source._source, "verbatim") == 0) {
_viewer->keymap(_verbatim_keymap);
} else if (strcmp(source._source, "import") == 0) {
} else if (strcmp(source._source, "macro") == 0) {
_viewer->choose("macro", true);
_viewer->choose(source._name, true);
} else if (strcmp(source._source, "parameter") == 0) {
_viewer->keymap(_encoded_keymap);
} else if (strcmp(source._source, "styled") == 0) {
_viewer->choose(source._name, true);
_viewer->keymap(_encoded_keymap);
}
if (_mark == _dot) {
if (new_style != _style) {
TextStyle& style = document->text_style(new_style);
_viewer->choose(style._font, true);
_viewer->choose(style._size, true);
_viewer->choose(style._color, true);
_viewer->choose(style._alignment, true);
}
} else {
long dot = Math::min(_dot, _mark);
long mark = Math::max(_dot, _mark);
long style = 0;
for (long i = dot; i < mark; ++i) {
if (_text->item_style(i) != style) {
style = _text->item_style(i);
TextStyle& info = document->text_style(style);
_viewer->choose(info._font, true);
_viewer->choose(info._size, true);
_viewer->choose(info._color, true);
_viewer->choose(info._alignment, true);
}
}
}
}
_source = new_source;
_style = new_style;
}
void TextView::keystroke (Event& e) {
char s[1];
if (e.mapkey(s, sizeof(s)) > 0) {
boolean pending_repair = handle_char(s[0]);
if (pending_repair && !e.pending()) {
repair();
}
}
}
void TextView::select (Event& e) {
if (!e.shift_is_down()) {
dot(index(e.pointer_x(), e.pointer_y()));
}
do {
if (!e.pending()) {
mark(index(e.pointer_x(), e.pointer_y()));
mark_selection();
}
e.read();
} while (e.type() != Event::up);
selection_changed(true);
}