2 * Copyright 2011-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Hamish Morrison, hamish@lavabit.com
8 * John Scipione, jscipione@gmail.com
12 #include "NetworkTimeView.h"
23 #include <FindDirectory.h>
28 #include <ScrollView.h>
30 #include <TextControl.h>
33 #include "TimeMessages.h"
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "Time"
40 // #pragma mark - Settings
45 fMessage(kMsgNetworkTimeSettings
)
59 Settings::AddServer(const char* server
)
61 if (_GetStringByValue("server", server
) == B_ERROR
)
62 fMessage
.AddString("server", server
);
67 Settings::GetServer(int32 index
) const
70 fMessage
.FindString("server", index
, &server
);
76 Settings::RemoveServer(const char* server
)
78 int32 index
= _GetStringByValue("server", server
);
79 if (index
!= B_ERROR
) {
80 fMessage
.RemoveData("server", index
);
83 fMessage
.GetInfo("server", NULL
, &count
);
84 if (GetDefaultServer() >= count
)
85 SetDefaultServer(count
- 1);
91 Settings::SetDefaultServer(int32 index
)
93 if (fMessage
.ReplaceInt32("default server", index
) != B_OK
)
94 fMessage
.AddInt32("default server", index
);
99 Settings::GetDefaultServer() const
102 fMessage
.FindInt32("default server", &index
);
108 Settings::SetTryAllServers(bool boolean
)
110 fMessage
.ReplaceBool("try all servers", boolean
);
115 Settings::GetTryAllServers() const
118 fMessage
.FindBool("try all servers", &boolean
);
124 Settings::SetSynchronizeAtBoot(bool boolean
)
126 fMessage
.ReplaceBool("synchronize at boot", boolean
);
131 Settings::GetSynchronizeAtBoot() const
134 fMessage
.FindBool("synchronize at boot", &boolean
);
140 Settings::ResetServersToDefaults()
142 fMessage
.RemoveName("server");
144 fMessage
.AddString("server", "pool.ntp.org");
145 fMessage
.AddString("server", "de.pool.ntp.org");
146 fMessage
.AddString("server", "time.nist.gov");
148 if (fMessage
.ReplaceInt32("default server", 0) != B_OK
)
149 fMessage
.AddInt32("default server", 0);
154 Settings::ResetToDefaults()
156 fMessage
.MakeEmpty();
157 ResetServersToDefaults();
159 fMessage
.AddBool("synchronize at boot", true);
160 fMessage
.AddBool("try all servers", true);
167 fMessage
= fOldMessage
;
172 Settings::SettingsChanged()
174 ssize_t oldSize
= fOldMessage
.FlattenedSize();
175 ssize_t newSize
= fMessage
.FlattenedSize();
177 if (oldSize
!= newSize
|| oldSize
< 0 || newSize
< 0)
180 char* oldBytes
= new (std::nothrow
) char[oldSize
];
181 if (oldBytes
== NULL
)
184 fOldMessage
.Flatten(oldBytes
, oldSize
);
185 char* newBytes
= new (std::nothrow
) char[newSize
];
186 if (newBytes
== NULL
) {
190 fMessage
.Flatten(newBytes
, newSize
);
192 int result
= memcmp(oldBytes
, newBytes
, oldSize
);
207 if ((status
= _GetPath(path
)) != B_OK
)
210 BFile
file(path
.Path(), B_READ_ONLY
);
211 if ((status
= file
.InitCheck()) != B_OK
)
215 if ((status
= load
.Unflatten(&file
)) != B_OK
)
218 if (load
.what
!= kMsgNetworkTimeSettings
)
222 fOldMessage
= fMessage
;
233 if ((status
= _GetPath(path
)) != B_OK
)
236 BFile
file(path
.Path(), B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
);
237 if ((status
= file
.InitCheck()) != B_OK
)
242 return fMessage
.Flatten(&file
);
247 Settings::_GetStringByValue(const char* name
, const char* value
)
250 for (int32 index
= 0; fMessage
.FindString(name
, index
, &string
) == B_OK
;
252 if (strcmp(string
, value
) == 0)
261 Settings::_GetPath(BPath
& path
)
263 status_t status
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
267 path
.Append("networktime settings");
273 // #pragma mark - NetworkTimeView
276 NetworkTimeView::NetworkTimeView(const char* name
)
278 BGroupView(name
, B_VERTICAL
, B_USE_DEFAULT_SPACING
),
280 fServerTextControl(NULL
),
284 fServerListView(NULL
),
285 fTryAllServersCheckBox(NULL
),
286 fSynchronizeAtBootCheckBox(NULL
),
287 fSynchronizeButton(NULL
),
288 fTextColor(ui_color(B_CONTROL_TEXT_COLOR
)),
289 fInvalidColor(ui_color(B_FAILURE_COLOR
)),
297 NetworkTimeView::~NetworkTimeView()
299 delete fServerTextControl
;
301 delete fRemoveButton
;
303 delete fServerListView
;
304 delete fTryAllServersCheckBox
;
305 delete fSynchronizeAtBootCheckBox
;
306 delete fSynchronizeButton
;
311 NetworkTimeView::MessageReceived(BMessage
* message
)
313 switch (message
->what
) {
314 case kMsgSetDefaultServer
:
316 int32 currentSelection
= fServerListView
->CurrentSelection();
317 if (currentSelection
< 0)
318 fServerListView
->Select(fSettings
.GetDefaultServer());
320 fSettings
.SetDefaultServer(currentSelection
);
321 Looper()->PostMessage(new BMessage(kMsgChange
));
326 case kMsgServerEdited
:
328 bool isValid
= _IsValidServerName(fServerTextControl
->Text());
329 fServerTextControl
->TextView()->SetFontAndColor(0,
330 fServerTextControl
->TextView()->TextLength(), NULL
, 0,
331 isValid
? &fTextColor
: &fInvalidColor
);
332 fAddButton
->SetEnabled(isValid
);
337 if (!_IsValidServerName(fServerTextControl
->Text()))
340 fSettings
.AddServer(fServerTextControl
->Text());
342 fServerTextControl
->SetText("");
343 Looper()->PostMessage(new BMessage(kMsgChange
));
346 case kMsgRemoveServer
:
348 int32 currentSelection
= fServerListView
->CurrentSelection();
349 if (currentSelection
< 0)
352 fSettings
.RemoveServer(((BStringItem
*)
353 fServerListView
->ItemAt(currentSelection
))->Text());
355 Looper()->PostMessage(new BMessage(kMsgChange
));
359 case kMsgResetServerList
:
360 fSettings
.ResetServersToDefaults();
362 Looper()->PostMessage(new BMessage(kMsgChange
));
365 case kMsgTryAllServers
:
366 fSettings
.SetTryAllServers(
367 fTryAllServersCheckBox
->Value());
368 Looper()->PostMessage(new BMessage(kMsgChange
));
371 case kMsgSynchronizeAtBoot
:
372 fSettings
.SetSynchronizeAtBoot(fSynchronizeAtBootCheckBox
->Value());
373 Looper()->PostMessage(new BMessage(kMsgChange
));
376 case kMsgStopSynchronization
:
377 if (fUpdateThread
>= B_OK
)
378 kill_thread(fUpdateThread
);
380 _DoneSynchronizing();
383 case kMsgSynchronize
:
385 if (fUpdateThread
>= B_OK
)
388 BMessenger
* messenger
= new BMessenger(this);
389 update_time(fSettings
, messenger
, &fUpdateThread
);
390 fSynchronizeButton
->SetLabel(B_TRANSLATE("Stop"));
391 fSynchronizeButton
->Message()->what
= kMsgStopSynchronization
;
395 case kMsgSynchronizationResult
:
397 _DoneSynchronizing();
400 if (message
->FindInt32("status", (int32
*)&status
) == B_OK
) {
404 const char* errorString
;
405 message
->FindString("error string", &errorString
);
409 if (message
->FindInt32("error code", &errorCode
) == B_OK
) {
410 snprintf(buffer
, sizeof(buffer
),
411 B_TRANSLATE("The following error occured "
412 "while synchronizing:\r\n%s: %s"),
413 errorString
, strerror(errorCode
));
415 snprintf(buffer
, sizeof(buffer
),
416 B_TRANSLATE("The following error occured "
417 "while synchronizing:\r\n%s"),
421 BAlert
* alert
= new BAlert(B_TRANSLATE("Time"), buffer
,
423 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
431 fTryAllServersCheckBox
->SetValue(fSettings
.GetTryAllServers());
432 fSynchronizeAtBootCheckBox
->SetValue(
433 fSettings
.GetSynchronizeAtBoot());
441 NetworkTimeView::AttachedToWindow()
443 fServerTextControl
->SetTarget(this);
444 fServerListView
->SetTarget(this);
445 fAddButton
->SetTarget(this);
446 fAddButton
->SetEnabled(false);
447 fRemoveButton
->SetTarget(this);
448 fResetButton
->SetTarget(this);
449 fTryAllServersCheckBox
->SetTarget(this);
450 fSynchronizeAtBootCheckBox
->SetTarget(this);
451 fSynchronizeButton
->SetTarget(this);
456 NetworkTimeView::CheckCanRevert()
458 return fSettings
.SettingsChanged();
463 NetworkTimeView::_InitView()
465 fServerTextControl
= new BTextControl(NULL
, NULL
,
466 new BMessage(kMsgAddServer
));
467 fServerTextControl
->SetModificationMessage(new BMessage(kMsgServerEdited
));
469 const float kButtonWidth
= fServerTextControl
->Frame().Height();
471 fAddButton
= new BButton("add", "+", new BMessage(kMsgAddServer
));
472 fAddButton
->SetToolTip(B_TRANSLATE("Add"));
473 fAddButton
->SetExplicitSize(BSize(kButtonWidth
, kButtonWidth
));
475 fRemoveButton
= new BButton("remove", "−", new BMessage(kMsgRemoveServer
));
476 fRemoveButton
->SetToolTip(B_TRANSLATE("Remove"));
477 fRemoveButton
->SetExplicitSize(BSize(kButtonWidth
, kButtonWidth
));
479 fServerListView
= new BListView("serverList");
480 fServerListView
->SetExplicitMinSize(BSize(B_SIZE_UNSET
, kButtonWidth
* 4));
481 fServerListView
->SetSelectionMessage(new BMessage(kMsgSetDefaultServer
));
482 BScrollView
* scrollView
= new BScrollView("serverScrollView",
483 fServerListView
, B_FRAME_EVENTS
| B_WILL_DRAW
, false, true);
486 fTryAllServersCheckBox
= new BCheckBox("tryAllServers",
487 B_TRANSLATE("Try all servers"), new BMessage(kMsgTryAllServers
));
488 fTryAllServersCheckBox
->SetValue(fSettings
.GetTryAllServers());
490 fSynchronizeAtBootCheckBox
= new BCheckBox("autoUpdate",
491 B_TRANSLATE("Synchronize at boot"),
492 new BMessage(kMsgSynchronizeAtBoot
));
493 fSynchronizeAtBootCheckBox
->SetValue(fSettings
.GetSynchronizeAtBoot());
495 fResetButton
= new BButton("reset",
496 B_TRANSLATE("Reset to default server list"),
497 new BMessage(kMsgResetServerList
));
499 fSynchronizeButton
= new BButton("update", B_TRANSLATE("Synchronize"),
500 new BMessage(kMsgSynchronize
));
502 BLayoutBuilder::Group
<>(this, B_VERTICAL
)
503 .AddGroup(B_VERTICAL
, B_USE_SMALL_SPACING
)
504 .AddGroup(B_HORIZONTAL
, B_USE_SMALL_SPACING
)
505 .Add(fServerTextControl
)
508 .AddGroup(B_HORIZONTAL
, B_USE_SMALL_SPACING
)
510 .AddGroup(B_VERTICAL
, B_USE_SMALL_SPACING
)
516 .AddGroup(B_HORIZONTAL
)
517 .AddGroup(B_VERTICAL
, 0)
518 .Add(fTryAllServersCheckBox
)
519 .Add(fSynchronizeAtBootCheckBox
)
522 .AddGroup(B_HORIZONTAL
)
525 .Add(fSynchronizeButton
)
527 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_WINDOW_SPACING
,
528 B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
);
533 NetworkTimeView::_UpdateServerList()
536 while ((item
= fServerListView
->RemoveItem((int32
)0)) != NULL
)
541 while ((server
= fSettings
.GetServer(index
++)) != NULL
)
542 fServerListView
->AddItem(new BStringItem(server
));
544 fServerListView
->Select(fSettings
.GetDefaultServer());
545 fServerListView
->ScrollToSelection();
547 fRemoveButton
->SetEnabled(fServerListView
->CountItems() > 0);
552 NetworkTimeView::_DoneSynchronizing()
555 fSynchronizeButton
->SetLabel(B_TRANSLATE("Synchronize again"));
556 fSynchronizeButton
->Message()->what
= kMsgSynchronize
;
561 NetworkTimeView::_IsValidServerName(const char* serverName
)
563 if (serverName
== NULL
|| *serverName
== '\0')
566 for (int32 i
= 0; serverName
[i
] != '\0'; i
++) {
567 char c
= serverName
[i
];
568 // Simple URL validation, no scheme should be present
569 if (!(isalnum(c
) || c
== '.' || c
== '-' || c
== '_'))
577 // #pragma mark - update functions
581 update_thread(void* params
)
583 BList
* list
= (BList
*)params
;
584 BMessenger
* messenger
= (BMessenger
*)list
->ItemAt(1);
586 const char* errorString
= NULL
;
588 status_t status
= update_time(*(Settings
*)list
->ItemAt(0),
589 &errorString
, &errorCode
);
591 BMessage
result(kMsgSynchronizationResult
);
592 result
.AddInt32("status", status
);
593 result
.AddString("error string", errorString
);
595 result
.AddInt32("error code", errorCode
);
597 messenger
->SendMessage(&result
);
605 update_time(const Settings
& settings
, BMessenger
* messenger
,
608 BList
* params
= new BList(2);
609 params
->AddItem((void*)&settings
);
610 params
->AddItem((void*)messenger
);
611 *thread
= spawn_thread(update_thread
, "ntpUpdate", 64, params
);
613 return resume_thread(*thread
);
618 update_time(const Settings
& settings
, const char** errorString
,
621 int32 defaultServer
= settings
.GetDefaultServer();
623 status_t status
= B_ENTRY_NOT_FOUND
;
624 const char* server
= settings
.GetServer(defaultServer
);
627 status
= ntp_update_time(server
, errorString
, errorCode
);
629 if (status
!= B_OK
&& settings
.GetTryAllServers()) {
630 for (int32 index
= 0; ; index
++) {
631 if (index
== defaultServer
)
634 server
= settings
.GetServer(index
);
638 status
= ntp_update_time(server
, errorString
, errorCode
);