/Source/externals/GData/Examples/ContactsSample/EditEntryWindowController.m

http://google-email-uploader-mac.googlecode.com/ · Objective C · 386 lines · 259 code · 79 blank · 48 comment · 50 complexity · acfb7b6c03fc0d91aa7f113ffb33a14e MD5 · raw file

  1. /* Copyright (c) 2008 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. //
  16. // EditEntryWindowController.m
  17. //
  18. #import "EditEntryWindowController.h"
  19. #import "GData/GDataEntryContact.h"
  20. #import "GData/GDataEntryContactGroup.h"
  21. // map from class of item to display name for item and the item's object
  22. // selectors to use for various editable fields in the nib
  23. //
  24. // for example, the value displayed will be obtained with stringValue
  25. // for postal address objects but with address for IM objects
  26. //
  27. // nil means "disable this edit field"
  28. typedef struct ItemSelectors {
  29. NSString *className;
  30. NSString *classDisplayName;
  31. NSString *valueKey;
  32. NSString *labelKey;
  33. NSString *relKey;
  34. NSString *titleKey;
  35. NSString *protocolKey;
  36. } ItemSelectors;
  37. static ItemSelectors sAllItemSelectors[] = {
  38. { @"GDataOrganization", @"Organization", @"orgName", @"label", @"rel", @"orgTitle", nil },
  39. { @"GDataEmail", @"E-mail", @"address", @"label", @"rel", nil, nil },
  40. { @"GDataIM", @"Instant Messaging", @"address", @"label", @"rel", nil, @"protocol" },
  41. { @"GDataPhoneNumber", @"Phone", @"stringValue", @"label", @"rel", nil, nil },
  42. { @"GDataStructuredPostalAddress", @"Postal", @"formattedAddress", @"label", @"rel", nil, nil },
  43. { @"GDataGroupMembershipInfo", @"Group", nil, nil, nil, nil, nil },
  44. { @"GDataExtendedProperty", @"Extended Property", @"unifiedStringValue", @"name", nil, nil, nil },
  45. { 0, 0, 0, 0, 0, 0 }
  46. };
  47. // given the object we're editing, get the list with the selectors
  48. // and the display name for that class of object
  49. static ItemSelectors *ItemSelectorsForObject(GDataObject *obj) {
  50. NSString *className = NSStringFromClass([obj class]);
  51. for (int idx = 0; sAllItemSelectors[idx].className; idx++) {
  52. if ([className isEqual:sAllItemSelectors[idx].className]) {
  53. return &sAllItemSelectors[idx];
  54. }
  55. }
  56. return NULL;
  57. }
  58. @interface EditEntryWindowController (PrivateMethods)
  59. - (void)setUIFromObject:(GDataObject *)obj;
  60. @end
  61. @implementation EditEntryWindowController
  62. - (id)init {
  63. return [self initWithWindowNibName:@"EditEntryWindow"];
  64. }
  65. - (void)awakeFromNib {
  66. if (mObject) {
  67. // copy data from the object to our dialog's controls
  68. [self setUIFromObject:mObject];
  69. }
  70. // make the lists of rel values for the rel combo box, and put in a dictionary
  71. // according to item class. This dictionary is used for the combo box
  72. // data source delegate methods
  73. //
  74. // When the combo box menu's data source asks for the menu items,
  75. // we'll just look up the rel strings from the class of the item
  76. // being edited.
  77. NSArray *standardRels = [NSArray arrayWithObjects:kGDataContactHome,
  78. kGDataContactWork, kGDataContactOther, nil];
  79. NSArray *orgRels = [NSArray arrayWithObjects:kGDataContactWork,
  80. kGDataContactOther, nil];
  81. NSArray *phoneRels = [NSArray arrayWithObjects:kGDataPhoneNumberHome,
  82. kGDataPhoneNumberMobile, kGDataPhoneNumberPager, kGDataPhoneNumberWork,
  83. kGDataPhoneNumberHomeFax, kGDataPhoneNumberWorkFax, kGDataPhoneNumberOther,
  84. nil];
  85. NSArray *noRels = [NSArray array];
  86. relsDict_ = [[NSDictionary alloc] initWithObjectsAndKeys:
  87. orgRels, @"GDataOrganization",
  88. standardRels, @"GDataEmail",
  89. standardRels, @"GDataIM",
  90. phoneRels, @"GDataPhoneNumber",
  91. standardRels, @"GDataStructuredPostalAddress",
  92. noRels, @"GDataGroupMembershipInfo",
  93. noRels, @"GDataExtendedProperty", nil];
  94. }
  95. - (void)dealloc {
  96. [relsDict_ release];
  97. [mObject release];
  98. [mGroupFeed release];
  99. [super dealloc];
  100. }
  101. #pragma mark -
  102. // given a key, get the value from the object for that key, or an empty string
  103. - (NSString *)stringValueForKey:(NSString *)key object:(GDataObject *)obj {
  104. if (key) {
  105. NSString *value = [obj valueForKey:key];
  106. if (value) {
  107. return value;
  108. }
  109. }
  110. return @"";
  111. }
  112. - (void)setUIFromObject:(GDataObject *)obj {
  113. ItemSelectors *sels = ItemSelectorsForObject(obj);
  114. NSString *value = [self stringValueForKey:(sels->valueKey) object:obj];
  115. NSString *label = [self stringValueForKey:(sels->labelKey) object:obj];
  116. NSString *rel = [self stringValueForKey:(sels->relKey) object:obj];
  117. NSString *title = [self stringValueForKey:(sels->titleKey) object:obj];
  118. NSString *protocol = [self stringValueForKey:(sels->protocolKey) object:obj];
  119. [mClassNameField setStringValue:(sels->classDisplayName)];
  120. [mValueField setStringValue:value];
  121. [mLabelField setStringValue:label];
  122. [mRelField setStringValue:rel];
  123. [mOrgTitleField setStringValue:title];
  124. [mProtocolField setStringValue:protocol];
  125. [mValueField setEnabled:(sels->valueKey != nil)];
  126. [mLabelField setEnabled:(sels->labelKey != nil)];
  127. [mRelField setEnabled:(sels->relKey != nil)];
  128. [mOrgTitleField setEnabled:(sels->titleKey != nil)];
  129. [mProtocolField setEnabled:(sels->protocolKey != nil)];
  130. // group combo box
  131. if ([obj isKindOfClass:[GDataGroupMembershipInfo class]]) {
  132. [mGroupField setEnabled:YES];
  133. // set the field text to the group namefor this object's href
  134. NSString *objectID = [(GDataGroupMembershipInfo *)obj href];
  135. if (objectID) {
  136. GDataEntryContactGroup *groupEntry = [mGroupFeed entryForIdentifier:objectID];
  137. NSString *name = [[groupEntry title] stringValue];
  138. [mGroupField setStringValue:name];
  139. }
  140. } else {
  141. [mGroupField setEnabled:NO];
  142. }
  143. // "primary" checkbox
  144. if ([obj respondsToSelector:@selector(isPrimary)]) {
  145. BOOL isPrimary = [(id)obj isPrimary];
  146. [mPrimaryCheckbox setState:(isPrimary ? NSOnState : NSOffState)];
  147. [mPrimaryCheckbox setEnabled:YES];
  148. } else {
  149. [mPrimaryCheckbox setEnabled:NO];
  150. }
  151. // "deleted" checkbox
  152. if ([obj respondsToSelector:@selector(isDeleted)]) {
  153. BOOL isDeleted = [(id)obj isDeleted];
  154. [mDeletedCheckbox setState:(isDeleted ? NSOnState : NSOffState)];
  155. [mDeletedCheckbox setEnabled:YES];
  156. } else {
  157. [mDeletedCheckbox setEnabled:NO];
  158. }
  159. }
  160. - (NSString *)stringValueOrNilForField:(NSTextField *)field {
  161. NSString *str = [field stringValue];
  162. return ([str length] > 0) ? str : nil;
  163. }
  164. - (GDataObject *)objectFromUI {
  165. ItemSelectors *sels = ItemSelectorsForObject(mObject);
  166. NSString *value = [self stringValueOrNilForField:mValueField];
  167. NSString *label = [self stringValueOrNilForField:mLabelField];
  168. NSString *rel = [self stringValueOrNilForField:mRelField];
  169. NSString *title = [self stringValueOrNilForField:mOrgTitleField];
  170. NSString *protocol = [self stringValueOrNilForField:mProtocolField];
  171. GDataObject *newObj = [mObject copy];
  172. if (sels->valueKey) [newObj setValue:value forKey:sels->valueKey];
  173. if (sels->labelKey) [newObj setValue:label forKey:sels->labelKey];
  174. if (sels->relKey) [newObj setValue:rel forKey:sels->relKey];
  175. if (sels->titleKey) [newObj setValue:title forKey:sels->titleKey];
  176. if (sels->protocolKey) [newObj setValue:protocol forKey:sels->protocolKey];
  177. if ([mPrimaryCheckbox isEnabled]) {
  178. BOOL isPrimary = ([mPrimaryCheckbox state] == NSOnState);
  179. [(id)newObj setIsPrimary:isPrimary];
  180. }
  181. if ([mDeletedCheckbox isEnabled]) {
  182. BOOL isDeleted = ([mDeletedCheckbox state] == NSOnState);
  183. [(id)newObj setIsDeleted:isDeleted];
  184. }
  185. if ([mGroupField isEnabled]) {
  186. // find the index of the group entry that has the title in the combo box
  187. NSString *str = [mGroupField stringValue];
  188. NSArray *titles = [mGroupFeed valueForKeyPath:@"entries.title.stringValue"];
  189. int index = [titles indexOfObject:str];
  190. NSString *href;
  191. if (index != NSNotFound) {
  192. // we found the title; get the corresponding entry's ID
  193. href = [[[mGroupFeed entries] objectAtIndex:index] identifier];
  194. } else {
  195. // it wasn't a title, so assume the user entered a group's ID
  196. href = str;
  197. }
  198. [(id)newObj setHref:href];
  199. }
  200. return newObj;
  201. }
  202. #pragma mark -
  203. - (void)runModalForTarget:(id)target
  204. selector:(SEL)doneSelector
  205. groupFeed:(GDataFeedContactGroup *)groupFeed
  206. object:(GDataObject *)object
  207. {
  208. mTarget = target;
  209. mDoneSEL = doneSelector;
  210. mObject = [object retain];
  211. mGroupFeed = [groupFeed retain];
  212. [NSApp beginSheet:[self window]
  213. modalForWindow:[mTarget window]
  214. modalDelegate:self
  215. didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
  216. contextInfo:nil];
  217. }
  218. - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
  219. }
  220. - (void)closeDialog {
  221. // call the target to say we're done
  222. [mTarget performSelector:mDoneSEL
  223. withObject:[[self retain] autorelease]];
  224. [[self window] orderOut:self];
  225. [NSApp endSheet:[self window]];
  226. }
  227. - (IBAction)saveButtonClicked:(id)sender {
  228. mWasSaveClicked = YES;
  229. [self closeDialog];
  230. }
  231. - (IBAction)cancelButtonClicked:(id)sender {
  232. [self closeDialog];
  233. }
  234. - (GDataObject *)object {
  235. // copy from our dialog's controls into the object
  236. return [self objectFromUI];
  237. }
  238. - (BOOL)wasSaveClicked {
  239. return mWasSaveClicked;
  240. }
  241. #pragma mark Rel and Protocol combo box data source
  242. - (NSArray *)relsForCurrentObject {
  243. NSString *className = NSStringFromClass([mObject class]);
  244. return [relsDict_ objectForKey:className];
  245. }
  246. - (NSArray *)protocolsForCurrentObject {
  247. if ([mObject class] == [GDataIM class]) {
  248. return [NSArray arrayWithObjects:
  249. kGDataIMProtocolAIM, kGDataIMProtocolGoogleTalk, kGDataIMProtocolICQ,
  250. kGDataIMProtocolJabber, kGDataIMProtocolMSN, kGDataIMProtocolYahoo, nil];
  251. }
  252. return nil;
  253. }
  254. - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox {
  255. if (aComboBox == mRelField) {
  256. return [[self relsForCurrentObject] count];
  257. } else if (aComboBox == mGroupField) {
  258. return [[mGroupFeed entries] count];
  259. } else {
  260. return [[self protocolsForCurrentObject] count];
  261. }
  262. }
  263. - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index {
  264. if (aComboBox == mRelField) {
  265. return [[self relsForCurrentObject] objectAtIndex:index];
  266. } else if (aComboBox == mGroupField) {
  267. NSArray *titles = [mGroupFeed valueForKeyPath:@"entries.title.stringValue"];
  268. return [titles objectAtIndex:index];
  269. } else {
  270. return [[self protocolsForCurrentObject] objectAtIndex:index];
  271. }
  272. }
  273. @end
  274. @implementation GDataExtendedProperty (ContactsSampleAdditions)
  275. // getter that looks for a plain value or for an XML array;
  276. // setter that looks for a leading "<" to decide if it's an XML element
  277. - (NSString *)unifiedStringValue {
  278. NSString *result = [self value];
  279. if (result == nil) {
  280. NSArray *xmlStrings = [[self XMLValues] valueForKey:@"XMLString"];
  281. result = [xmlStrings componentsJoinedByString:@""];
  282. }
  283. return result;
  284. }
  285. - (void)setUnifiedStringValue:(NSString *)str {
  286. NSCharacterSet *wsSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  287. NSString *trimmed = [str stringByTrimmingCharactersInSet:wsSet];
  288. if ([trimmed hasPrefix:@"<"]) {
  289. // set as an XML element
  290. NSError *error = nil;
  291. NSXMLElement *element;
  292. element = [[[NSXMLElement alloc] initWithXMLString:str
  293. error:&error] autorelease];
  294. if (element) {
  295. [self setXMLValues:[NSArray arrayWithObject:element]];
  296. } else {
  297. NSLog(@"XML parse error: %@", error);
  298. [self setXMLValues:nil];
  299. }
  300. [self setValue:nil];
  301. } else {
  302. // set as an attribute string
  303. [self setValue:str];
  304. [self setXMLValues:nil];
  305. }
  306. }
  307. @end