PageRenderTime 50ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/osx/cocoa/dataview.mm

https://bitbucket.org/lennonchan/cafu
Objective C++ | 1548 lines | 1081 code | 252 blank | 215 comment | 112 complexity | 8896804a4d79e8bdaaf12b9cb3c0ed74 MD5 | raw file
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name: src/osx/cocoa/dataview.mm
  3. // Purpose: wxDataView
  4. // Author:
  5. // Modified by:
  6. // Created: 2009-01-31
  7. // RCS-ID: $Id: dataview.mm$
  8. // Copyright:
  9. // Licence: wxWindows licence
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #include "wx/wxprec.h"
  12. #if (wxUSE_DATAVIEWCTRL == 1) && !defined(wxUSE_GENERICDATAVIEWCTRL)
  13. #ifndef WX_PRECOMP
  14. #include "wx/app.h"
  15. #include "wx/toplevel.h"
  16. #include "wx/font.h"
  17. #include "wx/settings.h"
  18. #include "wx/utils.h"
  19. #endif
  20. #include "wx/osx/private.h"
  21. #include "wx/osx/cocoa/dataview.h"
  22. #include "wx/renderer.h"
  23. #include "wx/stopwatch.h"
  24. #include "wx/dcgraph.h"
  25. // ============================================================================
  26. // Constants used locally
  27. // ============================================================================
  28. #define DataViewPboardType @"OutlineViewItem"
  29. // ============================================================================
  30. // Classes used locally in dataview.mm
  31. // ============================================================================
  32. // ============================================================================
  33. // wxPointerObject
  34. // ============================================================================
  35. @implementation wxPointerObject
  36. -(id) init
  37. {
  38. self = [super init];
  39. if (self != nil)
  40. self->pointer = NULL;
  41. return self;
  42. }
  43. -(id) initWithPointer:(void*) initPointer
  44. {
  45. self = [super init];
  46. if (self != nil)
  47. self->pointer = initPointer;
  48. return self;
  49. }
  50. //
  51. // inherited methods from NSObject
  52. //
  53. -(BOOL) isEqual:(id)object
  54. {
  55. return (object != nil) &&
  56. ([object isKindOfClass:[wxPointerObject class]]) &&
  57. (pointer == [((wxPointerObject*) object) pointer]);
  58. }
  59. -(NSUInteger) hash
  60. {
  61. return (NSUInteger) pointer;
  62. }
  63. -(void*) pointer
  64. {
  65. return pointer;
  66. }
  67. -(void) setPointer:(void*) newPointer
  68. {
  69. pointer = newPointer;
  70. }
  71. @end
  72. namespace
  73. {
  74. inline wxDataViewItem wxDataViewItemFromItem(id item)
  75. {
  76. return wxDataViewItem([static_cast<wxPointerObject *>(item) pointer]);
  77. }
  78. inline wxDataViewItem wxDataViewItemFromMaybeNilItem(id item)
  79. {
  80. return item == nil ? wxDataViewItem() : wxDataViewItemFromItem(item);
  81. }
  82. } // anonymous namespace
  83. // ----------------------------------------------------------------------------
  84. // wxCustomRendererObject
  85. // ----------------------------------------------------------------------------
  86. @interface wxCustomRendererObject : NSObject <NSCopying>
  87. {
  88. @public
  89. wxDataViewCustomRenderer* customRenderer; // not owned by the class
  90. }
  91. -(id) init;
  92. -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer;
  93. @end
  94. @implementation wxCustomRendererObject
  95. -(id) init
  96. {
  97. self = [super init];
  98. if (self != nil)
  99. {
  100. customRenderer = NULL;
  101. }
  102. return self;
  103. }
  104. -(id) initWithRenderer:(wxDataViewCustomRenderer*)renderer
  105. {
  106. self = [super init];
  107. if (self != nil)
  108. {
  109. customRenderer = renderer;
  110. }
  111. return self;
  112. }
  113. -(id) copyWithZone:(NSZone*)zone
  114. {
  115. wxCustomRendererObject* copy;
  116. copy = [[[self class] allocWithZone:zone] init];
  117. copy->customRenderer = customRenderer;
  118. return copy;
  119. }
  120. @end
  121. // ----------------------------------------------------------------------------
  122. // wxDVCNSTableColumn: exists only to override NSTableColumn:dataCellForRow:
  123. // ----------------------------------------------------------------------------
  124. @interface wxDVCNSTableColumn : NSTableColumn
  125. {
  126. }
  127. -(id) dataCellForRow:(NSInteger)row;
  128. @end
  129. @implementation wxDVCNSTableColumn
  130. -(id) dataCellForRow:(NSInteger)row
  131. {
  132. // what we want to do here is to simply return nil for the cells which
  133. // shouldn't show anything as otherwise we would show e.g. empty combo box
  134. // or progress cells in the columns using the corresponding types even for
  135. // the container rows which is wrong
  136. // half of the problem is just finding the objects we need from the column
  137. // pointer which is itself stashed inside wxPointerObject which we use as
  138. // our identifier
  139. const wxDataViewColumn * const
  140. dvCol = static_cast<wxDataViewColumn *>(
  141. [(wxPointerObject *)[self identifier] pointer]
  142. );
  143. const wxDataViewCtrl * const dvc = dvCol->GetOwner();
  144. const wxCocoaDataViewControl * const
  145. peer = static_cast<wxCocoaDataViewControl *>(dvc->GetPeer());
  146. // once we do have everything, simply ask NSOutlineView for the item...
  147. const id item = peer->GetItemAtRow(row);
  148. if ( item )
  149. {
  150. // ... and if it succeeded, ask the model whether it has any value
  151. wxDataViewItem dvItem(wxDataViewItemFromItem(item));
  152. if ( !dvc->GetModel()->HasValue(dvItem, dvCol->GetModelColumn()) )
  153. return nil;
  154. }
  155. return [super dataCellForRow:row];
  156. }
  157. @end
  158. // ============================================================================
  159. // local helpers
  160. // ============================================================================
  161. namespace
  162. {
  163. // convert from NSObject to different C++ types: all these functions check
  164. // that the conversion really makes sense and assert if it doesn't
  165. wxString ObjectToString(NSObject *object)
  166. {
  167. wxCHECK_MSG( [object isKindOfClass:[NSString class]], "",
  168. wxString::Format
  169. (
  170. "string expected but got %s",
  171. wxCFStringRef::AsString([object className])
  172. ));
  173. return wxCFStringRef([((NSString*) object) retain]).AsString();
  174. }
  175. bool ObjectToBool(NSObject *object)
  176. {
  177. // actually the value must be of NSCFBoolean class but it's private so we
  178. // can't check for it directly
  179. wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], false,
  180. wxString::Format
  181. (
  182. "number expected but got %s",
  183. wxCFStringRef::AsString([object className])
  184. ));
  185. return [(NSNumber *)object boolValue];
  186. }
  187. long ObjectToLong(NSObject *object)
  188. {
  189. wxCHECK_MSG( [object isKindOfClass:[NSNumber class]], -1,
  190. wxString::Format
  191. (
  192. "number expected but got %s",
  193. wxCFStringRef::AsString([object className])
  194. ));
  195. return [(NSNumber *)object longValue];
  196. }
  197. wxDateTime ObjectToDate(NSObject *object)
  198. {
  199. wxCHECK_MSG( [object isKindOfClass:[NSDate class]], wxInvalidDateTime,
  200. wxString::Format
  201. (
  202. "date expected but got %s",
  203. wxCFStringRef::AsString([object className])
  204. ));
  205. // get the number of seconds since 1970-01-01 UTC and this is the only
  206. // way to convert a double to a wxLongLong
  207. const wxLongLong seconds = [((NSDate*) object) timeIntervalSince1970];
  208. wxDateTime dt(1, wxDateTime::Jan, 1970);
  209. dt.Add(wxTimeSpan(0,0,seconds));
  210. // the user has entered a date in the local timezone but seconds
  211. // contains the number of seconds from date in the local timezone
  212. // since 1970-01-01 UTC; therefore, the timezone information has to be
  213. // transferred to wxWidgets, too:
  214. dt.MakeFromTimezone(wxDateTime::UTC);
  215. return dt;
  216. }
  217. NSInteger CompareItems(id item1, id item2, void* context)
  218. {
  219. NSArray* const sortDescriptors = (NSArray*) context;
  220. NSUInteger const count = [sortDescriptors count];
  221. NSInteger result = NSOrderedSame;
  222. for ( NSUInteger i = 0; i < count && result == NSOrderedSame; ++i )
  223. {
  224. wxSortDescriptorObject* const
  225. sortDescriptor = (wxSortDescriptorObject*)
  226. [sortDescriptors objectAtIndex:i];
  227. int rc = [sortDescriptor modelPtr]->Compare
  228. (
  229. wxDataViewItemFromItem(item1),
  230. wxDataViewItemFromItem(item2),
  231. [sortDescriptor columnPtr]->GetModelColumn(),
  232. [sortDescriptor ascending] == YES
  233. );
  234. if ( rc < 0 )
  235. result = NSOrderedAscending;
  236. else if ( rc > 0 )
  237. result = NSOrderedDescending;
  238. }
  239. return result;
  240. }
  241. NSTextAlignment ConvertToNativeHorizontalTextAlignment(int alignment)
  242. {
  243. if (alignment & wxALIGN_CENTER_HORIZONTAL)
  244. return NSCenterTextAlignment;
  245. else if (alignment & wxALIGN_RIGHT)
  246. return NSRightTextAlignment;
  247. else
  248. return NSLeftTextAlignment;
  249. }
  250. NSTableColumn* CreateNativeColumn(const wxDataViewColumn *column)
  251. {
  252. wxDataViewRenderer * const renderer = column->GetRenderer();
  253. wxCHECK_MSG( renderer, NULL, "column should have a renderer" );
  254. wxDVCNSTableColumn * const nativeColumn(
  255. [[wxDVCNSTableColumn alloc] initWithIdentifier:
  256. [[[wxPointerObject alloc] initWithPointer:
  257. const_cast<wxDataViewColumn*>(column)]
  258. autorelease]]
  259. );
  260. // setting the size related parameters:
  261. int resizingMask;
  262. if (column->IsResizeable())
  263. {
  264. resizingMask = NSTableColumnUserResizingMask;
  265. [nativeColumn setMinWidth:column->GetMinWidth()];
  266. [nativeColumn setMaxWidth:column->GetMaxWidth()];
  267. }
  268. else // column is not resizable [by user]
  269. {
  270. // if the control doesn't show a header, make the columns resize
  271. // automatically, this is particularly important for the single column
  272. // controls (such as wxDataViewTreeCtrl) as their unique column should
  273. // always take up all the available splace
  274. resizingMask = column->GetOwner()->HasFlag(wxDV_NO_HEADER)
  275. ? NSTableColumnAutoresizingMask
  276. : NSTableColumnNoResizing;
  277. }
  278. [nativeColumn setResizingMask:resizingMask];
  279. #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  280. // setting the visibility:
  281. [nativeColumn setHidden:static_cast<BOOL>(column->IsHidden())];
  282. #endif
  283. wxDataViewRendererNativeData * const renderData = renderer->GetNativeData();
  284. // setting the header:
  285. [[nativeColumn headerCell] setAlignment:
  286. ConvertToNativeHorizontalTextAlignment(column->GetAlignment())];
  287. [[nativeColumn headerCell] setStringValue:
  288. [[wxCFStringRef(column->GetTitle()).AsNSString() retain] autorelease]];
  289. renderData->ApplyLineBreakMode([nativeColumn headerCell]);
  290. // setting data cell's properties:
  291. [[nativeColumn dataCell] setWraps:NO];
  292. // setting the default data cell:
  293. [nativeColumn setDataCell:renderData->GetColumnCell()];
  294. // setting the editablility:
  295. const bool isEditable = renderer->GetMode() == wxDATAVIEW_CELL_EDITABLE;
  296. [nativeColumn setEditable:isEditable];
  297. [[nativeColumn dataCell] setEditable:isEditable];
  298. return nativeColumn;
  299. }
  300. } // anonymous namespace
  301. // ============================================================================
  302. // Public helper functions for dataview implementation on OSX
  303. // ============================================================================
  304. wxWidgetImplType* CreateDataView(wxWindowMac* wxpeer,
  305. wxWindowMac* WXUNUSED(parent),
  306. wxWindowID WXUNUSED(id),
  307. const wxPoint& pos,
  308. const wxSize& size,
  309. long style,
  310. long WXUNUSED(extraStyle))
  311. {
  312. return new wxCocoaDataViewControl(wxpeer,pos,size,style);
  313. }
  314. // ============================================================================
  315. // wxSortDescriptorObject
  316. // ============================================================================
  317. @implementation wxSortDescriptorObject
  318. -(id) init
  319. {
  320. self = [super init];
  321. if (self != nil)
  322. {
  323. columnPtr = NULL;
  324. modelPtr = NULL;
  325. }
  326. return self;
  327. }
  328. -(id)
  329. initWithModelPtr:(wxDataViewModel*)initModelPtr
  330. sortingColumnPtr:(wxDataViewColumn*)initColumnPtr
  331. ascending:(BOOL)sortAscending
  332. {
  333. self = [super initWithKey:@"dummy" ascending:sortAscending];
  334. if (self != nil)
  335. {
  336. columnPtr = initColumnPtr;
  337. modelPtr = initModelPtr;
  338. }
  339. return self;
  340. }
  341. -(id) copyWithZone:(NSZone*)zone
  342. {
  343. wxSortDescriptorObject* copy;
  344. copy = [super copyWithZone:zone];
  345. copy->columnPtr = columnPtr;
  346. copy->modelPtr = modelPtr;
  347. return copy;
  348. }
  349. //
  350. // access to model column's index
  351. //
  352. -(wxDataViewColumn*) columnPtr
  353. {
  354. return columnPtr;
  355. }
  356. -(wxDataViewModel*) modelPtr
  357. {
  358. return modelPtr;
  359. }
  360. -(void) setColumnPtr:(wxDataViewColumn*)newColumnPtr
  361. {
  362. columnPtr = newColumnPtr;
  363. }
  364. -(void) setModelPtr:(wxDataViewModel*)newModelPtr
  365. {
  366. modelPtr = newModelPtr;
  367. }
  368. @end
  369. // ============================================================================
  370. // wxCocoaOutlineDataSource
  371. // ============================================================================
  372. @implementation wxCocoaOutlineDataSource
  373. //
  374. // constructors / destructor
  375. //
  376. -(id) init
  377. {
  378. self = [super init];
  379. if (self != nil)
  380. {
  381. implementation = NULL;
  382. model = NULL;
  383. currentParentItem = nil;
  384. children = [[NSMutableArray alloc] init];
  385. items = [[NSMutableSet alloc] init];
  386. }
  387. return self;
  388. }
  389. -(void) dealloc
  390. {
  391. [currentParentItem release];
  392. [children release];
  393. [items release];
  394. [super dealloc];
  395. }
  396. //
  397. // methods of informal protocol:
  398. //
  399. -(BOOL)
  400. outlineView:(NSOutlineView*)outlineView
  401. acceptDrop:(id<NSDraggingInfo>)info
  402. item:(id)item childIndex:(NSInteger)index
  403. {
  404. wxUnusedVar(outlineView);
  405. wxUnusedVar(index);
  406. NSArray* supportedTypes(
  407. [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]
  408. );
  409. NSPasteboard* pasteboard([info draggingPasteboard]);
  410. NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
  411. if ( bestType == nil )
  412. return FALSE;
  413. wxDataViewCtrl * const dvc(implementation->GetDataViewCtrl());
  414. wxCHECK_MSG( dvc, false,
  415. "Pointer to data view control not set correctly." );
  416. wxCHECK_MSG( dvc->GetModel(), false,
  417. "Pointer to model not set correctly." );
  418. wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP, dvc->GetId());
  419. event.SetEventObject(dvc);
  420. event.SetItem(wxDataViewItemFromItem(item));
  421. event.SetModel(dvc->GetModel());
  422. BOOL dragSuccessful = false;
  423. if ( [bestType compare:DataViewPboardType] == NSOrderedSame )
  424. {
  425. NSArray* dataArray((NSArray*)
  426. [pasteboard propertyListForType:DataViewPboardType]);
  427. NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
  428. indexDraggedItem = 0;
  429. while (indexDraggedItem < noOfDraggedItems)
  430. {
  431. wxDataObjectComposite* dataObjects(
  432. implementation->GetDnDDataObjects((NSData*)
  433. [dataArray objectAtIndex:indexDraggedItem]));
  434. if (dataObjects && (dataObjects->GetFormatCount() > 0))
  435. {
  436. wxMemoryBuffer buffer;
  437. // copy data into data object:
  438. event.SetDataObject(dataObjects);
  439. event.SetDataFormat(
  440. implementation->GetDnDDataFormat(dataObjects));
  441. // copy data into buffer:
  442. dataObjects->GetDataHere(
  443. event.GetDataFormat().GetType(),
  444. buffer.GetWriteBuf(event.GetDataSize()));
  445. buffer.UngetWriteBuf(event.GetDataSize());
  446. event.SetDataBuffer(buffer.GetData());
  447. // finally, send event:
  448. if (dvc->HandleWindowEvent(event) && event.IsAllowed())
  449. {
  450. dragSuccessful = true;
  451. ++indexDraggedItem;
  452. }
  453. else
  454. {
  455. dragSuccessful = true;
  456. indexDraggedItem = noOfDraggedItems; // stop loop
  457. }
  458. }
  459. else
  460. {
  461. dragSuccessful = false;
  462. indexDraggedItem = noOfDraggedItems; // stop loop
  463. }
  464. // clean-up:
  465. delete dataObjects;
  466. }
  467. }
  468. else
  469. {
  470. // needed to convert internally used UTF-16 representation to a UTF-8
  471. // representation
  472. CFDataRef osxData;
  473. wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
  474. wxTextDataObject* textDataObject(new wxTextDataObject());
  475. osxData = ::CFStringCreateExternalRepresentation
  476. (
  477. kCFAllocatorDefault,
  478. (CFStringRef)[pasteboard stringForType:NSStringPboardType],
  479. kCFStringEncodingUTF8,
  480. 32
  481. );
  482. if (textDataObject->SetData(::CFDataGetLength(osxData),
  483. ::CFDataGetBytePtr(osxData)))
  484. dataObjects->Add(textDataObject);
  485. else
  486. delete textDataObject;
  487. // send event if data could be copied:
  488. if (dataObjects->GetFormatCount() > 0)
  489. {
  490. event.SetDataObject(dataObjects);
  491. event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
  492. if (dvc->HandleWindowEvent(event) && event.IsAllowed())
  493. dragSuccessful = true;
  494. else
  495. dragSuccessful = false;
  496. }
  497. else
  498. dragSuccessful = false;
  499. // clean up:
  500. ::CFRelease(osxData);
  501. delete dataObjects;
  502. }
  503. return dragSuccessful;
  504. }
  505. -(id) outlineView:(NSOutlineView*)outlineView
  506. child:(NSInteger)index
  507. ofItem:(id)item
  508. {
  509. wxUnusedVar(outlineView);
  510. if ((item == currentParentItem) &&
  511. (index < ((NSInteger) [self getChildCount])))
  512. return [self getChild:index];
  513. wxDataViewItemArray dataViewChildren;
  514. wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
  515. model->GetChildren(wxDataViewItemFromMaybeNilItem(item), dataViewChildren);
  516. [self bufferItem:item withChildren:&dataViewChildren];
  517. if ([sortDescriptors count] > 0)
  518. [children sortUsingFunction:CompareItems context:sortDescriptors];
  519. return [self getChild:index];
  520. }
  521. -(BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
  522. {
  523. wxUnusedVar(outlineView);
  524. wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
  525. return model->IsContainer(wxDataViewItemFromItem(item));
  526. }
  527. -(NSInteger) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
  528. {
  529. wxUnusedVar(outlineView);
  530. NSInteger noOfChildren;
  531. wxDataViewItemArray dataViewChildren;
  532. wxCHECK_MSG( model, 0, "Valid model in data source does not exist." );
  533. noOfChildren = model->GetChildren(wxDataViewItemFromMaybeNilItem(item),
  534. dataViewChildren);
  535. [self bufferItem:item withChildren:&dataViewChildren];
  536. if ([sortDescriptors count] > 0)
  537. [children sortUsingFunction:CompareItems context:sortDescriptors];
  538. return noOfChildren;
  539. }
  540. -(id)
  541. outlineView:(NSOutlineView*)outlineView
  542. objectValueForTableColumn:(NSTableColumn*)tableColumn
  543. byItem:(id)item
  544. {
  545. wxUnusedVar(outlineView);
  546. wxCHECK_MSG( model, nil, "Valid model in data source does not exist." );
  547. wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
  548. const unsigned colIdx = col->GetModelColumn();
  549. wxDataViewItem dataViewItem(wxDataViewItemFromItem(item));
  550. if ( model->HasValue(dataViewItem, colIdx) )
  551. {
  552. wxVariant value;
  553. model->GetValue(value,dataViewItem, colIdx);
  554. col->GetRenderer()->SetValue(value);
  555. }
  556. return nil;
  557. }
  558. -(void)
  559. outlineView:(NSOutlineView*)outlineView
  560. setObjectValue:(id)object
  561. forTableColumn:(NSTableColumn*)tableColumn
  562. byItem:(id)item
  563. {
  564. wxUnusedVar(outlineView);
  565. wxDataViewColumn* col(static_cast<wxDataViewColumn*>([[tableColumn identifier] pointer]));
  566. col->GetRenderer()->
  567. OSXOnCellChanged(object, wxDataViewItemFromItem(item), col->GetModelColumn());
  568. }
  569. -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
  570. {
  571. wxUnusedVar(oldDescriptors);
  572. // Warning: the new sort descriptors are guaranteed to be only of type
  573. // NSSortDescriptor! Therefore, the sort descriptors for the data source
  574. // have to be converted.
  575. NSArray* newDescriptors;
  576. NSMutableArray* wxSortDescriptors;
  577. NSUInteger noOfDescriptors;
  578. wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
  579. // convert NSSortDescriptors to wxSortDescriptorObjects:
  580. newDescriptors = [outlineView sortDescriptors];
  581. noOfDescriptors = [newDescriptors count];
  582. wxSortDescriptors = [NSMutableArray arrayWithCapacity:noOfDescriptors];
  583. for (NSUInteger i=0; i<noOfDescriptors; ++i)
  584. {
  585. NSSortDescriptor* const newDescriptor = [newDescriptors objectAtIndex:i];
  586. [wxSortDescriptors addObject:[[[wxSortDescriptorObject alloc] initWithModelPtr:model
  587. sortingColumnPtr:dvc->GetColumn([[newDescriptor key] intValue])
  588. ascending:[newDescriptor ascending]] autorelease]];
  589. }
  590. [(wxCocoaOutlineDataSource*)[outlineView dataSource] setSortDescriptors:wxSortDescriptors];
  591. // send first the event to wxWidgets that the sorting has changed so that
  592. // the program can do special actions before the sorting actually starts:
  593. wxDataViewEvent event(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED,dvc->GetId()); // variable defintion
  594. event.SetEventObject(dvc);
  595. if (noOfDescriptors > 0)
  596. {
  597. wxDataViewColumn* const col = [[wxSortDescriptors objectAtIndex:0] columnPtr];
  598. event.SetColumn(dvc->GetColumnPosition(col));
  599. event.SetDataViewColumn(col);
  600. }
  601. dvc->GetEventHandler()->ProcessEvent(event);
  602. // start re-ordering the data;
  603. // children's buffer must be cleared first because it contains the old order:
  604. [self clearChildren];
  605. // sorting is done while reloading the data:
  606. [outlineView reloadData];
  607. }
  608. -(NSDragOperation) outlineView:(NSOutlineView*)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
  609. {
  610. wxUnusedVar(outlineView);
  611. wxUnusedVar(index);
  612. NSArray* supportedTypes([NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]);
  613. NSPasteboard* pasteboard([info draggingPasteboard]);
  614. NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
  615. if (bestType == nil)
  616. return NSDragOperationNone;
  617. NSDragOperation dragOperation = NSDragOperationNone;
  618. wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl());
  619. wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly.");
  620. wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly.");
  621. wxDataViewEvent
  622. event(wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE,dvc->GetId());
  623. event.SetEventObject(dvc);
  624. event.SetItem(wxDataViewItemFromItem(item));
  625. event.SetModel(dvc->GetModel());
  626. if ([bestType compare:DataViewPboardType] == NSOrderedSame)
  627. {
  628. NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]);
  629. NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
  630. indexDraggedItem = 0;
  631. while (indexDraggedItem < noOfDraggedItems)
  632. {
  633. wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem]));
  634. if (dataObjects && (dataObjects->GetFormatCount() > 0))
  635. {
  636. wxMemoryBuffer buffer;
  637. // copy data into data object:
  638. event.SetDataObject(dataObjects);
  639. event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
  640. // copy data into buffer:
  641. dataObjects->GetDataHere(event.GetDataFormat().GetType(),buffer.GetWriteBuf(event.GetDataSize()));
  642. buffer.UngetWriteBuf(event.GetDataSize());
  643. event.SetDataBuffer(buffer.GetData());
  644. // finally, send event:
  645. if (dvc->HandleWindowEvent(event) && event.IsAllowed())
  646. {
  647. dragOperation = NSDragOperationEvery;
  648. ++indexDraggedItem;
  649. }
  650. else
  651. {
  652. dragOperation = NSDragOperationNone;
  653. indexDraggedItem = noOfDraggedItems; // stop loop
  654. }
  655. }
  656. else
  657. {
  658. dragOperation = NSDragOperationNone;
  659. indexDraggedItem = noOfDraggedItems; // stop loop
  660. }
  661. // clean-up:
  662. delete dataObjects;
  663. }
  664. }
  665. else
  666. {
  667. // needed to convert internally used UTF-16 representation to a UTF-8
  668. // representation
  669. CFDataRef osxData;
  670. wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
  671. wxTextDataObject* textDataObject(new wxTextDataObject());
  672. osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType],kCFStringEncodingUTF8,32);
  673. if (textDataObject->SetData(::CFDataGetLength(osxData),::CFDataGetBytePtr(osxData)))
  674. dataObjects->Add(textDataObject);
  675. else
  676. delete textDataObject;
  677. // send event if data could be copied:
  678. if (dataObjects->GetFormatCount() > 0)
  679. {
  680. event.SetDataObject(dataObjects);
  681. event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects));
  682. if (dvc->HandleWindowEvent(event) && event.IsAllowed())
  683. dragOperation = NSDragOperationEvery;
  684. else
  685. dragOperation = NSDragOperationNone;
  686. }
  687. else
  688. dragOperation = NSDragOperationNone;
  689. // clean up:
  690. ::CFRelease(osxData);
  691. delete dataObjects;
  692. }
  693. return dragOperation;
  694. }
  695. -(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard
  696. {
  697. wxUnusedVar(outlineView);
  698. // the pasteboard will be filled up with an array containing the data as
  699. // returned by the events (including the data type) and a concatenation of
  700. // text (string) data; the text data will only be put onto the pasteboard
  701. // if for all items a string representation exists
  702. wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
  703. wxDataViewItemArray dataViewItems;
  704. wxCHECK_MSG(dvc, false,"Pointer to data view control not set correctly.");
  705. wxCHECK_MSG(dvc->GetModel(),false,"Pointer to model not set correctly.");
  706. if ([writeItems count] > 0)
  707. {
  708. bool dataStringAvailable(true); // a flag indicating if for all items a data string is available
  709. NSMutableArray* dataArray = [NSMutableArray arrayWithCapacity:[writeItems count]]; // data of all items
  710. wxString dataString; // contains the string data of all items
  711. // send a begin drag event for all selected items and proceed with
  712. // dragging unless the event is vetoed:
  713. wxDataViewEvent
  714. event(wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG,dvc->GetId());
  715. event.SetEventObject(dvc);
  716. event.SetModel(dvc->GetModel());
  717. for (size_t itemCounter=0; itemCounter<[writeItems count]; ++itemCounter)
  718. {
  719. bool itemStringAvailable(false); // a flag indicating if for the current item a string is available
  720. wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item
  721. wxString itemString; // contains the TAB concatenated data of an item
  722. event.SetItem(
  723. wxDataViewItemFromItem([writeItems objectAtIndex:itemCounter]));
  724. itemString = ::ConcatenateDataViewItemValues(dvc,event.GetItem());
  725. itemObject->Add(new wxTextDataObject(itemString));
  726. event.SetDataObject(itemObject);
  727. // check if event has not been vetoed:
  728. if (dvc->HandleWindowEvent(event) && event.IsAllowed() && (event.GetDataObject()->GetFormatCount() > 0))
  729. {
  730. size_t const noOfFormats = event.GetDataObject()->GetFormatCount();
  731. wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]);
  732. event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get);
  733. for (size_t formatCounter=0; formatCounter<noOfFormats; ++formatCounter)
  734. {
  735. // constant definitions for abbreviational purposes:
  736. wxDataFormatId const idDataFormat = dataFormats[formatCounter].GetType();
  737. size_t const dataSize = event.GetDataObject()->GetDataSize(idDataFormat);
  738. size_t const dataBufferSize = sizeof(wxDataFormatId)+dataSize;
  739. // variable definitions (used in all case statements):
  740. wxMemoryBuffer dataBuffer(dataBufferSize);
  741. dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId));
  742. switch (idDataFormat)
  743. {
  744. case wxDF_TEXT:
  745. // otherwise wxDF_UNICODETEXT already filled up
  746. // the string; and the UNICODE representation has
  747. // priority
  748. if (!itemStringAvailable)
  749. {
  750. event.GetDataObject()->GetDataHere(wxDF_TEXT,dataBuffer.GetAppendBuf(dataSize));
  751. dataBuffer.UngetAppendBuf(dataSize);
  752. [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
  753. itemString = wxString(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),wxConvLocal);
  754. itemStringAvailable = true;
  755. }
  756. break;
  757. case wxDF_UNICODETEXT:
  758. {
  759. event.GetDataObject()->GetDataHere(wxDF_UNICODETEXT,dataBuffer.GetAppendBuf(dataSize));
  760. dataBuffer.UngetAppendBuf(dataSize);
  761. if (itemStringAvailable) // does an object already exist as an ASCII text (see wxDF_TEXT case statement)?
  762. [dataArray replaceObjectAtIndex:itemCounter withObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
  763. else
  764. [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]];
  765. itemString = wxString::FromUTF8(static_cast<char const*>(dataBuffer.GetData())+sizeof(wxDataFormatId),dataSize);
  766. itemStringAvailable = true;
  767. } /* block */
  768. break;
  769. default:
  770. wxFAIL_MSG("Data object has invalid or unsupported data format");
  771. [dataArray release];
  772. return NO;
  773. }
  774. }
  775. delete[] dataFormats;
  776. delete itemObject;
  777. if (dataStringAvailable)
  778. if (itemStringAvailable)
  779. {
  780. if (itemCounter > 0)
  781. dataString << wxT('\n');
  782. dataString << itemString;
  783. }
  784. else
  785. dataStringAvailable = false;
  786. }
  787. else
  788. {
  789. [dataArray release];
  790. delete itemObject;
  791. return NO; // dragging was vetoed or no data available
  792. }
  793. }
  794. if (dataStringAvailable)
  795. {
  796. wxCFStringRef osxString(dataString);
  797. [pasteboard declareTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] owner:nil];
  798. [pasteboard setPropertyList:dataArray forType:DataViewPboardType];
  799. [pasteboard setString:osxString.AsNSString() forType:NSStringPboardType];
  800. }
  801. else
  802. {
  803. [pasteboard declareTypes:[NSArray arrayWithObject:DataViewPboardType] owner:nil];
  804. [pasteboard setPropertyList:dataArray forType:DataViewPboardType];
  805. }
  806. return YES;
  807. }
  808. else
  809. return NO; // no items to drag (should never occur)
  810. }
  811. //
  812. // buffer handling
  813. //
  814. -(void) addToBuffer:(wxPointerObject*)item
  815. {
  816. [items addObject:item];
  817. }
  818. -(void) clearBuffer
  819. {
  820. [items removeAllObjects];
  821. }
  822. -(wxPointerObject*) getDataViewItemFromBuffer:(const wxDataViewItem&)item
  823. {
  824. return [items member:[[[wxPointerObject alloc] initWithPointer:item.GetID()] autorelease]];
  825. }
  826. -(wxPointerObject*) getItemFromBuffer:(wxPointerObject*)item
  827. {
  828. return [items member:item];
  829. }
  830. -(BOOL) isInBuffer:(wxPointerObject*)item
  831. {
  832. return [items containsObject:item];
  833. }
  834. -(void) removeFromBuffer:(wxPointerObject*)item
  835. {
  836. [items removeObject:item];
  837. }
  838. //
  839. // children handling
  840. //
  841. -(void) appendChild:(wxPointerObject*)item
  842. {
  843. [children addObject:item];
  844. }
  845. -(void) clearChildren
  846. {
  847. [children removeAllObjects];
  848. }
  849. -(wxPointerObject*) getChild:(NSUInteger)index
  850. {
  851. return [children objectAtIndex:index];
  852. }
  853. -(NSUInteger) getChildCount
  854. {
  855. return [children count];
  856. }
  857. -(void) removeChild:(NSUInteger)index
  858. {
  859. [children removeObjectAtIndex:index];
  860. }
  861. //
  862. // buffer handling
  863. //
  864. -(void) clearBuffers
  865. {
  866. [self clearBuffer];
  867. [self clearChildren];
  868. [self setCurrentParentItem:nil];
  869. }
  870. //
  871. // sorting
  872. //
  873. -(NSArray*) sortDescriptors
  874. {
  875. return sortDescriptors;
  876. }
  877. -(void) setSortDescriptors:(NSArray*)newSortDescriptors
  878. {
  879. [newSortDescriptors retain];
  880. [sortDescriptors release];
  881. sortDescriptors = newSortDescriptors;
  882. }
  883. //
  884. // access to wxWidget's implementation
  885. //
  886. -(wxPointerObject*) currentParentItem
  887. {
  888. return currentParentItem;
  889. }
  890. -(wxCocoaDataViewControl*) implementation
  891. {
  892. return implementation;
  893. }
  894. -(wxDataViewModel*) model
  895. {
  896. return model;
  897. }
  898. -(void) setCurrentParentItem:(wxPointerObject*)newCurrentParentItem
  899. {
  900. [newCurrentParentItem retain];
  901. [currentParentItem release];
  902. currentParentItem = newCurrentParentItem;
  903. }
  904. -(void) setImplementation:(wxCocoaDataViewControl*) newImplementation
  905. {
  906. implementation = newImplementation;
  907. }
  908. -(void) setModel:(wxDataViewModel*) newModel
  909. {
  910. model = newModel;
  911. }
  912. //
  913. // other methods
  914. //
  915. -(void) bufferItem:(wxPointerObject*)parentItem withChildren:(wxDataViewItemArray*)dataViewChildrenPtr
  916. {
  917. NSInteger const noOfChildren = (*dataViewChildrenPtr).GetCount();
  918. [self setCurrentParentItem:parentItem];
  919. [self clearChildren];
  920. for (NSInteger indexChild=0; indexChild<noOfChildren; ++indexChild)
  921. {
  922. wxPointerObject* bufferedPointerObject;
  923. wxPointerObject* newPointerObject([[wxPointerObject alloc] initWithPointer:(*dataViewChildrenPtr)[indexChild].GetID()]);
  924. // The next statement and test looks strange but there is
  925. // unfortunately no workaround: due to the fact that two pointer
  926. // objects are identical if their pointers are identical - because the
  927. // method isEqual has been overloaded - the set operation will only
  928. // add a new pointer object if there is not already one in the set
  929. // having the same pointer. On the other side the children's array
  930. // would always add the new pointer object. This means that different
  931. // pointer objects are stored in the set and array. This will finally
  932. // lead to a crash as objects diverge. To solve this issue it is first
  933. // tested if the child already exists in the set and if it is the case
  934. // the sets object is going to be appended to the array, otheriwse the
  935. // new pointer object is added to the set and array:
  936. bufferedPointerObject = [self getItemFromBuffer:newPointerObject];
  937. if (bufferedPointerObject == nil)
  938. {
  939. [items addObject:newPointerObject];
  940. [children addObject:newPointerObject];
  941. }
  942. else
  943. [children addObject:bufferedPointerObject];
  944. [newPointerObject release];
  945. }
  946. }
  947. @end
  948. // ============================================================================
  949. // wxCustomCell
  950. // ============================================================================
  951. @implementation wxCustomCell
  952. -(NSSize) cellSize
  953. {
  954. wxCustomRendererObject * const
  955. obj = static_cast<wxCustomRendererObject *>([self objectValue]);
  956. const wxSize size = obj->customRenderer->GetSize();
  957. return NSMakeSize(size.x, size.y);
  958. }
  959. //
  960. // implementations
  961. //
  962. -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
  963. {
  964. wxCustomRendererObject * const
  965. obj = static_cast<wxCustomRendererObject *>([self objectValue]);
  966. if ( !obj )
  967. {
  968. // this may happen for the custom cells in container rows: they don't
  969. // have any values
  970. return;
  971. }
  972. wxDataViewCustomRenderer * const renderer = obj->customRenderer;
  973. // if this method is called everything is already setup correctly,
  974. CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
  975. CGContextSaveGState( context );
  976. if ( ![controlView isFlipped] )
  977. {
  978. CGContextTranslateCTM( context, 0, [controlView bounds].size.height );
  979. CGContextScaleCTM( context, 1, -1 );
  980. }
  981. wxGCDC dc;
  982. wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context);
  983. dc.SetGraphicsContext(gc);
  984. int state = 0;
  985. if ( [self isHighlighted] )
  986. state |= wxDATAVIEW_CELL_SELECTED;
  987. renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), &dc, state);
  988. CGContextRestoreGState( context );
  989. }
  990. -(NSRect) imageRectForBounds:(NSRect)cellFrame
  991. {
  992. return cellFrame;
  993. }
  994. -(NSRect) titleRectForBounds:(NSRect)cellFrame
  995. {
  996. return cellFrame;
  997. }
  998. @end
  999. // ============================================================================
  1000. // wxImageTextCell
  1001. // ============================================================================
  1002. @implementation wxImageTextCell
  1003. //
  1004. // initialization
  1005. //
  1006. -(id) init
  1007. {
  1008. self = [super init];
  1009. if (self != nil)
  1010. {
  1011. // initializing the text part:
  1012. [self setSelectable:YES];
  1013. // initializing the image part:
  1014. image = nil;
  1015. imageSize = NSMakeSize(16,16);
  1016. spaceImageText = 5.0;
  1017. xImageShift = 5.0;
  1018. }
  1019. return self;
  1020. }
  1021. -(id) copyWithZone:(NSZone*)zone
  1022. {
  1023. wxImageTextCell* cell;
  1024. cell = (wxImageTextCell*) [super copyWithZone:zone];
  1025. cell->image = [image retain];
  1026. cell->imageSize = imageSize;
  1027. cell->spaceImageText = spaceImageText;
  1028. cell->xImageShift = xImageShift;
  1029. return cell;
  1030. }
  1031. -(void) dealloc
  1032. {
  1033. [image release];
  1034. [super dealloc];
  1035. }
  1036. //
  1037. // alignment
  1038. //
  1039. -(NSTextAlignment) alignment
  1040. {
  1041. return cellAlignment;
  1042. }
  1043. -(void) setAlignment:(NSTextAlignment)newAlignment
  1044. {
  1045. cellAlignment = newAlignment;
  1046. switch (newAlignment)
  1047. {
  1048. case NSCenterTextAlignment:
  1049. case NSLeftTextAlignment:
  1050. case NSJustifiedTextAlignment:
  1051. case NSNaturalTextAlignment:
  1052. [super setAlignment:NSLeftTextAlignment];
  1053. break;
  1054. case NSRightTextAlignment:
  1055. [super setAlignment:NSRightTextAlignment];
  1056. break;
  1057. default:
  1058. wxFAIL_MSG("Unknown alignment type.");
  1059. }
  1060. }
  1061. //
  1062. // image access
  1063. //
  1064. -(NSImage*) image
  1065. {
  1066. return image;
  1067. }
  1068. -(void) setImage:(NSImage*)newImage
  1069. {
  1070. [newImage retain];
  1071. [image release];
  1072. image = newImage;
  1073. }
  1074. -(NSSize) imageSize
  1075. {
  1076. return imageSize;
  1077. }
  1078. -(void) setImageSize:(NSSize) newImageSize
  1079. {
  1080. imageSize = newImageSize;
  1081. }
  1082. //
  1083. // other methods
  1084. //
  1085. -(NSSize) cellImageSize
  1086. {
  1087. return NSMakeSize(imageSize.width+xImageShift+spaceImageText,imageSize.height);
  1088. }
  1089. -(NSSize) cellSize
  1090. {
  1091. NSSize cellSize([super cellSize]);
  1092. if (imageSize.height > cellSize.height)
  1093. cellSize.height = imageSize.height;
  1094. cellSize.width += imageSize.width+xImageShift+spaceImageText;
  1095. return cellSize;
  1096. }
  1097. -(NSSize) cellTextSize
  1098. {
  1099. return [super cellSize];
  1100. }
  1101. //
  1102. // implementations
  1103. //
  1104. -(void) determineCellParts:(NSRect)cellFrame imagePart:(NSRect*)imageFrame textPart:(NSRect*)textFrame
  1105. {
  1106. switch (cellAlignment)
  1107. {
  1108. case NSCenterTextAlignment:
  1109. {
  1110. CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
  1111. // if the cell's frame is smaller than its contents (at least
  1112. // in x-direction) make sure that the image is visible:
  1113. if (cellSpace <= 0)
  1114. NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
  1115. else // otherwise center the image and text in the cell's frame
  1116. NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+0.5*cellSpace,NSMinXEdge);
  1117. }
  1118. break;
  1119. case NSJustifiedTextAlignment:
  1120. case NSLeftTextAlignment:
  1121. case NSNaturalTextAlignment: // how to determine the natural writing direction? TODO
  1122. NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
  1123. break;
  1124. case NSRightTextAlignment:
  1125. {
  1126. CGFloat const cellSpace = cellFrame.size.width-[self cellSize].width;
  1127. // if the cell's frame is smaller than its contents (at least
  1128. // in x-direction) make sure that the image is visible:
  1129. if (cellSpace <= 0)
  1130. NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText,NSMinXEdge);
  1131. else // otherwise right align the image and text in the cell's frame
  1132. NSDivideRect(cellFrame,imageFrame,textFrame,xImageShift+imageSize.width+spaceImageText+cellSpace,NSMinXEdge);
  1133. }
  1134. break;
  1135. default:
  1136. *imageFrame = NSZeroRect;
  1137. *textFrame = NSZeroRect;
  1138. wxFAIL_MSG("Unhandled alignment type.");
  1139. }
  1140. }
  1141. -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
  1142. {
  1143. NSRect textFrame, imageFrame;
  1144. [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
  1145. // draw the image part by ourselves;
  1146. // check if the cell has to draw its own background (checking is done by
  1147. // the parameter of the textfield's cell):
  1148. if ([self drawsBackground])
  1149. {
  1150. [[self backgroundColor] set];
  1151. NSRectFill(imageFrame);
  1152. }
  1153. if (image != nil)
  1154. {
  1155. // the image is slightly shifted (xImageShift) and has a fixed size
  1156. // but the image's frame might be larger and starts currently on the
  1157. // left side of the cell's frame; therefore, the origin and the
  1158. // image's frame size have to be adjusted:
  1159. if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
  1160. {
  1161. imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
  1162. imageFrame.size.width = imageSize.width;
  1163. }
  1164. else
  1165. {
  1166. imageFrame.origin.x += xImageShift;
  1167. imageFrame.size.width -= xImageShift+spaceImageText;
  1168. }
  1169. // ...and the image has to be centered in the y-direction:
  1170. if (imageFrame.size.height > imageSize.height)
  1171. imageFrame.size.height = imageSize.height;
  1172. imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
  1173. // according to the documentation the coordinate system should be
  1174. // flipped for NSTableViews (y-coordinate goes from top to bottom); to
  1175. // draw an image correctly the coordinate system has to be transformed
  1176. // to a bottom-top coordinate system, otherwise the image's
  1177. // content is flipped:
  1178. NSAffineTransform* coordinateTransform([NSAffineTransform transform]);
  1179. if ([controlView isFlipped])
  1180. {
  1181. [coordinateTransform scaleXBy: 1.0 yBy:-1.0]; // first the coordinate system is brought back to bottom-top orientation
  1182. [coordinateTransform translateXBy:0.0 yBy:(-2.0)*imageFrame.origin.y-imageFrame.size.height]; // the coordinate system has to be moved to compensate for the
  1183. [coordinateTransform concat]; // other orientation and the position of the image's frame
  1184. }
  1185. [image drawInRect:imageFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; // suggested method to draw the image
  1186. // instead of compositeToPoint:operation:
  1187. // take back previous transformation (if the view is not flipped the
  1188. // coordinate transformation matrix contains the identity matrix and
  1189. // the next two operations do not change the content's transformation
  1190. // matrix):
  1191. [coordinateTransform invert];
  1192. [coordinateTransform concat];
  1193. }
  1194. // let the textfield cell draw the text part:
  1195. if (textFrame.size.width > [self cellTextSize].width)
  1196. {
  1197. // for unknown reasons the alignment of the text cell is ignored;
  1198. // therefore change the size so that alignment does not influence the
  1199. // visualization anymore
  1200. textFrame.size.width = [self cellTextSize].width;
  1201. }
  1202. [super drawWithFrame:textFrame inView:controlView];
  1203. }
  1204. -(void) editWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject event:(NSEvent*)theEvent
  1205. {
  1206. NSRect textFrame, imageFrame;
  1207. [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame];
  1208. [super editWithFrame:textFrame inView:controlView editor:textObj delegate:anObject event:theEvent];
  1209. }
  1210. #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  1211. -(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)cellFrame ofView:(NSView*)controlView
  1212. {
  1213. NSPoint point = [controlView convertPoint:[event locationInWindow] fromView:nil];
  1214. NSRect imageFrame, textFrame;
  1215. [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
  1216. if (image != nil)
  1217. {
  1218. // the image is shifted...
  1219. if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
  1220. {
  1221. imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
  1222. imageFrame.size.width = imageSize.width;
  1223. }
  1224. else
  1225. {
  1226. imageFrame.origin.x += xImageShift;
  1227. imageFrame.size.width -= xImageShift+spaceImageText;
  1228. }
  1229. // ...and centered:
  1230. if (imageFrame.size.height > imageSize.height)
  1231. imageFrame.size.height = imageSize.height;
  1232. imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
  1233. // If the point is in the image rect, then it is a content hit (see
  1234. // documentation for hitTestForEvent:inRect:ofView):
  1235. if (NSMouseInRect(point, imageFrame, [controlView isFlipped]))
  1236. return NSCellHitContentArea;
  1237. }
  1238. // if the image was not hit let's try the text part:
  1239. if (textFrame.size.width > [self cellTextSize].width)
  1240. {
  1241. // for unknown reasons the alignment of the text cell is ignored;
  1242. // therefore change the size so that alignment does not influence the
  1243. // visualization anymore
  1244. textFrame.size.width = [self cellTextSize].width;
  1245. }
  1246. return [super hitTestForEvent:event inRect:textFrame ofView:controlView];
  1247. }
  1248. #endif
  1249. -(NSRect) imageRectForBounds:(NSRect)cellFrame
  1250. {
  1251. NSRect textFrame, imageFrame;
  1252. [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
  1253. if (imageFrame.size.width >= xImageShift+imageSize.width+spaceImageText)
  1254. {
  1255. imageFrame.origin.x += imageFrame.size.width-imageSize.width-spaceImageText;
  1256. imageFrame.size.width = imageSize.width;
  1257. }
  1258. else
  1259. {
  1260. imageFrame.origin.x += xImageShift;
  1261. imageFrame.size.width -= xImageShift+spaceImageText;
  1262. }
  1263. // ...and centered:
  1264. if (imageFrame.size.height > imageSize.height)
  1265. imageFrame.size.height = imageSize.height;
  1266. imageFrame.origin.y += ceil(0.5*(cellFrame.size.height-imageFrame.size.height));
  1267. return imageFrame;
  1268. }
  1269. -(void) selectWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength
  1270. {
  1271. NSRect textFrame, imageFrame;
  1272. [self determineCellParts:aRect imagePart:&imageFrame textPart:&textFrame];
  1273. [super selectWithFrame:textFrame inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
  1274. }
  1275. -(NSRect) titleRectForBounds:(NSRect)cellFrame
  1276. {
  1277. NSRect textFrame, imageFrame;
  1278. [self determineCellParts:cellFrame imagePart:&imageFrame textPart:&textFrame];
  1279. return textFrame;
  1280. }
  1281. @end
  1282. // ============================================================================
  1283. // wxCocoaOutlineView
  1284. // ============================================================================
  1285. @implementation wxCocoaOutlineView
  1286. //
  1287. // initializers / destructor
  1288. //
  1289. -(id) init
  1290. {
  1291. self = [super init];
  1292. if (self != nil)
  1293. {
  1294. currentlyEditedColumn =
  1295. currentlyEditedRow = -1;
  1296. [self registe