xref: /haiku/src/tests/add-ons/print/ppd/ui/PPDConfigView.cpp (revision bab64f65bb775dc23060e276f1f1c4498ab7af6c)
1 /*
2  * Copyright 2008, Haiku.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Michael Pfeiffer <laplace@users.sourceforge.net>
7  */
8 
9 #include "PPDConfigView.h"
10 #include "PPDParser.h"
11 #include "StatementListVisitor.h"
12 #include "UIUtils.h"
13 
14 #include <Box.h>
15 #include <CheckBox.h>
16 #include <Menu.h>
17 #include <MenuField.h>
18 #include <MenuItem.h>
19 #include <RadioButton.h>
20 #include <ScrollView.h>
21 #include <StringView.h>
22 #include <Window.h>
23 
24 // margin
25 const float kLeftMargin = 3.0;
26 const float kRightMargin = 3.0;
27 const float kTopMargin = 3.0;
28 const float kBottomMargin = 3.0;
29 
30 // space between views
31 const float kHorizontalSpace = 8.0;
32 const float kVerticalSpace = 8.0;
33 
34 // Message what values
35 const uint32 kMsgBooleanChanged   = 'bool';
36 const uint32 kMsgStringChanged    = 'strc';
37 
38 #include <stdio.h>
39 
40 class DefaultValueExtractor : public StatementListVisitor
41 {
42 	BMessage fDefaultValues;
43 
44 public:
DoDefault(Statement * statement)45 	void DoDefault(Statement* statement)
46 	{
47 		const char* keyword = statement->GetKeywordString();
48 		const char* value = statement->GetValueString();
49 		if (keyword != NULL && value != NULL) {
50 			fDefaultValues.AddString(keyword, value);
51 		}
52 	}
53 
GetDefaultValues()54 	const BMessage& GetDefaultValues()
55 	{
56 		return fDefaultValues;
57 	}
58 };
59 
60 class CategoryBuilder : public StatementListVisitor
61 {
62 	BOutlineListView* fCategories;
63 
64 public:
CategoryBuilder(BOutlineListView * categories)65 	CategoryBuilder(BOutlineListView* categories)
66 		: fCategories(categories)
67 	{}
68 
AddStatement(const char * text,Statement * statement)69 	void AddStatement(const char* text, Statement* statement)
70 	{
71 		if (text != NULL) {
72 			BStringItem* item = new CategoryItem(text, statement, GetLevel());
73 			fCategories->AddItem(item);
74 		}
75 	}
76 
BeginGroup(GroupStatement * group)77 	void BeginGroup(GroupStatement* group)
78 	{
79 		const char* translation = group->GetGroupTranslation();
80 		const char* name = group->GetGroupName();
81 
82 		const char* text = NULL;
83 		if (translation != NULL) {
84 			text = translation;
85 		} else {
86 			text = name;
87 		}
88 
89 		AddStatement(text, group->GetStatement());
90 	}
91 };
92 
93 
94 /*
95 DetailsBuilder adds views to the details view and sets the
96 value to the one specified in the settings.
97 
98 The group type determines the view to be used for user input:
99  Type     Input
100  ---------------------
101  Boolean  BRadioButton
102  PickOne  BMenuField
103  PickMany BCheckBox
104  Unknown  BCheckBox
105 */
106 class DetailsBuilder : public StatementListVisitor
107 {
108 	BView*          fParent;
109 	BView*          fDetails;
110 	BRect           fBounds;
111 	const char*     fKeyword;
112 	const char*     fValue;
113 	GroupStatement  fGroup;
114 	BMenu*          fMenu;
115 	BMenuField*     fMenuField;
116 	const BMessage& fSettings;
117 
118 	void AddView(BView* view);
119 
120 	BMessage* GetMessage(uint32 what, const char* option);
121 
122 public:
123 	DetailsBuilder(BView* parent, BView* details, BRect bounds, Statement* group, const BMessage& settings);
124 
GetBounds()125 	BRect GetBounds() { return fBounds; }
126 
127 	void Visit(StatementList* list);
128 
129 	void DoValue(Statement* statement);
130 };
131 
AddView(BView * view)132 void DetailsBuilder::AddView(BView* view)
133 {
134 	if (view != NULL) {
135 		fDetails->AddChild(view);
136 		view->ResizeToPreferred();
137 
138 		fBounds.OffsetBy(0, view->Bounds().Height()+1);
139 
140 		BControl* control = dynamic_cast<BControl*>(view);
141 		if (control != NULL) {
142 			control->SetTarget(fParent);
143 		}
144 	}
145 }
146 
DetailsBuilder(BView * parent,BView * details,BRect bounds,Statement * group,const BMessage & settings)147 DetailsBuilder::DetailsBuilder(BView* parent, BView* details, BRect bounds, Statement* group, const BMessage& settings)
148 	: fParent(parent)
149 	, fDetails(details)
150 	, fBounds(bounds)
151 	, fGroup(group)
152 	, fMenu(NULL)
153 	, fMenuField(NULL)
154 	, fSettings(settings)
155 {
156 	fKeyword = fGroup.GetGroupName();
157 
158 	if (fKeyword == NULL) return;
159 
160 	fValue = settings.FindString(fKeyword);
161 
162 	const char* label = fGroup.GetGroupTranslation();
163 	if (label == NULL) {
164 		label = fKeyword;
165 	}
166 
167 	BView* view = NULL;
168 	if (fGroup.GetType() == GroupStatement::kPickOne) {
169 		fMenu = new BMenu("<pick one>");
170 		fMenu->SetRadioMode(true);
171 		fMenu->SetLabelFromMarked(true);
172 		fMenuField = new BMenuField(fBounds, "menuField", label, fMenu);
173 		view = fMenuField;
174 	} else if (fGroup.GetType() == GroupStatement::kBoolean) {
175 		BMessage* message = GetMessage(kMsgBooleanChanged, "");
176 		BCheckBox* cb = new BCheckBox(fBounds, "", label, message);
177 		view = cb;
178 		cb->SetValue((fValue != NULL && strcmp(fValue, "True") == 0)
179 			? B_CONTROL_ON
180 			: B_CONTROL_OFF);
181 	}
182 
183 	AddView(view);
184 }
185 
Visit(StatementList * list)186 void DetailsBuilder::Visit(StatementList* list)
187 {
188 	if (fKeyword == NULL) return;
189 
190 	StatementListVisitor::Visit(list);
191 }
192 
GetMessage(uint32 what,const char * option)193 BMessage* DetailsBuilder::GetMessage(uint32 what, const char* option)
194 {
195 	BMessage* message = new BMessage(what);
196 	message->AddString("keyword", fKeyword);
197 
198 	if (option != NULL) {
199 		message->AddString("option", option);
200 	}
201 	return message;
202 }
203 
DoValue(Statement * statement)204 void DetailsBuilder::DoValue(Statement* statement)
205 {
206 	if (GetLevel() != 0) return;
207 	if (strcmp(fKeyword, statement->GetKeywordString()) != 0) return;
208 
209 	const char* text = NULL;
210 	const char* option = statement->GetOptionString();
211 	if (statement->GetTranslationString() != NULL) {
212 		text = statement->GetTranslationString();
213 	} else if (option != NULL) {
214 		text = option;
215 	}
216 
217 	if (text == NULL) return;
218 
219 	BView* view = NULL;
220 	BMessage* message = NULL;
221 
222 	if (fGroup.GetType() == GroupStatement::kPickMany ||
223 		fGroup.GetType() == GroupStatement::kUnknown) {
224 		message = GetMessage(kMsgStringChanged, option);
225 		view = new BCheckBox(fBounds, "", text, message);
226 	} else if (fGroup.GetType() == GroupStatement::kPickOne) {
227 		message = GetMessage(kMsgStringChanged, option);
228 		BMenuItem* item = new BMenuItem(text, message);
229 		item->SetTarget(fParent);
230 		fMenu->AddItem(item);
231 		if (fValue != NULL && option != NULL && strcmp(fValue, option) == 0) {
232 			item->SetMarked(true);
233 		}
234 	}
235 
236 	AddView(view);
237 }
238 
239 #define kBoxHeight       20
240 #define kBoxBottomMargin 4
241 #define kBoxLeftMargin   8
242 #define kBoxRightMargin  8
243 
244 #define kItemLeftMargin   5
245 #define kItemRightMargin  5
246 
247 class PPDBuilder : public StatementListVisitor
248 {
249 	BView*    fParent;
250 	BView*    fView;
251 	BRect     fBounds;
252 	BMessage& fSettings;
253 	BList     fNestedBoxes;
254 
IsTop()255 	bool IsTop()
256 	{
257 		return fNestedBoxes.CountItems() == 0;
258 	}
259 
Push(BView * view)260 	void Push(BView* view)
261 	{
262 		fNestedBoxes.AddItem(view);
263 	}
264 
Pop()265 	void Pop()
266 	{
267 		fNestedBoxes.RemoveItem((int32)fNestedBoxes.CountItems()-1);
268 	}
269 
GetView()270 	BView* GetView()
271 	{
272 		if (IsTop()) {
273 			return fView;
274 		} else {
275 			return (BView*)fNestedBoxes.ItemAt(fNestedBoxes.CountItems()-1);
276 		}
277 	}
278 
279 
280 
GetControlBounds()281 	BRect GetControlBounds()
282 	{
283 		if (IsTop()) {
284 			BRect bounds(fBounds);
285 			bounds.left  += kItemLeftMargin /** GetLevel()*/;
286 			bounds.right -= kItemRightMargin /** GetLevel()*/;
287 			return bounds;
288 		}
289 
290 		BView* box = GetView();
291 		BRect bounds(box->Bounds());
292 		bounds.top = bounds.bottom - kBoxBottomMargin;
293 		bounds.bottom = bounds.top + kBoxHeight;
294 		bounds.left += kBoxLeftMargin;
295 		bounds.right -= kBoxRightMargin;
296 		return bounds;
297 	}
298 
IsUIGroup(GroupStatement * group)299 	bool IsUIGroup(GroupStatement* group)
300 	{
301 		return group->IsUIGroup() || group->IsJCL();
302 	}
303 
UpdateParentHeight(float height)304 	void UpdateParentHeight(float height)
305 	{
306 		if (IsTop()) {
307 			fBounds.OffsetBy(0, height);
308 		} else {
309 			BView* parent = GetView();
310 			parent->ResizeBy(0, height);
311 		}
312 	}
313 
314 public:
PPDBuilder(BView * parent,BView * view,BMessage & settings)315 	PPDBuilder(BView* parent, BView* view, BMessage& settings)
316 		: fParent(parent)
317 		, fView(view)
318 		, fBounds(view->Bounds())
319 		, fSettings(settings)
320 	{
321 		RemoveChildren(view);
322 		fBounds.OffsetTo(0, 0);
323 		fBounds.left += kLeftMargin;
324 		fBounds.top += kTopMargin;
325 		fBounds.right -= kRightMargin;
326 	}
327 
GetBounds()328 	BRect GetBounds()
329 	{
330 		return BRect(0, 0, fView->Bounds().Width(), fBounds.top);
331 	}
332 
AddUIGroup(const char * text,Statement * statement)333 	void AddUIGroup(const char* text, Statement* statement)
334 	{
335 		if (statement->GetChildren() == NULL) return;
336 		DetailsBuilder builder(fParent, GetView(), GetControlBounds(), statement, fSettings);
337 		builder.Visit(statement->GetChildren());
338 
339 		if (IsTop()) {
340 			fBounds.OffsetTo(fBounds.left, builder.GetBounds().top);
341 		} else {
342 			BView* box = GetView();
343 			box->ResizeTo(box->Bounds().Width(), builder.GetBounds().top + kBoxBottomMargin);
344 		}
345 	}
346 
OpenGroup(const char * text)347 	void OpenGroup(const char* text)
348 	{
349 		if (text != NULL) {
350 			BBox* box = new BBox(GetControlBounds(), text);
351 			box->SetLabel(text);
352 			GetView()->AddChild(box);
353 			Push(box);
354 			box->ResizeTo(box->Bounds().Width(), kBoxHeight);
355 		}
356 	}
357 
CloseGroup()358 	void CloseGroup()
359 	{
360 		if (!IsTop()) {
361 			BView* box = GetView();
362 			Pop();
363 			UpdateParentHeight(box->Bounds().Height());
364 		}
365 	}
366 
BeginGroup(GroupStatement * group)367 	void BeginGroup(GroupStatement* group)
368 	{
369 		const char* translation = group->GetGroupTranslation();
370 		const char* name = group->GetGroupName();
371 
372 		const char* text = NULL;
373 		if (translation != NULL) {
374 			text = translation;
375 		} else {
376 			text = name;
377 		}
378 
379 		if (IsUIGroup(group)) {
380 			AddUIGroup(text, group->GetStatement());
381 		} else {
382 			OpenGroup(text);
383 		}
384 	}
385 
EndGroup(GroupStatement * group)386 	void EndGroup(GroupStatement* group)
387 	{
388 		if (!IsUIGroup(group)) {
389 			CloseGroup();
390 		}
391 	}
392 };
393 
PPDConfigView(BRect bounds,const char * name,uint32 resizeMask,uint32 flags)394 PPDConfigView::PPDConfigView(BRect bounds, const char *name, uint32 resizeMask, uint32 flags)
395 	: BView(bounds, name, resizeMask, flags)
396 	, fPPD(NULL)
397 {
398 	// add category outline list view
399 	bounds.OffsetTo(0, 0);
400 	BRect listBounds(bounds.left + kLeftMargin, bounds.top + kTopMargin,
401 		bounds.right - kHorizontalSpace, bounds.bottom - kBottomMargin);
402 	listBounds.right -= B_V_SCROLL_BAR_WIDTH;
403 	listBounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
404 
405 	BStringView* label = new BStringView(listBounds, "printer-settings", "Printer Settings:");
406 	AddChild(label);
407 	label->ResizeToPreferred();
408 
409 	listBounds.top += label->Bounds().bottom + 5;
410 
411 	// add details view
412 	fDetails = new BView(listBounds, "details", B_FOLLOW_ALL_SIDES, B_WILL_DRAW);
413 	fDetails->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
414 
415 	BScrollView* scrollView = new BScrollView("details-scroll-view",
416 		fDetails, B_FOLLOW_ALL_SIDES, 0, true, true);
417 
418 	AddChild(scrollView);
419 }
420 
SetScrollBar(BScrollBar * scroller,float contents,float client)421 void SetScrollBar(BScrollBar* scroller, float contents, float client)
422 {
423 	if (scroller != NULL) {
424 		float extent = contents - client;
425 		if (extent >= 0) {
426 			scroller->SetRange(0, extent);
427 			scroller->SetProportion(1-extent / contents);
428 			scroller->SetSteps(20, client);
429 		} else {
430 			scroller->SetRange(0, 0);
431 		}
432 	}
433 }
434 
FillCategories()435 void PPDConfigView::FillCategories()
436 {
437 	if (fPPD == NULL) return;
438 
439 	PPDBuilder builder(this, fDetails, fSettings);
440 	builder.Visit(fPPD);
441 	BScrollBar* scroller = fDetails->ScrollBar(B_VERTICAL);
442 	SetScrollBar(scroller, builder.GetBounds().Height(), fDetails->Bounds().Height());
443 	scroller = fDetails->ScrollBar(B_HORIZONTAL);
444 	SetScrollBar(scroller, builder.GetBounds().Width(), fDetails->Bounds().Width());
445 }
446 
FillDetails(Statement * statement)447 void PPDConfigView::FillDetails(Statement* statement)
448 {
449 	RemoveChildren(fDetails);
450 
451 	if (statement == NULL) {
452 		return;
453 	}
454 
455 	StatementList* children= statement->GetChildren();
456 	if (children == NULL) {
457 		return;
458 	}
459 
460 	BRect bounds(fDetails->Bounds());
461 	bounds.OffsetTo(kLeftMargin, kTopMargin);
462 	DetailsBuilder builder(this, fDetails, bounds, statement, fSettings);
463 	builder.Visit(children);
464 }
465 
BooleanChanged(BMessage * msg)466 void PPDConfigView::BooleanChanged(BMessage* msg)
467 {
468 	const char* keyword = msg->FindString("keyword");
469 
470 	int32 value;
471 	if (msg->FindInt32("be:value", &value) == B_OK) {
472 		const char* option;
473 		if (value) {
474 			option = "True";
475 		} else {
476 			option = "False";
477 		}
478 		fSettings.ReplaceString(keyword, option);
479 	}
480 }
481 
StringChanged(BMessage * msg)482 void PPDConfigView::StringChanged(BMessage* msg)
483 {
484 	const char* keyword = msg->FindString("keyword");
485 	const char* option = msg->FindString("option");
486 
487 	if (keyword != NULL && keyword != NULL) {
488 		fSettings.ReplaceString(keyword, option);
489 	}
490 }
491 
MessageReceived(BMessage * msg)492 void PPDConfigView::MessageReceived(BMessage* msg)
493 {
494 	switch (msg->what) {
495 		case kMsgBooleanChanged: BooleanChanged(msg);
496 			break;
497 		case kMsgStringChanged: StringChanged(msg);
498 			break;
499 	}
500 
501 	BView::MessageReceived(msg);
502 }
503 
SetupSettings(const BMessage & currentSettings)504 void PPDConfigView::SetupSettings(const BMessage& currentSettings)
505 {
506 	DefaultValueExtractor extractor;
507 	extractor.Visit(fPPD);
508 
509 	const BMessage &defaultValues(extractor.GetDefaultValues());
510 	fSettings.MakeEmpty();
511 
512 	char* name;
513 	type_code code;
514 	for (int32 index = 0; defaultValues.GetInfo(B_STRING_TYPE, index, &name, &code) == B_OK; index ++) {
515 		const char* value = currentSettings.FindString(name);
516 		if (value == NULL) {
517 			value = defaultValues.FindString(name);
518 		}
519 		if (value != NULL) {
520 			fSettings.AddString(name, value);
521 		}
522 	}
523 }
524 
Set(const char * file,const BMessage & currentSettings)525 void PPDConfigView::Set(const char* file, const BMessage& currentSettings)
526 {
527 	delete fPPD;
528 
529 	PPDParser parser(file);
530 	fPPD = parser.ParseAll();
531 	if (fPPD == NULL) {
532 		fprintf(stderr, "Parsing error (%s): %s\n", file, parser.GetErrorMessage());
533 	}
534 
535 	SetupSettings(currentSettings);
536 	FillCategories();
537 }
538