markierung (auch über mehrere zeilen einrücken)
-
TRichEdit::SelStart
-
Aber wohin dirigier ich den?
Im Grund interessiert mich in der '?'-Zeile nur, ob RE->SelStart kleiner als Ziel bleibt. Ist diese Bedingung nicht mehr erfüllt,
RE->SelStar = Ziel; // feddisch :p
void __fastcall TChild::TextBlockClick(TObject *Sender) { int Start=RE->SelStart, Ziel=RE->SelLength; RE->SelText = "\t" + RE->SelText; RE->SelStart = Start; //RE->SelStart = ?; }
Ab der 2. Zeile würde ich dem Text "\n\t" voransetzen. Gelingt es, bis dahin zu kommen, ließe sich ab da alles in einer Schleife regeln.
-
Probier mal selbjenes:
AnsiString Text(RichEdit1->SelText); Text.Insert("\t", 1); RichEdit1->SelText=StringReplace(Text, "\n", "\n\t", TReplaceFlags()<<rfReplaceAll);
Mußt das evtl. noch ein wenig "verfeinern" ...
-
Herrliche Idee, @Peter. Das gibt schon mal die Funktionen Absätze einrücken, Absätze trennen, trennen und einrücken. Wieder ein paar Features, die der "gemeine Notepad" nicht hat; das wollte einfach eingebaut werden. :p
In der Überlegung war übrigens ein denkfehler. Die erste Zeile braucht auch ein "\n" vor dem "\t", wenn noch keins vorhanden ist. Sonst verschiebt sich ja der Blockanfang beim weiter bearbeiten.
Die Funktion tut halt nur was bei vorhandenen "\n". Die Kunst wäre, die reinzubringen. RE dürfte beim WordWrap kein Steuerzeichen anlegen, also erkennt man die virtuellen umbrüche nicht. Kann man nicht doch irgendwie auf die VK_DOWN zugreifen? Oder wie kann man das anders regeln?
-
In der Überlegung war übrigens ein Denkfehler
Ich sagte doch, "Mußt das evtl. noch ein wenig 'verfeinern'" ...
Hatte die Idee so zwischendurch und dachte, gibst mal ne kleine "Anregung". Jetzt bist Du wieder gefordert, ich muß mich "leider" mit anderen Aufgabenstellungen beschäftigen
-
Hähä, ich hatte ja meinen Denkansatz gemeint. :p Man kann ja auch innerhalb eines Absatzes eine Einrückung haben wollen.
Jo, ich kann nur rumschnüffeln, ob ich was find. Die Werkzeuge, die ich kenne, dürften nicht reichen. "Das ist ein Profiproblem", hat @ascii gesagt, und da dürfte was dran sein. In Sachen Stringmanipulation verhält sich AnsiString wie ein kompakter Klotz.
-
Da stellt sich mir die Frage, was ist ein "Profiproblem" bzw. was sind sogenannte Profis
?
Wenn Dir AnsiString ein "Klotz am Bein" ist, dann wandle das Teil doch um und schaff mit Pointer, wo ist das Problem
Immer flexibel und kreativ sein, dazu braucht es keinen Profi
-
Na ganz so is es wohl nicht. Immerhin blieb der Thread ungelöst offen. Die Frage, wie man die Cursor im Fließtext eine Zeile tiefer setzt, findet keine Antwort. Also wohl doch schwerer Stoff? :p
Ich muß zB. aufgeben. In was umwandeln? Worauf den Pointer? Und dann? Nee, da sind ja nur Unbekannte ohne Weg. Also vielleicht später irgendwann flexibel und kreativ sein.
-
Was heisst "ungelöst offen"? Der Lösungsansatz ist da, und da sich der ursprüngliche Poster (unhöflicherweise) nicht mehr gemeldet hat ist die Sache für ihn wohl erfolgreich abgeschlossen.
Und auch deine Zusatzfrage lässt sich aus den ursprünglichen Informationen und meinem Hinweis auf SelStart beantworten.
-
Danke, @Jansan. Im ersten Moment hatte mich deine Torpedoantwort verblüfft. Denn entweder ich komm nun drauf, oder ich bin zu dumm für den Job und soll sofort aufgeben. Tatsächlich sind alle benötigten Infos im Thread vorhanden. Die Übung ist frischlingsgeeignet - wenn man drauf kommt.
@Peter, auch dir noch mal Dank für "kreativ" und "flexibel". Beides sollte man sein im Showgeschäft. :p
PS: Ich weiß noch nicht, wie ich die ersten 40 Kamele der Frühjahrsernte aufteilen werde. Wahrscheinlich werde ich sie einfach losschicken, sie sollen selbst entscheiden.
-
Sorry @Jansen, hatte gehofft, mit dem neuen Thread die Übersichtlichkeit zu erhöhen. Eine Lösung ist vorhanden, natürlich kann da noch einiges verbessert werden.
Im wesentlichen geht es nun um das Problem, daß nach der Implementierung des StringReplace-Codes die Zeilen nicht mehr in voller Länge genutzt werden.
Beschreibung: In der Form liegt ein RichEdit (Txt), alClient, PlainText, WordWrap.
Darüber (genau gleich groß) ein Dummy, RichEdit (Druide), alClient, PlainText, WordWrap, Visible=false.Ich markier einen beliebigen Textblock, der Code rückt jede Zeile ein.
Den Dummy nutz ich, weil der Textblock während der Aktionen schwer/gar nicht zu erhalten war. Wär für bessere Tips und Ideen dankbar.
Da der Cursor-Vorschub zum nächsten Zeilenanfang in der while-Schleife an "\r\n" hängen blieb, ersetze ich im Druiden alle "\r\n" gegen "\n". Nun klappt - soweit getestet - jede Situation.
Problem: Die Zeilen im eingerückten Block werden kürzer. Also der Text füllt nun nicht mehr die vollen möglichen Zeilenlängen auf. Das ist auch so, wenn im Block kein "\r\n" vorhanden ist. - Ich kann mir den Grund nicht erklären. Bringt ggf. StringReplace da was durcheinander?
Kann jemand den Fehler erkennen? Der Ausrufezeichen-Kommentar markiert die Stelle:
void __fastcall TChild::TextBlockClick(TObject *Sender) { // Txt = RE, Druide = DummyRE // Textblock ist markiert int Start = Txt->SelStart, Ziel = Txt->SelLength; // vor dem Textblock ein '\n' ? if (Txt->Text.SubString(Start,1) == "\n") { // Zeile einrücken, Cursor an den Zeilenanfang Txt->SelText = "\t" + Txt->SelText; Txt->SelStart = Start; CarPYold = Txt->CaretPos.y; // Cursor eine Zeile tiefer // Blockende beibehalten while (Txt->CaretPos.y <= CarPYold) { Txt->SelStart++; Ziel--; } // '\t' kam dazu, also Ziel++; } // kein '\n' vor dem Textblock else if (Txt->Text.SubString(Start,1) != "\n") { // "\n" + Zeile einrücken, Cursor an den Zeilenanfang Txt->SelText = "\n\t" + Txt->SelText; Txt->SelStart = Start; CarPYold = Txt->CaretPos.y+1; // Cursor eine Zeile tiefer // Blockende beibehalten while (Txt->CaretPos.y <= CarPYold) { Txt->SelStart++; Ziel--; } // "\n\t" kam dazu, also Ziel+=2; } // Restlichen Textblock an Dummy übergeben Txt->SelLength = Ziel; Druide->Clear(); Druide->Text = Txt->SelText; // alle "\r\n" in "\n" umwandeln /* !!!! hilft, aber die Blockzeilen werden kürzer !!!! */ AnsiString Alles(Druide->Text); Druide->Text=StringReplace(Alles, "\r\n", "\n", TReplaceFlags()<<rfReplaceAll); Txt->ClearSelection(); // Bei minimal 2 Zeilen kannst du fehlerfrei arbeiten, also while (Druide->Lines->Count>1) { // bau mal ein ENTER + Einrückung Druide->SelStart = 0; Druide->SelectAll(); Druide->SelText = "\n\t" + Druide->SelText; Druide->SelStart = 0; CarPYold = Druide->CaretPos.y+1; // Cursor an den Zeilenanfang eine Zeile tiefer while (Druide->CaretPos.y <= CarPYold) { Druide->SelStart++; Druide->Refresh(); } CarPYold = Druide->CaretPos.y; // markier die erste Zeile und schick sie ans Haupt-RE Ziel = Druide->SelStart; Druide->SelStart = 0; Druide->SelLength = Ziel; Txt->SelText = Druide->SelText; Druide->ClearSelection(); } // Nun noch die letzte Leile zum Haupt-RE schicken Txt->SelText = "\n\t" + Druide->Text; }
Dank an @AndreasW für Tips, wie sich die Aufgabe ggf. kompakter lösen ließe. Aus seinem Vorschlag
RichEdit1->Lines->Strings[0]=RichEdit1->Lines->Strings[0].Insert("\t",1);
Hatte ich dann die "Prototyp"Zeile gebaut:
Txt->Lines->Strings[Txt->Perform(EM_EXLINEFROMCHAR,0,Txt->SelStart)] = Txt->Lines->Strings[Txt->Perform(EM_EXLINEFROMCHAR,0, Txt->SelStart)].Insert("\t",1);
Geht vielleicht noch besser.? Muß probieren.
Aber wie gesagt, das oben angesprochene Hauptprob bleibt, die nur etwa zu 3/4 genutzte Zeilenlänge. Hier die Lösung zu finden, wär mir besonders wichtig.
-
Bei meinem ersten Beitrag hatte ich eigentlich an sowas gedacht (ungetestet):
int sstart = RichEdit1->Perform(EM_EXLINEFROMCHAR, 0, RichEdit1->SelStart); int ssende = RichEdit1->Perform(EM_EXLINEFROMCHAR, 0, RichEdit1->SelStart + RichEdit1->SelLength); for (int i = sstart; i <= ssende; i++) RichEdit1->Lines->Strings[i].Insert("\t", 1);
-
*Hilfe*, ich will hier raus :p . @Jansen, einen so schönen Stein, wie er in deinen Garten paßt, gibt es wahrscheinlich gar nicht.
Absolut top und elegant! Der Trost, ich hab bei der aufwändigen Übung etwas Stringmanipulation gelernt.
Aber jetzt kommt das ganz gemeine Osterei
: Auge sagte sofort "jaaaa", Debugger sagt auch "ja", alles stimmt (logo!). Und doch, im Text rührt sich rein gar nix. Mit Sleep(200) und Refresh() konnte ich aber wenigstens was zum wackeln bringen.
Also Projekt geschlossen, alle .obj usw. raus, dann sogar PC-Neustart. Es nützt nichts, der Text wird kein bischen verändert - auch ohne die laufende IDE nicht. An was kann sowas liegen? (Kein Wunder, daß ich Stringmanipulation für schwierig halte).
-Wer hat noch dieses Prob bzw. + den BCB3? Ole, meine Finger sind unegal. Aber soooo sehr?
-
War, wie gesagt, ungetestet. Aber mit ein wenig Kombinationsgabe hättest du da auch selbst drauf kommen können, oder?
RichEdit1->Lines->Strings[i] = RichEdit1->Lines->Strings[i].Insert("\t", 1);
-
Hatte ich getestet, aber als Insert("\n\t", 1). Das schafft erst mal entsprechende Leerzeilen und setzt dann einmal den Tab, nützt also nix. Insert("\t", 1) geht aber, eben getestet.
Da muß was gehen. So ein schöner Algor, das geb ich so schnell nicht auf. Erst die Abfrage auf "\n" vor dem Block, dann je bearbeitete Zeile die Restmarkierung wieder aktivieren und mit SelText arbeiten. So in etwa...
-
Ich kann dir nicht folgen. Inzwischen konnte ich es testen, das o.g. Beipiel (mit der korrigierten Zuweisung) funktioniert einwandfrei.
Was hast du nur immer mit dem \n, das ist doch völlig irrelevant!?
-
Hmmm, hast du mal in eine der eingerückten Zeilen (oder ggf. auch in der davor) weiteren Text geschrieben? Der Text kann sich auch über mehrere Zeilen hinweg verschieben, die Tabs sind nicht mehr am Zeilenanfang.
Ich will voneinander unabhängige Zeilen schaffen. Wenn dann eine Zeile länger wird und eine Folgezeile bildet, kann man die gezielt behandeln. Aber die geschaffene Gesamtstruktur bleibt erhalten. Darauf kommt es mir an.
-
"Und sie bewegt sich doch!"
"\v" ist nicht "\n", kann aber zu "\n" werden. :p Mit dem Trick klappt das Separieren der Zeilen.
// Alle Zeilen im markierten Block einrücken und voeinander trennen void __fastcall TChild::TextBlockClick(TObject *Sender) { // Txt ist ein RichEdit int Start = Txt->SelStart, Ende = Txt->SelLength, ssPlus = 0; int bkStart = Txt->Perform(EM_EXLINEFROMCHAR,0,Txt->SelStart); int bkEnde = Txt->Perform(EM_EXLINEFROMCHAR,0,Txt->SelStart+Txt->SelLength); for (int i = bkStart; i <= bkEnde; i++) // wenn der Block an einem Absatz begint if (i == bkStart && Txt->Text.SubString(Start,1) == "\n") { Txt->Lines->Strings[i] = Txt->Lines->Strings[i].Insert("\t",1); // Blocklängenkorrektur ssPlus += 1; } // sonst bzw. alle weiteren Zeilen else { Txt->Lines->Strings[i] = Txt->Lines->Strings[i].Insert("\r\v\t",1); // Blocklängenkorrektur ssPlus += 3; } // nochmal den Block markieren Txt->SelStart = Start; Txt->SelLength = (Ende + ssPlus); // die "\v" in "\n" umwandeln Txt->SelText=StringReplace(Txt->SelText, "\v", "\n", TReplaceFlags()<<rfReplaceAll); }
Letzter Schönheitsfehler: Läuft der Block über Absätze hinweg, entstehen zusätzliche Leerzeilen. Hat jemand 'ne Idee, wie sich das vermeiden läßt? Ich find die richtige Abfrage nicht. Txt->Lines->Strings[i].Trim(); hilft nicht.
-
Niemand 'ne Idee? Hätte es gern in der Schleife geregelt, aber am Schluß der Routine geht auch:
// Leerzeilen entfernen Txt->SelStart = Start; Txt->SelLength = (Ende + ssPlus); Txt->SelText=StringReplace(Txt->SelText, "\n\r\n", "\n", TReplaceFlags()<<rfReplaceAll);
Vorhanden gewesene Leerzeilen bleiben dabei erhalten - wie es sein soll.
-Insgesamt stehen also die beiden Wege mit und ohne Absatzseparierung zur Verfügung... falls es jemand interessiert... (ziemlich verdatenbankt, die wüste Jejend hier
).