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