private void InitializeMainMenu() { #region Initialize Controls // About Button - Links to further reading about Shadowmask. Button aboutButton = new Button(); aboutButton.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right); aboutButton.AutoSize = false; aboutButton.BackColor = this.BackColor; aboutButton.BackgroundImage = Properties.Resources.QuestionMark_Dark; aboutButton.BackgroundImageLayout = ImageLayout.Center; aboutButton.Cursor = Cursors.Hand; aboutButton.FlatAppearance.CheckedBackColor = aboutButton.BackColor; aboutButton.FlatAppearance.MouseOverBackColor = aboutButton.BackColor; aboutButton.FlatAppearance.BorderSize = 0; aboutButton.FlatStyle = FlatStyle.Flat; aboutButton.Size = aboutButton.BackgroundImage.Size; aboutButton.TabStop = false; // Activity Indicator - Displays if the configuration application is performing sensative operations (writing to files, lengthy computions). PictureBox activityIndicator = new PictureBox(); activityIndicator.Image = Properties.Resources.Spinner; activityIndicator.Name = "progressSpinner"; activityIndicator.Size = new Size(48, 48); activityIndicator.SizeMode = PictureBoxSizeMode.StretchImage; activityIndicator.Hide(); TextBox addressIntake = new TextBox(); addressIntake.Multiline = true; addressIntake.MinimumSize = new Size(this.Width / 3, 24); addressIntake.Multiline = false; addressIntake.Text = "https://example.com"; addressIntake.BorderStyle = BorderStyle.FixedSingle; addressIntake.TextAlign = HorizontalAlignment.Center; addressIntake.TabStop = false; addressIntake.Enabled = true; addressIntake.AutoCompleteMode = AutoCompleteMode.SuggestAppend; addressIntake.AutoCompleteSource = AutoCompleteSource.CustomSource; addressIntake.AutoCompleteCustomSource = Pull_Chrome__Address_History(); // Content Type Dropdown - Allows the user to select the type of wallpaper source material. ComboBox contentType = new ComboBox(); contentType.DataSource = new BindingSource(new Dictionary <string, string> { { "Image", "Image Files (*.PNG;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF" }, //{ "Video", "Video Files (*.MP4)|*.MP4" }, { "Webpage", "" }, //{ "YouTube Video", "" } }, null); contentType.DisplayMember = "Key"; contentType.DropDownStyle = ComboBoxStyle.DropDownList; contentType.FlatStyle = FlatStyle.Flat; contentType.ValueMember = "Value"; // Content Type Dropdown - Instruction Label Label contentType_Label = new Label(); contentType_Label.Anchor = AnchorStyles.Top; contentType_Label.AutoSize = true; contentType_Label.ForeColor = Color.White; contentType.TabStop = false; contentType_Label.Text = "Please select the type of content you wish to display."; contentType_Label.TextAlign = ContentAlignment.MiddleCenter; // Exit Button - Closes the configuration pane (not the entire application). Button exitButton = new Button(); exitButton.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left); exitButton.AutoSize = false; exitButton.BackColor = this.BackColor; exitButton.BackgroundImage = Properties.Resources.Exit; exitButton.BackgroundImageLayout = ImageLayout.Center; exitButton.Cursor = Cursors.Hand; exitButton.FlatAppearance.CheckedBackColor = exitButton.BackColor; exitButton.FlatAppearance.MouseOverBackColor = exitButton.BackColor; exitButton.FlatAppearance.BorderSize = 0; exitButton.FlatStyle = FlatStyle.Flat; exitButton.Size = exitButton.BackgroundImage.Size; exitButton.TabStop = false; // File Selection Dialog - A explorer dialog that allows the user to input images,videos,etc as wallpaper content. OpenFileDialog fileDialog = new OpenFileDialog(); fileDialog.Multiselect = false; fileDialog.Title = "Please select the desired wallpaper source..."; // File Selection Button - Launches the File Selection Dialog. Button fileIntake = new Button(); fileIntake.AutoSize = false; fileIntake.BackColor = Color.LightGray; fileIntake.FlatAppearance.BorderSize = 0; fileIntake.FlatStyle = FlatStyle.Flat; fileIntake.TabStop = false; fileIntake.Text = "No File"; fileIntake.TextAlign = ContentAlignment.MiddleCenter; // Content Selection Instructions - Assist user with content selection. Label intakeLabel = new Label(); intakeLabel.Anchor = AnchorStyles.Top; intakeLabel.AutoSize = true; intakeLabel.ForeColor = Color.White; intakeLabel.Text = "Please select either the URL hosting the content, or the content's source file"; intakeLabel.TextAlign = ContentAlignment.MiddleCenter; // Screen Selection Instructions - Assist user with monitor selection. Label instructionsLabel = new Label(); instructionsLabel.Anchor = AnchorStyles.Top; instructionsLabel.AutoSize = true; instructionsLabel.ForeColor = Color.White; instructionsLabel.Text = "Please select a screen to configure:"; instructionsLabel.TextAlign = ContentAlignment.MiddleCenter; // Content Layout Selection - Instructions Label layoutLabel = new Label(); layoutLabel.Anchor = AnchorStyles.Top; layoutLabel.AutoSize = true; layoutLabel.ForeColor = Color.White; layoutLabel.Text = "Please select a display layout"; layoutLabel.TextAlign = ContentAlignment.MiddleCenter; // Content Layout Dropdown - Allows the user to select how their content will be displayed (fill, tile, etc). ComboBox layoutType = new ComboBox(); layoutType.DropDownStyle = ComboBoxStyle.DropDownList; layoutType.FlatStyle = FlatStyle.Flat; layoutType.Items.Add("Fill"); layoutType.SelectedIndex = 0; layoutType.TabStop = false; // Monitor Selection - Allows the user to select a monitor to adjust wallpaper settings on. Dynamically built based on currently attached monitors. FlowLayoutPanel monitorSelection_Panel = new FlowLayoutPanel(); monitorSelection_Panel.Anchor = AnchorStyles.None; monitorSelection_Panel.AutoSize = true; monitorSelection_Panel.AutoSizeMode = AutoSizeMode.GrowAndShrink; monitorSelection_Panel.BackColor = this.BackColor; int screenCount = 1; foreach (Screen activeDisplay in Screen.AllScreens.OrderBy(screen => screen.Bounds.X)) { RadioButton monitorButton = new RadioButton(); monitorButton.Anchor = AnchorStyles.None; monitorButton.Appearance = Appearance.Button; monitorButton.BackColor = Color.LightGray; monitorButton.Cursor = Cursors.Hand; monitorButton.Dock = DockStyle.None; monitorButton.FlatAppearance.BorderSize = 0; monitorButton.FlatAppearance.MouseOverBackColor = System.Drawing.ColorTranslator.FromHtml("#2d89ef"); monitorButton.FlatStyle = FlatStyle.Flat; monitorButton.Size = new Size(activeDisplay.WorkingArea.Width / 10, activeDisplay.WorkingArea.Height / 10); monitorButton.TabStop = true; monitorButton.Text = screenCount.ToString(); monitorButton.TextAlign = ContentAlignment.MiddleCenter; monitorButton.CheckedChanged += MonitorButton_Checked; void MonitorButton_Checked(object sender, EventArgs e) { if (monitorButton.Checked) { monitorButton.BackColor = System.Drawing.ColorTranslator.FromHtml("#2d89ef"); monitorButton.FlatAppearance.BorderSize = 0; } else { monitorButton.BackColor = Color.LightGray; monitorButton.FlatAppearance.BorderSize = 0; } Settings_Changed(sender, e, false, true); } monitorSelection_Panel.Controls.Add(monitorButton); screenCount++; } // Advanced Settings Button - Brings the user to an advanced configuration window. Button settingsButton = new Button(); settingsButton.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left); settingsButton.AutoSize = false; settingsButton.BackColor = this.BackColor; settingsButton.BackgroundImage = Properties.Resources.Gear_Dark; settingsButton.BackgroundImageLayout = ImageLayout.Center; settingsButton.Cursor = Cursors.Hand; settingsButton.FlatAppearance.BorderSize = 0; settingsButton.FlatAppearance.CheckedBackColor = settingsButton.BackColor; settingsButton.FlatAppearance.MouseOverBackColor = settingsButton.BackColor; settingsButton.FlatStyle = FlatStyle.Flat; settingsButton.Size = settingsButton.BackgroundImage.Size; settingsButton.TabStop = false; // Version Label - Display's the application's current version. Label versionLabel = new Label(); versionLabel.Anchor = AnchorStyles.Bottom; versionLabel.AutoSize = false; versionLabel.Dock = DockStyle.Bottom; versionLabel.ForeColor = Color.White; versionLabel.Text = ProductName + " - " + ProductVersion; versionLabel.TextAlign = ContentAlignment.MiddleCenter; // ToolTip - Enables control tooltip functionality. ToolTip toolTip = new ToolTip(); toolTip.BackColor = Color.White; toolTip.SetToolTip(exitButton, "Save & Exit"); toolTip.SetToolTip(settingsButton, "Advanced Settings"); #endregion #region Add Controls to Form this.Controls.Add(aboutButton); this.Controls.Add(activityIndicator); this.Controls.Add(addressIntake); this.Controls.Add(contentType); this.Controls.Add(contentType_Label); this.Controls.Add(exitButton); this.Controls.Add(intakeLabel); this.Controls.Add(instructionsLabel); this.Controls.Add(fileIntake); this.Controls.Add(layoutLabel); this.Controls.Add(layoutType); this.Controls.Add(monitorSelection_Panel); this.Controls.Add(settingsButton); this.Controls.Add(versionLabel); #endregion #region Align Controls on Form // Independent Controls aboutButton.Location = new Point(this.Width - aboutButton.Width - 10, this.Height - aboutButton.Height - 10); activityIndicator.Location = new Point((this.Width / 2 - (activityIndicator.Width / 2)), this.Height - activityIndicator.Height - 50); exitButton.Location = new Point((this.Width - exitButton.Width - 10), 10); settingsButton.Location = new Point(10, this.Height - settingsButton.Height - 10); versionLabel.Location = new Point((this.Width / 2) - (versionLabel.Width / 2), this.Height - versionLabel.Height); // Location Dependent Controls instructionsLabel.Location = new Point((this.Width / 2) - (instructionsLabel.Width / 2), instructionsLabel.Height + exitButton.Height); monitorSelection_Panel.Location = new Point((this.Width / 2 - (monitorSelection_Panel.Width / 2)), (instructionsLabel.Location.Y + instructionsLabel.Height + 10)); contentType_Label.Location = new Point((this.Width / 2) - (contentType_Label.Width / 2), (monitorSelection_Panel.Location.Y + monitorSelection_Panel.Height + 20)); contentType.Location = new Point((this.Width / 2 - (contentType.Width / 2)), (contentType_Label.Location.Y + contentType_Label.Height + 10)); intakeLabel.Location = new Point((this.Width / 2) - (intakeLabel.Width / 2), (contentType.Location.Y + contentType.Height + 20)); addressIntake.Location = new Point(intakeLabel.Location.X + 5, (intakeLabel.Location.Y + intakeLabel.Height + 15)); fileIntake.Location = new Point(intakeLabel.Location.X + intakeLabel.Width - fileIntake.Width - 5, (intakeLabel.Location.Y + intakeLabel.Height + 15)); layoutLabel.Location = new Point((this.Width / 2) - (layoutLabel.Width / 2), (fileIntake.Location.Y + fileIntake.Height + 10)); layoutType.Location = new Point((this.Width / 2 - (layoutType.Width / 2)), (layoutLabel.Location.Y + layoutLabel.Height + 10)); #endregion #region Handle Control Actions /* Opens the project github on the default system web browser. * Triggered by a click to the "About" Button. */ void AboutButton_Click(object sender, EventArgs e) { System.Diagnostics.Process.Start("https://github.com/ProperEmergency/Shadowmask"); } aboutButton.Click += AboutButton_Click; /* Detects if a url was entered or modified, and triggers a Settings_Changed event. * Triggered by loss of focus to the address bar.*/ void AddressIntake_LostFocus(object sender, EventArgs e) { if (addressIntake.Modified) { addressIntake.Modified = false; Settings_Changed(sender, e); } } addressIntake.LostFocus += AddressIntake_LostFocus; /* Hides the configuration pane, and refreshes the wallpaper engine. * Triggered by a click to the exit button */ void ExitButton_Click(object sender, EventArgs e) { this.Hide(); Task.Run(() => wallpaperEngine.Change_Displayed_Content()); } exitButton.Click += ExitButton_Click; /* Opens a file selection dialog. * Once a file is selected, a wallpaper template is built and the Settings_Changed event triggered. * Triggered by a click to the file selection button.*/ void FileIntake_Click(object sender, EventArgs e) { fileDialog.Filter = contentType.SelectedValue.ToString(); if (fileDialog.ShowDialog() == DialogResult.OK) { TemplateEngine templateEngine = new TemplateEngine(); fileIntake.Text = templateEngine.Build_Custom_Template(fileDialog.FileName, fileDialog.SafeFileName, contentType.Text, layoutType.Text); Settings_Changed(sender, e); } } fileIntake.Click += FileIntake_Click; /* Pulls autocomplete history from typed Chrome URLs. * Used in addressIntake ComboBox.*/ AutoCompleteStringCollection Pull_Chrome__Address_History() { AutoCompleteStringCollection urlCollection = new AutoCompleteStringCollection(); try { File.Copy(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Google\Chrome\User Data\Default\History", Path.GetTempPath() + "Chrome_URL_Entry_History.db", true); string queryString = "SELECT url FROM urls WHERE typed_count > 0;"; string connectionString = "Data Source=" + Path.GetTempPath() + "Chrome_URL_Entry_History.db" + "; Version=3"; using (SQLiteConnection connection = new SQLiteConnection(connectionString)) { SQLiteCommand command = new SQLiteCommand(queryString, connection); connection.Open(); SQLiteDataReader reader = command.ExecuteReader(); try { while (reader.Read()) { urlCollection.Add(reader.GetString(0)); } } finally { reader.Close(); } connection.Close(); } } catch (Exception) { // } return(urlCollection); } /* Disables GUI to process settings changes, and writes/reads new settings values from the user config file. * Triggered by all user-modifiable controls.*/ void Settings_Changed(object sender, EventArgs e, bool writeRequired = true, bool readRequired = false) { activityIndicator.Show(); foreach (Control formControl in this.Controls.Cast <Control>().Where(c => !(c is Label) && !(c is PictureBox))) { formControl.Enabled = false; } if (readRequired) { Task.Run(() => Read_Settings()); } if (writeRequired) { Task.Run(() => Write_Settings()); } } contentType.SelectedIndexChanged += (sender, e) => Settings_Changed(sender, e); layoutType.SelectedIndexChanged += (sender, e) => Settings_Changed(sender, e); /* Reads new wallpaper configuration from user config, and updates GUI to match. * Triggered by Settings_Change event.*/ void Read_Settings() { var selectedMonitor = monitorSelection_Panel.Controls.OfType <RadioButton>().FirstOrDefault(r => r.Checked); int monitorIndex = Int32.Parse(selectedMonitor.Text) - 1; String[] currentTheme; try { currentTheme = Properties.Settings.Default.ThemeLayout[monitorIndex].Split(';'); } catch { currentTheme = new string[] { selectedMonitor.Text, "Webpage", Properties.Settings.Default.DefaultContent, "Fill" }; } contentType.Invoke((MethodInvoker) delegate { contentType.Text = currentTheme[1]; }); if (currentTheme[1] == "Webpage") { addressIntake.Invoke((MethodInvoker) delegate { addressIntake.Text = currentTheme[2]; }); fileIntake.Invoke((MethodInvoker) delegate { fileIntake.Text = "No File"; fileIntake.Enabled = false; }); } else { fileIntake.Invoke((MethodInvoker) delegate { fileIntake.Text = currentTheme[2]; }); addressIntake.Invoke((MethodInvoker) delegate { addressIntake.Clear(); addressIntake.Enabled = false; }); } Restore_GUI(); } /* Writes new wallpaper configuration to user config based off of GUI status. * Triggered by Settings_Change event.*/ void Write_Settings() { var selectedMonitor = monitorSelection_Panel.Controls.OfType <RadioButton>().FirstOrDefault(r => r.Checked); int monitorIndex = Int32.Parse(selectedMonitor.Text) - 1; string displayTheme = ""; string type = ""; string content = ""; string layout = ""; contentType.Invoke((MethodInvoker) delegate { type = contentType.Text; }); layoutType.Invoke((MethodInvoker) delegate { layout = layoutType.Text; }); if (type == "Webpage") { content = addressIntake.Text; } else { content = fileIntake.Text; } displayTheme = selectedMonitor.Text + ";" + type + ";" + content + ";" + layout; try { if (Properties.Settings.Default.ThemeLayout.Count > monitorIndex) { Properties.Settings.Default.ThemeLayout.RemoveAt(monitorIndex); } Properties.Settings.Default.ThemeLayout.Insert(monitorIndex, displayTheme); } catch (NullReferenceException) { Properties.Settings.Default.ThemeLayout = new System.Collections.Specialized.StringCollection(); Properties.Settings.Default.ThemeLayout.Add(displayTheme); } Properties.Settings.Default.Save(); Restore_GUI(); } /* Restores controls disabled by Settings_Changed event. * Triggered by Read_Settings or Write_Settings.*/ void Restore_GUI() { if (contentType.InvokeRequired) { string currentType = ""; contentType.Invoke((MethodInvoker) delegate { currentType = contentType.Text; }); if (currentType == "Webpage") { addressIntake.Invoke((MethodInvoker) delegate { addressIntake.Enabled = true; }); } else { fileIntake.Invoke((MethodInvoker) delegate { fileIntake.Enabled = true; }); } foreach (Control formControl in this.Controls.Cast <Control>().Where(c => (c != addressIntake) && (c != fileIntake))) { formControl.Invoke((MethodInvoker) delegate { formControl.Enabled = true; }); } } if (activityIndicator.InvokeRequired) { activityIndicator.Invoke((MethodInvoker) delegate { activityIndicator.Hide(); }); } } #endregion }