1/* 2 * Copyright 2008, François Revol, <revol@free.fr>. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5#include <Catalog.h> 6#include <ScrollView.h> 7#include <String.h> 8#include <Window.h> 9#ifdef __HAIKU__ 10#include <Layout.h> 11#endif 12 13#include <pwd.h> 14 15#include "LoginApp.h" 16#include "LoginView.h" 17 18#undef B_TRANSLATION_CONTEXT 19#define B_TRANSLATION_CONTEXT "Login View" 20 21#define LW 120 22#define CSEP 15 23#define BH 20 24#define BW 60 25 26class PwdItem : public BStringItem { 27public: 28 PwdItem(struct passwd *pwd, uint32 level = 0, 29 bool expanded = true) 30 : BStringItem("", level, expanded) 31 { 32 if (pwd) { 33 BString name(pwd->pw_gecos); 34 // TODO: truncate at first ; 35 fLogin = pwd->pw_name; 36 SetText(name.String()); 37 } 38 }; 39 virtual ~PwdItem() {}; 40 const char* Login() const { return fLogin.String(); }; 41private: 42 BString fLogin; 43}; 44 45 46LoginView::LoginView(BRect frame) 47 : BView(frame, "LoginView", B_FOLLOW_ALL, B_PULSE_NEEDED) 48{ 49 // TODO: when I don't need to test in BeOS anymore, 50 // rewrite to use layout engine. 51 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 52 SetLowUIColor(ViewUIColor()); 53 BRect r; 54 r.Set(CSEP, CSEP, LW, Bounds().Height() - 3 * CSEP - BH); 55 fUserList = new BListView(r, "users"); 56 BScrollView *sv = new BScrollView("userssv", fUserList, 57 B_FOLLOW_LEFT | B_FOLLOW_TOP, 0, false, true); 58 AddChild(sv); 59 fUserList->SetSelectionMessage(new BMessage(kUserSelected)); 60 fUserList->SetInvocationMessage(new BMessage(kUserInvoked)); 61 62 r.Set(LW + 30, Bounds().top + CSEP, 63 Bounds().right - CSEP, Bounds().top + CSEP + CSEP); 64 fLoginControl = new BTextControl(r, "login", B_TRANSLATE("Login:"), "", 65 new BMessage(kLoginEdited)); 66 AddChild(fLoginControl); 67 68 r.OffsetBySelf(0, CSEP + CSEP); 69 fPasswordControl = new BTextControl(r, "password", 70 B_TRANSLATE("Password:"), "", new BMessage(kPasswordEdited)); 71 fPasswordControl->TextView()->HideTyping(true); 72 AddChild(fPasswordControl); 73 74 r.OffsetBySelf(0, CSEP + CSEP); 75 fHidePasswordCheckBox = new BCheckBox(r, "hidepw", 76 B_TRANSLATE("Hide password"), new BMessage(kHidePassword)); 77 fHidePasswordCheckBox->SetValue(1); 78 AddChild(fHidePasswordCheckBox); 79 80 // buttons 81 float buttonWidth = BW; //(Bounds().Width() - 4 * CSEP) / 3; 82 BRect buttonRect(0, Bounds().bottom - BH, 83 buttonWidth, Bounds().bottom); 84 buttonRect.OffsetBySelf(CSEP, -CSEP); 85 86 fHaltButton = new BButton(buttonRect, "halt", B_TRANSLATE("Halt"), 87 new BMessage(kHaltAction)); 88 fHaltButton->ResizeToPreferred(); 89 AddChild(fHaltButton); 90 91 buttonRect.OffsetBySelf(CSEP + fHaltButton->Frame().Width(), 0); 92 fRebootButton = new BButton(buttonRect, "reboot", B_TRANSLATE("Reboot"), 93 new BMessage(kRebootAction)); 94 95 fRebootButton->ResizeToPreferred(); 96 AddChild(fRebootButton); 97 98 BRect infoRect(buttonRect); 99 infoRect.OffsetBySelf(fRebootButton->Frame().Width() + CSEP, 0); 100 101 buttonRect.OffsetToSelf(Bounds().Width() - CSEP - buttonWidth, 102 Bounds().Height() - CSEP - BH); 103 fLoginButton = new BButton(buttonRect, "ok", B_TRANSLATE("OK"), 104 new BMessage(kAttemptLogin)); 105 fLoginButton->ResizeToPreferred(); 106 AddChild(fLoginButton); 107 108 infoRect.right = buttonRect.left - CSEP + 5; 109 BString info; 110 //info << hostname; 111 fInfoView = new BStringView(infoRect, "info", info.String()); 112 AddChild(fInfoView); 113} 114 115 116LoginView::~LoginView() 117{ 118} 119 120void 121LoginView::AttachedToWindow() 122{ 123 fUserList->SetTarget(this); 124 fLoginControl->SetTarget(this); 125 fPasswordControl->SetTarget(this); 126 fHidePasswordCheckBox->SetTarget(this); 127 fHaltButton->SetTarget(be_app_messenger); 128 fRebootButton->SetTarget(be_app_messenger); 129 fLoginButton->SetTarget(this); 130 Window()->SetDefaultButton(fLoginButton); 131 //fLoginControl->MakeFocus(); 132 fUserList->MakeFocus(); 133 // populate user list 134 BMessenger(this).SendMessage(kAddNextUser); 135 136 // size window relative to buttons 137 BRect bounds = Window()->Bounds(); 138 float spacing = fHaltButton->Frame().left; 139 bounds.bottom = fLoginButton->Frame().bottom + spacing; 140 bounds.right = fLoginButton->Frame().right + spacing; 141 Window()->ResizeTo(bounds.Width(), bounds.Height()); 142 143 // Center info view 144 BPoint leftTop = fInfoView->Frame().LeftTop(); 145 leftTop.y += fHaltButton->Frame().Height() / 2; 146 leftTop.y -= fInfoView->Bounds().Height() / 2; 147 fInfoView->MoveTo(leftTop); 148} 149 150 151void 152LoginView::MessageReceived(BMessage *message) 153{ 154 switch (message->what) { 155 case kSetProgress: 156 break; 157 case kAddNextUser: 158 AddNextUser(); 159 break; 160 case kUserSelected: 161 { 162 int32 selection = fUserList->CurrentSelection(); 163 if (selection > -1) { 164 PwdItem *item = dynamic_cast<PwdItem *>( 165 fUserList->ItemAt(selection)); 166 if (item) 167 fLoginControl->SetText(item->Login()); 168 } 169 break; 170 } 171 case kUserInvoked: 172 fPasswordControl->MakeFocus(); 173 break; 174 case kLoginEdited: 175 break; 176 case kHidePassword: 177 fPasswordControl->TextView()->HideTyping( 178 fHidePasswordCheckBox->Value()); 179 break; 180 case kAttemptLogin: 181 { 182 // if no pass specified and we were selecting the user, 183 // give a chance to enter the password 184 // else we might want to enter an empty password. 185 if (strlen(fPasswordControl->Text()) < 1 186 && (fUserList->IsFocus() || fLoginControl->IsFocus())) { 187 fPasswordControl->MakeFocus(); 188 break; 189 } 190 BMessage *m = new BMessage(kAttemptLogin); 191 m->AddString("login", fLoginControl->Text()); 192 m->AddString("password", fPasswordControl->Text()); 193 be_app->PostMessage(m, NULL, this); 194 break; 195 } 196 case kLoginBad: 197 fPasswordControl->SetText(""); 198 EnableControls(false); 199 fInfoView->SetText(B_TRANSLATE("Invalid login!")); 200 if (Window()) { 201 BPoint savedPos = Window()->Frame().LeftTop(); 202 for (int i = 0; i < 10; i++) { 203 BPoint p(savedPos); 204 p.x += (i%2) ? 10 : -10; 205 Window()->MoveTo(p); 206 Window()->UpdateIfNeeded(); 207 snooze(30000); 208 } 209 Window()->MoveTo(savedPos); 210 } 211 EnableControls(true); 212 break; 213 case kLoginOk: 214 // XXX: quit ? 215 if (Window()) { 216 Window()->Hide(); 217 } 218 break; 219 default: 220 message->PrintToStream(); 221 BView::MessageReceived(message); 222 } 223} 224 225 226void 227LoginView::Pulse() 228{ 229 BString info; 230 time_t now = time(NULL); 231 struct tm *t = localtime(&now); 232 // TODO: use strftime and locale settings 233 info << asctime(t); 234 info.RemoveSet("\r\n"); 235 fInfoView->SetText(info.String()); 236} 237 238 239void 240LoginView::AddNextUser() 241{ 242 struct passwd *pwd; 243 if (fUserList->CountItems() < 1) 244 setpwent(); 245 246 pwd = getpwent(); 247 248 if (pwd && pwd->pw_shell && 249 strcmp(pwd->pw_shell, "false") && 250 strcmp(pwd->pw_shell, "true") && 251 strcmp(pwd->pw_shell, "/bin/false") && 252 strcmp(pwd->pw_shell, "/bin/true")) { 253 // not disabled 254 PwdItem *item = new PwdItem(pwd); 255 fUserList->AddItem(item); 256 } 257 if (pwd) 258 BMessenger(this).SendMessage(kAddNextUser); 259 else 260 endpwent(); 261} 262 263 264void 265LoginView::EnableControls(bool enable) 266{ 267 int32 i; 268 for (i = 0; i < fUserList->CountItems(); i++) { 269 fUserList->ItemAt(i)->SetEnabled(enable); 270 } 271 fLoginControl->SetEnabled(enable); 272 fPasswordControl->SetEnabled(enable); 273 fHidePasswordCheckBox->SetEnabled(enable); 274 fHaltButton->SetEnabled(enable); 275 fRebootButton->SetEnabled(enable); 276 fLoginButton->SetEnabled(enable); 277} 278 279