// // GnomePlatform.cs // // Author: // Geoff Norton // // 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 Gnome; using MonoDevelop.Ide.Desktop; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics; using System.IO; using MonoDevelop.Core.Execution; using MonoDevelop.Core; namespace MonoDevelop.Platform { public class GnomePlatform : PlatformService { static bool useGio; Gnome.ThumbnailFactory thumbnailFactory = new Gnome.ThumbnailFactory (Gnome.ThumbnailSize.Normal); static GnomePlatform () { try { Gio.GetDefaultForType ("text/plain"); useGio = true; } catch (Exception ex) { Console.WriteLine (ex); } //apparently Gnome.Icon needs GnomeVFS initialized even when we're using GIO. Gnome.Vfs.Vfs.Initialize (); } DesktopApplication GetGnomeVfsDefaultApplication (string mimeType) { var app = Gnome.Vfs.Mime.GetDefaultApplication (mimeType); if (app != null) return (DesktopApplication) Marshal.PtrToStructure (app.Handle, typeof(DesktopApplication)); else return null; } IEnumerable GetGnomeVfsApplications (string mimeType) { var def = GetGnomeVfsDefaultApplication (mimeType); var list = new List (); var apps = Gnome.Vfs.Mime.GetAllApplications (mimeType); foreach (var app in apps) { var dap = (GnomeVfsApp) Marshal.PtrToStructure (app.Handle, typeof(GnomeVfsApp)); if (!string.IsNullOrEmpty (dap.Command) && !string.IsNullOrEmpty (dap.DisplayName) && !dap.Command.Contains ("monodevelop ")) { var isDefault = def != null && def.Id == dap.Command; list.Add (new GnomeDesktopApplication (dap.Command, dap.DisplayName, isDefault)); } } return list; } public override IEnumerable GetApplications (string filename) { var mimeType = GetMimeTypeForUri (filename); return GetApplicationsForMimeType (mimeType); } IEnumerable GetApplicationsForMimeType (string mimeType) { if (useGio) return Gio.GetAllForType (mimeType); else return GetGnomeVfsApplications (mimeType); } struct GnomeVfsApp { public string Id, DisplayName, Command; } protected override string OnGetMimeTypeDescription (string mt) { if (useGio) return Gio.GetMimeTypeDescription (mt); else return Gnome.Vfs.Mime.GetDescription (mt); } protected override string OnGetMimeTypeForUri (string uri) { if (uri == null) return null; if (useGio) { string mt = Gio.GetMimeTypeForUri (uri); if (mt != null) return mt; } return Gnome.Vfs.MimeType.GetMimeTypeForUri (ConvertFileNameToVFS (uri)); } protected override bool OnGetMimeTypeIsText (string mimeType) { // If gedit can open the file, this editor also can do it foreach (DesktopApplication app in GetApplicationsForMimeType (mimeType)) if (app.Id == "gedit") return true; return base.OnGetMimeTypeIsText (mimeType); } public override void ShowUrl (string url) { Gnome.Url.Show (url); } public override string DefaultMonospaceFont { get { try { return (string) (new GConf.Client ().Get ("/desktop/gnome/interface/monospace_font_name")); } catch (Exception) { return "Monospace 11"; } } } public override string Name { get { return "Gnome"; } } protected override string OnGetIconIdForFile (string filename) { if (filename == "Documentation") { return "gnome-fs-regular"; } if (System.IO.Directory.Exists (filename)) { return "gnome-fs-directory"; } else if (System.IO.File.Exists (filename)) { filename = EscapeFileName (filename); if (filename == null) return "gnome-fs-regular"; string icon = null; Gnome.IconLookupResultFlags result; try { icon = Gnome.Icon.LookupSync (IconTheme.Default, thumbnailFactory, filename, null, Gnome.IconLookupFlags.None, out result); } catch {} if (icon != null && icon.Length > 0) return icon; } return "gnome-fs-regular"; } protected override Xwt.Drawing.Image OnGetIconForFile (string filename) { string icon = OnGetIconIdForFile (filename); return GetIconForType (icon); } string EscapeFileName (string filename) { foreach (char c in filename) { // FIXME: This is a temporary workaround. In some systems, files with // accented characters make LookupSync crash. Still trying to find out why. if ((int)c < 32 || (int)c > 127) return null; } return ConvertFileNameToVFS (filename); } static string ConvertFileNameToVFS (string fileName) { string result = fileName; result = result.Replace ("%", "%25"); result = result.Replace ("#", "%23"); result = result.Replace ("?", "%3F"); return result; } delegate string TerminalRunnerHandler (string command, string args, string dir, string title, bool pause); delegate string TerminalOpenFolderRunnerHandler (string dir); string terminal_command; bool terminal_probed; TerminalRunnerHandler runner; TerminalOpenFolderRunnerHandler openDirectoryRunner; public override IProcessAsyncOperation StartConsoleProcess (string command, string arguments, string workingDirectory, IDictionary environmentVariables, string title, bool pauseWhenFinished) { ProbeTerminal (); string exec = runner (command, arguments, workingDirectory, title, pauseWhenFinished); var psi = new ProcessStartInfo (terminal_command, exec) { CreateNoWindow = true, UseShellExecute = false, }; foreach (var env in environmentVariables) psi.EnvironmentVariables [env.Key] = env.Value; ProcessWrapper proc = new ProcessWrapper (); proc.StartInfo = psi; proc.Start (); return proc; } #region Terminal runner implementations private static string GnomeTerminalRunner (string command, string args, string dir, string title, bool pause) { string extra_commands = pause ? BashPause.Replace ("'", "\\\"") : String.Empty; return String.Format (@" --disable-factory --title ""{4}"" -e ""bash -c 'cd {3} ; {0} {1} ; {2}'""", command, EscapeArgs (args), extra_commands, EscapeDir (dir), title); } private static string XtermRunner (string command, string args, string dir, string title, bool pause) { string extra_commands = pause ? BashPause : String.Empty; return String.Format (@" -title ""{4}"" -e bash -c ""cd {3} ; '{0}' {1} ; {2}""", command, EscapeArgs (args), extra_commands, EscapeDir (dir), title); } private static string KdeTerminalRunner (string command, string args, string dir, string title, bool pause) { string extra_commands = pause ? BashPause.Replace ("'", "\"") : String.Empty; return String.Format (@" --nofork --caption ""{4}"" --workdir=""{3}"" -e ""bash"" -c '{0} {1} ; {2}'", command, args, extra_commands, EscapeDir (dir), title); } private static string GnomeTerminalOpenFolderRunner (string dir) { return string.Format(@" --working-directory=""{0}""", EscapeDir(dir)); } private static string XtermOpenFolderRunner (string dir) { return string.Format(@" -e bash -c ""cd {0}""", EscapeDir(dir)); } private static string KdeTerminalOpenFolderRunner (string dir) { return string.Format(@" --nofork --workdir=""{0}""", EscapeDir(dir)); } private static string EscapeArgs (string args) { return args.Replace ("\\", "\\\\").Replace ("\"", "\\\""); } private static string EscapeDir (string dir) { return dir.Replace (" ", "\\ ").Replace (";", "\\;"); } private static string BashPause { get { return @"echo; read -p 'Press any key to continue...' -n1;"; } } #endregion #region Probing for preferred terminal private void ProbeTerminal () { if (terminal_probed) { return; } terminal_probed = true; string fallback_terminal = PropertyService.Get ("MonoDevelop.Shell", "xterm"); string preferred_terminal; TerminalRunnerHandler preferred_runner = null; TerminalRunnerHandler fallback_runner = XtermRunner; TerminalOpenFolderRunnerHandler preferedOpenFolderRunner = null; TerminalOpenFolderRunnerHandler fallbackOpenFolderRunner = XtermOpenFolderRunner; if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("GNOME_DESKTOP_SESSION_ID"))) { preferred_terminal = "gnome-terminal"; preferred_runner = GnomeTerminalRunner; preferedOpenFolderRunner = GnomeTerminalOpenFolderRunner; } else if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MATE_DESKTOP_SESSION_ID"))) { preferred_terminal = "mate-terminal"; preferred_runner = GnomeTerminalRunner; preferedOpenFolderRunner = GnomeTerminalOpenFolderRunner; } else if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("KDE_SESSION_VERSION"))) { preferred_terminal = "konsole"; preferred_runner = KdeTerminalRunner; preferedOpenFolderRunner = KdeTerminalOpenFolderRunner; } else { preferred_terminal = fallback_terminal; preferred_runner = fallback_runner; preferedOpenFolderRunner = fallbackOpenFolderRunner; } terminal_command = FindExec (preferred_terminal); if (terminal_command != null) { runner = preferred_runner; openDirectoryRunner = preferedOpenFolderRunner; return; } terminal_command = FindExec (fallback_terminal); runner = fallback_runner; openDirectoryRunner = fallbackOpenFolderRunner; } private string FindExec (string command) { foreach (string path in GetExecPaths ()) { string full_path = Path.Combine (path, command); try { FileInfo info = new FileInfo (full_path); // FIXME: System.IO is super lame, should check for 0755 if (info.Exists) { return full_path; } } catch { } } return null; } private string [] GetExecPaths () { string path = Environment.GetEnvironmentVariable ("PATH"); if (String.IsNullOrEmpty (path)) { return new string [] { "/bin", "/usr/bin", "/usr/local/bin" }; } // this is super lame, should handle quoting/escaping return path.Split (':'); } #endregion public override bool CanOpenTerminal { get { return true; } } public override void OpenTerminal (FilePath directory, IDictionary environmentVariables, string title) { ProbeTerminal (); Runtime.ProcessService.StartProcess (terminal_command, openDirectoryRunner(directory), directory, null); } } class GnomeDesktopApplication : DesktopApplication { public GnomeDesktopApplication (string command, string displayName, bool isDefault) : base (command, displayName, isDefault) { } string Command { get { return Id; } } public override void Launch (params string[] files) { // TODO: implement all other cases if (Command.IndexOf ("%f") != -1) { foreach (string s in files) { string cmd = Command.Replace ("%f", "\"" + s + "\""); Process.Start (cmd); } } else if (Command.IndexOf ("%F") != -1) { string[] fs = new string [files.Length]; for (int n=0; n