comparison ios/dw.m @ 2765:f734185664cc

iOS: Initial tree view implementation for iOS. Still needs a lot of work, callbacks not implemented. Root node needs to be hidden from view.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Thu, 07 Apr 2022 21:54:35 +0000
parents 4c602db2d2cf
children 9b9bc2c2bbad
comparison
equal deleted inserted replaced
2764:4c602db2d2cf 2765:f734185664cc
2 * Dynamic Windows: 2 * Dynamic Windows:
3 * A GTK like implementation of the iOS GUI 3 * A GTK like implementation of the iOS GUI
4 * 4 *
5 * (C) 2011-2022 Brian Smith <brian@dbsoft.org> 5 * (C) 2011-2022 Brian Smith <brian@dbsoft.org>
6 * (C) 2011-2021 Mark Hessling <mark@rexx.org> 6 * (C) 2011-2021 Mark Hessling <mark@rexx.org>
7 * (C) 2017 Ralph Shane (Base tree view implementation)
7 * 8 *
8 * Requires 13.0 or later. 9 * Requires 13.0 or later.
9 * clang -g -o dwtest -D__IOS__ -I. dwtest.c ios/dw.m -framework UIKit -framework WebKit -framework Foundation -framework UserNotifications 10 * clang -g -o dwtest -D__IOS__ -I. dwtest.c ios/dw.m -framework UIKit -framework WebKit -framework Foundation -framework UserNotifications
10 */ 11 */
11 #import <Foundation/Foundation.h> 12 #import <Foundation/Foundation.h>
1597 @end 1598 @end
1598 1599
1599 #define _DW_BUTTON_TYPE_NORMAL 0 1600 #define _DW_BUTTON_TYPE_NORMAL 0
1600 #define _DW_BUTTON_TYPE_CHECK 1 1601 #define _DW_BUTTON_TYPE_CHECK 1
1601 #define _DW_BUTTON_TYPE_RADIO 2 1602 #define _DW_BUTTON_TYPE_RADIO 2
1603 #define _DW_BUTTON_TYPE_TREE 3
1602 1604
1603 /* Subclass for a button type */ 1605 /* Subclass for a button type */
1604 @interface DWButton : UIButton 1606 @interface DWButton : UIButton
1605 { 1607 {
1606 void *userdata; 1608 void *userdata;
1607 DWBox *parent; 1609 DWBox *parent;
1608 int type, checkstate; 1610 int type, checkstate;
1609 } 1611 }
1612 @property(nonatomic, strong) void(^didCheckedChanged)(BOOL checked);
1610 -(void *)userdata; 1613 -(void *)userdata;
1611 -(void)setUserdata:(void *)input; 1614 -(void)setUserdata:(void *)input;
1612 -(void)buttonClicked:(id)sender; 1615 -(void)buttonClicked:(id)sender;
1613 -(void)setParent:(DWBox *)input; 1616 -(void)setParent:(DWBox *)input;
1614 -(DWBox *)parent; 1617 -(DWBox *)parent;
1622 -(void *)userdata { return userdata; } 1625 -(void *)userdata { return userdata; }
1623 -(void)setUserdata:(void *)input { userdata = input; } 1626 -(void)setUserdata:(void *)input { userdata = input; }
1624 -(void)buttonClicked:(id)sender 1627 -(void)buttonClicked:(id)sender
1625 { 1628 {
1626 /* Toggle the button */ 1629 /* Toggle the button */
1627 if(type == _DW_BUTTON_TYPE_CHECK) 1630 if(type == _DW_BUTTON_TYPE_CHECK || type == _DW_BUTTON_TYPE_TREE)
1628 [self setCheckState:(checkstate ? FALSE : TRUE)]; 1631 [self setCheckState:(checkstate ? FALSE : TRUE)];
1629 else if(type == _DW_BUTTON_TYPE_RADIO) 1632 else if(type == _DW_BUTTON_TYPE_RADIO)
1630 [self setCheckState:TRUE]; 1633 [self setCheckState:TRUE];
1631 1634
1632 _dw_event_handler(self, nil, _DW_EVENT_CLICKED); 1635 _dw_event_handler(self, nil, _DW_EVENT_CLICKED);
1654 } 1657 }
1655 } 1658 }
1656 } 1659 }
1657 } 1660 }
1658 } 1661 }
1662 if(_didCheckedChanged)
1663 _didCheckedChanged(checkstate);
1659 } 1664 }
1660 -(void)setParent:(DWBox *)input { parent = input; } 1665 -(void)setParent:(DWBox *)input { parent = input; }
1661 -(DWBox *)parent { return parent; } 1666 -(DWBox *)parent { return parent; }
1662 -(int)type { return type; } 1667 -(int)type { return type; }
1663 -(void)setType:(int)input 1668 -(void)setType:(int)input
1688 { 1693 {
1689 if(checkstate) 1694 if(checkstate)
1690 imagename = @"largecircle.fill.circle"; 1695 imagename = @"largecircle.fill.circle";
1691 else 1696 else
1692 imagename = @"circle"; 1697 imagename = @"circle";
1698 }
1699 break;
1700 case _DW_BUTTON_TYPE_TREE:
1701 {
1702 if(checkstate)
1703 imagename = @"chevron.down";
1704 else
1705 imagename = @"chevron.forward";
1693 } 1706 }
1694 break; 1707 break;
1695 } 1708 }
1696 if(imagename) 1709 if(imagename)
1697 { 1710 {
2641 { 2654 {
2642 if([self allowsMultipleSelection]) 2655 if([self allowsMultipleSelection])
2643 [self tableView:tableView didSelectRowAtIndexPath:indexPath]; 2656 [self tableView:tableView didSelectRowAtIndexPath:indexPath];
2644 } 2657 }
2645 -(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; } 2658 -(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
2659 @end
2660
2661 /* Custom tree view subclasses: DWTree, DWTreeItem, DWTreeViewCell, DWTreeViewCellDelegate */
2662 @interface DWTreeItem : NSObject
2663 @property(nonatomic, strong) UIImage *icon;
2664 @property(nonatomic, strong) NSString *title;
2665 @property(nonatomic, assign) void *data;
2666 @property(nonatomic, weak) DWTreeItem *parent;
2667 @property(nonatomic, retain, readonly) NSMutableArray<DWTreeItem *> *children;
2668 @property(nonatomic, assign, readonly) NSUInteger levelDepth;
2669 @property(nonatomic, assign, readonly) BOOL isRoot;
2670 @property(nonatomic, assign, readonly) BOOL hasChildren;
2671 @property(nonatomic, assign) BOOL expanded;
2672 -(instancetype)initWithIcon:(UIImage *)icon Title:(NSString *)title andData:(void *)itemdata;
2673 -(void)insertChildAfter:(DWTreeItem *)treeItem;
2674 -(void)appendChild:(DWTreeItem *)newChild;
2675 -(void)removeFromParent;
2676 -(void)moveToDestination:(DWTreeItem *)destination;
2677 -(BOOL)containsTreeItem:(DWTreeItem *)treeItem;
2678 -(NSArray<DWTreeItem *> *)visibleNodes;
2679 @end
2680
2681 @implementation DWTreeItem
2682 {
2683 NSArray *_flattenedTreeCache;
2684 }
2685 -(void)dealloc { NSLog(@"DWTreeItem \"%@\" dealloc", self.title); [_title release]; [_icon release]; [super dealloc]; }
2686 -(instancetype)initWithIcon:(UIImage *)icon Title:(NSString *)title andData:(void *)itemdata
2687 {
2688 if(self = [super init])
2689 {
2690 _title = [title retain];
2691 _icon = [icon retain];
2692 _data = itemdata;
2693 _children = [[NSMutableArray alloc] initWithCapacity:1];
2694 }
2695 return self;
2696 }
2697 -(NSString *)title
2698 {
2699 if(_title)
2700 return _title;
2701 return self.description;
2702 }
2703 -(UIImage *)icon
2704 {
2705 if(_icon)
2706 return _icon;
2707 return nil;
2708 }
2709 -(NSArray<DWTreeItem *> *)visibleNodes
2710 {
2711 NSMutableArray *allElements = [[NSMutableArray alloc] init];
2712 [allElements addObject:self];
2713 if(_expanded)
2714 {
2715 for (DWTreeItem *child in _children)
2716 [allElements addObjectsFromArray:[child visibleNodes]];
2717 }
2718 return allElements;
2719 }
2720 -(void)insertChildAfter:(DWTreeItem *)treeItem
2721 {
2722 DWTreeItem *parent = self.parent;
2723 NSUInteger index = [parent.children indexOfObject:self];
2724 if(index == NSNotFound)
2725 index = [parent.children count];
2726 treeItem.parent = parent;
2727 [parent.children insertObject:treeItem atIndex:(index+1)];
2728 }
2729 -(void)appendChild:(DWTreeItem *)newChild
2730 {
2731 newChild.parent = self;
2732 [_children addObject:newChild];
2733 }
2734 -(void)removeFromParent
2735 {
2736 DWTreeItem *parent = self.parent;
2737 if(parent)
2738 {
2739 [parent.children removeObject:self];
2740 self.parent = nil;
2741 }
2742 }
2743 -(void)moveToDestination:(DWTreeItem *)destination
2744 {
2745 NSAssert([self containsTreeItem:destination]==NO, @"[self containsTreeItem:destination] something went wrong!");
2746 if(self == destination || destination == nil)
2747 return;
2748 [self removeFromParent];
2749
2750 [destination insertChildAfter:self];
2751 }
2752 -(BOOL)containsTreeItem:(DWTreeItem *)treeItem
2753 {
2754 DWTreeItem *parent = treeItem.parent;
2755 if(parent == nil)
2756 return NO;
2757 if(self == parent)
2758 return YES;
2759 return [self containsTreeItem:parent];
2760 }
2761 -(NSUInteger)levelDepth
2762 {
2763 NSUInteger cnt = 0;
2764 if(_parent != nil)
2765 {
2766 cnt += 1;
2767 cnt += [_parent levelDepth];
2768 }
2769 return cnt;
2770 }
2771 -(BOOL)isRoot { return (!_parent); }
2772 -(BOOL)hasChildren { return (_children.count > 0); }
2773 @end
2774
2775 @class DWTreeViewCell;
2776 @protocol DWTreeViewCellDelegate <NSObject>
2777 //@optional
2778 - (BOOL) queryExpandableInTreeViewCell:(DWTreeViewCell *)treeViewCell;
2779 - (void) treeViewCell:(DWTreeViewCell *)treeViewCell expanded:(BOOL)expanded;
2780 @end
2781
2782 @interface DWTreeViewCell : UITableViewCell
2783 @property(nonatomic, strong) UILabel *titleLabel;
2784 @property(nonatomic) NSUInteger level;
2785 @property(nonatomic) BOOL expanded;
2786 @property(nonatomic) BOOL isFolder;
2787 @property(nonatomic, assign) id <DWTreeViewCellDelegate> delegate;
2788 -(instancetype)initWithStyle:(UITableViewCellStyle)style
2789 reuseIdentifier:(NSString *)reuseIdentifier
2790 level:(NSUInteger)level
2791 expanded:(BOOL)expanded;
2792 @end
2793
2794 CGRect DWRectInflate(CGRect rect, CGFloat dx, CGFloat dy)
2795 {
2796 return CGRectMake(rect.origin.x-dx, rect.origin.y-dy, rect.size.width+2*dx, rect.size.height+2*dy);
2797 }
2798
2799 static CGFloat IMG_HEIGHT_WIDTH = 20;
2800 static CGFloat XOFFSET = 3;
2801
2802 @implementation DWTreeViewCell
2803 {
2804 DWButton *_arrowImageButton;
2805 UIImageView *_itemImage;
2806 }
2807 -(id)initWithStyle:(UITableViewCellStyle)style
2808 reuseIdentifier:(NSString *)reuseIdentifier
2809 level:(NSUInteger)level
2810 expanded:(BOOL)expanded
2811 {
2812 self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
2813
2814 if(self)
2815 {
2816 _level = level;
2817 _expanded = expanded;
2818
2819 UIView *content = self.contentView;
2820
2821 UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
2822 titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
2823 titleLabel.numberOfLines = 0;
2824 titleLabel.textAlignment = NSTextAlignmentLeft;
2825 [content addSubview:titleLabel];
2826 _titleLabel = titleLabel;
2827
2828 UIImageView *itemImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, IMG_HEIGHT_WIDTH, IMG_HEIGHT_WIDTH)];
2829 [content addSubview:itemImage];
2830 _itemImage = itemImage;
2831
2832 DWButton *arrowImageButton = [[DWButton alloc] initWithFrame:CGRectMake(0, 0, IMG_HEIGHT_WIDTH, IMG_HEIGHT_WIDTH)];
2833 [arrowImageButton setType:_DW_BUTTON_TYPE_TREE];
2834 [arrowImageButton setDidCheckedChanged:^(BOOL checked) {
2835 _expanded = checked;
2836 if([_delegate respondsToSelector:@selector(treeViewCell:expanded:)])
2837 [_delegate treeViewCell:self expanded:checked];
2838 }];
2839 [arrowImageButton setCheckState:_expanded];
2840 [content addSubview:arrowImageButton];
2841 _arrowImageButton = arrowImageButton;
2842 }
2843 return self;
2844 }
2845 #pragma mark -
2846 #pragma mark Other overrides
2847 -(void)layoutSubviews
2848 {
2849 [super layoutSubviews];
2850
2851 CGSize size = self.contentView.bounds.size;
2852 CGFloat stepSize = size.height;
2853 CGRect rc = CGRectMake(_level * stepSize, 0, stepSize, stepSize);
2854 _arrowImageButton.frame = DWRectInflate(rc, -XOFFSET, -XOFFSET);
2855
2856 rc = CGRectMake((_level + 1) * stepSize, 0, stepSize, stepSize);
2857 _itemImage.frame = DWRectInflate(rc, -XOFFSET, -XOFFSET);
2858 _titleLabel.frame = CGRectMake((_level + 2) * stepSize, 0, size.width - (_level + 3) * stepSize, stepSize);
2859 }
2860 @end
2861
2862 @class DWTree;
2863
2864 @protocol DWTreeViewDelegate <NSObject>
2865 @required
2866 -(NSInteger)numberOfRowsInTreeView:(DWTree *)treeView;
2867 -(DWTreeItem *)treeView:(DWTree *)treeView treeItemForRow:(NSInteger)row;
2868 -(NSInteger)treeView:(DWTree *)treeView rowForTreeItem:(DWTreeItem *)treeItem;
2869 -(void)treeView:(DWTree *)treeView removeTreeItem:(DWTreeItem *)treeItem;
2870 -(void)treeView:(DWTree *)treeView moveTreeItem:(DWTreeItem *)treeItem to:(DWTreeItem *)to;
2871 -(void)treeView:(DWTree *)treeView addTreeItem:(DWTreeItem *)treeItem;
2872 //@optional
2873 -(void)treeView:(DWTree *)treeView didSelectForTreeItem:(DWTreeItem *)treeItem;
2874 -(BOOL)treeView:(DWTree *)treeView queryExpandableInTreeItem:(DWTreeItem *)treeItem;
2875 -(void)treeView:(DWTree *)treeView treeItem:(DWTreeItem *)treeItem expanded:(BOOL)expanded;
2876 @optional
2877 -(BOOL)treeView:(DWTree *)treeView canEditTreeItem:(DWTreeItem *)treeItem;
2878 -(BOOL)treeView:(DWTree *)treeView canMoveTreeItem:(DWTreeItem *)treeItem;
2879 @end
2880
2881 @interface DWTree : UITableView
2882 @property(nonatomic, strong) UIFont *font;
2883 @property(nonatomic, strong) DWTreeItem *treeItem;
2884 @property(nonatomic, weak) id<DWTreeViewDelegate> treeViewDelegate;
2885 -(instancetype)initWithFrame:(CGRect)frame;
2886 -(void)insertTreeItem:(DWTreeItem *)treeItem;
2887 @end
2888
2889 @interface DWTree () <UITableViewDataSource, UITableViewDelegate, DWTreeViewCellDelegate, DWTreeViewDelegate>
2890 @end
2891
2892 @implementation DWTree
2893 {
2894 DWTreeItem *_rootNode;
2895 DWTreeItem *_selectedNode;
2896 }
2897 -(instancetype)initWithFrame:(CGRect)frame
2898 {
2899 if(self = [super initWithFrame:frame])
2900 {
2901 self.delegate=self;
2902 self.dataSource=self;
2903 _treeViewDelegate=self;
2904 self.separatorStyle= UITableViewCellSeparatorStyleNone;
2905 _font = [UIFont systemFontOfSize:16];
2906 _rootNode = [[[DWTreeItem alloc] initWithIcon:nil Title:@"@Root" andData:NULL] retain];
2907 _rootNode.expanded = YES;
2908 }
2909 return self;
2910 }
2911 -(void)dealloc { [_rootNode release]; [super dealloc]; }
2912 -(void)insertTreeItem:(DWTreeItem *)treeItem
2913 {
2914 DWTreeItem *targetNode = nil;
2915
2916 NSArray<DWTreeViewCell *> *cells = [self visibleCells];
2917
2918 // Target the selected tree node first if any
2919 for(DWTreeViewCell *cell in cells)
2920 {
2921 DWTreeItem *iter = [self treeItemForTreeViewCell:cell];
2922 if(iter == _selectedNode)
2923 {
2924 targetNode = iter;
2925 break;
2926 }
2927 }
2928 // Otherwise target first visible node if any
2929 if(targetNode == nil && [cells count])
2930 targetNode = [self treeItemForTreeViewCell:cells[0]];
2931 // Finally put it on the root level
2932 if(targetNode == nil)
2933 targetNode = _rootNode;
2934 // If target is still nil something went horrible wrong
2935 NSAssert(targetNode, @"targetNode == nil, something went wrong!");
2936 [targetNode insertChildAfter:treeItem];
2937
2938 if([_treeViewDelegate respondsToSelector:@selector(treeView:addTreeItem:)])
2939 [_treeViewDelegate treeView:self addTreeItem:treeItem];
2940
2941 [self reloadData];
2942 [self resetSelection:NO];
2943 }
2944 -(void)setFont:(UIFont *)font
2945 {
2946 _font = font;
2947 [self reloadData];
2948 [self resetSelection:NO];
2949 }
2950 -(void)resetSelection:(BOOL)delay
2951 {
2952 NSInteger row = NSNotFound;
2953 if([_treeViewDelegate respondsToSelector:@selector(treeView:rowForTreeItem:)])
2954 row = [_treeViewDelegate treeView:self rowForTreeItem:_selectedNode];
2955 if(row != NSNotFound)
2956 {
2957 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
2958 dispatch_block_t run = ^ {
2959 [self selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
2960 };
2961 if(delay)
2962 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), run);
2963 else
2964 run();
2965 }
2966 }
2967 #pragma mark - DWTreeViewDelegate
2968 -(NSInteger)numberOfRowsInTreeView:(DWTree *)treeView { return [_rootNode visibleNodes].count; }
2969 -(void)treeView:(DWTree *)treeView addTreeItem:(DWTreeItem *)treeItem {}
2970 -(void)treeView:(DWTree *)treeView didSelectForTreeItem:(DWTreeItem *)treeItem {}
2971 -(void)treeView:(DWTree *)treeView moveTreeItem:(DWTreeItem *)treeItem to:(DWTreeItem *)to {}
2972 -(BOOL)treeView:(DWTree *)treeView queryExpandableInTreeItem:(DWTreeItem *)treeItem { return YES; }
2973 -(void)treeView:(DWTree *)treeView removeTreeItem:(DWTreeItem *)treeItem {}
2974 -(NSInteger)treeView:(DWTree *)treeView rowForTreeItem:(DWTreeItem *)treeItem { return [[_rootNode visibleNodes] indexOfObject:treeItem]; }
2975 -(void)treeView:(DWTree *)treeView treeItem:(DWTreeItem *)treeItem expanded:(BOOL)expanded {}
2976 - (DWTreeItem *)treeView:(DWTree *)treeView treeItemForRow:(NSInteger)row { return [[_rootNode visibleNodes] objectAtIndex:row]; }
2977 #pragma mark - UITableViewDataSource
2978 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }
2979 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
2980 {
2981 NSInteger count = 0;
2982 if([_treeViewDelegate respondsToSelector:@selector(numberOfRowsInTreeView:)])
2983 count = [_treeViewDelegate numberOfRowsInTreeView:self];
2984 return count;
2985 }
2986 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
2987 {
2988 static NSString *CellIdentifier = @"Cell";
2989
2990 DWTreeItem *treeItem = [self treeItemForIndexPath:indexPath];
2991 DWTreeViewCell *cell = [[DWTreeViewCell alloc] initWithStyle:UITableViewCellStyleDefault
2992 reuseIdentifier:CellIdentifier
2993 level:[treeItem levelDepth]
2994 expanded:treeItem.expanded];
2995 cell.titleLabel.text = treeItem.title;
2996 cell.imageView.image = treeItem.icon;
2997 cell.titleLabel.font = _font;
2998 //cell.selectionStyle = UITableViewCellSelectionStyleNone;
2999 cell.delegate = self;
3000 return cell;
3001 }
3002 -(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
3003 {
3004 DWTreeItem *treeItem = [self treeItemForIndexPath:indexPath];
3005 if([_treeViewDelegate respondsToSelector:@selector(treeView:canEditTreeItem:)])
3006 return [_treeViewDelegate treeView:self canEditTreeItem:treeItem];
3007 else
3008 return (treeItem.isRoot == NO);
3009 }
3010 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
3011 {
3012 DWTreeItem *treeItem = [self treeItemForIndexPath:indexPath];
3013 if(editingStyle == UITableViewCellEditingStyleDelete)
3014 {
3015 [treeItem removeFromParent];
3016 if([_treeViewDelegate respondsToSelector:@selector(treeView:removeTreeItem:)])
3017 [_treeViewDelegate treeView:self removeTreeItem:treeItem];
3018 if(treeItem.expanded && treeItem.hasChildren)
3019 [self reloadData];
3020 else
3021 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
3022 [self resetSelection:YES];
3023 } else if (editingStyle == UITableViewCellEditingStyleInsert) {
3024 // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
3025 }
3026 }
3027 -(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
3028 {
3029 if([fromIndexPath isEqual:toIndexPath])
3030 return;
3031 DWTreeItem *srcNode = [self treeItemForIndexPath:fromIndexPath];
3032 DWTreeItem *targetNode = [self treeItemForIndexPath:toIndexPath];
3033 [srcNode moveToDestination:targetNode];
3034
3035 if([_treeViewDelegate respondsToSelector:@selector(treeView:moveTreeItem:to:)])
3036 [_treeViewDelegate treeView:self moveTreeItem:srcNode to:targetNode];
3037
3038 [self reloadData];
3039 [self resetSelection:NO];
3040 }
3041 -(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
3042 {
3043 DWTreeItem *treeItem = [self treeItemForIndexPath:indexPath];
3044 if([_treeViewDelegate respondsToSelector:@selector(treeView:canMoveTreeItem:)])
3045 return [_treeViewDelegate treeView:self canMoveTreeItem:treeItem];
3046 else
3047 return (treeItem.isRoot == NO);
3048 }
3049 #pragma mark - DWTableViewDelegate
3050 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
3051 {
3052 DWTreeItem *treeItem = [self treeItemForIndexPath:indexPath];
3053 if([_treeViewDelegate respondsToSelector:@selector(treeView:didSelectForTreeItem:)])
3054 [_treeViewDelegate treeView:self didSelectForTreeItem:treeItem];
3055 _selectedNode = treeItem;
3056 }
3057 -(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
3058 {
3059 DWTreeItem *srcNode = [self treeItemForIndexPath:sourceIndexPath];
3060 DWTreeItem *targetNode = [self treeItemForIndexPath:proposedDestinationIndexPath];
3061 if([srcNode containsTreeItem:targetNode] || srcNode==targetNode)
3062 return sourceIndexPath;
3063 // NSLog(@"Moving to target node \"%@\"", targetNode.title);
3064 return proposedDestinationIndexPath;
3065 }
3066 #pragma mark - DWTreeViewCellDelegate
3067 -(BOOL)queryExpandableInTreeViewCell:(DWTreeViewCell *)treeViewCell
3068 {
3069 BOOL allow = YES;
3070 if([_treeViewDelegate respondsToSelector:@selector(treeView:queryExpandableInTreeItem:)])
3071 {
3072 DWTreeItem *treeItem = [self treeItemForTreeViewCell:treeViewCell];
3073 allow = [_treeViewDelegate treeView:self queryExpandableInTreeItem:treeItem];
3074 }
3075 return allow;
3076 }
3077 -(void)treeViewCell:(DWTreeViewCell *)treeViewCell expanded:(BOOL)expanded
3078 {
3079 DWTreeItem *treeItem = [self treeItemForTreeViewCell:treeViewCell];
3080 treeItem.expanded = expanded;
3081 if(treeItem.hasChildren)
3082 {
3083 [self reloadData];
3084 [self resetSelection:NO];
3085 }
3086 if([_treeViewDelegate respondsToSelector:@selector(treeView:treeItem:expanded:)])
3087 [_treeViewDelegate treeView:self treeItem:treeItem expanded:expanded];
3088 }
3089 #pragma mark - retrieve TreeItem object from special cell
3090 -(DWTreeItem *)treeItemForTreeViewCell:(DWTreeViewCell *)treeViewCell
3091 {
3092 NSIndexPath *indexPath = [self indexPathForCell:treeViewCell];
3093 return [self treeItemForIndexPath:indexPath];
3094 }
3095 -(DWTreeItem *)treeItemForIndexPath:(NSIndexPath *)indexPath
3096 {
3097 DWTreeItem *treeItem = nil;
3098 if([_treeViewDelegate respondsToSelector:@selector(treeView:treeItemForRow:)])
3099 treeItem = [_treeViewDelegate treeView:self treeItemForRow:indexPath.row];
3100 NSAssert(treeItem, @"Can't get the Tree Node data");
3101 return treeItem;
3102 }
2646 @end 3103 @end
2647 3104
2648 /* Subclass for a Calendar type */ 3105 /* Subclass for a Calendar type */
2649 @interface DWCalendar : UIDatePicker 3106 @interface DWCalendar : UIDatePicker
2650 { 3107 {
6080 * id: An ID to be used for getting the resource from the 6537 * id: An ID to be used for getting the resource from the
6081 * resource file. 6538 * resource file.
6082 * Returns: 6539 * Returns:
6083 * A handle to a tree window or NULL on failure. 6540 * A handle to a tree window or NULL on failure.
6084 */ 6541 */
6085 HWND API dw_tree_new(ULONG cid) 6542 DW_FUNCTION_DEFINITION(dw_tree_new, HWND, ULONG cid)
6086 { 6543 DW_FUNCTION_ADD_PARAM1(cid)
6087 /* TODO: Implement tree for iOS if possible */ 6544 DW_FUNCTION_RETURN(dw_tree_new, HWND)
6088 return 0; 6545 DW_FUNCTION_RESTORE_PARAM1(cid, ULONG)
6546 {
6547 DW_FUNCTION_INIT;
6548 DWTree *tree = [[[DWTree alloc] init] retain];
6549 [tree setTag:cid];
6550 [tree autorelease];
6551 DW_FUNCTION_RETURN_THIS(tree);
6089 } 6552 }
6090 6553
6091 /* 6554 /*
6092 * Inserts an item into a tree window (widget) after another item. 6555 * Inserts an item into a tree window (widget) after another item.
6093 * Parameters: 6556 * Parameters:
6098 * parent: Parent handle or 0 if root. 6561 * parent: Parent handle or 0 if root.
6099 * itemdata: Item specific data. 6562 * itemdata: Item specific data.
6100 * Returns: 6563 * Returns:
6101 * A handle to a tree item or NULL on failure. 6564 * A handle to a tree item or NULL on failure.
6102 */ 6565 */
6103 HTREEITEM API dw_tree_insert_after(HWND handle, HTREEITEM item, const char *title, HICN icon, HTREEITEM parent, void *itemdata) 6566 DW_FUNCTION_DEFINITION(dw_tree_insert_after, HTREEITEM, HWND handle, HTREEITEM item, const char *title, HICN icon, HTREEITEM parent, void *itemdata)
6104 { 6567 DW_FUNCTION_ADD_PARAM6(handle, item, title, icon, parent, itemdata)
6105 /* TODO: Implement tree for iOS if possible */ 6568 DW_FUNCTION_RETURN(dw_tree_insert_after, HTREEITEM)
6106 return 0; 6569 DW_FUNCTION_RESTORE_PARAM6(handle, HWND, item, HTREEITEM, title, char *, icon, HICN, parent, HTREEITEM, itemdata, void *)
6570 {
6571 DW_FUNCTION_INIT;
6572 DWTree *tree = handle;
6573 DWTreeItem *treeparent = item ? item : parent;
6574 DWTreeItem *treeitem = [[[DWTreeItem alloc] initWithIcon:icon
6575 Title:[NSString stringWithUTF8String:(title ? title : "")]
6576 andData:itemdata] retain];
6577 if(treeparent)
6578 {
6579 if(item)
6580 [treeparent insertChildAfter:treeitem];
6581 else
6582 [treeparent appendChild:treeitem];
6583 }
6584 else
6585 [tree insertTreeItem:treeitem];
6586 [treeitem autorelease];
6587 DW_FUNCTION_RETURN_THIS(treeitem);
6107 } 6588 }
6108 6589
6109 /* 6590 /*
6110 * Inserts an item into a tree window (widget). 6591 * Inserts an item into a tree window (widget).
6111 * Parameters: 6592 * Parameters:
6117 * Returns: 6598 * Returns:
6118 * A handle to a tree item or NULL on failure. 6599 * A handle to a tree item or NULL on failure.
6119 */ 6600 */
6120 HTREEITEM API dw_tree_insert(HWND handle, const char *title, HICN icon, HTREEITEM parent, void *itemdata) 6601 HTREEITEM API dw_tree_insert(HWND handle, const char *title, HICN icon, HTREEITEM parent, void *itemdata)
6121 { 6602 {
6122 /* TODO: Implement tree for iOS if possible */ 6603 return dw_tree_insert_after(handle, NULL, title, icon, parent, itemdata);
6123 return 0;
6124 } 6604 }
6125 6605
6126 /* 6606 /*
6127 * Gets the text an item in a tree window (widget). 6607 * Gets the text an item in a tree window (widget).
6128 * Parameters: 6608 * Parameters:
6129 * handle: Handle to the tree containing the item. 6609 * handle: Handle to the tree containing the item.
6130 * item: Handle of the item to be modified. 6610 * item: Handle of the item to be modified.
6131 * Returns: 6611 * Returns:
6132 * A malloc()ed buffer of item text to be dw_free()ed or NULL on error. 6612 * A malloc()ed buffer of item text to be dw_free()ed or NULL on error.
6133 */ 6613 */
6134 char * API dw_tree_get_title(HWND handle, HTREEITEM item) 6614 DW_FUNCTION_DEFINITION(dw_tree_get_title, char *, HWND handle, HTREEITEM item)
6135 { 6615 DW_FUNCTION_ADD_PARAM2(handle, item)
6136 /* TODO: Implement tree for iOS if possible */ 6616 DW_FUNCTION_RETURN(dw_tree_get_title, char *)
6137 return NULL; 6617 DW_FUNCTION_RESTORE_PARAM2(DW_UNUSED(handle), HWND, item, HTREEITEM)
6618 {
6619 DW_FUNCTION_INIT;
6620 char *title = NULL;
6621 DWTreeItem *treeitem = item;
6622 if(treeitem)
6623 title = strdup([treeitem.title UTF8String]);
6624 DW_FUNCTION_RETURN_THIS(title);
6138 } 6625 }
6139 6626
6140 /* 6627 /*
6141 * Gets the text an item in a tree window (widget). 6628 * Gets the text an item in a tree window (widget).
6142 * Parameters: 6629 * Parameters:
6143 * handle: Handle to the tree containing the item. 6630 * handle: Handle to the tree containing the item.
6144 * item: Handle of the item to be modified. 6631 * item: Handle of the item to be modified.
6145 * Returns: 6632 * Returns:
6146 * A handle to a tree item or NULL on failure. 6633 * A handle to a tree item or NULL on failure.
6147 */ 6634 */
6148 HTREEITEM API dw_tree_get_parent(HWND handle, HTREEITEM item) 6635 DW_FUNCTION_DEFINITION(dw_tree_get_parent, HTREEITEM, HWND handle, HTREEITEM item)
6149 { 6636 DW_FUNCTION_ADD_PARAM2(handle, item)
6150 /* TODO: Implement tree for iOS if possible */ 6637 DW_FUNCTION_RETURN(dw_tree_get_parent, HTREEITEM)
6151 return 0; 6638 DW_FUNCTION_RESTORE_PARAM2(DW_UNUSED(handle), HWND, item, HTREEITEM)
6639 {
6640 DW_FUNCTION_INIT;
6641 DWTreeItem *treeparent = nil;
6642 DWTreeItem *treeitem = item;
6643 if(treeitem)
6644 treeparent = treeitem.parent;
6645 DW_FUNCTION_RETURN_THIS(treeparent);
6152 } 6646 }
6153 6647
6154 /* 6648 /*
6155 * Sets the text and icon of an item in a tree window (widget). 6649 * Sets the text and icon of an item in a tree window (widget).
6156 * Parameters: 6650 * Parameters: