Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

textedit.cc

Go to the documentation of this file.
00001 /*
00002  *      HT Editor
00003  *      textedit.cc
00004  *
00005  *      Copyright (C) 1999-2002 Sebastian Biallas (sb@web-productions.de)
00006  *      Copyright (C) 1999-2002 Stefan Weyergraf (stefan@weyergraf.de)
00007  *
00008  *      This program is free software; you can redistribute it and/or modify
00009  *      it under the terms of the GNU General Public License version 2 as
00010  *      published by the Free Software Foundation.
00011  *
00012  *      This program is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU General Public License
00018  *      along with this program; if not, write to the Free Software
00019  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 /*      if (test_str_to_ofs(&start->offset, d.start.text, d.start.textlen, format, "start-offset")
00058         && test_str_to_ofs(&end->offset, d.end.text, d.end.textlen, format, "end-offset")) {*/
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 /*      } else {
00063                 request = NULL;
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;                      // SC_*
00073         UINT search_mode_mask;          // SEARCHMODE_*
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 //      dialog->select_search_mode(lastsearchmodeid);
00118         
00119         if (dialog->run(false)) {
00120                 int modeid = dialog->get_search_modeid();
00121 //              lastsearchmodeid = modeid;
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 /* create history entry */
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 /* search */
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  *      CLASS ht_undo_data
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  *      CLASS ht_undo_data_delete_string
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  *      CLASS ht_undo_data_delete_string2
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  *      CLASS ht_undo_data_insert_string
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  *      CLASS ht_undo_data_overwrite_string
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  *      CLASS ht_undo_data_split_line
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  *      CLASS ht_undo_data_join_line
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  *      INSERT/DELETE BLOCK
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  *      CLASS ht_undo_data_insert_block
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  *      CLASS ht_undo_data_delete_block
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         // FIXME
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  *      CLASS ht_text_editor_undo
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                         // remove all pending redo's
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                         // clean_state eventually becomes unreachable
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  *      CLASS ht_text_viewer
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 //      set_lexer(l, own_l);
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 //#define TIME_DRAW
01172 #ifdef TIME_DRAW
01173         timer_handle h=new_timer();
01174         start_timer(h);
01175 #endif /* TIME_DRAW */
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 //FIXME:debug:          if (!textfile->getline((top_line+y)|0x80000000, line, sizeof line, &state)) break;
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 /* TIME_DRAW */
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                                                 // FIXME: !!!!!!!!?
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  * 0xffffffff --> ignore this line
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         // FIXME api
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 /* mode (input) */
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 /* mode (text) */
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         // FIXME: is this "the right thing"?
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  *      CLASS ht_text_editor
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                                         // FIXME: !! call undolist->mark_clean() !!
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                                                         // place cursor only
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                                 // FIXME: implementme correctly/better
02374 /*                              if (undo_list->current_position) {
02375                                         ((ht_undo_data*)undo_list->get(undo_list->current_position-1))->gettext(buf2, sizeof buf2);
02376                                 } else {
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                         // FIXME: somewhat hacked
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                                 // if last line
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 

Generated on Fri May 7 21:15:44 2004 by doxygen 1.3.5