2006-03-21 Alexander Olk <alex.olk@googlemail.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DataGridDrawingLogic.cs
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:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
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.
19 //
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Author:
23 //      Jordi Mas i Hernandez <jordi@ximian.com>
24 //
25 // Datagrid drawing logic
26 //
27
28 // NOT COMPLETE
29
30 using System.Drawing;
31 using System.Drawing.Drawing2D;
32
33 namespace System.Windows.Forms
34 {
35         internal class DataGridDrawing
36         {
37                 #region Local Variables
38
39                 private DataGrid grid;
40
41                 // Areas
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
51
52
53                 public DataGridDrawing (DataGrid datagrid)
54                 {
55                          grid = datagrid;                        
56                 }
57
58                 #region Public Instance Methods
59
60                 // Calc the max with of all columns
61                 internal int CalcAllColumnsWidth ()
62                 {
63                         int width = 0;
64                         int cnt = grid.CurrentTableStyle.GridColumnStyles.Count;
65
66                         for (int col = 0; col < cnt; col++) {
67                                 width += grid.CurrentTableStyle.GridColumnStyles[col].Width;
68                         }
69
70                         return width;
71                 }
72
73                 // Gets a column from a pixel
74                 public int FromPixelToColumn (int pixel)
75                 {
76                         int width = 0;
77                         int cnt = grid.CurrentTableStyle.GridColumnStyles.Count;
78
79                         if (cnt == 0)
80                                 return 0;
81                                 
82                         if (grid.CurrentTableStyle.CurrentRowHeadersVisible)
83                                 width += rowshdrs_area.X + rowshdrs_area.Width;
84
85                         for (int col = 0; col < cnt; col++) {
86                                 width += grid.CurrentTableStyle.GridColumnStyles[col].Width;
87
88                                 if (pixel < width)
89                                         return col;
90                         }
91
92                         return cnt - 1;
93                 }
94
95                 //
96                 public int GetColumnStartingPixel (int my_col)
97                 {
98                         int width = 0;
99                         int cnt = grid.CurrentTableStyle.GridColumnStyles.Count;
100
101                         for (int col = 0; col < cnt; col++) {
102
103                                 if (my_col == col)
104                                         return width;
105
106                                 width += grid.CurrentTableStyle.GridColumnStyles[col].Width;
107                         }
108
109                         return 0;
110                 }
111                 
112                 // Which column has to be the first visible column to ensure a column visibility
113                 public int GetFirstColumnForColumnVisilibility (int current_first_visiblecolumn, int column)
114                 {
115                         int new_col = column;
116                         int width = 0;
117                         
118                         if (column > current_first_visiblecolumn) { // Going forward                                                            
119                                 for (new_col = column; new_col >= 0; new_col--){
120                                         width += grid.CurrentTableStyle.GridColumnStyles[new_col].Width;
121                                         
122                                         if (width >= cells_area.Width)
123                                                 return new_col + 1;
124                                                 //return new_col < grid.CurrentTableStyle.GridColumnStyles.Count ? new_col + 1 : grid.CurrentTableStyle.GridColumnStyles.Count;
125                                 }
126                                 return 0;
127                         } else {                                
128                                 return  column;
129                         }                       
130                 }
131
132                 public void CalcGridAreas ()
133                 {
134                         if (grid.IsHandleCreated == false) // Delay calculations until the handle is created
135                                 return;
136
137                         /* Order is important. E.g. row headers max. height depends on caption */
138                         grid.horz_pixeloffset = 0;                      
139                         CalcCaption ();
140                         CalcParentRows ();
141                         CalcRowsHeaders ();
142                         CalcColumnsHeader ();
143                         CalcCellsArea ();
144
145                         UpdateVisibleRowCount (); // need it to be able to calcultate the need of horz scrollbar
146                         if (SetUpVerticalScrollBar ()) { // We need a Vertical ScrollBar
147                                 
148                                 if (grid.ShowParentRowsVisible) {
149                                         parent_rows.Width -= grid.vert_scrollbar.Width;
150                                 }
151
152                                 if (grid.columnheaders_visible == false || grid.CurrentTableStyle.GridColumnStyles.Count == 0) {
153                                         if (columnshdrs_area.X + columnshdrs_area.Width > grid.vert_scrollbar.Location.X) {
154                                                 columnshdrs_area.Width -= grid.vert_scrollbar.Width;
155                                         }
156                                 }
157
158                                 if (cells_area.X + cells_area.Width >= grid.vert_scrollbar.Location.X) {
159                                         cells_area.Width -= grid.vert_scrollbar.Width;
160                                 }
161                         }
162
163                         if (SetUpHorizontalScrollBar ()) { // We need a Horizontal ScrollBar
164                                 cells_area.Height -= grid.horiz_scrollbar.Height;
165
166                                 if (rowshdrs_area.Y + rowshdrs_area.Height > grid.ClientRectangle.Y + grid.ClientRectangle.Height) {
167                                         rowshdrs_area.Height -= grid.horiz_scrollbar.Height;
168                                         rowshdrs_maxheight -= grid.horiz_scrollbar.Height;
169                                 }
170                         }
171
172                         // Reajust scrollbars to avoid overlapping at the corners
173                         if (grid.vert_scrollbar.Visible && grid.horiz_scrollbar.Visible) {
174                                 grid.horiz_scrollbar.Width -= grid.vert_scrollbar.Width;
175                                 grid.vert_scrollbar.Height -= grid.horiz_scrollbar.Height;
176                         }
177
178                         UpdateVisibleColumn ();
179                         UpdateVisibleRowCount ();
180
181                         //Console.WriteLine ("DataGridDrawing.CalcGridAreas caption_area:{0}", caption_area);
182                         //Console.WriteLine ("DataGridDrawing.CalcGridAreas parent_rows:{0}", parent_rows);
183                         //Console.WriteLine ("DataGridDrawing.CalcGridAreas rowshdrs_area:{0}", rowshdrs_area);
184                         //Console.WriteLine ("DataGridDrawing.CalcGridAreas columnshdrs_area:{0}", columnshdrs_area);
185                         //Console.WriteLine ("DataGridDrawing.CalcGridAreas cells:{0}", cells_area);
186                 }
187
188                 public void CalcCaption ()
189                 {
190                         if (grid.caption_visible == false) {
191                                 caption_area = Rectangle.Empty;
192                                 return;
193                         }
194
195                         caption_area.X = grid.ClientRectangle.X;
196                         caption_area.Y = grid.ClientRectangle.Y;
197                         caption_area.Width = grid.ClientRectangle.Width;
198                         caption_area.Height = grid.CaptionFont.Height + 6;
199
200                         //Console.WriteLine ("DataGridDrawing.CalcCaption {0}", caption_area);
201                 }
202
203                 public void CalcCellsArea ()
204                 {
205                         if (grid.caption_visible) {
206                                 cells_area.Y = caption_area.Y + caption_area.Height;
207                         } else {
208                                 cells_area.Y = grid.ClientRectangle.Y;
209                         }
210
211                         if (grid.ShowParentRowsVisible) {
212                                 cells_area.Y += parent_rows.Height;
213                         }
214
215                         if (!(grid.columnheaders_visible == false || grid.CurrentTableStyle.GridColumnStyles.Count == 0)) {
216                                 cells_area.Y += columnshdrs_area.Height;
217                         }
218
219                         cells_area.X = grid.ClientRectangle.X + rowshdrs_area.Width;
220                         cells_area.Width = grid.ClientRectangle.X + grid.ClientRectangle.Width - cells_area.X;
221                         cells_area.Height = grid.ClientRectangle.Y + grid.ClientRectangle.Height - cells_area.Y;
222
223                         //Console.WriteLine ("DataGridDrawing.CalcCellsArea {0}", cells_area);
224                 }
225
226                 public void CalcColumnsHeader ()
227                 {
228                         int width_all_cols, max_width_cols;
229                         
230                         if (grid.columnheaders_visible == false || grid.CurrentTableStyle.GridColumnStyles.Count == 0) {
231                                 columnshdrs_area = Rectangle.Empty;                             
232                                 return;
233                         }
234
235                         if (grid.caption_visible) {
236                                 columnshdrs_area.Y = caption_area.Y + caption_area.Height;
237                         } else {
238                                 columnshdrs_area.Y = grid.ClientRectangle.Y;
239                         }
240
241                         if (grid.ShowParentRowsVisible) {
242                                 columnshdrs_area.Y += parent_rows.Height;
243                         }
244
245                         columnshdrs_area.X = grid.ClientRectangle.X;
246                         columnshdrs_area.Height = ColumnsHeaderHeight;
247                         width_all_cols = CalcAllColumnsWidth ();
248
249                         // TODO: take into account Scrollbars
250                         columnshdrs_maxwidth = grid.ClientRectangle.X + grid.ClientRectangle.Width - columnshdrs_area.X;
251                         max_width_cols = columnshdrs_maxwidth;
252
253                         if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
254                                 max_width_cols -= grid.RowHeaderWidth;
255                         }
256
257                         if (width_all_cols > max_width_cols) {
258                                 columnshdrs_area.Width = columnshdrs_maxwidth;
259                         } else {
260                                 columnshdrs_area.Width = width_all_cols;
261
262                                 if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
263                                         columnshdrs_area.Width += grid.RowHeaderWidth;
264                                 }
265                         }
266
267                         //Console.WriteLine ("DataGridDrawing.CalcColumnsHeader {0}", columnshdrs_area);
268                 }
269
270                 public void CalcParentRows ()
271                 {
272                         if (grid.ShowParentRowsVisible == false) {
273                                 parent_rows = Rectangle.Empty;
274                                 return;
275                         }
276
277                         if (grid.caption_visible) {
278                                 parent_rows.Y = caption_area.Y + caption_area.Height;
279
280                         } else {
281                                 parent_rows.Y = grid.ClientRectangle.Y;
282                         }
283
284                         parent_rows.X = grid.ClientRectangle.X;
285                         parent_rows.Width = grid.ClientRectangle.Width;
286                         parent_rows.Height = grid.CaptionFont.Height + 3;
287
288                         //Console.WriteLine ("DataGridDrawing.CalcParentRows {0}", parent_rows);
289                 }
290
291                 public void CalcRowsHeaders ()
292                 {
293                         if (grid.CurrentTableStyle.CurrentRowHeadersVisible == false) {
294                                 rowshdrs_area = Rectangle.Empty;
295                                 return;
296                         }
297
298                         if (grid.caption_visible) {
299                                 rowshdrs_area.Y = caption_area.Y + caption_area.Height;
300                         } else {
301                                 rowshdrs_area.Y = grid.ClientRectangle.Y;
302                         }
303
304                         if (grid.ShowParentRowsVisible) {
305                                 rowshdrs_area.Y += parent_rows.Height;
306                         }
307
308                         if (!(grid.columnheaders_visible == false || grid.CurrentTableStyle.GridColumnStyles.Count == 0)) { // first block is painted by ColumnHeader
309                                 rowshdrs_area.Y += ColumnsHeaderHeight;
310                         }
311
312                         rowshdrs_area.X = grid.ClientRectangle.X;
313                         rowshdrs_area.Width = grid.RowHeaderWidth;
314                         rowshdrs_area.Height = grid.visiblerow_count * grid.RowHeight;
315                         rowshdrs_maxheight = grid.ClientRectangle.Height + grid.ClientRectangle.Y - rowshdrs_area.Y;
316
317                         //Console.WriteLine ("DataGridDrawing.CalcRowsHeaders {0} {1}", rowshdrs_area,
318                         //      rowshdrs_maxheight);
319                 }
320
321                 public void UpdateVisibleColumn ()
322                 {
323                         if (grid.CurrentTableStyle.GridColumnStyles.Count == 0) {
324                                 grid.visiblecolumn_count = 0;
325                                 return; 
326                         }
327                         
328                         int col;
329                         int max_pixel = grid.horz_pixeloffset + cells_area.Width;
330                         grid.first_visiblecolumn = FromPixelToColumn (grid.horz_pixeloffset);
331
332                         col = FromPixelToColumn (max_pixel);
333                         
334                         grid.visiblecolumn_count = 1 + col - grid.first_visiblecolumn;
335                         
336                         if (grid.first_visiblecolumn + grid.visiblecolumn_count + 1 < grid.CurrentTableStyle.GridColumnStyles.Count) { 
337                                 grid.visiblecolumn_count++; // Partially visible column
338                         }
339                 }
340
341                 public void UpdateVisibleRowCount ()
342                 {
343                         int max_height = cells_area.Height;
344                         int total_rows = grid.RowsCount;
345                         
346                         if (grid.ShowEditRow && grid.RowsCount > 0) {
347                                 total_rows++;
348                         }
349
350                         int rows_height = (total_rows - grid.first_visiblerow) * grid.RowHeight;
351                         int max_rows = max_height / grid.RowHeight;
352                                                 
353                         if (max_rows > total_rows) {
354                                 max_rows = total_rows;
355                         }
356
357                         if (rows_height > cells_area.Height) {
358                                 grid.visiblerow_count = max_rows;
359                         } else {
360                                 grid.visiblerow_count = total_rows;
361                         }       
362
363                         CalcRowsHeaders (); // Height depends on num of visible rows            
364                         
365                         if (grid.visiblerow_count + grid.first_visiblerow > total_rows)
366                                 grid.visiblerow_count = total_rows - grid.first_visiblerow;
367
368                         if (grid.visiblerow_count < max_rows) {
369                                 grid.visiblerow_count = max_rows;
370                                 grid.first_visiblerow = total_rows - max_rows;
371                                 grid.Invalidate ();
372                         }
373                         
374                 }
375
376                 // From Point to Cell
377                 public DataGrid.HitTestInfo HitTest (int x, int y)
378                 {
379                         DataGrid.HitTestInfo hit = new DataGrid.HitTestInfo ();
380
381                         // TODO: Add missing ColumnResize and RowResize checks
382                         if (columnshdrs_area.Contains (x, y)) {
383                                 hit.type = DataGrid.HitTestType.ColumnHeader;
384                                 hit.column = FromPixelToColumn (x + grid.horz_pixeloffset);
385                                 return hit;
386                         }
387
388                         if (rowshdrs_area.Contains (x, y)) {
389                                 hit.type = DataGrid.HitTestType.RowHeader;
390                                 int posy;
391                                 int rcnt = grid.FirstVisibleRow + grid.VisibleRowCount;
392                                 for (int r = grid.FirstVisibleRow; r < rcnt; r++) {
393                                         posy = cells_area.Y + ((r - grid.FirstVisibleRow) * grid.RowHeight);
394                                         if (y <= posy + grid.RowHeight) { // Found row
395                                                 hit.row = r;
396                                                 break;
397                                         }
398                                 }
399                                 return hit;
400                         }
401
402                         if (caption_area.Contains (x, y)) {
403                                 hit.type = DataGrid.HitTestType.Caption;
404                                 return hit;
405                         }
406
407                         if (parent_rows.Contains (x, y)) {
408                                 hit.type = DataGrid.HitTestType.ParentRows;
409                                 return hit;
410                         }
411
412                         int pos_y, pos_x, width;
413                         int rowcnt = grid.FirstVisibleRow + grid.VisibleRowCount;
414                         for (int row = grid.FirstVisibleRow; row < rowcnt; row++) {
415                                 pos_y = cells_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
416
417                                 if (y <= pos_y + grid.RowHeight) { // Found row
418                                         hit.row = row;
419                                         hit.type = DataGrid.HitTestType.Cell;                                   
420                                         int col_pixel;
421                                         int column_cnt = grid.first_visiblecolumn + grid.visiblecolumn_count;
422                                         for (int column = grid.first_visiblecolumn; column < column_cnt; column++) {
423
424                                                 col_pixel = GetColumnStartingPixel (column);
425                                                 pos_x = cells_area.X + col_pixel - grid.horz_pixeloffset;
426                                                 width = grid.CurrentTableStyle.GridColumnStyles[column].Width;
427
428                                                 if (x <= pos_x + width) { // Column found
429                                                         hit.column = column;
430                                                         break;
431                                                 }
432                                         }
433
434                                         break;
435                                 }
436                         }
437
438                         return hit;
439                 }
440
441                 public Rectangle GetCellBounds (int row, int col)
442                 {
443                         Rectangle bounds = new Rectangle ();
444                         int col_pixel;
445
446                         bounds.Width = grid.CurrentTableStyle.GridColumnStyles[col].Width;
447                         bounds.Height = grid.RowHeight;
448                         bounds.Y = cells_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
449                         col_pixel = GetColumnStartingPixel (col);
450                         bounds.X = cells_area.X + col_pixel - grid.horz_pixeloffset;
451                         return bounds;
452                 }
453
454                 public void InvalidateCaption ()
455                 {
456                         if (caption_area.IsEmpty)
457                                 return;
458
459                         grid.Invalidate (caption_area);
460                 }
461                 
462                 
463                 public void InvalidateRow (int row)
464                 {
465                         if (row < grid.FirstVisibleRow || row > grid.FirstVisibleRow + grid.VisibleRowCount) {
466                                 return;
467                         }
468
469                         Rectangle rect_row = new Rectangle ();
470
471                         rect_row.X = cells_area.X;
472                         rect_row.Width = cells_area.Width;
473                         rect_row.Height = grid.RowHeight;
474                         rect_row.Y = cells_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
475                         grid.Invalidate (rect_row);
476                 }
477
478                 public void InvalidateRowHeader (int row)
479                 {
480                         Rectangle rect_rowhdr = new Rectangle ();
481                         rect_rowhdr.X = rowshdrs_area.X;
482                         rect_rowhdr.Width = rowshdrs_area.Width;
483                         rect_rowhdr.Height = grid.RowHeight;
484                         rect_rowhdr.Y = rowshdrs_area.Y + ((row - grid.FirstVisibleRow) * grid.RowHeight);
485                         grid.Invalidate (rect_rowhdr);
486                 }       
487
488                 public void InvalidateColumn (DataGridColumnStyle column)
489                 {
490                         Rectangle rect_col = new Rectangle ();
491                         int col_pixel;
492                         int col = -1;
493
494                         col = grid.CurrentTableStyle.GridColumnStyles.IndexOf (column);
495
496                         if (col == -1) {
497                                 return;
498                         }
499
500                         rect_col.Width = column.Width;
501                         col_pixel = GetColumnStartingPixel (col);
502                         rect_col.X = cells_area.X + col_pixel - grid.horz_pixeloffset;
503                         rect_col.Y = cells_area.Y;
504                         rect_col.Height = cells_area.Height;
505                         grid.Invalidate (rect_col);
506                 }
507                                 
508                 // Return true if the scrollbar is needed
509                 public bool SetUpHorizontalScrollBar ()
510                 {
511                         int width_all = CalcAllColumnsWidth ();
512
513                         if (width_all <= cells_area.Width) {
514                                 grid.horiz_scrollbar.Visible = false;
515                                 grid.Controls.Remove (grid.horiz_scrollbar);
516                                 return false;
517                         }
518
519                         grid.horiz_scrollbar.Location = new Point (grid.ClientRectangle.X, grid.ClientRectangle.Y +
520                                 grid.ClientRectangle.Height - grid.horiz_scrollbar.Height);
521
522                         grid.horiz_scrollbar.Size = new Size (grid.ClientRectangle.Width,
523                                 grid.horiz_scrollbar.Height);
524
525                         grid.horiz_scrollbar.Maximum = width_all;// - cells_area.Width;
526                         grid.horiz_scrollbar.LargeChange = cells_area.Width;
527                         grid.Controls.Add (grid.horiz_scrollbar);
528                         grid.horiz_scrollbar.Visible = true;
529                         return true;
530                 }
531
532                 // Return true if the scrollbar is needed
533                 public bool SetUpVerticalScrollBar ()
534                 {
535                         int y, height;
536                         int allrows = grid.RowsCount;
537
538                         if (grid.ShowEditRow && grid.RowsCount > 0) {
539                                 allrows++;
540                         }
541                         
542                         if (grid.visiblerow_count == allrows) {
543                                 grid.vert_scrollbar.Visible = false;
544                                 grid.Controls.Remove (grid.vert_scrollbar);
545                                 return false;
546                         }
547                         
548                         if (grid.caption_visible) {
549                                 y = grid.ClientRectangle.Y + caption_area.Height;
550                                 height = grid.ClientRectangle.Height - caption_area.Height;
551                         } else {
552                                 y = grid.ClientRectangle.Y;
553                                 height = grid.ClientRectangle.Height;
554                         }
555
556                         grid.vert_scrollbar.Location = new Point (grid.ClientRectangle.X +
557                                 grid.ClientRectangle.Width - grid.vert_scrollbar.Width, y);
558
559                         grid.vert_scrollbar.Size = new Size (grid.vert_scrollbar.Width,
560                                 height);
561
562                         grid.vert_scrollbar.Maximum = grid.RowsCount;
563                         
564                         if (grid.ShowEditRow && grid.RowsCount > 0) {
565                                 grid.vert_scrollbar.Maximum++;  
566                         }
567                         
568                         grid.vert_scrollbar.LargeChange = VLargeChange;
569
570                         grid.Controls.Add (grid.vert_scrollbar);
571                         grid.vert_scrollbar.Visible = true;
572                         return true;
573                 }
574
575                 #endregion // Public Instance Methods
576
577                 #region Instance Properties
578                 public Rectangle CellsArea {
579                         get {
580                                 return cells_area;
581                         }
582                 }
583
584                 // Returns the ColumnsHeader area excluding the rectangle shared with RowsHeader
585                 public Rectangle ColumnsHeadersArea {
586                         get {
587                                 Rectangle columns_area = columnshdrs_area;
588
589                                 if (grid.CurrentTableStyle.CurrentRowHeadersVisible) {
590                                         columns_area.X += grid.RowHeaderWidth;
591                                         columns_area.Width -= grid.RowHeaderWidth;
592                                 }
593                                 return columns_area;
594                         }
595                 }
596
597                 public int ColumnsHeaderHeight {
598                         get {
599                                 return grid.CurrentTableStyle.HeaderFont.Height + 6;
600                         }
601                 }
602
603                 public Rectangle RowsHeadersArea {
604                         get {
605                                 return rowshdrs_area;
606                         }
607                 }
608
609                 public int VLargeChange {
610                         get {
611                                 return cells_area.Height / grid.RowHeight;
612                         }
613                 }
614
615                 #endregion Instance Properties
616         }
617 }