#ifndef WX_PRECOMP
#include "wx/icon.h"
+ #include "wx/log.h"
#endif //WX_PRECOMP
#import <AppKit/NSCursor.h>
#import <AppKit/NSImage.h>
#include "wx/cocoa/string.h"
+#include "wx/cocoa/autorelease.h"
IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxBitmap)
// End of data copied from src/mac/carbon/cursor.cpp
///////////////////////////////////////////////////////////////////////////
-/* wxStockCursor
+/* NSCursorCreateWithPrivateId
* Returns a newly allocated (i.e. retainCount == 1) NSCursor based on the
* classic Mac OS cursor data in this source file. This allows us to
* implement the "stock" wxWidgets cursors which aren't present in Cocoa.
- * FIXME: This function should be static.
- * FIXME: This function should use "Alloc" in preference to "Get" to follow
- * the proper Cocoa naming conventions.
*/
-NSCursor* wxGetStockCursor( short sIndex )
+static inline NSCursor* NSCursorCreateWithPrivateId(short sIndex)
{
ClassicCursor* pCursor = &gMacCursors[sIndex];
[theImage release];
}
-// Cursors by stock number
-wxCursor::wxCursor(int cursor_type)
-{
- m_refData = new wxCursorRefData;
-
- switch (cursor_type)
- {
- case wxCURSOR_IBEAM:
- M_CURSORDATA->m_hCursor = [[NSCursor IBeamCursor] retain];
- break;
- case wxCURSOR_ARROW:
- M_CURSORDATA->m_hCursor = [[NSCursor arrowCursor] retain];
- break;
-/* TODO:
- case wxCURSOR_COPY_ARROW:
- M_CURSORDATA->m_themeCursor = kThemeCopyArrowCursor ;
- break;
- case wxCURSOR_WAIT:
- M_CURSORDATA->m_themeCursor = kThemeWatchCursor ;
- break;
- case wxCURSOR_CROSS:
- M_CURSORDATA->m_themeCursor = kThemeCrossCursor;
- break;
- case wxCURSOR_SIZENWSE:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSizeNWSE);
- }
- break;
-*/
- case wxCURSOR_SIZENESW:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSizeNESW);
- }
- break;
-/* TODO:
- case wxCURSOR_SIZEWE:
- {
- M_CURSORDATA->m_themeCursor = kThemeResizeLeftRightCursor;
- }
- break;
-*/
- case wxCURSOR_SIZENS:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSizeNS);
- }
- break;
- case wxCURSOR_SIZING:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSize);
- }
- break;
-/* TODO:
- case wxCURSOR_HAND:
- {
- M_CURSORDATA->m_themeCursor = kThemePointingHandCursor;
- }
- break;
-*/
- case wxCURSOR_BULLSEYE:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorBullseye);
- }
- break;
- case wxCURSOR_PENCIL:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPencil);
- }
- break;
- case wxCURSOR_MAGNIFIER:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorMagnifier);
- }
- break;
- case wxCURSOR_NO_ENTRY:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorNoEntry);
- }
- break;
-/* TODO:
- case wxCURSOR_WATCH:
- {
- M_CURSORDATA->m_themeCursor = kThemeWatchCursor;
- break;
- }
-*/
- case wxCURSOR_PAINT_BRUSH:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPaintBrush);
- break;
- }
- case wxCURSOR_POINT_LEFT:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPointLeft);
- break;
- }
- case wxCURSOR_POINT_RIGHT:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPointRight);
- break;
- }
- case wxCURSOR_QUESTION_ARROW:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorQuestionArrow);
- break;
- }
- case wxCURSOR_BLANK:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorBlank);
- break;
- }
- case wxCURSOR_RIGHT_ARROW:
- {
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorRightArrow);
- break;
- }
- case wxCURSOR_SPRAYCAN:
+// Returns a system cursor given the NSCursor class method selector or
+// nil if NSCursor does not respond to the message.
+// For example, OS X before 10.3 won't respond to pointingHandCursor.
+static inline NSCursor* GetSystemCursorWithSelector(SEL cursorSelector)
+{
+ if([NSCursor respondsToSelector: cursorSelector])
+ return [NSCursor performSelector: cursorSelector];
+ else
+ return nil;
+}
+
+// Please maintain order as if this were an array keyed on wxStockCursor
+static inline SEL GetCursorSelectorForStockCursor(int stock_cursor_id)
+{
+ switch(stock_cursor_id)
+ {
+ case wxCURSOR_ARROW: return @selector(arrowCursor);
+ case wxCURSOR_RIGHT_ARROW: break;
+ case wxCURSOR_BULLSEYE: break;
+ case wxCURSOR_CHAR: break;
+ case wxCURSOR_CROSS: return @selector(crosshairCursor);
+ case wxCURSOR_HAND: return @selector(pointingHandCursor);
+ case wxCURSOR_IBEAM: return @selector(IBeamCursor);
+ case wxCURSOR_LEFT_BUTTON: break;
+ case wxCURSOR_MAGNIFIER: break;
+ case wxCURSOR_MIDDLE_BUTTON: break;
+ case wxCURSOR_NO_ENTRY: break;
+ case wxCURSOR_PAINT_BRUSH: break;
+ case wxCURSOR_PENCIL: break;
+ case wxCURSOR_POINT_LEFT: break;
+ case wxCURSOR_POINT_RIGHT: break;
+ case wxCURSOR_QUESTION_ARROW: break;
+ case wxCURSOR_RIGHT_BUTTON: break;
+ case wxCURSOR_SIZENESW: break;
+ case wxCURSOR_SIZENS: return @selector(resizeUpDownCursor);
+ case wxCURSOR_SIZENWSE: break;
+ case wxCURSOR_SIZEWE: return @selector(resizeLeftRightCursor);
+ case wxCURSOR_SIZING: break;
+ case wxCURSOR_SPRAYCAN: break;
+ case wxCURSOR_WAIT: break;
+ case wxCURSOR_WATCH: break;
+ case wxCURSOR_BLANK: break;
+ case wxCURSOR_ARROWWAIT: break;
+ default: break;
+ }
+ return NULL;
+}
+
+// Please maintain order as if this were an array keyed on wxStockCursor
+static inline int GetPrivateCursorIdForStockCursor(int stock_cursor_id)
+{
+ switch(stock_cursor_id)
+ {
+ case wxCURSOR_ARROW: break; // NSCursor
+ case wxCURSOR_RIGHT_ARROW: return kwxCursorRightArrow;
+ case wxCURSOR_BULLSEYE: return kwxCursorBullseye;
+ case wxCURSOR_CHAR: break;
+ case wxCURSOR_CROSS: break; // NSCursor
+ case wxCURSOR_HAND: break; // NSCursor (OS X >= 10.3)
+ case wxCURSOR_IBEAM: break; // NSCursor
+ case wxCURSOR_LEFT_BUTTON: break;
+ case wxCURSOR_MAGNIFIER: return kwxCursorMagnifier;
+ case wxCURSOR_MIDDLE_BUTTON: break;
+ case wxCURSOR_NO_ENTRY: return kwxCursorNoEntry;
+ case wxCURSOR_PAINT_BRUSH: return kwxCursorPaintBrush;
+ case wxCURSOR_PENCIL: return kwxCursorPencil;
+ case wxCURSOR_POINT_LEFT: return kwxCursorPointLeft;
+ case wxCURSOR_POINT_RIGHT: return kwxCursorPointRight;
+ case wxCURSOR_QUESTION_ARROW: return kwxCursorQuestionArrow;
+ case wxCURSOR_RIGHT_BUTTON: break;
+ case wxCURSOR_SIZENESW: return kwxCursorSizeNESW;
+ case wxCURSOR_SIZENS: return kwxCursorSizeNS; // also NSCursor
+ case wxCURSOR_SIZENWSE: return kwxCursorSizeNWSE;
+ case wxCURSOR_SIZEWE: break; // NSCursor
+ case wxCURSOR_SIZING: return kwxCursorSize;
+ case wxCURSOR_SPRAYCAN: return kwxCursorRoller;
+ case wxCURSOR_WAIT: break;
+ case wxCURSOR_WATCH: break;
+ case wxCURSOR_BLANK: return kwxCursorBlank;
+ case wxCURSOR_ARROWWAIT: break;
+ default: break;
+ }
+ return -1;
+}
+
+// Keep an array of stock cursors so they can share wxCursorRefData and thus
+// wxObject::IsSameAs will return true.
+// They will obviously be destroyed at static destruction time which should
+// theoretically be fine.
+static wxCursor s_stockCursors[wxCURSOR_MAX];
+
+// Cursors by stock number (enum wxStockCursor)
+wxCursor::wxCursor(int stock_cursor_id)
+{
+ // We default-constructed wxObject so our m_refData == NULL
+ if(stock_cursor_id >= 0 && stock_cursor_id < wxCURSOR_MAX)
+ {
+ // Attempt to reference an existing stock cursor
+ Ref(s_stockCursors[stock_cursor_id]);
+ }
+ // If we succeeded in getting an existing stock cursor, we're done.
+ if(m_refData != NULL)
+ return;
+
+ m_refData = new wxCursorRefData;
+
+ M_CURSORDATA->m_hCursor = nil;
+
+ wxCHECK_RET( stock_cursor_id > wxCURSOR_NONE && stock_cursor_id < wxCURSOR_MAX,
+ wxT("invalid cursor id in wxCursor() ctor") );
+
+ // Stage 1: Try a system cursor
+ SEL cursorSelector;
+ if( (cursorSelector = GetCursorSelectorForStockCursor(stock_cursor_id)) != NULL)
+ {
+ M_CURSORDATA->m_hCursor = [GetSystemCursorWithSelector(cursorSelector) retain];
+ }
+
+ // TODO: Provide a pointing hand for OS X < 10.3 if desired
+
+ // Stage 2: Try one of the 'CURS'-style cursors
+ if(M_CURSORDATA->m_hCursor == nil)
+ {
+ int privateId;
+ if( (privateId = GetPrivateCursorIdForStockCursor(stock_cursor_id)) >= 0)
{
- M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorRoller);
- break;
+ M_CURSORDATA->m_hCursor = NSCursorCreateWithPrivateId(privateId);
}
- case wxCURSOR_CHAR:
- case wxCURSOR_LEFT_BUTTON:
- case wxCURSOR_RIGHT_BUTTON:
- case wxCURSOR_MIDDLE_BUTTON:
- default:
- break;
+ }
+
+ // Stage 3: Give up, complain, and use a normal arrow
+ if(M_CURSORDATA->m_hCursor == nil)
+ {
+ wxLogDebug(wxT("Could not find suitable cursor for wxStockCursor = %d. Using normal pointer."), stock_cursor_id);
+ M_CURSORDATA->m_hCursor = [[NSCursor arrowCursor] retain];
+ }
+
+ // This should never happen as the arrowCursor should always exist.
+ wxASSERT(M_CURSORDATA->m_hCursor != nil);
+
+ // Store ourself as the new stock cursor for this ID so that future
+ // calls will share the same ref data.
+ if(stock_cursor_id >= 0 && stock_cursor_id < wxCURSOR_MAX)
+ {
+ s_stockCursors[stock_cursor_id] = *this;
}
}