예제 #1
0
        public CommitInfo()
        {
            InitializeComponent();
            Translate();
            GitUICommandsSourceSet += (a, uiCommandsSource) =>
            {
                _sortedRefs = null;
            };

            _commitDataManager = new CommitDataManager(() => Module);

            IHeaderRenderStyleProvider headerRenderer;
            IHeaderLabelFormatter      labelFormatter;

            if (EnvUtils.IsMonoRuntime())
            {
                labelFormatter = new MonospacedHeaderLabelFormatter();
                headerRenderer = new MonospacedHeaderRenderStyleProvider();
            }
            else
            {
                labelFormatter = new TabbedHeaderLabelFormatter();
                headerRenderer = new TabbedHeaderRenderStyleProvider();
            }

            _commitDataHeaderRenderer = new CommitDataHeaderRenderer(labelFormatter, _dateFormatter, headerRenderer, _linkFactory);
            _commitDataBodyRenderer   = new CommitDataBodyRenderer(() => Module, _linkFactory);

            RevisionInfo.Font = AppSettings.Font;
            using (Graphics g = CreateGraphics())
            {
                _RevisionHeader.Font = _commitDataHeaderRenderer.GetFont(g);
            }
            _RevisionHeader.SelectionTabs = _commitDataHeaderRenderer.GetTabStops().ToArray();
        }
예제 #2
0
        private void ApplyLayout()
        {
            var heightRowCommit = 0f;
            var heightRowTag    = 0f;

            if (EnvUtils.IsMonoRuntime())
            {
                // TODO: RussKie to provide
            }
            else
            {
                if (txtTagGpgInfo.Visible)
                {
                    heightRowCommit = 50f;
                    heightRowTag    = 50f;
                }
                else
                {
                    heightRowCommit = 100f;
                }

                tableLayoutPanel1.RowStyles[0].SizeType = SizeType.Percent;
                tableLayoutPanel1.RowStyles[1].SizeType = SizeType.Percent;
            }
            tableLayoutPanel1.RowStyles[0].Height = heightRowCommit;
            tableLayoutPanel1.RowStyles[1].Height = heightRowTag;
        }
예제 #3
0
        private void RecalculateSizeConstraints()
        {
            if (!EnvUtils.IsMonoRuntime())
            {
                MinimumSize = MaximumSize = new Size(0, 0);
            }

            remoteOptionsPanel.Visible            = Remotebranch.Checked;
            tableLayoutPanel1.RowStyles[2].Height = Remotebranch.Checked ? _controls[remoteOptionsPanel] : 0;
            tableLayoutPanel1.Height = _controls.Select(c => c.Key.Visible ? c.Value : 0).Sum() + tableLayoutPanel1.Padding.Top + tableLayoutPanel1.Padding.Bottom;
            Height = tableLayoutPanel1.Height + tableLayoutPanel1.Margin.Top + tableLayoutPanel1.Margin.Bottom + 40;

            if (EnvUtils.IsMonoRuntime())
            {
                var minWidth = tableLayoutPanel1.PreferredSize.Width;
                if (Width < minWidth)
                {
                    Width = minWidth;
                }
            }
            else
            {
                MinimumSize = new Size(tableLayoutPanel1.PreferredSize.Width + 40, Height);
                MaximumSize = new Size(Screen.PrimaryScreen.Bounds.Width, Height);
            }
        }
예제 #4
0
        private int GetRevisionHeaderHeight()
        {
            if (EnvUtils.IsMonoRuntime())
            {
                return((int)(_RevisionHeader.Lines.Length * (0.8 + _RevisionHeader.Font.GetHeight())));
            }

            return(_RevisionHeader.GetPreferredSize(new System.Drawing.Size(0, 0)).Height);
        }
예제 #5
0
        private int GetRevisionHeaderHeight()
        {
            if (EnvUtils.IsMonoRuntime())
            {
                return((int)(_RevisionHeader.Lines.Length * (0.8 + _RevisionHeader.Font.GetHeight())));
            }

            return(_headerResize.Height);
        }
예제 #6
0
        private void ApplyLayout()
        {
            var controls1 = new Control[]
            {
                setBranchPanel,
                horLine,
                remoteOptionsPanel,
                localChangesPanel
            };

            if (EnvUtils.IsMonoRuntime())
            {
                var margin = flowLayoutPanel2.Controls[0].Margin;
                flowLayoutPanel2.Height  = flowLayoutPanel2.Controls.OfType <Control>().Max(c => c.Height) + margin.Top + margin.Bottom;
                localChangesGB.Height    = flowLayoutPanel2.Height * 2 + localChangesGB.Padding.Top + localChangesGB.Padding.Bottom;
                localChangesPanel.Height = localChangesGB.Height + localChangesGB.Margin.Top + localChangesGB.Margin.Bottom;

                Ok.AutoSize = false;
                var buttonWidth = TextRenderer.MeasureText(Ok.Text, AppSettings.Font).Width + 10;

                localChangesPanel.ColumnStyles[1].Width = buttonWidth + Ok.Margin.Left + Ok.Margin.Right;
                Ok.Width = buttonWidth;

                localChangesGB.Width   = localChangesPanel.Width - (int)localChangesPanel.ColumnStyles[1].Width;
                flowLayoutPanel2.Width = localChangesGB.Width - 2 * localChangesGB.Padding.Left - 2 * localChangesGB.Padding.Right;
            }
            else
            {
                Ok.Anchor = AnchorStyles.Right;

                flowLayoutPanel2.AutoSize = true;
                flowLayoutPanel2.Dock     = DockStyle.Fill;
                localChangesGB.AutoSize   = true;
                localChangesGB.Height     = flowLayoutPanel2.Height * 2 + localChangesGB.Padding.Top + localChangesGB.Padding.Bottom;
                localChangesPanel.ColumnStyles[1].Width = Ok.Width + 10;
                var height = localChangesGB.Height + localChangesGB.Margin.Top + localChangesGB.Margin.Bottom;
                localChangesPanel.RowStyles[0].Height = height;
                localChangesPanel.Height = height;

                Width = tableLayoutPanel1.PreferredSize.Width + 40;
            }

            for (var i = 0; i < controls1.Length; i++)
            {
                var margin = controls1[i].Margin;
                var height = controls1[i].Height + margin.Top + margin.Bottom;
                _controls.Add(controls1[i], height);

                tableLayoutPanel1.RowStyles[i].Height   = height;
                tableLayoutPanel1.RowStyles[i].SizeType = SizeType.Absolute;
            }

            tableLayoutPanel1.RowStyles[2].Height = Remotebranch.Checked ? _controls[remoteOptionsPanel] : 0;
            tableLayoutPanel1.Height = tableLayoutPanel1.Height - _controls[remoteOptionsPanel];
        }
예제 #7
0
        private void UpdateSelectedFileViewers()
        {
            var selectedRows = FileChanges.GetSelectedRevisions();

            if (selectedRows.Count == 0)
            {
                return;
            }

            GitRevision revision = selectedRows[0];
            var         children = FileChanges.GetRevisionChildren(revision.Guid);

            var fileName = revision.Name;

            if (string.IsNullOrEmpty(fileName))
            {
                fileName = FileName;
            }

            SetTitle(fileName);

            if (tabControl1.SelectedTab == BlameTab)
            {
                Blame.LoadBlame(revision, children, fileName, FileChanges, BlameTab, Diff.Encoding);
            }
            else if (tabControl1.SelectedTab == ViewTab)
            {
                var scrollpos = View.ScrollPos;

                View.Encoding = Diff.Encoding;
                View.ViewGitItemRevision(fileName, revision.Guid);
                View.ScrollPos = scrollpos;
            }
            else if (tabControl1.SelectedTab == DiffTab)
            {
                GitItemStatus file = new GitItemStatus();
                file.IsTracked   = true;
                file.Name        = fileName;
                file.IsSubmodule = GitModule.IsValidGitWorkingDir(Path.Combine(Module.WorkingDir, fileName));
                Diff.ViewChanges(FileChanges.GetSelectedRevisions(), file, "You need to select at least one revision to view diff.");
            }

            if (!EnvUtils.IsMonoRuntime())
            {
                if (_buildReportTabPageExtension == null)
                {
                    _buildReportTabPageExtension = new BuildReportTabPageExtension(tabControl1, _buildReportTabCaption.Text);
                }

                _buildReportTabPageExtension.FillBuildReport(selectedRows.Count == 1 ? revision : null);
            }
        }
예제 #8
0
        private void copyCommitInfoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var commitInfo = string.Empty;

            if (EnvUtils.IsMonoRuntime())
            {
                commitInfo = $"{_RevisionHeader.Text}{Environment.NewLine}{RevisionInfo.Text}";
            }
            else
            {
                commitInfo = $"{_RevisionHeader.GetPlaintText()}{Environment.NewLine}{RevisionInfo.GetPlaintText()}";
            }
            Clipboard.SetText(commitInfo);
        }
        /// <summary>
        /// Retrieves the icon associated with the given file type.
        /// The retrieved icons are cached by extensions.
        /// </summary>
        /// <param name="workingDirectory">The git repository working directory.</param>
        /// <param name="relativeFilePath">The relative path to the file.</param>
        /// <returns>The icon associaited with the given file type or <see langword="null"/>.</returns>
        /// <remarks>
        /// The method takes two parameters to performance reasons - the full path is esstablished 
        /// only if the file type has not been processed already and the extensions is not cached.
        /// </remarks>
        public Icon Get(string workingDirectory, string relativeFilePath)
        {
            if (EnvUtils.IsMonoRuntime())
            {
                // Mono does not support icon extraction
                // https://github.com/mono/mono/blob/master/mcs/class/System.Drawing/System.Drawing/Icon.cs#L314
                return null;
            }

            var extension = Path.GetExtension(relativeFilePath);
            if (string.IsNullOrWhiteSpace(extension))
            {
                return null;
            }

            var icon = LoadedFileIcons.GetOrAdd(extension, ext =>
            {
                string tempFile = null;
                try
                {
                    // if the file doesn't exist - create a blank temp file with the required extension
                    // so we can call Icon.ExtractAssociatedIcon on it
                    // this may have a slight overhead, however an alternative would be extracting
                    // extensions from the registry and using p/invokes and WinAPI, which have
                    // significantly higher maintenance overhead.

                    var fullPath = Path.Combine(workingDirectory, relativeFilePath);
                    if (!_fileSystem.File.Exists(fullPath))
                    {
                        tempFile = CreateTempFile(Path.GetFileName(fullPath));
                        fullPath = tempFile;
                    }

                    return Icon.ExtractAssociatedIcon(fullPath);
                }
                catch
                {
                    return null;
                }
                finally
                {
                    if (!string.IsNullOrEmpty(tempFile))
                    {
                        DeleteFile(tempFile);
                    }
                }
            });
            return icon;
        }
예제 #10
0
        private void ApplyMonoLayout()
        {
            if (!EnvUtils.IsMonoRuntime())
            {
                return;
            }

            tableLayoutPanel2.RowStyles[0].SizeType = SizeType.Absolute;
            tableLayoutPanel2.RowStyles[0].Height   = _rowBranchComboHeight + 4;

            // Mono doesn't like referencing dimensions from controls, use temp variables
            var col1Width = helpImageDisplayUserControl1.Width + 6;
            var col2Width = _groupBoxWidth + 26;

            tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.Absolute;
            tableLayoutPanel1.ColumnStyles[0].Width    = col1Width;
            tableLayoutPanel1.ColumnStyles[1].SizeType = SizeType.Absolute;
            tableLayoutPanel1.ColumnStyles[1].Width    = col2Width;
        }
예제 #11
0
        public FormCreateBranch(GitUICommands aCommands, GitRevision revision)
            : base(aCommands)
        {
            _branchNameNormaliser    = new GitBranchNameNormaliser();
            CheckoutAfterCreation    = true;
            UserAbleToChangeRevision = true;
            CouldBeOrphan            = true;

            InitializeComponent();
            Translate();

            // Mono is having troubles dynamically sizing the groupbox
            groupBox1.AutoSize = !EnvUtils.IsMonoRuntime();

            commitPickerSmallControl1.UICommandsSource = this;
            if (IsUICommandsInitialized)
            {
                commitPickerSmallControl1.SetSelectedCommitHash(revision == null ? Module.GetCurrentCheckout() : revision.Guid);
            }
        }
예제 #12
0
        public CommitInfo()
        {
            InitializeComponent();
            Translate();
            GitUICommandsSourceSet += (a, uiCommandsSource) =>
            {
                _sortedRefs = null;
            };

            _commitDataManager = new CommitDataManager(() => Module);

            IHeaderRenderStyleProvider headerRenderer;
            IHeaderLabelFormatter      labelFormatter;

            if (EnvUtils.IsMonoRuntime())
            {
                labelFormatter = new MonospacedHeaderLabelFormatter();
                headerRenderer = new MonospacedHeaderRenderStyleProvider();
            }
            else
            {
                labelFormatter = new TabbedHeaderLabelFormatter();
                headerRenderer = new TabbedHeaderRenderStyleProvider();
            }

            _commitDataHeaderRenderer         = new CommitDataHeaderRenderer(labelFormatter, _dateFormatter, headerRenderer, _linkFactory);
            _commitDataBodyRenderer           = new CommitDataBodyRenderer(() => Module, _linkFactory);
            _externalLinksLoader              = new ExternalLinksLoader();
            _effectiveLinkDefinitionsProvider = new ConfiguredLinkDefinitionsProvider(_externalLinksLoader);
            _gitRevisionExternalLinksParser   = new GitRevisionExternalLinksParser(_effectiveLinkDefinitionsProvider);

            RevisionInfo.Font = AppSettings.Font;
            using (Graphics g = CreateGraphics())
            {
                _RevisionHeader.Font = _commitDataHeaderRenderer.GetFont(g);
            }
            _RevisionHeader.SelectionTabs = _commitDataHeaderRenderer.GetTabStops().ToArray();

            Hotkeys = HotkeySettingsManager.LoadHotkeys(FormBrowse.HotkeySettingsName);
            addNoteToolStripMenuItem.ShortcutKeyDisplayString = GetShortcutKeys((int)FormBrowse.Commands.AddNotes).ToShortcutKeyDisplayString();
        }
예제 #13
0
        private void ApplyLayout()
        {
            var heightRowCommit = 0f;
            var heightRowTag    = 0f;

            if (EnvUtils.IsMonoRuntime())
            {
                if (txtTagGpgInfo.Visible)
                {
                    heightRowCommit  =
                        heightRowTag = (float)tableLayoutPanel1.Height / 2;
                }
                else
                {
                    heightRowCommit = tableLayoutPanel1.Height;
                }

                tableLayoutPanel1.RowStyles[0].SizeType = SizeType.Absolute;
                tableLayoutPanel1.RowStyles[1].SizeType = SizeType.Absolute;
            }
            else
            {
                if (txtTagGpgInfo.Visible)
                {
                    heightRowCommit = 50f;
                    heightRowTag    = 50f;
                }
                else
                {
                    heightRowCommit = 100f;
                }

                tableLayoutPanel1.RowStyles[0].SizeType = SizeType.Percent;
                tableLayoutPanel1.RowStyles[1].SizeType = SizeType.Percent;
            }
            tableLayoutPanel1.RowStyles[0].Height = heightRowCommit;
            tableLayoutPanel1.RowStyles[1].Height = heightRowTag;
        }
예제 #14
0
        private static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (!EnvUtils.IsMonoRuntime())
            {
                try
                {
                    NBug.Settings.UIMode = NBug.Enums.UIMode.Full;

                    // Uncomment the following after testing to see that NBug is working as configured
                    NBug.Settings.ReleaseMode = true;
                    NBug.Settings.ExitApplicationImmediately = false;
                    NBug.Settings.WriteLogToDisk             = false;
                    NBug.Settings.MaxQueuedReports           = 10;
                    NBug.Settings.StopReportingAfter         = 90;
                    NBug.Settings.SleepBeforeSend            = 30;
                    NBug.Settings.StoragePath = NBug.Enums.StoragePath.WindowsTemp;

                    AppDomain.CurrentDomain.UnhandledException += NBug.Handler.UnhandledException;
                    Application.ThreadException += NBug.Handler.ThreadException;
                }
                catch (TypeInitializationException tie)
                {
                    // is this exception caused by the configuration?
                    if (tie.InnerException != null &&
                        tie.InnerException.GetType()
                        .IsSubclassOf(typeof(System.Configuration.ConfigurationException)))
                    {
                        HandleConfigurationException((System.Configuration.ConfigurationException)tie.InnerException);
                    }
                }
            }

            string[] args = Environment.GetCommandLineArgs();
            FormSplash.ShowSplash();
            //Store here SynchronizationContext.Current, because later sometimes it can be null
            //see http://stackoverflow.com/questions/11621372/synchronizationcontext-current-is-null-in-continuation-on-the-main-ui-thread
            GitUIExtensions.UISynchronizationContext     = SynchronizationContext.Current;
            AsyncLoader.DefaultContinuationTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            Application.DoEvents();

            AppSettings.LoadSettings();
            if (EnvUtils.RunningOnWindows())
            {
                WebBrowserEmulationMode.SetBrowserFeatureControl();

                //Quick HOME check:
                FormSplash.SetAction("Checking home path...");
                Application.DoEvents();

                FormFixHome.CheckHomePath();
            }
            //Register plugins
            FormSplash.SetAction("Loading plugins...");
            Application.DoEvents();

            if (string.IsNullOrEmpty(AppSettings.Translation))
            {
                using (var formChoose = new FormChooseTranslation())
                {
                    formChoose.ShowDialog();
                }
            }

            try
            {
                if (!(args.Length >= 2 && args[1].Equals("uninstall")) &&
                    (AppSettings.CheckSettings ||
                     string.IsNullOrEmpty(AppSettings.GitCommandValue) ||
                     !File.Exists(AppSettings.GitCommandValue)))
                {
                    FormSplash.SetAction("Checking settings...");
                    Application.DoEvents();

                    GitUICommands     uiCommands         = new GitUICommands(string.Empty);
                    var               commonLogic        = new CommonLogic(uiCommands.Module);
                    var               checkSettingsLogic = new CheckSettingsLogic(commonLogic);
                    ISettingsPageHost fakePageHost       = new SettingsPageHostMock(checkSettingsLogic);
                    using (var checklistSettingsPage = SettingsPageBase.Create <ChecklistSettingsPage>(fakePageHost))
                    {
                        if (!checklistSettingsPage.CheckSettings())
                        {
                            if (!checkSettingsLogic.AutoSolveAllSettings())
                            {
                                uiCommands.StartSettingsDialog();
                            }
                        }
                    }
                }
            }
            catch
            {
                // TODO: remove catch-all
            }

            FormSplash.HideSplash();

            if (EnvUtils.RunningOnWindows())
            {
                MouseWheelRedirector.Active = true;
            }

            GitUICommands uCommands = new GitUICommands(GetWorkingDir(args));

            if (args.Length <= 1)
            {
                uCommands.StartBrowseDialog();
            }
            else  // if we are here args.Length > 1
            {
                uCommands.RunCommand(args);
            }

            AppSettings.SaveSettings();
        }
예제 #15
0
        public void FillBuildReport(GitRevision revision)
        {
            if (EnvUtils.IsMonoRuntime())
            {
                return;
            }

            if (selectedGitRevision != null)
            {
                selectedGitRevision.PropertyChanged -= RevisionPropertyChanged;
            }
            selectedGitRevision = revision;
            if (selectedGitRevision != null)
            {
                selectedGitRevision.PropertyChanged += RevisionPropertyChanged;
            }

            var buildInfoIsAvailable =
                !(revision == null || revision.BuildStatus == null || string.IsNullOrEmpty(revision.BuildStatus.Url));

            tabControl.SuspendLayout();

            try
            {
                if (buildInfoIsAvailable)
                {
                    if (buildReportTabPage == null)
                    {
                        CreateBuildReportTabPage(tabControl);
                    }

                    var isFavIconMissing = buildReportTabPage.ImageIndex < 0;

                    if (isFavIconMissing || tabControl.SelectedTab == buildReportTabPage)
                    {
                        try
                        {
                            if (revision.BuildStatus.ShowInBuildReportTab)
                            {
                                url = null;
                                buildReportWebBrowser.Navigate(revision.BuildStatus.Url);
                            }
                            else
                            {
                                url = revision.BuildStatus.Url;
                                buildReportWebBrowser.Navigate("about:blank");
                            }

                            if (isFavIconMissing)
                            {
                                buildReportWebBrowser.Navigated += BuildReportWebBrowserOnNavigated;
                            }
                        }
                        catch
                        {
                            //No propagation to the user if the report fails
                        }
                    }

                    if (!tabControl.Controls.Contains(buildReportTabPage))
                    {
                        tabControl.Controls.Add(buildReportTabPage);
                    }
                }
                else
                {
                    if (buildReportTabPage != null && tabControl.Controls.Contains(buildReportTabPage))
                    {
                        buildReportWebBrowser.Stop();
                        buildReportWebBrowser.Document.Write(string.Empty);
                        tabControl.Controls.Remove(buildReportTabPage);
                    }
                }
            }
            finally
            {
                tabControl.ResumeLayout();
            }
        }
예제 #16
0
        private static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (!EnvUtils.IsMonoRuntime())
            {
                NBug.Settings.UIMode = NBug.Enums.UIMode.Full;

                // Uncomment the following after testing to see that NBug is working as configured
                NBug.Settings.ReleaseMode = true;
                NBug.Settings.ExitApplicationImmediately = false;
                NBug.Settings.WriteLogToDisk             = true;
                NBug.Settings.MaxQueuedReports           = 10;
                NBug.Settings.StopReportingAfter         = 90;
                NBug.Settings.SleepBeforeSend            = 30;
                NBug.Settings.StoragePath = "WindowsTemp";

                AppDomain.CurrentDomain.UnhandledException += NBug.Handler.UnhandledException;
                Application.ThreadException += NBug.Handler.ThreadException;
            }

            string[] args = Environment.GetCommandLineArgs();
            FormSplash.ShowSplash();
            //Store here SynchronizationContext.Current, because later sometimes it can be null
            //see http://stackoverflow.com/questions/11621372/synchronizationcontext-current-is-null-in-continuation-on-the-main-ui-thread
            GitUIExtensions.UISynchronizationContext = SynchronizationContext.Current;
            Application.DoEvents();

            Settings.LoadSettings();
            if (EnvUtils.RunningOnWindows())
            {
                //Quick HOME check:
                FormSplash.SetAction("Checking home path...");
                Application.DoEvents();

                FormFixHome.CheckHomePath();
            }
            //Register plugins
            FormSplash.SetAction("Loading plugins...");
            Application.DoEvents();

            if (string.IsNullOrEmpty(Settings.Translation))
            {
                using (var formChoose = new FormChooseTranslation())
                {
                    formChoose.ShowDialog();
                }
            }

            try
            {
                if (Application.UserAppDataRegistry == null ||
                    Settings.GetBool("checksettings", true) ||
                    string.IsNullOrEmpty(Settings.GitCommand))
                {
                    FormSplash.SetAction("Checking settings...");
                    Application.DoEvents();

                    GitUICommands uiCommands         = new GitUICommands(string.Empty);
                    var           commonLogic        = new CommonLogic(uiCommands.Module);
                    var           checkSettingsLogic = new CheckSettingsLogic(commonLogic, uiCommands.Module);
                    using (var checklistSettingsPage = new ChecklistSettingsPage(commonLogic, checkSettingsLogic, uiCommands.Module, null))
                    {
                        if (!checklistSettingsPage.CheckSettings())
                        {
                            checkSettingsLogic.AutoSolveAllSettings();
                            uiCommands.StartSettingsDialog();
                        }
                    }
                }
            }
            catch
            {
                // TODO: remove catch-all
            }

            FormSplash.HideSplash();

            if (EnvUtils.RunningOnWindows())
            {
                MouseWheelRedirector.Active = true;
            }

            GitUICommands uCommands = new GitUICommands(GetWorkingDir(args));

            if (args.Length <= 1)
            {
                uCommands.StartBrowseDialog();
            }
            else  // if we are here args.Length > 1
            {
                uCommands.RunCommand(args);
            }

            Settings.SaveSettings();
        }
 protected override void SettingsToPage()
 {
     chkShowRevisionInfoNextToRevisionGrid.Checked = AppSettings.ShowRevisionInfoNextToRevisionGrid;
     chkShowRevisionInfoNextToRevisionGrid.Visible = !EnvUtils.IsMonoRuntime();
     base.SettingsToPage();
 }