unit ce_interfaces;

{$I ce_defines.inc}

interface

uses
  Classes, SysUtils, actnList, menus, process,
  ce_synmemo, ce_observer;

type

  // describes the project kind. Used as a hint to cast ICECommonProject.getProject()
  TCEProjectFormat = (pfNative, pfDub);

  (**
   * Common project interface
   *)
  ICECommonProject = interface
  ['ICECommonProject']
    // indicates the project format
    function getFormat: TCEProjectFormat;
    // returns an untyped object that can be casted using getFormat()
    function getProject: TObject;

    //// project file
    //function filename: string;
    //procedure loadFromFile(const aFilename: string);
    //procedure saveToFile(const aFilename: string);
    //procedure save;
    //
    //// common project properties
    //function sourceCount: integer;
    //function source(index: integer): string;
    //function stringImportCount: integer;
    //function stringImport(index: integer): string;
    //function moduleImportCount: integer;
    //function moduleImport(index: integer): string;
    //function configurationCount: integer;
    //function configuration(index: integer): string;
    //function outputFilename: string;
  end;

  (**
   * An implementer declares some actions on demand.
   *)
  ICEContextualActions = interface
  ['ICEContextualActions']
    // declares a context name for the actions
    function contextName: string;
    // action count, called before contextAction()
    function contextActionCount: integer;
    // declares actions, called in loop, from 0 to contextActionCount-1
    function contextAction(index: integer): TAction;
  end;



  (**
   * An implementer is informed about the current file(s).
   *)
  ICEMultiDocObserver = interface
  ['ICEMultiDocObserver']
    // aDoc has been created (empty, runnable, project source, ...).
    procedure docNew(aDoc: TCESynMemo);
    // aDoc is the document being edited.
    procedure docFocused(aDoc: TCESynMemo);
    // aDoc content has just been modified (edited, saved).
    procedure docChanged(aDoc: TCESynMemo);
    // aDoc is about to be closed.
    procedure docClosing(aDoc: TCESynMemo);
  end;
  (**
   * An implementer informs some ICEMultiDocObserver about the current file(s)
   *)
  TCEMultiDocSubject = class(TCECustomSubject)
  protected
    function acceptObserver(aObject: TObject): boolean; override;
  end;



  (**
   * An implementer is informed about the current project(s).
   *)
  ICEProjectObserver = interface
  ['ICEProjectObserver']
    // aProject has been created/opened
    procedure projNew(aProject: ICECommonProject);
    // aProject has been modified: switches, source name, ...
    procedure projChanged(aProject: ICECommonProject);
    // aProject is about to be closed.
    procedure projClosing(aProject: ICECommonProject);
    // not called yet: aProject is always the same
    procedure projFocused(aProject: ICECommonProject);
    // aProject is about to be compiled
    procedure projCompiling(aProject: ICECommonProject);
  end;
  (**
   * An implementer informs some ICEProjectObserver about the current project(s)
   *)
  TCEProjectSubject = class(TCECustomSubject)
  protected
    function acceptObserver(aObject: TObject): boolean; override;
  end;



  (**
   * An implementer can add a main menu entry.
   *)
  ICEMainMenuProvider = interface
  ['ICEMainMenuProvider']
    // item is a new mainMenu entry. item must be filled with the sub-items to be added.
    procedure menuDeclare(item: TMenuItem);
    // item is the mainMenu entry declared previously. the sub items can be updated, deleted.
    procedure menuUpdate(item: TMenuItem);
  end;
  (**
   * An implementer collects and updates its observers menus.
   *)
  TCEMainMenuSubject = class(TCECustomSubject)
  protected
    function acceptObserver(aObject: TObject): boolean; override;
  end;



  (**
   * An implementer declares some actions which have their own main menu entry and
   * whose shortcuts are automatically handled
   *)
  ICEActionProvider = interface
  ['ICEActionProvider']
    // the action handler will clear the references to the actions collected previously and start collecting if result.
    function actHandlerWantRecollect: boolean;
    // the action handler starts to collect the actions if result.
    function actHandlerWantFirst: boolean;
    // the handler continue collecting action if result.
    function actHandlerWantNext(out category: string; out action: TCustomAction): boolean;
    // the handler update the state of a particular action.
    procedure actHandleUpdater(action: TCustomAction);
  end;
  (**
   * An implementer handles its observers actions.
   *)
  TCEActionProviderSubject = class(TCECustomSubject)
  protected
    function acceptObserver(aObject: TObject): boolean; override;
  end;



  (**
   * An implementer can expose some customizable shortcuts to be edited in a dedicated widget.
   *)
  ICEEditableShortCut = interface
  ['ICEEditableShortCut']
    // a TCEEditableShortCutSubject will start to collect shortcuts if result
    function scedWantFirst: boolean;
    // a TCEEditableShortCutSubject collects the information on the shortcuts while result
    function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean;
    // a TCEEditableShortCutSubject sends the possibly modified shortcut
    procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut);
  end;
  (**
   * An implementer manages its observers shortcuts.
   *)
  TCEEditableShortCutSubject = class(TCECustomSubject)
  protected
    function acceptObserver(aObject: TObject): boolean; override;
  end;



  // the option editor uses this value as a hint to cast and display an option container.
  TOptionEditorKind = (oekGeneric, oekForm, oekControl);
  // event generated by the option editor and passed to an ICEEditableOptions.
  // the oeeChange event only happends if the container is oekGeneric.
  TOptionEditorEvent = (oeeCancel, oeeAccept, oeeChange, oeeSelectCat);
  (**
   * An implementer can expose some options to be edited in a dedicated widget.
   *)
  ICEEditableOptions = interface
  ['ICEEditableOptions']
    // the widget wants the category.
    function optionedWantCategory(): string;
    // the widget wants to know if the options will use a generic editor or a custom form.
    function optionedWantEditorKind: TOptionEditorKind;
    // the widget wants the custom option editor TCustomForm, TWinControl or the TPersistent containing the options.
    function optionedWantContainer: TPersistent;
    // the option editor informs that something has happened.
    procedure optionedEvent(anEvent: TOptionEditorEvent);
    // the option editor wants to know if an editor allows another category to be displayed (not called for oekGeneric).
    function optionedOptionsModified: boolean;
  end;
  (**
   * An implementer displays its observers editable options.
   *)
  TCEEditableOptionsSubject = class(TCECustomSubject)
  protected
    function acceptObserver(aObject: TObject): boolean; override;
  end;



  /// describes the message kind, 'amkAuto' implies that an ICELogMessageObserver guess the kind.
  TCEAppMessageKind = (amkAuto, amkBub, amkInf, amkHint, amkWarn, amkErr);
  /// describes the message context. Used by a ICELogMessageObserver to filter the messages.
  TCEAppMessageCtxt = (amcAll, amcEdit, amcProj, amcApp, amcMisc);

  (**
   * Single service provided by the messages widget.
   *)
  ICEMessagesDisplay = interface(ICESingleService)
    // displays a message
    procedure message(const aValue: string; aData: Pointer; aCtxt: TCEAppMessageCtxt; aKind: TCEAppMessageKind);
    // clears the messages related to the context aCtxt.
    procedure clearByContext(aCtxt: TCEAppMessageCtxt);
    // clears the messages related to the data aData.
    procedure clearByData(aData: Pointer);
  end;



  (**
   * Single service provided by the process-input widget.
   *)
  ICEProcInputHandler = interface(ICESingleService)
    // add an entry to the list of process which can receive an user input.
    procedure addProcess(aProcess: TProcess);
    // removes an entry.
    procedure removeProcess(aProcess: TProcess);
    // indicates the current process
    function process: TProcess;
  end;



  (**
   * Single service related to the documents as a collection.
   *)
  ICEMultiDocHandler = interface(ICESingleService)
    // returns the count of opened document
    function documentCount: Integer;
    // returns the index-th document
    function getDocument(index: Integer): TCESynMemo;
    // returns true if the document matching aFielanme is already opened.
    function findDocument(aFilename: string): TCESynMemo;
    // open or set the focus on the document matching aFilename
    procedure openDocument(aFilename: string);
    // close the index-th document
    function closeDocument(index: Integer): boolean;
    // conveniance property
    property document[index: integer]: TCESynMemo read getDocument;
  end;



  (**
   * Single service provided by the library manager
   * In both cases, if someAliases is empty then all the available entries are passed.
   *)
  ICELibraryInformer = interface(ICESingleService)
    // fills aList with the filenames of the static libraries matching to someAliases content.
    procedure getLibsFiles(someAliases: TStrings; aList: TStrings);
    // fills aList with the path to static libraries sources matching to someAliases content.
    procedure getLibsPaths(someAliases: TStrings; aList: TStrings);
    // fills aList with all the available libraries aliases.
    procedure getLibsAliases(aList: TStrings);
  end;


{
  subject primitives:

  A subject cannot necessarly provides all the informations the observers expect.
  It can compose using the following "primitives".
}

  (**
   * TCEMultiDocSubject primitives.
   *)
  procedure subjDocNew(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);      {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjDocClosing(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);  {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjDocFocused(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);  {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjDocChanged(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);  {$IFDEF RELEASE}inline;{$ENDIF}

  (**
   * TCEProjectSubject primitives.
   *)
  procedure subjProjNew(aSubject: TCEProjectSubject; aProj: ICECommonProject);     {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjProjClosing(aSubject: TCEProjectSubject; aProj: ICECommonProject); {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjProjFocused(aSubject: TCEProjectSubject; aProj: ICECommonProject); {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjProjChanged(aSubject: TCEProjectSubject; aProj: ICECommonProject); {$IFDEF RELEASE}inline;{$ENDIF}
  procedure subjProjCompiling(aSubject: TCEProjectSubject; aProj: ICECommonProject);{$IFDEF RELEASE}inline;{$ENDIF}


{
  Service getters:

  The first overload assign the variable only when not yet set, the second is
  designed for a punctual usage, for example if a widget needs the service in
  a single and rarely called method.
}

  function getMessageDisplay(var obj: ICEMessagesDisplay): ICEMessagesDisplay; overload;
  function getMessageDisplay: ICEMessagesDisplay; overload;

  function getprocInputHandler(var obj: ICEProcInputHandler): ICEProcInputHandler; overload;
  function getprocInputHandler: ICEProcInputHandler; overload;

  function getMultiDocHandler(var obj: ICEMultiDocHandler): ICEMultiDocHandler; overload;
  function getMultiDocHandler: ICEMultiDocHandler; overload;

  function getLibraryInformer(var obj: ICELibraryInformer): ICELibraryInformer; overload;
  function getLibraryInformer: ICELibraryInformer; overload;

implementation

{$REGION TCEMultiDocSubject ----------------------------------------------------}
function TCEMultiDocSubject.acceptObserver(aObject: TObject): boolean;
begin
  exit(aObject is ICEMultiDocObserver);
end;

procedure subjDocNew(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEMultiDocObserver).docNew(aDoc);
end;

procedure subjDocClosing(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEMultiDocObserver).docClosing(aDoc);
end;

procedure subjDocFocused(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEMultiDocObserver).docFocused(aDoc);
end;

procedure subjDocChanged(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEMultiDocObserver).docChanged(aDoc);
end;
{$ENDREGION}

{$REGION TCEProjectSubject -----------------------------------------------------}
function TCEProjectSubject.acceptObserver(aObject: TObject): boolean;
begin
  exit(aObject is ICEProjectObserver);
end;

procedure subjProjNew(aSubject: TCEProjectSubject; aProj: ICECommonProject);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEProjectObserver).ProjNew(aProj);
end;

procedure subjProjClosing(aSubject: TCEProjectSubject; aProj: ICECommonProject);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEProjectObserver).projClosing(aProj);
end;

procedure subjProjFocused(aSubject: TCEProjectSubject; aProj: ICECommonProject);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEProjectObserver).projFocused(aProj);
end;

procedure subjProjChanged(aSubject: TCEProjectSubject; aProj: ICECommonProject);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEProjectObserver).projChanged(aProj);
end;

procedure subjProjCompiling(aSubject: TCEProjectSubject; aProj: ICECommonProject);
var
  i: Integer;
begin
  with aSubject do for i:= 0 to fObservers.Count-1 do
    (fObservers.Items[i] as ICEProjectObserver).projCompiling(aProj);
end;
{$ENDREGION}

{$REGION Misc subjects ---------------------------------------------------------}
function TCEMainMenuSubject.acceptObserver(aObject: TObject): boolean;
begin
  exit(aObject is ICEMainMenuProvider);
end;

function TCEEditableShortCutSubject.acceptObserver(aObject: TObject): boolean;
begin
  exit(aObject is ICEEditableShortCut);
end;

function TCEEditableOptionsSubject.acceptObserver(aObject: TObject): boolean;
begin
  exit(aObject is ICEEditableOptions);
end;

function TCEActionProviderSubject.acceptObserver(aObject: TObject): boolean;
begin
  exit(aObject is ICEActionProvider);
end;
{$ENDREGION}

{$REGION ICESingleService getters ----------------------------------------------}
function getMessageDisplay(var obj: ICEMessagesDisplay): ICEMessagesDisplay;
begin
  if obj = nil then
    obj := EntitiesConnector.getSingleService('ICEMessagesDisplay') as ICEMessagesDisplay;
  exit(obj);
end;

function getMessageDisplay: ICEMessagesDisplay;
begin
  exit(EntitiesConnector.getSingleService('ICEMessagesDisplay') as ICEMessagesDisplay);
end;

function getprocInputHandler(var obj: ICEProcInputHandler): ICEProcInputHandler;
begin
  if obj = nil then
    obj := EntitiesConnector.getSingleService('ICEProcInputHandler') as ICEProcInputHandler;
  exit(obj);
end;

function getprocInputHandler: ICEProcInputHandler;
begin
  exit(EntitiesConnector.getSingleService('ICEProcInputHandler') as ICEProcInputHandler);
end;

function getMultiDocHandler(var obj: ICEMultiDocHandler): ICEMultiDocHandler;
begin
  if obj = nil then
    obj := EntitiesConnector.getSingleService('ICEMultiDocHandler') as ICEMultiDocHandler;
  exit(obj);
end;

function getMultiDocHandler: ICEMultiDocHandler;
begin
  exit(EntitiesConnector.getSingleService('ICEMultiDocHandler') as ICEMultiDocHandler);
end;

function getLibraryInformer(var obj: ICELibraryInformer): ICELibraryInformer;
begin
  if obj = nil then
    obj := EntitiesConnector.getSingleService('ICELibraryInformer') as ICELibraryInformer;
  exit(obj);
end;

function getLibraryInformer: ICELibraryInformer;
begin
  exit(EntitiesConnector.getSingleService('ICELibraryInformer') as ICELibraryInformer);
end;
{$ENDREGION}

end.
