1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        sheet.cpp
3 // Purpose:     wxSheet and related classes
4 // Author:      Michael Bedward (based on code by Julian Smart, Robin Dunn)
5 // Modified by: John Labenski, Robin Dunn, Vadim Zeitlin
6 // Created:     1/08/1999
7 // RCS-ID:      $Id$
8 // Copyright:   (c) John Labenski, Michael Bedward (mbedward@ozemail.com.au)
9 // Licence:     wxWidgets licence
10 ///////////////////////////////////////////////////////////////////////////////
11 
12 // wxSheet code is based on wxGrid, to ensure that it stays up to date changes
13 // in the wxGrid files should be noted and incorporated if applicable.
14 // Note: very little code remains, update colours and sizing...
15 // wx/include/wx/generic/grid.h         1.140
16 // wx/include/wx/generic/gridctrl.h     1.15
17 // wx/include/wx/generic/gridsel.h      1.18
18 // wx/src/generic/grid.cpp              1.318
19 // wx/src/generic/gridctrl.cpp          1.15
20 // wx/src/generic/gridsel.cpp           1.20
21 
22 // For compilers that support precompilation, includes "wx/wx.h".
23 #include "wx/wxprec.h"
24 
25 #ifdef __BORLANDC__
26     #pragma hdrstop
27 #endif
28 
29 #ifndef WX_PRECOMP
30     #include "wx/defs.h"
31     #include "wx/utils.h"
32     #include "wx/dcclient.h"
33     #include "wx/settings.h"
34     #include "wx/log.h"
35     #include "wx/dcscreen.h"
36 #endif // WX_PRECOMP
37 
38 #include "wx/sheet/sheet.h"
39 #include "wx/sheet/sheetspt.h" // for wxSheetSplitterEvent, wxEVT_SHEET_SPLIT_BEGIN
40 
41 #include "wx/timer.h"
42 #include "wx/clipbrd.h"
43 #include "wx/renderer.h"
44 
45 // Required for wxIs... functions
46 #include <cctype>
47 
48 #define PRINT_BLOCK(s, b) wxPrintf(wxT("%s %d %d %d %d - w%d h%d\n"), wxT(s), b.GetTop(), b.GetLeft(), b.GetBottom(), b.GetRight(), b.GetWidth(), b.GetHeight());
49 #define PRINT_RECT(s, b)  wxPrintf(wxT("%s %d %d %d %d - w%d h%d\n"), wxT(s), b.GetTop(), b.GetLeft(), b.GetBottom(), b.GetRight(), b.GetWidth(), b.GetHeight());
50 
51 #define SPLIT_BUTTON_WIDTH 6
52 
53 #define wxStrF wxString::Format
54 
55 #ifndef wxSL_INVERSE // !wxCHECK_VERSION(2,5,3) // FIXME temp fix until 2.5.4
56     #define SetOwnBackgroundColour SetBackgroundColour
57     #define SetOwnForegroundColour SetForegroundColour
58 #endif
59 
60 #ifdef __wxGTK__
61     //#define USE_RENDERNATIVE 1
62 #endif // __wxGTK__
63 
wxRectIsEmpty(const wxRect & rect)64 static inline bool wxRectIsEmpty(const wxRect& rect)
65 {
66     return (rect.width < 1) || (rect.height < 1);
67 }
68 
69 // ----------------------------------------------------------------------------
70 // conditional compilation
71 // ----------------------------------------------------------------------------
72 
73 // Gambit modification for custom border drawing
74 #define WXSHEET_DRAW_LINES 0
75 #ifndef WXSHEET_DRAW_LINES
76     #define WXSHEET_DRAW_LINES 1
77 #endif
78 
79 // ----------------------------------------------------------------------------
80 // globals
81 // ----------------------------------------------------------------------------
82 
83 //#define DEBUG_ATTR_CACHE
84 #ifdef DEBUG_ATTR_CACHE
85     static size_t gs_nAttrCacheHits = 0;
86     static size_t gs_nAttrCacheMisses = 0;
87 #endif // DEBUG_ATTR_CACHE
88 
89 // ----------------------------------------------------------------------------
90 // constants
91 // ----------------------------------------------------------------------------
92 
93 // scroll line size
94 // TODO: this doesn't work at all, sheet cells have different sizes and approx
95 //       calculations don't work as because of the size mismatch scrollbars
96 //       sometimes fail to be shown when they should be or vice versa
97 //
98 //       The scroll bars may be a little flakey once in a while, but that is
99 //       surely much less horrible than having scroll lines of only 1!!!
100 //       -- Robin
101 //
102 //       Well, it's still seriously broken so it might be better but needs
103 //       fixing anyhow
104 //       -- Vadim
105 #define SHEET_SCROLL_LINE_X 15  // 1
106 #define SHEET_SCROLL_LINE_Y SHEET_SCROLL_LINE_X
107 
108 // ----------------------------------------------------------------------------
109 // wxSheetDataTypeInfo: for the data type registry
110 // ----------------------------------------------------------------------------
111 
112 class wxSheetDataTypeInfo
113 {
114 public:
wxSheetDataTypeInfo(const wxString & typeName,const wxSheetCellRenderer & renderer,const wxSheetCellEditor & editor)115     wxSheetDataTypeInfo(const wxString& typeName,
116                         const wxSheetCellRenderer& renderer,
117                         const wxSheetCellEditor& editor)
118         : m_typeName(typeName), m_renderer(renderer), m_editor(editor) {}
119 
120     wxString            m_typeName;
121     wxSheetCellRenderer m_renderer;
122     wxSheetCellEditor   m_editor;
123 };
124 
125 // ----------------------------------------------------------------------------
126 // wxSheetTypeRegistry
127 // ----------------------------------------------------------------------------
128 class WXDLLIMPEXP_SHEET wxSheetTypeRegistry
129 {
130 public:
wxSheetTypeRegistry()131     wxSheetTypeRegistry() {}
132     virtual ~wxSheetTypeRegistry();
133 
134     // register a new data type returning position
135     int RegisterDataType(const wxString& typeName,
136                          const wxSheetCellRenderer& renderer,
137                          const wxSheetCellEditor& editor);
138 
139     // find one of already registered data types
140     int FindRegisteredDataType(const wxString& typeName);
141 
142     // try to FindRegisteredDataType(), if this fails and typeName is one of
143     // standard typenames, register it and return its index
144     int FindDataType(const wxString& typeName);
145 
146     // try to FindDataType(), if it fails, see if it is not one of already
147     // registered data types with some params in which case clone the
148     // registered data type and set params for it
149     int FindOrCloneDataType(const wxString& typeName);
150 
151     const wxSheetCellRenderer& GetRenderer(int index);
152     const wxSheetCellEditor&   GetEditor(int index);
153 
154 private:
155     wxArrayPtrVoid m_typeInfo;
156 };
157 
~wxSheetTypeRegistry()158 wxSheetTypeRegistry::~wxSheetTypeRegistry()
159 {
160     size_t i, count = m_typeInfo.Count();
161     for ( i = 0; i < count; i++ )
162         delete (wxSheetDataTypeInfo*)m_typeInfo[i];
163 }
164 
RegisterDataType(const wxString & typeName,const wxSheetCellRenderer & renderer,const wxSheetCellEditor & editor)165 int wxSheetTypeRegistry::RegisterDataType(const wxString& typeName,
166                                           const wxSheetCellRenderer& renderer,
167                                           const wxSheetCellEditor& editor)
168 {
169     wxSheetDataTypeInfo* info = new wxSheetDataTypeInfo(typeName, renderer, editor);
170 
171     // is it already registered?
172     int index = FindRegisteredDataType(typeName);
173     if ( index != wxNOT_FOUND )
174     {
175         wxSheetDataTypeInfo *oldInfo = (wxSheetDataTypeInfo*)m_typeInfo[index];
176         delete oldInfo;
177         m_typeInfo[index] = info;
178         return index;
179     }
180 
181     m_typeInfo.Add(info);
182     return m_typeInfo.GetCount() - 1;
183 }
184 
FindRegisteredDataType(const wxString & typeName)185 int wxSheetTypeRegistry::FindRegisteredDataType(const wxString& typeName)
186 {
187     if (typeName.IsEmpty())
188         return wxNOT_FOUND;
189 
190     size_t i, count = m_typeInfo.GetCount();
191     for ( i = 0; i < count; i++ )
192     {
193         if (typeName == ((wxSheetDataTypeInfo*)m_typeInfo[i])->m_typeName)
194             return i;
195     }
196 
197     return wxNOT_FOUND;
198 }
199 
FindDataType(const wxString & typeName)200 int wxSheetTypeRegistry::FindDataType(const wxString& typeName)
201 {
202     int index = FindRegisteredDataType(typeName);
203     if (index != wxNOT_FOUND)
204         return index;
205 
206     // if one of the standard ones, register it "on the fly"
207 #if wxUSE_TEXTCTRL
208     if ( typeName == wxSHEET_VALUE_STRING )
209     {
210         wxSheetCellRenderer renderer(new wxSheetCellStringRendererRefData());
211         wxSheetCellEditor editor(new wxSheetCellTextEditorRefData());
212         return RegisterDataType(wxSHEET_VALUE_STRING, renderer, editor);
213     }
214     else if ( typeName == wxSHEET_VALUE_NUMBER )
215     {
216         wxSheetCellRenderer renderer(new wxSheetCellNumberRendererRefData());
217         wxSheetCellEditor editor(new wxSheetCellNumberEditorRefData());
218         return RegisterDataType(wxSHEET_VALUE_NUMBER, renderer, editor);
219     }
220     else if ( typeName == wxSHEET_VALUE_FLOAT )
221     {
222         wxSheetCellRenderer renderer(new wxSheetCellFloatRendererRefData());
223         wxSheetCellEditor editor(new wxSheetCellFloatEditorRefData());
224         return RegisterDataType(wxSHEET_VALUE_FLOAT, renderer, editor);
225     }
226     else if ( typeName == wxSHEET_VALUE_LABEL )
227     {
228         wxSheetCellRenderer renderer(new wxSheetCellRolColLabelRendererRefData());
229         wxSheetCellEditor editor(new wxSheetCellTextEditorRefData());
230         return RegisterDataType(wxSHEET_VALUE_LABEL, renderer, editor);
231     }
232 #endif // wxUSE_TEXTCTRL
233 #if wxUSE_CHECKBOX
234     else if ( typeName == wxSHEET_VALUE_BOOL )
235     {
236         wxSheetCellRenderer renderer(new wxSheetCellBoolRendererRefData());
237         wxSheetCellEditor editor(new wxSheetCellBoolEditorRefData());
238         return RegisterDataType(wxSHEET_VALUE_BOOL, renderer, editor);
239     }
240 #endif // wxUSE_CHECKBOX
241 #if wxUSE_COMBOBOX
242     else if ( typeName == wxSHEET_VALUE_CHOICE )
243     {
244         wxSheetCellRenderer renderer(new wxSheetCellStringRendererRefData());
245         wxSheetCellEditor editor(new wxSheetCellChoiceEditorRefData());
246         return RegisterDataType(wxSHEET_VALUE_CHOICE, renderer, editor);
247     }
248 #endif // wxUSE_COMBOBOX
249 
250     return wxNOT_FOUND;
251 }
252 
FindOrCloneDataType(const wxString & typeName)253 int wxSheetTypeRegistry::FindOrCloneDataType(const wxString& typeName)
254 {
255     if (typeName.IsEmpty())
256         return wxNOT_FOUND;
257     int index = FindDataType(typeName);
258     if ( index != wxNOT_FOUND )
259         return index;
260 
261     // the first part of the typename is the "real" type, anything after ':'
262     // are the parameters for the renderer
263     index = FindDataType(typeName.BeforeFirst(_T(':')));
264     if ( index == wxNOT_FOUND )
265         return wxNOT_FOUND;
266 
267     wxSheetCellRenderer renderer(GetRenderer(index).Clone());
268     wxSheetCellEditor editor(GetEditor(index).Clone());
269 
270     // do it even if there are no parameters to reset them to defaults
271     wxString params = typeName.AfterFirst(_T(':'));
272     renderer.SetParameters(params);
273     editor.SetParameters(params);
274 
275     return RegisterDataType(typeName, renderer, editor);
276 }
277 
GetRenderer(int index)278 const wxSheetCellRenderer& wxSheetTypeRegistry::GetRenderer(int index)
279 {
280     wxCHECK_MSG((index >= 0) && (index < int(m_typeInfo.GetCount())), wxNullSheetCellRenderer,
281                 wxT("Invalid index in wxSheetTypeRegistry::GetRenderer"));
282     return ((wxSheetDataTypeInfo*)m_typeInfo[index])->m_renderer;
283 }
284 
GetEditor(int index)285 const wxSheetCellEditor& wxSheetTypeRegistry::GetEditor(int index)
286 {
287     wxCHECK_MSG((index >= 0) && (index < int(m_typeInfo.GetCount())), wxNullSheetCellEditor,
288                 wxT("Invalid index in wxSheetTypeRegistry::GetEditor"));
289     return ((wxSheetDataTypeInfo*)m_typeInfo[index])->m_editor;
290 }
291 
292 // ----------------------------------------------------------------------------
293 // wxSheetChildWindow
294 // ----------------------------------------------------------------------------
IMPLEMENT_ABSTRACT_CLASS(wxSheetChildWindow,wxWindow)295 IMPLEMENT_ABSTRACT_CLASS( wxSheetChildWindow, wxWindow )
296 
297 BEGIN_EVENT_TABLE( wxSheetChildWindow, wxWindow )
298     EVT_PAINT        ( wxSheetChildWindow::OnPaint )
299     EVT_MOUSEWHEEL   ( wxSheetChildWindow::OnMouse )
300     EVT_MOUSE_EVENTS ( wxSheetChildWindow::OnMouse )
301     EVT_KEY_DOWN     ( wxSheetChildWindow::OnKeyAndChar )
302     EVT_KEY_UP       ( wxSheetChildWindow::OnKeyAndChar )
303     EVT_CHAR         ( wxSheetChildWindow::OnKeyAndChar )
304     EVT_SET_FOCUS    ( wxSheetChildWindow::OnFocus )
305     EVT_KILL_FOCUS   ( wxSheetChildWindow::OnFocus )
306 END_EVENT_TABLE()
307 
308 wxSheetChildWindow::wxSheetChildWindow( wxSheet *parent, wxWindowID id,
309                                         const wxPoint &pos, const wxSize &size,
310                                         long style, const wxString& name )
311                    :wxWindow( parent, id, pos, size, style, name ),
312                     m_owner(parent), m_mouseCursor(wxSheet::WXSHEET_CURSOR_SELECT_CELL)
313 {
314 }
315 
OnPaint(wxPaintEvent & event)316 void wxSheetChildWindow::OnPaint( wxPaintEvent& event )
317 {
318     if (!m_owner || !m_owner->GetEventHandler()->ProcessEvent(event))
319     {
320         wxPaintDC dc(this); // MSW at least requires this
321     }
322 }
323 
OnMouse(wxMouseEvent & event)324 void wxSheetChildWindow::OnMouse( wxMouseEvent& event )
325 {
326     if (!m_owner || !m_owner->GetEventHandler()->ProcessEvent(event))
327         event.Skip();
328 }
329 
330 // This seems to be required for wxMotif otherwise the mouse
331 // cursor must be in the cell edit control to get key events
OnKeyAndChar(wxKeyEvent & event)332 void wxSheetChildWindow::OnKeyAndChar( wxKeyEvent& event )
333 {
334     if (!m_owner || !m_owner->GetEventHandler()->ProcessEvent(event))
335         event.Skip();
336 }
337 
OnFocus(wxFocusEvent & event)338 void wxSheetChildWindow::OnFocus( wxFocusEvent& event )
339 {
340     if (!m_owner || !m_owner->GetEventHandler()->ProcessEvent(event))
341         event.Skip();
342 }
343 
OnEraseBackground(wxEraseEvent & event)344 void wxSheetChildWindow::OnEraseBackground( wxEraseEvent& event)
345 {
346     if (!m_owner || !m_owner->GetEventHandler()->ProcessEvent(event))
347         event.Skip(false);
348 }
349 
350 //-----------------------------------------------------------------------------
351 // wxSheetRefData
352 //-----------------------------------------------------------------------------
wxSheetRefData()353 wxSheetRefData::wxSheetRefData()
354 {
355     m_table    = NULL;
356     m_ownTable = false;
357 
358     m_typeRegistry = new wxSheetTypeRegistry;
359 
360     m_rowEdges.SetDefaultSize(WXSHEET_DEFAULT_ROW_HEIGHT);
361     m_colEdges.SetDefaultSize(WXSHEET_DEFAULT_COL_WIDTH);
362     m_rowEdges.SetMinAllowedSize(WXSHEET_MIN_ROW_HEIGHT);
363     m_colEdges.SetMinAllowedSize(WXSHEET_MIN_COL_WIDTH);
364 
365     m_rowLabelWidth  = WXSHEET_DEFAULT_ROW_LABEL_WIDTH;
366     m_colLabelHeight = WXSHEET_DEFAULT_COL_LABEL_HEIGHT;
367 
368     m_equal_col_widths = 0;
369 
370     m_gridLineColour   = wxColour( 192, 192, 192 );
371     m_gridLinesEnabled = wxBOTH;
372     m_cursorCellHighlightColour     = *wxBLACK;
373     m_cursorCellHighlightPenWidth   = 2;
374     m_cursorCellHighlightROPenWidth = 1;
375     m_labelOutlineColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW);
376 
377     m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
378     m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
379 
380     m_editable = true;  // default for whole grid
381 
382     m_canDrag = wxSHEET_DragCellSize;
383 
384     m_cursorCoords = wxNullSheetCoords;
385 
386     m_selectingAnchor = wxNullSheetCoords;
387     m_selectionMode   = wxSHEET_SelectCells;
388     m_selection       = new wxSheetSelection;
389     m_selectionBackground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
390     m_selectionForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
391 
392     m_cellEditorCoords = wxNullSheetCoords;
393 
394     m_pasting = false;
395 }
396 
~wxSheetRefData()397 wxSheetRefData::~wxSheetRefData()
398 {
399     if (m_ownTable && m_table)
400         delete m_table;
401 
402     delete m_typeRegistry;
403     delete m_selection;
404 }
405 
FindSheet(wxSheet * sheet) const406 int wxSheetRefData::FindSheet(wxSheet* sheet) const
407 {
408     return m_sheets.Index(sheet);
409 }
AddSheet(wxSheet * sheet)410 void wxSheetRefData::AddSheet(wxSheet* sheet)
411 {
412     wxCHECK_RET(sheet, wxT("Invalid sheet"));
413 
414     // not an error, just let them do it to avoid having to check
415     if (!HasSheet(sheet))
416         m_sheets.Add(sheet);
417 }
RemoveSheet(wxSheet * sheet)418 void wxSheetRefData::RemoveSheet(wxSheet* sheet)
419 {
420     wxCHECK_RET(sheet, wxT("Invalid sheet"));
421 
422     // not an error, if not found allows for this to be called in destructor
423     const int index = FindSheet(sheet);
424     if (index != wxNOT_FOUND)
425         m_sheets.RemoveAt(index);
426 }
427 
428 //-----------------------------------------------------------------------------
429 // RTTI stuff for ?
430 //-----------------------------------------------------------------------------
431 
432 #if wxUSE_EXTENDED_RTTI
433 WX_DEFINE_FLAGS( wxSheetStyle )
434 
wxBEGIN_FLAGS(wxSheetStyle)435 wxBEGIN_FLAGS( wxSheetStyle )
436     // new style border flags, we put them first to
437     // use them for streaming out
438     wxFLAGS_MEMBER(wxBORDER_SIMPLE)
439     wxFLAGS_MEMBER(wxBORDER_SUNKEN)
440     wxFLAGS_MEMBER(wxBORDER_DOUBLE)
441     wxFLAGS_MEMBER(wxBORDER_RAISED)
442     wxFLAGS_MEMBER(wxBORDER_STATIC)
443     wxFLAGS_MEMBER(wxBORDER_NONE)
444 
445     // old style border flags
446     wxFLAGS_MEMBER(wxSIMPLE_BORDER)
447     wxFLAGS_MEMBER(wxSUNKEN_BORDER)
448     wxFLAGS_MEMBER(wxDOUBLE_BORDER)
449     wxFLAGS_MEMBER(wxRAISED_BORDER)
450     wxFLAGS_MEMBER(wxSTATIC_BORDER)
451     wxFLAGS_MEMBER(wxBORDER)
452 
453     // standard window styles
454     wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
455     wxFLAGS_MEMBER(wxCLIP_CHILDREN)
456     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
457     wxFLAGS_MEMBER(wxWANTS_CHARS)
458     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
459     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
460     wxFLAGS_MEMBER(wxVSCROLL)
461     wxFLAGS_MEMBER(wxHSCROLL)
462 
463 wxEND_FLAGS( wxSheetStyle )
464 
465 IMPLEMENT_DYNAMIC_CLASS_XTI(wxSheet, wxWindow,"wx/sheet/sheet.h")
466 
467 wxBEGIN_PROPERTIES_TABLE(wxSheet)
468     wxHIDE_PROPERTY( Children )
469     wxPROPERTY_FLAGS( WindowStyle , wxSheetStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
470 wxEND_PROPERTIES_TABLE()
471 
472 wxBEGIN_HANDLERS_TABLE(wxSheet)
473 wxEND_HANDLERS_TABLE()
474 
475 wxCONSTRUCTOR_5( wxSheet , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
476 
477 /*
478  TODO : Expose more information of a list's layout etc. via appropriate objects (a la NotebookPageInfo)
479 */
480 #else
481 IMPLEMENT_DYNAMIC_CLASS( wxSheet, wxWindow )
482 #endif
483 
484 BEGIN_EVENT_TABLE( wxSheet, wxWindow )
485     EVT_PAINT           ( wxSheet::OnPaint )
486     EVT_SIZE            ( wxSheet::OnSize )
487     //EVT_ERASE_BACKGROUND( wxSheet::OnEraseBackground )
488     EVT_KEY_DOWN        ( wxSheet::OnKeyDown )
489     EVT_KEY_UP          ( wxSheet::OnKeyUp )
490     EVT_CHAR            ( wxSheet::OnChar )
491     EVT_MOUSEWHEEL      ( wxSheet::OnMouseWheel )
492     EVT_MOUSE_EVENTS    ( wxSheet::OnMouse )
493     EVT_COMMAND_SCROLL  ( wxSheet::ID_HORIZ_SCROLLBAR,  wxSheet::OnScroll )
494     EVT_COMMAND_SCROLL  ( wxSheet::ID_VERT_SCROLLBAR,   wxSheet::OnScroll )
495     EVT_TIMER           ( wxSheet::ID_MOUSE_DRAG_TIMER, wxSheet::OnMouseTimer )
496 END_EVENT_TABLE()
497 
498 void wxSheet::Init()
499 {
500     m_cornerLabelWin = NULL;
501     m_rowLabelWin    = NULL;
502     m_colLabelWin    = NULL;
503     m_gridWin        = NULL;
504     m_horizScrollBar = NULL;
505     m_vertScrollBar  = NULL;
506 
507     m_scrollBarMode  = wxSheet::SB_AS_NEEDED;
508 
509     m_keySelecting    = false;
510 
511     // init attr cache
512     m_cacheAttr       = wxNullSheetCellAttr;
513     m_cacheAttrCoords = wxNullSheetCoords;
514     m_cacheAttrType   = -1;
515 
516     m_inOnKeyDown = false;
517     m_batchCount  = 0;
518     m_resizing    = false;
519 
520     m_mouseCursorMode = WXSHEET_CURSOR_SELECT_CELL;
521     m_mouseCursor     = WXSHEET_CURSOR_SELECT_CELL;
522 
523     m_winCapture = (wxWindow *)NULL;
524 
525     m_dragLastPos     = -1;
526     m_dragRowOrCol    = -1;
527     m_isDragging      = false;
528     m_startDragPos    = wxDefaultPosition;
529 
530     m_waitForSlowClick = false;
531     m_mouseTimer       = NULL;
532 
533     m_enable_split_vert  = false;
534     m_enable_split_horiz = false;
535 
536     m_refData = new wxSheetRefData; // ALWAYS CREATED and ALWAYS EXISTS!
537 }
538 
539 // NOTE: If using the default visual attributes works everywhere then this can
540 // be removed as well as the #else cases below.
541 #define _USE_VISATTR 0
542 
543 #if _USE_VISATTR
544 #include "wx/listbox.h"
545 #endif
546 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)547 bool wxSheet::Create( wxWindow *parent, wxWindowID id,
548                       const wxPoint& pos, const wxSize& size,
549                       long style, const wxString& name )
550 {
551     if (!wxWindow::Create(parent, id, pos, size, style | wxWANTS_CHARS, name))
552         return false;
553 
554 #if wxCHECK_VERSION(2,5,2) && _USE_VISATTR
555     wxVisualAttributes gva = wxListBox::GetClassDefaultAttributes();
556     wxVisualAttributes lva = wxPanel::GetClassDefaultAttributes();
557 
558     wxColour gfg = gva.colFg;  // grid foreground
559     wxColour gbg = gva.colBg;  // grid background
560     wxColour lfg = lva.colFg;  // label foreground
561     wxColour lbg = lva.colBg;  // label background
562 #else // 2.4.x
563     wxColour gfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
564     wxColour gbg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
565     wxColour lfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
566     wxColour lbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
567 #endif //wxCHECK_VERSION(2,5,2)
568 
569     // Set default cell attributes
570     wxSheetCellAttr *attr = &GetSheetRefData()->m_defaultGridCellAttr;
571     attr->Create();
572     attr->SetKind(wxSHEET_AttrDefault);
573     attr->SetFont(GetFont()); // need window created for this
574     attr->SetAlignment(wxALIGN_LEFT | wxALIGN_TOP);
575     attr->SetOrientation(wxHORIZONTAL);
576     attr->SetLevel(wxSHEET_AttrLevelBottom);
577     attr->SetOverflow(true);
578     attr->SetOverflowMarker(true);
579     attr->SetShowEditor(false);
580     attr->SetReadOnly(false);
581     attr->SetForegroundColour(gfg);
582     attr->SetBackgroundColour(gbg);
583     attr->SetRenderer(GetDefaultRendererForType(wxSHEET_VALUE_STRING));
584     attr->SetEditor(GetDefaultEditorForType(wxSHEET_VALUE_STRING));
585 
586     wxFont labelFont = wxWindow::GetFont();
587     labelFont.SetWeight( wxBOLD );
588 
589     // default row label attr
590     attr = &GetSheetRefData()->m_defaultRowLabelAttr;
591     attr->Create();
592     attr->SetKind(wxSHEET_AttrDefault);
593     attr->SetFont(labelFont); // need window created for this
594     attr->SetAlignment(wxALIGN_CENTRE);
595     attr->SetOrientation(wxHORIZONTAL);
596     attr->SetLevel(wxSHEET_AttrLevelBottom);
597     attr->SetOverflow(false);
598     attr->SetOverflowMarker(false); // just cutoff, looks better?
599     attr->SetShowEditor(false);
600     attr->SetReadOnly(true);
601     attr->SetForegroundColour(lfg);
602     attr->SetBackgroundColour(lbg);
603     attr->SetRenderer(GetDefaultRendererForType(wxSHEET_VALUE_LABEL));
604     attr->SetEditor(GetDefaultEditorForType(wxSHEET_VALUE_STRING));
605 
606     // default col and corner label attr are identical to row label attr
607     GetSheetRefData()->m_defaultColLabelAttr    = attr->Clone();
608     GetSheetRefData()->m_defaultCornerLabelAttr = attr->Clone();
609 
610     // subwindow components that make up the wxSheet
611     m_gridWin        = new wxSheetChildWindow( this, ID_GRID_WINDOW );
612     m_rowLabelWin    = new wxSheetChildWindow( this, ID_ROW_LABEL_WINDOW );
613     m_colLabelWin    = new wxSheetChildWindow( this, ID_COL_LABEL_WINDOW );
614     m_cornerLabelWin = new wxSheetChildWindow( this, ID_CORNER_LABEL_WINDOW );
615     m_horizScrollBar = new wxScrollBar(this, ID_HORIZ_SCROLLBAR, wxDefaultPosition,
616                                        wxDefaultSize, wxSB_HORIZONTAL);
617     m_vertScrollBar  = new wxScrollBar(this, ID_VERT_SCROLLBAR, wxDefaultPosition,
618                                        wxDefaultSize, wxSB_VERTICAL);
619 
620     // det the default colours for each window
621     m_cornerLabelWin->SetOwnForegroundColour(lfg);
622     m_cornerLabelWin->SetOwnBackgroundColour(lbg);
623     m_rowLabelWin->SetOwnForegroundColour(lfg);
624     m_rowLabelWin->SetOwnBackgroundColour(lbg);
625     m_colLabelWin->SetOwnForegroundColour(lfg);
626     m_colLabelWin->SetOwnBackgroundColour(lbg);
627 
628     m_gridWin->SetOwnForegroundColour(gfg);
629     m_gridWin->SetOwnBackgroundColour(gbg);
630 
631     // figure out what a reasonable size would be
632     int char_w = 5, char_h = 10;
633     GetTextExtent(wxT("W1!(jJ"), &char_w, &char_h, NULL, NULL, &labelFont);
634 
635 #if defined(__WXMOTIF__) || defined(__WXGTK__)  // see also text ctrl sizing in ShowCellEditControl()
636     char_h += 8;
637 #else
638     char_h += 6; // test in wx24
639 #endif
640 
641     GetSheetRefData()->m_rowEdges.SetDefaultSize(char_h);
642 
643     GetSheetRefData()->AddSheet(this);
644 
645 #if wxCHECK_VERSION(2,7,2)
646     SetInitialSize(size);
647 #elif wxCHECK_VERSION(2,5,2)
648     SetBestFittingSize(size);
649 #endif // wxCHECK_VERSION(2,5,2)
650 
651     return true;
652 }
653 
~wxSheet()654 wxSheet::~wxSheet()
655 {
656     SetCaptureWindow(NULL);
657     StopMouseTimer();
658     GetSheetRefData()->RemoveSheet(this);
659 
660     // destroy the edit control if this is the "parent"
661     if (GetEditControl().Ok() && GetEditControl().GetControl())
662     {
663         wxWindowID win_id = GetEditControl().GetControl()->GetId();
664         if (FindWindow(win_id) == GetEditControl().GetControl())
665         {
666             GetSheetRefData()->m_cellEditor.DestroyControl();
667             GetSheetRefData()->m_cellEditor.Destroy();
668             GetSheetRefData()->m_cellEditorCoords = wxNullSheetCoords;
669         }
670     }
671 
672 #ifdef DEBUG_ATTR_CACHE
673     size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses;
674     wxPrintf(_T("wxSheet attribute cache statistics: "
675                 "total: %u, hits: %u (%u%%)\n"),
676              total, gs_nAttrCacheHits,
677              total ? (gs_nAttrCacheHits*100) / total : 0);
678 #endif
679 }
680 
Destroy()681 bool wxSheet::Destroy()
682 {
683     SetCaptureWindow(NULL);
684     StopMouseTimer();
685     GetSheetRefData()->RemoveSheet(this);
686     return wxWindow::Destroy();
687 }
688 
RefSheet(wxSheet * sheet)689 void wxSheet::RefSheet(wxSheet* sheet)
690 {
691     wxCHECK_RET(sheet, wxT("Invalid wxSheet"));
692     SetCaptureWindow(NULL);
693     StopMouseTimer();
694     GetSheetRefData()->RemoveSheet(this);  // remove from old data
695     UnRef();
696     Ref(*sheet);
697     GetSheetRefData()->AddSheet(this);
698     CalcWindowSizes();
699 }
700 
Clone(wxWindowID id)701 wxSheet* wxSheet::Clone(wxWindowID id)
702 {
703     wxSheet *sheet = (wxSheet*)GetClassInfo()->CreateObject();
704     sheet->Create(GetParent(), id);
705     return sheet;
706 }
707 
Enable(bool enable)708 bool wxSheet::Enable(bool enable)
709 {
710     if ( !wxWindow::Enable(enable) )
711   	    return false;
712 
713   	// redraw in the new state
714   	Refresh();
715 
716   	return true;
717 }
718 
719 // ------------------------------------------------------------------------
720 
SetTable(wxSheetTable * table,bool takeOwnership)721 bool wxSheet::SetTable( wxSheetTable *table, bool takeOwnership )
722 {
723     if ( GetSheetRefData()->m_table )
724     {
725         if (GetSheetRefData()->m_ownTable)
726             delete GetSheetRefData()->m_table;
727 
728         if (GetSelection())
729             GetSelection()->Clear();
730 
731         GetSheetRefData()->m_table = NULL;
732         GetSheetRefData()->m_rowEdges.Clear();
733         GetSheetRefData()->m_colEdges.Clear();
734     }
735 
736     if (table)
737     {
738         GetSheetRefData()->m_rowEdges.UpdatePos(0, table->GetNumberRows());
739         GetSheetRefData()->m_colEdges.UpdatePos(0, table->GetNumberCols());
740 
741         GetSheetRefData()->m_table = table;
742         GetSheetRefData()->m_table->SetView( this );
743         GetSheetRefData()->m_ownTable = takeOwnership;
744 
745         CalcWindowSizes();
746     }
747 
748     return true;
749 }
750 
CreateGrid(int numRows,int numCols,int options)751 bool wxSheet::CreateGrid( int numRows, int numCols, int options )
752 {
753     // Create a table and assign it's data structures
754     wxSheetTable *table = new wxSheetTable( this );
755     table->SetGridCellValueProvider(new wxSheetValueProviderString(numRows, numCols, options), true);
756     //table->SetGridCellValueProvider(new wxSheetValueProviderSparseString(numRows, numCols, options), true);
757 
758     // label data containers are optional and needed only if you Set values for them
759     // we create both of them.
760     // NB. They're the same as the grid data but only have one row or col each.
761     // NB. wxSHEET_ValueProviderColPref is used for the col labels since
762     //     we expect more cols than rows
763     table->SetRowLabelValueProvider(new wxSheetValueProviderString(numRows, 1), true);
764     table->SetColLabelValueProvider(new wxSheetValueProviderString(1, numCols, wxSHEET_ValueProviderColPref), true);
765     // Set the attribute provider if you plan to "Set" attributes for particular
766     //  cells/rows/cols, otherwise the default attributes will suffice
767     table->SetAttrProvider(new wxSheetCellAttrProvider, true);
768 
769     return SetTable(table, true);
770 }
771 
772 // ------------------------------------------------------------------------
773 // Dimensions of the sheet
774 
GetCellCoordsType(const wxSheetCoords & coords)775 wxSheetCell_Type wxSheet::GetCellCoordsType(const wxSheetCoords& coords)
776 {
777     if ((coords.m_row >= 0) && (coords.m_col >= 0))
778     {
779         return wxSHEET_CELL_GRID;
780     }
781     else if (coords.m_row == -1)
782     {
783         if (coords.m_col == -1)
784             return wxSHEET_CELL_CORNERLABEL;
785         if (coords.m_col >= 0)
786             return wxSHEET_CELL_COLLABEL;
787     }
788     else if (coords.m_col == -1)
789     {
790         if (coords.m_row >= 0)
791             return wxSHEET_CELL_ROWLABEL;
792     }
793 
794     return wxSHEET_CELL_UNKNOWN;
795 }
796 
ClearValues(int update)797 void wxSheet::ClearValues(int update)
798 {
799     if ( GetTable() )
800     {
801         if (IsCellEditControlCreated())
802             DisableCellEditControl(false);
803 
804         GetTable()->ClearValues(update);
805         RefreshGridWindow();
806     }
807 }
808 
UpdateRows(size_t row,int numRows,int update)809 bool wxSheet::UpdateRows( size_t row, int numRows, int update )
810 {
811     wxCHECK_MSG(GetTable(), false, wxT("Called wxSheet::UpdateRows() before calling CreateGrid()"));
812     // the table should then call DoUpdateRows back on the sheet before returning
813     return GetTable()->UpdateRows(row, numRows, update);
814 }
UpdateCols(size_t col,int numCols,int update)815 bool wxSheet::UpdateCols( size_t col, int numCols, int update )
816 {
817     wxCHECK_MSG(GetTable(), false, wxT("Called wxSheet::UpdateCols() before calling CreateGrid()"));
818     // the table should then call DoUpdateRows back on the sheet before returning
819     return GetTable()->UpdateCols(col, numCols, update);
820 }
821 
SetNumberCols(size_t cols,int update)822 bool wxSheet::SetNumberCols( size_t cols, int update )
823 {
824     const int n_cols = int(cols) - GetNumberCols();
825     if (n_cols > 0)
826         return AppendCols(n_cols, update);
827     else if (n_cols < 0)
828         return DeleteCols(cols, -n_cols, update);
829 
830     return false;
831 }
832 
SetNumberRows(size_t rows,int update)833 bool wxSheet::SetNumberRows( size_t rows, int update )
834 {
835     const int n_rows = int(rows) - GetNumberRows();
836     if (n_rows > 0)
837         return AppendRows(n_rows, update);
838     else if (n_rows < 0)
839         return DeleteRows(rows, -n_rows, update);
840 
841     return false;
842 }
843 
DoUpdateRows(size_t row,int numRows,int update)844 bool wxSheet::DoUpdateRows( size_t row, int numRows, int update)
845 {
846     // Clear the attribute cache as the attribute might refer to a invalid cell
847     ClearAttrCache();
848     if (numRows == 0) return false;
849 
850     // By the same reasoning, the editor should be dismissed if columns are
851     // added or removed. And for consistency, it should IMHO always be
852     // removed, not only if the cell "underneath" it actually changes.
853     // For now, I intentionally do not save the editor's content as the
854     // cell it might want to save that stuff to might no longer exist.
855     if (IsCellEditControlCreated())
856         DisableCellEditControl(false);
857 
858     // FIXME - do I want to UpdateRows/Cols for selection or just clear them
859     //         the event it would send would be confusing at best
860     //         first a deselect and then a select... should sel follow insert?
861     //         I don't think it's worth the confusion and since a click on
862     //         a cell is deselect all, the selection is not that precious.
863     //ClearSelection(true);
864 
865     GetSheetRefData()->m_rowEdges.UpdatePos(row, numRows);
866 
867     // Update any data structures
868     if ( ((update & wxSHEET_UpdateSelection) != 0) && GetSelection() )
869         GetSelection()->UpdateRows( row, numRows );
870 
871     GetSheetRefData()->m_cursorCoords.UpdateRows(row, numRows);
872     // try to force the cursor to a valid cell
873     if ( !ContainsGridCell(GetGridCursorCell()) && ContainsGridCell(wxSheetCoords(0,0)) )
874         SetGridCursorCell( wxSheetCoords(0, 0) );
875     else
876         GetSheetRefData()->m_cursorCoords = wxNullSheetCoords;
877 
878     AdjustScrollbars();
879     RefreshRowLabelWindow(true);
880     RefreshGridWindow();
881     return true;
882 }
883 
DoUpdateCols(size_t col,int numCols,int update)884 bool wxSheet::DoUpdateCols( size_t col, int numCols, int update)
885 {
886     ClearAttrCache();
887     if (numCols == 0) return false;
888 
889     if (IsCellEditControlCreated())
890         DisableCellEditControl(false);
891 
892     GetSheetRefData()->m_colEdges.UpdatePos(col, numCols);
893 
894     if ( ((update & wxSHEET_UpdateSelection) != 0) && GetSelection() )
895         GetSelection()->UpdateCols( col, numCols );
896 
897     GetSheetRefData()->m_cursorCoords.UpdateCols(col, numCols);
898     if ( !ContainsGridCell(GetGridCursorCell()) && ContainsGridCell(wxSheetCoords(0,0)) )
899         SetGridCursorCell( wxSheetCoords(0, 0) );
900     else
901         GetSheetRefData()->m_cursorCoords = wxNullSheetCoords;
902 
903     AdjustScrollbars();
904     RefreshColLabelWindow(true);
905     RefreshGridWindow();
906     return true;
907 }
908 
909 // ----------------------------------------------------------------------------
910 // Dimensions of the row and column sizes
911 
SetDefaultRowHeight(int height,bool resizeExistingRows)912 void wxSheet::SetDefaultRowHeight( int height, bool resizeExistingRows )
913 {
914     GetSheetRefData()->m_rowEdges.SetDefaultSize(height, resizeExistingRows);
915     if ( resizeExistingRows && !GetBatchCount() )
916         CalcWindowSizes(true);
917 }
918 
SetDefaultColWidth(int width,bool resizeExistingCols)919 void wxSheet::SetDefaultColWidth( int width, bool resizeExistingCols )
920 {
921     GetSheetRefData()->m_colEdges.SetDefaultSize(width, resizeExistingCols);
922     if ( resizeExistingCols && !GetBatchCount() )
923         CalcWindowSizes(true);
924 }
925 
SetRowHeight(int row,int height)926 void wxSheet::SetRowHeight( int row, int height )
927 {
928     if (row == -1)
929     {
930         SetColLabelHeight(height);
931         return;
932     }
933 
934     wxCHECK_RET(ContainsGridRow(row), _T("invalid row index") );
935 
936     const int old_height = GetRowHeight(row);
937 
938     // if < 0 calc new height from label
939     if ( height < 0 )
940         height = GetCellBestSize(wxSheetCoords(row, -1)).y + 6; // FIXME test this
941 
942     // should we check that it's bigger than GetMinimalRowHeight(row) here?
943     //                                                                 (VZ)
944     // No, because it is reasonable to assume the library user know's
945     // what he is doing. However whe should test against the weaker
946     // constariant of minimalAcceptableHeight, as this breaks rendering
947     if ( height == old_height )
948         return;
949     if (height < GetMinimalAcceptableRowHeight())
950         height = GetMinimalAcceptableRowHeight();
951 
952     GetSheetRefData()->m_rowEdges.SetSize(row, height);
953 
954     if ( !GetBatchCount() )
955     {
956         CalcWindowSizes(true);
957         SetGridOrigin(GetGridOrigin(), true);
958         RefreshBlock(wxSheetBlock(row, -1, GetNumberRows()-row+1, GetNumberCols()+1));
959     }
960 }
961 
SetColWidth(int col,int width)962 void wxSheet::SetColWidth( int col, int width )
963 {
964     if (col == -1)
965     {
966         SetRowLabelWidth(width);
967         return;
968     }
969 
970     wxCHECK_RET(ContainsGridCol(col), _T("invalid column index") );
971 
972     const int old_width = GetColWidth(col);
973 
974     if ( width < 0 )
975         width = GetCellBestSize(wxSheetCoords(-1, col)).x + 6;
976 
977     if ( width == old_width )
978         return;
979     if ( width < GetMinimalAcceptableColWidth() )
980         width = GetMinimalAcceptableColWidth();
981 
982     GetSheetRefData()->m_colEdges.SetSize(col, width);
983 
984     if ( !GetBatchCount() )
985     {
986         CalcWindowSizes(true);
987         SetGridOrigin(GetGridOrigin(), true);
988         RefreshBlock(wxSheetBlock(-1, col, GetNumberRows()+1, GetNumberCols()-col+1));
989     }
990 }
991 
GetColWidth(int col) const992 int wxSheet::GetColWidth(int col) const
993 {
994     if (col == -1)
995         return GetRowLabelWidth();
996 
997     return GetSheetRefData()->m_colEdges.GetSize(col);
998 }
GetColLeft(int col) const999 int wxSheet::GetColLeft(int col) const
1000 {
1001     if (col == -1)
1002         return 1; // 1 for border
1003 
1004     return GetSheetRefData()->m_colEdges.GetMin(col);
1005 }
GetColRight(int col) const1006 int wxSheet::GetColRight(int col) const
1007 {
1008     if (col == -1)
1009         return GetRowLabelWidth();
1010 
1011     return GetSheetRefData()->m_colEdges.GetMax(col);
1012 }
1013 
GetRowHeight(int row) const1014 int wxSheet::GetRowHeight(int row) const
1015 {
1016     if (row == -1)
1017         return GetColLabelHeight();
1018 
1019     return GetSheetRefData()->m_rowEdges.GetSize(row);
1020 }
GetRowTop(int row) const1021 int wxSheet::GetRowTop(int row) const
1022 {
1023     if (row == -1)
1024         return 1; // 1 for border
1025 
1026     return GetSheetRefData()->m_rowEdges.GetMin(row);
1027 }
GetRowBottom(int row) const1028 int wxSheet::GetRowBottom(int row) const
1029 {
1030     if (row == -1)
1031         return GetColLabelHeight();
1032 
1033     return GetSheetRefData()->m_rowEdges.GetMax(row);
1034 }
1035 
GetCellSize(const wxSheetCoords & coords) const1036 wxSize wxSheet::GetCellSize(const wxSheetCoords& coords) const
1037 {
1038     return wxSize(GetColWidth(coords.m_row), GetRowHeight(coords.m_row));
1039 }
1040 
IsCellShown(const wxSheetCoords & coords) const1041 bool wxSheet::IsCellShown( const wxSheetCoords& coords ) const
1042 {
1043     wxCHECK_MSG(ContainsCell(coords), false, wxT("Invalid coords in wxSheet::IsCellShown"));
1044     return (GetColWidth(coords.GetCol()) > 0) && (GetRowHeight(coords.GetRow()) > 0);
1045 }
1046 
GetCellBestSize(const wxSheetCoords & coords,wxDC * dc) const1047 wxSize wxSheet::GetCellBestSize(const wxSheetCoords& coords, wxDC *dc) const
1048 {
1049     wxCHECK_MSG(ContainsCell(coords), wxSize(GetDefaultColWidth(), GetDefaultRowHeight()), wxT("Invalid coords"));
1050     wxSheetCellAttr attr(GetAttr(coords));
1051     wxSheet *sheet = (wxSheet*)this;
1052     wxSheetCellRenderer renderer(attr.GetRenderer(sheet, coords));
1053     if (!dc)
1054     {
1055         wxClientDC cdc(sheet);
1056         return renderer.GetBestSize(*sheet, attr, cdc, coords);
1057     }
1058 
1059     return renderer.GetBestSize(*sheet, attr, *dc, coords);
1060 }
1061 
GetRowBestHeight(int row) const1062 int wxSheet::GetRowBestHeight(int row) const
1063 {
1064     wxCHECK_MSG(ContainsGridRow(row), GetDefaultRowHeight(), wxT("Invalid coords in wxSheet::GetRowBestHeight"));
1065     wxClientDC dc(m_gridWin);
1066     wxCoord bestHeight = 0;
1067     const int numCols = GetNumberCols();
1068     wxSheetCoords coords( row, row );
1069     for ( coords.m_col = -1; coords.m_col < numCols; coords.m_col++ )
1070     {
1071         const int height = GetCellBestSize(coords, &dc).GetHeight();
1072         if ( height > bestHeight )
1073             bestHeight = height;
1074     }
1075 
1076     return bestHeight;
1077 }
1078 
GetColBestWidth(int col) const1079 int wxSheet::GetColBestWidth(int col) const
1080 {
1081     wxCHECK_MSG(ContainsGridCol(col), GetDefaultColWidth(), wxT("Invalid col in wxSheet::GetColBestWidth"));
1082     wxClientDC dc(m_gridWin);
1083     wxCoord bestWidth = 0;
1084     const int numRows = GetNumberRows();
1085     wxSheetCoords coords( col, col );
1086     for ( coords.m_row = -1; coords.m_row < numRows; coords.m_row++ )
1087     {
1088         const int width = GetCellBestSize(coords, &dc).GetWidth();
1089         if ( width > bestWidth )
1090             bestWidth = width;
1091     }
1092 
1093     return bestWidth;
1094 }
1095 
1096 // ----------------------------------------------------------------------------
1097 // Row/Col label size and attributes
1098 
GetRowLabelWidth(bool zero_not_shown) const1099 int wxSheet::GetRowLabelWidth(bool zero_not_shown) const
1100 {
1101     if (zero_not_shown && !m_cornerLabelWin->IsShown() && !m_rowLabelWin->IsShown())
1102         return 0;
1103 
1104     return GetSheetRefData()->m_rowLabelWidth;
1105 }
1106 
GetColLabelHeight(bool zero_not_shown) const1107 int wxSheet::GetColLabelHeight(bool zero_not_shown) const
1108 {
1109     if (zero_not_shown && !m_cornerLabelWin->IsShown() && !m_colLabelWin->IsShown())
1110         return 0;
1111 
1112     return GetSheetRefData()->m_colLabelHeight;
1113 }
1114 
SetRowLabelWidth(int width)1115 void wxSheet::SetRowLabelWidth( int width )
1116 {
1117     width = wxMax( width, 0 );
1118 
1119     if ( width == 0 )
1120     {
1121         m_rowLabelWin->Show( false );
1122         m_cornerLabelWin->Show( false );
1123     }
1124     else if ( GetRowLabelWidth(false) > 0 )
1125     {
1126         GetSheetRefData()->m_rowLabelWidth = width;
1127 
1128         m_rowLabelWin->Show( true );
1129         if ( GetColLabelHeight() > 0 )
1130             m_cornerLabelWin->Show( true );
1131     }
1132 
1133     CalcWindowSizes();
1134 }
1135 
SetColLabelHeight(int height)1136 void wxSheet::SetColLabelHeight( int height )
1137 {
1138     height = wxMax( height, 0 );
1139 
1140     if ( height == 0 )
1141     {
1142         m_colLabelWin->Show( false );
1143         m_cornerLabelWin->Show( false );
1144     }
1145     else if ( GetColLabelHeight(false) > 0 )
1146     {
1147         GetSheetRefData()->m_colLabelHeight = height;
1148 
1149         m_colLabelWin->Show( true );
1150         if ( GetRowLabelWidth() > 0 )
1151             m_cornerLabelWin->Show( true );
1152     }
1153 
1154     CalcWindowSizes();
1155 }
1156 
1157 // ----------------------------------------------------------------------------
1158 // Auto sizing of the row/col widths/heights
1159 
AutoSizeRow(int row,bool setAsMin)1160 int wxSheet::AutoSizeRow( int row, bool setAsMin )
1161 {
1162     wxCHECK_MSG(ContainsGridRow(row), 0, wxT("Invalid coords in wxSheet::AutoSizeRow"));
1163     int bestHeight = GetRowBestHeight(row);
1164 
1165     // if empty row - give default extent (notice that if extentMax is less
1166     // than default extent but != 0, it's ok)
1167     if ( bestHeight == 0 )
1168         bestHeight = GetDefaultRowHeight();
1169     else
1170         bestHeight += 6; // leave some space around text
1171 
1172     if (GetRowHeight(row) != bestHeight)
1173         SetRowHeight(row, bestHeight);
1174     if ( setAsMin )
1175         SetMinimalRowHeight(row, bestHeight);
1176 
1177     return bestHeight;
1178 }
AutoSizeCol(int col,bool setAsMin)1179 int wxSheet::AutoSizeCol( int col, bool setAsMin )
1180 {
1181     wxCHECK_MSG(ContainsGridCol(col), 0, wxT("Invalid col in wxSheet::AutoSizeCol"));
1182     int bestWidth = GetColBestWidth(col);
1183 
1184     if ( bestWidth == 0 )
1185         bestWidth = GetDefaultColWidth();
1186     else
1187         bestWidth += 10; // leave some space around text
1188 
1189     if (GetColWidth(col) != bestWidth)
1190         SetColWidth(col, bestWidth);
1191     if ( setAsMin )
1192         SetMinimalColWidth(col, bestWidth);
1193 
1194     return bestWidth;
1195 }
1196 
AutoSizeRows(bool setAsMin)1197 void wxSheet::AutoSizeRows(bool setAsMin)
1198 {
1199     BeginBatch();
1200 
1201     const int numRows = GetNumberRows();
1202     for ( int row = 0; row < numRows; row++ )
1203         AutoSizeRow(row, setAsMin);
1204 
1205     EndBatch();
1206 }
AutoSizeCols(bool setAsMin)1207 void wxSheet::AutoSizeCols(bool setAsMin)
1208 {
1209     BeginBatch();
1210 
1211     const int numCols = GetNumberCols();
1212     for ( int col = 0; col < numCols; col++ )
1213         AutoSizeCol(col, setAsMin);
1214 
1215     EndBatch();
1216 }
1217 
AutoSize(bool setAsMin)1218 void wxSheet::AutoSize(bool setAsMin)
1219 {
1220     BeginBatch();
1221 
1222     SetMargins(0, 0);
1223     AutoSizeRows(setAsMin);
1224     AutoSizeCols(setAsMin);
1225 
1226     wxSize sizeFit(GetGridVirtualSize());
1227     sizeFit.x += GetRowLabelWidth();
1228     sizeFit.y += GetColLabelHeight();
1229 
1230     wxSize winSize(GetClientSize());
1231 
1232     // distribute the extra space between the columns/rows to avoid having
1233     // extra white space
1234     const int numCols = GetNumberCols();
1235     const int numRows = GetNumberRows();
1236 
1237     // Remove the extra m_extraWidth + 1 added above
1238     wxCoord diff = winSize.x - sizeFit.x;
1239     if ( diff && numCols )
1240     {
1241         // try to resize the columns uniformly
1242         wxCoord diffPerCol = diff / numCols;
1243         if ( diffPerCol != 0 )
1244         {
1245             for ( int col = 0; col < numCols; col++ )
1246                 SetColWidth(col, GetColWidth(col) + diffPerCol);
1247         }
1248 
1249         // add remaining amount to the last columns
1250         diff -= diffPerCol * numCols;
1251         if ( diff != 0 )
1252         {
1253             for ( int col = numCols - 1; col >= numCols - diff; col-- )
1254                 SetColWidth(col, GetColWidth(col) + 1);
1255         }
1256     }
1257 
1258     // same for rows
1259     diff = winSize.y - sizeFit.y;
1260     if ( diff && numRows )
1261     {
1262         // try to resize the columns uniformly
1263         wxCoord diffPerRow = diff / numRows;
1264         if ( diffPerRow != 0 )
1265         {
1266             for ( int row = 0; row < numRows; row++ )
1267                 SetRowHeight(row, GetRowHeight(row) + diffPerRow);
1268         }
1269 
1270         // add remaining amount to the last rows
1271         diff -= diffPerRow * numRows;
1272         if ( diff != 0 )
1273         {
1274             for ( int row = numRows - 1; row >= numRows - diff; row-- )
1275                 SetRowHeight(row, GetRowHeight(row) + 1);
1276         }
1277     }
1278 
1279     sizeFit = GetGridVirtualSize();
1280     sizeFit.x += GetRowLabelWidth();
1281     sizeFit.y += GetColLabelHeight();
1282 
1283     SetClientSize(sizeFit + wxSize(2,2)); // FIXME ? Where's the extra 2 from in wxGTK
1284     EndBatch();
1285 }
1286 
AutoSizeRowLabelHeight(int row)1287 void wxSheet::AutoSizeRowLabelHeight( int row )
1288 {
1289     wxCHECK_RET(ContainsGridRow(row), wxT("Invalid row in wxSheet::AutoSizeRowLabelHeight"));
1290 
1291     // Hide the edit control, so it won't interfer with drag-shrinking.
1292     if ( IsCellEditControlShown() )
1293         DisableCellEditControl(true);
1294 
1295     // autosize row height depending on label text
1296     const wxSheetCoords coords(row, -1);
1297     wxSize size(GetCellBestSize(coords));
1298 
1299     if (size.y < GetDefaultRowHeight())
1300         size.y = GetDefaultRowHeight();
1301 
1302     SetRowHeight(row, size.y);
1303 }
AutoSizeColLabelWidth(int col)1304 void wxSheet::AutoSizeColLabelWidth( int col )
1305 {
1306     wxCHECK_RET(ContainsGridCol(col), wxT("Invalid col in wxSheet::AutoSizeColLabelWidth"));
1307 
1308     // Hide the edit control, so it won't interfer with drag-shrinking.
1309     if ( IsCellEditControlShown() )
1310         DisableCellEditControl(true);
1311 
1312     // autosize column width depending on label text
1313     const wxSheetCoords coords(-1, col);
1314     wxSize size(GetCellBestSize(coords));
1315 
1316     if (size.x < GetDefaultColWidth())
1317         size.x = GetDefaultColWidth();
1318 
1319     SetColWidth(col, size.x);
1320 }
1321 
SetEqualColWidths(int min_width)1322 void wxSheet::SetEqualColWidths(int min_width)
1323 {
1324     GetSheetRefData()->m_equal_col_widths = wxMax(min_width, 0);
1325     const int numCols = GetNumberCols();
1326 
1327     // don't fail here, since EVT_SIZEs are generated before the grid is
1328     if ((min_width > 0) && (numCols > 0) && GetTable() && m_gridWin)
1329     {
1330         const int colwidth = m_gridWin->GetClientSize().GetWidth()/numCols;
1331         SetDefaultColWidth( colwidth, true );
1332     }
1333 }
1334 
1335 // ----------------------------------------------------------------------------
1336 // Grid line and cell highlight colouring
1337 
EnableGridLines(int dir)1338 void wxSheet::EnableGridLines( int dir )
1339 {
1340     GetSheetRefData()->m_gridLinesEnabled = dir;
1341     RefreshGridWindow(false);
1342 }
1343 
SetGridLineColour(const wxColour & colour)1344 void wxSheet::SetGridLineColour( const wxColour& colour )
1345 {
1346     wxCHECK_RET(colour.Ok(), wxT("Invalid colour in wxSheet::SetGridLineColour"));
1347     GetSheetRefData()->m_gridLineColour = colour;
1348     RefreshGridWindow(false);
1349 }
1350 
SetCursorCellHighlightColour(const wxColour & colour)1351 void wxSheet::SetCursorCellHighlightColour( const wxColour& colour )
1352 {
1353     wxCHECK_RET(colour.Ok(), wxT("Invalid colour in wxSheet::SetGridLineColour"));
1354     GetSheetRefData()->m_cursorCellHighlightColour = colour;
1355 
1356     if (ContainsCell(GetGridCursorCell()))
1357         RefreshCell(GetGridCursorCell(), true);
1358 }
1359 
SetCursorCellHighlightPenWidth(int width)1360 void wxSheet::SetCursorCellHighlightPenWidth(int width)
1361 {
1362     wxCHECK_RET(width >= 0, wxT("Invalid pen width in wxSheet::SetGridLineColour"));
1363     GetSheetRefData()->m_cursorCellHighlightPenWidth = width;
1364 
1365     if (ContainsCell(GetGridCursorCell()))
1366         RefreshCell(GetGridCursorCell(), true);
1367 }
1368 
SetCursorCellHighlightROPenWidth(int width)1369 void wxSheet::SetCursorCellHighlightROPenWidth(int width)
1370 {
1371     wxCHECK_RET(width >= 0, wxT("Invalid pen width in wxSheet::SetCursorCellHighlightROPenWidth"));
1372     GetSheetRefData()->m_cursorCellHighlightROPenWidth = width;
1373 
1374     if (ContainsCell(GetGridCursorCell()))
1375         RefreshCell(GetGridCursorCell(), true);
1376 }
1377 
SetLabelOutlineColour(const wxColour & colour)1378 void wxSheet::SetLabelOutlineColour( const wxColour& colour )
1379 {
1380     wxCHECK_RET(colour.Ok(), wxT("Invalid colour in wxSheet::SetLabelOutlineColour"));
1381     GetSheetRefData()->m_labelOutlineColour = colour;
1382 
1383     RefreshColLabelWindow(true);
1384     RefreshRowLabelWindow(true);
1385     RefreshCornerLabelWindow(true);
1386 }
1387 
1388 // ----------------------------------------------------------------------------
1389 // Spanned cells
1390 
HasSpannedCells() const1391 bool wxSheet::HasSpannedCells() const
1392 {
1393     return GetTable() && GetTable()->HasSpannedCells();
1394 }
GetCellOwner(const wxSheetCoords & coords) const1395 wxSheetCoords wxSheet::GetCellOwner( const wxSheetCoords& coords ) const
1396 {
1397     return GetCellBlock(coords).GetLeftTop();
1398 }
GetCellBlock(const wxSheetCoords & coords) const1399 wxSheetBlock wxSheet::GetCellBlock( const wxSheetCoords& coords ) const
1400 {
1401     return GetTable() ? GetTable()->GetCellBlock(coords) : wxSheetBlock(coords, 1, 1);
1402 }
GetCellSpan(const wxSheetCoords & coords) const1403 wxSheetCoords wxSheet::GetCellSpan( const wxSheetCoords& coords ) const
1404 {
1405     const wxSheetBlock block(GetCellBlock(coords));
1406     if (coords == block.GetLeftTop())
1407         return block.GetSize();
1408     else
1409         return block.GetLeftTop() - coords;
1410 }
SetCellSpan(const wxSheetBlock & block)1411 void wxSheet::SetCellSpan( const wxSheetBlock& block )
1412 {
1413     wxCHECK_RET(GetTable(), wxT("The wxSheetTable doesn't exist to SetCellSpan"));
1414     GetTable()->SetCellSpan(block);
1415 }
1416 
1417 // ****************************************************************************
1418 
GetOrCreateAttr(const wxSheetCoords & coords,wxSheetAttr_Type type) const1419 wxSheetCellAttr wxSheet::GetOrCreateAttr(const wxSheetCoords& coords, wxSheetAttr_Type type) const
1420 {
1421     wxSheetCellAttr attr;
1422     wxCHECK_MSG( type != wxSHEET_AttrAny, attr, wxT("Cannot create attribute of type wxSHEET_AttrAny") );
1423 
1424     if (IsGridCell(coords))
1425     {
1426         switch (type)
1427         {
1428             case wxSHEET_AttrDefault : return DoGetDefaultGridAttr();
1429             case wxSHEET_AttrCell    :
1430             case wxSHEET_AttrRow     :
1431             case wxSHEET_AttrCol     :
1432             {
1433                 wxCHECK_MSG(GetTable() && ContainsGridCell(coords), attr, wxT("Invalid table or attr coords"));
1434                 attr = GetTable()->GetAttr(coords, type);
1435                 if ( !attr.Ok() )
1436                 {
1437                     attr.Create();
1438                     attr.SetKind(type);
1439                     attr.SetDefaultAttr(DoGetDefaultGridAttr());
1440                     GetTable()->SetAttr(coords, attr, type);
1441                 }
1442                 else
1443                     InitAttr(attr, DoGetDefaultGridAttr());
1444 
1445                 return attr;
1446             }
1447             default :
1448             {
1449                 wxFAIL_MSG(wxT("Grid cell attribute kind invalid for GetOrCreateAttr"));
1450                 return attr;
1451             }
1452         }
1453     }
1454     else if (IsCornerLabelCell(coords))
1455     {
1456         switch (type)
1457         {
1458             case wxSHEET_AttrDefault : // only one of these, ever
1459             case wxSHEET_AttrCell    : return DoGetDefaultCornerLabelAttr();
1460             default :
1461             {
1462                 wxFAIL_MSG(wxT("Grid cell attribute kind invalid for GetOrCreateAttr"));
1463                 return attr;
1464             }
1465         }
1466     }
1467     else if (IsRowLabelCell(coords))
1468     {
1469         switch (type)
1470         {
1471             case wxSHEET_AttrDefault : return DoGetDefaultRowLabelAttr();
1472             case wxSHEET_AttrCell    :
1473             {
1474                 wxCHECK_MSG(GetTable() && ContainsRowLabelCell(coords), attr, wxT("Invalid table or attr coords"));
1475                 attr = GetTable()->GetAttr(coords, type);
1476                 if ( !attr.Ok() )
1477                 {
1478                     attr.Create();
1479                     attr.SetKind(type);
1480                     attr.SetDefaultAttr(DoGetDefaultRowLabelAttr());
1481                     GetTable()->SetAttr(coords, attr, type);
1482                 }
1483                 else
1484                     InitAttr(attr, DoGetDefaultRowLabelAttr());
1485 
1486                 return attr;
1487             }
1488             default :
1489             {
1490                 wxFAIL_MSG(wxT("Grid cell attribute kind invalid for GetOrCreateAttr"));
1491                 return attr;
1492             }
1493         }
1494     }
1495     else if (IsColLabelCell(coords))
1496     {
1497         switch (type)
1498         {
1499             case wxSHEET_AttrDefault : return DoGetDefaultColLabelAttr();
1500             case wxSHEET_AttrCell    :
1501             {
1502                 wxCHECK_MSG(GetTable() && ContainsColLabelCell(coords), attr, wxT("Invalid table or attr coords"));
1503                 attr = GetTable()->GetAttr(coords, type);
1504                 if ( !attr.Ok() )
1505                 {
1506                     attr.Create();
1507                     attr.SetKind(type);
1508                     attr.SetDefaultAttr(DoGetDefaultColLabelAttr());
1509                     GetTable()->SetAttr(coords, attr, type);
1510                 }
1511                 else
1512                     InitAttr(attr, DoGetDefaultColLabelAttr());
1513 
1514                 return attr;
1515             }
1516             default :
1517             {
1518                 wxFAIL_MSG(wxT("Grid cell attribute kind invalid for GetOrCreateAttr"));
1519                 return attr;
1520             }
1521         }
1522     }
1523 
1524     wxFAIL_MSG(wxString::Format(wxT("Unable to get or create attribute for cell(%d,%d)"), coords.m_row, coords.m_col));
1525     return wxSheetCellAttr();
1526 }
1527 
GetAttr(const wxSheetCoords & coords,wxSheetAttr_Type type) const1528 wxSheetCellAttr wxSheet::GetAttr(const wxSheetCoords& coords, wxSheetAttr_Type type) const
1529 {
1530     if (IsGridCell(coords))
1531     {
1532         switch (type)
1533         {
1534             case wxSHEET_AttrDefault : return DoGetDefaultGridAttr();
1535             case wxSHEET_AttrCell    :
1536             case wxSHEET_AttrRow     :
1537             case wxSHEET_AttrCol     :
1538             case wxSHEET_AttrAny     :
1539             default                  :
1540             {
1541                 wxCHECK_MSG(ContainsGridCell(coords), DoGetDefaultGridAttr(), wxT("Invalid attr coords"));
1542                 wxSheetCellAttr attr;
1543 
1544                 if ( !LookupAttr(coords, type, attr) )
1545                 {
1546                     if (GetTable())
1547                     {
1548                         attr = GetTable()->GetAttr(coords, type);
1549                         if (attr.Ok())
1550                             CacheAttr(coords, attr, type);
1551                     }
1552                 }
1553 
1554                 if ((type == wxSHEET_AttrAny) && !attr.Ok())
1555                     attr = DoGetDefaultGridAttr();
1556 
1557                 return attr;
1558             }
1559         }
1560     }
1561     else if (IsCornerLabelCell(coords))
1562     {
1563         switch (type)
1564         {
1565             case wxSHEET_AttrDefault :
1566             case wxSHEET_AttrCell    : // only one of these, ever
1567             case wxSHEET_AttrAny     : return DoGetDefaultCornerLabelAttr();
1568             default :
1569             {
1570                 wxFAIL_MSG(wxT("Invalid attr type for corner label"));
1571             }
1572         }
1573 
1574         return wxNullSheetCellAttr;
1575     }
1576     else if (IsRowLabelCell(coords))
1577     {
1578         switch (type)
1579         {
1580             case wxSHEET_AttrDefault : return DoGetDefaultRowLabelAttr();
1581             case wxSHEET_AttrCell    :
1582             case wxSHEET_AttrAny     :
1583             {
1584                 wxCHECK_MSG(ContainsRowLabelCell(coords), DoGetDefaultRowLabelAttr(), wxT("Invalid attr coords"));
1585                 wxSheetCellAttr attr;
1586                 if (GetTable())
1587                     attr = GetTable()->GetAttr(coords, type);
1588                 if ((type == wxSHEET_AttrAny) && !attr.Ok())
1589                     attr = DoGetDefaultRowLabelAttr();
1590 
1591                 return attr;
1592             }
1593             default :
1594             {
1595                 wxFAIL_MSG(wxT("Invalid attr type for row label"));
1596             }
1597         }
1598     }
1599     else if (IsColLabelCell(coords))
1600     {
1601         switch (type)
1602         {
1603             case wxSHEET_AttrDefault : return DoGetDefaultColLabelAttr();
1604             case wxSHEET_AttrCell    :
1605             case wxSHEET_AttrAny     :
1606             {
1607                 wxCHECK_MSG(ContainsColLabelCell(coords), DoGetDefaultColLabelAttr(), wxT("Invalid attr coords"));
1608                 wxSheetCellAttr attr;
1609                 if (GetTable())
1610                     attr = GetTable()->GetAttr(coords, type);
1611                 if ((type == wxSHEET_AttrAny) && !attr.Ok())
1612                     attr = DoGetDefaultColLabelAttr();
1613 
1614                 return attr;
1615             }
1616             default :
1617             {
1618                 wxFAIL_MSG(wxT("Invalid attr type for col label"));
1619             }
1620         }
1621     }
1622 
1623     // return garbage, maybe they can hobble along for a bit this way
1624     wxFAIL_MSG(wxString::Format(wxT("Unable to get attribute for cell(%d,%d)"), coords.m_row, coords.m_col));
1625     return wxSheetCellAttr(true);
1626 }
1627 
SetAttr(const wxSheetCoords & coords,const wxSheetCellAttr & attr_,wxSheetAttr_Type type)1628 void wxSheet::SetAttr(const wxSheetCoords& coords,
1629                       const wxSheetCellAttr& attr_, wxSheetAttr_Type type)
1630 {
1631     wxCHECK_RET(attr_.Ok(), wxT("Invalid attribute in wxSheet::SetDefaultAttr"));
1632     wxCHECK_RET( type != wxSHEET_AttrAny, wxT("Cannot create attribute of type wxSHEET_AttrAny") );
1633     wxSheetCellAttr attr(attr_);
1634     ClearAttrCache();
1635 
1636     if (IsGridCell(coords))
1637     {
1638         switch (type)
1639         {
1640             case wxSHEET_AttrDefault :
1641             {
1642                 wxSheetCellAttr defAttr(DoGetDefaultGridAttr());
1643                 defAttr.UpdateWith(attr);
1644                 // force good vals after update
1645                 defAttr.SetDefaultAttr(wxNullSheetCellAttr);
1646                 defAttr.SetKind(wxSHEET_AttrDefault);
1647                 return;
1648             }
1649             case wxSHEET_AttrCell :
1650             case wxSHEET_AttrRow  :
1651             case wxSHEET_AttrCol  :
1652             {
1653                 wxCHECK_RET(GetTable() && ContainsGridCell(coords), wxT("Invalid table or attr coords"));
1654                 if (attr.Ok())
1655                     InitAttr(attr, DoGetDefaultGridAttr());
1656 
1657                 GetTable()->SetAttr(coords, attr, type);
1658                 return;
1659             }
1660             default :
1661             {
1662                 wxFAIL_MSG(wxT("Invalid attr type for grid cell"));
1663                 return;
1664             }
1665         }
1666     }
1667     else if (IsCornerLabelCell(coords))
1668     {
1669         // only one attr for the corner window
1670         switch (type)
1671         {
1672             case wxSHEET_AttrDefault :
1673             case wxSHEET_AttrCell    :
1674             {
1675                 wxSheetCellAttr defAttr(DoGetDefaultCornerLabelAttr());
1676                 defAttr.UpdateWith(attr);
1677                 // force good vals after update
1678                 defAttr.SetDefaultAttr(wxNullSheetCellAttr);
1679                 defAttr.SetKind(wxSHEET_AttrDefault);
1680                 return;
1681             }
1682             default :
1683             {
1684                 wxFAIL_MSG(wxT("Invalid attr type for corner label"));
1685                 return;
1686             }
1687         }
1688     }
1689     else if (IsRowLabelCell(coords))
1690     {
1691         switch (type)
1692         {
1693             case wxSHEET_AttrDefault :
1694             {
1695                 wxSheetCellAttr defAttr(DoGetDefaultRowLabelAttr());
1696                 defAttr.UpdateWith(attr);
1697                 // force good vals after update
1698                 defAttr.SetDefaultAttr(wxNullSheetCellAttr);
1699                 defAttr.SetKind(wxSHEET_AttrDefault);
1700                 return;
1701             }
1702             case wxSHEET_AttrCell :
1703             {
1704                 wxCHECK_RET(GetTable() && ContainsRowLabelCell(coords), wxT("Invalid table or attr coords"));
1705                 if (attr.Ok())
1706                     InitAttr(attr, DoGetDefaultRowLabelAttr());
1707 
1708                 GetTable()->SetAttr(coords, attr, type);
1709                 return;
1710             }
1711             default :
1712             {
1713                 wxFAIL_MSG(wxT("Invalid attr type for row label"));
1714                 return;
1715             }
1716         }
1717     }
1718     else if (IsColLabelCell(coords))
1719     {
1720         switch (type)
1721         {
1722             case wxSHEET_AttrDefault :
1723             {
1724                 wxSheetCellAttr defAttr(DoGetDefaultRowLabelAttr());
1725                 defAttr.UpdateWith(attr);
1726                 // force good vals after update
1727                 defAttr.SetDefaultAttr(wxNullSheetCellAttr);
1728                 defAttr.SetKind(wxSHEET_AttrDefault);
1729                 return;
1730             }
1731             case wxSHEET_AttrCell :
1732             {
1733                 wxCHECK_RET(GetTable() && ContainsColLabelCell(coords), wxT("Invalid table or attr coords"));
1734                 if (attr.Ok())
1735                     InitAttr(attr, DoGetDefaultColLabelAttr());
1736 
1737                 GetTable()->SetAttr(coords, attr, type);
1738                 return;
1739             }
1740             default :
1741             {
1742                 wxFAIL_MSG(wxT("Invalid attr type for col label"));
1743                 return;
1744             }
1745         }
1746     }
1747 
1748     wxFAIL_MSG(wxString::Format(wxT("Unable to set attribute for cell(%d,%d)"), coords.m_row, coords.m_col));
1749 }
1750 
InitAttr(wxSheetCellAttr & initAttr,const wxSheetCellAttr & defAttr) const1751 bool wxSheet::InitAttr( wxSheetCellAttr& initAttr, const wxSheetCellAttr& defAttr ) const
1752 {
1753     if (initAttr == defAttr)
1754         return false;
1755 
1756     wxSheetCellAttr attr(initAttr);
1757     wxSheetCellAttr attrDef(attr.GetDefaultAttr());
1758     // only 100000 def attr, should be enough?
1759     for (int n=0; n<100000; n++)
1760     {
1761         if (!attrDef.Ok())
1762         {
1763             attr.SetDefaultAttr(defAttr);
1764             return true;
1765         }
1766         else if (attrDef == defAttr)
1767         {
1768             return false; // already set
1769         }
1770 
1771         attr = attrDef;
1772         attrDef = attr.GetDefaultAttr();
1773     }
1774 
1775     wxFAIL_MSG(wxT("Unable to set default attribute for cell"));
1776     return false;
1777 }
1778 
GetAttrBackgroundColour(const wxSheetCoords & coords,wxSheetAttr_Type type) const1779 const wxColour& wxSheet::GetAttrBackgroundColour( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1780 {
1781     return GetAttr(coords, type).GetBackgroundColour();
1782 }
GetAttrForegoundColour(const wxSheetCoords & coords,wxSheetAttr_Type type) const1783 const wxColour& wxSheet::GetAttrForegoundColour( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1784 {
1785     return GetAttr(coords, type).GetForegroundColour();
1786 }
GetAttrFont(const wxSheetCoords & coords,wxSheetAttr_Type type) const1787 const wxFont& wxSheet::GetAttrFont( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1788 {
1789     return GetAttr(coords, type).GetFont();
1790 }
GetAttrAlignment(const wxSheetCoords & coords,wxSheetAttr_Type type) const1791 int wxSheet::GetAttrAlignment( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1792 {
1793     return GetAttr(coords, type).GetAlignment();
1794 }
GetAttrOrientation(const wxSheetCoords & coords,wxSheetAttr_Type type) const1795 int wxSheet::GetAttrOrientation( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1796 {
1797     return GetAttr(coords, type).GetOrientation();
1798 }
GetAttrLevel(const wxSheetCoords & coords,wxSheetAttr_Type type) const1799 int wxSheet::GetAttrLevel( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1800 {
1801     return GetAttr(coords, type).GetLevel();
1802 }
GetAttrOverflow(const wxSheetCoords & coords,wxSheetAttr_Type type) const1803 bool wxSheet::GetAttrOverflow( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1804 {
1805     return GetAttr(coords, type).GetOverflow();
1806 }
GetAttrOverflowMarker(const wxSheetCoords & coords,wxSheetAttr_Type type) const1807 bool wxSheet::GetAttrOverflowMarker( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1808 {
1809     return GetAttr(coords, type).GetOverflowMarker();
1810 }
GetAttrShowEditor(const wxSheetCoords & coords,wxSheetAttr_Type type) const1811 bool wxSheet::GetAttrShowEditor( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1812 {
1813     return GetAttr(coords, type).GetShowEditor();
1814 }
GetAttrReadOnly(const wxSheetCoords & coords,wxSheetAttr_Type type) const1815 bool wxSheet::GetAttrReadOnly( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1816 {
1817     return GetAttr(coords, type).GetReadOnly();
1818 }
GetAttrRenderer(const wxSheetCoords & coords,wxSheetAttr_Type type) const1819 wxSheetCellRenderer wxSheet::GetAttrRenderer( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1820 {
1821     return GetAttr(coords, type).GetRenderer((wxSheet*)this, coords);
1822 }
GetAttrEditor(const wxSheetCoords & coords,wxSheetAttr_Type type) const1823 wxSheetCellEditor wxSheet::GetAttrEditor( const wxSheetCoords& coords, wxSheetAttr_Type type ) const
1824 {
1825     return GetAttr(coords, type).GetEditor((wxSheet*)this, coords);
1826 }
1827 
SetAttrBackgroundColour(const wxSheetCoords & coords,const wxColour & colour,wxSheetAttr_Type type)1828 void wxSheet::SetAttrBackgroundColour( const wxSheetCoords& coords, const wxColour& colour, wxSheetAttr_Type type )
1829 {
1830     GetOrCreateAttr(coords, type).SetBackgroundColour(colour);
1831     RefreshAttrChange(coords, type);
1832 }
SetAttrForegroundColour(const wxSheetCoords & coords,const wxColour & colour,wxSheetAttr_Type type)1833 void wxSheet::SetAttrForegroundColour( const wxSheetCoords& coords, const wxColour& colour, wxSheetAttr_Type type )
1834 {
1835     GetOrCreateAttr(coords, type).SetForegroundColour(colour);
1836     RefreshAttrChange(coords, type);
1837 }
SetAttrFont(const wxSheetCoords & coords,const wxFont & font,wxSheetAttr_Type type)1838 void wxSheet::SetAttrFont( const wxSheetCoords& coords, const wxFont& font, wxSheetAttr_Type type )
1839 {
1840     GetOrCreateAttr(coords, type).SetFont(font);
1841     RefreshAttrChange(coords, type);
1842 }
SetAttrAlignment(const wxSheetCoords & coords,int align,wxSheetAttr_Type type)1843 void wxSheet::SetAttrAlignment( const wxSheetCoords& coords, int align, wxSheetAttr_Type type )
1844 {
1845     GetOrCreateAttr(coords, type).SetAlignment(align);
1846     RefreshAttrChange(coords, type);
1847 }
SetAttrOrientation(const wxSheetCoords & coords,int orient,wxSheetAttr_Type type)1848 void wxSheet::SetAttrOrientation( const wxSheetCoords& coords, int orient, wxSheetAttr_Type type )
1849 {
1850     GetOrCreateAttr(coords, type).SetOrientation(orient);
1851     RefreshAttrChange(coords, type);
1852 }
SetAttrLevel(const wxSheetCoords & coords,int level,wxSheetAttr_Type type)1853 void wxSheet::SetAttrLevel( const wxSheetCoords& coords, int level, wxSheetAttr_Type type )
1854 {
1855     wxCHECK_RET(type != wxSHEET_AttrDefault, wxT("Unable to change level of default attribute"));
1856     GetOrCreateAttr(coords, type).SetLevel(wxSheetAttrLevel_Type(level));
1857     RefreshAttrChange(coords, type);
1858 }
SetAttrOverflow(const wxSheetCoords & coords,bool allow,wxSheetAttr_Type type)1859 void wxSheet::SetAttrOverflow( const wxSheetCoords& coords, bool allow, wxSheetAttr_Type type )
1860 {
1861     GetOrCreateAttr(coords, type).SetOverflow(allow);
1862     RefreshAttrChange(coords, type);
1863 }
SetAttrOverflowMarker(const wxSheetCoords & coords,bool draw_marker,wxSheetAttr_Type type)1864 void wxSheet::SetAttrOverflowMarker( const wxSheetCoords& coords, bool draw_marker, wxSheetAttr_Type type )
1865 {
1866     GetOrCreateAttr(coords, type).SetOverflowMarker(draw_marker);
1867     RefreshAttrChange(coords, type);
1868 }
SetAttrShowEditor(const wxSheetCoords & coords,bool show_editor,wxSheetAttr_Type type)1869 void wxSheet::SetAttrShowEditor( const wxSheetCoords& coords, bool show_editor, wxSheetAttr_Type type )
1870 {
1871     GetOrCreateAttr(coords, type).SetShowEditor(show_editor);
1872     RefreshAttrChange(coords, type);
1873 }
SetAttrReadOnly(const wxSheetCoords & coords,bool isReadOnly,wxSheetAttr_Type type)1874 void wxSheet::SetAttrReadOnly( const wxSheetCoords& coords, bool isReadOnly, wxSheetAttr_Type type )
1875 {
1876     GetOrCreateAttr(coords, type).SetReadOnly(isReadOnly);
1877     RefreshAttrChange(coords, type);
1878 }
SetAttrRenderer(const wxSheetCoords & coords,const wxSheetCellRenderer & renderer,wxSheetAttr_Type type)1879 void wxSheet::SetAttrRenderer( const wxSheetCoords& coords, const wxSheetCellRenderer& renderer, wxSheetAttr_Type type )
1880 {
1881     GetOrCreateAttr(coords, type).SetRenderer(renderer);
1882     RefreshAttrChange(coords, type);
1883 }
SetAttrEditor(const wxSheetCoords & coords,const wxSheetCellEditor & editor,wxSheetAttr_Type type)1884 void wxSheet::SetAttrEditor( const wxSheetCoords& coords, const wxSheetCellEditor& editor, wxSheetAttr_Type type )
1885 {
1886     GetOrCreateAttr(coords, type).SetEditor(editor);
1887     RefreshAttrChange(coords, type);
1888 }
1889 
1890 // ----------------------------------------------------------------------------
1891 // Get/Set the attributes for individual sheet cells/rows/cols
1892 
SetColFormatFloat(int col,int width,int precision)1893 void wxSheet::SetColFormatFloat(int col, int width, int precision)
1894 {
1895     wxString typeName = wxSHEET_VALUE_FLOAT;
1896     if ( (width != -1) || (precision != -1) )
1897         typeName << _T(':') << width << _T(',') << precision;
1898 
1899     SetColFormatCustom(col, typeName);
1900 }
SetColFormatCustom(int col,const wxString & typeName)1901 void wxSheet::SetColFormatCustom(int col, const wxString& typeName)
1902 {
1903     wxCHECK_RET(ContainsGridCol(col), wxT("Invalid col in wxSheet::SetColFormatCustom"));
1904     wxSheetCellRenderer ren(GetDefaultRendererForType(typeName));
1905     wxCHECK_RET(ren.Ok(), wxT("Invalid renderer in wxSheet::SetColFormatCustom"));
1906     SetAttrRenderer(wxSheetCoords(0,col), ren.Clone(), wxSHEET_AttrCol);
1907 }
1908 
1909 // ----------------------------------------------------------------------------
1910 // Get/Set cell value
1911 
GetCellValue(const wxSheetCoords & coords)1912 wxString wxSheet::GetCellValue( const wxSheetCoords& coords )
1913 {
1914     if ( GetTable() )
1915         return GetTable()->GetValue( coords );
1916 
1917     return wxEmptyString;
1918 }
1919 
SetCellValue(const wxSheetCoords & coords,const wxString & s)1920 void wxSheet::SetCellValue( const wxSheetCoords& coords, const wxString& s )
1921 {
1922     wxCHECK_RET( GetTable(), wxT("Invalid sheet table for SetCellValue"));
1923     GetTable()->SetValue( coords, s );
1924 
1925     // Note: If we are using IsCellEditControlEnabled,
1926     // this interacts badly with calling SetCellValue from
1927     // an EVT_SHEET_CELL_CHANGE handler.
1928     if ((GetEditControlCoords() == coords) && IsCellEditControlShown())
1929     {
1930         HideCellEditControl();
1931         ShowCellEditControl(); // will reread data from table
1932     }
1933     else
1934         RefreshCell(coords, false);
1935 }
1936 
HasCellValue(const wxSheetCoords & coords)1937 bool wxSheet::HasCellValue( const wxSheetCoords& coords )
1938 {
1939     if ( GetTable() )
1940         return GetTable()->HasValue( coords );
1941 
1942     // this tries the table too, but maybe they've only overridden GetCellValue
1943     return !GetCellValue(coords).IsEmpty();
1944 }
1945 
1946 // ----------------------------------------------------------------------------
1947 // Register mapping between data types to Renderers/Editors
1948 
RegisterDataType(const wxString & typeName,const wxSheetCellRenderer & renderer,const wxSheetCellEditor & editor)1949 void wxSheet::RegisterDataType(const wxString& typeName,
1950                                const wxSheetCellRenderer& renderer,
1951                                const wxSheetCellEditor& editor)
1952 {
1953     GetSheetRefData()->m_typeRegistry->RegisterDataType(typeName, renderer, editor);
1954 }
1955 
GetDefaultEditorForType(const wxString & typeName) const1956 wxSheetCellEditor wxSheet::GetDefaultEditorForType(const wxString& typeName) const
1957 {
1958     int index = GetSheetRefData()->m_typeRegistry->FindOrCloneDataType(typeName);
1959     wxCHECK_MSG(index != wxNOT_FOUND, wxNullSheetCellEditor, wxT("Unknown data type name"));
1960 
1961     return GetSheetRefData()->m_typeRegistry->GetEditor(index);
1962 }
GetDefaultRendererForType(const wxString & typeName) const1963 wxSheetCellRenderer wxSheet::GetDefaultRendererForType(const wxString& typeName) const
1964 {
1965     int index = GetSheetRefData()->m_typeRegistry->FindOrCloneDataType(typeName);
1966     wxCHECK_MSG(index != wxNOT_FOUND, wxNullSheetCellRenderer, wxT("Unknown data type name"));
1967 
1968     return GetSheetRefData()->m_typeRegistry->GetRenderer(index);
1969 }
1970 
GetDefaultEditorForCell(const wxSheetCoords & coords) const1971 wxSheetCellEditor wxSheet::GetDefaultEditorForCell(const wxSheetCoords& coords) const
1972 {
1973     wxCHECK_MSG(GetTable(), wxNullSheetCellEditor, wxT("Grid table is not created"));
1974     //return GetDefaultEditorForType(GetTable()->GetTypeName(coords));
1975 
1976     // Default is to NOT use this as a default, but the defAttr's editor
1977     int index = GetSheetRefData()->m_typeRegistry->FindOrCloneDataType(GetTable()->GetTypeName(coords));
1978     if (index == wxNOT_FOUND)
1979         return wxNullSheetCellEditor;
1980 
1981     return GetSheetRefData()->m_typeRegistry->GetEditor(index);
1982 
1983 }
GetDefaultRendererForCell(const wxSheetCoords & coords) const1984 wxSheetCellRenderer wxSheet::GetDefaultRendererForCell(const wxSheetCoords& coords) const
1985 {
1986     if (!GetTable()) return wxNullSheetCellRenderer;
1987     //return GetDefaultRendererForType(GetTable()->GetTypeName(coords));
1988 
1989     // Default is to NOT use this as a default, but the defAttr's renderer
1990     int index = GetSheetRefData()->m_typeRegistry->FindOrCloneDataType(GetTable()->GetTypeName(coords));
1991     if (index == wxNOT_FOUND)
1992         return wxNullSheetCellRenderer;
1993 
1994     return GetSheetRefData()->m_typeRegistry->GetRenderer(index);
1995 }
1996 
1997 // ----------------------------------------------------------------------------
1998 // Cursor movement and visibility functions
1999 
IsCellVisible(const wxSheetCoords & coords,bool wholeCellVisible) const2000 bool wxSheet::IsCellVisible( const wxSheetCoords& coords, bool wholeCellVisible ) const
2001 {
2002     wxCHECK_MSG(ContainsCell(coords), false, wxT("Invalid coords in wxSheet::IsCellVisible"));
2003 
2004     const wxRect clientRect(wxPoint(0,0), GetWindowForCoords(coords)->GetClientSize());
2005     const wxRect devRect(CellToRect(coords, true));  // rect in device coords
2006 
2007     if ( wholeCellVisible )                     // is the whole cell visible?
2008     {
2009         wxRect intersectRect( devRect.Intersect(clientRect) );
2010         return intersectRect == devRect;
2011     }
2012 
2013     return devRect.Intersects(clientRect); // is the cell partly visible ?
2014 }
IsRowVisible(int row,bool wholeRowVisible) const2015 bool wxSheet::IsRowVisible( int row, bool wholeRowVisible ) const
2016 {
2017     if (row == -1)
2018         return m_colLabelWin && m_colLabelWin->IsShown();
2019 
2020     wxCHECK_MSG(ContainsGridRow(row), false, wxT("Invalid row in wxSheet::IsRowVisible"));
2021 
2022     const wxRect clientRect(wxPoint(0,0), m_gridWin->GetClientSize());
2023     const wxRect logRect(0, GetRowTop(row), clientRect.width, GetRowHeight(row));
2024     const wxRect devRect(clientRect.x, CalcScrolledRect(logRect).y, logRect.width, logRect.height);
2025 
2026     if (wholeRowVisible)                  // is the whole row visible?
2027     {
2028         wxRect intersectRect( devRect.Intersect(clientRect) );
2029         return intersectRect == devRect;
2030     }
2031 
2032     return devRect.Intersects(clientRect); // is the cell partly visible ?
2033 }
IsColVisible(int col,bool wholeColVisible) const2034 bool wxSheet::IsColVisible( int col, bool wholeColVisible ) const
2035 {
2036     if (col == -1)
2037         return m_rowLabelWin && m_rowLabelWin->IsShown();
2038 
2039     wxCHECK_MSG(ContainsGridCol(col), false, wxT("Invalid row in wxSheet::IsColVisible"));
2040 
2041     const wxRect clientRect(wxPoint(0,0), m_gridWin->GetClientSize());
2042     const wxRect logRect(GetColLeft(col), 0, GetColWidth(col), clientRect.height);
2043     const wxRect devRect(CalcScrolledRect(logRect).x, clientRect.y, logRect.width, logRect.height);
2044 
2045     if (wholeColVisible)                  // is the whole row visible?
2046     {
2047         wxRect intersectRect( devRect.Intersect(clientRect) );
2048         return intersectRect == devRect;
2049     }
2050 
2051     return devRect.Intersects(clientRect); // is the cell partly visible ?
2052 }
2053 
MakeCellVisible(const wxSheetCoords & coords)2054 void wxSheet::MakeCellVisible( const wxSheetCoords& coords )
2055 {
2056     wxCHECK_RET(ContainsCell(coords), wxT("Invalid coords in wxSheet::MakeCellVisible"));
2057 
2058     if (IsCornerLabelCell(coords))
2059         return;
2060 
2061     int xpos = -1, ypos = -1;
2062 
2063     const wxRect logRect( CellToRect(coords) );        // cell rect in logical coords
2064     const wxRect devRect( CalcScrolledRect(logRect) ); // rect in device coords
2065 
2066     int x0, y0, cw, ch;
2067     m_gridWin->GetClientSize( &cw, &ch );
2068     CalcUnscrolledPosition(0, 0, &x0, &y0);
2069 
2070     if (!IsColLabelCell(coords))
2071     {
2072         if ( devRect.GetTop() < 0 )
2073         {
2074             ypos = logRect.GetTop();
2075             ypos -= SHEET_SCROLL_LINE_Y;  // add a little padding
2076             if (ypos < 0) ypos = 0;
2077         }
2078         else if ( devRect.GetBottom() > ch )
2079         {
2080             ypos = y0 + (devRect.GetBottom() - ch);
2081             ypos += SHEET_SCROLL_LINE_Y;
2082             if (ypos < 0) ypos = 0;
2083         }
2084     }
2085 
2086     if (!IsRowLabelCell(coords))
2087     {
2088         if ( devRect.GetLeft() < 0 )
2089         {
2090             xpos = logRect.GetLeft();
2091             xpos -= SHEET_SCROLL_LINE_X;
2092             if (xpos < 0) xpos = 0;
2093         }
2094         else if ( devRect.GetRight() > cw )
2095         {
2096             xpos = x0 + (devRect.GetRight() - cw);
2097             xpos += SHEET_SCROLL_LINE_X;
2098             if (xpos < 0) xpos = 0;
2099         }
2100 
2101     }
2102 
2103     // -1 means leave that postion the same, see if (x/ypos < 0) x/ypos = 0 above
2104     if ((xpos != -1) || (ypos != -1))
2105         SetGridOrigin( xpos, ypos, true, true );
2106 }
2107 
SetGridCursorCell(const wxSheetCoords & coords)2108 void wxSheet::SetGridCursorCell( const wxSheetCoords& coords )
2109 {
2110     // Note: can set to invalid coords < 0 to hide cursor, but not out of range
2111     wxCHECK_RET((coords.GetRow() < GetNumberRows()) &&
2112                 (coords.GetCol() < GetNumberCols()),
2113                 wxT("Invalid coords in wxSheet::SetGridCursorCell"));
2114 
2115     if (GetGridCursorCell() == coords)
2116         return;
2117 
2118     m_waitForSlowClick = false;
2119 
2120     // the event has been intercepted - do nothing
2121     if (SendEvent(wxEVT_SHEET_SELECTING_CELL, coords) != EVT_SKIPPED)
2122         return;
2123 
2124     const wxSheetCoords oldCursorCell(GetGridCursorCell());
2125     if ( ContainsGridCell(oldCursorCell) )
2126     {
2127         if (IsCellEditControlCreated())
2128             DisableCellEditControl(true);
2129 
2130         // Otherwise refresh redraws the highlight!
2131         GetSheetRefData()->m_cursorCoords = coords;
2132         RefreshCell(oldCursorCell, true);
2133     }
2134 
2135     GetSheetRefData()->m_cursorCoords = coords;
2136 
2137     if (ContainsGridCell(GetGridCursorCell()))
2138         RefreshCell(GetGridCursorCell(), true);
2139 
2140     SendEvent(wxEVT_SHEET_SELECTED_CELL, coords);
2141 }
2142 
DoMoveCursor(const wxSheetCoords & relCoords,bool expandSelection)2143 bool wxSheet::DoMoveCursor( const wxSheetCoords& relCoords, bool expandSelection )
2144 {
2145     const wxSheetCoords cursorCoords(GetGridCursorCell());
2146     const wxSheetCoords coords( cursorCoords + relCoords );
2147     if ( (relCoords == wxSheetCoords(0,0)) ||
2148          !ContainsGridCell(cursorCoords) || !ContainsGridCell(coords) )
2149         return false;
2150 
2151     if ( expandSelection)
2152     {
2153         m_keySelecting = true;
2154         if ( !ContainsGridCell(GetSelectingAnchor()) )
2155             SetSelectingAnchor(cursorCoords);
2156     }
2157     else
2158         ClearSelection(true);
2159 
2160     MakeCellVisible( coords );
2161     SetGridCursorCell( coords );
2162     if (expandSelection && !HasSelectionMode(wxSHEET_SelectNone))
2163         HighlightSelectingBlock( GetSelectingAnchor(), GetGridCursorCell() );
2164 
2165     return true;
2166 }
2167 
DoMoveCursorBlock(const wxSheetCoords & relDir,bool expandSelection)2168 bool wxSheet::DoMoveCursorBlock( const wxSheetCoords& relDir, bool expandSelection )
2169 {
2170     const wxSheetCoords cursorCoords(GetGridCursorCell());
2171     wxSheetCoords coords(cursorCoords + relDir);
2172     if ( !ContainsGridCell(cursorCoords) || !ContainsGridCell(coords) )
2173         return false;
2174 
2175     if ( !HasCellValue(cursorCoords) )
2176     {
2177         // starting in an empty cell: find the next block of non-empty cells
2178         while ( ContainsGridCell(coords) )
2179         {
2180             if (HasCellValue(coords) || !ContainsGridCell(coords + relDir))
2181                 break;
2182 
2183             coords += relDir;
2184         }
2185     }
2186     else if ( !HasCellValue(coords) )
2187     {
2188         // starting at the edge of a block: find the next block
2189         while ( ContainsGridCell(coords + relDir) )
2190         {
2191             coords += relDir;
2192             if ( HasCellValue(coords) )
2193                 break;
2194         }
2195     }
2196     else
2197     {
2198         // starting within a block with value: find the edge of the block
2199         while ( ContainsGridCell(coords + relDir) )
2200         {
2201             if ( !HasCellValue(coords + relDir) )
2202                 break;
2203 
2204             coords += relDir;
2205         }
2206     }
2207 
2208     return DoMoveCursor(coords-cursorCoords, expandSelection);
2209 }
2210 
DoMoveCursorUpDownPage(bool page_up,bool expandSelection)2211 bool wxSheet::DoMoveCursorUpDownPage( bool page_up, bool expandSelection )
2212 {
2213     if (!ContainsGridCell(GetGridCursorCell()))
2214         return false;
2215 
2216     const int row = GetGridCursorRow();
2217 
2218     if ((page_up && (row < 1)) || (!page_up && (row >= GetNumberRows() - 1)))
2219         return false;
2220 
2221     int cw, ch;
2222     m_gridWin->GetClientSize( &cw, &ch );
2223 
2224     int y = GetRowTop(row) + (page_up ? (-ch + 1) : ch);
2225     const int newRow = YToGridRow( y, true );
2226     return DoMoveCursor(wxSheetCoords(newRow - row, 0), expandSelection);
2227 }
2228 
2229 // ----------------------------------------------------------------------------
2230 // Cell/Row/Col selection and deselection
2231 
HasSelection(bool selecting) const2232 bool wxSheet::HasSelection(bool selecting) const
2233 {
2234     if (selecting)
2235         return (!GetSelectingBlock().IsEmpty() ||
2236                 (GetSelection() && GetSelection()->HasSelection()));
2237 
2238     return GetSelection() && GetSelection()->HasSelection();
2239 }
IsCellSelected(const wxSheetCoords & coords) const2240 bool wxSheet::IsCellSelected( const wxSheetCoords& coords ) const
2241 {
2242     return ( GetSelectingBlock().Contains(coords) ||
2243             (GetSelection() && GetSelection()->Contains(coords)) );
2244 }
IsRowSelected(int row) const2245 bool wxSheet::IsRowSelected( int row ) const
2246 {
2247     wxSheetBlock rowBlock(row, 0, 1, GetNumberCols()); // not +1
2248     return ( GetSelectingBlock().Contains(rowBlock) ||
2249             (GetSelection() && GetSelection()->Contains(rowBlock)) );
2250 }
IsColSelected(int col) const2251 bool wxSheet::IsColSelected( int col ) const
2252 {
2253     wxSheetBlock colBlock(0, col, GetNumberRows(), 1); // not +1
2254     return ( GetSelectingBlock().Contains(colBlock) ||
2255             (GetSelection() && GetSelection()->Contains(colBlock)) );
2256 }
2257 
SetSelectionMode(wxSheetSelectionMode_Type selmode)2258 void wxSheet::SetSelectionMode(wxSheetSelectionMode_Type selmode)
2259 {
2260     wxCHECK_RET( GetSelection(),
2261                  wxT("Called wxSheet::SetSelectionMode() before calling CreateGrid()") );
2262 
2263     // yes we could patch up the selection, but does it really make sense?
2264     // like you'd have to send a slew of confusing (de)selection events
2265     ClearSelection(true);
2266     GetSheetRefData()->m_selectionMode = selmode;
2267 }
2268 
SelectRow(int row,bool addToSelected,bool sendEvt)2269 bool wxSheet::SelectRow( int row, bool addToSelected, bool sendEvt )
2270 {
2271     if (HasSelectionMode(wxSHEET_SelectCols))
2272         return false;
2273 
2274     // selection to # of cols means whole row is selected
2275     return SelectBlock( wxSheetBlock(row, 0, 1, GetNumberCols()+1),
2276                         addToSelected, sendEvt );
2277 }
SelectRows(int rowTop,int rowBottom,bool addToSelected,bool sendEvt)2278 bool wxSheet::SelectRows( int rowTop, int rowBottom, bool addToSelected, bool sendEvt )
2279 {
2280     if (HasSelectionMode(wxSHEET_SelectCols) || (rowTop > rowBottom))
2281         return false;
2282 
2283     // selection to # of cols means whole row is selected
2284     return SelectBlock( wxSheetBlock(rowTop, 0, rowBottom-rowTop+1, GetNumberCols()+1),
2285                         addToSelected, sendEvt );
2286 }
SelectCol(int col,bool addToSelected,bool sendEvt)2287 bool wxSheet::SelectCol( int col, bool addToSelected, bool sendEvt )
2288 {
2289     if (HasSelectionMode(wxSHEET_SelectRows))
2290         return false;
2291 
2292     // selection to # of rows means whole col is selected
2293     return SelectBlock( wxSheetBlock(0, col, GetNumberRows()+1, 1),
2294                         addToSelected, sendEvt );
2295 }
SelectCols(int colLeft,int colRight,bool addToSelected,bool sendEvt)2296 bool wxSheet::SelectCols( int colLeft, int colRight, bool addToSelected, bool sendEvt )
2297 {
2298     if (HasSelectionMode(wxSHEET_SelectRows) || (colLeft > colRight))
2299         return false;
2300 
2301     // selection to # of rows means whole col is selected
2302     return SelectBlock( wxSheetBlock(0, colLeft, GetNumberRows()+1, colRight-colLeft+1),
2303                         addToSelected, sendEvt );
2304 }
SelectCell(const wxSheetCoords & coords,bool addToSelected,bool sendEvt)2305 bool wxSheet::SelectCell( const wxSheetCoords& coords, bool addToSelected, bool sendEvt )
2306 {
2307     return SelectBlock(wxSheetBlock(coords, 1, 1), addToSelected, sendEvt);
2308 }
SelectBlock(const wxSheetBlock & block,bool addToSelected,bool sendEvt)2309 bool wxSheet::SelectBlock( const wxSheetBlock& block, bool addToSelected, bool sendEvt )
2310 {
2311     if (!GetSelection())
2312         return false;
2313 
2314     if ( !addToSelected )
2315         ClearSelection(sendEvt);
2316 
2317     wxArraySheetBlock addedBlocks;
2318     if (!GetSelection()->SelectBlock( block, true, &addedBlocks ))
2319         return false;
2320 
2321     if (!GetBatchCount())
2322     {
2323         wxSheetBlock bounds;
2324 
2325         for (size_t n=0; n<addedBlocks.GetCount(); n++)
2326             bounds = bounds.ExpandUnion(addedBlocks[n]);
2327 
2328         RefreshGridCellBlock(bounds);
2329     }
2330 
2331     if (sendEvt)
2332         SendRangeEvent(wxEVT_SHEET_RANGE_SELECTED, block, true, addToSelected);
2333 
2334     return true;
2335 }
2336 
SelectAll(bool sendEvt)2337 bool wxSheet::SelectAll(bool sendEvt)
2338 {
2339     BeginBatch();
2340     ClearSelection(false); // clear old and only have new, no event/refresh
2341     EndBatch(false);
2342     // select to # rows/col means everything everywhere is selected
2343     return SelectBlock(wxSheetBlock(0, 0, GetNumberRows(), GetNumberCols()),
2344                        false, sendEvt);
2345 }
2346 
DeselectRow(int row,bool sendEvt)2347 bool wxSheet::DeselectRow( int row, bool sendEvt )
2348 {
2349     if (HasSelectionMode(wxSHEET_SelectCols))
2350         return false;
2351 
2352     // deselection to # of cols means whole row is deselected
2353     return DeselectBlock( wxSheetBlock(row, 0, 1, GetNumberCols()), sendEvt );
2354 }
DeselectRows(int rowTop,int rowBottom,bool sendEvt)2355 bool wxSheet::DeselectRows( int rowTop, int rowBottom, bool sendEvt )
2356 {
2357     if (HasSelectionMode(wxSHEET_SelectCols) || (rowTop > rowBottom))
2358         return false;
2359 
2360     // selection to # of cols means whole row is deselected
2361     return DeselectBlock( wxSheetBlock(rowTop, 0, rowBottom-rowTop+1, GetNumberCols()+1),
2362                           sendEvt );
2363 }
DeselectCol(int col,bool sendEvt)2364 bool wxSheet::DeselectCol( int col, bool sendEvt )
2365 {
2366     if (HasSelectionMode(wxSHEET_SelectRows))
2367         return false;
2368 
2369     // deselection to # of rows means whole col is deselected
2370     return DeselectBlock( wxSheetBlock(0, col, GetNumberRows(), 1), sendEvt );
2371 }
DeselectCols(int colLeft,int colRight,bool sendEvt)2372 bool wxSheet::DeselectCols( int colLeft, int colRight, bool sendEvt )
2373 {
2374     if (HasSelectionMode(wxSHEET_SelectRows) || (colLeft > colRight))
2375         return false;
2376 
2377     // selection to # of rows means whole col is deselected
2378     return DeselectBlock( wxSheetBlock(0, colLeft, GetNumberRows()+1, colRight-colLeft+1),
2379                           sendEvt );
2380 }
DeselectCell(const wxSheetCoords & coords,bool sendEvt)2381 bool wxSheet::DeselectCell( const wxSheetCoords& coords, bool sendEvt )
2382 {
2383     return DeselectBlock(wxSheetBlock(coords, 1, 1), sendEvt);
2384 }
2385 
DeselectBlock(const wxSheetBlock & block,bool sendEvt)2386 bool wxSheet::DeselectBlock( const wxSheetBlock& block, bool sendEvt )
2387 {
2388     if (!GetSelection())
2389         return false;
2390 
2391     // do they want to clear the whole grid
2392     wxSheetBlock gridBlock(0, 0, GetNumberRows(), GetNumberCols());
2393     if (gridBlock.Intersect(block) == gridBlock)
2394         return ClearSelection(sendEvt);
2395 
2396     wxArraySheetBlock deletedBlocks;
2397     if (!GetSelection()->DeselectBlock( block, true, &deletedBlocks ))
2398         return false;
2399 
2400     if (!GetBatchCount())
2401     {
2402         wxSheetBlock bounds;
2403 
2404         for (size_t n=0; n<deletedBlocks.GetCount(); n++)
2405             bounds = bounds.ExpandUnion(deletedBlocks[n]);
2406 
2407         RefreshGridCellBlock(bounds);
2408     }
2409 
2410     if (sendEvt)
2411         SendRangeEvent(wxEVT_SHEET_RANGE_SELECTED, block, false, false);
2412 
2413     return true;
2414 }
2415 
ClearSelection(bool send_event)2416 bool wxSheet::ClearSelection(bool send_event)
2417 {
2418     if ( !HasSelection() )
2419         return false;
2420 
2421     // no evt for clearing m_selectingBlock, it's not a real selection
2422     if ( !HasSelection(false) )
2423         send_event = false;
2424 
2425     wxSheetBlock bounds(GetSelectingBlock().ExpandUnion(GetSelection()->GetBoundingBlock()));
2426     SetSelectingBlock(wxNullSheetBlock);
2427     GetSelection()->Clear();
2428 
2429     RefreshGridCellBlock(bounds);
2430 
2431     if (send_event)
2432     {
2433         // One deselection event, indicating deselection of _all_ cells.
2434         SendRangeEvent( wxEVT_SHEET_RANGE_SELECTED,
2435                         wxSheetBlock(0, 0, GetNumberRows(), GetNumberCols()),
2436                         false, false );
2437     }
2438 
2439     return true;
2440 }
2441 
ToggleCellSelection(const wxSheetCoords & coords,bool addToSelected,bool sendEvt)2442 bool wxSheet::ToggleCellSelection( const wxSheetCoords& coords, bool addToSelected, bool sendEvt )
2443 {
2444     int sel = IsCellSelected(coords);
2445     if (!sel)
2446         return SelectCell(coords, addToSelected, sendEvt);
2447 
2448     return DeselectCell(coords, sendEvt );
2449 }
ToggleRowSelection(int row,bool addToSelected,bool sendEvt)2450 bool wxSheet::ToggleRowSelection( int row, bool addToSelected, bool sendEvt )
2451 {
2452     int sel = IsRowSelected(row);
2453     if (!sel)
2454         return SelectRow(row, addToSelected, sendEvt);
2455 
2456     return DeselectRow(row, sendEvt );
2457 }
ToggleColSelection(int col,bool addToSelected,bool sendEvt)2458 bool wxSheet::ToggleColSelection( int col, bool addToSelected, bool sendEvt )
2459 {
2460     int sel = IsColSelected(col);
2461     if (!sel)
2462         return SelectCol(col, addToSelected, sendEvt);
2463 
2464     return DeselectCol(col, sendEvt );
2465 }
2466 
2467 // ----------------------------------------------------------------------------
2468 // Copy/Paste
2469 
2470 #include "wx/dataobj.h"
2471 
2472 //#define wxDF_wxSHEET (wxDF_MAX+1001)  // works w/ GTK 1.2 non unicode
2473 const wxChar* wxDF_wxSHEET = wxT("wxDF_wxSHEET");
2474 
2475 class wxSheetDataObject : public wxDataObjectSimple
2476 {
2477 public:
wxSheetDataObject()2478     wxSheetDataObject() : wxDataObjectSimple(wxDF_wxSHEET) {}
wxSheetDataObject(const wxSheetValueProviderSparseString & values)2479     wxSheetDataObject(const wxSheetValueProviderSparseString &values)
2480         : wxDataObjectSimple(wxDF_wxSHEET), m_values(values)
2481         {
2482             CreateDataString();
2483             m_values.ClearValues();
2484         }
2485 
GetDataSize() const2486     virtual size_t GetDataSize() const { return (m_data.Len()+1)*sizeof(wxChar); }
2487     virtual bool GetDataHere(void *buf) const;
2488     virtual bool SetData(size_t len, const void *buf);
2489 
GetValues() const2490     const wxSheetValueProviderSparseString& GetValues() const { return m_values; }
2491 
2492     void CreateDataString();
2493 
2494 protected:
2495     wxSheetValueProviderSparseString m_values;
2496     wxString m_data;
2497 private:
2498     // I don't understand these? from include/wx/gtk/dataobj2.h
2499     // virtual function hiding supression
GetDataSize(const wxDataFormat & format) const2500     size_t GetDataSize(const wxDataFormat& format) const
2501         { return(wxDataObjectSimple::GetDataSize(format)); }
GetDataHere(const wxDataFormat & format,void * pBuf) const2502     bool GetDataHere(const wxDataFormat& format, void* pBuf) const
2503         { return(wxDataObjectSimple::GetDataHere(format, pBuf)); }
SetData(const wxDataFormat & format,size_t nLen,const void * pBuf)2504     bool SetData(const wxDataFormat& format, size_t nLen, const void* pBuf)
2505         { return(wxDataObjectSimple::SetData(format, nLen, pBuf)); }
2506 };
2507 
2508 // FIXME! this is just a hack and kinda works
2509 const wxString s_nextRow(wxT("\1\1"), 2); // just increment row number
2510 const wxString s_nextCol(wxT("\1\2"), 2); // end of value marker
2511 const wxString s_newRow(wxT("\1\3"), 2);  // specify a row number
2512 const wxString s_newCol(wxT("\1\4"), 2);  // specify a col number
2513 
2514 //const wxString s_nextRow(wxT("s")); // just increment row number
2515 //const wxString s_nextCol(wxT("d")); // end of value marker
2516 //const wxString s_newRow(wxT("r"));  // specify a row number
2517 //const wxString s_newCol(wxT("c"));  // specify a col number
2518 // Data string looks like this: `r23c12:size:some data string`
2519 //  'r' followed by a number specifies a row #
2520 //      without a number means next row
2521 //  'c' followed by a number specifies a col #
2522 //      without a number means next col
2523 //  size is the number of chars of the data string
2524 
2525 // Data string looks like this: start by specifying the row and col number
2526 // then data and end data marker (s_nextCol) more data, if you skip cols
2527 // then specify the col number with s_newCol.
2528 // [s_newRow<row>:][s_newCol<col>:]hello[s_nextCol]text[s_nextCol][s_newCol<colNum>:]More[s_nextCol]
2529 
CreateDataString()2530 void wxSheetDataObject::CreateDataString()
2531 {
2532     m_data.Clear();
2533     wxPairArrayIntPairArraySheetString &values = m_values.GetData();
2534     m_values.RemoveEmpty();
2535 
2536     if (values.GetCount() == 0u)
2537         return;
2538 
2539     int c, ncols, r, nrows = values.GetCount();
2540     int row, col;
2541     int last_row = values.GetItemKey(0) - 10, last_col;
2542 
2543     for (r=0; r<nrows; r++)
2544     {
2545         row = values.GetItemKey(r);
2546         if (last_row+1 < row)
2547             m_data += s_newRow + wxString::Format(wxT("%d:"), row);
2548         else
2549             m_data += s_nextRow;
2550 
2551         col = values.GetItemValue(r).GetItemKey(0);
2552         m_data += s_newCol + wxString::Format(wxT("%d:"), col);
2553         last_col = col;
2554 
2555         ncols = values.GetItemValue(r).GetCount();
2556         for (c=0; c<ncols; c++)
2557         {
2558             col = values.GetItemValue(r).GetItemKey(c);
2559             if (last_col+1 < col)
2560                 m_data += s_newCol + wxString::Format(wxT("%d:"), col);
2561 
2562             m_data += values.GetItemValue(r).GetItemValue(c) + s_nextCol;
2563             last_col = col;
2564         }
2565     }
2566 }
2567 
GetDataHere(void * buf) const2568 bool wxSheetDataObject::GetDataHere(void *buf) const
2569 {
2570     size_t len = GetDataSize();
2571     memset( (char *)buf, 0, len );
2572     memcpy( buf, m_data.c_str(), len );
2573     return true;
2574 }
2575 
SetData(size_t len,const void * buf)2576 bool wxSheetDataObject::SetData(size_t len, const void *buf)
2577 {
2578     wxPairArrayIntPairArraySheetString &values = m_values.GetData();
2579     values.Clear();
2580     m_data.Clear();
2581 
2582     if (len < 2u)
2583         return false; // I guess?
2584 
2585 #if wxCHECK_VERSION(2, 9, 0)
2586     wxString strBuf((const wchar_t*) wxConvertMB2WX((const char *)buf), len); // probably not Unicode safe
2587 #else
2588     wxString strBuf(wxConvertMB2WX((const char *)buf), len); // probably not Unicode safe
2589 #endif
2590     m_data = strBuf;
2591 
2592     //wxPrintf(wxT("Data len %d %d\n"), m_data.Len(), len);
2593 
2594     const wxChar *c = strBuf.GetData();
2595     size_t n, last_n = 0, str_len = strBuf.Len();
2596     int row=0, col=0;
2597     long long_val = 0;
2598 
2599     for ( n=0; n < str_len-1; n++, c++ )
2600     {
2601         // The next row, increment row counter
2602         if ( (*c == s_nextRow[0]) && (*(c+1) == s_nextRow[1]) ) // s_nextRow
2603         {
2604             //wxPrintf(wxT("Next row\n"));
2605             c++; n++; last_n = n+1;
2606             row++;
2607         }
2608         // a new row, read new row number
2609         else if ( (*c == s_newRow[0]) && (*(c+1) == s_newRow[1]) ) // s_newRow
2610         {
2611             c++; n++; last_n = n+1;
2612             for ( ; n < str_len-1; n++, c++ ) { if (*c == wxT(':')) break; }
2613 
2614             wxString strVal(strBuf.Mid(last_n, n-last_n));
2615             //wxPrintf(wxT("Getting rownum %d %d '%s'\n"), last_n, n, strVal.c_str());
2616             if ( strVal.ToLong(&long_val) )
2617                 row = int(long_val);
2618             else
2619                 return false;
2620 
2621             last_n = n+1;
2622         }
2623         // read the next col value
2624         else if ( (*c == s_nextCol[0]) && (*(c+1) == s_nextCol[1]) ) // s_nextCol
2625         {
2626             wxString strVal(strBuf.Mid(last_n, n-last_n));
2627             values.GetOrCreateValue(row).SetValue(col, strVal);
2628             //wxPrintf(wxT("Next col val %d '%s'\n"), strVal.Len(), strVal.c_str());
2629             c++; n++; last_n = n+1;
2630             col++;
2631         }
2632         // a some skipped cols, read new col number and value
2633         else if ( (*c == s_newCol[0]) && (*(c+1) == s_newCol[1]) ) // s_newCol
2634         {
2635             c++; n++; last_n = n+1;
2636             for ( ; n < str_len-1; n++, c++ ) { if (*c == wxT(':')) break; }
2637 
2638             wxString strVal(strBuf.Mid(last_n, n-last_n));
2639             //wxPrintf(wxT("Getting colnum %d %d '%s'\n"), last_n, n, strVal.c_str());
2640             if ( strVal.ToLong(&long_val) )
2641                 col = int(long_val);
2642             else
2643                 return false;
2644 
2645             last_n = n+1;
2646         }
2647     }
2648 
2649     //wxPrintf(wxT("Got some data %d %d\n"), len, m_values.GetCount());
2650     return true;
2651 }
2652 
CopyInternalSelectionToClipboard(const wxChar & colSep)2653 bool wxSheet::CopyInternalSelectionToClipboard(const wxChar& colSep)
2654 {
2655     if (!wxTheClipboard->Open())
2656         return false;
2657 
2658     // save data to clipboard, we want the internal data back if possible
2659     wxDataObjectComposite *data = new wxDataObjectComposite;
2660     data->Add(new wxSheetDataObject(GetSheetRefData()->m_copiedData), true);
2661     data->Add(new wxTextDataObject(CopyInternalSelectionToString(colSep)), false);
2662     wxTheClipboard->SetData( data );
2663 
2664     wxTheClipboard->Close();
2665     return true;
2666 }
2667 
CopyCurrentSelectionToClipboard(bool copy_cursor,const wxChar & colSep)2668 bool wxSheet::CopyCurrentSelectionToClipboard(bool copy_cursor, const wxChar& colSep)
2669 {
2670     CopyCurrentSelectionInternal(copy_cursor);
2671     return CopyInternalSelectionToClipboard(colSep);
2672 }
2673 
PasteFromClipboard(const wxSheetCoords & topLeft,const wxChar & colSep)2674 bool wxSheet::PasteFromClipboard( const wxSheetCoords &topLeft,
2675                                   const wxChar& colSep )
2676 {
2677     if ( !wxTheClipboard->Open() )
2678         return false;
2679 
2680     bool ret = false;
2681 
2682     wxSheetDataObject sheetData;
2683     if (wxTheClipboard->IsSupported(wxDF_wxSHEET) && wxTheClipboard->GetData(sheetData))
2684     {
2685         GetSheetRefData()->m_copiedData = sheetData.GetValues();
2686         ret = PasteInternalCopiedSelection( topLeft );
2687     }
2688     else if (wxTheClipboard->IsSupported(wxDF_TEXT))
2689     {
2690         wxTextDataObject textData;
2691         if (wxTheClipboard->GetData(textData))
2692         {
2693             CopyStringToSelection(textData.GetText(), colSep);
2694             ret = PasteInternalCopiedSelection( topLeft );
2695         }
2696     }
2697 
2698     wxTheClipboard->Close();
2699     return ret;
2700 }
2701 
CopyCurrentSelectionInternal(bool copy_cursor)2702 bool wxSheet::CopyCurrentSelectionInternal(bool copy_cursor)
2703 {
2704     wxPairArrayIntPairArraySheetString &copiedData = GetSheetRefData()->m_copiedData.GetData();
2705     copiedData.Clear();
2706 
2707     wxSheetSelection sel = *GetSelection();
2708     if (!GetSelectingBlock().IsEmpty())
2709         sel.SelectBlock(GetSelectingBlock());
2710 
2711     sel.SetBoundingBlock(wxSheetBlock(0, 0, GetNumberRows(), GetNumberCols()));
2712 
2713     if (!sel.HasSelection())
2714     {
2715         if (copy_cursor && ContainsGridCell(GetGridCursorCell()))
2716             sel.SelectBlock(wxSheetBlock(GetGridCursorCell(), 1, 1));
2717         else
2718             return false;
2719     }
2720 
2721     wxSheetSelectionIterator selIter(sel);
2722     wxSheetCoords cell;
2723     while (selIter.GetNext(cell))
2724         copiedData.GetOrCreateValue(cell.m_row).SetValue(cell.m_col, GetCellValue(cell));
2725 
2726     return copiedData.GetCount() > 0;
2727 }
2728 
CopyInternalSelectionToString(const wxChar & colSep)2729 wxString wxSheet::CopyInternalSelectionToString(const wxChar& colSep)
2730 {
2731     wxPairArrayIntPairArraySheetString &copiedData = GetSheetRefData()->m_copiedData.GetData();
2732     wxString value;
2733     GetSheetRefData()->m_copiedData.RemoveEmpty();
2734     if (copiedData.GetCount() == 0)
2735         return value;
2736 
2737     int i, r, c;
2738     int row, col;
2739     int nrows = copiedData.GetCount();
2740 
2741     // find leftmost col
2742     int left_col = copiedData.GetItemValue(0).GetItemKey(0);
2743     for (r=1; r<nrows; r++)
2744     {
2745         if (left_col > copiedData.GetItemValue(r).GetItemKey(0))
2746             left_col = copiedData.GetItemValue(r).GetItemKey(0);
2747     }
2748 
2749     int last_row=copiedData.GetItemKey(0), last_col=left_col;
2750     for (r=0; r<nrows; r++)
2751     {
2752         row = copiedData.GetItemKey(r);
2753         int ncols = copiedData.GetItemValue(r).GetCount();
2754         for (i = last_row; i<row; i++)
2755             value += wxT("\n");
2756 
2757         last_col = left_col;
2758 
2759         for (c=0; c<ncols; c++)
2760         {
2761             col = copiedData.GetItemValue(r).GetItemKey(c);
2762             for (i = last_col; i<col; i++)
2763                 value += colSep;
2764 
2765             value += copiedData.GetItemValue(r).GetItemValue(c);
2766             last_col = col;
2767         }
2768         last_row = row;
2769     }
2770 
2771     return value;
2772 }
2773 
CopyStringToSelection(const wxString & string,const wxChar & colSep)2774 bool wxSheet::CopyStringToSelection(const wxString &string, const wxChar& colSep)
2775 {
2776     wxPairArrayIntPairArraySheetString &copiedData = GetSheetRefData()->m_copiedData.GetData();
2777     copiedData.Clear();
2778 
2779     if (string.IsEmpty())
2780         return false;
2781 
2782     const wxChar *c = string.GetData();
2783     int n, len = string.Length();
2784 
2785     int row = 0, col = 0;
2786     wxString buf;
2787 
2788     for (n=0; n<len; n++, c++)
2789     {
2790         if (((*c) == wxT('\r')) || ((*c) == wxT('\n')))
2791         {
2792             // check for DOS's \r\n and discard the trailing \n
2793             if (((*c) == wxT('\r')) && (n < len - 1) && (c[1] == wxT('\n')))
2794             {
2795                 c++;
2796                 n++;
2797             }
2798 
2799             copiedData.GetOrCreateValue(row).SetValue(col, buf);
2800             col = 0;
2801             row++;
2802             buf.Empty();
2803         }
2804         else if ((*c) == colSep)
2805         {
2806             copiedData.GetOrCreateValue(row).SetValue(col, buf);
2807             col++;
2808             buf.Empty();
2809         }
2810         else
2811             buf += *c;
2812     }
2813 
2814     if (!buf.IsEmpty())
2815         copiedData.GetOrCreateValue(row).SetValue(col, buf);
2816 
2817     return true;
2818 }
2819 
PasteInternalCopiedSelection(const wxSheetCoords & topLeft_)2820 bool wxSheet::PasteInternalCopiedSelection(const wxSheetCoords &topLeft_)
2821 {
2822     wxPairArrayIntPairArraySheetString &copiedData = GetSheetRefData()->m_copiedData.GetData();
2823     // clean up, shouldn't happen but don't want to have to check it later
2824     GetSheetRefData()->m_copiedData.RemoveEmpty();
2825     if (copiedData.GetCount() == 0) return false;
2826 
2827     wxSheetBlock currentBlock = GetSelection()->GetBoundingBlock().ExpandUnion(GetSelectingBlock());
2828     bool is_selection = !currentBlock.IsEmpty();
2829 
2830     wxSheetCoords topLeft(topLeft_);
2831     if (topLeft == wxNullSheetCoords)
2832     {
2833         if (is_selection)
2834             topLeft = currentBlock.GetLeftTop();
2835         else
2836             topLeft = GetGridCursorCell();
2837     }
2838 
2839     if (!ContainsGridCell(topLeft))
2840         return false;
2841 
2842     wxSheetCoords copiedTopLeft( copiedData.GetItemKey(0),
2843                                  copiedData.GetItemValue(0).GetItemKey(0) );
2844 
2845     wxSheetCoords shiftCell = topLeft - copiedTopLeft;
2846 
2847     GetSheetRefData()->m_pasting = true;
2848 
2849     wxSheetCoords cell, pasteCell;
2850     size_t r, nrows = copiedData.GetCount();
2851     size_t c, ncols;
2852     for (r=0; r<nrows; r++)
2853     {
2854         cell.m_row = copiedData.GetItemKey(r);
2855         ncols = copiedData.GetItemValue(r).GetCount();
2856 
2857         // all done
2858         if (cell.m_row+shiftCell.m_row >= GetNumberRows())
2859             break;
2860 
2861         for (c=0; c<ncols; c++)
2862         {
2863             cell.m_col = copiedData.GetItemValue(r).GetItemKey(c);
2864             pasteCell = cell + shiftCell;
2865             if (pasteCell.m_col >= GetNumberCols())
2866                 break;
2867 
2868             const wxString& value = copiedData.GetItemValue(r).GetItemValue(c);
2869             // only paste into selected cells if selection
2870             if (is_selection && IsCellSelected(pasteCell))
2871                 SetCellValue(pasteCell, value);
2872             else
2873                 SetCellValue(pasteCell, value);
2874         }
2875     }
2876 
2877     GetSheetRefData()->m_pasting = false;
2878     return true;
2879 }
2880 
2881 // ----------------------------------------------------------------------------
2882 // Edit control functions (mostly used internally)
2883 
EnableEditing(bool edit)2884 void wxSheet::EnableEditing( bool edit )
2885 {
2886     // TODO: improve this ?
2887     if ( edit != IsEditable() )
2888     {
2889         if (!edit && IsCellEditControlCreated())
2890             DisableCellEditControl(true);
2891 
2892         GetSheetRefData()->m_editable = edit;
2893     }
2894 }
2895 
EnableCellEditControl(const wxSheetCoords & coords_)2896 bool wxSheet::EnableCellEditControl( const wxSheetCoords& coords_ )
2897 {
2898     // move to owner cell since that's where the editor is
2899     const wxSheetCoords coords(GetCellOwner(coords_));
2900     //wxCHECK_MSG(CanEnableCellControl(coords), false, _T("can't enable editing for this cell!"));
2901     if (!CanEnableCellControl(coords)) {
2902       return false;
2903     }
2904 
2905     // already editing elsewhere, disable it
2906     if (IsCellEditControlCreated() && !DisableCellEditControl(true))
2907         return false;
2908 
2909     if (SendEvent( wxEVT_SHEET_EDITOR_ENABLED, coords ) == EVT_VETOED)
2910         return false;
2911 
2912     // guarantee that it's visible
2913     MakeCellVisible(coords);
2914     GetSheetRefData()->m_cellEditorCoords = coords;
2915 
2916     // Get the appropriate editor for this cell
2917     wxSheetCellAttr attr(GetAttr(coords));
2918     GetSheetRefData()->m_cellEditor = attr.GetEditor(this, coords);
2919     if (!GetEditControl().Ok())
2920     {
2921         GetSheetRefData()->m_cellEditorCoords = wxNullSheetCoords;
2922         wxFAIL_MSG(wxT("Unable to get cell edit control"));
2923         return false;
2924     }
2925 
2926     return ShowCellEditControl();
2927 }
2928 
DisableCellEditControl(bool save_value)2929 bool wxSheet::DisableCellEditControl( bool save_value )
2930 {
2931     //wxCHECK_RET(, wxT("Edit control not enabled in wxSheet::DisableCellEditControl"));
2932     if (!IsCellEditControlCreated())
2933     {
2934         // just in case something failed earlier, make sure it's gone
2935         if (GetEditControl().Ok())
2936             GetSheetRefData()->m_cellEditor.Destroy();
2937 
2938         GetSheetRefData()->m_cellEditorCoords = wxNullSheetCoords;
2939         return true;
2940     }
2941 
2942     if (SendEvent(wxEVT_SHEET_EDITOR_DISABLED, GetEditControlCoords()) == EVT_VETOED)
2943         return false;
2944 
2945     if (save_value)
2946         SaveEditControlValue();
2947 
2948     HideCellEditControl();
2949     // FIXME always destroy it else memory leak, maybe fixed with handler OnDestroy
2950     GetSheetRefData()->m_cellEditor.DestroyControl();
2951     GetSheetRefData()->m_cellEditor.Destroy();
2952     GetSheetRefData()->m_cellEditorCoords = wxNullSheetCoords;
2953     return true;
2954 }
2955 
CanEnableCellControl(const wxSheetCoords & coords) const2956 bool wxSheet::CanEnableCellControl(const wxSheetCoords& coords) const
2957 {
2958     wxCHECK_MSG(ContainsCell(coords), false, wxT("Invalid coords"));
2959     return IsEditable() && !GetAttr(coords).GetReadOnly();
2960 }
IsCellEditControlCreated() const2961 bool wxSheet::IsCellEditControlCreated() const
2962 {
2963     return GetEditControl().Ok() && GetEditControl().IsCreated();
2964 }
IsCellEditControlShown() const2965 bool wxSheet::IsCellEditControlShown() const
2966 {
2967     return GetEditControl().Ok() && GetEditControl().IsCreated() &&
2968            GetEditControl().IsShown();
2969 }
2970 
ShowCellEditControl()2971 bool wxSheet::ShowCellEditControl()
2972 {
2973     wxCHECK_MSG(CanEnableCellControl(GetEditControlCoords()), false, // also checks coords
2974                 wxT("Editor not enabled in wxSheet::ShowCellEditControl"));
2975 
2976     wxWindow *win = GetWindowForCoords(GetEditControlCoords());
2977 
2978     // If the control's parent is not correct we must recreate it
2979     if (GetEditControl().GetControl() && (GetEditControl().GetControl()->GetParent() != win))
2980         GetSheetRefData()->m_cellEditor.DestroyControl();
2981 
2982     // Make sure the editor is created
2983     if (!GetEditControl().IsCreated())
2984     {
2985         GetSheetRefData()->m_cellEditor.CreateEditor(win, -1,
2986                                new wxSheetCellEditorEvtHandler(this, GetEditControl()),
2987                                this);
2988 
2989         wxSheetEditorCreatedEvent evt(GetId(), wxEVT_SHEET_EDITOR_CREATED,
2990                                       this, GetEditControlCoords(),
2991                                       GetSheetRefData()->m_cellEditor.GetControl());
2992         GetEventHandler()->ProcessEvent(evt);
2993 
2994         if (!GetEditControl().IsCreated())
2995         {
2996             // kill it all off, something went wrong
2997             if (GetEditControl().GetControl())
2998                 GetSheetRefData()->m_cellEditor.DestroyControl();
2999 
3000             GetSheetRefData()->m_cellEditor.Destroy();
3001             GetSheetRefData()->m_cellEditorCoords = wxNullSheetCoords;
3002             wxFAIL_MSG(wxT("Unable to create edit control"));
3003             return false;
3004         }
3005     }
3006 
3007     wxSheetCellAttr attr(GetAttr(GetEditControlCoords()));
3008 
3009     // the rectangle bounding the cell
3010     wxRect rect( CellToRect(GetEditControlCoords(), true) );
3011 
3012     // FIXME use GetBestSize
3013 
3014     // resize editor to overflow into righthand cells if wider than cell
3015     wxString value = GetCellValue(GetEditControlCoords());
3016     int clientWidth = win->GetClientSize().GetWidth();
3017     if (!value.IsEmpty() && attr.GetOverflow() &&
3018         (GetEditControlCoords().m_col < GetNumberCols()))
3019     {
3020         int w=rect.width, h=0;
3021         // expand width if text only if text is wider than cell width
3022         GetTextExtent(value, &w, &h, NULL, NULL, &attr.GetFont());
3023 
3024         if (w > rect.width)
3025         {
3026             wxSheetCoords cellSize(GetCellSpan(GetEditControlCoords()));
3027             wxSheetCoords c(GetEditControlCoords());
3028             wxSheetCoords oneCell(1,1);
3029             int numCols = GetNumberCols();
3030             for (c.m_col = c.m_col + cellSize.m_col; c.m_col < numCols; c.m_col++)
3031             {
3032                 cellSize = GetCellSpan(c);
3033                // looks weird going over a spanned cell
3034                 if ((rect.width < w) && (rect.GetRight() < clientWidth) && (cellSize == oneCell))
3035                     rect.width += GetColWidth(c.m_col);
3036                 else
3037                     break;
3038             }
3039         }
3040     }
3041 
3042     // clip width to window size
3043     rect.width = wxMin(rect.width, clientWidth - rect.x);
3044 
3045     GetSheetRefData()->m_cellEditor.SetSize( rect, attr );
3046     GetSheetRefData()->m_cellEditor.Show( true, attr );
3047 
3048     // recalc dimensions, maybe expand the scrolled window to account for editor
3049     CalcWindowSizes();
3050 
3051     GetSheetRefData()->m_cellEditor.BeginEdit(GetEditControlCoords(), this);
3052 
3053     // FIXME other spreadsheets don't clear cells to right, it looks weird? why not?
3054     // if this is empty, cell to right maybe overflowed into from left, clear it
3055     //if (value.IsEmpty() && (m_cellEditorCoords.m_col < m_numCols - 1))
3056     //    RefreshCell(m_cellEditorCoords+wxSheetCoords(0, 1));
3057 
3058     return true;
3059 }
3060 
HideCellEditControl()3061 bool wxSheet::HideCellEditControl()
3062 {
3063     wxCHECK_MSG(IsCellEditControlShown(), false, wxT("Edit control not shown in wxSheet::HideCellEditControl"));
3064 
3065     GetSheetRefData()->m_cellEditor.Show( false, GetAttr(GetEditControlCoords()) );
3066     m_gridWin->SetFocus();
3067     RefreshCell(GetEditControlCoords(), false);
3068     return true;
3069 }
3070 
SaveEditControlValue()3071 void wxSheet::SaveEditControlValue()
3072 {
3073     wxCHECK_RET(IsCellEditControlCreated(), wxT("Edit control not shown in wxSheet::HideCellEditControl"));
3074 
3075     wxString oldval = GetCellValue(GetEditControlCoords());
3076 
3077     if (GetSheetRefData()->m_cellEditor.EndEdit(GetEditControlCoords(), this))
3078     {
3079         // Event has been vetoed, set the data back.
3080         if ( SendEvent(wxEVT_SHEET_CELL_VALUE_CHANGED, GetEditControlCoords()) == EVT_VETOED )
3081             SetCellValue(GetEditControlCoords(), oldval);
3082     }
3083 }
3084 
3085 // ----------------------------------------------------------------------------
3086 // Drawing functions
3087 
EndBatch(bool refresh)3088 void wxSheet::EndBatch(bool refresh)
3089 {
3090     if ( m_batchCount > 0 )
3091     {
3092         m_batchCount--;
3093         if ( !m_batchCount && refresh )
3094         {
3095             m_batchCount++;        // temp block refresh for window sizing
3096             AdjustScrollbars();
3097             m_batchCount--;
3098             Refresh(true);
3099         }
3100     }
3101 }
3102 
Refresh(bool eraseb,const wxRect * rect_)3103 void wxSheet::Refresh(bool eraseb, const wxRect* rect_)
3104 {
3105     // Don't do anything if between Begin/EndBatch...
3106     // EndBatch() will do all this on the last nested one anyway.
3107     if (GetBatchCount())
3108         return;
3109 
3110     if (rect_)
3111     {
3112         const wxRect rect(*rect_);
3113         const int rowLabelWidth  = GetRowLabelWidth();
3114         const int colLabelHeight = GetColLabelHeight();
3115         const wxSize extentSize(GetGridExtent());
3116 
3117         wxRect cornerRect(0, 0, rowLabelWidth, colLabelHeight);
3118         cornerRect.Intersect(rect);
3119 
3120         wxRect rowLabelRect(0, colLabelHeight, rowLabelWidth, extentSize.y);
3121         rowLabelRect.Intersect(rect);
3122         rowLabelRect.y -= colLabelHeight;
3123 
3124         wxRect colLabelRect(rowLabelWidth, 0, extentSize.x, colLabelHeight);
3125         colLabelRect.Intersect(rect);
3126         colLabelRect.x -= rowLabelWidth;
3127 
3128         wxRect gridRect(rowLabelWidth, colLabelHeight, extentSize.x, extentSize.y);
3129         gridRect.Intersect(rect);
3130         gridRect.x -= rowLabelWidth;
3131         gridRect.y -= colLabelHeight;
3132 
3133         if ( !wxRectIsEmpty(cornerRect) )
3134             RefreshCornerLabelWindow(eraseb, &cornerRect);
3135         if ( !wxRectIsEmpty(rowLabelRect) )
3136             RefreshRowLabelWindow(eraseb, &rowLabelRect);
3137         if ( !wxRectIsEmpty(colLabelRect) )
3138             RefreshColLabelWindow(eraseb, &colLabelRect);
3139         if ( !wxRectIsEmpty(gridRect) )
3140             RefreshGridWindow(eraseb, &gridRect);
3141     }
3142     else
3143     {
3144         RefreshCornerLabelWindow(eraseb, NULL);
3145         RefreshRowLabelWindow(eraseb, NULL);
3146         RefreshColLabelWindow(eraseb, NULL);
3147         RefreshGridWindow(eraseb, NULL);
3148     }
3149 }
RefreshGridWindow(bool eraseb,const wxRect * rect)3150 void wxSheet::RefreshGridWindow(bool eraseb, const wxRect* rect)
3151 {
3152     if (GetBatchCount())
3153         return;
3154 
3155     size_t n, count = GetSheetRefData()->GetSheetCount();
3156     for (n=0; n<count; n++)
3157     {
3158         wxSheet* s = GetSheetRefData()->GetSheet(n);
3159         wxWindow *win = (wxWindow*)s->GetGridWindow();
3160         if (win && win->IsShown())
3161         {
3162             if (rect)
3163             {
3164                 wxRect scrolledRect(s->CalcScrolledRect(*rect));
3165                 const wxRect refedRect(wxPoint(0,0), win->GetSize());
3166                 scrolledRect.Intersect(refedRect);
3167                 if (!wxRectIsEmpty(scrolledRect))
3168                     win->Refresh(eraseb, &scrolledRect);
3169             }
3170             else
3171                 win->Refresh(eraseb, NULL);
3172         }
3173     }
3174 }
RefreshRowLabelWindow(bool eraseb,const wxRect * rect)3175 void wxSheet::RefreshRowLabelWindow(bool eraseb, const wxRect* rect)
3176 {
3177     if (GetBatchCount())
3178         return;
3179 
3180     size_t n, count = GetSheetRefData()->GetSheetCount();
3181     for (n=0; n<count; n++)
3182     {
3183         wxSheet* s = GetSheetRefData()->GetSheet(n);
3184         wxWindow *win = (wxWindow*)s->GetRowLabelWindow();
3185         if (win && win->IsShown())
3186         {
3187             if (rect)
3188             {
3189                 wxRect scrolledRect(*rect);
3190                 s->CalcScrolledPosition(0, scrolledRect.y, NULL, &scrolledRect.y);
3191                 const wxRect refedRect(wxPoint(0,0), win->GetSize());
3192                 scrolledRect.Intersect(refedRect);
3193                 if (!wxRectIsEmpty(scrolledRect))
3194                     win->Refresh(eraseb, &scrolledRect);
3195             }
3196             else
3197                 win->Refresh(eraseb, NULL);
3198         }
3199     }
3200 }
RefreshColLabelWindow(bool eraseb,const wxRect * rect)3201 void wxSheet::RefreshColLabelWindow(bool eraseb, const wxRect* rect)
3202 {
3203     if (GetBatchCount())
3204         return;
3205 
3206     size_t n, count = GetSheetRefData()->GetSheetCount();
3207     for (n=0; n<count; n++)
3208     {
3209         wxSheet* s = GetSheetRefData()->GetSheet(n);
3210         wxWindow *win = (wxWindow*)s->GetColLabelWindow();
3211         if (win && win->IsShown())
3212         {
3213             if (rect)
3214             {
3215                 wxRect scrolledRect(*rect);
3216                 s->CalcScrolledPosition(scrolledRect.x, 0, &scrolledRect.x, NULL);
3217                 const wxRect refedRect(wxPoint(0,0), win->GetSize());
3218                 scrolledRect.Intersect(refedRect);
3219                 if (!wxRectIsEmpty(scrolledRect))
3220                     win->Refresh(eraseb, &scrolledRect);
3221             }
3222             else
3223                 win->Refresh(eraseb, NULL);
3224         }
3225     }
3226 }
RefreshCornerLabelWindow(bool eraseb,const wxRect * rect)3227 void wxSheet::RefreshCornerLabelWindow(bool eraseb, const wxRect* rect)
3228 {
3229     if (GetBatchCount())
3230         return;
3231 
3232     size_t n, count = GetSheetRefData()->GetSheetCount();
3233     for (n=0; n<count; n++)
3234     {
3235         wxSheet* s = GetSheetRefData()->GetSheet(n);
3236         wxWindow *win = (wxWindow*)s->GetCornerLabelWindow();
3237         if (win && win->IsShown())
3238             win->Refresh(eraseb, rect);
3239     }
3240 }
3241 
RefreshCell(const wxSheetCoords & coords,bool single_cell)3242 void wxSheet::RefreshCell(const wxSheetCoords& coords, bool single_cell)
3243 {
3244     if ( GetBatchCount() )
3245         return;
3246 
3247     //wxPrintf(wxT("RefreshCell %d %d\n"), coords.m_row, coords.m_col);
3248 
3249     if (IsCornerLabelCell(coords))
3250     {
3251         RefreshCornerLabelWindow(true);
3252         return;
3253     }
3254 
3255     wxRect rect(CellToRect(coords, false));
3256 
3257     if (ContainsRowLabelCell(coords))
3258     {
3259         RefreshRowLabelWindow( true, &rect );
3260         return;
3261     }
3262     else if (ContainsColLabelCell(coords))
3263     {
3264         RefreshColLabelWindow( true, &rect );
3265         return;
3266     }
3267     else if (ContainsGridCell(coords))
3268     {
3269         if (!single_cell)
3270         {
3271             // hack to draw previous cell, if this cell turns empty and can be
3272             // overflowed into we need to erase the previous cell's |> cutoff marker
3273             if (coords.m_col > 0)
3274                 rect.x = GetColLeft(coords.m_col-1);
3275             // we do have to draw the whole row right though
3276             rect.width = GetVirtualSize().x - rect.x;
3277         }
3278 
3279         RefreshGridWindow( false, &rect );
3280     }
3281     else
3282         wxFAIL_MSG(wxT("Invalid coords in wxSheet::RefreshCell"));
3283 }
RefreshBlock(const wxSheetBlock & block)3284 void wxSheet::RefreshBlock(const wxSheetBlock& block)
3285 {
3286     if ( GetBatchCount() || block.IsEmpty() )
3287         return;
3288 
3289     //PRINT_BLOCK(wxT("RefreshBlock "), block)
3290 
3291     // This function also refreshes beyond the labels and grid since if a
3292     //   cell is resized smaller that may have to be repainted
3293 
3294     wxSheetBlock b;
3295     const wxSheetCoords coords(block.GetLeftTop());
3296     const wxSize winSize(GetGridExtent());
3297     const int numRows = GetNumberRows();
3298     const int numCols = GetNumberCols();
3299 
3300     // Corner Labels
3301     if (IsCornerLabelCell(coords))
3302     {
3303         RefreshCornerLabelWindow(true);
3304     }
3305     // Row Labels
3306     b = block.Intersect(wxSheetBlock(0, -1, numRows, 1));
3307     if (!b.IsEmpty())
3308     {
3309         wxRect rect(BlockToLogicalRect(b));
3310         if (b.GetBottom() == numRows - 1)
3311             rect.height = winSize.y - rect.y;
3312         RefreshRowLabelWindow( true, &rect );
3313     }
3314     // Col Labels
3315     b = block.Intersect(wxSheetBlock(-1, 0, 1, numCols));
3316     if (!b.IsEmpty())
3317     {
3318         wxRect rect(BlockToLogicalRect(b));
3319         if (b.GetRight() == numCols - 1)
3320             rect.width = winSize.x - rect.x;
3321         RefreshColLabelWindow( true, &rect );
3322     }
3323     // Grid cells
3324     b = block.Intersect(wxSheetBlock(0, 0, numRows, numCols));
3325     if (!b.IsEmpty())
3326     {
3327         wxRect rect(BlockToLogicalRect(b));
3328         if (b.GetBottom() == numRows - 1)
3329             rect.height = winSize.y - rect.y;
3330         if (b.GetRight() == numCols - 1)
3331             rect.width = winSize.x - rect.x;
3332         RefreshGridWindow( false, &rect );
3333     }
3334 }
RefreshRow(int row)3335 void wxSheet::RefreshRow( int row )
3336 {
3337     RefreshBlock(wxSheetBlock(row, 0, 1, GetNumberCols()));
3338 }
RefreshCol(int col)3339 void wxSheet::RefreshCol( int col )
3340 {
3341     RefreshBlock(wxSheetBlock(0, col, GetNumberRows(), 1));
3342 }
RefreshGridCellBlock(const wxSheetBlock & block)3343 void wxSheet::RefreshGridCellBlock( const wxSheetBlock& block )
3344 {
3345     RefreshBlock(block);
3346 }
3347 
RefreshAttrChange(const wxSheetCoords & coords,wxSheetAttr_Type type)3348 void wxSheet::RefreshAttrChange(const wxSheetCoords& coords, wxSheetAttr_Type type)
3349 {
3350     switch (GetCellCoordsType(coords))
3351     {
3352         case wxSHEET_CELL_GRID :
3353         {
3354             switch (type)
3355             {
3356                 case wxSHEET_AttrDefault :
3357                 {
3358                     m_gridWin->SetBackgroundColour(DoGetDefaultGridAttr().GetBackgroundColour());
3359                     RefreshGridWindow(false);
3360                     break;
3361                 }
3362                 case wxSHEET_AttrCol :
3363                 {
3364                     if (ContainsGridCol(coords.m_col)) RefreshCol(coords.m_col);
3365                     break;
3366                 }
3367                 case wxSHEET_AttrRow :
3368                 {
3369                     if (ContainsGridRow(coords.m_row)) RefreshRow(coords.m_row);
3370                     break;
3371                 }
3372                 case wxSHEET_AttrCell :
3373                 default               :
3374                 {
3375                     if (ContainsGridCell(coords)) RefreshCell(coords, false);
3376                     break;
3377                 }
3378             }
3379             break;
3380         }
3381         case wxSHEET_CELL_CORNERLABEL :
3382         {
3383             m_cornerLabelWin->SetBackgroundColour(DoGetDefaultCornerLabelAttr().GetBackgroundColour());
3384             RefreshCornerLabelWindow(true);
3385             break;
3386         }
3387         case wxSHEET_CELL_ROWLABEL :
3388         {
3389             switch (type)
3390             {
3391                 case wxSHEET_AttrDefault :
3392                 {
3393                     m_rowLabelWin->SetBackgroundColour(DoGetDefaultRowLabelAttr().GetBackgroundColour());
3394                     RefreshRowLabelWindow(true);
3395                     break;
3396                 }
3397                 case wxSHEET_AttrCell :
3398                 default               :
3399                 {
3400                     if (ContainsRowLabelCell(coords))
3401                         RefreshCell(coords);
3402                     break;
3403                 }
3404             }
3405             break;
3406         }
3407         case wxSHEET_CELL_COLLABEL :
3408         {
3409             switch (type)
3410             {
3411                 case wxSHEET_AttrDefault :
3412                 {
3413                     m_colLabelWin->SetBackgroundColour(DoGetDefaultColLabelAttr().GetBackgroundColour());
3414                     RefreshColLabelWindow(true);
3415                     break;
3416                 }
3417                 case wxSHEET_AttrCell :
3418                 default               :
3419                 {
3420                     if (ContainsColLabelCell(coords))
3421                         RefreshCell(coords);
3422                     break;
3423                 }
3424             }
3425             break;
3426         }
3427         default : break;
3428     }
3429 }
3430 
OnPaint(wxPaintEvent & event)3431 void wxSheet::OnPaint( wxPaintEvent& event )
3432 {
3433     wxWindow *win = (wxWindow*)event.GetEventObject();
3434     wxPaintDC dc(win);
3435     wxRegion reg(win->GetUpdateRegion());
3436 
3437     if (!IsShown()) return;
3438 
3439     if (win == this)
3440     {
3441         PaintSheetWindow( dc, reg );
3442     }
3443     else if (win == m_cornerLabelWin)
3444     {
3445         if (m_cornerLabelWin->IsShown())
3446         {
3447             PaintCornerLabelWindow( dc, reg );
3448         }
3449     }
3450     else if (win == m_rowLabelWin)
3451     {
3452         if (m_rowLabelWin->IsShown())
3453         {
3454             PrepareRowLabelDC( dc );
3455             PaintRowLabelWindow( dc, reg );
3456         }
3457     }
3458     else if (win == m_colLabelWin)
3459     {
3460         if (m_colLabelWin->IsShown())
3461         {
3462             PrepareColLabelDC( dc );
3463             PaintColLabelWindow( dc, reg );
3464         }
3465     }
3466     else if (win == m_gridWin)
3467     {
3468         if (m_gridWin->IsShown())
3469         {
3470             PrepareGridDC( dc );
3471             PaintGridWindow( dc, reg );
3472         }
3473     }
3474 }
3475 
OnEraseBackground(wxEraseEvent & event)3476 void wxSheet::OnEraseBackground( wxEraseEvent& event )
3477 {
3478     wxWindow *win = (wxWindow*)event.GetEventObject();
3479 
3480     if (win == this)
3481         event.Skip(false);
3482     else if ((win == m_cornerLabelWin) || (win == m_rowLabelWin) || (win == m_colLabelWin))
3483         event.Skip(false);
3484     else if (win == m_gridWin)
3485         event.Skip(false);
3486 }
3487 
DrawSplitterButton(wxDC & dc,const wxRect & rect)3488 void wxSheet::DrawSplitterButton(wxDC &dc, const wxRect& rect)
3489 {
3490     //dc.SetPen(*wxBLACK_PEN);
3491     //dc.SetBrush(*wxBLACK_BRUSH);
3492     //dc.DrawRectangle(rect);
3493 
3494     wxPen highlight(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT), 1, wxSOLID);
3495     wxPen shadow(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW), 1, wxSOLID);
3496     wxPen darkShadow(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW), 1, wxSOLID);
3497 
3498     dc.SetPen(shadow);
3499     dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
3500     dc.DrawRectangle(rect);
3501     dc.DrawLine(rect.GetRight()-1, rect.y+2, rect.GetRight()-1, rect.GetBottom()-1);
3502     dc.DrawLine(rect.x+2, rect.GetBottom()-1, rect.GetRight(), rect.GetBottom()-1);
3503 
3504     dc.SetPen(darkShadow);
3505     dc.DrawLine(rect.GetRight(), rect.y+1, rect.GetRight(), rect.GetBottom());
3506     dc.DrawLine(rect.x+1, rect.GetBottom(), rect.GetRight(), rect.GetBottom());
3507 
3508     dc.SetPen(highlight);
3509     dc.DrawLine(rect.x+1, rect.y+1, rect.GetRight()-1, rect.y+1);
3510     dc.DrawLine(rect.x+1, rect.y+1, rect.x+1, rect.GetBottom()-1);
3511 }
3512 
PaintSheetWindow(wxDC & dc,const wxRegion & WXUNUSED (reg))3513 void wxSheet::PaintSheetWindow( wxDC& dc, const wxRegion& WXUNUSED(reg) )
3514 {
3515     // draw the bottom right square area between the scrollbars
3516     // since we trap EVT_ERASE_BACKGROUND
3517     if (m_vertScrollBar->IsShown() && m_horizScrollBar->IsShown())
3518     {
3519         dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
3520         dc.SetPen(*wxTRANSPARENT_PEN);
3521         wxSize clientSize = GetClientSize();
3522         int top  = m_vertScrollBar->GetRect().GetBottom();
3523         int left = m_horizScrollBar->GetRect().GetRight() + m_horizSplitRect.GetWidth();
3524         dc.DrawRectangle(left, top, clientSize.x-left, clientSize.y-top);
3525     }
3526 
3527     if (!wxRectIsEmpty(m_vertSplitRect))
3528         DrawSplitterButton(dc, m_vertSplitRect);
3529     if (!wxRectIsEmpty(m_horizSplitRect))
3530         DrawSplitterButton(dc, m_horizSplitRect);
3531 }
3532 
PaintGridWindow(wxDC & dc,const wxRegion & reg)3533 void wxSheet::PaintGridWindow( wxDC& dc, const wxRegion& reg )
3534 {
3535     wxSheetSelection dirtyBlocks;
3536     CalcCellsExposed( reg, dirtyBlocks );
3537 
3538     DrawGridCells( dc, dirtyBlocks );
3539 #if WXSHEET_DRAW_LINES
3540     DrawAllGridLines( dc, reg );
3541 #endif
3542 
3543     DrawGridSpace( dc );
3544     DrawCursorHighlight( dc, dirtyBlocks );
3545 }
PaintRowLabelWindow(wxDC & dc,const wxRegion & reg)3546 void wxSheet::PaintRowLabelWindow( wxDC& dc, const wxRegion& reg )
3547 {
3548     const int numRows = GetNumberRows();
3549     if (0 && numRows)
3550     {
3551 
3552         wxRect r(0, GetRowBottom(numRows-1), GetRowLabelWidth(), GetGridVirtualSize(true).y);
3553         r = CalcScrolledRect(r);
3554         r.Intersect(reg.GetBox());
3555         dc.DrawRectangle(r);
3556     }
3557 
3558     wxArrayInt rows;
3559     if (CalcRowLabelsExposed(reg, rows))
3560         DrawRowLabels( dc, rows );
3561 
3562 }
PaintColLabelWindow(wxDC & dc,const wxRegion & reg)3563 void wxSheet::PaintColLabelWindow( wxDC& dc, const wxRegion& reg )
3564 {
3565     const int numCols = GetNumberCols();
3566     if (0 && numCols)
3567     {
3568         wxRect r(GetColRight(numCols-1), 0, GetGridVirtualSize(true).x, GetColLabelHeight());
3569         r = CalcScrolledRect(r);
3570         r.Intersect(reg.GetBox());
3571         dc.DrawRectangle(r);
3572     }
3573 
3574     wxArrayInt cols;
3575     if (CalcColLabelsExposed(reg, cols))
3576         DrawColLabels( dc, cols );
3577 }
PaintCornerLabelWindow(wxDC & dc,const wxRegion & WXUNUSED (reg))3578 void wxSheet::PaintCornerLabelWindow( wxDC& dc, const wxRegion& WXUNUSED(reg) )
3579 {
3580     DrawCornerLabel( dc );
3581 }
3582 
FindOverflowCell(const wxSheetCoords & coords,wxDC & dc)3583 int wxSheet::FindOverflowCell( const wxSheetCoords& coords, wxDC& dc )
3584 {
3585     wxSheetCoords c(coords);
3586     while (c.m_col > 0)
3587     {
3588         c.m_col = GetTable()->GetFirstNonEmptyColToLeft(c);
3589         if (c.m_col < 0)
3590             return -1;
3591 
3592         // if !empty, stop, this is the only cell that could overflow
3593         // Must check since default implementation of
3594         // GetFirstNonEmptyColToLeft just returns the previous col
3595         if (HasCellValue(c))
3596         {
3597             wxSheetCellAttr attr(GetAttr(c));
3598             if (attr.GetOverflow())
3599             {
3600                 // check if this cell actually does overflow into coords
3601                 int width = attr.GetRenderer(this, c).GetBestSize(*this, attr, dc, c).GetWidth();
3602                 if (GetColLeft(c.m_col)+width > GetColLeft(coords.m_col))
3603                     return c.m_col;
3604             }
3605             break;
3606         }
3607     }
3608     return -1;
3609 }
3610 
3611 // Note - this function only draws cells that are in the list of
3612 // exposed cells (usually set from the update region by CalcExposedCells)
DrawGridCells(wxDC & dc,const wxSheetSelection & blockSel)3613 void wxSheet::DrawGridCells( wxDC& dc, const wxSheetSelection& blockSel )
3614 {
3615     if ( !GetNumberRows() || !GetNumberCols() || GetBatchCount() )
3616         return;
3617 
3618     //static long counter = 0; ++counter;      // for testing only
3619 
3620     wxSheetSelection removedSel;                // removed spanned
3621     wxSheetCoords coords;
3622     wxSheetCoords lastCoords(-1,-1);
3623     wxSheetBlock  cellBlock;
3624     wxPairArrayIntInt leftCols;
3625 
3626     // paint cells in reverse order bottom to top, right to left for overflow
3627     wxSheetSelectionIterator revIter(blockSel, wxSSI_REVERSE);
3628     while (revIter.GetNext(coords) != wxSHEET_SELECTIONITER_GET_END)
3629     {
3630         // already painted this cell
3631         if (removedSel.Contains(coords))
3632         {
3633             lastCoords = coords;
3634             continue;
3635         }
3636 
3637         // trim out cells part of spanned cells, leaving only the owner cell
3638         cellBlock = GetCellBlock(coords);
3639         if (!cellBlock.IsOneCell())
3640         {
3641             // forget part of spanned cell, we only care about owner cell
3642             coords = cellBlock.GetLeftTop();
3643             // don't bother checking whole block again
3644             removedSel.SelectBlock(cellBlock, false);
3645         }
3646 
3647         // if empty find cell to left that might overflow into this one
3648         // only need to check left side of each block, when row increments
3649         if ((lastCoords.m_row > coords.m_row) && !HasCellValue(lastCoords))
3650         {
3651             int overflow_col = FindOverflowCell(lastCoords, dc);
3652             if ((overflow_col >= 0) && !blockSel.Contains(lastCoords.m_row, overflow_col))
3653                 leftCols.SetValue(lastCoords.m_row, overflow_col);
3654         }
3655 
3656         //wxPrintf(wxT("%ld Drawing Cell %d %d - has %d %d\n"), counter, coords.m_row, coords.m_col, HasCellValue(coords), HasCellValue(lastCoords)); fflush(stdout);
3657         DrawCell( dc, coords );
3658         lastCoords = coords;
3659     }
3660 
3661     // check the top left cell of the last block too
3662     if (ContainsGridCell(lastCoords) && !HasCellValue(lastCoords))
3663     {
3664         int overflow_col = FindOverflowCell(lastCoords, dc);
3665         if ((overflow_col >= 0) && !blockSel.Contains(lastCoords.m_row, overflow_col))
3666             leftCols.SetValue(lastCoords.m_row, overflow_col);
3667     }
3668 
3669     // now redraw the cells that could have overflowed into these
3670     int n, count = leftCols.GetCount();
3671     for (n = 0; n < count; n++)
3672     {
3673         coords.Set(leftCols.GetItemKey(n), leftCols.GetItemValue(n));
3674         DrawCell( dc, coords );
3675         //wxPrintf(wxT("%ld Overflow Drawing Cell %d %d\n"), counter, coords.m_row, coords.m_col); fflush(stdout);
3676     }
3677 }
3678 
3679 /*
3680 // Note - this function only draws cells that are in the list of
3681 // exposed cells (usually set from the update region by CalcExposedCells)
3682 void wxSheet::DrawGridCells( wxDC& dc, const wxSheetSelection& blockSel_ )
3683 {
3684     if ( !GetNumberRows() || !GetNumberCols() || GetBatchCount() )
3685         return;
3686 
3687     wxSheetSelection blockSel(blockSel_);       // modified to remove spanned cells
3688     wxSheetSelection removedSel;                // removed spanned
3689     wxSheetSelectionIterator selIter(blockSel); // forward iterator
3690 
3691     wxSheetCoords coords;
3692     wxSheetBlock  cellBlock;
3693 
3694     int check_overflow_row = -1;
3695 
3696     while (selIter.GetNext(coords))
3697     {
3698         if (removedSel.Contains(coords))
3699             continue;
3700 
3701         // trim out cells part of spanned cells, leaving only the owner cell
3702         cellBlock = GetCellBlock(coords);
3703         if (!cellBlock.IsOneCell())
3704         {
3705             // forget part of spanned cell, we only care about owner cell
3706             coords = cellBlock.GetLeftTop();
3707             // remove whole spanned cell and add back the owner
3708             blockSel.DeselectBlock(cellBlock, false);
3709             blockSel.SelectBlock(wxSheetBlock(coords, 1, 1), false);
3710             // don't bother checking whole block again
3711             removedSel.SelectBlock(cellBlock, false);
3712         }
3713 
3714         int bottom = cellBlock.GetBottom();
3715 
3716         // if empty find cell to left that might overflow into this one
3717         // only need to check left side of each block, hence check_overflow_row
3718         if ((bottom > check_overflow_row) && !HasValue()) //GetTable() && !GetTable()->HasValue(coords))
3719         {
3720             check_overflow_row = bottom;
3721 
3722             wxSheetCoords c;
3723             for ( c.m_row = coords.m_row; c.m_row <= bottom; c.m_row++ )
3724             {
3725                 c.m_col = coords.m_col;
3726                 while (c.m_col > 0)
3727                 {
3728                     c.m_col = GetTable()->GetFirstNonEmptyColToLeft(c);
3729                     if (c.m_col < 0)
3730                         break;
3731 
3732                     // if !empty, stop, this is the only cell that could overflow
3733                     // Must check since default implementation of
3734                     // GetFirstNonEmptyColToLeft just returns the previous col
3735                     if (GetTable()->HasValue(c))
3736                     {
3737                         wxSheetCellAttr attr(GetAttr(c));
3738                         if (attr.GetOverflow())
3739                         {
3740                             // check if this cell actually does overflow into coords
3741                             int width = attr.GetRenderer(this, c).GetBestSize(*this, attr, dc, c).GetWidth();
3742 
3743                             if (GetColLeft(c.m_col)+width > GetColLeft(coords.m_col))
3744                                 blockSel.SelectBlock(wxSheetBlock(c, 1, 1), false);
3745                         }
3746                         break;
3747                     }
3748                 }
3749             }
3750         }
3751     }
3752 
3753     //static long counter = 0; ++counter;
3754 
3755     // paint cells in reverse order bottom to top, right to left for overflow
3756     wxSheetSelectionIterator revIter(blockSel, wxSSI_REVERSE);
3757     while (revIter.GetNext(coords))
3758     {
3759         //wxPrintf(wxT("%ld Drawing Cell %d %d\n"), counter, coords.m_row, coords.m_col);
3760         DrawCell( dc, coords );
3761     }
3762 }
3763 */
3764 
DrawGridSpace(wxDC & dc)3765 void wxSheet::DrawGridSpace( wxDC& dc )
3766 {
3767     int cw, ch;
3768     m_gridWin->GetClientSize( &cw, &ch );
3769 
3770     int right, bottom;
3771     CalcUnscrolledPosition( cw, ch, &right, &bottom );
3772 
3773     const int numRows = GetNumberRows();
3774     const int numCols = GetNumberCols();
3775     const int bottomRow = numRows > 0 ? GetRowBottom(numRows - 1) + 1: 0;
3776     const int rightCol  = numCols > 0 ? GetColRight(numCols - 1)  + 1: 0;
3777 
3778     if ( (right > rightCol) || (bottom > bottomRow) )
3779     {
3780         int left, top;
3781         CalcUnscrolledPosition( 0, 0, &left, &top );
3782 
3783         dc.SetBrush( wxBrush(GetAttrBackgroundColour(wxSheetCoords(0,0), wxSHEET_AttrDefault), wxSOLID) );
3784         dc.SetPen( *wxTRANSPARENT_PEN );
3785 
3786         if ( right > rightCol )
3787             dc.DrawRectangle( rightCol, top, right - rightCol, ch );
3788         if ( bottom > bottomRow )
3789             dc.DrawRectangle( left, bottomRow, cw, bottom - bottomRow );
3790     }
3791 }
3792 
DrawCell(wxDC & dc,const wxSheetCoords & coords)3793 void wxSheet::DrawCell( wxDC& dc, const wxSheetCoords& coords )
3794 {
3795     wxRect rect(CellToRect(coords));
3796     if (wxRectIsEmpty(rect))  // !IsShown
3797         return;
3798 
3799     // we draw the cell border ourselves
3800 #if !WXSHEET_DRAW_LINES
3801     if ( GridLinesEnabled() != 0 )
3802         DrawCellBorder( dc, coords );
3803 #endif
3804 
3805     wxSheetCellAttr attr(GetAttr(coords));
3806 
3807     // if the editor is shown, we should use it and not the renderer
3808     if ( (coords == GetEditControlCoords()) && IsCellEditControlShown() )
3809         GetSheetRefData()->m_cellEditor.PaintBackground(*this, attr, dc, rect, coords, IsCellSelected(coords));
3810     else
3811         attr.GetRenderer(this, coords).Draw(*this, attr, dc, rect, coords, IsCellSelected(coords));
3812 }
3813 
DrawCursorHighlight(wxDC & dc,const wxSheetSelection & blockSel)3814 void wxSheet::DrawCursorHighlight(wxDC& dc, const wxSheetSelection& blockSel)
3815 {
3816     // Make sure that the cursor is valid (just in case)
3817     if ( !ContainsGridCell(GetGridCursorCell()) && GetNumberRows() && GetNumberCols() )
3818         GetSheetRefData()->m_cursorCoords.Set(0, 0);
3819 
3820     // don't show highlight when the edit control is shown
3821     if (!blockSel.Contains(GetGridCursorCell()) || IsCellEditControlShown())
3822         return;
3823 
3824     DrawCursorCellHighlight(dc, GetAttr(GetGridCursorCell()));
3825 }
3826 
DrawCursorCellHighlight(wxDC & dc,const wxSheetCellAttr & attr)3827 void wxSheet::DrawCursorCellHighlight( wxDC& dc, const wxSheetCellAttr& attr )
3828 {
3829     if (!ContainsGridCell(GetGridCursorCell()))
3830         return;
3831 
3832     wxSheetCoords coords(GetCellOwner(GetGridCursorCell()));
3833     wxRect rect( CellToRect(coords) );
3834     if (wxRectIsEmpty(rect))  // !IsCellShown
3835         return;
3836 
3837     // hmmm... what could we do here to show that the cell is disabled?
3838     // for now, I just draw a thinner border than for the other ones, but
3839     // it doesn't look really good
3840 
3841     int penWidth = attr.GetReadOnly() ? GetCursorCellHighlightROPenWidth() :
3842                                         GetCursorCellHighlightPenWidth();
3843 
3844     if (penWidth > 0)
3845     {
3846         // The center of the drawn line is where the position/width/height of
3847         // the rectangle is actually at, (on wxMSW atr least,) so we will
3848         // reduce the size of the rectangle to compensate for the thickness of
3849         // the line.  If this is too strange on non wxMSW platforms then
3850         // please #ifdef this appropriately.
3851         rect.x += penWidth/2;
3852         rect.y += penWidth/2;
3853         rect.width -= penWidth-1;
3854         rect.height -= penWidth-1;
3855 
3856         // Now draw the rectangle
3857         // use the cellHighlightColour if the cell is inside a selection, this
3858         // will ensure the cell is always visible.
3859         dc.SetPen(wxPen(IsCellSelected(coords) ? GetSelectionForeground() :
3860                                                  GetCursorCellHighlightColour(),
3861                           penWidth, wxSOLID));
3862         dc.SetBrush(*wxTRANSPARENT_BRUSH);
3863         dc.DrawRectangle(rect);
3864     }
3865 
3866 #if 0
3867         // VZ: my experiments with 3d borders...
3868 
3869         // how to properly set colours for arbitrary bg?
3870         wxCoord x1 = rect.x,
3871                 y1 = rect.y,
3872                 x2 = rect.x + rect.width -1,
3873                 y2 = rect.y + rect.height -1;
3874 
3875         dc.SetPen(*wxWHITE_PEN);
3876         dc.DrawLine(x1, y1, x2, y1);
3877         dc.DrawLine(x1, y1, x1, y2);
3878 
3879         dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1);
3880         dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2 );
3881 
3882         dc.SetPen(*wxBLACK_PEN);
3883         dc.DrawLine(x1, y2, x2, y2);
3884         dc.DrawLine(x2, y1, x2, y2+1);
3885 #endif // 0
3886 }
3887 
DrawCellBorder(wxDC & dc,const wxSheetCoords & coords)3888 void wxSheet::DrawCellBorder( wxDC& dc, const wxSheetCoords& coords )
3889 {
3890     wxRect rect(CellToRect( coords ));
3891     if ( wxRectIsEmpty(rect) )  // !IsCellShown
3892         return;
3893 
3894     dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
3895     if ((GridLinesEnabled() & wxVERTICAL) != 0)
3896     {
3897         // right hand border
3898         dc.DrawLine( rect.x + rect.width, rect.y,
3899                      rect.x + rect.width, rect.y + rect.height + 1 );
3900     }
3901     if ((GridLinesEnabled() & wxHORIZONTAL) != 0)
3902     {
3903         // bottom border
3904         dc.DrawLine( rect.x,              rect.y + rect.height,
3905                      rect.x + rect.width, rect.y + rect.height);
3906     }
3907 }
3908 
3909 // TODO: remove this ???
3910 // This is used to redraw all grid lines e.g. when the grid line colour
3911 // has been changed
DrawAllGridLines(wxDC & dc,const wxRegion & WXUNUSED (reg))3912 void wxSheet::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
3913 {
3914 #if !WXSHEET_DRAW_LINES
3915     return;
3916 #endif
3917 
3918     const int numRows = GetNumberRows();
3919     const int numCols = GetNumberCols();
3920 
3921     if ( GetBatchCount() || (GridLinesEnabled() == 0) || !numRows || !numCols )
3922         return;
3923 
3924     int top, bottom, left, right;
3925 
3926 #if 0  //#ifndef __WXGTK__
3927     if (reg.IsEmpty())
3928     {
3929         int cw, ch;
3930         m_gridWin->GetClientSize(&cw, &ch);
3931 
3932         // virtual coords of visible area
3933         CalcUnscrolledPosition( 0, 0, &left, &top );
3934         CalcUnscrolledPosition( cw, ch, &right, &bottom );
3935     }
3936     else
3937     {
3938         wxCoord x, y, w, h;
3939         reg.GetBox(x, y, w, h);
3940         CalcUnscrolledPosition( x, y, &left, &top );
3941         CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
3942     }
3943 #else
3944     int cw, ch;
3945     m_gridWin->GetClientSize(&cw, &ch);
3946     CalcUnscrolledPosition( 0, 0, &left, &top );
3947     CalcUnscrolledPosition( cw, ch, &right, &bottom );
3948 #endif
3949 
3950     // avoid drawing grid lines past the last row and col
3951     right  = wxMin( right,  GetColRight(numCols - 1) );
3952     bottom = wxMin( bottom, GetRowBottom(numRows - 1) );
3953 
3954     // no gridlines inside spanned cells, clip them out
3955     int leftCol   = XToGridCol(left, true);
3956     int topRow    = YToGridRow(top, true);
3957     int rightCol  = XToGridCol(right, true);
3958     int bottomRow = YToGridRow(bottom, true);
3959     int i;
3960 
3961     if (HasSpannedCells())
3962     {
3963         const wxSheetBlock block(topRow, leftCol, bottomRow-topRow+1, rightCol-leftCol+1);
3964         wxRegion clippedcells(0, 0, cw, ch);
3965         bool done = false;
3966 
3967         if (GetSpannedBlocks())
3968         {
3969             const wxSheetSelection* spannedBlocks = GetSpannedBlocks();
3970             const int count = spannedBlocks->GetCount();
3971 
3972             for (i=spannedBlocks->FindTopRow(topRow); i<count; i++)
3973             {
3974                 const wxSheetBlock &b = spannedBlocks->GetBlock(i);
3975                 if (block.Intersects(b))
3976                 {
3977                     clippedcells.Subtract(BlockToRect(b, true));
3978                     done = true;
3979                 }
3980                 else if (bottomRow < b.GetTop())
3981                     break;
3982             }
3983         }
3984         else // grind through it
3985         {
3986             wxSheetCoords c;
3987             for (c.m_row = topRow; c.m_row <= bottomRow; c.m_row++)
3988             {
3989                 for (c.m_col = leftCol; c.m_col <= rightCol; c.m_col++)
3990                 {
3991                     const wxSheetBlock b(GetCellBlock(c));
3992                     if (!b.IsOneCell())
3993                     {
3994                         done = true;
3995                         clippedcells.Subtract(BlockToRect(b, true));
3996                     }
3997                 }
3998             }
3999         }
4000 
4001         if (done)
4002             dc.SetDeviceClippingRegion( clippedcells );
4003     }
4004 
4005     dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
4006 
4007     if ((GridLinesEnabled() & wxHORIZONTAL) != 0)
4008     {
4009         for ( i = topRow; i < numRows; i++ )
4010         {
4011             int rowBottom = GetRowBottom(i);
4012             if ( rowBottom > bottom )
4013                 break;
4014 
4015             if ( rowBottom >= top )
4016                 dc.DrawLine( left, rowBottom, right, rowBottom );
4017         }
4018     }
4019 
4020     if ((GridLinesEnabled() & wxVERTICAL) != 0)
4021     {
4022         for ( i = leftCol; i < numCols; i++ )
4023         {
4024             int colRight = GetColRight(i);
4025             if ( colRight > right )
4026                 break;
4027 
4028             if ( colRight >= left )
4029                 dc.DrawLine( colRight, top, colRight, bottom );
4030         }
4031     }
4032 
4033     dc.DestroyClippingRegion();
4034 }
4035 
DrawRowLabels(wxDC & dc,const wxArrayInt & rows)4036 void wxSheet::DrawRowLabels( wxDC& dc, const wxArrayInt& rows )
4037 {
4038     size_t i, numLabels = rows.GetCount();
4039     if ( !GetNumberRows() || !numLabels ) return;
4040 
4041     for ( i = 0; i < numLabels; i++ )
4042         DrawCell( dc, wxSheetCoords(rows[i], -1) );
4043 
4044 /*  // sample native rendernative code - FIXME
4045     rect.SetX( 1 );
4046     rect.SetY( GetRowTop(row) + 1 );
4047     rect.SetWidth( m_rowLabelWidth - 2 );
4048     rect.SetHeight( GetRowHeight(row) - 2 );
4049     CalcScrolledPosition( 0, rect.y, NULL, &rect.y );
4050     wxWindowDC *win_dc = (wxWindowDC*) &dc;
4051     wxRendererNative::Get().DrawHeaderButton( win_dc->m_owner, dc, rect, 0 );
4052 */
4053 
4054     // Now draw the dividing lines
4055     dc.SetPen( wxPen(GetLabelOutlineColour(), 1, wxSOLID) );
4056     int top;
4057     CalcUnscrolledPosition(0, 0, NULL, &top);
4058     //int top = GetRowTop(rows[0]);
4059     int bottom = top + m_gridWin->GetSize().y; //GetRowBottom(rows[numLabels-1]);
4060     int width = GetRowLabelWidth();
4061     // left of row labels
4062     dc.DrawLine(0, top, 0, bottom);
4063     // right of row labels
4064     dc.DrawLine(width-1, top, width-1, bottom);
4065     // draw bottoms
4066     wxSheetCoords coords(0, -1);
4067     for ( i = 0; i < numLabels; i++ )
4068     {
4069         coords.m_row = rows[i];
4070         bottom = GetRowBottom(GetCellBlock(coords).GetBottom());
4071         dc.DrawLine(0, bottom, width-1, bottom);
4072     }
4073 }
4074 
DrawColLabels(wxDC & dc,const wxArrayInt & cols)4075 void wxSheet::DrawColLabels( wxDC& dc, const wxArrayInt& cols )
4076 {
4077     size_t i, numLabels = cols.GetCount();
4078     if ( !GetNumberCols() || !numLabels ) return;
4079 
4080     for ( i = 0; i < numLabels; i++ )
4081         DrawCell( dc, wxSheetCoords(-1, cols[i]) );
4082 
4083 /*  // sample native rendernative code - FIXME
4084     rect.SetX( colLeft + 1 );
4085     rect.SetY( 1 );
4086     rect.SetWidth( GetColWidth(col) - 2 );
4087     rect.SetHeight( m_colLabelHeight - 2 );
4088     wxWindowDC *win_dc = (wxWindowDC*) &dc;
4089     wxRendererNative::Get().DrawHeaderButton( win_dc->m_owner, dc, rect, 0 );
4090 */
4091 
4092     // Now draw the dividing lines
4093     dc.SetPen( wxPen(GetLabelOutlineColour(), 1, wxSOLID) );
4094     int left;
4095     CalcUnscrolledPosition(0, 0, &left, NULL);
4096     //int left = GetColLeft(cols[0]);
4097     int right = left + m_gridWin->GetSize().x; //GetColRight(cols[numLabels-1]);
4098     int height = GetColLabelHeight();
4099     // top of col labels
4100     dc.DrawLine(left, 0, right, 0);
4101     // bottom of col labels
4102     dc.DrawLine(left, height-1, right, height-1);
4103     // draw rights
4104     wxSheetCoords coords(-1, 0);
4105     for ( i = 0; i < numLabels; i++ )
4106     {
4107         coords.m_col = cols[i];
4108         right = GetColRight(GetCellBlock(coords).GetRight());
4109         dc.DrawLine(right, 0, right, height-1);
4110     }
4111 }
4112 
DrawCornerLabel(wxDC & dc)4113 void wxSheet::DrawCornerLabel( wxDC& dc )
4114 {
4115     DrawCell( dc, wxSheetCoords(-1, -1) );
4116     wxSize size(m_cornerLabelWin->GetClientSize());
4117 
4118 #ifdef USE_RENDERNATIVE
4119     wxRect rect(1, 1, size.x-2, size.y-2);
4120     //rect.SetX( 1 );
4121     //rect.SetY( 1 );
4122     //rect.SetWidth( client_width - 2 );
4123     //rect.SetHeight( client_height - 2 );
4124     wxRendererNative::Get().DrawHeaderButton( this, dc, rect, 0 );
4125 #else
4126     // Now draw the dividing lines
4127     dc.SetPen( wxPen(GetLabelOutlineColour(), 1, wxSOLID) );
4128     dc.DrawLine(0, 0, size.x, 0);                   // top
4129     dc.DrawLine(0, size.y-1, size.x, size.y-1);     // bottom
4130     dc.DrawLine(0, 0, 0, size.y-1);                 // left
4131     dc.DrawLine(size.x-1, 0, size.x-1, size.y-1);   // right
4132 #endif
4133 }
4134 
DrawRowColResizingMarker(int newDragPos)4135 void wxSheet::DrawRowColResizingMarker( int newDragPos )
4136 {
4137     if ( !HasMouseCursorMode(WXSHEET_CURSOR_RESIZING) ||
4138          ((m_dragLastPos == -1) && (newDragPos == -1)) )
4139         return;
4140 
4141     wxClientDC dc( m_gridWin );
4142     PrepareGridDC( dc );
4143 
4144     int left = 0, top = 0;
4145     CalcUnscrolledPosition( 0, 0, &left, &top );
4146 
4147     int right = GetColRight(GetNumberCols()-1);
4148     int bottom = GetRowBottom(GetNumberRows()-1);
4149     int cw = 0, ch = 0;
4150     m_gridWin->GetClientSize( &cw, &ch );
4151     right = wxMin(left+cw, right);
4152     bottom = wxMin(top+ch, bottom);
4153 
4154     const bool resizingRow = HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW);
4155 
4156     if (GridLinesEnabled())
4157     {
4158         dc.SetPen(wxPen(GetGridLineColour(), 3, wxSOLID));
4159 
4160         // Draw the anchor marker so you know what row/col you're resizing
4161         if (resizingRow)
4162         {
4163             int anchor = GetRowTop(m_dragRowOrCol);
4164             dc.DrawLine( left, anchor, right, anchor );
4165         }
4166         else // resizing col
4167         {
4168             int anchor = GetColLeft(m_dragRowOrCol);
4169             dc.DrawLine( anchor, top, anchor, bottom );
4170         }
4171     }
4172 
4173 #if wxCHECK_VERSION(2, 9, 0)
4174     wxRasterOperationMode log_fn = dc.GetLogicalFunction();
4175 #else
4176     int log_fn = dc.GetLogicalFunction();
4177 #endif  /* wxCHECK_VERSION */
4178     dc.SetLogicalFunction(wxINVERT);
4179 
4180     if (m_dragLastPos >= 0)
4181     {
4182         if (resizingRow)
4183             dc.DrawLine( left, m_dragLastPos, right, m_dragLastPos );
4184         else // resizing col
4185             dc.DrawLine( m_dragLastPos, top, m_dragLastPos, bottom );
4186     }
4187 
4188     if (newDragPos >= 0)
4189     {
4190         m_dragLastPos = newDragPos;
4191 
4192         if (resizingRow)
4193             dc.DrawLine( left, m_dragLastPos, right, m_dragLastPos );
4194         else // resizing col
4195             dc.DrawLine( m_dragLastPos, top, m_dragLastPos, bottom );
4196     }
4197 
4198     dc.SetLogicalFunction(log_fn); // set it back since nobody else wants invert
4199 }
4200 
CalcRowLabelsExposed(const wxRegion & reg,wxArrayInt & rowLabels) const4201 bool wxSheet::CalcRowLabelsExposed( const wxRegion& reg, wxArrayInt& rowLabels ) const
4202 {
4203     const int numRows = GetNumberRows();
4204     if (!numRows)
4205         return false;
4206 
4207     const bool spanned = HasSpannedCells();
4208     wxRegionIterator iter( reg );
4209 
4210     while ( iter )
4211     {
4212         wxRect r(iter.GetRect());
4213 
4214         // TODO: remove this when we can...
4215         // There is a bug in wxMotif that gives garbage update
4216         // rectangles if you jump-scroll a long way by clicking the
4217         // scrollbar with middle button.  This is a work-around
4218         //
4219 #if defined(__WXMOTIF__)
4220         int cw, ch;
4221         m_gridWin->GetClientSize( &cw, &ch );
4222         if ( r.GetTop() > ch ) r.SetTop( 0 );
4223         r.SetBottom( wxMin( r.GetBottom(), ch ) );
4224 #endif
4225 
4226         // logical bounds of update region
4227         int top;
4228         CalcUnscrolledPosition( 0, r.GetTop(), NULL, &top );
4229         int bottom = top + r.GetHeight();
4230         //CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
4231 
4232         // find the row labels within these bounds
4233         for ( int row = YToGridRow(top, true); row < numRows;  row++ )
4234         {
4235             if ( GetRowBottom(row) < top )
4236                 continue;
4237             if ( GetRowTop(row) > bottom )
4238                 break;
4239 
4240             if (spanned)
4241             {
4242                 const wxSheetBlock block(GetCellBlock(wxSheetCoords(row, -1)));
4243 
4244                 if (block.GetHeight() >= 1)
4245                 {
4246                     rowLabels.Add( block.GetTop() );
4247                     row = block.GetBottom();
4248                 }
4249                 else if (block.GetHeight() < 1)
4250                     row = block.GetBottom();
4251             }
4252             else
4253                 rowLabels.Add( row );
4254         }
4255 
4256         iter++ ;
4257     }
4258 
4259     return rowLabels.GetCount() > 0u;
4260 }
4261 
CalcColLabelsExposed(const wxRegion & reg,wxArrayInt & colLabels) const4262 bool wxSheet::CalcColLabelsExposed( const wxRegion& reg, wxArrayInt& colLabels ) const
4263 {
4264     const int numCols = GetNumberCols();
4265     if (!numCols)
4266         return false;
4267 
4268     const bool spanned = HasSpannedCells();
4269     wxRegionIterator iter( reg );
4270 
4271     while ( iter )
4272     {
4273         wxRect r(iter.GetRect());
4274 
4275         // TODO: remove this when we can...
4276         // There is a bug in wxMotif that gives garbage update
4277         // rectangles if you jump-scroll a long way by clicking the
4278         // scrollbar with middle button.  This is a work-around
4279         //
4280 #if defined(__WXMOTIF__)
4281         int cw, ch;
4282         m_gridWin->GetClientSize( &cw, &ch );
4283         if ( r.GetLeft() > cw ) r.SetLeft( 0 );
4284         r.SetRight( wxMin( r.GetRight(), cw ) );
4285 #endif
4286 
4287         // logical bounds of update region
4288         int left;
4289         CalcUnscrolledPosition( r.GetLeft(), 0, &left, NULL );
4290         int right = left + r.GetWidth();
4291         //CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
4292 
4293         // find the cells within these bounds
4294         for ( int col = XToGridCol(left, true); col < numCols;  col++ )
4295         {
4296             if ( GetColRight(col) < left )
4297                 continue;
4298             if ( GetColLeft(col) > right )
4299                 break;
4300 
4301             if (spanned)
4302             {
4303                 const wxSheetBlock block(GetCellBlock(wxSheetCoords(-1, col)));
4304 
4305                 if (block.GetWidth() >= 1)
4306                 {
4307                     colLabels.Add( block.GetLeft() );
4308                     col = block.GetRight();
4309                 }
4310                 else if (block.GetWidth() < 1)
4311                     col = block.GetRight();
4312             }
4313             else
4314                 colLabels.Add( col );
4315         }
4316 
4317         iter++ ;
4318     }
4319     return colLabels.GetCount() > 0u;
4320 }
4321 
CalcCellsExposed(const wxRegion & reg,wxSheetSelection & blockSel) const4322 bool wxSheet::CalcCellsExposed( const wxRegion& reg, wxSheetSelection& blockSel ) const
4323 {
4324     const int numRows = GetNumberRows();
4325     const int numCols = GetNumberCols();
4326     if (!numRows || !numCols)
4327         return false;
4328 
4329     wxRegionIterator iter( reg );
4330     while ( iter )
4331     {
4332         wxRect r(iter.GetRect());
4333 
4334         // TODO: remove this when we can...
4335         // There is a bug in wxMotif that gives garbage update
4336         // rectangles if you jump-scroll a long way by clicking the
4337         // scrollbar with middle button.  This is a work-around
4338         //
4339 #if defined(__WXMOTIF__)
4340         int cw, ch;
4341         m_gridWin->GetClientSize( &cw, &ch );
4342         if ( r.GetTop() > ch ) r.SetTop( 0 );
4343         if ( r.GetLeft() > cw ) r.SetLeft( 0 );
4344         r.SetRight( wxMin( r.GetRight(), cw ) );
4345         r.SetBottom( wxMin( r.GetBottom(), ch ) );
4346 #endif
4347 
4348         // logical bounds of update region
4349         int left, top, right, bottom;
4350         CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
4351         CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
4352 
4353         // find the cells within these bounds
4354         wxSheetBlock block(YToGridRow(top, true), XToGridCol(left, true), 0, 0);
4355         int row, col;
4356         for ( row = block.GetTop(); row < numRows; row++ )
4357         {
4358             if ( GetRowBottom(row) <= top )
4359                 continue;
4360             else if ( GetRowTop(row) > bottom )
4361                 break;
4362         }
4363 
4364         for ( col = block.GetLeft(); col < numCols; col++ )
4365         {
4366             if ( GetColRight(col) <= left )
4367                 continue;
4368             else if ( GetColLeft(col) > right )
4369                 break;
4370         }
4371 
4372         block.SetRight(col - 1);
4373         block.SetBottom(row - 1);
4374 
4375         blockSel.SelectBlock(block, false);
4376 
4377         iter++;
4378     }
4379 
4380     return blockSel.GetCount() > 0;
4381 }
4382 
DrawTextRectangle(wxDC & dc,const wxString & value,const wxRect & rect,int align,int textOrientation)4383 void wxSheet::DrawTextRectangle( wxDC& dc, const wxString& value,
4384                                  const wxRect& rect, int align,
4385                                  int textOrientation )
4386 {
4387     wxArrayString lines;
4388     if (StringToLines( value, lines ) > 0)
4389         DrawTextRectangle( dc, lines, rect, align, textOrientation );
4390 }
4391 
DrawTextRectangle(wxDC & dc,const wxArrayString & lines,const wxRect & rect,int align,int textOrientation)4392 void wxSheet::DrawTextRectangle( wxDC& dc, const wxArrayString& lines,
4393                                  const wxRect& rect, int align,
4394                                  int textOrientation )
4395 {
4396     int nLines = lines.GetCount();
4397     if ( (nLines == 0) || ((nLines == 1) && lines[0].IsEmpty()) )
4398         return;
4399 
4400     dc.SetClippingRegion( rect );
4401 
4402     int l;
4403     float x = 0.0, y = 0.0;
4404     long textWidth=0, textHeight=0;
4405     wxCoord lineWidth=0, lineHeight=0;
4406     wxArrayInt lineWidths, lineHeights;
4407 
4408     // Measure the text extent once, Gtk2 is slow (takes 2sec off 23sec run)
4409     for ( l = 0; l < nLines; l++ )
4410     {
4411         dc.GetTextExtent(lines[l], &lineWidth, &lineHeight);
4412         lineWidths.Add(lineWidth);
4413         lineHeights.Add(lineHeight);
4414         textHeight += lineHeight;
4415         if (lineWidth > textWidth)
4416             textWidth = lineWidth;
4417     }
4418 
4419     // swap width and height if vertically orientated
4420     if ( textOrientation == wxVERTICAL )
4421     {
4422         long tmp = textHeight;
4423         textHeight = textWidth;
4424         textWidth = tmp;
4425     }
4426 
4427     if ((align & wxALIGN_BOTTOM) != 0)
4428     {
4429         if ( textOrientation == wxHORIZONTAL )
4430             y = rect.y + (rect.height - textHeight - 1);
4431         else // wxVERTICAL
4432             x = rect.x + rect.width - textWidth;
4433     }
4434     else if ((align & wxALIGN_CENTRE_VERTICAL) != 0)
4435     {
4436         if ( textOrientation == wxHORIZONTAL )
4437             y = rect.y + (rect.height - textHeight)/2;
4438         else // wxVERTICAL
4439             x = rect.x + (rect.width - textWidth)/2;
4440     }
4441     else // wxALIGN_TOP
4442     {
4443         if ( textOrientation == wxHORIZONTAL )
4444             y = rect.y + 1;
4445         else // wxVERTICAL
4446             x = rect.x + 1;
4447     }
4448 
4449     // Align each line of a multi-line label
4450     for ( l = 0; l < nLines; l++ )
4451     {
4452         lineWidth  = lineWidths[l];
4453         lineHeight = lineHeights[l];
4454 
4455         if ((align & wxALIGN_RIGHT) != 0)
4456         {
4457             if ( textOrientation == wxHORIZONTAL )
4458                 x = rect.x + (rect.width - lineWidth - 1);
4459             else // wxVERTICAL
4460                 y = rect.y + lineWidth + 1;
4461         }
4462         else if ((align & wxALIGN_CENTRE_HORIZONTAL) != 0)
4463         {
4464             if ( textOrientation == wxHORIZONTAL )
4465                 x = rect.x + (rect.width - lineWidth)/2;
4466             else // wxVERTICAL
4467                 y = rect.y + rect.height - (rect.height - lineWidth)/2;
4468         }
4469         else // wxALIGN_LEFT
4470         {
4471             if ( textOrientation == wxHORIZONTAL )
4472                 x = rect.x + 1;
4473             else // wxVERTICAL
4474                 y = rect.y + rect.height - 1;
4475         }
4476 
4477         if ( textOrientation == wxHORIZONTAL )
4478         {
4479             dc.DrawText( lines[l], (int)x, (int)y );
4480             y += lineHeight;
4481         }
4482         else // wxVERTICAL
4483         {
4484             dc.DrawRotatedText( lines[l], (int)x, (int)y, 90.0 );
4485             x += lineHeight;
4486         }
4487     }
4488 
4489     dc.DestroyClippingRegion();
4490 }
4491 
StringToLines(const wxString & value,wxArrayString & lines) const4492 int wxSheet::StringToLines( const wxString& value, wxArrayString& lines ) const
4493 {
4494     size_t len = value.Length();
4495     if (!len)
4496         return 0;
4497 
4498     const wxChar *c = value.GetData();
4499     size_t pos, count = 0, start_pos = 0;
4500 
4501     for (pos = 0; pos < len; pos++, c++)
4502     {
4503         if ((*c == wxT('\n') || (*c == wxT('\r'))))
4504         {
4505             if (pos == start_pos)
4506                 lines.Add(wxEmptyString);
4507             else
4508                 lines.Add(value.Mid(start_pos, pos - start_pos));
4509 
4510             start_pos = pos + 1;
4511             count++;
4512 
4513             // Check for DOS line endings and skip them
4514             if ((*c == wxT('\r')) && (pos + 1 < len) && (c[1] == wxT('\n')))
4515             {
4516                 c++;
4517                 pos++;
4518                 start_pos++;
4519             }
4520         }
4521     }
4522 
4523     if ( start_pos < len )
4524     {
4525         if (start_pos == 0)
4526             lines.Add(value);
4527         else
4528             lines.Add(value.Mid(start_pos));
4529 
4530         count++;
4531     }
4532 
4533     return count;
4534 }
4535 
GetTextBoxSize(wxDC & dc,const wxArrayString & lines,long * width,long * height) const4536 bool wxSheet::GetTextBoxSize( wxDC& dc, const wxArrayString& lines,
4537                               long *width, long *height ) const
4538 {
4539     long w = 0, h = 0;
4540     wxCoord lineW, lineH;
4541     size_t i, count = lines.GetCount();
4542     for ( i = 0; i < count; i++ )
4543     {
4544         dc.GetTextExtent( lines[i], &lineW, &lineH );
4545         if (w < lineW) w = lineW;
4546         h += lineH;
4547     }
4548 
4549     if (width)  *width  = w;
4550     if (height) *height = h;
4551 
4552     return (w > 0) && (h > 0);
4553 }
4554 
4555 // ----------------------------------------------------------------------------
4556 
XYToGridCell(int x,int y,bool clipToMinMax) const4557 wxSheetCoords wxSheet::XYToGridCell( int x, int y, bool clipToMinMax ) const
4558 {
4559     return wxSheetCoords(YToGridRow(y, clipToMinMax), XToGridCol(x, clipToMinMax));
4560 }
YToGridRow(int y,bool clipToMinMax) const4561 int wxSheet::YToGridRow( int y, bool clipToMinMax ) const
4562 {
4563     return GetSheetRefData()->m_rowEdges.FindIndex(y, clipToMinMax);
4564 }
XToGridCol(int x,bool clipToMinMax) const4565 int wxSheet::XToGridCol( int x, bool clipToMinMax ) const
4566 {
4567     return GetSheetRefData()->m_colEdges.FindIndex(x, clipToMinMax);
4568 }
YToEdgeOfGridRow(int y) const4569 int wxSheet::YToEdgeOfGridRow( int y ) const
4570 {
4571     return GetSheetRefData()->m_rowEdges.FindMaxEdgeIndex(y);
4572 }
XToEdgeOfGridCol(int x) const4573 int wxSheet::XToEdgeOfGridCol( int x ) const
4574 {
4575     return GetSheetRefData()->m_colEdges.FindMaxEdgeIndex(x);
4576 }
4577 
CellToRect(const wxSheetCoords & coords,bool getDeviceRect) const4578 wxRect wxSheet::CellToRect( const wxSheetCoords& coords, bool getDeviceRect ) const
4579 {
4580     wxCHECK_MSG(ContainsCell(coords), wxRect(0,0,0,0), wxT("Invalid coords"));
4581     return BlockToRect(GetCellBlock(coords), getDeviceRect);
4582 }
4583 
BlockToRect(const wxSheetBlock & block,bool getDeviceRect) const4584 wxRect wxSheet::BlockToRect( const wxSheetBlock& block, bool getDeviceRect ) const
4585 {
4586     wxRect rect(BlockToLogicalRect(block, false));
4587 
4588     // if grid lines are enabled, then the area of the cell is a bit smaller
4589     if ((GridLinesEnabled() & wxHORIZONTAL) != 0)
4590         rect.height--;
4591     if ((GridLinesEnabled() & wxVERTICAL) != 0)
4592         rect.width--;
4593 
4594     if (getDeviceRect)
4595     {
4596         switch (GetCellCoordsType(block.GetLeftTop()))
4597         {
4598             case wxSHEET_CELL_GRID     : return CalcScrolledRect(rect);
4599             case wxSHEET_CELL_ROWLABEL : CalcScrolledPosition(0, rect.y, NULL, &rect.y); break;
4600             case wxSHEET_CELL_COLLABEL : CalcScrolledPosition(rect.x, 0, &rect.x, NULL); break;
4601             default : break;
4602         }
4603     }
4604 
4605     return rect;
4606 }
4607 
ExpandSpannedBlock(const wxSheetBlock & block_) const4608 wxSheetBlock wxSheet::ExpandSpannedBlock(const wxSheetBlock& block_) const
4609 {
4610     if (!HasSpannedCells() || block_.IsEmpty())
4611         return block_;
4612 
4613     wxSheetBlock block(block_);
4614 
4615     if (GetSpannedBlocks())
4616     {
4617         const wxSheetSelection* spannedBlocks = GetSpannedBlocks();
4618         size_t n, count = spannedBlocks->GetCount();
4619         for (n=0; n<count; n++)
4620         {
4621             const wxSheetBlock& b = spannedBlocks->GetBlock(n);
4622             if (block_.Intersects(b)) // use original block so it doesn't keep growing
4623                 block = block.Union(b);
4624         }
4625     }
4626     else // brute force expansion
4627     {
4628         wxSheetCoords c;
4629         int row_bottom = block_.GetBottom();
4630         int col_right  = block_.GetRight();
4631         for (c.m_row = block_.GetTop(); c.m_row <= row_bottom; c.m_row++)
4632         {
4633             for (c.m_col = block_.GetLeft(); c.m_col <= col_right; c.m_col++)
4634             {
4635                 block = block.Union(GetCellBlock(c));
4636             }
4637         }
4638     }
4639 
4640     return block;
4641 }
4642 
BlockToLogicalRect(const wxSheetBlock & block_,bool expand_spanned) const4643 wxRect wxSheet::BlockToLogicalRect( const wxSheetBlock& block_, bool expand_spanned ) const
4644 {
4645     wxSheetBlock block(expand_spanned ? ExpandSpannedBlock(block_) : block_);
4646 
4647     wxRect rect(GetColLeft(block.GetLeft()), GetRowTop(block.GetTop()), 0, 0);
4648     rect.width  = GetColRight(block.GetRight())   - rect.x + 1;
4649     rect.height = GetRowBottom(block.GetBottom()) - rect.y + 1;
4650     return rect;
4651 }
4652 
BlockToDeviceRect(const wxSheetBlock & block_,bool expand_spanned) const4653 wxRect wxSheet::BlockToDeviceRect( const wxSheetBlock &block_, bool expand_spanned) const
4654 {
4655     // merely convert to scrolled coords
4656     return CalcScrolledRect(BlockToLogicalRect(block_, expand_spanned));
4657 }
4658 
LogicalGridRectToBlock(const wxRect & rect,bool wholeCell) const4659 wxSheetBlock wxSheet::LogicalGridRectToBlock(const wxRect &rect, bool wholeCell) const
4660 {
4661     const int numRows = GetNumberRows();
4662     const int numCols = GetNumberCols();
4663     if (!numRows || !numCols) return wxNullSheetBlock;
4664 
4665     int leftCol   = XToGridCol(rect.GetLeft(),   true);
4666     int topRow    = YToGridRow(rect.GetTop(),    true);
4667     int rightCol  = XToGridCol(rect.GetRight(),  true);
4668     int bottomRow = YToGridRow(rect.GetBottom(), true);
4669 
4670     if (wholeCell)
4671     {
4672         if (GetColLeft(leftCol) < rect.GetLeft())
4673             leftCol++;
4674         if (GetColRight(rightCol) > rect.GetRight())
4675             rightCol--;
4676         if (GetRowTop(topRow) < rect.GetTop())
4677             topRow++;
4678         if (GetRowBottom(bottomRow) > rect.GetBottom())
4679             bottomRow--;
4680     }
4681 
4682     wxSheetBlock block(topRow, leftCol, bottomRow-topRow+1, rightCol-leftCol+1);
4683     return block.Intersect(wxSheetBlock(0, 0, numRows, numCols));
4684 }
4685 
GetVisibleGridCellsBlock(bool wholeCellVisible) const4686 wxSheetBlock wxSheet::GetVisibleGridCellsBlock(bool wholeCellVisible) const
4687 {
4688     wxRect rect(CalcUnscrolledRect(wxRect(wxPoint(0,0), m_gridWin->GetClientSize())));
4689     return LogicalGridRectToBlock(rect, wholeCellVisible);
4690 }
4691 
AlignInRect(int align,const wxRect & rect,const wxSize & size,bool inside) const4692 wxPoint wxSheet::AlignInRect( int align, const wxRect& rect, const wxSize& size, bool inside ) const
4693 {
4694     wxPoint origin(rect.x, rect.y);
4695 
4696     // if it won't fit horizontally, then it must be aligned left
4697     if (inside && (size.x > rect.width))
4698     {
4699         align &= ~wxALIGN_RIGHT;
4700         align &= ~wxALIGN_CENTRE_HORIZONTAL;
4701     }
4702     // if it won't fit vertically, then it must be aligned to the top
4703     if (inside && (size.y > rect.height))
4704     {
4705         align &= ~wxALIGN_BOTTOM;
4706         align &= ~wxALIGN_CENTRE_VERTICAL;
4707     }
4708 
4709     if ((align & wxALIGN_RIGHT) != 0)
4710         origin.x += rect.width - size.x - 1;
4711     else if ((align & wxALIGN_CENTRE_HORIZONTAL) != 0)
4712         origin.x += (rect.width - size.x)/2;
4713     //else // wxALIGN_LEFT
4714 
4715     if ((align & wxALIGN_BOTTOM) != 0)
4716         origin.y += rect.height - size.y - 1;
4717     else if ((align & wxALIGN_CENTRE_VERTICAL) != 0)
4718         origin.y += (rect.height - size.y)/2;
4719     //else // wxALIGN_TOP
4720 
4721     return origin;
4722 }
4723 
4724 // ----------------------------------------------------------------------------
4725 // Scrolling functions
4726 
GetGridVirtualSize(bool add_margin) const4727 wxSize wxSheet::GetGridVirtualSize(bool add_margin) const
4728 {
4729     wxSize size;
4730     const int numRows = GetNumberRows();
4731     const int numCols = GetNumberCols();
4732     if ((numCols > 0) && (numRows > 0))
4733     {
4734         size.x = GetColRight(numCols - 1);
4735         size.y = GetRowBottom(numRows - 1);
4736     }
4737 
4738     if (add_margin)
4739     {
4740         size.x += GetSheetRefData()->m_marginSize.x;
4741         size.y += GetSheetRefData()->m_marginSize.y;
4742     }
4743 
4744     return size;
4745 }
4746 
GetGridExtent() const4747 wxSize wxSheet::GetGridExtent() const
4748 {
4749     wxSize s(GetGridVirtualSize(true));
4750     if (GetGridWindow())
4751     {
4752         wxSize winSize(GetGridWindow()->GetSize());
4753         if (winSize.x > s.x) s.x = winSize.x;
4754         if (winSize.y > s.y) s.y = winSize.y;
4755     }
4756     return s;
4757 }
4758 
SetGridOrigin(int x,int y,bool adjustScrollBars,bool sendEvt)4759 void wxSheet::SetGridOrigin( int x, int y, bool adjustScrollBars, bool sendEvt )
4760 {
4761     if (!m_gridWin)
4762         return;
4763 
4764     // normally -1 for don't change
4765     if (x == -1) x = m_gridOrigin.x;
4766     if (y == -1) y = m_gridOrigin.y;
4767 
4768     // during OnSize gridWin isn't resized yet
4769     int cw, ch;
4770     GetClientSize( &cw, &ch );
4771     cw -= GetRowLabelWidth();
4772     ch -= GetColLabelHeight();
4773     if ( m_vertScrollBar->IsShown() )
4774         cw -= m_vertScrollBar->GetSize().x;
4775     if ( m_horizScrollBar->IsShown() )
4776         ch -= m_horizScrollBar->GetSize().y;
4777 
4778     // Force fitting, don't allow scrolling out of bounds
4779     wxSize virtSize(GetGridVirtualSize());
4780     if ((x < 0) || (virtSize.x < cw))
4781         x = 0;
4782     else if (x > virtSize.x-cw)
4783         x = virtSize.x - cw;
4784     if ((y < 0) || (virtSize.y < ch))
4785         y = 0;
4786     else if (y > virtSize.y-ch)
4787         y = virtSize.y - ch;
4788 
4789     int dx = m_gridOrigin.x - x;
4790     int dy = m_gridOrigin.y - y;
4791 
4792     if ((dx == 0) && (dy == 0))
4793         return;
4794 
4795     m_gridOrigin.x = x;
4796     m_gridOrigin.y = y;
4797 
4798 /*
4799     wxRect rect( (dx >= 0) ? 0 : cw+dx,
4800                  (dy >= 0) ? 0 : ch+dy,
4801                  dy != 0 ? cw : abs(dx),
4802                  dx != 0 ? ch : abs(dy) );
4803 */
4804 
4805     if (adjustScrollBars)
4806         AdjustScrollbars();
4807 
4808     // FIXME - or at least check, GTK calcs rect for you, does MSW?
4809     m_gridWin->ScrollWindow( dx, dy ); //, &rect );
4810     if (dx != 0)
4811         m_colLabelWin->ScrollWindow( dx, 0 ); //, &rect );
4812     if (dy != 0)
4813         m_rowLabelWin->ScrollWindow( 0, dy ); //, &rect );
4814 
4815     // Let the windows refresh before next scroll event, otherwise windows
4816     //  don't line up
4817     //wxYieldIfNeeded();
4818 
4819     if (sendEvt)
4820         SendEvent(wxEVT_SHEET_VIEW_CHANGED, GetGridCursorCell());
4821 }
4822 
OnScroll(wxScrollEvent & event)4823 void wxSheet::OnScroll( wxScrollEvent &event )
4824 {
4825     if (!m_gridWin)
4826         return;
4827 
4828     event.Skip();
4829 
4830     int pos = event.GetPosition();
4831     //wxPrintf(wxT("Pos %d %d Length %d Left %d Right %d\n"), pos, m_horizScrollBar->GetThumbPosition(), m_horizScrollBar->GetThumbSize(), pos*m_horizScrollBar->GetThumbSize(), pos*m_horizScrollBar->GetThumbSize()+GetGridWindow()->GetClientSize().x);
4832 
4833     if (event.GetId() == ID_HORIZ_SCROLLBAR)
4834         SetGridOrigin( pos*15, -1, false, true );
4835         //SetGridOrigin( pos*m_horizScrollBar->GetThumbSize(), -1, false, true );
4836     else if (event.GetId() == ID_VERT_SCROLLBAR)
4837         SetGridOrigin( -1, pos*15, false, true );
4838         //SetGridOrigin( -1, pos*m_vertScrollBar->GetThumbSize(), false, true );
4839 }
4840 
AdjustScrollbars(bool calc_win_sizes)4841 void wxSheet::AdjustScrollbars(bool calc_win_sizes)
4842 {
4843     if (!m_gridWin || m_resizing)
4844         return;
4845 
4846     m_resizing = true;
4847 
4848     bool horizSbShown = m_horizScrollBar->IsShown();
4849     bool vertSbShown  = m_vertScrollBar->IsShown();
4850     int  sb_width  = m_vertScrollBar->GetSize().x;
4851     int  sb_height = m_horizScrollBar->GetSize().y;
4852 
4853     int cw, ch;
4854     GetClientSize( &cw, &ch );
4855 
4856     // Grid window width and height, may be in OnSize so not sized yet
4857     int gw = cw - GetRowLabelWidth();
4858     int gh = ch - GetColLabelHeight();
4859 
4860     // grid total size
4861     wxSize virtSize(GetGridVirtualSize());
4862 /*
4863     // take into account editor if shown // FIXME what is this?
4864     if ( 0 && IsCellEditControlShown() )
4865     {
4866         int w2, h2;
4867         int r = GetGridCursorRow();
4868         int c = GetGridCursorCol();
4869         int x = GetColLeft(c);
4870         int y = GetRowTop(r);
4871 
4872         // how big is the editor
4873         GetEditControl().GetControl()->GetSize(&w2, &h2);
4874         w2 += x;
4875         h2 += y;
4876         if( w2 > virtSize.x ) virtSize.x = w2;
4877         if( h2 > virtSize.y ) virtSize.y = h2;
4878     }
4879 */
4880     // Figure out if we need the scrollbars at all
4881     bool need_Xscroll = (m_scrollBarMode & SB_HORIZ_NEVER) != 0 ? false :
4882         (((m_scrollBarMode & SB_HORIZ_ALWAYS) != 0) ? true : virtSize.x > gw);
4883     bool need_Yscroll = (m_scrollBarMode & SB_VERT_NEVER ) != 0 ? false :
4884         (((m_scrollBarMode & SB_VERT_ALWAYS ) != 0) ? true : virtSize.y > gh);
4885 
4886     // Now cut down size due to the scrollbars if shown
4887     if (need_Xscroll) gh -= sb_height;
4888     if (need_Yscroll) gw -= sb_width;
4889 
4890     // Maybe now that it's smaller we need the other scrollbar
4891     need_Xscroll = (m_scrollBarMode & SB_HORIZ_NEVER) != 0 ? false :
4892         (((m_scrollBarMode & SB_HORIZ_ALWAYS) != 0) ? true : virtSize.x > gw);
4893     need_Yscroll = (m_scrollBarMode & SB_VERT_NEVER ) != 0 ? false :
4894         (((m_scrollBarMode & SB_VERT_ALWAYS ) != 0) ? true : virtSize.y > gh);
4895 
4896     //if (need_Xscroll) ch -= sb_height;
4897     //if (need_Yscroll) cw -= sb_width;
4898     //bool horiz_splitter = need_Xscroll && m_enable_split_horiz;
4899     //bool vert_splitter  = need_Yscroll && m_enable_split_vert;
4900 
4901     // width and height of the horiz and vert scrollbars
4902     //int sw = cw - (vert_splitter  ? SPLIT_BUTTON_WIDTH : 0);
4903     //int sh = ch - (horiz_splitter ? SPLIT_BUTTON_WIDTH : 0);
4904 
4905     // set scrollbar parameters
4906     int thumbX = SHEET_SCROLL_LINE_X;
4907     int thumbY = SHEET_SCROLL_LINE_Y;
4908 
4909     //virtSize.x += sw - gw;
4910     //virtSize.y += sh - gh;
4911 
4912     // FIXME this is wrong for GTK and MSW, but why?
4913     int rangeX = !need_Xscroll ? 0 : 1 + (virtSize.x - gw + thumbX - 1)/thumbX;
4914     int rangeY = !need_Yscroll ? 0 : 1 + (virtSize.y - gh + thumbY - 1)/thumbY;
4915 
4916     int pageX = int((gw * 0.9) / thumbX);
4917     int pageY = int((gh * 0.9) / thumbY);
4918 
4919     int posX = m_gridOrigin.x/thumbX;
4920     int posY = m_gridOrigin.y/thumbY;
4921 
4922     thumbX = 1;
4923     thumbY = 1;
4924 
4925     //wxPrintf(wxT("Pos %d %d, virtSize %d %d, range %d %d, thumb %d %d page %d %d, win %d %d \n"),
4926     //    posX, posY, virtSize.x, virtSize.y, rangeX, rangeY, thumbX, thumbY, pageX, pageY, gw, gh);
4927 
4928     if (need_Xscroll)
4929         m_horizScrollBar->SetScrollbar(posX, thumbX, rangeX, pageX);
4930     if (need_Yscroll)
4931         m_vertScrollBar->SetScrollbar(posY, thumbY, rangeY, pageY);
4932 
4933     //wxPrintf(wxT("Set pos %d range %d, thumb %d, page %d\n"),
4934     //    m_horizScrollBar->GetThumbPosition(), m_horizScrollBar->GetRange(), m_horizScrollBar->GetThumbSize(), m_horizScrollBar->GetPageSize());
4935 
4936     // recalculate the windows sizes and positions if we added/removed scrollbar
4937     bool calcSizes = false;
4938 
4939     if (need_Xscroll != horizSbShown)
4940     {
4941         calcSizes = true;
4942         m_horizScrollBar->Show(need_Xscroll);
4943     }
4944     if (need_Yscroll != vertSbShown)
4945     {
4946         calcSizes = true;
4947         m_vertScrollBar->Show(need_Yscroll);
4948     }
4949 
4950     m_resizing = false;
4951 
4952     if (calcSizes && calc_win_sizes)
4953         CalcWindowSizes(false);
4954 }
4955 
PrepareGridDC(wxDC & dc)4956 void wxSheet::PrepareGridDC( wxDC& dc )
4957 {
4958     dc.SetDeviceOrigin( -m_gridOrigin.x, -m_gridOrigin.y );
4959 }
4960 
PrepareRowLabelDC(wxDC & dc)4961 void wxSheet::PrepareRowLabelDC( wxDC& dc )
4962 {
4963     dc.SetDeviceOrigin( 0, -m_gridOrigin.y );
4964 }
4965 
PrepareColLabelDC(wxDC & dc)4966 void wxSheet::PrepareColLabelDC( wxDC& dc )
4967 {
4968     dc.SetDeviceOrigin( -m_gridOrigin.x, 0 );
4969 }
4970 
4971 // ----------------------------------------------------------------------------
4972 
4973 // Checks and returns a suitable horiz or vert alignment, if invalid replaces
4974 //   with wxSheetCellAttr::NoHorizAlign/NoVertAlign
CheckAlignment(int align)4975 static int CheckAlignment(int align)
4976 {
4977     // note: it's a shame that wxALIGN_LEFT = wxALIGN_TOP = 0
4978 
4979     int count = 0;
4980     if ((align & wxSHEET_AttrAlignRight)       != 0) count++;
4981     if ((align & wxSHEET_AttrAlignCenterHoriz) != 0) count++;
4982     if ((align & wxSHEET_AttrAlignHorizUnset)  != 0) count++;
4983     if (count > 1)
4984     {
4985         align &= ~wxSHEET_AttrAlignHoriz_Mask; // clear whatever is there
4986         align |= wxSHEET_AttrAlignHorizUnset;  // set to no alignment
4987     }
4988 
4989     count = 0;
4990     if ((align & wxSHEET_AttrAlignBottom)     != 0) count++;
4991     if ((align & wxSHEET_AttrAlignCenterVert) != 0) count++;
4992     if ((align & wxSHEET_AttrAlignVertUnset)  != 0) count++;
4993     if (count > 1)
4994     {
4995         align &= ~wxSHEET_AttrAlignVert_Mask; // clear whatever is there
4996         align |= wxSHEET_AttrAlignVertUnset;  // set to no alignment
4997     }
4998 
4999     return align;
5000 }
5001 
SetAlignment(int orig_align,int hAlign,int vAlign)5002 int wxSheet::SetAlignment(int orig_align, int hAlign, int vAlign)
5003 {
5004     if (hAlign != -1)
5005     {
5006         orig_align &= ~wxSHEET_AttrAlignHoriz_Mask;           // clear old
5007         orig_align |= (hAlign & wxSHEET_AttrAlignHoriz_Mask); // set new
5008     }
5009     if (vAlign != -1)
5010     {
5011         orig_align &= ~wxSHEET_AttrAlignVert_Mask;
5012         orig_align |= (vAlign & wxSHEET_AttrAlignVert_Mask);
5013     }
5014 
5015     return CheckAlignment(orig_align);
5016 }
5017 
5018 // ----------------------------------------------------------------------------
5019 
HasFocus() const5020 bool wxSheet::HasFocus() const
5021 {
5022     wxWindow *win = FindFocus();
5023     return win && ((win==(wxSheet*)this) || (win==m_gridWin) || (win==m_rowLabelWin) ||
5024            (win == m_colLabelWin) || (win == m_cornerLabelWin));
5025 }
5026 
5027 // ----------------------------------------------------------------------------
5028 // Event handlers
OnMouse(wxMouseEvent & event)5029 void wxSheet::OnMouse( wxMouseEvent& event )
5030 {
5031     wxWindow *win = (wxWindow*)event.GetEventObject();
5032 
5033     if (win == this)
5034         ProcessSheetMouseEvent(event);
5035     else if (win == m_rowLabelWin)
5036         ProcessRowLabelMouseEvent(event);
5037     else if (win == m_colLabelWin)
5038         ProcessColLabelMouseEvent(event);
5039     else if (win == m_cornerLabelWin)
5040         ProcessCornerLabelMouseEvent(event);
5041     else if (win == m_gridWin)
5042         ProcessGridCellMouseEvent(event);
5043     else
5044         event.Skip();
5045 }
5046 
OnMouseWheel(wxMouseEvent & event)5047 void wxSheet::OnMouseWheel( wxMouseEvent& event )
5048 {
5049     wxWindow *win = (wxWindow*)event.GetEventObject();
5050 
5051     // Scroll up and down by a 1/3 of the height of the window
5052     if ((win == m_rowLabelWin) || (win == m_gridWin))
5053     {
5054         // GTK doesn't have good wheel events
5055         if (GetNumberRows() > 0)
5056         {
5057             wxPoint origin(GetGridOrigin());
5058             wxSize size(GetGridWindow()->GetClientSize());
5059             int dy = event.GetWheelRotation() < 0 ? 1 : -1;
5060             SetGridOrigin( origin.x, origin.y + dy * size.y/3, true, true );
5061         }
5062     }
5063     // Scroll sideways by a 1/3 of the width of the window
5064     else if (win == m_colLabelWin)
5065     {
5066         if (GetNumberCols() > 0)
5067         {
5068             wxPoint origin(GetGridOrigin());
5069             wxSize size(GetGridWindow()->GetClientSize());
5070             int dx = event.GetWheelRotation() < 0 ? 1 : -1;
5071             SetGridOrigin( origin.x + dx * size.x/3, origin.y, true, true );
5072         }
5073     }
5074 
5075     event.Skip();
5076 }
5077 
ProcessSheetMouseEvent(wxMouseEvent & event)5078 void wxSheet::ProcessSheetMouseEvent( wxMouseEvent& event )
5079 {
5080     if (!m_enable_split_vert && !m_enable_split_horiz)
5081         return;
5082 
5083     wxPoint mousePos = event.GetPosition();
5084 
5085     if (event.LeftDown())
5086     {
5087 #if wxCHECK_VERSION(2,7,0)
5088         if (m_vertSplitRect.Contains(mousePos) || m_horizSplitRect.Contains(mousePos))
5089 #else
5090         if (m_vertSplitRect.Inside(mousePos) || m_horizSplitRect.Inside(mousePos))
5091 #endif
5092             SetCaptureWindow(this);
5093     }
5094     else if (event.LeftUp())
5095     {
5096         SetCaptureWindow(NULL);
5097     }
5098     else if (event.Dragging() && HasCapture() && HasMouseCursorMode(WXSHEET_CURSOR_SPLITTING))
5099     {
5100         wxSheetSplitterEvent splitEvent(GetId(), wxEVT_SHEET_SPLIT_BEGIN);
5101         splitEvent.SetEventObject(this);
5102         splitEvent.m_vert_split = HasMouseCursorMode(WXSHEET_CURSOR_SPLIT_VERTICAL);
5103 
5104         SetCaptureWindow(NULL);
5105         SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, this);
5106         GetEventHandler()->ProcessEvent(splitEvent);
5107     }
5108     else if ((event.Leaving() || event.Entering()) && !HasCapture())
5109     {
5110         SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, this);
5111     }
5112     else if (event.Moving() && !HasCapture())
5113     {
5114 #if wxCHECK_VERSION(2,7,0)
5115         if (m_vertSplitRect.Contains(mousePos))
5116             SetMouseCursorMode(WXSHEET_CURSOR_SPLIT_VERTICAL, this);
5117         else if (m_horizSplitRect.Contains(mousePos))
5118             SetMouseCursorMode(WXSHEET_CURSOR_SPLIT_HORIZONTAL, this);
5119         else
5120             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, this);
5121 #else
5122         if (m_vertSplitRect.Inside(mousePos))
5123             SetMouseCursorMode(WXSHEET_CURSOR_SPLIT_VERTICAL, this);
5124         else if (m_horizSplitRect.Inside(mousePos))
5125             SetMouseCursorMode(WXSHEET_CURSOR_SPLIT_HORIZONTAL, this);
5126         else
5127             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, this);
5128 #endif
5129     }
5130 }
5131 
ProcessRowLabelMouseEvent(wxMouseEvent & event)5132 void wxSheet::ProcessRowLabelMouseEvent( wxMouseEvent& event )
5133 {
5134     int x, y;
5135     m_mousePos = event.GetPosition();
5136     CalcUnscrolledPosition( m_mousePos.x, m_mousePos.y, &x, &y );
5137     wxSheetCoords coords(YToGridRow(y), -1);
5138 
5139     if ( event.Entering() || event.Leaving() )
5140     {
5141         if (!event.Dragging() && !m_isDragging)
5142             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_rowLabelWin);
5143 
5144         return;
5145     }
5146 
5147     if ( event.Dragging() && event.LeftIsDown() )
5148     {
5149         if (!m_isDragging)
5150         {
5151             m_isDragging = true;
5152             SetCaptureWindow(m_rowLabelWin);
5153         }
5154 
5155         if ( event.LeftIsDown() )
5156         {
5157             bool can_scroll = true;
5158 
5159             switch ( GetMouseCursorMode() )
5160             {
5161                 case WXSHEET_CURSOR_RESIZE_ROW:
5162                 {
5163                     y = wxMax( y, GetRowTop(m_dragRowOrCol) +
5164                                   GetMinimalRowHeight(m_dragRowOrCol));
5165                     can_scroll = false; // y != m_dragLastPos;
5166                     DrawRowColResizingMarker( y );
5167                     break;
5168                 }
5169                 case WXSHEET_CURSOR_SELECT_ROW:
5170                 {
5171                     if (HasSelectionMode(wxSHEET_SelectNone|wxSHEET_SelectCols))
5172                         break;
5173 
5174                     // check for clearing here since we didn't if editing allowed
5175                     bool add = event.ShiftDown() || event.ControlDown();
5176                     if (HasSelection(false) && !add)
5177                         ClearSelection(true);
5178 
5179                     if (ContainsGridCell(GetSelectingAnchor()) && ContainsRowLabelCell(coords))
5180                     {
5181                         HighlightSelectingBlock(GetSelectingAnchor(),
5182                                                 wxSheetCoords(coords.m_row, GetNumberCols()+1));
5183                     }
5184                     break;
5185                 }
5186                 default:
5187                     break;
5188             }
5189 
5190             if (can_scroll &&
5191                 ((m_mousePos.y < 0) || (m_mousePos.y > m_rowLabelWin->GetClientSize().GetHeight())))
5192             {
5193                 if (!m_mouseTimer)
5194                     StartMouseTimer();
5195             }
5196             else
5197                 StopMouseTimer();
5198         }
5199         return;
5200     }
5201 
5202     StopMouseTimer();
5203     SetCaptureWindow(NULL);
5204     m_isDragging = false;
5205 
5206     if ( event.LeftDown() )
5207     {
5208         if (IsCellEditControlShown())
5209             DisableCellEditControl(true);
5210 
5211         // don't send a label click event for a hit on the edge of the row label
5212         // this is probably the user wanting to resize the row
5213         if ( YToEdgeOfGridRow(y) < 0 )
5214         {
5215             if ( ContainsRowLabelCell(coords)  &&
5216                  (SendEvent(wxEVT_SHEET_LABEL_LEFT_DOWN, coords, &event) == EVT_SKIPPED) &&
5217                  !HasSelectionMode(wxSHEET_SelectNone|wxSHEET_SelectCols) )
5218             {
5219                 bool add = event.ShiftDown() || event.ControlDown();
5220                 wxSheetBlock block;
5221 
5222                 if ( event.ShiftDown() )
5223                 {
5224                     SetSelectingAnchor(wxSheetCoords(GetGridCursorRow(), 0));
5225                     block = wxSheetBlock(GetSelectingAnchor(),
5226                                          wxSheetCoords(coords.m_row, GetNumberCols()+1));
5227                 }
5228                 else
5229                 {
5230                     SetSelectingAnchor(wxSheetCoords(coords.m_row, 0));
5231                     block = wxSheetBlock(GetSelectingAnchor(), 1, GetNumberCols()+1);
5232                 }
5233 
5234                 // if you can edit the row label then don't select row until drag
5235                 bool can_edit = CanEnableCellControl(coords);
5236                 if (!add && HasSelection() &&
5237                     (SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5238                                    block, false, false, &event) != EVT_VETOED))
5239                 {
5240                     ClearSelection(true);
5241                 }
5242 
5243                 if (!can_edit)
5244                     HighlightSelectingBlock(block);
5245 
5246                 SetMouseCursorMode(WXSHEET_CURSOR_SELECT_ROW, m_rowLabelWin);
5247             }
5248         }
5249         else
5250         {
5251             // starting to drag-resize a row
5252             if ( CanDragRowSize() )
5253             {
5254                 SetMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW, m_rowLabelWin);
5255                 SetCaptureWindow(m_rowLabelWin);
5256             }
5257         }
5258     }
5259     else if ( event.LeftDClick() )
5260     {
5261         int row = YToEdgeOfGridRow(y);
5262         if ( row < 0 )
5263         {
5264             if ( ContainsRowLabelCell(coords) &&
5265                  (SendEvent(wxEVT_SHEET_LABEL_LEFT_DCLICK, coords, &event) == EVT_SKIPPED))
5266             {
5267                 if (CanEnableCellControl(coords))
5268                 {
5269                     m_waitForSlowClick = false;
5270                     ClearSelection(true);
5271                     EnableCellEditControl(coords);
5272                     if ( IsCellEditControlCreated() )
5273                         GetSheetRefData()->m_cellEditor.StartingClick();
5274                 }
5275             }
5276         }
5277         else
5278         {
5279             // adjust row height depending on label text
5280             if (CanDragRowSize())
5281                 AutoSizeRowLabelHeight( row );
5282 
5283             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_colLabelWin);
5284             m_dragLastPos  = -1;
5285         }
5286     }
5287     else if ( event.LeftUp() )
5288     {
5289         StopMouseTimer();
5290         SetCaptureWindow(NULL);
5291 
5292         if ( HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW) )
5293         {
5294             // Note: send event *after* doing default processing in this case
5295             if (DoEndDragResizeRowCol())
5296                 SendEvent( wxEVT_SHEET_ROW_SIZE, wxSheetCoords(m_dragRowOrCol, -1), &event );
5297         }
5298         else if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_ROW) )
5299         {
5300             if (!GetSelectingBlock().IsEmpty())
5301             {
5302                 bool add = event.ShiftDown() || event.ControlDown();
5303 
5304                 if (SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5305                              GetSelectingBlock(), true, add, &event) != EVT_VETOED)
5306                 {
5307                     SelectRows(GetSelectingBlock().GetTop(), GetSelectingBlock().GetBottom(), add, true);
5308                     SetSelectingBlock(wxNullSheetBlock);
5309                 }
5310             }
5311         }
5312 
5313         SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_rowLabelWin);
5314         m_dragLastPos  = -1;
5315         if (!m_keySelecting)
5316             SetSelectingAnchor(wxNullSheetCoords);
5317 
5318         // send after default processing, they can use own evt handler
5319         SendEvent(wxEVT_SHEET_LABEL_LEFT_UP, coords, &event);
5320     }
5321     else if ( event.RightDown() )
5322     {
5323         if (IsCellEditControlCreated())
5324             DisableCellEditControl(true);
5325 
5326         if ( ContainsRowLabelCell(coords) &&
5327 	        (SendEvent(wxEVT_SHEET_LABEL_RIGHT_DOWN, coords, &event) == EVT_SKIPPED))
5328         {
5329             // no default action at the moment
5330         }
5331     }
5332     else if ( event.RightDClick() )
5333     {
5334         if (IsCellEditControlCreated())
5335             DisableCellEditControl(true);
5336 
5337         if ( ContainsRowLabelCell(coords) &&
5338 	        (SendEvent(wxEVT_SHEET_LABEL_RIGHT_DCLICK, coords, &event) == EVT_SKIPPED))
5339         {
5340             // no default action at the moment
5341         }
5342     }
5343     else if ( event.RightUp() )
5344     {
5345         if (IsCellEditControlCreated())
5346             DisableCellEditControl(true);
5347 
5348         if ( ContainsRowLabelCell(coords) &&
5349 	        (SendEvent(wxEVT_SHEET_LABEL_RIGHT_UP, coords, &event) == EVT_SKIPPED))
5350         {
5351             // no default action at the moment
5352         }
5353     }
5354     else if ( event.Moving() )
5355     {
5356         m_dragRowOrCol = YToEdgeOfGridRow( y );
5357         if ( m_dragRowOrCol >= 0 )
5358         {
5359             if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5360             {
5361                 // don't capture the mouse yet
5362                 if ( CanDragRowSize() )
5363                 {
5364                     SetMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW, m_rowLabelWin);
5365                 }
5366             }
5367         }
5368         else if ( !HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5369         {
5370             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_rowLabelWin);
5371         }
5372     }
5373 }
5374 
ProcessColLabelMouseEvent(wxMouseEvent & event)5375 void wxSheet::ProcessColLabelMouseEvent( wxMouseEvent& event )
5376 {
5377     int x, y;
5378     m_mousePos = event.GetPosition();
5379     CalcUnscrolledPosition( m_mousePos.x, m_mousePos.y, &x, &y );
5380     wxSheetCoords coords(-1, XToGridCol(x));
5381 
5382     if ( event.Entering() || event.Leaving() )
5383     {
5384         if (!event.Dragging() && !m_isDragging)
5385             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_colLabelWin);
5386 
5387         return;
5388     }
5389 
5390     if ( event.Dragging() && event.LeftIsDown() )
5391     {
5392         if (!m_isDragging)
5393         {
5394             m_isDragging = true;
5395             SetCaptureWindow(m_colLabelWin);
5396         }
5397 
5398         if ( event.LeftIsDown() )
5399         {
5400             bool can_scroll = true;
5401 
5402             switch( GetMouseCursorMode() )
5403             {
5404                 case WXSHEET_CURSOR_RESIZE_COL :
5405                 {
5406                     x = wxMax( x, GetColLeft(m_dragRowOrCol) +
5407                                   GetMinimalColWidth(m_dragRowOrCol));
5408                     can_scroll = false; // x != m_dragLastPos;
5409                     DrawRowColResizingMarker( x );
5410                     break;
5411                 }
5412                 case WXSHEET_CURSOR_SELECT_COL :
5413                 {
5414                     if (HasSelectionMode(wxSHEET_SelectNone|wxSHEET_SelectRows))
5415                         break;
5416 
5417                     // check for clearing here since we didn't if editing allowed
5418                     bool add = event.ShiftDown() || event.ControlDown();
5419                     if (HasSelection(false) && !add)
5420                         ClearSelection(true);
5421 
5422                     if (ContainsGridCell(GetSelectingAnchor()) && ContainsColLabelCell(coords))
5423                     {
5424                         HighlightSelectingBlock(GetSelectingAnchor(),
5425                                                 wxSheetCoords(GetNumberRows()+1, coords.m_col));
5426                     }
5427                     break;
5428                 }
5429                 default:
5430                     break;
5431             }
5432 
5433             if (can_scroll &&
5434                 ((m_mousePos.x < 0) || (m_mousePos.x > m_colLabelWin->GetClientSize().GetWidth())))
5435             {
5436                 if (!m_mouseTimer)
5437                     StartMouseTimer();
5438             }
5439             else
5440                 StopMouseTimer();
5441         }
5442         return;
5443     }
5444 
5445     StopMouseTimer();
5446     SetCaptureWindow(NULL);
5447     m_isDragging = false;
5448 
5449     if ( event.LeftDown() )
5450     {
5451         if (IsCellEditControlShown())
5452             DisableCellEditControl(true);
5453 
5454         // don't send a label click event for a hit on the edge of the col label
5455         //  this is probably the user wanting to resize the col
5456         if ( XToEdgeOfGridCol(x) < 0 )
5457         {
5458             if ( ContainsColLabelCell(coords) &&
5459                  (SendEvent(wxEVT_SHEET_LABEL_LEFT_DOWN, coords, &event) == EVT_SKIPPED) &&
5460                  !HasSelectionMode(wxSHEET_SelectNone|wxSHEET_SelectRows) )
5461             {
5462                 bool add = event.ShiftDown() || event.ControlDown();
5463                 wxSheetBlock block;
5464 
5465                 if ( event.ShiftDown() )
5466                 {
5467                     SetSelectingAnchor(wxSheetCoords(0, GetGridCursorCol()));
5468                     block = wxSheetBlock(GetSelectingAnchor(),
5469                                          wxSheetCoords(GetNumberRows()+1, coords.m_col));
5470                 }
5471                 else
5472                 {
5473                     SetSelectingAnchor(wxSheetCoords(0, coords.m_col));
5474                     block = wxSheetBlock(GetSelectingAnchor(), GetNumberRows()+1, 1);
5475                 }
5476 
5477                 // if you can edit the row label then don't select row
5478                 bool can_edit = CanEnableCellControl(coords);
5479                 if (!add && HasSelection() &&
5480                     (SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5481                                    block, false, false, &event) != EVT_VETOED))
5482                 {
5483                     ClearSelection(true);
5484                 }
5485 
5486                 if (!can_edit)
5487                     HighlightSelectingBlock(block);
5488 
5489                 SetMouseCursorMode(WXSHEET_CURSOR_SELECT_COL, m_colLabelWin);
5490             }
5491         }
5492         else
5493         {
5494             // starting to drag-resize a col
5495             if ( CanDragColSize() )
5496             {
5497                 SetMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL, m_colLabelWin);
5498                 SetCaptureWindow(m_colLabelWin);
5499             }
5500         }
5501     }
5502 
5503     if ( event.LeftDClick() )
5504     {
5505         int col = XToEdgeOfGridCol(x);
5506         if ( col < 0 )
5507         {
5508             if ( ContainsColLabelCell(coords) &&
5509                  (SendEvent(wxEVT_SHEET_LABEL_LEFT_DCLICK, coords, &event) == EVT_SKIPPED))
5510             {
5511                 if (CanEnableCellControl(coords))
5512                 {
5513                     m_waitForSlowClick = false;
5514                     ClearSelection(true);
5515                     EnableCellEditControl(coords);
5516                     if ( IsCellEditControlCreated() )
5517                         GetSheetRefData()->m_cellEditor.StartingClick();
5518                 }
5519             }
5520         }
5521         else
5522         {
5523             // adjust column width depending on label text
5524             if (CanDragColSize())
5525                 AutoSizeColLabelWidth( col );
5526 
5527             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_colLabelWin);
5528             m_dragLastPos  = -1;
5529         }
5530     }
5531     else if ( event.LeftUp() )
5532     {
5533         StopMouseTimer();
5534         SetCaptureWindow(NULL);
5535 
5536         if ( HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL) )
5537         {
5538             // Note: send event *after* doing default processing in this case
5539             if (DoEndDragResizeRowCol())
5540                 SendEvent( wxEVT_SHEET_COL_SIZE, wxSheetCoords(-1, m_dragRowOrCol), &event );
5541         }
5542         else if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_COL) )
5543         {
5544             if (!GetSelectingBlock().IsEmpty())
5545             {
5546                 bool add = event.ShiftDown() || event.ControlDown();
5547 
5548                 if (SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5549                              GetSelectingBlock(), true, add, &event) != EVT_VETOED)
5550                 {
5551                     SelectCols(GetSelectingBlock().GetLeft(), GetSelectingBlock().GetRight(), add, true);
5552                     SetSelectingBlock(wxNullSheetBlock);
5553                 }
5554             }
5555         }
5556 
5557         SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_colLabelWin);
5558         m_dragLastPos  = -1;
5559         if (!m_keySelecting)
5560             SetSelectingAnchor(wxNullSheetCoords);
5561 
5562         // send after default processing, they can use own evt handler
5563         SendEvent(wxEVT_SHEET_LABEL_LEFT_UP, coords, &event);
5564     }
5565     else if ( event.RightDown() )
5566     {
5567         if (IsCellEditControlCreated())
5568             DisableCellEditControl(true);
5569 
5570         if (ContainsColLabelCell(coords) &&
5571             (SendEvent(wxEVT_SHEET_LABEL_RIGHT_DOWN, coords, &event) == EVT_SKIPPED))
5572         {
5573             // no default action at the moment
5574         }
5575     }
5576     else if ( event.RightDClick() )
5577     {
5578         if (IsCellEditControlCreated())
5579             DisableCellEditControl(true);
5580 
5581         if (ContainsColLabelCell(coords) &&
5582             (SendEvent(wxEVT_SHEET_LABEL_RIGHT_DCLICK, coords, &event) == EVT_SKIPPED))
5583         {
5584             // no default action at the moment
5585         }
5586     }
5587     else if ( event.RightUp() )
5588     {
5589         if (IsCellEditControlCreated())
5590             DisableCellEditControl(true);
5591 
5592         if (ContainsColLabelCell(coords) &&
5593             (SendEvent(wxEVT_SHEET_LABEL_RIGHT_UP, coords, &event) == EVT_SKIPPED))
5594         {
5595             // no default action at the moment
5596         }
5597     }
5598     else if ( event.Moving() )
5599     {
5600         m_dragRowOrCol = XToEdgeOfGridCol( x );
5601         if ( m_dragRowOrCol >= 0 )
5602         {
5603             if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5604             {
5605                 // don't capture the cursor yet
5606                 if ( CanDragColSize() )
5607                     SetMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL, m_colLabelWin);
5608             }
5609         }
5610         else if ( !HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5611             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_colLabelWin);
5612     }
5613 }
5614 
ProcessCornerLabelMouseEvent(wxMouseEvent & event)5615 void wxSheet::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
5616 {
5617     wxSheetCoords coords(-1,-1);
5618 
5619     if ( event.Dragging() )
5620     {
5621         if ( event.LeftIsDown() )
5622         {
5623             if ((GetNumberRows() > 0) && (GetNumberCols() > 0) && !HasSelectionMode(wxSHEET_SelectNone))
5624             {
5625                 SetSelectingAnchor(wxSheetCoords(0, 0));
5626                 HighlightSelectingBlock(GetSelectingAnchor(),
5627                                         wxSheetCoords(GetNumberRows()+1, GetNumberCols()+1));
5628             }
5629         }
5630         return;
5631     }
5632 
5633     if ( event.LeftDown() )
5634     {
5635         SetSelectingBlock(wxNullSheetBlock);
5636 
5637         if ( SendEvent(wxEVT_SHEET_LABEL_LEFT_DOWN, coords, &event) == EVT_SKIPPED)
5638         {
5639             wxSheetBlock block(0, 0, GetNumberRows()+1, GetNumberCols()+1);
5640 
5641             // if you can edit the corner label then don't select everything
5642             bool can_edit = CanEnableCellControl(coords);
5643             if (!can_edit && !HasSelectionMode(wxSHEET_SelectNone) &&
5644                 (SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5645                                block, true, false, &event) != EVT_VETOED))
5646             {
5647                 SelectAll(true);
5648             }
5649         }
5650     }
5651     else if ( event.LeftDClick() )
5652     {
5653         if (SendEvent(wxEVT_SHEET_LABEL_LEFT_DCLICK, coords, &event) == EVT_SKIPPED)
5654         {
5655             if (CanEnableCellControl(coords))
5656             {
5657                 ClearSelection(true);
5658                 EnableCellEditControl(coords);
5659                 if ( IsCellEditControlCreated() )
5660                     GetSheetRefData()->m_cellEditor.StartingClick();
5661 
5662                 m_waitForSlowClick = false;
5663             }
5664         }
5665     }
5666     else if ( event.LeftUp() )
5667     {
5668         if (!GetSelectingBlock().IsEmpty())
5669         {
5670             if (SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5671                            GetSelectingBlock(), true, false, &event) != EVT_VETOED)
5672             {
5673                 SelectAll(true);
5674                 SetSelectingBlock(wxNullSheetBlock);
5675             }
5676         }
5677 
5678         // send after default processing, they can use own evt handler
5679         SendEvent(wxEVT_SHEET_LABEL_LEFT_UP, coords, &event);
5680     }
5681     else if ( event.RightDown() )
5682     {
5683         if (IsCellEditControlCreated())
5684             DisableCellEditControl(true);
5685 
5686         if (SendEvent(wxEVT_SHEET_LABEL_RIGHT_DOWN, coords, &event) == EVT_SKIPPED)
5687         {
5688             // no default action at the moment
5689         }
5690     }
5691     else if ( event.RightDClick() )
5692     {
5693         if (IsCellEditControlCreated())
5694             DisableCellEditControl(true);
5695 
5696         if (SendEvent(wxEVT_SHEET_LABEL_RIGHT_DCLICK, coords, &event) == EVT_SKIPPED)
5697         {
5698             // no default action at the moment
5699         }
5700     }
5701     else if ( event.RightUp() )
5702     {
5703         if (IsCellEditControlCreated())
5704             DisableCellEditControl(true);
5705 
5706         if (SendEvent(wxEVT_SHEET_LABEL_RIGHT_UP, coords, &event) == EVT_SKIPPED)
5707         {
5708             // no default action at the moment
5709         }
5710     }
5711 }
5712 
ProcessGridCellMouseEvent(wxMouseEvent & event)5713 void wxSheet::ProcessGridCellMouseEvent( wxMouseEvent& event )
5714 {
5715     int x, y;
5716     m_mousePos = event.GetPosition();
5717     CalcUnscrolledPosition( m_mousePos.x, m_mousePos.y, &x, &y );
5718     wxSheetCoords coords(XYToGridCell( x, y ));
5719 
5720     //wxPrintf("Mouse %d %d, %d %d\n", x, y, m_mousePos.x, m_mousePos.y);
5721 
5722     // VZ: if we do this, the mode is reset to WXSHEET_CURSOR_SELECT_CELL
5723     //     immediately after it becomes WXSHEET_CURSOR_RESIZE_ROW/COL under wxGTK
5724     if ( event.Entering() || event.Leaving() )
5725     {
5726         //SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_gridWin);
5727         //m_gridWin->SetCursor( *wxSTANDARD_CURSOR );
5728         return;
5729     }
5730 
5731     if ( event.Dragging() && event.LeftIsDown() )
5732     {
5733         //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol());
5734 
5735         // Don't start doing anything until the mouse has been dragged at
5736         // least 3 pixels in any direction...
5737         if (!m_isDragging)
5738         {
5739             SetCaptureWindow(m_gridWin);
5740 
5741             if (m_startDragPos == wxDefaultPosition)
5742             {
5743                 m_startDragPos = m_mousePos;
5744                 return;
5745             }
5746             if ((abs(m_startDragPos.x - m_mousePos.x) < 4) &&
5747                 (abs(m_startDragPos.y - m_mousePos.y) < 4))
5748                 return;
5749         }
5750 
5751         bool can_scroll = true;
5752         m_isDragging = true;
5753         if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5754         {
5755             // Hide the edit control, so it won't interfer with drag-shrinking.
5756             if ( IsCellEditControlShown() )
5757                 DisableCellEditControl(true);
5758 
5759             if ( ContainsGridCell(coords) && !HasSelectionMode(wxSHEET_SelectNone) )
5760             {
5761                 HighlightSelectingBlock( GetSelectingAnchor(), coords );
5762             }
5763         }
5764         else if ( HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW) )
5765         {
5766             y = wxMax( y, GetRowTop(m_dragRowOrCol) +
5767                           GetMinimalRowHeight(m_dragRowOrCol) );
5768             can_scroll = false; // y != m_dragLastPos;
5769             DrawRowColResizingMarker( y );
5770         }
5771         else if ( HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL) )
5772         {
5773             x = wxMax( x, GetColLeft(m_dragRowOrCol) +
5774                           GetMinimalColWidth(m_dragRowOrCol));
5775             can_scroll = false; // x != m_dragLastPos;
5776             DrawRowColResizingMarker( x );
5777         }
5778 
5779         if (can_scroll &&
5780 #if wxCHECK_VERSION(2,7,0)
5781             !wxRect(wxPoint(0,0), m_gridWin->GetClientSize()).Contains(m_mousePos))
5782 #else
5783             !wxRect(wxPoint(0,0), m_gridWin->GetClientSize()).Inside(m_mousePos))
5784 #endif
5785         {
5786             if (!m_mouseTimer)
5787                 StartMouseTimer();
5788         }
5789         else
5790             StopMouseTimer();
5791 
5792         return;
5793     }
5794 
5795     StopMouseTimer();
5796     SetCaptureWindow(NULL);
5797     m_isDragging = false;
5798     m_startDragPos = wxDefaultPosition;
5799 
5800     if ( event.LeftDown() && ContainsGridCell(coords) )
5801     {
5802         if ( SendEvent(wxEVT_SHEET_CELL_LEFT_DOWN, coords, &event) == EVT_SKIPPED)
5803         {
5804             if ( !event.ControlDown() && HasSelection() )
5805             {
5806                 wxSheetBlock block(0, 0, GetNumberRows()-1, GetNumberCols()-1);
5807 
5808                 if (!HasSelectionMode(wxSHEET_SelectNone) &&
5809                     SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5810                                    block, false, false, &event) != EVT_VETOED)
5811                 {
5812                     ClearSelection(true);
5813                 }
5814             }
5815 
5816             if ( event.ShiftDown() )
5817             {
5818                 wxSheetBlock block(GetGridCursorCell(), coords);
5819 
5820                 if (!HasSelectionMode(wxSHEET_SelectNone) &&
5821                     SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5822                                     block, true, true, &event) != EVT_VETOED)
5823                 {
5824                     SelectBlock(block, event.ControlDown(), true);
5825                 }
5826             }
5827             else if ( (XToEdgeOfGridCol(x) < 0) && (YToEdgeOfGridRow(y) < 0) )
5828             {
5829                 if (IsCellEditControlCreated())
5830                     DisableCellEditControl(true);
5831 
5832                 MakeCellVisible( coords );
5833 
5834                 if ( !ContainsGridCell(GetSelectingAnchor()) )
5835                     SetSelectingAnchor(coords);
5836 
5837                 if ( event.ControlDown() )
5838                 {
5839                     // toggle cell selection
5840                     int sel = IsCellSelected(coords);
5841                     wxSheetBlock block(coords, 1, 1);
5842                     if (!HasSelectionMode(wxSHEET_SelectNone) &&
5843                         SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5844                                        block, !sel, !sel, &event) != EVT_VETOED)
5845                     {
5846                         ToggleCellSelection(coords, !sel, true);
5847                         SetSelectingBlock(wxNullSheetBlock);
5848                     }
5849                 }
5850                 else
5851                 {
5852                     if (GetGridCursorCell() == coords)
5853                         m_waitForSlowClick = true;
5854 
5855                     SetGridCursorCell( coords );
5856                     // FIXME weird? Highlight a whole row/col when not in select cells?
5857                     if ( !HasSelectionMode(wxSHEET_SelectNone|wxSHEET_SelectCells) )
5858                         HighlightSelectingBlock( coords, coords );
5859                 }
5860             }
5861         }
5862     }
5863     else if ( event.LeftDClick() && ContainsGridCell(coords) )
5864     {
5865         if ((XToEdgeOfGridCol(x) < 0) && (YToEdgeOfGridRow(y) < 0) &&
5866             (SendEvent(wxEVT_SHEET_CELL_LEFT_DCLICK, coords, &event) == EVT_SKIPPED))
5867         {
5868             if ((coords == GetGridCursorCell()) && CanEnableCellControl(GetGridCursorCell()))
5869             {
5870                 ClearSelection(true);
5871                 EnableCellEditControl(GetGridCursorCell());
5872                 if ( IsCellEditControlCreated() )
5873                     GetSheetRefData()->m_cellEditor.StartingClick();
5874 
5875                 m_waitForSlowClick = false;
5876             }
5877         }
5878     }
5879     else if ( event.LeftUp() )
5880     {
5881         StopMouseTimer();
5882         SetCaptureWindow(NULL);
5883 
5884         if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5885         {
5886             if ((coords == GetGridCursorCell()) && m_waitForSlowClick &&
5887                 CanEnableCellControl(GetGridCursorCell()))
5888             {
5889                 ClearSelection(true);
5890                 EnableCellEditControl(GetGridCursorCell());
5891                 if ( IsCellEditControlCreated() )
5892                     GetSheetRefData()->m_cellEditor.StartingClick();
5893 
5894                 m_waitForSlowClick = false;
5895             }
5896             else if ( !GetSelectingBlock().IsEmpty() )
5897             {
5898                 if (!HasSelectionMode(wxSHEET_SelectNone) &&
5899                     SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
5900                                    GetSelectingBlock(), true, true, &event) != EVT_VETOED)
5901                 {
5902                     SelectBlock(GetSelectingBlock(), true, true);
5903                     SetSelectingBlock(wxNullSheetBlock);
5904                 }
5905             }
5906         }
5907         else if ( HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW) )
5908         {
5909             // Note: send event *after* doing default processing in this case
5910             if (DoEndDragResizeRowCol())
5911                 SendEvent( wxEVT_SHEET_ROW_SIZE, wxSheetCoords(m_dragRowOrCol, -1), &event );
5912 
5913             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_gridWin);
5914         }
5915         else if ( HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL) )
5916         {
5917             // Note: send event *after* doing default processing in this case
5918             if (DoEndDragResizeRowCol())
5919                 SendEvent( wxEVT_SHEET_COL_SIZE, wxSheetCoords(-1, m_dragRowOrCol), &event );
5920 
5921             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_gridWin);
5922         }
5923 
5924         // Show edit control, if it has been hidden for drag-shrinking.
5925         if (IsCellEditControlCreated() && !IsCellEditControlShown())
5926             ShowCellEditControl();
5927 
5928         if (!m_keySelecting)
5929             SetSelectingAnchor(wxNullSheetCoords);
5930 
5931         m_dragLastPos = -1;
5932 
5933         // send after default processing, they can use own evt handler
5934         SendEvent(wxEVT_SHEET_CELL_LEFT_UP, coords, &event);
5935     }
5936     else if ( event.RightDown() && ContainsGridCell(coords) )
5937     {
5938         if (IsCellEditControlCreated())
5939             DisableCellEditControl(true);
5940 
5941         if (SendEvent(wxEVT_SHEET_CELL_RIGHT_DOWN, coords, &event) == EVT_SKIPPED)
5942         {
5943             // no default action at the moment
5944         }
5945     }
5946     else if ( event.RightDClick() && ContainsGridCell(coords) )
5947     {
5948         if (IsCellEditControlCreated())
5949             DisableCellEditControl(true);
5950 
5951         if (SendEvent(wxEVT_SHEET_CELL_RIGHT_DCLICK, coords, &event) == EVT_SKIPPED)
5952         {
5953             // no default action at the moment
5954         }
5955     }
5956     else if ( event.RightUp() && ContainsGridCell(coords) )
5957     {
5958         if (IsCellEditControlCreated())
5959             DisableCellEditControl(true);
5960 
5961         if (SendEvent(wxEVT_SHEET_CELL_RIGHT_UP, coords, &event) == EVT_SKIPPED)
5962         {
5963             // no default action at the moment
5964         }
5965     }
5966     else if ( event.Moving() && !event.IsButton() )
5967     {
5968         if ( (coords.GetRow() < 0) || (coords.GetCol() < 0) )
5969         {
5970             // out of grid cell area
5971             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_gridWin);
5972             return;
5973         }
5974 
5975         int dragRow = YToEdgeOfGridRow( y );
5976         int dragCol = XToEdgeOfGridCol( x );
5977 
5978         // check if this is inside a spanned cell, if so don't drag
5979         if ( (dragRow >= 0) || (dragCol >= 0) )
5980         {
5981             const wxSheetBlock cellBlock(GetCellBlock(coords));
5982             if (!cellBlock.IsOneCell() &&
5983                 ((dragRow != cellBlock.GetBottom()) || (dragCol != cellBlock.GetRight())))
5984                 dragRow = dragCol = -1;
5985         }
5986 
5987         // Dragging on the corner of a cell to resize in both
5988         // directions is not implemented yet...
5989         if ( (dragRow >= 0) && (dragCol >= 0) )
5990         {
5991             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_gridWin);
5992         }
5993         else if ( dragRow >= 0 )
5994         {
5995             m_dragRowOrCol = dragRow;
5996 
5997             if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
5998             {
5999                 if ( CanDragRowSize() && CanDragGridSize() )
6000                 {
6001                     SetMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW, m_gridWin);
6002                     SetCaptureWindow(m_gridWin);
6003                 }
6004             }
6005 
6006             if ( dragCol >= 0 )
6007                 m_dragRowOrCol = dragCol;
6008         }
6009         else if ( dragCol >= 0 )
6010         {
6011             m_dragRowOrCol = dragCol;
6012 
6013             if ( HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
6014             {
6015                 if ( CanDragColSize() && CanDragGridSize() )
6016                 {
6017                     SetMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL, m_gridWin);
6018                     SetCaptureWindow(m_gridWin);
6019                 }
6020             }
6021         }
6022         // Neither on a row or col edge
6023         else if ( !HasMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL) )
6024             SetMouseCursorMode(WXSHEET_CURSOR_SELECT_CELL, m_gridWin);
6025     }
6026 }
6027 
OnKeyDown(wxKeyEvent & event)6028 void wxSheet::OnKeyDown( wxKeyEvent& event )
6029 {
6030     //wxCHECK_RET(!m_inOnKeyDown, wxT("wxSheet::OnKeyDown called while already active"));
6031     // yield called from SetGridOrigin causes this in MSW
6032 
6033     if (m_inOnKeyDown)
6034         return;
6035 
6036     m_inOnKeyDown = true;
6037 
6038     // FIXME - is this really a good idea? probably not, should make own "key" event
6039     // propagate the event up and see if it gets processed
6040     //wxWindow *parent = GetParent();
6041     //wxKeyEvent keyEvt( event );
6042     //keyEvt.SetEventObject( parent );
6043     //!parent->GetEventHandler()->ProcessEvent(keyEvt) &&
6044 
6045     if ( GetNumberRows() && GetNumberCols() )
6046     {
6047         // try local handlers
6048         switch ( event.GetKeyCode() )
6049         {
6050             case WXK_RETURN:
6051             case WXK_NUMPAD_ENTER:
6052             {
6053                 if ( event.ControlDown() )
6054                     event.Skip();  // to let the edit control have the return
6055                 else
6056                 {
6057                     if ( IsCellEditControlCreated() )
6058                         DisableCellEditControl(true);
6059                     if ( GetGridCursorRow() < GetNumberRows()-1 )
6060                         MoveCursorDown( event.ShiftDown() );
6061                 }
6062                 break;
6063             }
6064             case WXK_ESCAPE:
6065             {
6066                 ClearSelection();
6067                 break;
6068             }
6069             case WXK_TAB:
6070             {
6071                 if (event.ShiftDown())
6072                 {
6073                     if ( IsCellEditControlCreated() )
6074                         DisableCellEditControl(true);
6075                     if ( GetGridCursorCol() > 0 )
6076                         MoveCursorLeft( false );
6077                 }
6078                 else
6079                 {
6080                     if ( IsCellEditControlCreated() )
6081                         DisableCellEditControl(true);
6082                     if ( GetGridCursorCol() < GetNumberCols()-1 )
6083                         MoveCursorRight( false );
6084                 }
6085                 break;
6086             }
6087             case WXK_SPACE:
6088             {
6089                 if ( event.ShiftDown() && ContainsGridCell(GetGridCursorCell()) )
6090                 {
6091                     wxSheetBlock block(GetGridCursorRow(), 0, 1, GetNumberCols()+1);
6092 
6093                     if (!GetSelectingBlock().IsEmpty() &&
6094                         GetSelectingBlock().Contains(GetGridCursorCell()))
6095                     {
6096                         block.SetTop(GetSelectingBlock().GetTop());
6097                         block.SetBottom(GetSelectingBlock().GetBottom());
6098                     }
6099 
6100                     if (!HasSelectionMode(wxSHEET_SelectNone) &&
6101                         SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
6102                                        block, true, false, &event) != EVT_VETOED)
6103                     {
6104                         SelectRows(block.GetTop(), block.GetBottom(), false, true);
6105                     }
6106 
6107                     break;
6108                 }
6109                 if ( event.ControlDown() && ContainsGridCell(GetGridCursorCell()) )
6110                 {
6111                     wxSheetBlock block(0, GetGridCursorCol(), GetNumberRows()+1, 1);
6112 
6113                     if (!GetSelectingBlock().IsEmpty() &&
6114                         GetSelectingBlock().Contains(GetGridCursorCell()))
6115                     {
6116                         block.SetLeft(GetSelectingBlock().GetLeft());
6117                         block.SetRight(GetSelectingBlock().GetRight());
6118                     }
6119 
6120                     if (!HasSelectionMode(wxSHEET_SelectNone) &&
6121                         SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
6122                                        block, true, false, &event) != EVT_VETOED)
6123                     {
6124                         SelectCols(block.GetLeft(), block.GetRight(), false, true);
6125                     }
6126 
6127                     break;
6128                 }
6129                 if ( event.AltDown() && ContainsGridCell(GetGridCursorCell()) )
6130                 {
6131                     // Toggle cell selection FIXME not sure how useful this is
6132                     bool sel = IsCellSelected(GetGridCursorCell());
6133                     wxSheetBlock block(GetGridCursorCell(), 1, 1);
6134                     if (!HasSelectionMode(wxSHEET_SelectNone) &&
6135                         SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
6136                                        block, !sel, !sel, &event) != EVT_VETOED)
6137                     {
6138                         ToggleCellSelection(GetGridCursorCell(), !sel, true);
6139                     }
6140 
6141                     break;
6142                 }
6143                 if ( !IsEditable() )
6144                 {
6145                     MoveCursorRight( false );
6146                     break;
6147                 }
6148                 // Otherwise fall through to default
6149             }
6150             default:
6151             {
6152                 // is it possible to edit the current cell at all?
6153                 if ( !IsCellEditControlCreated() && CanEnableCellControl(GetGridCursorCell()) )
6154                 {
6155                     // yes, now check whether the cells editor accepts the key
6156                     wxSheetCellEditor editor(GetAttr(GetGridCursorCell()).GetEditor(this, GetGridCursorCell()));
6157 
6158                     // <F2> is special and will always start editing, for
6159                     // other keys - ask the editor itself
6160                     if ( ((event.GetKeyCode() == WXK_F2) && !event.HasModifiers())
6161                          || editor.IsAcceptedKey(event) )
6162                     {
6163                         // ensure cell is visble
6164                         MakeCellVisible(GetGridCursorCell());
6165                         EnableCellEditControl(GetGridCursorCell());
6166 
6167                         // a problem can arise if the cell is not completely
6168                         // visible (even after calling MakeCellVisible the
6169                         // control is not created and calling StartingKey will
6170                         // crash the app
6171                         if ( IsCellEditControlCreated() )
6172                             GetSheetRefData()->m_cellEditor.StartingKey(event);
6173                     }
6174                     else
6175                         event.Skip();
6176                 }
6177                 else
6178                 {
6179                     // let others process char events with modifiers or all
6180                     // char events for readonly cells
6181                     event.Skip();
6182                 }
6183                 break;
6184             }
6185         }
6186     }
6187 
6188     m_inOnKeyDown = false;
6189 }
6190 
OnKeyUp(wxKeyEvent & event)6191 void wxSheet::OnKeyUp( wxKeyEvent& event )
6192 {
6193     if ( m_keySelecting && (event.GetKeyCode() == WXK_SHIFT) )
6194     {
6195         if ( !GetSelectingBlock().IsEmpty() )
6196         {
6197             if (!HasSelectionMode(wxSHEET_SelectNone) &&
6198                 SendRangeEvent(wxEVT_SHEET_RANGE_SELECTING,
6199                             GetSelectingBlock(), true, true, &event) != EVT_VETOED)
6200             {
6201                 SelectBlock(GetSelectingBlock(), true, true);
6202                 SetSelectingBlock(wxNullSheetBlock);
6203             }
6204 
6205             SetSelectingAnchor(wxNullSheetCoords);
6206         }
6207 
6208         m_keySelecting = false;
6209     }
6210 
6211     event.Skip();
6212 }
6213 
OnChar(wxKeyEvent & event)6214 void wxSheet::OnChar( wxKeyEvent& event )
6215 {
6216     //wxCHECK_RET(!m_inOnKeyDown, wxT("wxSheet::OnKeyDown called while already active"));
6217     // yield called from SetGridOrigin causes this in MSW
6218     if (m_inOnKeyDown)
6219         return;
6220 
6221     if ( !GetNumberRows() || !GetNumberCols() )
6222     {
6223         event.Skip();
6224         return;
6225     }
6226 
6227     m_inOnKeyDown = true;
6228     int keyMods = GetKeyModifiers(&event);
6229 
6230     // try local handlers
6231     switch ( event.GetKeyCode() )
6232     {
6233         case WXK_UP:
6234         {
6235             if (!ContainsGridCell(GetGridCursorCell())) break;
6236             if ( event.ControlDown() )
6237                 MoveCursorUpBlock( event.ShiftDown() );
6238             else if ( keyMods == ALT_DOWN )
6239                 SetRowHeight(GetGridCursorRow(), GetRowHeight(GetGridCursorRow())-5);
6240             else
6241                 MoveCursorUp( event.ShiftDown() );
6242 
6243             break;
6244         }
6245         case WXK_DOWN:
6246         {
6247             if (!ContainsGridCell(GetGridCursorCell())) break;
6248             if ( event.ControlDown() )
6249                 MoveCursorDownBlock( event.ShiftDown() );
6250             else if ( keyMods == ALT_DOWN )
6251                 SetRowHeight(GetGridCursorRow(), GetRowHeight(GetGridCursorRow())+5);
6252             else
6253                 MoveCursorDown( event.ShiftDown() );
6254 
6255             break;
6256         }
6257         case WXK_LEFT:
6258         {
6259             if (!ContainsGridCell(GetGridCursorCell())) break;
6260             if ( event.ControlDown() )
6261                 MoveCursorLeftBlock( event.ShiftDown() );
6262             else if ( keyMods == ALT_DOWN )
6263                 SetColWidth(GetGridCursorCol(), GetColWidth(GetGridCursorCol())-5);
6264             else
6265                 MoveCursorLeft( event.ShiftDown() );
6266 
6267             break;
6268         }
6269         case WXK_RIGHT:
6270         {
6271             if (!ContainsGridCell(GetGridCursorCell())) break;
6272             if ( event.ControlDown() )
6273                 MoveCursorRightBlock( event.ShiftDown() );
6274             else if ( keyMods == ALT_DOWN )
6275                 SetColWidth(GetGridCursorCol(), GetColWidth(GetGridCursorCol())+5);
6276             else
6277                 MoveCursorRight( event.ShiftDown() );
6278 
6279             break;
6280         }
6281         case WXK_PAGEUP:
6282         {
6283             MoveCursorUpPage( event.ShiftDown() );
6284             break;
6285         }
6286         case WXK_PAGEDOWN:
6287         {
6288             MoveCursorDownPage( event.ShiftDown() );
6289             break;
6290         }
6291         case WXK_HOME :
6292         {
6293             if ( event.ControlDown() )
6294             {
6295                 wxSheetCoords coords( 0, 0 );
6296                 if ( ContainsGridCell(coords) )
6297                 {
6298                     wxSheetCoords lastCoords(GetGridCursorCell());
6299                     MakeCellVisible( coords );
6300                     SetGridCursorCell( coords );
6301                     if ( event.ShiftDown() && ContainsGridCell(lastCoords) )
6302                     {
6303                         m_keySelecting = true;
6304                         if ( !ContainsGridCell(GetSelectingAnchor()) )
6305                             SetSelectingAnchor(lastCoords);
6306 
6307                         HighlightSelectingBlock(GetSelectingAnchor(), GetGridCursorCell());
6308                     }
6309                 }
6310             }
6311             else
6312                 event.Skip();
6313 
6314             break;
6315         }
6316         case WXK_END:
6317         {
6318             if ( event.ControlDown() )
6319             {
6320                 wxSheetCoords coords(GetNumberRows()-1, GetNumberCols()-1);
6321                 if ( ContainsGridCell(coords) )
6322                 {
6323                     wxSheetCoords lastCoords(GetGridCursorCell());
6324                     MakeCellVisible( coords );
6325                     SetGridCursorCell( coords );
6326                     if ( event.ShiftDown() && ContainsGridCell(lastCoords) )
6327                     {
6328                         m_keySelecting = true;
6329                         if ( !ContainsGridCell(GetSelectingAnchor()) )
6330                             SetSelectingAnchor(lastCoords);
6331 
6332                         HighlightSelectingBlock(GetSelectingAnchor(), GetGridCursorCell());
6333                     }
6334                 }
6335             }
6336             else
6337                 event.Skip();
6338 
6339             break;
6340         }
6341         default :
6342             event.Skip();
6343     }
6344 
6345     m_inOnKeyDown = false;
6346 }
6347 
StopMouseTimer()6348 void wxSheet::StopMouseTimer()
6349 {
6350     if (m_mouseTimer)
6351     {
6352         if (m_mouseTimer->IsRunning())
6353             m_mouseTimer->Stop();
6354 
6355         delete m_mouseTimer;
6356         m_mouseTimer = NULL;
6357     }
6358 }
StartMouseTimer()6359 void wxSheet::StartMouseTimer()
6360 {
6361     if (!m_mouseTimer)
6362         m_mouseTimer = new wxTimer(this, ID_MOUSE_DRAG_TIMER);
6363 
6364     if (!m_mouseTimer->IsRunning())
6365         m_mouseTimer->Start(100, true); // one shot
6366 }
6367 
OnMouseTimer(wxTimerEvent & WXUNUSED (event))6368 void wxSheet::OnMouseTimer( wxTimerEvent &WXUNUSED(event) )
6369 {
6370     // the window must be captured and thus m_mousePos is for that window
6371     wxWindow *win = GetCaptureWindow();
6372 
6373     if (!win || (GetNumberCols() < 1) || (GetNumberRows() < 1))
6374     {
6375         StopMouseTimer();
6376         return;
6377     }
6378 
6379     wxSize clientSize(win->GetClientSize());
6380     int dx = (m_mousePos.x < 0) ? -1 : ((m_mousePos.x > clientSize.x) ? 1 : 0);
6381     int dy = (m_mousePos.y < 0) ? -1 : ((m_mousePos.y > clientSize.y) ? 1 : 0);
6382 
6383     if (win == m_rowLabelWin)
6384         dx = 0;
6385     else if (win == m_colLabelWin)
6386         dy = 0;
6387 
6388     if ((dx == 0) && (dy == 0)) // mouse is back in the window
6389     {
6390         StopMouseTimer();
6391         return;
6392     }
6393 
6394     /*wxSize cSize = */ m_gridWin->GetClientSize();
6395     SetGridOrigin( m_gridOrigin.x + dx*SHEET_SCROLL_LINE_X,
6396                    m_gridOrigin.y + dy*SHEET_SCROLL_LINE_Y, true, true );
6397 
6398     // send fake mouse event to process, assume left down and we're dragging
6399     wxMouseEvent mEvt(wxEVT_MOTION);
6400     mEvt.SetEventObject(win);
6401     mEvt.m_leftDown = true;
6402     mEvt.m_x = m_mousePos.x;
6403     mEvt.m_y = m_mousePos.y;
6404 
6405     win->GetEventHandler()->ProcessEvent(mEvt);
6406     StartMouseTimer();
6407 }
6408 
DoEndDragResizeRowCol()6409 bool wxSheet::DoEndDragResizeRowCol()
6410 {
6411     if ( m_dragLastPos < 0 )
6412         return false;
6413 
6414     if (IsCellEditControlCreated())
6415         DisableCellEditControl(true);
6416 
6417     // erase the last line and resize the row/col
6418     DrawRowColResizingMarker();
6419 
6420     if (HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_ROW))
6421     {
6422         int height = m_dragLastPos - GetRowTop(m_dragRowOrCol);
6423         const int minHeight = GetMinimalRowHeight(m_dragRowOrCol);
6424         if (minHeight > height) height = minHeight;
6425         if (height != GetRowHeight(m_dragRowOrCol))
6426         {
6427             SetRowHeight( m_dragRowOrCol, height );
6428             return true;
6429         }
6430     }
6431     else if (HasMouseCursorMode(WXSHEET_CURSOR_RESIZE_COL))
6432     {
6433         int width = m_dragLastPos - GetColLeft(m_dragRowOrCol);
6434         const int minWidth = GetMinimalColWidth(m_dragRowOrCol);
6435         if (minWidth > width) width = minWidth;
6436         if (width != GetColWidth(m_dragRowOrCol))
6437         {
6438             SetColWidth( m_dragRowOrCol, width );
6439             return true;
6440         }
6441     }
6442 
6443     return false;
6444 }
6445 
SetMouseCursorMode(MouseCursorMode mode,wxWindow * win)6446 void wxSheet::SetMouseCursorMode(MouseCursorMode mode, wxWindow *win)
6447 {
6448     wxCHECK_RET(win, wxT("Invalid window"));
6449 
6450     m_mouseCursorMode = mode;
6451 
6452     if (win == m_gridWin)
6453     {
6454         if (m_gridWin->m_mouseCursor == mode)
6455             return;
6456 
6457         m_gridWin->m_mouseCursor = mode;
6458     }
6459     else if (win == m_cornerLabelWin)
6460     {
6461         if (m_cornerLabelWin->m_mouseCursor == mode)
6462             return;
6463 
6464         m_cornerLabelWin->m_mouseCursor = mode;
6465     }
6466     else if (win == m_rowLabelWin)
6467     {
6468         if (m_rowLabelWin->m_mouseCursor == mode)
6469             return;
6470 
6471         m_rowLabelWin->m_mouseCursor = mode;
6472     }
6473     else if (win == m_colLabelWin)
6474     {
6475         if (m_colLabelWin->m_mouseCursor == mode)
6476             return;
6477 
6478         m_colLabelWin->m_mouseCursor = mode;
6479     }
6480     else if (win == this)
6481     {
6482         if (m_mouseCursor == mode)
6483             return;
6484 
6485         m_mouseCursor = mode;
6486     }
6487 
6488     switch ( mode )
6489     {
6490         case WXSHEET_CURSOR_RESIZE_ROW :
6491         case WXSHEET_CURSOR_SPLIT_VERTICAL :
6492         {
6493             win->SetCursor( GetSheetRefData()->m_rowResizeCursor );
6494             break;
6495         }
6496         case WXSHEET_CURSOR_RESIZE_COL :
6497         case WXSHEET_CURSOR_SPLIT_HORIZONTAL :
6498         {
6499             win->SetCursor( GetSheetRefData()->m_colResizeCursor );
6500             break;
6501         }
6502         default:
6503             win->SetCursor( *wxSTANDARD_CURSOR );
6504     }
6505 }
6506 
SetCaptureWindow(wxWindow * win)6507 void wxSheet::SetCaptureWindow(wxWindow *win)
6508 {
6509     if (m_winCapture && (m_winCapture != win) && m_winCapture->HasCapture())
6510         m_winCapture->ReleaseMouse();
6511 
6512     m_winCapture = win;
6513 
6514     if (m_winCapture && (!m_winCapture->HasCapture()))
6515         m_winCapture->CaptureMouse();
6516 }
6517 
GetWindowForCoords(const wxSheetCoords & coords) const6518 wxWindow* wxSheet::GetWindowForCoords( const wxSheetCoords& coords ) const
6519 {
6520     if (IsGridCell(coords))
6521         return m_gridWin;
6522     if (IsRowLabelCell(coords))
6523         return m_rowLabelWin;
6524     if (IsColLabelCell(coords))
6525         return m_colLabelWin;
6526     if (IsCornerLabelCell(coords))
6527         return m_cornerLabelWin;
6528 
6529     wxFAIL_MSG(wxString::Format(wxT("Unable to get window for coords (%d,%d)"), coords.m_row, coords.m_col));
6530     return NULL;
6531 }
6532 
6533 // ----- event handlers
6534 
6535 // Generate a grid event based on a mouse/key event and
6536 // return the result of ProcessEvent()
SendEvent(const wxEventType type,const wxSheetCoords & coords,wxEvent * mouseOrKeyEvt)6537 int wxSheet::SendEvent( const wxEventType type, const wxSheetCoords& coords,
6538                         wxEvent *mouseOrKeyEvt )
6539 {
6540     //wxMouseEvent *mouseEvt = wxDynamicCast(mouseOrKeyEvt, wxMouseEvent);
6541     //wxPoint pos = mouseEvt ? mouseEvt->GetPosition() : wxPoint(-1, -1);
6542     //pos += wxPoint(GetRowLabelWidth(), GetColLabelHeight());
6543 
6544     wxSheetEvent sheetEvt(GetId(), type, this, coords, wxPoint(-1,-1), IsSelecting());
6545     sheetEvt.SetKeysDownMousePos(mouseOrKeyEvt);
6546     return DoSendEvent(&sheetEvt);
6547 }
6548 
SendRangeEvent(const wxEventType type,const wxSheetBlock & block,bool selecting,bool add,wxEvent * mouseOrKeyEvt)6549 int wxSheet::SendRangeEvent( const wxEventType type, const wxSheetBlock& block,
6550                              bool selecting, bool add, wxEvent *mouseOrKeyEvt )
6551 {
6552     if ( type == wxEVT_SHEET_RANGE_SELECTED )
6553     {
6554         wxSheetRangeSelectEvent sheetEvt(GetId(), type, this, block, selecting, add );
6555 
6556         sheetEvt.SetKeysDownMousePos(mouseOrKeyEvt);
6557         sheetEvt.m_coords = GetGridCursorCell();
6558         return DoSendEvent(&sheetEvt);
6559     }
6560 
6561     return 0;
6562 }
6563 
DoSendEvent(wxSheetEvent * event)6564 int wxSheet::DoSendEvent(wxSheetEvent *event)
6565 {
6566     wxCHECK_MSG(event, 0, wxT("invalid event in wxSheet::DoSendEvent"));
6567     bool claimed = GetEventHandler()->ProcessEvent(*event);
6568     bool vetoed  = !event->IsAllowed();
6569 
6570     // A Veto'd event may not be claimed, test this first
6571     if (vetoed) return EVT_VETOED;
6572     return claimed ? EVT_CLAIMED : EVT_SKIPPED;
6573 }
6574 
HighlightSelectingBlock(const wxSheetBlock & block_)6575 void wxSheet::HighlightSelectingBlock( const wxSheetBlock &block_ )
6576 {
6577     wxSheetBlock block(block_.GetAligned());
6578 
6579     if (block == GetSelectingBlock())
6580         return;
6581 
6582     if ( GetSelection() && !block.IsEmpty() )
6583     {
6584         // make sure block that's selected goes full width/height
6585         if ( HasSelectionMode(wxSHEET_SelectRows) )
6586         {
6587             block.SetLeft(0);
6588             block.SetWidth(GetNumberCols() - 1);
6589         }
6590         else if ( HasSelectionMode(wxSHEET_SelectCols) )
6591         {
6592             block.SetTop(0);
6593             block.SetHeight(GetNumberRows() - 1);
6594         }
6595     }
6596 
6597     wxSheetBlock oldSelBlock(GetSelectingBlock());
6598     SetSelectingBlock(block.IsEmpty() ? wxNullSheetBlock : block);
6599 
6600     // First the case that we selected a completely new area
6601     if ( oldSelBlock.IsEmpty() )
6602     {
6603         RefreshGridCellBlock(block);
6604     }
6605     // New selection is empty, erase old one
6606     else if ( block.IsEmpty() )
6607     {
6608         RefreshGridCellBlock(oldSelBlock);
6609     }
6610     // two selections don't intersect at all, not expected, but ok I guess
6611     else if ( !block.Intersects(oldSelBlock) )
6612     {
6613         RefreshGridCellBlock(block.Union(oldSelBlock));
6614     }
6615     // Now handle changing an existing selection area.
6616     else if ( oldSelBlock != block )
6617     {
6618         // FIXME - this is not great
6619         wxSheetBlock changed[8];
6620         wxSheetBlock bounds;
6621 
6622         oldSelBlock.Delete(block, changed[0], changed[1], changed[2], changed[3]);
6623         block.Delete(oldSelBlock, changed[4], changed[5], changed[6], changed[7]);
6624 
6625         {
6626             for (int n=0; n<8; n++)
6627                 bounds = bounds.ExpandUnion(changed[n]);
6628 
6629             RefreshGridCellBlock(bounds);
6630         }
6631     }
6632 }
6633 
6634 // ------ functions to get/send data (see also public functions)
6635 
GetModelValues()6636 bool wxSheet::GetModelValues()
6637 {
6638     // Hide the editor, so it won't hide a changed value.
6639     if (IsCellEditControlShown())
6640         HideCellEditControl();
6641 
6642     if ( GetTable() )
6643     {
6644         RefreshGridWindow(); // all we need to do is repaint the grid
6645         return true;
6646     }
6647 
6648     return false;
6649 }
6650 
SetModelValues()6651 bool wxSheet::SetModelValues()
6652 {
6653     // Disable the editor, so it won't hide a changed value.
6654     // FIXME: Do we also want to save the current value of the editor first? yes?
6655     if (IsCellEditControlCreated())
6656         DisableCellEditControl(true);
6657 
6658     if ( GetTable() )
6659     {
6660         wxSheetCoords coords;
6661         int numRows = GetNumberRows();
6662         int numCols = GetNumberCols();
6663         for ( coords.m_row = 0; coords.m_row < numRows; coords.m_row++ )
6664         {
6665             for ( coords.m_col = 0; coords.m_col < numCols; coords.m_col++ )
6666                 GetTable()->SetValue( coords, GetCellValue(coords) );
6667         }
6668 
6669         return true;
6670     }
6671 
6672     return false;
6673 }
6674 
6675 // ----------------------------------------------------------------------------
6676 // Attrbitute cache
6677 
ClearAttrCache()6678 void wxSheet::ClearAttrCache()
6679 {
6680     if ( m_cacheAttrType != -1 )
6681     {
6682         m_cacheAttr.Destroy();
6683         m_cacheAttrCoords = wxNullSheetCoords;
6684         m_cacheAttrType = -1;
6685     }
6686 }
6687 
CacheAttr(const wxSheetCoords & coords,const wxSheetCellAttr & attr,wxSheetAttr_Type type) const6688 void wxSheet::CacheAttr(const wxSheetCoords& coords, const wxSheetCellAttr &attr,
6689                         wxSheetAttr_Type type ) const
6690 {
6691     if ( attr.Ok() )
6692     {
6693         wxSheet *self = (wxSheet *)this;  // const_cast
6694         self->m_cacheAttr = attr;
6695         self->m_cacheAttrCoords = coords;
6696         self->m_cacheAttrType = type;
6697     }
6698 }
6699 
LookupAttr(const wxSheetCoords & coords,wxSheetAttr_Type type,wxSheetCellAttr & attr) const6700 bool wxSheet::LookupAttr(const wxSheetCoords& coords, wxSheetAttr_Type type,
6701                          wxSheetCellAttr &attr ) const
6702 {
6703     if ( (type == m_cacheAttrType) && (coords == m_cacheAttrCoords) )
6704     {
6705         attr = m_cacheAttr;
6706 
6707 #ifdef DEBUG_ATTR_CACHE
6708         gs_nAttrCacheHits++;
6709 #endif
6710 
6711         return true;
6712     }
6713 
6714 #ifdef DEBUG_ATTR_CACHE
6715     gs_nAttrCacheMisses++;
6716 #endif
6717     return false;
6718 }
6719 
6720 // ----------------------------------------------------------------------------
6721 // Gui Sizing functions
6722 
CalcWindowSizes(bool adjustScrollBars)6723 void wxSheet::CalcWindowSizes(bool adjustScrollBars)
6724 {
6725     if (!m_gridWin || m_resizing)
6726         return;
6727 
6728     if (adjustScrollBars)
6729         AdjustScrollbars(false);
6730 
6731     m_resizing = true;
6732 
6733     int cw, ch;
6734     GetClientSize( &cw, &ch );
6735     wxRect rect;
6736 
6737     const int rowLabelWidth  = GetRowLabelWidth();
6738     const int colLabelHeight = GetColLabelHeight();
6739     int  sb_width  = m_vertScrollBar->GetSize().x;
6740     int  sb_height = m_horizScrollBar->GetSize().y;
6741     bool horiz_sb  = m_horizScrollBar->IsShown();
6742     bool vert_sb   = m_vertScrollBar->IsShown();
6743     if (horiz_sb) ch -= sb_width;
6744     if (vert_sb ) cw -= sb_width;
6745 
6746     bool horiz_splitter = horiz_sb && m_enable_split_horiz;
6747     bool vert_splitter  = vert_sb  && m_enable_split_vert;
6748 
6749     if ( horiz_sb )
6750     {
6751         rect = wxRect(0, ch, cw, sb_height);
6752         if (horiz_splitter)
6753         {
6754             rect.width -= SPLIT_BUTTON_WIDTH;
6755             m_horizSplitRect = wxRect(rect.GetRight(), rect.GetTop(), SPLIT_BUTTON_WIDTH, rect.GetHeight());
6756         }
6757         else
6758             m_horizSplitRect = wxRect(0,0,0,0);
6759 
6760         if (rect != m_horizScrollBar->GetRect())
6761             m_horizScrollBar->SetSize( rect );
6762     }
6763     if ( vert_sb )
6764     {
6765         rect = wxRect(cw, 0, sb_width, ch);
6766         if (vert_splitter)
6767         {
6768             rect.height -= SPLIT_BUTTON_WIDTH;
6769             m_vertSplitRect = wxRect(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), SPLIT_BUTTON_WIDTH);
6770             rect.y += SPLIT_BUTTON_WIDTH;
6771         }
6772         else
6773             m_vertSplitRect = wxRect(0,0,0,0);
6774 
6775         if (rect != m_vertScrollBar->GetRect())
6776             m_vertScrollBar->SetSize( rect );
6777     }
6778     if ( m_cornerLabelWin->IsShown() )
6779     {
6780         rect = wxRect(0, 0, rowLabelWidth, colLabelHeight);
6781         if (rect != m_cornerLabelWin->GetRect())
6782             m_cornerLabelWin->SetSize( rect );
6783     }
6784     if ( m_colLabelWin->IsShown() )
6785     {
6786         rect = wxRect(rowLabelWidth, 0, cw-rowLabelWidth, colLabelHeight);
6787         if (rect != m_colLabelWin->GetRect())
6788             m_colLabelWin->SetSize( rect );
6789     }
6790     if ( m_rowLabelWin->IsShown() )
6791     {
6792         rect = wxRect(0, colLabelHeight, rowLabelWidth, ch-colLabelHeight);
6793         if (rect != m_rowLabelWin->GetRect())
6794             m_rowLabelWin->SetSize( rect );
6795     }
6796     if ( m_gridWin->IsShown() )
6797     {
6798         rect = wxRect(rowLabelWidth, colLabelHeight, cw-rowLabelWidth, ch-colLabelHeight);
6799         if (rect != m_gridWin->GetRect())
6800             m_gridWin->SetSize( rect );
6801 
6802         //PRINT_RECT("Set grid rect ", rect);
6803         //PRINT_RECT("Get grid rect ", wxRect(wxPoint(0,0), m_gridWin->GetSize()));
6804     }
6805 
6806     m_resizing = false;
6807 
6808 #ifdef __WXMSW__
6809     // MSW at least needs a little help making sure that the corner and
6810     //  the splitter rects get painted
6811     wxClientDC dc(this);
6812     PaintSheetWindow(dc, wxRect(0, 0, cw, ch));
6813 #endif // __WXMSW__
6814 }
6815 
OnSize(wxSizeEvent & event)6816 void wxSheet::OnSize(wxSizeEvent& event)
6817 {
6818     //PRINT_RECT(wxT("SheetRect"), GetRect());
6819     CalcWindowSizes(true);
6820     SetGridOrigin(m_gridOrigin.x, m_gridOrigin.y, true, true);
6821     SetEqualColWidths(GetSheetRefData()->m_equal_col_widths);
6822 
6823     event.Skip();
6824 }
6825 
DoGetBestSize() const6826 wxSize wxSheet::DoGetBestSize() const
6827 {
6828     wxSize displaySize(wxGetClientDisplayRect().GetSize());
6829     wxSize size(GetGridVirtualSize(true));
6830     size.x += GetRowLabelWidth();
6831     size.y += GetColLabelHeight();
6832 
6833     if ( size.x > displaySize.x/2 ) size.x = displaySize.x/2;
6834     if ( size.y > displaySize.y/2 ) size.y = displaySize.y/2;
6835 
6836     // NOTE: This size should be cached, but first we need to add calls to
6837     // InvalidateBestSize everywhere that could change the results of this
6838     // calculation.
6839     // CacheBestSize(size);
6840 
6841     return size;
6842 }
6843 
GetKeyModifiers(wxEvent * mouseOrKeyEvent) const6844 int wxSheet::GetKeyModifiers(wxEvent *mouseOrKeyEvent) const
6845 {
6846     // In GTK meta is the numLock key
6847     int mods = NO_MODIFIERS;
6848     wxMouseEvent *mouseEvt = wxDynamicCast(mouseOrKeyEvent, wxMouseEvent);
6849 
6850     if (mouseEvt)
6851     {
6852         if (mouseEvt->ControlDown())
6853             mods |= CTRL_DOWN;
6854         if (mouseEvt->ShiftDown())
6855             mods |= SHIFT_DOWN;
6856         if (mouseEvt->AltDown())
6857             mods |= ALT_DOWN;
6858         //if (mouseEvt->MetaDown())    // meta is numlock in GTK
6859         //    mods |= META_DOWN;
6860     }
6861     else
6862     {
6863         wxKeyEvent *keyEvt = wxDynamicCast(mouseOrKeyEvent, wxKeyEvent);
6864         if (keyEvt)
6865         {
6866             if (keyEvt->ControlDown())
6867                 mods |= CTRL_DOWN;
6868             if (keyEvt->ShiftDown())
6869                 mods |= SHIFT_DOWN;
6870             if (keyEvt->AltDown())
6871                 mods |= ALT_DOWN;
6872             //if (keyEvt->MetaDown())
6873             //    mods |= META_DOWN;
6874         }
6875     }
6876 
6877     //wxPrintf("Mods c%d s%d a%d m%d, %d\n", CTRL_DOWN, SHIFT_DOWN, ALT_DOWN, META_DOWN, mods);
6878 
6879     return mods;
6880 }
6881 
6882 // ----------------------------------------------------------------------------
6883 // events
6884 // ----------------------------------------------------------------------------
6885 
6886 DEFINE_EVENT_TYPE(wxEVT_SHEET_VIEW_CHANGED)
DEFINE_EVENT_TYPE(wxEVT_SHEET_SELECTING_CELL)6887 DEFINE_EVENT_TYPE(wxEVT_SHEET_SELECTING_CELL)
6888 DEFINE_EVENT_TYPE(wxEVT_SHEET_SELECTED_CELL)
6889 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_LEFT_DOWN)
6890 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_RIGHT_DOWN)
6891 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_LEFT_UP)
6892 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_RIGHT_UP)
6893 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_LEFT_DCLICK)
6894 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_RIGHT_DCLICK)
6895 DEFINE_EVENT_TYPE(wxEVT_SHEET_LABEL_LEFT_DOWN)
6896 DEFINE_EVENT_TYPE(wxEVT_SHEET_LABEL_RIGHT_DOWN)
6897 DEFINE_EVENT_TYPE(wxEVT_SHEET_LABEL_LEFT_UP)
6898 DEFINE_EVENT_TYPE(wxEVT_SHEET_LABEL_RIGHT_UP)
6899 DEFINE_EVENT_TYPE(wxEVT_SHEET_LABEL_LEFT_DCLICK)
6900 DEFINE_EVENT_TYPE(wxEVT_SHEET_LABEL_RIGHT_DCLICK)
6901 DEFINE_EVENT_TYPE(wxEVT_SHEET_ROW_SIZE)
6902 DEFINE_EVENT_TYPE(wxEVT_SHEET_COL_SIZE)
6903 DEFINE_EVENT_TYPE(wxEVT_SHEET_RANGE_SELECTING)
6904 DEFINE_EVENT_TYPE(wxEVT_SHEET_RANGE_SELECTED)
6905 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_VALUE_CHANGING)
6906 DEFINE_EVENT_TYPE(wxEVT_SHEET_CELL_VALUE_CHANGED)
6907 DEFINE_EVENT_TYPE(wxEVT_SHEET_EDITOR_ENABLED)
6908 DEFINE_EVENT_TYPE(wxEVT_SHEET_EDITOR_DISABLED)
6909 DEFINE_EVENT_TYPE(wxEVT_SHEET_EDITOR_CREATED)
6910 
6911 // ----------------------------------------------------------------------------
6912 // wxSheetEvent
6913 // ----------------------------------------------------------------------------
6914 IMPLEMENT_DYNAMIC_CLASS( wxSheetEvent, wxNotifyEvent )
6915 
6916 wxSheetEvent::wxSheetEvent(int id, wxEventType type, wxObject* obj,
6917                            const wxSheetCoords& coords, const wxPoint &pos,
6918                            bool sel )
6919     : wxNotifyEvent(type, id), m_coords(coords), m_pos(pos), m_selecting(sel),
6920                   m_control(false), m_shift(false), m_alt(false), m_meta(false)
6921 {
6922     SetEventObject(obj);
6923 }
6924 
SetKeysDownMousePos(wxEvent * mouseOrKeyEvent)6925 void wxSheetEvent::SetKeysDownMousePos(wxEvent *mouseOrKeyEvent)
6926 {
6927     wxMouseEvent *mouseEvt = wxDynamicCast(mouseOrKeyEvent, wxMouseEvent);
6928     if (mouseEvt)
6929     {
6930         m_control = mouseEvt->ControlDown();
6931         m_shift   = mouseEvt->ShiftDown();
6932         m_alt     = mouseEvt->AltDown();
6933         m_meta    = mouseEvt->MetaDown();
6934         m_pos     = mouseEvt->GetPosition();
6935     }
6936     else
6937     {
6938         wxKeyEvent *keyEvt = wxDynamicCast(mouseOrKeyEvent, wxKeyEvent);
6939         if (keyEvt)
6940         {
6941             m_control = keyEvt->ControlDown();
6942             m_shift   = keyEvt->ShiftDown();
6943             m_alt     = keyEvt->AltDown();
6944             m_meta    = keyEvt->MetaDown();
6945             m_pos     = keyEvt->GetPosition();
6946         }
6947         else
6948             return;  // neither mouse nor key event
6949     }
6950 
6951     // FIXME - do I really want to scroll the position? or leave it as is
6952     // we've set the position from the event, now scroll it
6953     wxSheet *sheet = wxDynamicCast(GetEventObject(), wxSheet);
6954     wxWindow *win = wxDynamicCast(mouseOrKeyEvent->GetEventObject(), wxWindow);
6955     if (sheet && win)
6956     {
6957         if ( win == sheet->GetGridWindow())
6958             m_pos = sheet->CalcUnscrolledPosition(m_pos);
6959         else if (win == sheet->GetRowLabelWindow())
6960             sheet->CalcUnscrolledPosition(0, m_pos.y, NULL, &m_pos.y);
6961         else if (win == sheet->GetColLabelWindow())
6962             sheet->CalcUnscrolledPosition(m_pos.x, 0, &m_pos.x, NULL);
6963     }
6964 }
6965 
6966 // ----------------------------------------------------------------------------
6967 // wxSheetRangeSelectEvent
6968 // ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxSheetRangeSelectEvent,wxSheetEvent)6969 IMPLEMENT_DYNAMIC_CLASS( wxSheetRangeSelectEvent, wxSheetEvent )
6970 
6971 wxSheetRangeSelectEvent::wxSheetRangeSelectEvent(int id, wxEventType type, wxObject* obj,
6972                                                  const wxSheetBlock& block,
6973                                                  bool sel, bool add )
6974         : wxSheetEvent(id, type, obj, wxNullSheetCoords, wxPoint(-1, -1), sel),
6975           m_block(block), m_add(add)
6976 {
6977 }
6978 
6979 // ----------------------------------------------------------------------------
6980 // wxSheetEditorCreatedEvent
6981 // ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxSheetEditorCreatedEvent,wxCommandEvent)6982 IMPLEMENT_DYNAMIC_CLASS(wxSheetEditorCreatedEvent, wxCommandEvent)
6983 
6984 wxSheetEditorCreatedEvent::wxSheetEditorCreatedEvent(int id, wxEventType type,
6985                                                      wxObject* obj,
6986                                                      const wxSheetCoords& coords,
6987                                                      wxWindow* ctrl)
6988     : wxCommandEvent(type, id), m_coords(coords), m_ctrl(ctrl)
6989 {
6990     SetEventObject(obj);
6991 }
6992 
6993 
6994 // Notes:
6995 //   Regex to CSV
6996 //      http://dotnetjunkies.com/WebLog/chris.taylor/archive/2004/04/09/11039.aspx
6997 //      ((?<field>[^\",\\r\\n]+)|\"(?<field>([^\"]|\"\")+)\")(,|(?<rowbreak>\\r\\n|\\n|$))
6998 //
6999 //      http://www.codeguru.com/Cpp/Cpp/string/net/article.php/c8153/
7000 //      String* pattern = S",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))"
7001 
7002 /*
7003  http://www.xbeat.net/vbspeed/c_ParseCSV.php
7004 Private Sub Class_Initialize()
7005     Set regEx = New VBScript_RegExp_55.RegExp
7006     regEx.Global = True
7007     regEx.IgnoreCase = True
7008     regEx.Pattern = "(\s*""[^""]*""\s*,)|(\s*[^,]*\s*,)" 'The magic...
7009 End Sub
7010 
7011 
7012 Friend Function ParseCSV02(sExpr$, arVals$()) As Long
7013     Dim lCnt&
7014     Set mc = regEx.Execute(sExpr & ",")
7015     ReDim arVals(mc.Count - 1) As String
7016     For Each m In mc
7017         arVals(lCnt) = Trim$(Left$(m.Value, m.Length - 1))
7018         If Left$(arVals(lCnt), 1) = """" And Right$(arVals(lCnt), 1) = """" Then
7019             arVals(lCnt) = Mid$(arVals(lCnt), 2, Len(arVals(lCnt)) - 2)
7020         End If
7021         lCnt = lCnt + 1
7022     Next m
7023     ParseCSV02 = mc.Count
7024 End Function
7025 */
7026 
7027 #include "wx/regex.h"
7028 
7029 
7030 
7031 class wxCSV_IO
7032 {
7033 public :
7034     wxCSV_IO( bool t=false );
7035 
7036 
7037     wxArrayString ParseLine(const wxString& line);
7038     bool Test();
7039 
7040     wxRegEx m_regex;
7041 };
7042 
wxCSV_IO(bool t)7043 wxCSV_IO::wxCSV_IO(bool t)
7044 {
7045 
7046 
7047 //                      "(\s*""[^""]*""\s*,)|(\s*[^,]*\s*,)" 'The magic...
7048 //    m_regex.Compile(wxT("(\\s*\"[^\"]*\"\\s*,)|(\\s*[^,]*\\s*,)"), wxRE_ICASE);
7049     m_regex.Compile(wxT("(\\s*\"[^\"]*\"\\s*,)|(\\s*[^,]*\\s*,)"), wxRE_ICASE);
7050     //m_regex.Compile(wxT(",(?=([^\"]*"[^\"]*\")*(?![^\"]*\"))"),  wxRE_ICASE);
7051     //m_regex.Compile(wxT(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))"), wxRE_ICASE);
7052 
7053     if (t) Test();
7054 }
7055 
ParseLine(const wxString & line_)7056 wxArrayString wxCSV_IO::ParseLine(const wxString& line_)
7057 {
7058     wxArrayString values;
7059     wxString line(line_ + wxT(",")); // add trailing ',' for "a,b" case
7060 
7061     // See if there is a match at all
7062     while ( m_regex.Matches(line) )
7063     {
7064         // Get the first match (BUG? can't use GetMatch(line, n))
7065         wxString val(m_regex.GetMatch(line, 0));
7066         size_t len = val.Length();
7067         if ((len > 0u) && val.Last() == wxT(',')) // GetMatch returns "val,"
7068             val = val.Mid(0, len-1);
7069 
7070         //wxPrintf(wxT("VALUE '")+val+wxT("' LINE '")+line+wxT("'\n"));
7071 
7072         line = line.Mid(len);              // remove the value from the line
7073         val = val.Strip(wxString::both);   // strip blank spaces from value
7074                                            // should've quoted ie. "  a a "
7075         len = val.Length();
7076         // remove the leading and trailing quotes
7077         if ((len > 1u) && (val.GetChar(0) == wxT('\"')) && (val.GetChar(len-1) == wxT('\"')))
7078             val = val.AfterFirst(wxT('\"')).BeforeLast(wxT('\"'));
7079 
7080         values.Add(val);
7081     }
7082 
7083     return values;
7084 }
7085 
7086 
Joint(const wxArrayString & a)7087 wxString Joint(const wxArrayString& a)
7088 {
7089     if (a.GetCount() == 0u) return wxEmptyString;
7090 
7091     wxString s = wxT("'")+ a[0] + wxT("'\t ");
7092     for (size_t n = 1u; n < a.GetCount(); n++) s += wxT("'")+ a[n] + wxT("'\t ");
7093     return s;
7094 }
7095 
CSV_TEST(const wxString & instr,const wxArrayString & ans,const wxArrayString & res)7096 void CSV_TEST(const wxString& instr, const wxArrayString& ans, const wxArrayString &res)
7097 {
7098     wxString msg(wxT("'") + instr + wxT("'\t ") + Joint(ans) + wxT(" ** \t ") + Joint(res));
7099 
7100     if ( ans.GetCount() != res.GetCount() )
7101         wxPrintf(wxT("COUNT MISMATCH ERROR! \n"));
7102 
7103     for (size_t n = 0; n < wxMin(ans.GetCount(), res.GetCount()); n++)
7104         if (ans[n] != res[n]) wxPrintf(wxT("Error in item %u\n"), n);
7105 
7106     wxPrintf(msg + wxT("\n"));
7107 }
7108 
7109 #define CSVT1(str, a1) { wxArrayString ar; ar.Add(wxT(a1)); CSV_TEST(wxT(str), ar, ParseLine(wxT(str))); }
7110 #define CSVT2(str, a1, a2) { wxArrayString ar; ar.Add(wxT(a1)); ar.Add(wxT(a2)); CSV_TEST(wxT(str), ar, ParseLine(wxT(str))); }
7111 #define CSVT3(str, a1, a2, a3) { wxArrayString ar; ar.Add(wxT(a1)); ar.Add(wxT(a2)); ar.Add(wxT(a3)); CSV_TEST(wxT(str), ar, ParseLine(wxT(str))); }
7112 
Test()7113 bool wxCSV_IO::Test()
7114 {
7115     CSVT3("a,b,c", "a", "b", "c")
7116     CSVT3("\"a\",b,c", "a", "b", "c")
7117     CSVT3("'a',b,c", "'a'", "b", "c")
7118     CSVT3("  a  ,  b  ,  c  ", "a", "b", "c")
7119     CSVT2("aa,bb;cc", "aa", "bb;cc")
7120     CSVT1("", "")
7121     CSVT1("a", "a")
7122     CSVT3(",b,", "", "b", "")
7123     CSVT3(",,c", "", "", "c")
7124     CSVT3(",,", "", "", "")
7125     CSVT2("\"\",b", "", "b")
7126     CSVT2("\" \",b", " ", "b")
7127     CSVT1("\"a,b\"", "a,b")
7128     CSVT2("\"a,b\",c", "a,b", "c")
7129     CSVT2("\" a , b \", c", " a , b ", "c")
7130     CSVT2("a b,c", "a b", "c")
7131     CSVT2("a\"b,c", "a\"b", "c")
7132     CSVT2("\"a\"\"b\",c", "a\"b", "c")
7133     CSVT2("a\"\"b,c", "a\"\"b", "c")       // BROKEN
7134     CSVT3("a,b\",c", "a", "b\"", "c")
7135     CSVT3("a,b\"\",c", "a", "b\"\"", "c")
7136     CSVT3("a,\"B: \"\"Hi, I'm B\"\"\",c", "a", "B: \"Hi, I'm B\"", "c")
7137 
7138     return true;
7139 }
7140 
7141 //wxCSV_IO csvTest(true);
7142