1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 // Jordi Mas i Hernandez <jordi@ximian.com>
25 // Datagrid drawing logic
31 using System.Drawing.Drawing2D;
33 namespace System.Windows.Forms
35 internal class DataGridDrawing
37 #region Local Variables
39 private DataGrid grid;
42 internal Rectangle caption_area;
43 internal Rectangle parent_rows;
44 internal Rectangle columnshdrs_area; // Used columns header area
45 internal int columnshdrs_maxwidth; // Total width (max width) for columns headrs
46 internal Rectangle rowshdrs_area; // Used Headers rows area
47 internal int rowshdrs_maxheight; // Total height for rows (max height)
48 internal Rectangle cells_area;
49 internal Font font_newrow = new Font (FontFamily.GenericSansSerif, 16);
50 #endregion // Local Variables
53 public DataGridDrawing (DataGrid datagrid)
58 #region Public Instance Methods
60 // Calc the max with of all columns
61 internal int CalcAllColumnsWidth ()
64 int cnt = grid.CurrentTableStyle.GridColumnStyles.Count;
66 for (int col = 0; col < cnt; col++) {
67 width += grid.CurrentTableStyle.GridColumnStyles[col].Width;
73 // Gets a column from a pixel
74 private int FromPixelToColumn (int pixel, out int column_x)
77 int cnt = grid.CurrentTableStyle.GridColumnStyles.Count;
83 if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
84 width += rowshdrs_area.X + rowshdrs_area.Width;
85 column_x += rowshdrs_area.X + rowshdrs_area.Width;
88 for (int col = 0; col < cnt; col++) {
89 width += grid.CurrentTableStyle.GridColumnStyles[col].Width;
94 column_x += grid.CurrentTableStyle.GridColumnStyles[col].Width;
101 public int GetColumnStartingPixel (int my_col)
104 int cnt = grid.CurrentTableStyle.GridColumnStyles.Count;
106 for (int col = 0; col < cnt; col++) {
111 width += grid.CurrentTableStyle.GridColumnStyles[col].Width;
117 // Which column has to be the first visible column to ensure a column visibility
118 public int GetFirstColumnForColumnVisilibility (int current_first_visiblecolumn, int column)
120 int new_col = column;
123 if (column > current_first_visiblecolumn) { // Going forward
124 for (new_col = column; new_col >= 0; new_col--){
125 width += grid.CurrentTableStyle.GridColumnStyles[new_col].Width;
127 if (width >= cells_area.Width)
129 //return new_col < grid.CurrentTableStyle.GridColumnStyles.Count ? new_col + 1 : grid.CurrentTableStyle.GridColumnStyles.Count;
137 bool in_calc_grid_areas;
138 public void CalcGridAreas ()
140 if (grid.IsHandleCreated == false) // Delay calculations until the handle is created
143 /* make sure we don't happen to end up in this method again */
144 if (in_calc_grid_areas)
147 in_calc_grid_areas = true;
149 /* Order is important. E.g. row headers max. height depends on caption */
150 grid.horz_pixeloffset = 0;
153 CalcRowsHeaders (grid.visiblerow_count);
154 CalcColumnsHeader ();
157 bool needHoriz = false;
158 bool needVert = false;
160 /* figure out which scrollbars we need, and what the visible areas are */
161 int visible_cells_width = cells_area.Width;
162 int visible_cells_height = cells_area.Height;
163 int width_of_all_columns = CalcAllColumnsWidth ();
164 int allrows = grid.RowsCount;
165 if (grid.ShowEditRow && grid.RowsCount > 0)
168 /* use a loop to iteratively calculate whether
169 * we need horiz/vert scrollbars. */
170 for (int i = 0; i < 3; i ++) {
172 visible_cells_width = cells_area.Width - grid.vert_scrollbar.Width;
174 visible_cells_height = cells_area.Height - grid.horiz_scrollbar.Height;
176 UpdateVisibleRowCount ();
178 needHoriz = (width_of_all_columns > visible_cells_width);
179 needVert = (grid.visiblerow_count != allrows);
182 int horiz_scrollbar_width = grid.ClientRectangle.Width;
183 int horiz_scrollbar_maximum = 0;
184 int vert_scrollbar_height = 0;
185 int vert_scrollbar_maximum = 0;
188 SetUpVerticalScrollBar (out vert_scrollbar_height, out vert_scrollbar_maximum);
191 SetUpHorizontalScrollBar (out horiz_scrollbar_maximum);
193 cells_area.Width = visible_cells_width;
194 cells_area.Height = visible_cells_height;
196 if (needVert && needHoriz) {
197 if (grid.ShowParentRowsVisible) {
198 parent_rows.Width -= grid.vert_scrollbar.Width;
201 if (!ShowingColumnHeaders) {
202 if (columnshdrs_area.X + columnshdrs_area.Width > grid.vert_scrollbar.Location.X) {
203 columnshdrs_area.Width -= grid.vert_scrollbar.Width;
207 horiz_scrollbar_width -= grid.vert_scrollbar.Width;
208 vert_scrollbar_height -= grid.horiz_scrollbar.Height;
212 if (rowshdrs_area.Y + rowshdrs_area.Height > grid.ClientRectangle.Y + grid.ClientRectangle.Height) {
213 rowshdrs_area.Height -= grid.horiz_scrollbar.Height;
214 rowshdrs_maxheight -= grid.horiz_scrollbar.Height;
217 grid.vert_scrollbar.Height = vert_scrollbar_height;
218 grid.vert_scrollbar.Maximum = vert_scrollbar_maximum;
219 grid.Controls.Add (grid.vert_scrollbar);
220 grid.vert_scrollbar.Visible = true;
223 grid.Controls.Remove (grid.vert_scrollbar);
224 grid.vert_scrollbar.Visible = false;
228 grid.horiz_scrollbar.Width = horiz_scrollbar_width;
229 grid.horiz_scrollbar.Maximum = horiz_scrollbar_maximum;
230 grid.Controls.Add (grid.horiz_scrollbar);
231 grid.horiz_scrollbar.Visible = true;
234 grid.Controls.Remove (grid.horiz_scrollbar);
235 grid.horiz_scrollbar.Visible = false;
238 UpdateVisibleColumn ();
239 UpdateVisibleRowCount ();
241 //Console.WriteLine ("DataGridDrawing.CalcGridAreas caption_area:{0}", caption_area);
242 //Console.WriteLine ("DataGridDrawing.CalcGridAreas parent_rows:{0}", parent_rows);
243 //Console.WriteLine ("DataGridDrawing.CalcGridAreas rowshdrs_area:{0}", rowshdrs_area);
244 //Console.WriteLine ("DataGridDrawing.CalcGridAreas columnshdrs_area:{0}", columnshdrs_area);
245 //Console.WriteLine ("DataGridDrawing.CalcGridAreas cells:{0}", cells_area);
247 in_calc_grid_areas = false;
250 private void CalcCaption ()
252 if (grid.caption_visible == false) {
253 caption_area = Rectangle.Empty;
257 caption_area.X = grid.ClientRectangle.X;
258 caption_area.Y = grid.ClientRectangle.Y;
259 caption_area.Width = grid.ClientRectangle.Width;
260 caption_area.Height = grid.CaptionFont.Height + 6;
262 //Console.WriteLine ("DataGridDrawing.CalcCaption {0}", caption_area);
265 private void CalcCellsArea ()
267 if (grid.caption_visible) {
268 cells_area.Y = caption_area.Y + caption_area.Height;
270 cells_area.Y = grid.ClientRectangle.Y;
273 if (grid.ShowParentRowsVisible) {
274 cells_area.Y += parent_rows.Height;
277 if (ShowingColumnHeaders) {
278 cells_area.Y += columnshdrs_area.Height;
281 cells_area.X = grid.ClientRectangle.X + rowshdrs_area.Width;
282 cells_area.Width = grid.ClientRectangle.X + grid.ClientRectangle.Width - cells_area.X;
283 cells_area.Height = grid.ClientRectangle.Y + grid.ClientRectangle.Height - cells_area.Y;
285 //Console.WriteLine ("DataGridDrawing.CalcCellsArea {0}", cells_area);
288 private void CalcColumnsHeader ()
290 int width_all_cols, max_width_cols;
292 if (!ShowingColumnHeaders) {
293 columnshdrs_area = Rectangle.Empty;
297 if (grid.caption_visible) {
298 columnshdrs_area.Y = caption_area.Y + caption_area.Height;
300 columnshdrs_area.Y = grid.ClientRectangle.Y;
303 if (grid.ShowParentRowsVisible) {
304 columnshdrs_area.Y += parent_rows.Height;
307 columnshdrs_area.X = grid.ClientRectangle.X;
308 columnshdrs_area.Height = ColumnsHeaderHeight;
309 width_all_cols = CalcAllColumnsWidth ();
311 // TODO: take into account Scrollbars
312 columnshdrs_maxwidth = grid.ClientRectangle.X + grid.ClientRectangle.Width - columnshdrs_area.X;
313 max_width_cols = columnshdrs_maxwidth;
315 if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
316 max_width_cols -= grid.RowHeaderWidth;
319 if (width_all_cols > max_width_cols) {
320 columnshdrs_area.Width = columnshdrs_maxwidth;
322 columnshdrs_area.Width = width_all_cols;
324 if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
325 columnshdrs_area.Width += grid.RowHeaderWidth;
329 //Console.WriteLine ("DataGridDrawing.CalcColumnsHeader {0}", columnshdrs_area);
332 private void CalcParentRows ()
334 if (grid.ShowParentRowsVisible == false) {
335 parent_rows = Rectangle.Empty;
339 if (grid.caption_visible) {
340 parent_rows.Y = caption_area.Y + caption_area.Height;
343 parent_rows.Y = grid.ClientRectangle.Y;
346 parent_rows.X = grid.ClientRectangle.X;
347 parent_rows.Width = grid.ClientRectangle.Width;
348 parent_rows.Height = grid.CaptionFont.Height + 3;
350 //Console.WriteLine ("DataGridDrawing.CalcParentRows {0}", parent_rows);
353 private void CalcRowsHeaders (int visiblerow_count)
355 if (grid.CurrentTableStyle.CurrentRowHeadersVisible == false) {
356 rowshdrs_area = Rectangle.Empty;
360 if (grid.caption_visible) {
361 rowshdrs_area.Y = caption_area.Y + caption_area.Height;
363 rowshdrs_area.Y = grid.ClientRectangle.Y;
366 if (grid.ShowParentRowsVisible) {
367 rowshdrs_area.Y += parent_rows.Height;
370 if (ShowingColumnHeaders) { // first block is painted by ColumnHeader
371 rowshdrs_area.Y += ColumnsHeaderHeight;
374 rowshdrs_area.X = grid.ClientRectangle.X;
375 rowshdrs_area.Width = grid.RowHeaderWidth;
376 rowshdrs_area.Height = visiblerow_count * grid.RowHeight;
377 rowshdrs_maxheight = grid.ClientRectangle.Height + grid.ClientRectangle.Y - rowshdrs_area.Y;
379 //Console.WriteLine ("DataGridDrawing.CalcRowsHeaders {0} {1}", rowshdrs_area,
380 // rowshdrs_maxheight);
383 private int GetVisibleRowCount (int visibleHeight)
386 int total_rows = grid.RowsCount;
388 if (grid.ShowEditRow && grid.RowsCount > 0) {
392 int rows_height = (total_rows - grid.first_visiblerow) * grid.RowHeight;
393 int max_rows = visibleHeight / grid.RowHeight;
395 if (max_rows > total_rows) {
396 max_rows = total_rows;
399 if (rows_height > cells_area.Height) {
405 if (rv + grid.first_visiblerow > total_rows)
406 rv = total_rows - grid.first_visiblerow;
410 public void UpdateVisibleColumn ()
412 if (grid.CurrentTableStyle.GridColumnStyles.Count == 0) {
413 grid.visiblecolumn_count = 0;
418 int max_pixel = grid.horz_pixeloffset + cells_area.Width;
421 grid.first_visiblecolumn = FromPixelToColumn (grid.horz_pixeloffset, out unused);
423 col = FromPixelToColumn (max_pixel, out unused);
425 grid.visiblecolumn_count = 1 + col - grid.first_visiblecolumn;
427 if (grid.first_visiblecolumn + grid.visiblecolumn_count < grid.CurrentTableStyle.GridColumnStyles.Count) {
428 grid.visiblecolumn_count++; // Partially visible column
432 public void UpdateVisibleRowCount ()
434 int max_height = cells_area.Height;
435 int max_rows = max_height / grid.RowHeight;
437 int total_rows = grid.RowsCount;
439 if (grid.ShowEditRow && grid.RowsCount > 0) {
443 if (max_rows > total_rows) {
444 max_rows = total_rows;
447 grid.visiblerow_count = GetVisibleRowCount (max_height);
449 CalcRowsHeaders (grid.visiblerow_count); // Height depends on num of visible rows
451 if (grid.visiblerow_count < max_rows) {
452 grid.visiblerow_count = max_rows;
453 grid.first_visiblerow = total_rows - max_rows;
459 const int RESIZE_AREA_SIZE = 5;
461 // From Point to Cell
462 public DataGrid.HitTestInfo HitTest (int x, int y)
464 DataGrid.HitTestInfo hit = new DataGrid.HitTestInfo ();
466 if (columnshdrs_area.Contains (x, y)) {
467 int offset_x = x + grid.horz_pixeloffset;
469 int column_under_mouse = FromPixelToColumn (offset_x, out column_x);
471 if ((column_x + grid.CurrentTableStyle.GridColumnStyles[column_under_mouse].Width - offset_x < RESIZE_AREA_SIZE)
472 && column_under_mouse < grid.CurrentTableStyle.GridColumnStyles.Count) {
473 hit.type = DataGrid.HitTestType.ColumnResize;
474 hit.column = column_under_mouse;
477 hit.type = DataGrid.HitTestType.ColumnHeader;
478 hit.column = column_under_mouse;
483 // TODO: Add missing RowResize checks
484 if (rowshdrs_area.Contains (x, y)) {
485 hit.type = DataGrid.HitTestType.RowHeader;
487 int rcnt = grid.FirstVisibleRow + grid.VisibleRowCount;
488 for (int r = grid.FirstVisibleRow; r < rcnt; r++) {
489 posy = cells_area.Y + ((r - grid.FirstVisibleRow) * grid.RowHeight);
490 if (y <= posy + grid.RowHeight) { // Found row
498 if (caption_area.Contains (x, y)) {
499 hit.type = DataGrid.HitTestType.Caption;
503 if (parent_rows.Contains (x, y)) {
504 hit.type = DataGrid.HitTestType.ParentRows;
508 int pos_y, pos_x, width;
509 int rowcnt = grid.FirstVisibleRow + grid.VisibleRowCount;
510 for (int row = grid.FirstVisibleRow; row < rowcnt; row++) {
511 pos_y = cells_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
513 if (y <= pos_y + grid.RowHeight) { // Found row
515 hit.type = DataGrid.HitTestType.Cell;
517 int column_cnt = grid.first_visiblecolumn + grid.visiblecolumn_count;
518 for (int column = grid.first_visiblecolumn; column < column_cnt; column++) {
520 col_pixel = GetColumnStartingPixel (column);
521 pos_x = cells_area.X + col_pixel - grid.horz_pixeloffset;
522 width = grid.CurrentTableStyle.GridColumnStyles[column].Width;
524 if (x <= pos_x + width) { // Column found
537 public Rectangle GetCellBounds (int row, int col)
539 Rectangle bounds = new Rectangle ();
542 bounds.Width = grid.CurrentTableStyle.GridColumnStyles[col].Width;
543 bounds.Height = grid.RowHeight;
544 bounds.Y = cells_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
545 col_pixel = GetColumnStartingPixel (col);
546 bounds.X = cells_area.X + col_pixel - grid.horz_pixeloffset;
550 public void InvalidateCaption ()
552 if (caption_area.IsEmpty)
555 grid.Invalidate (caption_area);
558 public void InvalidateCells ()
560 if (cells_area.IsEmpty)
563 grid.Invalidate (cells_area);
566 public void InvalidateRow (int row)
568 if (row < grid.FirstVisibleRow || row > grid.FirstVisibleRow + grid.VisibleRowCount) {
572 Rectangle rect_row = new Rectangle ();
574 int row_width = CalcAllColumnsWidth ();
575 if (row_width > cells_area.Width)
576 row_width = cells_area.Width;
577 rect_row.X = cells_area.X;
578 rect_row.Width = row_width;
579 rect_row.Height = grid.RowHeight;
580 rect_row.Y = cells_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
581 grid.Invalidate (rect_row);
584 public void InvalidateRowHeader (int row)
586 Rectangle rect_rowhdr = new Rectangle ();
587 rect_rowhdr.X = rowshdrs_area.X;
588 rect_rowhdr.Width = rowshdrs_area.Width;
589 rect_rowhdr.Height = grid.RowHeight;
590 rect_rowhdr.Y = rowshdrs_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
591 grid.Invalidate (rect_rowhdr);
594 public void InvalidateColumn (DataGridColumnStyle column)
596 Rectangle rect_col = new Rectangle ();
600 col = grid.CurrentTableStyle.GridColumnStyles.IndexOf (column);
606 rect_col.Width = column.Width;
607 col_pixel = GetColumnStartingPixel (col);
608 rect_col.X = cells_area.X + col_pixel - grid.horz_pixeloffset;
609 rect_col.Y = cells_area.Y;
610 rect_col.Height = cells_area.Height;
611 grid.Invalidate (rect_col);
614 public void DrawResizeLine (int x)
616 XplatUI.DrawReversibleRectangle (grid.Handle,
617 new Rectangle (x, CellsArea.Y, 1, CellsArea.Height),
621 void SetUpHorizontalScrollBar (out int maximum)
623 maximum = CalcAllColumnsWidth ();
625 grid.horiz_scrollbar.Location = new Point (grid.ClientRectangle.X, grid.ClientRectangle.Y +
626 grid.ClientRectangle.Height - grid.horiz_scrollbar.Height);
628 grid.horiz_scrollbar.Size = new Size (grid.ClientRectangle.Width,
629 grid.horiz_scrollbar.Height);
631 grid.horiz_scrollbar.LargeChange = cells_area.Width;
635 void SetUpVerticalScrollBar (out int height, out int maximum)
639 if (grid.caption_visible) {
640 y = grid.ClientRectangle.Y + caption_area.Height;
641 height = grid.ClientRectangle.Height - caption_area.Height;
643 y = grid.ClientRectangle.Y;
644 height = grid.ClientRectangle.Height;
647 grid.vert_scrollbar.Location = new Point (grid.ClientRectangle.X +
648 grid.ClientRectangle.Width - grid.vert_scrollbar.Width, y);
650 grid.vert_scrollbar.Size = new Size (grid.vert_scrollbar.Width,
653 maximum = grid.RowsCount;
655 if (grid.ShowEditRow && grid.RowsCount > 0) {
659 grid.vert_scrollbar.LargeChange = VLargeChange;
662 #endregion // Public Instance Methods
664 #region Instance Properties
665 public Rectangle CellsArea {
671 // Returns the ColumnsHeader area excluding the rectangle shared with RowsHeader
672 public Rectangle ColumnsHeadersArea {
674 Rectangle columns_area = columnshdrs_area;
676 if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
677 columns_area.X += grid.RowHeaderWidth;
678 columns_area.Width -= grid.RowHeaderWidth;
684 bool ShowingColumnHeaders {
685 get { return grid.columnheaders_visible != false && grid.CurrentTableStyle.GridColumnStyles.Count > 0; }
688 int ColumnsHeaderHeight {
690 return grid.CurrentTableStyle.HeaderFont.Height + 6;
694 public Rectangle RowsHeadersArea {
696 return rowshdrs_area;
700 public int VLargeChange {
702 return cells_area.Height / grid.RowHeight;
706 #endregion Instance Properties