/* PoorManWindow.cpp * * Philip Harrison * Started: 4/25/2004 * Version: 0.1 */ #include "PoorManWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PoorManApplication.h" #include "PoorManPreferencesWindow.h" #include "PoorManView.h" #include "PoorManServer.h" #include "PoorManLogger.h" #include "constants.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "PoorMan" #define DATE_FORMAT B_SHORT_DATE_FORMAT #define TIME_FORMAT B_MEDIUM_TIME_FORMAT PoorManWindow::PoorManWindow(BRect frame) : BWindow(frame, STR_APP_NAME, B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS), fStatus(false), fHits(0), fPrefWindow(NULL), fLogFile(NULL), fServer(NULL) { //preferences init fWebDirectory.SetTo(STR_DEFAULT_WEB_DIRECTORY); fIndexFileName.SetTo("index.html"); fDirListFlag = false; fLogConsoleFlag = true; fLogFileFlag = false; fLogPath.SetTo(""); fMaxConnections = (int16)32; fIsZoomed = true; fLastWidth = 318.0f; fLastHeight = 320.0f; this->fFrame = frame; fSetwindowFrame.Set(112.0f, 60.0f, 492.0f, 340.0f); // PoorMan Window SetSizeLimits(318, 1600, 53, 1200); // limit the size of the size of the window // ----------------------------------------------------------------- // Three Labels // Status String fStatusView = new BStringView("Status View", B_TRANSLATE("Status: Stopped")); // Directory String fDirView = new BStringView("Dir View", B_TRANSLATE("Directory: (none)")); // Hits String fHitsView = new BStringView("Hit View", B_TRANSLATE("Hits: 0")); // ----------------------------------------------------------------- // Logging View fLoggingView = new BTextView(STR_TXT_VIEW, B_WILL_DRAW ); fLoggingView->MakeEditable(false); // user cannot change the text fLoggingView->MakeSelectable(true); fLoggingView->SetViewColor(WHITE); fLoggingView->SetStylable(true); // create the scroll view fScrollView = new BScrollView("Scroll View", fLoggingView, B_WILL_DRAW | B_FRAME_EVENTS, // Make sure articles on border do not occur when resizing false, true); fLoggingView->MakeFocus(true); // ----------------------------------------------------------------- // menu bar fFileMenuBar = new BMenuBar("File Menu Bar"); // menus fFileMenu = BuildFileMenu(); if (fFileMenu) fFileMenuBar->AddItem(fFileMenu); fEditMenu = BuildEditMenu(); if (fEditMenu) fFileMenuBar->AddItem(fEditMenu); fControlsMenu = BuildControlsMenu(); if (fControlsMenu) fFileMenuBar->AddItem(fControlsMenu); // File Panels BWindow* change_title; fSaveConsoleFilePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, B_FILE_NODE, false, new BMessage(MSG_FILE_PANEL_SAVE_CONSOLE)); change_title = fSaveConsoleFilePanel->Window(); change_title->SetTitle(STR_FILEPANEL_SAVE_CONSOLE); fSaveConsoleSelectionFilePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, B_FILE_NODE, false, new BMessage(MSG_FILE_PANEL_SAVE_CONSOLE_SELECTION)); change_title = fSaveConsoleSelectionFilePanel->Window(); change_title->SetTitle(STR_FILEPANEL_SAVE_CONSOLE_SELECTION); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .SetInsets(0) .Add(fFileMenuBar) .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING) .SetInsets(B_USE_WINDOW_INSETS) .AddGroup(B_HORIZONTAL) .Add(fStatusView) .AddGlue() .Add(fHitsView) .End() .AddGroup(B_HORIZONTAL) .Add(fDirView) .AddGlue() .End() .Add(fScrollView); pthread_rwlock_init(&fLogFileLock, NULL); } PoorManWindow::~PoorManWindow() { delete fServer; delete fLogFile; pthread_rwlock_destroy(&fLogFileLock); } void PoorManWindow::MessageReceived(BMessage* message) { switch (message->what) { case MSG_MENU_FILE_SAVE_AS: fSaveConsoleFilePanel->Show(); break; case MSG_FILE_PANEL_SAVE_CONSOLE: printf("FilePanel: Save console\n"); SaveConsole(message, false); break; case MSG_MENU_FILE_SAVE_SELECTION: fSaveConsoleSelectionFilePanel->Show(); break; case MSG_FILE_PANEL_SAVE_CONSOLE_SELECTION: printf("FilePanel: Save console selection\n"); SaveConsole(message, true); break; case MSG_FILE_PANEL_SELECT_WEB_DIR: fPrefWindow->MessageReceived(message); break; case MSG_MENU_EDIT_PREF: fPrefWindow = new PoorManPreferencesWindow(fSetwindowFrame, STR_WIN_NAME_PREF); fPrefWindow->Show(); break; case MSG_MENU_CTRL_RUN: if (fStatus) StopServer(); else StartServer(); break; case MSG_MENU_CTRL_CLEAR_HIT: SetHits(0); //UpdateHitsLabel(); break; case MSG_MENU_CTRL_CLEAR_CONSOLE: fLoggingView->SelectAll(); fLoggingView->Delete(); break; case MSG_MENU_CTRL_CLEAR_LOG: FILE* f; f = fopen(fLogPath.String(), "w"); fclose(f); break; case MSG_LOG: { if (!fLogConsoleFlag && !fLogFileFlag) break; time_t time; const char* address; rgb_color color; const void* pointer; ssize_t size; const char* msg; BString line; if (message->FindString("cstring", &msg) != B_OK) break; if (message->FindData("time_t", B_TIME_TYPE, &pointer, &size) != B_OK) time = -1; else time = *static_cast(pointer); if (message->FindString("addr", &address) != B_OK) address = NULL; if (message->FindData("rgb_color", B_RGB_COLOR_TYPE, &pointer, &size) != B_OK) color = BLACK; else color = *static_cast(pointer); if (time != -1) { BString timeString; if (BDateTimeFormat().Format(timeString, time, DATE_FORMAT, TIME_FORMAT) == B_OK) { line << '[' << timeString << "]: "; } } if (address != NULL) { line << '(' << address << ") "; } line << msg; text_run run; text_run_array runs; run.offset = 0; run.color = color; runs.count = 1; runs.runs[0] = run; if (Lock()) { if (fLogConsoleFlag) { fLoggingView->Insert(fLoggingView->TextLength(), line.String(), line.Length(), &runs); fLoggingView->ScrollToOffset(fLoggingView->TextLength()); } if (fLogFileFlag) { if (pthread_rwlock_rdlock(&fLogFileLock) == 0) { fLogFile->Write(line.String(), line.Length()); pthread_rwlock_unlock(&fLogFileLock); } } Unlock(); } break; } default: BWindow::MessageReceived(message); break; } } void PoorManWindow::FrameMoved(BPoint origin) { fFrame.left = origin.x; fFrame.top = origin.y; } void PoorManWindow::FrameResized(float width, float height) { if (fIsZoomed) { fLastWidth = width; fLastHeight = height; } } bool PoorManWindow::QuitRequested() { if (fStatus) { time_t now = time(NULL); BString timeString; BDateTimeFormat().Format(timeString, now, DATE_FORMAT, TIME_FORMAT); BString line; line << "[" << timeString << "]: " << B_TRANSLATE("Shutting down.") << "\n"; if (fLogConsoleFlag) { fLoggingView->Insert(fLoggingView->TextLength(), line, line.Length()); fLoggingView->ScrollToOffset(fLoggingView->TextLength()); } if (fLogFileFlag) { if (pthread_rwlock_rdlock(&fLogFileLock) == 0) { fLogFile->Write(line, line.Length()); pthread_rwlock_unlock(&fLogFileLock); } } fServer->Stop(); fStatus = false; UpdateStatusLabelAndMenuItem(); } SaveSettings(); be_app_messenger.SendMessage(B_QUIT_REQUESTED); return true; } void PoorManWindow::Zoom(BPoint origin, float width, float height) { if (fIsZoomed) { // Change to the Minimal size fIsZoomed = false; ResizeTo(318, 53); } else { // Change to the Zoomed size fIsZoomed = true; ResizeTo(fLastWidth, fLastHeight); } } void PoorManWindow::SetHits(uint32 num) { fHits = num; UpdateHitsLabel(); } // Private: Methods ------------------------------------------ BMenu* PoorManWindow::BuildFileMenu() const { BMenu* ptrFileMenu = new BMenu(STR_MNU_FILE); ptrFileMenu->AddItem(new BMenuItem(STR_MNU_FILE_SAVE_AS, new BMessage(MSG_MENU_FILE_SAVE_AS), CMD_FILE_SAVE_AS)); ptrFileMenu->AddItem(new BMenuItem(STR_MNU_FILE_SAVE_SELECTION, new BMessage(MSG_MENU_FILE_SAVE_SELECTION))); ptrFileMenu->AddSeparatorItem(); ptrFileMenu->AddItem(new BMenuItem(STR_MNU_FILE_QUIT, new BMessage(B_QUIT_REQUESTED), CMD_FILE_QUIT)); return ptrFileMenu; } BMenu* PoorManWindow::BuildEditMenu() const { BMenu* ptrEditMenu = new BMenu(STR_MNU_EDIT); BMenuItem* CopyMenuItem = new BMenuItem(STR_MNU_EDIT_COPY, new BMessage(B_COPY), CMD_EDIT_COPY); ptrEditMenu->AddItem(CopyMenuItem); CopyMenuItem->SetTarget(fLoggingView, NULL); ptrEditMenu->AddSeparatorItem(); BMenuItem* SelectAllMenuItem = new BMenuItem(STR_MNU_EDIT_SELECT_ALL, new BMessage(B_SELECT_ALL), CMD_EDIT_SELECT_ALL); ptrEditMenu->AddItem(SelectAllMenuItem); SelectAllMenuItem->SetTarget(fLoggingView, NULL); ptrEditMenu->AddSeparatorItem(); BMenuItem* PrefMenuItem = new BMenuItem(STR_MNU_EDIT_PREF, new BMessage(MSG_MENU_EDIT_PREF)); ptrEditMenu->AddItem(PrefMenuItem); return ptrEditMenu; } BMenu* PoorManWindow::BuildControlsMenu() const { BMenu* ptrControlMenu = new BMenu(STR_MNU_CTRL); BMenuItem* RunServerMenuItem = new BMenuItem(STR_MNU_CTRL_RUN_SERVER, new BMessage(MSG_MENU_CTRL_RUN)); RunServerMenuItem->SetMarked(false); ptrControlMenu->AddItem(RunServerMenuItem); BMenuItem* ClearHitCounterMenuItem = new BMenuItem(STR_MNU_CTRL_CLEAR_HIT_COUNTER, new BMessage(MSG_MENU_CTRL_CLEAR_HIT)); ptrControlMenu->AddItem(ClearHitCounterMenuItem); ptrControlMenu->AddSeparatorItem(); BMenuItem* ClearConsoleLogMenuItem = new BMenuItem(STR_MNU_CTRL_CLEAR_CONSOLE, new BMessage(MSG_MENU_CTRL_CLEAR_CONSOLE)); ptrControlMenu->AddItem(ClearConsoleLogMenuItem); BMenuItem* ClearLogFileMenuItem = new BMenuItem(STR_MNU_CTRL_CLEAR_LOG_FILE, new BMessage(MSG_MENU_CTRL_CLEAR_LOG)); ptrControlMenu->AddItem(ClearLogFileMenuItem); return ptrControlMenu; } void PoorManWindow::SetDirLabel(const char* name) { BString dirPath(B_TRANSLATE("Directory: ")); dirPath.Append(name); if (Lock()) { fDirView->SetText(dirPath.String()); Unlock(); } } void PoorManWindow::UpdateStatusLabelAndMenuItem() { if (Lock()) { if (fStatus) fStatusView->SetText(B_TRANSLATE("Status: Running")); else fStatusView->SetText(B_TRANSLATE("Status: Stopped")); fControlsMenu->FindItem(STR_MNU_CTRL_RUN_SERVER)->SetMarked(fStatus); Unlock(); } } void PoorManWindow::UpdateHitsLabel() { if (Lock()) { sprintf(fHitsLabel, B_TRANSLATE("Hits: %lu"), (long unsigned)GetHits()); fHitsView->SetText(fHitsLabel); Unlock(); } } status_t PoorManWindow::SaveConsole(BMessage* message, bool selection) { entry_ref ref; const char* name; BPath path; BEntry entry; status_t err = B_OK; FILE* f; err = message->FindRef("directory", &ref); if (err != B_OK) return err; err = message->FindString("name", &name); if (err != B_OK) return err; err = entry.SetTo(&ref); if (err != B_OK) return err; entry.GetPath(&path); path.Append(name); if (!(f = fopen(path.Path(), "w"))) return B_ERROR; if (!selection) { // write the data to the file err = fwrite(fLoggingView->Text(), 1, fLoggingView->TextLength(), f); } else { // find the selected text and write it to a file int32 start = 0, end = 0; fLoggingView->GetSelection(&start, &end); BString buffer; char * buffData = buffer.LockBuffer(end - start + 1); // copy the selected text from the TextView to the buffer fLoggingView->GetText(start, end - start, buffData); buffer.UnlockBuffer(end - start + 1); err = fwrite(buffer.String(), 1, end - start + 1, f); } fclose(f); return err; } void PoorManWindow::DefaultSettings() { BAlert* serverAlert = new BAlert(B_TRANSLATE("Error Server"), STR_ERR_CANT_START, B_TRANSLATE("OK")); serverAlert->SetFlags(serverAlert->Flags() | B_CLOSE_ON_ESCAPE); BAlert* dirAlert = new BAlert(B_TRANSLATE("Error Dir"), STR_ERR_WEB_DIR, B_TRANSLATE("Cancel"), B_TRANSLATE("Select"), B_TRANSLATE("Create public_html"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING); dirAlert->SetShortcut(0, B_ESCAPE); int32 buttonIndex = dirAlert->Go(); switch (buttonIndex) { case 0: if (Lock()) Quit(); be_app_messenger.SendMessage(B_QUIT_REQUESTED); break; case 1: fPrefWindow = new PoorManPreferencesWindow( fSetwindowFrame, STR_WIN_NAME_PREF); fPrefWindow->ShowWebDirFilePanel(); break; case 2: if (create_directory(STR_DEFAULT_WEB_DIRECTORY, 0755) != B_OK) { serverAlert->Go(); if (Lock()) Quit(); be_app_messenger.SendMessage(B_QUIT_REQUESTED); break; } BAlert* dirCreatedAlert = new BAlert(B_TRANSLATE("Dir Created"), STR_DIR_CREATED, B_TRANSLATE("OK")); dirCreatedAlert->SetFlags(dirCreatedAlert->Flags() | B_CLOSE_ON_ESCAPE); dirCreatedAlert->Go(); SetWebDir(STR_DEFAULT_WEB_DIRECTORY); be_app->PostMessage(kStartServer); break; } } status_t PoorManWindow::ReadSettings() { BPath p; BFile f; BMessage m; if (find_directory(B_USER_SETTINGS_DIRECTORY, &p) != B_OK) return B_ERROR; p.Append(STR_SETTINGS_FILE_NAME); f.SetTo(p.Path(), B_READ_ONLY); if (f.InitCheck() != B_OK) return B_ERROR; if (m.Unflatten(&f) != B_OK) return B_ERROR; if (MSG_PREF_FILE != m.what) return B_ERROR; //site tab if (m.FindString("fWebDirectory", &fWebDirectory) != B_OK) fWebDirectory.SetTo(STR_DEFAULT_WEB_DIRECTORY); if (m.FindString("fIndexFileName", &fIndexFileName) != B_OK) fIndexFileName.SetTo("index.html"); if (m.FindBool("fDirListFlag", &fDirListFlag) != B_OK) fDirListFlag = false; //logging tab if (m.FindBool("fLogConsoleFlag", &fLogConsoleFlag) != B_OK) fLogConsoleFlag = true; if (m.FindBool("fLogFileFlag", &fLogFileFlag) != B_OK) fLogFileFlag = false; if (m.FindString("fLogPath", &fLogPath) != B_OK) fLogPath.SetTo(""); //advance tab if (m.FindInt16("fMaxConnections", &fMaxConnections) != B_OK) fMaxConnections = (int16)32; //windows' position and size if (m.FindRect("frame", &fFrame) != B_OK) fFrame.Set(82.0f, 30.0f, 400.0f, 350.0f); if (m.FindRect("fSetwindowFrame", &fSetwindowFrame) != B_OK) fSetwindowFrame.Set(112.0f, 60.0f, 492.0f, 340.0f); if (m.FindBool("fIsZoomed", &fIsZoomed) != B_OK) fIsZoomed = true; if (m.FindFloat("fLastWidth", &fLastWidth) != B_OK) fLastWidth = 318.0f; if (m.FindFloat("fLastHeight", &fLastHeight) != B_OK) fLastHeight = 320.0f; fIsZoomed?ResizeTo(fLastWidth, fLastHeight):ResizeTo(318, 53); MoveTo(fFrame.left, fFrame.top); fLogFile = new BFile(fLogPath.String(), B_CREATE_FILE | B_WRITE_ONLY | B_OPEN_AT_END); if (fLogFile->InitCheck() != B_OK) { fLogFileFlag = false; //log it to console, "log to file unavailable." return B_OK; } SetDirLabel(fWebDirectory.String()); return B_OK; } status_t PoorManWindow::SaveSettings() { BPath p; BFile f; BMessage m(MSG_PREF_FILE); //site tab m.AddString("fWebDirectory", fWebDirectory); m.AddString("fIndexFileName", fIndexFileName); m.AddBool("fDirListFlag", fDirListFlag); //logging tab m.AddBool("fLogConsoleFlag", fLogConsoleFlag); m.AddBool("fLogFileFlag", fLogFileFlag); m.AddString("fLogPath", fLogPath); //advance tab m.AddInt16("fMaxConnections", fMaxConnections); //windows' position and size m.AddRect("frame", fFrame); m.AddRect("fSetwindowFrame", fSetwindowFrame); m.AddBool("fIsZoomed", fIsZoomed); m.AddFloat("fLastWidth", fLastWidth); m.AddFloat("fLastHeight", fLastHeight); if (find_directory(B_USER_SETTINGS_DIRECTORY, &p) != B_OK) return B_ERROR; p.Append(STR_SETTINGS_FILE_NAME); f.SetTo(p.Path(), B_WRITE_ONLY | B_ERASE_FILE | B_CREATE_FILE); if (f.InitCheck() != B_OK) return B_ERROR; if (m.Flatten(&f) != B_OK) return B_ERROR; return B_OK; } status_t PoorManWindow::StartServer() { if (fServer == NULL) fServer = new PoorManServer(fWebDirectory.String(), fMaxConnections, fDirListFlag, fIndexFileName.String()); poorman_log(B_TRANSLATE("Starting up... ")); if (fServer->Run() != B_OK) { return B_ERROR; } fStatus = true; UpdateStatusLabelAndMenuItem(); poorman_log(B_TRANSLATE("done.\n"), false, NULL, GREEN); return B_OK; } status_t PoorManWindow::StopServer() { if (fServer == NULL) return B_ERROR; poorman_log(B_TRANSLATE("Shutting down.\n")); fServer->Stop(); fStatus = false; UpdateStatusLabelAndMenuItem(); return B_OK; } void PoorManWindow::SetLogPath(const char* str) { if (!strcmp(fLogPath, str)) return; BFile* temp = new BFile(str, B_CREATE_FILE | B_WRITE_ONLY | B_OPEN_AT_END); if (temp->InitCheck() != B_OK) { delete temp; return; } if (pthread_rwlock_wrlock(&fLogFileLock) == 0) { delete fLogFile; fLogFile = temp; pthread_rwlock_unlock(&fLogFileLock); } else { delete temp; return; } fLogPath.SetTo(str); }