I gave up and took your advice and wrote my own Find dialog which works as I need it. :)
I may have to. Just frustrating that the standard dialog and finder approach worked fine before in WPT5 but doesn't now and I cant figure out why. I just want this project of mine to be done. Arg.
In WPT5 I did not need to use SetFocus. I was just trying to figure out why I have to manually focus the WPRichText in WPT9 using the standard dialog. Still haven't figured that out yet. The manual focus only works after the standard dialog itself (not your OnSerchReplaceMessage event) reports that it didn't find anything (even thought there is a match). After that dialog, I can click in the WPRichText and select find, and it works until the next load of the WPRichText.
I tried your new dialog, and that issue is not present which is good, but have other questions/problems:
Why does TWPSearchReplaceDialog always start with the Replace checkbox set even though my options are all cleared? I might want to find, not replace:
WPSearchReplaceDlg1.Options := ;
I ended up replacing my first few test texts with blank when I thought I was just doing a find. But was lucky it didn't save because the replace didn't invoke the OnChange event, which is a problem in itself.
When doing a replace, the Count parameter in the OnSearchRepalceMessage is always 0. What is this Count? I can't find anything in the documentation, sorry.
Is there a way to autofill the search box? For example, if my text cursor is on a word, I'd like to just do a CRTL+F and the dialog pops up with that word (WPRichText1.CPWord) in the dialog.
One last thing for future consideration in your dialogs...
As I've mention in the past, it would be nice if you allow the programmer to decide where the dialog should show up. Currently it always shows up in the top left corner, which is not nice on a 4K 40" monitor when my app is a relatively small form in the middle of the monitor. Like the old baseball reference "out in left field" in the literal sense. ;)
Thanks, I will check out the new dialog.
FYI, I do not use TDBWPRichText, just the normal TWPRichText. My app does not lend itself to be able to use a TDBWPRichText. Depending on what the user is doing, I load a specific record from the database and need to analyze it before loading the TWPRichText. So for spell checking all the pertinent database records, I navigate through a TreeView and pull the corresponding record via a class object. I won't go into any more detail, but using TDBWPRichText is not an option.
So, when clicking next, and there are no more matches, I do the next treeview item that can be searched on and load that specific record. I need and event to tell me that no more matches exist in the current WPRichText where I can then move to the next treeview item and begin again, seamlessly. The treeview items are a random order of objects that relate to database objects, and the user needs to see where they are in relation to the treeview.
I did use Finder in WPT5 and it worked well. For WPT9 I even tried setting focus in code on the newly loaded WPRichText, but had the issue I mentioned in my previous post where I had to manually focus the WPRichText to get it to find next.
I will definitely try out the new dialog for both Find and Replace. I hope I also find how to keep a total count of the replacements as I was able to do in WPT5 (not real important, but can useful to the user).
Having trouble converting the Find and Replace dialogs.from WPT5 to WPT9 as they have changed. The only demo I found was the FindText demo, but it was not much help.
- In WPT5, FindDialog had multiple parameters to be able to pass in the TFindOptions, now it doesn't. The ReplaceDialog had TReplaceOptions and TotalCount, now it doesn't, but I need a total count.
- The old event OnTextNotFound is now OnSearchReplaceMessage.
I need to be able to find and/or replace text within multiple WPRichTexts from a database that get loaded into the same WPRichText in sequence (and saved as needed when using replace), and keep a total count of how many words were replaced.
Basically the same logic I used in WPT5 is not working in WPT9, The only changes were commenting out those parameters previously used in WPT5, and using that other event.
What is happening is that it works up until it finds a match, i.e. loops loading each WPRichText out of the database just fine. Once it finds a WPRichText with a match, it highlights it, but clicking on find next just displays its own "text not found" msg even if there are more matches in the current WPRichText and does not invoke the OnSearchReplaceMessage event in order for me to move on to the next WPRichText.
It is as if the find dialog is no longer hooked to the recently loaded WPRichText. If I then click in the currently loaded WPRichText, it then continues correctly until it loads a new RichText, where it then highlights the first match and gets "unhooked" again somehow. I'm thinking I'm missing something when reloading the WPRichText during a find that is currently in progress.
- Loads first/next WPRichText
- highlights the match if found else repeats step 1 because it invokes OnSearchReplaceMessage event
- click next
- displays its own "text not found", does not invoke the OnSearchReplaceMessage event
- I click inside the WPRichText
- Click next
- Highlights the match
- click next
- Highlights the next match if there is one, else goes to step 1 because it invokes OnSearchReplaceMessage event
c) it appends fine, cool.
b) Nice. That will most likely come in handy when our customers create what we call RapidRemarks (reusable comments). Single paragraphs with attributes could potentially be used.
c) Ahh, much better I think! I use LoadFromString and LoadFromStream elsewhere, so this makes it consistent. But still OK for appending? (which is what I'm doing here, not replacing). I'll find out.
I simply just need to cast it to a string it seems:
dest.SelectionAsString := string(src.AsANSIString('WPTOOLS'));
I traced it down to the exact statement which is stripping off the attributes of the last paragraph.
dest.SelectionAsString := src.AsString;
If I change it to this, it works:
dest.SelectionAsString := src.AsANSIString('WPTOOLS');
However, it gives me the warning "Implicit string cast from 'AnsiString' to 'String'
How to properly avoid that warning?
Seems I had changed that line during the initial re-write in order to avoid the warning. My bad.
Just tried that but did not affect the result. In the image shown below, the left side is the print preview of the assembled report. The right side is the editor view of the one topic. As you can see, there are paragraphs above the paragraph that lost its attributes.
Somehow there is more to it than that. Yes, the example I sent you falls into that category, and if I add things in front of that last paragraph in the example, it works OK. However, in my app itself, as long as the last paragraph has a border, it gets lost whether or not there is anything in front of it or not. I guess I need to track it down further and send you a new example once I figure out the process that is failing me. I thought I had it, but unfortunately for me I guess not. In my app it is all constructed during a complicated mailmerge, but my example does not do that, so maybe something there. I'll work on it some more, sigh.
Found what is causing my issue. It is the AppendAsSection operation. If I append a section where the last paragraph has a border, the border is lost. If the paragraph has anything after it, the border is not lost. I will send you an example.
In trying to create a demo, it turns out it has nothing directly to do with deleting trailing blank lines. It is in how the Contents.options is being handled differently now than it did in WPT5 during a merge. Something about using mmUseFirstLoadedParProps and/or mmUseFirst_AlsoBorders options I suspect, maybe others because for some reason I use different options at different phases of a merge building my final report from a bunch of layouts and other things. So, by still having a blank line after a paragraph with borders doesn't try to wipe out that bordered paragraph's properties with what is being merged in is my guess.
I seem to remember having a lot of issues with WPT5 in dealing with paragraphs taking on attributes of the merged paragraphs or vice-versa. So maybe something was fixed that I can now back out. I'll have to do some experimenting, not something I'm looking forward to due to the many hours I invested in the report generation over the years with so many different scenarios I had to troubleshoot. Basically the report generation of mine is a house of cards.
Consider this thread closed.
Yes, I always use the OnRequestHTTPImage event, except in one place where I can't (which I won't get into). I tried to find that event in the hint stuff earlier, but for some reason could not find it. With your instruction I easily found it this time and set up the event but it still does not work. Debugging shows that the event is invoked and the image is loaded into the TextObject, but it is not showing up in the hint. Below is the same code I use in my RapidRemark manager which works for a WPRichText.Code
- procedure TfrmWhisperReporter.RRHintRequestHTTPImage(
- RTFData: TWPRTFDataCollectionBase; Reader: TWPCustomTextReader;
- const LoadPath, url: string; TextObject: TWPTextObj; var Ok: Boolean);
- vImage: TRapidRemarkImage;
- vImageName: String;
- if fRapidRemarkHintID <= 0 then exit;
- if (TextObject.ObjRef <> nil) and (TextObject.ObjRef.StreamName <> '') then
- vImageName := TextObject.ObjRef.StreamName
- else if url <> '' then
- vImageName := url
- else if TextObject.ObjType = wpobjImage then
- vImageName := TextObject.Source;
- if vImageName = '' then exit;
- vImage := TRapidRemarkImage.Create();
- if not vImage.LoadBy(fRapidRemarkHintID, vImageName) then exit;
- TextObject.LoadObjFromStream(ExtractFileExt(vImage.Name), vImage.Image);
- Ok := true;
It is invoked this way:
The images are referenced by name only. I parse out the name and pull it in as a stream from our database. Here is the code I am trying to use when popping up a hint of what we call a RapidRemark. RapidRemarks are a list of saved comments users can quickly add to a report comment. They can click on a node in a tree view to see a view of the RapidRemark in a hint window before applying it to the report. Sometimes these saved comments include images or frequently used diagrams. This is the same basic code logic I use elsewhere to resolve linked images but tailored here for this hint stuff.Code
- procedure TfrmWhisperReporter.ShowRapidRemarkHint(pRapidRemark: TRapidRemark; pRect: TRect);
- procedure LoadImages();
- i: Integer;
- vImageName: String;
- vTextObj: TWPTextObj;
- function AddImage(): Boolean;
- vImage: TRapidRemarkImage;
- Result := false;
- vImage := TRapidRemarkImage.Create();
- if not vImage.LoadBy(pRapidRemark.ID, vImageName) then exit;
- Result := vTextObj.LoadObjFromStream(ExtractFileExt(vImage.Name), vImage.Image);
- for i := 0 to fRapidRemarkHint.RTF.RTFData.TextObjects.ObjCount-1 do
- vTextObj := fRapidRemarkHint.RTF.RTFData.TextObjects.ObjList[i];
- if (vTextObj <> nil) and ((vTextObj.ObjType = wpobjImage) or vTextObj.IsImage) then
- vImageName := '';
- if (vTextObj.ObjRef <> nil) and (vTextObj.ObjRef.StreamName <> '') then
- vImageName := vTextObj.ObjRef.StreamName
- else if vTextObj.ObjType = wpobjImage then
- vImageName := vTextObj.Source;
- if vImageName <> '' then
No problem on the Title stuff, but one last question I think. Is there a way to support linked images? Currently they show up as white space even though I loop through each of the TextObjects (RTF.RTFData.TextObjects) and load the linked image before it invokes the Draw method via the ShowHint method.
Maybe have a SetTextFromStream method, or Stream property for the official release? I load RTF from a database into a TMemoryStream which I usually load into a WPRichText using LoadFromStream.
So, using this is not as pretty as using aHint.Text assignment. Just saying.
Very nice! Exactly what I was looking for. Thank you!
Couple of things to point out... the hint title shows up in the middle (vertically) of the hint box. Pretty much behind the RTF text. I don't really need a title, so no big deal.
The other thing is I'd like to change the background color. If I use a dark VCL style, the black text is against a dark background.
What I got to work for the most part is to use this in the class Create constructor:
FRTFText.Memo.PaperColor := clInfoBk;
This produces the hint window with the clInfoBk colored rectangle, but draws the clInfoBk rectangle slightly lower within the hint window by a few pixels. You can see the VCL style background color behind the clInofBk rectangle, and the hint window outline and stem is covered by the clInfoBk rectangle at the bottom due to the offset. If I don't use that PaperColor setting, this does not happen.
So, is this probably not the right way to set the background color?
FWIW, if I change your Draw statement to this, it lines up correctly whether using the PaperColor or not:
Wow, awesome! Wasn't expecting this response! I'll give it a shot as well as looking forward to the next update.