haiku/src/apps/poorman/PoorManWindow.cpp

751 lines
17 KiB
C++

/* PoorManWindow.cpp
*
* Philip Harrison
* Started: 4/25/2004
* Version: 0.1
*/
#include "PoorManWindow.h"
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <Alert.h>
#include <Box.h>
#include <Catalog.h>
#include <DateTimeFormat.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <OS.h>
#include <Path.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <StringView.h>
#include <TypeConstants.h>
#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<const time_t*>(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<const rgb_color*>(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"), 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);
}