Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Import MobileOrg source tree

  • Loading branch information...
commit 85eaca300f396c51d89095928da0b50784426fd9 1 parent 677c795
@richard richard authored
Showing with 14,088 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +42 −0 Classes/Capture/NewNoteController.h
  3. +228 −0 Classes/Capture/NewNoteController.m
  4. +40 −0 Classes/Capture/NoteListController.h
  5. +283 −0 Classes/Capture/NoteListController.m
  6. +36 −0 Classes/DataModel/FileChecksum.h
  7. +31 −0 Classes/DataModel/FileChecksum.m
  8. +40 −0 Classes/DataModel/LocalEditAction.h
  9. +35 −0 Classes/DataModel/LocalEditAction.m
  10. +80 −0 Classes/DataModel/Node.h
  11. +539 −0 Classes/DataModel/Node.m
  12. +44 −0 Classes/DataModel/Note.h
  13. +102 −0 Classes/DataModel/Note.m
  14. +79 −0 Classes/MobileOrgAppDelegate.h
  15. +275 −0 Classes/MobileOrgAppDelegate.m
  16. +69 −0 Classes/Outline/ActionMenuController.h
  17. +286 −0 Classes/Outline/ActionMenuController.m
  18. +39 −0 Classes/Outline/DetailsViewController.h
  19. +494 −0 Classes/Outline/DetailsViewController.m
  20. +42 −0 Classes/Outline/DocumentViewController.h
  21. +194 −0 Classes/Outline/DocumentViewController.m
  22. +51 −0 Classes/Outline/NodeTextEditController.h
  23. +253 −0 Classes/Outline/NodeTextEditController.m
  24. +37 −0 Classes/Outline/OutlineTableView.h
  25. +107 −0 Classes/Outline/OutlineTableView.m
  26. +64 −0 Classes/Outline/OutlineViewController.h
  27. +415 −0 Classes/Outline/OutlineViewController.m
  28. +40 −0 Classes/Outline/PriorityEditController.h
  29. +146 −0 Classes/Outline/PriorityEditController.m
  30. +44 −0 Classes/Outline/TagEditController.h
  31. +292 −0 Classes/Outline/TagEditController.m
  32. +40 −0 Classes/Outline/TodoStateEditController.h
  33. +209 −0 Classes/Outline/TodoStateEditController.m
  34. +34 −0 Classes/Parsing/ChecksumFileParser.h
  35. +115 −0 Classes/Parsing/ChecksumFileParser.m
  36. +47 −0 Classes/Parsing/EditEntity.h
  37. +47 −0 Classes/Parsing/EditEntity.m
  38. +40 −0 Classes/Parsing/EditsFileParser.h
  39. +272 −0 Classes/Parsing/EditsFileParser.m
  40. +39 −0 Classes/Parsing/OrgFileParser.h
  41. +470 −0 Classes/Parsing/OrgFileParser.m
  42. +40 −0 Classes/Search/SearchController.h
  43. +290 −0 Classes/Search/SearchController.m
  44. +41 −0 Classes/Settings/OutlineState.h
  45. +51 −0 Classes/Settings/OutlineState.m
  46. +52 −0 Classes/Settings/SessionManager.h
  47. +139 −0 Classes/Settings/SessionManager.m
  48. +66 −0 Classes/Settings/Settings.h
  49. +285 −0 Classes/Settings/Settings.m
  50. +31 −0 Classes/Settings/SettingsController.h
  51. +362 −0 Classes/Settings/SettingsController.m
  52. +47 −0 Classes/Status/StatusViewController.h
  53. +215 −0 Classes/Status/StatusViewController.m
  54. +67 −0 Classes/Sync/SyncManager.h
  55. +821 −0 Classes/Sync/SyncManager.m
  56. +51 −0 Classes/Sync/TransferContext.h
  57. +54 −0 Classes/Sync/TransferContext.m
  58. +47 −0 Classes/Sync/TransferManager.h
  59. +325 −0 Classes/Sync/TransferManager.m
  60. +34 −0 Classes/Sync/TransferManagerDelegate.h
  61. +88 −0 Classes/ThirdParty/Reachability/Reachability.h
  62. +274 −0 Classes/ThirdParty/Reachability/Reachability.m
  63. +220 −0 Classes/ThirdParty/RegexKitLite/RegexKitLite.h
  64. +1,634 −0 Classes/ThirdParty/RegexKitLite/RegexKitLite.m
  65. +85 −0 Classes/Utilities/DataUtils.h
  66. +704 −0 Classes/Utilities/DataUtils.m
  67. +35 −0 Classes/Utilities/GlobalUtils.h
  68. +51 −0 Classes/Utilities/GlobalUtils.m
  69. +34 −0 Classes/Utilities/RoundedLabel.h
  70. +99 −0 Classes/Utilities/RoundedLabel.m
  71. +31 −0 Classes/Utilities/StatusUtils.h
  72. +31 −0 Classes/Utilities/StatusUtils.m
  73. +33 −0 Classes/Utilities/TableUtils.h
  74. +258 −0 Classes/Utilities/TableUtils.m
  75. +8 −0 Entitlements.plist
  76. BIN  Icon-Small.png
  77. BIN  Icon-beta.png
  78. BIN  Icon-debug.png
  79. BIN  Icon.png
  80. +339 −0 LICENSE.txt
  81. +200 −0 MainWindow.xib
  82. +32 −0 MobileOrg-Info-AdHoc.plist
  83. +32 −0 MobileOrg-Info-Debug.plist
  84. +32 −0 MobileOrg-Info.plist
  85. BIN  MobileOrg.xcdatamodel/elements
  86. BIN  MobileOrg.xcdatamodel/layout
  87. +756 −0 MobileOrg.xcodeproj/project.pbxproj
  88. +16 −0 MobileOrg_Prefix.pch
  89. BIN  Other/Icon.psd
  90. +100 −0 Resources/Css/DocumentView.css
  91. BIN  Resources/Images/AlertTextField.png
  92. BIN  Resources/Images/Default.png
  93. BIN  Resources/Images/back.png
  94. BIN  Resources/Images/capture.png
  95. BIN  Resources/Images/children.png
  96. BIN  Resources/Images/down.png
  97. BIN  Resources/Images/flag.png
  98. BIN  Resources/Images/flagged.png
  99. BIN  Resources/Images/forward.png
  100. BIN  Resources/Images/home.png
  101. BIN  Resources/Images/inbox.png
  102. BIN  Resources/Images/noflag.png
  103. BIN  Resources/Images/note_entry.png
  104. BIN  Resources/Images/outline.png
  105. BIN  Resources/Images/search.png
  106. BIN  Resources/Images/settings.png
  107. BIN  Resources/Images/table_gray.png
  108. BIN  Resources/Images/up.png
  109. +34 −0 Resources/Javascript/DocumentView.js
  110. +3 −0  Scripts/bump_build.sh
  111. +3 −0  Scripts/set_version.sh
  112. +4 −0 Scripts/what_version.sh
  113. +31 −0 main.m
  114. +78 −0 setup_neon.sh
  115. +79 −0 setup_openssl.sh
View
6 .gitignore
@@ -0,0 +1,6 @@
+build
+*.pbxuser
+*.mode1v3
+*.perspectivev3
+.DS_Store
+vendor-libs
View
42 Classes/Capture/NewNoteController.h
@@ -0,0 +1,42 @@
+//
+// NewNoteController.h
+// MobileOrg
+//
+// Created by Richard Moreland on 10/6/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <Foundation/Foundation.h>
+
+@class Note;
+
+@interface NewNoteController : UIViewController <UITextViewDelegate> {
+ UITextView *textField;
+ UIBarButtonItem *doneButton;
+ UIBarButtonItem *addButton;
+ Note *note;
+ bool keyboardShown;
+ bool showKeyboardOnLoad;
+}
+
+@property (nonatomic, retain) UITextView *textField;
+@property (nonatomic, retain) UIBarButtonItem *doneButton;
+@property (nonatomic, retain) UIBarButtonItem *addButton;
+@property (nonatomic, retain) Note *note;
+@property (nonatomic) bool showKeyboardOnLoad;
+
+@end
View
228 Classes/Capture/NewNoteController.m
@@ -0,0 +1,228 @@
+//
+// NewNoteController.m
+// MobileOrg
+//
+// Created by Richard Moreland on 10/6/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "NewNoteController.h"
+#import "Note.h"
+#import "DataUtils.h"
+#import "GlobalUtils.h"
+#import "MobileOrgAppDelegate.h"
+#import "NoteListController.h"
+
+@implementation NewNoteController
+
+@synthesize textField;
+@synthesize note;
+@synthesize doneButton, addButton;
+@synthesize showKeyboardOnLoad;
+
+- (void)save:(bool)giveUpKeyboard {
+ if (!note.text || [note.text compare:textField.text] != NSOrderedSame) {
+ note.text = textField.text;
+ note.createdAt = [NSDate date];
+ note.locallyModified = [NSNumber numberWithBool:true];
+ Save();
+ }
+
+ if (giveUpKeyboard)
+ [textField resignFirstResponder];
+}
+
+- (void)save {
+ [self save:true];
+}
+
+- (void)textViewDidChange:(UITextView *)textView {
+ [self save:false];
+}
+
+- (void)add {
+ [self save:true];
+
+ Note *newNote = (Note*)[NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:[note managedObjectContext]];
+ [newNote setCreatedAt:[NSDate date]];
+ [newNote setNoteId:UUID()];
+ [newNote setLocallyModified:[NSNumber numberWithBool:true]];
+
+ Save();
+
+ [[AppInstance() noteListController] updateNoteCount];
+ [[AppInstance() noteListController] editNote:newNote withKeyboard:true];
+}
+
+- (void)done {
+}
+
+// Call this method somewhere in your view controller setup code.
+- (void)registerForKeyboardNotifications
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(keyboardWasShown:)
+ name:UIKeyboardDidShowNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(keyboardWasHidden:)
+ name:UIKeyboardDidHideNotification object:nil];
+}
+
+- (void)unregisterForKeyboardNotifications
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:UIKeyboardDidShowNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:UIKeyboardDidHideNotification object:nil];
+}
+
+// Implement loadView to create a view hierarchy programmatically, without using a nib.
+- (void)loadView {
+ textField = [[UITextView alloc] init];
+ [textField setScrollEnabled:YES];
+ [textField setScrollsToTop:YES];
+ [textField setFont:[UIFont systemFontOfSize:14.0]];
+ [textField setDelegate:self];
+ [self setView:textField];
+}
+
+// Called when the UIKeyboardDidShowNotification is sent.
+- (void)keyboardWasShown:(NSNotification*)aNotification
+{
+ if (keyboardShown)
+ return;
+
+ // Resize the scroll view (which is the root view of the window)
+ switch ([[UIDevice currentDevice] orientation]) {
+ case UIDeviceOrientationLandscapeLeft:
+ case UIDeviceOrientationLandscapeRight:
+ [[self view] setFrame:CGRectMake(0, 0, 480, 135)];
+ break;
+
+ case UIDeviceOrientationUnknown:
+ case UIDeviceOrientationPortrait:
+ case UIDeviceOrientationPortraitUpsideDown:
+ case UIDeviceOrientationFaceUp:
+ case UIDeviceOrientationFaceDown:
+ default:
+ [[self view] setFrame:CGRectMake(0, 0, 320, 220)];
+ break;
+ }
+
+ // Scroll the active text field into view.
+ [textField scrollRangeToVisible:NSMakeRange([textField.text length], 0)];
+
+ keyboardShown = YES;
+
+ self.navigationItem.rightBarButtonItem = doneButton;
+}
+
+
+// Called when the UIKeyboardDidHideNotification is sent
+- (void)keyboardWasHidden:(NSNotification*)aNotification
+{
+ NSDictionary* info = [aNotification userInfo];
+
+ // Get the size of the keyboard.
+ NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
+ CGSize keyboardSize = [aValue CGRectValue].size;
+
+ // Reset the height of the scroll view to its original value
+ CGRect viewFrame = [[self view] frame];
+ viewFrame.size.height += keyboardSize.height;
+ viewFrame.size.height -= 55;
+ [[self view] setFrame:viewFrame];
+
+ keyboardShown = NO;
+
+ self.navigationItem.rightBarButtonItem = addButton;
+}
+
+// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ [textField setText:[note text]];
+
+ if ([note isFlagEntry]) {
+ if (!note.text || [note.text length] == 0) {
+ self.title = @"New flagging note";
+ [textField becomeFirstResponder];
+ } else {
+ self.title = @"Edit flagging note";
+ }
+ } else {
+ if (!note.text || [note.text length] == 0) {
+ self.title = @"New note";
+ [textField becomeFirstResponder];
+ } else {
+ self.title = @"Edit note";
+ }
+ }
+
+ addButton = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add)];
+
+ doneButton = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(save)];
+
+ if (self.showKeyboardOnLoad) {
+ [textField becomeFirstResponder];
+ self.navigationItem.rightBarButtonItem = doneButton;
+ } else {
+ self.navigationItem.rightBarButtonItem = addButton;
+ }
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ return YES;
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ [self save:true];
+ [self registerForKeyboardNotifications];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ [self unregisterForKeyboardNotifications];
+}
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Release any cached data, images, etc that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Release any retained subviews of the main view.
+ // e.g. self.myOutlet = nil;
+}
+
+- (void)dealloc {
+ [note release];
+ [doneButton release];
+ [addButton release];
+ [textField release];
+ [super dealloc];
+}
+
+@end
View
40 Classes/Capture/NoteListController.h
@@ -0,0 +1,40 @@
+//
+// NoteListController.h
+// MobileOrg
+//
+// Created by Richard Moreland on 10/6/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <UIKit/UIKit.h>
+
+@class Note;
+
+@interface NoteListController : UITableViewController {
+ NSMutableArray *notesArray;
+ UIBarButtonItem *addButton;
+ UIBarButtonItem *editButton;
+ UIBarButtonItem *doneButton;
+}
+
+@property (nonatomic, retain) NSMutableArray *notesArray;
+
+- (void)addNote;
+- (void)updateNoteCount;
+- (void)editNote:(Note*)note withKeyboard:(bool)keyboard;
+
+@end
View
283 Classes/Capture/NoteListController.m
@@ -0,0 +1,283 @@
+//
+// NoteListController.m
+// MobileOrg
+//
+// Created by Richard Moreland on 10/6/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "NoteListController.h"
+#import "NewNoteController.h"
+#import "Note.h"
+#import "Node.h"
+#import "DataUtils.h"
+#import "GlobalUtils.h"
+
+@implementation NoteListController
+
+@synthesize notesArray;
+
+- (void)refreshData {
+ self.notesArray = [[AllActiveNotes() mutableCopy] autorelease];
+
+ [[self tableView] reloadData];
+
+ int noteCount = CountLocalNotes();
+ if (noteCount > 0) {
+ self.navigationController.tabBarItem.badgeValue = [[NSNumber numberWithInt:noteCount] stringValue];
+ } else {
+ self.navigationController.tabBarItem.badgeValue = nil;
+ }
+
+ if ([self.notesArray count] > 0) {
+ self.navigationItem.leftBarButtonItem.enabled = YES;
+ } else {
+ self.navigationItem.leftBarButtonItem.enabled = NO;
+ }
+}
+
+- (void)onSyncComplete {
+ [self refreshData];
+}
+
+- (void)stopEditing {
+ if ([self isEditing]) {
+ self.navigationItem.leftBarButtonItem = editButton;
+ [self setEditing:NO animated:YES];
+ }
+}
+
+- (void)addNote {
+
+ [self stopEditing];
+
+ Note *newNote = (Note*)[NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:[AppInstance() managedObjectContext]];
+ [newNote setCreatedAt:[NSDate date]];
+ [newNote setNoteId:UUID()];
+ [newNote setLocallyModified:[NSNumber numberWithBool:true]];
+
+ Save();
+
+ [self editNote:newNote withKeyboard:true];
+
+ [self updateNoteCount];
+}
+
+- (void)updateNoteCount {
+ [self refreshData];
+}
+
+- (void)edit {
+ if ([self isEditing]) {
+ [self stopEditing];
+ } else {
+ [self setEditing:YES animated:YES];
+ self.navigationItem.leftBarButtonItem = doneButton;
+ }
+}
+
+- (void)editNote:(Note*)note withKeyboard:(bool)keyboard {
+
+ [self.navigationController popToRootViewControllerAnimated:NO];
+
+ NewNoteController *newNoteController = [[NewNoteController alloc] initWithNibName:nil bundle:nil];
+ newNoteController.note = note;
+ newNoteController.showKeyboardOnLoad = keyboard;
+
+ // TODO: Store that we are about to be editing this note..? maybe
+ // Rethink this
+ //[SettingsController storeSelectedNote:note];
+
+ // Push the detail view controller.
+ [self.navigationController pushViewController:newNoteController animated:YES];
+
+ [newNoteController release];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ addButton = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNote)];
+ self.navigationItem.rightBarButtonItem = addButton;
+
+ editButton = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(edit)];
+
+ doneButton = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(edit)];
+ self.navigationItem.leftBarButtonItem = editButton;
+
+ // Subscribe to onSyncComplete messages (only the root controller needs to do this)
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onSyncComplete)
+ name:@"SyncComplete"
+ object:nil];
+
+ [self refreshData];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+
+ [self refreshData];
+
+ // TODO: Store in the session that there is no selected note
+ //[SettingsController storeSelectedNote:nil];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ [self stopEditing];
+}
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Release any cached data, images, etc that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Release any retained subviews of the main view.
+ // e.g. self.myOutlet = nil;
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ return YES;
+}
+
+#pragma mark Table view methods
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return 1;
+}
+
+// Customize the number of rows in the table view.
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return [notesArray count];
+}
+
+// Customize the appearance of table view cells.
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ Note *note = (Note*)[notesArray objectAtIndex:indexPath.row];
+
+ if ([note isFlagEntry]) {
+
+ static NSString *CellIdentifier = @"NoteFlaggedCell";
+
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
+ }
+
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"flagged" ofType:@"png"];
+ UIImage *flagImage = [UIImage imageWithContentsOfFile:path];
+ cell.imageView.image = flagImage;
+
+ Node *node = ResolveNode(note.nodeId);
+ cell.textLabel.text = [node headingForDisplay];
+
+ NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+ [formatter setDateFormat:@"YYYY-MM-dd EEE HH:mm"];
+ NSString *createdAtStr = [formatter stringFromDate:[note createdAt]];
+ [formatter release];
+
+ if (note.flagAction && [note.flagAction length] > 0) {
+ cell.detailTextLabel.text = [NSString stringWithFormat:@"F(%@) %@", note.flagAction, createdAtStr];
+ } else {
+ cell.detailTextLabel.text = [NSString stringWithFormat:@"F() %@", createdAtStr];
+ }
+
+ cell.accessoryType = UIButtonTypeDetailDisclosure;
+
+ return cell;
+
+ } else {
+
+ static NSString *CellIdentifier = @"NoteCell";
+
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
+ }
+
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"note_entry" ofType:@"png"];
+ UIImage *flagImage = [UIImage imageWithContentsOfFile:path];
+ cell.imageView.image = flagImage;
+
+ cell.textLabel.text = [note heading];
+
+ NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+ [formatter setDateFormat:@"YYYY-MM-dd EEE HH:mm"];
+ cell.detailTextLabel.text = [formatter stringFromDate:[note createdAt]];
+ [formatter release];
+
+ cell.accessoryType = UIButtonTypeDetailDisclosure;
+
+ return cell;
+ }
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ Note *note = (Note*)[notesArray objectAtIndex:indexPath.row];
+ [self editNote:note withKeyboard:false];
+}
+
+- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
+ Note *note = (Note*)[notesArray objectAtIndex:indexPath.row];
+ [self editNote:note withKeyboard:false];
+}
+
+- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (editingStyle == UITableViewCellEditingStyleDelete) {
+
+ Note *noteToDelete = [notesArray objectAtIndex:indexPath.row];
+
+ noteToDelete.locallyModified = [NSNumber numberWithBool:true];
+ noteToDelete.deleted = [NSNumber numberWithBool:true];
+
+ Save();
+
+ [notesArray removeObjectAtIndex:[indexPath row]];
+
+ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
+
+ [self refreshData];
+
+ if ([notesArray count] == 0) {
+ [self stopEditing];
+ self.navigationItem.leftBarButtonItem.enabled = NO;
+ }
+ }
+}
+
+- (void)dealloc {
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"SyncComplete"
+ object:nil];
+
+ [notesArray release];
+ [editButton release];
+ [doneButton release];
+ [addButton release];
+ [super dealloc];
+}
+
+@end
View
36 Classes/DataModel/FileChecksum.h
@@ -0,0 +1,36 @@
+//
+// FileChecksum.h
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <CoreData/CoreData.h>
+
+
+@interface FileChecksum : NSManagedObject
+{
+}
+
+@property (nonatomic, retain) NSString * filename;
+@property (nonatomic, retain) NSString * checksum;
+
+@end
+
+
+
View
31 Classes/DataModel/FileChecksum.m
@@ -0,0 +1,31 @@
+//
+// FileChecksum.m
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "FileChecksum.h"
+
+
+@implementation FileChecksum
+
+@dynamic filename;
+@dynamic checksum;
+
+@end
View
40 Classes/DataModel/LocalEditAction.h
@@ -0,0 +1,40 @@
+//
+// LocalEditAction.h
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <CoreData/CoreData.h>
+
+@class Node;
+
+@interface LocalEditAction : NSManagedObject
+{
+}
+
+@property (nonatomic, copy) NSString * oldValue;
+@property (nonatomic, copy) NSString * newValue;
+@property (nonatomic, copy) NSString * actionType;
+@property (nonatomic, retain) NSDate * createdAt;
+@property (nonatomic, copy) NSString * nodeId;
+
+@end
+
+
+
View
35 Classes/DataModel/LocalEditAction.m
@@ -0,0 +1,35 @@
+//
+// LocalEditAction.m
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "LocalEditAction.h"
+
+#import "Node.h"
+
+@implementation LocalEditAction
+
+@dynamic oldValue;
+@dynamic newValue;
+@dynamic actionType;
+@dynamic createdAt;
+@dynamic nodeId;
+
+@end
View
80 Classes/DataModel/Node.h
@@ -0,0 +1,80 @@
+//
+// Node.h
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <CoreData/CoreData.h>
+
+@class LocalEditAction;
+
+@interface Node : NSManagedObject
+{
+}
+
+@property (nonatomic, retain) NSString * body;
+@property (nonatomic, retain) NSString * heading;
+@property (nonatomic, retain) NSNumber * sequenceIndex;
+@property (nonatomic, retain) NSString * todoState;
+@property (nonatomic, retain) NSString * tags;
+@property (nonatomic, retain) NSString * inheritedTags;
+@property (nonatomic, retain) NSString * referencedNodeId;
+@property (nonatomic, retain) NSString * nodeId;
+@property (nonatomic, retain) NSString * outlinePath;
+@property (nonatomic, retain) NSNumber * indentLevel;
+@property (nonatomic, retain) NSNumber * readOnly;
+@property (nonatomic, retain) NSString * priority;
+@property (nonatomic, retain) Node * parent;
+@property (nonatomic, retain) NSSet* notes;
+@property (nonatomic, retain) NSSet* children;
+
+- (NSString*)bestId;
+- (NSString*)headingForDisplay;
+- (NSString*)beforeText;
+- (NSString*)afterText;
+- (NSString*)bodyForDisplay;
+- (NSString*)completeTags;
+- (NSString*)tagsForDisplay;
+- (bool)hasTag:(NSString*)tag;
+- (bool)hasInheritedTag:(NSString*)tag;
+- (void)toggleTag:(NSString*)tag;
+- (void)addTag:(NSString*)tag;
+- (void)removeTag:(NSString*)tag;
+- (NSArray*)sortedChildren;
+- (NSString*)resolveLink:(NSString*)link;
+- (bool)isLink;
+- (NSString*)linkFile;
+- (bool)isBrokenLink;
+- (NSString*)linkTitle;
+- (void)collectLinks:(NSMutableArray*)links;
+- (NSString*)htmlForDocumentViewLevel:(int)level;
+- (NSString*)ownerFile;
+
+@end
+
+
+@interface Node (CoreDataGeneratedAccessors)
+
+- (void)addChildrenObject:(Node *)value;
+- (void)removeChildrenObject:(Node *)value;
+- (void)addChildren:(NSSet *)value;
+- (void)removeChildren:(NSSet *)value;
+
+@end
+
View
539 Classes/DataModel/Node.m
@@ -0,0 +1,539 @@
+//
+// Node.m
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "Node.h"
+#import "LocalEditAction.h"
+#import "DataUtils.h"
+#import "Settings.h"
+#import "RegexKitLite.h"
+
+@implementation Node
+
+@dynamic body;
+@dynamic heading;
+@dynamic sequenceIndex;
+@dynamic todoState;
+@dynamic tags;
+@dynamic inheritedTags;
+@dynamic referencedNodeId;
+@dynamic nodeId;
+@dynamic outlinePath;
+@dynamic indentLevel;
+@dynamic readOnly;
+@dynamic priority;
+@dynamic parent;
+@dynamic notes;
+@dynamic children;
+
+static NSString *kFileLinkRegex = @"\\[\\[file:([a-zA-Z0-9/\\-_]+\\.org)\\]\\[(.*)\\]\\]";
+
+- (NSComparisonResult)sequenceIndexCompare:(Node*)obj
+{
+ NSComparisonResult retVal = NSOrderedSame;
+ if ([self.sequenceIndex intValue] > [obj.sequenceIndex intValue])
+ retVal = NSOrderedDescending;
+ else if ([self.sequenceIndex intValue] < [obj.sequenceIndex intValue])
+ retVal = NSOrderedAscending;
+ return retVal;
+}
+
+- (NSString*)bestId {
+ if (self.nodeId && [self.nodeId length] > 0) {
+ return [NSString stringWithFormat:@"id:%@", self.nodeId];
+ } else if (self.referencedNodeId && [self.referencedNodeId length] > 0) {
+ return self.referencedNodeId;
+ } else {
+ return self.outlinePath;
+ }
+}
+
+// Make this handle <break> business, show linked node's title instead, etc
+- (NSString*)headingForDisplay {
+
+ NSString *ret = [self heading];
+
+ // File nodes (level 0) should just show their filename
+ if ([[self indentLevel] intValue] == 0) {
+ ret = [ret lastPathComponent];
+ }
+
+ // If the node is a link, show the title of the link
+ if ([self isLink]) {
+ NSRange link_location = [[self heading] rangeOfString:@"[["];
+ NSString *link_text = [[self heading] substringFromIndex:link_location.location];
+ NSRange last_location = [link_text rangeOfString:@"]]"];
+ if (last_location.location != NSNotFound) {
+ link_text = [link_text substringToIndex:last_location.location+last_location.length];
+ ret = [[self heading] stringByReplacingOccurrencesOfString:link_text withString:[self linkTitle]];
+ }
+ }
+
+ // If the heading has <break> in it, just show the part before <break>
+ NSRange break_range = [ret rangeOfString:@"<break>"];
+ if (break_range.location != NSNotFound) {
+ ret = [ret substringToIndex:break_range.location];
+ }
+
+ ret = [ret stringByReplacingOccurrencesOfRegex:@"<before>.*</before>" withString:@""];
+ ret = [ret stringByReplacingOccurrencesOfRegex:@"<after>.*</after>" withString:@""];
+
+ ret = [ret stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ return ret;
+}
+
+- (NSString*)beforeText {
+ NSArray *ret = [self.heading captureComponentsMatchedByRegex:@"<before>(.*)</before>"];
+ if ([ret count] > 0) {
+ return [ret objectAtIndex:1];
+ }
+ return nil;
+}
+
+- (NSString*)afterText {
+ NSArray *ret = [self.heading captureComponentsMatchedByRegex:@"<after>(.*)</after>"];
+ if ([ret count] > 0) {
+ return [ret objectAtIndex:1];
+ }
+ return nil;
+}
+
+- (NSString*)bodyForDisplay {
+ NSString *summary = [self body];
+
+ NSRange break_range = [[self heading] rangeOfString:@"<break>"];
+ if (break_range.location != NSNotFound) {
+ summary = [[self heading] substringFromIndex:break_range.location+break_range.length];
+ }
+
+ summary = [summary stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ return summary;
+}
+
+- (NSString*)completeTags {
+ // This should aggregate the actual and inherited tags of this node. This is used to determine
+ // what the inherited tags will be, a child object calls completeTags on its parent for what to put
+ // on the 'left hand side' of :a::b:
+ //
+ // The results should jsut be :a:b:c:d:
+
+ NSString *ret = self.inheritedTags;
+ if (!ret || [ret length] == 0) {
+ return self.tags;
+ }
+
+ ret = [ret stringByAppendingFormat:self.tags];
+ ret = [ret stringByReplacingOccurrencesOfString:@"::" withString:@":"];
+
+ return ret;
+}
+
+- (NSString*)tagsForDisplay {
+ // We want to display like this:
+ // :no:inherited:tags:
+ // :some:inherited:tags::and:some:locals
+ // :just:inherited:tags::
+ // :just:local:tags
+
+ NSString *ret = self.inheritedTags;
+ if (!ret || [ret length] == 0) {
+ return self.tags;
+ }
+
+ if (!self.tags || [self.tags length] == 0) {
+ ret = [ret stringByAppendingString:@":"];
+ } else {
+ ret = [ret stringByAppendingString:self.tags];
+ }
+ return ret;
+}
+
+- (bool)hasTag:(NSString*)tag {
+ if (self.tags &&
+ [self.tags length] > 0 &&
+ [self.tags rangeOfString:[NSString stringWithFormat:@":%@:", tag]].location != NSNotFound) {
+ return true;
+ }
+ return false;
+}
+
+- (bool)hasInheritedTag:(NSString*)tag {
+ if (self.inheritedTags &&
+ [self.inheritedTags length] > 0 &&
+ [self.inheritedTags rangeOfString:[NSString stringWithFormat:@":%@:", tag]].location != NSNotFound) {
+ return true;
+ }
+ return false;
+}
+
+- (void)toggleTag:(NSString*)tag {
+ // Existing tags could be:
+ // - ''
+ // - ':a:'
+ // - ':a:b:'
+ if ([self hasTag:tag]) {
+ [self removeTag:tag];
+ } else {
+ [self addTag:tag];
+ }
+}
+
+- (void)addTag:(NSString*)tag {
+ if (!self.tags || [self.tags length] == 0) {
+ self.tags = [NSString stringWithFormat:@":%@:", tag];
+ } else {
+ self.tags = [self.tags stringByAppendingFormat:@"%@:", tag];
+ }
+}
+
+- (void)removeTag:(NSString*)tag {
+ // Remove :a: from the string, replace with :
+ // If the string is just :, make it empty
+ // If the string doesn't start with :, add it to the front
+ // If the string doesn't end with :, add it to the end
+
+ self.tags = [self.tags stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@":%@:", tag] withString:@":"];
+
+ if ([self.tags length] == 1) {
+ self.tags = @"";
+ } else {
+ if ([self.tags characterAtIndex:0] != ':') {
+ self.tags = [NSString stringWithFormat:@":%@", self.tags];
+ }
+
+ if ([self.tags characterAtIndex:[self.tags length]-1] != ':') {
+ self.tags = [self.tags stringByAppendingString:@":"];
+ }
+ }
+}
+
+- (NSArray*)sortedChildren {
+ return [[[self children] allObjects] sortedArrayUsingSelector:@selector(sequenceIndexCompare:)];
+}
+
+// Possibilities:
+// - file.org
+// - other/file.org, where our file parent has no subdir/link
+// - RESULT: other/file.org
+// - other/file.org, where our file parent has a subdir/link subdir/main.org
+// - RESULT: subdir/other/file.org
+// - ../file.org, where our file parent has no subdir
+// - RESULT: Cannot resolve
+// - ../file.org, where our file parent has a subdir/link as subdir/another/main.org
+// - RESULT: subdir/file.org
+// - ../../file.org, where our file parent is subdir/another/main.org
+// - RESULT: file.org
+- (NSString*)resolveLink:(NSString*)link {
+
+ // See if the file node parent is in a subdir. If it is, we have to use its path as the root
+ NSString *root = @"";
+ Node *node = self;
+ while ([node parent]) {
+ node = [node parent];
+ }
+
+ if (node != self || [[self indentLevel] intValue] == 0) {
+ NSString *root_linkfile = [node heading];
+ if ([root_linkfile rangeOfString:@"/"].location != NSNotFound) {
+ root = [root_linkfile stringByReplacingOccurrencesOfString:[root_linkfile lastPathComponent] withString:@""];
+ }
+ }
+
+ // Figure out how many levels we have to go up
+ int upLevels = 0;
+ while ([link rangeOfString:@"../"].location == 0) {
+ upLevels++;
+ link = [link substringFromIndex:3];
+ }
+
+ // Hack off path components from the root path for each upLevel
+ for (int i = 0; i < upLevels; i++) {
+ if ([root rangeOfString:@"/"].location == NSNotFound) {
+ // ERROR: The link points to a path further up than our root, nothing we can do
+ return nil;
+ }
+ root = [root stringByReplacingOccurrencesOfString:[root lastPathComponent] withString:@""];
+ root = [root substringToIndex:[root length]-1];
+ }
+
+ link = [root stringByAppendingString:link];
+
+ return link;
+}
+
+- (bool)isLink {
+ NSRange linkRange = [[self heading] rangeOfString:@"[[file:"];
+ if (linkRange.location != NSNotFound) {
+ return true;
+ }
+ return false;
+}
+
+// This will construct the link relative to the root of the file, sort of. It isn't perfect yet.
+- (NSString*)linkFile {
+ NSRange link_location = [[self heading] rangeOfString:@"[[file:"];
+ NSString *link_text = [[self heading] substringFromIndex:link_location.location];
+ if (link_location.location != NSNotFound) {
+ NSString *link = [link_text stringByReplacingOccurrencesOfString:@"[[file:" withString:@""];
+ link = [link substringToIndex:[link rangeOfString:@"]"].location];
+ return [self resolveLink:link];
+ }
+ return nil;
+}
+
+- (bool)isBrokenLink {
+ if ([self isLink]) {
+ return !NodeWithFilename([self linkFile]);
+ }
+ return false;
+}
+
+- (NSString*)linkTitle {
+ NSRange link_location = [[self heading] rangeOfString:@"[[file:"];
+ if (link_location.location != NSNotFound) {
+ NSRange first_bracket_location = [[self heading] rangeOfString:@"]["];
+ if (first_bracket_location.location != NSNotFound) {
+ NSString *link_title = [[self heading] substringFromIndex:first_bracket_location.location+first_bracket_location.length];
+ NSRange second_bracket_location = [link_title rangeOfString:@"]"];
+ if (second_bracket_location.location != NSNotFound) {
+ link_title = [link_title substringToIndex:second_bracket_location.location];
+ return link_title;
+ }
+ }
+ }
+ return nil;
+}
+
+- (void)collectLinks:(NSMutableArray*)links {
+ if ([self isLink]) {
+ NSString *link = [self linkFile];
+ if (![links containsObject:link]) {
+ [links addObject:link];
+ }
+ }
+
+ // Collect links from body text
+ {
+ NSArray *matches = [[self body] arrayOfCaptureComponentsMatchedByRegex:kFileLinkRegex];
+ for (NSArray *match in matches) {
+ NSString *link = [self resolveLink:[match objectAtIndex:1]];
+ if (link && [link length] > 1) {
+ if (![links containsObject:link]) {
+ [links addObject:link];
+ }
+ }
+ }
+ }
+
+ for (Node *child in [self children]) {
+ [child collectLinks:links];
+ }
+}
+
+- (NSString*)markupLine:(NSString*)line {
+ // First, look for links of this form [[http://...][Title]]
+ {
+ NSString *regexString = @"\\[\\[(https?://[a-zA-Z0-9\\-.]+(?::(\\d+))?(?:(?:/[a-zA-Z0-9\\-._?,'+\\&%$=~*!():@#\\\\]*)+)?)\\]\\[([a-zA-Z0-9/\\-_\\. '!?]+)\\]\\]";
+ line = [line stringByReplacingOccurrencesOfRegex:regexString withString:@"<a href='$1'>$3</a>"];
+ }
+
+ // Then look for standalone links
+ // Be careful not to turn the portion inside href="" from above into a link, so we force a space before the link.
+ {
+ NSString *regexString = @"(?<!'|\")(https?://[a-zA-Z0-9\\-.]+(?::(\\d+))?(?:(?:/[a-zA-Z0-9\\-._?,'+\\&%$=~*!():@#\\\\]*)+)?)";
+ line = [line stringByReplacingOccurrencesOfRegex:regexString withString:@"<a href='$1'>$1</a>"];
+ }
+
+ // Any file: links that link to org files?
+ {
+ NSString *regexString = @"\\[\\[file:([a-zA-Z0-9/\\-\\._]+\\.org)\\]\\[([a-zA-Z0-9/\\-_\\. '!?]+)\\]\\]";
+ line = [line stringByReplacingOccurrencesOfRegex:regexString withString:@"<a href='orgfile:$1'>$2</a>"];
+ }
+
+ // Add strong for *text*
+ {
+ NSString *regexString = @"(?<=\\A|\\s|\\()\\*(\\w[\\w ]*)\\*(?=\\s|\\z|\\)|[\\.,:])";
+ line = [line stringByReplacingOccurrencesOfRegex:regexString withString:@"<strong>$1</strong>"];
+ }
+
+ // Add em for /text/
+ {
+ NSString *regexString = @"(?<=\\A|\\s|\\()\\/(\\w[\\w ]*)\\/(?=\\s|\\z|\\)|[\\.,:])";
+ line = [line stringByReplacingOccurrencesOfRegex:regexString withString:@"<em>$1</em>"];
+ }
+
+ // Add underline for _text_
+ {
+ NSString *regexString = @"(?<=\\A|\\s|\\()_(\\w[\\w ]*)_(?=\\s|\\z|\\)|[\\.,:])";
+ line = [line stringByReplacingOccurrencesOfRegex:regexString withString:@"<span style='text-decoration: underline;'>$1</span>"];
+ }
+
+ return line;
+}
+
+- (NSString*)htmlForDocumentViewLevel:(int)level {
+ NSString *ret = @"";
+ @try {
+ NSString *title = [self headingForDisplay];
+ if ([[self todoState] length] > 0) {
+ if ([[Settings instance] isTodoState:[self todoState]]) {
+ title = [NSString stringWithFormat:@"<span class='keyword-todo'>%@</span> %@", [self todoState], title];
+ } else {
+ title = [NSString stringWithFormat:@"<span class='keyword-done'>%@</span> %@", [self todoState], title];
+ }
+ }
+ if ([[self tags] length] > 0) {
+ title = [NSString stringWithFormat:@"%@<span class='tags'>%@</span>", title, [self tags]];
+ }
+ if (level == 0) {
+ ret = [ret stringByAppendingString:@"<html><head><meta name='viewport' content='width=960, user-scalable=yes'><link rel='stylesheet' href='DocumentView.css' /><script type='text/javascript' src='DocumentView.js'></script></head><body>"];
+ ret = [ret stringByAppendingFormat:@"<h1>%@</h1>", title];
+ } else if (level == 1) {
+ ret = [ret stringByAppendingFormat:@"<h2>%@</h2>", title];
+ } else if (level == 2) {
+ ret = [ret stringByAppendingFormat:@"<h3>%@</h3>", title];
+ } else if (level == 3) {
+ ret = [ret stringByAppendingFormat:@"<h4>%@</h4>", title];
+ } else if (level == 4) {
+ ret = [ret stringByAppendingFormat:@"<h5>%@</h5>", title];
+ } else if (level == 5) {
+ ret = [ret stringByAppendingFormat:@"<h6>%@</h6>", title];
+ }
+
+ if ([self body] && [[self body] length] > 0) {
+ NSScanner *theScanner;
+ theScanner = [NSScanner scannerWithString:[self body]];
+
+ NSString *line;
+ NSCharacterSet *eolSet;
+ eolSet = [NSCharacterSet characterSetWithCharactersInString:@"\n"];
+
+ [theScanner setCharactersToBeSkipped:eolSet];
+
+ NSString *formatted_body = @"";
+
+ bool was_pre = false;
+ bool in_drawer = false;
+
+ int drawer_count = 0;
+
+ while ([theScanner isAtEnd] == NO) {
+ if ([theScanner scanUpToCharactersFromSet:eolSet intoString:&line]) {
+
+ NSString *stripped_line = [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ if (stripped_line && [stripped_line length] > 0) {
+
+ if ([stripped_line length] > 2 && [stripped_line characterAtIndex:0] == ':' && [stripped_line characterAtIndex:[stripped_line length]-1] == ':') {
+ NSString *drawer_title = [stripped_line substringWithRange:NSMakeRange(1, [stripped_line length]-2)];
+ if (in_drawer) {
+ if ([drawer_title compare:@"END"] == NSOrderedSame) {
+ in_drawer = false;
+ formatted_body = [formatted_body stringByAppendingString:@"</div></div>"];
+ continue;
+ }
+ } else {
+ // TODO: Make sure drawer_title isn't in a disallowed list
+ in_drawer = true;
+ drawer_count++;
+ formatted_body = [formatted_body stringByAppendingFormat:@"<div class='drawer'><span class='drawer-heading' onclick='toggleDrawer(\"%d-%d\")'><span id='drawer-toggle-%d-%d'>Show</span> %@</span>", level, drawer_count, level, drawer_count, drawer_title];
+ formatted_body = [formatted_body stringByAppendingFormat:@"<div style='display: none;' class='drawer-body' id='drawer-body-%d-%d'>", level, drawer_count];
+ continue;
+ }
+ }
+
+ if ([stripped_line characterAtIndex:0] == '#') { // It is a comment line, color it
+ if (was_pre)
+ formatted_body = [formatted_body stringByAppendingString:@"</pre>"];
+ formatted_body = [formatted_body stringByAppendingFormat:@"<span class='comment'>%@</span><br>", [self markupLine:stripped_line]];
+ continue;
+ } else if ([stripped_line characterAtIndex:0] == '|' || (!in_drawer && [stripped_line characterAtIndex:0] == ':')) { // Table or : entry
+ if (!was_pre)
+ formatted_body = [formatted_body stringByAppendingString:@"<pre>"];
+ if ([stripped_line characterAtIndex:0] == ':') {
+ if ([stripped_line length] >= 3) {
+ // Trim off the part after ': ', make sure we dont overrun the string
+ stripped_line = [stripped_line substringFromIndex:2];
+ } else {
+ stripped_line = @"";
+ }
+ }
+ formatted_body = [formatted_body stringByAppendingString:[self markupLine:stripped_line]];
+ formatted_body = [formatted_body stringByAppendingString:@"\n"];
+ was_pre = true;
+ continue;
+ }
+ }
+
+ if (was_pre) {
+ formatted_body = [formatted_body stringByAppendingString:@"</pre>"];
+ was_pre = false;
+ }
+ line = [self markupLine:line];
+
+ if (!stripped_line || [stripped_line length] == 0) {
+ formatted_body = [formatted_body stringByAppendingString:@"<p>"];
+ } else {
+ formatted_body = [formatted_body stringByAppendingString:line];
+ formatted_body = [formatted_body stringByAppendingString:@"<br>"];
+ }
+ }
+ }
+
+ if (was_pre)
+ formatted_body = [formatted_body stringByAppendingString:@"</pre>"];
+
+ ret = [ret stringByAppendingString:formatted_body];
+ }
+
+ if ([self isLink]) {
+ // TODO: If we want document view to traverse links, we'd do it here
+ } else {
+ for (Node *child in [self sortedChildren]) {
+ ret = [ret stringByAppendingString:[child htmlForDocumentViewLevel:level+1]];
+ }
+ }
+
+ if (level == 0) {
+ ret = [ret stringByAppendingString:@"</body></html>"];
+ }
+ } @catch (NSException *e) {
+ ret = @"Error generating HTML for this node";
+ }
+ return ret;
+}
+
+- (NSString*)ownerFile {
+ Node *node = self;
+ while ([node parent]) {
+ node = [node parent];
+ }
+
+ if ([[node indentLevel] intValue] == 0) {
+ return [node heading];
+ }
+
+ return nil;
+}
+
+@end
View
44 Classes/DataModel/Note.h
@@ -0,0 +1,44 @@
+//
+// Note.h
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <CoreData/CoreData.h>
+
+@class Node;
+
+@interface Note : NSManagedObject
+{
+}
+
+@property (nonatomic, retain) NSString * text;
+@property (nonatomic, retain) NSDate * createdAt;
+@property (nonatomic, retain) NSString * nodeId;
+@property (nonatomic, retain) NSString * flagAction;
+@property (nonatomic, retain) NSString * noteId;
+@property (nonatomic, retain) NSNumber * locallyModified;
+@property (nonatomic, retain) NSNumber * deleted;
+
+- (NSString*)heading;
+- (NSString*)body;
+- (bool)isFlagEntry;
+- (NSString*)orgLine;
+
+@end
View
102 Classes/DataModel/Note.m
@@ -0,0 +1,102 @@
+//
+// Note.m
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "Note.h"
+#import "Node.h"
+#import "DataUtils.h"
+
+@implementation Note
+
+@dynamic text;
+@dynamic createdAt;
+@dynamic nodeId;
+@dynamic flagAction;
+@dynamic noteId;
+@dynamic locallyModified;
+@dynamic deleted;
+
+- (NSString*)heading {
+ // If it is a flag entry, the flag part is the title, the rest is the body
+ if ([self isFlagEntry]) {
+ Node *node = ResolveNode(self.nodeId);
+ return [NSString stringWithFormat:@"F(%@) [[%@][%@]]", self.flagAction, self.nodeId, [node headingForDisplay]];
+ }
+
+ if (!self.text || [self.text length] == 0) {
+ return @"No title";
+ }
+
+ NSRange rangeOfFirstNewline = [self.text rangeOfString:@"\n"];
+ if (rangeOfFirstNewline.location != NSNotFound) {
+ return [self.text substringToIndex:rangeOfFirstNewline.location];
+ } else {
+ return self.text;
+ }
+}
+
+- (NSString*)body {
+ // If it is a flag entry, the flag part is the title, the rest is the body
+ if ([self isFlagEntry]) {
+ return [self text];
+ }
+
+ if (!self.text || [self.text length] == 0) {
+ return @"";
+ }
+ NSRange rangeOfFirstNewline = [self.text rangeOfString:@"\n"];
+ if (rangeOfFirstNewline.location != NSNotFound && [self.text length] > rangeOfFirstNewline.location+1) {
+ return [self.text substringFromIndex:rangeOfFirstNewline.location+1];
+ } else {
+ return @"";
+ }
+}
+
+- (bool)isFlagEntry {
+ return self.nodeId && [self.nodeId length] > 0;
+}
+
+// * first line of the note
+// [2009-09-09 Wed 09:25]
+// Rest of the note
+- (NSString*)orgLine {
+ NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+ [formatter setDateFormat:@"YYYY-MM-dd EEE HH:mm"];
+ NSString *timestamp = [formatter stringFromDate:[self createdAt]];
+ [formatter release];
+
+ NSString *bodyStr = [self body];
+ if (bodyStr && [bodyStr length] > 0) {
+ // Make the body text indented by 2 spaces on each line
+ // Actually, don't do this. It makes it easier to later figure out what the original
+ // intent was.
+ // bodyStr = [bodyStr stringByReplacingOccurrencesOfString:@"\n" withString:@"\n "];
+
+ // Then get rid of any extra spaces or newlines at the ends
+ bodyStr = [bodyStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ return [NSString stringWithFormat:@"* %@\n[%@]\n%@\n", [self heading], timestamp, bodyStr];
+ } else {
+ return [NSString stringWithFormat:@"* %@\n[%@]\n", [self heading], timestamp];
+ }
+}
+
+@end
View
79 Classes/MobileOrgAppDelegate.h
@@ -0,0 +1,79 @@
+//
+// MobileOrgAppDelegate.h
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+@class OutlineViewController;
+@class NoteListController;
+@class SearchController;
+@class SettingsController;
+@class Reachability;
+
+@interface MobileOrgAppDelegate : NSObject <UIApplicationDelegate> {
+
+ NSManagedObjectModel *managedObjectModel;
+ NSManagedObjectContext *managedObjectContext;
+ NSPersistentStoreCoordinator *persistentStoreCoordinator;
+
+ UITabBarController *tabBarController;
+
+ OutlineViewController *rootOutlineController;
+ UINavigationController *rootOutlineNavigationController;
+
+ NoteListController *noteListController;
+ UINavigationController *noteListNavigationController;
+
+ SearchController *searchController;
+ UINavigationController *searchNavigationController;
+
+ SettingsController *settingsController;
+ UINavigationController *settingsNavigationController;
+
+ UIWindow *window;
+
+ Reachability *internetReach;
+}
+
+@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
+@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
+@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
+
+@property (nonatomic, retain) IBOutlet UIWindow *window;
+
+@property (nonatomic, readonly) UITabBarController *tabBarController;
+
+@property (nonatomic, readonly) OutlineViewController *rootOutlineController;
+@property (nonatomic, readonly) UINavigationController *rootOutlineNavigationController;
+
+@property (nonatomic, readonly) NoteListController *noteListController;
+@property (nonatomic, readonly) UINavigationController *noteListNavigationController;
+
+@property (nonatomic, readonly) SearchController *searchController;
+@property (nonatomic, readonly) UINavigationController *searchNavigationController;
+
+@property (nonatomic, readonly) SettingsController *settingsController;
+@property (nonatomic, readonly) UINavigationController *settingsNavigationController;
+
+@property (nonatomic, retain) Reachability *internetReach;
+
+- (NSString *)applicationDocumentsDirectory;
+
+@end
+
View
275 Classes/MobileOrgAppDelegate.m
@@ -0,0 +1,275 @@
+//
+// MobileOrgAppDelegate.m
+// MobileOrg
+//
+// Created by Richard Moreland on 9/30/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "MobileOrgAppDelegate.h"
+#import "OutlineViewController.h"
+#import "NoteListController.h"
+#import "SearchController.h"
+#import "SettingsController.h"
+#import "DataUtils.h"
+#import "Reachability.h"
+#import "SessionManager.h"
+
+@interface MobileOrgAppDelegate(private)
+- (void)updateInterfaceWithReachability:(Reachability*)curReach;
+@end
+
+@implementation MobileOrgAppDelegate
+
+@synthesize window;
+@synthesize internetReach;
+
+#pragma mark -
+#pragma mark Application lifecycle
+
+- (void)applicationDidFinishLaunching:(UIApplication *)application {
+
+ NSArray *tabViewControllers = [NSArray arrayWithObjects:
+ self.rootOutlineNavigationController,
+ self.noteListNavigationController,
+ self.searchNavigationController,
+ self.settingsNavigationController,
+ nil];
+ [[self tabBarController] setViewControllers:tabViewControllers];
+
+ self.rootOutlineNavigationController.tabBarItem.image = [UIImage imageNamed:@"outline.png"];
+ self.noteListNavigationController.tabBarItem.image = [UIImage imageNamed:@"inbox.png"];
+ self.searchNavigationController.tabBarItem.image = [UIImage imageNamed:@"search.png"];
+ self.settingsNavigationController.tabBarItem.image = [UIImage imageNamed:@"settings.png"];
+
+ [window addSubview:[[self tabBarController] view]];
+
+ [self.noteListController updateNoteCount];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(reachabilityChanged:)
+ name:kReachabilityChangedNotification object: nil];
+
+ internetReach = [[Reachability reachabilityForInternetConnection] retain];
+ [internetReach startNotifer];
+ [self updateInterfaceWithReachability:internetReach];
+
+ [[SessionManager instance] restore];
+
+ [window makeKeyAndVisible];
+}
+
+/**
+ applicationWillTerminate: saves changes in the application's managed object context before the application terminates.
+ */
+- (void)applicationWillTerminate:(UIApplication *)application {
+
+ [[SessionManager instance] storeCurrentTab];
+
+ NSError *error = nil;
+ if (managedObjectContext != nil) {
+ if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
+ /*
+ Replace this implementation with code to handle the error appropriately.
+
+ abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
+ */
+ NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
+ abort();
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark User Interface
+
+- (UITabBarController*)tabBarController {
+ if (tabBarController == nil) {
+ tabBarController = [[UITabBarController alloc] init];
+ }
+ return tabBarController;
+}
+
+- (OutlineViewController*)rootOutlineController {
+ if (rootOutlineController == nil) {
+ rootOutlineController = [[OutlineViewController alloc] initWithRootNode:RootNode()];
+ }
+ return rootOutlineController;
+}
+
+- (UINavigationController*)rootOutlineNavigationController {
+ if (rootOutlineNavigationController == nil) {
+ rootOutlineNavigationController = [[UINavigationController alloc] initWithRootViewController:[self rootOutlineController]];
+ }
+ return rootOutlineNavigationController;
+}
+
+- (NoteListController*)noteListController {
+ if (noteListController == nil) {
+ noteListController = [[NoteListController alloc] initWithStyle:UITableViewStylePlain];
+ noteListController.title = @"Capture";
+ }
+ return noteListController;
+}
+
+- (UINavigationController*)noteListNavigationController {
+ if (noteListNavigationController == nil) {
+ noteListNavigationController = [[UINavigationController alloc] initWithRootViewController:self.noteListController];
+ }
+ return noteListNavigationController;
+}
+
+- (SearchController*)searchController {
+ if (searchController == nil) {
+ searchController = [[SearchController alloc] initWithStyle:UITableViewStylePlain];
+ searchController.title = @"Search";
+ }
+ return searchController;
+}
+
+- (UINavigationController*)searchNavigationController {
+ if (searchNavigationController == nil) {
+ searchNavigationController = [[UINavigationController alloc] initWithRootViewController:self.searchController];
+ }
+ return searchNavigationController;
+}
+
+- (SettingsController*)settingsController {
+ if (settingsController == nil) {
+ settingsController = [[SettingsController alloc] initWithStyle:UITableViewStyleGrouped];
+ settingsController.title = @"Settings";
+ }
+ return settingsController;
+}
+
+- (UINavigationController*)settingsNavigationController {
+ if (settingsNavigationController == nil) {
+ settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:self.settingsController];
+ }
+ return settingsNavigationController;
+}
+
+- (void)updateInterfaceWithReachability:(Reachability*)curReach {
+ if(curReach == internetReach) {
+ NetworkStatus netStatus = [curReach currentReachabilityStatus];
+ if (netStatus == NotReachable) {
+ [[self rootOutlineController] setHasConnectivity:NO];
+ } else {
+ [[self rootOutlineController] setHasConnectivity:YES];
+ }
+ }
+}
+
+- (void)reachabilityChanged:(NSNotification*)note {
+ Reachability* curReach = [note object];
+ NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
+ [self updateInterfaceWithReachability: curReach];
+}
+
+#pragma mark -
+#pragma mark Core Data stack
+
+/**
+ Returns the managed object context for the application.
+ If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
+ */
+- (NSManagedObjectContext *) managedObjectContext {
+
+ if (managedObjectContext != nil) {
+ return managedObjectContext;
+ }
+
+ NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
+ if (coordinator != nil) {
+ managedObjectContext = [[NSManagedObjectContext alloc] init];
+ [managedObjectContext setPersistentStoreCoordinator: coordinator];
+ }
+ return managedObjectContext;
+}
+
+/**
+ Returns the managed object model for the application.
+ If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
+ */
+- (NSManagedObjectModel *)managedObjectModel {
+
+ if (managedObjectModel != nil) {
+ return managedObjectModel;
+ }
+ managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
+ return managedObjectModel;
+}
+
+/**
+ Returns the persistent store coordinator for the application.
+ If the coordinator doesn't already exist, it is created and the application's store added to it.
+ */
+- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
+
+ if (persistentStoreCoordinator != nil) {
+ return persistentStoreCoordinator;
+ }
+
+ NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"MobileOrg.sqlite"]];
+
+ NSError *error;
+ persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
+
+ NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
+ [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
+
+ if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
+ // Handle error
+ }
+
+ return persistentStoreCoordinator;
+}
+
+#pragma mark -
+#pragma mark Application's Documents directory
+
+/**
+ Returns the path to the application's Documents directory.
+ */
+- (NSString *)applicationDocumentsDirectory {
+ return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc {
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:kReachabilityChangedNotification];
+
+ [rootOutlineController release];
+ [rootOutlineNavigationController release];
+
+ [tabBarController release];
+
+ [managedObjectContext release];
+ [managedObjectModel release];
+ [persistentStoreCoordinator release];
+
+ [internetReach release];
+
+ [window release];
+ [super dealloc];
+}
+
+@end
View
69 Classes/Outline/ActionMenuController.h
@@ -0,0 +1,69 @@
+//
+// ActionMenuController.h
+// MobileOrg
+//
+// Created by Richard Moreland on 10/10/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import <UIKit/UIKit.h>
+
+@class Node;
+@class OutlineViewController;
+
+@interface ActionMenuController : UIViewController {
+ UILabel *titleField;
+ UIButton *doneButton;
+ UIButton *doneAndArchiveButton;
+ UIButton *flagButton;
+ UIButton *flagWithNoteButton;
+ UIButton *documentViewButton;
+ UIButton *cancelButton;
+
+ Node *node;
+ UITableViewCell *cell;
+ UINavigationController *firstNavController;
+ OutlineViewController *parentController;
+
+ bool showDocumentViewButton;
+
+ UIView *actionView;
+}
+
+- (void)onDone;
+- (void)onDoneAndArchive;
+- (void)onFlag;
+- (void)onFlagWithNote;
+- (void)onDocumentView;
+- (void)onCancel;
+
+@property (nonatomic, retain) Node *node;
+@property (nonatomic, retain) UITableViewCell *cell;
+@property (nonatomic, retain) UINavigationController *firstNavController;
+@property (nonatomic, retain) OutlineViewController *parentController;
+
+@property (nonatomic, readonly) UILabel *titleField;
+@property (nonatomic, readonly) UIButton *doneButton;
+@property (nonatomic, readonly) UIButton *doneAndArchiveButton;
+@property (nonatomic, readonly) UIButton *flagButton;
+@property (nonatomic, readonly) UIButton *flagWithNoteButton;
+@property (nonatomic, readonly) UIButton *documentViewButton;
+@property (nonatomic, readonly) UIButton *cancelButton;
+
+@property (nonatomic) bool showDocumentViewButton;
+
+@end
View
286 Classes/Outline/ActionMenuController.m
@@ -0,0 +1,286 @@
+//
+// ActionMenuController.m
+// MobileOrg
+//
+// Created by Richard Moreland on 10/10/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#import "ActionMenuController.h"
+#import "Node.h"
+#import "MobileOrgAppDelegate.h"
+#import "GlobalUtils.h"
+#import "DataUtils.h"
+#import "Note.h"
+#import "NoteListController.h"
+#import "OutlineViewController.h"
+
+@implementation ActionMenuController
+
+@synthesize node;
+@synthesize cell;
+@synthesize firstNavController;
+@synthesize showDocumentViewButton;
+@synthesize parentController;
+
+- (void)close {
+ if (cell) {
+ [cell setHighlighted:NO];
+ }
+ [firstNavController dismissModalViewControllerAnimated:YES];
+}
+
+- (void)addFlag:(NSString*)action andEdit:(bool)edit {
+ Note *note = (Note*)[NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:[node managedObjectContext]];
+ note.nodeId = [node bestId];
+ note.createdAt = [NSDate date];
+ note.flagAction = action;
+ note.noteId = UUID();
+ note.locallyModified = [NSNumber numberWithBool:true];
+
+ Save();
+
+ [[AppInstance() noteListController] updateNoteCount];
+
+ if (edit) {
+ [[AppInstance() noteListController] editNote:note withKeyboard:true];
+ [[AppInstance() tabBarController] setSelectedIndex:1];
+ }
+}
+
+- (void)onDone {
+ [self addFlag:@"d" andEdit:false];
+ [self close];
+}
+
+- (void)onDoneAndArchive {
+ [self addFlag:@"d-a" andEdit:false];
+ [self close];
+}
+
+- (void)onFlag {
+ [self addFlag:@"" andEdit:false];
+ [self close];
+}
+
+- (void)onFlagWithNote {
+ [self addFlag:@"" andEdit:true];
+ [self close];
+}
+
+- (void)onDocumentView {
+ [self close];
+ [parentController selectRowAtIndexPath:[parentController pathForNode:node] withType:OutlineSelectionTypeDocumentView andAnimation:YES];
+}
+
+- (void)onCancel {
+ [self close];
+}
+
+- (void)layoutButtons {
+ int halfButtonWidth = 130;
+ int fullButtonWidth = 280;
+ int buttonHeight = 40;
+ int buttonVSpacing = 55;
+
+ int leftButtonX = 0;
+ int rightButtonX = 0;
+ int yOffset = 0;
+
+ switch ([[UIDevice currentDevice] orientation]) {
+ case UIDeviceOrientationLandscapeLeft:
+ case UIDeviceOrientationLandscapeRight:
+ leftButtonX = 100;
+ rightButtonX = 250;
+ yOffset = 10;
+ actionView.frame = CGRectMake(0, 0, 480, 320);
+ break;
+
+ case UIDeviceOrientationUnknown:
+ case UIDeviceOrientationPortrait:
+ case UIDeviceOrientationPortraitUpsideDown:
+ case UIDeviceOrientationFaceUp:
+ case UIDeviceOrientationFaceDown:
+ default:
+ leftButtonX = 20;
+ rightButtonX = 170;
+ yOffset = 40;
+ actionView.frame = CGRectMake(0, 0, 320, 480);
+ break;
+ }
+
+ titleField.frame = CGRectMake(leftButtonX, yOffset, fullButtonWidth, buttonHeight);
+ yOffset += buttonVSpacing;
+
+ doneButton.frame = CGRectMake(leftButtonX, yOffset, halfButtonWidth, buttonHeight);
+ doneAndArchiveButton.frame = CGRectMake(rightButtonX, yOffset, halfButtonWidth, buttonHeight);
+ yOffset += buttonVSpacing;
+
+ flagButton.frame = CGRectMake(leftButtonX, yOffset, halfButtonWidth, buttonHeight);
+ flagWithNoteButton.frame = CGRectMake(rightButtonX, yOffset, halfButtonWidth, buttonHeight);
+ yOffset += buttonVSpacing;
+
+ if (showDocumentViewButton) {
+ documentViewButton.frame = CGRectMake(leftButtonX, yOffset, fullButtonWidth, buttonHeight);
+ yOffset += buttonVSpacing;
+ }
+
+ cancelButton.frame = CGRectMake(leftButtonX, yOffset, fullButtonWidth, buttonHeight);
+
+ [actionView setNeedsLayout];
+ [actionView setNeedsDisplay];
+}
+
+- (void)loadView {
+ [super loadView];
+
+ self.view.backgroundColor = [UIColor colorWithWhite:0.1 alpha:1.0];
+
+ actionView = [[UIView alloc] init];
+ [actionView addSubview:self.titleField];
+ [actionView addSubview:self.doneButton];
+ [actionView addSubview:self.doneAndArchiveButton];
+ [actionView addSubview:self.flagButton];
+ [actionView addSubview:self.flagWithNoteButton];
+ [actionView addSubview:self.documentViewButton];
+ [actionView addSubview:self.cancelButton];
+
+ [self.view addSubview:actionView];
+
+ [self layoutButtons];
+}
+
+- (void)didRotate:(NSNotification *)notification {
+ [self layoutButtons];
+}
+
+- (UILabel*)titleField {
+ if (titleField == nil) {
+ titleField = [[UILabel alloc] init];
+ [titleField setText:[node headingForDisplay]];
+ [titleField setTextAlignment:UITextAlignmentCenter];
+ [titleField setTextColor:[UIColor whiteColor]];
+ [titleField setBackgroundColor:[UIColor clearColor]];
+ }
+ return titleField;
+}
+
+- (UIButton*)doneButton {
+ if (doneButton == nil) {
+ doneButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
+ [doneButton setTitle:@"Mark as Done" forState:UIControlStateNormal];
+ [doneButton addTarget:self action:@selector(onDone) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return doneButton;
+}
+
+- (UIButton*)doneAndArchiveButton {
+ if (doneAndArchiveButton == nil) {
+ doneAndArchiveButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
+ [doneAndArchiveButton setTitle:@"...and Archive" forState:UIControlStateNormal];
+ [doneAndArchiveButton addTarget:self action:@selector(onDoneAndArchive) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return doneAndArchiveButton;
+}
+
+- (UIButton*)flagButton {
+ if (flagButton == nil) {
+ flagButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
+ [flagButton setTitle:@"Flag item" forState:UIControlStateNormal];
+ [flagButton addTarget:self action:@selector(onFlag) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return flagButton;
+}
+
+- (UIButton*)flagWithNoteButton {
+ if (flagWithNoteButton == nil) {
+ flagWithNoteButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
+ [flagWithNoteButton setTitle:@"...with Note" forState:UIControlStateNormal];
+ [flagWithNoteButton addTarget:self action:@selector(onFlagWithNote) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return flagWithNoteButton;
+}
+
+- (UIButton*)documentViewButton {
+ if (documentViewButton == nil) {
+ documentViewButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
+ [documentViewButton setTitle:@"View item as a Document" forState:UIControlStateNormal];
+ [documentViewButton addTarget:self action:@selector(onDocumentView) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return documentViewButton;
+}
+
+- (UIButton*)cancelButton {
+ if (cancelButton == nil) {
+ cancelButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
+ [cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
+ [cancelButton addTarget:self action:@selector(onCancel) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return cancelButton;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ [titleField setText:[node headingForDisplay]];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(didRotate:)
+ name:UIDeviceOrientationDidChangeNotification object:nil];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:UIDeviceOrientationDidChangeNotification object:nil];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ return YES;
+}
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Release any cached data, images, etc that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Release any retained subviews of the main view.
+ // e.g. self.myOutlet = nil;
+}
+
+
+- (void)dealloc {
+ [doneButton release];
+ [doneAndArchiveButton release];
+ [flagButton release];
+ [flagWithNoteButton release];
+ [documentViewButton release];
+ [cancelButton release];
+ [actionView release];
+ [node release];
+ [cell release];
+ [firstNavController release];
+ [parentController release];
+ [super dealloc];
+}
+
+@end
View
39 Classes/Outline/DetailsViewController.h
@@ -0,0 +1,39 @@
+//
+// DetailsViewController.h
+// MobileOrg
+//
+// Created by Richard Moreland on 10/1/09.
+// Copyright 2009 Richard Moreland.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software