/*
 * Copyright 2008-2015, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "ActivityWindow.h"
 
#include <stdio.h>
 
#include <Application.h>
#include <Catalog.h>
#include <File.h>
#include <FindDirectory.h>
#ifdef __HAIKU__
#include <GroupLayout.h>
#endif
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Path.h>
#include <Roster.h>
 
#include "ActivityMonitor.h"
#include "ActivityView.h"
#include "DataSource.h"
#include "SettingsWindow.h"
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ActivityWindow"
 
 
static const uint32 kMsgAddView = 'advw';
static const uint32 kMsgAlwaysOnTop = 'alot';
static const uint32 kMsgShowSettings = 'shst';
 
 
ActivityWindow::ActivityWindow()
	:
	BWindow(BRect(100, 100, 500, 350), B_TRANSLATE_SYSTEM_NAME("ActivityMonitor"),
	B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE)
{
	BMessage settings;
	_LoadSettings(settings);
 
	BRect frame;
	if (settings.FindRect("window frame", &frame) == B_OK) {
		MoveTo(frame.LeftTop());
		ResizeTo(frame.Width(), frame.Height());
	} else {
		float scaling = be_plain_font->Size() / 12.0f;
		ResizeTo(Frame().Width() * scaling, Frame().Height() * scaling);
		CenterOnScreen();
	}
 
#ifdef __HAIKU__
	BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0);
	SetLayout(layout);
 
	// create GUI
 
	BMenuBar* menuBar = new BMenuBar("menu");
	layout->AddView(menuBar);
 
	fLayout = new BGroupLayout(B_VERTICAL);
	float inset = ceilf(be_plain_font->Size() * 0.7);
	fLayout->SetInsets(B_USE_WINDOW_SPACING);
	fLayout->SetSpacing(inset);
 
	BView* top = new BView("top", 0, fLayout);
	layout->AddView(top);
	top->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
 
	BMessage viewState;
	int32 count = 0;
	for (int32 i = 0; settings.FindMessage("activity view", i, &viewState)
			== B_OK; i++) {
		ActivityView* view = new ActivityView("ActivityMonitor", &viewState);
		fLayout->AddItem(view->CreateHistoryLayoutItem());
		fLayout->AddItem(view->CreateLegendLayoutItem());
		count++;
	}
	if (count == 0) {
		// Add default views (memory & CPU usage)
		_AddDefaultView();
		_AddDefaultView();
	}
#else	// !__HAIKU__
	BView *layout = new BView(Bounds(), "topmost", B_FOLLOW_NONE, 0);
	AddChild(layout);
 
	// create GUI
	BRect mbRect(Bounds());
	mbRect.bottom = 10;
	BMenuBar* menuBar = new BMenuBar(mbRect, "menu");
	layout->AddChild(menuBar);
 
	BRect topRect(Bounds());
	topRect.top = menuBar->Bounds().bottom + 1;
 
	BView* top = new BView(topRect, "top", B_FOLLOW_ALL, 0);
	layout->AddChild(top);
 
	BMessage viewState;
	int32 count = 0;
	ActivityView *aview;
	BRect rect;
	for (int32 i = 0; settings.FindMessage("activity view", i, &viewState)
			== B_OK; i++) {
		aview = new ActivityView("ActivityMonitor", &viewState);
		if (!rect.IsValid())
			rect = aview->Bounds();
		else
			rect.OffsetBySelf(0.0, aview->Bounds().Height());
		top->AddChild(aview);
		count++;
	}
	if (count == 0)
		top->AddChild(new ActivityView("ActivityMonitor", NULL));
 
#endif
	// add menu
 
	// "File" menu
	BMenu* menu = new BMenu(B_TRANSLATE("File"));
	menu->AddItem(new BMenuItem(B_TRANSLATE("Add graph"),
		new BMessage(kMsgAddView)));
	menu->AddSeparatorItem();
 
	menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
		new BMessage(B_QUIT_REQUESTED), 'Q'));
	menu->SetTargetForItems(this);
	menuBar->AddItem(menu);
 
	// "Settings" menu
	menu = new BMenu(B_TRANSLATE("Settings"));
	menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS),
		new BMessage(kMsgShowSettings)));
 
	menu->AddSeparatorItem();
	fAlwaysOnTop = new BMenuItem(B_TRANSLATE("Always on top"), new BMessage(kMsgAlwaysOnTop));
	_SetAlwaysOnTop(settings.GetBool("always on top", false));
	menu->AddItem(fAlwaysOnTop);
 
	menu->SetTargetForItems(this);
	menuBar->AddItem(menu);
}
 
 
ActivityWindow::~ActivityWindow()
{
}
 
 
void
ActivityWindow::MessageReceived(BMessage* message)
{
	if (message->WasDropped()) {
		_MessageDropped(message);
		return;
	}
 
	switch (message->what) {
		case B_REFS_RECEIVED:
		case B_SIMPLE_DATA:
			_MessageDropped(message);
			break;
 
		case kMsgAddView:
		{
#ifdef __HAIKU__
			BView* firstView = fLayout->View()->ChildAt(0);
 
			_AddDefaultView();
 
			if (firstView != NULL)
				ResizeBy(0, firstView->Bounds().Height() + fLayout->Spacing());
#endif
			break;
		}
 
		case kMsgRemoveView:
		{
#ifdef __HAIKU__
			BView* view;
			if (message->FindPointer("view", (void**)&view) != B_OK)
				break;
 
			view->RemoveSelf();
			ResizeBy(0, -view->Bounds().Height() - fLayout->Spacing());
			delete view;
#endif
			break;
		}
 
		case kMsgShowSettings:
		{
			if (fSettingsWindow.IsValid()) {
				// Just bring the window to front (via scripting)
				BMessage toFront(B_SET_PROPERTY);
				toFront.AddSpecifier("Active");
				toFront.AddBool("data", true);
				fSettingsWindow.SendMessage(&toFront);
			} else {
				// Open new settings window
				BWindow* window = new SettingsWindow(this);
				window->Show();
 
				fSettingsWindow = window;
			}
			break;
		}
 
		case kMsgAlwaysOnTop:
		{
			_SetAlwaysOnTop(!fAlwaysOnTop->IsMarked());
			break;
		}
 
		case kMsgTimeIntervalUpdated:
			BroadcastToActivityViews(message);
			break;
 
		default:
			BWindow::MessageReceived(message);
			break;
	}
}
 
 
bool
ActivityWindow::QuitRequested()
{
	_SaveSettings();
	be_app->PostMessage(B_QUIT_REQUESTED);
	return true;
}
 
 
int32
ActivityWindow::ActivityViewCount() const
{
#ifdef __HAIKU__
	return fLayout->View()->CountChildren();
#else
	return 1;
#endif
}
 
 
ActivityView*
ActivityWindow::ActivityViewAt(int32 index) const
{
	return dynamic_cast<ActivityView*>(fLayout->View()->ChildAt(index));
}
 
 
bool
ActivityWindow::IsAlwaysOnTop() const
{
	return fAlwaysOnTop->IsMarked();
}
 
 
void
ActivityWindow::BroadcastToActivityViews(BMessage* message, BView* exceptToView)
{
	BView* view;
	for (int32 i = 0; (view = ActivityViewAt(i)) != NULL; i++) {
		if (view != exceptToView)
			PostMessage(message, view);
	}
}
 
 
bigtime_t
ActivityWindow::RefreshInterval() const
{
	ActivityView* view = ActivityViewAt(0);
	if (view != 0)
		return view->RefreshInterval();
 
	return 100000;
}
 
 
status_t
ActivityWindow::_OpenSettings(BFile& file, uint32 mode)
{
	BPath path;
	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
		return B_ERROR;
 
	path.Append("ActivityMonitor settings");
 
	return file.SetTo(path.Path(), mode);
}
 
 
status_t
ActivityWindow::_LoadSettings(BMessage& settings)
{
	BFile file;
	status_t status = _OpenSettings(file, B_READ_ONLY);
	if (status < B_OK)
		return status;
 
	return settings.Unflatten(&file);
}
 
 
status_t
ActivityWindow::_SaveSettings()
{
	BFile file;
	status_t status = _OpenSettings(file, B_WRITE_ONLY | B_CREATE_FILE
		| B_ERASE_FILE);
	if (status < B_OK)
		return status;
 
	BMessage settings('actm');
	status = settings.AddRect("window frame", Frame());
	if (status != B_OK)
		return status;
 
	status = settings.SetBool("always on top", fAlwaysOnTop->IsMarked());
	if (status != B_OK)
		return status;
 
#ifdef __HAIKU__
	BView* top = fLayout->View();
#else
	BView* top = ChildAt(0);
#endif
	int32 count = top->CountChildren();
	for (int32 i = 0; i < count; i++) {
		ActivityView* view = dynamic_cast<ActivityView*>(top->ChildAt(i));
		if (view == NULL)
			continue;
 
		BMessage viewState;
		status = view->SaveState(viewState);
		if (status == B_OK)
			status = settings.AddMessage("activity view", &viewState);
		if (status != B_OK)
			break;
	}
 
	if (status == B_OK)
		status = settings.Flatten(&file);
 
	return status;
}
 
 
void
ActivityWindow::_AddDefaultView()
{
	BMessage settings;
	settings.AddInt64("refresh interval", RefreshInterval());
 
	ActivityView* view = new ActivityView("ActivityMonitor", &settings);
 
	switch (ActivityViewCount()) {
		case 0:
			// The first view defaults to memory usage
			view->AddDataSource(new UsedMemoryDataSource());
			view->AddDataSource(new CachedMemoryDataSource());
			view->AddDataSource(new SwapSpaceDataSource());
			break;
		case 2:
			// The third view defaults to network in/out
			view->AddDataSource(new NetworkUsageDataSource(true));
			view->AddDataSource(new NetworkUsageDataSource(false));
			break;
		case 1:
		default:
			// Everything beyond that defaults to a CPU usage view
			view->AddDataSource(new CPUUsageDataSource());
			break;
	}
 
	fLayout->AddItem(view->CreateHistoryLayoutItem());
	fLayout->AddItem(view->CreateLegendLayoutItem());
}
 
 
void
ActivityWindow::_MessageDropped(BMessage* message)
{
	entry_ref ref;
	if (message->FindRef("refs", &ref) != B_OK) {
		// TODO: If app, then launch it, and add ActivityView for this one?
	}
}
 
 
void
ActivityWindow::_SetAlwaysOnTop(bool alwaysOnTop)
{
	SetFeel(alwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL);
	fAlwaysOnTop->SetMarked(alwaysOnTop);
	if (fSettingsWindow.IsValid() && alwaysOnTop) {
		// Change the settings window feel to modal (via scripting)
		BMessage toFront(B_SET_PROPERTY);
		toFront.AddSpecifier("Feel");
		toFront.AddInt32("data", B_MODAL_ALL_WINDOW_FEEL);
		fSettingsWindow.SendMessage(&toFront);
	}
}

V773 Visibility scope of the 'view' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'view' pointer was exited without releasing the memory. A memory leak is possible.