/// <summary> /// Update tooltip when mouse moves over picture /// </summary> private void HistoryPictureOnMouseMove(object sender, MouseEventArgs e) { var p = Cursor.Position; var x = p.X; var y = p.Y + 20; if (_lastPosition.X == x && _lastPosition.Y == y && _lastScrollPositionY == e.Y) { return; } if (e.X < GraphStartPixel) { return; } if (_indexToDaynumber == null || _indexToDaynumber.Length == 0) { return; } _lastPosition = new Point(x, y); // var absoluteY = y - historyPicture.Location.Y - Location.Y; var dayIndex = (e.Y - GraphSpacing / 2) / (GraphHeight + GraphSpacing); if (dayIndex < 0) { dayIndex = 0; } if (dayIndex >= _indexToDaynumber.Length) { dayIndex = _indexToDaynumber.Length - 1; } var dayNumber = _indexToDaynumber[dayIndex]; var second = (((uint)e.X - GraphStartPixel) * _graphSeconds / _graphWidth) + _graphFirstSecond; var graphPixel = e.X - GraphStartPixel; var slot = DaySlots.GetValueOrDefault(dayNumber).FirstOrDefault(d => d.EndX >= graphPixel); if (slot == null || slot.StartX > graphPixel) { _tipLabel.Text = $"{SecondToTime(second)} (no activity)"; } else { _tipLabel.Text = $"{SecondToTime(second)} {slot.Describe(_showSharedHistory)}"; } _tipLabel.Location = _lastPosition; _lastScrollPositionY = e.Y; if (!_tipLabel.Visible) { _tipLabel.ShowFloating(); } }
private DaySlots SplitDayInSlots(DayOfWeekInfo dayOfWeekInfo) { try { ThrowExceptionIsNotValidDataDay(dayOfWeekInfo); var availableSlots = new List <IntervalSlot>(); var workStartAt = GetDateTimeFrom(dayOfWeekInfo, dayOfWeekInfo.WorkPeriod.StartHour, 0); var workEndsAt = GetDateTimeFrom(dayOfWeekInfo, dayOfWeekInfo.WorkPeriod.EndHour, 0); var lunchStartAt = GetDateTimeFrom(dayOfWeekInfo, dayOfWeekInfo.WorkPeriod.LunchStartHour, 0); var lunchEndsAt = GetDateTimeFrom(dayOfWeekInfo, dayOfWeekInfo.WorkPeriod.LunchEndHour, 0); while (workStartAt != workEndsAt) { int minutes = +_weekData.SlotDurationMinutes; var slotToAdd = GetDateTimeFrom(dayOfWeekInfo, workStartAt.Hour, workStartAt.Minute); if (IsAvailableSlot(dayOfWeekInfo, slotToAdd, lunchStartAt, lunchEndsAt)) { availableSlots.Add(new IntervalSlot(slotToAdd, _weekData.SlotDurationMinutes)); } workStartAt = workStartAt.AddMinutes(minutes); } return(new DaySlots(dayOfWeekInfo.CurrentDate, availableSlots)); } catch (Exception err) { _loggerProvider.Log(err); return(DaySlots.CreateDayWithNoAvailability(dayOfWeekInfo.CurrentDate)); } }
private static IEnumerable <FreeSlot> GetDayFreeSlots(DateTime startOfDay, int slotDurationMinutes, DaySlots day) { if (day?.WorkPeriod == null) { return(null); } var freeSlots = new List <FreeSlot>(); var slotStart = new DateTime(startOfDay.Year, startOfDay.Month, startOfDay.Day, day.WorkPeriod.StartHour, 0, 0); var dayEnd = new DateTime(startOfDay.Year, startOfDay.Month, startOfDay.Day, day.WorkPeriod.EndHour, 0, 0); var slotEnd = slotStart.AddMinutes(slotDurationMinutes); while (slotEnd <= dayEnd) { if (!day.IsInLunchPeriod(slotStart, slotEnd) && !day.IsSlotBusy(slotStart, slotEnd)) { freeSlots.Add(new FreeSlot { StartTime = slotStart, EndTime = slotEnd, }); } slotStart = slotStart.AddMinutes(slotDurationMinutes); slotEnd = slotStart.AddMinutes(slotDurationMinutes); } return(freeSlots); }
private void DrawHistory(Bitmap target, Dictionary <string, MonthActivities> history) { // Figure out when first activity started and when last activity ended (for the whole month) var firstTime = history.MinOrDefault(c => c.Value.Days.MinOrDefault(d => d.Value.First().StartSecond)); var lastTime = history.MaxOrDefault(c => c.Value.Days.MaxOrDefault(d => d.Value.Last().EndSecond)); // Create list of programs used, the most used first var programs = history.SelectMany(c => c.Value.Days.Values.SelectMany(x => x.Where(a => a.WasActive) .Select(a => new { Id = c.Value.ApplicationNames[a.ApplicationNameIndex], Seconds = a.EndSecond - a.StartSecond }))) .GroupBy(z => z.Id).ToDictionary(x => x.Key, a => a.Sum(s => s.Seconds)) .OrderByDescending(x => x.Value); // Assign unique colors to the most used programs var brushLookup = programs.Zip(_programBrushes, (program, color) => new { program, color }) .ToDictionary(x => x.program.Key, x => x.color); var idleBrush = new SolidBrush(Color.Gray); _graphFirstSecond = firstTime; var graphicsObj = Graphics.FromImage(_historyGraph); var pen = new Pen(Color.Olive, 1); _graphWidth = (uint)(target.Width - 80); _graphSeconds = lastTime - firstTime; if (_graphSeconds == 0) { _graphSeconds = 1; } var daylineHeight = 2; // Draw hour lines var greypen = new Pen(Color.LightGray, 1); var firstHour = (int)Math.Ceiling(firstTime / 3600.0); var lastHour = (int)Math.Floor(lastTime / 3600.0); for (int x = firstHour; x <= lastHour; x++) { var xpixel = (((x * 3600) - firstTime) * _graphWidth) / _graphSeconds + GraphStartPixel; graphicsObj.DrawLine(greypen, xpixel, 10, xpixel, target.Height); } // Draw each day DaySlots.Clear(); int currentY = GraphSpacing; foreach (var dayNumber in _indexToDaynumber) { DaySlots.Add(dayNumber, new List <ScreenSlot>()); var dayHistory = history.Where(x => x.Value.Days.ContainsKey(dayNumber)) .ToDictionary(x => x.Key, x => x.Value.Days[dayNumber]); // At what pixel is this day's first and last activities? var todaysFirstSecond = dayHistory.Min(x => x.Value.FirstActiveSecond); var todaysLastSecond = dayHistory.Max(x => x.Value.LastActiveSecond); var startpixel = (todaysFirstSecond - firstTime) * _graphWidth / _graphSeconds; var endPixel = (todaysLastSecond - firstTime) * _graphWidth / _graphSeconds; // Draw a small line representing the whole day var graphbox = new Rectangle((int)startpixel + GraphStartPixel, currentY + GraphHeight / 2 - daylineHeight / 2, (int)(endPixel - startpixel) + 1, daylineHeight); graphicsObj.DrawRectangle(pen, graphbox); var startz = dayHistory.SelectMany(x => x.Value) .GroupBy(a => (a.StartSecond - firstTime) * _graphWidth / _graphSeconds) .ToDictionary(x => x.Key, x => x.ToList()); var endz = dayHistory.SelectMany(x => x.Value) .GroupBy(a => (a.EndSecond - firstTime) * _graphWidth / _graphSeconds) .ToDictionary(x => x.Key, x => x.ToList()); var allPixels = startz.Keys.Concat(endz.Keys).Distinct() .OrderBy(x => x).ToList(); var activeActivities = new HashSet <ActivitySpan>(); for (var index = 0; index < allPixels.Count - 1; index++) { startpixel = allPixels[index]; endPixel = allPixels[index + 1] - 0; // Add new activities if (startz.ContainsKey(startpixel)) { activeActivities.AddRange(startz[startpixel]); } // Remove ending activities if (endz.ContainsKey(startpixel)) { foreach (var endedSpan in endz[startpixel]) { activeActivities.Remove(endedSpan); } } var span = new ScreenSlot(startpixel, endPixel, currentY, activeActivities); DaySlots[dayNumber].Add(span); // Select brush Brush thisBrush; if (!span.WasActive) { thisBrush = idleBrush; } else if (activeActivities.Count(x => x.WasActive) == 1) { thisBrush = ActivityBrush(activeActivities.Single(x => x.WasActive), brushLookup); } else { var colorList = activeActivities.Select(a => ActivityBrush(a, brushLookup).Color) .Distinct().OrderBy(x => x.Name).ToList(); var key = string.Join("|", colorList.Select(x => x.Name)); if (_textureBrushes.ContainsKey(key)) { thisBrush = _textureBrushes[key]; } else { var scale = 3; var img = new Bitmap(1, colorList.Count * scale); var ix = 0; foreach (var color in colorList) { for (int i = 0; i < scale; i++) { img.SetPixel(0, ix++, color); } } thisBrush = new TextureBrush(img); _textureBrushes[key] = thisBrush; } } // Draw graphicsObj.FillRectangle(thisBrush, span.DrawBox); } currentY += GraphSpacing + GraphHeight; } graphicsObj.Dispose(); }