How do I get Welcome Page browser navigate to some URI from within OTA package wizard?
Asked Answered
S

3

10

What I'm trying to do is to create an ability to view (not to edit) the HTML pages included into project. The Welcome Page already has embedded web browser, so it appears to be a good candidate for that.

Curios why? Here is a question with background info.

Silver answered 9/11, 2011 at 23:1 Comment(1)
Hello everyone, and thank you for the your input on the problem! Sorry for delay, currently, i little preoccupied with matters of work and really hope what i will finish by tomorrow and finally will do some steps toward reinstating Project Page Option feature into impared IDEs using your ideas.Silver
G
8

In case you're willing to use a hack like this:

type
  TOpenNewURLModule = procedure(const URL: string; EditorForm: TCustomForm);

procedure OpenURL(const URL: string);
var
  EditWindow: INTAEditWindow;
  Lib: HMODULE;
  OpenNewURLModule: TOpenNewURLModule;
begin
  EditWindow := (BorlandIDEServices as INTAEditorServices).TopEditWindow;
  if not Assigned(EditWindow) or not Assigned(EditWindow.Form) then
    Exit;

  Lib := GetModuleHandle('startpageide150.bpl');
  if Lib = 0 then
    Exit;
  OpenNewURLModule := GetProcAddress(Lib, '@Urlmodule@OpenNewURLModule$qqrx20System@UnicodeStringp22Editorform@TEditWindow');
  if @OpenNewURLModule <> nil then
    OpenNewURLModule(URL, EditWindow.Form);
end;

Cons:

  • it's a hack (startpageidexx.bpl is not exposed through the API or documented)
  • it's hard-coded for Delphi XE (you need a different file name for other versions, the method signature might be different, too - e.g. in older AnsiString versions)
  • it does nothing if there is no edit window (there has to be at least one open module)
  • it always opens a new browser view

EDIT: It seems to be possible to reuse an existing open Welcome page, as well as make this hack compatible with older versions of Delphi. The following shows two alternatives, Delphi XE and Delphi 2007 (both seem to be working):

type
  IURLModule = interface(IOTAModuleData)
  ['{9D215B02-6073-45DC-B007-1A2DBCE2D693}']
    function GetURL: string;
    procedure SetURL(const URL: string);
    property URL: string read GetURL write SetURL;
  end;
  TOpenNewURLModule = procedure(const URL: string; EditorForm: TCustomForm);

function FindURLModule: IURLModule;
var
  I: Integer;
begin
  Result := nil;
  with BorlandIDEServices as IOTAModuleServices do
    for I := 0 to ModuleCount - 1 do
      if Supports(Modules[I], IURLModule, Result) then
        Break;
end;

procedure OpenURL(const URL: string; ReuseExistingView: Boolean = True);
{$IFDEF VER220} // Delphi XE
const
  SStartPageIDE = 'startpageide150.bpl';
  SOpenNewURLModule = '@Urlmodule@OpenNewURLModule$qqrx20System@UnicodeStringp22Editorform@TEditWindow';
{$ENDIF}
{$IFDEF VER185} // Delphi 2007
const
  SStartPageIDE = 'startpageide100.bpl';
  SOpenNewURLModule = '@Urlmodule@OpenNewURLModule$qqrx17System@AnsiStringp22Editorform@TEditWindow';
{$ENDIF}
var
  Module: IURLModule;
  EditWindow: INTAEditWindow;
  Lib: HMODULE;
  OpenNewURLModule: TOpenNewURLModule;
begin
  EditWindow := nil;
  Module := nil;
  if ReuseExistingView then
    Module := FindURLModule;
  if Assigned(Module) then
  begin
    Module.URL := URL;
    (Module as IOTAModule).Show;
  end
  else
  begin
{$IFDEF VER220}
    EditWindow := (BorlandIDEServices as INTAEditorServices).TopEditWindow;
{$ENDIF}
{$IFDEF VER185}
  if Assigned((BorlandIDEServices as IOTAEditorServices).TopView) then
    EditWindow := (BorlandIDEServices as IOTAEditorServices).TopView.GetEditWindow;
{$ENDIF}
    if not Assigned(EditWindow) or not Assigned(EditWindow.Form) then
      Exit;
    Lib := GetModuleHandle(SStartPageIDE);
    if Lib = 0 then
      Exit;

    OpenNewURLModule := GetProcAddress(Lib, SOpenNewURLModule);
    if @OpenNewURLModule <> nil then
      OpenNewURLModule(URL, EditWindow.Form);
  end;
end;

Remaining cons:

  • it's still a hack
  • it's still hard-coded, for Delphi XE (Unicode) and Delphi 2007 (ANSI)
  • it still does nothing if there is no edit window

Perhaps you can use this as a start if you need compatibility for other versions.

Gautama answered 29/11, 2011 at 11:38 Comment(4)
Note, i had moderate success with early binding of the function from the package DLL, eg: function KeyCodeToShortCut(KeyCode: Integer): TShortCut; overload; register; external 'coreide70.bpl' name '@Vedtypes@KeyCodeToShortCut$qqri';. Only if we could reconstruct .dcp from .bpl we could use them as packages (goodbye, branching on the version numbers!)...Silver
Yes, having the .dcp's would be awesome!Gautama
No, sorry for the delay :-) I didnt really expect what bounty can bring something new to this question, so didnt schedule a time to analyze the proposed solutions. May i ask how you figured out that IURLModule interface structure?Silver
I don't remember the details anymore, but I started with (IOTAModule as TObject).ClassName, then GetProcAddress(lib, '@unitname@classname@') to obtain the class reference, and RTTI from there you can walk through the ancestry, get the list of implemented interfaces (GUIDs are stored in RTTI, too). And of course, dependency viewer and tdump startide150.bpl.Gautama
A
26

Here is a solution I've made specifically for you...

enter image description here

Download the source from here, extract and load the package in Delphi (I made it in Delphi XE, but it'll load in any version! You will need to change the Unit Output path in Project Options on pre-XE versions, though)... install the package.

In the Help menu, find Create Browser and click it. This will then create and display a tab which navigates to my blog (for the purpose of example).

You can easily modify this to suit your needs! The Help Menu Item code is located in EditWizard.MenuItem.pas, and can be disregarded! Just note that it's making a call when clicked to (BorlandIDEServices as IOTAEditorViewServices).ShowEditorView(CreateTab('http://www.simonjstuart.com'));, which is what actually creates a browser tab instance!

All the code for the Browser Tab (including its Frame layout) is contained in EditorWizard.Frame.pas, which makes it really easy to modify to suit your needs!

The unit EditorWizard.Wizard.pas contains the small amount of code needed to register the custom browser tab into the IDE.

Some tweaking will of course be required on your part, but this should certainly serve as a very acceptable base for what you're trying to do.

Enjoy :)

Aqaba answered 30/11, 2011 at 20:19 Comment(7)
Just an aside: your question (and my work producing this solution) has given me a great idea! I'm going to make a free (redistributable) plugin to provide an editor-tabbed Chromium browser other plugins can call really easily to display whatever they need (raw HTML, web pages, documentation sites etc).Aqaba
Note, this is not a Welcome Page (i have concerns about stability of IDE with multiple embedded web browsers), however it indeed answers the part on registering a frame as page of tabbed EditWindows (see my comment to Remy Lebeau's answer)Silver
Your question does not explicitly stipulate that the Welcome Page browser must be used.Aqaba
Also, you don't need to create a separate browser for each tab! You can reparent one TWebBrowser instance to the currently selected frame, and reload the last loaded URL each time you're changing between tabs! Super simple modification to my solution.Aqaba
Nice and easy sample but the question was specifically about the Welcome Page browser: "How do I get Welcome Page browser navigate to some URI from within OTA package wizard?" If it wasn't I would have provided something similar to your answer.Gautama
@TOndrej the reason I provided this answer is to serve as an alternative to the albeit insanely clever hack you provided (which I must say I have actually upvoted simply because it's so clever).Aqaba
@Aqaba Thanks, and as I said I really like your sample, too! (The Welcome Page browser seems to use the same technique!) I only think the question asked specifically about the Welcome Page browser so unfortunately that's impossible without a hack like mine or something similar. Anyway, let the OP decide which answer suits him best.Gautama
G
8

In case you're willing to use a hack like this:

type
  TOpenNewURLModule = procedure(const URL: string; EditorForm: TCustomForm);

procedure OpenURL(const URL: string);
var
  EditWindow: INTAEditWindow;
  Lib: HMODULE;
  OpenNewURLModule: TOpenNewURLModule;
begin
  EditWindow := (BorlandIDEServices as INTAEditorServices).TopEditWindow;
  if not Assigned(EditWindow) or not Assigned(EditWindow.Form) then
    Exit;

  Lib := GetModuleHandle('startpageide150.bpl');
  if Lib = 0 then
    Exit;
  OpenNewURLModule := GetProcAddress(Lib, '@Urlmodule@OpenNewURLModule$qqrx20System@UnicodeStringp22Editorform@TEditWindow');
  if @OpenNewURLModule <> nil then
    OpenNewURLModule(URL, EditWindow.Form);
end;

Cons:

  • it's a hack (startpageidexx.bpl is not exposed through the API or documented)
  • it's hard-coded for Delphi XE (you need a different file name for other versions, the method signature might be different, too - e.g. in older AnsiString versions)
  • it does nothing if there is no edit window (there has to be at least one open module)
  • it always opens a new browser view

EDIT: It seems to be possible to reuse an existing open Welcome page, as well as make this hack compatible with older versions of Delphi. The following shows two alternatives, Delphi XE and Delphi 2007 (both seem to be working):

type
  IURLModule = interface(IOTAModuleData)
  ['{9D215B02-6073-45DC-B007-1A2DBCE2D693}']
    function GetURL: string;
    procedure SetURL(const URL: string);
    property URL: string read GetURL write SetURL;
  end;
  TOpenNewURLModule = procedure(const URL: string; EditorForm: TCustomForm);

function FindURLModule: IURLModule;
var
  I: Integer;
begin
  Result := nil;
  with BorlandIDEServices as IOTAModuleServices do
    for I := 0 to ModuleCount - 1 do
      if Supports(Modules[I], IURLModule, Result) then
        Break;
end;

procedure OpenURL(const URL: string; ReuseExistingView: Boolean = True);
{$IFDEF VER220} // Delphi XE
const
  SStartPageIDE = 'startpageide150.bpl';
  SOpenNewURLModule = '@Urlmodule@OpenNewURLModule$qqrx20System@UnicodeStringp22Editorform@TEditWindow';
{$ENDIF}
{$IFDEF VER185} // Delphi 2007
const
  SStartPageIDE = 'startpageide100.bpl';
  SOpenNewURLModule = '@Urlmodule@OpenNewURLModule$qqrx17System@AnsiStringp22Editorform@TEditWindow';
{$ENDIF}
var
  Module: IURLModule;
  EditWindow: INTAEditWindow;
  Lib: HMODULE;
  OpenNewURLModule: TOpenNewURLModule;
begin
  EditWindow := nil;
  Module := nil;
  if ReuseExistingView then
    Module := FindURLModule;
  if Assigned(Module) then
  begin
    Module.URL := URL;
    (Module as IOTAModule).Show;
  end
  else
  begin
{$IFDEF VER220}
    EditWindow := (BorlandIDEServices as INTAEditorServices).TopEditWindow;
{$ENDIF}
{$IFDEF VER185}
  if Assigned((BorlandIDEServices as IOTAEditorServices).TopView) then
    EditWindow := (BorlandIDEServices as IOTAEditorServices).TopView.GetEditWindow;
{$ENDIF}
    if not Assigned(EditWindow) or not Assigned(EditWindow.Form) then
      Exit;
    Lib := GetModuleHandle(SStartPageIDE);
    if Lib = 0 then
      Exit;

    OpenNewURLModule := GetProcAddress(Lib, SOpenNewURLModule);
    if @OpenNewURLModule <> nil then
      OpenNewURLModule(URL, EditWindow.Form);
  end;
end;

Remaining cons:

  • it's still a hack
  • it's still hard-coded, for Delphi XE (Unicode) and Delphi 2007 (ANSI)
  • it still does nothing if there is no edit window

Perhaps you can use this as a start if you need compatibility for other versions.

Gautama answered 29/11, 2011 at 11:38 Comment(4)
Note, i had moderate success with early binding of the function from the package DLL, eg: function KeyCodeToShortCut(KeyCode: Integer): TShortCut; overload; register; external 'coreide70.bpl' name '@Vedtypes@KeyCodeToShortCut$qqri';. Only if we could reconstruct .dcp from .bpl we could use them as packages (goodbye, branching on the version numbers!)...Silver
Yes, having the .dcp's would be awesome!Gautama
No, sorry for the delay :-) I didnt really expect what bounty can bring something new to this question, so didnt schedule a time to analyze the proposed solutions. May i ask how you figured out that IURLModule interface structure?Silver
I don't remember the details anymore, but I started with (IOTAModule as TObject).ClassName, then GetProcAddress(lib, '@unitname@classname@') to obtain the class reference, and RTTI from there you can walk through the ancestry, get the list of implemented interfaces (GUIDs are stored in RTTI, too). And of course, dependency viewer and tdump startide150.bpl.Gautama
C
3

You are better off displaying your own TForm with a TWebBrowser component on it that you can load HTML into.

Carolyn answered 10/11, 2011 at 1:42 Comment(4)
Of course, how? TForm cannot be hosted as IDE tab. Please be specific.Silver
An OTA plugin can add dialogs to the IDE, or just display its own floating windows.Carolyn
IMO, floating windows are completely useless with docked layout. How can I add a page to tabbed interface of EditWindows?Silver
@user539484 I'll put together an editor-docked browser-containing form for you to build on. It's actually easier than you might think... but far too much to document on here! Expect my solution in an hour or so!Aqaba

© 2022 - 2024 — McMap. All rights reserved.