00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "evalx.h"
00023 #include "cmds.h"
00024 #include "htctrl.h"
00025 #include "htdialog.h"
00026 #include "htmenu.h"
00027 #include "htobj.h"
00028 #include "htpal.h"
00029 #include "htclipboard.h"
00030 #include "htiobox.h"
00031 #include "textedit.h"
00032 #include "tools.h"
00033 #include "snprintf.h"
00034
00035 #include <errno.h>
00036 #include <limits.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <unistd.h>
00040
00041
00042 #include "htatom.h"
00043 #include "hthist.h"
00044 #include "htsearch.h"
00045
00046 static ht_search_request* create_request_hexascii(text_search_pos *start, text_search_pos *end, ht_view *f, UINT search_class)
00047 {
00048 ht_hexascii_search_form *form=(ht_hexascii_search_form*)f;
00049 ht_hexascii_search_form_data d;
00050 form->databuf_get(&d, sizeof d);
00051
00052 ht_fxbin_search_request *request;
00053
00054 if (!d.str.textlen) {
00055 throw new ht_io_exception("%s: string is empty", "hex/ascii");
00056 }
00057
00058
00059 request = new ht_fxbin_search_request(search_class,
00060 d.options.state & 1 ? SF_FXBIN_CASEINSENSITIVE: 0,
00061 d.str.textlen, d.str.text);
00062
00063
00064
00065 return request;
00066 }
00067
00068 typedef ht_search_request* (*create_request_func)(text_search_pos *ret_start, text_search_pos *ret_end, ht_view *form, UINT search_class);
00069
00070 struct ht_text_search_method {
00071 char *name;
00072 UINT search_class;
00073 UINT search_mode_mask;
00074 HT_ATOM histid;
00075 create_form_func create_form;
00076 create_request_func create_request;
00077 create_desc_func create_desc;
00078 };
00079
00080 static ht_text_search_method text_search_methods[] =
00081 {
00082 { "bin: hex/ascii", SC_PHYSICAL, SEARCHMODE_BIN, HISTATOM_SEARCH_BIN,
00083 create_form_hexascii, create_request_hexascii, create_desc_hexascii }
00084 };
00085
00086 ht_search_request *text_search_dialog(ht_text_viewer *text_viewer, UINT searchmodes, const text_viewer_pos *end_pos)
00087 {
00088 ht_search_request *result = NULL;
00089 bounds b;
00090 b.w = 50;
00091 b.h = 15;
00092 b.x = (screen->size.w-b.w)/2;
00093 b.y = (screen->size.h-b.h)/2;
00094 ht_search_dialog *dialog = new ht_search_dialog();
00095 dialog->init(&b, "search");
00096
00097 bounds k;
00098 dialog->search_mode_xgroup->getbounds(&k);
00099
00100 k.x = 0;
00101 k.y = 0;
00102
00103 int modes = 0;
00104 int i = 0;
00105 ht_text_search_method *q = text_search_methods;
00106 while (q->name) {
00107 if (q->search_mode_mask & searchmodes) {
00108 bounds v = k;
00109 ht_view *form = q->create_form(&v, q->histid);
00110 dialog->insert_search_mode(i, q->name, form);
00111 modes++;
00112 }
00113 q++;
00114 i++;
00115 }
00116
00117
00118
00119 if (dialog->run(false)) {
00120 int modeid = dialog->get_search_modeid();
00121
00122
00123 ht_text_search_method *s = &text_search_methods[modeid];
00124 ht_view *form = dialog->get_search_modeform();
00125
00126 text_search_pos start, end;
00127
00128 try {
00129
00130 if (s->create_desc) {
00131 char hist_desc[1024];
00132 s->create_desc(hist_desc, sizeof hist_desc, form);
00133 insert_history_entry((ht_list*)find_atom(s->histid), hist_desc, form);
00134 }
00135
00136 switch (s->search_class) {
00137 case SC_PHYSICAL: {
00138 text_viewer_pos cursor;
00139 text_viewer->get_cursor_pos(&cursor);
00140 start.offset = 0;
00141 text_viewer->pos_to_offset(&cursor, &start.offset);
00142 end.offset = 0xffffffff;
00143 break;
00144 }
00145 }
00146 result = s->create_request(&start, &end, form, s->search_class);
00147 } catch (ht_exception *e) {
00148 errorbox("error: %s", e->what());
00149 }
00150 }
00151 dialog->done();
00152 delete dialog;
00153 return result;
00154 }
00155
00156
00157
00158
00159 ht_undo_data::ht_undo_data()
00160 {
00161 }
00162
00163 bool ht_undo_data::combine(ht_undo_data *ud)
00164 {
00165 return false;
00166 }
00167
00168
00169
00170
00171
00172 ht_undo_data_delete_string::ht_undo_data_delete_string(text_viewer_pos *APos, text_viewer_pos *BPos, void *String, UINT Len)
00173 {
00174 apos = *APos;
00175 bpos = *BPos;
00176 if (Len) {
00177 string = malloc(Len);
00178 memcpy(string, String, Len);
00179 } else {
00180 string = NULL;
00181 }
00182 len = Len;
00183 }
00184
00185 ht_undo_data_delete_string::~ht_undo_data_delete_string()
00186 {
00187 if (string) free(string);
00188 }
00189
00190 bool ht_undo_data_delete_string::combine(ht_undo_data *ud)
00191 {
00192 if (ud->object_id()==object_id()) {
00193 ht_undo_data_delete_string *ud2 = (ht_undo_data_delete_string *)ud;
00194 if (ud2->apos.line == apos.line) {
00195 if (ud2->bpos.pofs + ud2->len == bpos.pofs) {
00196 string = realloc(string, len+ud2->len);
00197 memmove((byte*)string+ud2->len, string, len);
00198 memcpy(string, ud2->string, ud2->len);
00199 len += ud2->len;
00200 bpos = ud2->bpos;
00201 return true;
00202 }
00203 }
00204 }
00205 return false;
00206 }
00207
00208 UINT ht_undo_data_delete_string::getsize()
00209 {
00210 return len+sizeof *this;
00211 }
00212
00213 void ht_undo_data_delete_string::gettext(char *text, UINT maxlen)
00214 {
00215 char *buf = (char *) malloc(len+1);
00216 bin2str(buf, string, len);
00217 ht_snprintf(text, maxlen, "deletion of '%s' at %d:%d", buf, bpos.line+1, bpos.pofs+1);
00218 free(buf);
00219 }
00220
00221 OBJECT_ID ht_undo_data_delete_string::object_id() const
00222 {
00223 return ATOM_HT_UNDO_DATA_DELETE;
00224 }
00225
00226 void ht_undo_data_delete_string::apply(ht_text_editor *te)
00227 {
00228 te->goto_line(apos.line);
00229 te->cursor_pput(apos.pofs);
00230 te->delete_chars(bpos.line, bpos.pofs, len);
00231 te->goto_line(bpos.line);
00232 te->cursor_pput(bpos.pofs);
00233 }
00234
00235 void ht_undo_data_delete_string::unapply(ht_text_editor *te, bool *goto_only)
00236 {
00237 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00238 te->goto_line(apos.line);
00239 te->cursor_pput(apos.pofs);
00240 te->goto_line(bpos.line);
00241 te->cursor_pput(bpos.pofs);
00242 return;
00243 }
00244 *goto_only = false;
00245 if (string) {
00246 te->insert_chars(bpos.line, bpos.pofs, string, len);
00247 }
00248 te->goto_line(apos.line);
00249 te->cursor_pput(apos.pofs);
00250 }
00251
00252
00253
00254
00255
00256 ht_undo_data_delete_string2::ht_undo_data_delete_string2(text_viewer_pos *APos, text_viewer_pos *BPos, void *String, UINT Len)
00257 {
00258 apos = *APos;
00259 bpos = *BPos;
00260 if (Len) {
00261 string = malloc(Len);
00262 memcpy(string, String, Len);
00263 } else {
00264 string = NULL;
00265 }
00266 len = Len;
00267 }
00268
00269 ht_undo_data_delete_string2::~ht_undo_data_delete_string2()
00270 {
00271 if (string) free(string);
00272 }
00273
00274 bool ht_undo_data_delete_string2::combine(ht_undo_data *ud)
00275 {
00276 if (ud->object_id()==object_id()) {
00277 ht_undo_data_delete_string2 *ud2 = (ht_undo_data_delete_string2 *)ud;
00278 if (ud2->apos.line == apos.line) {
00279 if (ud2->apos.pofs == apos.pofs) {
00280 string = realloc(string, len+ud2->len);
00281 memcpy((byte*)string+len, ud2->string, ud2->len);
00282 len += ud2->len;
00283 return true;
00284 }
00285 }
00286 }
00287 return false;
00288 }
00289
00290 UINT ht_undo_data_delete_string2::getsize()
00291 {
00292 return len+sizeof *this;
00293 }
00294
00295 void ht_undo_data_delete_string2::gettext(char *text, UINT maxlen)
00296 {
00297 char *buf = (char *) malloc(len+1);
00298 bin2str(buf, string, len);
00299 ht_snprintf(text, maxlen, "deletion of '%s' at %d:%d", buf, apos.line+1, apos.pofs+1);
00300 free(buf);
00301 }
00302
00303 OBJECT_ID ht_undo_data_delete_string2::object_id() const
00304 {
00305 return ATOM_HT_UNDO_DATA_DELETE2;
00306 }
00307
00308 void ht_undo_data_delete_string2::apply(ht_text_editor *te)
00309 {
00310 te->goto_line(bpos.line);
00311 te->cursor_pput(bpos.pofs);
00312 te->delete_chars(apos.line, apos.pofs, len);
00313 te->goto_line(apos.line);
00314 te->cursor_pput(apos.pofs);
00315 }
00316
00317 void ht_undo_data_delete_string2::unapply(ht_text_editor *te, bool *goto_only)
00318 {
00319 if (*goto_only && (apos.line != te->top_line + te->cursory || te->physical_cursorx()!=apos.pofs)) {
00320 te->goto_line(bpos.line);
00321 te->cursor_pput(bpos.pofs);
00322 te->goto_line(apos.line);
00323 te->cursor_pput(apos.pofs);
00324 return;
00325 }
00326 te->goto_line(apos.line);
00327 te->cursor_pput(apos.pofs);
00328 *goto_only = false;
00329 if (string) {
00330 te->insert_chars(apos.line, apos.pofs, string, len);
00331 }
00332 }
00333
00334
00335
00336
00337
00338 ht_undo_data_insert_string::ht_undo_data_insert_string(text_viewer_pos *APos, text_viewer_pos *BPos, void *String, UINT Len)
00339 {
00340 apos = *APos;
00341 bpos = *BPos;
00342 if (Len) {
00343 string = malloc(Len);
00344 memcpy(string, String, Len);
00345 } else {
00346 string = NULL;
00347 }
00348 len = Len;
00349 }
00350
00351 ht_undo_data_insert_string::~ht_undo_data_insert_string()
00352 {
00353 if (string) free(string);
00354 }
00355
00356 bool ht_undo_data_insert_string::combine(ht_undo_data *ud)
00357 {
00358 if (ud->object_id()==object_id()) {
00359 ht_undo_data_insert_string *ud2 = (ht_undo_data_insert_string *)ud;
00360 if (ud2->cpos.line == cpos.line) {
00361 if (ud2->apos.pofs == apos.pofs + len) {
00362 string = realloc(string, len + ud2->len);
00363 memcpy((byte*)string+len, ud2->string, ud2->len);
00364 len += ud2->len;
00365 bpos = ud2->bpos;
00366 return true;
00367 }
00368 }
00369 }
00370 return false;
00371 }
00372
00373 UINT ht_undo_data_insert_string::getsize()
00374 {
00375 return len+sizeof *this;
00376 }
00377
00378 void ht_undo_data_insert_string::gettext(char *text, UINT maxlen)
00379 {
00380 char *buf = (char *) malloc(len+1);
00381 bin2str(buf, string, len);
00382 ht_snprintf(text, maxlen, "insertion of '%s' at %d:%d", buf, apos.line+1, apos.pofs+1);
00383 free(buf);
00384 }
00385
00386 OBJECT_ID ht_undo_data_insert_string::object_id() const
00387 {
00388 return ATOM_HT_UNDO_DATA_INSERT;
00389 }
00390
00391 void ht_undo_data_insert_string::apply(ht_text_editor *te)
00392 {
00393 te->goto_line(apos.line);
00394 te->cursor_pput(apos.pofs);
00395 if (string) {
00396 UINT l = te->get_line_length(apos.line);
00397 cpos.line = apos.line;
00398 if (apos.pofs > l) {
00399 UINT k = apos.pofs - l;
00400 te->indent(apos.line, l, k);
00401 cpos.pofs = l;
00402 } else {
00403 cpos.pofs = apos.pofs;
00404 }
00405 te->insert_chars(apos.line, apos.pofs, string, len);
00406 }
00407 te->goto_line(bpos.line);
00408 te->cursor_pput(bpos.pofs);
00409 }
00410
00411 void ht_undo_data_insert_string::unapply(ht_text_editor *te, bool *goto_only)
00412 {
00413 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00414 te->goto_line(apos.line);
00415 te->cursor_pput(apos.pofs);
00416 te->goto_line(bpos.line);
00417 te->cursor_pput(bpos.pofs);
00418 return;
00419 }
00420 *goto_only = false;
00421 te->unindent(cpos.line, cpos.pofs, bpos.pofs-cpos.pofs);
00422 te->goto_line(apos.line);
00423 te->cursor_pput(apos.pofs);
00424 }
00425
00426
00427
00428
00429 ht_undo_data_overwrite_string::ht_undo_data_overwrite_string(text_viewer_pos *APos, text_viewer_pos *BPos, void *String, UINT Len, void *String2, UINT Len2)
00430 {
00431 apos = *APos;
00432 bpos = *BPos;
00433 if (Len) {
00434 string = malloc(Len);
00435 memcpy(string, String, Len);
00436 } else {
00437 string = NULL;
00438 }
00439 len = Len;
00440 if (Len2) {
00441 string2 = malloc(Len2);
00442 memcpy(string2, String2, Len2);
00443 } else {
00444 string2 = NULL;
00445 }
00446 len2 = Len2;
00447 }
00448
00449 ht_undo_data_overwrite_string::~ht_undo_data_overwrite_string()
00450 {
00451 if (string) free(string);
00452 if (string2) free(string2);
00453 }
00454
00455 bool ht_undo_data_overwrite_string::combine(ht_undo_data *ud)
00456 {
00457 if (ud->object_id()==object_id()) {
00458 ht_undo_data_overwrite_string *ud2 = (ht_undo_data_overwrite_string *)ud;
00459 if (ud2->cpos.line == cpos.line) {
00460 if (ud2->apos.pofs == apos.pofs + len) {
00461 string = realloc(string, len + ud2->len);
00462 memcpy((byte*)string+len, ud2->string, ud2->len);
00463 len += ud2->len;
00464 bpos = ud2->bpos;
00465 if (ud2->len2) {
00466 if (!len2) {
00467 string2 = malloc(ud2->len2);
00468 memcpy(string2, ud2->string2, ud2->len2);
00469 } else {
00470 string2 = realloc(string2, len2 + ud2->len2);
00471 memcpy((byte*)string2+len2, ud2->string2, ud2->len2);
00472 }
00473 len2 += ud2->len;
00474 }
00475 return true;
00476 }
00477 }
00478 }
00479 return false;
00480 }
00481
00482 UINT ht_undo_data_overwrite_string::getsize()
00483 {
00484 return len+len2+sizeof(*this);
00485 }
00486
00487 void ht_undo_data_overwrite_string::gettext(char *text, UINT maxlen)
00488 {
00489 char *buf = (char *) malloc(len+1);
00490 bin2str(buf, string, len);
00491 ht_snprintf(text, maxlen, "insertion of '%s' at %d:%d", buf, apos.line+1, apos.pofs+1);
00492 free(buf);
00493 }
00494
00495 OBJECT_ID ht_undo_data_overwrite_string::object_id() const
00496 {
00497 return ATOM_HT_UNDO_DATA_OVERWRITE;
00498 }
00499
00500 void ht_undo_data_overwrite_string::apply(ht_text_editor *te)
00501 {
00502 if (len2) te->delete_chars(apos.line, apos.pofs, len2);
00503 te->goto_line(apos.line);
00504 te->cursor_pput(apos.pofs);
00505 if (string) {
00506 UINT l = te->get_line_length(apos.line);
00507 cpos.line = apos.line;
00508 if (apos.pofs > l) {
00509 UINT k = apos.pofs - l;
00510 te->indent(apos.line, l, k);
00511 cpos.pofs = l;
00512 } else {
00513 cpos.pofs = apos.pofs;
00514 }
00515 te->insert_chars(apos.line, apos.pofs, string, len);
00516 }
00517 te->goto_line(bpos.line);
00518 te->cursor_pput(bpos.pofs);
00519 }
00520
00521 void ht_undo_data_overwrite_string::unapply(ht_text_editor *te, bool *goto_only)
00522 {
00523 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00524 te->goto_line(apos.line);
00525 te->cursor_pput(apos.pofs);
00526 te->goto_line(bpos.line);
00527 te->cursor_pput(bpos.pofs);
00528 return;
00529 }
00530 *goto_only = false;
00531 te->unindent(cpos.line, cpos.pofs, bpos.pofs-cpos.pofs);
00532 if (len2) te->insert_chars(apos.line, apos.pofs, string2, len2);
00533 te->goto_line(apos.line);
00534 te->cursor_pput(apos.pofs);
00535 }
00536
00537
00538
00539
00540 ht_undo_data_split_line::ht_undo_data_split_line(text_viewer_pos *APos, text_viewer_pos *BPos, UINT Indent)
00541 {
00542 apos = *APos;
00543 bpos = *BPos;
00544 indent = Indent;
00545 }
00546
00547 ht_undo_data_split_line::~ht_undo_data_split_line()
00548 {
00549 }
00550
00551 UINT ht_undo_data_split_line::getsize()
00552 {
00553 return sizeof *this;
00554 }
00555
00556 void ht_undo_data_split_line::gettext(char *text, UINT maxlen)
00557 {
00558 ht_snprintf(text, maxlen, "split line at %d:%d", apos.line+1, apos.pofs+1);
00559 }
00560
00561 OBJECT_ID ht_undo_data_split_line::object_id() const
00562 {
00563 return ATOM_HT_UNDO_DATA_SPLIT_LINE;
00564 }
00565
00566 void ht_undo_data_split_line::apply(ht_text_editor *te)
00567 {
00568 te->split_line(apos.line, apos.pofs);
00569 if (indent) te->indent(bpos.line, 0, indent);
00570 te->goto_line(bpos.line);
00571 te->cursor_pput(bpos.pofs);
00572 }
00573
00574 void ht_undo_data_split_line::unapply(ht_text_editor *te, bool *goto_only)
00575 {
00576 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00577 te->goto_line(apos.line);
00578 te->cursor_pput(apos.pofs);
00579 te->goto_line(bpos.line);
00580 te->cursor_pput(bpos.pofs);
00581 return;
00582 }
00583 *goto_only = false;
00584 if (indent) te->unindent(bpos.line, 0, indent);
00585 te->concat_lines(apos.line);
00586 te->goto_line(apos.line);
00587 te->cursor_pput(apos.pofs);
00588 }
00589
00590
00591
00592
00593 ht_undo_data_join_line::ht_undo_data_join_line(text_viewer_pos *APos, text_viewer_pos *BPos)
00594 {
00595 apos = *APos;
00596 bpos = *BPos;
00597 }
00598
00599 ht_undo_data_join_line::~ht_undo_data_join_line()
00600 {
00601 }
00602
00603 UINT ht_undo_data_join_line::getsize()
00604 {
00605 return sizeof *this;
00606 }
00607
00608 void ht_undo_data_join_line::gettext(char *text, UINT maxlen)
00609 {
00610 ht_snprintf(text, maxlen, "join lines %d and %d", bpos.line+1, bpos.line+2);
00611 }
00612
00613 OBJECT_ID ht_undo_data_join_line::object_id() const
00614 {
00615 return ATOM_HT_UNDO_DATA_JOIN_LINE;
00616 }
00617
00618 void ht_undo_data_join_line::apply(ht_text_editor *te)
00619 {
00620 UINT l = te->get_line_length(apos.line);
00621 cpos.line = apos.line;
00622 if (apos.pofs > l) {
00623 UINT k = apos.pofs - l;
00624 te->indent(apos.line, l, k);
00625 cpos.pofs = l;
00626 } else {
00627 cpos.pofs = apos.pofs;
00628 }
00629
00630 te->concat_lines(bpos.line);
00631 te->goto_line(bpos.line);
00632 te->cursor_pput(bpos.pofs);
00633 }
00634
00635 void ht_undo_data_join_line::unapply(ht_text_editor *te, bool *goto_only)
00636 {
00637 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00638 te->goto_line(bpos.line);
00639 te->cursor_pput(bpos.pofs);
00640 return;
00641 }
00642 *goto_only = false;
00643 te->split_line(bpos.line, bpos.pofs);
00644 if (cpos.line == bpos.line) te->unindent(cpos.line, cpos.pofs, bpos.pofs-cpos.pofs);
00645 te->goto_line(apos.line);
00646 te->cursor_pput(apos.pofs);
00647 }
00648
00649
00650
00651
00652
00653 text_viewer_pos insert_text_block(ht_text_editor *te, text_viewer_pos apos, text_viewer_pos bpos, void *block, UINT size)
00654 {
00655 text_viewer_pos cpos;
00656
00657 te->goto_line(apos.line);
00658 te->cursor_pput(apos.pofs);
00659
00660 ht_textfile *textfile = te->get_textfile();
00661 FILEOFS o;
00662
00663 textfile->convert_line2ofs(apos.line, apos.pofs, &o);
00664
00665 UINT l = te->get_line_length(apos.line);
00666 cpos.line = apos.line;
00667 if (apos.pofs > l) {
00668 UINT k = apos.pofs - l;
00669 te->indent(apos.line, l, k);
00670 cpos.pofs = l;
00671 o += k;
00672 } else {
00673 cpos.pofs = apos.pofs;
00674 }
00675
00676 textfile->seek(o);
00677 textfile->write(block, size);
00678
00679 UINT s = size;
00680 text_viewer_pos start, end;
00681 textfile->convert_ofs2line(o, &start.line, &start.pofs);
00682 textfile->convert_ofs2line(o+s, &end.line, &end.pofs);
00683 te->select_set(&start, &end);
00684 te->goto_line(bpos.line);
00685 te->cursor_pput(bpos.pofs);
00686
00687 return cpos;
00688 }
00689
00690 void delete_text_block(ht_text_editor *te, text_viewer_pos apos, text_viewer_pos bpos, text_viewer_pos cpos, text_viewer_pos sel_start, text_viewer_pos sel_end, bool copy, void **block, UINT *size)
00691 {
00692 ht_textfile *textfile = te->get_textfile();
00693
00694 FILEOFS s, e;
00695 if (textfile->convert_line2ofs(sel_start.line, sel_start.pofs, &s) &&
00696 textfile->convert_line2ofs(sel_end.line, sel_end.pofs, &e) && copy) {
00697 UINT sz = e-s;
00698 void *bl = malloc(sz);
00699 textfile->seek(s);
00700 textfile->read(bl, sz);
00701 *block = bl;
00702 *size = sz;
00703 }
00704
00705 int k=0;
00706 if (sel_start.line < sel_end.line) {
00707 k=textfile->getlinelength(sel_start.line)-sel_start.pofs;
00708 } else {
00709 k=sel_end.pofs-sel_start.pofs;
00710 }
00711 te->delete_chars(sel_start.line, sel_start.pofs, k);
00712 if (sel_start.line < sel_end.line) {
00713 te->delete_chars(sel_end.line, 0, sel_end.pofs);
00714 if (sel_start.line+1 < sel_end.line) {
00715 te->delete_lines(sel_start.line+1, sel_end.line-sel_start.line-1);
00716 }
00717 te->concat_lines(sel_start.line);
00718 }
00719 te->unindent(cpos.line, cpos.pofs, apos.pofs-cpos.pofs);
00720
00721 te->goto_line(apos.line);
00722 te->cursor_pput(apos.pofs);
00723 te->select_clear();
00724
00725 te->goto_line(bpos.line);
00726 te->cursor_pput(bpos.pofs);
00727 }
00728
00729
00730
00731
00732
00733 ht_undo_data_insert_block::ht_undo_data_insert_block(text_viewer_pos *Apos, text_viewer_pos *Bpos, void *Block, UINT Size)
00734 {
00735 apos = *Apos;
00736 bpos = *Bpos;
00737 sel_start.line = sel_end.line = 0;
00738 sel_start.pofs = sel_end.pofs = 0;
00739 size = Size;
00740 block = malloc(size);
00741 memcpy(block, Block, size);
00742 }
00743
00744 ht_undo_data_insert_block::~ht_undo_data_insert_block()
00745 {
00746 free(block);
00747 }
00748
00749 UINT ht_undo_data_insert_block::getsize()
00750 {
00751 return (sizeof *this)+size;
00752 }
00753
00754 void ht_undo_data_insert_block::gettext(char *text, UINT maxlen)
00755 {
00756 ht_snprintf(text, maxlen, "insert block ...");
00757 }
00758
00759 OBJECT_ID ht_undo_data_insert_block::object_id() const
00760 {
00761 return ATOM_HT_UNDO_DATA_INSERT_BLOCK;
00762 }
00763
00764 void ht_undo_data_insert_block::apply(ht_text_editor *te)
00765 {
00766 cpos = insert_text_block(te, apos, bpos, block, size);
00767 if (text_viewer_pos_compare(&sel_start, &sel_end) == 0) {
00768 te->get_selection(&sel_start, &sel_end);
00769 }
00770 }
00771
00772 void ht_undo_data_insert_block::unapply(ht_text_editor *te, bool *goto_only)
00773 {
00774 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00775 te->goto_line(apos.line);
00776 te->cursor_pput(apos.pofs);
00777 te->goto_line(bpos.line);
00778 te->cursor_pput(bpos.pofs);
00779 return;
00780 }
00781 *goto_only = false;
00782 delete_text_block(te, bpos, apos, cpos, sel_start, sel_end, false, NULL, NULL);
00783 }
00784
00785
00786
00787
00788
00789 ht_undo_data_delete_block::ht_undo_data_delete_block(text_viewer_pos *Apos, text_viewer_pos *Bpos, text_viewer_pos *Sel_start, text_viewer_pos *Sel_end)
00790 {
00791 apos = *Apos;
00792 bpos = *Bpos;
00793 sel_start = *Sel_start;
00794 sel_end = *Sel_end;
00795 size = 0;
00796 block = NULL;
00797 }
00798
00799 ht_undo_data_delete_block::~ht_undo_data_delete_block()
00800 {
00801 if (block) free(block);
00802 }
00803
00804 UINT ht_undo_data_delete_block::getsize()
00805 {
00806 return (sizeof *this)+size;
00807 }
00808
00809 void ht_undo_data_delete_block::gettext(char *text, UINT maxlen)
00810 {
00811
00812 ht_snprintf(text, maxlen, "delete block ...");
00813 }
00814
00815 OBJECT_ID ht_undo_data_delete_block::object_id() const
00816 {
00817 return ATOM_HT_UNDO_DATA_DELETE_BLOCK;
00818 }
00819
00820 void ht_undo_data_delete_block::apply(ht_text_editor *te)
00821 {
00822 if (block) free(block);
00823 delete_text_block(te, apos, bpos, apos, sel_start, sel_end, true, &block, &size);
00824 }
00825
00826 void ht_undo_data_delete_block::unapply(ht_text_editor *te, bool *goto_only)
00827 {
00828 if (*goto_only && (bpos.line != te->top_line + te->cursory || te->physical_cursorx()!=bpos.pofs)) {
00829 te->goto_line(sel_end.line);
00830 te->cursor_pput(sel_end.pofs);
00831 te->goto_line(bpos.line);
00832 te->cursor_pput(bpos.pofs);
00833 return;
00834 }
00835 *goto_only = false;
00836 insert_text_block(te, bpos, sel_end, block, size);
00837 }
00838
00839
00840
00841
00842 void ht_text_editor_undo::init(UINT max_undo_size)
00843 {
00844 ht_clist::init();
00845 size = 0;
00846 max_size = max_undo_size;
00847 clean_state = 0;
00848 goto_state = true;
00849 current_position = 0;
00850 }
00851
00852 int ht_text_editor_undo::get_current_position()
00853 {
00854 return current_position;
00855 }
00856
00857 void ht_text_editor_undo::insert_undo(ht_text_editor *tv, ht_undo_data *undo)
00858 {
00859 if (undo) {
00860 if (current_position!=(int)c_entry_count) {
00861
00862 UINT test=c_entry_count;
00863 for (UINT i=current_position; i<test; i++) {
00864 ht_undo_data *u = (ht_undo_data*)get(current_position);
00865 size-=u->getsize();
00866 del(current_position);
00867 }
00868 assert(current_position == (int)c_entry_count);
00869
00870 if (clean_state > current_position) {
00871 clean_state = -1;
00872 }
00873 }
00874 undo->apply(tv);
00875 UINT gsize = undo->getsize();
00876 while (size + gsize > max_size) {
00877 if (clean_state > -1) clean_state--;
00878 if (c_entry_count) {
00879 size-=((ht_undo_data*)get(0))->getsize();
00880 del(0);
00881 if (current_position) current_position--;
00882 } else {
00883 delete undo;
00884 return;
00885 }
00886 }
00887 if (c_entry_count && !is_clean()) {
00888 ht_undo_data *u = (ht_undo_data*)get(c_entry_count-1);
00889 UINT zsize = u->getsize();
00890 if (u->combine(undo)) {
00891 size-=zsize;
00892 size+=u->getsize();
00893 delete undo;
00894 return;
00895 }
00896 }
00897 current_position++;
00898 append(undo);
00899 size+=gsize;
00900 }
00901 goto_state = true;
00902 }
00903
00904 void ht_text_editor_undo::mark_clean()
00905 {
00906 clean_state = current_position;
00907 goto_state = true;
00908 }
00909
00910 bool ht_text_editor_undo::is_clean()
00911 {
00912 return clean_state == current_position;
00913 }
00914
00915 bool ht_text_editor_undo::is_clean(int i)
00916 {
00917 return clean_state == i;
00918 }
00919
00920 void ht_text_editor_undo::redo(ht_text_editor *te)
00921 {
00922 goto_state = true;
00923 if (current_position < (int)c_entry_count) {
00924 ht_undo_data *u = (ht_undo_data*)get(current_position);
00925 u->apply(te);
00926 current_position++;
00927 }
00928 }
00929
00930 void ht_text_editor_undo::undo(ht_text_editor *te, bool place_cursor_first)
00931 {
00932 if (current_position) {
00933 ht_undo_data *u = (ht_undo_data*)get(current_position-1);
00934 bool goto_state_test = place_cursor_first ? goto_state : false;
00935 u->unapply(te, &goto_state_test);
00936 if (!goto_state_test) {
00937 current_position--;
00938 }
00939 goto_state = !goto_state_test;
00940 }
00941 }
00942
00943
00944
00945
00946 int text_viewer_pos_compare(text_viewer_pos *a, text_viewer_pos *b)
00947 {
00948 if (a->line==b->line) {
00949 return a->pofs-b->pofs;
00950 }
00951 return a->line-b->line;
00952 }
00953
00954
00955
00956
00957
00958 void ht_text_viewer::init(bounds *b, bool own_t, ht_textfile *t, ht_list *l)
00959 {
00960 ht_view::init(b, VO_OWNBUFFER | VO_SELECTABLE | VO_RESIZE, "text viewer");
00961 VIEW_DEBUG_NAME("ht_text_viewer");
00962
00963 growmode = MK_GM(GMH_FIT, GMV_FIT);
00964
00965 own_textfile = false;
00966 textfile = NULL;
00967 set_textfile(t, own_t);
00968
00969 own_lexer = false;
00970 lexer = NULL;
00971
00972 lexers = l;
00973
00974 top_line = 0;
00975 xofs = 0;
00976 cursorx = 0;
00977 cursory = 0;
00978 select_clear();
00979
00980 selectcursor = false;
00981
00982 EOL_string = NULL;
00983 EOF_string = NULL;
00984
00985 show_EOL = false;
00986 show_EOF = false;
00987 highlight_wrap = true;
00988
00989 last_search_request = NULL;
00990
00991 config_changed();
00992 }
00993
00994 void ht_text_viewer::done()
00995 {
00996 if (last_search_request) delete last_search_request;
00997
00998 if (own_textfile) {
00999 textfile->done();
01000 delete textfile;
01001 }
01002
01003 if (own_lexer && lexer) {
01004 lexer->done();
01005 delete lexer;
01006 }
01007
01008 if (EOL_string) free(EOL_string);
01009 if (EOF_string) free(EOF_string);
01010
01011 ht_view::done();
01012 }
01013
01014 UINT ht_text_viewer::char_vsize(char c, UINT x)
01015 {
01016 if (c=='\t') return tab_size - x % tab_size;
01017 return 1;
01018 }
01019
01020 void ht_text_viewer::clipboard_copy_cmd()
01021 {
01022 if (text_viewer_pos_compare(&sel_start, &sel_end)<0) {
01023 FILEOFS s, e;
01024 if (textfile->convert_line2ofs(sel_start.line, sel_start.pofs, &s)
01025 && textfile->convert_line2ofs(sel_end.line, sel_end.pofs, &e)) {
01026 char dsc[1024];
01027 ht_snprintf(dsc, sizeof dsc, "%s::%s", textfile->get_desc(), desc);
01028 clipboard_copy(dsc, textfile, s, e-s);
01029 }
01030 }
01031 }
01032
01033 void ht_text_viewer::config_changed()
01034 {
01035 if (EOL_string) free(EOL_string);
01036 if (EOF_string) free(EOF_string);
01037 EOL_string = get_config_string("editor/EOL");
01038 EOF_string = get_config_string("editor/EOF");
01039 tab_size = get_config_dword("editor/tab size");
01040 tab_size = MIN(MAX(tab_size, 1), 16);
01041
01042 if (lexer) lexer->config_changed();
01043 ht_view::config_changed();
01044 }
01045
01046 bool ht_text_viewer::continue_search()
01047 {
01048 if (last_search_request) {
01049 ht_search_result *r = NULL;
01050 FILEOFS o, no;
01051 text_viewer_pos cursor;
01052 get_cursor_pos(&cursor);
01053 o = 0;
01054 pos_to_offset(&cursor, &o);
01055 no = o+1;
01056 try {
01057 if (last_search_request->search_class == SC_PHYSICAL) {
01058 text_search_pos start, end;
01059 start.offset = no;
01060 end.offset = last_search_end_ofs;
01061 r = search(last_search_request, &start, &end);
01062 }
01063 } catch (ht_exception *e) {
01064 errorbox("error: %s", e->what());
01065 }
01066
01067 if (r) return show_search_result(r);
01068 }
01069 return false;
01070 }
01071
01072 UINT ht_text_viewer::cursor_up(UINT n)
01073 {
01074 if (cursory>n) cursory-=n; else {
01075 n=scroll_up(n-cursory);
01076 cursory=0;
01077 }
01078 return n;
01079 }
01080
01081 UINT ht_text_viewer::cursor_down(UINT n)
01082 {
01083 UINT lmh=textfile->linecount()-top_line;
01084 if (lmh>(UINT)size.h) lmh=size.h;
01085 if (cursory+n>lmh-1) {
01086 UINT k = scroll_down(cursory+n-(lmh-1));
01087 lmh = textfile->linecount()-top_line;
01088 if (lmh>(UINT)size.h) lmh = size.h;
01089 n = k+(lmh-1)-cursory;
01090 cursory = lmh-1;
01091 } else cursory+=n;
01092 return n;
01093 }
01094
01095 UINT ht_text_viewer::cursor_left(UINT n)
01096 {
01097 UINT p;
01098 if (cursorx+xofs>n) p=cursorx+xofs-n; else p=0;
01099 cursor_vput(p);
01100 return 1;
01101 }
01102
01103 UINT ht_text_viewer::cursor_right(UINT n)
01104 {
01105 cursor_vput(cursorx+xofs+n);
01106 return 1;
01107 }
01108
01109 void ht_text_viewer::cursor_home()
01110 {
01111 cursor_vput(0);
01112 }
01113
01114 void ht_text_viewer::cursor_end()
01115 {
01116 cursor_vput(get_line_vlength(top_line+cursory));
01117 }
01118
01119 void ht_text_viewer::cursor_pput(UINT dx)
01120 {
01121 UINT vx = 0, px = 0;
01122 char line[1024];
01123 char *linep=line;
01124
01125 UINT linelen;
01126 if (!textfile->getline(top_line+cursory, 0, line, sizeof line, &linelen, NULL)) return;
01127
01128 while (linelen--) {
01129 if (px==dx) break;
01130 int k = char_vsize(*(linep++), vx);
01131 vx += k;
01132 px++;
01133 }
01134 vx += dx-px;
01135 if (xofs > vx) {
01136 cursorx = 0;
01137 xofs = vx;
01138 } else {
01139 cursorx = vx-xofs;
01140 if (cursorx > (UINT)size.w-1) {
01141 xofs += cursorx-(size.w-1);
01142 cursorx = size.w-1;
01143 }
01144 }
01145 }
01146
01147 void ht_text_viewer::cursor_set(text_viewer_pos *pos)
01148 {
01149 goto_line(pos->line);
01150 cursor_pput(pos->pofs);
01151 }
01152
01153 void ht_text_viewer::cursor_vput(UINT vx)
01154 {
01155 if ((vx>=xofs) && (vx<xofs+size.w)) {
01156 cursorx=vx-xofs;
01157 } else if (vx<xofs) {
01158 xofs=vx;
01159 cursorx=0;
01160 } else if (vx>(UINT)size.w-1) {
01161 xofs=vx-(size.w-1);
01162 cursorx=size.w-1;
01163 } else {
01164 xofs=0;
01165 cursorx=vx;
01166 }
01167 }
01168
01169 void ht_text_viewer::draw()
01170 {
01171
01172 #ifdef TIME_DRAW
01173 timer_handle h=new_timer();
01174 start_timer(h);
01175 #endif
01176 if (!textfile) return;
01177
01178 vcp bgcolor = get_bgcolor();
01179 vcp metacolor = lexer ? lexer->getcolor_syntax(palidx_syntax_meta) :
01180 VCP(VCP_BACKGROUND(bgcolor), VCP_BACKGROUND(bgcolor));
01181 bool drawmeta = (lexer != NULL);
01182 clear(bgcolor);
01183 char line[1024];
01184 text_viewer_pos pos;
01185 pos.line=top_line;
01186 int y;
01187 for (y=0; y<size.h; y++) {
01188 lexer_state state;
01189
01190 pos.pofs=0;
01191
01192
01193 UINT linelen;
01194 if (!textfile->getline(top_line+y, 0, line, sizeof line, &linelen, &state)) break;
01195 line[linelen]=0;
01196
01197 UINT x=0;
01198 if (lexer) {
01199 char *linep=(char*)line;
01200 UINT toklen;
01201 lexer_token tok;
01202 bool start_of_line=true;
01203 text_pos p;
01204 p.line=pos.line;
01205 p.pofs=pos.pofs;
01206 int prev_linelen = -1;
01207 while ((tok=lexer->gettoken(linep, linelen, p, start_of_line, &state, &toklen)) || (!*linep && (linelen>0))) {
01208 UINT k, i;
01209 UINT vtoklen=toklen;
01210 bool print=true;
01211 bool is_tab=((toklen==1) && (*linep=='\t'));
01212 vcp color=lexer->gettoken_color(tok);
01213
01214 if (is_tab) vtoklen=tab_size-x%tab_size;
01215
01216 if (x>=xofs) {
01217 k=x-xofs;
01218 i=0;
01219 if (k>(UINT)size.w-1) {
01220 break;
01221 }
01222 } else if (x+vtoklen>=xofs) {
01223 k=0;
01224 i=xofs-x;
01225 } else {
01226 print=false;
01227 }
01228 if (print) {
01229 if (is_tab) {
01230 char tab[17];
01231 UINT z;
01232 for (z=0; z<vtoklen; z++) tab[z]=' ';
01233 tab[z]=0;
01234 render_str(k, y, color, &pos, vtoklen-i, tab+i, false);
01235 } else {
01236 render_str(k, y, color, &pos, vtoklen-i, linep+i, true);
01237 }
01238 }
01239 x+=vtoklen;
01240 linep+=toklen;
01241 linelen-=toklen;
01242 pos.pofs+=toklen;
01243 start_of_line=false;
01244 p.line=pos.line;
01245 p.pofs=pos.pofs;
01246 if (!linelen && !prev_linelen) break;
01247 prev_linelen = linelen;
01248 }
01249 if (drawmeta) render_meta(x-xofs, y, &pos, metacolor);
01250 } else {
01251 char *linep=line;
01252 while (linelen--) {
01253 UINT vtoklen=char_vsize(*linep, x);
01254 if (x>=xofs) {
01255 if (x-xofs>(UINT)size.w-1) break;
01256 if (*linep=='\t') {
01257 vcp c=bgcolor;
01258 char tab[17];
01259 UINT z;
01260 for (z=0; z<vtoklen; z++) tab[z]=' ';
01261 tab[z]=0;
01262 render_str_color(&c, &pos);
01263 buf_lprint(x-xofs, y, c, z, tab);
01264 } else {
01265 vcp c=bgcolor;
01266 render_str_color(&c, &pos);
01267 buf_printchar(x-xofs, y, c, *linep);
01268 }
01269 }
01270 x+=vtoklen;
01271 linep++;
01272 pos.pofs++;
01273 }
01274 if (drawmeta) render_meta(x-xofs, y, &pos, metacolor);
01275 }
01276 if (highlight_wrap && (pos.line<sel_end.line) &&
01277 (pos.line>=sel_start.line)) {
01278 int q = (drawmeta && show_EOL) ? strlen(EOL_string) : 0;
01279 int p = (x+q>xofs) ? x+q-xofs : 0;
01280 fill(p, y, size.w-p, 1, getcolor(palidx_generic_input_selected), ' ');
01281 }
01282 pos.line++;
01283 }
01284 if (focused) setcursor(cursorx, cursory, get_cursor_mode());
01285
01286 #ifdef TIME_DRAW
01287 stop_timer(h);
01288 int tix=get_timer_tick(h);
01289 delete_timer(h);
01290 buf_printf(40, 0, bgcolor, "%dtix", tix);
01291 #endif
01292 }
01293
01294 char *ht_text_viewer::func(UINT i, bool execute)
01295 {
01296 switch (i) {
01297 case 5: {
01298 if (execute) {
01299 sendmsg(cmd_text_viewer_goto);
01300 }
01301 return "goto";
01302 }
01303 case 7: {
01304 if (execute) {
01305 text_viewer_pos end_pos;
01306 UINT search_caps = SEARCHMODE_BIN;
01307 ht_search_request *request = text_search_dialog(this, search_caps, &end_pos);
01308 ht_search_result *result = NULL;
01309 if (request) {
01310 text_search_pos start, end;
01311 text_viewer_pos cursor;
01312 get_cursor_pos(&cursor);
01313 pos_to_offset(&cursor, &start.offset);
01314 end.offset = 0xffffffff;
01315 result = search(request, &start, &end);
01316 if (result) {
01317
01318 if (!show_search_result(result)) infobox("couldn't display result (internal error)");
01319 delete result;
01320 } else infobox("not found");
01321 }
01322 }
01323 return "search";
01324 }
01325 default:
01326 return false;
01327 }
01328 return 0;
01329 }
01330
01331 vcp ht_text_viewer::get_bgcolor()
01332 {
01333 return getcolor(palidx_generic_body);
01334 }
01335
01336 void ht_text_viewer::get_cursor_pos(text_viewer_pos *cursor)
01337 {
01338 cursor->pofs = physical_cursorx();
01339 cursor->line = top_line+cursory;
01340 }
01341
01342 cursor_mode ht_text_viewer::get_cursor_mode()
01343 {
01344 return cm_normal;
01345 }
01346
01347 ht_syntax_lexer *ht_text_viewer::get_lexer()
01348 {
01349 return lexer;
01350 }
01351
01352
01353
01354
01355 UINT ht_text_viewer::get_line_indent(UINT line)
01356 {
01357 char s[1024];
01358 UINT i, r, j;
01359 textfile->getline(line, 0, s, 1024, &i, NULL);
01360 if (i==0) return 0xffffffff;
01361 j = r = 0;
01362 while (i && (s[j]==' ' || s[j]=='\t')) {
01363 r += char_vsize(s[j], r);
01364 j++;
01365 i--;
01366 }
01367
01368 if (i==0) return 0xffffffff;
01369
01370 return r;
01371 }
01372
01373 UINT ht_text_viewer::get_line_length(UINT line)
01374 {
01375 return textfile->getlinelength(line);
01376 }
01377
01378 UINT ht_text_viewer::get_line_vlength(UINT line)
01379 {
01380 char l[1024];
01381 char *linep=l;
01382 UINT vl=0;
01383
01384 UINT linelen;
01385 if (!textfile->getline(line, 0, l, sizeof l, &linelen, NULL)) return 0;
01386
01387 while (linelen--) vl+=char_vsize(*(linep++), vl);
01388
01389 return vl;
01390 }
01391
01392 void ht_text_viewer::get_pindicator_str(char *buf)
01393 {
01394
01395 ht_syntax_lexer *l = get_lexer();
01396 const char *ln = l ? l->getname() : NULL;
01397 buf += sprintf(buf, " %d:%d ", top_line+cursory+1, xofs+cursorx+1);
01398 if (ln) sprintf(buf, "(%s) ", ln);
01399 }
01400
01401 bool ht_text_viewer::get_vscrollbar_pos(int *pstart, int *psize)
01402 {
01403 return (scrollbar_pos(top_line, size.h, textfile->linecount(), pstart, psize));
01404 }
01405
01406 void ht_text_viewer::get_selection(text_viewer_pos *start, text_viewer_pos *end)
01407 {
01408 *start = sel_start;
01409 *end = sel_end;
01410 }
01411
01412 ht_textfile *ht_text_viewer::get_textfile()
01413 {
01414 return textfile;
01415 }
01416
01417 bool ht_text_viewer::get_hscrollbar_pos(int *pstart, int *psize)
01418 {
01419 return false;
01420 }
01421
01422 bool ht_text_viewer::goto_line(UINT line)
01423 {
01424 if (line >= textfile->linecount()) {
01425 return false;
01426 }
01427 if ((line >= top_line) && (line - top_line < (UINT)size.h)) {
01428 cursory = line - top_line;
01429 } else {
01430 cursory = 0;
01431 if (line > top_line) {
01432 top_line = line;
01433 cursor_down(cursor_up(size.h));
01434 } else {
01435 top_line = line;
01436 cursor_up(cursor_down(size.h));
01437 }
01438 }
01439 return true;
01440 }
01441
01442 void ht_text_viewer::handlemsg(htmsg *msg)
01443 {
01444 switch (msg->msg) {
01445 case msg_keypressed: {
01446 int k=msg->data1.integer;
01447 bool sel;
01448 switch (k) {
01449 case K_Alt_S:
01450 selectcursor=!selectcursor;
01451 clearmsg(msg);
01452 return;
01453 case K_Control_Shift_Right:
01454 case K_Control_Right: {
01455 sel=(k==K_Control_Shift_Right) != selectcursor;
01456 char line[1024];
01457 UINT linelen;
01458 UINT i=0;
01459 UINT px=physical_cursorx();
01460 bool phase = true;
01461 while (1) {
01462 if (!textfile->getline(top_line+cursory+i, 0, line, sizeof line, &linelen, NULL)) return;
01463 if (!linelen) {
01464 phase = false;
01465 } else while (px < linelen) {
01466 if (phase ^ ((line[px]>='0' && line[px]<='9') || (line[px]>='A' && line[px]<='Z') || (line[px]>='a' && line[px]<='z') || line[px]=='_')) {
01467 phase = !phase;
01468 if (phase) {
01469 if (sel) select_start();
01470 goto_line(top_line+cursory+i);
01471 cursor_pput(px);
01472 if (sel) select_end();
01473 dirtyview();
01474 clearmsg(msg);
01475 return;
01476 }
01477 }
01478 px++;
01479 }
01480 phase = false;
01481 px = 0;
01482 i++;
01483 }
01484 }
01485 case K_Control_Shift_Left:
01486 case K_Control_Left: {
01487 sel=(k==K_Control_Shift_Left) != selectcursor;
01488 char line[1024];
01489 UINT linelen;
01490 int i=top_line+cursory;
01491 UINT px=physical_cursorx();
01492 bool phase = true;
01493 while (i >= 0) {
01494 if (!textfile->getline(i, 0, line, sizeof line, &linelen, NULL)) return;
01495 if (px > linelen) px = linelen;
01496 while (px > 0) {
01497 if (phase ^ !((line[px-1]>='0' && line[px-1]<='9') || (line[px-1]>='A' && line[px-1]<='Z') || (line[px-1]>='a' && line[px-1]<='z') || line[px-1]=='_')) {
01498 phase = !phase;
01499 if (phase) goto bloed3;
01500 }
01501 px--;
01502 }
01503 if (!px && !phase) goto bloed3;
01504 px = (UINT)-1;
01505 i--;
01506 }
01507 return;
01508 bloed3:
01509 if (sel) select_start();
01510 goto_line(i);
01511 cursor_pput(px);
01512 if (sel) select_end();
01513 dirtyview();
01514 clearmsg(msg);
01515 return;
01516 }
01517 case K_Control_J:
01518 sendmsg(cmd_text_viewer_goto);
01519 clearmsg(msg);
01520 return;
01521 case K_Up:
01522 case K_Shift_Up:
01523 sel=(k==K_Shift_Up) != selectcursor;
01524 if (sel) select_start();
01525 cursor_up(1);
01526 if (sel) select_end();
01527 dirtyview();
01528 clearmsg(msg);
01529 return;
01530 case K_Down:
01531 case K_Shift_Down:
01532 sel=(k==K_Shift_Down) != selectcursor;
01533 if (sel) select_start();
01534 cursor_down(1);
01535 if (sel) select_end();
01536 dirtyview();
01537 clearmsg(msg);
01538 return;
01539 case K_Left:
01540 case K_Shift_Left:
01541 sel=(k==K_Shift_Left) != selectcursor;
01542 if (sel) select_start();
01543 cursor_left(1);
01544 if (sel) select_end();
01545 dirtyview();
01546 clearmsg(msg);
01547 return;
01548 case K_Right:
01549 case K_Shift_Right:
01550 sel=(k==K_Shift_Right) != selectcursor;
01551 if (sel) select_start();
01552 cursor_right(1);
01553 if (sel) select_end();
01554 dirtyview();
01555 clearmsg(msg);
01556 return;
01557 case K_PageUp:
01558 case K_Shift_PageUp:
01559 sel=(k==K_Shift_PageUp) != selectcursor;
01560 if (sel) select_start();
01561 scroll_up(size.h-1);
01562 if (sel) select_end();
01563 dirtyview();
01564 clearmsg(msg);
01565 return;
01566 case K_PageDown:
01567 case K_Shift_PageDown:
01568 sel=(k==K_Shift_PageDown) != selectcursor;
01569 if (sel) select_start();
01570 scroll_down(size.h-1);
01571 if (sel) select_end();
01572 dirtyview();
01573 clearmsg(msg);
01574 return;
01575 case K_Home:
01576 case K_Shift_Home:
01577 sel=(k==K_Shift_Home) != selectcursor;
01578 if (sel) select_start();
01579 cursor_home();
01580 if (sel) select_end();
01581 dirtyview();
01582 clearmsg(msg);
01583 return;
01584 case K_End:
01585 case K_Shift_End:
01586 sel=(k==K_Shift_End) != selectcursor;
01587 if (sel) select_start();
01588 cursor_end();
01589 if (sel) select_end();
01590 dirtyview();
01591 clearmsg(msg);
01592 return;
01593 case K_Control_PageUp:
01594 sel=selectcursor;
01595 if (sel) select_start();
01596 top_line=0;
01597 cursorx=0;
01598 cursory=0;
01599 if (sel) select_end();
01600 dirtyview();
01601 clearmsg(msg);
01602 return;
01603 case K_Control_PageDown:
01604 sel=selectcursor;
01605 if (sel) select_start();
01606 top_line=textfile->linecount()-1;
01607 cursory=0;
01608 cursor_pput(0);
01609 scroll_down(size.h-1);
01610 scroll_up(size.h-1);
01611 cursor_down(size.h-1);
01612 if (sel) select_end();
01613 dirtyview();
01614 clearmsg(msg);
01615 return;
01616 case K_Alt_C:
01617 case K_Control_Insert:
01618 sendmsg(cmd_edit_copy);
01619 clearmsg(msg);
01620 return;
01621 case K_Control_L:
01622 case K_Shift_F7:
01623 if (!continue_search()) infobox("no further matches");
01624 dirtyview();
01625 clearmsg(msg);
01626 return;
01627 }
01628 break;
01629 }
01630 case cmd_edit_copy: {
01631 clipboard_copy_cmd();
01632 clearmsg(msg);
01633 return;
01634 }
01635 case cmd_text_viewer_goto: {
01636 char line[1024];
01637 line[0]=0;
01638 if (inputbox("goto", "line", line, 1024)) {
01639 eval_scalar r;
01640 if (eval(&r, line, NULL, NULL, NULL)) {
01641 eval_int i;
01642 scalar_context_int(&r, &i);
01643 if (!i.value || !goto_line(QWORD_GET_INT(i.value)-1)) {
01644 errorbox("no such line: %d!", i.value);
01645 }
01646 }
01647 }
01648 return;
01649 }
01650 case cmd_text_viewer_change_highlight: {
01651 popup_change_highlight();
01652 dirtyview();
01653 clearmsg(msg);
01654 return;
01655 }
01656 case msg_get_scrollinfo: {
01657 switch (msg->data1.integer) {
01658 case gsi_pindicator: {
01659 get_pindicator_str((char*)msg->data2.ptr);
01660 clearmsg(msg);
01661 return;
01662 }
01663 case gsi_hscrollbar: {
01664 gsi_scrollbar_t *p=(gsi_scrollbar_t*)msg->data2.ptr;
01665 if (!get_hscrollbar_pos(&p->pstart, &p->psize)) {
01666 p->pstart = 0;
01667 p->psize = 100;
01668 }
01669 clearmsg(msg);
01670 return;
01671 }
01672 case gsi_vscrollbar: {
01673 gsi_scrollbar_t *p=(gsi_scrollbar_t*)msg->data2.ptr;
01674 if (!get_vscrollbar_pos(&p->pstart, &p->psize)) {
01675 p->pstart = 0;
01676 p->psize = 100;
01677 }
01678 clearmsg(msg);
01679 return;
01680 }
01681 }
01682 break;
01683 }
01684 case msg_funcexec:
01685 if (func(msg->data1.integer, 1)) {
01686 clearmsg(msg);
01687 return;
01688 }
01689 break;
01690 case msg_funcquery: {
01691 char *s=func(msg->data1.integer, 0);
01692 if (s) {
01693 msg->msg=msg_retval;
01694 msg->data1.str=s;
01695 }
01696 break;
01697 }
01698 }
01699 ht_view::handlemsg(msg);
01700 }
01701
01702 void ht_text_viewer::make_pos_physical(text_viewer_pos *p)
01703 {
01704 UINT l=textfile->getlinelength(p->line);
01705 if (p->pofs > l) p->pofs=l;
01706 }
01707
01708 void ht_text_viewer::normalize_selection()
01709 {
01710 if ((text_viewer_pos_compare(&sel_end, &sel_start)<=0)) {
01711 sel_start.line=0;
01712 sel_start.pofs=0;
01713 sel_end.line=0;
01714 sel_end.pofs=0;
01715 }
01716 }
01717
01718 UINT ht_text_viewer::physical_cursorx()
01719 {
01720 int vx=0, px=0, v=cursorx+xofs;
01721 char line[1024];
01722 char *linep=line;
01723
01724 UINT linelen;
01725 if (!textfile->getline(top_line+cursory, 0, line, sizeof line, &linelen, NULL)) return 0;
01726
01727 while (linelen--) {
01728 int k=char_vsize(*(linep++), vx);
01729 vx+=k;
01730 v-=k;
01731 if (v<0) break;
01732 px++;
01733 }
01734 if (v>0) px+=v;
01735
01736 return px;
01737 }
01738
01739 void ht_text_viewer::popup_change_highlight()
01740 {
01741 bounds b, c;
01742
01743 app->getbounds(&b);
01744
01745 b.x = (b.w - 40) / 2,
01746 b.y = (b.h - 8) / 2;
01747 b.w = 40;
01748 b.h = 8;
01749
01750 ht_dialog *d=new ht_dialog();
01751 d->init(&b, "change highlighting mode", FS_KILLER | FS_TITLE | FS_MOVE | FS_RESIZE);
01752
01753 b.x=0;
01754 b.y=0;
01755
01756
01757 c=b;
01758 c.x=0;
01759 c.y=1;
01760 c.w=b.w-2-c.x;
01761 c.h=b.h-2-c.y;
01762
01763 ht_itext_listbox *mode_input=new ht_itext_listbox();
01764 mode_input->init(&c);
01765
01766 mode_input->insert_str(-1, "no highlighting");
01767 UINT lc = lexers->count();
01768 int selected = -1;
01769 for (UINT i=0; i<lc; i++) {
01770 ht_syntax_lexer *l = (ht_syntax_lexer*)lexers->get(i);
01771 mode_input->insert_str(i, l->getname());
01772 if (lexer && (strcmp(lexer->getname(), l->getname()) == 0)) {
01773 selected = i+1;
01774 }
01775 }
01776 mode_input->update();
01777 if (selected >= 0) mode_input->gotoItemByPosition(selected);
01778 d->insert(mode_input);
01779
01780
01781 c=b;
01782 c.x=0;
01783 c.y=0;
01784 c.w=30;
01785 c.h=1;
01786
01787 ht_label *mode_text=new ht_label();
01788 mode_text->init(&c, "choose ~highlighting mode", mode_input);
01789
01790 d->insert(mode_text);
01791
01792 if (d->run(false)) {
01793 struct {
01794 ht_listbox_data type;
01795 } data;
01796
01797 d->databuf_get(&data, sizeof data);
01798
01799 ht_syntax_lexer *l = (ht_syntax_lexer*)lexers->get(
01800 mode_input->getID(data.type.cursor_ptr));
01801 set_lexer(l, false);
01802 }
01803
01804 d->done();
01805 delete d;
01806 }
01807
01808 bool ht_text_viewer::pos_to_offset(text_viewer_pos *pos, FILEOFS *ofs)
01809 {
01810 return textfile->convert_line2ofs(pos->line, pos->pofs, ofs);
01811 }
01812
01813 int ht_text_viewer::ppos_str(char *buf, UINT bufsize, text_viewer_pos *ppos)
01814 {
01815 return ht_snprintf(buf, bufsize, "some pos");
01816 }
01817
01818 void ht_text_viewer::render_meta(UINT x, UINT y, text_viewer_pos *pos, vcp color)
01819 {
01820 text_viewer_pos p=*pos;
01821 render_str_color(&color, &p);
01822 if (pos->line == textfile->linecount()-1) {
01823 if (show_EOF && EOF_string) buf_print(x, y, color, EOF_string);
01824 } else {
01825 if (show_EOL && EOL_string) buf_print(x, y, color, EOL_string);
01826 }
01827 }
01828
01829 void ht_text_viewer::render_str(int x, int y, vcp color, text_viewer_pos *pos, UINT len, char *str, bool multi)
01830 {
01831 if (((pos->line == sel_start.line) || (pos->line == sel_end.line)) &&
01832 (text_viewer_pos_compare(&sel_start, &sel_end) != 0)) {
01833 text_viewer_pos p=*pos;
01834 vcp c;
01835 while (len--) {
01836 c=color;
01837 render_str_color(&c, &p);
01838 buf_lprint0(x++, y, c, 1, str);
01839 str++;
01840 if (multi) p.pofs++;
01841 }
01842 } else {
01843 render_str_color(&color, pos);
01844 buf_lprint0(x, y, color, len, str);
01845 }
01846 }
01847
01848 int ht_text_viewer::buf_lprint0(int x, int y, int c, int l, char *text)
01849 {
01850 while (l--) {
01851 char s = *text;
01852 if (!s) s = ' ';
01853 buf_printchar(x++, y, c, s);
01854 text++;
01855 }
01856 return l;
01857 }
01858
01859 void ht_text_viewer::render_str_color(vcp *color, text_viewer_pos *pos)
01860 {
01861 if ((text_viewer_pos_compare(pos, &sel_start)>=0) &&
01862 (text_viewer_pos_compare(pos, &sel_end)<0)) {
01863 vcp selcolor = getcolor(palidx_generic_input_selected);
01864 *color=vcp_mix(*color, selcolor);
01865 }
01866 }
01867
01868 void ht_text_viewer::resize(int rw, int rh)
01869 {
01870 ht_view::resize(rw, rh);
01871 if ((int)cursorx>size.w-1) {
01872 xofs+=(int)cursorx-(size.w-1);
01873 cursorx=size.w-1;
01874 }
01875 if ((int)cursory>size.h-1) {
01876 top_line+=(int)cursory-(size.h-1);
01877 cursory=size.h-1;
01878 }
01879 }
01880
01881 UINT ht_text_viewer::scroll_up(UINT n)
01882 {
01883 if (top_line > n) top_line -= n; else {
01884 int q = top_line;
01885 top_line = 0;
01886 cursory = 0;
01887 return q;
01888 }
01889 return n;
01890 }
01891
01892 UINT ht_text_viewer::scroll_down(UINT n)
01893 {
01894 UINT lc=textfile->linecount();
01895 if (top_line+n+size.h <= lc) top_line+=n; else {
01896 if (lc-top_line>=(UINT)size.h) {
01897 int q=top_line;
01898 top_line=lc-size.h;
01899 cursory=size.h-1;
01900 return top_line-q;
01901 }
01902 cursory=lc-top_line-1;
01903 return 0;
01904 }
01905 return n;
01906 }
01907
01908 UINT ht_text_viewer::scroll_left(UINT n)
01909 {
01910 if (xofs>n) xofs-=n; else xofs=0;
01911 return n;
01912 }
01913
01914 UINT ht_text_viewer::scroll_right(UINT n)
01915 {
01916 xofs+=n;
01917 return n;
01918 }
01919
01920 ht_search_result *ht_text_viewer::search(ht_search_request *request, text_search_pos *s, text_search_pos *e)
01921 {
01922 if (request != last_search_request) {
01923 if (last_search_request) delete last_search_request;
01924 last_search_request = (ht_search_request*)request->duplicate();
01925 }
01926 last_search_end_ofs = e->offset;
01927
01928 switch (request->search_class) {
01929 case SC_PHYSICAL: {
01930 FILEOFS start = s->offset, end = e->offset;
01931 return linear_bin_search(request, start, end, textfile, 0, 0xffffffff);
01932 }
01933 }
01934 return NULL;
01935 }
01936
01937 void ht_text_viewer::select_add(text_viewer_pos *s, text_viewer_pos *e)
01938 {
01939 text_viewer_pos start=*s, end=*e;
01940 make_pos_physical(&start);
01941 make_pos_physical(&end);
01942 bool downward = true;
01943 if (text_viewer_pos_compare(&start, &end)>0) {
01944 text_viewer_pos temp=start;
01945 downward = false;
01946 start=end;
01947 end=temp;
01948 }
01949
01950 if ((text_viewer_pos_compare(&end, &sel_end)==0) && !downward) {
01951 sel_end=start;
01952 } else if ((text_viewer_pos_compare(&start, &sel_end)==0) && downward) {
01953 sel_end=end;
01954 } else if ((text_viewer_pos_compare(&end, &sel_start)==0) && !downward) {
01955 sel_start=start;
01956 } else if ((text_viewer_pos_compare(&start, &sel_start)==0) && downward){
01957 sel_start=end;
01958 } else {
01959 sel_start=start;
01960 sel_end=end;
01961 }
01962 if (text_viewer_pos_compare(&sel_start, &sel_end)>0) {
01963 text_viewer_pos temp=sel_start;
01964 sel_start=sel_end;
01965 sel_end=temp;
01966 }
01967 normalize_selection();
01968 }
01969
01970 void ht_text_viewer::select_clear()
01971 {
01972 sel_start.line=0;
01973 sel_start.pofs=0;
01974 sel_end.line=0;
01975 sel_end.pofs=0;
01976 }
01977
01978 void ht_text_viewer::select_set(text_viewer_pos *s, text_viewer_pos *e)
01979 {
01980 sel_start=*s;
01981 sel_end=*e;
01982 normalize_selection();
01983 }
01984
01985 void ht_text_viewer::select_start()
01986 {
01987 selectmode=true;
01988 selectstart.line=top_line+cursory;
01989 selectstart.pofs=physical_cursorx();
01990 }
01991
01992 void ht_text_viewer::select_end()
01993 {
01994 text_viewer_pos p;
01995 selectmode=false;
01996 p.line=top_line+cursory;
01997 p.pofs=physical_cursorx();
01998 select_add(&selectstart, &p);
01999 }
02000
02001 void ht_text_viewer::set_lexer(ht_syntax_lexer *l, bool own_l)
02002 {
02003 if (own_lexer) {
02004 lexer->done();
02005 delete lexer;
02006 }
02007
02008 if (l) l->config_changed();
02009 lexer=l;
02010 own_lexer=own_l;
02011 textfile->set_lexer(l);
02012 }
02013
02014 void ht_text_viewer::set_textfile(ht_textfile *t, bool own_t)
02015 {
02016 if (own_textfile) {
02017 textfile->done();
02018 delete textfile;
02019 }
02020 textfile=t;
02021 own_textfile=own_t;
02022 }
02023
02024 bool ht_text_viewer::show_search_result(ht_search_result *result)
02025 {
02026 switch (result->search_class) {
02027 case SC_PHYSICAL: {
02028 ht_physical_search_result *r = (ht_physical_search_result*)result;
02029 text_viewer_pos start, end;
02030 textfile->convert_ofs2line(r->offset, &start.line, &start.pofs);
02031 textfile->convert_ofs2line(r->offset+r->size, &end.line, &end.pofs);
02032 select_set(&start, &end);
02033 cursor_set(&start);
02034 }
02035 }
02036 return true;
02037 }
02038
02039
02040
02041
02042
02043 void ht_text_editor::init(bounds *b, bool own_t, ht_textfile *t, ht_list *l, UINT e)
02044 {
02045 ht_text_viewer::init(b, own_t, t, l);
02046 edit_options=e;
02047 if (edit_options & TEXTEDITOPT_UNDO) {
02048 undo_list = new ht_text_editor_undo();
02049 undo_list->init(1024*1024);
02050 } else {
02051 undo_list = NULL;
02052 }
02053 overwrite_mode = false;
02054
02055 show_EOL = true;
02056 show_EOF = true;
02057 }
02058
02059 void ht_text_editor::done()
02060 {
02061 if (undo_list) {
02062 undo_list->destroy();
02063 delete undo_list;
02064 }
02065 ht_text_viewer::done();
02066 }
02067
02068 void ht_text_editor::clipboard_cut_cmd()
02069 {
02070 clipboard_copy_cmd();
02071 clipboard_delete_cmd();
02072 }
02073
02074 void ht_text_editor::clipboard_delete_cmd()
02075 {
02076 if (sel_start.line || sel_start.pofs || sel_end.line || sel_end.pofs) {
02077 text_viewer_pos apos, bpos;
02078 UINT px=physical_cursorx();
02079 apos.line = top_line+cursory;
02080 apos.pofs = px;
02081 bpos = sel_start;
02082 textoperation_apply(new ht_undo_data_delete_block(&apos, &bpos, &sel_start, &sel_end));
02083 }
02084 }
02085
02086 void ht_text_editor::clipboard_paste_cmd()
02087 {
02088 UINT bsize = clipboard_getsize();
02089 void *block = malloc(bsize);
02090 clipboard_paste(block, bsize);
02091
02092 text_viewer_pos apos, bpos;
02093 UINT px=physical_cursorx();
02094 apos.line = bpos.line = top_line+cursory;
02095 apos.pofs = bpos.pofs = px;
02096 textoperation_apply(new ht_undo_data_insert_block(&apos, &bpos, block, bsize));
02097 free(block);
02098 }
02099
02100 bool ht_text_editor::concat_lines(UINT a)
02101 {
02102 UINT b=a+1;
02103 if (textfile->has_line(a) && textfile->has_line(b)) {
02104 UINT alen=textfile->getlinelength(a);
02105 char *aline=(char*)malloc(alen+1);
02106 UINT alinelen;
02107 textfile->getline(a, 0, aline, alen+1, &alinelen, NULL);
02108
02109 text_viewer_pos ss, se;
02110
02111 ss=sel_start;
02112 se=sel_end;
02113
02114 insert_chars(b, 0, aline, alinelen);
02115
02116 free(aline);
02117
02118 delete_lines(a, 1);
02119
02120 if (b>ss.line) {
02121 if (b==se.line) {
02122 se.pofs+=alen;
02123 se.line--;
02124 } else if (b<se.line) {
02125 se.line--;
02126 }
02127 } else {
02128 if (b==ss.line) {
02129 ss.pofs+=alen;
02130 }
02131 ss.line--;
02132 se.line--;
02133 }
02134
02135 sel_start=ss;
02136 sel_end=se;
02137 normalize_selection();
02138 return true;
02139 }
02140 return false;
02141 }
02142
02143 void ht_text_editor::config_changed()
02144 {
02145 auto_indent = get_config_dword("editor/auto indent");
02146 ht_text_viewer::config_changed();
02147 }
02148
02149 void ht_text_editor::delete_chars(UINT line, UINT ofs, UINT count)
02150 {
02151 text_viewer_pos pos;
02152 pos.line=line;
02153 pos.pofs=ofs;
02154 if ((sel_end.line==pos.line) &&
02155 (text_viewer_pos_compare(&pos, &sel_start)>=0) &&
02156 (text_viewer_pos_compare(&pos, &sel_end)<0)) {
02157 sel_end.pofs-=count;
02158 } else if ((sel_start.line==pos.line) &&
02159 (text_viewer_pos_compare(&pos, &sel_start)<0)) {
02160 sel_start.pofs-=count;
02161 if (sel_end.line==pos.line) sel_end.pofs-=count;
02162 }
02163 textfile->delete_chars(line, ofs, count);
02164 }
02165
02166 void ht_text_editor::delete_lines(UINT line, UINT count)
02167 {
02168 if (sel_start.line+1>line) sel_start.line--;
02169 if (sel_end.line>line) sel_end.line--;
02170 normalize_selection();
02171 textfile->delete_lines(line, count);
02172 }
02173
02174 char *ht_text_editor::func(UINT i, bool execute)
02175 {
02176 switch (i) {
02177 case 2:
02178 if (execute) {
02179 if (textfile->get_filename()) {
02180 sendmsg(cmd_file_save);
02181 } else {
02182
02183 app->sendmsg(cmd_file_saveas);
02184 bool dirty = true;
02185 textfile->cntl(FCNTL_MODS_IS_DIRTY, 0, 0x7fffffff, &dirty);
02186 if (undo_list && !dirty) {
02187 undo_list->mark_clean();
02188 }
02189 }
02190 }
02191 return "save";
02192
02193 }
02194 return ht_text_viewer::func(i, execute);
02195 }
02196
02197 vcp ht_text_editor::get_bgcolor()
02198 {
02199 return getcolor(focused ? palidx_generic_input_focused :
02200 palidx_generic_input_unfocused);
02201 }
02202
02203 cursor_mode ht_text_editor::get_cursor_mode()
02204 {
02205 return overwrite_mode ? cm_overwrite : cm_normal;
02206 }
02207
02208 void ht_text_editor::get_pindicator_str(char *buf)
02209 {
02210 ht_syntax_lexer *l = get_lexer();
02211 const char *ln = l ? l->getname() : NULL;
02212 bool dirty = true;
02213 textfile->cntl(FCNTL_MODS_IS_DIRTY, 0, 0x7fffffff, &dirty);
02214 buf += sprintf(buf, "%c%d:%d ", dirty ? '*' : ' ', top_line+cursory+1, xofs+cursorx+1);
02215 if (ln) sprintf(buf, "(%s) ", ln);
02216 }
02217
02218 void ht_text_editor::handlemsg(htmsg *msg)
02219 {
02220 switch (msg->msg) {
02221 case msg_keypressed: {
02222 int k=msg->data1.integer;
02223 switch (k) {
02224 case K_Insert: {
02225 overwrite_mode = !overwrite_mode;
02226 dirtyview();
02227 clearmsg(msg);
02228 return;
02229 }
02230 case K_Return: {
02231 UINT px=physical_cursorx();
02232 text_viewer_pos apos, bpos;
02233 apos.line = top_line+cursory;
02234 bpos.line = apos.line+1;
02235 apos.pofs = px;
02236 bpos.pofs = 0;
02237 if (auto_indent) {
02238 int i=0;
02239 bpos.pofs = 0xffffffff;
02240 while (bpos.pofs==0xffffffff && (apos.line-i) && (i<32)) {
02241 bpos.pofs = get_line_indent(apos.line-i);
02242 i++;
02243 }
02244 if (bpos.pofs == 0xffffffff) bpos.pofs = 0;
02245 }
02246 UINT indent = bpos.pofs;
02247 if (px >= get_line_length(top_line+cursory)) indent = 0;
02248 textoperation_apply(new ht_undo_data_split_line(&apos, &bpos, indent));
02249
02250 dirtyview();
02251 clearmsg(msg);
02252 return;
02253 }
02254 case K_Delete: {
02255 UINT cx=physical_cursorx();
02256 if (cx < get_line_length(top_line+cursory)) {
02257 UINT px=physical_cursorx();
02258 text_viewer_pos apos, bpos;
02259 char s[1024];
02260 apos.line = bpos.line = top_line+cursory;
02261 apos.pofs = px;
02262 bpos.pofs = px;
02263 UINT i;
02264 textfile->getline(apos.line, 0, s, 1024, &i, NULL);
02265 textoperation_apply(new ht_undo_data_delete_string2(&apos, &bpos, &s[px], 1));
02266 } else if (textfile->has_line(top_line+cursory+1)) {
02267 UINT px=physical_cursorx();
02268 text_viewer_pos apos, bpos;
02269 apos.line = top_line+cursory;
02270 apos.pofs = px;
02271 bpos.line = top_line+cursory;
02272 bpos.pofs = px;
02273 textoperation_apply(new ht_undo_data_join_line(&apos, &bpos));
02274 }
02275 dirtyview();
02276 clearmsg(msg);
02277 return;
02278 }
02279 case K_Backspace: {
02280 UINT cx=physical_cursorx();
02281 if (cx) {
02282 if (cx <= textfile->getlinelength(top_line+cursory)) {
02283 UINT px=physical_cursorx()-1;
02284 text_viewer_pos apos, bpos;
02285 char s[1024];
02286 apos.line = bpos.line = top_line+cursory;
02287 apos.pofs = cx;
02288 bpos.pofs = px;
02289 UINT i;
02290 textfile->getline(apos.line, 0, s, 1024, &i, NULL);
02291 textoperation_apply(new ht_undo_data_delete_string(&apos, &bpos, &s[px], 1));
02292 } else {
02293
02294 cursor_pput(cx-1);
02295 }
02296 } else {
02297 if (top_line+cursory) {
02298 UINT px=physical_cursorx();
02299 text_viewer_pos apos, bpos;
02300 apos.line = top_line+cursory;
02301 apos.pofs = px;
02302 bpos.line = apos.line-1;
02303 bpos.pofs = get_line_length(bpos.line);
02304 textoperation_apply(new ht_undo_data_join_line(&apos, &bpos));
02305 }
02306 }
02307 dirtyview();
02308 clearmsg(msg);
02309 return;
02310 }
02311 case K_Alt_U:
02312 case K_Alt_Backspace: {
02313 sendmsg(cmd_text_editor_undo);
02314 clearmsg(msg);
02315 return;
02316 }
02317 case K_Alt_R: {
02318 sendmsg(cmd_text_editor_redo);
02319 clearmsg(msg);
02320 return;
02321 }
02322 case K_Alt_V:
02323 case K_Shift_Insert:
02324 sendmsg(cmd_edit_paste);
02325 clearmsg(msg);
02326 return;
02327 case K_Alt_X:
02328 case K_Shift_Delete:
02329 sendmsg(cmd_edit_cut);
02330 clearmsg(msg);
02331 return;
02332 case K_Alt_D:
02333 case K_Control_Delete:
02334 sendmsg(cmd_edit_delete);
02335 clearmsg(msg);
02336 return;
02337 case K_Control_Y:
02338 sendmsg(cmd_text_editor_delete_line);
02339 clearmsg(msg);
02340 return;
02341 default: {
02342 int k=msg->data1.integer;
02343 if (((k>=' ') && (k<=255)) ||
02344 ((k=='\t') && (edit_options & TEXTEDITOPT_INPUTTABS))) {
02345 char s=k;
02346 text_viewer_pos apos, bpos;
02347 UINT px=physical_cursorx();
02348 apos.line = bpos.line = top_line+cursory;
02349 apos.pofs = px;
02350 bpos.pofs = px+1;
02351 if (overwrite_mode) {
02352 char old[1024];
02353 UINT i, j=0;
02354 textfile->getline(apos.line, 0, old, 1024, &i, NULL);
02355 if (i>px) j = 1;
02356 textoperation_apply(new ht_undo_data_overwrite_string(&apos, &bpos, &s, 1, &old[px], j));
02357 } else {
02358 textoperation_apply(new ht_undo_data_insert_string(&apos, &bpos, &s, 1));
02359 }
02360 dirtyview();
02361 clearmsg(msg);
02362 return;
02363 }
02364 }
02365 }
02366 break;
02367 }
02368 case msg_contextmenuquery: {
02369 ht_static_context_menu *m=new ht_static_context_menu();
02370 m->init("~Texteditor");
02371 if (undo_list) {
02372 char buf[30], buf2[20];
02373
02374
02375
02376
02377
02378 buf2[0] = 0;
02379 ht_snprintf(buf, sizeof buf, "~Undo %s", buf2);
02380
02381 m->insert_entry(buf, "Alt+U", cmd_text_editor_undo, 0, 1);
02382 m->insert_entry("~Redo", "Alt+R", cmd_text_editor_redo, 0, 1);
02383 m->insert_entry("~Protocol", "Alt+P", cmd_text_editor_protocol, K_Alt_P, 1);
02384 m->insert_separator();
02385 }
02386 m->insert_entry("Change ~highlight", "", cmd_text_viewer_change_highlight, 0, 1);
02387 m->insert_separator();
02388
02389 m->insert_entry("~Delete line", "Control+Y", cmd_text_editor_delete_line, 0, 1);
02390 m->insert_separator();
02391 m->insert_entry("~Go to line", "", cmd_text_viewer_goto, 0, 1);
02392 msg->msg = msg_retval;
02393 msg->data1.ptr = m;
02394 return;
02395 }
02396 case cmd_edit_paste: {
02397 clipboard_paste_cmd();
02398 dirtyview();
02399 clearmsg(msg);
02400 return;
02401 }
02402 case cmd_edit_cut: {
02403 clipboard_cut_cmd();
02404 dirtyview();
02405 clearmsg(msg);
02406 return;
02407 }
02408 case cmd_edit_delete: {
02409 clipboard_delete_cmd();
02410 dirtyview();
02411 clearmsg(msg);
02412 return;
02413 }
02414 case cmd_file_save: {
02415 if (save()) clearmsg(msg);
02416 return;
02417 }
02418 case cmd_text_editor_undo: {
02419 undo(true);
02420 dirtyview();
02421 clearmsg(msg);
02422 return;
02423 }
02424 case cmd_text_editor_redo: {
02425 redo();
02426 dirtyview();
02427 clearmsg(msg);
02428 return;
02429 }
02430 case cmd_text_editor_protocol: {
02431 show_protocol();
02432 dirtyview();
02433 clearmsg(msg);
02434 return;
02435 }
02436 case cmd_text_editor_delete_line: {
02437 if (top_line+cursory <= textfile->linecount()-1) {
02438 text_viewer_pos apos, bpos, a, b;
02439 apos.line = top_line+cursory;
02440 apos.pofs = physical_cursorx();
02441 bpos.line = top_line+cursory;
02442 bpos.pofs = 0;
02443 a.line = top_line+cursory;
02444 a.pofs = 0;
02445 b = a;
02446
02447 if (b.line+1 > textfile->linecount()-1) {
02448 b.pofs = textfile->getlinelength(b.line);
02449 } else {
02450 b.line++;
02451 }
02452 if ((a.line != b.line) || (a.pofs != b.pofs))
02453 textoperation_apply(new ht_undo_data_delete_block(&apos, &bpos, &a, &b));
02454 }
02455 dirtyview();
02456 clearmsg(msg);
02457 return;
02458 }
02459 }
02460 ht_text_viewer::handlemsg(msg);
02461 }
02462
02463 void ht_text_editor::indent(UINT line, UINT start, UINT size)
02464 {
02465 char *w = (char*)malloc(size);
02466 memset(w, ' ', size);
02467 textfile->insert_chars(line, start, w, size);
02468 free(w);
02469 }
02470
02471 void ht_text_editor::unindent(UINT line, UINT start, UINT size)
02472 {
02473 textfile->delete_chars(line, start, size);
02474 }
02475
02476 void ht_text_editor::insert_chars(UINT line, UINT ofs, void *chars, UINT len)
02477 {
02478 text_viewer_pos pos;
02479 pos.line=line;
02480 pos.pofs=ofs;
02481 if ((sel_end.line==pos.line) &&
02482 (text_viewer_pos_compare(&pos, &sel_start)>=0) &&
02483 (text_viewer_pos_compare(&pos, &sel_end)<0)) {
02484 sel_end.pofs+=len;
02485 } else if ((sel_start.line==pos.line) &&
02486 (text_viewer_pos_compare(&pos, &sel_start)<0)) {
02487 sel_start.pofs+=len;
02488 if (sel_end.line==pos.line) sel_end.pofs+=len;
02489 }
02490 textfile->insert_chars(line, ofs, chars, len);
02491 }
02492
02493 void ht_text_editor::insert_lines(UINT line, UINT count)
02494 {
02495 if (sel_start.line+1>line) sel_start.line++;
02496 if (sel_end.line>line) sel_end.line++;
02497 normalize_selection();
02498 textfile->insert_lines(line, count);
02499 }
02500
02501 bool ht_text_editor::save()
02502 {
02503 if (!textfile->get_filename()) return false;
02504 dirtyview();
02505
02506 ht_temp_file *temp = NULL;
02507
02508 temp = new ht_temp_file();
02509 temp->init(FAM_READ | FAM_WRITE);
02510
02511 if (temp->get_error()) {
02512 errorbox("error creating temporary file");
02513 return false;
02514 }
02515
02516 ht_streamfile *old = textfile->get_layered();
02517 char *oldname = strdup(textfile->get_filename());
02518
02519 old->seek(0);
02520 old->copy_to(temp);
02521
02522 pstat_t st1, st2;
02523 old->pstat(&st1);
02524 temp->pstat(&st2);
02525 if ((st1.size != st2.size) || (st1.size_high != st2.size_high)) {
02526 errorbox("couldn't write backup file - file not saved. (o=%d, t=%d)", st1.size_high, st2.size_high);
02527
02528 temp->done();
02529 delete temp;
02530
02531 free(oldname);
02532
02533 return false;
02534 }
02535
02536 textfile->set_layered_assume(temp, false);
02537
02538 old->set_access_mode(FAM_WRITE);
02539 old->truncate(0);
02540
02541 textfile->seek(0);
02542 textfile->copy_to(old);
02543
02544 old->set_access_mode(FAM_READ);
02545
02546 textfile->set_layered_assume(old, true);
02547
02548 temp->done();
02549 delete temp;
02550
02551 free(oldname);
02552
02553 if (undo_list) {
02554 undo_list->mark_clean();
02555 }
02556 return true;
02557 }
02558
02559 void ht_text_editor::show_protocol()
02560 {
02561 bounds c, b;
02562 app->getbounds(&c);
02563 b.w=c.w*5/6;
02564 UINT bh=b.h=c.h*5/6;
02565 b.x=(c.w-b.w)/2;
02566 b.y=(c.h-b.h)/2;
02567 ht_dialog *dialog;
02568 NEW_OBJECT(dialog, ht_dialog, &b, "protocol", FS_KILLER | FS_TITLE | FS_MOVE);
02569 BOUNDS_ASSIGN(b, 1, 0, b.w-4, 1);
02570 ht_statictext *text;
02571 NEW_OBJECT(text, ht_statictext, &b, " ", align_left);
02572 dialog->insert(text);
02573 b.y = 1;
02574 b.h = bh-5;
02575 ht_text_listbox *list;
02576 NEW_OBJECT(list, ht_text_listbox, &b, 2, 1);
02577 UINT cp = undo_list->get_current_position();
02578 char *od;
02579 if (undo_list->is_clean(0)) {
02580 od = "disk";
02581 } else {
02582 od = " ";
02583 }
02584 list->insert_str(0, od, "--- initial state ---");
02585 for (UINT i=0; i<undo_list->count(); i++) {
02586 char buf[1024];
02587 ((ht_undo_data*)undo_list->get(i))->gettext(buf, 1024);
02588 if (undo_list->is_clean(i+1)) {
02589 od = "disk";
02590 } else {
02591 od = " ";
02592 }
02593 list->insert_str(i+1, od, buf);
02594 }
02595 list->update();
02596 list->gotoItemByPosition(cp);
02597 dialog->insert(list);
02598 if (dialog->run(false) == button_ok) {
02599 ht_listbox_data d;
02600 list->databuf_get(&d, sizeof d);
02601 int a = list->getID(d.cursor_ptr);
02602 int b = cp;
02603
02604 if (a-b < 0) {
02605 for (int i=0; i < b-a; i++) {
02606 undo(false);
02607 }
02608 } else if (a-b > 0) {
02609 for (int i=0; i < a-b; i++) {
02610 redo();
02611 }
02612 }
02613 }
02614 dialog->done();
02615 delete dialog;
02616 }
02617
02618 void ht_text_editor::split_line(UINT a, UINT pos)
02619 {
02620 UINT l=textfile->getlinelength(a);
02621 if (pos>l) pos=l;
02622 char *aline=(char*)malloc(pos+1);
02623 UINT alinelen;
02624 textfile->getline(a, 0, aline, pos+1, &alinelen, NULL);
02625
02626 text_viewer_pos p, ss, se;
02627 p.line=a;
02628 p.pofs=pos;
02629
02630 ss=sel_start;
02631 se=sel_end;
02632
02633 insert_lines(a, 1);
02634 insert_chars(a, 0, aline, alinelen);
02635
02636 delete_chars(a+1, 0, pos);
02637 free(aline);
02638
02639 if (text_viewer_pos_compare(&p, &ss)>0) {
02640 if (text_viewer_pos_compare(&p, &se)<0) {
02641 if (se.line==p.line) {
02642 se.pofs-=pos;
02643 }
02644 se.line++;
02645 }
02646 } else {
02647 if (ss.line==p.line) {
02648 ss.pofs-=pos;
02649 }
02650 ss.line++;
02651 se.line++;
02652 }
02653 sel_start=ss;
02654 sel_end=se;
02655 normalize_selection();
02656 }
02657
02658 void ht_text_editor::textoperation_apply(ht_undo_data *ud)
02659 {
02660 if (undo_list) {
02661 undo_list->insert_undo(this, ud);
02662 } else {
02663 ud->apply(this);
02664 delete ud;
02665 }
02666 }
02667
02668 void ht_text_editor::redo()
02669 {
02670 if (undo_list) {
02671 undo_list->redo(this);
02672 if (undo_list->is_clean()) textfile->cntl(FCNTL_MODS_CLEAR_DIRTY);
02673 }
02674 }
02675
02676 void ht_text_editor::undo(bool place_cursor_first)
02677 {
02678 if (undo_list) {
02679 undo_list->undo(this, place_cursor_first);
02680 if (undo_list->is_clean()) textfile->cntl(FCNTL_MODS_CLEAR_DIRTY);
02681 }
02682 }
02683