Home | News | Projects | Releases
Bugs | RFE | Repositories | Help
frmMain: Manage windows from Window menu.
[xestiacalendar/.git] / source / forms / main / frmMain.cpp
1 // frmMain.h - frmMain form functions
2 //
3 // (c) 2016-2017 Xestia Software Development.
4 //
5 // This file is part of Xestia Calendar.
6 //
7 // Xestia Calendar is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
10 //
11 // Xestia Calendar is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Calendar. If not, see <http://www.gnu.org/licenses/>
19 #include "frmMain.h"
21 frmMain::frmMain( wxWindow* parent )
22 :
23 frmMainADT( parent )
24 {
25         
26         // Setup the default settings if they don't
27         // exist.
28         
29         // Setup the preferences.
30         wxString prefDirectory = GetUserPrefDir();
31         preferences = new XCALPreferences(prefDirectory);
32         
33         // Load the settings.
34         
35         wxString fullPrefPath;
36         
37         fullPrefPath.Append(prefDirectory);
38         fullPrefPath.Append(wxT("settings"));
40         wxFileConfig *settingfile = new wxFileConfig("", "", fullPrefPath);
41     
42         wxString ValueInc;
43         settingfile->Read(wxT("SaveWindowPosition"), &ValueInc);
44     
45         if (ValueInc == wxT("true")){
46                 
47                 wxRect windowPosition;
48         
49                 long posX, posY, posH, posW = 0;
51                 bool posXValid, posYValid, posHValid, posWValid = false;
53                 posXValid = settingfile->Read(wxT("WindowPositionX"), &posX);
54                 posYValid = settingfile->Read(wxT("WindowPositionY"), &posY);
55                 posHValid = settingfile->Read(wxT("WindowPositionHeight"), &posH);
56                 posWValid = settingfile->Read(wxT("WindowPositionWidth"), &posW);
58                 if (posXValid == true && posYValid == true && posHValid == true && posWValid == true){
60                         this->SetSize(posX, posY, posH, posW);
62                 } else {
64                         this->SetSize(-1, -1, 800, 600);
65                         
66                 }
67         
68         }
70         // Load the account data.
71         
72         LoadAccountData();
73         
74         // Setup the form control.
75         
76         mainCalendarCtrl = new XCCalendarCtrl(this, &calendarData);
77         szrMain->Add(mainCalendarCtrl, 1, wxEXPAND, 5);
78         szrMain->Layout();
79         
80         Connect(ID_PROCESSCALENDAR, XCMAIN_PROCESSCALENDAR, wxCommandEventHandler(frmMain::ProcessCalendar));
81         Connect(ID_EDITCALENDAR, XCMAIN_EDITCALENDAR, wxCommandEventHandler(frmMain::EditCalendar));
82         Connect(ID_DELETECALENDAR, XCMAIN_DELETECALENDAR, wxCommandEventHandler(frmMain::DeleteCalendar));
83         Connect(ID_DELETEEVENT, XCMAIN_DELETEEVENT, wxCommandEventHandler(frmMain::DeleteEvent));
84         Connect(ID_ADDENTRY, XCMAIN_ADDEVENT, wxCommandEventHandler(frmMain::AddEvent));
85         Connect(ID_UPDATEENTRY, XCMAIN_UPDATEEVENT, wxCommandEventHandler(frmMain::UpdateEvent));
86         Connect(ID_EDITEVENT, XCMAIN_EDITEVENT, wxCommandEventHandler(frmMain::EditEvent));
87         
88         Connect(ID_ADDWINDOW, XCMAIN_ADDWINDOWINFO, wxCommandEventHandler(frmMain::WindowAdd));
89         Connect(ID_UPDATEWINDOW, XCMAIN_UPDATEWINDOWINFO, wxCommandEventHandler(frmMain::WindowUpdate));
90         Connect(ID_DELETEWINDOW, XCMAIN_DELETEWINDOWINFO, wxCommandEventHandler(frmMain::WindowDelete));
91         
92 }
94 void frmMain::QuitApp( wxCloseEvent& event )
95 {
97         // Run the QuitApp function.
99         QuitApp();
103 void frmMain::QuitApp( wxCommandEvent& event )
105     
106         // Run the QuitApp function.
107     
108         QuitApp();
109     
112 void frmMain::QuitApp()
115         // Function to run when quitting.
117         //Go through the windows and close each one (be it search
118         //or contact editor). Abort if user wants to cancel.
120         // Close the contact editor windows.
122         // Close the contact windows.
124         // Close the search windows.
126         // Write out the ETag databases.
128         // Save Preferences: Save the window position if that option is enabled.
130         wxString SetFilename = GetUserPrefDir();
132 #if defined(__HAIKU__)
136 #elif defined(__WIN32__)
138         SetFilename.Append(wxT("settings"));
140 #else
142         // *nix OSes
144         SetFilename.Append(wxT("settings"));
146 #endif
148         wxFileConfig *cfgfile = new wxFileConfig("", "", SetFilename);
150         bool SaveWindowPos = false;
151         wxString SaveWindowInc;
152         cfgfile->Read(wxT("SaveWindowPosition"), &SaveWindowInc);
154         if (SaveWindowInc == wxT("true")){
155         
156                 SaveWindowPos = true;
157         
158         }
160         if (SaveWindowPos == true){
161         
162                 wxRect frmMainPos = this->GetRect();
163         
164                 cfgfile->Write(wxT("WindowPositionX"), frmMainPos.GetX());
165                 cfgfile->Write(wxT("WindowPositionY"), frmMainPos.GetY());
166                 cfgfile->Write(wxT("WindowPositionHeight"), frmMainPos.GetHeight());
167                 cfgfile->Write(wxT("WindowPositionWidth"), frmMainPos.GetWidth());
168         
169         }
171         delete cfgfile;
172         cfgfile = nullptr;
174         //Everything closed... exit.
176         std::exit(0);
178         Close();
182 void frmMain::LoadAccountData(){
183         
184         // Get the list of accounts and put into the calendar data storage.
185         
186         int accountCount = preferences->accounts.GetCount();
187         
188         for (int accountSeek = 0; accountSeek < accountCount; accountSeek++){
189                 
190                 CDSAccountResult addResult = calendarData.AddAccount(string(preferences->accounts.GetAccountName(accountSeek).mb_str()), accountSeek);
191                 
192         }
193         
194         // Get the list of calendars and put them into the calendar data storage.
195         
196         for (int accountSeek = 0; accountSeek < accountCount; accountSeek++){
197         
198                 CDSGetAccountInfo accountInfo = calendarData.GetAccount(string(preferences->accounts.GetAccountName(accountSeek).mb_str()));
199         
200                 // Build the path.
201                 
202                 string calendarListFilename = string(GetUserDir().mb_str());
203                 calendarListFilename += "accounts/";
204                 calendarListFilename += string(preferences->accounts.GetAccountDirectory(accountSeek).mb_str());
205                 calendarListFilename += ".";
206                 calendarListFilename += string(preferences->accounts.GetAccountType(accountSeek).mb_str());
207                 
208                 // Get the list of calendars.
209                 
210                 XCAccountCalendarList calendarList(calendarListFilename);
211                 
212                 // Add the calendar and set the calendar ID for it.
213                 
214                 for (int calendarSeek = 0; calendarSeek < calendarList.calendarShortName.size(); calendarSeek++){
215                         
216                         // Add the calendar.
217                         
218                         CDSCalendarResult calendarAddResult = calendarData.AddCalendar(accountInfo.accountID, 
219                                 calendarList.calendarName[calendarSeek], 
220                                 calendarList.calendarShortName[calendarSeek],
221                                 calendarList.calendarColour[calendarSeek],
222                                 calendarList.calendarDescription[calendarSeek]);
223                         
224                         // Set the calendar ID.
225                         
226                         CDSGetCalendarInfo calendarInfo = calendarData.GetCalendar(string(preferences->accounts.GetAccountName(accountSeek).mb_str()), calendarList.calendarShortName[calendarSeek]);
227                         calendarList.calendarStorageID[calendarSeek] = calendarInfo.calendarID;
228                         
229                         // Find the entries and load each entry.
230                         
231                         string calendarListDirectory = calendarListFilename;
232                         calendarListDirectory += "/";
233                         calendarListDirectory += calendarList.calendarShortName[calendarSeek];
234                         calendarListDirectory += "/";
235                         
236                         wxDir entryListDirectory(calendarListDirectory);
237                         wxString entryListFilename;
238                         
239                         bool continueProcessing = entryListDirectory.GetFirst(&entryListFilename, "*.ics", wxDIR_NO_FOLLOW|wxDIR_FILES);
240                         
241                         while (continueProcessing){
242                                 
243                                 string entryListFullFilename;
244                                 entryListFullFilename += calendarListDirectory;
245                                 entryListFullFilename += string(entryListFilename.mb_str());
246                                 
247                                 continueProcessing = entryListDirectory.GetNext(&entryListFilename);
248                                 CDSAddEntryResult addEventResult = calendarData.AddEvent(calendarInfo.calendarID, entryListFullFilename);
249                                 
250                         }                       
251                         
252                 }
253                 
254         }
255         
258 void frmMain::ShowPreferencesWindow( wxCommandEvent& event )
261         // Open the preferences window.
262     
263         reloadAccounts = FALSE;
264     
265         frmPreferences *framePreferences = new frmPreferences ( this );
266         framePreferences->SetupPointers(&reloadAccounts);
267         framePreferences->ShowModal();
268         delete framePreferences;
269         framePreferences = NULL;
270     
271         // Reload the preferences.
272         
273         if (reloadAccounts == true){
274             
275                 // Reload the accounts as a change has been made within
276                 // the application.
277         
278                 wxString prefDirectory = GetUserPrefDir();
279                 XCALPreferences *oldPreferences = preferences;
280                 preferences = new XCALPreferences(prefDirectory);
281                 
282                 delete oldPreferences;
283                 oldPreferences = nullptr;
284                 
285                 // Clear all of the data from the calendar data storage.
286                 
287                 calendarData.Clear();
288                 LoadAccountData();
289                 
290                 // Politely ask the calendar control to reload it's view.
292                 wxCommandEvent updateGrid(XCCALENDARCTRL_CHANGEGRID);
293                 updateGrid.SetId(ID_CHANGEGRID);
294                 wxPostEvent(mainCalendarCtrl, updateGrid);
295                 
296         }
297     
300 void frmMain::ShowAboutWindow( wxCommandEvent& event )
303         // Show the about window.
304     
305         frmAbout *frameAbout = new frmAbout ( this );
306         frameAbout->SetupAboutWindow();
307         frameAbout->ShowModal();
308         delete frameAbout;
309         frameAbout = NULL;
310     
313 void frmMain::ShowUpdateWindow( wxCommandEvent& event )
316         frmUpdate *frameUpdate = new frmUpdate ( this );
317         frameUpdate->FetchData();
318         frameUpdate->ShowModal();
319         delete frameUpdate;
320         frameUpdate = NULL;
321         
324 void frmMain::OpenNewAccountDialog( wxCommandEvent& event )
326         
327         // Open the new account dialog.
328     
329         reloadAccounts = false;
330     
331         frmNewAccount *frameNewAccount = new frmNewAccount ( this );
332         frameNewAccount->SetupPointers(&reloadAccounts, &calendarData);
333         frameNewAccount->ShowModal();
334         delete frameNewAccount;
335         frameNewAccount = NULL;
336         
337         // Reload the preferences.
338         
339         if (reloadAccounts == true){
340             
341                 // Reload the accounts as a change has been made within
342                 // the application.
343         
344                 wxString prefDirectory = GetUserPrefDir();
345                 XCALPreferences *oldPreferences = preferences;
346                 preferences = new XCALPreferences(prefDirectory);
347                 
348                 delete oldPreferences;
349                 oldPreferences = nullptr;
350                 
351                 // Clear all of the data from the calendar data storage.
352                 
353                 calendarData.Clear();
354                 LoadAccountData();
355                 
356                 // Politely ask the calendar control to reload it's view.
358                 wxCommandEvent updateGrid(XCCALENDARCTRL_CHANGEGRID);
359                 updateGrid.SetId(ID_CHANGEGRID);
360                 wxPostEvent(mainCalendarCtrl, updateGrid);
361                 
362         }
363         
366 void frmMain::CreateNewCalendar( wxCommandEvent& event )
368         
369         frmCalendarEditor *frameNewCalendar = new frmCalendarEditor ( this );
370         frameNewCalendar->SetMode(false);
371         frameNewCalendar->SetupAccounts(preferences);
372         frameNewCalendar->ShowModal();
373         delete frameNewCalendar;
374         frameNewCalendar = nullptr;
375         
378 void frmMain::EditCalendar( wxCommandEvent& event )
380         
381         // Get the calendar data.
382         
383         CDSGetCalendarInfo calendarInfo = calendarData.GetCalendar(event.GetInt());
384         
385         frmCalendarEditor *frameNewCalendar = new frmCalendarEditor ( this );
386         frameNewCalendar->SetMode(true);
387         frameNewCalendar->SetupAccounts(preferences);
388         frameNewCalendar->SetData(event.GetInt(), calendarInfo.accountName, calendarInfo.calendarName, calendarInfo.calendarDescription, calendarInfo.calendarColour);
389         frameNewCalendar->ShowModal();
390         delete frameNewCalendar;
391         frameNewCalendar = nullptr;
392         
395 void frmMain::DeleteCalendar( wxCommandEvent& event )
398         CalendarProperties *calendarEventInfo = (CalendarProperties*)event.GetClientData();
399         
400         // Get the calendar data.
401         
402         CDSGetCalendarInfo calendarInfo = calendarData.GetCalendar(calendarEventInfo->calendarID);
403         
404         if (wxMessageBox(wxString::Format("Are you sure you want to delete the calendar %s from the %s account?", calendarInfo.calendarName, calendarInfo.accountName), "Delete calendar", wxYES_NO|wxICON_QUESTION) == wxNO){
405                 return;
406         }
407         
408         // Go through the grid and delete each calendar entry widget that 
409         // is associated with the calendar.
410         
411         wxCommandEvent deleteCalendar(XCCALENDARCTRL_DELETECALENDARENTRIES);
412         deleteCalendar.SetId(ID_DELETECALENDARENTRIES);
413         deleteCalendar.SetInt(calendarInfo.calendarID);
414         wxPostEvent(mainCalendarCtrl, deleteCalendar);
415         
416         // Get the account configuration file and delete the calendar information.
417         
418         CDSGetAccountInfo accountInfo = calendarData.GetAccount(calendarInfo.accountName);
419         
420         string accountDirectoryPath = string(GetUserDir().mb_str());    
421         accountDirectoryPath += "accounts/";
422         accountDirectoryPath += string(preferences->accounts.GetAccountDirectory(calendarEventInfo->accountPreferencesID).mb_str());
423         accountDirectoryPath += ".";
424         accountDirectoryPath += string(preferences->accounts.GetAccountType(calendarEventInfo->accountPreferencesID).mb_str());
425         accountDirectoryPath += "/";
426         
427         string calendarListFilenameFull = accountDirectoryPath;
428         calendarListFilenameFull += "calendarlist.db";
429         
430         string calendarDirectoryPath = accountDirectoryPath;
431         calendarDirectoryPath += calendarInfo.calendarTextID;
432         
433         wxFileConfig *calendarListFile = new wxFileConfig("", "", wxString(calendarListFilenameFull));
434         
435         //calendarListFile->SetPath(wxString(calendarInfo.calendarTextID));
436         calendarListFile->DeleteGroup(wxString(calendarInfo.calendarTextID));
437         
438         // Delete the calendar directory.
440         wxDir entryListDirectory((wxString)calendarDirectoryPath.c_str());
441         wxString entryListFilename;
442         
443         bool continueProcessing = entryListDirectory.GetFirst(&entryListFilename, "*", wxDIR_NO_FOLLOW|wxDIR_FILES);
444                         
445         while (continueProcessing){
446                                 
447                 string entryListFullFilename;
448                 entryListFullFilename += calendarDirectoryPath;
449                 entryListFullFilename += "/";
450                 entryListFullFilename += string(entryListFilename.mb_str());
451                 
452                 wxRemoveFile(wxString(entryListFullFilename.c_str()));
453                 
454                 continueProcessing = entryListDirectory.GetNext(&entryListFilename);
455                                 
456         }
458         wxRmdir(calendarDirectoryPath);
459         
460         // Delete the calendar from the calendar data storage.
461         
462         calendarData.DeleteCalendar(calendarEventInfo->calendarID);
463         
464         delete calendarListFile;
465         calendarListFile = nullptr;
466         
467         delete calendarEventInfo;
468         calendarEventInfo = nullptr;
469         
472 void frmMain::CreateNewEvent( wxCommandEvent& event ){
473         
474         frmEventEditor *frameNewEvent = new frmEventEditor ( this );
475         frameNewEvent->SetupForm(&calendarData, preferences);
476         frameNewEvent->SetWindowMenuItemID(++WindowMenuItemID);
477         
478         // Add the window to the window list.
479         
480         WindowData *newWindowData = new WindowData;
481     
482         newWindowData->DataType = 1;
483         newWindowData->WindowPointer = (void*)frameNewEvent;
484         newWindowData->WindowID = WindowMenuItemID;
485         
486         wxCommandEvent addevent(XCMAIN_ADDWINDOWINFO);
487         addevent.SetId(ID_ADDWINDOW);
488         addevent.SetClientData(newWindowData);
489         wxPostEvent(this, addevent);
490         
491         frameNewEvent->Show();
492         
495 void frmMain::EditEvent( wxCommandEvent& event ){
496         
497         frmEventEditor *frameEditEvent = new frmEventEditor ( this );
498         frameEditEvent->SetEventID(event.GetInt());
499         frameEditEvent->SetEditMode(true);
500         frameEditEvent->SetWindowMenuItemID(++WindowMenuItemID);
501         frameEditEvent->SetupForm(&calendarData, preferences);
502         
503         // Add the window to the window list.
504         
505         WindowData *newWindowData = new WindowData;
506     
507         newWindowData->DataType = 1;
508         newWindowData->WindowPointer = (void*)frameEditEvent;
509         newWindowData->WindowID = WindowMenuItemID;
510         
511         wxCommandEvent addevent(XCMAIN_ADDWINDOWINFO);
512         addevent.SetId(ID_ADDWINDOW);
513         addevent.SetClientData(newWindowData);
514         wxPostEvent(this, addevent);
516         // Setup the form and display it.
517         
518         frameEditEvent->Show();
519         
522 void frmMain::DeleteEvent( wxCommandEvent& event ){
523         
524         EventProperties *eventInfo = (EventProperties*)event.GetClientData();
525         
526         // Get the event information.
527         
528         CDSGetCalendarEntryInfo eventDeleteData = calendarData.GetEvent(eventInfo->eventID);
529         CDSGetCalendarInfo calendarDeleteData = calendarData.GetCalendar(eventInfo->calendarID);
530         
531         if (wxMessageBox(wxString::Format("Are you sure you want to delete the event %s from the %s calendar?", eventDeleteData.entryName, calendarDeleteData.calendarName), "Delete event", wxYES_NO|wxICON_QUESTION) == wxNO){
532                 return;
533         }
534         
535         // Go through the grid and delete the entry from the GUI.
536         
537         wxCommandEvent deleteEvent(XCCALENDARCTRL_DELETEENTRY);
538         deleteEvent.SetId(ID_DELETEENTRY);
539         deleteEvent.SetInt(eventInfo->eventID);
540         wxPostEvent(mainCalendarCtrl, deleteEvent);
541         
542         // Get the filename and delete the entry.
543         
544         wxRemoveFile(wxString(eventDeleteData.entryFilename.c_str()));
545         
546         // Delete the entry from the calendar data storage.
547         
548         calendarData.DeleteEvent(eventInfo->eventID);
549         
550         delete eventInfo;
551         eventInfo = nullptr;
552         
555 void frmMain::AddEvent( wxCommandEvent& event )
557         
558         EventProperties *eventInfo = (EventProperties*)event.GetClientData();
559         
560         wxCommandEvent addEvent(XCCALENDARCTRL_ADDENTRY);
561         addEvent.SetId(ID_ADDENTRY);
562         addEvent.SetClientData(eventInfo);
563         wxPostEvent(mainCalendarCtrl, addEvent);
564         
567 void frmMain::UpdateEvent( wxCommandEvent& event )
569         
570         EventProperties *eventInfo = (EventProperties*)event.GetClientData();
571         
572         wxCommandEvent updateEvent(XCCALENDARCTRL_UPDATEENTRY);
573         updateEvent.SetId(ID_UPDATEENTRY);
574         updateEvent.SetClientData(eventInfo);
575         wxPostEvent(mainCalendarCtrl, updateEvent);
576         
579 void frmMain::ProcessCalendar( wxCommandEvent& event )
581         
582         CalendarProperties *calendarInfo = (CalendarProperties*)event.GetClientData();
583         
584         // Get the account name.
585         
586         CDSGetAccountInfo accountInfo = calendarData.GetAccount(calendarInfo->accountName);
587         
588         // Build the account directory path.
589         
590         string accountDirectoryPath = string(GetUserDir().mb_str());
591         accountDirectoryPath += "accounts/";
592         accountDirectoryPath += string(preferences->accounts.GetAccountDirectory(calendarInfo->accountPreferencesID).mb_str());
593         accountDirectoryPath += ".";
594         accountDirectoryPath += string(preferences->accounts.GetAccountType(calendarInfo->accountPreferencesID).mb_str());
595         accountDirectoryPath += "/";
596         
597         // Generate a UUID for the new calendar.
598         
599         string calendarPath = accountDirectoryPath;
601         string calendarUUID = "";
603         if (calendarInfo->editMode == false){
605                 calendarUUID = GenerateUUID();
606                 calendarPath += calendarUUID;
607                 
608         } else {
609         
610                 calendarUUID = calendarData.GetCalendar(calendarInfo->calendarID).calendarTextID;
611                 calendarPath += calendarPath;
612                 
613         }
614         
615         calendarPath += "/";
616         
617         // Open the account's calendar file and add to the list of calendars.
618         
619         string calendarListFilenameFull = accountDirectoryPath;
620         calendarListFilenameFull += "calendarlist.db";
621         
622         wxFileConfig *calendarListFile = new wxFileConfig("", "", wxString(calendarListFilenameFull));
623         
624         wxString calendarDescription = wxString(calendarInfo->calendarDescription);
625         
626         calendarDescription.Replace("\n", "\\n", true);
627         
628         // Create the directory for the calendar entries if 
629         // not editing a calendar.
630         
631         calendarListFile->SetPath(wxString(calendarUUID));
632         calendarListFile->Write(wxT("name"), wxString(calendarInfo->calendarName));
633         calendarListFile->Write(wxT("description"), calendarDescription);
634         calendarListFile->Write(wxT("colour"), wxString(calendarInfo->calendarColour));
635         
636         if (calendarInfo->editMode == false){
637         
638                 wxMkDir(wxString(calendarPath), wxS_DIR_DEFAULT);
639                 calendarData.AddCalendar(accountInfo.accountID, calendarInfo->calendarName, calendarUUID, calendarInfo->calendarColour, calendarInfo->calendarDescription);
640                 
641         } else {
642                 
643                 calendarData.UpdateCalendar(calendarInfo->calendarID, calendarInfo->calendarName, calendarInfo->calendarColour, calendarInfo->calendarDescription);
644                 
645                 // Post event to update the colour in the calendar entries.
646                 
647                 updateColourData.calendarID = calendarInfo->calendarID;
648                 updateColourData.newColour.red = calendarInfo->calendarColour.red;
649                 updateColourData.newColour.green = calendarInfo->calendarColour.green;
650                 updateColourData.newColour.blue = calendarInfo->calendarColour.blue;
651                 updateColourData.newColour.alpha = calendarInfo->calendarColour.alpha;
652                 
653                 wxCommandEvent updateColour(XCCALENDARCTRL_UPDATECALENDARCOLOUR);
654                 updateColour.SetClientData(&updateColourData);
655                 updateColour.SetId(ID_UPDATECOLOUR);
656                 wxPostEvent(mainCalendarCtrl, updateColour);
657                 
658         }
659         
660         // Add the calendar to the calendar data storage.
661         
662         delete calendarListFile;
663         calendarListFile = nullptr;
664         
665         delete calendarInfo;
666         calendarInfo = nullptr;
667         
670 void frmMain::ShowHelp( wxCommandEvent& event )
672         
673         wxMessageBox(_("The help documentation will be implemented in a future version of Xestia Calendar."), _("Unimplemented"));
674         
Xestia Software Development
Yn Maystri
© 2006 - 2019 Xestia Software Development
Software

Xestia Address Book
Xestia Calendar
Development

Xestia Gelforn
Everything else

About
News
Privacy Policy