#include #include #include #include #include #include #include #define TEXT_TMP_SIZE 1024 static TString _TEXT_TMP(TEXT_TMP_SIZE); static char* TEXT_TMP = (char*)(const char*)_TEXT_TMP; class _HotSpot : public TObject { public: // TArray _spots; // tokenstrings char _bg, _fg; _HotSpot (char fg, char bg) { _fg = fg; _bg = bg; } virtual ~ _HotSpot () { } }; // @doc EXTERNAL // @mfunc Permette di settare gli hot_spot void TTextfile::set_hotspots ( char fg, // @parm Colore di foreground da utilizzare per l'hotspot char bg) // @parm Colore di background da utilizzare per l'hotspot // @comm Aggiunge all'array gli hotspots relativi alla pagina in memoria // (come con xytext) { _HotSpot *hp = new _HotSpot (fg, bg); _hotspots.add (hp); } style TTextfile::_trans_style (char ch) { switch (ch) { case 'r': return normal; case 'i': return italic; case 'b': return bold; case 'u': return underlined; case 't': return tabbed; default: return normal; } } void TTextfile::_save_changes() { TWait_cursor hourglass; // fa i dovuti replace anche sul disco (solo replace di linee esistenti) long line = 0l; fclose(_index); remove(_indname); const TString oldfile(_filename); _filename.temp("txtf"); FILE* newf = fopen(_filename, "a+"); if ((_index = fopen(_indname, "w+b")) == NULL || newf == NULL) { NFCHECK("Impossibile aprire files temporanei"); freeze(); return; } // fseek(_instr, 0l, SEEK_SET); rewind(_instr); while (!feof(_instr)) { const long l = ftell(newf); fwrite (&l, sizeof(long), 1, _index); if (ferror(_index) || ferror(newf)) { error_box ("Errore di scrittura file temporaneo: scrittura interrotta"); freeze (); } if (fgets(TEXT_TMP, TEXT_TMP_SIZE, _instr) == NULL) break; if (line >= _page_start && line <= _page_end) { TString& lin = (TString&)(_page[(int)(line - _page_start)]); if (_dirty_lines[line - _page_start]) { strcpy(TEXT_TMP, lin); strcat(TEXT_TMP, "\n"); } } fprintf(newf, "%s", TEXT_TMP); line++; } fflush(_index); fclose(_instr); fclose(newf); remove(oldfile); rename(_filename, oldfile); _filename = oldfile; _instr = fopen(_filename, "a+"); } void TTextfile::_read_page (long n) { if (_dirty_lines.ones() > 0l) { _save_changes(); _dirty_lines.reset(); } switch (_direction) { case down: _page_start = n; break; case up: _page_start = n + _page_size; break; case updown: _page_start = n - (_page_size / 2l); break; } if (_page_start < 0l) _page_start = 0l; if ((_page_start + _page_size) > _lines) _page_end = _lines - 1; else _page_end = _page_start + _page_size - 1; // zap hotspots _spots.destroy (); long l = 0l; if (_page_start != 0l) { fseek (_index, _page_start * (long) sizeof (long), SEEK_SET); fread (&l, sizeof (long), 1, _index); } fseek (_instr, l, SEEK_SET); for (long i = _page_start; i <= _page_end; i++) { if (feof (_instr)) break; fgets (TEXT_TMP, TEXT_TMP_SIZE, _instr); TEXT_TMP[strlen (TEXT_TMP) - 1] = '\0'; TString & ts = (TString &) _page[(int) (i - _page_start)]; CHECK(ts.size() > 0, "Corrupted string"); ts = TEXT_TMP; if (_interactive) { TString hcol (6); // find hotspots and compile list int len = 0; const char *cp; read_line (i, 0, FALSE); while (cp = piece ()) { for (int z = 0; z < _hotspots.items (); z++) { _HotSpot & hs = (_HotSpot &) _hotspots[z]; if (hs._fg == get_foreground () && hs._bg == get_background ()) { TToken_string *tts = new TToken_string (50); tts->add (i); // line number tts->add (len); tts->add (len + (int) strlen (cp)); tts->add (cp); tts->add (z); _spots.add (tts); break; } } len += strlen (cp); } } } } // @doc EXTERNAL // @mfunc Legge il testo formattato void TTextfile::read_line ( long n, // @parm Numero della linea da leggere long pos, // @parm Numero della colonna da leggere (mai utilizzata) bool pg) // @parm Fa si' che se la linea non e' nella pagina corrente non si faccia nulla, // diversamente rilegge una nuova pagina dal file. { CHECK (_isopen, "Attempt operation on closed file"); // CHECKD (n >= 0 && n < _lines, "Line not present", n); if (pg && !_in_page (n)) _read_page (n); TString *tp = (TString *) _page.objptr (int (n - _page_start)); if (tp == NULL) return; const char *sp = (const char *) (*tp); _item = 0; _line = ""; int ndx = 0, p = 0; bool first = TRUE; _cur_line = n; char ch; int col = ((int) 'w' << 8) | (int) 'n'; long stl = (long) col << 16; while (ch = *sp++) { if (ch == '<' && *(sp) == '@') { // merge field if rel != NULL; // else fill with whitespace const char * save_sp = sp; TToken_string id(80, '@'); sp++; bool terminated = FALSE; while ((ch = *sp++) != '>' && !terminated) if (ch != '\0') id << ch; else terminated = TRUE; if (!terminated) // Prosegue normalmente se non trova la matching > { // id contains tokenstring separated by @ // but with casinations for possible lack of spacing // add spaces if needed for (int i = 0; i < id.len(); i++) { if (id[i] == '@' && id [i+1] == '@') { id.insert(" ", i+1); i+= 2; } } // parse string int len; TString80 file; TString16 field; TString80 format; char just; file = id.get(); format = id.get(); len = (int)id.get_long(); just = id.get_char(); int pos = 0; if ((pos = file.find("->")) == -1) error_box("field specification error"); else { file.cut(pos); field = file.mid(pos+2); } if (len == 0) len = id.len(); TString txt(512); TFieldtypes type; if (_rel != NULL) { // retrieve file and field if (atoi(file) == 0) { // tabella TLocalisamfile& t = _rel->lfile(file); txt = t.get(field); type = t.curr().type(field); } else { TLocalisamfile& f = _rel->lfile(atoi(file)); txt = f.get(field); type = f.curr().type(field); } // apply format to date and number switch (type) { case _longfld: case _realfld: { real r(txt); txt = r.string(format); } break; case _datefld: { TDate dd(txt); TFormatted_date d(dd, format); txt = d.string(); } break; default: break; } // justify as requested if (txt.len() < len) { switch(type) { case _longfld: case _realfld: txt.right_just(len); break; default: txt.left_just(len); break; } } } else { txt.left_just(len); } // ficca il testo cola' dove si puote const txtlen = txt.len(); for (int k = 0; k < txtlen; k++) TEXT_TMP[ndx++] = txt[k]; } // see (!terminated) above else // Restore sp pointer { sp = save_sp; ch = '<'; } } if (ch == '@' || (ch == '$' && *(sp) == '[')) { if (!first && p >= pos) { CHECKD(ndx < TEXT_TMP_SIZE, "Bad TEXT_TMP index ", ndx); _styles[_item++] = stl; TEXT_TMP[ndx] = '\0'; _line.add (TEXT_TMP); ndx = 0; } while (ch && (ch == '@' || (ch == '$' && *sp == '['))) { if (ch == '@') // font style change ? { const char c = *sp++; if (c == '@') { _styles[_item++] = stl; _line.add("@"); } else { style sss = _trans_style (c); if (sss == normal) stl = (long) col << 16; else stl |= (long) sss; } } else if (ch == '$' && *sp == '[') // color change { ++sp; // eat '[' col = *sp++; ++sp; // eat ',' col |= ((int) (*sp++) << 8); ++sp; // eat ']' stl = (stl & 0x0000ffff) | ((long) col << 16); } ch = *sp++; } // while } if (ch && p >= pos) { CHECKD(ndx < TEXT_TMP_SIZE, "Bad TEXT_TMP index ", ndx); first = FALSE; TEXT_TMP[ndx++] = ch; } p++; } _styles[_item++] = stl; TEXT_TMP[ndx] = '\0'; _line.add(TEXT_TMP); _item = 0; } // @doc EXTERNAL // @mfunc Ritorna la stringa di caratteri senza formattazione // // @rdesc Ritorna la stringa di caratteri ed eventualmente con i campi sostituiti // se la relazione non e' NULL const char *TTextfile::line( long j, // @parm Riga di cui ritornare la stringa long pos, // @parm Colonna di cui ritornare la stringa int howmuch) // @parm Numero di caratteri utili della stringa da ritornare (default -1) // @comm Se

assume valore -1 ritorna tutta la pagina { if (_cur_line != j) read_line (j); *TEXT_TMP = '\0'; /* _line.restart (); for (int i = 0; i < _line.items (); i++) strcat (TEXT_TMP, (const char *) _line.get ()); */ FOR_EACH_TOKEN(_line, l) strcat (TEXT_TMP, l); if (howmuch != -1) { if (((unsigned int)pos+howmuch) < strlen(TEXT_TMP)) TEXT_TMP[pos+howmuch] = '\0'; } return strlen(TEXT_TMP) > (word)pos ? &(TEXT_TMP[pos]) : ""; } // @doc EXTERNAL // @mfunc Cerca una stringa di testo all'interno di una riga // // @rdesc Ritorna la posizione in cui e' stato rintracciato il testo. Ritorna -1 se non e' stato // trovato il testo passato. long TTextfile::search( const char* txt, // @parm Testo da cercare int& ret, // @parm Intero in cui posizionare la posizne del carattere long from, // @parm Posizione all'interno della riga da cui iniziare la ricerca (default 0) bool down, // @parm Indica se la ricerca va effettuata all'indietro: // // @flag TRUE | Ricerca dalla posizione indicata all'inizio della riga (default) // @flag FALSE | Ricerca dalla posizione indicata alla fine della riga bool casesens, // @parm Indica se ricerca il testo con criterio case sensitive (default FALSE) bool regexp) // @parm indica se considerare la stringa un'espressione regolare (def. FALSE) // @comm Cerca in una riga per volta rispettando i formati { TString lin(512); TString text(txt); if (!casesens) text.lower(); if (regexp && text.right(1) != "*") text << '*'; for (long i = from; down ? (i < lines()) : (i >= 0); down ? i++ : i--) { lin = line(i); if (!casesens) lin.lower(); if (regexp) { for (ret = 0; lin[ret]; ret++) { if (match(text, lin.mid(ret))) return i; } } else { ret = lin.find(text); if (ret >= 0) return i; } } return -1l; } // @doc EXTERNAL // @mfunc Sostituisce un testo all'interno di una riga // // @rdesc Ritorna la posizione in cui e' stato sostituito il testo. Ritorna -1 se non e' stata // fatto nessuna operazione di sostituzione. int TTextfile::replace( long l, // @parm Numero della riga nella quale sostituire il testo const char* txt, // @parm Test da inserire nella riga int pos, // @parm Posizione nella quale inserire il testo (default 0) int len) // @parm Lunghezza del testo da sostituire (default 0) { if (_cur_line != l) read_line(l); TString& line = (TString&)_page[int(l-_page_start)]; char ch; int i = 0, cnt = 0, skip = 0; bool sforating = FALSE; // here's a nice casin while(i < 256) { if (!sforating) { ch = line[i++]; if (ch == '\0') sforating = TRUE; else if (ch == '@' && strchr("ribuokt",line[i]) != NULL) { skip +=2; i++; cnt--; } else if (ch == '$' && line[i] == '[') { skip +=3; i++; cnt--; while(line[i++] != ']') if (line[i] == '\0') return -1; else skip++; } } if (cnt == pos) { line.overwrite(txt, cnt+skip); _dirty_lines.set(l-_page_start); return cnt; } else cnt++; } return -1; } // @doc EXTERNAL // @mfunc Ritorna la stringa di caratteri con la formattazione // // @rdesc La stringa ritornata ha la formattazione (ovvere tutti i @codes, in modo che anche // la printer la possa utilizzare) const char *TTextfile::line_formatted( long j) //parm Numero della riga da ritornare { if (_cur_line != j) read_line (j); TString* tp = (TString*)_page.objptr(int(j-_page_start)); strcpy(TEXT_TMP, (const char*)(*tp)); return TEXT_TMP; } long TTextfile::get_attribute (int pos) { long stl = 0; if (pos == -1) { CHECK (_item > 0, "must execute piece() before style()!"); stl = _styles[_item - 1]; } else { int x = 0, nd = 0; const char *c; _line.restart (); while ((c = _line.get ()) != NULL) { x += strlen (c); stl = _styles[nd++]; if ((x - 1) >= pos) break; } } return stl; } // @doc EXTERNAL // @mfunc Ritorna lo stile del piece // // @rdesc Ritorna un numero (vedi ) indicante lo stile int TTextfile::get_style ( int pos) // @parm Posizione del carattere di cui ritornare lo stile (default -1) // @comm Se viene passato a

valore -1 ritorna lo stile di tutta la piece, altrimente // solamente quello del carattere selezionato. { const long x = get_attribute(pos) & ~tabbed; return (int) (x & 0x0000ffff); } // @doc EXTERNAL // @mfunc Ritorna il colore di background del piece // // @rdesc Ritorna il codice del colore, NULL se il carattere non esiste char TTextfile::get_background ( int pos) // @parm Posizione del carattere di cui conoscere il colore di background (default -1) // @comm Se

e' minore di 0 ritorna il colore dell'intero piece, altrimenti solamente // quello del carattere selezionato. // // @xref { long x = get_attribute (pos); return (char) (x >> 24); } // @doc EXTERNAL // @mfunc Ritorna il colore di foreground del piece // // @rdesc Ritorna il codice del colore, NULL se il carattere non esiste char TTextfile::get_foreground ( int pos) // @parm Posizione del carattere di cui conoscere il colore di background (default -1) // @comm Se

e' minore di 0 ritorna il colore dell'intero piece, altrimenti solamente // quello del carattere selezionato. // // @xref { long x = get_attribute (pos); return (char) ((x >> 16) & 0x000000ff); } const char* TTextfile::piece() { const char* l = _line.get(_item); if (l == NULL) return NULL; _item++; return strcpy(TEXT_TMP, l); } // @doc EXTERNAL // @mfunc Ritorna la parola alla posizione indicata // // @rdesc Stringa indicante la parola cercate const char *TTextfile::word_at ( long x, // @parm Numero della parola da ritornare long y) // @parm Numero della linea su cui cercare la parole { CHECK (_isopen, "Attempt operation on closed file"); TString s (line (y)); int x2 = 0; if (x < s.len ()) { while (isspace (s[(int) x])) { if (x == (s.len () - 1) && y < (_lines - 1l)) { s = line (++y); x = 0l; } else if (x < (s.len () - 1)) x++; else break; } while (isalnum (s[(int) x])) TEXT_TMP[x2++] = s[(int) x++]; } TEXT_TMP[x2] = '\0'; return TEXT_TMP; } // @doc EXTERNAL // @mfunc Aggiunge una riga al text (con i formati del caso) // // @rdesc Ritorna il risultato dell'operazione: // // @flag TRUE | Se la riga e' stat aggiuntac correttamente // @flag FALSE | Se non e' riuscot ad aggiungere la riga bool TTextfile::append ( const char *l) // @parm Riga da aggiungere { CHECK (_isopen, "Attempt operation on closed file"); if (!_accept) return FALSE; fseek (_instr, 0l, SEEK_END); fseek (_index, 0l, SEEK_END); long cpos = ftell (_instr); fprintf (_instr, "%s\n", l); fwrite (&cpos, sizeof (long), 1, _index); if (ferror (_index) || ferror (_instr)) { error_box ("Errore di scrittura file temporaneo: scrittura interrotta"); freeze (); } fflush (_index); fflush (_instr); _lines++; _dirty = TRUE; if ((_lines) < (_page_start + _page_size)) { if (_interactive) { _page.add (new TString(l), int(_lines - _page_start - 1)); _page_end++; int len = 0; const char *cp; read_line (_lines - 1); while (cp = piece ()) { for (int z = 0; z < _hotspots.items (); z++) { _HotSpot & hs = (_HotSpot &) _hotspots[z]; if (hs._fg == get_foreground () && hs._bg == get_background ()) { TToken_string *tts = new TToken_string (50); tts->add (_lines - 1l); // line number // tts->add (len); tts->add (len + (int) strlen (cp)); tts->add (cp); tts->add (z); _spots.add (tts); break; } } len += strlen (cp); } } return TRUE; } return FALSE; } void TTextfile::close () { if (_isopen) { fclose (_instr); fclose (_index); _instr = _index = NULL; _isopen = FALSE; } } void TTextfile::print () { printer().print_txt(*this); } // @doc EXTERNAL // @mfunc Scrive il testo (non formattato) su file // // @rdesc Ritorna il risultato dell'operazione di scrittura // // @flag TRUE | Se e' riuscito a scrivere il testo // @flag FALSE | Se ha avuto qualche problema nella gestione del file in cui scrivere bool TTextfile::write ( const char *path, // @parm Nome del file in cui scrivere il testo TPoint * from, // @parm Punto da cui iniziare a scrivere il testo TPoint * to) // @parm Punto a cui terminare di scrivere il testo { bool ok = FALSE; FILE *fp; if ((fp = fopen (path, "w")) != NULL) { ok = TRUE; TString s(512); long starty = from == NULL ? 0l : from->y; int startx = from == NULL ? 0 : (int) from->x; long endy = to == NULL ? _lines - 1l : to->y; int endx = to == NULL ? -1 : (int) to->x; for (long j = starty; j <= endy; j++) { s = line(j); if (j == endy && endx == -1) endx = s.len(); if (j == starty && j == endy) s = s.sub(startx, endx); else if (j == starty) s = s.mid(startx); else if (j == endy) s = s.left(endx); fprintf(fp, "%s\n", (const char *) s); } fclose (fp); } else warning_box ("Impossibile scrivere il file %s; scrittura fallita", path); return ok; } // @doc EXTERNAL // @mfunc Scrive il testo da punto a punto (non formattato) su string_array void TTextfile::write( TString_array& arr, // @parm Array in cui scrivere il testo TPoint * from, // @parm Punto da cui iniziare a scrivere il testo TPoint * to) // @parm Punto a cui terminare di scrivere il testo { arr.destroy(); TString s(512); long starty = from == NULL ? 0l : from->y; int startx = from == NULL ? 0 : (int) from->x; long endy = to == NULL ? _lines - 1l : to->y; int endx = to == NULL ? -1 : (int) to->x; for (long j = starty; j <= endy; j++) { s = line(j); if (j == endy && endx == -1) endx = s.len(); if (j == starty && j == endy) s = s.sub(startx, endx); else if (j == starty) s = s.mid(startx); else if (j == endy) s = s.left(endx); arr.add(s); } } void TTextfile::destroy () { CHECK(_istemp, "destroy() chiamata su testo permanente!"); if (_page.items () > 0) { if (_index) fclose (_index); if (_instr) fclose (_instr); remove ((const char *) _filename); remove ((const char *) _indname); _page_start = _lines = 0l; _page_end = _cur_line = -1l; _accept = TRUE; _instr = fopen (_filename, "a+"); _indname.temp (); _index = fopen (_indname, "w+b"); if (_index == NULL || _instr == NULL) { error_box ("Impossibile aprire files temporanei"); freeze (); } _isopen = TRUE; // _page.destroy (); _spots.destroy (); } } TTextfile ::TTextfile (const char *file, int pagesize, direction preferred, bool interactive): _page_size (pagesize), _page (pagesize), _filename (file), _lines (0l), _index (NULL), _page_start (0l), _page_end (-1l), _direction (preferred), _dirty (FALSE), _istemp (FALSE), _item (0), _line (256), _cur_line (-1), _hotspots (4), _accept (TRUE), _dirty_lines(pagesize), _interactive(interactive), _rel(NULL) { // open file & build index if (file == NULL || *file <= ' ') { _filename.temp("txtf"); _istemp = TRUE; } for (int i = 0; i < pagesize; i++) _page.add(new TString(132)); _isopen = TRUE; _instr = fopen (_filename, "a+"); _indname.temp("txti"); _index = fopen (_indname, "w+b"); if (_index == NULL || _instr == NULL) { yesnofatal_box ("Impossibile aprire files temporanei"); freeze (); } if (!_istemp) while (!feof(_instr)) { const long l = ftell (_instr); fwrite (&l, sizeof (long), 1, _index); if (ferror(_index) || ferror(_instr)) { error_box("Errore di scrittura file temporaneo: scrittura interrotta"); freeze(); } if (fgets (TEXT_TMP, TEXT_TMP_SIZE, _instr) == NULL) break; // if (TEXT_TMP[strlen(TEXT_TMP)-1] == '\n') // TEXT_TMP[strlen(TEXT_TMP)-1] = '\0'; // if ((_lines) < (_page_start + _page_size)) // { // TString *ll = new TString (TEXT_TMP); // _page.add(ll); // _page_end++; // TBI process links // } _lines++; } } TTextfile::~TTextfile () { if (_index) fclose (_index); if (_instr) fclose (_instr); if (_istemp) remove ((const char *) _filename); remove ((const char *) _indname); }