3

I have notes in the database stored as html with the DHtml component. But I want to change format to RTF and use DevExpress TcxRichEdit instead because I feel that is a simpler component, more stable etc. Users have problems with dissapearing texts that I of course cannot reproduce.

TcxRichEdit is working fine and can save and load notes again. The problem is the old notes in html format. I have tried this routine but I never got it to work. An rtf string was generated but it was not accepted by TcxRichEdit.

Then I got the idea to use the clipboard. By having the DHtml and TcxRichEdit side by side I should copy and paste between them and let the clipboard do the actual conversion. In practice it was not so simple as I thought...

Here is some code:

  function _ConvertToRtf(aHtml: String; aRichEdit: TcxRichEdit): Boolean;
  begin
    Result := False;
    if (aHtml <> '') and (aHtml <> '&nbsp;') then
    begin
      if not AnsiStartsStr('{\rtf1', aHtml) then
      begin
        vHtmlDlg.InitDoc := aHtml;
        vHtmlDlg.Editor.Stop;
        if vHtmlDlg.Editor.SelectAll then
          if vHtmlDlg.Editor.CutToClipboard then
          begin
            aRichEdit.Clear;
            aRichEdit.PasteFromClipboard;
          end;
      end;

      Result := True;
    end;
  end;

Problem is that vHtmlDlg.Editor.SelectAll always return False.

function TCustomProfDHTMLEdit.SelectAll: Boolean;
const
  CGID_MSHTML: TGUID = '{DE4BA900-59CA-11CF-9592-444553540000}';
var
  D: IDispatch;
  CommandTarget: IOleCommandTarget;
  vaIn, vaOut: OleVariant;
  hr: HRESULT;
begin
  Result := False;
  if GetDOM(D) then
  try
    CommandTarget := D as IOleCommandTarget;
    hr := CommandTarget.Exec(@CGID_MSHTML, 31, OLECMDEXECOPT_DODEFAULT, vaIn, vaOut);
    Result := SUCCEEDED(hr)
  except
  end
end;

It is actually GetDOM that returns False:

function TProfDHTMLEdit2.GetDOM(out P: IDispatch): Boolean;
begin
  if Busy then
  begin
    P := nil;
    Result := False
  end
  else
    try
      P := (IDispatch(GetOleObject) as IWebBrowser2).Document;
      Result := True
    except
      P := nil;
      Result := False
    end
end;

No it is GetBusy that returns true...

function TProfDHTMLEdit2.GetBusy: Boolean;
begin
  if FDocumentCompleteReason <> dcrUndefined then
    Result := True
  else
    Result := False
end;

So I have tried the dig deeper and depper in the html component but I still don't understand why I cannot use SelectAll.

Here is a simplified version of how I initialize and use it.

  vHtmlDlg := TDhtmlEditorForm.Create(nil);
  vHtmlDlg.Show;
  vHtmlDlg.BrowseMode := False;
  try
   // Call ConvertToRtf with strings in a loop here 
  finally
    vHtmlDlg.Free;
  end;

Any idea why SelectAll returns false and didn't work ?

Edit1: One more thing. Docs for the html component are here http://www.profgrid.com/documentation/htmledit/ The stop command seems to stop loading an HTML page into the control. I used in by chance because in another place it prevents a lockup when loading data in html. It would anyway be real nice to get the conversion and get rid of the HTML component!

Edit2: I found the solution at last and it was quite simple. Just add the Dhtml component to a form at designtime instead of create it in code. That way the busy property was false and it simply works. There is no need to check busy in a while loop as this is done in SetSource method. I give jachquate the checkmark as he note me on that the component was asynced. The final code for convert a string look like this:

  function _ConvertToRtf(aHtml: String; aRichEdit: TcxRichEdit): Integer;
  begin
    if (aHtml <> '') and (aHtml <> '&nbsp;') then
    begin
      if not AnsiStartsStr('{\rtf1', aHtml) then
      begin
        DhtmlMemo.Source := aHtml;
        if DhtmlMemo.SelectAll then
          if DhtmlMemo.CutToClipboard then
          begin
            aRichEdit.Clear;
            aRichEdit.PasteFromClipboard;
          end;

        if VarIsNull(aRichEdit.EditValue) then
          Result := 0    // Not valid. The caller would delete the note.
        else
          Result := 2;   // String was converted
      end
      else
        Result := 1;     // String already in rtf. Do nothing.
    end
    else
      Result := 0;       
  end;

Thanks for the support and commitment on my problem!

Community
  • 1
  • 1
Roland Bengtsson
  • 5,058
  • 9
  • 58
  • 99

2 Answers2

2

You may have a chance if you put in a delay after the cut/copy, before the paste. But in general, using the clipboard like this is very bad practice. The clipboard is provided for the convenience of the user, not the programmer. Any clipboard-aware programs are going to react to this, including any remote desktop/citrix sessions which will be trying to pump this crud across the network.

Chris Thornton
  • 15,620
  • 5
  • 37
  • 62
  • I assume he's writing a small "converter" app that he's going to run once, so the interference with other apps is not a problem –  Mar 26 '11 at 11:13
2

As this is a one time conversion, I'm with you in regard to use the clipboard.

The HTML component looks like a kind of Async component, so you must have to wait, because it would be processing the loading/representing the provided HTML in other threads, all encapsulated by the component. I do not know the particular component, but I bet this will work:

  function _ConvertToRtf(aHtml: String; aRichEdit: TcxRichEdit): Boolean;
  begin
    Result := False;
    if (aHtml <> '') and (aHtml <> '&nbsp;') then
    begin
      if not AnsiStartsStr('{\rtf1', aHtml) then
      begin
        vHtmlDlg.InitDoc := aHtml;
        vHtmlDlg.Editor.Stop;
        //before or after stop, I'm not sure what stop means
        while vHtmlDlg.Busy do
          Sleep(1); // or maybe Application.ProcessMessages, try both
        if vHtmlDlg.Editor.SelectAll then
          if vHtmlDlg.Editor.CutToClipboard then
          begin
            aRichEdit.Clear;
            aRichEdit.PasteFromClipboard;
            Result := True;  //of course you return true only if this succeeds.
          end;
      end;
    end;
  end;

If this is a multiple-time conversion to be done on a user machine, read the @Chris answer.

Community
  • 1
  • 1
jachguate
  • 16,976
  • 3
  • 57
  • 98
  • Yes, it is a about 700 html-strings that need to be converted to RTF format. A bit boring to do that manually.... – Roland Bengtsson Mar 26 '11 at 06:33
  • And it is not on the users machine. It is on a server that have many users connected. So when version is changed with the new Richedit component I run a maintanance method that do the conversion in the database. After that the users can log in. – Roland Bengtsson Mar 26 '11 at 06:49
  • @Roland, did you try my suggestion? It looks to me like you have to get rid of the stop method and just wait (as suggested) until the component is not busy anymore – jachguate Mar 26 '11 at 06:55
  • While you can get it to work with While Busy Sleep I would rather write a class and hook into relevant WebBrowser events –  Mar 26 '11 at 11:26
  • @Daniel, just remember this is one time conversion of some hundreds (thousands?) records, IMHO the less effort solution is the best, as performance or correctness is not the most important thing. – jachguate Mar 26 '11 at 18:33