diff options
author | Robert Nordan <rpvn@robpvn.net> | 2013-10-11 16:35:04 +0400 |
---|---|---|
committer | Robert Nordan <rpvn@robpvn.net> | 2013-10-11 16:35:04 +0400 |
commit | f10aa40291c3daf2a32d614f0bdbba1b529b543a (patch) | |
tree | 7b3c19f630c72f8afe051966e1505d792470f92e /Mono.Addins.GuiGtk3/Mono.Addins.Gui | |
parent | 0aa5fb5f949dcb8c13a2c6c0fc58b5aa7e06a634 (diff) |
Add new GTK UI project
Currently have stubs for all UI elements, but not connected up
Diffstat (limited to 'Mono.Addins.GuiGtk3/Mono.Addins.Gui')
17 files changed, 3757 insertions, 0 deletions
diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInfoView.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInfoView.cs new file mode 100644 index 0000000..f58f246 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInfoView.cs @@ -0,0 +1,471 @@ +// +// AddinInfoView.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Collections.Generic; +using Mono.Addins.Setup; +using System.Text; +using Mono.Unix; +using System.Linq; +using UI = Gtk.Builder.ObjectAttribute; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + [System.ComponentModel.ToolboxItem(true)] + class AddinInfoView : Gtk.Bin + { + //From UI files + [UI] EventBox eboxButs; + [UI] Label labelName; + [UI] Label labelDesc; + [UI] Button urlButton; + [UI] HBox boxTitle; + [UI] EventBox boxHeader; + [UI] HBox headerBox; + [UI] Button btnDisable; + [UI] Button btnInstall; + [UI] Button btnUninstall; + [UI] Button btnUpdate; + [UI] Label labelHeader; + [UI] Image imageHeader; + [UI] Label labelVersion; + [UI] EventBox ebox; + [UI] EventBox ebox2; + [UI] VBox vboxDesc; + [UI] ScrolledWindow scrolledwindow; + + List<AddinRepositoryEntry> selectedEntry = new List<AddinRepositoryEntry> (); + List<Addin> selectedAddin = new List<Addin> (); + SetupService service; + HeaderBox topHeaderBox; + List<Gtk.Widget> previewImages = new List<Gtk.Widget> (); + ImageContainer titleIcon; + int titleWidth; + string infoUrl; + + public event EventHandler InstallClicked; + public event EventHandler UninstallClicked; + public event EventHandler UpdateClicked; + public event EventHandler EnableDisableClicked; + + public AddinInfoView () + { + this.Build (); + AllowInstall = true; + titleWidth = labelName.SizeRequest ().Width; + + HeaderBox hb = new HeaderBox (1,1,1,1); + hb.Show (); + hb.Replace (this); + + hb = new HeaderBox (1,0,0,0); + hb.SetPadding (6,6,6,6); + hb.Show (); + hb.GradientBackround = true; + hb.Replace (eboxButs); + + hb = new HeaderBox (0,1,0,0); + hb.SetPadding (6,6,6,6); + hb.Show (); + hb.GradientBackround = true; + hb.Replace (boxHeader); + topHeaderBox = hb; + } + + public void Init (SetupService service) + { + this.service = service; + } + + public bool AllowInstall { get; set; } + + public List<AddinRepositoryEntry> SelectedEntries { + get { + return this.selectedEntry; + } + } + + public List<Addin> SelectedAddins { + get { + return this.selectedAddin; + } + } + + public void ShowAddins (object[] data) + { + selectedEntry.Clear (); + selectedAddin.Clear (); + eboxButs.Visible = true; + topHeaderBox.Hide (); + urlButton.Hide (); + + if (titleIcon != null) { + boxTitle.Remove (titleIcon); + titleIcon.Destroy (); + titleIcon = null; + } + + foreach (var img in previewImages) { + ((Gtk.Container)img.Parent).Remove (img); + img.Destroy (); + } + previewImages.Clear (); + + if (data.Length == 1) { + headerBox.Show (); + ShowAddin (data[0]); + } + else if (data.Length > 1) { + headerBox.Hide (); + StringBuilder sb = new StringBuilder (); + sb.Append (Catalog.GetString ("Multiple selection:\n\n")); + bool allowUpdate = AllowInstall; + bool allowInstall = true; + bool allowUninstall = AllowInstall; + bool allowEnable = true; + bool allowDisable = true; + + foreach (object o in data) { + Addin installed; + if (o is Addin) { + Addin a = (Addin)o; + installed = a; + selectedAddin.Add (a); + sb.Append (a.Name); + } + else { + AddinRepositoryEntry entry = (AddinRepositoryEntry) o; + selectedEntry.Add (entry); + sb.Append (entry.Addin.Name); + installed = AddinManager.Registry.GetAddin (Addin.GetIdName (entry.Addin.Id)); + } + if (installed != null) { + if (GetUpdate (installed) == null) + allowUpdate = false; + allowInstall = false; + if (installed.Enabled) + allowEnable = false; + else + allowDisable = false; + } else + allowEnable = allowDisable = allowUninstall = allowUpdate = false; + + sb.Append ('\n'); + labelDesc.Text = sb.ToString (); + + if (allowEnable) { + btnDisable.Visible = true; + btnDisable.Label = Catalog.GetString ("Enable"); + } else if (allowDisable) { + btnDisable.Visible = true; + btnDisable.Label = Catalog.GetString ("Disable"); + } else + btnDisable.Visible = false; + btnInstall.Visible = allowInstall; + btnUninstall.Visible = allowUninstall; + btnUpdate.Visible = allowUpdate; + } + } + else { + headerBox.Hide (); + btnDisable.Visible = false; + btnInstall.Visible = false; + btnUninstall.Visible = false; + btnUpdate.Visible = false; + eboxButs.Visible = false; + labelDesc.Text = Catalog.GetString ("No selection"); + } + } + + + void ShowAddin (object data) + { + AddinHeader sinfo = null; + Addin installed = null; + AddinHeader updateInfo = null; + string repo = ""; + string downloadSize = null; + + topHeaderBox.Hide (); + + if (data is Addin) { + installed = (Addin) data; + sinfo = SetupService.GetAddinHeader (installed); + var entry = GetUpdate (installed); + if (entry != null) { + updateInfo = entry.Addin; + selectedEntry.Add (entry); + } + foreach (var prop in sinfo.Properties) { + if (prop.Name.StartsWith ("PreviewImage")) + previewImages.Add (new ImageContainer (installed, prop.Value)); + } + string icon32 = sinfo.Properties.GetPropertyValue ("Icon32"); + if (icon32.Length > 0) + titleIcon = new ImageContainer (installed, icon32); + } + else if (data is AddinRepositoryEntry) { + AddinRepositoryEntry entry = (AddinRepositoryEntry) data; + sinfo = entry.Addin; + installed = AddinManager.Registry.GetAddin (Addin.GetIdName (sinfo.Id)); + if (installed != null && Addin.CompareVersions (installed.Version, sinfo.Version) > 0) + updateInfo = sinfo; + selectedEntry.Add (entry); + string rname = !string.IsNullOrEmpty (entry.RepositoryName) ? entry.RepositoryName : entry.RepositoryUrl; + repo = "<small><b>" + Catalog.GetString ("Available in repository:") + "</b>\n" + GLib.Markup.EscapeText (rname) + "\n\n</small>"; + foreach (var prop in sinfo.Properties) { + if (prop.Name.StartsWith ("PreviewImage")) + previewImages.Add (new ImageContainer (entry, prop.Value)); + } + string icon32 = sinfo.Properties.GetPropertyValue ("Icon32"); + if (icon32.Length > 0) + titleIcon = new ImageContainer (entry, icon32); + int size; + if (int.TryParse (sinfo.Properties.GetPropertyValue ("DownloadSize"), out size)) { + float fs = ((float)size) / 1048576f; + downloadSize = fs.ToString ("0.00 MB"); + } + } else + selectedEntry.Clear (); + + if (installed != null) + selectedAddin.Add (installed); + + string missingDepsTxt = null; + + if (sinfo == null) { + btnDisable.Visible = false; + btnUninstall.Visible = false; + btnUpdate.Visible = false; + } else { + string version; + string newVersion = null; + if (installed != null) { + btnInstall.Visible = false; + btnUpdate.Visible = updateInfo != null && AllowInstall; + btnDisable.Visible = true; + btnDisable.Label = installed.Enabled ? Catalog.GetString ("Disable") : Catalog.GetString ("Enable"); + btnDisable.Visible = installed.Description.CanDisable; + btnUninstall.Visible = installed.Description.CanUninstall; + version = installed.Version; + var missingDeps = Services.GetMissingDependencies (installed); + if (updateInfo != null) { + newVersion = updateInfo.Version; + labelHeader.Markup = "<b><span color='black'>" + Catalog.GetString ("Update available") + "</span></b>"; +// topHeaderBox.BackgroundColor = new Gdk.Color (0, 132, 208); + imageHeader.Pixbuf = Gdk.Pixbuf.LoadFromResource ("software-update-available.png"); + topHeaderBox.BackgroundColor = new Gdk.Color (255, 176, 0); + topHeaderBox.Show (); + } + else if (missingDeps.Any ()) { + labelHeader.Markup = "<b><span color='black'>" + Catalog.GetString ("This add-in can't be loaded due to missing dependencies") + "</span></b>"; + topHeaderBox.BackgroundColor = new Gdk.Color (255, 176, 0); + imageHeader.SetFromStock (Gtk.Stock.DialogWarning, Gtk.IconSize.Menu); + topHeaderBox.Show (); + missingDepsTxt = ""; + foreach (var mdep in missingDeps) { + if (mdep.Found != null) + missingDepsTxt += "\n" + string.Format (Catalog.GetString ("Required: {0} v{1}, found v{2}"), mdep.Addin, mdep.Required, mdep.Found); + else + missingDepsTxt += "\n" + string.Format (Catalog.GetString ("Missing: {0} v{1}"), mdep.Addin, mdep.Required); + } + } + } else { + btnInstall.Visible = AllowInstall; + btnUpdate.Visible = false; + btnDisable.Visible = false; + btnUninstall.Visible = false; + version = sinfo.Version; + } + labelName.Markup = "<b><big>" + GLib.Markup.EscapeText(sinfo.Name) + "</big></b>"; + + string ver; + if (newVersion != null) { + ver = "<small><b>" + Catalog.GetString ("Installed version") + ":</b> " + version + "</small>\n"; + ver += "<small><b>" + Catalog.GetString ("Repository version") + ":</b> " + newVersion + "</small>"; + } + else + ver = "<small><b>" + Catalog.GetString ("Version") + " " + version + "</b></small>"; + + if (downloadSize != null) + ver += "\n<small><b>" + Catalog.GetString ("Download size") + ":</b> " + downloadSize + "</small>"; + if (missingDepsTxt != null) + ver += "\n\n" + GLib.Markup.EscapeText (Catalog.GetString ("The following depedencies required by this add-in are not available:")) + missingDepsTxt; + labelVersion.Markup = ver; + + string desc = GLib.Markup.EscapeText (sinfo.Description); + labelDesc.Markup = repo + GLib.Markup.EscapeText (desc); + + foreach (var img in previewImages) + vboxDesc.PackStart (img, false, false, 0); + + urlButton.Visible = !string.IsNullOrEmpty (sinfo.Url); + infoUrl = sinfo.Url; + + if (titleIcon != null) { + boxTitle.PackEnd (titleIcon, false, false, 0); + labelName.WidthRequest = titleWidth - 32; + labelVersion.WidthRequest = titleWidth - 32; + } else { + labelName.WidthRequest = titleWidth; + labelVersion.WidthRequest = titleWidth; + } + + if (IsRealized) + SetComponentsBg (); + } + } + + public AddinRepositoryEntry GetUpdate (Addin a) + { + AddinRepositoryEntry[] updates = service.Repositories.GetAvailableAddinUpdates (Addin.GetIdName (a.Id)); + AddinRepositoryEntry best = null; + string bestVersion = a.Version; + foreach (AddinRepositoryEntry e in updates) { + if (Addin.CompareVersions (bestVersion, e.Addin.Version) > 0) { + best = e; + bestVersion = e.Addin.Version; + } + } + return best; + } + + protected virtual void OnBtnInstallClicked (object sender, System.EventArgs e) + { + if (InstallClicked != null) + InstallClicked (this, e); + } + + protected virtual void OnBtnDisableClicked (object sender, System.EventArgs e) + { + if (EnableDisableClicked != null) + EnableDisableClicked (this, e); + } + + protected virtual void OnBtnUpdateClicked (object sender, System.EventArgs e) + { + if (UpdateClicked != null) + UpdateClicked (this, e); + } + + protected virtual void OnBtnUninstallClicked (object sender, System.EventArgs e) + { + if (UninstallClicked != null) + UninstallClicked (this, e); + } + + protected override void OnRealized () + { + base.OnRealized (); + HslColor gcol = ebox.Style.Background (Gtk.StateType.Normal); + gcol.L -= 0.03; + ebox.ModifyBg (Gtk.StateType.Normal, gcol); + ebox2.ModifyBg (Gtk.StateType.Normal, gcol); + scrolledwindow.ModifyBg (Gtk.StateType.Normal, gcol); + SetComponentsBg (); + } + + void SetComponentsBg () + { + HslColor gcol = ebox.Style.Background (Gtk.StateType.Normal); + //gcol.L -= 0.03; + if (titleIcon != null) + titleIcon.ModifyBg (Gtk.StateType.Normal, gcol); + foreach (var i in previewImages) + i.ModifyBg (Gtk.StateType.Normal, gcol); + } + + protected virtual void OnUrlButtonClicked (object sender, System.EventArgs e) + { + System.Diagnostics.Process.Start (infoUrl); + } + } + + class ImageContainer: Gtk.EventBox + { + AddinRepositoryEntry aentry; + IAsyncResult aresult; + Gtk.Image image; + bool destroyed; + + ImageContainer () + { + image = new Gtk.Image (); + Add (image); + image.SetAlignment (0.5f, 0f); + Show (); + } + + public ImageContainer (AddinRepositoryEntry aentry, string fileName): this () + { + this.aentry = aentry; + aresult = aentry.BeginDownloadSupportFile (fileName, ImageDownloaded, null); + } + + public ImageContainer (Addin addin, string fileName): this () + { + string path = System.IO.Path.Combine (addin.Description.BasePath, fileName); + LoadImage (File.OpenRead (path)); + } + + void ImageDownloaded (object state) + { + Gtk.Application.Invoke (delegate { + if (destroyed) + return; + try { + LoadImage (aentry.EndDownloadSupportFile (aresult)); + } catch { + // ignore + } + }); + } + + void LoadImage (Stream s) + { + using (s) { + Gdk.PixbufLoader loader = new Gdk.PixbufLoader (s); + Gdk.Pixbuf pix = image.Pixbuf = loader.Pixbuf; + loader.Dispose (); + if (pix.Width > 250) { + Gdk.Pixbuf spix = pix.ScaleSimple (250, (250 * pix.Height) / pix.Width, Gdk.InterpType.Hyper); + pix.Dispose (); + pix = spix; + } + image.Pixbuf = pix; + image.Show (); + } + } + + protected override void OnDestroyed () + { + destroyed = true; + base.OnDestroyed (); + } + } +} + diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstaller.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstaller.cs new file mode 100644 index 0000000..b2f2f85 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstaller.cs @@ -0,0 +1,25 @@ + + +using System; +using Mono.Addins.Setup; +using Mono.Unix; + +namespace Mono.Addins.GuiGtk3 +{ + public class AddinInstaller: IAddinInstaller + { + public void InstallAddins (AddinRegistry reg, string message, string[] addinIds) + { + AddinInstallerDialog dlg = new AddinInstallerDialog (reg, message, addinIds); + try { + if (dlg.Run () == (int) Gtk.ResponseType.Cancel) + throw new InstallException (Catalog.GetString ("Installation cancelled")); + else if (dlg.ErrMessage != null) + throw new InstallException (dlg.ErrMessage); + } + finally { + dlg.Destroy (); + } + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstallerDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstallerDialog.cs new file mode 100644 index 0000000..7bdfc4b --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstallerDialog.cs @@ -0,0 +1,186 @@ +// +// AddinInstallerDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Threading; +using System.Collections; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using Mono.Unix; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + internal class AddinInstallerDialog : Gtk.Dialog, IProgressStatus + { + //From UI File + [UI] Label addinList; + [UI] ProgressBar progressBar; + [UI] Button buttonCancel; + [UI] Button buttonOk; + + PackageCollection entries = new PackageCollection (); + string[] addinIds; + bool addinsNotFound; + string errMessage; + SetupService setup; + + public AddinInstallerDialog (AddinRegistry reg, string message, string[] addinIds) + { + this.Build(); + + this.addinIds = addinIds; + setup = new SetupService (reg); + + if (!CheckAddins (true)) + UpdateRepos (); + } + + bool CheckAddins (bool updating) + { + string txt = ""; + entries.Clear (); + bool addinsNotFound = false; + foreach (string id in addinIds) { + string name = Addin.GetIdName (id); + string version = Addin.GetIdVersion (id); + AddinRepositoryEntry[] ares = setup.Repositories.GetAvailableAddin (name, version); + if (ares.Length == 0) { + addinsNotFound = true; + if (updating) + txt += "<span foreground='grey'><b>" + name + " " + version + "</b> (searching add-in)</span>\n"; + else + txt += "<span foreground='red'><b>" + name + " " + version + "</b> (not found)</span>\n"; + } else { + entries.Add (Package.FromRepository (ares[0])); + txt += "<b>" + ares[0].Addin.Name + " " + ares[0].Addin.Version + "</b>\n"; + } + } + PackageCollection toUninstall; + DependencyCollection unresolved; + if (!setup.ResolveDependencies (this, entries, out toUninstall, out unresolved)) { + foreach (Dependency dep in unresolved) { + txt += "<span foreground='red'><b>" + dep.Name + "</b> (not found)</span>\n"; + } + addinsNotFound = true; + } + addinList.Markup = txt; + return !addinsNotFound; + } + + void UpdateRepos () + { + progressBar.Show (); + setup.Repositories.UpdateAllRepositories (this); + progressBar.Hide (); + addinsNotFound = CheckAddins (false); + if (errMessage != null) { + Services.ShowError (null, errMessage, this, true); + errMessage = null; + } + } + + public int LogLevel { + get { + return 1; + } + } + + public bool IsCanceled { + get { + return false; + } + } + + public bool AddinsNotFound { + get { + return addinsNotFound; + } + } + + public string ErrMessage { + get { + return errMessage; + } + } + + public void SetMessage (string msg) + { + progressBar.Text = msg; + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + + public void SetProgress (double progress) + { + progressBar.Fraction = progress; + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + + public void Log (string msg) + { + } + + public void ReportWarning (string message) + { + } + + public void ReportError (string message, System.Exception exception) + { + errMessage = message; + } + + public void Cancel () + { + } + + protected virtual void OnButtonOkClicked (object sender, System.EventArgs e) + { + if (addinsNotFound) { + errMessage = Catalog.GetString ("Some of the required add-ins were not found"); + Respond (Gtk.ResponseType.Ok); + } + else { + errMessage = null; + progressBar.Show (); + progressBar.Fraction = 0; + progressBar.Text = ""; + bool res = setup.Install (this, entries); + if (!res) { + buttonCancel.Sensitive = buttonOk.Sensitive = false; + if (errMessage == null) + errMessage = Catalog.GetString ("Installation failed"); + Services.ShowError (null, errMessage, this, true); + } + } + Respond (Gtk.ResponseType.Ok); + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerDialog.cs new file mode 100644 index 0000000..8cf5c52 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerDialog.cs @@ -0,0 +1,573 @@ +// +// AddinManagerDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; +using Mono.Addins.Setup; +using Mono.Addins; +using Mono.Unix; +using System.Threading; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + class AddinManagerDialog : Dialog, IDisposable + { + //Connected from the UI file + [UI] TreeView addinTree; + [UI] TreeView galleryTreeView; + [UI] TreeView updatesTreeView; + [UI] ComboBox repoCombo; + [UI] VBox vboxUpdates; + [UI] VBox vboxGallery; + [UI] ScrolledWindow scrolledUpdates; + [UI] ScrolledWindow scrolledGallery; + [UI] EventBox eboxRepoUpdates; + [UI] EventBox eboxRepo; + [UI] Label labelUpdates; + [UI] Button buttonUpdateAll; + //Manually fill in from the UI File + [UI] AddinInfoView addininfoInstalled; //TODO: Might need to init manually + [UI] AddinInfoView addininfoGallery; + [UI] AddinInfoView addininfoUpdates; + [UI] Notebook notebook; + + AddinTreeWidget tree; + AddinTreeWidget galleryTree; + AddinTreeWidget updatesTree; + + SetupService service = new SetupService (); + ListStore repoStore; + int lastRepoActive; + SearchEntry filterEntry; + Label installedTabLabel; + Label updatesTabLabel; + Label galleryTabLabel; + + const string AllRepoMarker = "__ALL"; + const string ManageRepoMarker = "__MANAGE"; + + internal bool AllowInstall + { + set { + addininfoInstalled.AllowInstall = value; + addininfoGallery.AllowInstall = value; + addininfoUpdates.AllowInstall = value; + } + } + + public AddinManagerDialog (Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); +// TransientFor = parent; +// HasSeparator = false; + +// Services.PlaceDialog (this, parent); + Show (); + + addininfoInstalled.Init (service); + addininfoGallery.Init (service); + + addinTree.Selection.Mode = SelectionMode.Multiple; + tree = new AddinTreeWidget (addinTree); + addinTree.Selection.Changed += OnSelectionChanged; + tree.VersionVisible = false; + + galleryTreeView.Selection.Mode = SelectionMode.Multiple; + galleryTree = new AddinTreeWidget (galleryTreeView); + galleryTree.VersionVisible = false; + galleryTree.ShowInstalledMarkers = true; + galleryTreeView.Selection.Changed += OnGallerySelectionChanged; + + updatesTreeView.Selection.Mode = SelectionMode.Multiple; + updatesTree = new AddinTreeWidget (updatesTreeView); + updatesTree.VersionVisible = false; + updatesTree.ShowCategories = false; + updatesTree.ShowInstalledMarkers = true; + updatesTreeView.Selection.Changed += OnGallerySelectionChanged; + + repoStore = new ListStore (typeof(string), typeof(string)); + repoCombo.Model = repoStore; + CellRendererText crt = new CellRendererText (); + repoCombo.PackStart (crt, true); + repoCombo.AddAttribute (crt, "text", 0); + repoCombo.RowSeparatorFunc = delegate(ITreeModel model, TreeIter iter) { + string val = (string) model.GetValue (iter, 0); + return val == "---"; + }; + + // Make sure the tree has the focus when switching tabs + + vboxUpdates.FocusChain = new Widget [] { scrolledUpdates, eboxRepoUpdates }; + vboxGallery.FocusChain = new Widget [] { scrolledGallery, eboxRepo }; + + // Improve the look of the headers + + HBox tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("plugin-22.png")), false, false, 0); + installedTabLabel = new Label (Catalog.GetString ("Installed")); + tab.PackStart (installedTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (0), tab); + + tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("plugin-update-22.png")), false, false, 0); + updatesTabLabel = new Label (Catalog.GetString ("Updates")); + tab.PackStart (updatesTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (1), tab); + + tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("system-software-update_22.png")), false, false, 0); + galleryTabLabel = new Label (Catalog.GetString ("Gallery")); + tab.PackStart (galleryTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (2), tab); + + // Gradient header for the updates and gallery tabs + + HeaderBox hb = new HeaderBox (1, 0, 1, 1); + hb.SetPadding (6,6,6,6); + hb.GradientBackround = true; + hb.Show (); + hb.Replace (eboxRepo); + + hb = new HeaderBox (1, 0, 1, 1); + hb.SetPadding (6,6,6,6); + hb.GradientBackround = true; + hb.Show (); + hb.Replace (eboxRepoUpdates); + + InsertFilterEntry (); + + FillRepos (); + repoCombo.Active = 0; + + LoadAll (); + } + + void InsertFilterEntry () + { + filterEntry = new SearchEntry (); + filterEntry.Entry.SetSizeRequest (200, filterEntry.Entry.SizeRequest ().Height); + filterEntry.Parent = notebook; + filterEntry.Show (); + notebook.SizeAllocated += delegate { + RepositionFilter (); + }; + filterEntry.TextChanged += delegate { + tree.SetFilter (filterEntry.Text); + galleryTree.SetFilter (filterEntry.Text); + updatesTree.SetFilter (filterEntry.Text); + LoadAll (); + addinTree.ExpandAll (); + galleryTreeView.ExpandAll (); + }; + RepositionFilter (); + } + + void RepositionFilter () + { + int w = filterEntry.SizeRequest ().Width; + int h = filterEntry.SizeRequest ().Height; + var alloc = notebook.Allocation; + filterEntry.SetAllocation (new Gdk.Rectangle (alloc.Left + alloc.Width - 1 - w, alloc.Y, w, h)); + } + +// public override void Dispose () +// { +// base.Dispose (); +// Destroy (); +// } + + internal void OnSelectionChanged (object sender, EventArgs args) + { + UpdateAddinInfo (); + } + + internal void OnManageRepos (object sender, EventArgs e) + { + ManageSitesDialog dlg = new ManageSitesDialog (this, service); + try { + dlg.Run (); + } finally { + dlg.Destroy (); + } + } + + void LoadAll () + { + LoadInstalled (); + LoadGallery (); + LoadUpdates (); + UpdateAddinInfo (); + } + + void UpdateAddinInfo () + { + addininfoInstalled.ShowAddins (tree.ActiveAddinsData); + addininfoGallery.ShowAddins (galleryTree.ActiveAddinsData); + addininfoUpdates.ShowAddins (updatesTree.ActiveAddinsData); + } + + void LoadInstalled () + { + object s = tree.SaveStatus (); + + int count = 0; + tree.Clear (); + foreach (Addin ainfo in AddinManager.Registry.GetModules (AddinSearchFlags.IncludeAddins | AddinSearchFlags.LatestVersionsOnly)) { + if (Services.InApplicationNamespace (service, ainfo.Id) && !ainfo.Description.IsHidden) { + AddinHeader ah = SetupService.GetAddinHeader (ainfo); + if (IsFiltered (ah)) + continue; + AddinStatus st = AddinStatus.Installed; + if (!ainfo.Enabled || Services.GetMissingDependencies (ainfo).Any()) + st |= AddinStatus.Disabled; + if (addininfoInstalled.GetUpdate (ainfo) != null) + st |= AddinStatus.HasUpdate; + tree.AddAddin (ah, ainfo, st); + count++; + } + } + + if (count > 0) + tree.RestoreStatus (s); + else + tree.ShowEmptyMessage (); + + UpdateAddinInfo (); + + installedTabLabel.Text = Catalog.GetString ("Installed"); + + if (filterEntry.Text.Length != 0 && count > 0) + installedTabLabel.Text += " (" + count + ")"; + } + + void FillRepos () + { + int i = repoCombo.Active; + repoStore.Clear (); + + repoStore.AppendValues (Catalog.GetString ("All repositories"), AllRepoMarker); + + foreach (AddinRepository rep in service.Repositories.GetRepositories ()) { + if (rep.Enabled) + repoStore.AppendValues (rep.Title, rep.Url); + } + repoStore.AppendValues ("---", ""); + repoStore.AppendValues (Catalog.GetString ("Manage Repositories..."), ManageRepoMarker); + repoCombo.Active = i; + } + + string GetRepoSelection () + { + Gtk.TreeIter iter; + if (!repoCombo.GetActiveIter (out iter)) + return null; + return (string) repoStore.GetValue (iter, 1); + } + + void LoadGallery () + { + object s = galleryTree.SaveStatus (); + + galleryTree.Clear (); + + string rep = GetRepoSelection (); + + AddinRepositoryEntry[] reps; + if (rep == AllRepoMarker) + reps = service.Repositories.GetAvailableAddins (RepositorySearchFlags.LatestVersionsOnly); + else + reps = service.Repositories.GetAvailableAddins (rep, RepositorySearchFlags.LatestVersionsOnly); + + int count = 0; + + foreach (AddinRepositoryEntry arep in reps) + { + if (!Services.InApplicationNamespace (service, arep.Addin.Id)) + continue; + + if (IsFiltered (arep.Addin)) + continue; + + AddinStatus status = AddinStatus.NotInstalled; + + // Find whatever version is installed + Addin sinfo = AddinManager.Registry.GetAddin (Addin.GetIdName (arep.Addin.Id)); + + if (sinfo != null) { + status |= AddinStatus.Installed; + if (!sinfo.Enabled || Services.GetMissingDependencies (sinfo).Any()) + status |= AddinStatus.Disabled; + if (Addin.CompareVersions (sinfo.Version, arep.Addin.Version) > 0) + status |= AddinStatus.HasUpdate; + } + galleryTree.AddAddin (arep.Addin, arep, status); + count++; + } + + if (count > 0) + galleryTree.RestoreStatus (s); + else + galleryTree.ShowEmptyMessage (); + + galleryTabLabel.Text = Catalog.GetString ("Gallery"); + + if (filterEntry.Text.Length != 0 && count > 0) + galleryTabLabel.Text += " (" + count + ")"; + } + + void LoadUpdates () + { + object s = updatesTree.SaveStatus (); + + updatesTree.Clear (); + + AddinRepositoryEntry[] reps; + reps = service.Repositories.GetAvailableAddins (RepositorySearchFlags.LatestVersionsOnly); + + int count = 0; + + foreach (AddinRepositoryEntry arep in reps) + { + if (!Services.InApplicationNamespace (service, arep.Addin.Id)) + continue; + + // Find whatever version is installed + Addin sinfo = AddinManager.Registry.GetAddin (Addin.GetIdName (arep.Addin.Id)); + if (sinfo == null || !sinfo.Enabled || Addin.CompareVersions (sinfo.Version, arep.Addin.Version) <= 0) + continue; + + if (IsFiltered (arep.Addin)) + continue; + + AddinStatus status = AddinStatus.Installed; + if (!sinfo.Enabled || Services.GetMissingDependencies (sinfo).Any()) + status |= AddinStatus.Disabled; + + updatesTree.AddAddin (arep.Addin, arep, status | AddinStatus.HasUpdate); + count++; + } + + labelUpdates.Text = string.Format (Catalog.GetPluralString ("{0} update available", "{0} updates available", count), count); + updatesTabLabel.Text = Catalog.GetString ("Updates"); + if (count > 0) + updatesTabLabel.Text += " (" + count + ")"; + + buttonUpdateAll.Visible = count > 0; + + if (count > 0) + updatesTree.RestoreStatus (s); + else + updatesTree.ShowEmptyMessage (); + } + + bool IsFiltered (AddinHeader ah) + { + if (filterEntry.Text.Length == 0) + return false; + if (ah.Name.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + if (ah.Description.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + if (ah.Id.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + return true; + } + + void ManageSites () + { + ManageSitesDialog dlg = new ManageSitesDialog (this, service); + try { + dlg.Run (); + repoCombo.Active = lastRepoActive; + FillRepos (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnRepoComboChanged (object sender, System.EventArgs e) + { + if (GetRepoSelection () == ManageRepoMarker) + ManageSites (); + else + LoadGallery (); + lastRepoActive = repoCombo.Active; + } + + protected virtual void OnGallerySelectionChanged (object sender, System.EventArgs e) + { + UpdateAddinInfo (); + } + + protected virtual void OnButtonRefreshClicked (object sender, System.EventArgs e) + { + ProgressDialog pdlg = new ProgressDialog (this); + pdlg.Show (); + pdlg.SetMessage (AddinManager.CurrentLocalizer.GetString ("Updating repository")); + bool updateDone = false; + + Thread t = new Thread (delegate () { + try { + service.Repositories.UpdateAllRepositories (pdlg); + } finally { + updateDone = true; + } + }); + t.Start (); + while (!updateDone) { + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + Thread.Sleep (50); + } + pdlg.Destroy (); + LoadGallery (); + LoadUpdates (); + } + + protected virtual void OnInstallClicked (object sender, System.EventArgs e) + { + InstallDialog dlg = new InstallDialog (this, service); + try { + List<AddinRepositoryEntry> selectedEntry = ((AddinInfoView)sender).SelectedEntries; + dlg.InitForInstall (selectedEntry.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnUninstallClicked (object sender, System.EventArgs e) + { + List<Addin> selectedAddin = ((AddinInfoView)sender).SelectedAddins; + InstallDialog dlg = new InstallDialog (this, service); + try { + dlg.InitForUninstall (selectedAddin.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) { + LoadAll (); + } + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnUpdateClicked (object sender, System.EventArgs e) + { + List<AddinRepositoryEntry> selectedEntry = ((AddinInfoView)sender).SelectedEntries; + InstallDialog dlg = new InstallDialog (this, service); + try { + dlg.InitForInstall (selectedEntry.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnEnableDisableClicked (object sender, System.EventArgs e) + { + try { + foreach (Addin a in ((AddinInfoView)sender).SelectedAddins) { + a.Enabled = !a.Enabled; + } + LoadAll (); + } + catch (Exception ex) { + Services.ShowError (ex, null, this, true); + } + } + + protected virtual void OnUpdateAll (object sender, System.EventArgs e) + { + object[] data = updatesTree.AddinsData; + AddinRepositoryEntry[] entries = new AddinRepositoryEntry [data.Length]; + Array.Copy (data, entries, data.Length); + InstallDialog dlg = new InstallDialog (this, service); + try { + dlg.InitForInstall (entries); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + static string lastFolder; + + protected virtual void OnButtonInstallFromFileClicked (object sender, System.EventArgs e) + { + string[] files; + Gtk.FileChooserDialog dlg = new Gtk.FileChooserDialog (Catalog.GetString ("Install Add-in Package"), this, FileChooserAction.Open); + try { + if (lastFolder != null) + dlg.SetCurrentFolder (lastFolder); + else + dlg.SetCurrentFolder (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); + dlg.SelectMultiple = true; + + Gtk.FileFilter f = new Gtk.FileFilter (); + f.AddPattern ("*.mpack"); + f.Name = Catalog.GetString ("Add-in packages"); + dlg.AddFilter (f); + + f = new Gtk.FileFilter (); + f.AddPattern ("*"); + f.Name = Catalog.GetString ("All files"); + dlg.AddFilter (f); + + dlg.AddButton (Gtk.Stock.Cancel, ResponseType.Cancel); + dlg.AddButton (Gtk.Stock.Open, ResponseType.Ok); + if (dlg.Run () != (int) Gtk.ResponseType.Ok) + return; + files = dlg.Filenames; + lastFolder = dlg.CurrentFolder; + } finally { + dlg.Destroy (); + } + + InstallDialog idlg = new InstallDialog (this, service); + try { + idlg.InitForInstall (files); + if (idlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + idlg.Destroy (); + } + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerWindow.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerWindow.cs new file mode 100644 index 0000000..ce98235 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerWindow.cs @@ -0,0 +1,75 @@ +// +// AddinManagerWindow.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins.GuiGtk3 +{ + public class AddinManagerWindow + { + private static bool mAllowInstall = true; + + public static bool AllowInstall + { + get { return mAllowInstall; } + set { mAllowInstall = value; } + } + + private AddinManagerWindow() + { + } + + private static void InitDialog (AddinManagerDialog dlg) + { + dlg.AllowInstall = AllowInstall; + } + + public static Gtk.Window Show (Gtk.Window parent) + { + + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.AddinManagerDialog.ui", null); + AddinManagerDialog dlg = new AddinManagerDialog (builder, builder.GetObject ("window1").Handle); + InitDialog (dlg); + parent.Add (dlg); + dlg.Show (); + return dlg; + } + +// public static void Run (Gtk.Window parent) +// { +// AddinManagerDialog dlg = new AddinManagerDialog (parent); +// try { +// InitDialog (dlg); +// dlg.Run (); +// } finally { +// dlg.Destroy (); +// } +// } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinTreeWidget.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinTreeWidget.cs new file mode 100644 index 0000000..3326f3c --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinTreeWidget.cs @@ -0,0 +1,572 @@ +// +// AddinTreeWidget.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using Gtk; +using Gdk; +using Mono.Addins; +using Mono.Addins.Setup; +using Mono.Unix; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Mono.Addins.GuiGtk3 +{ + public class AddinTreeWidget + { + protected Gtk.TreeView treeView; + protected Gtk.TreeStore treeStore; + bool allowSelection; + ArrayList selected = new ArrayList (); + Hashtable addinData = new Hashtable (); + TreeViewColumn versionColumn; + string filter; + Dictionary<string,Gdk.Pixbuf> cachedIcons = new Dictionary<string, Gdk.Pixbuf> (); + bool disposed; + + Gdk.Pixbuf iconInstalled; + Gdk.Pixbuf updateOverlay; + Gdk.Pixbuf installedOverlay; + + public event EventHandler SelectionChanged; + + const int ColAddin = 0; + const int ColData = 1; + const int ColName = 2; + const int ColVersion = 3; + const int ColAllowSelection = 4; + const int ColSelected = 5; + const int ColImage = 6; + const int ColShowImage = 7; + + public AddinTreeWidget (Gtk.TreeView treeView) + { + iconInstalled = Gdk.Pixbuf.LoadFromResource ("plugin-32.png"); + updateOverlay = Gdk.Pixbuf.LoadFromResource ("software-update-available-overlay.png"); + installedOverlay = Gdk.Pixbuf.LoadFromResource ("installed-overlay.png"); + + this.treeView = treeView; + ArrayList list = new ArrayList (); + AddStoreTypes (list); + Type[] types = (Type[]) list.ToArray (typeof(Type)); + treeStore = new Gtk.TreeStore (types); + treeView.Model = treeStore; + CreateColumns (); + ShowCategories = true; + + treeView.Destroyed += HandleTreeViewDestroyed; + } + + void HandleTreeViewDestroyed (object sender, EventArgs e) + { + disposed = true; + foreach (var px in cachedIcons.Values) + if (px != null) px.Dispose (); + } + + internal void SetFilter (string text) + { + this.filter = text; + } + + internal void ShowEmptyMessage () + { + treeStore.AppendValues (null, null, Catalog.GetString ("No add-ins found"), "", false, false, null, false); + } + + protected virtual void AddStoreTypes (ArrayList list) + { + list.Add (typeof(object)); + list.Add (typeof(object)); + list.Add (typeof(string)); + list.Add (typeof(string)); + list.Add (typeof(bool)); + list.Add (typeof(bool)); + list.Add (typeof (Pixbuf)); + list.Add (typeof(bool)); + } + + protected virtual void CreateColumns () + { + TreeViewColumn col = new TreeViewColumn (); + col.Title = Catalog.GetString ("Add-in"); + + CellRendererToggle crtog = new CellRendererToggle (); + crtog.Activatable = true; + crtog.Toggled += new ToggledHandler (OnAddinToggled); + col.PackStart (crtog, false); + + CellRendererPixbuf pr = new CellRendererPixbuf (); + col.PackStart (pr, false); + col.AddAttribute (pr, "pixbuf", ColImage); + col.AddAttribute (pr, "visible", ColShowImage); + + CellRendererText crt = new CellRendererText (); + crt.Ellipsize = Pango.EllipsizeMode.End; + col.PackStart (crt, true); + + col.AddAttribute (crt, "markup", ColName); + col.AddAttribute (crtog, "visible", ColAllowSelection); + col.AddAttribute (crtog, "active", ColSelected); + col.Expand = true; + treeView.AppendColumn (col); + + col = new TreeViewColumn (); + col.Title = Catalog.GetString ("Version"); + col.PackStart (crt, true); + col.AddAttribute (crt, "markup", ColVersion); + versionColumn = col; + treeView.AppendColumn (col); + } + + public bool AllowSelection { + get { return allowSelection; } + set { allowSelection = value; } + } + + public bool VersionVisible { + get { + return versionColumn.Visible; + } + set { + versionColumn.Visible = value; + treeView.HeadersVisible = value; + } + } + + public bool ShowCategories { get; set; } + + void OnAddinToggled (object o, ToggledArgs args) + { + TreeIter it; + if (treeStore.GetIter (out it, new TreePath (args.Path))) { + bool sel = !(bool) treeStore.GetValue (it, 5); + treeStore.SetValue (it, 5, sel); + AddinHeader info = (AddinHeader) treeStore.GetValue (it, 0); + if (sel) + selected.Add (info); + else + selected.Remove (info); + + OnSelectionChanged (EventArgs.Empty); + } + } + + protected virtual void OnSelectionChanged (EventArgs e) + { + if (SelectionChanged != null) + SelectionChanged (this, e); + } + + public void Clear () + { + addinData.Clear (); + selected.Clear (); + treeStore.Clear (); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, bool enabled) + { + return AddAddin (info, dataItem, enabled, true); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, bool enabled, bool userDir) + { + return AddAddin (info, dataItem, enabled ? AddinStatus.Installed : AddinStatus.Disabled | AddinStatus.Installed); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, AddinStatus status) + { + addinData [info] = dataItem; + TreeIter iter; + if (ShowCategories) { + TreeIter piter = TreeIter.Zero; + if (info.Category == "") { + string otherCat = Catalog.GetString ("Other"); + piter = FindCategory (otherCat); + } else { + piter = FindCategory (info.Category); + } + iter = treeStore.AppendNode (piter); + } else { + iter = treeStore.AppendNode (); + } + UpdateRow (iter, info, dataItem, status); + return iter; + } + + protected virtual void UpdateRow (TreeIter iter, AddinHeader info, object dataItem, AddinStatus status) + { + bool sel = selected.Contains (info); + + treeStore.SetValue (iter, ColAddin, info); + treeStore.SetValue (iter, ColData, dataItem); + + string name = EscapeWithFilterMarker (info.Name); + if (!string.IsNullOrEmpty (info.Description)) { + string desc = info.Description; + int i = desc.IndexOf ('\n'); + if (i != -1) + desc = desc.Substring (0, i); + name += "\n<small><span foreground=\"darkgrey\">" + EscapeWithFilterMarker (desc) + "</span></small>"; + } + + if (status != AddinStatus.Disabled) { + treeStore.SetValue (iter, ColName, name); + treeStore.SetValue (iter, ColVersion, info.Version); + treeStore.SetValue (iter, ColAllowSelection, allowSelection); + } + else { + treeStore.SetValue (iter, ColName, "<span foreground=\"grey\">" + name + "</span>"); + treeStore.SetValue (iter, ColVersion, "<span foreground=\"grey\">" + info.Version + "</span>"); + treeStore.SetValue (iter, ColAllowSelection, false); + } + + treeStore.SetValue (iter, ColShowImage, true); + treeStore.SetValue (iter, ColSelected, sel); + SetRowIcon (iter, info, dataItem, status); + } + + void SetRowIcon (TreeIter it, AddinHeader info, object dataItem, AddinStatus status) + { + string customIcom = info.Properties.GetPropertyValue ("Icon32"); + string iconId = info.Id + " " + info.Version + " " + customIcom; + Gdk.Pixbuf customPix; + + if (customIcom.Length == 0) { + customPix = null; + iconId = "__"; + } + else if (!cachedIcons.TryGetValue (iconId, out customPix)) { + + if (dataItem is Addin) { + string file = Path.Combine (((Addin)dataItem).Description.BasePath, customIcom); + if (File.Exists (file)) { + try { + customPix = new Gdk.Pixbuf (file); + } catch (Exception ex) { + Console.WriteLine (ex); + } + } + cachedIcons [iconId] = customPix; + } + else if (dataItem is AddinRepositoryEntry) { + AddinRepositoryEntry arep = (AddinRepositoryEntry) dataItem; + string tmpId = iconId; + arep.BeginDownloadSupportFile (customIcom, delegate (IAsyncResult res) { + Gtk.Application.Invoke (delegate { + LoadRemoteIcon (it, tmpId, arep, res, info, dataItem, status); + }); + }, null); + iconId = "__"; + } + } + + StoreIcon (it, iconId, customPix, status); + } + + Gdk.Pixbuf GetCachedIcon (string id, string effect, Func<Gdk.Pixbuf> pixbufGenerator) + { + Gdk.Pixbuf pix; + if (!cachedIcons.TryGetValue (id + "_" + effect, out pix)) + cachedIcons [id + "_" + effect] = pix = pixbufGenerator (); + return pix; + } + + internal bool ShowInstalledMarkers = false; + + void StoreIcon (TreeIter it, string iconId, Gdk.Pixbuf customPix, AddinStatus status) + { + if (customPix == null) + customPix = iconInstalled; + + if ((status & AddinStatus.Installed) == 0) { + treeStore.SetValue (it, ColImage, customPix); + return; + } else if (ShowInstalledMarkers && (status & AddinStatus.HasUpdate) == 0) { + customPix = GetCachedIcon (iconId, "InstalledOverlay", delegate { return Services.AddIconOverlay (customPix, installedOverlay); }); + iconId = iconId + "_Installed"; + } + + if ((status & AddinStatus.Disabled) != 0) { + customPix = GetCachedIcon (iconId, "Desaturate", delegate { return Services.DesaturateIcon (customPix); }); + iconId = iconId + "_Desaturate"; + } + if ((status & AddinStatus.HasUpdate) != 0) + customPix = GetCachedIcon (iconId, "UpdateOverlay", delegate { return Services.AddIconOverlay (customPix, updateOverlay); }); + + treeStore.SetValue (it, ColImage, customPix); + } + + + void LoadRemoteIcon (TreeIter it, string iconId, AddinRepositoryEntry arep, IAsyncResult res, AddinHeader info, object dataItem, AddinStatus status) + { + if (!disposed && treeStore.IterIsValid (it)) { + Gdk.Pixbuf customPix = null; + try { + Gdk.PixbufLoader loader = new Gdk.PixbufLoader (arep.EndDownloadSupportFile (res)); + customPix = loader.Pixbuf; + } catch (Exception ex) { + Console.WriteLine (ex); + } + cachedIcons [iconId] = customPix; + StoreIcon (it, iconId, customPix, status); + } + } + + string EscapeWithFilterMarker (string txt) + { + if (string.IsNullOrEmpty (filter)) + return GLib.Markup.EscapeText (txt); + + StringBuilder sb = new StringBuilder (); + int last = 0; + int i = txt.IndexOf (filter, StringComparison.CurrentCultureIgnoreCase); + while (i != -1) { + sb.Append (GLib.Markup.EscapeText (txt.Substring (last, i - last))); + sb.Append ("<span color='blue'>").Append (txt.Substring (i, filter.Length)).Append ("</span>"); + last = i + filter.Length; + i = txt.IndexOf (filter, last, StringComparison.CurrentCultureIgnoreCase); + } + if (last < txt.Length) + sb.Append (GLib.Markup.EscapeText (txt.Substring (last, txt.Length - last))); + return sb.ToString (); + } + + public object GetAddinData (AddinHeader info) + { + return addinData [info]; + } + + public AddinHeader[] GetSelectedAddins () + { + return (AddinHeader[]) selected.ToArray (typeof(AddinHeader)); + } + + TreeIter FindCategory (string namePath) + { + TreeIter iter = TreeIter.Zero; + string[] paths = namePath.Split ('/'); + foreach (string name in paths) { + TreeIter child; + if (!FindCategory (iter, name, out child)) { + if (iter.Equals (TreeIter.Zero)) + iter = treeStore.AppendValues (null, null, name, "", false, false, null, false); + else + iter = treeStore.AppendValues (iter, null, null, name, "", false, false, null, false); + } + else + iter = child; + } + return iter; + } + + bool FindCategory (TreeIter piter, string name, out TreeIter child) + { + if (piter.Equals (TreeIter.Zero)) { + if (!treeStore.GetIterFirst (out child)) + return false; + } + else if (!treeStore.IterChildren (out child, piter)) + return false; + + do { + if (((string) treeStore.GetValue (child, ColName)) == name) { + return true; + } + } while (treeStore.IterNext (ref child)); + + return false; + } + + public AddinHeader ActiveAddin { + get { + AddinHeader[] sel = ActiveAddins; + if (sel.Length > 0) + return sel[0]; + else + return null; + } + } + + public AddinHeader[] ActiveAddins { + get { + List<AddinHeader> list = new List<AddinHeader> (); + foreach (TreePath p in treeView.Selection.GetSelectedRows ()) { + TreeIter iter; + treeStore.GetIter (out iter, p); + AddinHeader ah = (AddinHeader) treeStore.GetValue (iter, 0); + if (ah != null) + list.Add (ah); + } + return list.ToArray (); + } + } + + public object ActiveAddinData { + get { + AddinHeader ai = ActiveAddin; + return ai != null ? GetAddinData (ai) : null; + } + } + + public object[] ActiveAddinsData { + get { + List<object> res = new List<object> (); + foreach (AddinHeader ai in ActiveAddins) { + res.Add (GetAddinData (ai)); + } + return res.ToArray (); + } + } + + public object[] AddinsData { + get { + object[] data = new object [addinData.Count]; + addinData.Values.CopyTo (data, 0); + return data; + } + } + + public object SaveStatus () + { + TreeIter iter; + ArrayList list = new ArrayList (); + + // Save the current selection + list.Add (treeView.Selection.GetSelectedRows ()); + + if (!treeStore.GetIterFirst (out iter)) + return null; + + // Save the expand state + do { + SaveStatus (list, iter); + } while (treeStore.IterNext (ref iter)); + + return list; + } + + void SaveStatus (ArrayList list, TreeIter iter) + { + Gtk.TreePath path = treeStore.GetPath (iter); + if (treeView.GetRowExpanded (path)) + list.Add (path); + if (treeStore.IterChildren (out iter, iter)) { + do { + SaveStatus (list, iter); + } while (treeStore.IterNext (ref iter)); + } + } + + public void RestoreStatus (object ob) + { + if (ob == null) + return; + + // The first element is the selection + ArrayList list = (ArrayList) ob; + TreePath[] selpaths = (TreePath[]) list [0]; + list.RemoveAt (0); + + foreach (TreePath path in list) + treeView.ExpandRow (path, false); + + foreach (TreePath p in selpaths) + treeView.Selection.SelectPath (p); + } + + public void SelectAll () + { + TreeIter iter; + + if (!treeStore.GetIterFirst (out iter)) + return; + do { + SelectAll (iter); + } while (treeStore.IterNext (ref iter)); + OnSelectionChanged (EventArgs.Empty); + } + + void SelectAll (TreeIter iter) + { + AddinHeader info = (AddinHeader) treeStore.GetValue (iter, ColAddin); + + if (info != null) { + treeStore.SetValue (iter, ColSelected, true); + if (!selected.Contains (info)) + selected.Add (info); + treeView.ExpandToPath (treeStore.GetPath (iter)); + } else { + if (treeStore.IterChildren (out iter, iter)) { + do { + SelectAll (iter); + } while (treeStore.IterNext (ref iter)); + } + } + } + + public void UnselectAll () + { + TreeIter iter; + if (!treeStore.GetIterFirst (out iter)) + return; + do { + UnselectAll (iter); + } while (treeStore.IterNext (ref iter)); + OnSelectionChanged (EventArgs.Empty); + } + + void UnselectAll (TreeIter iter) + { + AddinHeader info = (AddinHeader) treeStore.GetValue (iter, ColAddin); + if (info != null) { + treeStore.SetValue (iter, ColSelected, false); + selected.Remove (info); + } else { + if (treeStore.IterChildren (out iter, iter)) { + do { + UnselectAll (iter); + } while (treeStore.IterNext (ref iter)); + } + } + } + } + + [Flags] + public enum AddinStatus + { + NotInstalled = 0, + Installed = 1, + Disabled = 2, + HasUpdate = 4 + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ErrorDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ErrorDialog.cs new file mode 100644 index 0000000..2ecdd5b --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ErrorDialog.cs @@ -0,0 +1,104 @@ +// +// ErrorDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + class ErrorDialog : Dialog + { + //From UI File + [UI] Button okButton; + [UI] Expander expander; + [UI] Label descriptionLabel; + [UI] TextView detailsTextView; + + TextTag tagNoWrap; + TextTag tagWrap; + + public ErrorDialog (Window parent) + { + Build (); + TransientFor = parent; + okButton.Clicked += new EventHandler (OnClose); + expander.Activated += new EventHandler (OnExpanded); + descriptionLabel.ModifyBg (StateType.Normal, new Gdk.Color (255,0,0)); + + tagNoWrap = new TextTag ("nowrap"); + tagNoWrap.WrapMode = WrapMode.None; + detailsTextView.Buffer.TagTable.Add (tagNoWrap); + + tagWrap = new TextTag ("wrap"); + tagWrap.WrapMode = WrapMode.Word; + detailsTextView.Buffer.TagTable.Add (tagWrap); + + expander.Visible = false; + } + + public string Message { + get { return descriptionLabel.Text; } + set { + string message = value; + while (message.EndsWith ("\r") || message.EndsWith ("\n")) + message = message.Substring (0, message.Length - 1); + if (!message.EndsWith (".")) message += "."; + descriptionLabel.Text = message; + } + } + + public void AddDetails (string text, bool wrapped) + { + TextIter it = detailsTextView.Buffer.EndIter; + if (wrapped) + detailsTextView.Buffer.InsertWithTags (ref it, text, tagWrap); + else + detailsTextView.Buffer.InsertWithTags (ref it, text, tagNoWrap); + expander.Visible = true; + } + + void OnClose (object sender, EventArgs args) + { + Destroy (); + } + + void OnExpanded (object sender, EventArgs args) + { + GLib.Timeout.Add (100, new GLib.TimeoutHandler (UpdateSize)); + } + + bool UpdateSize () + { + int w, h; + GetSize (out w, out h); + Resize (w, 1); + return false; + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HeaderBox.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HeaderBox.cs new file mode 100644 index 0000000..ddbd282 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HeaderBox.cs @@ -0,0 +1,196 @@ +// +// HeaderBox.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + class HeaderBox: Bin + { + Gtk.Widget child; + int topMargin; + int bottomMargin; + int leftMargin; + int rightMargin; + + int topPadding; + int bottomPadding; + int leftPadding; + int rightPadding; + + bool useCustomColor; + Gdk.Color customColor; + + public HeaderBox () + { + } + + public HeaderBox (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + SetMargins (topMargin, bottomMargin, leftMargin, rightMargin); + } + + public void Replace (Gtk.Bin parent) + { + Gtk.Widget c = parent.Child; + parent.Remove (c); + Add (c); + parent.Add (this); + } + + public void SetMargins (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + this.topMargin = topMargin; + this.bottomMargin = bottomMargin; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + } + + public void SetPadding (int topPadding, int bottomPadding, int leftPadding, int rightPadding) + { + this.topPadding = topPadding; + this.bottomPadding = bottomPadding; + this.leftPadding = leftPadding; + this.rightPadding = rightPadding; + } + + public bool GradientBackround { get; set; } + + public Gdk.Color BackgroundColor { + get { return customColor; } + set { customColor = value; useCustomColor = true; } + } + + public void ResetBackgroundColor () + { + useCustomColor = false; + } + + protected override void OnAdded (Widget widget) + { + base.OnAdded (widget); + child = widget; + } + +// protected override void OnSizeRequested (ref Requisition requisition) +// { +// if (child != null) { +// requisition = child.SizeRequest (); +// requisition.Width += leftMargin + rightMargin + leftPadding + rightPadding; +// requisition.Height += topMargin + bottomMargin + topPadding + bottomPadding; +// } else { +// requisition.Width = 0; +// requisition.Height = 0; +// } +// } + + public new void GetPreferredWidth (out int width) + { + if (child != null) { + Requisition req = child.SizeRequest (); + req.Width += leftMargin + rightMargin + leftPadding + rightPadding; + width = req.Width; + } else { + width = 0; + } + } + + public new void GetPreferredHeight (out int height) + { + if (child != null) { + Requisition req = child.SizeRequest (); + req.Height += topMargin + bottomMargin + topPadding + bottomPadding; + height = req.Height; + } else { + height = 0; + } + } + + protected override void OnSizeAllocated (Gdk.Rectangle allocation) + { + base.OnSizeAllocated (allocation); + if (allocation.Width > leftMargin + rightMargin + leftPadding + rightPadding) { + allocation.X += leftMargin + leftPadding; + allocation.Width -= leftMargin + rightMargin + leftPadding + rightPadding; + } + if (allocation.Height > topMargin + bottomMargin + topPadding + bottomPadding) { + allocation.Y += topMargin + topPadding; + allocation.Height -= topMargin + bottomMargin + topPadding + bottomPadding; + } + if (child != null) + child.SizeAllocate (allocation); + } + + public new void Draw (Cairo.Context cr) + { + Gdk.Rectangle rect; + + if (GradientBackround) { + rect = new Gdk.Rectangle (Allocation.X, Allocation.Y, Allocation.Width, Allocation.Height); + HslColor gcol = useCustomColor ? customColor : Parent.Style.Background (Gtk.StateType.Normal); + + cr.NewPath (); + cr.MoveTo (rect.X, rect.Y); + cr.RelLineTo (rect.Width, 0); + cr.RelLineTo (0, rect.Height); + cr.RelLineTo (-rect.Width, 0); + cr.RelLineTo (0, -rect.Height); + cr.ClosePath (); + using (Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Y + rect.Height - 1)) { + Cairo.Color color1 = gcol; + pat.AddColorStop (0, color1); + gcol.L -= 0.1; + if (gcol.L < 0) + gcol.L = 0; + pat.AddColorStop (1, gcol); + cr.Pattern = pat; + cr.FillPreserve (); + } + + } + + bool res = base.OnExposeEvent (evnt); + + Gdk.GC borderColor = Parent.Style.DarkGC (Gtk.StateType.Normal); + + rect = Allocation; + for (int n=0; n<topMargin; n++) + GdkWindow.DrawLine (borderColor, rect.X, rect.Y + n, rect.Left + rect.Width - 1, rect.Y + n); + + for (int n=0; n<bottomMargin; n++) + GdkWindow.DrawLine (borderColor, rect.X, rect.Top + rect.Height - 1 - n, rect.Left + rect.Width - 1, rect.Top + rect.Height - 1 - n); + + for (int n=0; n<leftMargin; n++) + GdkWindow.DrawLine (borderColor, rect.X + n, rect.Y, rect.X + n, rect.Top + rect.Height - 1); + + for (int n=0; n<rightMargin; n++) + GdkWindow.DrawLine (borderColor, rect.Left + rect.Width - 1 - n, rect.Y, rect.Left + rect.Width - 1 - n, rect.Top + rect.Height - 1); + + return res; + } + } +} + diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HoverImageButton.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HoverImageButton.cs new file mode 100644 index 0000000..220d4e9 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HoverImageButton.cs @@ -0,0 +1,265 @@ +/*************************************************************************** + * HoverImageButton.cs + * + * Copyright (C) 2007 Novell, Inc. + * Written by Aaron Bockover <abockover@novell.com> + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + class HoverImageButton : EventBox + { + private static Gdk.Cursor hand_cursor = new Gdk.Cursor(Gdk.CursorType.Hand1); + + private IconSize icon_size = IconSize.Menu; + private string [] icon_names = { "image-missing", Stock.MissingImage }; + private Gdk.Pixbuf normal_pixbuf; + private Gdk.Pixbuf active_pixbuf; + private Image image; + private bool is_hovering; + private bool is_pressed; + + private bool draw_focus = true; + + private event EventHandler clicked; + + public event EventHandler Clicked { + add { clicked += value; } + remove { clicked -= value; } + } + + public HoverImageButton() + { + CanFocus = true; + + image = new Image(); + image.Show(); + Add(image); + } + + public HoverImageButton(IconSize size, string icon_name) : this(size, new string [] { icon_name }) + { + } + + public HoverImageButton(IconSize size, string [] icon_names) : this() + { + this.icon_size = size; + this.icon_names = icon_names; + } + + public new void Activate() + { + EventHandler handler = clicked; + if(handler != null) { + handler(this, EventArgs.Empty); + } + } + + private bool changing_style = false; + protected override void OnStyleSet(Style previous_style) + { + if(changing_style) { + return; + } + + changing_style = true; + if (normal_pixbuf == null) + LoadPixbufs(); + changing_style = false; + } + + protected override bool OnEnterNotifyEvent(Gdk.EventCrossing evnt) + { + image.GdkWindow.Cursor = hand_cursor; + is_hovering = true; + UpdateImage(); + return base.OnEnterNotifyEvent(evnt); + } + + protected override bool OnLeaveNotifyEvent(Gdk.EventCrossing evnt) + { + is_hovering = false; + UpdateImage(); + return base.OnLeaveNotifyEvent(evnt); + } + + protected override bool OnFocusInEvent(Gdk.EventFocus evnt) + { + bool ret = base.OnFocusInEvent(evnt); + UpdateImage(); + return ret; + } + + protected override bool OnFocusOutEvent(Gdk.EventFocus evnt) + { + bool ret = base.OnFocusOutEvent(evnt); + UpdateImage(); + return ret; + } + + protected override bool OnButtonPressEvent(Gdk.EventButton evnt) + { + if(evnt.Button != 1) { + return base.OnButtonPressEvent(evnt); + } + + HasFocus = true; + is_pressed = true; + QueueDraw(); + + return base.OnButtonPressEvent(evnt); + } + + protected override bool OnButtonReleaseEvent(Gdk.EventButton evnt) + { + if(evnt.Button != 1) { + return base.OnButtonReleaseEvent(evnt); + } + + is_pressed = false; + QueueDraw(); + Activate(); + + return base.OnButtonReleaseEvent(evnt); + } + + public new void Draw (Cairo.Context cr) + { + base.Draw (cr); + + PropagateDraw (Child, cr); + + if(HasFocus && draw_focus) { + Style.PaintFocus(Style, cr, StateType.Normal, this, "button", + 0, 0, Allocation.Width, Allocation.Height); + } + } + + private void UpdateImage() + { + image.Pixbuf = is_hovering || is_pressed || HasFocus + ? active_pixbuf : normal_pixbuf; + } + + private void LoadPixbufs() + { + int width, height; + Icon.SizeLookup(icon_size, out width, out height); + IconTheme theme = IconTheme.GetForScreen(Screen); + + if(normal_pixbuf != null) { + normal_pixbuf.Dispose(); + normal_pixbuf = null; + } + + if(active_pixbuf != null) { + active_pixbuf.Dispose(); + active_pixbuf = null; + } + + for(int i = 0; i < icon_names.Length; i++) { + try { + normal_pixbuf = RenderIcon(icon_names[i], icon_size, null) + ?? theme.LoadIcon(icon_names[i], width, 0); + active_pixbuf = ColorShiftPixbuf(normal_pixbuf, 30); + break; + } catch { + } + } + + UpdateImage(); + } + + public Gdk.Pixbuf Pixbuf { + get { return this.normal_pixbuf; } + set { + this.normal_pixbuf = value; + active_pixbuf = ColorShiftPixbuf(normal_pixbuf, 30); + UpdateImage(); + } + } + + + private static byte PixelClamp(int val) + { + return (byte)System.Math.Max(0, System.Math.Min(255, val)); + } + + private unsafe Gdk.Pixbuf ColorShiftPixbuf(Gdk.Pixbuf src, byte shift) + { + Gdk.Pixbuf dest = new Gdk.Pixbuf(src.Colorspace, src.HasAlpha, src.BitsPerSample, src.Width, src.Height); + + byte *src_pixels_orig = (byte *)src.Pixels; + byte *dest_pixels_orig = (byte *)dest.Pixels; + + for(int i = 0; i < src.Height; i++) { + byte *src_pixels = src_pixels_orig + i * src.Rowstride; + byte *dest_pixels = dest_pixels_orig + i * dest.Rowstride; + + for(int j = 0; j < src.Width; j++) { + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + + if(src.HasAlpha) { + *(dest_pixels++) = *(src_pixels++); + } + } + } + + return dest; + } + + public string [] IconNames { + get { return icon_names; } + set { + icon_names = value; + LoadPixbufs(); + } + } + + public IconSize IconSize { + get { return icon_size; } + set { + icon_size = value; + LoadPixbufs(); + } + } + + public Image Image { + get { return image; } + } + + public bool DrawFocus { + get { return draw_focus; } + set { + draw_focus = value; + QueueDraw(); + } + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HslColor.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HslColor.cs new file mode 100644 index 0000000..0012d24 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HslColor.cs @@ -0,0 +1,164 @@ +// +// HslColor.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Gdk; + +namespace Mono.Addins.GuiGtk3 +{ + struct HslColor + { + public double H { + get; + set; + } + + public double S { + get; + set; + } + + public double L { + get; + set; + } + + static Gdk.Color black = new Gdk.Color (0, 0, 0); + public static implicit operator Color (HslColor hsl) + { + if (hsl.L > 1) hsl.L = 1; + if (hsl.L < 0) hsl.L = 0; + if (hsl.H > 1) hsl.H = 1; + if (hsl.H < 0) hsl.H = 0; + if (hsl.S > 1) hsl.S = 1; + if (hsl.S < 0) hsl.S = 0; + + double r = 0, g = 0, b = 0; + + if (hsl.L == 0) + return black; + + if (hsl.S == 0) { + r = g = b = hsl.L; + } else { + double temp2 = hsl.L <= 0.5 ? hsl.L * (1.0 + hsl.S) : hsl.L + hsl.S -(hsl.L * hsl.S); + double temp1 = 2.0 * hsl.L - temp2; + + double[] t3 = new double[] { hsl.H + 1.0 / 3.0, hsl.H, hsl.H - 1.0 / 3.0}; + double[] clr= new double[] { 0, 0, 0}; + for (int i = 0; i < 3; i++) { + if (t3[i] < 0) + t3[i] += 1.0; + if (t3[i] > 1) + t3[i]-=1.0; + if (6.0 * t3[i] < 1.0) + clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0; + else if (2.0 * t3[i] < 1.0) + clr[i] = temp2; + else if (3.0 * t3[i] < 2.0) + clr[i] = (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - t3[i]) * 6.0); + else + clr[i] = temp1; + } + + r = clr[0]; + g = clr[1]; + b = clr[2]; + } + return new Color ((byte)(255 * r), + (byte)(255 * g), + (byte)(255 * b)); + } + + public static Cairo.Color ToCairoColor (Gdk.Color color) + { + return new Cairo.Color ((double)color.Red / ushort.MaxValue, + (double)color.Green / ushort.MaxValue, + (double)color.Blue / ushort.MaxValue); + } + + public static implicit operator Cairo.Color (HslColor hsl) + { + return ToCairoColor ((Gdk.Color)hsl); + } + + public static implicit operator HslColor (Color color) + { + return new HslColor (color); + } + + public HslColor (Color color) : this () + { + double r = color.Red / (double)ushort.MaxValue; + double g = color.Green / (double)ushort.MaxValue; + double b = color.Blue / (double)ushort.MaxValue; + + double v = System.Math.Max (r, g); + v = System.Math.Max (v, b); + + double m = System.Math.Min (r, g); + m = System.Math.Min (m, b); + + this.L = (m + v) / 2.0; + if (this.L <= 0.0) + return; + double vm = v - m; + this.S = vm; + + if (this.S > 0.0) { + this.S /= (this.L <= 0.5) ? (v + m) : (2.0 - v - m); + } else { + return; + } + + double r2 = (v - r) / vm; + double g2 = (v - g) / vm; + double b2 = (v - b) / vm; + + if (r == v) { + this.H = (g == m ? 5.0 + b2 : 1.0 - g2); + } else if (g == v) { + this.H = (b == m ? 1.0 + r2 : 3.0 - b2); + } else { + this.H = (r == m ? 3.0 + g2 : 5.0 - r2); + } + this.H /= 6.0; + } + + public static double Brightness (Gdk.Color c) + { + double r = c.Red / (double)ushort.MaxValue; + double g = c.Green / (double)ushort.MaxValue; + double b = c.Blue / (double)ushort.MaxValue; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public override string ToString () + { + return string.Format ("[HslColor: H={0}, S={1}, L={2}]", H, S, L); + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallDialog.cs new file mode 100644 index 0000000..464fbec --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallDialog.cs @@ -0,0 +1,288 @@ +// +// InstallDialog.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using System.Text; +using Mono.Unix; +using System.Threading; +using System.Linq; +using System.Collections.Generic; +using UI = Gtk.Builder.ObjectAttribute; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + internal class InstallDialog : Gtk.Dialog + { + //From UI File + [UI] VBox boxProgress; + [UI] Button buttonOk; + [UI] Label labelInfo; + [UI] ScrolledWindow scrolledwindow1; + [UI] HSeparator insSeparator; + [UI] Label globalProgressLabel; + [UI] ProgressBar mainProgressBar; + [UI] Button buttonCancel; + + string[] filesToInstall; + AddinRepositoryEntry[] addinsToInstall; + PackageCollection packagesToInstall; + SetupService service; + Gtk.ResponseType response = Gtk.ResponseType.None; + IEnumerable<string> uninstallIds; + InstallMonitor installMonitor; + bool installing; + const int MaxHeight = 350; + + public InstallDialog (Gtk.Window parent, SetupService service) + { + this.Build (); + this.service = service; + TransientFor = parent; + WindowPosition = Gtk.WindowPosition.CenterOnParent; +// Services.PlaceDialog (this, parent); + boxProgress.Visible = false; + Resizable = false; + } + + public void InitForInstall (AddinRepositoryEntry[] addinsToInstall) + { + this.addinsToInstall = addinsToInstall; + FillSummaryPage (); +// Services.PlaceDialog (this, TransientFor); + } + + public void InitForInstall (string[] filesToInstall) + { + this.filesToInstall = filesToInstall; + FillSummaryPage (); +// Services.PlaceDialog (this, TransientFor); + } + + public void InitForUninstall (Addin[] info) + { + this.uninstallIds = info.Select (a => a.Id); + buttonOk.Label = Catalog.GetString ("Uninstall"); + + HashSet<Addin> sinfos = new HashSet<Addin> (); + + StringBuilder sb = new StringBuilder (); + sb.Append ("<b>").Append (Catalog.GetString ("The following packages will be uninstalled:")).Append ("</b>\n\n"); + foreach (var a in info) { + sb.Append (a.Name + "\n\n"); + sinfos.UnionWith (service.GetDependentAddins (a.Id, true)); + } + + if (sinfos.Count > 0) { + sb.Append ("<b>").Append (Catalog.GetString ("There are other add-ins that depend on the previous ones which will also be uninstalled:")).Append ("</b>\n\n"); + foreach (Addin si in sinfos) + sb.Append (si.Description.Name + "\n"); + } + + ShowMessage (sb.ToString ()); +// Services.PlaceDialog (this, TransientFor); + } + + void FillSummaryPage () + { + PackageCollection packs = new PackageCollection (); + + if (filesToInstall != null) { + foreach (string file in filesToInstall) { + packs.Add (Package.FromFile (file)); + } + } + else { + foreach (AddinRepositoryEntry arep in addinsToInstall) { + packs.Add (Package.FromRepository (arep)); + } + } + + packagesToInstall = new PackageCollection (packs); + + PackageCollection toUninstall; + DependencyCollection unresolved; + bool res; + + InstallMonitor m = new InstallMonitor (); + res = service.ResolveDependencies (m, packs, out toUninstall, out unresolved); + + StringBuilder sb = new StringBuilder (); + if (!res) { + sb.Append ("<b><span foreground=\"red\">").Append (Catalog.GetString ("The selected add-ins can't be installed because there are dependency conflicts.")).Append ("</span></b>\n"); + foreach (string s in m.Errors) { + sb.Append ("<b><span foreground=\"red\">" + s + "</span></b>\n"); + } + sb.Append ("\n"); + } + + if (m.Warnings.Count != 0) { + foreach (string w in m.Warnings) { + sb.Append ("<b><span foreground=\"red\">" + w + "</span></b>\n"); + } + sb.Append ("\n"); + } + + sb.Append ("<b>").Append (Catalog.GetString ("The following packages will be installed:")).Append ("</b>\n\n"); + foreach (Package p in packs) { + sb.Append (p.Name); + if (!p.SharedInstall) + sb.Append (Catalog.GetString (" (in user directory)")); + sb.Append ("\n"); + } + sb.Append ("\n"); + + if (toUninstall.Count > 0) { + sb.Append ("<b>").Append (Catalog.GetString ("The following packages need to be uninstalled:")).Append ("</b>\n\n"); + foreach (Package p in toUninstall) { + sb.Append (p.Name + "\n"); + } + sb.Append ("\n"); + } + + if (unresolved.Count > 0) { + sb.Append ("<b>").Append (Catalog.GetString ("The following dependencies could not be resolved:")).Append ("</b>\n\n"); + foreach (Dependency p in unresolved) { + sb.Append (p.Name + "\n"); + } + sb.Append ("\n"); + } + buttonOk.Sensitive = res; + ShowMessage (sb.ToString ()); + } + + void ShowMessage (string txt) + { + labelInfo.Markup = txt.TrimEnd ('\n','\t',' '); + if (labelInfo.SizeRequest ().Height > MaxHeight) { + scrolledwindow1.VscrollbarPolicy = Gtk.PolicyType.Automatic; + scrolledwindow1.HeightRequest = MaxHeight; + } + else { + scrolledwindow1.HeightRequest = labelInfo.SizeRequest ().Height; + } + } + + protected virtual void OnButtonOkClicked (object sender, System.EventArgs e) + { + if (response != Gtk.ResponseType.None) { + Respond (response); + return; + } + Install (); + } + + protected virtual void OnButtonCancelClicked (object sender, System.EventArgs e) + { + if (installing) { + if (Services.AskQuestion (Catalog.GetString ("Are you sure you want to cancel the installation?"))) + installMonitor.Cancel (); + } else + Respond (Gtk.ResponseType.Cancel); + } + + void Install () + { + insSeparator.Visible = true; + boxProgress.Visible = true; + buttonOk.Sensitive = false; + + string txt; + string errmessage; + string warnmessage; + + ThreadStart oper; + + if (uninstallIds == null) { + installMonitor = new InstallMonitor (globalProgressLabel, mainProgressBar, Catalog.GetString ("Installing Add-ins")); + oper = new ThreadStart (RunInstall); + errmessage = Catalog.GetString ("The installation failed!"); + warnmessage = Catalog.GetString ("The installation has completed with warnings."); + } else { + installMonitor = new InstallMonitor (globalProgressLabel, mainProgressBar, Catalog.GetString ("Uninstalling Add-ins")); + oper = new ThreadStart (RunUninstall); + errmessage = Catalog.GetString ("The uninstallation failed!"); + warnmessage = Catalog.GetString ("The uninstallation has completed with warnings."); + } + + installing = true; + oper (); + installing = false; + + buttonCancel.Visible = false; + buttonOk.Label = Gtk.Stock.Close; + buttonOk.UseStock = true; + + if (installMonitor.Success && installMonitor.Warnings.Count == 0) { + Respond (Gtk.ResponseType.Ok); + return; + } else if (installMonitor.Success) { + txt = "<b>" + warnmessage + "</b>\n\n"; + foreach (string s in installMonitor.Warnings) + txt += GLib.Markup.EscapeText (s) + "\n"; + response = Gtk.ResponseType.Ok; + buttonOk.Sensitive = true; + } else { + buttonCancel.Label = Gtk.Stock.Close; + buttonCancel.UseStock = true; + txt = "<span foreground=\"red\"><b>" + errmessage + "</b></span>\n\n"; + foreach (string s in installMonitor.Errors) + txt += GLib.Markup.EscapeText (s) + "\n"; + response = Gtk.ResponseType.Cancel; + buttonOk.Sensitive = true; + } + + ShowMessage (txt); + } + + void RunInstall () + { + try { + if (filesToInstall != null) + service.Install (installMonitor, filesToInstall); + else + service.Install (installMonitor, packagesToInstall); + } catch (Exception ex) { + installMonitor.Errors.Add (ex.Message); + } finally { + installMonitor.Dispose (); + } + } + + void RunUninstall () + { + try { + service.Uninstall (installMonitor, uninstallIds); + } catch (Exception ex) { + installMonitor.Errors.Add (ex.Message); + } finally { + installMonitor.Dispose (); + } + } + } +} + diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallMonitor.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallMonitor.cs new file mode 100644 index 0000000..e5deaa7 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallMonitor.cs @@ -0,0 +1,133 @@ +// +// AddinInstallDialog.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Text; +using System.Threading; +using System.Collections; +using System.Collections.Specialized; +using System.Diagnostics; +using Mono.Unix; +using Gtk; +using Mono.Addins.Setup; +using Mono.Addins.Description; +namespace Mono.Addins.GuiGtk3 +{ + class InstallMonitor: IProgressStatus, IDisposable + { + Label progressLabel; + ProgressBar progressBar; + StringCollection errors = new StringCollection (); + StringCollection warnings = new StringCollection (); + bool canceled; + bool done; + string mainOperation; + + public InstallMonitor (Label progressLabel, ProgressBar progressBar, string mainOperation) + { + this.progressLabel = progressLabel; + this.progressBar = progressBar; + this.mainOperation = mainOperation; + } + + public InstallMonitor () + { + } + + public void SetMessage (string msg) + { + if (progressLabel != null) + progressLabel.Markup = "<b>" + GLib.Markup.EscapeText (mainOperation) + "</b>\n" + GLib.Markup.EscapeText (msg); + RunPendingEvents (); + } + + public void SetProgress (double progress) + { + if (progressBar != null) + progressBar.Fraction = progress; + RunPendingEvents (); + } + + public void Log (string msg) + { + Console.WriteLine (msg); + } + + public void ReportWarning (string message) + { + warnings.Add (message); + } + + public void ReportError (string message, Exception exception) + { + errors.Add (message); + } + + public bool IsCanceled { + get { return canceled; } + } + + public StringCollection Errors { + get { return errors; } + } + + public StringCollection Warnings { + get { return warnings; } + } + + public void Cancel () + { + canceled = true; + } + + public int LogLevel { + get { return 1; } + } + + public void Dispose () + { + done = true; + } + + public void WaitForCompleted () + { + while (!done) { + RunPendingEvents (); + Thread.Sleep (50); + } + } + + public bool Success { + get { return errors.Count == 0; } + } + + void RunPendingEvents () + { + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ManageSitesDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ManageSitesDialog.cs new file mode 100644 index 0000000..df35c62 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ManageSitesDialog.cs @@ -0,0 +1,179 @@ +// +// ManageSitesDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using Mono.Unix; +using System.Threading; + +using Mono.Addins.Setup; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + + +namespace Mono.Addins.GuiGtk3 +{ + class ManageSitesDialog : Dialog + { + //From UI File + [UI] TreeView repoTree; + [UI] Button btnRemove; + + ListStore treeStore; + SetupService service; + + public ManageSitesDialog (Gtk.Window parent, SetupService service) + { + Build (); + TransientFor = parent; +// Services.PlaceDialog (this, parent); + this.service = service; + treeStore = new Gtk.ListStore (typeof (string), typeof (string), typeof(bool)); + repoTree.Model = treeStore; + repoTree.HeadersVisible = false; + var crt = new Gtk.CellRendererToggle (); + crt.Toggled += HandleRepoToggled; + repoTree.AppendColumn ("", crt, "active", 2); + repoTree.AppendColumn ("", new Gtk.CellRendererText (), "markup", 1); + repoTree.Selection.Changed += new EventHandler(OnSelect); + + AddinRepository[] reps = service.Repositories.GetRepositories (); + foreach (AddinRepository rep in reps) + AppendRepository (rep); + + btnRemove.Sensitive = false; + } + +// public override void Dispose () +// { +// base.Dispose (); +// Destroy (); +// } + + void AppendRepository (AddinRepository rep) + { + string txt = GLib.Markup.EscapeText (rep.Title) + "\n<span color='darkgray'>" + GLib.Markup.EscapeText (rep.Url) + "</span>"; + treeStore.AppendValues (rep.Url, txt, rep.Enabled); + } + + protected void OnAdd (object sender, EventArgs e) + { + NewSiteDialog dlg = new NewSiteDialog (this); + try { + if (dlg.Run ()) { + string url = dlg.Url; + if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://")) { + url = "http://" + url; + } + + try { + new Uri (url); + } catch { + Services.ShowError (null, "Invalid url: " + url, null, true); + } + + if (!service.Repositories.ContainsRepository (url)) { + ProgressDialog pdlg = new ProgressDialog (this); + pdlg.Show (); + pdlg.SetMessage (AddinManager.CurrentLocalizer.GetString ("Registering repository")); + + bool done = false; + AddinRepository rr = null; + Exception error = null; + + ThreadPool.QueueUserWorkItem (delegate { + try { + rr = service.Repositories.RegisterRepository (pdlg, url, true); + } catch (System.Exception ex) { + error = ex; + } finally { + done = true; + } + }); + + while (!done) { + if (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + else + Thread.Sleep (100); + } + + pdlg.Destroy (); + + if (pdlg.HadError) { + if (rr != null) + service.Repositories.RemoveRepository (rr.Url); + return; + } + + if (error != null) { + Services.ShowError (error, "The repository could not be registered", null, true); + return; + } + + AppendRepository (rr); + } + } + } finally { + dlg.Destroy (); + } + } + + protected void OnRemove (object sender, EventArgs e) + { + Gtk.ITreeModel foo; + Gtk.TreeIter iter; + if (!repoTree.Selection.GetSelected (out foo, out iter)) + return; + + string rep = (string) treeStore.GetValue (iter, 0); + service.Repositories.RemoveRepository (rep); + + treeStore.Remove (ref iter); + } + + void HandleRepoToggled (object o, ToggledArgs args) + { + Gtk.TreeIter iter; + if (!treeStore.GetIterFromString (out iter, args.Path)) + return; + + bool newVal = !(bool) treeStore.GetValue (iter, 2); + string rep = (string) treeStore.GetValue (iter, 0); + service.Repositories.SetRepositoryEnabled (rep, newVal); + + treeStore.SetValue (iter, 2, newVal); + } + + protected void OnSelect(object sender, EventArgs e) + { + btnRemove.Sensitive = repoTree.Selection.CountSelectedRows() > 0; + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/NewSiteDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/NewSiteDialog.cs new file mode 100644 index 0000000..719b546 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/NewSiteDialog.cs @@ -0,0 +1,124 @@ +// +// NewSiteDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + class NewSiteDialog : Dialog + { + //From UI File + [UI] Entry pathEntry; + [UI] Entry urlText; + [UI] RadioButton btnOnlineRep; + [UI] Button btnOk; + + public NewSiteDialog (Gtk.Window parent) + { + Build (); + TransientFor = parent; +// Services.PlaceDialog (this, parent); + pathEntry.Sensitive = false; + CheckValues (); + } + +// public override void Dispose () +// { +// base.Dispose (); +// Destroy (); +// } + + public string Url { + get { + if (btnOnlineRep.Active) + return urlText.Text; + else if (pathEntry.Text.Length > 0) + return "file://" + pathEntry.Text; + else + return string.Empty; + } + } + + void CheckValues () + { + btnOk.Sensitive = (Url != ""); + } + + public new bool Run () + { + ShowAll (); + return ((ResponseType) base.Run ()) == ResponseType.Ok; + } + + protected void OnClose (object sender, EventArgs args) + { + Destroy (); + } + + protected void OnOptionClicked (object sender, EventArgs e) + { + if (btnOnlineRep.Active) { + urlText.Sensitive = true; + pathEntry.Sensitive = false; + } else { + urlText.Sensitive = false; + pathEntry.Sensitive = true; + } + CheckValues (); + } + + protected virtual void OnButtonBrowseClicked(object sender, System.EventArgs e) + { + FileChooserDialog dlg = new FileChooserDialog ("Select Folder", this, FileChooserAction.SelectFolder); + try { + dlg.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel); + dlg.AddButton (Gtk.Stock.Open, Gtk.ResponseType.Ok); + + dlg.SetFilename (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); + if (dlg.Run () == (int) ResponseType.Ok) { + pathEntry.Text = dlg.Filename; + } + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnPathEntryChanged(object sender, System.EventArgs e) + { + CheckValues (); + } + + protected virtual void OnUrlTextChanged (object sender, System.EventArgs e) + { + CheckValues (); + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ProgressDialog.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ProgressDialog.cs new file mode 100644 index 0000000..34105e2 --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ProgressDialog.cs @@ -0,0 +1,120 @@ +// ProgressDialog.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + internal class ProgressDialog : Gtk.Dialog, IProgressStatus + { + //From UI file + [UI] Label labelMessage; + [UI] ProgressBar progressbar; + [UI] TextView textview; + [UI] Button buttonCancel; + + bool cancelled; + bool hadError; + + public ProgressDialog (Gtk.Window parent) + { + this.Build(); +// Services.PlaceDialog (this, parent); + } + + public bool IsCanceled { + get { + return cancelled; + } + } + + public int LogLevel { + get { + return 1; + } + } + + public bool HadError { + get { + return hadError; + } + } + + public void SetMessage (string msg) + { + Gtk.Application.Invoke (delegate { + labelMessage.Text = msg; + }); + } + + public void SetProgress (double progress) + { + Gtk.Application.Invoke (delegate { + progressbar.Fraction = progress; + }); + } + + public void Log (string msg) + { + Gtk.Application.Invoke (delegate { + Gtk.TextIter it = textview.Buffer.EndIter; + textview.Buffer.Insert (ref it, msg + "\n"); + }); + } + + public void ReportWarning (string message) + { + Log ("WARNING: " + message); + } + + public void ReportError (string message, Exception exception) + { + Log ("Error: " + message); + if (exception != null) + Log (exception.ToString ()); + Gtk.Application.Invoke (delegate { + Services.ShowError (exception, message, null, true); + }); + hadError = true; + } + + public void Cancel () + { + Gtk.Application.Invoke (delegate { + cancelled = true; + buttonCancel.Sensitive = false; + }); + } + + protected virtual void OnButtonCancelClicked (object sender, System.EventArgs e) + { + Cancel (); + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/SearchEntry.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/SearchEntry.cs new file mode 100644 index 0000000..f3a667d --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/SearchEntry.cs @@ -0,0 +1,130 @@ +// +// SearchEntry.cs +// +// Author: +// Aaron Bockover <abockover@novell.com> +// Gabriel Burt <gburt@novell.com> +// +// Copyright 2007-2010 Novell, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + [System.ComponentModel.ToolboxItem(true)] + class SearchEntry : EventBox + { + HBox box = new HBox (); + Gtk.Entry entry = new Gtk.Entry (); + HoverImageButton iconFind; + HoverImageButton iconClean; + const int notifyDelay = 50; + bool notifying; + + public SearchEntry () + { + entry.HasFrame = false; + box.PackStart (entry, true, true, 0); + iconFind = new HoverImageButton (IconSize.Menu, Gtk.Stock.Find); + box.PackStart (iconFind, false, false, 0); + iconClean = new HoverImageButton (IconSize.Menu, Gtk.Stock.Clear); + box.PackStart (iconClean, false, false, 0); + box.BorderWidth = 1; + + HeaderBox hbox = new HeaderBox (1,1,1,1); + hbox.Show (); + hbox.Add (box); + Add (hbox); + + ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + iconClean.ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + iconFind.ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + + iconClean.BorderWidth = 1; + iconClean.CanFocus = false; + iconFind.BorderWidth = 1; + iconFind.CanFocus = false; + + iconClean.Clicked += delegate { + entry.Text = string.Empty; + }; + + iconFind.Clicked += delegate { + FireSearch (); + }; + + entry.Activated += delegate { + FireSearch (); + }; + + ShowAll (); + UpdateIcon (); + + entry.Changed += delegate { + UpdateIcon (); + FireSearch (); + }; + } + + public event EventHandler TextChanged; + + public Gtk.Entry Entry { + get { + return this.entry; + } + set { + entry = value; + } + } + + public string Text { + get { return entry.Text; } + } + + void UpdateIcon () + { + if (entry.Text.Length > 0) { + iconFind.Hide (); + iconClean.Show (); + } + else { + iconFind.Show (); + iconClean.Hide (); + } + } + + void FireSearch () + { + if (!notifying) { + notifying = true; + GLib.Timeout.Add (notifyDelay, delegate { + notifying = false; + if (TextChanged != null) + TextChanged (this, EventArgs.Empty); + return false; + }); + } + } + } +} diff --git a/Mono.Addins.GuiGtk3/Mono.Addins.Gui/Services.cs b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/Services.cs new file mode 100644 index 0000000..24d4d9f --- /dev/null +++ b/Mono.Addins.GuiGtk3/Mono.Addins.Gui/Services.cs @@ -0,0 +1,152 @@ +// +// Services.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using Mono.Unix; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using System.Linq; +using System.Collections.Generic; + +namespace Mono.Addins.GuiGtk3 +{ + internal class Services + { + public static bool InApplicationNamespace (SetupService service, string id) + { + return service.ApplicationNamespace == null || id.StartsWith (service.ApplicationNamespace + "."); + } + + public static bool AskQuestion (string question) + { + MessageDialog md = new MessageDialog (null, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.YesNo, question); + try { + int response = md.Run (); + return ((ResponseType) response == ResponseType.Yes); + } finally { + md.Destroy (); + } + } + + public static void ShowError (Exception ex, string message, Window parent, bool modal) + { + ErrorDialog dlg = new ErrorDialog (parent); + + if (message == null) { + if (ex != null) + dlg.Message = string.Format (Catalog.GetString ("Exception occurred: {0}"), ex.Message); + else { + dlg.Message = "An unknown error occurred"; + dlg.AddDetails (Environment.StackTrace, false); + } + } else + dlg.Message = message; + + if (ex != null) { + dlg.AddDetails (string.Format (Catalog.GetString ("Exception occurred: {0}"), ex.Message) + "\n\n", true); + dlg.AddDetails (ex.ToString (), false); + } + + if (modal) { + dlg.Run (); + dlg.Destroy (); + } else + dlg.Show (); + } + + public struct MissingDepInfo + { + public string Addin; + public string Required; + public string Found; + } + + public static IEnumerable<MissingDepInfo> GetMissingDependencies (Addin addin) + { + IEnumerable<Addin> allAddins = AddinManager.Registry.GetAddins ().Union (AddinManager.Registry.GetAddinRoots ()); + foreach (var dep in addin.Description.MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep != null) { + if (!allAddins.Any (a => Addin.GetIdName (a.Id) == Addin.GetIdName (adep.FullAddinId) && a.SupportsVersion (adep.Version))) { + Addin found = allAddins.FirstOrDefault (a => Addin.GetIdName (a.Id) == Addin.GetIdName (adep.FullAddinId)); + yield return new MissingDepInfo () { Addin = Addin.GetIdName (adep.FullAddinId), Required = adep.Version, Found = found != null ? found.Version : null }; + } + } + } + } + + public static Gdk.Pixbuf AddIconOverlay (Gdk.Pixbuf target, Gdk.Pixbuf overlay) + { + Gdk.Pixbuf res = new Gdk.Pixbuf (target.Colorspace, target.HasAlpha, target.BitsPerSample, target.Width, target.Height); + res.Fill (0); + target.CopyArea (0, 0, target.Width, target.Height, res, 0, 0); + overlay.Composite (res, 0, 0, overlay.Width, overlay.Height, 0, 0, 1, 1, Gdk.InterpType.Bilinear, 255); + return res; + } + + public static Gdk.Pixbuf DesaturateIcon (Gdk.Pixbuf source) + { + Gdk.Pixbuf dest = new Gdk.Pixbuf (source.Colorspace, source.HasAlpha, source.BitsPerSample, source.Width, source.Height); + dest.Fill (0); + source.SaturateAndPixelate (dest, 0, false); + return dest; + } + + public static Gdk.Pixbuf FadeIcon (Gdk.Pixbuf source) + { + Gdk.Pixbuf result = source.Copy (); + result.Fill (0); + result = result.AddAlpha (true, 0, 0, 0); + source.Composite (result, 0, 0, source.Width, source.Height, 0, 0, 1, 1, Gdk.InterpType.Bilinear, 128); + return result; + } + +// /// <summary> +// /// Positions a dialog relative to its parent on platforms where default placement is known to be poor. +// /// </summary> +// public static void PlaceDialog (Window child, Window parent) +// { +// CenterWindow (child, parent); +// } +// +// /// <summary>Centers a window relative to its parent.</summary> +// static void CenterWindow (Window child, Window parent) +// { +// child.Child.Show (); +// int w, h, winw, winh, x, y, winx, winy; +// child.GetSize (out w, out h); +// parent.GetSize (out winw, out winh); +// parent.GetPosition (out winx, out winy); +// x = System.Math.Max (0, (winw - w) /2) + winx; +// y = System.Math.Max (0, (winh - h) /2) + winy; +// child.Move (x, y); +// } + } +} |