15adca30aSAxel Dörfler /*
25adca30aSAxel Dörfler * Copyright 2005, Waldemar Kornewald <wkornew@gmx.net>
35adca30aSAxel Dörfler * Distributed under the terms of the MIT License.
45adca30aSAxel Dörfler */
55adca30aSAxel Dörfler
65adca30aSAxel Dörfler #include "ConnectionView.h"
75adca30aSAxel Dörfler #include "PPPDeskbarReplicant.h"
85adca30aSAxel Dörfler #include <MessageDriverSettingsUtils.h>
95adca30aSAxel Dörfler
105adca30aSAxel Dörfler #include <Application.h>
115adca30aSAxel Dörfler #include <Box.h>
125adca30aSAxel Dörfler #include <Button.h>
135adca30aSAxel Dörfler #include <Deskbar.h>
145adca30aSAxel Dörfler #include <Entry.h>
155adca30aSAxel Dörfler #include <File.h>
165adca30aSAxel Dörfler #include <String.h>
175adca30aSAxel Dörfler #include <StringView.h>
185adca30aSAxel Dörfler #include <TextControl.h>
195adca30aSAxel Dörfler #include <Window.h>
205adca30aSAxel Dörfler
215adca30aSAxel Dörfler #include <PPPInterface.h>
225adca30aSAxel Dörfler #include <settings_tools.h>
235adca30aSAxel Dörfler #include <algorithm>
245adca30aSAxel Dörfler // for max()
255adca30aSAxel Dörfler
265adca30aSAxel Dörfler using std::max;
275adca30aSAxel Dörfler
285adca30aSAxel Dörfler // GUI constants
295adca30aSAxel Dörfler static const uint32 kDefaultButtonWidth = 80;
305adca30aSAxel Dörfler
315adca30aSAxel Dörfler // message constants
325adca30aSAxel Dörfler static const uint32 kMsgCancel = 'CANC';
335adca30aSAxel Dörfler static const uint32 kMsgConnect = 'CONN';
345adca30aSAxel Dörfler static const uint32 kMsgUpdate = 'MUPD';
355adca30aSAxel Dörfler
365adca30aSAxel Dörfler // labels
379563f44bSStephan Aßmus static const char *kLabelSavePassword = "Save password";
385adca30aSAxel Dörfler static const char *kLabelName = "Username: ";
395adca30aSAxel Dörfler static const char *kLabelPassword = "Password: ";
405adca30aSAxel Dörfler static const char *kLabelConnect = "Connect";
415adca30aSAxel Dörfler static const char *kLabelCancel = "Cancel";
425adca30aSAxel Dörfler static const char *kLabelAuthentication = "Authentication";
435adca30aSAxel Dörfler
445adca30aSAxel Dörfler // connection status strings
455adca30aSAxel Dörfler static const char *kTextConnecting = "Connecting...";
465adca30aSAxel Dörfler static const char *kTextConnectionEstablished = "Connection established.";
475adca30aSAxel Dörfler static const char *kTextNotConnected = "Not connected.";
485adca30aSAxel Dörfler static const char *kTextDeviceUpFailed = "Failed to connect.";
495adca30aSAxel Dörfler static const char *kTextAuthenticating = "Authenticating...";
505adca30aSAxel Dörfler static const char *kTextAuthenticationFailed = "Authentication failed!";
515adca30aSAxel Dörfler static const char *kTextConnectionLost = "Connection lost!";
525adca30aSAxel Dörfler
535adca30aSAxel Dörfler
ConnectionView(BRect rect,const BString & interfaceName)545adca30aSAxel Dörfler ConnectionView::ConnectionView(BRect rect, const BString& interfaceName)
555adca30aSAxel Dörfler : BView(rect, "ConnectionView", B_FOLLOW_NONE, 0),
565adca30aSAxel Dörfler fListener(this),
575adca30aSAxel Dörfler fInterfaceName(interfaceName),
585adca30aSAxel Dörfler fKeepLabel(false)
595adca30aSAxel Dörfler {
60*f696e88aSlooncraz SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
615adca30aSAxel Dörfler
625adca30aSAxel Dörfler rect = Bounds();
635adca30aSAxel Dörfler rect.InsetBy(5, 5);
645adca30aSAxel Dörfler rect.bottom = rect.top
655adca30aSAxel Dörfler + 25 // space for topmost control
665adca30aSAxel Dörfler + 3 * 20 // size of controls
675adca30aSAxel Dörfler + 3 * 5; // space beween controls and bottom of box
685adca30aSAxel Dörfler BBox *authenticationBox = new BBox(rect, "Authentication");
695adca30aSAxel Dörfler authenticationBox->SetLabel(kLabelAuthentication);
705adca30aSAxel Dörfler rect = authenticationBox->Bounds();
715adca30aSAxel Dörfler rect.InsetBy(10, 20);
725adca30aSAxel Dörfler rect.bottom = rect.top + 20;
735adca30aSAxel Dörfler fUsername = new BTextControl(rect, "username", kLabelName, NULL, NULL);
745adca30aSAxel Dörfler rect.top = rect.bottom + 5;
755adca30aSAxel Dörfler rect.bottom = rect.top + 20;
765adca30aSAxel Dörfler fPassword = new BTextControl(rect, "password", kLabelPassword, NULL, NULL);
775adca30aSAxel Dörfler fPassword->TextView()->HideTyping(true);
785adca30aSAxel Dörfler
795adca30aSAxel Dörfler // set dividers
805adca30aSAxel Dörfler float width = max(StringWidth(fUsername->Label()),
815adca30aSAxel Dörfler StringWidth(fPassword->Label()));
825adca30aSAxel Dörfler fUsername->SetDivider(width + 5);
835adca30aSAxel Dörfler fPassword->SetDivider(width + 5);
845adca30aSAxel Dörfler
855adca30aSAxel Dörfler rect.top = rect.bottom + 5;
865adca30aSAxel Dörfler rect.bottom = rect.top + 20;
875adca30aSAxel Dörfler fSavePassword = new BCheckBox(rect, "SavePassword", kLabelSavePassword, NULL);
885adca30aSAxel Dörfler
895adca30aSAxel Dörfler authenticationBox->AddChild(fUsername);
905adca30aSAxel Dörfler authenticationBox->AddChild(fPassword);
915adca30aSAxel Dörfler authenticationBox->AddChild(fSavePassword);
925adca30aSAxel Dörfler AddChild(authenticationBox);
935adca30aSAxel Dörfler
945adca30aSAxel Dörfler rect = authenticationBox->Frame();
955adca30aSAxel Dörfler rect.top = rect.bottom + 10;
965adca30aSAxel Dörfler rect.bottom = rect.top + 15;
975adca30aSAxel Dörfler fAttemptView = new BStringView(rect, "AttemptView", "");
985adca30aSAxel Dörfler AddChild(fAttemptView);
995adca30aSAxel Dörfler
1005adca30aSAxel Dörfler // add status view
1015adca30aSAxel Dörfler rect.top = rect.bottom + 5;
1025adca30aSAxel Dörfler rect.bottom = rect.top + 15;
1035adca30aSAxel Dörfler fStatusView = new BStringView(rect, "StatusView", "");
1045adca30aSAxel Dörfler AddChild(fStatusView);
1055adca30aSAxel Dörfler
1065adca30aSAxel Dörfler // add "Connect" and "Cancel" buttons
1075adca30aSAxel Dörfler rect.top = rect.bottom + 10;
1085adca30aSAxel Dörfler rect.bottom = rect.top + 25;
1095adca30aSAxel Dörfler rect.right = rect.left + kDefaultButtonWidth;
1105adca30aSAxel Dörfler fConnectButton = new BButton(rect, "ConnectButton", kLabelConnect,
1115adca30aSAxel Dörfler new BMessage(kMsgConnect));
1125adca30aSAxel Dörfler
1135adca30aSAxel Dörfler rect.left = rect.right + 10;
1145adca30aSAxel Dörfler rect.right = rect.left + kDefaultButtonWidth;
1155adca30aSAxel Dörfler fCancelButton = new BButton(rect, "CancelButton", kLabelCancel,
1165adca30aSAxel Dörfler new BMessage(kMsgCancel));
1175adca30aSAxel Dörfler
1185adca30aSAxel Dörfler AddChild(fConnectButton);
1195adca30aSAxel Dörfler AddChild(fCancelButton);
1205adca30aSAxel Dörfler }
1215adca30aSAxel Dörfler
1225adca30aSAxel Dörfler
1235adca30aSAxel Dörfler void
AttachedToWindow()1245adca30aSAxel Dörfler ConnectionView::AttachedToWindow()
1255adca30aSAxel Dörfler {
1265adca30aSAxel Dörfler Reload();
1275adca30aSAxel Dörfler fListener.WatchManager();
1285adca30aSAxel Dörfler WatchInterface(fListener.Manager().InterfaceWithName(fInterfaceName.String()));
1295adca30aSAxel Dörfler
1305adca30aSAxel Dörfler Window()->SetDefaultButton(fConnectButton);
1315adca30aSAxel Dörfler fConnectButton->SetTarget(this);
1325adca30aSAxel Dörfler fCancelButton->SetTarget(this);
1335adca30aSAxel Dörfler }
1345adca30aSAxel Dörfler
1355adca30aSAxel Dörfler
1365adca30aSAxel Dörfler void
MessageReceived(BMessage * message)1375adca30aSAxel Dörfler ConnectionView::MessageReceived(BMessage *message)
1385adca30aSAxel Dörfler {
1395adca30aSAxel Dörfler switch(message->what) {
1405adca30aSAxel Dörfler case PPP_REPORT_MESSAGE:
1415adca30aSAxel Dörfler HandleReportMessage(message);
1425adca30aSAxel Dörfler break;
1435adca30aSAxel Dörfler
1445adca30aSAxel Dörfler case kMsgConnect:
1455adca30aSAxel Dörfler Connect();
1465adca30aSAxel Dörfler break;
1475adca30aSAxel Dörfler
1485adca30aSAxel Dörfler case kMsgCancel:
1495adca30aSAxel Dörfler Cancel();
1505adca30aSAxel Dörfler break;
1515adca30aSAxel Dörfler
1525adca30aSAxel Dörfler default:
1535adca30aSAxel Dörfler BView::MessageReceived(message);
1545adca30aSAxel Dörfler }
1555adca30aSAxel Dörfler }
1565adca30aSAxel Dörfler
1575adca30aSAxel Dörfler
1585adca30aSAxel Dörfler // update authentication UI
1595adca30aSAxel Dörfler void
Reload()1605adca30aSAxel Dörfler ConnectionView::Reload()
1615adca30aSAxel Dörfler {
1625adca30aSAxel Dörfler // load username and password
1635adca30aSAxel Dörfler BString path("ptpnet/");
1645adca30aSAxel Dörfler path << fInterfaceName;
1655adca30aSAxel Dörfler fSettings.MakeEmpty();
1665adca30aSAxel Dörfler ReadMessageDriverSettings(path.String(), &fSettings);
1675adca30aSAxel Dörfler
1685adca30aSAxel Dörfler fHasUsername = fHasPassword = false;
1695adca30aSAxel Dörfler BString username, password;
1705adca30aSAxel Dörfler
1715adca30aSAxel Dörfler BMessage parameter;
1725adca30aSAxel Dörfler int32 parameterIndex = 0;
1735adca30aSAxel Dörfler if(FindMessageParameter(PPP_USERNAME_KEY, fSettings, ¶meter, ¶meterIndex)
1745adca30aSAxel Dörfler && parameter.FindString(MDSU_VALUES, &username) == B_OK)
1755adca30aSAxel Dörfler fHasUsername = true;
1765adca30aSAxel Dörfler
1775adca30aSAxel Dörfler parameterIndex = 0;
1785adca30aSAxel Dörfler if(FindMessageParameter(PPP_PASSWORD_KEY, fSettings, ¶meter, ¶meterIndex)
1795adca30aSAxel Dörfler && parameter.FindString(MDSU_VALUES, &password) == B_OK)
1805adca30aSAxel Dörfler fHasPassword = true;
1815adca30aSAxel Dörfler
1825adca30aSAxel Dörfler fUsername->SetText(username.String());
1835adca30aSAxel Dörfler fPassword->SetText(password.String());
1845adca30aSAxel Dörfler fSavePassword->SetValue(fHasPassword);
1855adca30aSAxel Dörfler
1865adca30aSAxel Dörfler fUsername->SetEnabled(fHasUsername);
1875adca30aSAxel Dörfler fPassword->SetEnabled(fHasUsername);
1885adca30aSAxel Dörfler fSavePassword->SetEnabled(fHasUsername);
1895adca30aSAxel Dörfler }
1905adca30aSAxel Dörfler
1915adca30aSAxel Dörfler
1925adca30aSAxel Dörfler void
Connect()1935adca30aSAxel Dörfler ConnectionView::Connect()
1945adca30aSAxel Dörfler {
1955adca30aSAxel Dörfler PPPInterface interface(PPPManager().CreateInterfaceWithName(
1965adca30aSAxel Dörfler fInterfaceName.String()));
1975adca30aSAxel Dörfler interface.SetUsername(Username());
1985adca30aSAxel Dörfler interface.SetPassword(Password());
1995adca30aSAxel Dörfler interface.SetAskBeforeConnecting(false);
2005adca30aSAxel Dörfler interface.Up();
2015adca30aSAxel Dörfler
2025adca30aSAxel Dörfler // save settings
2035adca30aSAxel Dörfler if(fHasUsername) {
2045adca30aSAxel Dörfler BMessage parameter;
2055adca30aSAxel Dörfler int32 index = 0;
2065adca30aSAxel Dörfler if(FindMessageParameter(PPP_USERNAME_KEY, fSettings, ¶meter, &index))
2075adca30aSAxel Dörfler fSettings.RemoveData(MDSU_PARAMETERS, index);
2085adca30aSAxel Dörfler parameter.MakeEmpty();
2095adca30aSAxel Dörfler parameter.AddString(MDSU_NAME, PPP_USERNAME_KEY);
2105adca30aSAxel Dörfler parameter.AddString(MDSU_VALUES, Username());
2115adca30aSAxel Dörfler fSettings.AddMessage(MDSU_PARAMETERS, ¶meter);
2125adca30aSAxel Dörfler
2135adca30aSAxel Dörfler index = 0;
2145adca30aSAxel Dörfler if(FindMessageParameter(PPP_PASSWORD_KEY, fSettings, ¶meter, &index))
2155adca30aSAxel Dörfler fSettings.RemoveData(MDSU_PARAMETERS, index);
2165adca30aSAxel Dörfler if(DoesSavePassword()) {
2175adca30aSAxel Dörfler parameter.MakeEmpty();
2185adca30aSAxel Dörfler parameter.AddString(MDSU_NAME, PPP_PASSWORD_KEY);
2195adca30aSAxel Dörfler parameter.AddString(MDSU_VALUES, Password());
2205adca30aSAxel Dörfler fSettings.AddMessage(MDSU_PARAMETERS, ¶meter);
2215adca30aSAxel Dörfler }
2225adca30aSAxel Dörfler
2235adca30aSAxel Dörfler BEntry entry;
2245adca30aSAxel Dörfler if(interface.GetSettingsEntry(&entry) == B_OK) {
2255adca30aSAxel Dörfler BFile file(&entry, B_WRITE_ONLY);
2265adca30aSAxel Dörfler WriteMessageDriverSettings(file, fSettings);
2275adca30aSAxel Dörfler }
2285adca30aSAxel Dörfler }
2295adca30aSAxel Dörfler
2305adca30aSAxel Dörfler Reload();
2315adca30aSAxel Dörfler }
2325adca30aSAxel Dörfler
2335adca30aSAxel Dörfler
2345adca30aSAxel Dörfler void
Cancel()2355adca30aSAxel Dörfler ConnectionView::Cancel()
2365adca30aSAxel Dörfler {
2375adca30aSAxel Dörfler PPPInterface interface(fListener.Interface());
2385adca30aSAxel Dörfler bool quit = false;
2395adca30aSAxel Dörfler ppp_interface_info_t info;
2405adca30aSAxel Dörfler if(interface.GetInterfaceInfo(&info) && info.info.phase < PPP_ESTABLISHMENT_PHASE)
2415adca30aSAxel Dörfler quit = true;
2425adca30aSAxel Dörfler interface.Down();
2435adca30aSAxel Dörfler if(quit)
2445adca30aSAxel Dörfler Window()->Quit();
2455adca30aSAxel Dörfler }
2465adca30aSAxel Dörfler
2475adca30aSAxel Dörfler
2485adca30aSAxel Dörfler // Clean up before our window quits (called by ConnectionWindow).
2495adca30aSAxel Dörfler void
CleanUp()2505adca30aSAxel Dörfler ConnectionView::CleanUp()
2515adca30aSAxel Dörfler {
2525adca30aSAxel Dörfler fListener.StopWatchingInterface();
2535adca30aSAxel Dörfler fListener.StopWatchingManager();
2545adca30aSAxel Dörfler }
2555adca30aSAxel Dörfler
2565adca30aSAxel Dörfler
2575adca30aSAxel Dörfler BString
AttemptString() const2585adca30aSAxel Dörfler ConnectionView::AttemptString() const
2595adca30aSAxel Dörfler {
2605adca30aSAxel Dörfler PPPInterface interface(fListener.Interface());
2615adca30aSAxel Dörfler ppp_interface_info_t info;
2625adca30aSAxel Dörfler if(!interface.GetInterfaceInfo(&info))
2635adca30aSAxel Dörfler return BString("");
2645adca30aSAxel Dörfler BString attempt;
2655adca30aSAxel Dörfler attempt << "Attempt " << info.info.connectAttempt << " of " <<
2665adca30aSAxel Dörfler info.info.connectRetriesLimit + 1;
2675adca30aSAxel Dörfler
2685adca30aSAxel Dörfler return attempt;
2695adca30aSAxel Dörfler }
2705adca30aSAxel Dörfler
2715adca30aSAxel Dörfler
2725adca30aSAxel Dörfler void
HandleReportMessage(BMessage * message)2735adca30aSAxel Dörfler ConnectionView::HandleReportMessage(BMessage *message)
2745adca30aSAxel Dörfler {
2755adca30aSAxel Dörfler ppp_interface_id id;
2765adca30aSAxel Dörfler if(message->FindInt32("interface", reinterpret_cast<int32*>(&id)) != B_OK
2775adca30aSAxel Dörfler || (fListener.Interface() != PPP_UNDEFINED_INTERFACE_ID
2785adca30aSAxel Dörfler && id != fListener.Interface()))
2795adca30aSAxel Dörfler return;
2805adca30aSAxel Dörfler
2815adca30aSAxel Dörfler int32 type, code;
2825adca30aSAxel Dörfler message->FindInt32("type", &type);
2835adca30aSAxel Dörfler message->FindInt32("code", &code);
2845adca30aSAxel Dörfler
2855adca30aSAxel Dörfler if(type == PPP_MANAGER_REPORT && code == PPP_REPORT_INTERFACE_CREATED) {
2865adca30aSAxel Dörfler PPPInterface interface(id);
2875adca30aSAxel Dörfler if(interface.InitCheck() != B_OK || fInterfaceName != interface.Name())
2885adca30aSAxel Dörfler return;
2895adca30aSAxel Dörfler
2905adca30aSAxel Dörfler WatchInterface(id);
2915adca30aSAxel Dörfler
2925adca30aSAxel Dörfler if(((fHasUsername && !fHasPassword) || fAskBeforeConnecting)
2935adca30aSAxel Dörfler && Window()->IsHidden())
2945adca30aSAxel Dörfler Window()->Show();
2955adca30aSAxel Dörfler } else if(type == PPP_CONNECTION_REPORT)
2965adca30aSAxel Dörfler UpdateStatus(code);
2975adca30aSAxel Dörfler else if(type == PPP_DESTRUCTION_REPORT)
2985adca30aSAxel Dörfler fListener.StopWatchingInterface();
2995adca30aSAxel Dörfler }
3005adca30aSAxel Dörfler
3015adca30aSAxel Dörfler
3025adca30aSAxel Dörfler void
UpdateStatus(int32 code)3035adca30aSAxel Dörfler ConnectionView::UpdateStatus(int32 code)
3045adca30aSAxel Dörfler {
3055adca30aSAxel Dörfler BString attemptString = AttemptString();
3065adca30aSAxel Dörfler fAttemptView->SetText(attemptString.String());
3075adca30aSAxel Dörfler
3085adca30aSAxel Dörfler if(code == PPP_REPORT_UP_SUCCESSFUL) {
3095adca30aSAxel Dörfler fStatusView->SetText(kTextConnectionEstablished);
3105adca30aSAxel Dörfler PPPDeskbarReplicant *item = new PPPDeskbarReplicant(fListener.Interface());
3115adca30aSAxel Dörfler BDeskbar().AddItem(item);
3125adca30aSAxel Dörfler delete item;
3135adca30aSAxel Dörfler Window()->Quit();
3145adca30aSAxel Dörfler return;
3155adca30aSAxel Dörfler }
3165adca30aSAxel Dörfler
3175adca30aSAxel Dörfler // maybe the status string must not be changed (codes that set fKeepLabel to false
3185adca30aSAxel Dörfler // should still be handled)
3195adca30aSAxel Dörfler if(fKeepLabel && code != PPP_REPORT_GOING_UP && code != PPP_REPORT_UP_SUCCESSFUL)
3205adca30aSAxel Dörfler return;
3215adca30aSAxel Dörfler
3225adca30aSAxel Dörfler if(fListener.InitCheck() != B_OK) {
3235adca30aSAxel Dörfler fStatusView->SetText(kTextConnectionLost);
3245adca30aSAxel Dörfler return;
3255adca30aSAxel Dörfler }
3265adca30aSAxel Dörfler
3275adca30aSAxel Dörfler // only errors should set fKeepLabel to true
3285adca30aSAxel Dörfler switch(code) {
3295adca30aSAxel Dörfler case PPP_REPORT_GOING_UP:
3305adca30aSAxel Dörfler fKeepLabel = false;
3315adca30aSAxel Dörfler fStatusView->SetText(kTextConnecting);
3325adca30aSAxel Dörfler break;
3335adca30aSAxel Dörfler
3345adca30aSAxel Dörfler case PPP_REPORT_DOWN_SUCCESSFUL:
3355adca30aSAxel Dörfler fStatusView->SetText(kTextNotConnected);
3365adca30aSAxel Dörfler break;
3375adca30aSAxel Dörfler
3385adca30aSAxel Dörfler case PPP_REPORT_DEVICE_UP_FAILED:
3395adca30aSAxel Dörfler fKeepLabel = true;
3405adca30aSAxel Dörfler fStatusView->SetText(kTextDeviceUpFailed);
3415adca30aSAxel Dörfler break;
3425adca30aSAxel Dörfler
3435adca30aSAxel Dörfler case PPP_REPORT_AUTHENTICATION_REQUESTED:
3445adca30aSAxel Dörfler fStatusView->SetText(kTextAuthenticating);
3455adca30aSAxel Dörfler break;
3465adca30aSAxel Dörfler
3475adca30aSAxel Dörfler case PPP_REPORT_AUTHENTICATION_FAILED:
3485adca30aSAxel Dörfler fKeepLabel = true;
3495adca30aSAxel Dörfler fStatusView->SetText(kTextAuthenticationFailed);
3505adca30aSAxel Dörfler break;
3515adca30aSAxel Dörfler
3525adca30aSAxel Dörfler case PPP_REPORT_CONNECTION_LOST:
3535adca30aSAxel Dörfler fKeepLabel = true;
3545adca30aSAxel Dörfler fStatusView->SetText(kTextConnectionLost);
3555adca30aSAxel Dörfler break;
3565adca30aSAxel Dörfler }
3575adca30aSAxel Dörfler }
3585adca30aSAxel Dörfler
3595adca30aSAxel Dörfler
3605adca30aSAxel Dörfler void
WatchInterface(ppp_interface_id ID)3615adca30aSAxel Dörfler ConnectionView::WatchInterface(ppp_interface_id ID)
3625adca30aSAxel Dörfler {
3635adca30aSAxel Dörfler fListener.WatchInterface(ID);
3645adca30aSAxel Dörfler
3655adca30aSAxel Dörfler // update status
3665adca30aSAxel Dörfler Reload();
3675adca30aSAxel Dörfler PPPInterface interface(fListener.Interface());
3685adca30aSAxel Dörfler ppp_interface_info_t info;
3695adca30aSAxel Dörfler if(!interface.GetInterfaceInfo(&info)) {
3705adca30aSAxel Dörfler UpdateStatus(PPP_REPORT_DOWN_SUCCESSFUL);
3715adca30aSAxel Dörfler fAskBeforeConnecting = false;
3725adca30aSAxel Dörfler } else
3735adca30aSAxel Dörfler fAskBeforeConnecting = info.info.askBeforeConnecting;
3745adca30aSAxel Dörfler }
375