diff --git a/.gitignore b/.gitignore index b4e2039..a51fd68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,9 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - # User-specific files *.suo *.user *.userosscache *.sln.docstates -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -20,12 +12,9 @@ bld/ [Oo]bj/ [Ll]og/ -src/bin/ # Visual Studio 2015 cache/options directory .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ @@ -284,3 +273,7 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs + +# Exclude build but include storage dir +src/bin/net8.0-windows/runtimes +src/bin/net8.0-windows/*.* diff --git a/README.md b/README.md index f3501d1..72ab4ab 100644 --- a/README.md +++ b/README.md @@ -1,106 +1,180 @@ ![SharpBrowser](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/logo3.png) -SharpBrowser is the fastest open source C# web browser there is! Slightly faster than Google Chrome when rendering web pages due to lightweight CEF renderer. We compared every available .NET browsing engine and finally settled on the high-performance [CefSharp](https://github.com/cefsharp/CefSharp/). Released under the permissive MIT license. +SharpBrowser is the fastest and most full-featured open source C# web browser there is! Slightly faster than Google Chrome when rendering web pages due to lightweight CEF renderer. We compared every available .NET browsing engine and finally settled on the high-performance [CefSharp](https://github.com/cefsharp/CefSharp/). Released under the permissive MIT license. ## Features -- HTML5, CSS3, JS, HTML5 Video, WebGL 3D, WebAssembly, etc -- Tabbed browsing -- Address bar (also opens Google) -- Back, Forward, Stop, Refresh -- Developer tools +![SharpBrowser](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/topbar.png) + +### Modern + +- [HTML5, CSS3, JS, Video, PDF, WebGL, WebAssembly, WebRTC, WebMIDI](#screenshots) +- Tabbed browsing with website favicons +- Address bar (also opens Google or any search engine) +- Back, Forward, Stop, Refresh, Home, Menu button +- Print and Print to PDF - Search bar (also highlights all instances) -- Download manager +- File downloads and download manager +- View online & offline webpages +- Fullscreen mode + +### Smart + +- [Hotkeys for all features](#hotkeys) +- Saves open tabs and resumes the browsing session when reopened +- Saves cookies and web cache in the `AppData` folder +- Saves application settings in a custom JSON file +- Saves favicons in a disk cache for fast reuse +- Developer tools +- Web app permission handling +- Popups open in new tabs + +### Extensible + +- [Easily add your own branding, styling, buttons or hotkeys](#customization) - Custom error pages - Custom context menu -- Easily add vendor-specific branding, buttons or hotkeys -- View online & offline webpages +- Custom application main menu +- Custom settings +- Custom installer package using InnoSetup ## Hotkeys Hotkeys | Function ------------ | ------------- +Ctrl+MouseWheel | Zoom in/out Ctrl+T | Add a new tab -Ctrl+N | Add a new window Ctrl+W | Close active tab +Ctrl+MiddleClick | Close Tab F5 | Refresh active tab +F11 | Toggle fullscreen F12 | Open developer tools Ctrl+Tab | Switch to the next tab Ctrl+Shift+Tab | Switch to the previous tab Ctrl+F | Open search bar (Enter to find next, Esc to close) +Ctrl+P | Print +Ctrl+Shift+P | Print to PDF -## System requirements - -- You need [VC++ 2019 Runtime](https://aka.ms/vs/17/release/vc_redist.x64.exe) 32-bit and 64-bit versions - -- You need .NET 6. - -- You need to install the version of VC++ Runtime that CEFSharp needs. Since we are using CefSharp 106, according to [this](https://github.com/cefsharp/CefSharp/#release-branches) we need the above versions - - -## Getting started - -- See the [Compilation Guide](docs/Compilation.md) for steps to get started. - ## Documentation -- [User Guide](docs/Users.md) -- [Compilation Guide](docs/Compilation.md) -- [Configuration Guide](docs/Configuration.md) -- [Distribution Guide](docs/Distribution.md) - - -## Code - -- SharpBrowser uses CefSharp 106 and is built on NET 6 -- SharpBrowser supports AnyCPU as well as x86/x64 specific builds -- `MainForm.cs` - main web browser UI and related functionality -- `Handlers` - various handlers that we have registered with CefSharp that enable deeper integration between us and CefSharp -- `Data/JSON.cs` - fast JSON serializer/deserializer -- `bin` - Binaries are included in the `bin` folder due to the complex CefSharp setup required. Don't empty this folder. -- `bin/storage` - HTML and JS required for downloads manager and custom error pages +- See the [Compilation Guide](docs/Compilation.md) for steps to get started. +- See the [Distribution Guide](docs/Distribution.md) to create a custom setup installer. -## Credits -- [Robin Rodricks](https://github.com/robinrodricks) - SharpBrowser project. -- [Alex Maitland](https://github.com/amaitland) - CefSharp project, wrapper for CEF embeddable browser. -- [Ahmet Uzun](https://github.com/postacik) - Original browser project. +### System requirements -## Screenshots +- You need .NET 8 on Windows 64-bit. -### Apple.com +- You need [VC++ 2019 Runtime](https://aka.ms/vs/17/release/vc_redist.x64.exe) (64-bit) +- You might need [VC++ 2017 Runtime](https://www.microsoft.com/en-in/download/details.aspx?id=48145) (64-bit) -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/1.png) +- You need to install the version of VC++ Runtime that CEFSharp needs. As per our CefSharp version, according to [this](https://github.com/cefsharp/CefSharp/#release-branches), we need the above versions -### WebAssembly & WebGL -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/5.png) +### Customization -### YouTube +- To customize the browser branding, name, URL, default search engine, default proxy, modify the `BrowserConfig` class. -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/6.png) +- To customize the application icon, change `sharpbrowser.ico` inside the `Resources` folder. -### Google Maps +- To customize the tab size and tab colors, modify the `BrowserTabStyle` class. -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/2.png) +- To enable or disable Web Camera, Microphone, Javascript, WebGL, WebRTC, WebMIDI, LocalStorage, modify the `BrowserConfig` class. -### Search Bar +- To register hotkeys for your own commands, modify the `HotkeyManager` class. -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/search.png) +- To register your own commands into the main menu, open the form designed for `MainForm` and click the `MainMenu` object. Add `IconMenuItem` objects into that menu. -### Downloads Tab +- To register your own commands into the page context-menu, modify `ContextMenuHandler.OnBeforeContextMenu` function, and then implement the command inside `ContextMenuHandler.OnContextMenuCommand`. -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/3.png) +- To setup how web app permissions are handled, modify `PermissionHandler.OnShowPermissionPrompt` (some flags are already inside `BrowserConfig` and can easily be changed). -### Developer Tools +- To add new settings saved in the JSON file, simply call `ConfigManager.Get*` and `ConfigManager.Set` anywhere in your code. It will save into the file automatically. -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/4.png) -### Custom Error Pages +### Code -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/error1.png) +- SharpBrowser uses CefSharp 134 and is built on NET. +- SharpBrowser only supports Windows x64 platform. +- `MainForm.cs` - main web browser UI and related functionality +- `Managers` - classes that manage various types of browsing functionality, like settings, downloads and hotkeys +- `Handlers` - various handlers that we have registered with CefSharp that enable deeper integration between us and CefSharp +- `bin` - Binaries are included in the `bin` folder due to the complex CefSharp setup required. Don't empty this folder. +- `bin/storage` - HTML and JS required for downloads manager and custom error pages -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/error2.png) +## Screenshots + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Apple.com
+ +
+ Google Maps
+ +
+ WebGL (test)
+ +
+ PDF Viewer
+ +
+ Web Camera (test)
+ +
+ WebMIDI (test)
+ +
+ WebAssembly (test)
+ +
+ YouTube
+ +
+ Search Bar
+ +
+ Main Menu
+ +
+ Downloads Tab
+ +
+ Developer Tools
+ +
+ Custom Error Page 1
+ +
+ Custom Error Page 2
+ +
\ No newline at end of file diff --git a/docs/Configuration.md b/docs/Configuration.md deleted file mode 100644 index 7b458d8..0000000 --- a/docs/Configuration.md +++ /dev/null @@ -1,31 +0,0 @@ -# How to customize SharpBrowser - -Options: -### Branding -The title of the window and application in Windows -### AcceptLanguage -The language you distribute it in -### UserAgent -The browser's user agent string, which shows to websites what browser it is -### HomepageURL -The home page of your browser which shows up when you press the home button -### NewTabURL -The page you will see when you press "New Tab" -### InternalURL -The main URL for internal pages -### DownloadsURL -The URL for the downloads page -### FileNotFoundURL -The File not found error internal url -### CannotConnectURL -The cannot correct error url -### SearchURL -The search string; it must be the string before the search result -Examples: -``` -https://www.google.com/search?q= - -https://www.bing.com/search?q= - -https://duckduckgo.com/?q= -``` \ No newline at end of file diff --git a/images/1.png b/images/1.png deleted file mode 100644 index 8a03b1d..0000000 Binary files a/images/1.png and /dev/null differ diff --git a/images/2.png b/images/2.png deleted file mode 100644 index 1f0f2b3..0000000 Binary files a/images/2.png and /dev/null differ diff --git a/images/3.png b/images/3.png deleted file mode 100644 index 8c2a1ac..0000000 Binary files a/images/3.png and /dev/null differ diff --git a/images/5.png b/images/5.png deleted file mode 100644 index abeb0fd..0000000 Binary files a/images/5.png and /dev/null differ diff --git a/images/6.png b/images/6.png deleted file mode 100644 index 621ad32..0000000 Binary files a/images/6.png and /dev/null differ diff --git a/images/apple.png b/images/apple.png new file mode 100644 index 0000000..fef6d49 Binary files /dev/null and b/images/apple.png differ diff --git a/images/4.png b/images/devtools.png similarity index 100% rename from images/4.png rename to images/devtools.png diff --git a/images/downloads.png b/images/downloads.png new file mode 100644 index 0000000..86e71e4 Binary files /dev/null and b/images/downloads.png differ diff --git a/images/googlemaps.png b/images/googlemaps.png new file mode 100644 index 0000000..bf32143 Binary files /dev/null and b/images/googlemaps.png differ diff --git a/images/mainmenu.png b/images/mainmenu.png new file mode 100644 index 0000000..aae81eb Binary files /dev/null and b/images/mainmenu.png differ diff --git a/images/pdf.png b/images/pdf.png new file mode 100644 index 0000000..67906ed Binary files /dev/null and b/images/pdf.png differ diff --git a/images/topbar.png b/images/topbar.png new file mode 100644 index 0000000..b587ff8 Binary files /dev/null and b/images/topbar.png differ diff --git a/images/wasm.png b/images/wasm.png new file mode 100644 index 0000000..ccb4286 Binary files /dev/null and b/images/wasm.png differ diff --git a/images/webcam.png b/images/webcam.png new file mode 100644 index 0000000..d6969d0 Binary files /dev/null and b/images/webcam.png differ diff --git a/images/webgl.png b/images/webgl.png new file mode 100644 index 0000000..a3099de Binary files /dev/null and b/images/webgl.png differ diff --git a/images/webmidi.png b/images/webmidi.png new file mode 100644 index 0000000..af186c9 Binary files /dev/null and b/images/webmidi.png differ diff --git a/images/youtube.png b/images/youtube.png new file mode 100644 index 0000000..5d71754 Binary files /dev/null and b/images/youtube.png differ diff --git a/setup/SharpBrowserSetup.exe b/setup/SharpBrowserSetup.exe deleted file mode 100644 index dd10222..0000000 Binary files a/setup/SharpBrowserSetup.exe and /dev/null differ diff --git a/src/Browser/BrowserConfig.cs b/src/Browser/BrowserConfig.cs deleted file mode 100644 index e547a12..0000000 --- a/src/Browser/BrowserConfig.cs +++ /dev/null @@ -1,36 +0,0 @@ -using CefSharp; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SharpBrowser.Browser { - internal static class BrowserConfig { - - - public static string Branding = "SharpBrowser"; - public static string AcceptLanguage = "en-US,en;q=0.9"; - public static string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 /CefSharp Browser" + Cef.CefSharpVersion; // UserAgent to fix issue with Google account authentication - public static string HomepageURL = "https://www.google.com"; - public static string NewTabURL = "about:blank"; - public static string InternalURL = "sharpbrowser"; - public static string DownloadsURL = "sharpbrowser://storage/downloads.html"; - public static string FileNotFoundURL = "sharpbrowser://storage/errors/notFound.html"; - public static string CannotConnectURL = "sharpbrowser://storage/errors/cannotConnect.html"; - public static string SearchURL = "https://www.google.com/search?q="; - - public static bool WebSecurity = true; - public static bool CrossDomainSecurity = true; - public static bool WebGL = true; - public static bool ApplicationCache = true; - - public static bool Proxy = false; - public static string ProxyIP = "123.123.123.123"; - public static int ProxyPort = 123; - public static string ProxyUsername = "username"; - public static string ProxyPassword = "pass"; - public static string ProxyBypassList = ""; - - } -} diff --git a/src/Config/BrowserConfig.cs b/src/Config/BrowserConfig.cs new file mode 100644 index 0000000..ca5e6ce --- /dev/null +++ b/src/Config/BrowserConfig.cs @@ -0,0 +1,193 @@ +using CefSharp; +using CefSharp.WinForms; +using System.Net; + +namespace SharpBrowser.Config { + internal static class BrowserConfig { + + /// + /// The title of the window and application in Windows. + /// + public static string Branding = "SharpBrowser"; + /// + /// The folder name in AppData. + /// + public static string AppID = "SharpBrowser"; + /// + /// The language you distribute it in + /// + public static string AcceptLanguage = "en-US,en;q=0.9"; + /// + /// The browser's user agent string, which identifies itself to websites + /// + public static string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 CefSharp/" + Cef.CefSharpVersion; // UserAgent to fix issue with Google account authentication + /// + /// The home page of your browser which shows up when you press the home button + /// + public static string HomepageURL = "https://www.google.com"; + /// + /// The page you will see when you press "New Tab". + /// + public static string NewTabURL = "about:blank"; + /// + /// The main URL prefix for internal pages. + /// + public static string InternalScheme = "sharpbrowser"; + /// + /// The URL for the downloads page + /// + public static string DownloadsURL = InternalScheme + "://storage/downloads.html"; + /// + /// URL to display when local file is not fonud + /// + public static string FileNotFoundURL = InternalScheme + "://storage/errors/notFound.html"; + /// + /// URL to display when internet connection is not available. + /// + public static string CannotConnectURL = InternalScheme + "://storage/errors/cannotConnect.html"; + /// + /// The search string; it must be the string before the search result. + /// Examples: + /// https://www.google.com/search?q= + /// https://www.bing.com/search?q= + /// https://duckduckgo.com/?q= + /// + public static string SearchURL = "https://www.google.com/search?q="; + + + + /// + /// Should we save the tabs that are open and re-open them on the next startup? + /// + public static bool SaveOpenTabs = true; + /// + /// Is WebGL enabled for webpages? + /// + public static bool WebGL = true; + /// + /// Can JS on webpages access WebRTC streams? + /// + public static bool WebRTC = true; + /// + /// Can JS on webpages access MIDI devices using WebMidi API? + /// + public static bool WebMidi = true; + /// + /// Can JS on webpages access Web Cameras? + /// + public static bool Camera = true; + /// + /// Can JS on webpages access Microphone? + /// + public static bool Microphone = true; + /// + /// Is JS enabled for webpages? + /// + public static bool Javascript = true; + /// + /// Can JS on webpages access the clipboard? + /// + public static bool JavascriptClipboard = true; + /// + /// Can webpages access local files? + /// + public static bool LocalFiles = false; + /// + /// Can webpages access local storage API? + /// + public static bool LocalStorage = true; + /// + /// Can users resize text areas on webpages? + /// + public static bool TextAreaResize = true; + + + //3way to set proxy, needs enum to reduce complication. + public static ProxyMode currentProxyMode = ProxyMode.AutoDetect; + //public static bool useSystemProxy = true; + + /// + /// If true then the following proxy is used for all browsing and downloads. + /// + //public static bool customProxy = false; + public static string ProxyIP = "123.123.123.123"; + public static int ProxyPort = 123; + public static string ProxyUsername = "username"; + public static string ProxyPassword = "pass"; + public static string ProxyBypassList = ""; + + public enum ProxyMode + { + /// + /// aka use system proxy + /// + AutoDetect, + CustomProxy, + NoProxy, + } + + /// + /// Load the above config into the CEF `BrowserSettings` object. + /// + public static BrowserSettings GetCefConfig() { + BrowserSettings config = new BrowserSettings(); + + config.TextAreaResize = TextAreaResize.ToCefState(); + config.LocalStorage = LocalStorage.ToCefState(); + config.WebGl = WebGL.ToCefState(); + config.Javascript = Javascript.ToCefState(); + config.JavascriptAccessClipboard = JavascriptClipboard.ToCefState(); + config.JavascriptCloseWindows = CefState.Disabled; + config.JavascriptDomPaste = JavascriptClipboard.ToCefState(); + config.RemoteFonts = CefState.Enabled; + + return config; + } + public static CefState ToCefState(this bool value) { + return value ? CefState.Enabled : CefState.Disabled; + } + + /// + /// Load the above config into the CEF `CefSettings` object. + /// + public static void GetCefSettings(CefSettings settings) { + + // add user agent settings + settings.UserAgent = UserAgent; + settings.AcceptLanguageList = AcceptLanguage; + settings.IgnoreCertificateErrors = true; + + // needed for loading local images + if (LocalFiles) { + settings.CefCommandLineArgs.Add("disable-web-security", "1"); + settings.CefCommandLineArgs.Add("allow-file-access-from-files", "1"); + } + + // enable webRTC streams + if (WebRTC) { + settings.CefCommandLineArgs.Add("enable-media-stream", "1"); + } + + if (currentProxyMode == ProxyMode.AutoDetect) + { + //settings.CefCommandLineArgs.Add("proxy-auto-detect"); // or maybe do nothing. + } + else if (currentProxyMode == ProxyMode.CustomProxy) + { + // enable proxy if wanted + CefSharpSettings.Proxy = new ProxyOptions( + ProxyIP, + ProxyPort.ToString(), + ProxyUsername,ProxyPassword, + ProxyBypassList); + } + else if (currentProxyMode == ProxyMode.NoProxy) + { + + // disable proxy if not wanted + settings.CefCommandLineArgs.Add("no-proxy-server"); + } + } + + } +} diff --git a/src/Config/BrowserTabStyle.cs b/src/Config/BrowserTabStyle.cs new file mode 100644 index 0000000..196cd3b --- /dev/null +++ b/src/Config/BrowserTabStyle.cs @@ -0,0 +1,36 @@ +using System.Drawing; + +namespace SharpBrowser.Config { + internal static class BrowserTabStyle { + + // Tab styles + + public static int TabHeight = 40; + public static int TabLeftPadding = 10; + + public static int TabCloseButton_XOffset = 28; + public static int TabButton_Y = 10; + public static int Tab_IconSize = 16; + + public static Color TabBackColor_Rollover = Color.LightGray; + public static Color TabBackColor_Selected = Color.FromArgb(255, 255, 255); + public static Color TabBackColor_Normal = Color.FromArgb(225, 225, 225); + + public static SolidBrush BackColor = new SolidBrush(TabBackColor_Normal); + + public static Color TabBorderColor = Color.LightGray; + public static float TabBorderThickness = 2; + + // Close tab button (X) + + public static SolidBrush TabCloseButton_TextColor = new SolidBrush(Color.DarkSlateGray); + public static SolidBrush TabCloseButton_RollOverColor = new SolidBrush(Color.LightGray); + + // New tab button (+) + + public static SolidBrush TabNewButton_TextColor = new SolidBrush(Color.DarkSlateGray); + public static SolidBrush TabNewButton_RollOverColor = new SolidBrush(Color.LightGray); + + + } +} diff --git a/src/Controls/BorderedTextBox.cs b/src/Controls/BorderedTextBox.cs new file mode 100644 index 0000000..00d95f1 --- /dev/null +++ b/src/Controls/BorderedTextBox.cs @@ -0,0 +1,141 @@ +using SharpBrowser.Config; +using SharpBrowser.Utils; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Reflection.Metadata; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace SharpBrowser.Controls { + public static class BorderedTextBoxUtils { + + /// + /// Wraps an exisiting textbox with a panel which has roundd borders and a custom backcolor. + /// Returns the panel which wraps the textbox. + /// + public static BorderedTextBox ToBordered(this TextBox tbx) { + + //backup previous TBX status + var tbxOriginalParent = tbx.Parent; + var loc = tbx.Location; + var size = tbx.Size; + var anchor = tbx.Anchor; + var dock = tbx.Dock; + + + //restore them to Panel + var panel = new BorderedTextBox(tbx); + panel.Location = loc; + panel.Size = size; + //bordered_tbx.Height= size.Height+5; + panel.Anchor = anchor; + panel.Dock = dock; + tbxOriginalParent.Controls.Add(panel); + + + panel.BackColor = Color.Transparent; + + //remove tbx min size. it causes tbx getting clipped. + if (tbx.MinimumSize.Height != 0 && tbx.MinimumSize.Height >= panel.MinimumSize.Height) { + tbx.MinimumSize = new Size(); + } + + + return panel; + } + } + + public class BorderedTextBox : Panel { + private TextBox textBox; + private bool focusedAlways = false; + private Color normalBorderColor = Color.LightGray; + private Color focusedBorderColor = Color.FromArgb(153, 187, 239); //edge light blue + public int borderThickness = 2; + + public TextBox TextBox { + get { return textBox; } + //set { textBox = value; } + } + public bool FocusedAlways { + get { return focusedAlways; } + set { focusedAlways = value; } + } + + public BorderedTextBox(TextBox tbx = null) { + this.DoubleBuffered = true; + this.Padding = new Padding(1 + borderThickness * 2); + this.Height += borderThickness * 2; + + if (tbx == null) + textBox = new TextBox(); + else + textBox = tbx; + + this.TextBox.AutoSize = false; + this.TextBox.BorderStyle = BorderStyle.None; + this.TextBox.Dock = DockStyle.Fill; + this.TextBox.Enter += TextBox_Refresh; + this.TextBox.Leave += TextBox_Refresh; + this.TextBox.Resize += TextBox_Refresh; + this.Controls.Add(this.TextBox); + + } + + + + private void TextBox_Refresh(object sender, EventArgs e) => this.Invalidate(); + + protected override void OnPaint(PaintEventArgs e) { + this.Padding = new Padding( + left: 15 + borderThickness, + right: 15 + borderThickness, + top: 1 + borderThickness + 5, + bottom: borderThickness + 5 + ); + + this.AutoSize = false; + var txtHeight = MeasureHeight(textBox); + this.Height = txtHeight + + Padding.Top + Padding.Bottom; + + + var color = this.TextBox.Focused || focusedAlways ? focusedBorderColor : normalBorderColor; + using (Pen borderPen = new Pen(color, borderThickness)) { + var brushbg = new SolidBrush(textBox.BackColor); + + var w = this.ClientSize.Width - borderThickness * 2; + var h = this.ClientSize.Height - borderThickness * 2; + var radius = h / 2; + + e.Graphics.FillRoundRectangle(brushbg, + new Rectangle(0 + borderThickness, 0 + borderThickness,w,h),radius); + + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + e.Graphics.DrawRoundRectangle(borderPen, + new Rectangle(0 + borderThickness, 0 + borderThickness,w,h),radius); + } + base.OnPaint(e); + } + + + static int MeasureHeight(TextBox textbox) { + Size size = TextRenderer.MeasureText("AĞÜüğGgpPa", textbox.Font, Size.Empty, TextFormatFlags.TextBoxControl); + //textbox.MinimumSize = new Size(0, s.Height + 1 + 3); + return size.Height; + + } + + + } + + + +} \ No newline at end of file diff --git a/src/Properties/Resources.resx b/src/Controls/BrowserTabStrip/BaseStyledPanel.resx similarity index 100% rename from src/Properties/Resources.resx rename to src/Controls/BrowserTabStrip/BaseStyledPanel.resx diff --git a/src/Controls/BrowserTabStrip/BrowserTabStripItem.cs b/src/Controls/BrowserTabStrip/BrowserTabPage.cs similarity index 84% rename from src/Controls/BrowserTabStrip/BrowserTabStripItem.cs rename to src/Controls/BrowserTabStrip/BrowserTabPage.cs index 31eed2b..bfe80f1 100644 --- a/src/Controls/BrowserTabStrip/BrowserTabStripItem.cs +++ b/src/Controls/BrowserTabStrip/BrowserTabPage.cs @@ -4,13 +4,17 @@ using System.Windows.Forms; namespace SharpBrowser.Controls.BrowserTabStrip { + + /// + /// This is a panel that holds controls to be displayed in this tab, like the web browser. + /// [ToolboxItem(false)] [DefaultProperty("Title")] [DefaultEvent("Changed")] - public class BrowserTabStripItem : Panel { + public class BrowserTabPage : Panel { private RectangleF stripRect = Rectangle.Empty; - private Image image; + private Bitmap image; private bool canClose = true; @@ -70,12 +74,15 @@ public bool IsDrawn { } [DefaultValue(null)] - public Image Image { + public Bitmap Image { get { return image; } set { - image = value; + if (image != value) { + image = value; + OnChanged(); + } } } @@ -120,15 +127,15 @@ public bool Selected { public event EventHandler Changed; - public BrowserTabStripItem() + public BrowserTabPage() : this(string.Empty, null) { } - public BrowserTabStripItem(Control displayControl) + public BrowserTabPage(Control displayControl) : this(string.Empty, displayControl) { } - public BrowserTabStripItem(string caption, Control displayControl) { + public BrowserTabPage(string caption, Control displayControl) { SetStyle(ControlStyles.OptimizedDoubleBuffer, value: true); SetStyle(ControlStyles.ResizeRedraw, value: true); SetStyle(ControlStyles.UserPaint, value: true); @@ -144,9 +151,11 @@ public BrowserTabStripItem(string caption, Control displayControl) { protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (disposing && image != null) { + + // don't dispose favicons as they are shared across multiple tabs + /*if (disposing && image != null) { image.Dispose(); - } + }*/ } public bool ShouldSerializeIsDrawn() { @@ -180,7 +189,7 @@ private void UpdateText(string caption, Control displayControl) { } } - public void Assign(BrowserTabStripItem item) { + public void Assign(BrowserTabPage item) { Visible = item.Visible; Text = item.Text; CanClose = item.CanClose; diff --git a/src/Controls/BrowserTabStrip/BrowserTabPage.resx b/src/Controls/BrowserTabStrip/BrowserTabPage.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/src/Controls/BrowserTabStrip/BrowserTabPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/BrowserTabStrip.cs b/src/Controls/BrowserTabStrip/BrowserTabStrip.cs index 40a5027..c5c6d5d 100644 --- a/src/Controls/BrowserTabStrip/BrowserTabStrip.cs +++ b/src/Controls/BrowserTabStrip/BrowserTabStrip.cs @@ -1,51 +1,42 @@ +using SharpBrowser.Config; +using SharpBrowser.Controls.BrowserTabStrip.Buttons; +using SharpBrowser.Utils; using System; +using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace SharpBrowser.Controls.BrowserTabStrip { + + /// + /// This is the tab strip that displays tab buttons that are clickable, as well as tab pages. + /// [DefaultEvent("TabStripItemSelectionChanged")] [DefaultProperty("Items")] [ToolboxItem(true)] - public class BrowserTabStrip : BaseStyledPanel, ISupportInitialize, IDisposable { - private const int TEXT_LEFT_MARGIN = 15; - - private const int TEXT_RIGHT_MARGIN = 10; - - private const int DEF_HEADER_HEIGHT = 28; - - private const int DEF_BUTTON_HEIGHT = 28; - - private const int DEF_GLYPH_WIDTH = 40; - - private int DEF_START_POS = 10; + internal class BrowserTabStrip : BaseStyledPanel, ISupportInitialize, IDisposable { - private Rectangle stripButtonRect = Rectangle.Empty; - - private BrowserTabStripItem selectedItem; + public int TabButton_Height => BrowserTabStyle.TabHeight; + private BrowserTabPage selectedItem; private ContextMenuStrip menu; - - private BrowserTabStripCloseButton closeButton; - + private CloseTabButton closeButton; + private NewTabButton newTabButton; private BrowserTabStripItemCollection items; - private StringFormat sf; - - private static Font defaultFont = new Font("Tahoma", 8.25f, FontStyle.Regular); + private StringFormat DrawStringFormat; private bool isIniting; - - private bool menuOpen; - public int MaxTabSize = 200; - public int AddButtonWidth = 40; + private Point LastMousePos; + [RefreshProperties(RefreshProperties.All)] [DefaultValue(null)] - public BrowserTabStripItem SelectedItem { + public BrowserTabPage SelectedTab { get { return selectedItem; } @@ -54,20 +45,23 @@ public BrowserTabStripItem SelectedItem { return; } if (value == null && Items.Count > 0) { - BrowserTabStripItem fATabStripItem = Items[0]; + /*BrowserTabItem fATabStripItem = Items[0]; if (fATabStripItem.Visible) { selectedItem = fATabStripItem; selectedItem.Selected = true; selectedItem.Dock = DockStyle.Fill; - } + }*/ + selectedItem = null; + return; } else { selectedItem = value; } - foreach (BrowserTabStripItem item in Items) { + + foreach (BrowserTabPage item in Items) { if (item == selectedItem) { SelectItem(item); - item.Dock = DockStyle.Fill; + //item.Dock = DockStyle.Fill; item.Show(); } else { @@ -75,12 +69,15 @@ public BrowserTabStripItem SelectedItem { item.Hide(); } } - SelectItem(selectedItem); + Invalidate(); + Refresh(); + + /*SelectItem(selectedItem); Invalidate(); if (!selectedItem.IsDrawn) { Items.MoveTo(0, selectedItem); Invalidate(); - } + }*/ OnTabStripItemChanged(new TabStripItemChangedEventArgs(selectedItem, BrowserTabStripItemChangeTypes.SelectionChanged)); } } @@ -105,14 +102,11 @@ public BrowserTabStripItem SelectedItem { public new ControlCollection Controls => base.Controls; public event TabStripItemClosingHandler TabStripItemClosing; - public event TabStripItemChangedHandler TabStripItemSelectionChanged; - public event HandledEventHandler MenuItemsLoading; - public event EventHandler MenuItemsLoaded; - public event EventHandler TabStripItemClosed; + public event EventHandler TabStripNewTab; public BrowserTabStrip() { BeginInit(); @@ -129,14 +123,17 @@ public BrowserTabStrip() { menu.Renderer = base.ToolStripRenderer; menu.ItemClicked += OnMenuItemClicked; menu.VisibleChanged += OnMenuVisibleChanged; - closeButton = new BrowserTabStripCloseButton(base.ToolStripRenderer); - Font = defaultFont; - sf = new StringFormat(); + closeButton = new CloseTabButton(base.ToolStripRenderer); + newTabButton = new NewTabButton(base.ToolStripRenderer); + DrawStringFormat = new StringFormat(); EndInit(); UpdateLayout(); } public HitTestResult HitTest(Point pt) { + if (newTabButton.IsVisible && newTabButton.Rect.Contains(pt)) { + return HitTestResult.NewButton; + } if (closeButton.IsVisible && closeButton.Rect.Contains(pt)) { return HitTestResult.CloseButton; } @@ -146,40 +143,48 @@ public HitTestResult HitTest(Point pt) { return HitTestResult.None; } - public void AddTab(BrowserTabStripItem tabItem) { - AddTab(tabItem, autoSelect: false); - } + public void AddTab(BrowserTabPage tabItem) => AddTab(tabItem, autoSelect: false); + + public void AddTab(BrowserTabPage tabItem, bool autoSelect) { - public void AddTab(BrowserTabStripItem tabItem, bool autoSelect) { + // add this tab to my collection tabItem.Dock = DockStyle.Fill; Items.Add(tabItem); + + // select the new tab if ((autoSelect && tabItem.Visible) || (tabItem.Visible && Items.DrawnCount < 1)) { - SelectedItem = tabItem; + SelectedTab = tabItem; SelectItem(tabItem); } } - public void RemoveTab(BrowserTabStripItem tabItem) { + public void RemoveTab(BrowserTabPage tabItem) { + + // if tab is found in my collection int num = Items.IndexOf(tabItem); if (num >= 0) { + + // remove the tab UnSelectItem(tabItem); Items.Remove(tabItem); - } - if (Items.Count > 0) { - if (Items[num - 1] != null) { - SelectedItem = Items[num - 1]; - } - else { - SelectedItem = Items.FirstVisible; + + // select last item + if (Items.Count > 0) { + if (Items[num] != null) { + SelectedTab = Items[num]; + } + else { + SelectedTab = Items[items.Count - 1]; + } } } } - public BrowserTabStripItem GetTabItemByPoint(Point pt) { - BrowserTabStripItem result = null; + public BrowserTabPage GetTabItemByPoint(Point pt) { + BrowserTabPage result = null; bool flag = false; for (int i = 0; i < Items.Count; i++) { - BrowserTabStripItem fATabStripItem = Items[i]; + BrowserTabPage fATabStripItem = Items[i]; if (fATabStripItem.StripRect.Contains(pt) && fATabStripItem.Visible && fATabStripItem.IsDrawn) { result = fATabStripItem; flag = true; @@ -191,8 +196,7 @@ public BrowserTabStripItem GetTabItemByPoint(Point pt) { return result; } - public virtual void ShowMenu() { - } + public virtual void ShowMenu() { } internal void UnDrawAll() { for (int i = 0; i < Items.Count; i++) { @@ -200,35 +204,26 @@ internal void UnDrawAll() { } } - internal void SelectItem(BrowserTabStripItem tabItem) { - tabItem.Dock = DockStyle.Fill; + internal void SelectItem(BrowserTabPage tabItem) { + //tabItem.Dock = DockStyle.Fill; tabItem.Visible = true; tabItem.Selected = true; } - internal void UnSelectItem(BrowserTabStripItem tabItem) { - tabItem.Selected = false; - } + internal void UnSelectItem(BrowserTabPage tabItem) => tabItem.Selected = false; - protected internal virtual void OnTabStripItemClosing(TabStripItemClosingEventArgs e) { - if (this.TabStripItemClosing != null) { - this.TabStripItemClosing(e); - } - } + protected internal virtual void OnTabStripItemClosing(TabStripItemClosingEventArgs e) => this.TabStripItemClosing?.Invoke(e); protected internal virtual void OnTabStripItemClosed(EventArgs e) { - selectedItem = null; - if (this.TabStripItemClosed != null) { - this.TabStripItemClosed(this, e); - } + this.TabStripItemClosed?.Invoke(this, e); } - - protected virtual void OnMenuItemsLoading(HandledEventArgs e) { - if (this.MenuItemsLoading != null) { - this.MenuItemsLoading(this, e); - } + protected internal virtual void OnTabStripNewTab(EventArgs e) { + this.TabStripNewTab?.Invoke(this, e); } + + protected virtual void OnMenuItemsLoading(HandledEventArgs e) => this.MenuItemsLoading?.Invoke(this, e); + protected virtual void OnMenuItemsLoaded(EventArgs e) { if (this.MenuItemsLoaded != null) { this.MenuItemsLoaded(this, e); @@ -236,16 +231,14 @@ protected virtual void OnMenuItemsLoaded(EventArgs e) { } protected virtual void OnTabStripItemChanged(TabStripItemChangedEventArgs e) { - if (this.TabStripItemSelectionChanged != null) { - this.TabStripItemSelectionChanged(e); - } + this.TabStripItemSelectionChanged?.Invoke(e); } protected virtual void OnMenuItemsLoad(EventArgs e) { menu.RightToLeft = RightToLeft; menu.Items.Clear(); for (int i = 0; i < Items.Count; i++) { - BrowserTabStripItem fATabStripItem = Items[i]; + BrowserTabPage fATabStripItem = Items[i]; if (fATabStripItem.Visible) { ToolStripMenuItem toolStripMenuItem = new ToolStripMenuItem(fATabStripItem.Title); toolStripMenuItem.Tag = fATabStripItem; @@ -262,93 +255,72 @@ protected override void OnRightToLeftChanged(EventArgs e) { Invalidate(); } - protected override void OnPaint(PaintEventArgs e) { - SetDefaultSelected(); - Rectangle clientRectangle = base.ClientRectangle; - clientRectangle.Width--; - clientRectangle.Height--; - DEF_START_POS = 10; - e.Graphics.DrawRectangle(SystemPens.ControlDark, clientRectangle); - e.Graphics.FillRectangle(Brushes.White, clientRectangle); - e.Graphics.FillRectangle(SystemBrushes.GradientInactiveCaption, new Rectangle(clientRectangle.X, clientRectangle.Y, clientRectangle.Width, 28)); - e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - for (int i = 0; i < Items.Count; i++) { - BrowserTabStripItem fATabStripItem = Items[i]; - if (fATabStripItem.Visible || base.DesignMode) { - OnCalcTabPage(e.Graphics, fATabStripItem); - fATabStripItem.IsDrawn = false; - OnDrawTabButton(e.Graphics, fATabStripItem); - } - } - if (selectedItem != null) { - OnDrawTabButton(e.Graphics, selectedItem); - } - if (Items.DrawnCount == 0 || Items.VisibleCount == 0) { - e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, 28), new Point(base.ClientRectangle.Width, 28)); - } - else if (SelectedItem != null && SelectedItem.IsDrawn) { - int num = (int)(SelectedItem.StripRect.Height / 4f); - Point point = new Point((int)SelectedItem.StripRect.Left - num, 28); - e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, 28), point); - point.X += (int)SelectedItem.StripRect.Width + num * 2; - e.Graphics.DrawLine(SystemPens.ControlDark, point, new Point(base.ClientRectangle.Width, 28)); - } - if (SelectedItem != null && SelectedItem.CanClose) { - closeButton.IsVisible = true; - closeButton.CalcBounds(selectedItem); - closeButton.Draw(e.Graphics); - } - else { - closeButton.IsVisible = false; - } - } - protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); HitTestResult hitTestResult = HitTest(e.Location); + if (hitTestResult == HitTestResult.TabItem) { - BrowserTabStripItem tabItemByPoint = GetTabItemByPoint(e.Location); + + // select the tab if clicked + BrowserTabPage tabItemByPoint = GetTabItemByPoint(e.Location); if (tabItemByPoint != null) { - SelectedItem = tabItemByPoint; + SelectedTab = tabItemByPoint; Invalidate(); } + + // close if middle clicked on a tab + if (e.Button == MouseButtons.Middle) { + CloseActiveTab(); + } + } - else { - if (e.Button != MouseButtons.Left || hitTestResult != 0) { - return; + else if (hitTestResult == HitTestResult.CloseButton) { + if (e.Button == MouseButtons.Left) { + CloseActiveTab(); } - if (SelectedItem != null) { - TabStripItemClosingEventArgs tabStripItemClosingEventArgs = new TabStripItemClosingEventArgs(SelectedItem); - OnTabStripItemClosing(tabStripItemClosingEventArgs); - if (!tabStripItemClosingEventArgs.Cancel && SelectedItem.CanClose) { - RemoveTab(SelectedItem); - OnTabStripItemClosed(EventArgs.Empty); - } + } + else if (hitTestResult == HitTestResult.NewButton) { + if (e.Button == MouseButtons.Left) { + OnTabStripNewTab(EventArgs.Empty); } - Invalidate(); } } - protected override void OnMouseMove(MouseEventArgs e) { - base.OnMouseMove(e); - if (closeButton.IsVisible) { - if (closeButton.Rect.Contains(e.Location)) { - closeButton.IsMouseOver = true; - Invalidate(closeButton.RedrawRect); - } - else if (closeButton.IsMouseOver) { - closeButton.IsMouseOver = false; - Invalidate(closeButton.RedrawRect); + private void CloseActiveTab() { + if (SelectedTab != null) { + TabStripItemClosingEventArgs tabStripItemClosingEventArgs = new TabStripItemClosingEventArgs(SelectedTab); + OnTabStripItemClosing(tabStripItemClosingEventArgs); + if (!tabStripItemClosingEventArgs.Cancel && SelectedTab.CanClose) { + RemoveTab(SelectedTab); + OnTabStripItemClosed(EventArgs.Empty); } } + Invalidate(); } + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); + + LastMousePos = e.Location; + + // manually process events for buttons + closeButton.ProcessRolloverEvents(this, e); + newTabButton.ProcessRolloverEvents(this, e); + + this.Invalidate(); + } + + protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); - closeButton.IsMouseOver = false; - if (closeButton.IsVisible) { - Invalidate(closeButton.RedrawRect); - } + + LastMousePos = Point.Empty; + + // manually process events for buttons + closeButton.ProcessRolloutEvents(this); + newTabButton.ProcessRolloutEvents(this); + + this.Invalidate(); } protected override void OnSizeChanged(EventArgs e) { @@ -358,99 +330,234 @@ protected override void OnSizeChanged(EventArgs e) { } } - private void SetDefaultSelected() { - if (selectedItem == null && Items.Count > 0) { - SelectedItem = Items[0]; - } - for (int i = 0; i < Items.Count; i++) { - BrowserTabStripItem fATabStripItem = Items[i]; - fATabStripItem.Dock = DockStyle.Fill; - } - } - private void OnMenuItemClicked(object sender, ToolStripItemClickedEventArgs e) { - BrowserTabStripItem fATabStripItem2 = (SelectedItem = (BrowserTabStripItem)e.ClickedItem.Tag); + BrowserTabPage fATabStripItem2 = (SelectedTab = (BrowserTabPage)e.ClickedItem.Tag); } private void OnMenuVisibleChanged(object sender, EventArgs e) { if (!menu.Visible) { - menuOpen = false; } } - private void OnCalcTabPage(Graphics g, BrowserTabStripItem currentItem) { - _ = Font; - int num = 0; - if (currentItem.Title == "+") { - num = AddButtonWidth; + /// + /// Main rendering of the entire tab strip. + /// + protected override void OnPaint(PaintEventArgs e) { + //SetDefaultSelected(); + Rectangle clientRectangle = base.ClientRectangle; + clientRectangle.Width--; + clientRectangle.Height--; + TabStartX = BrowserTabStyle.TabLeftPadding; + e.Graphics.DrawRectangle(SystemPens.ControlDark, clientRectangle); + e.Graphics.FillRectangle(Brushes.White, clientRectangle); + e.Graphics.FillRectangle(BrowserTabStyle.BackColor, new Rectangle(clientRectangle.X, clientRectangle.Y, clientRectangle.Width, BrowserTabStyle.TabHeight)); + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + + //-------------------------------------------------------- + // DRAW ALL TABS + for (int i = 0; i < Items.Count; i++) { + BrowserTabPage fATabStripItem = Items[i]; + if (fATabStripItem.Visible || base.DesignMode) { + OnCalcTabPage(e.Graphics, fATabStripItem); + fATabStripItem.IsDrawn = false; + DrawSingleTabButton(e.Graphics, fATabStripItem); + } + } + + //-------------------------------------------------------- + // DRAW BOTTOM LINE on the left/right side of the selected tab + + if (SelectedTab != null && SelectedTab.IsDrawn) { + var lineColorPen = new Pen(BrowserTabStyle.TabBorderColor, BrowserTabStyle.TabBorderThickness); + + Point point = new Point((int)SelectedTab.StripRect.Left - TabRadius, BrowserTabStyle.TabHeight); + e.Graphics.DrawLine(lineColorPen, new Point(0, BrowserTabStyle.TabHeight), point); + point.X += (int)SelectedTab.StripRect.Width + TabRadius + 2; + e.Graphics.DrawLine(lineColorPen, point, new Point(base.ClientRectangle.Width, BrowserTabStyle.TabHeight)); + } + + //-------------------------------------------------------- + // DRAW CLOSE BUTTON FOR SELECTED TAB + if (SelectedTab != null && SelectedTab.CanClose) { + closeButton.IsVisible = true; + closeButton.CalcBounds(selectedItem, true); + closeButton.Draw(e.Graphics); } else { - num = (base.Width - (AddButtonWidth + 20)) / (items.Count - 1); - if (num > MaxTabSize) { - num = MaxTabSize; - } + closeButton.IsVisible = false; } - RectangleF rectangleF2 = (currentItem.StripRect = new RectangleF(DEF_START_POS, 3f, num, 28f)); - DEF_START_POS += num; + + //-------------------------------------------------------- + // DRAW NEW BUTTON + newTabButton.IsVisible = true; + newTabButton.CalcBounds(Items[items.Count - 1], false); + newTabButton.Draw(e.Graphics); + //-------------------------------------------------------- + } - private SizeF MeasureTabWidth(Graphics g, BrowserTabStripItem currentItem, Font currentFont) { - SizeF result = g.MeasureString(currentItem.Title, currentFont, new SizeF(200f, 28f), sf); - result.Width += 25f; - return result; + /// + /// ready for future use, (_ [] X) , if we put Tabs and Close/Minimize buttons on TitleBar + /// + int atRight_ReservedWidth = 250; + private int TabStartX = BrowserTabStyle.TabLeftPadding; + private void OnCalcTabPage(Graphics g, BrowserTabPage currentItem) { + //_ = Font; + int calcWidth = 0; + calcWidth = (base.Width - atRight_ReservedWidth - (AddButtonWidth + 20)) / Math.Max(1, (items.Count - 1)); + if (calcWidth > MaxTabSize) { + calcWidth = MaxTabSize; + } + RectangleF rectangleF2 = (currentItem.StripRect = new RectangleF(TabStartX, 3f, calcWidth, BrowserTabStyle.TabHeight)); + TabStartX += calcWidth; } - private void OnDrawTabButton(Graphics g, BrowserTabStripItem currentItem) { - Items.IndexOf(currentItem); + + + bool styleNotPill = true; + int TabRadius = 8; + + + /// + /// Draws a single tab header button. + /// + private void DrawSingleTabButton(Graphics g, BrowserTabPage tab) { + Items.IndexOf(tab); Font font = Font; - RectangleF stripRect = currentItem.StripRect; - GraphicsPath graphicsPath = new GraphicsPath(); - float left = stripRect.Left; - float right = stripRect.Right; - float num = 3f; - float num2 = stripRect.Bottom - 1f; - float num3 = stripRect.Width; - float num4 = stripRect.Height; - float num5 = num4 / 4f; - graphicsPath.AddLine(left - num5, num2, left + num5, num); - graphicsPath.AddLine(right - num5, num, right + num5, num2); - graphicsPath.CloseFigure(); - SolidBrush brush = new SolidBrush((currentItem == SelectedItem) ? Color.White : SystemColors.GradientInactiveCaption); - g.FillPath(brush, graphicsPath); - g.DrawPath(SystemPens.ControlDark, graphicsPath); - if (currentItem == SelectedItem) { - g.DrawLine(new Pen(brush), left - 9f, num4 + 2f, left + num3 - 1f, num4 + 2f); - } - PointF location = new PointF(left + 15f, 5f); - RectangleF layoutRectangle = stripRect; - layoutRectangle.Location = location; - layoutRectangle.Width = num3 - (layoutRectangle.Left - left) - 4f; - if (currentItem == selectedItem) { - layoutRectangle.Width -= 15f; - } - layoutRectangle.Height = 23f; - if (currentItem == SelectedItem) { - g.DrawString(currentItem.Title, font, new SolidBrush(ForeColor), layoutRectangle, sf); + + bool isActiveTab = tab == SelectedTab; + bool is_atRightof_ActiveTab = Items.IndexOf(tab) == Items.IndexOf(selectedItem) + 1; + + RectangleF stripRect = tab.StripRect; + var sr = stripRect; + + //-------------------------------------------------------- + // Calc Rect for the Tab + //-------------------------------------------------------- + var tabDrawnRect = new RectangleF(sr.Left, 1, sr.Width - 2, sr.Height - 2); + //tabDrawnRect.Height += 2; // hides bottom Line of Rect. + tabDrawnRect.Y += 8; + tabDrawnRect.Height -= 8; + if (styleNotPill) + tabDrawnRect.Height += 2; + //-------------------------------------------------------- + + ////--Draw Rectange Tabs + //g.FillRectangle(brush, tabDrawnRect); + //g.DrawRectangle(SystemPens.ControlDark, tabDrawnRect); + + ////--Draw Pill Style Tabs + //g.FillRoundRectangle(brush, tabDrawnRect,TabRadius); + //g.DrawRoundRectangle(SystemPens.ControlDark, tabDrawnRect, TabRadius); + + //-------------------------------------------------------- + // Style for the tab button + //-------------------------------------------------------- + var tabColor = BrowserTabStyle.TabBackColor_Normal; + var borderColor = BrowserTabStyle.TabBackColor_Normal; + if (tab == SelectedTab) { + tabColor = BrowserTabStyle.TabBackColor_Selected; + borderColor = BrowserTabStyle.TabBorderColor; } - else { - g.DrawString(currentItem.Title, font, new SolidBrush(ForeColor), layoutRectangle, sf); + else if (tabDrawnRect.Contains(LastMousePos)) { + tabColor = BrowserTabStyle.TabBackColor_Rollover; + } + var brush = new SolidBrush(tabColor); + var pen = new Pen(borderColor, BrowserTabStyle.TabBorderThickness); + + //-------------------------------------------------------- + // Rounded Chrome Tabs + //-------------------------------------------------------- + var tabpathNew = + isActiveTab ? + tabDrawnRect.CreateTabPath_Roundtop_RoundBottomOut(TabRadius) : + tabDrawnRect.CreateTabPath_roundAll(TabRadius); + + g.FillPath(brush, tabpathNew); + g.DrawPath(pen, tabpathNew); + // draw tab seperator line: | TAB1 | TAB2 + if (!isActiveTab && !is_atRightof_ActiveTab) { + int margin = 14; + sr.Y += 2; //move rect down + g.DrawLine(SystemPens.ControlDark, sr.X, sr.Y + margin, sr.X, sr.Y + sr.Height - margin); + } + + //g.DrawPath(SystemPens.ControlDark, graphicsPath); + if (tab == SelectedTab) { + //g.DrawLine(new Pen(brush), sr_left + 19f, sr_height + 2f, sr_left + sr_width - 1f, sr_height + 2f); + } + //-------------------------------------------------------- + + var ForeColorSel = ForeColor; + + + //-------------------------------------------------------- + // Tab Text + //-------------------------------------------------------- + + // margin for Tab Text, Vertically Center + var textRect = tabDrawnRect; + textRect.X += 10; + textRect.Width -= 10; + + // leave gap for icon if any + if (tab.Image != null) { + var extraSize = BrowserTabStyle.Tab_IconSize + 5; + textRect.X += extraSize; + textRect.Width -= extraSize; + } + + // draw tab text title + if (tab == SelectedTab) + { + textRect.Width -= 25; } - currentItem.IsDrawn = true; + // FIX: fix janky text rendering and bad kerning by using TextRenderer instead of DrawString + g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + TextRenderer.DrawText(g,tab.Title,font,Rectangle.Round(textRect),ForeColorSel, + TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis + ); + //-------------------------------------------------------- + + + //-------------------------------------------------------- + // Tab Icon + //-------------------------------------------------------- + if (tab.Image != null) { + + // center align the icon in the given space, but right align it to the text + var size = (int)BrowserTabStyle.Tab_IconSize; + var iconX = textRect.X - (5 + size); + var iconY = textRect.Y + ((textRect.Height - size) / 2); + + // draw tab icon + g.InterpolationMode = InterpolationMode.NearestNeighbor; + g.DrawImage(tab.Image, (int)iconX, (int)iconY, size, size); + } + + + tab.IsDrawn = true; } + private void UpdateLayout() { - sf.Trimming = StringTrimming.EllipsisCharacter; - sf.FormatFlags |= StringFormatFlags.NoWrap; - sf.FormatFlags &= StringFormatFlags.DirectionRightToLeft; - stripButtonRect = new Rectangle(0, 0, base.ClientSize.Width - 40 - 2, 10); - base.DockPadding.Top = 29; + //sf.Trimming = StringTrimming.Character; + DrawStringFormat.Trimming = StringTrimming.EllipsisCharacter; + DrawStringFormat.FormatFlags = StringFormatFlags.NoWrap; + //sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft; //this line causes multiline.//is this arabic?? + DrawStringFormat.LineAlignment = StringAlignment.Center; + + DrawStringFormat.Alignment = StringAlignment.Near; + + base.DockPadding.Top = BrowserTabStyle.TabHeight + 1; base.DockPadding.Bottom = 1; base.DockPadding.Right = 1; base.DockPadding.Left = 1; } private void OnCollectionChanged(object sender, CollectionChangeEventArgs e) { - BrowserTabStripItem fATabStripItem = (BrowserTabStripItem)e.Element; + BrowserTabPage fATabStripItem = (BrowserTabPage)e.Element; if (e.Action == CollectionChangeAction.Add) { Controls.Add(fATabStripItem); OnTabStripItemChanged(new TabStripItemChangedEventArgs(fATabStripItem, BrowserTabStripItemChangeTypes.Added)); @@ -466,39 +573,17 @@ private void OnCollectionChanged(object sender, CollectionChangeEventArgs e) { Invalidate(); } - public bool ShouldSerializeFont() { - if (Font != null) { - return !Font.Equals(defaultFont); - } - return false; - } - - public bool ShouldSerializeSelectedItem() { - return true; - } - - public bool ShouldSerializeItems() { - return items.Count > 0; - } - - public new void ResetFont() { - Font = defaultFont; - } - - public void BeginInit() { - isIniting = true; - } - - public void EndInit() { - isIniting = false; - } + public bool ShouldSerializeSelectedItem() => true; + public bool ShouldSerializeItems() => items.Count > 0; + public void BeginInit() => isIniting = true; + public void EndInit() => isIniting = false; protected override void Dispose(bool disposing) { if (disposing) { items.CollectionChanged -= OnCollectionChanged; menu.ItemClicked -= OnMenuItemClicked; menu.VisibleChanged -= OnMenuVisibleChanged; - foreach (BrowserTabStripItem item in items) { + foreach (BrowserTabPage item in items) { if (item != null && !item.IsDisposed) { item.Dispose(); } @@ -506,11 +591,33 @@ protected override void Dispose(bool disposing) { if (menu != null && !menu.IsDisposed) { menu.Dispose(); } - if (sf != null) { - sf.Dispose(); + if (DrawStringFormat != null) { + DrawStringFormat.Dispose(); } } base.Dispose(disposing); } + + public List Tabs { + get { + var tabs = new List(); + foreach (BrowserTabPage item in items) { + tabs.Add(item); + } + return tabs; + } + } + + public int SelectedIndex { + get { + return Items.IndexOf(SelectedTab); + } + set { + if (Items[value] != null) { + SelectedTab = Items[value]; + } + } + } + } } \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/BrowserTabStrip.resx b/src/Controls/BrowserTabStrip/BrowserTabStrip.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/src/Controls/BrowserTabStrip/BrowserTabStrip.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/BrowserTabStripCloseButton.cs b/src/Controls/BrowserTabStrip/BrowserTabStripCloseButton.cs deleted file mode 100644 index aa130ec..0000000 --- a/src/Controls/BrowserTabStrip/BrowserTabStripCloseButton.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Drawing; -using System.Windows.Forms; - -namespace SharpBrowser.Controls.BrowserTabStrip { - internal class BrowserTabStripCloseButton { - public Rectangle Rect = Rectangle.Empty; - - public Rectangle RedrawRect = Rectangle.Empty; - - public bool IsMouseOver; - - public bool IsVisible; - - public ToolStripProfessionalRenderer Renderer; - - internal BrowserTabStripCloseButton(ToolStripProfessionalRenderer renderer) { - Renderer = renderer; - } - - public void CalcBounds(BrowserTabStripItem tab) { - Rect = new Rectangle((int)tab.StripRect.Right - 20, (int)tab.StripRect.Top + 5, 15, 15); - RedrawRect = new Rectangle(Rect.X - 2, Rect.Y - 2, Rect.Width + 4, Rect.Height + 4); - } - - public void Draw(Graphics g) { - if (IsVisible) { - Color color = (IsMouseOver ? Color.White : Color.DarkGray); - g.FillRectangle(Brushes.White, Rect); - if (IsMouseOver) { - g.FillEllipse(Brushes.IndianRed, Rect); - } - int num = 4; - Pen pen = new Pen(color, 1.6f); - g.DrawLine(pen, Rect.Left + num, Rect.Top + num, Rect.Right - num, Rect.Bottom - num); - g.DrawLine(pen, Rect.Right - num, Rect.Top + num, Rect.Left + num, Rect.Bottom - num); - pen.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/Buttons/CloseTabButton.cs b/src/Controls/BrowserTabStrip/Buttons/CloseTabButton.cs new file mode 100644 index 0000000..59c0c90 --- /dev/null +++ b/src/Controls/BrowserTabStrip/Buttons/CloseTabButton.cs @@ -0,0 +1,24 @@ +using SharpBrowser.Config; +using SharpBrowser.Utils; +using System.Drawing; +using System.Windows.Forms; + +namespace SharpBrowser.Controls.BrowserTabStrip.Buttons { + internal class CloseTabButton(ToolStripProfessionalRenderer renderer) : TabButtonBase(renderer) { + + public override void Draw(Graphics g) { + if (IsVisible) { + //g.FillRectangle(Brushes.White, Rect); + if (IsMouseOver) { + g.FillRoundRectangle(BrowserTabStyle.TabCloseButton_RollOverColor, Rect, 10); + } + int padding = 6; + Pen pen = new Pen(BrowserTabStyle.TabCloseButton_TextColor, 1.6f); + g.DrawLine(pen, Rect.Left + padding, Rect.Top + padding, Rect.Right - padding, Rect.Bottom - padding); + g.DrawLine(pen, Rect.Right - padding, Rect.Top + padding, Rect.Left + padding, Rect.Bottom - padding); + pen.Dispose(); + } + } + + } +} \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/Buttons/NewTabButton.cs b/src/Controls/BrowserTabStrip/Buttons/NewTabButton.cs new file mode 100644 index 0000000..d37d7e8 --- /dev/null +++ b/src/Controls/BrowserTabStrip/Buttons/NewTabButton.cs @@ -0,0 +1,26 @@ +using SharpBrowser.Config; +using SharpBrowser.Utils; +using System.Drawing; +using System.Windows.Forms; + +namespace SharpBrowser.Controls.BrowserTabStrip.Buttons { + internal class NewTabButton(ToolStripProfessionalRenderer renderer) : TabButtonBase(renderer) { + + public override void Draw(Graphics g) { + if (IsVisible) { + //g.FillRectangle(Brushes.White, Rect); + if (IsMouseOver) { + g.FillRoundRectangle(BrowserTabStyle.TabNewButton_RollOverColor, Rect, 10); + } + int num = 4; + int centerX = Rect.X + Rect.Width / 2; + int centerY = Rect.Y + Rect.Height / 2; + Pen pen = new Pen(BrowserTabStyle.TabNewButton_TextColor, 1.6f); + g.DrawLine(pen, centerX, Rect.Top + num, centerX, Rect.Bottom - num); + g.DrawLine(pen, Rect.Right - num, centerY, Rect.Left + num, centerY); + pen.Dispose(); + } + } + + } +} \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/Buttons/TabButtonBase.cs b/src/Controls/BrowserTabStrip/Buttons/TabButtonBase.cs new file mode 100644 index 0000000..ec0be13 --- /dev/null +++ b/src/Controls/BrowserTabStrip/Buttons/TabButtonBase.cs @@ -0,0 +1,44 @@ +using SharpBrowser.Config; +using System.Drawing; +using System.Windows.Forms; + +namespace SharpBrowser.Controls.BrowserTabStrip.Buttons { + internal abstract class TabButtonBase { + + public Rectangle Rect = Rectangle.Empty; + public Rectangle RedrawRect = Rectangle.Empty; + public bool IsMouseOver; + public bool IsVisible; + public ToolStripProfessionalRenderer Renderer; + + internal TabButtonBase(ToolStripProfessionalRenderer renderer) { + Renderer = renderer; + } + + public void CalcBounds(BrowserTabPage tab, bool displayInButton) { + var tabrect = tab.StripRect; + var x = displayInButton ? (int)tab.StripRect.Right - BrowserTabStyle.TabCloseButton_XOffset : (int)tab.StripRect.Right + 10; + var y = (int)tab.StripRect.Top + BrowserTabStyle.TabButton_Y; + Rect = new Rectangle(x, y, 20, 20); + RedrawRect = new Rectangle(Rect.X - 2, Rect.Y - 2, Rect.Width + 4, Rect.Height + 4); + } + + public virtual void Draw(Graphics g) { + } + + public void ProcessRolloverEvents(Control parent, MouseEventArgs e) { + if (IsVisible) { + if (Rect.Contains(e.Location)) { + IsMouseOver = true; + } + else if (IsMouseOver) { + IsMouseOver = false; + } + } + } + public void ProcessRolloutEvents(Control parent) { + IsMouseOver = false; + } + + } +} diff --git a/src/Controls/BrowserTabStrip/Data/BrowserTabStripItemCollection.cs b/src/Controls/BrowserTabStrip/Data/BrowserTabStripItemCollection.cs index 9ae50a6..e1a1a5e 100644 --- a/src/Controls/BrowserTabStrip/Data/BrowserTabStripItemCollection.cs +++ b/src/Controls/BrowserTabStrip/Data/BrowserTabStripItemCollection.cs @@ -5,12 +5,12 @@ namespace SharpBrowser.Controls.BrowserTabStrip { public class BrowserTabStripItemCollection : CollectionWithEvents { private int lockUpdate; - public BrowserTabStripItem this[int index] { + public BrowserTabPage this[int index] { get { if (index < 0 || base.List.Count - 1 < index) { return null; } - return (BrowserTabStripItem)base.List[index]; + return (BrowserTabPage)base.List[index]; } set { base.List[index] = value; @@ -34,28 +34,6 @@ public virtual int DrawnCount { } } - public virtual BrowserTabStripItem LastVisible { - get { - for (int num = base.Count - 1; num > 0; num--) { - if (this[num].Visible) { - return this[num]; - } - } - return null; - } - } - - public virtual BrowserTabStripItem FirstVisible { - get { - for (int i = 0; i < base.Count; i++) { - if (this[i].Visible) { - return this[i]; - } - } - return null; - } - } - [Browsable(false)] public virtual int VisibleCount { get { @@ -96,10 +74,10 @@ protected virtual void EndUpdate() { } } - public virtual void AddRange(BrowserTabStripItem[] items) { + public virtual void AddRange(BrowserTabPage[] items) { BeginUpdate(); try { - foreach (BrowserTabStripItem value in items) { + foreach (BrowserTabPage value in items) { base.List.Add(value); } } @@ -113,8 +91,8 @@ public virtual void Assign(BrowserTabStripItemCollection collection) { try { Clear(); for (int i = 0; i < collection.Count; i++) { - BrowserTabStripItem item = collection[i]; - BrowserTabStripItem fATabStripItem = new BrowserTabStripItem(); + BrowserTabPage item = collection[i]; + BrowserTabPage fATabStripItem = new BrowserTabPage(); fATabStripItem.Assign(item); Add(fATabStripItem); } @@ -124,7 +102,7 @@ public virtual void Assign(BrowserTabStripItemCollection collection) { } } - public virtual int Add(BrowserTabStripItem item) { + public virtual int Add(BrowserTabPage item) { int num = IndexOf(item); if (num == -1) { num = base.List.Add(item); @@ -132,13 +110,13 @@ public virtual int Add(BrowserTabStripItem item) { return num; } - public virtual void Remove(BrowserTabStripItem item) { + public virtual void Remove(BrowserTabPage item) { if (base.List.Contains(item)) { base.List.Remove(item); } } - public virtual BrowserTabStripItem MoveTo(int newIndex, BrowserTabStripItem item) { + public virtual BrowserTabPage MoveTo(int newIndex, BrowserTabPage item) { int num = base.List.IndexOf(item); if (num >= 0) { RemoveAt(num); @@ -148,29 +126,29 @@ public virtual BrowserTabStripItem MoveTo(int newIndex, BrowserTabStripItem item return null; } - public virtual int IndexOf(BrowserTabStripItem item) { + public virtual int IndexOf(BrowserTabPage item) { return base.List.IndexOf(item); } - public virtual bool Contains(BrowserTabStripItem item) { + public virtual bool Contains(BrowserTabPage item) { return base.List.Contains(item); } - public virtual void Insert(int index, BrowserTabStripItem item) { + public virtual void Insert(int index, BrowserTabPage item) { if (!Contains(item)) { base.List.Insert(index, item); } } protected override void OnInsertComplete(int index, object item) { - BrowserTabStripItem fATabStripItem = item as BrowserTabStripItem; + BrowserTabPage fATabStripItem = item as BrowserTabPage; fATabStripItem.Changed += OnItem_Changed; OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, item)); } protected override void OnRemove(int index, object item) { base.OnRemove(index, item); - BrowserTabStripItem fATabStripItem = item as BrowserTabStripItem; + BrowserTabPage fATabStripItem = item as BrowserTabPage; fATabStripItem.Changed -= OnItem_Changed; OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, item)); } diff --git a/src/Controls/BrowserTabStrip/Enums/HitTestResult.cs b/src/Controls/BrowserTabStrip/Enums/HitTestResult.cs index 77f7070..a0f0799 100644 --- a/src/Controls/BrowserTabStrip/Enums/HitTestResult.cs +++ b/src/Controls/BrowserTabStrip/Enums/HitTestResult.cs @@ -2,6 +2,7 @@ namespace SharpBrowser.Controls.BrowserTabStrip { public enum HitTestResult { CloseButton, TabItem, + NewButton, None } } \ No newline at end of file diff --git a/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemChangedEventArgs.cs b/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemChangedEventArgs.cs index 3b91df9..163f7ea 100644 --- a/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemChangedEventArgs.cs +++ b/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemChangedEventArgs.cs @@ -2,15 +2,15 @@ namespace SharpBrowser.Controls.BrowserTabStrip { public class TabStripItemChangedEventArgs : EventArgs { - private BrowserTabStripItem itm; + private BrowserTabPage itm; private BrowserTabStripItemChangeTypes changeType; public BrowserTabStripItemChangeTypes ChangeType => changeType; - public BrowserTabStripItem Item => itm; + public BrowserTabPage Item => itm; - public TabStripItemChangedEventArgs(BrowserTabStripItem item, BrowserTabStripItemChangeTypes type) { + public TabStripItemChangedEventArgs(BrowserTabPage item, BrowserTabStripItemChangeTypes type) { changeType = type; itm = item; } diff --git a/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemClosingEventArgs.cs b/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemClosingEventArgs.cs index cd60a35..e33bb4a 100644 --- a/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemClosingEventArgs.cs +++ b/src/Controls/BrowserTabStrip/Events/BrowserTabStripItemClosingEventArgs.cs @@ -4,9 +4,9 @@ namespace SharpBrowser.Controls.BrowserTabStrip { public class TabStripItemClosingEventArgs : EventArgs { private bool _cancel; - private BrowserTabStripItem _item; + private BrowserTabPage _item; - public BrowserTabStripItem Item { + public BrowserTabPage Item { get { return _item; } @@ -24,7 +24,7 @@ public bool Cancel { } } - public TabStripItemClosingEventArgs(BrowserTabStripItem item) { + public TabStripItemClosingEventArgs(BrowserTabPage item) { _item = item; } } diff --git a/src/Controls/CircularDownloadProgress.cs b/src/Controls/CircularDownloadProgress.cs new file mode 100644 index 0000000..b118115 --- /dev/null +++ b/src/Controls/CircularDownloadProgress.cs @@ -0,0 +1,144 @@ +using SharpBrowser.Managers; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar; + +namespace SharpBrowser.Controls +{ + + /// + /// Draws Download Progress as Circle to Given Button.. + /// + public class CircularDownloadProgress + { + + public CircularDownloadProgress(Button btnDL) + { + + init_downloads_indicator(btnDL); + } + //------ draw Circular downloading Progress + + Button _btnDL; + public void init_downloads_indicator(Button btnDL) + { + _btnDL = btnDL; + btnDL.Paint += btnDL_Paint; + var tmr_downloader = new System.Windows.Forms.Timer(); + tmr_downloader.Interval = 500; + tmr_downloader.Tick += Tmr_downloader_Tick; + tmr_downloader.Start(); + } + + int testdl_pct = 0; + private void Tmr_downloader_Tick(object sender, EventArgs e) + { + + bool isDownloading=false; + bool needs_lastRefresh=false; + try + { + isDownloading = DownloadManager.DownloadsInProgress(); + if (!isDownloading) + { + var curdlitemKV_val = DownloadManager.Downloads + .Where(x => DateTime.Now.Subtract( x.Value.EndTime ?? DateTime.MinValue).TotalSeconds <3 ) + .Select(x=>x.Value) + .FirstOrDefault(); + //var curdlitem = curdlitemKV.Value; + if(curdlitemKV_val != null) + needs_lastRefresh = true; + } + } + catch (Exception ex) { } + + if (isDownloading || needs_lastRefresh) + _btnDL.Refresh(); + + //BtnDownloads.Invalidate(); + + + //testdl_pct = testdl_pct + 10; + //if (testdl_pct > 100) + // testdl_pct = 0; + } + + private void btnDL_Paint(object sender, PaintEventArgs e) + { + try + { + var isDownloading = DownloadManager.DownloadsInProgress(); + float pct_ofRecentDownloadingItem = 0; + if (isDownloading) + { + var curdlitemKV = DownloadManager.Downloads.Where(x => x.Value.IsInProgress).FirstOrDefault(); + var curdlitem = curdlitemKV.Value; + pct_ofRecentDownloadingItem = (int)(curdlitem.ReceivedBytes * 100.0f / curdlitem.TotalBytes); + } + + //var isDownloading = true; + if (isDownloading) + { + //var pct1 = 100; // 100 %; + //var pct2 = 50; // 50 %; + //var pct3 =25; // 20 %; + var pct = testdl_pct; //test val; // <<<<---- input download percentage HERE;; + pct = (int)pct_ofRecentDownloadingItem; + + var pctAs360val = pct / 100.0f * 360; + var arc_StartOffset = 90; + + //Color activeColorORG = Color.FromArgb(11, 87, 208); + //Color activeLightColor = Color.FromArgb(76, 194, 255); + Color activeColor = Color.FromArgb(27, 117, 208); + int gray = 200; + var myGray = Color.FromArgb(gray, gray, gray); + + //var loc = BtnDownloads.Location; + //var sz = BtnDownloads.Size; + var loc = new Point(0, 0); + var sz = _btnDL.ClientRectangle; + var pad = 0; + //var btng = BtnDownloads.CreateGraphics(); + + var thickness = 4; + + var btng = e.Graphics; + //var btng = BtnDownloads.CreateGraphics(); + btng.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; + + + var szHW_min = Math.Min(sz.Width, sz.Height); + var szHW_max_minus_min = Math.Abs(sz.Width - sz.Height) / 2; //aka center it. vertical. + Rectangle rect = new Rectangle( + loc.X + pad + thickness / 2, + loc.Y + pad + thickness / 2, + szHW_min - 1 * pad - thickness / 2 * 2 - 1, + szHW_min - 1 * pad - thickness / 2 * 2 - 1 + ); + + rect.Offset(szHW_max_minus_min, 0); + //sz.Width - 1 * pad - thickness / 2 * 2 - 1, + //sz.Height - 1 * pad - thickness / 2 * 2 - 1 + //); + //btng.FillRectangle(new Pen(new SolidBrush(Color.Black), 10).Brush, rect); + btng.DrawArc(new Pen(new SolidBrush(myGray), thickness), rect, 0 + arc_StartOffset, 360); + btng.DrawArc(new Pen(new SolidBrush(activeColor), thickness), rect, 0 + arc_StartOffset, pctAs360val); + + } + } + catch (Exception ex) + { + Console.WriteLine(ex + "ERROR at btnDL_Paint:"); + } + } + + + + } +} diff --git a/src/Data/JSON.cs b/src/Data/JSON.cs deleted file mode 100644 index 4ee7464..0000000 --- a/src/Data/JSON.cs +++ /dev/null @@ -1,1997 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Data; -using System.Globalization; -using System.IO; -using System.Text; -using System.Reflection.Emit; -using System.Reflection; -using System.Data; -using System.Xml; - - -#region JSON - -public delegate string Serialize(object data); -public delegate object Deserialize(string data); - -public class JSONParameters -{ - /// - /// Use the optimized fast Dataset Schema format (dfault = True) - /// - public bool UseOptimizedDatasetSchema = true; - /// - /// Use the fast GUID format (default = True) - /// - public bool UseFastGuid = true; - /// - /// Serialize null values to the output (default = True) - /// - public bool SerializeNullValues = true; - /// - /// Use the UTC date format (default = True) - /// - public bool UseUTCDateTime = true; - /// - /// Show the readonly properties of types in the output (default = False) - /// - public bool ShowReadOnlyProperties = false; - /// - /// Use the $types extension to optimise the output json (default = True) - /// - public bool UsingGlobalTypes = true; - /// - /// - /// - public bool IgnoreCaseOnDeserialize = false; - /// - /// Anonymous types have read only properties - /// - public bool EnableAnonymousTypes = false; - /// - /// Enable fastJSON extensions $types, $type, $map (default = True) - /// - public bool UseExtensions = true; -} - -public class JSON -{ - public readonly static JSON Instance = new JSON(); - - private JSON() - { - } - /// - /// You can set these paramters globally for all calls - /// - public JSONParameters Parameters = new JSONParameters(); - private JSONParameters _params; - // FIX : extensions off should not output $types - public string ToJSON(object obj) - { - _params = Parameters; - Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; - return ToJSON(obj, Parameters); - } - - public string ToJSON(object obj, JSONParameters param) - { - _params = param; - Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; - // FEATURE : enable extensions when you can deserialize anon types - if (_params.EnableAnonymousTypes) { _params.UseExtensions = false; _params.UsingGlobalTypes = false; } - return new JSONSerializer(param).ConvertToJSON(obj); - } - - public object Parse(string json) - { - _params = Parameters; - Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; - return new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode(); - } - - public T ToObject(string json) - { - return (T)ToObject(json, typeof(T)); - } - - public object ToObject(string json) - { - return ToObject(json, null); - } - - public object ToObject(string json, Type type) - { - _params = Parameters; - Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; - Dictionary ht = new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode() as Dictionary; - if (ht == null) return null; - return ParseDictionary(ht, null, type, null); - } - - public string Beautify(string input) - { - return Formatter.PrettyPrint(input); - } - - public object FillObject(object input, string json) - { - _params = Parameters; - Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; - Dictionary ht = new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode() as Dictionary; - if (ht == null) return null; - return ParseDictionary(ht, null, input.GetType(), input); - } - - public object DeepCopy(object obj) - { - return ToObject(ToJSON(obj)); - } - - public T DeepCopy(T obj) - { - return ToObject(ToJSON(obj)); - } - - #region [ JSON specific reflection ] - - private struct myPropInfo - { - public bool filled; - public Type pt; - public Type bt; - public Type changeType; - public bool isDictionary; - public bool isValueType; - public bool isGenericType; - public bool isArray; - public bool isByteArray; - public bool isGuid; - public bool isDataSet; - public bool isDataTable; - public bool isHashtable; - public Reflection.GenericSetter setter; - public bool isEnum; - public bool isDateTime; - public Type[] GenericTypes; - public bool isInt; - public bool isLong; - public bool isString; - public bool isBool; - public bool isClass; - public Reflection.GenericGetter getter; - public bool isStringDictionary; - public string Name; - public bool CanWrite; - } - - SafeDictionary> _propertycache = new SafeDictionary>(); - private SafeDictionary Getproperties(Type type, string typename) - { - SafeDictionary sd = null; - if (_propertycache.TryGetValue(typename, out sd)) - { - return sd; - } - else - { - sd = new SafeDictionary(); - PropertyInfo[] pr = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (PropertyInfo p in pr) - { - myPropInfo d = CreateMyProp(p.PropertyType, p.Name); - d.CanWrite = p.CanWrite; - d.setter = Reflection.CreateSetMethod(type, p); - d.getter = Reflection.CreateGetMethod(type, p); - sd.Add(p.Name, d); - } - FieldInfo[] fi = type.GetFields(BindingFlags.Public | BindingFlags.Instance); - foreach (FieldInfo f in fi) - { - myPropInfo d = CreateMyProp(f.FieldType, f.Name); - d.setter = Reflection.CreateSetField(type, f); - d.getter = Reflection.CreateGetField(type, f); - sd.Add(f.Name, d); - } - - _propertycache.Add(typename, sd); - return sd; - } - } - - private myPropInfo CreateMyProp(Type t, string name) - { - myPropInfo d = new myPropInfo(); - d.filled = true; - d.CanWrite = true; - d.pt = t; - d.Name = name; - d.isDictionary = t.Name.Contains("Dictionary"); - if (d.isDictionary) - d.GenericTypes = t.GetGenericArguments(); - d.isValueType = t.IsValueType; - d.isGenericType = t.IsGenericType; - d.isArray = t.IsArray; - if (d.isArray) - d.bt = t.GetElementType(); - if (d.isGenericType) - d.bt = t.GetGenericArguments()[0]; - d.isByteArray = t == typeof(byte[]); - d.isGuid = (t == typeof(Guid) || t == typeof(Guid?)); - d.isHashtable = t == typeof(Hashtable); - d.isDataSet = t == typeof(DataSet); - d.isDataTable = t == typeof(DataTable); - - d.changeType = GetChangeType(t); - d.isEnum = t.IsEnum; - d.isDateTime = t == typeof(DateTime) || t == typeof(DateTime?); - d.isInt = t == typeof(int) || t == typeof(int?); - d.isLong = t == typeof(long) || t == typeof(long?); - d.isString = t == typeof(string); - d.isBool = t == typeof(bool) || t == typeof(bool?); - d.isClass = t.IsClass; - - if (d.isDictionary && d.GenericTypes.Length > 0 && d.GenericTypes[0] == typeof(string)) - d.isStringDictionary = true; - - return d; - } - - private object ChangeType(object value, Type conversionType) - { - if (conversionType == typeof(int)) - return (int)CreateLong((string)value); - - else if (conversionType == typeof(long)) - return CreateLong((string)value); - - else if (conversionType == typeof(string)) - return (string)value; - - else if (conversionType == typeof(Guid)) - return CreateGuid((string)value); - - else if (conversionType.IsEnum) - return CreateEnum(conversionType, (string)value); - - return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture); - } - #endregion - - #region [ p r i v a t e m e t h o d s ] - bool usingglobals = false; - private object ParseDictionary(Dictionary d, Dictionary globaltypes, Type type, object input) - { - object tn = ""; - - if (d.TryGetValue("$types", out tn)) - { - usingglobals = true; - globaltypes = new Dictionary(); - foreach (var kv in (Dictionary)tn) - { - globaltypes.Add((string)kv.Value, kv.Key); - } - } - - bool found = d.TryGetValue("$type", out tn); - if (found == false && type == typeof(System.Object)) - { - return CreateDataset(d, globaltypes); - } - - if (found) - { - if (usingglobals) - { - object tname = ""; - if (globaltypes.TryGetValue((string)tn, out tname)) - tn = tname; - } - type = Reflection.Instance.GetTypeFromCache((string)tn); - } - - if (type == null) - throw new Exception("Cannot determine type"); - - string typename = type.FullName; - object o = input; - if (o == null) - o = Reflection.Instance.FastCreateInstance(type); - SafeDictionary props = Getproperties(type, typename); - foreach (string n in d.Keys) - { - string name = n; - if (_params.IgnoreCaseOnDeserialize) name = name.ToLower(); - if (name == "$map") - { - ProcessMap(o, props, (Dictionary)d[name]); - continue; - } - myPropInfo pi; - if (props.TryGetValue(name, out pi) == false) - continue; - if (pi.filled == true) - { - object v = d[name]; - - if (v != null) - { - object oset = null; - - if (pi.isInt) - oset = (int)CreateLong((string)v); - - else if (pi.isLong) - oset = CreateLong((string)v); - - else if (pi.isString) - oset = (string)v; - - else if (pi.isBool) - oset = (bool)v; - - else if (pi.isGenericType && pi.isValueType == false && pi.isDictionary == false) - oset = CreateGenericList((ArrayList)v, pi.pt, pi.bt, globaltypes); - - else if (pi.isByteArray) - oset = Convert.FromBase64String((string)v); - - else if (pi.isArray && pi.isValueType == false) - oset = CreateArray((ArrayList)v, pi.pt, pi.bt, globaltypes); - - else if (pi.isGuid) - oset = CreateGuid((string)v); - - else if (pi.isDataSet) - oset = CreateDataset((Dictionary)v, globaltypes); - - else if (pi.isDataTable) - oset = this.CreateDataTable((Dictionary)v, globaltypes); - - else if (pi.isStringDictionary) - oset = CreateStringKeyDictionary((Dictionary)v, pi.pt, pi.GenericTypes, globaltypes); - - else if (pi.isDictionary || pi.isHashtable) - oset = CreateDictionary((ArrayList)v, pi.pt, pi.GenericTypes, globaltypes); - - else if (pi.isEnum) - oset = CreateEnum(pi.pt, (string)v); - - else if (pi.isDateTime) - oset = CreateDateTime((string)v); - - else if (pi.isClass && v is Dictionary) - oset = ParseDictionary((Dictionary)v, globaltypes, pi.pt, null); - - else if (pi.isValueType) - oset = ChangeType(v, pi.changeType); - - else if (v is ArrayList) - oset = CreateArray((ArrayList)v, pi.pt, typeof(object), globaltypes); - else - oset = v; - - if (pi.CanWrite) - o = pi.setter(o, oset); - } - } - } - return o; - } - - private void ProcessMap(object obj, SafeDictionary props, Dictionary dic) - { - foreach (KeyValuePair kv in dic) - { - myPropInfo p = props[kv.Key]; - object o = p.getter(obj); - Type t = Type.GetType((string)kv.Value); - if (t == typeof(Guid)) - p.setter(obj, CreateGuid((string)o)); - } - } - - private long CreateLong(string s) - { - long num = 0; - bool neg = false; - foreach (char cc in s) - { - if (cc == '-') - neg = true; - else if (cc == '+') - neg = false; - else - { - num *= 10; - num += (int)(cc - '0'); - } - } - - return neg ? -num : num; - } - - private object CreateEnum(Type pt, string v) - { - // TODO : optimize create enum - return Enum.Parse(pt, v); - } - - private Guid CreateGuid(string s) - { - if (s.Length > 30) - return new Guid(s); - else - return new Guid(Convert.FromBase64String(s)); - } - - private DateTime CreateDateTime(string value) - { - bool utc = false; - // 0123456789012345678 - // datetime format = yyyy-MM-dd HH:mm:ss - int year = (int)CreateLong(value.Substring(0, 4)); - int month = (int)CreateLong(value.Substring(5, 2)); - int day = (int)CreateLong(value.Substring(8, 2)); - int hour = (int)CreateLong(value.Substring(11, 2)); - int min = (int)CreateLong(value.Substring(14, 2)); - int sec = (int)CreateLong(value.Substring(17, 2)); - - if (value.EndsWith("Z")) - utc = true; - - if (_params.UseUTCDateTime == false && utc == false) - return new DateTime(year, month, day, hour, min, sec); - else - return new DateTime(year, month, day, hour, min, sec, DateTimeKind.Utc).ToLocalTime(); - } - - private object CreateArray(ArrayList data, Type pt, Type bt, Dictionary globalTypes) - { - ArrayList col = new ArrayList(); - // create an array of objects - foreach (object ob in data) - { - if (ob is IDictionary) - col.Add(ParseDictionary((Dictionary)ob, globalTypes, bt, null)); - else - col.Add(ChangeType(ob, bt)); - } - return col.ToArray(bt); - } - - - private object CreateGenericList(ArrayList data, Type pt, Type bt, Dictionary globalTypes) - { - IList col = (IList)Reflection.Instance.FastCreateInstance(pt); - // create an array of objects - foreach (object ob in data) - { - if (ob is IDictionary) - col.Add(ParseDictionary((Dictionary)ob, globalTypes, bt, null)); - else if (ob is ArrayList) - col.Add(((ArrayList)ob).ToArray()); - else - col.Add(ChangeType(ob, bt)); - } - return col; - } - - private object CreateStringKeyDictionary(Dictionary reader, Type pt, Type[] types, Dictionary globalTypes) - { - var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt); - Type t1 = null; - Type t2 = null; - if (types != null) - { - t1 = types[0]; - t2 = types[1]; - } - - foreach (KeyValuePair values in reader) - { - var key = values.Key;//ChangeType(values.Key, t1); - object val = null; - if (values.Value is Dictionary) - val = ParseDictionary((Dictionary)values.Value, globalTypes, t2, null); - else - val = ChangeType(values.Value, t2); - col.Add(key, val); - } - - return col; - } - - private object CreateDictionary(ArrayList reader, Type pt, Type[] types, Dictionary globalTypes) - { - IDictionary col = (IDictionary)Reflection.Instance.FastCreateInstance(pt); - Type t1 = null; - Type t2 = null; - if (types != null) - { - t1 = types[0]; - t2 = types[1]; - } - - foreach (Dictionary values in reader) - { - object key = values["k"]; - object val = values["v"]; - - if (key is Dictionary) - key = ParseDictionary((Dictionary)key, globalTypes, t1, null); - else - key = ChangeType(key, t1); - - if (val is Dictionary) - val = ParseDictionary((Dictionary)val, globalTypes, t2, null); - else - val = ChangeType(val, t2); - - col.Add(key, val); - } - - return col; - } - - private Type GetChangeType(Type conversionType) - { - if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) - return conversionType.GetGenericArguments()[0]; - - return conversionType; - } - - private DataSet CreateDataset(Dictionary reader, Dictionary globalTypes) - { - DataSet ds = new DataSet(); - ds.EnforceConstraints = false; - ds.BeginInit(); - - // read dataset schema here - ReadSchema(reader, ds, globalTypes); - - foreach (KeyValuePair pair in reader) - { - if (pair.Key == "$type" || pair.Key == "$schema") continue; - - ArrayList rows = (ArrayList)pair.Value; - if (rows == null) continue; - - DataTable dt = ds.Tables[pair.Key]; - ReadDataTable(rows, dt); - } - - ds.EndInit(); - - return ds; - } - - private void ReadSchema(Dictionary reader, DataSet ds, Dictionary globalTypes) - { - var schema = reader["$schema"]; - - if (schema is string) - { - TextReader tr = new StringReader((string)schema); - ds.ReadXmlSchema(tr); - } - else - { - DatasetSchema ms = (DatasetSchema)ParseDictionary((Dictionary)schema, globalTypes, typeof(DatasetSchema), null); - ds.DataSetName = ms.Name; - for (int i = 0; i < ms.Info.Count; i += 3) - { - if (ds.Tables.Contains(ms.Info[i]) == false) - ds.Tables.Add(ms.Info[i]); - ds.Tables[ms.Info[i]].Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2])); - } - } - } - - private void ReadDataTable(ArrayList rows, DataTable dt) - { - dt.BeginInit(); - dt.BeginLoadData(); - List guidcols = new List(); - List datecol = new List(); - - foreach (DataColumn c in dt.Columns) - { - if (c.DataType == typeof(Guid) || c.DataType == typeof(Guid?)) - guidcols.Add(c.Ordinal); - if (_params.UseUTCDateTime && (c.DataType == typeof(DateTime) || c.DataType == typeof(DateTime?))) - datecol.Add(c.Ordinal); - } - - foreach (ArrayList row in rows) - { - object[] v = new object[row.Count]; - row.CopyTo(v, 0); - foreach (int i in guidcols) - { - string s = (string)v[i]; - if (s != null && s.Length < 36) - v[i] = new Guid(Convert.FromBase64String(s)); - } - if (_params.UseUTCDateTime) - { - foreach (int i in datecol) - { - string s = (string)v[i]; - if (s != null) - v[i] = CreateDateTime(s); - } - } - dt.Rows.Add(v); - } - - dt.EndLoadData(); - dt.EndInit(); - } - - DataTable CreateDataTable(Dictionary reader, Dictionary globalTypes) - { - var dt = new DataTable(); - - // read dataset schema here - var schema = reader["$schema"]; - - if (schema is string) - { - TextReader tr = new StringReader((string)schema); - dt.ReadXmlSchema(tr); - } - else - { - var ms = (DatasetSchema)this.ParseDictionary((Dictionary)schema, globalTypes, typeof(DatasetSchema), null); - dt.TableName = ms.Info[0]; - for (int i = 0; i < ms.Info.Count; i += 3) - { - dt.Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2])); - } - } - - foreach (var pair in reader) - { - if (pair.Key == "$type" || pair.Key == "$schema") - continue; - - var rows = (ArrayList)pair.Value; - if (rows == null) - continue; - - if (!dt.TableName.Equals(pair.Key, StringComparison.InvariantCultureIgnoreCase)) - continue; - - ReadDataTable(rows, dt); - } - - return dt; - } - - #endregion -} - -#endregion - - - - - -#region Serializer -internal class JSONSerializer -{ - private readonly StringBuilder _output = new StringBuilder(); - readonly int _MAX_DEPTH = 10; - int _current_depth = 0; - private Dictionary _globalTypes = new Dictionary(); - private JSONParameters _params; - - internal JSONSerializer(JSONParameters param) - { - _params = param; - } - - internal string ConvertToJSON(object obj) - { - WriteValue(obj); - - string str = ""; - if (_params.UsingGlobalTypes) - { - StringBuilder sb = new StringBuilder(); - sb.Append("\"$types\":{"); - bool pendingSeparator = false; - foreach (var kv in _globalTypes) - { - if (pendingSeparator) sb.Append(','); - pendingSeparator = true; - sb.Append("\""); - sb.Append(kv.Key); - sb.Append("\":\""); - sb.Append(kv.Value); - sb.Append("\""); - } - sb.Append("},"); - str = _output.Replace("$types$", sb.ToString()).ToString(); - } - else - str = _output.ToString(); - - return str; - } - - private void WriteValue(object obj) - { - if (obj == null || obj is DBNull) - _output.Append("null"); - - else if (obj is string || obj is char) - WriteString(obj.ToString()); - - else if (obj is Guid) - WriteGuid((Guid)obj); - - else if (obj is bool) - _output.Append(((bool)obj) ? "true" : "false"); // conform to standard - - else if ( - obj is int || obj is long || obj is double || - obj is decimal || obj is float || - obj is byte || obj is short || - obj is sbyte || obj is ushort || - obj is uint || obj is ulong - ) - _output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo)); - - else if (obj is DateTime) - WriteDateTime((DateTime)obj); - - else if (obj is IDictionary && obj.GetType().IsGenericType && obj.GetType().GetGenericArguments()[0] == typeof(string)) - WriteStringDictionary((IDictionary)obj); - - else if (obj is IDictionary) - WriteDictionary((IDictionary)obj); - - else if (obj is DataSet) - WriteDataset((DataSet)obj); - - else if (obj is DataTable) - this.WriteDataTable((DataTable)obj); - - else if (obj is byte[]) - WriteBytes((byte[])obj); - - else if (obj is Array || obj is IList || obj is ICollection) - WriteArray((IEnumerable)obj); - - else if (obj is Enum) - WriteEnum((Enum)obj); - - else - WriteObject(obj); - } - - private void WriteEnum(Enum e) - { - // TODO : optimize enum write - WriteStringFast(e.ToString()); - } - - private void WriteGuid(Guid g) - { - if (_params.UseFastGuid == false) - WriteStringFast(g.ToString()); - else - WriteBytes(g.ToByteArray()); - } - - private void WriteBytes(byte[] bytes) - { - WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None)); - } - - private void WriteDateTime(DateTime dateTime) - { - // datetime format standard : yyyy-MM-dd HH:mm:ss - DateTime dt = dateTime; - if (_params.UseUTCDateTime) - dt = dateTime.ToUniversalTime(); - - _output.Append("\""); - _output.Append(dt.Year.ToString("0000", NumberFormatInfo.InvariantInfo)); - _output.Append("-"); - _output.Append(dt.Month.ToString("00", NumberFormatInfo.InvariantInfo)); - _output.Append("-"); - _output.Append(dt.Day.ToString("00", NumberFormatInfo.InvariantInfo)); - _output.Append(" "); - _output.Append(dt.Hour.ToString("00", NumberFormatInfo.InvariantInfo)); - _output.Append(":"); - _output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo)); - _output.Append(":"); - _output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo)); - - if (_params.UseUTCDateTime) - _output.Append("Z"); - - _output.Append("\""); - } - private DatasetSchema GetSchema(DataTable ds) - { - if (ds == null) return null; - - DatasetSchema m = new DatasetSchema(); - m.Info = new List(); - m.Name = ds.TableName; - - foreach (DataColumn c in ds.Columns) - { - m.Info.Add(ds.TableName); - m.Info.Add(c.ColumnName); - m.Info.Add(c.DataType.ToString()); - } - // FEATURE : serialize relations and constraints here - - return m; - } - - private DatasetSchema GetSchema(DataSet ds) - { - if (ds == null) return null; - - DatasetSchema m = new DatasetSchema(); - m.Info = new List(); - m.Name = ds.DataSetName; - - foreach (DataTable t in ds.Tables) - { - foreach (DataColumn c in t.Columns) - { - m.Info.Add(t.TableName); - m.Info.Add(c.ColumnName); - m.Info.Add(c.DataType.ToString()); - } - } - // FEATURE : serialize relations and constraints here - - return m; - } - - private string GetXmlSchema(DataTable dt) - { - using (var writer = new StringWriter()) - { - dt.WriteXmlSchema(writer); - return dt.ToString(); - } - } - - private void WriteDataset(DataSet ds) - { - _output.Append('{'); - if ( _params.UseExtensions) - { - WritePair("$schema", _params.UseOptimizedDatasetSchema ? (object)GetSchema(ds) : ds.GetXmlSchema()); - _output.Append(','); - } - bool tablesep = false; - foreach (DataTable table in ds.Tables) - { - if (tablesep) _output.Append(","); - tablesep = true; - WriteDataTableData(table); - } - // end dataset - _output.Append('}'); - } - - private void WriteDataTableData(DataTable table) - { - _output.Append('\"'); - _output.Append(table.TableName); - _output.Append("\":["); - DataColumnCollection cols = table.Columns; - bool rowseparator = false; - foreach (DataRow row in table.Rows) - { - if (rowseparator) _output.Append(","); - rowseparator = true; - _output.Append('['); - - bool pendingSeperator = false; - foreach (DataColumn column in cols) - { - if (pendingSeperator) _output.Append(','); - WriteValue(row[column]); - pendingSeperator = true; - } - _output.Append(']'); - } - - _output.Append(']'); - } - - void WriteDataTable(DataTable dt) - { - this._output.Append('{'); - if (_params.UseExtensions) - { - this.WritePair("$schema", _params.UseOptimizedDatasetSchema ? (object)this.GetSchema(dt) : this.GetXmlSchema(dt)); - this._output.Append(','); - } - - WriteDataTableData(dt); - - // end datatable - this._output.Append('}'); - } - bool _TypesWritten = false; - private void WriteObject(object obj) - { - if (_params.UsingGlobalTypes == false) - _output.Append('{'); - else - { - if (_TypesWritten== false) - _output.Append("{$types$"); - else - _output.Append("{"); - } - _TypesWritten = true; - _current_depth++; - if (_current_depth > _MAX_DEPTH) - throw new Exception("Serializer encountered maximum depth of " + _MAX_DEPTH); - - - Dictionary map = new Dictionary(); - Type t = obj.GetType(); - bool append = false; - if (_params.UseExtensions) - { - if (_params.UsingGlobalTypes == false) - WritePairFast("$type", Reflection.Instance.GetTypeAssemblyName(t)); - else - { - int dt = 0; - string ct = Reflection.Instance.GetTypeAssemblyName(t); - if (_globalTypes.TryGetValue(ct, out dt) == false) - { - dt = _globalTypes.Count + 1; - _globalTypes.Add(ct, dt); - } - WritePairFast("$type", dt.ToString()); - } - append = true; - } - - List g = Reflection.Instance.GetGetters(t); - foreach (var p in g) - { - if (append) - _output.Append(','); - object o = p.Getter(obj); - if ((o == null || o is DBNull) && _params.SerializeNullValues == false) - append = false; - else - { - WritePair(p.Name, o); - if (o != null && _params.UseExtensions) - { - Type tt = o.GetType(); - if (tt == typeof(System.Object)) - map.Add(p.Name, tt.ToString()); - } - append = true; - } - } - if (map.Count > 0 && _params.UseExtensions) - { - _output.Append(",\"$map\":"); - WriteStringDictionary(map); - } - _current_depth--; - _output.Append('}'); - _current_depth--; - - } - - private void WritePairFast(string name, string value) - { - if ((value == null) && _params.SerializeNullValues == false) - return; - WriteStringFast(name); - - _output.Append(':'); - - WriteStringFast(value); - } - - private void WritePair(string name, object value) - { - if ((value == null || value is DBNull) && _params.SerializeNullValues == false) - return; - WriteStringFast(name); - - _output.Append(':'); - - WriteValue(value); - } - - private void WriteArray(IEnumerable array) - { - _output.Append('['); - - bool pendingSeperator = false; - - foreach (object obj in array) - { - if (pendingSeperator) _output.Append(','); - - WriteValue(obj); - - pendingSeperator = true; - } - _output.Append(']'); - } - - private void WriteStringDictionary(IDictionary dic) - { - _output.Append('{'); - - bool pendingSeparator = false; - - foreach (DictionaryEntry entry in dic) - { - if (pendingSeparator) _output.Append(','); - - WritePair((string)entry.Key, entry.Value); - - pendingSeparator = true; - } - _output.Append('}'); - } - - private void WriteDictionary(IDictionary dic) - { - _output.Append('['); - - bool pendingSeparator = false; - - foreach (DictionaryEntry entry in dic) - { - if (pendingSeparator) _output.Append(','); - _output.Append('{'); - WritePair("k", entry.Key); - _output.Append(","); - WritePair("v", entry.Value); - _output.Append('}'); - - pendingSeparator = true; - } - _output.Append(']'); - } - - private void WriteStringFast(string s) - { - _output.Append('\"'); - _output.Append(s); - _output.Append('\"'); - } - - private void WriteString(string s) - { - _output.Append('\"'); - - int runIndex = -1; - - for (var index = 0; index < s.Length; ++index) - { - var c = s[index]; - - if (c >= ' ' && c < 128 && c != '\"' && c != '\\') - { - if (runIndex == -1) - { - runIndex = index; - } - - continue; - } - - if (runIndex != -1) - { - _output.Append(s, runIndex, index - runIndex); - runIndex = -1; - } - - switch (c) - { - case '\t': _output.Append("\\t"); break; - case '\r': _output.Append("\\r"); break; - case '\n': _output.Append("\\n"); break; - case '"': - case '\\': _output.Append('\\'); _output.Append(c); break; - default: - _output.Append("\\u"); - _output.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo)); - break; - } - } - - if (runIndex != -1) - { - _output.Append(s, runIndex, s.Length - runIndex); - } - - _output.Append('\"'); - } -} - - -#endregion - - - - - -#region Reflection -internal class Getters -{ - public string Name; - public Reflection.GenericGetter Getter; - public Type propertyType; -} - -internal class Reflection -{ - public readonly static Reflection Instance = new Reflection(); - private Reflection() - { - } - - public bool ShowReadOnlyProperties = false; - internal delegate object GenericSetter(object target, object value); - internal delegate object GenericGetter(object obj); - private delegate object CreateObject(); - - private SafeDictionary _tyname = new SafeDictionary(); - private SafeDictionary _typecache = new SafeDictionary(); - private SafeDictionary _constrcache = new SafeDictionary(); - private SafeDictionary> _getterscache = new SafeDictionary>(); - - #region [ PROPERTY GET SET ] - internal string GetTypeAssemblyName(Type t) - { - string val = ""; - if (_tyname.TryGetValue(t, out val)) - return val; - else - { - string s = t.AssemblyQualifiedName; - _tyname.Add(t, s); - return s; - } - } - - internal Type GetTypeFromCache(string typename) - { - Type val = null; - if (_typecache.TryGetValue(typename, out val)) - return val; - else - { - Type t = Type.GetType(typename); - _typecache.Add(typename, t); - return t; - } - } - - internal object FastCreateInstance(Type objtype) - { - try - { - CreateObject c = null; - if (_constrcache.TryGetValue(objtype, out c)) - { - return c(); - } - else - { - DynamicMethod dynMethod = new DynamicMethod("_", - MethodAttributes.Public | MethodAttributes.Static, - CallingConventions.Standard, - typeof(object), - null, - objtype, false); - ILGenerator ilGen = dynMethod.GetILGenerator(); - - if (objtype.IsClass) - { - ilGen.Emit(OpCodes.Newobj, objtype.GetConstructor(Type.EmptyTypes)); - ilGen.Emit(OpCodes.Ret); - c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject)); - _constrcache.Add(objtype, c); - } - else // structs - { - var lv = ilGen.DeclareLocal(objtype); - ilGen.Emit(OpCodes.Ldloca_S, lv); - ilGen.Emit(OpCodes.Initobj, objtype); - ilGen.Emit(OpCodes.Ldloc_0); - ilGen.Emit(OpCodes.Box, objtype); - ilGen.Emit(OpCodes.Ret); - c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject)); - _constrcache.Add(objtype, c); - } - return c(); - } - } - catch (Exception exc) - { - throw new Exception(string.Format("Failed to fast create instance for type '{0}' from assemebly '{1}'", - objtype.FullName, objtype.AssemblyQualifiedName), exc); - } - } - - internal static GenericSetter CreateSetField(Type type, FieldInfo fieldInfo) - { - Type[] arguments = new Type[2]; - arguments[0] = arguments[1] = typeof(object); - - DynamicMethod dynamicSet = new DynamicMethod("_", typeof(object), arguments, type, true); - ILGenerator il = dynamicSet.GetILGenerator(); - - if (!type.IsClass) // structs - { - var lv = il.DeclareLocal(type); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Unbox_Any, type); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, lv); - il.Emit(OpCodes.Ldarg_1); - if (fieldInfo.FieldType.IsClass) - il.Emit(OpCodes.Castclass, fieldInfo.FieldType); - else - il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); - il.Emit(OpCodes.Stfld, fieldInfo); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Box, type); - il.Emit(OpCodes.Ret); - } - else - { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - if (fieldInfo.FieldType.IsValueType) - il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); - il.Emit(OpCodes.Stfld, fieldInfo); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ret); - } - return (GenericSetter)dynamicSet.CreateDelegate(typeof(GenericSetter)); - } - - internal static GenericSetter CreateSetMethod(Type type, PropertyInfo propertyInfo) - { - MethodInfo setMethod = propertyInfo.GetSetMethod(); - if (setMethod == null) - return null; - - Type[] arguments = new Type[2]; - arguments[0] = arguments[1] = typeof(object); - - DynamicMethod setter = new DynamicMethod("_", typeof(object), arguments, type); - ILGenerator il = setter.GetILGenerator(); - - if (!type.IsClass) // structs - { - var lv = il.DeclareLocal(type); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Unbox_Any, type); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, lv); - il.Emit(OpCodes.Ldarg_1); - if (propertyInfo.PropertyType.IsClass) - il.Emit(OpCodes.Castclass, propertyInfo.PropertyType); - else - il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); - il.EmitCall(OpCodes.Call, setMethod, null); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Box, type); - } - else - { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); - il.Emit(OpCodes.Ldarg_1); - if (propertyInfo.PropertyType.IsClass) - il.Emit(OpCodes.Castclass, propertyInfo.PropertyType); - else - il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); - il.EmitCall(OpCodes.Callvirt, setMethod, null); - il.Emit(OpCodes.Ldarg_0); - } - - il.Emit(OpCodes.Ret); - - return (GenericSetter)setter.CreateDelegate(typeof(GenericSetter)); - } - - internal static GenericGetter CreateGetField(Type type, FieldInfo fieldInfo) - { - DynamicMethod dynamicGet = new DynamicMethod("_", typeof(object), new Type[] { typeof(object) }, type, true); - ILGenerator il = dynamicGet.GetILGenerator(); - - if (!type.IsClass) // structs - { - var lv = il.DeclareLocal(type); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Unbox_Any, type); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, lv); - il.Emit(OpCodes.Ldfld, fieldInfo); - if (fieldInfo.FieldType.IsValueType) - il.Emit(OpCodes.Box, fieldInfo.FieldType); - } - else - { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, fieldInfo); - if (fieldInfo.FieldType.IsValueType) - il.Emit(OpCodes.Box, fieldInfo.FieldType); - } - - il.Emit(OpCodes.Ret); - - return (GenericGetter)dynamicGet.CreateDelegate(typeof(GenericGetter)); - } - - internal static GenericGetter CreateGetMethod(Type type, PropertyInfo propertyInfo) - { - MethodInfo getMethod = propertyInfo.GetGetMethod(); - if (getMethod == null) - return null; - - Type[] arguments = new Type[1]; - arguments[0] = typeof(object); - - DynamicMethod getter = new DynamicMethod("_", typeof(object), arguments, type); - ILGenerator il = getter.GetILGenerator(); - - if (!type.IsClass) // structs - { - var lv = il.DeclareLocal(type); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Unbox_Any, type); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, lv); - il.EmitCall(OpCodes.Call, getMethod, null); - if (propertyInfo.PropertyType.IsValueType) - il.Emit(OpCodes.Box, propertyInfo.PropertyType); - } - else - { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); - il.EmitCall(OpCodes.Callvirt, getMethod, null); - if (propertyInfo.PropertyType.IsValueType) - il.Emit(OpCodes.Box, propertyInfo.PropertyType); - } - - il.Emit(OpCodes.Ret); - - return (GenericGetter)getter.CreateDelegate(typeof(GenericGetter)); - } - - internal List GetGetters(Type type) - { - List val = null; - if (_getterscache.TryGetValue(type, out val)) - return val; - - PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - List getters = new List(); - foreach (PropertyInfo p in props) - { - if (!p.CanWrite && ShowReadOnlyProperties == false) continue; - - object[] att = p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false); - if (att != null && att.Length > 0) - continue; - - GenericGetter g = CreateGetMethod(type, p); - if (g != null) - { - Getters gg = new Getters(); - gg.Name = p.Name; - gg.Getter = g; - gg.propertyType = p.PropertyType; - getters.Add(gg); - } - } - - FieldInfo[] fi = type.GetFields(BindingFlags.Instance | BindingFlags.Public); - foreach (var f in fi) - { - object[] att = f.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false); - if (att != null && att.Length > 0) - continue; - - GenericGetter g = CreateGetField(type, f); - if (g != null) - { - Getters gg = new Getters(); - gg.Name = f.Name; - gg.Getter = g; - gg.propertyType = f.FieldType; - getters.Add(gg); - } - } - - _getterscache.Add(type, getters); - return getters; - } - - #endregion -} - #endregion - - - - - - #region SafeDictionary -internal class SafeDictionary -{ - private readonly object _Padlock = new object(); - private readonly Dictionary _Dictionary; - - public SafeDictionary(int capacity) - { - _Dictionary = new Dictionary(capacity); - } - - public SafeDictionary() - { - _Dictionary = new Dictionary(); - } - - public bool TryGetValue(TKey key, out TValue value) - { - lock (_Padlock) - return _Dictionary.TryGetValue(key, out value); - } - - public TValue this[TKey key] - { - get - { - lock (_Padlock) - return _Dictionary[key]; - } - set - { - lock (_Padlock) - _Dictionary[key] = value; - } - } - - public void Add(TKey key, TValue value) - { - lock (_Padlock) - { - if (_Dictionary.ContainsKey(key) == false) - _Dictionary.Add(key, value); - } - } -} -#endregion - - - - -#region Formatter -internal static class Formatter -{ - public static string Indent = " "; - - public static void AppendIndent(StringBuilder sb, int count) - { - for (; count > 0; --count) sb.Append(Indent); - } - - public static bool IsEscaped(StringBuilder sb, int index) - { - bool escaped = false; - while (index > 0 && sb[--index] == '\\') escaped = !escaped; - return escaped; - } - - public static string PrettyPrint(string input) - { - var output = new StringBuilder(input.Length * 2); - char? quote = null; - int depth = 0; - - for (int i = 0; i < input.Length; ++i) - { - char ch = input[i]; - - switch (ch) - { - case '{': - case '[': - output.Append(ch); - if (!quote.HasValue) - { - output.AppendLine(); - AppendIndent(output, ++depth); - } - break; - case '}': - case ']': - if (quote.HasValue) - output.Append(ch); - else - { - output.AppendLine(); - AppendIndent(output, --depth); - output.Append(ch); - } - break; - case '"': - case '\'': - output.Append(ch); - if (quote.HasValue) - { - if (!IsEscaped(output, i)) - quote = null; - } - else quote = ch; - break; - case ',': - output.Append(ch); - if (!quote.HasValue) - { - output.AppendLine(); - AppendIndent(output, depth); - } - break; - case ':': - if (quote.HasValue) output.Append(ch); - else output.Append(" : "); - break; - default: - if (quote.HasValue || !char.IsWhiteSpace(ch)) - output.Append(ch); - break; - } - } - - return output.ToString(); - } -} -#endregion - - - - -#region Getters - public class DatasetSchema -{ - public List Info { get; set; } - public string Name { get; set; } -} -#endregion - - - - - - -#region Parser - -/// -/// This class encodes and decodes JSON strings. -/// Spec. details, see http://www.json.org/ -/// -/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable. -/// All numbers are parsed to doubles. -/// -internal class JsonParser -{ - enum Token - { - None = -1, // Used to denote no Lookahead available - Curly_Open, - Curly_Close, - Squared_Open, - Squared_Close, - Colon, - Comma, - String, - Number, - True, - False, - Null - } - - readonly char[] json; - readonly StringBuilder s = new StringBuilder(); - Token lookAheadToken = Token.None; - int index; - bool _ignorecase = false; - - - internal JsonParser(string json, bool ignorecase) - { - this.json = json.ToCharArray(); - _ignorecase = ignorecase; - } - - public object Decode() - { - return ParseValue(); - } - - private Dictionary ParseObject() - { - Dictionary table = new Dictionary(); - - ConsumeToken(); // { - - while (true) - { - switch (LookAhead()) - { - - case Token.Comma: - ConsumeToken(); - break; - - case Token.Curly_Close: - ConsumeToken(); - return table; - - default: - { - - // name - string name = ParseString(); - if (_ignorecase) - name = name.ToLower(); - - // : - if (NextToken() != Token.Colon) - { - throw new Exception("Expected colon at index " + index); - } - - // value - object value = ParseValue(); - - table[name] = value; - } - break; - } - } - } - - private ArrayList ParseArray() - { - ArrayList array = new ArrayList(); - - ConsumeToken(); // [ - - while (true) - { - switch (LookAhead()) - { - - case Token.Comma: - ConsumeToken(); - break; - - case Token.Squared_Close: - ConsumeToken(); - return array; - - default: - { - array.Add(ParseValue()); - } - break; - } - } - } - - private object ParseValue() - { - switch (LookAhead()) - { - case Token.Number: - return ParseNumber(); - - case Token.String: - return ParseString(); - - case Token.Curly_Open: - return ParseObject(); - - case Token.Squared_Open: - return ParseArray(); - - case Token.True: - ConsumeToken(); - return true; - - case Token.False: - ConsumeToken(); - return false; - - case Token.Null: - ConsumeToken(); - return null; - } - - throw new Exception("Unrecognized token at index" + index); - } - - private string ParseString() - { - ConsumeToken(); // " - - s.Length = 0; - - int runIndex = -1; - - while (index < json.Length) - { - var c = json[index++]; - - if (c == '"') - { - if (runIndex != -1) - { - if (s.Length == 0) - return new string(json, runIndex, index - runIndex - 1); - - s.Append(json, runIndex, index - runIndex - 1); - } - return s.ToString(); - } - - if (c != '\\') - { - if (runIndex == -1) - runIndex = index - 1; - - continue; - } - - if (index == json.Length) break; - - if (runIndex != -1) - { - s.Append(json, runIndex, index - runIndex - 1); - runIndex = -1; - } - - switch (json[index++]) - { - case '"': - s.Append('"'); - break; - - case '\\': - s.Append('\\'); - break; - - case '/': - s.Append('/'); - break; - - case 'b': - s.Append('\b'); - break; - - case 'f': - s.Append('\f'); - break; - - case 'n': - s.Append('\n'); - break; - - case 'r': - s.Append('\r'); - break; - - case 't': - s.Append('\t'); - break; - - case 'u': - { - int remainingLength = json.Length - index; - if (remainingLength < 4) break; - - // parse the 32 bit hex into an integer codepoint - uint codePoint = ParseUnicode(json[index], json[index + 1], json[index + 2], json[index + 3]); - s.Append((char)codePoint); - - // skip 4 chars - index += 4; - } - break; - } - } - - throw new Exception("Unexpectedly reached end of string"); - } - - private uint ParseSingleChar(char c1, uint multipliyer) - { - uint p1 = 0; - if (c1 >= '0' && c1 <= '9') - p1 = (uint)(c1 - '0') * multipliyer; - else if (c1 >= 'A' && c1 <= 'F') - p1 = (uint)((c1 - 'A') + 10) * multipliyer; - else if (c1 >= 'a' && c1 <= 'f') - p1 = (uint)((c1 - 'a') + 10) * multipliyer; - return p1; - } - - private uint ParseUnicode(char c1, char c2, char c3, char c4) - { - uint p1 = ParseSingleChar(c1, 0x1000); - uint p2 = ParseSingleChar(c2, 0x100); - uint p3 = ParseSingleChar(c3, 0x10); - uint p4 = ParseSingleChar(c4, 1); - - return p1 + p2 + p3 + p4; - } - - private string ParseNumber() - { - ConsumeToken(); - - // Need to start back one place because the first digit is also a token and would have been consumed - var startIndex = index - 1; - - do - { - var c = json[index]; - - if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E') - { - if (++index == json.Length) throw new Exception("Unexpected end of string whilst parsing number"); - continue; - } - - break; - } while (true); - - return new string(json, startIndex, index - startIndex); - } - - private Token LookAhead() - { - if (lookAheadToken != Token.None) return lookAheadToken; - - return lookAheadToken = NextTokenCore(); - } - - private void ConsumeToken() - { - lookAheadToken = Token.None; - } - - private Token NextToken() - { - var result = lookAheadToken != Token.None ? lookAheadToken : NextTokenCore(); - - lookAheadToken = Token.None; - - return result; - } - - private Token NextTokenCore() - { - char c; - - // Skip past whitespace - do - { - c = json[index]; - - if (c > ' ') break; - if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break; - - } while (++index < json.Length); - - if (index == json.Length) - { - throw new Exception("Reached end of string unexpectedly"); - } - - c = json[index]; - - index++; - - //if (c >= '0' && c <= '9') - // return Token.Number; - - switch (c) - { - case '{': - return Token.Curly_Open; - - case '}': - return Token.Curly_Close; - - case '[': - return Token.Squared_Open; - - case ']': - return Token.Squared_Close; - - case ',': - return Token.Comma; - - case '"': - return Token.String; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '-': case '+': case '.': - return Token.Number; - - case ':': - return Token.Colon; - - case 'f': - if (json.Length - index >= 4 && - json[index + 0] == 'a' && - json[index + 1] == 'l' && - json[index + 2] == 's' && - json[index + 3] == 'e') - { - index += 4; - return Token.False; - } - break; - - case 't': - if (json.Length - index >= 3 && - json[index + 0] == 'r' && - json[index + 1] == 'u' && - json[index + 2] == 'e') - { - index += 3; - return Token.True; - } - break; - - case 'n': - if (json.Length - index >= 3 && - json[index + 0] == 'u' && - json[index + 1] == 'l' && - json[index + 2] == 'l') - { - index += 3; - return Token.Null; - } - break; - - } - - throw new Exception("Could not find token at index " + --index); - } -} -#endregion - diff --git a/src/Handlers/ContextMenuHandler.cs b/src/Handlers/ContextMenuHandler.cs index 0dc2da7..641a7b7 100644 --- a/src/Handlers/ContextMenuHandler.cs +++ b/src/Handlers/ContextMenuHandler.cs @@ -6,7 +6,7 @@ using System.Windows.Forms; using CefSharp.WinForms; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class ContextMenuHandler : IContextMenuHandler { private const int ShowDevTools = 26501; @@ -156,20 +156,8 @@ public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, I myForm.RefreshActiveTab(); }); } - if (id == SaveAsPdf) - { - - SaveFileDialog sfd = new SaveFileDialog(); - sfd.Filter = "PDF Files | *.pdf"; - if (sfd.ShowDialog() == DialogResult.OK) - { - //string path = Path.GetFileName(sfd.FileName); - browser.PrintToPdfAsync(sfd.FileName, new PdfPrintSettings() - { - SelectionOnly = false, - BackgroundsEnabled = true - }); - } + if (id == SaveAsPdf) { + SaveAsPDF(browser); } if (id == Print) { @@ -179,6 +167,17 @@ public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, I return false; } + public static void SaveAsPDF(IBrowser browser) { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.Filter = "PDF Files|*.pdf"; + //sfd.DefaultExt = (browser.MainFrame.Name ?? "Document") + ".pdf"; + if (sfd.ShowDialog() == DialogResult.OK) { + browser.PrintToPdfAsync(sfd.FileName, new PdfPrintSettings() { + PrintBackground = true, + }); + } + } + // // Summary: // Called when the context menu is dismissed irregardless of whether the menu was diff --git a/src/Handlers/DownloadHandler.cs b/src/Handlers/DownloadHandler.cs index 54c5c43..fb8e294 100644 --- a/src/Handlers/DownloadHandler.cs +++ b/src/Handlers/DownloadHandler.cs @@ -1,6 +1,7 @@ using CefSharp; +using SharpBrowser.Managers; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class DownloadHandler : IDownloadHandler { readonly MainForm myForm; @@ -49,31 +50,35 @@ public bool CanDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, string // // callback: // Callback interface used to asynchronously continue a download. - public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem item, IBeforeDownloadCallback callback) { + + public bool OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem item, IBeforeDownloadCallback callback) { if (!callback.IsDisposed) { using (callback) { - myForm.UpdateDownloadItem(item); + DownloadManager.UpdateDownloadItem(item); // ask browser what path it wants to save the file into - string path = myForm.CalcDownloadPath(item); + string path = DownloadManager.CalcDownloadPath(item); // if file should not be saved, path will be null, so skip file if (path == null) { // skip file callback.Continue(path, false); + return false; } else { // open the downloads tab - myForm.OpenDownloadsTab(); + myForm.OpenDownloads(); callback.Continue(path, true); + return true; } } } + return false; } // @@ -94,11 +99,12 @@ public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadI // callback: // The callback used to Cancel/Pause/Resume the process public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) { - myForm.UpdateDownloadItem(downloadItem); - if (downloadItem.IsInProgress && myForm.CancelRequests.Contains(downloadItem.Id)) { + DownloadManager.UpdateDownloadItem(downloadItem); + if (downloadItem.IsInProgress && DownloadManager.CancelRequests.Contains(downloadItem.Id)) { callback.Cancel(); } //Console.WriteLine(downloadItem.Url + " %" + downloadItem.PercentComplete + " complete"); } + } } \ No newline at end of file diff --git a/src/Handlers/HostHandler.cs b/src/Handlers/HostHandler.cs index ff8a470..651ca0e 100644 --- a/src/Handlers/HostHandler.cs +++ b/src/Handlers/HostHandler.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows.Forms; +using Newtonsoft.Json; +using SharpBrowser.Managers; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { /// /// functions in this class are accessible by JS using the code `host.X()` @@ -19,18 +16,14 @@ public void addNewBrowserTab(string url, bool focusNewTab = true) { myForm.AddNewBrowserTab(url, focusNewTab); } public string getDownloads() { - lock (myForm.downloads) { - string x = JSON.Instance.ToJSON(myForm.downloads); - return x; + lock (DownloadManager.Downloads) { + var json = JsonConvert.SerializeObject(DownloadManager.Downloads.Values); + return json; } } public bool cancelDownload(int downloadId) { - lock (myForm.downloadCancelRequests) { - if (!myForm.downloadCancelRequests.Contains(downloadId)) { - myForm.downloadCancelRequests.Add(downloadId); - } - } + DownloadManager.Cancel(downloadId); return true; } public void refreshActiveTab() { diff --git a/src/Handlers/KeyboardHandler.cs b/src/Handlers/KeyboardHandler.cs index 91f40c4..0141eca 100644 --- a/src/Handlers/KeyboardHandler.cs +++ b/src/Handlers/KeyboardHandler.cs @@ -1,16 +1,16 @@ - using System; +using System; using System.Collections.Generic; using System.Windows.Forms; using CefSharp; -using SharpBrowser.Browser.Model; +using SharpBrowser.Model; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class KeyboardHandler : IKeyboardHandler { MainForm myForm; public static List Hotkeys = new List(); public static void AddHotKey(Form form, Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { - Utils.AddHotKey(form, function, key, ctrl, shift, alt); + WinFormsUtils.AddHotKey(form, function, key, ctrl, shift, alt); Hotkeys.Add(new BrowserHotKey(function, key, ctrl, shift, alt)); } diff --git a/src/Handlers/LifeSpanHandler.cs b/src/Handlers/LifeSpanHandler.cs index a24f18c..11e31c3 100644 --- a/src/Handlers/LifeSpanHandler.cs +++ b/src/Handlers/LifeSpanHandler.cs @@ -1,6 +1,6 @@ using CefSharp; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class LifeSpanHandler : ILifeSpanHandler { MainForm myForm; diff --git a/src/Handlers/PermissionHandler.cs b/src/Handlers/PermissionHandler.cs new file mode 100644 index 0000000..691c84d --- /dev/null +++ b/src/Handlers/PermissionHandler.cs @@ -0,0 +1,101 @@ +using CefSharp; +using SharpBrowser.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpBrowser.Handlers { + internal class PermissionHandler : IPermissionHandler { + /// + /// Called when a permission prompt handled via OnShowPermissionPrompt(IWebBrowser, IBrowser, UInt64, String, PermissionRequestType, IPermissionPromptCallback) is dismissed. result will be the value passed to Continue(PermissionRequestResult) or Ignore if the dialog was dismissed for other reasons such as navigation, browser closure, etc. This method will not be called if OnShowPermissionPrompt(IWebBrowser, IBrowser, UInt64, String, PermissionRequestType, IPermissionPromptCallback) returned false for promptId. + /// + /// Will match the value that was passed to OnShowPermissionPrompt + /// will be the value passed to Continue(PermissionRequestResult) or Ignore if the dialog was dismissed for other reasons such as navigation, browser closure, etc. This method will not be called if OnShowPermissionPrompt(IWebBrowser, IBrowser, UInt64, String, PermissionRequestType, IPermissionPromptCallback) returned false for promptId. + void IPermissionHandler.OnDismissPermissionPrompt(IWebBrowser chromiumWebBrowser, IBrowser browser, ulong promptId, PermissionRequestResult result) { + + } + + /// + /// Called when a page requests permission to access media. + /// With the Chrome runtime, default handling will display the permission request UI. + /// With the Alloy runtime, default handling will deny the request. + /// This method will not be called if the "--enable-media-stream" command-line switch is used to grant all permissions. + /// + /// is the URL origin requesting permission. + /// is a combination of values that represent the requested permissions + /// Callback interface used for asynchronous continuation of media access. + bool IPermissionHandler.OnRequestMediaAccessPermission(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string requestingOrigin, MediaAccessPermissionType requestedPermissions, IMediaAccessCallback callback) { + return true; + } + + /// + /// Called when a page should show a permission prompt. + /// + /// Uniquely identifies the prompt. + /// Is the URL origin requesting permission. + /// Is a combination of values from PermissionRequestType that represent the requested permissions. + /// Callback interface used for asynchronous continuation of permission prompts. + bool IPermissionHandler.OnShowPermissionPrompt(IWebBrowser chromiumWebBrowser, IBrowser browser, ulong promptId, string requestingOrigin, PermissionRequestType requestedPermissions, IPermissionPromptCallback callback) { + + var allow = false; + + + // DENY FEATURES THAT ARE NOT SUPPORTED + switch (requestedPermissions) { + + case PermissionRequestType.Notifications: + case PermissionRequestType.TopLevelStorageAccess: + case PermissionRequestType.WindowManagement: + case PermissionRequestType.RegisterProtocolHandler: + allow = false; + break; + + + + // ACCEPT/DENY BASED ON APP CONFIG + + case PermissionRequestType.MidiSysex: + allow = BrowserConfig.WebMidi; + break; + case PermissionRequestType.CameraStream: + case PermissionRequestType.CameraPanTiltZoom: + allow = BrowserConfig.Camera; + break; + case PermissionRequestType.MicStream: + allow = BrowserConfig.Microphone; + break; + case PermissionRequestType.Clipboard: + allow = BrowserConfig.JavascriptClipboard; + break; + case PermissionRequestType.FileSystemAccess: + allow = BrowserConfig.LocalFiles; + break; + case PermissionRequestType.StorageAccess: + allow = BrowserConfig.LocalStorage; + break; + + + // ACCEPT THINGS THAT ARE SUPPORTED + + case PermissionRequestType.LocalFonts: + case PermissionRequestType.MultipleDownloads: + allow = true; + break; + + } + + + // DENY UNKNOWN BY DEFAULT + if (allow) { + callback.Continue(PermissionRequestResult.Accept); + }else { + callback.Continue(PermissionRequestResult.Deny); + } + + // returning true means the browser has handled this request + return true; + } + } +} diff --git a/src/Handlers/RequestHandler.cs b/src/Handlers/RequestHandler.cs index 85d9ebb..2db90d7 100644 --- a/src/Handlers/RequestHandler.cs +++ b/src/Handlers/RequestHandler.cs @@ -4,10 +4,12 @@ using System.Windows.Forms; using CefSharp; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class RequestHandler : IRequestHandler { MainForm myForm; + public static bool settings__onCtrlClick_focusNewTab = true; + public RequestHandler(MainForm form) { myForm = form; } @@ -107,6 +109,8 @@ public bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFr public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback) { return true; } + + // // Summary: // Called on the UI thread before OnBeforeBrowse in certain limited cases where @@ -132,8 +136,36 @@ public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, Cef // Returns: // Return true to cancel the navigation or false to allow the navigation to // proceed in the source browser's top-level frame. - public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { + public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, + string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { + //return false; + + // check if the user intended to open the link in a new tab + // (Ctrl+Click often results in NewBackgroundTab or NewForegroundTab) + if (targetDisposition == WindowOpenDisposition.NewForegroundTab || + targetDisposition == WindowOpenDisposition.NewBackgroundTab) + { + // Check if it was a user gesture (actual click) + if (userGesture) + { + myForm.Invoke((MethodInvoker)(() => { + myForm.AddNewBrowserTab(targetUrl, + //focusNewTab: (targetDisposition == WindowOpenDisposition.NewForegroundTab), + focusNewTab: settings__onCtrlClick_focusNewTab, + browser.MainFrame.Url + ); + })); + + + //handled.. (dont load it in the current tab) + return true; + } + } + + // For all other cases, return false to let the default navigation proceed. return false; + + } // // Summary: @@ -177,7 +209,8 @@ public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string // Parameters: // status: // indicates how the process terminated. - public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status) { + public void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status, int errorCode, string errorMessage) { + } // // Summary: @@ -280,7 +313,6 @@ public void OnDocumentAvailableInMainFrame(IWebBrowser chromiumWebBrowser, IBrow } - - + } } \ No newline at end of file diff --git a/src/Handlers/ResourceRequestHandler.cs b/src/Handlers/ResourceRequestHandler.cs index 47fee87..bccf247 100644 --- a/src/Handlers/ResourceRequestHandler.cs +++ b/src/Handlers/ResourceRequestHandler.cs @@ -8,9 +8,10 @@ using System.Windows.Forms; using System.Drawing; using CefSharp.Callback; -using SharpBrowser.Browser; +using SharpBrowser.Config; +using SharpBrowser.Utils; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class ResourceRequestHandler : IResourceRequestHandler { readonly MainForm myForm; public ResourceRequestHandler(MainForm form) { diff --git a/src/Handlers/SchemeHandler.cs b/src/Handlers/SchemeHandler.cs index 0e3d172..e3f8a86 100644 --- a/src/Handlers/SchemeHandler.cs +++ b/src/Handlers/SchemeHandler.cs @@ -9,7 +9,7 @@ using System.Drawing; using CefSharp.Callback; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class SchemeHandler : IResourceHandler, IDisposable { private static string appPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\"; diff --git a/src/Handlers/SchemeHandlerFactory.cs b/src/Handlers/SchemeHandlerFactory.cs index d31d66c..06e958a 100644 --- a/src/Handlers/SchemeHandlerFactory.cs +++ b/src/Handlers/SchemeHandlerFactory.cs @@ -1,6 +1,6 @@ using CefSharp; -namespace SharpBrowser { +namespace SharpBrowser.Handlers { internal class SchemeHandlerFactory : ISchemeHandlerFactory { public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) { diff --git a/src/MainForm.Designer.cs b/src/MainForm.Designer.cs index 48f50cc..e9b7d9e 100644 --- a/src/MainForm.Designer.cs +++ b/src/MainForm.Designer.cs @@ -1,4 +1,7 @@ -namespace SharpBrowser +using FontAwesome.Sharp; +using System.Windows.Forms; + +namespace SharpBrowser { partial class MainForm { @@ -20,357 +23,548 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - #region Windows Form Designer generated code + #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() - { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); - this.menuStripTab = new System.Windows.Forms.ContextMenuStrip(this.components); - this.menuCloseTab = new System.Windows.Forms.ToolStripMenuItem(); - this.menuCloseOtherTabs = new System.Windows.Forms.ToolStripMenuItem(); - this.BtnRefresh = new System.Windows.Forms.Button(); - this.BtnStop = new System.Windows.Forms.Button(); - this.BtnForward = new System.Windows.Forms.Button(); - this.BtnBack = new System.Windows.Forms.Button(); - this.timer1 = new System.Windows.Forms.Timer(this.components); - this.BtnDownloads = new System.Windows.Forms.Button(); - this.TxtURL = new System.Windows.Forms.TextBox(); - this.PanelToolbar = new System.Windows.Forms.Panel(); - this.TabPages = new SharpBrowser.Controls.BrowserTabStrip.BrowserTabStrip(); - this.tabStrip1 = new SharpBrowser.Controls.BrowserTabStrip.BrowserTabStripItem(); - this.tabStripAdd = new SharpBrowser.Controls.BrowserTabStrip.BrowserTabStripItem(); - this.PanelStatus = new System.Windows.Forms.Panel(); - this.PanelSearch = new System.Windows.Forms.Panel(); - this.BtnNextSearch = new System.Windows.Forms.Button(); - this.BtnPrevSearch = new System.Windows.Forms.Button(); - this.BtnCloseSearch = new System.Windows.Forms.Button(); - this.TxtSearch = new System.Windows.Forms.TextBox(); - this.BtnHome = new System.Windows.Forms.Button(); - this.menuStripTab.SuspendLayout(); - this.PanelToolbar.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.TabPages)).BeginInit(); - this.TabPages.SuspendLayout(); - this.PanelSearch.SuspendLayout(); - this.SuspendLayout(); - // - // menuStripTab - // - this.menuStripTab.ImageScalingSize = new System.Drawing.Size(20, 20); - this.menuStripTab.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.menuCloseTab, - this.menuCloseOtherTabs}); - this.menuStripTab.Name = "menuStripTab"; - this.menuStripTab.Size = new System.Drawing.Size(198, 52); - // - // menuCloseTab - // - this.menuCloseTab.Name = "menuCloseTab"; - this.menuCloseTab.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F4))); - this.menuCloseTab.Size = new System.Drawing.Size(197, 24); - this.menuCloseTab.Text = "Close tab"; - this.menuCloseTab.Click += new System.EventHandler(this.menuCloseTab_Click); - // - // menuCloseOtherTabs - // - this.menuCloseOtherTabs.Name = "menuCloseOtherTabs"; - this.menuCloseOtherTabs.Size = new System.Drawing.Size(197, 24); - this.menuCloseOtherTabs.Text = "Close other tabs"; - this.menuCloseOtherTabs.Click += new System.EventHandler(this.menuCloseOtherTabs_Click); - // - // BtnRefresh - // - this.BtnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnRefresh.ForeColor = System.Drawing.Color.White; - this.BtnRefresh.Image = ((System.Drawing.Image)(resources.GetObject("BtnRefresh.Image"))); - this.BtnRefresh.Location = new System.Drawing.Point(878, 0); - this.BtnRefresh.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnRefresh.Name = "BtnRefresh"; - this.BtnRefresh.Size = new System.Drawing.Size(25, 30); - this.BtnRefresh.TabIndex = 3; - this.BtnRefresh.UseVisualStyleBackColor = true; - this.BtnRefresh.Click += new System.EventHandler(this.bRefresh_Click); - // - // BtnStop - // - this.BtnStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnStop.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnStop.ForeColor = System.Drawing.Color.White; - this.BtnStop.Image = ((System.Drawing.Image)(resources.GetObject("BtnStop.Image"))); - this.BtnStop.Location = new System.Drawing.Point(878, -2); - this.BtnStop.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnStop.Name = "BtnStop"; - this.BtnStop.Size = new System.Drawing.Size(25, 30); - this.BtnStop.TabIndex = 2; - this.BtnStop.UseVisualStyleBackColor = true; - this.BtnStop.Click += new System.EventHandler(this.bStop_Click); - // - // BtnForward - // - this.BtnForward.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnForward.ForeColor = System.Drawing.Color.White; - this.BtnForward.Image = ((System.Drawing.Image)(resources.GetObject("BtnForward.Image"))); - this.BtnForward.Location = new System.Drawing.Point(29, 0); - this.BtnForward.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnForward.Name = "BtnForward"; - this.BtnForward.Size = new System.Drawing.Size(25, 30); - this.BtnForward.TabIndex = 1; - this.BtnForward.UseVisualStyleBackColor = true; - this.BtnForward.Click += new System.EventHandler(this.bForward_Click); - // - // BtnBack - // - this.BtnBack.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnBack.ForeColor = System.Drawing.Color.White; - this.BtnBack.Image = ((System.Drawing.Image)(resources.GetObject("BtnBack.Image"))); - this.BtnBack.Location = new System.Drawing.Point(2, 0); - this.BtnBack.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnBack.Name = "BtnBack"; - this.BtnBack.Size = new System.Drawing.Size(25, 30); - this.BtnBack.TabIndex = 0; - this.BtnBack.UseVisualStyleBackColor = true; - this.BtnBack.Click += new System.EventHandler(this.bBack_Click); - // - // timer1 - // - this.timer1.Interval = 50; - this.timer1.Tick += new System.EventHandler(this.timer1_Tick); - // - // BtnDownloads - // - this.BtnDownloads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnDownloads.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnDownloads.ForeColor = System.Drawing.Color.White; - this.BtnDownloads.Image = ((System.Drawing.Image)(resources.GetObject("BtnDownloads.Image"))); - this.BtnDownloads.Location = new System.Drawing.Point(906, 0); - this.BtnDownloads.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnDownloads.Name = "BtnDownloads"; - this.BtnDownloads.Size = new System.Drawing.Size(25, 30); - this.BtnDownloads.TabIndex = 4; - this.BtnDownloads.Tag = "Downloads"; - this.BtnDownloads.UseVisualStyleBackColor = true; - this.BtnDownloads.Click += new System.EventHandler(this.bDownloads_Click); - // - // TxtURL - // - this.TxtURL.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.TxtURL.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.TxtURL.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.TxtURL.Location = new System.Drawing.Point(60, 5); - this.TxtURL.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.TxtURL.Name = "TxtURL"; - this.TxtURL.Size = new System.Drawing.Size(812, 27); - this.TxtURL.TabIndex = 5; - this.TxtURL.Click += new System.EventHandler(this.txtUrl_Click); - this.TxtURL.TextChanged += new System.EventHandler(this.txtUrl_TextChanged); - this.TxtURL.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TxtURL_KeyDown); - // - // PanelToolbar - // - this.PanelToolbar.BackColor = System.Drawing.Color.White; - this.PanelToolbar.Controls.Add(this.BtnHome); - this.PanelToolbar.Controls.Add(this.TxtURL); - this.PanelToolbar.Controls.Add(this.BtnDownloads); - this.PanelToolbar.Controls.Add(this.BtnForward); - this.PanelToolbar.Controls.Add(this.BtnBack); - this.PanelToolbar.Controls.Add(this.BtnRefresh); - this.PanelToolbar.Controls.Add(this.BtnStop); - this.PanelToolbar.Dock = System.Windows.Forms.DockStyle.Top; - this.PanelToolbar.Location = new System.Drawing.Point(0, 0); - this.PanelToolbar.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.PanelToolbar.Name = "PanelToolbar"; - this.PanelToolbar.Size = new System.Drawing.Size(934, 30); - this.PanelToolbar.TabIndex = 6; - // - // TabPages - // - this.TabPages.ContextMenuStrip = this.menuStripTab; - this.TabPages.Dock = System.Windows.Forms.DockStyle.Fill; - this.TabPages.Font = new System.Drawing.Font("Segoe UI", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.TabPages.Items.AddRange(new SharpBrowser.Controls.BrowserTabStrip.BrowserTabStripItem[] { - this.tabStrip1, - this.tabStripAdd}); - this.TabPages.Location = new System.Drawing.Point(0, 30); - this.TabPages.Name = "TabPages"; - this.TabPages.SelectedItem = this.tabStrip1; - this.TabPages.Size = new System.Drawing.Size(934, 621); - this.TabPages.TabIndex = 4; - this.TabPages.Text = "faTabStrip1"; - this.TabPages.TabStripItemSelectionChanged += new SharpBrowser.Controls.BrowserTabStrip.TabStripItemChangedHandler(this.OnTabsChanged); - this.TabPages.TabStripItemClosed += new System.EventHandler(this.OnTabClosed); - this.TabPages.MouseClick += new System.Windows.Forms.MouseEventHandler(this.tabPages_MouseClick); - // - // tabStrip1 - // - this.tabStrip1.IsDrawn = true; - this.tabStrip1.Name = "tabStrip1"; - this.tabStrip1.Selected = true; - this.tabStrip1.Size = new System.Drawing.Size(932, 591); - this.tabStrip1.TabIndex = 0; - this.tabStrip1.Title = "Loading..."; - // - // tabStripAdd - // - this.tabStripAdd.CanClose = false; - this.tabStripAdd.IsDrawn = true; - this.tabStripAdd.Name = "tabStripAdd"; - this.tabStripAdd.Size = new System.Drawing.Size(931, 601); - this.tabStripAdd.TabIndex = 1; - this.tabStripAdd.Title = "+"; - // - // PanelStatus - // - this.PanelStatus.Dock = System.Windows.Forms.DockStyle.Bottom; - this.PanelStatus.Location = new System.Drawing.Point(0, 651); - this.PanelStatus.Name = "PanelStatus"; - this.PanelStatus.Size = new System.Drawing.Size(934, 20); - this.PanelStatus.TabIndex = 8; - // - // PanelSearch - // - this.PanelSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.PanelSearch.BackColor = System.Drawing.Color.White; - this.PanelSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.PanelSearch.Controls.Add(this.BtnNextSearch); - this.PanelSearch.Controls.Add(this.BtnPrevSearch); - this.PanelSearch.Controls.Add(this.BtnCloseSearch); - this.PanelSearch.Controls.Add(this.TxtSearch); - this.PanelSearch.Location = new System.Drawing.Point(625, 41); - this.PanelSearch.Name = "PanelSearch"; - this.PanelSearch.Size = new System.Drawing.Size(307, 40); - this.PanelSearch.TabIndex = 9; - this.PanelSearch.Visible = false; - // - // BtnNextSearch - // - this.BtnNextSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnNextSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnNextSearch.ForeColor = System.Drawing.Color.White; - this.BtnNextSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnNextSearch.Image"))); - this.BtnNextSearch.Location = new System.Drawing.Point(239, 4); - this.BtnNextSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnNextSearch.Name = "BtnNextSearch"; - this.BtnNextSearch.Size = new System.Drawing.Size(25, 30); - this.BtnNextSearch.TabIndex = 9; - this.BtnNextSearch.Tag = "Find next (Enter)"; - this.BtnNextSearch.UseVisualStyleBackColor = true; - this.BtnNextSearch.Click += new System.EventHandler(this.BtnNextSearch_Click); - // - // BtnPrevSearch - // - this.BtnPrevSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnPrevSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnPrevSearch.ForeColor = System.Drawing.Color.White; - this.BtnPrevSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnPrevSearch.Image"))); - this.BtnPrevSearch.Location = new System.Drawing.Point(206, 4); - this.BtnPrevSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnPrevSearch.Name = "BtnPrevSearch"; - this.BtnPrevSearch.Size = new System.Drawing.Size(25, 30); - this.BtnPrevSearch.TabIndex = 8; - this.BtnPrevSearch.Tag = "Find previous (Shift+Enter)"; - this.BtnPrevSearch.UseVisualStyleBackColor = true; - this.BtnPrevSearch.Click += new System.EventHandler(this.BtnPrevSearch_Click); - // - // BtnCloseSearch - // - this.BtnCloseSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnCloseSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnCloseSearch.ForeColor = System.Drawing.Color.White; - this.BtnCloseSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnCloseSearch.Image"))); - this.BtnCloseSearch.Location = new System.Drawing.Point(272, 4); - this.BtnCloseSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnCloseSearch.Name = "BtnCloseSearch"; - this.BtnCloseSearch.Size = new System.Drawing.Size(25, 30); - this.BtnCloseSearch.TabIndex = 7; - this.BtnCloseSearch.Tag = "Close (Esc)"; - this.BtnCloseSearch.UseVisualStyleBackColor = true; - this.BtnCloseSearch.Click += new System.EventHandler(this.BtnClearSearch_Click); - // - // TxtSearch - // - this.TxtSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.TxtSearch.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.TxtSearch.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.TxtSearch.Location = new System.Drawing.Point(10, 6); - this.TxtSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.TxtSearch.Name = "TxtSearch"; - this.TxtSearch.Size = new System.Drawing.Size(181, 31); - this.TxtSearch.TabIndex = 6; - this.TxtSearch.TextChanged += new System.EventHandler(this.TxtSearch_TextChanged); - this.TxtSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TxtSearch_KeyDown); - // - // BtnHome - // - this.BtnHome.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.BtnHome.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.BtnHome.ForeColor = System.Drawing.Color.White; - this.BtnHome.Image = ((System.Drawing.Image)(resources.GetObject("BtnHome.Image"))); - this.BtnHome.Location = new System.Drawing.Point(847, 0); - this.BtnHome.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.BtnHome.Name = "BtnHome"; - this.BtnHome.Size = new System.Drawing.Size(25, 30); - this.BtnHome.TabIndex = 6; - this.BtnHome.Tag = "Home"; - this.BtnHome.UseVisualStyleBackColor = true; - this.BtnHome.Click += new System.EventHandler(this.BtnHome_Click); - // - // MainForm - // - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; - this.ClientSize = new System.Drawing.Size(934, 671); - this.Controls.Add(this.PanelSearch); - this.Controls.Add(this.TabPages); - this.Controls.Add(this.PanelToolbar); - this.Controls.Add(this.PanelStatus); - this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.Name = "MainForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Title"; - this.WindowState = System.Windows.Forms.FormWindowState.Maximized; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); - this.Load += new System.EventHandler(this.MainForm_Load); - this.menuStripTab.ResumeLayout(false); - this.PanelToolbar.ResumeLayout(false); - this.PanelToolbar.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.TabPages)).EndInit(); - this.TabPages.ResumeLayout(false); - this.PanelSearch.ResumeLayout(false); - this.PanelSearch.PerformLayout(); - this.ResumeLayout(false); + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + TabMenu = new ContextMenuStrip(components); + TMReload = new IconMenuItem(); + TMClose = new IconMenuItem(); + TMCloseOther = new IconMenuItem(); + BtnRefresh = new IconButton(); + BtnStop = new IconButton(); + BtnForward = new IconButton(); + BtnBack = new IconButton(); + BtnDownloads = new IconButton(); + TxtURL = new TextBox(); + PanelToolbar = new Panel(); + BtnMenu = new IconButton(); + BtnHome = new IconButton(); + TabPages = new SharpBrowser.Controls.BrowserTabStrip.BrowserTabStrip(); + tabStrip1 = new SharpBrowser.Controls.BrowserTabStrip.BrowserTabPage(); + PanelSearch = new Panel(); + BtnNextSearch = new Button(); + BtnPrevSearch = new Button(); + BtnCloseSearch = new Button(); + TxtSearch = new TextBox(); + MainMenu = new ContextMenuStrip(components); + MMNewTab = new IconMenuItem(); + MMNextTab = new IconMenuItem(); + MMPrevTab = new IconMenuItem(); + toolStripSeparator1 = new ToolStripSeparator(); + MMPrint = new IconMenuItem(); + MMPrintPDF = new IconMenuItem(); + MMDownloads = new IconMenuItem(); + MMFullscreen = new IconMenuItem(); + MMDevTools = new IconMenuItem(); + toolStripSeparator2 = new ToolStripSeparator(); + MMClose = new IconMenuItem(); + MMCloseOther = new IconMenuItem(); + TabMenu.SuspendLayout(); + PanelToolbar.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)TabPages).BeginInit(); + TabPages.SuspendLayout(); + PanelSearch.SuspendLayout(); + MainMenu.SuspendLayout(); + SuspendLayout(); + // + // TabMenu + // + TabMenu.ImageScalingSize = new System.Drawing.Size(20, 20); + TabMenu.Items.AddRange(new ToolStripItem[] { TMReload, TMClose, TMCloseOther }); + TabMenu.Name = "menuStripTab"; + TabMenu.Size = new System.Drawing.Size(173, 82); + // + // TMReload + // + TMReload.IconChar = IconChar.Refresh; + TMReload.IconColor = System.Drawing.Color.Black; + TMReload.IconFont = IconFont.Auto; + TMReload.Name = "TMReload"; + TMReload.Size = new System.Drawing.Size(172, 26); + TMReload.Text = "Reload tab"; + TMReload.Click += TMReload_Click; + // + // TMClose + // + TMClose.IconChar = IconChar.Close; + TMClose.IconColor = System.Drawing.Color.Black; + TMClose.IconFont = IconFont.Auto; + TMClose.Name = "TMClose"; + TMClose.ShortcutKeyDisplayString = "Ctrl+W"; + TMClose.Size = new System.Drawing.Size(172, 26); + TMClose.Text = "Close tab"; + TMClose.Click += TMCloseTab_Click; + // + // TMCloseOther + // + TMCloseOther.IconChar = IconChar.Eraser; + TMCloseOther.IconColor = System.Drawing.Color.Black; + TMCloseOther.IconFont = IconFont.Auto; + TMCloseOther.Name = "TMCloseOther"; + TMCloseOther.Size = new System.Drawing.Size(172, 26); + TMCloseOther.Text = "Close other tabs"; + TMCloseOther.Click += TMCloseOtherTabs_Click; + // + // BtnRefresh + // + BtnRefresh.BackgroundImageLayout = ImageLayout.Zoom; + BtnRefresh.FlatAppearance.BorderSize = 0; + BtnRefresh.FlatStyle = FlatStyle.Flat; + BtnRefresh.ForeColor = System.Drawing.Color.White; + BtnRefresh.IconChar = IconChar.Refresh; + BtnRefresh.IconColor = System.Drawing.Color.Black; + BtnRefresh.IconFont = IconFont.Auto; + BtnRefresh.IconSize = 30; + BtnRefresh.Location = new System.Drawing.Point(85, 5); + BtnRefresh.Margin = new Padding(3, 4, 3, 4); + BtnRefresh.Name = "BtnRefresh"; + BtnRefresh.Size = new System.Drawing.Size(36, 34); + BtnRefresh.TabIndex = 3; + BtnRefresh.UseVisualStyleBackColor = true; + BtnRefresh.Click += bRefresh_Click; + // + // BtnStop + // + BtnStop.BackgroundImageLayout = ImageLayout.Zoom; + BtnStop.FlatStyle = FlatStyle.Flat; + BtnStop.ForeColor = System.Drawing.Color.White; + BtnStop.IconChar = IconChar.Cancel; + BtnStop.IconColor = System.Drawing.Color.Black; + BtnStop.IconFont = IconFont.Auto; + BtnStop.IconSize = 30; + BtnStop.Location = new System.Drawing.Point(85, 5); + BtnStop.Margin = new Padding(3, 4, 3, 4); + BtnStop.Name = "BtnStop"; + BtnStop.Size = new System.Drawing.Size(36, 34); + BtnStop.TabIndex = 2; + BtnStop.UseVisualStyleBackColor = true; + BtnStop.Click += bStop_Click; + // + // BtnForward + // + BtnForward.BackgroundImageLayout = ImageLayout.Zoom; + BtnForward.FlatAppearance.BorderSize = 0; + BtnForward.FlatStyle = FlatStyle.Flat; + BtnForward.ForeColor = System.Drawing.Color.White; + BtnForward.IconChar = IconChar.ArrowRight; + BtnForward.IconColor = System.Drawing.Color.Black; + BtnForward.IconFont = IconFont.Auto; + BtnForward.IconSize = 30; + BtnForward.Location = new System.Drawing.Point(45, 5); + BtnForward.Margin = new Padding(3, 4, 3, 4); + BtnForward.Name = "BtnForward"; + BtnForward.Size = new System.Drawing.Size(36, 34); + BtnForward.TabIndex = 1; + BtnForward.UseVisualStyleBackColor = true; + BtnForward.Click += bForward_Click; + // + // BtnBack + // + BtnBack.BackgroundImageLayout = ImageLayout.Zoom; + BtnBack.FlatAppearance.BorderSize = 0; + BtnBack.FlatStyle = FlatStyle.Flat; + BtnBack.ForeColor = System.Drawing.Color.White; + BtnBack.IconChar = IconChar.ArrowLeft; + BtnBack.IconColor = System.Drawing.Color.Black; + BtnBack.IconFont = IconFont.Auto; + BtnBack.IconSize = 30; + BtnBack.Location = new System.Drawing.Point(5, 5); + BtnBack.Margin = new Padding(3, 4, 3, 4); + BtnBack.Name = "BtnBack"; + BtnBack.Size = new System.Drawing.Size(36, 34); + BtnBack.TabIndex = 0; + BtnBack.UseVisualStyleBackColor = true; + BtnBack.Click += bBack_Click; + // + // BtnDownloads + // + BtnDownloads.Anchor = AnchorStyles.Top | AnchorStyles.Right; + BtnDownloads.BackgroundImageLayout = ImageLayout.Zoom; + BtnDownloads.FlatAppearance.BorderSize = 0; + BtnDownloads.FlatStyle = FlatStyle.Flat; + BtnDownloads.ForeColor = System.Drawing.Color.White; + BtnDownloads.IconChar = IconChar.Download; + BtnDownloads.IconColor = System.Drawing.Color.Black; + BtnDownloads.IconFont = IconFont.Auto; + BtnDownloads.IconSize = 30; + BtnDownloads.Location = new System.Drawing.Point(794, 5); + BtnDownloads.Margin = new Padding(3, 4, 3, 4); + BtnDownloads.Name = "BtnDownloads"; + BtnDownloads.Size = new System.Drawing.Size(36, 34); + BtnDownloads.TabIndex = 4; + BtnDownloads.Tag = "Downloads"; + BtnDownloads.UseVisualStyleBackColor = true; + BtnDownloads.Click += bDownloads_Click; + // + // TxtURL + // + TxtURL.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + TxtURL.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); + TxtURL.Location = new System.Drawing.Point(132, 4); + TxtURL.Margin = new Padding(3, 4, 3, 4); + TxtURL.Name = "TxtURL"; + TxtURL.Size = new System.Drawing.Size(654, 29); + TxtURL.TabIndex = 5; + TxtURL.Click += TxtURL_Click; + TxtURL.Enter += TxtURL_Enter; + TxtURL.KeyDown += TxtURL_KeyDown; + // + // PanelToolbar + // + PanelToolbar.BackColor = System.Drawing.Color.FromArgb(247, 247, 247); + PanelToolbar.Controls.Add(BtnMenu); + PanelToolbar.Controls.Add(BtnHome); + PanelToolbar.Controls.Add(BtnDownloads); + PanelToolbar.Controls.Add(BtnForward); + PanelToolbar.Controls.Add(BtnBack); + PanelToolbar.Controls.Add(TxtURL); + PanelToolbar.Controls.Add(BtnRefresh); + PanelToolbar.Controls.Add(BtnStop); + PanelToolbar.Dock = DockStyle.Top; + PanelToolbar.Location = new System.Drawing.Point(0, 0); + PanelToolbar.Margin = new Padding(3, 4, 3, 4); + PanelToolbar.Name = "PanelToolbar"; + PanelToolbar.Size = new System.Drawing.Size(916, 45); + PanelToolbar.TabIndex = 6; + // + // BtnMenu + // + BtnMenu.Anchor = AnchorStyles.Top | AnchorStyles.Right; + BtnMenu.BackgroundImageLayout = ImageLayout.Zoom; + BtnMenu.FlatAppearance.BorderSize = 0; + BtnMenu.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Silver; + BtnMenu.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(224, 224, 224); + BtnMenu.FlatStyle = FlatStyle.Flat; + BtnMenu.ForeColor = System.Drawing.Color.White; + BtnMenu.IconChar = IconChar.Bars; + BtnMenu.IconColor = System.Drawing.Color.Black; + BtnMenu.IconFont = IconFont.Auto; + BtnMenu.IconSize = 30; + BtnMenu.Location = new System.Drawing.Point(874, 5); + BtnMenu.Margin = new Padding(3, 4, 3, 4); + BtnMenu.Name = "BtnMenu"; + BtnMenu.Size = new System.Drawing.Size(36, 34); + BtnMenu.TabIndex = 7; + BtnMenu.Tag = "Menu3dot"; + BtnMenu.Text = ""; + BtnMenu.UseVisualStyleBackColor = true; + BtnMenu.Click += BtnMenu_Click; + // + // BtnHome + // + BtnHome.Anchor = AnchorStyles.Top | AnchorStyles.Right; + BtnHome.BackgroundImageLayout = ImageLayout.Zoom; + BtnHome.FlatAppearance.BorderSize = 0; + BtnHome.FlatStyle = FlatStyle.Flat; + BtnHome.ForeColor = System.Drawing.Color.White; + BtnHome.IconChar = IconChar.House; + BtnHome.IconColor = System.Drawing.Color.Black; + BtnHome.IconFont = IconFont.Auto; + BtnHome.IconSize = 30; + BtnHome.Location = new System.Drawing.Point(834, 5); + BtnHome.Margin = new Padding(3, 4, 3, 4); + BtnHome.Name = "BtnHome"; + BtnHome.Size = new System.Drawing.Size(36, 34); + BtnHome.TabIndex = 6; + BtnHome.Tag = "Home"; + BtnHome.UseVisualStyleBackColor = true; + BtnHome.Click += BtnHome_Click; + // + // TabPages + // + TabPages.ContextMenuStrip = TabMenu; + TabPages.Dock = DockStyle.Fill; + TabPages.Font = new System.Drawing.Font("Segoe UI", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); + TabPages.Items.AddRange(new Controls.BrowserTabStrip.BrowserTabPage[] { tabStrip1 }); + TabPages.Location = new System.Drawing.Point(0, 45); + TabPages.Name = "TabPages"; + TabPages.Padding = new Padding(1, 41, 1, 1); + TabPages.SelectedIndex = 0; + TabPages.SelectedTab = tabStrip1; + TabPages.Size = new System.Drawing.Size(916, 427); + TabPages.TabIndex = 4; + TabPages.Text = "faTabStrip1"; + TabPages.TabStripItemSelectionChanged += OnTabsChanged; + TabPages.TabStripItemClosed += OnTabClosed; + TabPages.TabStripNewTab += OnNewTab; + // + // tabStrip1 + // + tabStrip1.Dock = DockStyle.Fill; + tabStrip1.IsDrawn = true; + tabStrip1.Location = new System.Drawing.Point(1, 41); + tabStrip1.Name = "tabStrip1"; + tabStrip1.Selected = true; + tabStrip1.Size = new System.Drawing.Size(914, 385); + tabStrip1.TabIndex = 0; + tabStrip1.Title = "Loading..."; + // + // PanelSearch + // + PanelSearch.Anchor = AnchorStyles.Top | AnchorStyles.Right; + PanelSearch.BackColor = System.Drawing.Color.White; + PanelSearch.BorderStyle = BorderStyle.FixedSingle; + PanelSearch.Controls.Add(BtnNextSearch); + PanelSearch.Controls.Add(BtnPrevSearch); + PanelSearch.Controls.Add(BtnCloseSearch); + PanelSearch.Controls.Add(TxtSearch); + PanelSearch.Location = new System.Drawing.Point(592, 115); + PanelSearch.Name = "PanelSearch"; + PanelSearch.Size = new System.Drawing.Size(307, 49); + PanelSearch.TabIndex = 9; + PanelSearch.Visible = false; + // + // BtnNextSearch + // + BtnNextSearch.Anchor = AnchorStyles.Top | AnchorStyles.Right; + BtnNextSearch.FlatStyle = FlatStyle.Flat; + BtnNextSearch.ForeColor = System.Drawing.Color.White; + BtnNextSearch.Image = (System.Drawing.Image)resources.GetObject("BtnNextSearch.Image"); + BtnNextSearch.Location = new System.Drawing.Point(239, 8); + BtnNextSearch.Margin = new Padding(3, 4, 3, 4); + BtnNextSearch.Name = "BtnNextSearch"; + BtnNextSearch.Size = new System.Drawing.Size(25, 30); + BtnNextSearch.TabIndex = 9; + BtnNextSearch.Tag = "Find next (Enter)"; + BtnNextSearch.UseVisualStyleBackColor = true; + BtnNextSearch.Click += BtnNextSearch_Click; + // + // BtnPrevSearch + // + BtnPrevSearch.Anchor = AnchorStyles.Top | AnchorStyles.Right; + BtnPrevSearch.FlatStyle = FlatStyle.Flat; + BtnPrevSearch.ForeColor = System.Drawing.Color.White; + BtnPrevSearch.Image = (System.Drawing.Image)resources.GetObject("BtnPrevSearch.Image"); + BtnPrevSearch.Location = new System.Drawing.Point(206, 8); + BtnPrevSearch.Margin = new Padding(3, 4, 3, 4); + BtnPrevSearch.Name = "BtnPrevSearch"; + BtnPrevSearch.Size = new System.Drawing.Size(25, 30); + BtnPrevSearch.TabIndex = 8; + BtnPrevSearch.Tag = "Find previous (Shift+Enter)"; + BtnPrevSearch.UseVisualStyleBackColor = true; + BtnPrevSearch.Click += BtnPrevSearch_Click; + // + // BtnCloseSearch + // + BtnCloseSearch.Anchor = AnchorStyles.Top | AnchorStyles.Right; + BtnCloseSearch.FlatStyle = FlatStyle.Flat; + BtnCloseSearch.ForeColor = System.Drawing.Color.White; + BtnCloseSearch.Image = (System.Drawing.Image)resources.GetObject("BtnCloseSearch.Image"); + BtnCloseSearch.Location = new System.Drawing.Point(272, 8); + BtnCloseSearch.Margin = new Padding(3, 4, 3, 4); + BtnCloseSearch.Name = "BtnCloseSearch"; + BtnCloseSearch.Size = new System.Drawing.Size(25, 30); + BtnCloseSearch.TabIndex = 7; + BtnCloseSearch.Tag = "Close (Esc)"; + BtnCloseSearch.UseVisualStyleBackColor = true; + BtnCloseSearch.Click += BtnClearSearch_Click; + // + // TxtSearch + // + TxtSearch.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + TxtSearch.BorderStyle = BorderStyle.None; + TxtSearch.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); + TxtSearch.Location = new System.Drawing.Point(9, 8); + TxtSearch.Margin = new Padding(3, 4, 3, 4); + TxtSearch.Name = "TxtSearch"; + TxtSearch.Size = new System.Drawing.Size(181, 25); + TxtSearch.TabIndex = 6; + TxtSearch.TextChanged += TxtSearch_TextChanged; + TxtSearch.KeyDown += TxtSearch_KeyDown; + // + // MainMenu + // + MainMenu.ImageScalingSize = new System.Drawing.Size(20, 20); + MainMenu.Items.AddRange(new ToolStripItem[] { MMNewTab, MMNextTab, MMPrevTab, toolStripSeparator1, MMPrint, MMPrintPDF, MMDownloads, MMFullscreen, MMDevTools, toolStripSeparator2, MMClose, MMCloseOther }); + MainMenu.Name = "menuStripTab"; + MainMenu.Size = new System.Drawing.Size(228, 276); + // + // MMNewTab + // + MMNewTab.IconChar = IconChar.Add; + MMNewTab.IconColor = System.Drawing.Color.Black; + MMNewTab.IconFont = IconFont.Auto; + MMNewTab.Name = "MMNewTab"; + MMNewTab.ShortcutKeyDisplayString = "Ctrl+T"; + MMNewTab.Size = new System.Drawing.Size(227, 26); + MMNewTab.Text = "New tab"; + MMNewTab.Click += MMNewTab_Click; + // + // MMNextTab + // + MMNextTab.IconChar = IconChar.ArrowRight; + MMNextTab.IconColor = System.Drawing.Color.Black; + MMNextTab.IconFont = IconFont.Auto; + MMNextTab.Name = "MMNextTab"; + MMNextTab.ShortcutKeyDisplayString = "Ctrl+Tab"; + MMNextTab.Size = new System.Drawing.Size(227, 26); + MMNextTab.Text = "Next tab"; + MMNextTab.Click += MMNextTab_Click; + // + // MMPrevTab + // + MMPrevTab.IconChar = IconChar.ArrowLeft; + MMPrevTab.IconColor = System.Drawing.Color.Black; + MMPrevTab.IconFont = IconFont.Auto; + MMPrevTab.Name = "MMPrevTab"; + MMPrevTab.ShortcutKeyDisplayString = "Ctrl+Shift+Tab"; + MMPrevTab.Size = new System.Drawing.Size(227, 26); + MMPrevTab.Text = "Previous tab"; + MMPrevTab.Click += MMPrevTab_Click; + // + // toolStripSeparator1 + // + toolStripSeparator1.Name = "toolStripSeparator1"; + toolStripSeparator1.Size = new System.Drawing.Size(224, 6); + // + // MMPrint + // + MMPrint.IconChar = IconChar.Print; + MMPrint.IconColor = System.Drawing.Color.Black; + MMPrint.IconFont = IconFont.Auto; + MMPrint.Name = "MMPrint"; + MMPrint.ShortcutKeyDisplayString = "Ctrl+P"; + MMPrint.Size = new System.Drawing.Size(227, 26); + MMPrint.Text = "Print..."; + MMPrint.Click += MMPrint_Click; + // + // MMPrintPDF + // + MMPrintPDF.IconChar = IconChar.FilePdf; + MMPrintPDF.IconColor = System.Drawing.Color.Black; + MMPrintPDF.IconFont = IconFont.Auto; + MMPrintPDF.Name = "MMPrintPDF"; + MMPrintPDF.ShortcutKeyDisplayString = "Ctrl+Shift+P"; + MMPrintPDF.Size = new System.Drawing.Size(227, 26); + MMPrintPDF.Text = "Print to PDF..."; + MMPrintPDF.Click += MMPrintPDF_Click; + // + // MMDownloads + // + MMDownloads.IconChar = IconChar.Download; + MMDownloads.IconColor = System.Drawing.Color.Black; + MMDownloads.IconFont = IconFont.Auto; + MMDownloads.Name = "MMDownloads"; + MMDownloads.Size = new System.Drawing.Size(227, 26); + MMDownloads.Text = "Downloads..."; + MMDownloads.Click += MMDownloads_Click; + // + // MMFullscreen + // + MMFullscreen.IconChar = IconChar.Expand; + MMFullscreen.IconColor = System.Drawing.Color.Black; + MMFullscreen.IconFont = IconFont.Auto; + MMFullscreen.Name = "MMFullscreen"; + MMFullscreen.ShortcutKeyDisplayString = "F11"; + MMFullscreen.Size = new System.Drawing.Size(227, 26); + MMFullscreen.Text = "Fullscreen"; + MMFullscreen.Click += MMFullscreen_Click; + // + // MMDevTools + // + MMDevTools.IconChar = IconChar.Code; + MMDevTools.IconColor = System.Drawing.Color.Black; + MMDevTools.IconFont = IconFont.Auto; + MMDevTools.Name = "MMDevTools"; + MMDevTools.ShortcutKeyDisplayString = "F12"; + MMDevTools.Size = new System.Drawing.Size(227, 26); + MMDevTools.Text = "Developer tools..."; + MMDevTools.Click += MMDevTools_Click; + // + // toolStripSeparator2 + // + toolStripSeparator2.Name = "toolStripSeparator2"; + toolStripSeparator2.Size = new System.Drawing.Size(224, 6); + // + // MMClose + // + MMClose.IconChar = IconChar.Close; + MMClose.IconColor = System.Drawing.Color.Black; + MMClose.IconFont = IconFont.Auto; + MMClose.Name = "MMClose"; + MMClose.ShortcutKeyDisplayString = "Ctrl+W"; + MMClose.Size = new System.Drawing.Size(227, 26); + MMClose.Text = "Close tab"; + MMClose.Click += MMClose_Click; + // + // MMCloseOther + // + MMCloseOther.IconChar = IconChar.Eraser; + MMCloseOther.IconColor = System.Drawing.Color.Black; + MMCloseOther.IconFont = IconFont.Auto; + MMCloseOther.Name = "MMCloseOther"; + MMCloseOther.Size = new System.Drawing.Size(227, 26); + MMCloseOther.Text = "Close other tabs"; + MMCloseOther.Click += MMCloseOther_Click; + // + // MainForm + // + AutoScaleMode = AutoScaleMode.None; + ClientSize = new System.Drawing.Size(916, 472); + Controls.Add(PanelSearch); + Controls.Add(TabPages); + Controls.Add(PanelToolbar); + Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); + Margin = new Padding(4, 5, 4, 5); + Name = "MainForm"; + StartPosition = FormStartPosition.CenterScreen; + Text = "Title"; + WindowState = FormWindowState.Maximized; + FormClosing += MainForm_FormClosing; + Load += MainForm_Load; + TabMenu.ResumeLayout(false); + PanelToolbar.ResumeLayout(false); + PanelToolbar.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)TabPages).EndInit(); + TabPages.ResumeLayout(false); + PanelSearch.ResumeLayout(false); + PanelSearch.PerformLayout(); + MainMenu.ResumeLayout(false); + ResumeLayout(false); + } - } - #endregion + #endregion private SharpBrowser.Controls.BrowserTabStrip.BrowserTabStrip TabPages; - private SharpBrowser.Controls.BrowserTabStrip.BrowserTabStripItem tabStrip1; - private SharpBrowser.Controls.BrowserTabStrip.BrowserTabStripItem tabStripAdd; - private System.Windows.Forms.Timer timer1; - private System.Windows.Forms.ContextMenuStrip menuStripTab; - private System.Windows.Forms.ToolStripMenuItem menuCloseTab; - private System.Windows.Forms.ToolStripMenuItem menuCloseOtherTabs; - private System.Windows.Forms.Button BtnForward; - private System.Windows.Forms.Button BtnBack; - private System.Windows.Forms.Button BtnStop; - private System.Windows.Forms.Button BtnRefresh; - private System.Windows.Forms.Button BtnDownloads; + private SharpBrowser.Controls.BrowserTabStrip.BrowserTabPage tabStrip1; + private System.Windows.Forms.ContextMenuStrip TabMenu; + private FontAwesome.Sharp.IconButton BtnForward; + private FontAwesome.Sharp.IconButton BtnBack; + private FontAwesome.Sharp.IconButton BtnStop; + private FontAwesome.Sharp.IconButton BtnRefresh; + private FontAwesome.Sharp.IconButton BtnDownloads; private System.Windows.Forms.TextBox TxtURL; private System.Windows.Forms.Panel PanelToolbar; - private System.Windows.Forms.Panel PanelStatus; private System.Windows.Forms.Panel PanelSearch; private System.Windows.Forms.TextBox TxtSearch; private System.Windows.Forms.Button BtnCloseSearch; private System.Windows.Forms.Button BtnPrevSearch; private System.Windows.Forms.Button BtnNextSearch; - private System.Windows.Forms.Button BtnHome; - } + private FontAwesome.Sharp.IconButton BtnHome; + private FontAwesome.Sharp.IconButton BtnMenu; + private ContextMenuStrip MainMenu; + private IconMenuItem MMClose; + private IconMenuItem MMCloseOther; + private IconMenuItem MMNewTab; + private ToolStripSeparator toolStripSeparator1; + private IconMenuItem TMReload; + private IconMenuItem TMClose; + private IconMenuItem TMCloseOther; + private IconMenuItem MMDownloads; + private IconMenuItem MMNextTab; + private IconMenuItem MMPrevTab; + private ToolStripSeparator toolStripSeparator2; + private IconMenuItem MMPrint; + private IconMenuItem MMPrintPDF; + private IconMenuItem MMDevTools; + private IconMenuItem MMFullscreen; + } } diff --git a/src/MainForm.cs b/src/MainForm.cs index 7238fc1..31e1497 100644 --- a/src/MainForm.cs +++ b/src/MainForm.cs @@ -2,103 +2,63 @@ using System.Windows.Forms; using System.Threading; using System.Diagnostics; -using System.Configuration; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Web; +using System.Linq; using CefSharp; using CefSharp.WinForms; using SharpBrowser.Controls.BrowserTabStrip; -using Timer = System.Windows.Forms.Timer; using System.Drawing; -using System.Reflection; -using SharpBrowser.Browser; -using SharpBrowser.Browser.Model; -using System.Windows.Forms.VisualStyles; +using SharpBrowser.Managers; +using SharpBrowser.Controls; +using SharpBrowser.Handlers; +using SharpBrowser.Config; +using SharpBrowser.Model; +using SharpBrowser.Utils; namespace SharpBrowser { /// - /// The main SharpBrowser form, supporting multiple tabs. - /// We used the x86 version of CefSharp, so the app works on 32-bit and 64-bit machines. - /// If you would only like to support 64-bit machines, simply change the DLL references. + /// The main SharpBrowser form supporting multiple tabs, and handling all the managers required for the browser. /// internal partial class MainForm : Form { - private string appPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\"; - public static MainForm Instance; - - public MainForm() { - Instance = this; InitializeComponent(); - InitBrowser(); - - SetFormTitle(null); - } private void MainForm_Load(object sender, EventArgs e) { - InitAppIcon(); - InitTooltips(this.Controls); - InitHotkeys(); - - } - - #region App Icon - - /// - /// embedding the resource using the Visual Studio designer results in a blurry icon. - /// the best way to get a non-blurry icon for Windows 7 apps. - /// - private void InitAppIcon() { - assembly = Assembly.GetAssembly(typeof(MainForm)); - Icon = new Icon(GetResourceStream("sharpbrowser.ico"), new Size(64, 64)); - } - - public static Assembly assembly = null; - public Stream GetResourceStream(string filename, bool withNamespace = true) { - try { - return assembly.GetManifestResourceStream("SharpBrowser.Resources." + filename); - } catch (System.Exception ex) { } - return null; - } - #endregion + // init managers + ConfigManager.Init(BrowserConfig.AppID); + DownloadManager.Init(); + BrowserManager.Init(this); + InitFavIcons(); - #region Tooltips & Hotkeys + // init the browser UI + InitBrowser(); + SetFormTitle(null); + IconManager.Init(this); + InitTooltips(this.Controls); + HotkeyManager.Init(this); + InitDisabledIcons(); + InitToolbar(); - /// - /// these hotkeys work when the user is focussed on the .NET form and its controls, - /// AND when the user is focussed on the browser (CefSharp portion) - /// - private void InitHotkeys() { + // start with the last tabs + LoadLastTabs(); - // browser hotkeys - KeyboardHandler.AddHotKey(this, CloseActiveTab, Keys.W, true); - KeyboardHandler.AddHotKey(this, CloseActiveTab, Keys.Escape, true); - KeyboardHandler.AddHotKey(this, AddBlankWindow, Keys.N, true); - KeyboardHandler.AddHotKey(this, AddBlankTab, Keys.T, true); - KeyboardHandler.AddHotKey(this, RefreshActiveTab, Keys.F5); - KeyboardHandler.AddHotKey(this, OpenDeveloperTools, Keys.F12); - KeyboardHandler.AddHotKey(this, NextTab, Keys.Tab, true); - KeyboardHandler.AddHotKey(this, PrevTab, Keys.Tab, true, true); + } - // search hotkeys - KeyboardHandler.AddHotKey(this, OpenSearch, Keys.F, true); - KeyboardHandler.AddHotKey(this, CloseSearch, Keys.Escape); - KeyboardHandler.AddHotKey(this, StopActiveTab, Keys.Escape); - KeyboardHandler.AddHotKey(this, ToggleFullscreen, Keys.F11); - } + #region Tooltips & Hotkeys /// /// we activate all the tooltips stored in the Tag property of the buttons @@ -123,61 +83,53 @@ public void InitTooltips(System.Windows.Forms.Control.ControlCollection parent) #endregion - #region Web Browser & Tabs + #region Browser UI - private BrowserTabStripItem newStrip; - private BrowserTabStripItem downloadsStrip; - private string currentFullURL; - private string currentCleanURL; - private string currentTitle; + private void SetFormTitle(string tabName) { - public HostHandler host; - private DownloadHandler dHandler; - private ContextMenuHandler mHandler; - private LifeSpanHandler lHandler; - private KeyboardHandler kHandler; - private RequestHandler rHandler; + if (tabName.CheckIfValid()) { - /// - /// this is done just once, to globally initialize CefSharp/CEF - /// - private void InitBrowser() { + this.Text = tabName + " - " + BrowserConfig.Branding; + currentTitle = tabName; - //CefSharpSettings.LegacyJavascriptBindingEnabled = true; - - Cef.EnableHighDPISupport(); - CefSettings settings = new CefSettings(); + } + else { - settings.RegisterScheme(new CefCustomScheme { - SchemeName = BrowserConfig.InternalURL, - SchemeHandlerFactory = new SchemeHandlerFactory() - }); + this.Text = BrowserConfig.Branding; + currentTitle = "New Tab"; + } - settings.UserAgent = BrowserConfig.UserAgent; - settings.AcceptLanguageList = BrowserConfig.AcceptLanguage; + } + + private void SetFormURL(string URL) { + + currentFullURL = URL; + currentCleanURL = URLUtils.CleanURL(URL); - settings.IgnoreCertificateErrors = true; - - settings.CachePath = GetAppDir("Cache"); + TxtURL.Text = currentCleanURL; - if (BrowserConfig.Proxy) { - CefSharpSettings.Proxy = new ProxyOptions(BrowserConfig.ProxyIP, - BrowserConfig.ProxyPort.ToString(), BrowserConfig.ProxyUsername, - BrowserConfig.ProxyPassword, BrowserConfig.ProxyBypassList); + if (CurTab != null) { + CurTab.CurURL = currentFullURL; } - Cef.Initialize(settings); + CloseSearch(); - dHandler = new DownloadHandler(this); - lHandler = new LifeSpanHandler(this); - mHandler = new ContextMenuHandler(this); - kHandler = new KeyboardHandler(this); - rHandler = new RequestHandler(this); + } - InitDownloads(); - host = new HostHandler(this); + #endregion + + #region Web Browser & Tabs + + private string currentFullURL; + private string currentCleanURL; + private string currentTitle; + + /// + /// this is done just once, to globally initialize CefSharp/CEF + /// + private void InitBrowser() { AddNewBrowser(tabStrip1, BrowserConfig.HomepageURL); @@ -187,27 +139,13 @@ private void InitBrowser() { /// this is done every time a new tab is openede /// private void ConfigureBrowser(ChromiumWebBrowser browser) { - - BrowserSettings config = new BrowserSettings(); - - //config.FileAccessFromFileUrls = (!CrossDomainSecurity).ToCefState(); - //config.UniversalAccessFromFileUrls = (!CrossDomainSecurity).ToCefState(); - //config.WebSecurity = WebSecurity.ToCefState(); - config.WebGl = BrowserConfig.WebGL.ToCefState(); - //config.ApplicationCache = ApplicationCache.ToCefState(); - - browser.BrowserSettings = config; + browser.BrowserSettings = BrowserConfig.GetCefConfig(); } - - private static string GetAppDir(string name) { - string winXPDir = @"C:\Documents and Settings\All Users\Application Data\"; - if (Directory.Exists(winXPDir)) { - return winXPDir + BrowserConfig.Branding + @"\" + name + @"\"; - } - return @"C:\ProgramData\" + BrowserConfig.Branding + @"\" + name + @"\"; - + private void InitFavIcons() { + FavIconManager.OnLoaded = Manager_OnFavIconLoaded; + FavIconManager.Init(); } private void LoadURL(string url) { @@ -223,25 +161,28 @@ private void LoadURL(string url) { newUrl = "http://localhost/"; - } else if (url.CheckIfFilePath() || url.CheckIfFilePath2()) { + } + else if (url.CheckIfFilePath() || url.CheckIfFilePath2()) { newUrl = url.PathToURL(); - } else { + } + else { Uri.TryCreate(url, UriKind.Absolute, out outUri); - if (!(urlLower.StartsWith("http") || urlLower.StartsWith(BrowserConfig.InternalURL))) { + if (!(urlLower.StartsWith("http") || urlLower.StartsWith(BrowserConfig.InternalScheme))) { if (outUri == null || outUri.Scheme != Uri.UriSchemeFile) newUrl = "http://" + url; } - if (urlLower.StartsWith(BrowserConfig.InternalURL + ":") || + if (urlLower.Contains("://") || // load URL if it seems valid (Uri.TryCreate(newUrl, UriKind.Absolute, out outUri) && ((outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps) && newUrl.Contains(".") || outUri.Scheme == Uri.UriSchemeFile))) { - } else { + } + else { // run search if unknown URL newUrl = BrowserConfig.SearchURL + HttpUtility.UrlEncode(url); @@ -262,97 +203,41 @@ private void LoadURL(string url) { } - private void SetFormTitle(string tabName) { - - if (tabName.CheckIfValid()) { - - this.Text = tabName + " - " + BrowserConfig.Branding; - currentTitle = tabName; - - } else { - - this.Text = BrowserConfig.Branding; - currentTitle = "New Tab"; - } - - } - - private void SetFormURL(string URL) { - - currentFullURL = URL; - currentCleanURL = CleanURL(URL); - - TxtURL.Text = currentCleanURL; - - CurTab.CurURL = currentFullURL; - - CloseSearch(); - - } - - private string CleanURL(string url) { - if (url.BeginsWith("about:")) { - return ""; - } - url = url.RemovePrefix("http://"); - url = url.RemovePrefix("https://"); - url = url.RemovePrefix("file://"); - url = url.RemovePrefix("/"); - return url.DecodeURL(); - } - private bool IsBlank(string url) { - return (url == "" || url == "about:blank"); - } - private bool IsBlankOrSystem(string url) { - return (url == "" || url.BeginsWith("about:") || url.BeginsWith("chrome:") || url.BeginsWith(BrowserConfig.InternalURL + ":")); - } - - public void AddBlankWindow() { - - // open a new instance of the browser - - ProcessStartInfo info = new ProcessStartInfo(Application.ExecutablePath, ""); - //info.WorkingDirectory = workingDir ?? exePath.GetPathDir(true); - info.LoadUserProfile = true; - - info.UseShellExecute = false; - info.RedirectStandardError = true; - info.RedirectStandardOutput = true; - info.RedirectStandardInput = true; - - Process.Start(info); - } - public void AddBlankTab() { - AddNewBrowserTab(""); - this.InvokeOnParent(delegate() { - TxtURL.Focus(); - }); - } - - public ChromiumWebBrowser AddNewBrowserTab(string url, bool focusNewTab = true, string refererUrl = null) { - return (ChromiumWebBrowser)this.Invoke((Func)delegate { + public ChromiumWebBrowser AddNewBrowserTab(string url, bool focusNewTab = true, string refererUrl = null, bool skipIfUrlAlreadyOpen = false, bool focusOnAddressBar = false) { + return Invoke((Func)delegate { // check if already exists - foreach (BrowserTabStripItem tab in TabPages.Items) { - BrowserTab tab2 = (BrowserTab)tab.Tag; - if (tab2 != null && (tab2.CurURL == url)) { - TabPages.SelectedItem = tab; - return tab2.Browser; + if (skipIfUrlAlreadyOpen) { + foreach (BrowserTabPage tab in TabPages.Items) { + BrowserTab tab2 = (BrowserTab)tab.Tag; + if (tab2 != null && (tab2.CurURL == url)) { + TabPages.SelectedTab = tab; + return tab2.Browser; + } } } - BrowserTabStripItem tabStrip = new BrowserTabStripItem(); + // new tab button and select it + BrowserTabPage tabStrip = new BrowserTabPage(); tabStrip.Title = "New Tab"; - TabPages.Items.Insert(TabPages.Items.Count - 1, tabStrip); - newStrip = tabStrip; + TabPages.AddTab(tabStrip, focusNewTab); - BrowserTab newTab = AddNewBrowser(newStrip, url); + // new browser + BrowserTab newTab = AddNewBrowser(tabStrip, url); newTab.RefererURL = refererUrl; - if (focusNewTab) timer1.Enabled = true; + + // focus on address bar + if (focusOnAddressBar) { + Thread.Sleep(1000); + TxtURL.Focus(); + } + return newTab.Browser; }); } - private BrowserTab AddNewBrowser(BrowserTabStripItem tabStrip, String url) { + + Panel pnlToolbarOverlay; + private BrowserTab AddNewBrowser(BrowserTabPage tabStrip, String url) { if (url == "") url = BrowserConfig.NewTabURL; ChromiumWebBrowser browser = new ChromiumWebBrowser(url); @@ -362,22 +247,25 @@ private BrowserTab AddNewBrowser(BrowserTabStripItem tabStrip, String url) { // set layout browser.Dock = DockStyle.Fill; tabStrip.Controls.Add(browser); + pnlToolbarOverlay = new Panel() { + Width = PanelToolbar.Width, + Height = PanelToolbar.Height, + Dock = DockStyle.Top, + }; + tabStrip.Controls.Add(pnlToolbarOverlay); browser.BringToFront(); // add events - browser.StatusMessage += Browser_StatusMessage; + //browser.StatusMessage += Browser_StatusMessage; browser.LoadingStateChanged += Browser_LoadingStateChanged; + browser.FrameLoadEnd += Browser_FrameLoadEnd; browser.TitleChanged += Browser_TitleChanged; browser.LoadError += Browser_LoadError; browser.AddressChanged += Browser_URLChanged; - browser.DownloadHandler = dHandler; - browser.MenuHandler = mHandler; - browser.LifeSpanHandler = lHandler; - browser.KeyboardHandler = kHandler; - browser.RequestHandler = rHandler; + BrowserManager.SetupHandlers(browser); // new tab obj - BrowserTab tab = new BrowserTab { + BrowserTab tab = new BrowserTab() { IsOpen = true, Browser = browser, Tab = tabStrip, @@ -390,14 +278,16 @@ private BrowserTab AddNewBrowser(BrowserTabStripItem tabStrip, String url) { // save tab obj in tabstrip tabStrip.Tag = tab; - if (url.StartsWith(BrowserConfig.InternalURL + ":")) { - browser.JavascriptObjectRepository.Register("host", host, BindingOptions.DefaultBinder); + // handle downloads page + if (url.StartsWith(BrowserConfig.InternalScheme + ":")) { + browser.JavascriptObjectRepository.Register("host", BrowserManager._HostHandler, BindingOptions.DefaultBinder); } + return tab; } public BrowserTab GetTabByBrowser(IWebBrowser browser) { - foreach (BrowserTabStripItem tab2 in TabPages.Items) { + foreach (BrowserTabPage tab2 in TabPages.Items) { BrowserTab tab = (BrowserTab)(tab2.Tag); if (tab != null && tab.Browser == browser) { return tab; @@ -406,111 +296,46 @@ public BrowserTab GetTabByBrowser(IWebBrowser browser) { return null; } - public void RefreshActiveTab() { - CurBrowser.Load(CurBrowser.Address); - } - - public void CloseActiveTab() { - if (CurTab != null/* && TabPages.Items.Count > 2*/) { - - // remove tab and save its index - int index = TabPages.Items.IndexOf(TabPages.SelectedItem); - TabPages.RemoveTab(TabPages.SelectedItem); - - // keep tab at same index focussed - if ((TabPages.Items.Count - 1) > index) { - TabPages.SelectedItem = TabPages.Items[index]; - } - } - } - private FormWindowState oldWindowState; - private FormBorderStyle oldBorderStyle; - private bool isFullScreen = false; - private void ToggleFullscreen() - { + private void OnTabClosed(object sender, EventArgs e) { - if (!isFullScreen) - { - oldWindowState = this.WindowState; - oldBorderStyle = this.FormBorderStyle; - this.FormBorderStyle = FormBorderStyle.None; - this.WindowState = FormWindowState.Maximized; - isFullScreen = true; + // if the very last tab is closed + if (TabPages.Items.Count < 1) { + OnLastTabClosed(); } - else - { - this.FormBorderStyle = oldBorderStyle; - this.WindowState = oldWindowState; - isFullScreen = false; - } - } - private void OnTabClosed(object sender, EventArgs e) { - } - private void OnTabClosing(SharpBrowser.Controls.BrowserTabStrip.TabStripItemClosingEventArgs e) { + private void OnLastTabClosed() { - // exit if invalid tab - if (CurTab == null){ - e.Cancel = true; - return; - } - - // add a blank tab if the very last tab is closed! - if (TabPages.Items.Count <= 2) { - AddBlankTab(); - //e.Cancel = true; - } + // close the window + this.Close(); + // the new tab is bugged so this is disabled + //AddNewTabInternal(BrowserConfig.NewTabURL, null); } - private void StopActiveTab() { - CurBrowser.Stop(); - } + public void StopActiveTab() => CurBrowser.Stop(); private bool IsOnFirstTab() { - return TabPages.SelectedItem == TabPages.Items[0]; + return TabPages.SelectedTab == TabPages.Items[0]; } private bool IsOnLastTab() { - return TabPages.SelectedItem == TabPages.Items[TabPages.Items.Count - 2]; + return TabPages.SelectedTab == TabPages.Items[TabPages.Items.Count - 1]; } - private int CurIndex { - get { - return TabPages.Items.IndexOf(TabPages.SelectedItem); - } - set { - TabPages.SelectedItem = TabPages.Items[value]; - } - } private int LastIndex { get { - return TabPages.Items.Count - 2; - } - } - - private void NextTab() { - if (IsOnLastTab()) { - CurIndex = 0; - } else { - CurIndex++; - } - } - private void PrevTab() { - if (IsOnFirstTab()) { - CurIndex = LastIndex; - } else { - CurIndex--; + return TabPages.Items.Count - 1; } } public ChromiumWebBrowser CurBrowser { get { - if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { - return ((BrowserTab)TabPages.SelectedItem.Tag).Browser; - } else { + if (TabPages.SelectedTab != null && TabPages.SelectedTab.Tag != null) { + return ((BrowserTab)TabPages.SelectedTab.Tag).Browser; + } + else { return null; } } @@ -518,16 +343,17 @@ public ChromiumWebBrowser CurBrowser { public BrowserTab CurTab { get { - if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { - return ((BrowserTab)TabPages.SelectedItem.Tag); - } else { + if (TabPages.SelectedTab != null && TabPages.SelectedTab.Tag != null) { + return ((BrowserTab)TabPages.SelectedTab.Tag); + } + else { return null; } } } public List GetAllTabs() { List tabs = new List(); - foreach (BrowserTabStripItem tabPage in TabPages.Items) { + foreach (BrowserTabPage tabPage in TabPages.Items) { if (tabPage.Tag != null) { tabs.Add((BrowserTab)tabPage.Tag); } @@ -535,24 +361,180 @@ public List GetAllTabs() { return tabs; } - public int CurTabLoadingDur { - get { - if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { - int loadTime = (int)(DateTime.Now - CurTab.DateCreated).TotalMilliseconds; - return loadTime; - } else { - return 0; + public bool IsFavIconLoaded(ChromiumWebBrowser browser) { + var tab = GetTabByBrowser(browser); + return (tab != null && tab.FavIcon != null); + } + + private void SetTabTitle(ChromiumWebBrowser browser, string text) { + + text = text.Trim(); + if (URLUtils.IsBlank(text)) { + text = "New Tab"; + } + + // save text + browser.Tag = text; + + // get tab of given browser + BrowserTabPage tabStrip = (BrowserTabPage)browser.Parent; + if (tabStrip != null) //fix error, when fast hit on close button + tabStrip.Title = text; + + // if current tab + if (browser == CurBrowser) { + + SetFormTitle(text); + + } + } + + public void InvokeIfNeeded(Action action) { + if (this.InvokeRequired) { + this.BeginInvoke(action); + } + else { + action.Invoke(); + } + } + + public void WaitForBrowserToInitialize(ChromiumWebBrowser browser) { + while (!browser.IsBrowserInitialized) { + Thread.Sleep(100); + } + } + + private void EnableBackButton(bool canGoBack) { + InvokeIfNeeded(() => BtnBack.Enabled = canGoBack); + } + private void EnableForwardButton(bool canGoForward) { + InvokeIfNeeded(() => BtnForward.Enabled = canGoForward); + } + + private async void OnNewTab(object o, EventArgs e) { + AddBlankTab(); + } + private async void OnTabsChanged(TabStripItemChangedEventArgs e) { + ChromiumWebBrowser browser = null; + + try { + // apparently this 'fix' reduces frequent exceptions + if (e.Item.Controls.Count >= 1 && e.Item.Controls[0] as ChromiumWebBrowser != null) + browser = ((ChromiumWebBrowser)e.Item.Controls[0]); + } + catch (System.Exception ex) { + } + + + if (e.ChangeType == BrowserTabStripItemChangeTypes.SelectionChanged) { + Tabs_OnSwitchedTab(); + + } + + if (e.ChangeType == BrowserTabStripItemChangeTypes.Removed) { + if (browser != null) { + browser.Dispose(); } } + } - private void Browser_URLChanged(object sender, AddressChangedEventArgs e) { + + private void TMReload_Click(object sender, EventArgs e) { + RefreshActiveTab(); + } + private void TMCloseTab_Click(object sender, EventArgs e) { + CloseActiveTab(); + } + + private void TMCloseOtherTabs_Click(object sender, EventArgs e) { + CloseOtherTabs(); + } + + + private void bBack_Click(object sender, EventArgs e) { Back(); } + + + private void bForward_Click(object sender, EventArgs e) { Forward(); } + + + private void bDownloads_Click(object sender, EventArgs e) { + OpenDownloads(); + } + + private void bRefresh_Click(object sender, EventArgs e) => RefreshActiveTab(); + + private void bStop_Click(object sender, EventArgs e) => StopActiveTab(); + + + #endregion + + #region Web Browser Events + + private ChromiumWebBrowser Tabs_OnSwitchedTab() { + ChromiumWebBrowser browser = CurBrowser; + if (browser != null) { + + // load the text/URL from this tab into the window + SetFormURL(browser.Address); + SetFormTitle(browser.Tag.ConvertToString() ?? "New Tab"); + + EnableBackButton(browser.CanGoBack); + EnableForwardButton(browser.CanGoForward); + + // focus on the browser + // FIX: important for hotkeys to work (for example a chain of Ctrl+W, Ctrl+W) + browser.Focus(); + } + else { + + // when a new tab is just created, the browser does not exist, so show default text/URL + SetFormURL(""); + SetFormTitle("Loading..."); + + EnableBackButton(false); + EnableForwardButton(false); + + } + + return browser; + } + + private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) { + + // for any tab, even background tabs, download the favicon + if (e.Frame.IsMain) { + + if (!URLUtils.IsBlankOrSystem(e.Url)) { + + FavIconManager.LoadFavicon(sender as ChromiumWebBrowser, false); + } + } + } + + private void Manager_OnFavIconLoaded(ChromiumWebBrowser browser, byte[] iconData) { InvokeIfNeeded(() => { + var tab = GetTabByBrowser(browser); + if (tab != null && tab.Tab != null) { + + Bitmap bitmap = null; + if (iconData != null) { + bitmap = new Bitmap(new MemoryStream(iconData)); + } + tab.FavIcon = bitmap; + tab.Tab.Image = bitmap; + } + + }); + } + + private void Browser_URLChanged(object sender, AddressChangedEventArgs e) { + InvokeIfNeeded(() => { // if current tab if (sender == CurBrowser) { - if (!Utils.IsFocussed(TxtURL)) { + if (!WinFormsUtils.IsFocussed(TxtURL)) { SetFormURL(e.Address); } @@ -585,30 +567,11 @@ private void Browser_TitleChanged(object sender, TitleChangedEventArgs e) { }); } - private void SetTabTitle(ChromiumWebBrowser browser, string text) { - - text = text.Trim(); - if (IsBlank(text)) { - text = "New Tab"; - } - - // save text - browser.Tag = text; - - // get tab of given browser - BrowserTabStripItem tabStrip = (BrowserTabStripItem)browser.Parent; - tabStrip.Title = text; - - - // if current tab - if (browser == CurBrowser) { + private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { - SetFormTitle(text); + var browser = sender as ChromiumWebBrowser; - } - } - - private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { + // if its the current tab, update browser UI based on its state if (sender == CurBrowser) { EnableBackButton(e.CanGoBack); @@ -619,7 +582,8 @@ private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEvent // set title //SetTabTitle(); - } else { + } + else { // after loaded / stopped InvokeIfNeeded(() => { @@ -628,166 +592,138 @@ private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEvent }); } } - } - public void InvokeIfNeeded(Action action) { - if (this.InvokeRequired) { - this.BeginInvoke(action); - } else { - action.Invoke(); - } - } - - private void Browser_StatusMessage(object sender, StatusMessageEventArgs e) { - } - - public void WaitForBrowserToInitialize(ChromiumWebBrowser browser) { - while (!browser.IsBrowserInitialized) { - Thread.Sleep(100); + // for any tab, check favicons + if (!URLUtils.IsBlankOrSystem(browser.Address)) { + if (!IsFavIconLoaded(browser)) { + FavIconManager.LoadFavicon(sender as ChromiumWebBrowser, true); + } } - } - private void EnableBackButton(bool canGoBack) { - InvokeIfNeeded(() => BtnBack.Enabled = canGoBack); - } - private void EnableForwardButton(bool canGoForward) { - InvokeIfNeeded(() => BtnForward.Enabled = canGoForward); } - private void OnTabsChanged(TabStripItemChangedEventArgs e) { - - - ChromiumWebBrowser browser = null; - try { - browser = ((ChromiumWebBrowser)e.Item.Controls[0]); - } catch (System.Exception ex) { } - + private void Browser_StatusMessage(object sender, StatusMessageEventArgs e) { } - if (e.ChangeType == BrowserTabStripItemChangeTypes.SelectionChanged) { - if (TabPages.SelectedItem == tabStripAdd) { - AddBlankTab(); - } else { - browser = CurBrowser; + #endregion - SetFormURL(browser.Address); - SetFormTitle(browser.Tag.ConvertToString() ?? "New Tab"); + #region Web Browser Commands + public void AddBlankWindow() { - EnableBackButton(browser.CanGoBack); - EnableForwardButton(browser.CanGoForward); + // DISABLED BECAUSE 2 CEFSHARP INSTANCES CAUSES A CRASH - } - } + /*// open a new instance of the browser - if (e.ChangeType == BrowserTabStripItemChangeTypes.Removed) { - if (e.Item == downloadsStrip) downloadsStrip = null; - if (browser != null) { - browser.Dispose(); - } - } + ProcessStartInfo info = new ProcessStartInfo(Application.ExecutablePath, ""); + //info.WorkingDirectory = workingDir ?? exePath.GetPathDir(true); + info.LoadUserProfile = true; - if (e.ChangeType == BrowserTabStripItemChangeTypes.Changed) { - if (browser != null) { - if (currentFullURL != "about:blank") { - browser.Focus(); - } - } - } + info.UseShellExecute = false; + info.RedirectStandardError = true; + info.RedirectStandardOutput = true; + info.RedirectStandardInput = true; + Process.Start(info);*/ } - - private void timer1_Tick(object sender, EventArgs e) { - TabPages.SelectedItem = newStrip; - timer1.Enabled = false; + public void AddBlankTab() { + AddNewBrowserTab(BrowserConfig.NewTabURL, true, null, false, true); } - private void menuCloseTab_Click(object sender, EventArgs e) { - CloseActiveTab(); + public void Back() { + CurBrowser.Back(); } - - private void menuCloseOtherTabs_Click(object sender, EventArgs e) { - List listToClose = new List(); - foreach (BrowserTabStripItem tab in TabPages.Items) { - if (tab != tabStripAdd && tab != TabPages.SelectedItem) listToClose.Add(tab); + public void Forward() { + CurBrowser.Forward(); + } + public void OpenDownloads() { + AddNewBrowserTab(BrowserConfig.DownloadsURL, true, null, true); + } + public void OpenDeveloperTools() { + CurBrowser.ShowDevTools(); + } + public void CloseOtherTabs() { + List listToClose = new List(); + foreach (BrowserTabPage tab in TabPages.Items) { + if (tab != TabPages.SelectedTab) listToClose.Add(tab); } - foreach (BrowserTabStripItem tab in listToClose) { + foreach (BrowserTabPage tab in listToClose) { TabPages.RemoveTab(tab); } - } - public List CancelRequests { - get { - return downloadCancelRequests; - } - } + public void RefreshActiveTab() => CurBrowser.Load(CurBrowser.Address); - private void bBack_Click(object sender, EventArgs e) { - CurBrowser.Back(); - } + public void CloseActiveTab() { - private void bForward_Click(object sender, EventArgs e) { - CurBrowser.Forward(); - } + // if any tab is open + var curTab = TabPages.SelectedTab; + if (CurTab != null) { - private void txtUrl_TextChanged(object sender, EventArgs e) { + // if this is the last tab, open a new tab also + if (TabPages.Items.Count <= 1) { + OnLastTabClosed(); + } - } + // remove tab and save its index + int index = TabPages.Items.IndexOf(curTab); + TabPages.RemoveTab(curTab); - private void bDownloads_Click(object sender, EventArgs e) { - AddNewBrowserTab(BrowserConfig.DownloadsURL); + // keep tab at same index focussed + if (TabPages.Items.Count > 1) { + if ((TabPages.Items.Count - 1) > index) { + TabPages.SelectedTab = TabPages.Items[index]; + } + } + } } - - private void bRefresh_Click(object sender, EventArgs e) { - RefreshActiveTab(); + public void NextTab() { + if (IsOnLastTab()) { + TabPages.SelectedIndex = 0; + } + else { + TabPages.SelectedIndex++; + } } - - private void bStop_Click(object sender, EventArgs e) { - StopActiveTab(); + public void PrevTab() { + if (IsOnFirstTab()) { + TabPages.SelectedIndex = LastIndex; + } + else { + TabPages.SelectedIndex--; + } + } + public void Print() { + CurBrowser.Print(); + } + public void PrintToPDF() { + ContextMenuHandler.SaveAsPDF(CurBrowser.GetBrowser()); } - private void TxtURL_KeyDown(object sender, KeyEventArgs e) { - // if ENTER or CTRL+ENTER pressed - if (e.IsHotkey(Keys.Enter) || e.IsHotkey(Keys.Enter, true)) { - LoadURL(TxtURL.Text); - // im handling this - e.Handled = true; - e.SuppressKeyPress = true; - - // defocus from url textbox - this.Focus(); - } + #endregion - // if full URL copied - if (e.IsHotkey(Keys.C, true) && Utils.IsFullySelected(TxtURL)) { + #region Fullscreen - // copy the real URL, not the pretty one - Clipboard.SetText(CurBrowser.Address, TextDataFormat.UnicodeText); + private FormWindowState oldWindowState; + private FormBorderStyle oldBorderStyle; + private bool isFullScreen = false; + public void ToggleFullscreen() { - // im handling this - e.Handled = true; - e.SuppressKeyPress = true; + if (!isFullScreen) { + oldWindowState = this.WindowState; + oldBorderStyle = this.FormBorderStyle; + this.FormBorderStyle = FormBorderStyle.None; + this.WindowState = FormWindowState.Maximized; + isFullScreen = true; } - } - - private void txtUrl_Click(object sender, EventArgs e) { - if (!Utils.HasSelection(TxtURL)) { - TxtURL.SelectAll(); + else { + this.FormBorderStyle = oldBorderStyle; + this.WindowState = oldWindowState; + isFullScreen = false; } } - private void OpenDeveloperTools() { - CurBrowser.ShowDevTools(); - } - - private void tabPages_MouseClick(object sender, MouseEventArgs e) { - /*if (e.Button == System.Windows.Forms.MouseButtons.Right) { - tabPages.GetTabItemByPoint(this.mouse - }*/ - } - #endregion #region Download Queue @@ -795,92 +731,109 @@ private void tabPages_MouseClick(object sender, MouseEventArgs e) { private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { // ask user if they are sure - if (DownloadsInProgress()) { + if (DownloadManager.DownloadsInProgress()) { if (MessageBox.Show("Downloads are in progress. Cancel those and exit?", "Confirm exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) { e.Cancel = true; return; } } + // save tabs + SaveTabsBeforeClosing(); + // dispose all browsers try { foreach (TabPage tab in TabPages.Items) { ChromiumWebBrowser browser = (ChromiumWebBrowser)tab.Controls[0]; browser.Dispose(); } - } catch (System.Exception ex) { } + } + catch (System.Exception ex) { } } - public Dictionary downloads; - public Dictionary downloadNames; - public List downloadCancelRequests; - /// - /// we must store download metadata in a list, since CefSharp does not + /// open a new tab with the downloads URL /// - private void InitDownloads() { + private void btnDownloads_Click(object sender, EventArgs e) => OpenDownloads(); + + #endregion + + #region Toolbar + + private void InitToolbar() { + TxtURL.ToBordered(); + PanelToolbar.Dock = DockStyle.None; + PanelToolbar.BringToFront(); + PanelToolbar.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + PanelToolbar.Location = new Point(0, TabPages.TabButton_Height + 1); //for different dpi, need stable way to get it. + PanelToolbar.Width = this.Width; + PanelToolbar.Width = pnlToolbarOverlay.Width; - downloads = new Dictionary(); - downloadNames = new Dictionary(); - downloadCancelRequests = new List(); + if (Debugger.IsAttached) + pnlToolbarOverlay.BackColor = Color.Cyan; + new CircularDownloadProgress(BtnDownloads); } - public Dictionary Downloads { - get { - return downloads; - } + private void InitDisabledIcons() { + BtnBack.EnabledChanged += (s1, e1) => SetIconByState(s1); + BtnForward.EnabledChanged += (s1, e1) => SetIconByState(s1); + BtnStop.EnabledChanged += (s1, e1) => SetIconByState(s1); + BtnRefresh.EnabledChanged += (s1, e1) => SetIconByState(s1); + BtnDownloads.EnabledChanged += (s1, e1) => SetIconByState(s1); + BtnHome.EnabledChanged += (s1, e1) => SetIconByState(s1); + BtnMenu.EnabledChanged += (s1, e1) => SetIconByState(s1); + } + private async void SetIconByState(object senderBtn) { + var faBtn = senderBtn as FontAwesome.Sharp.IconButton; + //faBtn.IconColor = faBtn.Enabled ? Color.Black : PanelToolbar.BackColor.ChangeColorBrightness(0); + faBtn.IconColor = faBtn.Enabled ? Color.Black : Color.Black.ChangeColorBrightness(0.8); } - public void UpdateDownloadItem(DownloadItem item) { - lock (downloads) { + #endregion - // SuggestedFileName comes full only in the first attempt so keep it somewhere - if (item.SuggestedFileName != "") { - downloadNames[item.Id] = item.SuggestedFileName; - } + #region Address Bar - // Set it back if it is empty - if (item.SuggestedFileName == "" && downloadNames.ContainsKey(item.Id)) { - item.SuggestedFileName = downloadNames[item.Id]; - } + private void TxtURL_KeyDown(object sender, KeyEventArgs e) { - downloads[item.Id] = item; + // if ENTER or CTRL+ENTER pressed + if (e.IsHotkey(Keys.Enter) || e.IsHotkey(Keys.Enter, true)) { + LoadURL(TxtURL.Text); + + // im handling this + e.Handled = true; + e.SuppressKeyPress = true; - //UpdateSnipProgress(); + // defocus from url textbox + this.Focus(); } - } - public string CalcDownloadPath(DownloadItem item) { - return item.SuggestedFileName; - } + // if full URL copied + if (e.IsHotkey(Keys.C, true) && WinFormsUtils.IsFullySelected(TxtURL)) { - public bool DownloadsInProgress() { - foreach (DownloadItem item in downloads.Values) { - if (item.IsInProgress) { - return true; - } + // copy the real URL, not the pretty one + Clipboard.SetText(CurBrowser.Address, TextDataFormat.UnicodeText); + + // im handling this + e.Handled = true; + e.SuppressKeyPress = true; } - return false; } - /// - /// open a new tab with the downloads URL - /// - private void btnDownloads_Click(object sender, EventArgs e) { - OpenDownloadsTab(); + private bool TxtURL_JustEntered = false; + private void TxtURL_Enter(object sender, EventArgs e) { + TxtURL.SelectAll(); + TxtURL_JustEntered = true; } - - public void OpenDownloadsTab() { - if (downloadsStrip != null && ((ChromiumWebBrowser)downloadsStrip.Controls[0]).Address == BrowserConfig.DownloadsURL) { - TabPages.SelectedItem = downloadsStrip; - } else { - ChromiumWebBrowser brw = AddNewBrowserTab(BrowserConfig.DownloadsURL); - downloadsStrip = (BrowserTabStripItem)brw.Parent; + private void TxtURL_Click(object sender, EventArgs e) { + if (TxtURL_JustEntered) { + TxtURL.SelectAll(); } + TxtURL_JustEntered = false; } + #endregion #region Search Bar @@ -888,57 +841,51 @@ public void OpenDownloadsTab() { bool searchOpen = false; string lastSearch = ""; - private void OpenSearch() { + public void OpenSearch() { if (!searchOpen) { searchOpen = true; - InvokeIfNeeded(delegate() { + InvokeIfNeeded(delegate () { PanelSearch.Visible = true; TxtSearch.Text = lastSearch; TxtSearch.Focus(); TxtSearch.SelectAll(); }); - } else { - InvokeIfNeeded(delegate() { + } + else { + InvokeIfNeeded(delegate () { TxtSearch.Focus(); TxtSearch.SelectAll(); }); } } - private void CloseSearch() { + public void CloseSearch() { if (searchOpen) { searchOpen = false; - InvokeIfNeeded(delegate() { + InvokeIfNeeded(delegate () { PanelSearch.Visible = false; CurBrowser.GetBrowser().StopFinding(true); }); } } - private void BtnClearSearch_Click(object sender, EventArgs e) { - CloseSearch(); - } + private void BtnClearSearch_Click(object sender, EventArgs e) => CloseSearch(); - private void BtnPrevSearch_Click(object sender, EventArgs e) { - FindTextOnPage(false); - } - private void BtnNextSearch_Click(object sender, EventArgs e) { - FindTextOnPage(true); - } + private void BtnPrevSearch_Click(object sender, EventArgs e) => FindTextOnPage(false); + private void BtnNextSearch_Click(object sender, EventArgs e) => FindTextOnPage(true); - private void FindTextOnPage(bool next = true) { + public void FindTextOnPage(bool next = true) { bool first = lastSearch != TxtSearch.Text; lastSearch = TxtSearch.Text; if (lastSearch.CheckIfValid()) { CurBrowser.GetBrowser().Find(lastSearch, true, false, !first); - } else { + } + else { CurBrowser.GetBrowser().StopFinding(true); } TxtSearch.Focus(); } - private void TxtSearch_TextChanged(object sender, EventArgs e) { - FindTextOnPage(true); - } + private void TxtSearch_TextChanged(object sender, EventArgs e) => FindTextOnPage(true); private void TxtSearch_KeyDown(object sender, KeyEventArgs e) { if (e.IsHotkey(Keys.Enter)) { @@ -951,16 +898,82 @@ private void TxtSearch_KeyDown(object sender, KeyEventArgs e) { - #endregion + #endregion + + #region Home Button + private void BtnHome_Click(object sender, EventArgs e) => CurBrowser.Load(BrowserConfig.HomepageURL); + #endregion + + #region Main Menu Button + + private void BtnMenu_Click(object sender, EventArgs e) { + var buttonScreenPoint = BtnMenu.PointToScreen(Point.Empty); + int x = buttonScreenPoint.X + BtnMenu.Width - MainMenu.Width; + int y = buttonScreenPoint.Y + BtnMenu.Height; + + MainMenu.Show(x, y); + } + + + private void MMNewTab_Click(object sender, EventArgs e) => AddBlankTab(); + private void MMNewWindow_Click(object sender, EventArgs e) => AddBlankWindow(); + private void MMNextTab_Click(object sender, EventArgs e) => NextTab(); + private void MMPrevTab_Click(object sender, EventArgs e) => PrevTab(); + + private void MMDownloads_Click(object sender, EventArgs e) => OpenDownloads(); + private void MMPrint_Click(object sender, EventArgs e) => Print(); + private void MMPrintPDF_Click(object sender, EventArgs e) => PrintToPDF(); + + private void MMClose_Click(object sender, EventArgs e) => CloseActiveTab(); + private void MMCloseOther_Click(object sender, EventArgs e) => CloseOtherTabs(); + + private void MMDevTools_Click(object sender, EventArgs e) => OpenDeveloperTools(); + private void MMFullscreen_Click(object sender, EventArgs e) => ToggleFullscreen(); + #endregion + + #region Saved Settings + + private void LoadLastTabs() { + if (BrowserConfig.SaveOpenTabs) { + + // load last tabs + var tabs = ConfigManager.GetString("browser.lastTabs", "").Split("||"); + + // open them all + var added = false; + foreach (var url in tabs) { + if (url.Length > 0) { + AddNewBrowserTab(url, false); + added = true; + } + } + + // close the default tab if tabs were restored + if (added) { + CloseActiveTab(); + } + + // switch to the last active tab + TabPages.SelectedIndex = ConfigManager.GetInt("browser.lastTab", 0); + } + } + private void SaveTabsBeforeClosing() { + if (BrowserConfig.SaveOpenTabs) { + + // get current URLs + var urls = TabPages.Tabs.Select(t => (t.Tag as BrowserTab).CurURL).ToList().Join("||"); + + // save in settings + ConfigManager.Set("browser.lastTabs", urls); + ConfigManager.Set("browser.lastTab", TabPages.SelectedIndex); + ConfigManager.SaveSettings(); + } + } + + #endregion - #region Home Button - private void BtnHome_Click(object sender, EventArgs e) - { - CurBrowser.Load(BrowserConfig.HomepageURL); - } - #endregion - } -} + } +} \ No newline at end of file diff --git a/src/MainForm.resx b/src/MainForm.resx index 4cc1acb..89fa46a 100644 --- a/src/MainForm.resx +++ b/src/MainForm.resx @@ -1,17 +1,17 @@  - @@ -117,74 +117,19 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 325, 17 + + 856, 17 - - - - iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAAQhJREFUOE/V - lNERwiAQROnAEizBEvzxyzFJCZZgCZaSEixAiCVYQkpICcpCIHdwYH50xjezo0M2Cwdc1O9ohk51pled - fmRqhv3sWsFp2NmXnjbsVZcNhhe4ie1YxuG+WRdGpW/xP0MKa83VlUeFsc5MzBfE8EZq6OcnOW5yMyZ+ - EugNdNZyGGj1hXgXRVp9Xh7YsmuUwqBI2JuwZzWoN9UfcTRbsYRP5QPsqfPbc4jQy8k1zQ4Zdpj0Zrge - ZUHQFFtLIm0EVo0UiDJqsKrSqyavEOrdzFgNwK+/h7xLskqWQHSL3KclscMI+OX7PcOJl1dMZMvEN1ME - IRAFZuxj/hEY3XjYhu+i1BtXQGc6/upxWQAAAABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAATlJREFUOE+9 - U9uNAkEMSweUQAmUQAmUQAmUcCXwy3FIWwIlUMKVQAlbAtg5BzIPdvk4YSnSyuN4PJlZ+xi+zRY/ZhvU - 19HsUtVasnnQCLVH022qoDkfzFZq64NmEP/WzRM1vjRVsmuI+a3jFsejAdOFDjXWGgdIziZEg+gGNMQ6 - TULrm2v5DxLF4ll0g9osn4gXKJkfd5tEW9EFOskGaDnz4J6n0qxceDJbin6gZ6Yljmogx7SiPKEPuSCF - KTMihxFVXMhFlGPOjECIx5sV9dwlJ3zHjADvYYrTgdipyWf4rhlMeCmtRgaxwB1nzYg8v+LZECB7v9xL - M6TLT625zN7MxmZXgCOBQf712nQBmnK3LEYxOccQlddGJlV7HxBw0HVjU9yYAdQ2DzSt0cQ3lsfA4i83 - ner/YHYH1F8a8lpKiKEAAAAASUVORK5CYII= - - - - - iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAAQtJREFUOE+9 - lNERwiAMhtnAERzBEXzxybMwgiM4giM4giM4gFBHcARH6Aiav0BLaLD0vPO7+19o+CEhqfoPe7dWxt6U - cR3pPZV9KG1PIXqGpjW0qWCUiw7d3Vdhp4C2R3kjDrBPYZ1EtxVp2q28gcwO7aaPMe6affPS7tx/Z+Ck - mmDZtOOp4wbToCBKNa+TZMoeCbfIA7jGtCMT07SWabraXcLqPLnpQGqIx1mCca/vhmidWnztx54dQJpx - saT8oNwMjzfgp4MbpJo1Q0xe+7QWPJD3oWQGYf4ZxUmheY2UzMRJAaXRgqnv1amZ1PiMmgcaNGcW8ekX - bguRUfX/MAWFxkakG7W08X9DqQ9C2Z1KjUXdLwAAAABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAAP9JREFUOE+9 - U8ERwiAQpANLsARL8OPLMVCCJViCJViCJViAEEtICZZgCcoSwnBwJ8knO7MzmWRvuSN7ah0c3VYZ+1DG - fTy/Ne1LaXuJ6ga63vgiwaikP/Tw3MRKBtqe+UL39sUD897Td8ui6/dCwZC6QDGn0e4avhOw4swMwHOl - CfzQ0U/9rhYVZoBx91oXSX4SWiYCrjNh3MT8LomY6UxCOVlCbogMLkHTcHZgI1hD7W7kA/KYYxytEXZ/ - VQnjdlDBUlM0RRC2oRSxplQzsbp7aVNyU2k12U0BpOBinDGrzMitmJU/6C/nZnYcX14zGC2NVwAuGoUY - dyIOWw9K/QCyNJ4sXzBxfQAAAABJRU5ErkJggg== - - - - 238, 17 + + 17, 17 - - - iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAAM5JREFUOE/l - k20NwjAUReugEpBAP4QgYRKQgAQkTMIkTAISJgEJcO9yGzqybqW/ILzkJO3be6fduprfjxDCFGN8EI6V - bo8kSyjdHn8gRJMFAxjFQqgcGbz3R7WVA4UWp3lX8yZVQgYLK6SdyutiR/qZLMW7lOOq10TRAQ1nNFxy - 2JykJJsv6thLh3Tz9erTLnKQ57WzLCYcK7dW20s3n+za75EKJ+fciZRkYpRuW1hg/g855s60ULsQgivg - t+O8AzfwEnIVPWyGDum+Oox5AmJH+ASE2L9zAAAAAElFTkSuQmCC - - - - - iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAS - dAAAEnQB3mYfeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEPSURBVDhP1ZIx - DkRAFIYdQekEnECh4yIUCp2IWqJRq/QSR5AonMAVhNoJHOBt3lszIZ7dodhk/+SPNzP//yUzoYGC+r4n - q+grMM9z0DSNjDNqmiZomga6roO2bWEYBtpHfQT6vi9hwkEQ0JnjOId9oUug67qHwt6e51EmSRK5J8QC - TdM8ADhblkXZqqpoLXQC6rp+Kl8Zs6i6rumLOgC5kor3otW6rmzwjpFBwGVZ2MATbyz+8LGLogDbtsEw - DD6gYOwiA1nyRbMsY8Mqxq6QBKZpyoZVjF2h3wP317l6llvAOI63BNDMZf4UGEURGw7DcEsAzVwGu0IS - OM8z/ZhlWUrjehzHLQE0cxnsvgXwAvr4W8D8LvUNAAAAAElFTkSuQmCC - - + + 124, 17 + + + 224, 17 + + iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAABGdBTUEAALGPC/xhBQAAAJpJREFUSEtj @@ -210,4 +155,40 @@ jgHhUTAKUAEDAwB+R/3NjLhB/QAAAABJRU5ErkJggg== + + 1047, 17 + + + 354, 17 + + + 17, 54 + + + 136, 54 + + + 253, 54 + + + 354, 54 + + + 1159, 17 + + + 599, 54 + + + 476, 54 + + + 616, 17 + + + 721, 17 + + + 102 + \ No newline at end of file diff --git a/src/Managers/BrowserManager.cs b/src/Managers/BrowserManager.cs new file mode 100644 index 0000000..a6fe8f5 --- /dev/null +++ b/src/Managers/BrowserManager.cs @@ -0,0 +1,77 @@ +using CefSharp.WinForms; +using CefSharp; +using SharpBrowser.Handlers; +using System.IO; +using SharpBrowser.Config; +using SharpBrowser.Utils; + +namespace SharpBrowser.Managers { + internal static class BrowserManager { + + public static HostHandler _HostHandler; + + private static DownloadHandler dHandler; + private static ContextMenuHandler mHandler; + private static LifeSpanHandler lHandler; + private static KeyboardHandler kHandler; + private static RequestHandler rHandler; + private static PermissionHandler pHandler; + + public static void Init(MainForm form) { + + if (Cef.IsInitialized != true) { + CefSettings settings = new CefSettings(); + + settings.RegisterScheme(new CefCustomScheme { + SchemeName = BrowserConfig.InternalScheme, + SchemeHandlerFactory = new SchemeHandlerFactory() + }); + + //------------------------------------------------------------ + // FIX: this prevents a crash if 2 CefSharp apps are opened at once + + // init cache dirs in AppData Roaming + var rcPath = Path.Combine(ConfigManager.AppDataPath, "CefCache"); + // fix: CachePath MUST be a child of the RootCachePath as of CEF 128+ + var cPath = Path.Combine(rcPath, "_TempCache"); + + // create cache dirs + rcPath.EnsureFolderExists(); + cPath.EnsureFolderExists(); + + settings.RootCachePath = rcPath; + settings.CachePath = cPath; + //------------------------------------------------------------ + + BrowserConfig.GetCefSettings(settings); + + Cef.Initialize(settings); + + } + + if (dHandler == null) { + + dHandler = new DownloadHandler(form); + lHandler = new LifeSpanHandler(form); + mHandler = new ContextMenuHandler(form); + kHandler = new KeyboardHandler(form); + rHandler = new RequestHandler(form); + pHandler = new PermissionHandler(); + _HostHandler = new HostHandler(form); + + } + } + + /// + /// Register our handlers with the given CefSharp browser instance. + /// + public static void SetupHandlers(ChromiumWebBrowser browser) { + browser.DownloadHandler = dHandler; + browser.MenuHandler = mHandler; + browser.LifeSpanHandler = lHandler; + browser.KeyboardHandler = kHandler; + browser.RequestHandler = rHandler; + browser.PermissionHandler = pHandler; + } + } +} \ No newline at end of file diff --git a/src/Managers/ConfigManager.cs b/src/Managers/ConfigManager.cs new file mode 100644 index 0000000..700d8f6 --- /dev/null +++ b/src/Managers/ConfigManager.cs @@ -0,0 +1,123 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Timers; +using Newtonsoft.Json; +using SharpBrowser.Utils; + +namespace SharpBrowser.Managers { + + /// + /// App config manager which saves app-level settings in a JSON file. + /// + public static class ConfigManager { + + public static bool InitDone; + public static string AppDataPath; + private static string SettingsFilePath; + private static Dictionary SettingsDict; + private static bool SettingsChanged = false; + private static Timer SaveTimer; + + /// + /// Loads existing settings from the AppData folder if they exist. + /// Starts a timer that saves settings every second if changes have occurred. + /// + public static void Init(string appName) { + if (SettingsDict == null) { + AppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), appName); + AppDataPath.EnsureFolderExists(); + SettingsFilePath = Path.Combine(AppDataPath, "settings.json"); + + SettingsDict = LoadSettings(); + InitDone = true; + + SaveTimer = new Timer(1000); + SaveTimer.Elapsed += SaveTimerElapsed; + SaveTimer.Start(); + } + } + + /// + /// Returns the setting for the given key, with an optional default value + /// + public static object Get(string key, object defaultVal = null) { + if (SettingsDict == null) throw new Exception("You need to call ZConfig.Init() first!"); + if (SettingsDict.ContainsKey(key)) return SettingsDict[key]; + return defaultVal; + } + + /// + /// Returns the integer setting for the given key + /// + public static int GetInt(string key, int defaultVal = 0) { + var val = Convert.ToInt32(Get(key, defaultVal)); + return val == 0 ? defaultVal : val; + } + + /// + /// Returns the double setting for the given key + /// + public static double GetDouble(string key, double defaultVal = 0.0) { + var val = Convert.ToDouble(Get(key, defaultVal)); + return val == 0 ? defaultVal : val; + } + + /// + /// Returns the string setting for the given key + /// + public static string GetString(string key, string defaultVal = "") { + var value = Get(key, defaultVal); + if (value == null) return defaultVal; + return value is string valString ? valString : value.ToString(); + } + + /// + /// Returns the boolean setting for the given key + /// + public static bool GetBool(string key, bool defaultVal = false) { + var val = GetString(key, defaultVal.ToString()); + return val == "true" || val == "1"; + } + + /// + /// Saves a setting by key, and persists it to disk within 1 second. + /// + public static void Set(string key, object value) { + if (SettingsDict == null) throw new Exception("Call ConfigManager.Init() first!"); + SettingsDict[key] = value; + SettingsChanged = true; + } + + /// + /// Returns the settings from the JSON file or an empty dictionary if the file doesn't exist + /// + private static Dictionary LoadSettings() { + if (File.Exists(SettingsFilePath)) { + var json = File.ReadAllText(SettingsFilePath); + return JsonConvert.DeserializeObject>(json) ?? new Dictionary(); + } + return new Dictionary(); + } + + /// + /// Saves the settings to the JSON file + /// + public static void SaveSettings() { + var json = JsonConvert.SerializeObject(SettingsDict, Formatting.Indented); + File.WriteAllText(SettingsFilePath, json); + SettingsChanged = false; + } + + /// + /// Timer event handler that checks if settings have changed and saves them + /// + private static void SaveTimerElapsed(object sender, ElapsedEventArgs e) { + if (SettingsChanged) { + SaveSettings(); + } + } + } + +} diff --git a/src/Managers/DownloadManager.cs b/src/Managers/DownloadManager.cs new file mode 100644 index 0000000..f273e64 --- /dev/null +++ b/src/Managers/DownloadManager.cs @@ -0,0 +1,67 @@ +using CefSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpBrowser.Managers { + /// + /// DownloadManager stores download metadata in a list, since CefSharp does not. + /// + internal static class DownloadManager { + + private static Dictionary downloads; + private static Dictionary downloadNames; + private static List downloadCancelRequests; + + public static Dictionary Downloads => downloads; + public static string CalcDownloadPath(DownloadItem item) => item.SuggestedFileName; + public static List CancelRequests => downloadCancelRequests; + + public static void Init() { + + downloads = new Dictionary(); + downloadNames = new Dictionary(); + downloadCancelRequests = new List(); + + } + + public static void UpdateDownloadItem(DownloadItem item) { + lock (downloads) { + + // SuggestedFileName comes full only in the first attempt so keep it somewhere + if (item.SuggestedFileName != "") { + downloadNames[item.Id] = item.SuggestedFileName; + } + + // Set it back if it is empty + if (item.SuggestedFileName == "" && downloadNames.ContainsKey(item.Id)) { + item.SuggestedFileName = downloadNames[item.Id]; + } + + downloads[item.Id] = item; + + //UpdateSnipProgress(); + } + } + + public static bool DownloadsInProgress() { + foreach (DownloadItem item in downloads.Values) { + if (item.IsInProgress) { + return true; + } + } + return false; + } + + public static void Cancel(int downloadId) { + lock (downloadCancelRequests) { + if (!downloadCancelRequests.Contains(downloadId)) { + downloadCancelRequests.Add(downloadId); + } + } + } + + } +} diff --git a/src/Managers/FavIconManager.cs b/src/Managers/FavIconManager.cs new file mode 100644 index 0000000..7b2a2d4 --- /dev/null +++ b/src/Managers/FavIconManager.cs @@ -0,0 +1,248 @@ +using CefSharp; +using CefSharp.WinForms; +using SharpBrowser.Utils; +using System; +using System.Collections.Concurrent; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Shapes; +using static System.Windows.Forms.DataFormats; +using Path = System.IO.Path; + +namespace SharpBrowser.Managers { + + /// + /// Downloads and caches favicons for any given URL. + /// + /// The first time it will be slow as we have to: + /// 1. Check if the website has a favicon at the default path. + /// 2. Parse the HTML and look for 'link' tags that might link to an icon. + /// 3. Download the icon and store the bitmap file data. + /// + /// We use byte[] for cache storage instead of Bitmap, + /// because storing as Bitmap causes a lot of GDI errors during rendering. + /// + internal static class FavIconManager { + + /// + /// Callback to parent. + /// + public static Action OnLoaded; + + /// + /// Thread-safe cache with concurrent dictionary + /// + private static readonly ConcurrentDictionary FaviconCache = new ConcurrentDictionary(); + /// + /// Reuse HttpClient for better performance + /// + private static readonly HttpClient httpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) }; + + + public static void Init() { + Path.Combine(ConfigManager.AppDataPath, "FavIcons").EnsureFolderExists(); + } + + private static string GetIconPath(string domain) { + var cleanDomain = domain.RemovePrefix("www.").Replace(".", "_"); + return Path.Combine(ConfigManager.AppDataPath, "FavIcons\\" + cleanDomain + ".ico"); + } + + public static async void LoadFavicon(ChromiumWebBrowser browser, bool readCacheOnly) { + + //try { + var uri = new Uri(browser.Address); + var domain = uri.Host; + + //-------------------------------------------------------------- + // 1. Check in-mem cache first + if (FaviconCache.TryGetValue(domain, out byte[] cachedIcon)) { + OnLoaded(browser, cachedIcon); + return; + } + + //-------------------------------------------------------------- + // 2. Check on-disk cache second + + var path = GetIconPath(domain); + if (File.Exists(path)) { + var diskIcon = await File.ReadAllBytesAsync(path); + StoreFavicon(domain, diskIcon, false); + OnLoaded(browser, diskIcon); + return; + } + + byte[] iconBitmap = null; + + if (readCacheOnly) return; + + //-------------------------------------------------------------- + // 3. Try the standard favicon paths first + iconBitmap = await TryGetFaviconFromUrl($"{uri.Scheme}://{domain}/favicon.ico"); + if (iconBitmap == null) { + iconBitmap = await TryGetFaviconFromUrl($"{uri.Scheme}://{domain}/favicon.png"); + } + if (iconBitmap != null) { + //Console.WriteLine("Favicon loaded from default path."); + StoreFavicon(domain, iconBitmap, true); + OnLoaded(browser, iconBitmap); + return; + } + + //-------------------------------------------------------------- + // 4. Check in-mem cache again + if (FaviconCache.TryGetValue(domain, out byte[] cachedIcon2)) { + OnLoaded(browser, cachedIcon2); + return; + } + + //-------------------------------------------------------------- + // 5. Search for the link tag on the page for the icon path + if (browser is null || browser.IsDisposed || browser.Disposing) //because its async.. + return; + + var result = await browser.EvaluateScriptAsync(FavIconJS); + if (result.Success && result.Result is string iconHref && !string.IsNullOrWhiteSpace(iconHref)) { + try { + var iconUri = new Uri(iconHref, UriKind.RelativeOrAbsolute); + if (!iconUri.IsAbsoluteUri) { + iconUri = new Uri(uri, iconUri); + } + + iconBitmap = await TryGetFaviconFromUrl(iconUri.ToString()); + if (iconBitmap != null) { + //Console.WriteLine("Favicon loaded from tag."); + StoreFavicon(domain, iconBitmap, true); + OnLoaded(browser, iconBitmap); + return; + } + } + catch (Exception ex) { + //Console.WriteLine($"Error processing link favicon: {ex.Message}"); + } + } + + + // NOT FOUND! + //if you dont do this, there is residue favicon from previous Website. + //iconBitmap = NotFound_favico; + iconBitmap = null; + OnLoaded(browser, iconBitmap); + return; + + //Console.WriteLine("No favicon could be retrieved."); + /*} + catch (Exception ex) { + Console.WriteLine($"Error in favicon retrieval process: {ex.Message}"); + }*/ + } + + /// + /// Helper method to download the favicon bitmap from any favicon URL. + /// + private static async Task TryGetFaviconFromUrl(string iconUrl) { + try { + var response = await httpClient.GetAsync(iconUrl); + + if (!response.Content.Headers.ContentType?.MediaType.StartsWith("image") ?? false) { + // non image returned!! + return null; + } + + if (response.IsSuccessStatusCode) { + using (var stream = await response.Content.ReadAsStreamAsync()) { + try { + // Create memory stream to avoid stream disposal issues + using (var memoryStream = new MemoryStream()) { + await stream.CopyToAsync(memoryStream); + memoryStream.Position = 0; + return memoryStream.ToArray(); + } + } + catch (ArgumentException) { + // Invalid image format - silently fail + return null; + } + } + } + } + catch (Exception ex) { + //Console.WriteLine($"HTTP error for {iconUrl}: {ex.Message}"); + } + return null; + } + + /// + /// Helper method to safely store favicon in cache, using thread-safe code to prevent GDI errors. + /// + private static void StoreFavicon(string domain, byte[] icon, bool saveToDisk) { + if (icon != null) { + FaviconCache[domain] = icon; + if (saveToDisk) { + File.WriteAllBytesAsync(GetIconPath(domain), icon); + } + } + } + + + /// + /// JS script to search for HTML `link` tags on the loaded webpage, in order of preference. + /// + private static string FavIconJS = @" + (function() { + // Order of preference for rel attributes (most common first) + var relPreference = ['icon', 'shortcut icon', 'apple-touch-icon', 'apple-touch-icon-precomposed']; + var links = document.getElementsByTagName('link'); + + // First pass: try to find icons in preference order + for (var p = 0; p < relPreference.length; p++) { + var preferredRel = relPreference[p]; + for (var i = 0; i < links.length; i++) { + var rel = links[i].rel.toLowerCase(); + if (rel === preferredRel && links[i].href) { + var iconUrl = links[i].href; + if (iconUrl.indexOf('.svg') == -1){ + return iconUrl; + } + } + } + } + + // Second pass: any icon-like rel as a fallback + for (var i = 0; i < links.length; i++) { + var rel = links[i].rel.toLowerCase(); + if ((rel.indexOf('icon') !== -1) && links[i].href) { + var iconUrl = links[i].href; + if (iconUrl.indexOf('.svg') == -1){ + return iconUrl; + } + } + } + + return null; + })() + "; + + } + + public static class ImageExtensions + { + public static byte[] ToByteArray(this Image image) => ToByteArray(image, ImageFormat.Png); + + public static byte[] ToByteArray(this Image image, ImageFormat format) + { + using (MemoryStream ms = new MemoryStream()) + { + image.Save(ms, format); + return ms.ToArray(); + } + } + } + +} diff --git a/src/Managers/HotkeyManager.cs b/src/Managers/HotkeyManager.cs new file mode 100644 index 0000000..788f200 --- /dev/null +++ b/src/Managers/HotkeyManager.cs @@ -0,0 +1,34 @@ +using SharpBrowser.Handlers; +using System.Windows.Forms; + +namespace SharpBrowser.Managers { + internal static class HotkeyManager { + + /// + /// these hotkeys work when the user is focussed on the .NET form and its controls, + /// AND when the user is focussed on the browser (CefSharp portion) + /// + public static void Init(MainForm form) { + + // browser hotkeys + KeyboardHandler.AddHotKey(form, form.CloseActiveTab, Keys.W, true); + KeyboardHandler.AddHotKey(form, form.CloseActiveTab, Keys.Escape, true); + KeyboardHandler.AddHotKey(form, form.AddBlankTab, Keys.T, true); + KeyboardHandler.AddHotKey(form, form.RefreshActiveTab, Keys.F5); + KeyboardHandler.AddHotKey(form, form.OpenDeveloperTools, Keys.F12); + KeyboardHandler.AddHotKey(form, form.NextTab, Keys.Tab, true); + KeyboardHandler.AddHotKey(form, form.PrevTab, Keys.Tab, true, true); + KeyboardHandler.AddHotKey(form, form.Print, Keys.P, true); + KeyboardHandler.AddHotKey(form, form.PrintToPDF, Keys.P, true, true); + + // search hotkeys + KeyboardHandler.AddHotKey(form, form.OpenSearch, Keys.F, true); + KeyboardHandler.AddHotKey(form, form.CloseSearch, Keys.Escape); + KeyboardHandler.AddHotKey(form, form.StopActiveTab, Keys.Escape); + KeyboardHandler.AddHotKey(form, form.ToggleFullscreen, Keys.F11); + + + } + + } +} diff --git a/src/Managers/IconManager.cs b/src/Managers/IconManager.cs new file mode 100644 index 0000000..6654d97 --- /dev/null +++ b/src/Managers/IconManager.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; + +namespace SharpBrowser.Managers { + /// + /// embedding the resource using the Visual Studio designer results in a blurry icon. + /// so this is the best way to get a non-blurry icon for Windows 8+ apps. + /// + internal static class IconManager { + + public static Assembly assembly = null; + + public static void Init(Form form) { + if (assembly == null) assembly = Assembly.GetAssembly(typeof(MainForm)); + form.Icon = new Icon(GetResourceStream("sharpbrowser.ico"), new Size(64, 64)); + } + + public static Stream GetResourceStream(string filename, bool withNamespace = true) { + try { + return assembly.GetManifestResourceStream("SharpBrowser.Resources." + filename); + } + catch (System.Exception ex) { } + return null; + } + + } +} diff --git a/src/Browser/Model/BrowserHotKey.cs b/src/Model/BrowserHotKey.cs similarity index 93% rename from src/Browser/Model/BrowserHotKey.cs rename to src/Model/BrowserHotKey.cs index e1de096..3669f76 100644 --- a/src/Browser/Model/BrowserHotKey.cs +++ b/src/Model/BrowserHotKey.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using System.Windows.Forms; -namespace SharpBrowser.Browser.Model { +namespace SharpBrowser.Model { /// /// POCO for holding hotkey data /// diff --git a/src/Browser/Model/BrowserTab.cs b/src/Model/BrowserTab.cs similarity index 69% rename from src/Browser/Model/BrowserTab.cs rename to src/Model/BrowserTab.cs index 3b6a4ef..b00a4d1 100644 --- a/src/Browser/Model/BrowserTab.cs +++ b/src/Model/BrowserTab.cs @@ -1,12 +1,9 @@ using CefSharp.WinForms; using SharpBrowser.Controls.BrowserTabStrip; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Drawing; -namespace SharpBrowser.Browser.Model { +namespace SharpBrowser.Model { /// /// POCO created for holding data per tab /// @@ -22,8 +19,10 @@ internal class BrowserTab { public DateTime DateCreated; - public BrowserTabStripItem Tab; + public BrowserTabPage Tab; public ChromiumWebBrowser Browser; + public Bitmap FavIcon; + } } diff --git a/src/Program.cs b/src/Program.cs index 3ab00f6..eb19f8c 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -13,9 +13,19 @@ static class Program [STAThread] static void Main() { + + // ***this line is added*** + if (Environment.OSVersion.Version.Major >= 6) + SetProcessDPIAware(); + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } + + // ***also dllimport of that function*** + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern bool SetProcessDPIAware(); + } } diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index 84d3b41..9015782 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -10,8 +10,9 @@ namespace SharpBrowser.Properties { using System; - - + using System.Drawing; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -31,7 +32,8 @@ internal class Resources { [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } - + + /// /// Returns the cached ResourceManager instance used by this class. /// diff --git a/src/SharpBrowser.csproj b/src/SharpBrowser.csproj index 3b86eaa..31b7eca 100644 --- a/src/SharpBrowser.csproj +++ b/src/SharpBrowser.csproj @@ -1,14 +1,14 @@  - net6.0-windows + net8.0-windows WinExe - true + false false true true bin\ MinimumRecommendedRules.ruleset - AnyCPU;x64 + x64 Resources\sharpbrowser.ico @@ -24,7 +24,7 @@ Component - + Component @@ -32,9 +32,12 @@ - - - + + + + + + diff --git a/src/SharpBrowser.sln b/src/SharpBrowser.sln index db64e40..0e97afd 100644 --- a/src/SharpBrowser.sln +++ b/src/SharpBrowser.sln @@ -12,26 +12,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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 - {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|Any CPU.Build.0 = Debug|Any CPU {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|x64.ActiveCfg = Debug|x64 {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|x64.Build.0 = Debug|x64 - {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|x86.ActiveCfg = Debug|Any CPU - {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|x86.Build.0 = Debug|Any CPU - {703472B8-B275-4F3F-95B6-426036F8462E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {703472B8-B275-4F3F-95B6-426036F8462E}.Release|Any CPU.Build.0 = Release|Any CPU {703472B8-B275-4F3F-95B6-426036F8462E}.Release|x64.ActiveCfg = Release|x64 {703472B8-B275-4F3F-95B6-426036F8462E}.Release|x64.Build.0 = Release|x64 - {703472B8-B275-4F3F-95B6-426036F8462E}.Release|x86.ActiveCfg = Release|x86 - {703472B8-B275-4F3F-95B6-426036F8462E}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Utils/ColorUtils.cs b/src/Utils/ColorUtils.cs new file mode 100644 index 0000000..b618803 --- /dev/null +++ b/src/Utils/ColorUtils.cs @@ -0,0 +1,52 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace SharpBrowser.Utils { + public static class ColorUtils { + /// + /// Creates color with corrected brightness. + /// + /// Color to correct. + /// The brightness correction factor. Must be between -1 and 1. + /// Negative values produce darker colors. + /// + /// Corrected structure. + /// + public static Color ChangeColorBrightness(this Color color, float correctionFactor) { + correctionFactor = Math.Clamp(correctionFactor, -1, 1); + + float red = (float)color.R; + float green = (float)color.G; + float blue = (float)color.B; + + if (correctionFactor < 0) { + correctionFactor = 1 + correctionFactor; + red *= correctionFactor; + green *= correctionFactor; + blue *= correctionFactor; + } + else { + red = (255 - red) * correctionFactor + red; + green = (255 - green) * correctionFactor + green; + blue = (255 - blue) * correctionFactor + blue; + } + + return Color.FromArgb(color.A, (int)red, (int)green, (int)blue); + } + + /// + /// Creates color with corrected brightness. + /// + /// Color to correct. + /// The brightness correction factor. Must be between -1 and 1. + /// Negative values produce darker colors. + /// + /// Corrected structure. + /// + public static Color ChangeColorBrightness(this Color color, double correctionFactor) + => ChangeColorBrightness(color, (float)correctionFactor); + + + } +} \ No newline at end of file diff --git a/src/Utils/FileIconUtils.cs b/src/Utils/FileIconUtils.cs index 8d20580..eaf6844 100644 --- a/src/Utils/FileIconUtils.cs +++ b/src/Utils/FileIconUtils.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Collections.Generic; using System.IO; +using System.Linq; namespace SharpBrowser { @@ -309,8 +310,9 @@ public static Icon IconFromExtension(string extension, //gets the name of the file that have the icon. string IconLocation = ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString(); - string[] IconPath = IconLocation.Split(','); + List IconPath = IconLocation.Split(',').ToList(); + if (IconPath.Count == 1) IconPath.Add("0"); if (IconPath[1] == null) IconPath[1] = "0"; IntPtr[] Large = new IntPtr[1], Small = new IntPtr[1]; diff --git a/src/Utils/FilePathUtils.cs b/src/Utils/FilePathUtils.cs new file mode 100644 index 0000000..1706905 --- /dev/null +++ b/src/Utils/FilePathUtils.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; + +namespace SharpBrowser.Utils { + internal static class FilePathUtils { + + public static bool CheckIfFilePath(this string path) { + + if (path.Length >= 3) { + if (path[1] == ':') { + if (path[2] == '\\') { + if (Char.IsLetter(path[0])) { + return true; + } + } + } + } + return false; + } + + public static bool CheckIfFilePath2(this string path) { + + if (path.Length >= 3) { + if (path[1] == ':') { + if (path[2] == '/') { + if (Char.IsLetter(path[0])) { + return true; + } + } + } + } + return false; + } + + public static bool SupportedInFilePath(this char c) { + return !(c == '?' || c == '\'' || c == '\"' || c == '/' || c == '\\' || c == ';' || c == ':' || c == '&' || c == '*' || c == '<' || c == '>' || c == '|' || c == '{' || c == '}' || c == '[' || c == ']' || c == '(' || c == ')'); + } + + public static string ChangePathSlash(this string filePath, string slash) { + if (slash == "\\") { + if (filePath.Contains('/')) { + return filePath.Replace("/", "\\"); + } + } + if (slash == "/") { + if (filePath.Contains('\\')) { + return filePath.Replace("\\", "/"); + } + } + return filePath; + } + public static string FileURLToPath(this string url) { + return url.RemovePrefix("file:///").ChangePathSlash(@"\").DecodeURLForFilepath(); + } + + public static bool EnsureFolderExists(this string path) { + if (!Directory.Exists(path)) { + try { + Directory.CreateDirectory(path); + return true; + } + catch (Exception ex) { + } + } + return false; + } + } +} diff --git a/src/Utils/GraphicsUtils.cs b/src/Utils/GraphicsUtils.cs new file mode 100644 index 0000000..fd167dd --- /dev/null +++ b/src/Utils/GraphicsUtils.cs @@ -0,0 +1,181 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace SharpBrowser.Utils { + public static partial class GraphicsUtils { + /// + /// Fills a Rounded Rectangle with integers. + public static void FillRoundRectangle(this Graphics g, Brush brush, RectangleF rect, int radius) + => g.FillRoundRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, radius); + /// + /// Fills a Rounded Rectangle with integers. + public static void FillRoundRectangle(this Graphics g, Brush brush, Rectangle rect, int radius) + => g.FillRoundRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, radius); + + /// + /// Fills a Rounded Rectangle with continuous numbers. + /// + public static void FillRoundRectangle(this Graphics g, Brush brush, float x, float y, + float width, float height, int radius) { + RectangleF rectangle = new RectangleF(x, y, width, height); + GraphicsPath path = rectangle.GetRoundedRect(radius); + g.FillPath(brush, path); + } + + /// + /// Draws a Rounded Rectangle border with integers. + /// + public static void DrawRoundRectangle(this Graphics g, Pen pen, RectangleF rect, int radius) + => g.DrawRoundRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, radius); + /// + /// Draws a Rounded Rectangle border with integers. + /// + public static void DrawRoundRectangle(this Graphics g, Pen pen, Rectangle rect, int radius) + => g.DrawRoundRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, radius); + + /// + /// Draws a Rounded Rectangle border with continuous numbers. + /// + public static void DrawRoundRectangle(this Graphics g, Pen pen, float x, float y, + float width, float height, int radius) { + RectangleF rectangle = new RectangleF(x, y, width, height); + GraphicsPath path = rectangle.GetRoundedRect(radius); + g.DrawPath(pen, path); + } + + private static GraphicsPath GetRoundedRect(this RectangleF bounds, int radius) { + int diameter = radius * 2; + Size size = new Size(diameter, diameter); + RectangleF arc = new RectangleF(bounds.Location, size); + GraphicsPath path = new GraphicsPath(); + + if (radius == 0) { + path.AddRectangle(bounds); + return path; + } + + // top left arc + path.AddArc(arc, 180, 90); + + // top right arc + arc.X = bounds.Right - diameter; + path.AddArc(arc, 270, 90); + + // bottom right arc + arc.Y = bounds.Bottom - diameter; + path.AddArc(arc, 0, 90); + + // bottom left arc + arc.X = bounds.Left; + path.AddArc(arc, 90, 90); + + path.CloseFigure(); + return path; + } + + public static GraphicsPath CreateTabPath_roundTop(this RectangleF tabRect, float cornerRadius) { + GraphicsPath path = new GraphicsPath(); + + if (cornerRadius <= 0) { + path.AddRectangle(tabRect); // If no corner radius, just a rectangle + return path; + } + + float diameter = cornerRadius * 2; + RectangleF arcRect = new RectangleF(tabRect.Location, new SizeF(diameter, diameter)); + + // Top-Left Arc + path.AddArc(arcRect, 180, 90); + + // Top Line + PointF topRightCornerStart = new PointF(tabRect.Left + cornerRadius, tabRect.Top); + PointF topRightCornerEnd = new PointF(tabRect.Right - cornerRadius, tabRect.Top); + path.AddLine(topRightCornerStart, topRightCornerEnd); + + // Top-Right Arc + arcRect.X = tabRect.Right - diameter; + path.AddArc(arcRect, 270, 90); + + // Right Line + path.AddLine(new PointF(tabRect.Right, tabRect.Top + cornerRadius), new PointF(tabRect.Right, tabRect.Bottom)); + + // Bottom Line + path.AddLine(new PointF(tabRect.Right, tabRect.Bottom), new PointF(tabRect.Left, tabRect.Bottom)); + + // Left Line - Closing the path + path.AddLine(new PointF(tabRect.Left, tabRect.Bottom), new PointF(tabRect.Left, tabRect.Top + cornerRadius)); + + path.CloseFigure(); + + return path; + } + public static GraphicsPath CreateTabPath_roundAll(this RectangleF tabRect, float cornerRadius) { + GraphicsPath path = new GraphicsPath(); + + if (cornerRadius <= 0) { + path.AddRectangle(tabRect); + return path; + } + + float diameter = cornerRadius * 2; + RectangleF arcRect = new RectangleF(tabRect.Location, new SizeF(diameter, diameter)); + + // Top-Left Arc + path.AddArc(arcRect, 180, 90); + + // Top Line + PointF topRightCornerStart = new PointF(tabRect.Left + cornerRadius, tabRect.Top); + PointF topRightCornerEnd = new PointF(tabRect.Right - cornerRadius, tabRect.Top); + path.AddLine(topRightCornerStart, topRightCornerEnd); + + // Top-Right Arc + arcRect.X = tabRect.Right - diameter; + path.AddArc(arcRect, 270, 90); + + // Right Line + path.AddLine(new PointF(tabRect.Right, tabRect.Top + cornerRadius), new PointF(tabRect.Right, tabRect.Bottom - cornerRadius)); + + // Bottom-Right Arc (Bottom Round to Out - now Round All) + arcRect.Y = tabRect.Bottom - diameter; // Move arcRect to bottom-right corner + path.AddArc(arcRect, 0, 90); // Start at 0 degrees (right), sweep 90 degrees clockwise + + // Bottom Line (Not needed, arc goes to bottom-left corner) + + // Bottom-Left Arc (Bottom Round to Out - now Round All) + arcRect.X = tabRect.Left; // Move arcRect to bottom-left corner (X is already set to 0) + path.AddArc(arcRect, 90, 90); // Start at 90 degrees (bottom), sweep 90 degrees clockwise + + // Left Line - Closing the path + path.AddLine(new PointF(tabRect.Left, tabRect.Bottom - cornerRadius), new PointF(tabRect.Left, tabRect.Top + cornerRadius)); + + + path.CloseFigure(); + + return path; + } + + + public static GraphicsPath CreateTabPath_Active(Rectangle rect, int radius) { + GraphicsPath path = new GraphicsPath(); + // Ensure radius is not larger than half the width or height + radius = Math.Min(radius, Math.Min(rect.Width / 2, rect.Height / 2)); + + //// Bottom-left arc + path.AddArc(rect.X - radius * 2, rect.Y + rect.Height - radius * 2, radius * 2, radius * 2, 90, -90); + //// Top-left arc + path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90); + // Top-right arc + path.AddArc(rect.X + rect.Width - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90); + // Bottom-right arc + path.AddArc(rect.X + rect.Width, rect.Y + rect.Height - radius * 2, radius * 2, radius * 2, -90 * 2, -90); + + //path.CloseFigure(); // Close the path to connect the last and first points + return path; + } + public static GraphicsPath CreateTabPath_Roundtop_RoundBottomOut(this RectangleF tabRect, float cornerRadius) + => CreateTabPath_Active(Rectangle.Round(tabRect), (int)cornerRadius); + + + } +} \ No newline at end of file diff --git a/src/Utils/ImageUtils.cs b/src/Utils/ImageUtils.cs new file mode 100644 index 0000000..8391651 --- /dev/null +++ b/src/Utils/ImageUtils.cs @@ -0,0 +1,56 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace SharpBrowser.Utils { + public static class ImageUtils { + /// + /// btn.BackgroundImage Has no Disabled Effect . wee need lighter + /// + /// + /// 0 to 100 + /// + /// + /// + /// + public static Image Lighter(this Image imgLight, int level, int nRed, int nGreen, int nBlue) { + //convert image to graphics object + Graphics graphics = Graphics.FromImage(imgLight); + int conversion = (5 * (level - 50)); //calculate new alpha value + //create mask with blended alpha value and chosen color as pen + Pen pLight = new Pen(Color.FromArgb(conversion, nRed, nGreen, nBlue), imgLight.Width * 2); + //apply created mask to graphics object + graphics.DrawLine(pLight, -1, -1, imgLight.Width, imgLight.Height); + //save created graphics object and modify image object by that + graphics.Save(); + graphics.Dispose(); //dispose graphics object + return imgLight; //return modified image + } + + /// + /// recommended defaults level:90, color:Color.white + /// 90 level - fade effect is instense, + /// 10 level - has nearly no effect; + /// + /// + /// + /// + /// + public static Image Lighter(this Image imgLight, int level, Color col) + => imgLight.Lighter(level, col.R, col.G, col.B); + + /// + /// recommended defaults level:90, color:Color.white + /// 90 level - fade effect is instense, + /// 10 level - has nearly no effect; + /// + /// + /// + /// + /// + public static Image Lighter(this Image imgLight, int level = 90) { + var col = Color.White; + return imgLight.Lighter(level, col.R, col.G, col.B); + } + } +} \ No newline at end of file diff --git a/src/Utils/MiscUtils.cs b/src/Utils/MiscUtils.cs deleted file mode 100644 index 9fb0f38..0000000 --- a/src/Utils/MiscUtils.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using CefSharp; - -namespace SharpBrowser { - internal static class Utils { - - public static bool IsFocussed(TextBox tb) { - return tb.Focused; - } - - public static void AddHotKey(Form form, Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { - form.KeyPreview = true; - form.KeyDown += delegate(object sender, KeyEventArgs e) { - if (e.IsHotkey(key, ctrl, shift, alt)) { - function(); - } - }; - } - - public static bool IsFullySelected(TextBox tb) { - return tb.SelectionLength == tb.TextLength; - } - public static bool HasSelection(TextBox tb) { - return tb.SelectionLength > 0; - } - - - public static bool CheckIfFilePath(this string path) { - - if (path.Length >= 3) { - if (path[1] == ':') { - if (path[2] == '\\') { - if (Char.IsLetter(path[0])) { - return true; - } - } - } - } - return false; - } - - public static bool CheckIfFilePath2(this string path) { - - if (path.Length >= 3) { - if (path[1] == ':') { - if (path[2] == '/') { - if (Char.IsLetter(path[0])) { - return true; - } - } - } - } - return false; - } - - public static string GetAfter(this string text, string find, int startAt = 0, bool returnAll = false, bool forward = true) { - if (text == null) { return returnAll ? text : ""; } - int idx; - if (!forward) { - idx = text.LastIndexOf(find, text.Length - startAt, StringComparison.Ordinal); - } else { - idx = text.IndexOf(find, startAt, StringComparison.Ordinal); - } - if (idx == -1) { return returnAll ? text : ""; } - idx += find.Length; - return text.Substring(idx); - } - - public static string GetAfterLast(this string text, string find, bool returnAll = false) { - int idx = text.LastIndexOf(find, StringComparison.Ordinal); - if (idx == -1) { - return returnAll ? text : ""; - } - idx += find.Length; - return text.Substring(idx); - } - - public static bool SupportedInFilePath(this char c) { - return !(c == '?' || c == '\'' || c == '\"' || c == '/' || c == '\\' || c == ';' || c == ':' || c == '&' || c == '*' || c == '<' || c == '>' || c == '|' || c == '{' || c == '}' || c == '[' || c == ']' || c == '(' || c == ')'); - } - - public static string ChangePathSlash(this string filePath, string slash) { - if (slash == "\\") { - if (filePath.Contains('/')) { - return filePath.Replace("/", "\\"); - } - } - if (slash == "/") { - if (filePath.Contains('\\')) { - return filePath.Replace("\\", "/"); - } - } - return filePath; - } - public static string FileURLToPath(this string url) { - return url.RemovePrefix("file:///").ChangePathSlash(@"\").DecodeURLForFilepath(); - } - - public static bool FileNotExists(this string path) { - return !File.Exists(path); - } - - - public static string ConvertToString(this object o) { - if (o is string) { - return o as string; - } - return null; - } - public static bool CheckIfValid(this string text, bool trimAndCheck = false) { - return text != null && text.Length > 0; - } - - public static void InvokeOnParent(this Control control, MethodInvoker method) { - Control parent = control.Parent == null ? control : control.Parent; - if (parent.IsHandleCreated) { - parent.Invoke(method); - return; - } - } - - public static bool IsHotkey(this KeyEventArgs eventData, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { - return eventData.KeyCode == key && eventData.Control == ctrl && eventData.Shift == shift && eventData.Alt == alt; - } - - public static CefState ToCefState(this bool value) { - return value ? CefState.Enabled : CefState.Disabled; - } - public static bool IsBitmaskOn(this int num, int bitmask) { - return (num & bitmask) != 0; - } - - public static bool BeginsWith(this string str, string beginsWith, bool caseSensitive = true) { - if (beginsWith.Length > str.Length) { - return false; - } - if (beginsWith.Length == str.Length) { - return String.Equals(beginsWith, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); - } - return str.LastIndexOf(beginsWith, beginsWith.Length - 1, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) == 0; - } - public static string RemovePrefix(this string str, string prefix, bool caseSensitive = true) { - if (str.Length >= prefix.Length && str.BeginsWith(prefix, caseSensitive)) { - return str.Substring(prefix.Length); - } - return str; - } - - - } -} diff --git a/src/Utils/StringUtils.cs b/src/Utils/StringUtils.cs new file mode 100644 index 0000000..560a9e4 --- /dev/null +++ b/src/Utils/StringUtils.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpBrowser { + internal static class StringUtils { + public static string ConvertToString(this object o) { + if (o is string) { + return o as string; + } + return null; + } + public static bool CheckIfValid(this string text, bool trimAndCheck = false) { + return text != null && text.Length > 0; + } + public static bool BeginsWith(this string str, string beginsWith, bool caseSensitive = true) { + if (beginsWith.Length > str.Length) { + return false; + } + if (beginsWith.Length == str.Length) { + return String.Equals(beginsWith, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + } + return str.LastIndexOf(beginsWith, beginsWith.Length - 1, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) == 0; + } + public static string RemovePrefix(this string str, string prefix, bool caseSensitive = true) { + if (str.Length >= prefix.Length && str.BeginsWith(prefix, caseSensitive)) { + return str.Substring(prefix.Length); + } + return str; + } + public static string GetAfter(this string text, string find, int startAt = 0, bool returnAll = false, bool forward = true) { + if (text == null) { return returnAll ? text : ""; } + int idx; + if (!forward) { + idx = text.LastIndexOf(find, text.Length - startAt, StringComparison.Ordinal); + } + else { + idx = text.IndexOf(find, startAt, StringComparison.Ordinal); + } + if (idx == -1) { return returnAll ? text : ""; } + idx += find.Length; + return text.Substring(idx); + } + + public static string GetAfterLast(this string text, string find, bool returnAll = false) { + int idx = text.LastIndexOf(find, StringComparison.Ordinal); + if (idx == -1) { + return returnAll ? text : ""; + } + idx += find.Length; + return text.Substring(idx); + } + + public static string Join(this IList values, string separator) { + StringBuilder result = new StringBuilder(); + var last = values.Count - 1; + for (int i = 0; i < values.Count; i++) { + var str = values[i]; + if (str != null) { + result.Append(str); + } + if (i != last) { + result.Append(separator); + } + } + return result.ToString(); + } + } +} diff --git a/src/Utils/URLUtils.cs b/src/Utils/URLUtils.cs index 1f42f92..fc8689f 100644 --- a/src/Utils/URLUtils.cs +++ b/src/Utils/URLUtils.cs @@ -1,4 +1,6 @@ -using System; +using SharpBrowser.Config; +using SharpBrowser.Utils; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -7,6 +9,25 @@ namespace SharpBrowser { internal static class URLUtils { + public static string CleanURL(string url) { + if (url.BeginsWith("about:")) { + return ""; + } + url = url.RemovePrefix("http://"); + url = url.RemovePrefix("https://"); + url = url.RemovePrefix("file://"); + url = url.RemovePrefix("/"); + return url.DecodeURL(); + } + public static bool IsBlank(string url) { + if (url == null) return true; + return (url == "" || url == "about:blank"); + } + public static bool IsBlankOrSystem(string url) { + if (url == null) return true; + return (url == "" || url.BeginsWith("about:") || url.BeginsWith("chrome:") || url.BeginsWith(BrowserConfig.InternalScheme + ":")); + } + public static string PathToURL(this string filePath, string removeBaseDir = null) { if (!filePath.CheckIfValid()) { diff --git a/src/Utils/WinFormsUtils.cs b/src/Utils/WinFormsUtils.cs new file mode 100644 index 0000000..fa6872f --- /dev/null +++ b/src/Utils/WinFormsUtils.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using CefSharp; + +namespace SharpBrowser { + internal static class WinFormsUtils { + + public static bool IsFocussed(TextBox tb) { + return tb.Focused; + } + + public static void AddHotKey(Form form, Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { + form.KeyPreview = true; + form.KeyDown += delegate (object sender, KeyEventArgs e) { + if (e.IsHotkey(key, ctrl, shift, alt)) { + function(); + } + }; + } + + public static bool IsFullySelected(TextBox tb) { + return tb.SelectionLength == tb.TextLength; + } + public static bool HasSelection(TextBox tb) { + return tb.SelectionLength > 0; + } + + + public static bool FileNotExists(this string path) { + return !File.Exists(path); + } + + + + public static void InvokeOnParent(this Control control, MethodInvoker method) { + Control parent = control.Parent == null ? control : control.Parent; + if (parent.IsHandleCreated) { + parent.Invoke(method); + return; + } + } + + public static bool IsHotkey(this KeyEventArgs eventData, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { + return eventData.KeyCode == key && eventData.Control == ctrl && eventData.Shift == shift && eventData.Alt == alt; + } + + public static CefState ToCefState(this bool value) { + return value ? CefState.Enabled : CefState.Disabled; + } + public static bool IsBitmaskOn(this int num, int bitmask) { + return (num & bitmask) != 0; + } + + + + + } +} \ No newline at end of file diff --git a/src/bin/storage/action_link.css b/src/bin/net8.0-windows/storage/action_link.css similarity index 100% rename from src/bin/storage/action_link.css rename to src/bin/net8.0-windows/storage/action_link.css diff --git a/src/bin/storage/downloads.html b/src/bin/net8.0-windows/storage/downloads.html similarity index 99% rename from src/bin/storage/downloads.html rename to src/bin/net8.0-windows/storage/downloads.html index 97d5b36..8e6344f 100644 --- a/src/bin/storage/downloads.html +++ b/src/bin/net8.0-windows/storage/downloads.html @@ -178,7 +178,7 @@

Downloads

host.getDownloads().then(function(res) { var list = JSON.parse(res); $.each(list, function(key, item) { - UpdateItem(item.v); + UpdateItem(item); }); }); } diff --git a/src/bin/storage/errors/aboutNetError.css b/src/bin/net8.0-windows/storage/errors/aboutNetError.css similarity index 100% rename from src/bin/storage/errors/aboutNetError.css rename to src/bin/net8.0-windows/storage/errors/aboutNetError.css diff --git a/src/bin/storage/errors/aboutNetError_alert.svg b/src/bin/net8.0-windows/storage/errors/aboutNetError_alert.svg similarity index 100% rename from src/bin/storage/errors/aboutNetError_alert.svg rename to src/bin/net8.0-windows/storage/errors/aboutNetError_alert.svg diff --git a/src/bin/storage/errors/aboutNetError_info.svg b/src/bin/net8.0-windows/storage/errors/aboutNetError_info.svg similarity index 100% rename from src/bin/storage/errors/aboutNetError_info.svg rename to src/bin/net8.0-windows/storage/errors/aboutNetError_info.svg diff --git a/src/bin/storage/errors/cannotConnect.html b/src/bin/net8.0-windows/storage/errors/cannotConnect.html similarity index 100% rename from src/bin/storage/errors/cannotConnect.html rename to src/bin/net8.0-windows/storage/errors/cannotConnect.html diff --git a/src/bin/storage/errors/common.css b/src/bin/net8.0-windows/storage/errors/common.css similarity index 100% rename from src/bin/storage/errors/common.css rename to src/bin/net8.0-windows/storage/errors/common.css diff --git a/src/bin/storage/errors/notFound.html b/src/bin/net8.0-windows/storage/errors/notFound.html similarity index 100% rename from src/bin/storage/errors/notFound.html rename to src/bin/net8.0-windows/storage/errors/notFound.html diff --git a/src/bin/storage/errors/warning-16.png b/src/bin/net8.0-windows/storage/errors/warning-16.png similarity index 100% rename from src/bin/storage/errors/warning-16.png rename to src/bin/net8.0-windows/storage/errors/warning-16.png diff --git a/src/bin/storage/i18n_process.css b/src/bin/net8.0-windows/storage/i18n_process.css similarity index 100% rename from src/bin/storage/i18n_process.css rename to src/bin/net8.0-windows/storage/i18n_process.css diff --git a/src/bin/storage/jquery-1.10.2.min.js b/src/bin/net8.0-windows/storage/jquery-1.10.2.min.js similarity index 100% rename from src/bin/storage/jquery-1.10.2.min.js rename to src/bin/net8.0-windows/storage/jquery-1.10.2.min.js diff --git a/src/bin/storage/other.css b/src/bin/net8.0-windows/storage/other.css similarity index 100% rename from src/bin/storage/other.css rename to src/bin/net8.0-windows/storage/other.css diff --git a/src/bin/storage/progressbackground.png b/src/bin/net8.0-windows/storage/progressbackground.png similarity index 100% rename from src/bin/storage/progressbackground.png rename to src/bin/net8.0-windows/storage/progressbackground.png diff --git a/src/bin/storage/shared.css b/src/bin/net8.0-windows/storage/shared.css similarity index 100% rename from src/bin/storage/shared.css rename to src/bin/net8.0-windows/storage/shared.css diff --git a/src/bin/storage/text_defaults.css b/src/bin/net8.0-windows/storage/text_defaults.css similarity index 100% rename from src/bin/storage/text_defaults.css rename to src/bin/net8.0-windows/storage/text_defaults.css diff --git a/src/bin/storage/widgets.css b/src/bin/net8.0-windows/storage/widgets.css similarity index 100% rename from src/bin/storage/widgets.css rename to src/bin/net8.0-windows/storage/widgets.css