Wednesday, January 25, 2012

/*I got annoyed always refreshing my FogBugz inbox to see new issues. FogBugz comes with a feature to e-mail you when a ticket is assigned to you. However new ticket don't go to me, they go to my boss first. And he like us to check the box periodically since he is always busy. So this checks the box for me so I don't have to do it. It creates a colored notification icon, and the colors change whenever a new ticket arrives. I could easily be adapted to a variety of other situations.*/

using System;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace BugColor
{
static class Program
{
private static NotifyIcon ni;
private static Timer timer;
private static int count;

private static void menu_Close(object sender, EventArgs e)
{
ni.Visible = false;
Application.Exit();
}

public static Color HSVtoRGB(float h, float s, float v)
//see http://www.cs.rit.edu/~ncs/color/t_convert.html
{
if (0 > h || 360 < h)
{
throw new ArgumentOutOfRangeException(
"h",
h,
"Value must be within a range of 0 - 360.");
}

if (0 > s || 1 < s)
{
throw new ArgumentOutOfRangeException(
"s",
s,
"Value must be within a range of 0 - 1.");
}

if (0 > v || 1 < v)
{
throw new ArgumentOutOfRangeException(
"v",
v,
"Value must be within a range of 0 - 1.");
}

float r, g, b;

if (s == 0)
{
// achromatic (grey)
r = g = b = v;
}
else
{
h /= 60; // sector 0 to 5
int i = (int)(Math.Floor(h));
float f = h - i;
float p = v * (1 - s);
float q = v * (1 - s * f);
float t = v * (1 - s * (1 - f));

switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
break;
}
}
return Color.FromArgb(
Convert.ToInt32(r * 255),
Convert.ToInt32(g * 255),
Convert.ToInt32(b * 255));
}

[DllImport("user32.dll", EntryPoint = "DestroyIcon")]
static extern bool DestroyIcon(IntPtr hIcon);
//See http://social.msdn.microsoft.com/Forums/en/winforms/thread/3dd02621-4d7c-470d-b16b-610b11f8213c

private static void timer1_Tick(object sender, EventArgs e)
{
var con = new SqlConnection("Data Source=OPTIMUS-PRIME;Initial Catalog=fogbugz;Trusted_Connection=yes");
var cmd = new SqlCommand("SELECT MAX(ixBug) FROM Bug", con);
SolidBrush brl, brc, brr;
const float inc = 37f;

try
{
con.Open();
int nextCount = (int) cmd.ExecuteScalar();
con.Close();
if (nextCount == count)
return;
count = nextCount;
brl = new SolidBrush(HSVtoRGB(((count - 1) * inc) % 360f, 1, 1));
brc = new SolidBrush(HSVtoRGB((count * inc) % 360f, 1, 1));
brr = new SolidBrush(HSVtoRGB(((count + 1) * inc) % 360f, 1, 1));
}
catch(SqlException)
{
brl = brc = brr = new SolidBrush(Color.Black);
}

var bmp = ni.Icon.ToBitmap();
var g = Graphics.FromImage(bmp);
g.FillRectangle(brl, 0, 0, 5, 16);
g.FillRectangle(brc, 5, 0, 6, 16);
g.FillRectangle(brr, 11, 0, 5, 16);
brl.Dispose();
brc.Dispose();
brr.Dispose();
//ni.Icon.Dispose(); Does not destroy the icon, bug in .NET Framework
DestroyIcon(ni.Icon.Handle);
var h = bmp.GetHicon();
ni.Icon = Icon.FromHandle(h);
}

///
/// The main entry point for the application.
///

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

var bmp = new Bitmap(16, 16, PixelFormat.Format24bppRgb);
ni = new NotifyIcon
{
Icon = Icon.FromHandle(bmp.GetHicon()),
Visible = true,
ContextMenu = new ContextMenu()
};
ni.ContextMenu.MenuItems.Add("Close", menu_Close);

timer = new Timer {Interval = 1000};
timer.Tick += timer1_Tick;
timer.Enabled = true;

Application.Run();
}
}
}