+ // Set to 2 if also add all items in between
+ int addToExistingSelection = 0;
+
+ if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION )
+ {
+ if ( mouseEvent )
+ {
+ if ( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN ||
+ mouseEvent->GetEventType() == wxEVT_RIGHT_UP )
+ {
+ // Allow right-click for context menu without
+ // disturbing the selection.
+ if ( GetSelectedProperties().size() <= 1 ||
+ !alreadySelected )
+ return DoSelectAndEdit(prop, colIndex, selFlags);
+ return true;
+ }
+ else
+ {
+ if ( mouseEvent->ControlDown() )
+ {
+ addToExistingSelection = 1;
+ }
+ else if ( mouseEvent->ShiftDown() )
+ {
+ if ( selection.size() > 0 && !prop->IsCategory() )
+ addToExistingSelection = 2;
+ else
+ addToExistingSelection = 1;
+ }
+ }
+ }
+ }
+
+ if ( addToExistingSelection == 1 )
+ {
+ // Add/remove one
+ if ( !alreadySelected )
+ {
+ res = DoAddToSelection(prop, selFlags);
+ }
+ else if ( GetSelectedProperties().size() > 1 )
+ {
+ res = DoRemoveFromSelection(prop, selFlags);
+ }
+ }
+ else if ( addToExistingSelection == 2 )
+ {
+ // Add this, and all in between
+
+ // Find top selected property
+ wxPGProperty* topSelProp = selection[0];
+ int topSelPropY = topSelProp->GetY();
+ for ( unsigned int i=1; i<selection.size(); i++ )
+ {
+ wxPGProperty* p = selection[i];
+ int y = p->GetY();
+ if ( y < topSelPropY )
+ {
+ topSelProp = p;
+ topSelPropY = y;
+ }
+ }
+
+ wxPGProperty* startFrom;
+ wxPGProperty* stopAt;
+
+ if ( prop->GetY() <= topSelPropY )
+ {
+ // Property is above selection (or same)
+ startFrom = prop;
+ stopAt = topSelProp;
+ }
+ else
+ {
+ // Property is below selection
+ startFrom = topSelProp;
+ stopAt = prop;
+ }
+
+ // Iterate through properties in-between, and select them
+ wxPropertyGridIterator it;
+
+ for ( it = GetIterator(wxPG_ITERATE_VISIBLE, startFrom);
+ !it.AtEnd();
+ it++ )
+ {
+ wxPGProperty* p = *it;
+
+ if ( !p->IsCategory() &&
+ !m_pState->DoIsPropertySelected(p) )
+ {
+ DoAddToSelection(p, selFlags);
+ }
+
+ if ( p == stopAt )
+ break;
+ }
+ }
+ else
+ {
+ res = DoSelectAndEdit(prop, colIndex, selFlags);
+ }
+
+ return res;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection,
+ int selFlags )
+{
+ if ( newSelection.size() > 0 )
+ {
+ if ( !DoSelectProperty(newSelection[0], selFlags) )
+ return;
+ }
+ else
+ {
+ DoClearSelection(false, selFlags);
+ }
+
+ for ( unsigned int i = 1; i < newSelection.size(); i++ )
+ {
+ DoAddToSelection(newSelection[i], selFlags);
+ }
+
+ Refresh();
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::MakeColumnEditable( unsigned int column,
+ bool editable )
+{
+ wxASSERT( column != 1 );
+
+ wxArrayInt& cols = m_pState->m_editableColumns;
+
+ if ( editable )
+ {
+ cols.push_back(column);
+ }
+ else
+ {
+ for ( int i = cols.size() - 1; i > 0; i-- )
+ {
+ if ( cols[i] == (int)column )
+ cols.erase( cols.begin() + i );
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex,
+ int selFlags )
+{
+ wxPGProperty* selected = GetSelection();
+ wxCHECK_RET(selected, wxT("No property selected"));
+ wxCHECK_RET(colIndex != 1, wxT("Do not use this for column 1"));
+
+ if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
+ {
+ if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN,
+ selected, NULL, 0,
+ colIndex ) )
+ return;
+ }
+
+ wxString text;
+ const wxPGCell* cell = NULL;
+ if ( selected->HasCell(colIndex) )
+ {
+ cell = &selected->GetCell(colIndex);
+ if ( !cell->HasText() && colIndex == 0 )
+ text = selected->GetLabel();
+ }
+
+ if ( !cell )
+ {
+ if ( colIndex == 0 )
+ text = selected->GetLabel();
+ else
+ cell = &selected->GetOrCreateCell(colIndex);
+ }
+
+ if ( cell && cell->HasText() )
+ text = cell->GetText();
+
+ DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); // send event
+
+ m_selColumn = colIndex;
+
+ wxRect r = GetEditorWidgetRect(selected, m_selColumn);
+
+ wxWindow* tc = GenerateEditorTextCtrl(r.GetPosition(),
+ r.GetSize(),
+ text,
+ NULL,
+ wxTE_PROCESS_ENTER,
+ 0,
+ colIndex);
+
+ wxWindowID id = tc->GetId();
+ tc->Connect(id, wxEVT_COMMAND_TEXT_ENTER,
+ wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress),
+ NULL, this);
+ tc->Connect(id, wxEVT_KEY_DOWN,
+ wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress),
+ NULL, this);
+
+ tc->SetFocus();
+
+ m_labelEditor = wxStaticCast(tc, wxTextCtrl);
+ m_labelEditorProperty = selected;
+}
+
+// -----------------------------------------------------------------------
+
+void
+wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent& WXUNUSED(event) )
+{
+ DoEndLabelEdit(true);
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event )
+{
+ int keycode = event.GetKeyCode();
+
+ // If key code was registered as action trigger, then trigger that action
+ if ( keycode == WXK_ESCAPE )
+ {
+ DoEndLabelEdit(false);
+ }
+ else
+ {
+ event.Skip();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags )
+{
+ if ( !m_labelEditor )
+ return;
+
+ wxPGProperty* prop = m_labelEditorProperty;
+ wxASSERT(prop);
+
+ if ( commit )
+ {
+ if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
+ {
+ // wxPG_SEL_NOVALIDATE is passed correctly in selFlags
+ if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING,
+ prop, NULL, selFlags,
+ m_selColumn ) )
+ return;
+ }
+
+ wxString text = m_labelEditor->GetValue();
+ wxPGCell* cell = NULL;
+ if ( prop->HasCell(m_selColumn) )
+ {
+ cell = &prop->GetCell(m_selColumn);
+ }
+ else
+ {
+ if ( m_selColumn == 0 )
+ prop->SetLabel(text);
+ else
+ cell = &prop->GetOrCreateCell(m_selColumn);
+ }
+
+ if ( cell )
+ cell->SetText(text);
+ }
+
+ m_selColumn = 1;
+ int wasFocused = m_iFlags & wxPG_FL_FOCUSED;
+
+ DestroyEditorWnd(m_labelEditor);
+
+ m_labelEditor = NULL;
+ m_labelEditorProperty = NULL;
+
+ // Fix focus (needed at least on wxGTK)
+ if ( wasFocused )
+ SetFocusOnCanvas();
+
+ DrawItem(prop);
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::SetExtraStyle( long exStyle )
+{
+ if ( exStyle & wxPG_EX_ENABLE_TLP_TRACKING )
+ OnTLPChanging(::wxGetTopLevelParent(this));
+ else
+ OnTLPChanging(NULL);
+
+ if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING )
+ {
+#if defined(__WXMSW__)
+
+ /*
+ // Don't use WS_EX_COMPOSITED just now.
+ HWND hWnd;
+
+ if ( m_iFlags & wxPG_FL_IN_MANAGER )
+ hWnd = (HWND)GetParent()->GetHWND();
+ else
+ hWnd = (HWND)GetHWND();
+
+ ::SetWindowLong( hWnd, GWL_EXSTYLE,
+ ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED );
+ */
+
+//#elif defined(__WXGTK20__)
+#endif
+ // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window
+ // truly was double-buffered.
+ if ( !this->IsDoubleBuffered() )
+ {
+ exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING);
+ }
+ else
+ {
+ #if wxPG_DOUBLE_BUFFER
+ delete m_doubleBuffer;
+ m_doubleBuffer = NULL;
+ #endif
+ }
+ }
+
+ wxScrolledWindow::SetExtraStyle( exStyle );
+
+ if ( exStyle & wxPG_EX_INIT_NOCAT )
+ m_pState->InitNonCatMode();
+
+ if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS )
+ m_windowStyle |= wxPG_TOOLTIPS;
+
+ // Set global style
+ wxPGGlobalVars->m_extraStyle = exStyle;
+}
+
+// -----------------------------------------------------------------------
+
+// returns the best acceptable minimal size
+wxSize wxPropertyGrid::DoGetBestSize() const
+{
+ int lineHeight = wxMax(15, m_lineHeight);
+
+ // don't make the grid too tall (limit height to 10 items) but don't
+ // make it too small neither
+ int numLines = wxMin
+ (
+ wxMax(m_pState->m_properties->GetChildCount(), 3),
+ 10
+ );
+
+ wxClientDC dc(const_cast<wxPropertyGrid *>(this));
+ int width = m_marginWidth;
+ for ( unsigned int i = 0; i < m_pState->m_colWidths.size(); i++ )
+ {
+ width += m_pState->GetColumnFitWidth(dc, m_pState->DoGetRoot(), i, true);
+ }
+
+ const wxSize sz = wxSize(width, lineHeight*numLines + 40);
+
+ CacheBestSize(sz);
+ return sz;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP )
+{
+ if ( newTLP == m_tlp )
+ return;
+
+ wxLongLong currentTime = ::wxGetLocalTimeMillis();
+
+ //
+ // Parent changed so let's redetermine and re-hook the
+ // correct top-level window.
+ if ( m_tlp )
+ {
+ m_tlp->Disconnect( wxEVT_CLOSE_WINDOW,
+ wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
+ NULL, this );
+ m_tlpClosed = m_tlp;
+ m_tlpClosedTime = currentTime;
+ }
+
+ if ( newTLP )
+ {
+ // Only accept new tlp if same one was not just dismissed.
+ if ( newTLP != m_tlpClosed ||
+ m_tlpClosedTime+250 < currentTime )
+ {
+ newTLP->Connect( wxEVT_CLOSE_WINDOW,
+ wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
+ NULL, this );
+ m_tlpClosed = NULL;
+ }
+ else
+ {
+ newTLP = NULL;
+ }
+ }
+
+ m_tlp = newTLP;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnTLPClose( wxCloseEvent& event )
+{
+ // ClearSelection forces value validation/commit.
+ if ( event.CanVeto() && !DoClearSelection() )
+ {
+ event.Veto();
+ return;
+ }
+
+ // Ok, it can close, set tlp pointer to NULL. Some other event
+ // handler can of course veto the close, but our OnIdle() should
+ // then be able to regain the tlp pointer.
+ OnTLPChanging(NULL);
+
+ event.Skip();
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::Reparent( wxWindowBase *newParent )
+{
+ OnTLPChanging((wxWindow*)newParent);
+
+ bool res = wxScrolledWindow::Reparent(newParent);
+
+ return res;
+}
+
+// -----------------------------------------------------------------------
+// wxPropertyGrid Font and Colour Methods
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
+{
+ int x = 0, y = 0;
+
+ m_captionFont = wxScrolledWindow::GetFont();
+
+ GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
+ m_subgroup_extramargin = x + (x/2);
+ m_fontHeight = y;