using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.CodeDom; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using Mono.Remoting.Channels.Unix; namespace Stetic { internal class ApplicationBackend: MarshalByRefObject, IDisposable, IObjectViewer { PaletteBackend paletteWidget; WidgetPropertyTreeBackend propertiesWidget; SignalsEditorEditSession signalsWidget; ProjectViewBackend projectWidget; ArrayList globalWidgetLibraries = new ArrayList (); AssemblyResolverCallback widgetLibraryResolver; object targetViewerObject; bool allowInProcLibraries = true; Application appFrontend; string coreLibrary; ProjectBackend activeProject; WidgetEditSession activeDesignSession; static ApplicationBackendController controller; public ApplicationBackend (Application app) { appFrontend = app; coreLibrary = Path.GetFullPath (typeof(Registry).Assembly.Location); Registry.Initialize (new AssemblyWidgetLibrary (coreLibrary, typeof(Registry).Assembly)); WidgetDesignerBackend.DefaultObjectViewer = this; } public static void Main () { Gdk.Threads.Init (); Gtk.Application.Init (); System.Threading.Thread.CurrentThread.Name = "gui-thread"; // Read the remoting channel to use string channel = Console.In.ReadLine (); IServerChannelSinkProvider formatterSink = new BinaryServerFormatterSinkProvider (); formatterSink.Next = new GuiDispatchServerSinkProvider (); string unixPath = null; if (channel == "unix") { unixPath = System.IO.Path.GetTempFileName (); Hashtable props = new Hashtable (); props ["path"] = unixPath; props ["name"] = "__internal_unix"; ChannelServices.RegisterChannel (new UnixChannel (props, null, formatterSink), false); } else { Hashtable props = new Hashtable (); props ["port"] = 0; props ["name"] = "__internal_tcp"; ChannelServices.RegisterChannel (new TcpChannel (props, null, formatterSink), false); } // Read the reference to the application string sref = Console.In.ReadLine (); byte[] data = Convert.FromBase64String (sref); MemoryStream ms = new MemoryStream (data); BinaryFormatter bf = new BinaryFormatter (); controller = (ApplicationBackendController) bf.Deserialize (ms); ApplicationBackend backend = new ApplicationBackend (controller.Application); controller.Connect (backend); Gdk.Threads.Enter (); Gtk.Application.Run (); Gdk.Threads.Leave (); controller.Disconnect (backend); } public virtual void Dispose () { if (controller != null) { Gtk.Application.Quit (); } System.Runtime.Remoting.RemotingServices.Disconnect (this); } public override object InitializeLifetimeService () { // Will be disconnected when calling Dispose return null; } public AssemblyResolverCallback WidgetLibraryResolver { get { return widgetLibraryResolver; } set { widgetLibraryResolver = value; } } public MimeResolverDelegate MimeResolver { set { ResourceInfo.MimeResolver = value; } } public Editor.ShowUrlDelegate ShowUrl { set { Editor.NonContainerWarningDialog.ShowUrl = value; } } public bool ShowNonContainerWarning { get { return Wrapper.Container.ShowNonContainerWarning; } set { Wrapper.Container.ShowNonContainerWarning = value; } } public ProjectBackend LoadProject (string path) { ProjectBackend p = new ProjectBackend (this); if (System.IO.Path.GetExtension (path) == ".glade") { GladeFiles.Import (p, path); } else { p.Load (path); } return p; } public CodeGenerationResult GenerateProjectCode (GenerationOptions options, ProjectBackend[] projects) { return CodeGenerator.GenerateProjectCode (options, projects); } public ArrayList GlobalWidgetLibraries { get { return globalWidgetLibraries; } set { globalWidgetLibraries = value; } } public bool AllowInProcLibraries { get { return allowInProcLibraries; } set { allowInProcLibraries = value; } } public bool UpdateLibraries (ArrayList libraries, ArrayList projects, bool allowBackendRestart, bool forceUnload) { try { Registry.BeginChangeSet (); libraries.Add (Registry.CoreWidgetLibrary.Name); // Notify libraries that need to be unloaded and loaded again foreach (WidgetLibrary lib in Registry.RegisteredWidgetLibraries) { if (lib.NeedsReload) appFrontend.NotifyLibraryUnloaded (lib.Name); } if (!Registry.ReloadWidgetLibraries () && allowBackendRestart) return false; // Store a list of registered libraries, used later to know which // ones need to be unloaded ArrayList loaded = new ArrayList (); foreach (WidgetLibrary alib in Registry.RegisteredWidgetLibraries) loaded.Add (alib.Name); // Load required libraries Hashtable visited = new Hashtable (); LoadLibraries (new AssemblyResolver (this), visited, libraries); foreach (ProjectBackend project in projects) LoadLibraries (project.Resolver, visited, project.WidgetLibraries); // Unload libraries which are not required foreach (string name in loaded) { if (!visited.Contains (name)) { if (forceUnload && allowBackendRestart) return false; appFrontend.NotifyLibraryUnloaded (name); Registry.UnregisterWidgetLibrary (Registry.GetWidgetLibrary (name)); } } return true; } finally { Registry.EndChangeSet (); } } internal void LoadLibraries (AssemblyResolver resolver, IEnumerable libraries) { try { Registry.BeginChangeSet (); Hashtable visited = new Hashtable (); LoadLibraries (resolver, visited, libraries); } finally { Registry.EndChangeSet (); } } void LoadLibraries (AssemblyResolver resolver, Hashtable visited, IEnumerable libraries) { // Convert all assembly names to assembly paths before registering the libraries. // The registry and the library cache will only handle full paths. foreach (string s in libraries) { string sr = resolver.Resolve (s, null); if (sr != null) AddLibrary (resolver, visited, sr); } Registry.ReloadWidgetLibraries (); } WidgetLibrary AddLibrary (AssemblyResolver resolver, Hashtable visited, string s) { if (Registry.IsRegistered (s)) { WidgetLibrary lib = Registry.GetWidgetLibrary (s); CheckDependencies (resolver, visited, lib); return lib; } // Avoid registering direct references of libstetic if (Path.GetFileName (s) == "libstetic.dll" && s != coreLibrary) return null; WidgetLibrary alib = CreateLibrary (resolver, s); if (alib == null) return null; RegisterLibrary (resolver, visited, alib); return alib; } void RegisterLibrary (AssemblyResolver resolver, Hashtable visited, WidgetLibrary lib) { if (lib == null || visited.Contains (lib.Name)) return; visited [lib.Name] = lib; foreach (string s in lib.GetLibraryDependencies ()) { if (!Application.InternalIsWidgetLibrary (resolver, s)) continue; AddLibrary (resolver, visited, s); } try { Registry.RegisterWidgetLibrary (lib); } catch (Exception ex) { // Catch errors when loading a library to avoid aborting // the whole update method. After all, that's not a fatal // error (some widgets just won't be shown). // FIXME: return the error somewhere Console.WriteLine (ex); } } WidgetLibrary CreateLibrary (AssemblyResolver resolver, string name) { try { if (allowInProcLibraries) return new AssemblyWidgetLibrary (resolver, name); else return new CecilWidgetLibrary (resolver, name); } catch (Exception) { // FIXME: handle the error, but keep loading. return null; } } void CheckDependencies (AssemblyResolver resolver, Hashtable visited, WidgetLibrary lib) { if (visited.Contains (lib.Name) || lib.Name == coreLibrary) return; visited [lib.Name] = lib; foreach (string dep in lib.GetLibraryDependencies ()) { WidgetLibrary depLib = Registry.GetWidgetLibrary (dep); if (depLib == null) AddLibrary (resolver, visited, dep); else CheckDependencies (resolver, visited, depLib); } } public ProjectBackend ActiveProject { get { return activeProject; } set { if (activeProject == value) return; activeProject = value; if (paletteWidget != null) { paletteWidget.ProjectBackend = activeProject; paletteWidget.WidgetLibraries = GetActiveLibraries (); } if (propertiesWidget != null) propertiesWidget.ProjectBackend = activeProject; if (signalsWidget != null) signalsWidget.ProjectBackend = activeProject; } } public void SetActiveDesignSession (WidgetEditSession session) { activeDesignSession = session; if (projectWidget != null) projectWidget.Bind (session); } public WidgetLibrary[] GetActiveLibraries () { return GetProjectLibraries (activeProject); } public WidgetLibrary[] GetProjectLibraries (ProjectBackend project) { ArrayList projectLibs = new ArrayList (); if (project != null) projectLibs.AddRange (project.WidgetLibraries); ArrayList list = new ArrayList (); list.Add (Registry.CoreWidgetLibrary); foreach (WidgetLibrary alib in Registry.RegisteredWidgetLibraries) { if (project != null && !alib.SupportsGtkVersion (project.TargetGtkVersion)) continue; string aname = alib.Name; if (projectLibs.Contains (aname) || globalWidgetLibraries.Contains (aname)) list.Add (alib); } return (WidgetLibrary[]) list.ToArray (typeof(WidgetLibrary)); } public ProjectBackend CreateProject () { return new ProjectBackend (this); } object IObjectViewer.TargetObject { get { return targetViewerObject; } set { targetViewerObject = value; if (propertiesWidget != null) propertiesWidget.TargetObject = targetViewerObject; if (signalsWidget != null) signalsWidget.Editor.TargetObject = targetViewerObject; } } public PaletteBackend GetPaletteWidget () { if (paletteWidget == null) { paletteWidget = new PaletteBackend (this); paletteWidget.ProjectBackend = activeProject; paletteWidget.WidgetLibraries = GetActiveLibraries (); } return paletteWidget; } public void CreatePaletteWidgetPlug (uint socketId) { Gtk.Plug plug = new Gtk.Plug (socketId); plug.Decorated = false; // Gtk.Window plug = new Gtk.Window (""); plug.Add (GetPaletteWidget ()); plug.Show (); } public void DestroyPaletteWidgetPlug () { if (paletteWidget != null) { Gtk.Plug plug = (Gtk.Plug)paletteWidget.Parent; plug.Remove (paletteWidget); plug.Destroy (); } } public WidgetPropertyTreeBackend GetPropertiesWidget () { if (propertiesWidget == null) { propertiesWidget = new WidgetPropertyTreeBackend (); propertiesWidget.ProjectBackend = activeProject; propertiesWidget.TargetObject = targetViewerObject; } return propertiesWidget; } public void CreatePropertiesWidgetPlug (uint socketId) { Gtk.Plug plug = new Gtk.Plug (socketId); plug.Decorated = false; // Gtk.Window plug = new Gtk.Window (""); plug.Add (GetPropertiesWidget ()); plug.Show (); } public void DestroyPropertiesWidgetPlug () { if (propertiesWidget != null) { Gtk.Plug plug = (Gtk.Plug) propertiesWidget.Parent; plug.Remove (propertiesWidget); plug.Destroy (); } } public SignalsEditorEditSession GetSignalsWidget (SignalsEditorFrontend frontend) { if (signalsWidget == null) { signalsWidget = new SignalsEditorEditSession (frontend); signalsWidget.ProjectBackend = activeProject; signalsWidget.Editor.TargetObject = targetViewerObject; } return signalsWidget; } public SignalsEditorEditSession CreateSignalsWidgetPlug (SignalsEditorFrontend frontend, uint socketId) { Gtk.Plug plug = new Gtk.Plug (socketId); plug.Decorated = false; // Gtk.Window plug = new Gtk.Window (""); SignalsEditorEditSession session = GetSignalsWidget (frontend); plug.Add (session.Editor); plug.Show (); return session; } public void DestroySignalsWidgetPlug () { if (signalsWidget != null) { Gtk.Plug plug = (Gtk.Plug) signalsWidget.Editor.Parent; plug.Remove (signalsWidget.Editor); plug.Destroy (); } } public ProjectViewBackend GetProjectWidget (ProjectViewFrontend frontend) { if (projectWidget == null) { projectWidget = new ProjectViewBackend (frontend); projectWidget.Bind (activeDesignSession); } return projectWidget; } public void CreateProjectWidgetPlug (ProjectViewFrontend frontend, uint socketId) { Gtk.Plug plug = new Gtk.Plug (socketId); plug.Decorated = false; // Gtk.Window plug = new Gtk.Window (""); plug.Add (GetProjectWidget (frontend)); plug.Show (); } public void DestroyProjectWidgetPlug () { if (projectWidget != null) { Gtk.Plug plug = (Gtk.Plug)projectWidget.Parent; plug.Remove (projectWidget); plug.Destroy (); } } public ArrayList GetComponentTypes () { ArrayList list = new ArrayList (); foreach (ClassDescriptor cd in Registry.AllClasses) list.Add (cd.Name); return list; } public bool GetClassDescriptorInfo (string name, out string desc, out string className, out string category, out string targetGtkVersion, out string library, out byte[] icon) { ClassDescriptor cls = Registry.LookupClassByName (name); if (cls == null) { icon = null; desc = null; className = null; category = null; targetGtkVersion = null; library = null; return false; } desc = cls.Label; className = cls.WrappedTypeName; category = cls.Category; targetGtkVersion = cls.TargetGtkVersion; library = cls.Library.Name; icon = cls.Icon != null ? cls.Icon.SaveToBuffer ("png") : null; return true; } public object[] GetClassDescriptorInitializationValues (string name) { ClassDescriptor cls = Registry.LookupClassByName (name); ArrayList list = new ArrayList (); foreach (Stetic.PropertyDescriptor prop in cls.InitializationProperties) { if (prop.PropertyType.IsValueType) { // Avoid sending to the main process types which should not be loaded there if (prop.PropertyType.Assembly == typeof(object).Assembly || prop.PropertyType.Assembly == typeof(Gtk.Widget).Assembly || prop.PropertyType.Assembly == typeof(Gdk.Window).Assembly || prop.PropertyType.Assembly == typeof(GLib.Object).Assembly) { list.Add (Activator.CreateInstance (prop.PropertyType)); } else return new object [0]; } else list.Add (null); } return list.ToArray (); } public void ShowPaletteGroup (string name, string label) { GetPaletteWidget ().ShowGroup (name, label); } public void HidePaletteGroup (string name) { GetPaletteWidget ().HideGroup (name); } internal void GetClipboardOperations (object obj, out bool canCut, out bool canCopy, out bool canPaste) { Stetic.Wrapper.Widget wrapper = obj as Stetic.Wrapper.Widget; if (wrapper != null) { canCut = wrapper.InternalChildProperty == null && !wrapper.IsTopLevel; canCopy = !wrapper.IsTopLevel; canPaste = false; } else if (obj is Placeholder) { // FIXME: make it work for placeholders canCut = canCopy = false; canPaste = true; } else { canCut = canCopy = canPaste = false; } } internal void GetComponentInfo (object obj, out string name, out string type) { Stetic.Wrapper.Widget wrapper = obj as Stetic.Wrapper.Widget; name = wrapper.Wrapped.Name; type = wrapper.ClassDescriptor.Name; } internal void RenameWidget (Wrapper.Widget w, string newName) { w.Wrapped.Name = newName; } internal byte[] GetActionIcon (Wrapper.Action ac) { Gdk.Pixbuf pix = ac.RenderIcon (Gtk.IconSize.Menu); if (pix == null) return null; return pix.SaveToBuffer ("png"); } internal ArrayList GetWidgetChildren (Wrapper.Widget ww) { Stetic.Wrapper.Container cw = ww as Stetic.Wrapper.Container; if (cw == null) return null; ArrayList list = new ArrayList (); foreach (object ob in cw.RealChildren) { ObjectWrapper ow = ObjectWrapper.Lookup (ob); if (ow != null) list.Add (Component.GetSafeReference (ow)); } return list; } internal void RemoveWidgetSignal (ObjectWrapper wrapper, Signal signal) { foreach (Signal s in wrapper.Signals) { if (s.Handler == signal.Handler && s.SignalDescriptor.Name == signal.SignalDescriptor.Name) { wrapper.Signals.Remove (s); return; } } } internal Wrapper.ActionGroup[] GetActionGroups (Wrapper.Widget widget) { return widget.LocalActionGroups.ToArray (); } internal ObjectBindInfo[] GetBoundComponents (ObjectWrapper wrapper) { return CodeGenerator.GetFieldsToBind (wrapper).ToArray (); } internal ObjectWrapper GetPropertyTreeTarget () { return targetViewerObject as ObjectWrapper; } internal void BeginComponentDrag (ProjectBackend project, string desc, string className, ObjectWrapper wrapper, Gtk.Widget source, Gdk.DragContext ctx, ComponentDropCallback callback) { if (wrapper != null) { Stetic.Wrapper.ActionPaletteItem it = new Stetic.Wrapper.ActionPaletteItem (Gtk.UIManagerItemType.Menuitem, null, (Wrapper.Action) wrapper); DND.Drag (source, ctx, it); } else if (callback != null) { DND.Drag (source, ctx, delegate () { callback (); // If the class name has an assembly name, remove it now int i = className.IndexOf (','); if (i != -1) className = className.Substring (0, i); ClassDescriptor cls = Registry.LookupClassByName (className); if (cls != null) return cls.NewInstance (project) as Gtk.Widget; else { // Class not found, show an error string msg = string.Format ("The widget '{0}' could not be found.", className); Gtk.MessageDialog dlg = new Gtk.MessageDialog (null, Gtk.DialogFlags.Modal, Gtk.MessageType.Error, Gtk.ButtonsType.Close, msg); dlg.Run (); dlg.Destroy (); return null; } }, (desc != null && desc.Length > 0) ? desc : className ); } else { ClassDescriptor cls = Registry.LookupClassByName (className); DND.Drag (source, ctx, cls.NewInstance (project) as Gtk.Widget); } } public string ResolveAssembly (string assemblyName) { if (widgetLibraryResolver != null) return widgetLibraryResolver (assemblyName); else return null; } } }