From c937bcac0fb8a8df7c0cbfc8c478a2874fec3eb9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 16 Oct 2009 21:35:26 +0000 Subject: [PATCH] Add ellipsization support to wxDataViewCtrl. Implemented ellipsization in the generic, GTK and both OS X Carbon and Cocoa versions but it currently doesn't work well in GTK as it changes the item alignment unconditionally, this will need to be fixed later. The behaviour for the columns is currently inconsistent between ports too: under MSW they (natively) use wxELLIPSIZE_END, under GTK -- wxELLIPSIZE_NONE and under OS X the same ellipsization mode as the column contents, i.e. wxELLIPSIZE_MIDDLE by default. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62433 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/control.h | 3 + include/wx/dataview.h | 10 ++- include/wx/generic/dataview.h | 7 ++ include/wx/gtk/dataview.h | 2 + include/wx/osx/cocoa/dataview.h | 16 +++-- include/wx/osx/dataview.h | 3 + interface/wx/control.h | 3 + interface/wx/dataview.h | 38 ++++++++++ samples/dataview/mymodels.cpp | 3 +- src/common/ctrlcmn.cpp | 3 + src/generic/datavgen.cpp | 18 ++++- src/gtk/dataview.cpp | 29 ++++++++ src/osx/carbon/dataview.cpp | 119 ++++++++++++++++++++++++++------ src/osx/cocoa/dataview.mm | 69 ++++++++++++++++-- 15 files changed, 292 insertions(+), 32 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index d79c1041c9..1c86ad19ec 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -435,6 +435,7 @@ All (GUI): - Added wxMouseEventsManager. - Building OpenGL library is now enabled by default. - Fixed wxDataViewCtrl::Set{Foreground,Background}Colour(). +- Added wxDataViewRenderer::EnableEllipsize(). - Improve wxTreeCtrl::ScrollTo() in generic version (Raanan Barzel). - Added wxFont::[Make]{Bold,Italic,Smaller,Larger} and Scale[d]() methods. - Added wxDC::CopyAttributes() and use it in wxBufferedDC. diff --git a/include/wx/control.h b/include/wx/control.h index f947f0f203..8f1a1767c1 100644 --- a/include/wx/control.h +++ b/include/wx/control.h @@ -39,8 +39,11 @@ enum wxEllipsizeFlags wxELLIPSIZE_FLAGS_EXPAND_TABS }; +// NB: Don't change the order of these values, they're the same as in +// PangoEllipsizeMode enum. enum wxEllipsizeMode { + wxELLIPSIZE_NONE, wxELLIPSIZE_START, wxELLIPSIZE_MIDDLE, wxELLIPSIZE_END diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 9c6205d95e..ef5cc45dbd 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -411,7 +411,7 @@ public: wxDataViewRendererBase( const wxString &varianttype, wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int alignment = wxDVR_DEFAULT_ALIGNMENT ); - ~wxDataViewRendererBase(); + virtual ~wxDataViewRendererBase(); virtual bool Validate( wxVariant& WXUNUSED(value) ) { return true; } @@ -435,6 +435,14 @@ public: virtual void SetAlignment( int align ) = 0; virtual int GetAlignment() const = 0; + // enable or disable (if called with wxELLIPSIZE_NONE) replacing parts of + // the item text (hence this only makes sense for renderers showing + // text...) with ellipsis in order to make it fit the column width + virtual void EnableEllipsize(wxEllipsizeMode mode = wxELLIPSIZE_MIDDLE) = 0; + void DisableEllipsize() { EnableEllipsize(wxELLIPSIZE_NONE); } + + virtual wxEllipsizeMode GetEllipsizeMode() const = 0; + // in-place editing virtual bool HasEditorCtrl() const { return false; } diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index ec81d7e6de..a9c57e1c4e 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -57,6 +57,11 @@ public: virtual void SetAlignment( int align ); virtual int GetAlignment() const; + virtual void EnableEllipsize(wxEllipsizeMode mode = wxELLIPSIZE_MIDDLE) + { m_ellipsizeMode = mode; } + virtual wxEllipsizeMode GetEllipsizeMode() const + { return m_ellipsizeMode; } + virtual void SetMode( wxDataViewCellMode mode ) { m_mode=mode; } virtual wxDataViewCellMode GetMode() const @@ -110,6 +115,8 @@ private: int m_align; wxDataViewCellMode m_mode; + wxEllipsizeMode m_ellipsizeMode; + DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewRenderer) }; diff --git a/include/wx/gtk/dataview.h b/include/wx/gtk/dataview.h index d217d2f88e..8ed57ec0d8 100644 --- a/include/wx/gtk/dataview.h +++ b/include/wx/gtk/dataview.h @@ -38,6 +38,8 @@ public: virtual void SetAlignment( int align ); virtual int GetAlignment() const; + virtual void EnableEllipsize(wxEllipsizeMode mode = wxELLIPSIZE_MIDDLE); + virtual wxEllipsizeMode GetEllipsizeMode() const; // GTK-specific implementation // --------------------------- diff --git a/include/wx/osx/cocoa/dataview.h b/include/wx/osx/cocoa/dataview.h index a5aa4509b3..1fffc75bd7 100644 --- a/include/wx/osx/cocoa/dataview.h +++ b/include/wx/osx/cocoa/dataview.h @@ -219,12 +219,16 @@ public: m_origTextColour = [textColour retain]; } + // The ellipsization mode which we need to set for each cell being rendered. + void SetEllipsizeMode(wxEllipsizeMode mode) { m_ellipsizeMode = mode; } + wxEllipsizeMode GetEllipsizeMode() const { return m_ellipsizeMode; } + + // Set the line break mode for the given cell using our m_ellipsizeMode + void ApplyLineBreakMode(NSCell *cell); + private: - void Init() - { - m_origFont = NULL; - m_origTextColour = NULL; - } + // common part of all ctors + void Init(); id m_Item; // item NOT owned by renderer id m_Object; // object that can be used by renderer for storing special data (owned by renderer) @@ -237,6 +241,8 @@ private: // we own those if they're non-NULL NSFont *m_origFont; NSColor *m_origTextColour; + + wxEllipsizeMode m_ellipsizeMode; }; // ============================================================================ diff --git a/include/wx/osx/dataview.h b/include/wx/osx/dataview.h index e564a42b70..a484880222 100644 --- a/include/wx/osx/dataview.h +++ b/include/wx/osx/dataview.h @@ -61,6 +61,9 @@ public: return true; } + virtual void EnableEllipsize(wxEllipsizeMode mode = wxELLIPSIZE_MIDDLE); + virtual wxEllipsizeMode GetEllipsizeMode() const; + // // implementation // diff --git a/interface/wx/control.h b/interface/wx/control.h index 9dcbf8f136..d96a9bc28c 100644 --- a/interface/wx/control.h +++ b/interface/wx/control.h @@ -47,6 +47,9 @@ enum wxEllipsizeFlags */ enum wxEllipsizeMode { + /// Don't ellipsize the text at all. @since 2.9.1 + wxELLIPSIZE_NONE, + /// Put the ellipsis at the start of the string, if the string needs ellipsization. wxELLIPSIZE_START, diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index 4e6639b172..a76c28ae67 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -1060,11 +1060,49 @@ public: wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int align = wxDVR_DEFAULT_ALIGNMENT ); + /** + Enable or disable replacing parts of the item text with ellipsis to + make it fit the column width. + + This method only makes sense for the renderers working with text, such + as wxDataViewTextRenderer or wxDataViewIconTextRenderer. + + By default wxELLIPSIZE_MIDDLE is used. + + @param mode + Ellipsization mode, use wxELLIPSIZE_NONE to disable. + + @since 2.9.1 + */ + void EnableEllipsize(wxEllipsizeMode mode = wxELLIPSIZE_MIDDLE); + + /** + Disable replacing parts of the item text with ellipsis. + + If ellipsizing is disabled, the string will be truncated if it doesn't + fit. + + This is the same as @code EnableEllipsize(wxELLIPSIZE_NONE) @endcode. + + @since 2.9.1 + */ + void DisableEllipsize(); + /** Returns the alignment. See SetAlignment() */ virtual int GetAlignment() const; + /** + Returns the ellipsize mode used by the renderer. + + If the return value is wxELLIPSIZE_NONE, the text is simply truncated + if it doesn't fit. + + @see EnableEllipsize() + */ + wxEllipsizeMode GetEllipsizeMode() const; + /** Returns the cell mode. */ diff --git a/samples/dataview/mymodels.cpp b/samples/dataview/mymodels.cpp index 4be852d6ff..0424b24936 100644 --- a/samples/dataview/mymodels.cpp +++ b/samples/dataview/mymodels.cpp @@ -338,7 +338,8 @@ MyListModel::MyListModel() : static const unsigned NUMBER_REAL_ITEMS = 100; m_textColValues.reserve(NUMBER_REAL_ITEMS); - for (unsigned int i = 0; i < NUMBER_REAL_ITEMS; i++) + m_textColValues.push_back("first row with long label to test ellipsization"); + for (unsigned int i = 1; i < NUMBER_REAL_ITEMS; i++) { m_textColValues.push_back(wxString::Format("real row %d", i)); } diff --git a/src/common/ctrlcmn.cpp b/src/common/ctrlcmn.cpp index b4a0eb6e65..f7145a78a5 100644 --- a/src/common/ctrlcmn.cpp +++ b/src/common/ctrlcmn.cpp @@ -245,6 +245,8 @@ wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxD wxASSERT_MSG(!curLine.Contains('\n'), "Use Ellipsize() instead!"); + wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" ); + // NOTE: this function assumes that any mnemonic/tab character has already // been handled if it was necessary to handle them (see Ellipsize()) @@ -348,6 +350,7 @@ wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxD } break; + case wxELLIPSIZE_NONE: default: wxFAIL_MSG("invalid ellipsize mode"); return curLine; diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index bdeadffd36..1eeff556ad 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -612,6 +612,7 @@ wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, m_dc = NULL; m_align = align; m_mode = mode; + m_ellipsizeMode = wxELLIPSIZE_MIDDLE; } wxDataViewRenderer::~wxDataViewRenderer() @@ -752,7 +753,22 @@ wxDataViewCustomRenderer::RenderText(wxDC& dc, rectText.x += xoffset; rectText.width -= xoffset; - dc.DrawLabel(text, rectText, align); + // check if we want to ellipsize the text if it doesn't fit + wxString ellipsizedText; + if ( GetEllipsizeMode() != wxELLIPSIZE_NONE ) + { + ellipsizedText = wxControl::Ellipsize + ( + text, + dc, + GetEllipsizeMode(), + rect.width, + wxELLIPSIZE_FLAGS_NONE + ); + } + + dc.DrawLabel(ellipsizedText.empty() ? text : ellipsizedText, + rectText, align); } // --------------------------------------------------------- diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index d951f74ea9..3f8eb008c1 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -1691,6 +1691,35 @@ int wxDataViewRenderer::GetAlignment() const return m_alignment; } +void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode) +{ + if ( gtk_check_version(2, 6, 0) != NULL ) + return; + + // we use the same values in wxEllipsizeMode as PangoEllipsizeMode so we + // can just cast between them + GValue gvalue = { 0, }; + g_value_init( &gvalue, PANGO_TYPE_ELLIPSIZE_MODE ); + g_value_set_enum( &gvalue, static_cast(mode) ); + g_object_set_property( G_OBJECT(m_renderer), "ellipsize", &gvalue ); + g_value_unset( &gvalue ); +} + +wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const +{ + if ( gtk_check_version(2, 6, 0) != NULL ) + return wxELLIPSIZE_NONE; + + GValue gvalue = { 0, }; + g_value_init( &gvalue, PANGO_TYPE_ELLIPSIZE_MODE ); + g_object_get_property( G_OBJECT(m_renderer), "ellipsize", &gvalue ); + wxEllipsizeMode + mode = static_cast(g_value_get_enum( &gvalue )); + g_value_unset( &gvalue ); + + return mode; +} + void wxDataViewRenderer::GtkOnTextEdited(const gchar *itempath, const wxString& str) { diff --git a/src/osx/carbon/dataview.cpp b/src/osx/carbon/dataview.cpp index c3724b0048..38f94678c9 100644 --- a/src/osx/carbon/dataview.cpp +++ b/src/osx/carbon/dataview.cpp @@ -2186,35 +2186,114 @@ void wxDataViewRenderer::SetAlignment(int align) m_alignment = align; } +namespace +{ + +// get the browser control or NULL if anything went wrong (it's not supposed to +// so we assert if it did) +wxMacDataViewDataBrowserListViewControl * +GetBrowserFromCol(wxDataViewColumn *col) +{ + wxCHECK_MSG( col, NULL, "should have a valid column" ); + + wxDataViewCtrl * const dvc = col->GetOwner(); + wxCHECK_MSG( dvc, NULL, "column must be associated with the control" ); + + return static_cast(dvc->GetPeer()); +} + +} // anonymous namespace + void wxDataViewRenderer::SetMode(wxDataViewCellMode mode) { - wxDataViewColumn* dataViewColumnPtr; + wxDataViewColumn * const col = GetOwner(); + wxMacDataViewDataBrowserListViewControl * const + browser = GetBrowserFromCol(col); + wxCHECK_RET( browser, "must be fully initialized" ); + const DataBrowserPropertyID colID = col->GetNativeData()->GetPropertyID(); - m_mode = mode; - dataViewColumnPtr = GetOwner(); - if (dataViewColumnPtr != NULL) - { - wxDataViewCtrl* dataViewCtrlPtr(dataViewColumnPtr->GetOwner()); + DataBrowserPropertyFlags flags; + verify_noerr( browser->GetPropertyFlags(colID, &flags) ); - if (dataViewCtrlPtr != NULL) + if ( (mode == wxDATAVIEW_CELL_EDITABLE) || + (mode == wxDATAVIEW_CELL_ACTIVATABLE) ) + flags |= kDataBrowserPropertyIsEditable; + else + flags &= ~kDataBrowserPropertyIsEditable; + + verify_noerr( browser->SetPropertyFlags(colID, flags) ); +} + +void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode) +{ + wxDataViewColumn * const col = GetOwner(); + + wxMacDataViewDataBrowserListViewControl * const + browser = GetBrowserFromCol(col); + wxCHECK_RET( browser, "must be fully initialized" ); + + const DataBrowserPropertyID colID = col->GetNativeData()->GetPropertyID(); + + DataBrowserPropertyFlags flags; + browser->GetPropertyFlags(colID, &flags); + + flags &= ~(kDataBrowserDoNotTruncateText | + kDataBrowserTruncateTextAtStart | + kDataBrowserTruncateTextMiddle | + kDataBrowserTruncateTextAtEnd); + + int flagToSet = 0; + switch ( mode ) { - wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + case wxELLIPSIZE_NONE: + flagToSet = kDataBrowserDoNotTruncateText; + break; - if (macDataViewListCtrlPtr != NULL) - { - DataBrowserPropertyFlags flags; + case wxELLIPSIZE_START: + flagToSet = kDataBrowserTruncateTextAtStart; + break; - verify_noerr(macDataViewListCtrlPtr->GetPropertyFlags(dataViewColumnPtr->GetNativeData()->GetPropertyID(),&flags)); - if ((mode == wxDATAVIEW_CELL_EDITABLE) || - (mode == wxDATAVIEW_CELL_ACTIVATABLE)) - flags |= kDataBrowserPropertyIsEditable; - else - flags &= ~kDataBrowserPropertyIsEditable; - verify_noerr(macDataViewListCtrlPtr->SetPropertyFlags(dataViewColumnPtr->GetNativeData()->GetPropertyID(),flags)); - } + case wxELLIPSIZE_MIDDLE: + flagToSet = kDataBrowserTruncateTextMiddle; + break; + + case wxELLIPSIZE_END: + flagToSet = kDataBrowserTruncateTextAtEnd; + break; } - } + + wxCHECK_RET( flagToSet, "unknown wxEllipsizeMode value" ); + + flags |= flagToSet; + verify_noerr( browser->SetPropertyFlags(colID, flags) ); +} + +wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const +{ + wxDataViewColumn * const col = GetOwner(); + + wxMacDataViewDataBrowserListViewControl * const + browser = GetBrowserFromCol(col); + wxCHECK_MSG( browser, wxELLIPSIZE_NONE, "must be fully initialized" ); + + const DataBrowserPropertyID colID = col->GetNativeData()->GetPropertyID(); + + DataBrowserPropertyFlags flags; + browser->GetPropertyFlags(colID, &flags); + + if ( flags & kDataBrowserDoNotTruncateText ) + return wxELLIPSIZE_NONE; + if ( flags & kDataBrowserTruncateTextAtStart ) + return wxELLIPSIZE_START; + if ( flags & kDataBrowserTruncateTextMiddle ) + return wxELLIPSIZE_MIDDLE; + if ( flags & kDataBrowserTruncateTextAtEnd ) + return wxELLIPSIZE_END; + + wxFAIL_MSG( "unknown flags" ); + + return wxELLIPSIZE_NONE; } void wxDataViewRenderer::SetNativeData(wxDataViewRendererNativeData* newNativeDataPtr) diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 50ca83236f..127e4bb2a2 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -159,13 +159,19 @@ static NSTableColumn* CreateNativeColumn(wxDataViewColumn const* columnPtr) // setting the visibility: [nativeColumn setHidden:static_cast(columnPtr->IsHidden())]; #endif + + wxDataViewRendererNativeData * const + renderData = columnPtr->GetRenderer()->GetNativeData(); + // setting the header: [[nativeColumn headerCell] setAlignment:ConvertToNativeHorizontalTextAlignment(columnPtr->GetAlignment())]; [[nativeColumn headerCell] setStringValue:[[wxCFStringRef(columnPtr->GetTitle()).AsNSString() retain] autorelease]]; + renderData->ApplyLineBreakMode([nativeColumn headerCell]); + // setting data cell's properties: [[nativeColumn dataCell] setWraps:NO]; // setting the default data cell: - [nativeColumn setDataCell:columnPtr->GetRenderer()->GetNativeData()->GetColumnCell()]; + [nativeColumn setDataCell:renderData->GetColumnCell()]; // setting the editablility: bool const dataCellIsEditable = (columnPtr->GetRenderer()->GetMode() == wxDATAVIEW_CELL_EDITABLE); @@ -1026,7 +1032,6 @@ wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer, wxWindowMac* WXUNUSED(pare if (self != nil) { // initializing the text part: - [self setLineBreakMode:NSLineBreakByTruncatingMiddle]; [self setSelectable:YES]; // initializing the image part: image = nil; @@ -1566,6 +1571,7 @@ wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer, wxWindowMac* WXUNUSED(pare if ( colText ) [cell setTextColor:colText]; + data->SetColumnPtr(tableColumn); data->SetItem(item); data->SetItemCell(cell); @@ -2120,6 +2126,47 @@ wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObj } } +// ---------------------------------------------------------------------------- +// wxDataViewRendererNativeData +// ---------------------------------------------------------------------------- + +void wxDataViewRendererNativeData::Init() +{ + m_origFont = NULL; + m_origTextColour = NULL; + m_ellipsizeMode = wxELLIPSIZE_MIDDLE; + + if ( m_ColumnCell ) + ApplyLineBreakMode(m_ColumnCell); +} + +void wxDataViewRendererNativeData::ApplyLineBreakMode(NSCell *cell) +{ + NSLineBreakMode nsMode = NSLineBreakByWordWrapping; + switch ( m_ellipsizeMode ) + { + case wxELLIPSIZE_NONE: + nsMode = NSLineBreakByClipping; + break; + + case wxELLIPSIZE_START: + nsMode = NSLineBreakByTruncatingHead; + break; + + case wxELLIPSIZE_MIDDLE: + nsMode = NSLineBreakByTruncatingMiddle; + break; + + case wxELLIPSIZE_END: + nsMode = NSLineBreakByTruncatingTail; + break; + } + + wxASSERT_MSG( nsMode != NSLineBreakByWordWrapping, "unknown wxEllipsizeMode" ); + + [cell setLineBreakMode: nsMode]; +} + // --------------------------------------------------------- // wxDataViewRenderer // --------------------------------------------------------- @@ -2152,6 +2199,22 @@ void wxDataViewRenderer::SetNativeData(wxDataViewRendererNativeData* newNativeDa m_NativeDataPtr = newNativeDataPtr; } +void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode) +{ + // we need to store this value to apply it to the columns headerCell in + // CreateNativeColumn() + GetNativeData()->SetEllipsizeMode(mode); + + // but we may already apply it to the column cell which will be used for + // this column + GetNativeData()->ApplyLineBreakMode(GetNativeData()->GetColumnCell()); +} + +wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const +{ + return GetNativeData()->GetEllipsizeMode(); +} + IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase) // --------------------------------------------------------- @@ -2182,7 +2245,6 @@ wxDataViewTextRenderer::wxDataViewTextRenderer(wxString const& varianttype, wxDa cell = [[NSTextFieldCell alloc] init]; [cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)]; - [cell setLineBreakMode:NSLineBreakByTruncatingMiddle]; SetNativeData(new wxDataViewRendererNativeData(cell)); [cell release]; } @@ -2285,7 +2347,6 @@ wxDataViewDateRenderer::wxDataViewDateRenderer(wxString const& varianttype, wxDa [dateFormatter setDateStyle:NSDateFormatterShortStyle]; cell = [[NSTextFieldCell alloc] init]; [cell setFormatter:dateFormatter]; - [cell setLineBreakMode:NSLineBreakByTruncatingMiddle]; SetNativeData(new wxDataViewRendererNativeData(cell,[NSDate dateWithString:@"2000-12-30 20:00:00 +0000"])); [cell release]; [dateFormatter release];