Add wxGrid::Render() for drawing the grid to any wxDC.

In particular, this allows to print the grid contents easily.

Closes #14294.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@71577 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-05-27 13:00:19 +00:00
parent b55d57aa24
commit 779e28da63
6 changed files with 571 additions and 1 deletions

View File

@@ -526,6 +526,7 @@ All:
All (GUI):
- Add wxGrid::Render() for printing wxGrid (John Roberts).
- Added strike-through support to wxFont (Igor Korot).
- Add support for horizontal mouse wheel events to MSW and GTK (Lauri Nurmi).
- Fix infinite loop in wxHtmlEasyPrinting when trying to page break images

View File

@@ -65,6 +65,20 @@ enum wxGridDirection
wxGRID_ROW
};
// Flags used with wxGrid::Render() to select parts of the grid to draw.
enum wxGridRenderStyle
{
wxGRID_DRAW_ROWS_HEADER = 0x001,
wxGRID_DRAW_COLS_HEADER = 0x002,
wxGRID_DRAW_CELL_LINES = 0x004,
wxGRID_DRAW_BOX_RECT = 0x008,
wxGRID_DRAW_SELECTION = 0x010,
wxGRID_DRAW_DEFAULT = wxGRID_DRAW_ROWS_HEADER |
wxGRID_DRAW_COLS_HEADER |
wxGRID_DRAW_CELL_LINES |
wxGRID_DRAW_BOX_RECT
};
// ----------------------------------------------------------------------------
// forward declarations
// ----------------------------------------------------------------------------
@@ -1030,6 +1044,14 @@ public:
int verticalAlignment = wxALIGN_TOP,
int textOrientation = wxHORIZONTAL ) const;
// ------ grid render function for printing
//
void Render( wxDC& dc,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
const wxGridCellCoords& topLeft = wxGridCellCoords(-1, -1),
const wxGridCellCoords& bottomRight = wxGridCellCoords(-1, -1),
int style = wxGRID_DRAW_DEFAULT );
// Split a string containing newline characters into an array of
// strings and return the number of lines
@@ -2253,6 +2275,10 @@ private:
bool DoCanResizeLine(int line, const wxGridFixedIndicesSet *setFixed) const;
// Helper of Render(): set the scale to draw the cells at the right size.
void SetRenderScale( wxDC& dc, const wxPoint& pos, const wxSize& size,
int gridWidth, int gridHeight );
// these sets contain the indices of fixed, i.e. non-resizable
// interactively, grid rows or columns and are NULL if there are no fixed

View File

@@ -1850,6 +1850,50 @@ public:
CellSpan_Main
};
/**
Rendering styles supported by wxGrid::Render() method.
@since 2.9.4
*/
enum wxGridRenderStyle
{
/// Draw grid row header labels.
wxGRID_DRAW_ROWS_HEADER = 0x001,
/// Draw grid column header labels.
wxGRID_DRAW_COLS_HEADER = 0x002,
/// Draw grid cell border lines.
wxGRID_DRAW_CELL_LINES = 0x004,
/**
Draw a bounding rectangle around the rendered cell area.
Useful where row or column headers are not drawn or where there is
multi row or column cell clipping and therefore no cell border at
the rendered outer boundary.
*/
wxGRID_DRAW_BOX_RECT = 0x008,
/**
Draw the grid cell selection highlight if a selection is present.
At present the highlight colour drawn depends on whether the grid
window loses focus before drawing begins.
*/
wxGRID_DRAW_SELECTION = 0x010,
/**
The default render style.
Includes all except wxGRID_DRAW_SELECTION.
*/
wxGRID_DRAW_DEFAULT = wxGRID_DRAW_ROWS_HEADER |
wxGRID_DRAW_COLS_HEADER |
wxGRID_DRAW_CELL_LINES |
wxGRID_DRAW_BOX_RECT
};
/**
@name Constructors and Initialization
*/
@@ -4002,6 +4046,39 @@ public:
*/
void RefreshAttr(int row, int col);
/**
Draws part or all of a wxGrid on a wxDC for printing or display.
Pagination can be accomplished by using sequential Render() calls
with appropriate values in wxGridCellCoords topLeft and bottomRight.
@param dc
The wxDC to be drawn on.
@param pos
The position on the wxDC where rendering should begin. If not
specified drawing will begin at the wxDC MaxX() and MaxY().
@param size
The size of the area on the wxDC that the rendered wxGrid should
occupy. If not specified the drawing will be scaled to fit the
available dc width or height. The wxGrid's aspect ratio is
maintained whether or not size is specified.
@param topLeft
The top left cell of the block to be drawn. Defaults to ( 0, 0 ).
@param bottomRight
The bottom right cell of the block to be drawn. Defaults to row and
column counts.
@param style
A combination of values from wxGridRenderStyle.
@since 2.9.4
*/
void Render( wxDC& dc,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
const wxGridCellCoords& topLeft = wxGridCellCoords( -1, -1 ),
const wxGridCellCoords& bottomRight = wxGridCellCoords( -1, -1 ),
int style = wxGRID_DRAW_DEFAULT );
/**
Sets the cell attributes for all cells in the specified column.

View File

@@ -212,6 +212,9 @@ BEGIN_EVENT_TABLE( GridFrame, wxFrame )
EVT_MENU( ID_SET_HIGHLIGHT_WIDTH, GridFrame::OnSetHighlightWidth)
EVT_MENU( ID_SET_RO_HIGHLIGHT_WIDTH, GridFrame::OnSetROHighlightWidth)
EVT_MENU( wxID_PRINT, GridFrame::OnGridRender )
EVT_MENU( ID_RENDER_COORDS, GridFrame::OnGridRender )
EVT_GRID_LABEL_LEFT_CLICK( GridFrame::OnLabelLeftClick )
EVT_GRID_CELL_LEFT_CLICK( GridFrame::OnCellLeftClick )
EVT_GRID_ROW_SIZE( GridFrame::OnRowSize )
@@ -238,6 +241,38 @@ GridFrame::GridFrame()
fileMenu->Append( ID_VTABLE, wxT("&Virtual table test\tCtrl-V"));
fileMenu->Append( ID_BUGS_TABLE, wxT("&Bugs table test\tCtrl-B"));
fileMenu->Append( ID_TABULAR_TABLE, wxT("&Tabular table test\tCtrl-T"));
fileMenu->AppendSeparator();
wxMenu* setupMenu = new wxMenu;
wxMenuItem* item;
item = setupMenu->AppendCheckItem( ID_RENDER_ROW_LABEL,
"Render row labels" );
item->Check();
item = setupMenu->AppendCheckItem( ID_RENDER_COL_LABEL,
"Render column labels" );
item->Check();
item = setupMenu->AppendCheckItem( ID_RENDER_GRID_LINES,
"Render grid cell lines" );
item->Check();
item = setupMenu->AppendCheckItem( ID_RENDER_GRID_BORDER,
"Render border" );
item->Check();
item = setupMenu->AppendCheckItem( ID_RENDER_SELECT_HLIGHT,
"Render selection highlight" );
setupMenu->AppendSeparator();
setupMenu->AppendCheckItem( ID_RENDER_LOMETRIC,
"Use LOMETRIC mapping mode" );
setupMenu->AppendCheckItem( ID_RENDER_DEFAULT_SIZE,
"Use wxDefaultSize" );
setupMenu->AppendCheckItem( ID_RENDER_MARGIN,
"Logical 50 unit margin" );
setupMenu->AppendCheckItem( ID_RENDER_ZOOM,
"Zoom 125%" );
fileMenu->AppendSubMenu( setupMenu, "Render setup" );
fileMenu->Append( wxID_PRINT, "Render" );
fileMenu->Append( ID_RENDER_COORDS, "Render G5:P30" );
fileMenu->AppendSeparator();
fileMenu->Append( wxID_EXIT, wxT("E&xit\tAlt-X") );
@@ -2068,3 +2103,158 @@ void GridFrame::OnTabularTable(wxCommandEvent&)
{
new TabularGridFrame;
}
// Example using wxGrid::Render
// Displays a preset selection or, if it exists, a selection block
// Draws the selection to a wxBitmap and displays the bitmap
void GridFrame::OnGridRender( wxCommandEvent& event )
{
int styleRender = 0, i;
bool useLometric = false, defSize = false;
double zoom = 1;
wxSize sizeMargin( 0, 0 );
wxPoint pointOrigin( 0, 0 );
wxMenu* menu = GetMenuBar()->GetMenu( 0 );
wxMenuItem* menuItem = menu->FindItem( ID_RENDER_ROW_LABEL );
menu = menuItem->GetMenu();
if ( menu->FindItem( ID_RENDER_ROW_LABEL )->IsChecked() )
styleRender |= wxGRID_DRAW_ROWS_HEADER;
if ( menu->FindItem( ID_RENDER_COL_LABEL )->IsChecked() )
styleRender |= wxGRID_DRAW_COLS_HEADER;
if ( menu->FindItem( ID_RENDER_GRID_LINES )->IsChecked() )
styleRender |= wxGRID_DRAW_CELL_LINES;
if ( menu->FindItem( ID_RENDER_GRID_BORDER )->IsChecked() )
styleRender |= wxGRID_DRAW_BOX_RECT;
if ( menu->FindItem( ID_RENDER_SELECT_HLIGHT )->IsChecked() )
styleRender |= wxGRID_DRAW_SELECTION;
if ( menu->FindItem( ID_RENDER_LOMETRIC )->IsChecked() )
useLometric = true;
if ( menu->FindItem( ID_RENDER_MARGIN )->IsChecked() )
{
pointOrigin.x += 50;
pointOrigin.y += 50;
sizeMargin.IncBy( 50 );
}
if ( menu->FindItem( ID_RENDER_ZOOM )->IsChecked() )
zoom = 1.25;
if ( menu->FindItem( ID_RENDER_DEFAULT_SIZE )->IsChecked() )
defSize = true;
// init render area coords with a default row and col selection
wxGridCellCoords topLeft( 0, 0 ), bottomRight( 8, 6 );
// check whether we are printing a block selection
// other selection types not catered for here
if ( event.GetId() == ID_RENDER_COORDS )
{
topLeft.SetCol( 6 );
topLeft.SetRow( 4 );
bottomRight.SetCol( 15 );
bottomRight.SetRow( 29 );
}
else if ( grid->IsSelection() && grid->GetSelectionBlockTopLeft().Count() )
{
wxGridCellCoordsArray cells = grid->GetSelectionBlockTopLeft();
if ( grid->GetSelectionBlockBottomRight().Count() )
{
cells.Add( grid->GetSelectionBlockBottomRight()[ 0 ] );
topLeft.Set( cells[ 0 ].GetRow(),
cells[ 0 ].GetCol() );
bottomRight.Set( cells[ 1 ].GetRow(),
cells[ 1 ].GetCol() );
}
}
// sum col widths
wxSize sizeRender( 0, 0 );
wxGridSizesInfo sizeinfo = grid->GetColSizes();
for ( i = topLeft.GetCol(); i <= bottomRight.GetCol(); i++ )
{
sizeRender.x += sizeinfo.GetSize( i );
}
// sum row heights
sizeinfo = grid->GetRowSizes();
for ( i = topLeft.GetRow(); i <= bottomRight.GetRow(); i++ )
{
sizeRender.y += sizeinfo.GetSize( i );
}
if ( styleRender & wxGRID_DRAW_ROWS_HEADER )
sizeRender.x += grid->GetRowLabelSize();
if ( styleRender & wxGRID_DRAW_COLS_HEADER )
sizeRender.y += grid->GetColLabelSize();
sizeRender.x *= zoom;
sizeRender.y *= zoom;
// delete any existing render frame and create new one
wxWindow* win = FindWindow( "frameRender" );
if ( win )
win->Destroy();
wxFrame* frame = new wxFrame( this, wxID_ANY, "Grid Render" );
frame->SetClientSize( 780, 400 );
frame->SetName( "frameRender" );
wxPanel* canvas = new wxPanel( frame, wxID_ANY );
// make bitmap large enough for margins if any
if ( !defSize )
sizeRender.IncBy( sizeMargin * 2 );
else
sizeRender.IncBy( sizeMargin );
wxBitmap bmp( sizeRender );
// don't leave it larger or drawing will be scaled
sizeRender.DecBy( sizeMargin * 2 );
wxMemoryDC memDc(bmp);
// default row labels have no background colour so set background
memDc.SetBackground( wxBrush( canvas->GetBackgroundColour() ) );
memDc.Clear();
// convert sizeRender to mapping mode units if necessary
if ( useLometric )
{
memDc.SetMapMode( wxMM_LOMETRIC );
wxSize sizePPI = memDc.GetPPI();
sizeRender.x = memDc.DeviceToLogicalXRel( sizeRender.x );
sizeRender.y = memDc.DeviceToLogicalYRel( sizeRender.y );
}
// pass wxDefaultSize if menu item is checked
if ( defSize )
sizeRender = wxDefaultSize;
grid->Render( memDc,
pointOrigin,
sizeRender,
topLeft, bottomRight,
wxGridRenderStyle( styleRender ) );
m_gridBitmap = bmp;
canvas->Bind( wxEVT_PAINT, &GridFrame::OnRenderPaint, this );
frame->Show();
}
void GridFrame::OnRenderPaint( wxPaintEvent& event )
{
wxPanel* canvas = ( wxPanel* )event.GetEventObject();
wxPaintDC dc( canvas );
canvas->PrepareDC( dc );
if ( !m_gridBitmap.IsOk() )
return;;
wxMemoryDC memDc( m_gridBitmap );
dc.Blit( 0, 0,
m_gridBitmap.GetWidth(),
m_gridBitmap.GetHeight(),
&memDc, 0, 0 );
}

View File

@@ -111,6 +111,8 @@ public:
void OnVTable( wxCommandEvent& );
void OnBugsTable( wxCommandEvent& );
void OnTabularTable( wxCommandEvent& );
void OnGridRender( wxCommandEvent& event );
void OnRenderPaint( wxPaintEvent& event );
enum
{
@@ -174,7 +176,18 @@ public:
ID_SET_HIGHLIGHT_WIDTH,
ID_SET_RO_HIGHLIGHT_WIDTH,
ID_TESTFUNC
ID_TESTFUNC,
ID_RENDER_ROW_LABEL,
ID_RENDER_COL_LABEL,
ID_RENDER_GRID_LINES,
ID_RENDER_GRID_BORDER,
ID_RENDER_SELECT_HLIGHT,
ID_RENDER_LOMETRIC,
ID_RENDER_COORDS,
ID_RENDER_ZOOM,
ID_RENDER_MARGIN,
ID_RENDER_DEFAULT_SIZE,
};
#if wxUSE_LOG
@@ -184,6 +197,8 @@ public:
// add the cells to selection when using commands from select menu?
bool m_addToSel;
wxBitmap m_gridBitmap;
DECLARE_EVENT_TABLE()
};

View File

@@ -1776,6 +1776,267 @@ void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
m_owner->DrawHighlight( dc, dirtyCells );
}
void wxGrid::Render( wxDC& dc,
const wxPoint& position,
const wxSize& size,
const wxGridCellCoords& topLeft,
const wxGridCellCoords& bottomRight,
int style )
{
wxCHECK_RET( bottomRight.GetCol() < GetNumberCols(),
"Invalid right column" );
wxCHECK_RET( bottomRight.GetRow() < GetNumberRows(),
"Invalid bottom row" );
// store user settings and reset later
// remove grid selection, don't paint selection colour
// unless we have wxGRID_DRAW_SELECTION
// block selections are the only ones catered for here
wxGridCellCoordsArray selectedCells;
bool hasSelection = IsSelection();
if ( hasSelection && !( style & wxGRID_DRAW_SELECTION ) )
{
selectedCells = GetSelectionBlockTopLeft();
// non block selections may not have a bottom right
if ( GetSelectionBlockBottomRight().size() )
selectedCells.Add( GetSelectionBlockBottomRight()[ 0 ] );
ClearSelection();
}
// store user device origin
wxCoord userOriginX, userOriginY;
dc.GetDeviceOrigin( &userOriginX, &userOriginY );
// store user scale
double scaleUserX, scaleUserY;
dc.GetUserScale( &scaleUserX, &scaleUserY );
// set defaults if necessary
long colLeft = topLeft.GetCol() > -1 ? topLeft.GetCol() : 0;
long rowTop = topLeft.GetRow() > -1 ? topLeft.GetRow() : 0;
long colRight = bottomRight.GetCol() > -1 ? bottomRight.GetCol()
: GetNumberCols() - 1;
long rowBottom = bottomRight.GetRow() > -1 ? bottomRight.GetRow()
: GetNumberRows() - 1;
// get grid area size to be rendered
long offsetCols = 0, offsetRows = 0;
wxGridCellCoordsArray renderCells;
wxArrayInt arrayCols;
wxArrayInt arrayRows;
int col, row, gridWidth, gridHeight;
// fill array with cells to be rendered
// calculate width of cell drawn area
// and get grid column offset used where startcol > 0
gridWidth = 0;
wxGridSizesInfo sizeinfo = GetColSizes();
for ( col = 0; col <= colRight; col++ )
{
if ( col < colLeft )
{
offsetCols += sizeinfo.GetSize( col );
}
else
{
for ( row = rowTop; row <= rowBottom; row++ )
{
renderCells.Add( wxGridCellCoords( row, col ));
arrayRows.Add( row ); // column labels rendered in DrawColLabels
}
arrayCols.Add( col ); // row labels rendered in DrawRowLabels
gridWidth += sizeinfo.GetSize( col );
}
}
// calculate height of rendered cells
// and row Y offset where startrow > 0
gridHeight = 0;
sizeinfo = GetRowSizes();
for ( row = 0; row <= rowBottom; row++ )
{
if ( row < rowTop )
offsetRows += sizeinfo.GetSize( row );
else
gridHeight += sizeinfo.GetSize( row );
}
// add headers/labels to dimensions
if ( style & wxGRID_DRAW_ROWS_HEADER )
gridWidth += GetRowLabelSize();
if ( style & wxGRID_DRAW_COLS_HEADER )
gridHeight += GetColLabelSize();
// set drawing start position DeviceOrigin
// if not specified use previous dc draw extents MaxX and MaxY
wxPoint positionRender = position;
if ( !positionRender.IsFullySpecified() )
{
if ( positionRender.x == wxDefaultPosition.x )
positionRender.x = dc.MaxX();
if ( positionRender.y == wxDefaultPosition.y )
positionRender.y = dc.MaxY();
}
// positionRender should be in logical units
wxCoord originX = dc.LogicalToDeviceX( positionRender.x );
wxCoord originY = dc.LogicalToDeviceY( positionRender.y );
dc.SetDeviceOrigin( originX, originY );
SetRenderScale( dc, positionRender, size, gridWidth, gridHeight );
// draw row headers at specified origin
if ( GetRowLabelSize() > 0 && ( style & wxGRID_DRAW_ROWS_HEADER ) )
{
if ( style & wxGRID_DRAW_COLS_HEADER )
{
DrawCornerLabel( dc ); // do only if both col and row labels drawn
originY += dc.LogicalToDeviceYRel( GetColLabelSize() );
}
originY -= dc.LogicalToDeviceYRel( offsetRows );
dc.SetDeviceOrigin( originX, originY );
DrawRowLabels( dc, arrayRows );
// reset for columns
if ( style & wxGRID_DRAW_COLS_HEADER )
originY -= dc.LogicalToDeviceYRel( GetColLabelSize() );
originY += dc.LogicalToDeviceYRel( offsetRows );
// X offset so we don't overwrite row labels
originX += dc.LogicalToDeviceXRel( GetRowLabelSize() );
}
// subtract col offset where startcol > 0
originX -= dc.LogicalToDeviceXRel( offsetCols );
// no y offset for col labels, they are at the Y origin
// draw column labels
if ( style & wxGRID_DRAW_COLS_HEADER )
{
dc.SetDeviceOrigin( originX, originY );
DrawColLabels( dc, arrayCols );
// don't overwrite the labels, increment originY
originY += dc.LogicalToDeviceYRel( GetColLabelSize() );
}
// set device origin to draw grid cells and lines
originY -= dc.LogicalToDeviceYRel( offsetRows );
dc.SetDeviceOrigin( originX, originY );
// draw cell area background
dc.SetBrush( GetDefaultCellBackgroundColour() );
dc.SetPen( *wxTRANSPARENT_PEN );
// subtract headers from grid area dimensions
unsigned cellsWidth = gridWidth, cellsHeight = gridHeight;
if ( style & wxGRID_DRAW_ROWS_HEADER )
cellsWidth -= GetRowLabelSize();
if ( style & wxGRID_DRAW_COLS_HEADER )
cellsHeight -= GetColLabelSize();
dc.DrawRectangle( wxPoint( offsetCols, offsetRows ),
wxSize( cellsWidth, cellsHeight ) );
// draw cells
DrawGridCellArea( dc, renderCells );
// calculate region for drawing grid lines
if ( style & wxGRID_DRAW_CELL_LINES )
{
wxRegion regionClip( offsetCols,
offsetRows,
cellsWidth,
cellsHeight );
DrawRangeGridLines(dc, regionClip, renderCells[0], renderCells.Last());
}
// draw render rectangle bounding lines
// useful where there is multi cell row or col clipping and no cell border
if ( style & wxGRID_DRAW_BOX_RECT )
{
int bottom = offsetRows + cellsHeight,
right = offsetCols + cellsWidth - 1;
// horiz top line if we are not drawing column header/labels
if ( !( style & wxGRID_DRAW_COLS_HEADER ) )
{
int left = offsetCols;
left += ( style & wxGRID_DRAW_COLS_HEADER )
? - GetRowLabelSize() : 0;
dc.SetPen( GetRowGridLinePen( rowTop ) );
dc.DrawLine( left,
offsetRows,
right,
offsetRows );
}
// horiz bottom line
dc.SetPen( GetRowGridLinePen( rowBottom ) );
dc.DrawLine( offsetCols, bottom - 1, right, bottom - 1 );
// left vertical line if we are not drawing row header/labels
if ( !( style & wxGRID_DRAW_ROWS_HEADER ) )
{
int top = offsetRows;
top += ( style & wxGRID_DRAW_COLS_HEADER )
? - GetColLabelSize() : 0;
dc.SetPen( GetColGridLinePen( colLeft ) );
dc.DrawLine( offsetCols -1,
top,
offsetCols - 1,
bottom - 1 );
}
// right vertical line
dc.SetPen( GetColGridLinePen( colRight ) );
dc.DrawLine( right, offsetRows, right, bottom - 1 );
}
// restore user setings
dc.SetDeviceOrigin( userOriginX, userOriginY );
dc.SetUserScale( scaleUserX, scaleUserY );
if ( selectedCells.size() && !( style & wxGRID_DRAW_SELECTION ) )
{
SelectBlock( selectedCells[ 0 ].GetRow(),
selectedCells[ 0 ].GetCol(),
selectedCells[ selectedCells.size() -1 ].GetRow(),
selectedCells[ selectedCells.size() -1 ].GetCol() );
}
}
void
wxGrid::SetRenderScale(wxDC& dc,
const wxPoint& pos, const wxSize& size,
int gridWidth, int gridHeight)
{
double scaleX, scaleY;
wxSize sizeTemp;
if ( size.GetX() != wxDefaultSize.GetX() ) // size.x was specified
sizeTemp.SetWidth( size.GetX() );
else
sizeTemp.SetWidth( dc.DeviceToLogicalXRel( dc.GetSize().GetX() )
- pos.x );
if ( size.GetY() != wxDefaultSize.GetY() ) // size.y was specified
sizeTemp.SetHeight( size.GetY() );
else
sizeTemp.SetHeight( dc.DeviceToLogicalYRel( dc.GetSize().GetY() )
- pos.y );
scaleX = (double)( (double) sizeTemp.GetX() / (double) gridWidth );
scaleY = (double)( (double) sizeTemp.GetY() / (double) gridHeight );
dc.SetUserScale( wxMin( scaleX, scaleY), wxMin( scaleX, scaleY ) );
}
void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
{
wxWindow::ScrollWindow( dx, dy, rect );