| 1 | #import "RVBook.h" |
| 2 | |
| 3 | #import <UIKit/UINavigationBar.h> |
| 4 | #import <UIKit/UINavigationItem.h> |
| 5 | |
| 6 | #import <UIKit/UITransitionView.h> |
| 7 | |
| 8 | #import <UIKit/UIView-Geometry.h> |
| 9 | #import <UIKit/UIView-Hierarchy.h> |
| 10 | |
| 11 | #import <Foundation/Foundation.h> |
| 12 | #import <CoreGraphics/CGGeometry.h> |
| 13 | |
| 14 | #include <cstdio> |
| 15 | #include <cstdlib> |
| 16 | |
| 17 | #include <errno.h> |
| 18 | |
| 19 | #import "RVPage.h" |
| 20 | |
| 21 | @interface NSObject (UICaboodleRVBook) |
| 22 | - (float) widthForButtonContents:(float)width; |
| 23 | @end |
| 24 | |
| 25 | @implementation NSObject (UICaboodleRVBook) |
| 26 | |
| 27 | - (float) widthForButtonContents:(float)width { |
| 28 | return width; |
| 29 | } |
| 30 | |
| 31 | @end |
| 32 | |
| 33 | @interface UIImage (UICaboodleRVBook) |
| 34 | - (float) widthForButtonContents:(float)width; |
| 35 | @end |
| 36 | |
| 37 | @implementation UIImage (UICaboodleRVBook) |
| 38 | |
| 39 | - (float) widthForButtonContents:(float)width { |
| 40 | return [self size].width + 8; |
| 41 | } |
| 42 | |
| 43 | @end |
| 44 | |
| 45 | @interface RVNavigationBar : UINavigationBar { |
| 46 | } |
| 47 | |
| 48 | - (id) createButtonWithContents:(id)contents width:(float)width barStyle:(int)barStyle buttonStyle:(int)style isRight:(BOOL)right; |
| 49 | @end |
| 50 | |
| 51 | @implementation RVNavigationBar |
| 52 | |
| 53 | - (id) createButtonWithContents:(id)contents width:(float)width barStyle:(int)barStyle buttonStyle:(int)style isRight:(BOOL)right { |
| 54 | float adjust = [contents widthForButtonContents:width]; |
| 55 | width = adjust; |
| 56 | return [super createButtonWithContents:contents width:width barStyle:barStyle buttonStyle:style isRight:right]; |
| 57 | } |
| 58 | |
| 59 | @end |
| 60 | |
| 61 | @implementation RVBook |
| 62 | |
| 63 | - (void) dealloc { |
| 64 | [navbar_ setDelegate:nil]; |
| 65 | if (toolbar_ != nil) |
| 66 | [toolbar_ setDelegate:nil]; |
| 67 | |
| 68 | for (RVPage *page in pages_) |
| 69 | [page setBook:nil]; |
| 70 | |
| 71 | [pages_ release]; |
| 72 | [navbar_ release]; |
| 73 | [transition_ release]; |
| 74 | if (toolbar_ != nil) |
| 75 | [toolbar_ release]; |
| 76 | [super dealloc]; |
| 77 | } |
| 78 | |
| 79 | - (UINavigationBar *) navigationBar { |
| 80 | return navbar_; |
| 81 | } |
| 82 | |
| 83 | - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button { |
| 84 | _assert([pages_ count] != 0); |
| 85 | RVPage *page = [pages_ lastObject]; |
| 86 | switch (button) { |
| 87 | case 0: [page _rightButtonClicked]; break; |
| 88 | case 1: [page _leftButtonClicked]; break; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item { |
| 93 | _assert([pages_ count] != 0); |
| 94 | if (!resetting_) |
| 95 | [[pages_ lastObject] setPageActive:NO]; |
| 96 | [pages_ removeLastObject]; |
| 97 | if (!resetting_) |
| 98 | [self resetViewAnimated:YES toPage:[pages_ lastObject]]; |
| 99 | } |
| 100 | |
| 101 | - (id) initWithFrame:(CGRect)frame { |
| 102 | if ((self = [super initWithFrame:frame]) != nil) { |
| 103 | pages_ = [[NSMutableArray arrayWithCapacity:4] retain]; |
| 104 | |
| 105 | struct CGRect bounds = [self bounds]; |
| 106 | CGSize navsize = [UINavigationBar defaultSize]; |
| 107 | CGRect navrect = {{0, 0}, navsize}; |
| 108 | |
| 109 | navbar_ = [[RVNavigationBar alloc] initWithFrame:navrect]; |
| 110 | [self addSubview:navbar_]; |
| 111 | |
| 112 | [navbar_ setBarStyle:0]; |
| 113 | [navbar_ setDelegate:self]; |
| 114 | |
| 115 | transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake( |
| 116 | bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height |
| 117 | )]; |
| 118 | |
| 119 | [self addSubview:transition_]; |
| 120 | } return self; |
| 121 | } |
| 122 | |
| 123 | - (void) setDelegate:(id)delegate { |
| 124 | delegate_ = delegate; |
| 125 | } |
| 126 | |
| 127 | - (void) setHook:(id)hook { |
| 128 | hook_ = hook; |
| 129 | } |
| 130 | |
| 131 | - (void) setPage:(RVPage *)page { |
| 132 | if ([pages_ count] != 0) |
| 133 | [[pages_ lastObject] setPageActive:NO]; |
| 134 | |
| 135 | [navbar_ disableAnimation]; |
| 136 | resetting_ = true; |
| 137 | for (unsigned i(0), pages([pages_ count]); i != pages; ++i) |
| 138 | [navbar_ popNavigationItem]; |
| 139 | resetting_ = false; |
| 140 | |
| 141 | [self pushPage:page]; |
| 142 | [navbar_ enableAnimation]; |
| 143 | } |
| 144 | |
| 145 | - (void) swapPage:(RVPage *)page { |
| 146 | if ([pages_ count] == 0) |
| 147 | return [self pushPage:page]; |
| 148 | |
| 149 | [[pages_ lastObject] setPageActive:NO]; |
| 150 | |
| 151 | [navbar_ disableAnimation]; |
| 152 | resetting_ = true; |
| 153 | [navbar_ popNavigationItem]; |
| 154 | resetting_ = false; |
| 155 | |
| 156 | [self pushPage:page animated:NO]; |
| 157 | [navbar_ enableAnimation]; |
| 158 | } |
| 159 | |
| 160 | - (void) pushPage:(RVPage *)page animated:(BOOL)animated { |
| 161 | NSString *title = [self getTitleForPage:page]; |
| 162 | |
| 163 | NSString *backButtonTitle = [page backButtonTitle]; |
| 164 | if (backButtonTitle == nil) |
| 165 | backButtonTitle = title; |
| 166 | |
| 167 | UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease]; |
| 168 | [navitem setBackButtonTitle:backButtonTitle]; |
| 169 | [navbar_ pushNavigationItem:navitem]; |
| 170 | |
| 171 | [page setFrame:[transition_ bounds]]; |
| 172 | [transition_ transition:(animated ? 1 : 0) toView:page]; |
| 173 | [page setPageActive:YES]; |
| 174 | |
| 175 | [pages_ addObject:page]; |
| 176 | [self reloadButtonsForPage:page]; |
| 177 | |
| 178 | if ([navbar_ respondsToSelector:@selector(setAccessoryView:animate:removeOnPop:)]) |
| 179 | [navbar_ setAccessoryView:[page accessoryView] animate:animated removeOnPop:NO]; |
| 180 | else |
| 181 | [navbar_ setAccessoryView:[page accessoryView] animate:animated]; |
| 182 | } |
| 183 | |
| 184 | - (void) pushPage:(RVPage *)page { |
| 185 | if ([pages_ count] != 0) |
| 186 | [[pages_ lastObject] setPageActive:NO]; |
| 187 | [self pushPage:page animated:([pages_ count] == 0 ? NO : YES)]; |
| 188 | } |
| 189 | |
| 190 | - (void) pushBook:(RVBook *)book { |
| 191 | [delegate_ popUpBook:book]; |
| 192 | } |
| 193 | |
| 194 | - (void) popPages:(unsigned)pages { |
| 195 | if (pages == 0) |
| 196 | return; |
| 197 | |
| 198 | [[pages_ lastObject] setPageActive:NO]; |
| 199 | |
| 200 | resetting_ = true; |
| 201 | for (unsigned i(0); i != pages; ++i) |
| 202 | [navbar_ popNavigationItem]; |
| 203 | resetting_ = false; |
| 204 | |
| 205 | [self resetViewAnimated:YES toPage:[pages_ lastObject]]; |
| 206 | } |
| 207 | |
| 208 | - (void) resetViewAnimated:(BOOL)animated { |
| 209 | resetting_ = true; |
| 210 | |
| 211 | if ([pages_ count] > 1) { |
| 212 | [navbar_ disableAnimation]; |
| 213 | while ([pages_ count] != (animated ? 2 : 1)) |
| 214 | [navbar_ popNavigationItem]; |
| 215 | [navbar_ enableAnimation]; |
| 216 | if (animated) |
| 217 | [navbar_ popNavigationItem]; |
| 218 | } |
| 219 | |
| 220 | resetting_ = false; |
| 221 | |
| 222 | [self resetViewAnimated:animated toPage:[pages_ lastObject]]; |
| 223 | } |
| 224 | |
| 225 | - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page { |
| 226 | [page resetViewAnimated:animated]; |
| 227 | [page setFrame:[transition_ bounds]]; |
| 228 | [transition_ transition:(animated ? 2 : 0) toView:page]; |
| 229 | [page setPageActive:YES]; |
| 230 | [self reloadButtonsForPage:page]; |
| 231 | |
| 232 | if ([navbar_ respondsToSelector:@selector(setAccessoryView:animate:removeOnPop:)]) |
| 233 | [navbar_ setAccessoryView:[page accessoryView] animate:animated removeOnPop:NO]; |
| 234 | else |
| 235 | [navbar_ setAccessoryView:[page accessoryView] animate:animated]; |
| 236 | } |
| 237 | |
| 238 | - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page { |
| 239 | if ([pages_ count] == 0 || page != [pages_ lastObject]) |
| 240 | return; |
| 241 | UINavigationItem *navitem = [navbar_ topItem]; |
| 242 | [navitem setBackButtonTitle:title]; |
| 243 | } |
| 244 | |
| 245 | - (void) reloadTitleForPage:(RVPage *)page { |
| 246 | if ([pages_ count] == 0 || page != [pages_ lastObject]) |
| 247 | return; |
| 248 | UINavigationItem *navitem = [navbar_ topItem]; |
| 249 | NSString *title = [self getTitleForPage:page]; |
| 250 | [navitem setTitle:title]; |
| 251 | } |
| 252 | |
| 253 | - (void) _leftButtonTitle:(NSString *&)leftButtonTitle style:(UINavigationButtonStyle &)leftButtonStyle forPage:(RVPage *)page { |
| 254 | leftButtonTitle = [page leftButtonTitle]; |
| 255 | leftButtonStyle = [page leftButtonStyle]; |
| 256 | } |
| 257 | |
| 258 | - (void) reloadButtonsForPage:(RVPage *)page { |
| 259 | if ([pages_ count] == 0 || page != [pages_ lastObject]) |
| 260 | return; |
| 261 | |
| 262 | NSString *leftButtonTitle; |
| 263 | UINavigationButtonStyle leftButtonStyle; |
| 264 | [self _leftButtonTitle:leftButtonTitle style:leftButtonStyle forPage:page]; |
| 265 | |
| 266 | UINavigationButtonStyle rightButtonStyle = [page rightButtonStyle]; |
| 267 | //[navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle] leftBack:(leftButtonTitle == nil)]; |
| 268 | |
| 269 | [navbar_ |
| 270 | showLeftButton:leftButtonTitle |
| 271 | withStyle:leftButtonStyle |
| 272 | rightButton:[page rightButtonTitle] |
| 273 | withStyle:rightButtonStyle |
| 274 | ]; |
| 275 | } |
| 276 | |
| 277 | - (NSString *) getTitleForPage:(RVPage *)page { |
| 278 | return [page title]; |
| 279 | } |
| 280 | |
| 281 | - (void) reloadData { |
| 282 | size_t count([pages_ count]); |
| 283 | for (size_t i(0); i != count; ++i) { |
| 284 | RVPage *page([pages_ objectAtIndex:(count - i - 1)]); |
| 285 | [page reloadData]; |
| 286 | } |
| 287 | |
| 288 | if (count != 0) { |
| 289 | RVPage *page([pages_ lastObject]); |
| 290 | [self reloadButtonsForPage:page]; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | - (void) reloadButtons { |
| 295 | size_t count([pages_ count]); |
| 296 | if (count != 0) { |
| 297 | RVPage *page([pages_ lastObject]); |
| 298 | [self reloadButtonsForPage:page]; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | - (CGRect) pageBounds { |
| 303 | return [transition_ bounds]; |
| 304 | } |
| 305 | |
| 306 | - (void) close { |
| 307 | if (hook_ != nil) |
| 308 | [hook_ didCloseBook:self]; |
| 309 | } |
| 310 | |
| 311 | @end |
| 312 | |
| 313 | @implementation RVPopUpBook |
| 314 | |
| 315 | - (void) _leftButtonTitle:(NSString *&)leftButtonTitle style:(UINavigationButtonStyle &)leftButtonStyle forPage:(RVPage *)page { |
| 316 | [super _leftButtonTitle:leftButtonTitle style:leftButtonStyle forPage:page]; |
| 317 | if ((cancel_ = leftButtonTitle == nil && [pages_ count] == 1)) { |
| 318 | leftButtonTitle = [[NSBundle mainBundle] localizedStringForKey:@"CANCEL" value:nil table:nil]; |
| 319 | leftButtonStyle = UINavigationButtonStyleNormal; |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button { |
| 324 | if (button == 1 && cancel_) |
| 325 | [self close]; |
| 326 | else |
| 327 | [super navigationBar:navbar buttonClicked:button]; |
| 328 | } |
| 329 | |
| 330 | - (void) close { |
| 331 | [self popFromSuperviewAnimated:YES]; |
| 332 | [super close]; |
| 333 | } |
| 334 | |
| 335 | @end |