using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.InteropServices; using System.Windows.Forms; using TradeIdeas.TIProData.Interfaces; namespace TradeIdeas.TIProGUI { public partial class CustomChannelEditor : Form { private const int CHANNEL_IMAGE_WIDTH = 128; private const int CHANNEL_IMAGE_HEIGHT = 73; private const int UPLOAD_PBOX_HEIGHT = 346; //this is the maximum height (when I created in designer) private const int UPLOAD_PBOX_WIDTH = 346; //this is the maximum height (when I created in designer) private const int PBOX_UPLOAD_X = 3; private const int PBOX_UPLOAD_Y = 3; private Rectangle _cropRect; private int _xVal = 0; private int _yVal = 0; private Pen _cropPen; private bool _readyToPaint = false; private bool _paintCropBox = true; private static PrivateFontCollection _pfc; private Image _currentChannelImage = null; private Image _currentActiveChannelImage = null; private Image _currentBaseImage = null; private String _currentChannelName = ""; private String _cloudCodeURL = ""; private CustomChannelList.UserChannelData _dataEdit; private CustomChannelList.UserChannelData _dataClone; private Boolean _inEditMode = false; private String _imageResourceName = null; private IConnectionMaster _connectionMaster; public CustomChannelEditor(CustomChannelList.UserChannelData selectedItemToEdit = null) { InitializeComponent(); _connectionMaster = GuiEnvironment.FindConnectionMaster(""); InitOratorStdFont(); LoadImageResourceListView(); _xVal = pictureBoxUpload.Location.X; //initial position of the cropping rectangle _yVal = pictureBoxUpload.Location.Y; //initial position of the cropping rectangle _cropRect = new Rectangle(_xVal, _yVal, CHANNEL_IMAGE_WIDTH, CHANNEL_IMAGE_HEIGHT); btnCrop.Enabled = false; if (selectedItemToEdit != null) { _dataEdit = selectedItemToEdit; lblTitle.Text = "Update Existing Channel"; //All of the manipulations are done with a clone of the data object that's passed to the constructor //Doing editing on the original causes weird things to happen(images), even when this window is cancelled cause the image contained //within the UserChannelDat is a reference to the image that presides in the CustomChannelList produced in CustomChaneelList. _dataClone = CloneDataObject(_dataEdit); _currentBaseImage = _dataClone.baseImage; _currentChannelName = _dataClone.channelName; if (null != _currentBaseImage) { _currentChannelImage = CreateDynamicInactiveChannel(_currentBaseImage, _currentChannelName); _currentActiveChannelImage = CreateDynamicActiveChannel(_currentBaseImage, _currentChannelName); } if (null != _dataClone.imageResourceName && !_dataClone.imageResourceName.Equals("")) { _currentChannelImage = CreateDynamicInactiveResourceImageChannel(_dataClone.imageResourceName, _currentChannelName); _currentActiveChannelImage = CreateDynamicActiveResourceImageChannel(_dataClone.imageResourceName, _currentChannelName); int imgIndex = GuiEnvironment.GetCustomChannelImageResourceList().Images.IndexOfKey(_dataClone.imageResourceName); if (imgIndex != -1) _imageResourceName = imageResourceImageList.Images.Keys[imgIndex].ToString(); } //since we're editing, we will now populate the relevant picture boxes as well as the channel link text box) pictureBoxUpload.Image = _currentChannelImage; pictureBoxChannel.Image = _currentChannelImage; pictureBoxActiveChannel.Image = _currentActiveChannelImage; txtName.Text = _currentChannelName; txtCloudLink.Text = _dataClone.cloudLink; btnPreviewText.Enabled = true; btnUndo.Enabled = true; _inEditMode = true; updateGUI(true); } else if (imageResourceListView.Items.Count > 0) { lblTitle.Text = "Add New Channel"; imageResourceListView.Items[0].Selected = true; imageResourceListView.Select(); updateGUI(false); } } public static void InitOratorStdFont() { //Create your private font collection object. _pfc = new PrivateFontCollection(); //Select your font from the resources. //My font here is "OratorStd.ttf" int fontLength = Properties.Resources.OratorStd.Length; // create a buffer to read in to byte[] fontdata = Properties.Resources.OratorStd; // create an unsafe memory block for the font data System.IntPtr data = Marshal.AllocCoTaskMem(fontLength); // copy the bytes to the unsafe memory block Marshal.Copy(fontdata, 0, data, fontLength); // pass the font to the font collection _pfc.AddMemoryFont(data, fontLength); // free memory block Marshal.FreeCoTaskMem(data); } public Image CurrenBaseImage { get { return _currentBaseImage; } } public Image CurrentChannelImage { get { return _currentChannelImage; } } public Image CurrentActiveChannelImage { get { return _currentActiveChannelImage; } } public String CurrentChannelName { get { return _currentChannelName; } } public String CloudCodeURL { get { return _cloudCodeURL; } } public String ImageResourceName { get { return _imageResourceName; } } public CustomChannelList.UserChannelData getDataEditObject() { CustomChannelList.UserChannelData retVal = new CustomChannelList.UserChannelData(); retVal.id = _dataClone.id; retVal.cloudLink = txtCloudLink.Text; retVal.channelName = _currentChannelName; retVal.baseImage = _dataClone.baseImage; retVal.imageResourceName = ImageResourceName; return retVal; } private void btnSelect_Click(object sender, EventArgs e) { try { DialogResult result = openFileDialog1.ShowDialog(); if (result == DialogResult.OK) { // Clear the listview selection if (imageResourceListView.SelectedItems.Count > 0) imageResourceListView.SelectedItems[0].Selected = false; _imageResourceName = null; LoadImage(openFileDialog1.FileName); } } catch (Exception ex) { MessageBox.Show(ex.Message,"Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void LoadImageResourceListView() { LoadImageResourceThunbnail(); imageResourceListView.Items.Clear(); _imageResourceName = null; for (int i = 0; i < imageResourceThumbnail.Images.Count; i++) { ListViewItem guiItem = new ListViewItem(); string key = "ch_" + (i + 1).ToString(); guiItem.ImageIndex = imageResourceThumbnail.Images.IndexOfKey(key); if (guiItem.ImageIndex != -1) imageResourceListView.Items.Add(guiItem); } } private void LoadImageResourceThunbnail() { // Load the full size images to image List imageResourceImageList.Images.Clear(); imageResourceImageList = GuiEnvironment.GetCustomChannelImageResourceList(); // Load the thumbnail images from the full size image list imageResourceThumbnail.Images.Clear(); for (int i = 0; i < imageResourceImageList.Images.Count; i++) { string key = "ch_" + (i + 1).ToString(); imageResourceThumbnail.Images.Add(key, (Image)imageResourceImageList.Images[i].Clone()); } } private void LoadImage(string path) { updateGUI(false); _paintCropBox = true; pictureBoxUpload.Location = new Point(PBOX_UPLOAD_X, PBOX_UPLOAD_Y); pictureBoxUpload.Width = UPLOAD_PBOX_WIDTH; pictureBoxUpload.Height = UPLOAD_PBOX_HEIGHT; _cropRect.X = 0; //initial position of the cropping rectangle _cropRect.Y = 0; _yVal = 0; //initial position of the cropping rectangle pictureBoxUpload.Image = null; Image img = Image.FromFile(path); if (img != null) { //code snippet below for adjusting sizes inspired from http://www.blackbeltcoder.com 2011 Jonathan Wood int origX, origY, newX, newY; int trimX = 0, trimY = 0; // Default to size of source image newX = origX = img.Width; newY = origY = img.Height; // Does image exceed maximum dimensions (maximum dimensions are that of the upload panel)? if (origX > UPLOAD_PBOX_WIDTH || origY > UPLOAD_PBOX_HEIGHT) { if (checkBox1.Checked) //we'll trim the image to fit the maximum dimensions { double factor = Math.Max((double)UPLOAD_PBOX_WIDTH / (double)origX, (double)UPLOAD_PBOX_HEIGHT / (double)origY); newX = (int)Math.Ceiling((double)origX * factor); newY = (int)Math.Ceiling((double)origY * factor); trimX = newX - UPLOAD_PBOX_WIDTH; trimY = newY - UPLOAD_PBOX_HEIGHT; newX = newX - trimX; newY = newY - trimY; } else { //resize and keep within maximum dimensions (retaining the aspect ratio) double factor = Math.Min((double)UPLOAD_PBOX_WIDTH / (double)origX, (double)UPLOAD_PBOX_HEIGHT / (double)origY); newX = (int)Math.Ceiling((double)origX * factor); newY = (int)Math.Ceiling((double)origY * factor); } //produce the final resized image img = img.GetThumbnailImage(newX, newY, null, new IntPtr()); } pictureBoxUpload.Image = img; pictureBoxUpload.Width = img.Width; pictureBoxUpload.Height = img.Height; pictureBoxChannel.Image = null; pictureBoxActiveChannel.Image = null; if (_inEditMode) { pictureBoxActiveChannel.Image = null; } //Now show the crop rectangle: _readyToPaint = true; Pen cPen = getCropPen(); pictureBoxUpload.CreateGraphics().DrawRectangle(cPen, _cropRect); cPen.Dispose(); pictureBoxUpload.Invalidate(); btnCrop.Enabled = true; } } private Image Image { get { return pictureBoxUpload.Image; } } private void pictureBoxUpload_Paint(object sender, PaintEventArgs e) { if (_readyToPaint) { if (_paintCropBox) { int h = 5; _cropPen = new Pen(Color.Black, 2); _cropPen.DashStyle = DashStyle.Dash; e.Graphics.DrawRectangle(_cropPen, _cropRect); //The below are the tiny rectangles that are at the midpoint of each side of the cropping rectangle Pen side1 = new Pen(Color.White, 2); side1.DashStyle = DashStyle.Solid; int x1 = _cropRect.Location.X + (_cropRect.Width / 2); Rectangle rect1 = new Rectangle(x1, _cropRect.Location.Y, h, h); e.Graphics.DrawRectangle(side1, rect1); e.Graphics.FillRectangle(Brushes.White, rect1); Pen side2 = new Pen(Color.White, 2); side2.DashStyle = DashStyle.Solid; int x2 = _cropRect.Location.X + (_cropRect.Width / 2); Rectangle rect2 = new Rectangle(x2, _cropRect.Location.Y + _cropRect.Height - h, h, h); e.Graphics.DrawRectangle(side2, rect2); e.Graphics.FillRectangle(Brushes.White, rect2); Pen side3 = new Pen(Color.White, 2); side3.DashStyle = DashStyle.Solid; int x3 = _cropRect.Location.X; Rectangle rect3 = new Rectangle(x3, _cropRect.Location.Y + (_cropRect.Height/2 ), h, h); e.Graphics.DrawRectangle(side3, rect3); e.Graphics.FillRectangle(Brushes.White, rect3); Pen side4 = new Pen(Color.White, 2); side4.DashStyle = DashStyle.Solid; int x4 = _cropRect.Location.X + _cropRect.Width - h; Rectangle rect4 = new Rectangle(x4, _cropRect.Location.Y + (_cropRect.Height / 2), h, h); e.Graphics.DrawRectangle(side4, rect4); e.Graphics.FillRectangle(Brushes.White, rect4); _cropPen.Dispose(); side1.Dispose(); side2.Dispose(); side3.Dispose(); side4.Dispose(); } } } private Pen getCropPen() { Pen retVal = new Pen(Color.Black, 2); retVal.DashStyle = DashStyle.Dash; return retVal; } private void pictureBoxUpload_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left && _cropRect.Contains(e.Location)) { _cropRect.X = e.X + _xVal; _cropRect.Y = e.Y + _yVal; Cursor.Current = Cursors.Hand; pictureBoxUpload.Refresh(); } else { Cursor.Current = Cursors.Arrow; } } private void pictureBoxUpload_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left && _cropRect.Contains(e.Location)) { _xVal = _cropRect.X - e.X; _yVal = _cropRect.Y - e.Y; Cursor.Current = Cursors.Hand; } else { Cursor.Current = Cursors.Arrow; } } private void btnCrop_Click(object sender, EventArgs e) { Image img = Crop(pictureBoxUpload.Image, _cropRect.Size, _cropRect.Left - pictureBoxUpload.Left, _cropRect.Top - pictureBoxUpload.Top); Image clone = (Image)img.Clone(); if (_inEditMode) { _dataClone.baseImage = clone; } _currentBaseImage = clone; Image image = createChannelOverlay(img); pictureBoxUpload.Image = image; pictureBoxUpload.Width = image.Width; pictureBoxUpload.Height = image.Height; adjustPictureBoxLocation(); btnCrop.Enabled = false; //now add the image to the lower left hand picture box (what your channel will look like) pictureBoxChannel.Image = image; updateGUI(true); //make a copy of this image to store as backup image if we wish to "undo" makeBackupImage(image); //also make copy of this image in its active state if we wish to undo makeBackupActiveImage(image); pictureBoxActiveChannel.Image = _currentActiveChannelImage; } private Image Crop(Image image,Size targetSize,int x, int y) { Rectangle rect = new Rectangle(_cropRect.X, _cropRect.Y, targetSize.Width, targetSize.Height); Bitmap originalImg = new Bitmap(pictureBoxUpload.Image, pictureBoxUpload.Width, pictureBoxUpload.Height); Bitmap croppedImg = new Bitmap(targetSize.Width, targetSize.Height); croppedImg.SetResolution(image.HorizontalResolution, image.VerticalResolution); //make graphics.. Graphics gfx = Graphics.FromImage(croppedImg); gfx.SmoothingMode = SmoothingMode.AntiAlias; gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; gfx.PixelOffsetMode = PixelOffsetMode.HighQuality; gfx.CompositingQuality = CompositingQuality.HighQuality; _paintCropBox = false; //set image attributes gfx.DrawImage(originalImg, 0, 0, rect, GraphicsUnit.Pixel); gfx.Dispose(); return croppedImg; } private void makeBackupImage(Image image) { Image bakupImage = null; if (null != image) bakupImage = (Image)image.Clone(); _currentChannelImage = bakupImage; } private void makeBackupActiveImage(Image img) { Image bakupImage = null; if (null != img) bakupImage = (Image)img.Clone(); _currentActiveChannelImage = getActiveChannel(bakupImage); } private static Image createChannelOverlay(Image img) //this is the very subtle overlay gradient that you see on each channel image. It makes the white text stand out more.. { if (null == img) return null; Graphics gfx = Graphics.FromImage(img); Rectangle rect = new Rectangle(Point.Empty, img.Size); LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Transparent, Color.Transparent, LinearGradientMode.Vertical); var blend = new ColorBlend(); blend.Positions = new[] { 0, .4f, 1 }; // Make blending have sharper colors. blend.Colors = new[] { Color.FromArgb(0, Color.Transparent), Color.FromArgb(0, Color.Transparent), Color.FromArgb(165, Color.Black) }; brush.InterpolationColors = blend; gfx.FillRectangle(brush, rect); brush.Dispose(); gfx.Dispose(); return img; } private static Image getActiveChannel(Image img) { if (null == img) return null; using (Graphics gfx = Graphics.FromImage(img)) { Rectangle rect = new Rectangle(Point.Empty, img.Size); ControlPaint.DrawBorder(gfx, rect, Color.Orange, 2, ButtonBorderStyle.Solid, Color.Orange, 2, ButtonBorderStyle.Solid, Color.Orange, 2, ButtonBorderStyle.Solid, Color.Orange, 2, ButtonBorderStyle.Solid); Brush b = new SolidBrush(Color.White); StringFormat drawFormat = new StringFormat(); drawFormat.Alignment = StringAlignment.Center; RectangleF rectf = new RectangleF(0, 15, img.Width, 80); gfx.SmoothingMode = SmoothingMode.AntiAlias; gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; gfx.PixelOffsetMode = PixelOffsetMode.HighQuality; gfx.DrawString("ON", new Font(_pfc.Families[0], 24.0f, FontStyle.Regular), b, rectf, drawFormat); gfx.Dispose(); } return img; } private void adjustPictureBoxLocation() { int x = 0; int y = 0; if (pnlImageUpload.Width > pictureBoxUpload.Width) { x = (pnlImageUpload.Width - pictureBoxUpload.Width) / 2; } if (pnlImageUpload.Height > pictureBoxUpload.Height) { y = (pnlImageUpload.Height - pictureBoxUpload.Height) / 2; } pictureBoxUpload.Location = new Point(x, y); } private void btnPreviewText_Click(object sender, EventArgs e) { if (_inEditMode) { // Erase channel name from images. if (null != _dataClone.baseImage) { _currentChannelImage = CreateDynamicInactiveChannel(_currentBaseImage, ""); _currentActiveChannelImage = CreateDynamicActiveChannel(_currentBaseImage, ""); } else if (null != _dataClone.imageResourceName && !_dataClone.imageResourceName.Equals("")) { _currentChannelImage = CreateDynamicInactiveResourceImageChannel(_dataClone.imageResourceName, ""); _currentActiveChannelImage = CreateDynamicActiveResourceImageChannel(_dataClone.imageResourceName, ""); } } else { makeBackupImage(_currentChannelImage); makeBackupActiveImage(_currentActiveChannelImage); } _currentChannelName = txtName.Text.ToString().Trim(); Image textualImage = WriteText(_currentChannelImage, _currentChannelName, 0, 54); if (textualImage == null) return; Image textualImageActive = (Image)textualImage.Clone(); pictureBoxChannel.Image = textualImage; pictureBoxActiveChannel.Image = getActiveChannel(textualImageActive); pictureBoxActiveChannel.Invalidate(); pictureBoxChannel.Invalidate(); } private void btnUndo_Click(object sender, EventArgs e) { if (_inEditMode) { // Erase channel name from images. if (null != _dataClone.baseImage) { _currentChannelImage = CreateDynamicInactiveChannel(_currentBaseImage, ""); _currentActiveChannelImage = CreateDynamicActiveChannel(_currentBaseImage, ""); } else if (null != _dataClone.imageResourceName && !_dataClone.imageResourceName.Equals("")) { _currentChannelImage = CreateDynamicInactiveResourceImageChannel(_dataClone.imageResourceName, ""); _currentActiveChannelImage = CreateDynamicActiveResourceImageChannel(_dataClone.imageResourceName, ""); } } pictureBoxChannel.Image = _currentChannelImage; pictureBoxActiveChannel.Image = _currentActiveChannelImage; txtName.Clear(); } private void updateGUI(bool isEnabled) { txtName.Enabled = isEnabled; btnPreviewText.Enabled = isEnabled; btnUndo.Enabled = isEnabled; } public static CustomChannelList.UserChannelData CloneDataObject(CustomChannelList.UserChannelData dat) { CustomChannelList.UserChannelData retVal = new CustomChannelList.UserChannelData(); retVal.id = dat.id; retVal.cloudLink = dat.cloudLink; retVal.channelName = dat.channelName; retVal.baseImage = dat.baseImage; retVal.imageResourceName = dat.imageResourceName; return retVal; } public static Image CreateDynamicInactiveChannel(Image img, String channelName) //This is the channel (with it's name written on it) that the user will see when he brings up the listbox after downloading from server as well what he'll use in the editor as well { Image clone = (Image)img.Clone(); Image maskedImage = createChannelOverlay(clone); Image retval = WriteText(maskedImage, channelName, 0, 54); return retval; } public static Image CreateDynamicActiveChannel(Image img, String channelName) { Image clone = (Image)img.Clone(); Image maskedImage = createChannelOverlay(clone); Image orangeBorderedImage = getActiveChannel(maskedImage); Image retval = WriteText(orangeBorderedImage, channelName, 0, 54); return retval; } public static Image CreateDynamicInactiveResourceImageChannel(String key, String channelName) //This is the channel (with it's name written on it) that the user will see when he brings up the listbox after downloading from server as well what he'll use in the editor as well { int imgIndex = GuiEnvironment.GetCustomChannelImageResourceList().Images.IndexOfKey(key); if (imgIndex == -1) return null; Image clone = (Image)GuiEnvironment.GetCustomChannelImageResourceList().Images[imgIndex].Clone(); Image maskedImage = createChannelOverlay(clone); Image retval = WriteText(maskedImage, channelName, 0, 54); return retval; } public static Image CreateDynamicActiveResourceImageChannel(String key, String channelName) { int imgIndex = GuiEnvironment.GetCustomChannelImageResourceList().Images.IndexOfKey(key); if (imgIndex == -1) return null; Image clone = (Image)GuiEnvironment.GetCustomChannelImageResourceList().Images[imgIndex].Clone(); Image maskedImage = createChannelOverlay(clone); Image orangeBorderedImage = getActiveChannel(maskedImage); Image retval = WriteText(orangeBorderedImage, channelName, 0, 54); return retval; } private static Image WriteText(Image img,String name, int x, int y) { Image clone = null; if (null != img) clone = (Image)img.Clone(); if (null == clone) return clone; if (name == "") return img; Brush b = new SolidBrush(Color.White); StringFormat drawFormat = new StringFormat(); drawFormat.Alignment = StringAlignment.Center; RectangleF rectf = new RectangleF(x, y, 127, 15); Graphics g = Graphics.FromImage(clone); g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.DrawString(name, new Font(_pfc.Families[0], 11.0f, FontStyle.Regular), b, rectf, drawFormat); b.Dispose(); g.Dispose(); return clone; } private void btnOK_Click(object sender, EventArgs e) { if (txtCloudLink.Text.ToString().Trim() == "") { MessageBox.Show("Please enter your cloud code", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } else { _cloudCodeURL = txtCloudLink.Text.ToString().Trim(); _currentChannelName = txtName.Text.ToString().Trim(); _currentChannelImage = pictureBoxChannel.Image; _currentActiveChannelImage = pictureBoxActiveChannel.Image; this.DialogResult = DialogResult.OK; } } private void imageResourceListView_SelectedIndexChanged(object sender, EventArgs e) { if (imageResourceListView.SelectedItems.Count > 0) { // Load from full size image list and not the thunbnail image list. int imageIndex = imageResourceListView.SelectedItems[0].ImageIndex; Image img = (Image)imageResourceImageList.Images[imageIndex].Clone(); Image clone = (Image)img.Clone(); _imageResourceName = imageResourceImageList.Images.Keys[imageIndex].ToString(); _currentBaseImage = null; Image image = createChannelOverlay(img); pictureBoxUpload.Image = image; pictureBoxUpload.Width = image.Width; pictureBoxUpload.Height = image.Height; adjustPictureBoxLocation(); btnCrop.Enabled = false; //now add the image to the lower left hand picture box (what your channel will look like) pictureBoxChannel.Image = image; updateGUI(true); //make a copy of this image to store as backup image if we wish to "undo" makeBackupImage(image); //also make copy of this image in its active state if we wish to undo makeBackupActiveImage(image); pictureBoxActiveChannel.Image = _currentActiveChannelImage; } } private void btnSelectFromCloud_Click(object sender, EventArgs e) { using (LoadFromCloud dialog = new LoadFromCloud(_connectionMaster.SendManager, true)) { dialog.StartPosition = FormStartPosition.CenterParent; DialogResult result = dialog.ShowDialog(); if (result == DialogResult.OK) { txtCloudLink.Text = dialog.SelectedCloudLink(); btnOK.Focus(); } dialog.Close(); } } } }