Category Archives: Programming

Never buy an IoT product that depends upon the Cloud to work…

So Google bought Nest and then a couple of days ago killed off one of Nest’s home automation products, Revolv, which was sold with a Lifetime Subscription. Actually, they didn’t directly kill the product but will no longer provide the servers essential to its operation after May 2016 – effectively turning it into a brick.

Lesson learned – never purchase any product that depends upon the Cloud for its operation.

Wes Miller discusses this on his getwired.com blog:

What did I learn from Nest ?

” …
As I look back at Nest, it helps me frame the logic I’ll personally use when considering future IoT purchases. Ideally from now on, I’d like to consider instead:
1.Buying devices with open APIs or open firmware. If the APIs or firmware of Nest were opened up, the devices could have had alternative apps built against them by the open-source community (to generally poor, but possible, effect). This is about as likely to happen now as Nest sharing their windfall with early adopters like myself.
2.Buying devices with standards-based I/O (Bluetooth 4.0, Wi-Fi) and apps that can work without a Web point of contact. While a thermostat is a unique device that does clamor for a display, I think that most devices on the IoT should really have a limited, if any, display and rely on Web or smart phone apps over Wi-Fi or BT 4.0 in order to be configurable. Much like point 1, this would mean some way out if the company shutters its Web API.
3.Buying devices from larger companies. Most of the major thermostat manufacturers are making smarter thermostats now, although aesthetically, most are still crap.
4.Buying “dumb” alternatives. A minimalist programmable or simple non-programmable thermostat again.

In short, it’ll probably be a while before I spend money – especially premium money – on another IoT device.

… “

Backup Software Redux

The long standing quest to find functional Windows backup software has reached a terminus. I can fully recommend Macrium Reflect Professional v5:

https://macrium.com/

This software has worked well for me on both Windows 8 and Windows XP SP3.  I have it installed and working on 3 PCs and have been very pleased with it. Unlike Acronis and NovaBackup this company actually cares about and performs software engineering. There have been a few updates to add functionality, and they actually have a blog where they discuss the product and its development:

http://blog.macrium.com/

Recommended.

Annoying Android Bugs

Back in October of last year I purchased a Motorola XOOM tablet. Very nice, and it is supposed to be a “Google Experience” device – which hopefully means that the software will be maintained and updated.

When I purchased it the Android version was Honeycomb.  I added a few different apps – Evernote, Amazon Kindle Reader, News360, Google Reader, Google Currents and Google +. I enjoyed using it.

Somewhere along the way, I started experiencing an Android error message that would pop-up: “The Android.Media.Process has stopped.” There was an option to send a report when this happened and I always did but I think that they just fall on the floor or into a bit bucket somewhere. The problem got worse and worse so I started researching a solution.

Several people indicated that they got relief by going into the Settings, Apps, All Apps, Media Storage and clearing the cache. I did this and the problem disappeared for about a day then returned. So I basically suffered with it, clearing the cache whenever it happened and cursing at whoever might be reading the crash reports.

In January the Xoom was upgraded to Ice Cream Sandwich. Awesome – maybe this will fix the problem. Unfortunately no – the crashes kept occurring and could be temporarily ameliorated by clearing the media storage cache.

Doing some more research I saw a few people report that they were able to clear this problem by uninstalling Google + and Google Currents. What the heck, give it a try.

Voila !  So far the problem has disappeared.  What the heck – why should Google know how to write apps for their own operating system ?

DataGridView Custom ComboBox Column continued…

I found some code here that I was able to modify to accomplish what I wanted:

http://www.koders.com/csharp/fid2E788846AF503E85D1702ABB02A0434174BDD7BA.aspx?s=datagridview

Here’s what it kind of looks like in the interim:

and here’s the code with my modifications:

/**************************************************************************************************
 * 
 * FILE NAME:
 *      DataGridViewMultiColumnComboColumn.cs
 * 
 * AUTHOR:
 *      Issahar Gourfinkel, 
 *      gurfi@barak-online.net
 *      This code is Completely free. I will be happy if it will help somebody. 
 *      
 * CHANGES:
 *		Steven J. Ackerman
 *		sjackerman@verizon.net
 *		Made OwnerDrawVariable, DropDownList style
 *		Added OnMeasureItem(), OnDataSourceChanged(), OnValueMemberChanged()
 *		Changed OnDrawItem() to paint similiar to MultiColumnComboBox
 * 
 * DESCRIPTION:
 *      MultiColumnCombobox simulation for DataGridView cells controls.
 * 
 * DISCLAIMER OF WARRANTY:
 *      All of the code, information, instructions, and recommendations in this software component are 
 *      offered on a strictly "as is" basis. This material is offered as a free public resource, 
 *      without any warranty, expressed or implied.
 */

using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;

public class DataGridViewMultiColumnComboColumn : DataGridViewComboBoxColumn
{
    public DataGridViewMultiColumnComboColumn()
    {
        //Set the type used in the DataGridView
        this.CellTemplate = new DataGridViewMultiColumnComboCell();
    }
}
public class DataGridViewMultiColumnComboCell : DataGridViewComboBoxCell
{
    public override Type EditType
    {
        get
        {
            return typeof(DataGridViewMultiColumnComboEditingControl);
        }
    }
    public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
        DataGridViewMultiColumnComboEditingControl ctrl = DataGridView.EditingControl as DataGridViewMultiColumnComboEditingControl;
        ctrl.ownerCell = this;
    }
}
public class DataGridViewMultiColumnComboEditingControl : DataGridViewComboBoxEditingControl
{
	const int columnPadding = 5;
	private const int colorColumnWidth = 100;
	private float[] columnWidths = new float[0];
	private String[] columnNames = new String[0];
	private Type[] columnTypes = new Type[0];
	private int valueMemberColumnIndex = 0;

    public DataGridViewMultiColumnComboCell ownerCell = null;
    /**************************************************************************************************/
    public DataGridViewMultiColumnComboEditingControl()
        : base()
    {
        this.DrawMode = DrawMode.OwnerDrawVariable;
        this.DropDownStyle = ComboBoxStyle.DropDownList;
    }

	private void InitializeColumns()
	{
		if (DataManager != null)
		{
			PropertyDescriptorCollection propertyDescriptorCollection = DataManager.GetItemProperties();

			columnWidths = new float[propertyDescriptorCollection.Count];
			columnNames = new String[propertyDescriptorCollection.Count];
			columnTypes = new Type[propertyDescriptorCollection.Count];

			for (int colIndex = 0; colIndex < propertyDescriptorCollection.Count; colIndex++)
			{
				String name = propertyDescriptorCollection[colIndex].Name;
				Type type = propertyDescriptorCollection[colIndex].PropertyType;
				columnNames[colIndex] = name;
				columnTypes[colIndex] = type;
			}
		}
	}

	private void InitializeValueMemberColumn()
	{
		int colIndex = 0;
		foreach (String columnName in columnNames)
		{
			if (String.Compare(columnName, ValueMember, true, CultureInfo.CurrentUICulture) == 0)
			{
				valueMemberColumnIndex = colIndex;
				break;
			}
			colIndex++;
		}
	}

	private float CalculateTotalWidth()
	{
		float totWidth = 0;
		foreach (int width in columnWidths)
		{
			totWidth += (width + columnPadding);
		}
		return totWidth + SystemInformation.VerticalScrollBarWidth;
	}

	protected override void OnDataSourceChanged(EventArgs e)
	{
		base.OnDataSourceChanged(e);

		InitializeColumns();
	}

	protected override void OnValueMemberChanged(EventArgs e)
	{
		base.OnValueMemberChanged(e);

		InitializeValueMemberColumn();
	}

	protected override void OnDropDown(EventArgs e)
	{
		base.OnDropDown(e);
		this.DropDownWidth = (int)CalculateTotalWidth();
	}

	protected override void OnMeasureItem(MeasureItemEventArgs e)
	{
		base.OnMeasureItem(e);

		if (DesignMode)
			return;

		for (int colIndex = 0; colIndex < columnNames.Length; colIndex++)
		{
			if (columnTypes[colIndex] == typeof(Color))
			{
				columnWidths[colIndex] = colorColumnWidth;
			}
			else
			{
				string item = Convert.ToString(FilterItemOnProperty(Items[e.Index], columnNames[colIndex]));
				SizeF sizeF = e.Graphics.MeasureString(item, Font);
				columnWidths[colIndex] = Math.Max(columnWidths[colIndex], sizeF.Width);
			}
		}

		float totWidth = CalculateTotalWidth();

		e.ItemWidth = (int)totWidth;
	}

	/**************************************************************************************************/
    protected override void OnDrawItem(DrawItemEventArgs e)
    {

		base.OnDrawItem(e);

		if (DesignMode)
			return;

		e.DrawBackground();

		Rectangle boundsRect = e.Bounds;
		int lastRight = 0;

		using (Pen linePen = new Pen(SystemColors.GrayText))
		{
			using (SolidBrush forecolorBrush = new SolidBrush(e.ForeColor))
			{
				Brush brush;

				if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
				{
					brush = SystemBrushes.HighlightText;
				}
				else
				{
					brush = forecolorBrush;
				}

				if (columnNames.Length == 0)
				{
					e.Graphics.DrawString(Convert.ToString(Items[e.Index]), Font, brush, boundsRect);
				}
				else
				{
					for (int colIndex = 0; colIndex < columnNames.Length; colIndex++)
					{
						boundsRect.X = lastRight;
						boundsRect.Width = (int)columnWidths[colIndex] + columnPadding;
						lastRight = boundsRect.Right;

						if (columnTypes[colIndex] == typeof(Color))
						{
							const int distanceFromEdge = 2;
							boundsRect.Width = colorColumnWidth;
							lastRight = boundsRect.Right;
							Rectangle colorBoxRect = new Rectangle();
							colorBoxRect.X = boundsRect.X + distanceFromEdge;
							colorBoxRect.Y = boundsRect.Y + 1;
							colorBoxRect.Size = new Size((int)(1.5 * 17), boundsRect.Height - (2 * distanceFromEdge));
							RectangleF textBoxRect = new RectangleF();
							textBoxRect = RectangleF.FromLTRB(colorBoxRect.X + colorBoxRect.Width + 5, colorBoxRect.Y,
															  boundsRect.X + boundsRect.Width - distanceFromEdge,
															  colorBoxRect.Y + colorBoxRect.Height + 2);
							if (e.Index >= 0)
							{
								Color color = (Color) FilterItemOnProperty(Items[e.Index], columnNames[colIndex]);
								using (SolidBrush background = new SolidBrush(color))
								{
									e.Graphics.FillRectangle(background, colorBoxRect);
									e.Graphics.DrawRectangle(linePen, colorBoxRect);
									e.Graphics.DrawString("0x" + color.Name.ToString(), this.Font, brush, textBoxRect);
								}
							}
						}
						else
						{
							if (e.Index >= 0)
							{
								string item = Convert.ToString(FilterItemOnProperty(Items[e.Index], columnNames[colIndex]));

								if (colIndex == valueMemberColumnIndex)
								{
									using (Font boldFont = new Font(Font, FontStyle.Bold))
									{
										e.Graphics.DrawString(item, boldFont, brush, boundsRect);
									}
								}
								else
								{
									e.Graphics.DrawString(item, Font, brush, boundsRect);
								}
							}
						}

						if (colIndex < columnNames.Length - 1)
						{
							e.Graphics.DrawLine(linePen, boundsRect.Right, boundsRect.Top, boundsRect.Right, boundsRect.Bottom);
						}
					}
				}
			}
		}

		e.DrawFocusRectangle();
    }
    /**************************************************************************************************/
}


And here’s the form code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Acs;
using Backgrounder;

namespace ColorLcdDisplayTest
{
	public partial class EditDisplaySchemesDialog : Form
	{
		private AcsColorLcd320X240 _display;
		private BindingSource _schemesBindingSource = new BindingSource();
		private BindingSource _fontsBindingSource = new BindingSource();
		private const int _fontColumnFixedWidth = 100;
		private const int _colorColumnFixedWidth = 100;

		public EditDisplaySchemesDialog(AcsColorLcd320X240 display)
		{
			_display = display;

			InitializeComponent();

			_fontsBindingSource.DataSource = display.Fonts;
			dataGridViewSchemes.AutoGenerateColumns = false;

			// col 0
			DataGridViewTextBoxColumn textBoxColumn = new DataGridViewTextBoxColumn();
			textBoxColumn.Name = "Number";
			textBoxColumn.DataPropertyName = "SchemeNumber";
			textBoxColumn.HeaderText = "#";
			dataGridViewSchemes.Columns.Add(textBoxColumn);

			// col 1
			ColorPickerColumn colorPickerColumn = new ColorPickerColumn();
			colorPickerColumn.Name = "BlkReplColor";
			colorPickerColumn.DataPropertyName = "BlkReplColor";
			colorPickerColumn.HeaderText = "Replace Black";
			dataGridViewSchemes.Columns.Add(colorPickerColumn);

			// col 2
			colorPickerColumn = new ColorPickerColumn();
			colorPickerColumn.Name = "WhtReplColor";
			colorPickerColumn.DataPropertyName = "WhtReplColor";
			colorPickerColumn.HeaderText = "Replace White";
			dataGridViewSchemes.Columns.Add(colorPickerColumn);

			// col 3
			DataGridViewMultiColumnComboColumn fontPickerColumn = new DataGridViewMultiColumnComboColumn();
			fontPickerColumn.Name = "FontNumber";
			fontPickerColumn.DataPropertyName = "FontNumber";
			fontPickerColumn.DataSource = _fontsBindingSource;
			fontPickerColumn.DisplayMember = "Name";
			fontPickerColumn.ValueMember = "FontNumber";
			fontPickerColumn.HeaderText = "#-Font";
			dataGridViewSchemes.Columns.Add(fontPickerColumn);

			// col 4
			DataGridViewComboBoxColumn comboBoxColumn = new DataGridViewComboBoxColumn();
			comboBoxColumn.Name = "Colorize";
			comboBoxColumn.DataPropertyName = "Colorize";
			comboBoxColumn.DataSource = Enum.GetValues(typeof(AcsColorLcd320X240.IconColorize));
			comboBoxColumn.ValueType = typeof(AcsColorLcd320X240.IconColorize);
			comboBoxColumn.HeaderText = "Colorize";
			dataGridViewSchemes.Columns.Add(comboBoxColumn);

			// col 5
			textBoxColumn = new DataGridViewTextBoxColumn();
			textBoxColumn.Name = "TextPos";
			textBoxColumn.DataPropertyName = "TextPos";
			textBoxColumn.HeaderText = "Text Pos";
			dataGridViewSchemes.Columns.Add(textBoxColumn);

			// col 6
			comboBoxColumn = new DataGridViewComboBoxColumn();
			comboBoxColumn.Name = "IconTransp";
			comboBoxColumn.DataPropertyName = "IconTransp";
			comboBoxColumn.DataSource = Enum.GetValues(typeof(AcsColorLcd320X240.BitmapTransparencyMode));
			comboBoxColumn.ValueType = typeof(AcsColorLcd320X240.BitmapTransparencyMode);
			comboBoxColumn.HeaderText = "Transparency";
			dataGridViewSchemes.Columns.Add(comboBoxColumn);
		}

		private void refreshDataGrid()
		{
			_schemesBindingSource.DataSource = _display.Schemes;
			dataGridViewSchemes.DataSource = _schemesBindingSource;

			dataGridViewSchemes.AutoResizeColumns();

			dataGridViewSchemes.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
			dataGridViewSchemes.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
			dataGridViewSchemes.Columns[1].Width = _colorColumnFixedWidth;
			dataGridViewSchemes.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
			dataGridViewSchemes.Columns[2].Width = _colorColumnFixedWidth;
			dataGridViewSchemes.Columns[3].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCellsExceptHeader;
			dataGridViewSchemes.Columns[6].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
		}

		private void bindFonts()
		{
			_fontsBindingSource.DataSource = _display.Fonts;
		}

		private delegate void SetCommandResponseTimeoutCallback(object source, AcsColorLcd320X240.CommandResponse response);
		void _display_CommandResponseEvent(object source, AcsColorLcd320X240.CommandResponse commandResponse)
		{
			if (buttonReceive.InvokeRequired)
			{
				SetCommandResponseTimeoutCallback callback = new SetCommandResponseTimeoutCallback(_display_CommandResponseEvent);
				this.Invoke(callback, new object[] { source, commandResponse });
			}
			else
			{
				if (commandResponse != AcsColorLcd320X240.CommandResponse.CommandOk)
				{
					if (_display.Fonts.Count == 0)
					{
						_display.Fonts.AddNew();
					}
					if (_display.Schemes.Count == 0)
					{
						_display.Schemes.AddNew();
					}
					refreshDataGrid();
				}
				_display.CommandResponseEvent -= _display_CommandResponseEvent;
				buttonReceive.Enabled = true;
			}
		}

		private delegate void SetGridDataSourceCallback(object source);
		void _display_QuerySchemeItemResponseDoneEvent(object source)
		{
			if (dataGridViewSchemes.InvokeRequired)
			{
				SetGridDataSourceCallback callback = new SetGridDataSourceCallback(_display_QuerySchemeItemResponseDoneEvent);
				this.Invoke(callback, new object[] { source });
			}
			else
			{
				if (_display.Fonts.Count == 0)
				{
					_display.Fonts.AddNew();
				}
				refreshDataGrid();
				_display.QuerySchemeResponseDoneEvent -= _display_QuerySchemeItemResponseDoneEvent;
				_display.CommandResponseEvent -= _display_CommandResponseEvent;
				buttonReceive.Enabled = true;
			}
		}

		private void DisplaySchemesDialog_Shown(object sender, EventArgs e)
		{
			_display.ResetEvent += new AcsColorLcd320X240.ResetEventHandler(_display_ResetEvent);
			if (_display.Fonts.Count == 0)
			{
				_display.Fonts.AddNew();
			}

			if (_display.Schemes.Count == 0)
			{
				_display.Schemes.AddNew();
			}

			refreshDataGrid();
		}

		private void EnableControls(bool enable)
		{
			buttonLoadFonts.Enabled = enable;
			buttonSaveFonts.Enabled = enable;
			buttonReceive.Enabled = enable;
			buttonSend.Enabled = enable;
		}

		private delegate void BindFonts(object source);
		private void _display_QueryFontItemResponseDoneEvent(object source)
		{
			if (dataGridViewSchemes.InvokeRequired)
			{
				BindFonts callback = new BindFonts(_display_QueryFontItemResponseDoneEvent);
				this.Invoke(callback, new object[] {source});
			}
			else
			{
				bindFonts();
				_display.QueryFontItemResponseDoneEvent -= _display_QueryFontItemResponseDoneEvent;
				_display.CommandResponseEvent -= _display_CommandResponseEvent;
				refreshDataGrid();
			}
		}

		private delegate void CloseForm(object source);
		void _display_ResetEvent(object source)
		{
			if (this.InvokeRequired)
			{
				CloseForm callback = new CloseForm(_display_ResetEvent);
				this.BeginInvoke(callback, new object[]{source});
			}
			else
			{
				if (this.IsDisposed) return;
				if (!this.IsHandleCreated) return;

				DialogResult = DialogResult.Cancel;
				Close();
			}
		}

		private void EditDisplaySchemesDialog_FormClosing(object sender, FormClosingEventArgs e)
		{
			_display.ResetEvent -= _display_ResetEvent;

			if (Properties.Settings.Default.EditDisplaySchemesFormPosition == null)
			{
				Properties.Settings.Default.EditDisplaySchemesFormPosition = new WindowSettings();
			}
			Properties.Settings.Default.EditDisplaySchemesFormPosition.Record(this);
			Properties.Settings.Default.Save();
		}

		private void DisplaySchemesDialog_Load(object sender, EventArgs e)
		{
			if (Properties.Settings.Default.EditDisplaySchemesFormPosition != null)
			{
				Properties.Settings.Default.EditDisplaySchemesFormPosition.Restore(this);
			}
			else
			{
				this.Location = this.Owner.Location + (this.Owner.Size - this.Size);
			}
		}

		private void dataGridViewSchemes_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
		{
			DataGridView dataGridView = sender as DataGridView;

			if (dataGridView != null)
			{
				ColorDialog colorDialog = new ColorDialog();

				if (e.RowIndex < 0) 					return; 				AcsColorLcd320X240.Scheme scheme = dataGridView.Rows[e.RowIndex].DataBoundItem as AcsColorLcd320X240.Scheme; 				DataGridViewComboBoxCell comboBoxCell; 				DataGridViewComboBoxEditingControl comboBoxEditingControl; 				switch (e.ColumnIndex) 				{ 					case 1: 						colorDialog.Color = scheme.BlkReplColor; 						if (colorDialog.ShowDialog() == DialogResult.OK) 						{ 							scheme.BlkReplColor = colorDialog.Color; 							_display.SchemeColor((byte)e.RowIndex, scheme.BlkReplColor, scheme.WhtReplColor); 						} 						dataGridViewSchemes.InvalidateCell(e.ColumnIndex, e.RowIndex); 						break; 					case 2: 						colorDialog.Color = scheme.WhtReplColor; 						if (colorDialog.ShowDialog() == DialogResult.OK) 						{ 							scheme.WhtReplColor = colorDialog.Color; 							_display.SchemeColor((byte)e.RowIndex, scheme.BlkReplColor, scheme.WhtReplColor); 						} 						dataGridViewSchemes.InvalidateCell(e.ColumnIndex, e.RowIndex); 						break; 					case 5: 						EditDisplaySchemeTextPositionDialog schemeTextPositionDialog = new EditDisplaySchemeTextPositionDialog(); 						schemeTextPositionDialog.TextPosition = scheme.TextPos; 						schemeTextPositionDialog.StartPosition = FormStartPosition.Manual; 						if (schemeTextPositionDialog.ShowDialog(this) == DialogResult.OK) 						{ 							scheme.TextPos = schemeTextPositionDialog.TextPosition; 							_display.SchemeAttributes((byte)e.RowIndex, (byte)scheme.FontNumber, scheme.Colorize, scheme.TextPos, scheme.IconTransp); 							dataGridViewSchemes.InvalidateCell(e.ColumnIndex, e.RowIndex); 						} 						break; 					case 3: 					case 4: 					case 6: 						dataGridViewSchemes.BeginEdit(true); 						comboBoxCell = (DataGridViewComboBoxCell)dataGridViewSchemes[e.ColumnIndex, e.RowIndex]; 						dataGridViewSchemes.CurrentCell = comboBoxCell; 						comboBoxEditingControl = (DataGridViewComboBoxEditingControl)dataGridViewSchemes.EditingControl; 						comboBoxEditingControl.DroppedDown = true; 						break; 					default: 						break; 				} 			} 		} 		private void dataGridViewSchemes_CellValueChanged(object sender, DataGridViewCellEventArgs e) 		{ 			DataGridView dataGridView = sender as DataGridView; 			if (dataGridView != null) 			{ 				AcsColorLcd320X240.Scheme scheme = dataGridView.Rows[e.RowIndex].DataBoundItem as AcsColorLcd320X240.Scheme; 				switch (e.ColumnIndex) 				{ 					case 1: 					case 2: 						_display.SchemeColor((byte)scheme.SchemeNumber, scheme.BlkReplColor, scheme.WhtReplColor); 						break; 					case 3: 					case 4: 					case 6: 						dataGridViewSchemes.EndEdit(); 						_display.SchemeAttributes((byte)scheme.SchemeNumber, (byte)scheme.FontNumber, scheme.Colorize, scheme.TextPos, scheme.IconTransp); 						break; 					default: 						break; 				} 			} 		} 		private void buttonSaveSchemes_Click(object sender, EventArgs e) 		{ 			SaveFileDialog saveSchemes = new SaveFileDialog(); 			saveSchemes.Filter = "LCD schemes (*.schemes)|*.schemes|All files (*.*)|*.*"; 			if (saveSchemes.ShowDialog() == DialogResult.OK) 			{ 				using (StreamWriter sWriter = File.CreateText(saveSchemes.FileName)) 				{ 					string schemes = _display.SchemesToJSON(); 					sWriter.Write(schemes); 				} 			} 		} 		private void buttonLoadSchemes_Click(object sender, EventArgs e) 		{ 			OpenFileDialog loadSchemes = new OpenFileDialog(); 			loadSchemes.Filter = "LCD schemes (*.schemes)|*.schemes|All files (*.*)|*.*"; 			loadSchemes.CheckFileExists = true; 			if (loadSchemes.ShowDialog() == DialogResult.OK) 			{ 				using (StreamReader sReader = File.OpenText(loadSchemes.FileName)) 				{ 					string schemes = sReader.ReadToEnd(); 					_display.SchemesFromJSON(schemes); 					if (_display.Fonts.Count == 0) 					{ 						_display.Fonts.AddNew(); 					} 					refreshDataGrid(); 				} 			} 		} 		private ManualResetEvent manualResetEvent = new ManualResetEvent(false); 		private AcsColorLcd320X240.CommandResponse _response; 		private void ResetResponseWait() 		{ 			_response = AcsColorLcd320X240.CommandResponse.None; 			manualResetEvent.Reset(); 		} 		private bool AckResponseReceived() 		{ 			manualResetEvent.WaitOne(); 			return _response == AcsColorLcd320X240.CommandResponse.CommandOk; 		} 		void _display_WaitForCommandResponseEvent(object source, AcsColorLcd320X240.CommandResponse commandResponse) 		{ 			_response = commandResponse; 			manualResetEvent.Set(); 		} 		private BackgroundHelper backgroundHelper = new BackgroundHelper(); 		private void buttonSend_Click(object sender, EventArgs e) 		{ 			EnableControls(false); 			backgroundHelper.Background(() =>
			{
			    _display.CommandResponseEvent += new AcsColorLcd320X240.CommandResponseEventHandler(_display_WaitForCommandResponseEvent);

			    foreach (AcsColorLcd320X240.Scheme scheme in _display.Schemes)
			    {
			        ResetResponseWait();
			        _display.SchemeColor((byte) scheme.SchemeNumber, scheme.BlkReplColor,
			                            	scheme.WhtReplColor);
			        if (!AckResponseReceived())
			            goto exit;

			        ResetResponseWait();
			        _display.SchemeAttributes((byte) scheme.SchemeNumber, (byte) scheme.FontNumber,
			                            		scheme.Colorize, scheme.TextPos, scheme.IconTransp);
			        if (!AckResponseReceived())
			            goto exit;
			    }

			    exit:
					_display.CommandResponseEvent -= _display_WaitForCommandResponseEvent;
			});

			EnableControls(true);
		}

		private void buttonReceive_Click(object sender, EventArgs e)
		{
			buttonReceive.Enabled = false;
			if (_display != null)
			{
				_display.QuerySchemeResponseDoneEvent += new AcsColorLcd320X240.QuerySchemeResponseDoneEventHandler(_display_QuerySchemeItemResponseDoneEvent);
				_display.CommandResponseEvent += new AcsColorLcd320X240.CommandResponseEventHandler(_display_CommandResponseEvent);
				_display.QuerySchemes();
			}
		}

		private void dataGridViewSchemes_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			/* prevent user from saving/sending bad data */
			buttonSend.Enabled = false;
			buttonSaveFonts.Enabled = false;

			DataGridViewCellStyle errorStyle = new DataGridViewCellStyle();
			errorStyle.BackColor = Color.Yellow;
			dataGridViewSchemes.Rows[e.RowIndex].Cells[e.ColumnIndex].Style = errorStyle;

			e.Cancel = true;
		}

		private void dataGridViewSchemes_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
		{
			DataGridView dataGridView = sender as DataGridView;
			StringBuilder sb = new StringBuilder();

			if (dataGridView != null)
			{
				AcsColorLcd320X240.Scheme scheme = dataGridView.Rows[e.RowIndex].DataBoundItem as AcsColorLcd320X240.Scheme;

				// double row highlighting...
				if ((scheme.SchemeNumber & 2) != 0)
				{
					dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = Color.FromArgb(192, 255, 255);
				}

				// font # column on/off labeling...
				if (e.RowIndex >= 0 && e.ColumnIndex == 0)
				{
					if ((scheme.SchemeNumber & 1) != 0)
					{
						e.Value = sb.AppendFormat("{0}-on", scheme.SchemeNumber);
					}
					else
					{
						e.Value = sb.AppendFormat("{0}-off", scheme.SchemeNumber);
					}
				}

				// custom font labeling
				if (e.RowIndex >= 0 && e.ColumnIndex == 3)
				{
					e.Value = sb.AppendFormat("{0}-{1}", scheme.FontNumber, e.Value);
				}

				// custom text pos labeling
				if (e.RowIndex >= 0 && e.ColumnIndex == 5)
				{
					ushort value, valueX, valueY;

					value = ushort.Parse(e.Value.ToString());

					valueX = (ushort)((value >> 6) * 16);
					valueY = (ushort)(((value >> 2) & 15) * 16);

					switch (value)
					{
						case 0:
							e.Value = sb.AppendFormat("{0}-No Text", value);
							break;

						case 1:
							e.Value = sb.AppendFormat("{0}-Centered", value);
							break;

						default:
							e.Value = sb.AppendFormat("{0}-({1}%,{2}%)", value, valueX, valueY);
							break;
					}
				}
			}
		}
	}
}

DataGridView Custom ComboBox Column

I’m writing a .NET Winform application to communicate with and exercise the functionality of our forthcoming Color LCD. One of the attributes that can be selected is a font from a collection loaded into the display. Each entry in the collection has the font #, filename, foreground and background colors, spacing, transparency, etc.  In order to make it easy to ascertain which font is selected, I used an owner-draw drop list combobox that I found on CodeProject with a few changes to handle System.Drawing.Color properties and corrected the handling of the selected item coloring in the OnMeasureItem and OnDrawItem overrides:

MultiColumnComboBoxDropped

This works pretty good and allows the user to select a font based upon more that the name of the font.

I now needed to allow the user to perform this same selection when editing in a row of a DataGridView.  No problem I thought – just use this control in a custom DataGridViewComboBoxColumn class. I’m already using a customized DataGridViewButtonColumn that allows me to disable individual buttons in the column.

I again researched custom DataGridViewColumns and put together a derived class that I though should work. I had to derive a custom DataGridViewCell, DataGridViewEditingControl and DataGridViewColumn. Got everything to compile and run OK.

However it doesn’t quite work. When I try and BeginEdit() on one of these custom combobox cells, the mulitcolumncombobox class OnDropDown event gets called, but the EventArg is null. So you get a Null Reference Exception. The class looks like everything is connected up properly, the derived combobox has the correctly populated datasource, and it’s Item collection is correct.  Something is not hooked up correctly but I don’t know how to troubleshoot what is wrong. The call stack only shows the BeginEdit() call, <external code> and then the OnDropDown call with the null EventArgs parameter.  I spent about 1/2 a day trying to find solutions or comparable approaches without any luck.

So for right now, I removed this customized column, and am simply using a DataGridViewComboBoxColumn that just shows and edits the selected font using the name property of the datasource. It works, but it’s not as apparent to the user what the attributes of the selected font are.

I’m looking for some pointers on how to debug this or how to correctly hookup a derived combobox in custom DataGridViewComboBoxColumn. Maybe it needs to be derived from a DataGridViewTextBoxColumn instead ?

Thank you!

Acronis True Image Home 2012 – $40 of hell…

I’ve been using Acronis True Image Home 11 for quite some time – and was generally pleased with it’s operation. One thing that it wouldn’t do however was to backup over a network share. We recently installed a DLink NAS, and I wanted to be able to schedule backups to it – something that XP’s backup also wouldn’t do. So I purchased the latest Acronis version – True Image Home 2012.

Whatever you do, don’t buy this program – it’s a total piece of shit.

Whenever it starts it checks for a newer version – even if you say no, it exits the program and goes into the installer. It says that there is a newer version available, but then says that it can’t find it. If you quit the installer, the program hoses the existing install. Then, get this – it won’t even uninstall – it fails the uninstall. And even better, if you chose to send the error information to Acronis via a link in their install program, the support e-mail is rejected by their server ! After it hoses the install, two services that it installs consistently fail on subsequent reboots.

I downloaded a ‘cleaner’ from their website that purports to remove all traces of any versions of their software from your system and ran it. Took quite awhile, and seemed to do what it claimed to do. Funny that their uninstall process couldn’t do the same thing but who am I to tell them how to write commercial software.

Did a reinstall, and everything seemed OK.

Having wasted about 2 hours messing with this piece of shit I got on with my work. In the process I right clicked on a file to view its properties and was surprised to see a new tab labeled Acronis Recovery. What’s this ?  I clicked on the tab and my mouse froze. Keyboard still worked, but no mouse – great !  Plugging and un-plugging the Microsoft Intellimouse USB didn’t restore funtionality. However if I logged out and then logged back in, the mouse started working again. Sweet !  OK, don’t touch that new tab that Acronis has polluted Windows Explorer with.

At the end of the day, I wanted to perform a backup. So I started Acronis, and again it ignored my intentions and tried to install a newer version which, as before, it couldn’t find.  And again – no mouse.

Checking out Acronis’ support forum it seems that the mouse lockup is a very common problem. The only resolution is to not use a USB mouse.

Really ?  In 2011, don’t use a USB mouse ?  This is truly fucked.  Why using a USB mouse would cause it to freeze, but only when using Acronis software is beyond me. Why do they have to fuck with the mouse or USB stack at all ?

So today I removed Acronis True Image Home 2012. They will not receive any more of my money. And I will go out of my way to try and make sure that they don’t receive money from anybody else as well.

Death by MBA – Caveat Emptor.

UPDATE:

I’ve had good luck with Macrium Reflect under Windows 8 Pro x64. I’m considering purchasing a copy for my XP Pro SP3 system, but I am in the process of retiring that box. See http://www.macrium.com/

Embedded Linux: With friends like these, who needs enemies?

Over on DSP Design Line, Dan O’Dowd talks trash about Embedded Linux:

http://www.dspdesignline.com/showArticle.jhtml?articleID=207501083

"Embedded Linux is the most hyped embedded operating system ever. It is promoted as inexpensive, high quality, high productivity, reliable, widely available, and well supported. It is none of these things, as two of its greatest proponents have recently pointed out. Wind River Systems and MontaVista Software, companies that each describe themselves as "the leader" in embedded Linux, have both initiated marketing campaigns touting the horrors of using embedded Linux.

In the January/February 2008 issue of Military Embedded Systems, Jim Ready, the founder and chief technology officer of MontaVista, says "a [develop-it-yourself] embedded Linux distribution [is] a significant investment (read ‘big bucks’) in time and money." He estimates the three-year cost of a large scale embedded Linux deployment at $19,623,750. Here are some other quotes from the article:

"To keep abreast of the changes occurring on a daily basis a developer needs to monitor the email traffic of 11 different and unsynchronized open source projects… up to 5,000 messages a day with 1,000 of these being patches that need to be evaluated and possibly applied to the source base. Simply ignoring the traffic, figuring that the system in use seems to be working well enough, can lead to disastrous consequences later.

"A recent security patch that took all of 13 lines of code to implement against an embedded Linux system would have taken more than 800k lines of source patches to implement, if the previous trail of patches had been ignored. It’s a classic case of pay now or really pay later.

"If there ever were a situation where the ‘software money pit’ could really take hold, it’s in owning 30 million lines of constantly changing source code. Even in the simplest case, the development costs are typically in the millions of dollars."

Wind River delivers the same message in a recent full-page advertisement. It asks: "Choosing Linux as your next device operating system?" It answers: "CHAOS" in large crooked letters, followed by "fatal error," "system crash," "game over," and "panic."

Even the greatest critics of embedded Linux have never been so harsh. The experts say that embedded Linux is "CHAOS" and "a money pit." With friends like these, who needs enemies?

One would expect Wind River and MontaVista to tout the advantages of their embedded Linux support, but why trash the product on which their business is based? If they are being unfair to embedded Linux, the Linux community will rise up to denounce them, destroying their embedded Linux support business.

It’s more likely that Wind River and MontaVista are telling it as they see it–for marketing purposes. Marketing usually puts forward a problem (bad breath, headaches) that many potential customers will relate to, and then promises a solution. Why would Wind River and MontaVista put forward the problem of embedded Linux nightmares in marketing materials unless they think many potential customers have experienced those nightmares and need a solution? Wind River and MontaVista are certainly in a position to know how hard it is to use embedded Linux, because they are using it, supporting it, and selling it. And since their business is trying to pick up the pieces for companies that have already failed with embedded Linux, they have heard plenty of horror stories.

Wind River and MontaVista each say that they can tame the embedded Linux monster and make it work for customers. But can they? Trying to fix embedded Linux for eight years, MontaVista is reported to have lost over $60,000,000, going through five rounds of venture capital, three rounds of layoffs, and three CEOs in the last two years. Since jumping on the Linux bandwagon, Wind River has gone from profitability to losses, recently announcing a layoff of 7% of their staff.

So why are Wind River and MontaVista bashing embedded Linux? Each year, Embedded System Design magazine carries out a survey of embedded systems developers. Over a two year period from 2005 to 2007, the percentage of developers using embedded Linux and the percentage planning to use embedded Linux have both declined. And even more important, the percentage not interested in embedded Linux has nearly doubled.

According to ESD‘s analysis, most of those who are reasonable targets for embedded Linux (those with PC-like applications) have already adopted it. The rest have learned it is not appropriate and are moving on. See the article "Annual study uncovers the embedded market" (Richard Nass, www.embedded.com/design/opensource/201803499?pgno=2) for details of survey.

It seems clear what is happening: Wind River and MontaVista are trying to get the dwindling number of disenchanted embedded Linux users to pay them "big bucks" to escape the embedded Linux nightmare. They hope that if they can get enough customers signed up, they will finally get enough money to tame the beast.

But what happens if they cannot? There are indications that they may have exhausted the market. If Wind River fails to stem the tide, they will need to drop their support for embedded Linux to return to profitability. And if MontaVista doesn’t show some sign of stemming their losses soon, their investors will pull the plug. When Wind River and MontaVista abandon embedded Linux, their customers will have to live the embedded Linux nightmare that Wind River and MontaVista are telling them–all too clearly–that they will have.

This embedded Linux bashing from embedded Linux’s strongest proponents should give pause to those who are thinking through their embedded operating system strategy. If embedded Linux champions are saying that embedded Linux is terrible, why would anyone want to risk their products or their company on it?

Why would anyone use a product that its proponents say is awful? Would you buy a car from a salesman who admitted the car was a piece of junk just because he said he had a great service department? That’s what embedded Linux’s friends suggest that you do. With friends like these, who needs enemies?"

Windows XP can’t see second SATA drive, BIOS can…

Just a heads-up on a small problem that I’ve seen people struggling with and that I ran into recently…

I had a working Windows XP MCE system in the lab that used a SATA drive for C:. I added a second SATA drive that I had laying around. All drives appeared in the BIOS, but Windows XP would not show anything about the second drive – hardware manager, disk management, scan for hardware, nothing.

Turns out that the second hard drive’s partition was marked as Active. When I booted from a floppy, ran FDISK on the second drive and removed the active status, then Windows magically was able to see the drive.

This wasn’t a problem under Linux – it saw both drives with no problems even though both had active partitions.  Go figure.

Inversion of Control

Good collection of information about IoC on BitterCoder’s Wiki:

Container Tutorials

.NET takes down Home Depot

Not really, but it got your attention didn’t it ?

I went to our local Home Depot yesterday – found what I needed in 5 minutes, then attempted to check out. That took 20 minutes. The problem ?  All of the cash registers were exhibiting .NET Exception Error dialogs that hid behind splash screens that couldn’t be dismissed.

The cash registers are Windows 2000 Professional boxes and they must be old and networked with spit and chewing gum – they take about 5 minutes to boot up into the POS application.

Whoever wrote the POS application needs to be fired or sued. Anytime there was a problem, such as not being able to find a price, a .NET Exception message box appeared. This was subsequently covered over by a splash screen for the POS application – only the barest sliver behind the top of the splash box was visible.

And, wonderful Windows, when your only input device is a touch screen, the covered message box couldn’t be dismissed so the application locks – there was no way to move any of the displayed windows.

If recently resigned Home Depot CEO Bob Nardelli was responsible for this cluster fuck, then he deserved to be fired instead. The nearest Home Depot competitor, Lowes, is 20 miles away so we’re kinda stuck with this until somebody gets their act together.

So while it wasn’t .NET directly that brought Home Depot to it’s knees, it does have a part in it. Instead of playing with these new toys that they obviously don’t understand yet, the POS application’s authors should’ve made the app work first.