diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2138683
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Build results and NuGet Packages
+Build/
+Packages/
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..9dc9bd6
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Version 1.0.14306.1755
+
+#### 2014/11/02
+
+ - First version released to the public.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1032435
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ricardo Amaral
+
+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.
+
diff --git a/Libraries/EasySettings.dll b/Libraries/EasySettings.dll
new file mode 100644
index 0000000..e498d69
Binary files /dev/null and b/Libraries/EasySettings.dll differ
diff --git a/Libraries/VistaMenu.dll b/Libraries/VistaMenu.dll
new file mode 100644
index 0000000..f4a4f1f
Binary files /dev/null and b/Libraries/VistaMenu.dll differ
diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 0000000..1059eec
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..77fc2f7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,42 @@
+# SlackUI
+
+Slack for Windows - Unofficial, not affiliated with Slack Technologies.
+
+![SlackUI User Interface](http://rfgamaral.github.io/images/screenshots/slackui.jpg)
+
+SlackUI fills the missing gap on your Windows system with a dedicated Slack client bringing better integration with the underlying system. The main feature being the ability to lessen all distractions by minimizing the application to the notification area, while remaining easily accessible.
+
+## Features
+
+ - Minimalist user interface with native Windows look and feel.
+ - Powered by the Chromium Embedded Framework (CEF).
+ - Hide the application by minimizing to the notification area.
+ - Sign in directly to your Slack team domain of choice.
+ - Persist all cookies, sessions and cache to the disk.
+
+## Download
+
+Please refer to the [releases](https://github.com/rfgamaral/SlackUI/releases) page to downloaded the binaries for the latest release.
+
+##### Pre-requisites
+
+ - [.NET Framework 4.5](http://www.microsoft.com/en-us/download/details.aspx?id=42643) _(already included with Windows 8 and above)_
+
+## Usage Guide
+
+Please refer to the documentation on the [wiki](https://github.com/rfgamaral/SlackUI/wiki) to learn more about how to use SlackUI.
+
+## Changelog
+
+Please refer to the [CHANGELOG](CHANGELOG.md) file for the full changelog details.
+
+## Credits
+
+ - [CefSharp](https://github.com/cefsharp/CefSharp): .NET bindings for the Chromium Embedded Framework ([CEF](https://code.google.com/p/chromiumembedded)).
+ - [EasySettings](https://github.com/rfgamaral/EasySettings): Simple .NET library to easily manage applications user preferences.
+ - [VistaMenu](http://wyday.com/vistamenu): Renders menus with native Windows look and feel with support for icons.
+ - [Fugue Icons](https://github.com/yusukekamiyamane/fugue-icons): Beautiful icons designed by [Yusuke Kamiyamane](http://p.yusukekamiyamane.com).
+
+## License
+
+Use of this source code is governed by an MIT-style license that can be found in the [LICENSE](LICENSE) file.
diff --git a/SlackUI.sln b/SlackUI.sln
new file mode 100644
index 0000000..9086b3b
--- /dev/null
+++ b/SlackUI.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.30723.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlackUI", "SlackUI\SlackUI.csproj", "{18B3856C-6D33-432C-B88B-6D0C7B11B428}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Debug|x64.ActiveCfg = Debug|x64
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Debug|x64.Build.0 = Debug|x64
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Debug|x86.ActiveCfg = Debug|x86
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Debug|x86.Build.0 = Debug|x86
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Release|Any CPU.Build.0 = Release|Any CPU
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Release|x64.ActiveCfg = Release|x64
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Release|x64.Build.0 = Release|x64
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Release|x86.ActiveCfg = Release|x86
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/SlackUI/Controls/LinkLabelEx.cs b/SlackUI/Controls/LinkLabelEx.cs
new file mode 100644
index 0000000..bde9f46
--- /dev/null
+++ b/SlackUI/Controls/LinkLabelEx.cs
@@ -0,0 +1,41 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Windows.Forms;
+
+namespace SlackUI {
+
+ [System.ComponentModel.DesignerCategory("Code")]
+ internal class LinkLabelEx : LinkLabel {
+
+ #region Private Fields
+
+ private readonly static IntPtr IDC_HAND = new IntPtr(32649);
+
+ #endregion
+
+ #region Protected Methods
+
+ /*
+ * Handler for the overridden on mouse move event.
+ */
+ protected override void OnMouseMove(MouseEventArgs e) {
+ base.OnMouseMove(e);
+
+ // Show the system hand cursor if the base class decided to show the ugly one
+ if(OverrideCursor == Cursors.Hand) {
+ OverrideCursor = new Cursor(NativeMethods.LoadCursor(IntPtr.Zero, IDC_HAND));
+ }
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Core/ApplicationSettings.cs b/SlackUI/Core/ApplicationSettings.cs
new file mode 100644
index 0000000..21c953d
--- /dev/null
+++ b/SlackUI/Core/ApplicationSettings.cs
@@ -0,0 +1,203 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System.Drawing;
+using System.Windows.Forms;
+using Microsoft.Win32;
+using RA.Library.EasySettings;
+
+namespace SlackUI {
+
+ internal class ApplicationSettings {
+
+ #region Private Fields
+
+ private const string StartupRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";
+
+ private readonly static string StartupRegistryName = Application.ProductName;
+
+ private bool startWithWindows;
+ private Point windowLocation;
+ private Size windowSize;
+ private FormWindowState windowState;
+
+ #endregion
+
+ #region Internal Properties
+
+ /*
+ * The initial team domain to load on startup.
+ */
+ [EasySettingsAttribute("General", "")]
+ internal string InitialTeamToLoad {
+ get;
+ set;
+ }
+
+ /*
+ * Show the application on startup.
+ */
+ [EasySettingsAttribute("UserInterface", false)]
+ internal bool ShowOnStartup {
+ get;
+ set;
+ }
+
+ /*
+ * Show the application with a single click on the notification icon.
+ */
+ [EasySettingsAttribute("UserInterface", false)]
+ internal bool ShowWithSingleClick {
+ get;
+ set;
+ }
+
+ /*
+ * Start the application when windows is started.
+ */
+ [EasySettingsAttribute("General", false)]
+ internal bool StartWithWindows {
+ get {
+ return startWithWindows;
+ }
+
+ set {
+ startWithWindows = value;
+
+ // Gets the specified startup sub-key with write access
+ RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(StartupRegistryKey, true);
+
+ // Add or delete the startup value to the registry?
+ if(startWithWindows) {
+#if !DEBUG
+ registryKey.SetValue(StartupRegistryName, Application.ExecutablePath, RegistryValueKind.String);
+#endif
+ } else {
+ if(registryKey.GetValue(StartupRegistryName) != null) {
+#if !DEBUG
+ registryKey.DeleteValue(StartupRegistryName);
+#endif
+ }
+ }
+ }
+ }
+
+ /*
+ * Use the alternative white notification icon.
+ */
+ [EasySettingsAttribute("Appearance", false)]
+ internal bool WhiteNotificationIcon {
+ get;
+ set;
+ }
+
+ /*
+ * Wrapper form window location to restore window properties.
+ */
+ [EasySettingsAttribute("WrapperForm", null)]
+ internal Point WindowLocation {
+ get {
+ return windowLocation;
+ }
+
+ set {
+ windowLocation = value;
+
+ // Set the wrapper form window location
+ Program.WrapperForm.Location = windowLocation;
+ }
+ }
+
+ /*
+ * Wrapper form window size to restore window properties.
+ */
+ [EasySettingsAttribute("WrapperForm", null)]
+ internal Size WindowSize {
+ get {
+ return windowSize;
+ }
+
+ set {
+ windowSize = value;
+
+ // Prevent the window width be less than the minimum allowed
+ if(Program.WrapperForm.MinimumSize.Width > windowSize.Width) {
+ windowSize = new Size(Program.WrapperForm.MinimumSize.Width, windowSize.Height);
+ }
+
+ // Prevent the window height be less than the minimum allowed
+ if(Program.WrapperForm.MinimumSize.Height > windowSize.Width) {
+ windowSize = new Size(windowSize.Width, Program.WrapperForm.MinimumSize.Height);
+ }
+
+ // Set the wrapper form window size
+ Program.WrapperForm.Size = windowSize;
+ }
+ }
+
+ /*
+ * Wrapper form window state to restore window properties.
+ */
+ [EasySettingsAttribute("WrapperForm", typeof(FormWindowState), FormWindowState.Normal)]
+ internal FormWindowState WindowState {
+ get {
+ // Prevent the window state to be minimized
+ return (windowState == FormWindowState.Minimized ? FormWindowState.Normal : windowState);
+ }
+
+ set {
+ windowState = value;
+
+ // Set the wrapper form window state
+ Program.WrapperForm.WindowState = windowState;
+ }
+ }
+
+ #endregion
+
+ #region Internal Classes
+
+ internal static class Defaults {
+
+ #region Private Properties
+
+ /*
+ * Wrapper form window location default dynamic value.
+ */
+ private static Point WindowLocation {
+ get {
+ // Find the middle point in the primary monitor resolution
+ int hMiddlePoint = SystemInformation.WorkingArea.Width / 2;
+ int vMiddlePoint = SystemInformation.WorkingArea.Height / 2;
+
+ // Return the centered wrapper form window
+ return new Point(
+ hMiddlePoint - (Program.WrapperForm.Size.Width / 2),
+ vMiddlePoint - (Program.WrapperForm.Size.Height / 2)
+ );
+ }
+ }
+
+ /*
+ * Wrapper form window size default dynamic value.
+ */
+ private static Size WindowSize {
+ get {
+ return Program.WrapperForm.Size;
+ }
+ }
+
+ #endregion
+
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Core/NativeMethods.cs b/SlackUI/Core/NativeMethods.cs
new file mode 100644
index 0000000..1f583b8
--- /dev/null
+++ b/SlackUI/Core/NativeMethods.cs
@@ -0,0 +1,63 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace SlackUI {
+
+ internal static class NativeMethods {
+
+ #region Internal Enums
+
+ /*
+ * Enumerated flags for new menu items.
+ */
+ [Flags]
+ internal enum MenuFlags : uint {
+ MF_STRING = 0,
+ MF_SEPARATOR = 0x800
+ }
+
+ /*
+ * Enumerated flags for windows messages.
+ */
+ [Flags]
+ internal enum WindowsMessages : uint {
+ WM_EXITSIZEMOVE = 0x0232,
+ WM_SIZE = 0x5,
+ WM_SYSCOMMAND = 0x112
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ /*
+ * Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu.
+ */
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern bool AppendMenu(IntPtr hMenu, MenuFlags uFlags, UIntPtr uIDNewItem, string lpNewItem);
+
+ /*
+ * Enables the application to access the window menu for copying and modifying.
+ */
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
+
+ /*
+ * Loads the specified cursor resource from the system.
+ */
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Core/NotificationContext.cs b/SlackUI/Core/NotificationContext.cs
new file mode 100644
index 0000000..f0f7496
--- /dev/null
+++ b/SlackUI/Core/NotificationContext.cs
@@ -0,0 +1,182 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Windows.Forms;
+using CefSharp;
+using wyDay.Controls;
+
+namespace SlackUI {
+
+ internal class NotificationContext : ApplicationContext {
+
+ #region Private Fields
+
+ private NotifyIcon notifyIcon;
+
+ #endregion
+
+ #region Internal Constructors
+
+ /*
+ * Create a notification context with an icon and menu.
+ */
+ internal NotificationContext() {
+ // Create a VistaMenu control to stylize context menus
+ VistaMenu vistaMenu = new VistaMenu();
+
+ // Create menu items for the notification icon context menu
+ MenuItem menuItemShow = new MenuItem("Show " + Application.ProductName, menuItemShow_Click);
+ MenuItem menuItemSettings = new MenuItem("Settings...", menuItemSettings_Click);
+ MenuItem menuItemAbout = new MenuItem("About...", menuItemAbout_Click);
+ MenuItem menuItemExit = new MenuItem("Exit", menuItemExit_Click);
+
+ // Add icons to the notification icon context menu items
+ vistaMenu.SetImage(menuItemSettings, Properties.Resources.MenuSettingsIcon);
+ vistaMenu.SetImage(menuItemAbout, Properties.Resources.MenuAboutIcon);
+
+ // Set the show menu entry as default (bold)
+ menuItemShow.DefaultItem = true;
+
+ // Add the menu items to the notify icon context menu
+ ContextMenu contextMenuNotifyIcon = new ContextMenu(new MenuItem[] {
+ menuItemShow,
+ menuItemSettings,
+ new MenuItem("-"),
+ menuItemAbout,
+ new MenuItem("-"),
+ menuItemExit
+ });
+
+ // This needs to be called or the context menu will not have icons
+ ((System.ComponentModel.ISupportInitialize)(vistaMenu)).EndInit();
+
+ // Create a new instance of the notify icon
+ notifyIcon = new NotifyIcon() {
+ ContextMenu = contextMenuNotifyIcon,
+ Icon = Program.Settings.Data.WhiteNotificationIcon ?
+ Properties.Resources.SlackUI16White : Properties.Resources.SlackUI16,
+ Text = Application.ProductName,
+ Visible = true,
+ };
+
+ // Assign a click and double click event handler for the notification icon
+ notifyIcon.Click += notifyIcon_Click;
+ notifyIcon.DoubleClick += notifyIcon_DoubleClick;
+
+ // Show the wrapper form on startup?
+ if(Program.Settings.Data.ShowOnStartup) {
+ DisplayWrapperForm();
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /*
+ * Displays the wrapper form to the user.
+ */
+ private static void DisplayWrapperForm() {
+ // Focus the wrapper form or display it to the user
+ if(Program.WrapperForm.Visible) {
+ // Restore normal window state if form is minimized
+ if(Program.WrapperForm.WindowState == FormWindowState.Minimized) {
+ Program.WrapperForm.WindowState = FormWindowState.Normal;
+ }
+
+ // Activates the wrapper form and gives it focus
+ Program.WrapperForm.Activate();
+ } else {
+ // Prompt for initial team domain to load or shows the wrapper form
+ if(Program.Settings.Data.InitialTeamToLoad.Equals(String.Empty)) {
+ using(TeamPickerForm teamPickerForm = new TeamPickerForm()) {
+ if(teamPickerForm.ShowDialog() == DialogResult.OK) {
+ Program.Settings.Data.InitialTeamToLoad = teamPickerForm.SlackTeamDomain;
+ Program.WrapperForm.Show();
+ }
+ }
+ } else {
+ // Displays the wrapper form to the user and gives it focus
+ Program.WrapperForm.Show();
+ Program.WrapperForm.Activate();
+ }
+ }
+ }
+
+ /*
+ * Context menu open about form item click event handler.
+ */
+ private void menuItemAbout_Click(object sender, EventArgs e) {
+ AboutForm aboutForm = Application.OpenForms["AboutForm"] as AboutForm;
+
+ // Focus the about form if opened or create and show a new instance
+ if(aboutForm != null) {
+ aboutForm.Activate();
+ } else {
+ new AboutForm().Show();
+ }
+ }
+
+ /*
+ * Context menu exit item click event handler.
+ */
+ private void menuItemExit_Click(object sender, EventArgs e) {
+ // Persist all application settings
+ Program.Settings.Save();
+
+ // Shuts down CefSharp and the underlying CEF infrastructure
+ Cef.Shutdown();
+
+ // Release all notify icon resources
+ notifyIcon.Dispose();
+
+ // Terminate the application
+ Application.Exit();
+ }
+
+ /*
+ * Context menu open settings location item click event handler.
+ */
+ private void menuItemSettings_Click(object sender, EventArgs e) {
+ Process.Start(@Program.AppDataPath);
+ }
+
+ /*
+ * Context menu show item click event handler.
+ */
+ private void menuItemShow_Click(object sender, EventArgs e) {
+ DisplayWrapperForm();
+ }
+
+ /*
+ * Notification icon single click event handler.
+ */
+ private void notifyIcon_Click(object sender, EventArgs e) {
+ // Show the wrapper form only if single click is enabled
+ if(Program.Settings.Data.ShowWithSingleClick) {
+ DisplayWrapperForm();
+ }
+ }
+
+ /*
+ * Notification icon double click event handler.
+ */
+ private void notifyIcon_DoubleClick(object sender, EventArgs e) {
+ // Show the wrapper form only if single click is disabled
+ if(!Program.Settings.Data.ShowWithSingleClick) {
+ DisplayWrapperForm();
+ }
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Core/Program.cs b/SlackUI/Core/Program.cs
new file mode 100644
index 0000000..691d141
--- /dev/null
+++ b/SlackUI/Core/Program.cs
@@ -0,0 +1,106 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.IO;
+using System.Windows.Forms;
+using CefSharp;
+using RA.Library.EasySettings;
+
+namespace SlackUI {
+
+ internal static class Program {
+
+ #region Private Fields
+
+ private const string BrowserSubprocessFileName = "CefSharp.BrowserSubprocess.exe";
+ private const string CacheFolderName = "Cache";
+ private const string LogFileName = "Chromium.log";
+ private const string SettingsFileName = "settings.xml";
+
+ #endregion
+
+ #region Internal Properties
+
+ /*
+ * Full path for the application data folder.
+ */
+ internal static string AppDataPath {
+ get;
+ private set;
+ }
+
+ /*
+ * EasySettings instance to manage application settings.
+ */
+ internal static EasySettings Settings {
+ get;
+ private set;
+ }
+
+ /*
+ * Single wrapper form instance for easy access.
+ */
+ internal static WrapperForm WrapperForm {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #region Main Entry Point
+
+ /*
+ * The main entry point for the SlackUI application.
+ */
+ [STAThread]
+ private static void Main() {
+ // Enable visual styles and better text rendering
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(true);
+
+ // Get the default local application data path
+ AppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ AppDataPath = Path.Combine(AppDataPath, Application.ProductName);
+
+ #region --------------------[ DEBUG MODE BLOCK ]--------------------
+
+#if DEBUG
+ // Use a proper application data folder for debug mode
+ AppDataPath = Path.Combine(AppDataPath, "[DEBUG]");
+#endif
+
+ #endregion
+
+ // Create the application data folder if it doesn't exist
+ if(!Directory.Exists(AppDataPath)) {
+ Directory.CreateDirectory(AppDataPath);
+ }
+
+ // Initializes CefSharp with the default settings
+ Cef.Initialize(new CefSettings {
+ BrowserSubprocessPath = BrowserSubprocessFileName,
+ CachePath = Path.Combine(AppDataPath, CacheFolderName),
+ LogFile = Path.Combine(AppDataPath, LogFileName)
+ });
+
+ // Initialize a new instance of the wrapper form
+ WrapperForm = new WrapperForm();
+
+ // Initialize a new instance of the Easy Settings class
+ Settings = new EasySettings(Path.Combine(AppDataPath, SettingsFileName), "Settings");
+
+ // Run the application with a notification context
+ Application.Run(new NotificationContext());
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Extensions/ControlExtension.cs b/SlackUI/Extensions/ControlExtension.cs
new file mode 100644
index 0000000..3321a1a
--- /dev/null
+++ b/SlackUI/Extensions/ControlExtension.cs
@@ -0,0 +1,33 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Windows.Forms;
+
+namespace SlackUI {
+
+ internal static class ControlExtension {
+
+ #region Internal Methods
+
+ /*
+ * Executes the action asynchronously on the UI thread, without blocking the calling thread.
+ */
+ internal static void InvokeOnUiThreadIfRequired(this Control control, Action action) {
+ if(control.InvokeRequired) {
+ control.BeginInvoke(action);
+ } else {
+ action.Invoke();
+ }
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Extensions/NativeFontExtension.cs b/SlackUI/Extensions/NativeFontExtension.cs
new file mode 100644
index 0000000..b6cbccb
--- /dev/null
+++ b/SlackUI/Extensions/NativeFontExtension.cs
@@ -0,0 +1,171 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace SlackUI {
+
+ internal static class NativeFontExtension {
+
+ #region Private Fields
+
+ private static bool canRepairFonts = true;
+ private static List fontNamesList;
+
+ #endregion
+
+ #region Private Properties
+
+ /*
+ * Windows native font property.
+ */
+ private static Font NativeFont {
+ get;
+ set;
+ }
+
+ #endregion
+
+ #region Constructors
+
+ /*
+ * Initialize static data fields as soon as the class is first referenced.
+ */
+ static NativeFontExtension() {
+ // Do not attempt to fix fonts in design mode
+ if(Process.GetCurrentProcess().ProcessName.Contains("devenv")) {
+ canRepairFonts = false;
+ return;
+ }
+
+ // Do not fix fonts in Windows 95, 98, NT or similar
+ if(Environment.OSVersion.Platform == PlatformID.Win32Windows || Environment.OSVersion.Version.Major < 5) {
+ canRepairFonts = false;
+ return;
+ }
+
+ // Pick the right native font according to the Windows version
+ if(Environment.OSVersion.Version.Major < 6) {
+ NativeFont = SystemFonts.DialogFont; // Tahoma (hopefully)
+ } else {
+ NativeFont = SystemFonts.MessageBoxFont; // Segoe UI
+ }
+
+ // Initialize the list of fonts to be replaced for the native font
+ fontNamesList = new List(new string[] {
+ "Microsoft Sans Serif",
+ "Tahoma"
+ });
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ /*
+ * Apply the native font to all controls contained in the referenced form.
+ */
+ internal static void ApplyNativeFont(this Form form) {
+ // Return immediately if fonts cannot be repaired
+ if(!canRepairFonts) {
+ return;
+ }
+
+ // Disable the form auto scale mode
+ form.AutoScaleMode = AutoScaleMode.None;
+
+ // Repair the font name on this form
+ form.Font = RepairFontName(form.Font);
+
+ // Repair the font name on this form controls
+ RepairControlsFontName(form.Controls);
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /*
+ * Repair the font name for all controls contained in a control collection.
+ */
+ private static void RepairControlsFontName(Control.ControlCollection collection) {
+ // Loop through all controls and fix the font name recursively
+ foreach(Control control in collection) {
+ control.Font = RepairFontName(control.Font);
+ RepairControlsFontName(control.Controls);
+ }
+ }
+
+ /*
+ * Repair the font name keeping it's formatting (size and style).
+ */
+ private static Font RepairFontName(Font font) {
+ // Should this font be replaced?
+ if(fontNamesList.IndexOf(font.Name) > -1) {
+ bool useDefaultSize = true, useDefaultStyle = true;
+
+ // Does this font have a special size?
+ if(font.Size <= 8 || font.Size >= 9) {
+ useDefaultSize = false;
+ }
+
+ // Does this font have any special styles applied?
+ if(font.Italic || font.Strikeout || font.Underline || font.Bold) {
+ useDefaultStyle = false;
+ }
+
+ // Return the native font if the size and style were not changed
+ if(useDefaultSize && useDefaultStyle) {
+ return NativeFont;
+ }
+
+ // Initialize the default font style and size
+ FontStyle fontStyle = FontStyle.Regular;
+ float fontSize = NativeFont.SizeInPoints;
+
+ // Apply custom styles to the font?
+ if(!useDefaultStyle) {
+ if(font.Italic) {
+ fontStyle = fontStyle | FontStyle.Italic;
+ }
+
+ if(font.Strikeout) {
+ fontStyle = fontStyle | FontStyle.Strikeout;
+ }
+
+ if(font.Underline) {
+ fontStyle = fontStyle | FontStyle.Underline;
+ }
+
+ if(font.Bold) {
+ fontStyle = fontStyle | FontStyle.Bold;
+ }
+ }
+
+ // Apply custom size to the font?
+ if(!useDefaultSize) {
+ fontSize = font.SizeInPoints;
+ }
+
+ // Return the new repaired font
+ return new Font(NativeFont.Name, fontSize, fontStyle, GraphicsUnit.Point);
+ }
+
+ // Return the unchanged font
+ return font;
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Forms/AboutForm.Designer.cs b/SlackUI/Forms/AboutForm.Designer.cs
new file mode 100644
index 0000000..e54bdb3
--- /dev/null
+++ b/SlackUI/Forms/AboutForm.Designer.cs
@@ -0,0 +1,189 @@
+namespace SlackUI {
+ partial class AboutForm {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if(disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm));
+ this.headerPanel = new System.Windows.Forms.Panel();
+ this.aboutLogoPictureBox = new System.Windows.Forms.PictureBox();
+ this.applicationVersionLabel = new System.Windows.Forms.Label();
+ this.applicationNameLabel = new System.Windows.Forms.Label();
+ this.applicationDescriptionLabel = new System.Windows.Forms.Label();
+ this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
+ this.openSourceLabel = new SlackUI.LinkLabelEx();
+ this.copyrightLabel = new System.Windows.Forms.Label();
+ this.homepageLabel = new System.Windows.Forms.LinkLabel();
+ this.headerPanel.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.aboutLogoPictureBox)).BeginInit();
+ this.tableLayoutPanel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // headerPanel
+ //
+ this.headerPanel.BackColor = System.Drawing.Color.White;
+ this.headerPanel.Controls.Add(this.aboutLogoPictureBox);
+ this.headerPanel.Controls.Add(this.applicationVersionLabel);
+ this.headerPanel.Controls.Add(this.applicationNameLabel);
+ this.headerPanel.Dock = System.Windows.Forms.DockStyle.Top;
+ this.headerPanel.Location = new System.Drawing.Point(0, 0);
+ this.headerPanel.Name = "headerPanel";
+ this.headerPanel.Size = new System.Drawing.Size(404, 64);
+ this.headerPanel.TabIndex = 0;
+ this.headerPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.headerPanel_Paint);
+ //
+ // aboutLogoPictureBox
+ //
+ this.aboutLogoPictureBox.BackColor = System.Drawing.Color.Transparent;
+ this.aboutLogoPictureBox.Dock = System.Windows.Forms.DockStyle.Right;
+ this.aboutLogoPictureBox.Image = global::SlackUI.Properties.Resources.AboutLogo;
+ this.aboutLogoPictureBox.Location = new System.Drawing.Point(340, 0);
+ this.aboutLogoPictureBox.Name = "aboutLogoPictureBox";
+ this.aboutLogoPictureBox.Size = new System.Drawing.Size(64, 64);
+ this.aboutLogoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
+ this.aboutLogoPictureBox.TabIndex = 1;
+ this.aboutLogoPictureBox.TabStop = false;
+ //
+ // applicationVersionLabel
+ //
+ this.applicationVersionLabel.AutoSize = true;
+ this.applicationVersionLabel.BackColor = System.Drawing.Color.Transparent;
+ this.applicationVersionLabel.Location = new System.Drawing.Point(13, 33);
+ this.applicationVersionLabel.Name = "applicationVersionLabel";
+ this.applicationVersionLabel.Size = new System.Drawing.Size(59, 13);
+ this.applicationVersionLabel.TabIndex = 1;
+ this.applicationVersionLabel.Text = "Version {0}";
+ //
+ // applicationNameLabel
+ //
+ this.applicationNameLabel.AutoSize = true;
+ this.applicationNameLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.applicationNameLabel.Location = new System.Drawing.Point(12, 9);
+ this.applicationNameLabel.Name = "applicationNameLabel";
+ this.applicationNameLabel.Size = new System.Drawing.Size(175, 24);
+ this.applicationNameLabel.TabIndex = 0;
+ this.applicationNameLabel.Text = "Application Name";
+ //
+ // applicationDescriptionLabel
+ //
+ this.applicationDescriptionLabel.AutoSize = true;
+ this.applicationDescriptionLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.applicationDescriptionLabel.Location = new System.Drawing.Point(15, 12);
+ this.applicationDescriptionLabel.Name = "applicationDescriptionLabel";
+ this.applicationDescriptionLabel.Size = new System.Drawing.Size(374, 13);
+ this.applicationDescriptionLabel.TabIndex = 1;
+ this.applicationDescriptionLabel.Text = "Application Description";
+ //
+ // tableLayoutPanel1
+ //
+ this.tableLayoutPanel1.ColumnCount = 1;
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.Controls.Add(this.applicationDescriptionLabel, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.openSourceLabel, 0, 2);
+ this.tableLayoutPanel1.Controls.Add(this.copyrightLabel, 0, 4);
+ this.tableLayoutPanel1.Controls.Add(this.homepageLabel, 0, 5);
+ this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 64);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(12);
+ this.tableLayoutPanel1.RowCount = 6;
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 3F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tableLayoutPanel1.Size = new System.Drawing.Size(404, 137);
+ this.tableLayoutPanel1.TabIndex = 2;
+ //
+ // openSourceLabel
+ //
+ this.openSourceLabel.AutoSize = true;
+ this.openSourceLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.openSourceLabel.Location = new System.Drawing.Point(15, 28);
+ this.openSourceLabel.Name = "openSourceLabel";
+ this.openSourceLabel.Size = new System.Drawing.Size(374, 13);
+ this.openSourceLabel.TabIndex = 4;
+ this.openSourceLabel.TabStop = true;
+ this.openSourceLabel.Text = "{0} is open-source software, feel free to contribute on GitHub.";
+ this.openSourceLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.anyLabel_LinkClicked);
+ //
+ // copyrightLabel
+ //
+ this.copyrightLabel.AutoSize = true;
+ this.copyrightLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.copyrightLabel.Location = new System.Drawing.Point(15, 99);
+ this.copyrightLabel.Name = "copyrightLabel";
+ this.copyrightLabel.Size = new System.Drawing.Size(374, 13);
+ this.copyrightLabel.TabIndex = 5;
+ this.copyrightLabel.Text = "Application Copyright";
+ this.copyrightLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // homepageLabel
+ //
+ this.homepageLabel.AutoSize = true;
+ this.homepageLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.homepageLabel.Location = new System.Drawing.Point(15, 112);
+ this.homepageLabel.Name = "homepageLabel";
+ this.homepageLabel.Size = new System.Drawing.Size(374, 13);
+ this.homepageLabel.TabIndex = 6;
+ this.homepageLabel.TabStop = true;
+ this.homepageLabel.Text = "http://ricardoamaral.net";
+ this.homepageLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.homepageLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.anyLabel_LinkClicked);
+ //
+ // AboutForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(404, 201);
+ this.Controls.Add(this.tableLayoutPanel1);
+ this.Controls.Add(this.headerPanel);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "AboutForm";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "About {0}";
+ this.headerPanel.ResumeLayout(false);
+ this.headerPanel.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.aboutLogoPictureBox)).EndInit();
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel headerPanel;
+ private System.Windows.Forms.Label applicationNameLabel;
+ private System.Windows.Forms.Label applicationVersionLabel;
+ private System.Windows.Forms.PictureBox aboutLogoPictureBox;
+ private System.Windows.Forms.Label applicationDescriptionLabel;
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
+ private SlackUI.LinkLabelEx openSourceLabel;
+ private System.Windows.Forms.Label copyrightLabel;
+ private System.Windows.Forms.LinkLabel homepageLabel;
+ }
+}
\ No newline at end of file
diff --git a/SlackUI/Forms/AboutForm.cs b/SlackUI/Forms/AboutForm.cs
new file mode 100644
index 0000000..9d3b008
--- /dev/null
+++ b/SlackUI/Forms/AboutForm.cs
@@ -0,0 +1,99 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace SlackUI {
+
+ [System.ComponentModel.DesignerCategory("Form")]
+ internal partial class AboutForm : BaseForm {
+
+ #region Private Fields
+
+ private const string GitHubLink = "https://github.com/rfgamaral/SlackUI";
+ private const string GitHubLinkText = "GitHub";
+ private const string HomepageLink = "http://ricardoamaral.net";
+
+ #endregion Private Fields
+
+ #region Public Properties
+
+ /*
+ * Get the executing assembly copyright attribute.
+ */
+ public string AssemblyCopyright {
+ get {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(
+ typeof(AssemblyCopyrightAttribute), false);
+ return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
+ }
+ }
+
+ /*
+ * Get the executing assembly description attribute.
+ */
+ public string AssemblyDescription {
+ get {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(
+ typeof(AssemblyDescriptionAttribute), false);
+ return ((AssemblyDescriptionAttribute)attributes[0]).Description;
+ }
+ }
+
+ #endregion
+
+ #region Public Constructors
+
+ /*
+ * Create an about form with application information.
+ */
+ public AboutForm() {
+ InitializeComponent();
+
+ // Format the form title with the application name
+ Text = String.Format(Text, Application.ProductName);
+
+ // Fill application name, version, description and copyright labels
+ applicationNameLabel.Text = Application.ProductName;
+ applicationVersionLabel.Text = String.Format(applicationVersionLabel.Text, Application.ProductVersion);
+ applicationDescriptionLabel.Text = AssemblyDescription;
+ copyrightLabel.Text = AssemblyCopyright;
+
+ // Fill open-source and homepage labels with clickable links
+ openSourceLabel.Text = String.Format(openSourceLabel.Text, Application.ProductName);
+ openSourceLabel.Links.Add(openSourceLabel.Text.IndexOf(GitHubLinkText), GitHubLinkText.Length,
+ GitHubLink).Enabled = true;
+ homepageLabel.Links.Add(0, HomepageLink.Length, HomepageLink);
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /*
+ * Any label link clicked event handler.
+ */
+ private void anyLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
+ Process.Start(e.Link.LinkData.ToString());
+ }
+
+ /*
+ * Header panel paint event handler.
+ */
+ private void headerPanel_Paint(object sender, PaintEventArgs e) {
+ ControlPaint.DrawBorder3D(e.Graphics, headerPanel.ClientRectangle, Border3DStyle.Etched, Border3DSide.Bottom);
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Forms/AboutForm.resx b/SlackUI/Forms/AboutForm.resx
new file mode 100644
index 0000000..5d8950e
--- /dev/null
+++ b/SlackUI/Forms/AboutForm.resx
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD++/+o/fn/wff3+l8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/P/D5vbS/7XZgv///////Pn/SwAAAAD/9/Ra9ufjNQAA
+ AAAAAAAAAAAAAAAAAAAAAAAA/v78mP/////9//r2/////JLIOP+QxDj/1uu6//r2/4b//Pvf9P//////
+ ///++vmYAAAAAAAAAAAAAAAA///1MP////9rAOL/cBHm/5p9y/+EoUv/k8o3//n/6////fit7vz//wCr
+ 9v8AsPf//////wAAAAAAAAAAAAAAAP3/91f9+P//XgDg/3AO5/9eC5j/VABj/1MAb/+nfvT/49H7/5XX
+ /f8GuPv/Erf6///8+PQAAAAAAAAAAAAAAAAAAAAA/v/73vXv//+5m/X/VyVh/1MAZP9fBqL/cBDm/3IA
+ 5v8NAKr/AFW+/2+g5v////38/f/4vwAAAAAAAAAA9vb8Tfb4/4r9/f+t8f3g/5bPOP+Ls0P/sZzY/4NB
+ 6P9jANj/ABei/wAYpP9bD9D/ZgDi/9XB/v/+//ea/f3/Xf/////17cf///3z/8Pgn/+VyET/mstO////
+ ////////mcnz/wB10f8ANbD/bADj/2gA4f+fdO///f/2uvn6/77q36H/2slm/+DNbf+EmxH/iasp/77U
+ kf///////////zu8+/8Ftvr/gtX+///3///Sv/v///////z/+VP7+/+l/fnf/9zLav/KwF//f5cT/3+X
+ E//JwF3/385z/9jar/8Aq9n/ALL9/9Hw/////fqt/P/0gP//9zcAAAAAAAAAAPz9/8H////8vtOO/4mq
+ KP+Dmg//4M1u/+DNbP+Tt27/HZ9t/w2db//s4Kr///zs//v8/9UAAAAAAAAAAAAAAAAAAAAA/Pn/9ZrL
+ Uf+VyET/wd+c///77f/z5K//R6Z1/x6fbP9srW//4s1s/9fGW/////j/+Pj/RwAAAAAAAAAAAAAAAP//
+ //+XyEj/iMEh//T86f/++/+s8/7//wCx//8Irt//uNCz/+LRd//czG7///////Ly/yQAAAAAAAAAAAAA
+ AAD//v+K////////+P/+/P/T//j0gL7o//8Ar/b/AK/5//////z6+v/0//////7+/4kAAAAAAAAAAAAA
+ AAAAAAAAAAAAAPny/yT8+P9EAAAAAP/79zj/////e8/+/8jv////+vi7AAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//z5U//59Lr/+fSYAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA/P8AAPh/AADAAwAAwAMAAMADAADAAQAAwAAAAIAAAAAAAQAAAAMAAIADAADAAwAAwAMAAMAD
+ AAD+HwAA/z8AAA==
+
+
+
\ No newline at end of file
diff --git a/SlackUI/Forms/DefaultForm.cs b/SlackUI/Forms/DefaultForm.cs
new file mode 100644
index 0000000..0fcfdf2
--- /dev/null
+++ b/SlackUI/Forms/DefaultForm.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Windows.Forms;
+
+namespace SlackUI {
+
+ [System.ComponentModel.DesignerCategory("Code")]
+ public class BaseForm : Form {
+
+ #region Public Constructors
+
+ /*
+ * Create an empty base form with native fonts applied.
+ */
+ public BaseForm()
+ : base() {
+ }
+
+ #endregion Public Constructors
+
+ #region Protected Methods
+
+ /*
+ * Handler for the overridden on handle created event.
+ */
+ protected override void OnHandleCreated(EventArgs e) {
+ base.OnHandleCreated(e);
+
+ // Apply the native font to all controls contained in this form
+ this.ApplyNativeFont();
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Forms/TeamPickerForm.Designer.cs b/SlackUI/Forms/TeamPickerForm.Designer.cs
new file mode 100644
index 0000000..cf3b6fe
--- /dev/null
+++ b/SlackUI/Forms/TeamPickerForm.Designer.cs
@@ -0,0 +1,119 @@
+namespace SlackUI {
+ partial class TeamPickerForm {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if(disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TeamPickerForm));
+ this.teamGroupBox = new System.Windows.Forms.GroupBox();
+ this.teamDomainTextBox = new System.Windows.Forms.TextBox();
+ this.continueButton = new System.Windows.Forms.Button();
+ this.slackDomainLabel = new System.Windows.Forms.Label();
+ this.teamGroupBox.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // teamGroupBox
+ //
+ this.teamGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.teamGroupBox.Controls.Add(this.teamDomainTextBox);
+ this.teamGroupBox.Controls.Add(this.continueButton);
+ this.teamGroupBox.Controls.Add(this.slackDomainLabel);
+ this.teamGroupBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.teamGroupBox.Location = new System.Drawing.Point(12, 12);
+ this.teamGroupBox.Name = "teamGroupBox";
+ this.teamGroupBox.Size = new System.Drawing.Size(331, 137);
+ this.teamGroupBox.TabIndex = 0;
+ this.teamGroupBox.TabStop = false;
+ this.teamGroupBox.Text = "Enter your team\'s Slack domain:";
+ //
+ // teamDomainTextBox
+ //
+ this.teamDomainTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.teamDomainTextBox.Location = new System.Drawing.Point(11, 34);
+ this.teamDomainTextBox.Margin = new System.Windows.Forms.Padding(8, 12, 0, 3);
+ this.teamDomainTextBox.MaxLength = 63;
+ this.teamDomainTextBox.Name = "teamDomainTextBox";
+ this.teamDomainTextBox.Size = new System.Drawing.Size(200, 38);
+ this.teamDomainTextBox.TabIndex = 0;
+ this.teamDomainTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
+ this.teamDomainTextBox.TextChanged += new System.EventHandler(this.teamTextBox_TextChanged);
+ this.teamDomainTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.teamTextBox_KeyPress);
+ //
+ // continueButton
+ //
+ this.continueButton.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.continueButton.Enabled = false;
+ this.continueButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.continueButton.Location = new System.Drawing.Point(10, 83);
+ this.continueButton.Margin = new System.Windows.Forms.Padding(3, 8, 7, 7);
+ this.continueButton.Name = "continueButton";
+ this.continueButton.Size = new System.Drawing.Size(311, 44);
+ this.continueButton.TabIndex = 2;
+ this.continueButton.Text = "Continue ▶";
+ this.continueButton.UseVisualStyleBackColor = true;
+ this.continueButton.Click += new System.EventHandler(this.continueButton_Click);
+ //
+ // slackDomainLabel
+ //
+ this.slackDomainLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.slackDomainLabel.Location = new System.Drawing.Point(214, 34);
+ this.slackDomainLabel.Name = "slackDomainLabel";
+ this.slackDomainLabel.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3);
+ this.slackDomainLabel.Size = new System.Drawing.Size(107, 38);
+ this.slackDomainLabel.TabIndex = 1;
+ this.slackDomainLabel.Text = ".slack.com";
+ this.slackDomainLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
+ //
+ // TeamPickerForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(355, 161);
+ this.Controls.Add(this.teamGroupBox);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "TeamPickerForm";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Sign In";
+ this.Shown += new System.EventHandler(this.TeamPickerForm_Shown);
+ this.teamGroupBox.ResumeLayout(false);
+ this.teamGroupBox.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.GroupBox teamGroupBox;
+ private System.Windows.Forms.Label slackDomainLabel;
+ private System.Windows.Forms.Button continueButton;
+ private System.Windows.Forms.TextBox teamDomainTextBox;
+
+ }
+}
\ No newline at end of file
diff --git a/SlackUI/Forms/TeamPickerForm.cs b/SlackUI/Forms/TeamPickerForm.cs
new file mode 100644
index 0000000..6f18669
--- /dev/null
+++ b/SlackUI/Forms/TeamPickerForm.cs
@@ -0,0 +1,163 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.Net;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using Microsoft.WindowsAPICodePack.Dialogs;
+
+namespace SlackUI {
+
+ [System.ComponentModel.DesignerCategory("Form")]
+ internal partial class TeamPickerForm : BaseForm {
+
+ #region Internal Properties
+
+ /*
+ * Slack team domain typed by the user.
+ */
+ internal string SlackTeamDomain {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #region Private Fields
+
+ private const string FindYourTeamUrl = "http://slack.com/signin/find";
+ private const string SlackTeamUrl = "http://{0}.slack.com";
+
+ #endregion
+
+ #region Public Constructors
+
+ /*
+ * Create a team picker form to pick and validate a new team domain.
+ */
+ internal TeamPickerForm() {
+ InitializeComponent();
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /*
+ * Continue button click event handler.
+ */
+ private void continueButton_Click(object sender, EventArgs e) {
+ // Save team domain if valid otherwise show team not found information dialog
+ if(IsTeamDomainValid()) {
+ SlackTeamDomain = teamDomainTextBox.Text;
+ DialogResult = DialogResult.OK;
+ Close();
+ } else {
+ TaskDialog noTeamDialog = new TaskDialog() {
+ Caption = Application.ProductName,
+ HyperlinksEnabled = true,
+ Icon = TaskDialogStandardIcon.Information,
+ InstructionText = "We couldn't find your team.",
+ OwnerWindowHandle = Handle,
+ StandardButtons = TaskDialogStandardButtons.Ok,
+ Text = "If you can't remember your team's address, Slack can send you a reminder."
+ };
+
+ noTeamDialog.HyperlinkClick += delegate { Process.Start(FindYourTeamUrl); };
+
+ // Workaround for a bug in the Windows TaskDialog API
+ noTeamDialog.Opened += (td, ev) => {
+ TaskDialog taskDialog = td as TaskDialog;
+ taskDialog.Icon = noTeamDialog.Icon;
+ taskDialog.InstructionText = noTeamDialog.InstructionText;
+ };
+
+ noTeamDialog.Show();
+ }
+ }
+
+ /*
+ * Check if the picked team domain is valid (if it exists).
+ */
+ private bool IsTeamDomainValid() {
+ HttpStatusCode statusCode = default(HttpStatusCode);
+
+ // Create HEAD request for the picked team domain
+ WebRequest request = HttpWebRequest.Create(String.Format(SlackTeamUrl, teamDomainTextBox.Text));
+ request.Method = "HEAD";
+
+ // Change the form state to "wait mode"
+ Cursor.Current = Cursors.WaitCursor;
+ teamGroupBox.Enabled = false;
+
+ // Make the request and get the response status code
+ try {
+ using(HttpWebResponse response = request.GetResponse() as HttpWebResponse) {
+ if(response != null) {
+ statusCode = response.StatusCode;
+ }
+ }
+ } catch(WebException ex) {
+ using(HttpWebResponse response = ex.Response as HttpWebResponse) {
+ if(response != null) {
+ statusCode = response.StatusCode;
+ }
+ }
+ }
+
+ // Change the form state to "default mode"
+ Cursor.Current = Cursors.Default;
+ teamGroupBox.Enabled = true;
+
+ // Return true if team domain was found or false otherwise
+ return statusCode == HttpStatusCode.OK;
+ }
+
+ /*
+ * Team picker form shown event handler.
+ */
+ private void TeamPickerForm_Shown(object sender, EventArgs e) {
+ // Make sure the form is focused when shown
+ Activate();
+ }
+
+ /*
+ * Team text box key press event handler.
+ */
+ private void teamTextBox_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) {
+ // Virtually click the continue button if the return key was pressed
+ if(e.KeyChar == Convert.ToChar(Keys.Return, CultureInfo.InvariantCulture) && teamDomainTextBox.TextLength > 0) {
+ continueButton_Click(sender, null);
+ e.Handled = true;
+ return;
+ }
+
+ // Ignore the key press if it's an invalid character
+ if(!Char.IsControl(e.KeyChar) && !Regex.Match(e.KeyChar.ToString(), @"[a-z0-9\-\.]",
+ RegexOptions.IgnoreCase).Success) {
+ e.Handled = true;
+ }
+ }
+
+ /*
+ * Team text box text changed event handler.
+ */
+ private void teamTextBox_TextChanged(object sender, System.EventArgs e) {
+ // Validate the team text input and enable/disable the continue button accordingly
+ continueButton.Enabled = !(teamDomainTextBox.Text.Equals(String.Empty)) && Regex.Match(teamDomainTextBox.Text,
+ @"^[a-z0-9]([a-z0-9\-\.]*[a-z0-9]|[a-z0-9])?$", RegexOptions.IgnoreCase).Success;
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Forms/TeamPickerForm.resx b/SlackUI/Forms/TeamPickerForm.resx
new file mode 100644
index 0000000..5d8950e
--- /dev/null
+++ b/SlackUI/Forms/TeamPickerForm.resx
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD++/+o/fn/wff3+l8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/P/D5vbS/7XZgv///////Pn/SwAAAAD/9/Ra9ufjNQAA
+ AAAAAAAAAAAAAAAAAAAAAAAA/v78mP/////9//r2/////JLIOP+QxDj/1uu6//r2/4b//Pvf9P//////
+ ///++vmYAAAAAAAAAAAAAAAA///1MP////9rAOL/cBHm/5p9y/+EoUv/k8o3//n/6////fit7vz//wCr
+ 9v8AsPf//////wAAAAAAAAAAAAAAAP3/91f9+P//XgDg/3AO5/9eC5j/VABj/1MAb/+nfvT/49H7/5XX
+ /f8GuPv/Erf6///8+PQAAAAAAAAAAAAAAAAAAAAA/v/73vXv//+5m/X/VyVh/1MAZP9fBqL/cBDm/3IA
+ 5v8NAKr/AFW+/2+g5v////38/f/4vwAAAAAAAAAA9vb8Tfb4/4r9/f+t8f3g/5bPOP+Ls0P/sZzY/4NB
+ 6P9jANj/ABei/wAYpP9bD9D/ZgDi/9XB/v/+//ea/f3/Xf/////17cf///3z/8Pgn/+VyET/mstO////
+ ////////mcnz/wB10f8ANbD/bADj/2gA4f+fdO///f/2uvn6/77q36H/2slm/+DNbf+EmxH/iasp/77U
+ kf///////////zu8+/8Ftvr/gtX+///3///Sv/v///////z/+VP7+/+l/fnf/9zLav/KwF//f5cT/3+X
+ E//JwF3/385z/9jar/8Aq9n/ALL9/9Hw/////fqt/P/0gP//9zcAAAAAAAAAAPz9/8H////8vtOO/4mq
+ KP+Dmg//4M1u/+DNbP+Tt27/HZ9t/w2db//s4Kr///zs//v8/9UAAAAAAAAAAAAAAAAAAAAA/Pn/9ZrL
+ Uf+VyET/wd+c///77f/z5K//R6Z1/x6fbP9srW//4s1s/9fGW/////j/+Pj/RwAAAAAAAAAAAAAAAP//
+ //+XyEj/iMEh//T86f/++/+s8/7//wCx//8Irt//uNCz/+LRd//czG7///////Ly/yQAAAAAAAAAAAAA
+ AAD//v+K////////+P/+/P/T//j0gL7o//8Ar/b/AK/5//////z6+v/0//////7+/4kAAAAAAAAAAAAA
+ AAAAAAAAAAAAAPny/yT8+P9EAAAAAP/79zj/////e8/+/8jv////+vi7AAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//z5U//59Lr/+fSYAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA/P8AAPh/AADAAwAAwAMAAMADAADAAQAAwAAAAIAAAAAAAQAAAAMAAIADAADAAwAAwAMAAMAD
+ AAD+HwAA/z8AAA==
+
+
+
\ No newline at end of file
diff --git a/SlackUI/Forms/WrapperForm.Designer.cs b/SlackUI/Forms/WrapperForm.Designer.cs
new file mode 100644
index 0000000..5779e3b
--- /dev/null
+++ b/SlackUI/Forms/WrapperForm.Designer.cs
@@ -0,0 +1,92 @@
+namespace SlackUI {
+ partial class WrapperForm {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if(disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WrapperForm));
+ this.browserPanel = new System.Windows.Forms.Panel();
+ this.initialLoadOverlay = new System.Windows.Forms.Panel();
+ this.loadingMarquee = new System.Windows.Forms.ProgressBar();
+ this.browserPanel.SuspendLayout();
+ this.initialLoadOverlay.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // browserPanel
+ //
+ this.browserPanel.BackColor = System.Drawing.Color.White;
+ this.browserPanel.Controls.Add(this.initialLoadOverlay);
+ this.browserPanel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.browserPanel.Location = new System.Drawing.Point(0, 0);
+ this.browserPanel.Name = "browserPanel";
+ this.browserPanel.Size = new System.Drawing.Size(944, 596);
+ this.browserPanel.TabIndex = 0;
+ //
+ // initialLoadOverlay
+ //
+ this.initialLoadOverlay.BackgroundImage = global::SlackUI.Properties.Resources.PanelBackground;
+ this.initialLoadOverlay.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
+ this.initialLoadOverlay.Controls.Add(this.loadingMarquee);
+ this.initialLoadOverlay.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.initialLoadOverlay.Location = new System.Drawing.Point(0, 0);
+ this.initialLoadOverlay.Name = "initialLoadOverlay";
+ this.initialLoadOverlay.Size = new System.Drawing.Size(944, 596);
+ this.initialLoadOverlay.TabIndex = 0;
+ //
+ // loadingMarquee
+ //
+ this.loadingMarquee.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.loadingMarquee.Location = new System.Drawing.Point(12, 569);
+ this.loadingMarquee.MarqueeAnimationSpeed = 5;
+ this.loadingMarquee.Name = "loadingMarquee";
+ this.loadingMarquee.Size = new System.Drawing.Size(920, 15);
+ this.loadingMarquee.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
+ this.loadingMarquee.TabIndex = 0;
+ //
+ // WrapperForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(944, 596);
+ this.Controls.Add(this.browserPanel);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MinimumSize = new System.Drawing.Size(960, 635);
+ this.Name = "WrapperForm";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.Text = "SlackUI";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.WrapperForm_FormClosing);
+ this.browserPanel.ResumeLayout(false);
+ this.initialLoadOverlay.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel browserPanel;
+ private System.Windows.Forms.Panel initialLoadOverlay;
+ private System.Windows.Forms.ProgressBar loadingMarquee;
+
+ }
+}
+
diff --git a/SlackUI/Forms/WrapperForm.cs b/SlackUI/Forms/WrapperForm.cs
new file mode 100644
index 0000000..fa4b959
--- /dev/null
+++ b/SlackUI/Forms/WrapperForm.cs
@@ -0,0 +1,187 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using System;
+using System.Windows.Forms;
+using CefSharp.WinForms;
+
+namespace SlackUI {
+
+ [System.ComponentModel.DesignerCategory("Form")]
+ internal partial class WrapperForm : BaseForm {
+
+ #region Private Fields
+
+ private const string AboutBlankPage = "about:blank";
+ private const string SlackTeamUrl = "{0}.slack.com";
+
+ private const uint SYSMENU_DEVTOOLS_ID = 0x1;
+
+ private readonly ChromiumWebBrowser chromium;
+
+ private FormWindowState previousWindowState;
+
+ #endregion
+
+ #region Public Constructors
+
+ /*
+ * Create a wrapper form around the chromium web browser.
+ */
+ public WrapperForm() {
+ InitializeComponent();
+
+ // Initializes a new instance of the chromium web browser
+ chromium = new ChromiumWebBrowser(AboutBlankPage) {
+ Dock = DockStyle.Fill,
+ MenuHandler = new BrowserMenuHandler()
+ };
+
+ // Subscribe to multiple chromium web browser events
+ chromium.FrameLoadEnd += chromium_FrameLoadEnd;
+ chromium.NavStateChanged += chromium_NavStateChanged;
+
+ // Add the chromium web browser to the browser panel
+ browserPanel.Controls.Add(chromium);
+
+ // Save the current window state as the previous one
+ previousWindowState = WindowState;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /*
+ * Chromium web browser frame load end event handler.
+ */
+ private void chromium_FrameLoadEnd(object sender, CefSharp.FrameLoadEndEventArgs e) {
+ // Was the loaded page the first page load?
+ if(!e.Url.Contains(AboutBlankPage)) {
+ // Clean up unnecessary elements from 'Sign In' path based pages
+ chromium.ExecuteScriptAsync(Properties.Resources.SignInCleanup);
+
+ // Remove the initial load overlay from the form
+ this.InvokeOnUiThreadIfRequired(() => {
+ System.Threading.Thread.Sleep(100);
+ browserPanel.Controls.RemoveByKey("initialLoadOverlay");
+ });
+
+ // Unsubscribe the frame load end event
+ chromium.FrameLoadEnd -= chromium_FrameLoadEnd;
+ }
+ }
+
+ /*
+ * Chromium web browser navigation state changed event handler.
+ */
+ private void chromium_NavStateChanged(object sender, CefSharp.NavStateChangedEventArgs e) {
+ // Is the browser ready to load a page as the first page load?
+ if(!e.CanGoBack && !e.CanGoForward && e.CanReload) {
+ // Load the initial Slack team page address
+ chromium.Load(String.Format(SlackTeamUrl, Program.Settings.Data.InitialTeamToLoad));
+
+ // Unsubscribe the navigation state changed event
+ chromium.NavStateChanged -= chromium_NavStateChanged;
+ }
+ }
+
+ /*
+ * Save the wrapper form window location, size and state settings.
+ */
+ private void SaveWindowProperties() {
+ // Update the wrapper form window state setting
+ Program.Settings.Data.WindowState = WindowState;
+
+ // Update the wrapper form window location and size settings
+ if(WindowState == FormWindowState.Normal) {
+ Program.Settings.Data.WindowLocation = Location;
+ Program.Settings.Data.WindowSize = Size;
+ } else {
+ Program.Settings.Data.WindowLocation = RestoreBounds.Location;
+ Program.Settings.Data.WindowSize = RestoreBounds.Size;
+ }
+
+ // Persist new application settings
+ Program.Settings.Save();
+ }
+
+ /*
+ * Wrapper form form closing event handler.
+ */
+ private void WrapperForm_FormClosing(object sender, FormClosingEventArgs e) {
+ // Release all chromium web browser resources
+ if(e.CloseReason == CloseReason.ApplicationExitCall) {
+ if(chromium != null) {
+ chromium.Dispose();
+ }
+ }
+
+ // Hide instead of close if the user is closing the form
+ if(e.CloseReason == CloseReason.UserClosing) {
+ e.Cancel = true;
+ Hide();
+ }
+ }
+
+ #endregion
+
+ #region Protected Methods
+
+ /*
+ * Handler for the overridden on handle created event.
+ */
+ protected override void OnHandleCreated(EventArgs e) {
+ base.OnHandleCreated(e);
+
+ // Get this form's system menu handler
+ IntPtr systemMenu = NativeMethods.GetSystemMenu(Handle, false);
+
+ // Add a separator followed by the DevTools menu item
+ NativeMethods.AppendMenu(systemMenu, NativeMethods.MenuFlags.MF_SEPARATOR, UIntPtr.Zero, String.Empty);
+ NativeMethods.AppendMenu(systemMenu, NativeMethods.MenuFlags.MF_STRING, new UIntPtr(SYSMENU_DEVTOOLS_ID),
+ "&Show DevTools…");
+ }
+
+ /*
+ * Handler for the overridden Windows messages processor.
+ */
+ protected override void WndProc(ref Message m) {
+ base.WndProc(ref m);
+
+ // Handles detected Windows messages
+ switch(m.Msg) {
+ // Show DevTools if the respective item was selected from the system menu
+ case (int)NativeMethods.WindowsMessages.WM_SYSCOMMAND:
+ if((int)m.WParam == SYSMENU_DEVTOOLS_ID) {
+ chromium.ShowDevTools();
+ }
+
+ break;
+
+ // Save the wrapper form window location, size and state
+ case (int)NativeMethods.WindowsMessages.WM_EXITSIZEMOVE:
+ SaveWindowProperties();
+ break;
+
+ // Save the wrapper form window location, size and state
+ case (int)NativeMethods.WindowsMessages.WM_SIZE:
+ if(previousWindowState != WindowState) {
+ previousWindowState = WindowState;
+ SaveWindowProperties();
+ }
+
+ break;
+ }
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Forms/WrapperForm.resx b/SlackUI/Forms/WrapperForm.resx
new file mode 100644
index 0000000..5d8950e
--- /dev/null
+++ b/SlackUI/Forms/WrapperForm.resx
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD++/+o/fn/wff3+l8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/P/D5vbS/7XZgv///////Pn/SwAAAAD/9/Ra9ufjNQAA
+ AAAAAAAAAAAAAAAAAAAAAAAA/v78mP/////9//r2/////JLIOP+QxDj/1uu6//r2/4b//Pvf9P//////
+ ///++vmYAAAAAAAAAAAAAAAA///1MP////9rAOL/cBHm/5p9y/+EoUv/k8o3//n/6////fit7vz//wCr
+ 9v8AsPf//////wAAAAAAAAAAAAAAAP3/91f9+P//XgDg/3AO5/9eC5j/VABj/1MAb/+nfvT/49H7/5XX
+ /f8GuPv/Erf6///8+PQAAAAAAAAAAAAAAAAAAAAA/v/73vXv//+5m/X/VyVh/1MAZP9fBqL/cBDm/3IA
+ 5v8NAKr/AFW+/2+g5v////38/f/4vwAAAAAAAAAA9vb8Tfb4/4r9/f+t8f3g/5bPOP+Ls0P/sZzY/4NB
+ 6P9jANj/ABei/wAYpP9bD9D/ZgDi/9XB/v/+//ea/f3/Xf/////17cf///3z/8Pgn/+VyET/mstO////
+ ////////mcnz/wB10f8ANbD/bADj/2gA4f+fdO///f/2uvn6/77q36H/2slm/+DNbf+EmxH/iasp/77U
+ kf///////////zu8+/8Ftvr/gtX+///3///Sv/v///////z/+VP7+/+l/fnf/9zLav/KwF//f5cT/3+X
+ E//JwF3/385z/9jar/8Aq9n/ALL9/9Hw/////fqt/P/0gP//9zcAAAAAAAAAAPz9/8H////8vtOO/4mq
+ KP+Dmg//4M1u/+DNbP+Tt27/HZ9t/w2db//s4Kr///zs//v8/9UAAAAAAAAAAAAAAAAAAAAA/Pn/9ZrL
+ Uf+VyET/wd+c///77f/z5K//R6Z1/x6fbP9srW//4s1s/9fGW/////j/+Pj/RwAAAAAAAAAAAAAAAP//
+ //+XyEj/iMEh//T86f/++/+s8/7//wCx//8Irt//uNCz/+LRd//czG7///////Ly/yQAAAAAAAAAAAAA
+ AAD//v+K////////+P/+/P/T//j0gL7o//8Ar/b/AK/5//////z6+v/0//////7+/4kAAAAAAAAAAAAA
+ AAAAAAAAAAAAAPny/yT8+P9EAAAAAP/79zj/////e8/+/8jv////+vi7AAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//z5U//59Lr/+fSYAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA/P8AAPh/AADAAwAAwAMAAMADAADAAQAAwAAAAIAAAAAAAQAAAAMAAIADAADAAwAAwAMAAMAD
+ AAD+HwAA/z8AAA==
+
+
+
\ No newline at end of file
diff --git a/SlackUI/Handlers/BrowserMenuHandler.cs b/SlackUI/Handlers/BrowserMenuHandler.cs
new file mode 100644
index 0000000..744c8b4
--- /dev/null
+++ b/SlackUI/Handlers/BrowserMenuHandler.cs
@@ -0,0 +1,29 @@
+#region Copyright © 2014 Ricardo Amaral
+
+/*
+ * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
+ */
+
+#endregion
+
+using CefSharp;
+
+namespace SlackUI {
+
+ internal class BrowserMenuHandler : IMenuHandler {
+
+ #region Internal Methods
+
+ /*
+ * Handler for the browser context menu.
+ */
+ public bool OnBeforeContextMenu(IWebBrowser browser) {
+ // Disable the context menu
+ return false;
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/SlackUI/Properties/AssemblyInfo.cs b/SlackUI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..64c7421
--- /dev/null
+++ b/SlackUI/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("SlackUI")]
+[assembly: AssemblyDescription("Slack for Windows - Unofficial, not affiliated with Slack Technologies.")]
+[assembly: AssemblyCompany("Ricardo Amaral")]
+[assembly: AssemblyProduct("SlackUI")]
+[assembly: AssemblyCopyright("Copyright © 2014 Ricardo Amaral")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("5f2985ca-798c-4efd-a3c8-031846960a45")]
+
+[assembly: AssemblyVersion("1.0.14306.1755")]
+[assembly: AssemblyFileVersion("1.0.14306.1755")]
+
+[assembly: NeutralResourcesLanguageAttribute("en")]
+[assembly: CLSCompliant(true)]
diff --git a/SlackUI/Properties/Resources.Designer.cs b/SlackUI/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..b41c9cd
--- /dev/null
+++ b/SlackUI/Properties/Resources.Designer.cs
@@ -0,0 +1,133 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.34209
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SlackUI.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SlackUI.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap AboutLogo {
+ get {
+ object obj = ResourceManager.GetObject("AboutLogo", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap MenuAboutIcon {
+ get {
+ object obj = ResourceManager.GetObject("MenuAboutIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap MenuSettingsIcon {
+ get {
+ object obj = ResourceManager.GetObject("MenuSettingsIcon", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap PanelBackground {
+ get {
+ object obj = ResourceManager.GetObject("PanelBackground", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to alert(document.querySelector(".header_nav"));
+ ///document.querySelector(".header_nav").style.display = 'none';.
+ ///
+ internal static string SignInCleanup {
+ get {
+ return ResourceManager.GetString("SignInCleanup", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ ///
+ internal static System.Drawing.Icon SlackUI16 {
+ get {
+ object obj = ResourceManager.GetObject("SlackUI16", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ ///
+ internal static System.Drawing.Icon SlackUI16White {
+ get {
+ object obj = ResourceManager.GetObject("SlackUI16White", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+ }
+}
diff --git a/SlackUI/Properties/Resources.resx b/SlackUI/Properties/Resources.resx
new file mode 100644
index 0000000..6a4517d
--- /dev/null
+++ b/SlackUI/Properties/Resources.resx
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\Images\AboutLogo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\Images\MenuAboutIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\Images\MenuSettingsIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\Images\PanelBackground.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\Scripts\SignInCleanup.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ ..\Resources\Icons\SlackUI16.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\Icons\SlackUI16White.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/SlackUI/Resources/Icons/SlackUI.ico b/SlackUI/Resources/Icons/SlackUI.ico
new file mode 100644
index 0000000..15c0d94
Binary files /dev/null and b/SlackUI/Resources/Icons/SlackUI.ico differ
diff --git a/SlackUI/Resources/Icons/SlackUI16.ico b/SlackUI/Resources/Icons/SlackUI16.ico
new file mode 100644
index 0000000..165e118
Binary files /dev/null and b/SlackUI/Resources/Icons/SlackUI16.ico differ
diff --git a/SlackUI/Resources/Icons/SlackUI16White.ico b/SlackUI/Resources/Icons/SlackUI16White.ico
new file mode 100644
index 0000000..5b1c32f
Binary files /dev/null and b/SlackUI/Resources/Icons/SlackUI16White.ico differ
diff --git a/SlackUI/Resources/Images/AboutLogo.png b/SlackUI/Resources/Images/AboutLogo.png
new file mode 100644
index 0000000..26af02f
Binary files /dev/null and b/SlackUI/Resources/Images/AboutLogo.png differ
diff --git a/SlackUI/Resources/Images/MenuAboutIcon.png b/SlackUI/Resources/Images/MenuAboutIcon.png
new file mode 100644
index 0000000..ada2516
Binary files /dev/null and b/SlackUI/Resources/Images/MenuAboutIcon.png differ
diff --git a/SlackUI/Resources/Images/MenuSettingsIcon.png b/SlackUI/Resources/Images/MenuSettingsIcon.png
new file mode 100644
index 0000000..49be827
Binary files /dev/null and b/SlackUI/Resources/Images/MenuSettingsIcon.png differ
diff --git a/SlackUI/Resources/Images/PanelBackground.png b/SlackUI/Resources/Images/PanelBackground.png
new file mode 100644
index 0000000..9ed2397
Binary files /dev/null and b/SlackUI/Resources/Images/PanelBackground.png differ
diff --git a/SlackUI/Resources/Scripts/SignInCleanup.js b/SlackUI/Resources/Scripts/SignInCleanup.js
new file mode 100644
index 0000000..c154b5c
--- /dev/null
+++ b/SlackUI/Resources/Scripts/SignInCleanup.js
@@ -0,0 +1,11 @@
+/*
+ * Clean up unnecessary elements from 'Sign In' path based pages.
+ */
+(function (d) {
+
+ d.querySelector(".header_nav").style.display = 'none';
+
+ d.querySelector("#page_contents > .align_center:last-child").style.display = 'none';
+ d.querySelector("#page_contents").style.paddingBottom = 0;
+
+}(document));
diff --git a/SlackUI/SlackUI.csproj b/SlackUI/SlackUI.csproj
new file mode 100644
index 0000000..e1b9010
--- /dev/null
+++ b/SlackUI/SlackUI.csproj
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+ Debug
+ AnyCPU
+ {18B3856C-6D33-432C-B88B-6D0C7B11B428}
+ WinExe
+ Properties
+ SlackUI
+ SlackUI
+ v4.5
+ 512
+ false
+ ..\Build\Intermediate\
+ Properties\AssemblyInfo.cs
+ True
+ True
+ False
+ None.IncrementOnDemand.DateStamp.TimeStamp
+ None.IncrementOnDemand.DateStamp.TimeStamp
+ Release
+ 411393e1
+
+
+ AnyCPU
+ true
+ full
+ false
+ ..\Build\Output\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ AnyCPU
+ none
+ true
+ ..\Build\Output\Release\
+
+
+ prompt
+ 4
+ false
+ true
+
+
+ true
+ ..\Build\Output\Debug\x64\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+ true
+
+
+ ..\Build\Output\Release\x64\
+
+
+ true
+ none
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+ false
+ false
+
+
+ true
+ ..\Build\Output\Debug\x86\
+ DEBUG;TRACE
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+ true
+
+
+ ..\Build\Output\Release\x86\
+
+
+ true
+ none
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+ false
+ false
+
+
+ Resources\Icons\SlackUI.ico
+
+
+ OnOutputUpdated
+
+
+ SlackUI.Program
+
+
+
+ ..\Libraries\EasySettings.dll
+
+
+ ..\Packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll
+
+
+
+
+
+
+
+
+ ..\Libraries\VistaMenu.dll
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ AboutForm.cs
+
+
+ Form
+
+
+ WrapperForm.cs
+
+
+
+
+
+
+ Form
+
+
+ TeamPickerForm.cs
+
+
+ AboutForm.cs
+
+
+ WrapperForm.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ TeamPickerForm.cs
+
+
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.5 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
+
+
+
+
+ if $(ConfigurationName) == Release (xcopy /y /q "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\redist\$(PlatformName)\Microsoft.VC110.CRT\*.dll" "$(TargetDir)")
+
+
+
+
\ No newline at end of file
diff --git a/SlackUI/packages.config b/SlackUI/packages.config
new file mode 100644
index 0000000..f24aa57
--- /dev/null
+++ b/SlackUI/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file