1 /*
2  *  "GEDKeeper", the personal genealogical database editor.
3  *  Copyright (C) 2009-2021 by Sergey V. Zhdanovskih.
4  *
5  *  This file is part of "GEDKeeper".
6  *
7  *  This program is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 using GDModel;
22 
23 namespace GKCore.Operations
24 {
25     public enum OperationType
26     {
27         otNOP,
28 
29         otIndividualParentsAttach,
30         otIndividualParentsDetach,
31 
32         otFamilySpouseAttach,
33         otFamilySpouseDetach,
34 
35         otGroupMemberAttach,
36         otGroupMemberDetach,
37 
38         otSourceRepositoryCitationAdd,
39         otSourceRepositoryCitationRemove,
40 
41         otResearchTaskAdd,
42         otResearchTaskRemove,
43         otResearchCommunicationAdd,
44         otResearchCommunicationRemove,
45         otResearchGroupAdd,
46         otResearchGroupRemove,
47 
48         otRecordNoteAdd,
49         otRecordNoteRemove,
50 
51         otRecordMediaAdd,
52         otRecordMediaRemove,
53 
54         otRecordSourceCitAdd,
55         otRecordSourceCitRemove,
56 
57         otRecordEventAdd,
58         otRecordEventRemove,
59 
60         otIndividualAssociationAdd,
61         otIndividualAssociationRemove,
62 
63         otIndividualNameAdd,
64         otIndividualNameRemove,
65 
66         otIndividualURefAdd,
67         otIndividualURefRemove,
68 
69         otIndividualPortraitAttach,
70         otIndividualPortraitDetach,
71 
72         otIndividualBookmarkChange,
73         otIndividualPatriarchChange,
74         otIndividualSexChange
75     }
76 
77     /// <summary>
78     /// Processing operations of change one of the properties of the records.
79     /// </summary>
80     public class OrdinaryOperation : CustomOperation
81     {
82         private readonly OperationType fType;
83         private readonly GDMObject fObj;
84         private object fOldVal;
85         private readonly object fNewVal;
86 
OrdinaryOperation(UndoManager manager, OperationType type, GDMObject obj, object newVal)87         public OrdinaryOperation(UndoManager manager, OperationType type,
88                                  GDMObject obj, object newVal) : base(manager)
89         {
90             fType = type;
91             fObj = obj;
92             fNewVal = newVal;
93         }
94 
Redo()95         public override bool Redo()
96         {
97             return ProcessOperation(true);
98         }
99 
Undo()100         public override void Undo()
101         {
102             ProcessOperation(false);
103         }
104 
ProcessOperation(bool redo)105         private bool ProcessOperation(bool redo)
106         {
107             bool result;
108 
109             switch (fType) {
110                 case OperationType.otNOP:
111                     result = false;
112                     break;
113 
114                 case OperationType.otIndividualParentsAttach:
115                 case OperationType.otIndividualParentsDetach:
116                     result = ProcessIndividualParents(redo);
117                     break;
118 
119                 case OperationType.otFamilySpouseAttach:
120                 case OperationType.otFamilySpouseDetach:
121                     result = ProcessFamilySpouse(redo);
122                     break;
123 
124                 case OperationType.otGroupMemberAttach:
125                 case OperationType.otGroupMemberDetach:
126                     result = ProcessGroupMember(redo);
127                     break;
128 
129                 case OperationType.otSourceRepositoryCitationAdd:
130                 case OperationType.otSourceRepositoryCitationRemove:
131                     result = ProcessSourceRepositoryCitation(redo);
132                     break;
133 
134 
135                 case OperationType.otResearchTaskAdd:
136                 case OperationType.otResearchTaskRemove:
137                     result = ProcessResearchTask(redo);
138                     break;
139 
140                 case OperationType.otResearchCommunicationAdd:
141                 case OperationType.otResearchCommunicationRemove:
142                     result = ProcessResearchCommunication(redo);
143                     break;
144 
145                 case OperationType.otResearchGroupAdd:
146                 case OperationType.otResearchGroupRemove:
147                     result = ProcessResearchGroup(redo);
148                     break;
149 
150                 case OperationType.otRecordNoteAdd:
151                     result = ProcessRecordNoteAdd(redo);
152                     break;
153 
154                 case OperationType.otRecordNoteRemove:
155                     result = ProcessRecordNoteRemove(redo);
156                     break;
157 
158                 case OperationType.otRecordMediaAdd:
159                     result = ProcessRecordMediaAdd(redo);
160                     break;
161 
162                 case OperationType.otRecordMediaRemove:
163                     result = ProcessRecordMediaRemove(redo);
164                     break;
165 
166                 case OperationType.otRecordSourceCitAdd:
167                 case OperationType.otRecordSourceCitRemove:
168                     result = ProcessRecordSourceCit(redo);
169                     break;
170 
171                 case OperationType.otRecordEventAdd:
172                 case OperationType.otRecordEventRemove:
173                     result = ProcessRecordEvent(redo);
174                     break;
175 
176                 case OperationType.otIndividualAssociationAdd:
177                 case OperationType.otIndividualAssociationRemove:
178                     result = ProcessIndividualAssociation(redo);
179                     break;
180 
181                 case OperationType.otIndividualNameAdd:
182                 case OperationType.otIndividualNameRemove:
183                     result = ProcessIndividualName(redo);
184                     break;
185 
186                 case OperationType.otIndividualURefAdd:
187                 case OperationType.otIndividualURefRemove:
188                     result = ProcessIndividualURef(redo);
189                     break;
190 
191                 case OperationType.otIndividualPortraitAttach:
192                 case OperationType.otIndividualPortraitDetach:
193                     result = ProcessIndividualPortrait(redo);
194                     break;
195 
196                 case OperationType.otIndividualBookmarkChange:
197                     result = ProcessIndividualBookmarkChange(redo);
198                     break;
199 
200                 case OperationType.otIndividualPatriarchChange:
201                     result = ProcessIndividualPatriarchChange(redo);
202                     break;
203 
204                 case OperationType.otIndividualSexChange:
205                     result = ProcessIndividualSexChange(redo);
206                     break;
207 
208                 default:
209                     result = false;
210                     break;
211             }
212 
213             return result;
214         }
215 
ProcessIndividualParents(bool redo)216         private bool ProcessIndividualParents(bool redo)
217         {
218             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
219             GDMFamilyRecord familyRec = fNewVal as GDMFamilyRecord;
220 
221             if (iRec == null || familyRec == null) {
222                 return false;
223             }
224 
225             if (fType == OperationType.otIndividualParentsDetach) {
226                 redo = !redo;
227             }
228             if (redo) {
229                 familyRec.AddChild(iRec);
230             } else {
231                 familyRec.RemoveChild(iRec);
232             }
233             return true;
234         }
235 
ProcessFamilySpouse(bool redo)236         private bool ProcessFamilySpouse(bool redo)
237         {
238             GDMFamilyRecord famRec = fObj as GDMFamilyRecord;
239             GDMIndividualRecord spouseRec = fNewVal as GDMIndividualRecord;
240 
241             if (famRec == null || spouseRec == null) {
242                 return false;
243             }
244 
245             if (fType == OperationType.otFamilySpouseDetach) {
246                 redo = !redo;
247             }
248             if (redo) {
249                 famRec.AddSpouse(spouseRec);
250             } else {
251                 famRec.RemoveSpouse(spouseRec);
252             }
253             return true;
254         }
255 
ProcessGroupMember(bool redo)256         private bool ProcessGroupMember(bool redo)
257         {
258             GDMGroupRecord grpRec = fObj as GDMGroupRecord;
259             GDMIndividualRecord mbrRec = fNewVal as GDMIndividualRecord;
260 
261             if (grpRec == null || mbrRec == null) {
262                 return false;
263             }
264 
265             if (fType == OperationType.otGroupMemberDetach) {
266                 redo = !redo;
267             }
268             if (redo) {
269                 grpRec.AddMember(mbrRec);
270             } else {
271                 grpRec.RemoveMember(mbrRec);
272             }
273             return true;
274         }
275 
ProcessSourceRepositoryCitation(bool redo)276         private bool ProcessSourceRepositoryCitation(bool redo)
277         {
278             GDMSourceRecord srcRec = fObj as GDMSourceRecord;
279             GDMRepositoryRecord repRec = fNewVal as GDMRepositoryRecord;
280 
281             if (srcRec == null || repRec == null) {
282                 return false;
283             }
284 
285             if (fType == OperationType.otSourceRepositoryCitationRemove) {
286                 redo = !redo;
287             }
288             if (redo) {
289                 srcRec.AddRepository(repRec);
290             } else {
291                 srcRec.RemoveRepository(repRec);
292             }
293             return true;
294         }
295 
ProcessResearchTask(bool redo)296         private bool ProcessResearchTask(bool redo)
297         {
298             GDMResearchRecord resRec = fObj as GDMResearchRecord;
299             GDMTaskRecord taskRec = fNewVal as GDMTaskRecord;
300 
301             if (resRec == null || taskRec == null) {
302                 return false;
303             }
304 
305             if (fType == OperationType.otResearchTaskRemove) {
306                 redo = !redo;
307             }
308             if (redo) {
309                 resRec.AddTask(taskRec);
310             } else {
311                 resRec.RemoveTask(taskRec);
312             }
313             return true;
314         }
315 
ProcessResearchCommunication(bool redo)316         private bool ProcessResearchCommunication(bool redo)
317         {
318             GDMResearchRecord resRec = fObj as GDMResearchRecord;
319             GDMCommunicationRecord commRec = fNewVal as GDMCommunicationRecord;
320 
321             if (resRec == null || commRec == null) {
322                 return false;
323             }
324 
325             if (fType == OperationType.otResearchCommunicationRemove) {
326                 redo = !redo;
327             }
328             if (redo) {
329                 resRec.AddCommunication(commRec);
330             } else {
331                 resRec.RemoveCommunication(commRec);
332             }
333             return true;
334         }
335 
ProcessResearchGroup(bool redo)336         private bool ProcessResearchGroup(bool redo)
337         {
338             GDMResearchRecord resRec = fObj as GDMResearchRecord;
339             GDMGroupRecord grpRec = fNewVal as GDMGroupRecord;
340 
341             if (resRec == null || grpRec == null) {
342                 return false;
343             }
344 
345             if (fType == OperationType.otResearchGroupRemove) {
346                 redo = !redo;
347             }
348             if (redo) {
349                 resRec.AddGroup(grpRec);
350             } else {
351                 resRec.RemoveGroup(grpRec);
352             }
353             return true;
354         }
355 
ProcessRecordNoteAdd(bool redo)356         private bool ProcessRecordNoteAdd(bool redo)
357         {
358             var swl = fObj as IGDMStructWithNotes;
359             GDMNoteRecord noteRec = fNewVal as GDMNoteRecord;
360 
361             bool result = (swl != null && noteRec != null);
362             if (result) {
363                 if (redo) {
364                     GDMNotes notes = swl.AddNote(noteRec);
365                     fOldVal = notes;
366                 } else {
367                     GDMNotes notes = fOldVal as GDMNotes;
368                     swl.Notes.Delete(notes);
369                 }
370             }
371             return result;
372         }
373 
ProcessRecordNoteRemove(bool redo)374         private bool ProcessRecordNoteRemove(bool redo)
375         {
376             var swl = fObj as IGDMStructWithNotes;
377             GDMNotes notes = fNewVal as GDMNotes;
378 
379             bool result = (swl != null && notes != null);
380             if (result) {
381                 if (redo) {
382                     swl.Notes.Extract(notes); // bugfix(no delete!)
383                 } else {
384                     swl.Notes.Add(notes);
385                 }
386             }
387             return result;
388         }
389 
ProcessRecordMediaAdd(bool redo)390         private bool ProcessRecordMediaAdd(bool redo)
391         {
392             var swl = fObj as IGDMStructWithMultimediaLinks;
393             GDMMultimediaRecord mediaRec = fNewVal as GDMMultimediaRecord;
394 
395             bool result = (swl != null && mediaRec != null);
396             if (result) {
397                 if (redo) {
398                     GDMMultimediaLink mmLink = swl.AddMultimedia(mediaRec);
399                     fOldVal = mmLink;
400                 } else {
401                     GDMMultimediaLink mmLink = fOldVal as GDMMultimediaLink;
402                     swl.MultimediaLinks.Delete(mmLink);
403                 }
404             }
405             return result;
406         }
407 
ProcessRecordMediaRemove(bool redo)408         private bool ProcessRecordMediaRemove(bool redo)
409         {
410             var swl = fObj as IGDMStructWithMultimediaLinks;
411             GDMMultimediaLink mediaLink = fNewVal as GDMMultimediaLink;
412 
413             bool result = (swl != null && mediaLink != null);
414             if (result) {
415                 if (redo) {
416                     swl.MultimediaLinks.Extract(mediaLink); // bugfix(no delete!)
417                 } else {
418                     swl.MultimediaLinks.Add(mediaLink);
419                 }
420             }
421             return result;
422         }
423 
ProcessRecordSourceCit(bool redo)424         private bool ProcessRecordSourceCit(bool redo)
425         {
426             var swl = fObj as IGDMStructWithSourceCitations;
427             GDMSourceCitation sourceCit = fNewVal as GDMSourceCitation;
428 
429             if (swl == null || sourceCit == null) {
430                 return false;
431             }
432 
433             if (fType == OperationType.otRecordSourceCitRemove) {
434                 redo = !redo;
435             }
436             if (redo) {
437                 swl.SourceCitations.Add(sourceCit);
438             } else {
439                 swl.SourceCitations.Extract(sourceCit); // bugfix(no delete!)
440             }
441             return true;
442         }
443 
ProcessRecordEvent(bool redo)444         private bool ProcessRecordEvent(bool redo)
445         {
446             GDMRecordWithEvents rwe = fObj as GDMRecordWithEvents;
447             GDMCustomEvent evt = fNewVal as GDMCustomEvent;
448 
449             if (rwe == null || evt == null) {
450                 return false;
451             }
452 
453             if (fType == OperationType.otRecordEventRemove) {
454                 redo = !redo;
455             }
456             if (redo) {
457                 rwe.AddEvent(evt);
458             } else {
459                 rwe.Events.Extract(evt); // bugfix(no delete!)
460             }
461             return true;
462         }
463 
ProcessIndividualAssociation(bool redo)464         private bool ProcessIndividualAssociation(bool redo)
465         {
466             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
467             GDMAssociation asso = fNewVal as GDMAssociation;
468 
469             if (iRec == null || asso == null) {
470                 return false;
471             }
472 
473             if (fType == OperationType.otIndividualAssociationRemove) {
474                 redo = !redo;
475             }
476             if (redo) {
477                 iRec.Associations.Add(asso);
478             } else {
479                 iRec.Associations.Extract(asso);
480             }
481             return true;
482         }
483 
ProcessIndividualName(bool redo)484         private bool ProcessIndividualName(bool redo)
485         {
486             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
487             GDMPersonalName persName = fNewVal as GDMPersonalName;
488 
489             if (iRec == null || persName == null) {
490                 return false;
491             }
492 
493             if (fType == OperationType.otIndividualNameRemove) {
494                 redo = !redo;
495             }
496             if (redo) {
497                 iRec.PersonalNames.Add(persName);
498             } else {
499                 iRec.PersonalNames.Extract(persName);
500             }
501             return true;
502         }
503 
ProcessIndividualURef(bool redo)504         private bool ProcessIndividualURef(bool redo)
505         {
506             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
507             GDMUserReference uRef = fNewVal as GDMUserReference;
508 
509             if (iRec == null || uRef == null) {
510                 return false;
511             }
512 
513             if (fType == OperationType.otIndividualURefRemove) {
514                 redo = !redo;
515             }
516             if (redo) {
517                 iRec.UserReferences.Add(uRef);
518             } else {
519                 iRec.UserReferences.Extract(uRef);
520             }
521             return true;
522         }
523 
ProcessIndividualPortrait(bool redo)524         private bool ProcessIndividualPortrait(bool redo)
525         {
526             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
527             GDMMultimediaLink mmLink = fNewVal as GDMMultimediaLink;
528 
529             if (iRec == null || mmLink == null) {
530                 return false;
531             }
532 
533             if (fType == OperationType.otIndividualPortraitDetach) {
534                 redo = !redo;
535             }
536 
537             if (redo) {
538                 mmLink.IsPrimary = true;
539             } else {
540                 mmLink.IsPrimary = false;
541             }
542 
543             return true;
544         }
545 
546         /// <summary>
547         /// Processing of undo/redo operations bookmark change of personal records.
548         /// </summary>
549         /// <param name="redo"></param>
550         /// <returns></returns>
ProcessIndividualBookmarkChange(bool redo)551         private bool ProcessIndividualBookmarkChange(bool redo)
552         {
553             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
554 
555             if (iRec == null || fNewVal == null) {
556                 return false;
557             }
558 
559             if (redo) {
560                 fOldVal = iRec.Bookmark;
561                 iRec.Bookmark = (bool) fNewVal;
562             } else {
563                 iRec.Bookmark = (bool) fOldVal;
564             }
565             return true;
566         }
567 
568         /// <summary>
569         /// Processing of undo/redo operations patriarch's bookmark change of personal records.
570         /// </summary>
571         /// <param name="redo"></param>
572         /// <returns></returns>
ProcessIndividualPatriarchChange(bool redo)573         private bool ProcessIndividualPatriarchChange(bool redo)
574         {
575             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
576 
577             if (iRec == null || fNewVal == null) {
578                 return false;
579             }
580 
581             if (redo) {
582                 fOldVal = iRec.Patriarch;
583                 iRec.Patriarch = (bool) fNewVal;
584             } else {
585                 iRec.Patriarch = (bool) fOldVal;
586             }
587             return true;
588         }
589 
590         /// <summary>
591         /// Processing of undo/redo operations sex change of personal records.
592         /// </summary>
593         /// <param name="redo"></param>
594         /// <returns></returns>
ProcessIndividualSexChange(bool redo)595         private bool ProcessIndividualSexChange(bool redo)
596         {
597             GDMIndividualRecord iRec = fObj as GDMIndividualRecord;
598 
599             if (iRec == null || fNewVal == null) {
600                 return false;
601             }
602 
603             if (redo) {
604                 fOldVal = iRec.Sex;
605                 iRec.Sex = (GDMSex) fNewVal;
606             } else {
607                 iRec.Sex = (GDMSex) fOldVal;
608             }
609             return true;
610         }
611     }
612 }
613