PageRenderTime 51ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Three20UI/Sources/TTMessageController.m

https://github.com/GetMoPix/three20
Objective C | 801 lines | 536 code | 179 blank | 86 comment | 97 complexity | a6eb24a5ac1525c9116758006d79dc50 MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "Three20UI/TTMessageController.h"
  17. // UI
  18. #import "Three20UI/TTMessageControllerDelegate.h"
  19. #import "Three20UI/TTMessageRecipientField.h"
  20. #import "Three20UI/TTMessageTextField.h"
  21. #import "Three20UI/TTMessageSubjectField.h"
  22. #import "Three20UI/TTActivityLabel.h"
  23. #import "Three20UI/TTPickerTextField.h"
  24. #import "Three20UI/TTTextEditor.h"
  25. #import "Three20UI/TTTableViewDataSource.h"
  26. #import "Three20UI/UIViewAdditions.h"
  27. // UINavigator
  28. #import "Three20UINavigator/TTGlobalNavigatorMetrics.h"
  29. // UICommon
  30. #import "Three20UICommon/TTGlobalUICommon.h"
  31. #import "Three20UICommon/UIViewControllerAdditions.h"
  32. // Style
  33. #import "Three20Style/TTGlobalStyle.h"
  34. #import "Three20Style/TTDefaultStyleSheet.h"
  35. // Core
  36. #import "Three20Core/TTCorePreprocessorMacros.h"
  37. #import "Three20Core/TTGlobalCoreLocale.h"
  38. #import "Three20Core/TTGlobalCoreRects.h"
  39. #import "Three20Core/NSStringAdditions.h"
  40. #import "Three20Core/TTGlobalCore.h"
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. ///////////////////////////////////////////////////////////////////////////////////////////////////
  43. ///////////////////////////////////////////////////////////////////////////////////////////////////
  44. @implementation TTMessageController
  45. @synthesize fields = _fields;
  46. @synthesize isModified = _isModified;
  47. @synthesize showsRecipientPicker = _showsRecipientPicker;
  48. @synthesize requireNonEmptyMessageBody = _requireNonEmptyMessageBody;
  49. @synthesize dataSource = _dataSource;
  50. @synthesize delegate = _delegate;
  51. ///////////////////////////////////////////////////////////////////////////////////////////////////
  52. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  53. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  54. if (self) {
  55. _fields = [[NSArray alloc] initWithObjects:
  56. [[[TTMessageRecipientField alloc] initWithTitle: TTLocalizedString(@"To:", @"")
  57. required: YES] autorelease],
  58. [[[TTMessageSubjectField alloc] initWithTitle: TTLocalizedString(@"Subject:", @"")
  59. required: NO] autorelease],
  60. nil];
  61. self.title = TTLocalizedString(@"New Message", @"");
  62. self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc]
  63. initWithTitle: TTLocalizedString(@"Cancel", @"")
  64. style: UIBarButtonItemStyleBordered
  65. target: self
  66. action: @selector(cancel)] autorelease];
  67. self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
  68. initWithTitle: TTLocalizedString(@"Send", @"")
  69. style: UIBarButtonItemStyleDone
  70. target: self
  71. action: @selector(send)] autorelease];
  72. self.navigationItem.rightBarButtonItem.enabled = NO;
  73. }
  74. return self;
  75. }
  76. ///////////////////////////////////////////////////////////////////////////////////////////////////
  77. - (id)initWithRecipients:(NSArray*)recipients {
  78. self = [self initWithNibName:nil bundle:nil];
  79. if (self) {
  80. _initialRecipients = [recipients retain];
  81. }
  82. return self;
  83. }
  84. ///////////////////////////////////////////////////////////////////////////////////////////////////
  85. - (void)dealloc {
  86. TT_RELEASE_SAFELY(_dataSource);
  87. TT_RELEASE_SAFELY(_fields);
  88. TT_RELEASE_SAFELY(_initialRecipients);
  89. [super dealloc];
  90. }
  91. ///////////////////////////////////////////////////////////////////////////////////////////////////
  92. ///////////////////////////////////////////////////////////////////////////////////////////////////
  93. #pragma mark -
  94. #pragma mark Private
  95. ///////////////////////////////////////////////////////////////////////////////////////////////////
  96. - (void)cancel {
  97. [self cancel:YES];
  98. }
  99. ///////////////////////////////////////////////////////////////////////////////////////////////////
  100. - (void)createFieldViews {
  101. for (UIView* view in _fieldViews) {
  102. [view removeFromSuperview];
  103. }
  104. [_textEditor removeFromSuperview];
  105. [_fieldViews release];
  106. _fieldViews = [[NSMutableArray alloc] init];
  107. for (TTMessageField* field in _fields) {
  108. TTPickerTextField* textField = [field createViewForController:self];
  109. if (textField) {
  110. textField.delegate = self;
  111. textField.backgroundColor = TTSTYLEVAR(backgroundColor);
  112. textField.font = TTSTYLEVAR(messageFont);
  113. textField.returnKeyType = UIReturnKeyNext;
  114. textField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  115. [textField sizeToFit];
  116. UILabel* label = [[[UILabel alloc] init] autorelease];
  117. label.text = field.title;
  118. label.font = TTSTYLEVAR(messageFont);
  119. label.textColor = TTSTYLEVAR(messageFieldTextColor);
  120. [label sizeToFit];
  121. label.frame = CGRectInset(label.frame, -2, 0);
  122. textField.leftView = label;
  123. textField.leftViewMode = UITextFieldViewModeAlways;
  124. [_scrollView addSubview:textField];
  125. [_fieldViews addObject:textField];
  126. UIView* separator = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 1)] autorelease];
  127. separator.backgroundColor = TTSTYLEVAR(messageFieldSeparatorColor);
  128. separator.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  129. [_scrollView addSubview:separator];
  130. }
  131. }
  132. [_scrollView addSubview:_textEditor];
  133. }
  134. ///////////////////////////////////////////////////////////////////////////////////////////////////
  135. - (void)layoutViews {
  136. CGFloat y = 0.0f;
  137. for (UIView* view in _scrollView.subviews) {
  138. view.frame = CGRectMake(0, y, self.view.width, view.height);
  139. y += view.height;
  140. }
  141. _scrollView.contentSize = CGSizeMake(_scrollView.width, y);
  142. }
  143. ///////////////////////////////////////////////////////////////////////////////////////////////////
  144. - (BOOL)hasEnteredText {
  145. for (int i = 0; i < _fields.count; ++i) {
  146. TTMessageField* field = [_fields objectAtIndex:i];
  147. if (field.required) {
  148. if ([field isKindOfClass:[TTMessageRecipientField class]]) {
  149. TTPickerTextField* textField = [_fieldViews objectAtIndex:i];
  150. if (textField.cells.count) {
  151. return YES;
  152. }
  153. } else if ([field isKindOfClass:[TTMessageTextField class]]) {
  154. UITextField* textField = [_fieldViews objectAtIndex:i];
  155. if (TTIsStringWithAnyText(textField.text)
  156. && !textField.text.isWhitespaceAndNewlines) {
  157. return YES;
  158. }
  159. }
  160. }
  161. }
  162. return _textEditor.text.length;
  163. }
  164. ///////////////////////////////////////////////////////////////////////////////////////////////////
  165. - (BOOL)hasRequiredText {
  166. if (_requireNonEmptyMessageBody && [_textEditor.text isWhitespaceAndNewlines]) {
  167. return NO;
  168. }
  169. for (int i = 0; i < _fields.count; ++i) {
  170. TTMessageField* field = [_fields objectAtIndex:i];
  171. if (field.required) {
  172. if ([field isKindOfClass:[TTMessageRecipientField class]]) {
  173. TTPickerTextField* textField = [_fieldViews objectAtIndex:i];
  174. if (!textField.cells.count) {
  175. return NO;
  176. }
  177. } else if ([field isKindOfClass:[TTMessageTextField class]]) {
  178. UITextField* textField = [_fieldViews objectAtIndex:i];
  179. if (0 == textField.text.length || textField.text.isWhitespaceAndNewlines) {
  180. return NO;
  181. }
  182. }
  183. }
  184. }
  185. return YES;
  186. }
  187. ///////////////////////////////////////////////////////////////////////////////////////////////////
  188. - (void)updateSendCommand {
  189. self.navigationItem.rightBarButtonItem.enabled = [self hasRequiredText];
  190. }
  191. ///////////////////////////////////////////////////////////////////////////////////////////////////
  192. - (UITextField*)subjectField {
  193. for (int i = 0; i < _fields.count; ++i) {
  194. TTMessageField* field = [_fields objectAtIndex:i];
  195. if ([field isKindOfClass:[TTMessageSubjectField class]]) {
  196. return [_fieldViews objectAtIndex:i];
  197. }
  198. }
  199. return nil;
  200. }
  201. ///////////////////////////////////////////////////////////////////////////////////////////////////
  202. - (void)setTitleToSubject {
  203. UITextField* subjectField = self.subjectField;
  204. if (subjectField) {
  205. self.navigationItem.title = subjectField.text;
  206. }
  207. [self updateSendCommand];
  208. }
  209. ///////////////////////////////////////////////////////////////////////////////////////////////////
  210. - (NSInteger)fieldIndexOfFirstResponder {
  211. NSInteger fieldIndex = 0;
  212. for (UIView* view in _fieldViews) {
  213. if ([view isFirstResponder]) {
  214. return fieldIndex;
  215. }
  216. ++fieldIndex;
  217. }
  218. if (_textEditor.isFirstResponder) {
  219. return _fieldViews.count;
  220. }
  221. return -1;
  222. }
  223. ///////////////////////////////////////////////////////////////////////////////////////////////////
  224. - (void)setFieldIndexOfFirstResponder:(NSInteger)fieldIndex {
  225. if (fieldIndex < _fieldViews.count) {
  226. UIView* view = [_fieldViews objectAtIndex:fieldIndex];
  227. [view becomeFirstResponder];
  228. } else {
  229. [_textEditor becomeFirstResponder];
  230. }
  231. }
  232. ///////////////////////////////////////////////////////////////////////////////////////////////////
  233. - (void)showRecipientPicker {
  234. [self messageWillShowRecipientPicker];
  235. if ([_delegate respondsToSelector:@selector(composeControllerShowRecipientPicker:)]) {
  236. [_delegate composeControllerShowRecipientPicker:self];
  237. }
  238. }
  239. ///////////////////////////////////////////////////////////////////////////////////////////////////
  240. ///////////////////////////////////////////////////////////////////////////////////////////////////
  241. #pragma mark -
  242. #pragma mark UIViewController
  243. ///////////////////////////////////////////////////////////////////////////////////////////////////
  244. - (void)loadView {
  245. [super loadView];
  246. self.view.backgroundColor = TTSTYLEVAR(backgroundColor);
  247. _scrollView = [[[UIScrollView class] alloc] initWithFrame:TTKeyboardNavigationFrame()];
  248. _scrollView.backgroundColor = TTSTYLEVAR(backgroundColor);
  249. _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
  250. _scrollView.canCancelContentTouches = NO;
  251. _scrollView.showsVerticalScrollIndicator = NO;
  252. _scrollView.showsHorizontalScrollIndicator = NO;
  253. [self.view addSubview:_scrollView];
  254. _textEditor = [[TTTextEditor alloc] initWithFrame:CGRectMake(0, 0, _scrollView.width, 0)];
  255. _textEditor.delegate = self;
  256. _textEditor.backgroundColor = TTSTYLEVAR(backgroundColor);
  257. _textEditor.font = TTSTYLEVAR(messageFont);
  258. _textEditor.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  259. _textEditor.autoresizesToText = YES;
  260. _textEditor.showsExtraLine = YES;
  261. _textEditor.minNumberOfLines = 6;
  262. [_textEditor sizeToFit];
  263. [self createFieldViews];
  264. [self layoutViews];
  265. }
  266. ///////////////////////////////////////////////////////////////////////////////////////////////////
  267. - (void)viewDidUnload {
  268. [super viewDidUnload];
  269. TT_RELEASE_SAFELY(_scrollView);
  270. TT_RELEASE_SAFELY(_fieldViews);
  271. TT_RELEASE_SAFELY(_textEditor);
  272. TT_RELEASE_SAFELY(_activityView);
  273. }
  274. ///////////////////////////////////////////////////////////////////////////////////////////////////
  275. - (void)viewWillAppear:(BOOL)animated {
  276. [super viewWillAppear:animated];
  277. if (_initialRecipients) {
  278. for (id recipient in _initialRecipients) {
  279. [self addRecipient:recipient forFieldAtIndex:0];
  280. }
  281. TT_RELEASE_SAFELY(_initialRecipients);
  282. }
  283. if (!_frozenState) {
  284. for (NSInteger i = 0; i < _fields.count+1; ++i) {
  285. if (![self fieldHasValueAtIndex:i]) {
  286. UIView* view = [self viewForFieldAtIndex:i];
  287. [view becomeFirstResponder];
  288. return;
  289. }
  290. }
  291. [[self viewForFieldAtIndex:0] becomeFirstResponder];
  292. }
  293. [self updateSendCommand];
  294. }
  295. ///////////////////////////////////////////////////////////////////////////////////////////////////
  296. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  297. return TTIsSupportedOrientation(interfaceOrientation);
  298. }
  299. ///////////////////////////////////////////////////////////////////////////////////////////////////
  300. - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
  301. [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
  302. _scrollView.height = self.view.height - TTKeyboardHeight();
  303. [self layoutViews];
  304. }
  305. ///////////////////////////////////////////////////////////////////////////////////////////////////
  306. ///////////////////////////////////////////////////////////////////////////////////////////////////
  307. #pragma mark -
  308. #pragma mark UTViewController (TTCategory)
  309. ///////////////////////////////////////////////////////////////////////////////////////////////////
  310. - (BOOL)persistView:(NSMutableDictionary*)state {
  311. NSMutableArray* fields = [NSMutableArray array];
  312. for (NSInteger i = 0; i < _fields.count; ++i) {
  313. TTMessageField* field = [_fields objectAtIndex:i];
  314. UITextField* view = [_fieldViews objectAtIndex:i];
  315. id data = [field persistField:view];
  316. if (data) {
  317. [fields addObject:data];
  318. } else {
  319. [fields addObject:@""];
  320. }
  321. }
  322. [state setObject:fields forKey:@"fields"];
  323. NSString* body = self.body;
  324. if (body) {
  325. [state setObject:body forKey:@"body"];
  326. }
  327. CGFloat scrollY = _scrollView.contentOffset.y;
  328. [state setObject:[NSNumber numberWithFloat:scrollY] forKey:@"scrollOffsetY"];
  329. NSInteger firstResponder = [self fieldIndexOfFirstResponder];
  330. [state setObject:[NSNumber numberWithInt:firstResponder] forKey:@"firstResponder"];
  331. [state setObject:[NSNumber numberWithBool:YES] forKey:@"__important__"];
  332. return [super persistView:state];
  333. }
  334. ///////////////////////////////////////////////////////////////////////////////////////////////////
  335. - (void)restoreView:(NSDictionary*)state {
  336. self.view;
  337. TT_RELEASE_SAFELY(_initialRecipients);
  338. NSMutableArray* fields = [state objectForKey:@"fields"];
  339. for (NSInteger i = 0; i < fields.count; ++i) {
  340. TTMessageField* field = [_fields objectAtIndex:i];
  341. UITextField* view = [_fieldViews objectAtIndex:i];
  342. id data = [fields objectAtIndex:i];
  343. if (data != [NSNull null]) {
  344. [field restoreField:view withData:data];
  345. }
  346. }
  347. NSString* body = [state objectForKey:@"body"];
  348. if (body) {
  349. self.body = body;
  350. }
  351. NSNumber* scrollY = [state objectForKey:@"scrollOffsetY"];
  352. _scrollView.contentOffset = CGPointMake(0, scrollY.floatValue);
  353. NSInteger firstResponder = [[state objectForKey:@"firstResponder"] intValue];
  354. [self setFieldIndexOfFirstResponder:firstResponder];
  355. }
  356. ///////////////////////////////////////////////////////////////////////////////////////////////////
  357. ///////////////////////////////////////////////////////////////////////////////////////////////////
  358. #pragma mark -
  359. #pragma mark UITextFieldDelegate
  360. ///////////////////////////////////////////////////////////////////////////////////////////////////
  361. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
  362. replacementString:(NSString *)string {
  363. if (textField == self.subjectField) {
  364. _isModified = YES;
  365. [NSTimer scheduledTimerWithTimeInterval:0 target:self
  366. selector:@selector(setTitleToSubject) userInfo:nil repeats:NO];
  367. }
  368. return YES;
  369. }
  370. ///////////////////////////////////////////////////////////////////////////////////////////////////
  371. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  372. NSUInteger fieldIndex = [_fieldViews indexOfObject:textField];
  373. UIView* nextView = fieldIndex == _fieldViews.count-1
  374. ? _textEditor
  375. : [_fieldViews objectAtIndex:fieldIndex+1];
  376. [nextView becomeFirstResponder];
  377. return NO;
  378. }
  379. ///////////////////////////////////////////////////////////////////////////////////////////////////
  380. - (void)textField:(TTPickerTextField*)textField didAddCellAtIndex:(NSInteger)cellIndex {
  381. [self updateSendCommand];
  382. }
  383. ///////////////////////////////////////////////////////////////////////////////////////////////////
  384. - (void)textField:(TTPickerTextField*)textField didRemoveCellAtIndex:(NSInteger)cellIndex {
  385. [self updateSendCommand];
  386. }
  387. ///////////////////////////////////////////////////////////////////////////////////////////////////
  388. - (void)textFieldDidResize:(TTPickerTextField*)textField {
  389. [self layoutViews];
  390. }
  391. ///////////////////////////////////////////////////////////////////////////////////////////////////
  392. ///////////////////////////////////////////////////////////////////////////////////////////////////
  393. #pragma mark -
  394. #pragma mark TTTextEditorDelegate
  395. ///////////////////////////////////////////////////////////////////////////////////////////////////
  396. - (void)textEditorDidChange:(TTTextEditor*)textEditor {
  397. [self updateSendCommand];
  398. _isModified = YES;
  399. }
  400. ///////////////////////////////////////////////////////////////////////////////////////////////////
  401. - (BOOL)textEditor:(TTTextEditor*)textEditor shouldResizeBy:(CGFloat)height {
  402. _textEditor.frame = TTRectContract(_textEditor.frame, 0, -height);
  403. [self layoutViews];
  404. [_textEditor scrollContainerToCursor:_scrollView];
  405. return NO;
  406. }
  407. ///////////////////////////////////////////////////////////////////////////////////////////////////
  408. ///////////////////////////////////////////////////////////////////////////////////////////////////
  409. #pragma mark -
  410. #pragma mark UIAlertViewDelegate
  411. ///////////////////////////////////////////////////////////////////////////////////////////////////
  412. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
  413. if (buttonIndex == 0) {
  414. [self cancel:NO];
  415. }
  416. }
  417. ///////////////////////////////////////////////////////////////////////////////////////////////////
  418. ///////////////////////////////////////////////////////////////////////////////////////////////////
  419. #pragma mark -
  420. #pragma mark Properties
  421. ///////////////////////////////////////////////////////////////////////////////////////////////////
  422. - (NSString*)subject {
  423. self.view;
  424. for (int i = 0; i < _fields.count; ++i) {
  425. id field = [_fields objectAtIndex:i];
  426. if ([field isKindOfClass:[TTMessageSubjectField class]]) {
  427. UITextField* textField = [_fieldViews objectAtIndex:i];
  428. return textField.text;
  429. }
  430. }
  431. return nil;
  432. }
  433. ///////////////////////////////////////////////////////////////////////////////////////////////////
  434. - (void)setSubject:(NSString*)subject {
  435. self.view;
  436. for (int i = 0; i < _fields.count; ++i) {
  437. id field = [_fields objectAtIndex:i];
  438. if ([field isKindOfClass:[TTMessageSubjectField class]]) {
  439. UITextField* textField = [_fieldViews objectAtIndex:i];
  440. textField.text = subject;
  441. break;
  442. }
  443. }
  444. }
  445. ///////////////////////////////////////////////////////////////////////////////////////////////////
  446. - (NSString*)body {
  447. return _textEditor.text;
  448. }
  449. ///////////////////////////////////////////////////////////////////////////////////////////////////
  450. - (void)setBody:(NSString*)body {
  451. self.view;
  452. _textEditor.text = body;
  453. }
  454. ///////////////////////////////////////////////////////////////////////////////////////////////////
  455. - (void)setDataSource:(id<TTTableViewDataSource>)dataSource {
  456. if (dataSource != _dataSource) {
  457. [_dataSource release];
  458. _dataSource = [dataSource retain];
  459. for (UITextField* textField in _fieldViews) {
  460. if ([textField isKindOfClass:[TTPickerTextField class]]) {
  461. TTPickerTextField* menuTextField = (TTPickerTextField*)textField;
  462. menuTextField.dataSource = dataSource;
  463. }
  464. }
  465. }
  466. }
  467. ///////////////////////////////////////////////////////////////////////////////////////////////////
  468. - (void)setFields:(NSArray*)fields {
  469. if (fields != _fields) {
  470. [_fields release];
  471. _fields = [fields retain];
  472. if (_fieldViews) {
  473. [self createFieldViews];
  474. }
  475. }
  476. }
  477. ///////////////////////////////////////////////////////////////////////////////////////////////////
  478. - (void)addRecipient:(id)recipient forFieldAtIndex:(NSUInteger)fieldIndex {
  479. self.view;
  480. TTPickerTextField* textField = [_fieldViews objectAtIndex:fieldIndex];
  481. if ([textField isKindOfClass:[TTPickerTextField class]]) {
  482. NSString* label = [_dataSource tableView:textField.tableView labelForObject:recipient];
  483. if (label) {
  484. [textField addCellWithObject:recipient];
  485. }
  486. }
  487. }
  488. ///////////////////////////////////////////////////////////////////////////////////////////////////
  489. - (NSString*)textForFieldAtIndex:(NSUInteger)fieldIndex {
  490. self.view;
  491. NSString* text = nil;
  492. if (fieldIndex == _fieldViews.count) {
  493. text = _textEditor.text;
  494. } else {
  495. TTPickerTextField* textField = [_fieldViews objectAtIndex:fieldIndex];
  496. if ([textField isKindOfClass:[TTPickerTextField class]]) {
  497. text = textField.text;
  498. }
  499. }
  500. NSCharacterSet* whitespace = [NSCharacterSet whitespaceCharacterSet];
  501. return [text stringByTrimmingCharactersInSet:whitespace];
  502. }
  503. ///////////////////////////////////////////////////////////////////////////////////////////////////
  504. - (void)setText:(NSString*)text forFieldAtIndex:(NSUInteger)fieldIndex {
  505. self.view;
  506. if (fieldIndex == _fieldViews.count) {
  507. _textEditor.text = text;
  508. } else {
  509. TTPickerTextField* textField = [_fieldViews objectAtIndex:fieldIndex];
  510. if ([textField isKindOfClass:[TTPickerTextField class]]) {
  511. textField.text = text;
  512. }
  513. }
  514. }
  515. ///////////////////////////////////////////////////////////////////////////////////////////////////
  516. - (BOOL)fieldHasValueAtIndex:(NSUInteger)fieldIndex {
  517. self.view;
  518. if (fieldIndex == _fieldViews.count) {
  519. return _textEditor.text.length > 0;
  520. } else {
  521. TTMessageField* field = [_fields objectAtIndex:fieldIndex];
  522. if ([field isKindOfClass:[TTMessageRecipientField class]]) {
  523. TTPickerTextField* pickerTextField = [_fieldViews objectAtIndex:fieldIndex];
  524. return (TTIsStringWithAnyText(pickerTextField.text)
  525. && !pickerTextField.text.isWhitespaceAndNewlines)
  526. || pickerTextField.cellViews.count > 0;
  527. } else {
  528. UITextField* textField = [_fieldViews objectAtIndex:fieldIndex];
  529. return (TTIsStringWithAnyText(textField.text)
  530. && !textField.text.isWhitespaceAndNewlines);
  531. }
  532. }
  533. }
  534. ///////////////////////////////////////////////////////////////////////////////////////////////////
  535. - (UIView*)viewForFieldAtIndex:(NSUInteger)fieldIndex {
  536. self.view;
  537. if (fieldIndex == _fieldViews.count) {
  538. return _textEditor;
  539. } else {
  540. return [_fieldViews objectAtIndex:fieldIndex];
  541. }
  542. }
  543. ///////////////////////////////////////////////////////////////////////////////////////////////////
  544. - (void)send {
  545. NSMutableArray* fields = [[_fields mutableCopy] autorelease];
  546. for (int i = 0; i < fields.count; ++i) {
  547. id field = [fields objectAtIndex:i];
  548. if ([field isKindOfClass:[TTMessageRecipientField class]]) {
  549. TTPickerTextField* textField = [_fieldViews objectAtIndex:i];
  550. [(TTMessageRecipientField*)field setRecipients:textField.cells];
  551. } else if ([field isKindOfClass:[TTMessageTextField class]]) {
  552. UITextField* textField = [_fieldViews objectAtIndex:i];
  553. [(TTMessageTextField*)field setText:textField.text];
  554. }
  555. }
  556. TTMessageTextField* bodyField = [[[TTMessageTextField alloc] initWithTitle:nil
  557. required:NO] autorelease];
  558. bodyField.text = _textEditor.text;
  559. [fields addObject:bodyField];
  560. [self showActivityView:YES];
  561. [self messageWillSend:fields];
  562. if ([_delegate respondsToSelector:@selector(composeController:didSendFields:)]) {
  563. [_delegate composeController:self didSendFields:fields];
  564. }
  565. [self messageDidSend];
  566. }
  567. ///////////////////////////////////////////////////////////////////////////////////////////////////
  568. - (void)cancel:(BOOL)confirmIfNecessary {
  569. if (confirmIfNecessary && ![self messageShouldCancel]) {
  570. [self confirmCancellation];
  571. } else {
  572. if ([_delegate respondsToSelector:@selector(composeControllerWillCancel:)]) {
  573. [_delegate composeControllerWillCancel:self];
  574. }
  575. [self dismissModalViewController];
  576. }
  577. }
  578. ///////////////////////////////////////////////////////////////////////////////////////////////////
  579. - (void)confirmCancellation {
  580. UIAlertView* cancelAlertView = [[[UIAlertView alloc] initWithTitle:
  581. TTLocalizedString(@"Cancel", @"")
  582. message:TTLocalizedString(@"Are you sure you want to cancel?", @"")
  583. delegate:self
  584. cancelButtonTitle:TTLocalizedString(@"Yes", @"")
  585. otherButtonTitles:TTLocalizedString(@"No", @""), nil] autorelease];
  586. [cancelAlertView show];
  587. }
  588. ///////////////////////////////////////////////////////////////////////////////////////////////////
  589. - (void)showActivityView:(BOOL)show {
  590. self.navigationItem.rightBarButtonItem.enabled = !show;
  591. if (show) {
  592. if (!_activityView) {
  593. CGRect frame = CGRectMake(0, 0, self.view.width, _scrollView.height);
  594. _activityView = [[TTActivityLabel alloc] initWithFrame:frame
  595. style:TTActivityLabelStyleWhiteBox];
  596. _activityView.text = [self titleForSending];
  597. _activityView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  598. [self.view addSubview:_activityView];
  599. }
  600. } else {
  601. [_activityView removeFromSuperview];
  602. TT_RELEASE_SAFELY(_activityView);
  603. }
  604. }
  605. ///////////////////////////////////////////////////////////////////////////////////////////////////
  606. - (NSString*)titleForSending {
  607. return TTLocalizedString(@"Sending...", @"");
  608. }
  609. ///////////////////////////////////////////////////////////////////////////////////////////////////
  610. - (BOOL)messageShouldCancel {
  611. return ![self hasEnteredText] || !_isModified;
  612. }
  613. ///////////////////////////////////////////////////////////////////////////////////////////////////
  614. - (void)messageWillShowRecipientPicker {
  615. }
  616. ///////////////////////////////////////////////////////////////////////////////////////////////////
  617. - (void)messageWillSend:(NSArray*)fields {
  618. }
  619. ///////////////////////////////////////////////////////////////////////////////////////////////////
  620. - (void)messageDidSend {
  621. }
  622. @end