1/*
2 * Copyright (C) 2010 Stephan A��mus <superstippi@gmx.de>
3 * Copyright (C) 2010 Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
4 *
5 * Distributed under the terms of the MIT licence.
6 */
7
8#include "ApplicationWindow.h"
9
10#include <stdio.h>
11
12#include <Alert.h>
13#include <Button.h>
14#include <Catalog.h>
15#include <ControlLook.h>
16#include <Entry.h>
17#include <File.h>
18#include <FindDirectory.h>
19#include <GroupLayout.h>
20#include <GroupLayoutBuilder.h>
21#include <MenuBar.h>
22#include <MenuItem.h>
23#include <Path.h>
24#include <Roster.h>
25#include <ScrollView.h>
26#include <SeparatorView.h>
27#include <SpaceLayoutItem.h>
28
29#include "ApplicationView.h"
30
31
32enum {
33	INIT = 'init',
34};
35
36
37class ApplicationsContainerView : public BGroupView {
38public:
39	ApplicationsContainerView()
40		:
41		BGroupView(B_VERTICAL, 0.0)
42	{
43		SetFlags(Flags() | B_PULSE_NEEDED);
44		SetViewColor(245, 245, 245);
45		AddChild(BSpaceLayoutItem::CreateGlue());
46	}
47
48	virtual BSize MinSize()
49	{
50		BSize minSize = BGroupView::MinSize();
51		return BSize(minSize.width, 80);
52	}
53
54protected:
55	virtual void DoLayout()
56	{
57		BGroupView::DoLayout();
58		if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
59			BSize minSize = BGroupView::MinSize();
60			float height = Bounds().Height();
61			float max = minSize.height - height;
62			scrollBar->SetRange(0, max);
63			if (minSize.height > 0)
64				scrollBar->SetProportion(height / minSize.height);
65			else
66				scrollBar->SetProportion(1);
67		}
68	}
69};
70
71
72class ApplicationContainerScrollView : public BScrollView {
73public:
74	ApplicationContainerScrollView(BView* target)
75		:
76		BScrollView("Applications scroll view", target, 0, false, true,
77			B_NO_BORDER)
78	{
79	}
80
81protected:
82	virtual void DoLayout()
83	{
84		BScrollView::DoLayout();
85		// Tweak scroll bar layout to hide part of the frame for better looks.
86		BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
87		scrollBar->MoveBy(1, -1);
88		scrollBar->ResizeBy(0, 2);
89		Target()->ResizeBy(1, 0);
90		// Set the scroll steps
91		if (BView* item = Target()->ChildAt(0)) {
92			scrollBar->SetSteps(item->MinSize().height + 1,
93				item->MinSize().height + 1);
94		}
95	}
96};
97
98
99// #pragma mark -
100
101
102ApplicationWindow::ApplicationWindow(BRect frame, bool visible)
103	:
104	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("PackageManager"),
105		B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
106		B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
107	fMinimizeOnClose(false)
108{
109	SetPulseRate(1000000);
110
111	SetLayout(new BGroupLayout(B_VERTICAL, 0.0));
112
113	ApplicationsContainerView* downloadsGroupView = new ApplicationsContainerView();
114	fApplicationViewsLayout = downloadsGroupView->GroupLayout();
115
116	BMenuBar* menuBar = new BMenuBar("Menu bar");
117	BMenu* menu = new BMenu("Actions");
118	menu->AddItem(new BMenuItem("Apply changes",
119		new BMessage('NADA')));
120	menu->AddItem(new BMenuItem("Exit", new BMessage(B_QUIT_REQUESTED)));
121	menuBar->AddItem(menu);
122
123	fApplicationsScrollView = new ApplicationContainerScrollView(downloadsGroupView);
124
125	fDiscardButton = new BButton("Revert",
126		new BMessage('NADA'));
127	fDiscardButton->SetEnabled(false);
128
129	fApplyChangesButton = new BButton("Apply",
130		new BMessage('NADA'));
131	fApplyChangesButton->SetEnabled(false);
132
133	const float spacing = be_control_look->DefaultItemSpacing();
134
135	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0)
136		.Add(menuBar)
137		.Add(fApplicationsScrollView)
138		.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
139		.Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing)
140			.AddGlue()
141			.Add(fApplyChangesButton)
142			.Add(fDiscardButton)
143			.SetInsets(12, 5, 12, 5)
144		)
145	);
146
147	PostMessage(INIT);
148
149	if (!visible)
150		Hide();
151	Show();
152}
153
154
155ApplicationWindow::~ApplicationWindow()
156{
157}
158
159
160void
161ApplicationWindow::DispatchMessage(BMessage* message, BHandler* target)
162{
163	// We need to intercept mouse down events inside the area of download
164	// progress views (regardless of whether they have children at the click),
165	// so that they may display a context menu.
166	BPoint where;
167	int32 buttons;
168	if (message->what == B_MOUSE_DOWN
169		&& message->FindPoint("screen_where", &where) == B_OK
170		&& message->FindInt32("buttons", &buttons) == B_OK
171		&& (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
172		for (int32 i = fApplicationViewsLayout->CountItems() - 1;
173				BLayoutItem* item = fApplicationViewsLayout->ItemAt(i); i--) {
174			ApplicationView* view = dynamic_cast<ApplicationView*>(
175				item->View());
176			if (!view)
177				continue;
178			BPoint viewWhere(where);
179			view->ConvertFromScreen(&viewWhere);
180			if (view->Bounds().Contains(viewWhere)) {
181				view->ShowContextMenu(where);
182				return;
183			}
184		}
185	}
186	BWindow::DispatchMessage(message, target);
187}
188
189
190void
191ApplicationWindow::MessageReceived(BMessage* message)
192{
193	switch (message->what) {
194		case INIT:
195		{
196			break;
197		}
198
199		default:
200			BWindow::MessageReceived(message);
201			break;
202	}
203}
204
205
206bool
207ApplicationWindow::QuitRequested()
208{
209	if (fMinimizeOnClose) {
210		if (!IsMinimized())
211			Minimize(true);
212	} else {
213		if (!IsHidden())
214			Hide();
215	}
216	return false;
217}
218
219
220void
221ApplicationWindow::SetMinimizeOnClose(bool minimize)
222{
223	if (Lock()) {
224		fMinimizeOnClose = minimize;
225		Unlock();
226	}
227}
228
229
230// #pragma mark - private
231
232
233void
234ApplicationWindow::AddCategory(const char* name, const char* icon,
235	const char* description)
236{
237	ApplicationView* category = new ApplicationView(name, icon, description);
238	fApplicationViewsLayout->AddView(0, category);
239}
240
241
242void
243ApplicationWindow::AddApplication(const BMessage* info)
244{
245	ApplicationView* app = new ApplicationView(info);
246	fApplicationViewsLayout->AddView(0, app);
247}
248
249
250void
251ApplicationWindow::_ValidateButtonStatus()
252{
253	int32 finishedCount = 0;
254	int32 missingCount = 0;
255	for (int32 i = fApplicationViewsLayout->CountItems() - 1;
256			BLayoutItem* item = fApplicationViewsLayout->ItemAt(i); i--) {
257		ApplicationView* view = dynamic_cast<ApplicationView*>(
258			item->View());
259		if (!view)
260			continue;
261	}
262	fDiscardButton->SetEnabled(finishedCount > 0);
263	fApplyChangesButton->SetEnabled(missingCount > 0);
264}
265
266