PPD parser and configuration UI prototype from 2004. Maybe it can be of some use for the CUPS port HCD 2008 project.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26007 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Pfeiffer
2008-06-18 18:24:54 +00:00
parent ff0df461c4
commit 4420c1ceff
49 changed files with 7674 additions and 1 deletions
+4 -1
View File
@@ -1,5 +1,8 @@
SubDir HAIKU_TOP src tests add-ons print ;
# Skip PCL6 as jetlib.h is not in repository any more
# TODO if there are no license issues add it to directory "pcl6"
#SubInclude HAIKU_TOP src tests add-ons print pcl6 ;
SubInclude HAIKU_TOP src tests add-ons print pdf ;
SubInclude HAIKU_TOP src tests add-ons print pcl6 ;
SubInclude HAIKU_TOP src tests add-ons print ppd ;
SubInclude HAIKU_TOP src tests add-ons print transports ;
+6
View File
@@ -0,0 +1,6 @@
SubDir HAIKU_TOP src tests add-ons print ppd ;
SubInclude HAIKU_TOP src tests add-ons print ppd model ;
SubInclude HAIKU_TOP src tests add-ons print ppd parser ;
SubInclude HAIKU_TOP src tests add-ons print ppd ui ;
SubInclude HAIKU_TOP src tests add-ons print ppd test ;
@@ -0,0 +1,78 @@
PPD
# translationKeyword only if stand-alone = no *Keyword statements
"*Default"Keyword ":" StringValue ["/" translationString ].
"*"Keyword [Option ["/" translationString ]] ":" Value ["/" translationString ].
"*?"Keyword ":" InvocationValue ["/" translationString ].
"*Param"Keyword [Option ["/" translationString ]] ":" Value ["/" translationString ].
Line comments start with "*%".
*Include
Keyword = ident. # ,'.',/
Option = ident {"." ident}.
# translationString must not follow SymbolValue!
# translationString is terminated by ":" if it follows a Option
# or CR if it follows a Value.
Value = InvocationValue | QuotedValue | SymbolValue | StringValue | NoValue.
# ps code
# requires statement with option keyword!
# Must end with in separate line *End if multiline.
InvocationValue = '"' printable '"' ["/" translationString ].
# requires statement without an option keyword!
# *JCL can have an option keyword!
# Must end with in separate line *End if multiline.
QuotedValue = '"' literalSubstring '"'.
SymbolValue = "^" printable. # without whitespaces
# In case of translation string it is separated by newline or slash.
StringValue = printable . # first char must not be " or ^
literalSubstring = { hexadecimalSubstring | char }.
hexadecimalSubstring = "<" { whitespace* hexdigit hexdigit} ">".
hexdigit = ['0'..'9','a'..'f','A'..'F'].
# No option keyword present. Keyword stands alone.
NoValue =.
ident = identChar+.
identChar = [33..126].
printable = printableChar+.
printableChar = [32..126] | tab | lf | cr. # " belongs not to printableChar!
translationString = literalSubstring. # without lf and cr.
char = [32..255] | tab | lf | cr.
whitespace= space | tab.
tab=9.
lf=10.
cr=13.
space=32.
max size of MainKeyword = 40 characters
File Structure see 3.8
Standard Option Values:
True | False | None | Unknown
OpenUI Keyword: PickOne | PickMany | Boolean
CloseUI: Keyword
Open[Sub]Group: string
# InstallableOptions is a registered option!
Close[Sub]Group: string
UIConstraints: keyword1 option1 keyword2 option2
# option 1 can be omitted see page 57
# Unique name
ModelName: "text"
+17
View File
@@ -0,0 +1,17 @@
Scanner/Parser:
Handle JCLKeywords correctly.
Convert strings according to LanguageEncoding.
PPDConfigView.cpp:
Add outline sub items in reverse order
Provide UI for single and multiple choice
Read settings
Write settings
Read default settings
Revert and OK button
Handle constraints
Integrate to PS driver
- Page setup dialog
- Job setup dialog
- Provide PS driver setup model selection dialog
+12
View File
@@ -0,0 +1,12 @@
SubDir HAIKU_TOP src tests add-ons print ppd model ;
# SetSubDirSupportedPlatformsBeOSCompatible ;
StaticLibrary libppdtest.a :
PPD.cpp
Statement.cpp
StatementList.cpp
StatementListVisitor.cpp
StatementWrapper.cpp
Value.cpp
;
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "PPD.h"
#include <stdio.h>
PPD::PPD()
: StatementList(true)
, fSymbols(false)
{
}
PPD::~PPD()
{
}
void PPD::Print()
{
printf("<ppd>\n");
StatementList::Print();
printf("</ppd>\n");
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PPD_H
#define _PPD_H
#include "Statement.h"
// PostScript Printer Definiton
class PPD : public StatementList {
private:
StatementList fSymbols;
public:
PPD();
virtual ~PPD();
// Prints the PPD to stdout in XML
void Print();
};
#endif
@@ -0,0 +1,187 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "Statement.h"
#include <stdio.h>
Statement::Statement()
: fType(kUnknown)
, fKeyword(NULL)
, fOption(NULL)
, fValue(NULL)
, fChildren(NULL)
{
}
Statement::~Statement()
{
delete fKeyword;
delete fOption;
delete fValue;
delete fChildren;
}
void Statement::SetType(Type type)
{
fType = type;
}
Statement::Type Statement::GetType()
{
return fType;
}
void Statement::SetKeyword(BString* keyword)
{
fKeyword = keyword;
}
BString* Statement::GetKeyword()
{
return fKeyword;
}
void Statement::SetOption(Value* option)
{
fOption = option;
}
Value* Statement::GetOption()
{
return fOption;
}
void Statement::SetValue(Value* value)
{
fValue = value;
}
Value* Statement::GetValue()
{
return fValue;
}
StatementList* Statement::GetChildren()
{
return fChildren;
}
void Statement::AddChild(Statement* statement)
{
if (fChildren == NULL) {
fChildren = new StatementList(true);
}
fChildren->Add(statement);
}
const char* Statement::GetKeywordString()
{
if (fKeyword != NULL) {
return fKeyword->String();
}
return NULL;
}
const char* Statement::GetOptionString()
{
Value* option = GetOption();
if (option != NULL) {
return option->GetValueString();
}
return NULL;
}
const char* Statement::GetTranslationString()
{
Value* option = GetOption();
if (option != NULL) {
return option->GetTranslationString();
}
return NULL;
}
const char* Statement::GetValueString()
{
Value* value = GetValue();
if (value != NULL) {
return value->GetValueString();
}
return NULL;
}
const char* Statement::GetValueTranslationString()
{
Value* value = GetValue();
if (value != NULL) {
return value->GetTranslationString();
}
return NULL;
}
const char* Statement::ElementForType() {
switch (fType) {
case kDefault: return "Default";
break;
case kParam: return "Param";
break;
case kQuery: return "Query";
break;
case kValue: return "Value";
break;
case kUnknown: return "Unknown";
break;
}
return NULL;
}
void Statement::Print()
{
bool hasValue = fValue != NULL;
bool hasOption = fOption != NULL;
printf("<%s", ElementForType());
if (fKeyword != NULL) {
printf(" keyword=\"%s\"", fKeyword->String());
}
if (hasValue || hasOption) {
printf(">\n");
} else {
printf("/>\n");
}
if (hasOption) {
printf("\t<option>\n");
fOption->Print();
printf("\t</option>\n");
}
if (hasValue) {
printf("\t<value>\n");
fValue->Print();
printf("\t</value>\n");
}
if (GetChildren() != NULL) {
printf("\t<children>\n");
GetChildren()->Print();
printf("\t</children>\n");
}
if (hasValue || hasOption) {
printf("</%s>\n\n", ElementForType());
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _STATEMENT_H
#define _STATEMENT_H
#include "StatementList.h"
#include "Value.h"
class Statement
{
public:
enum Type {
kDefault,
kQuery,
kValue,
kParam,
kUnknown
};
private:
Type fType;
BString* fKeyword;
Value* fOption;
Value* fValue;
StatementList* fChildren;
const char* ElementForType();
public:
Statement();
virtual ~Statement();
void SetType(Type type);
Type GetType();
void SetKeyword(BString* keyword);
// mandatory in a valid statement
BString* GetKeyword();
void SetOption(Value* value);
// optional in a valid statement
Value* GetOption();
void SetValue(Value* value);
// optional in a valid statement
Value* GetValue();
void AddChild(Statement* statement);
// optional in a valid statement
StatementList* GetChildren();
// convenience methods
bool IsDefaultStatement() { return fType == kDefault; }
bool IsQueryStatement() { return fType == kQuery; }
bool IsValueStatement() { return fType == kValue; }
bool IsParamStatement() { return fType == kParam; }
bool IsUnknownStatement() { return fType == kUnknown; }
const char* GetKeywordString();
const char* GetOptionString();
const char* GetTranslationString();
const char* GetValueString();
const char* GetValueTranslationString();
void Print();
};
#endif
@@ -0,0 +1,74 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "StatementList.h"
#include "Statement.h"
#include <stdio.h>
StatementList::StatementList(bool ownsStatements)
: fOwnsStatements(ownsStatements)
{
}
StatementList::~StatementList()
{
if (fOwnsStatements) {
for (int32 i = 0; i < Size(); i ++) {
Statement* statement = StatementAt(i);
delete statement;
}
}
fList.MakeEmpty();
}
void StatementList::Add(Statement* statement)
{
fList.AddItem(statement);
}
void StatementList::Remove(Statement* statement)
{
fList.RemoveItem(statement);
}
int32 StatementList::Size()
{
return fList.CountItems();
}
Statement* StatementList::StatementAt(int32 index)
{
return (Statement*)fList.ItemAt(index);
}
Statement* StatementList::GetStatement(const char* keyword)
{
for (int32 i = 0; i < fList.CountItems(); i ++) {
if (strcmp(keyword, StatementAt(i)->GetKeywordString()) == 0) {
return StatementAt(i);
}
}
return NULL;
}
const char* StatementList::GetValue(const char* keyword)
{
Statement* statement = GetStatement(keyword);
if (statement != NULL) {
return statement->GetValueString();
}
return NULL;
}
void StatementList::Print()
{
for (int32 i = 0; i < Size(); i ++) {
StatementAt(i)->Print();
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _STATEMENT_LIST_H
#define _STATEMENT_LIST_H
#include <List.h>
class Statement;
class StatementList {
private:
BList fList;
bool fOwnsStatements;
public:
StatementList(bool ownsStatements);
~StatementList();
void Add(Statement* statement);
void Remove(Statement* statement);
int32 Size();
Statement* StatementAt(int32 index);
Statement* GetStatement(const char* keyword);
const char* GetValue(const char* keyword);
void Print();
};
#endif
@@ -0,0 +1,43 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "StatementListVisitor.h"
void StatementListVisitor::Visit(StatementList* list)
{
if (list == NULL) return;
const int32 n = list->Size();
for (int32 i = 0; i < n; i ++) {
Statement* statement = list->StatementAt(i);
GroupStatement group(statement);
if (group.IsOpenGroup()) {
BeginGroup(&group);
fLevel ++;
} else if (statement->IsValueStatement()) {
DoValue(statement);
} else if (statement->IsDefaultStatement()) {
DoDefault(statement);
} else if (statement->IsQueryStatement()) {
DoQuery(statement);
} else if (statement->IsParamStatement()) {
DoParam(statement);
}
StatementList* children = statement->GetChildren();
if (children != NULL) {
Visit(children);
}
// Close statements have been removed
if (group.IsOpenGroup()) {
fLevel --;
EndGroup(&group);
}
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _STATEMENT_LIST_VISITOR_H
#define _STATEMENT_LIST_VISITOR_H
#include "StatementWrapper.h"
#include "StatementList.h"
class StatementListVisitor {
private:
int32 fLevel;
public:
StatementListVisitor() : fLevel(0) {}
virtual ~StatementListVisitor() {}
virtual void Visit(StatementList* list);
// the nesting level
int32 GetLevel() const { return fLevel; }
virtual void BeginGroup(GroupStatement* group) {};
virtual void DoDefault(Statement* statement) {};
virtual void DoQuery(Statement* statement) {};
virtual void DoValue(Statement* statement) {};
virtual void DoParam(Statement* statement) {};
virtual void EndGroup(GroupStatement* group) {};
};
#endif
@@ -0,0 +1,120 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "StatementWrapper.h"
static const char* kOpenUIStatement = "OpenUI";
static const char* kCloseUIStatement = "CloseUI";
static const char* kOpenGroupStatement = "OpenGroup";
static const char* kCloseGroupStatement = "CloseGroup";
static const char* kOpenSubGroupStatement = "OpenSubGroup";
static const char* kCloseSubGroupStatement = "CloseSubGroup";
// JCL
static const char* kJCL = "JCL";
static const char* kJCLOpenUIStatement = "JCLOpenUI";
static const char* kJCLCloseUIStatement = "JCLCloseUI";
StatementWrapper::StatementWrapper(Statement* statement)
: fStatement(statement)
{
// nothing to do
}
GroupStatement::GroupStatement(Statement* statement)
: StatementWrapper(statement)
{
// nothing to do
}
bool GroupStatement::IsUIGroup()
{
return strcmp(GetKeyword(), kOpenUIStatement) == 0;
}
bool GroupStatement::IsGroup()
{
return strcmp(GetKeyword(), kOpenGroupStatement) == 0;
}
bool GroupStatement::IsSubGroup()
{
return strcmp(GetKeyword(), kOpenSubGroupStatement) == 0;
}
bool GroupStatement::IsJCL()
{
return strstr(GetKeyword(), kJCL) == GetKeyword();
}
bool GroupStatement::IsOpenGroup()
{
const char* keyword = GetKeyword();
return strcmp(keyword, kOpenUIStatement) == 0 ||
strcmp(keyword, kOpenGroupStatement) == 0 ||
strcmp(keyword, kOpenSubGroupStatement) == 0 ||
strcmp(keyword, kJCLOpenUIStatement) == 0;
}
bool GroupStatement::IsCloseGroup()
{
const char* keyword = GetKeyword();
return strcmp(keyword, kCloseUIStatement) == 0 ||
strcmp(keyword, kCloseGroupStatement) == 0 ||
strcmp(keyword, kCloseSubGroupStatement) == 0 ||
strcmp(keyword, kJCLCloseUIStatement) == 0;
}
Value* GroupStatement::GetValue()
{
if (strcmp(GetKeyword(), kOpenUIStatement) == 0 ||
strcmp(GetKeyword(), kJCLOpenUIStatement) == 0) {
return GetStatement()->GetOption();
} else {
return GetStatement()->GetValue();
}
}
const char* GroupStatement::GetGroupName()
{
Value* value = GetValue();
if (value == NULL) return NULL;
BString* string = value->GetValue();
if (string == NULL) return NULL;
const char* name = string->String();
if (name != NULL && *name == '*') {
// skip '*'
name ++;
}
return name;
}
const char* GroupStatement::GetGroupTranslation()
{
Value* value = GetValue();
if (value == NULL) return NULL;
BString* string = value->GetTranslation();
if (string == NULL) return NULL;
return string->String();
}
GroupStatement::Type GroupStatement::GetType()
{
const char* type = GetStatement()->GetValueString();
if (type == NULL) return kUnknown;
if (strstr(type, "PickOne") != NULL) return kPickOne;
if (strstr(type, "PickMany") != NULL) return kPickMany;
if (strstr(type, "Boolean") != NULL) return kBoolean;
return kUnknown;
}
@@ -0,0 +1,92 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _STATEMENT_WRAPPER_H
#define _STATEMENT_WRAPPER_H
#include "Statement.h"
// wrapper classes to provide specific access to
// statement members
class StatementWrapper
{
private:
Statement* fStatement;
public:
StatementWrapper(Statement* statement);
Statement* GetStatement() { return fStatement; }
const char* GetKeyword() { return fStatement->GetKeyword()->String(); }
};
class GroupStatement : public StatementWrapper
{
private:
Value* GetValue();
public:
GroupStatement(Statement* statement);
// test methods if the wrapped statement is a group statement
bool IsUIGroup();
bool IsGroup();
bool IsSubGroup();
bool IsOpenGroup();
bool IsCloseGroup();
bool IsJCL();
// accessors
const char* GetGroupName();
const char* GetGroupTranslation();
enum Type {
kPickOne,
kPickMany,
kBoolean,
kUnknown
};
Type GetType();
};
class ConstraintsStatement : public StatementWrapper
{
public:
ConstraintsStatement(Statement* statement);
// is this realy a constraints statement
bool IsConstraints();
const char* GetFirstKeyword();
const char* GetFirstOption();
const char* GetSecondKeyword();
const char* GetSecondOption();
};
class OrderDependencyStatement : public StatementWrapper
{
public:
OrderDependencyStatement(Statement* statement);
// is this realy a order dependency statement
bool IsOrderDependency();
// is this a NonUIOrderDependencyStatement
bool IsNonUI();
float GetOrder();
const char* GetSection();
const char* GetKeyword();
const char* GetOption();
};
#endif
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "Value.h"
#include <stdio.h>
Value::Value(BString* value, Type type)
: fType(type)
, fValue(value)
, fTranslation(NULL)
{
}
Value::~Value()
{
delete fValue;
delete fTranslation;
}
void Value::SetType(Type type)
{
fType = type;
}
Value::Type Value::GetType()
{
return fType;
}
void Value::SetValue(BString* value)
{
fValue = value;
}
BString* Value::GetValue()
{
return fValue;
}
void Value::SetTranslation(BString* translation)
{
fTranslation = translation;
}
BString* Value::GetTranslation()
{
return fTranslation;
}
const char* Value::GetValueString()
{
if (fValue != NULL) {
return fValue->String();
}
return NULL;
}
const char* Value::GetTranslationString()
{
if (fTranslation != NULL) {
return fTranslation->String();
}
return NULL;
}
const char* Value::ElementForType()
{
switch (fType) {
case kSymbolValue: return "Symbol";
break;
case kStringValue: return "String";
break;
case kInvocationValue: return "Invocation";
break;
case kQuotedValue: return "Quoted";
break;
case kUnknownValue: return "Unknown";
break;
}
return "NULL";
}
void Value::Print()
{
printf("\t\t<%s>\n", ElementForType());
if (fValue != NULL) {
printf("\t\t\t<value>%s</value>\n", fValue->String());
}
if (fTranslation != NULL) {
printf("\t\t\t<translation>%s</translation>\n", fTranslation->String());
}
printf("\t\t</%s>\n", ElementForType());
}
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _VALUE_H
#define _VALUE_H
#include <String.h>
class Value {
public:
enum Type {
kSymbolValue,
kStringValue,
kInvocationValue,
kQuotedValue,
kUnknownValue
};
private:
Type fType;
BString* fValue;
BString* fTranslation;
const char* ElementForType();
public:
Value(BString* value = NULL, Type type = kUnknownValue);
virtual ~Value();
void SetType(Type type);
Type GetType();
// mandatory in a valid statement
void SetValue(BString* value);
BString* GetValue();
// optional
void SetTranslation(BString* translation);
BString* GetTranslation();
// convenience methods
const char* GetValueString();
const char* GetTranslationString();
void Print();
};
#endif
@@ -0,0 +1,67 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _CHARACTER_CLASSES_H
#define _CHARACTER_CLASSES_H
#define kCr '\n'
#define kLf '\r'
#define kTab '\t'
#define kEof -1
inline bool IsWhitespaceSeparator(int ch)
{
return ch == ' ' || ch == kTab;
}
inline bool IsWhitespace(int ch)
{
return ch == ' ' || ch == kTab || ch == kLf || ch == kCr;
}
inline bool IsIdentChar(int ch) {
// TODO check '.' if is an identifier character
// in one of the PPD files delivered with BeOS R5
// '.' is used inside of an identifier
// if (ch == '.' || ch == '/' || ch == ':') return false;
if (ch == '/' || ch == ':') return false;
return 33 <= ch && ch <= 126;
}
inline bool IsOptionChar(int ch)
{
if (ch == '.') return true;
return IsIdentChar(ch);
}
inline bool IsChar(int ch)
{
if (ch == '"') return false;
return 32 <= ch && ch <= 255 || IsWhitespace(ch);
}
inline bool IsPrintableWithoutWhitespaces(int ch)
{
if (ch == '"') return false;
return 33 <= ch && ch <= 126;
}
inline bool IsPrintableWithWhitespaces(int ch)
{
return IsPrintableWithoutWhitespaces(ch) || IsWhitespace(ch);
}
inline bool IsStringChar(int ch)
{
if (IsWhitespaceSeparator(ch)) return true;
if (ch == '"') return true;
if (ch == '/') return false;
return IsPrintableWithoutWhitespaces(ch);
}
#endif
@@ -0,0 +1,13 @@
SubDir HAIKU_TOP src tests add-ons print ppd parser ;
# SetSubDirSupportedPlatformsBeOSCompatible ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd model ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd shared ;
StaticLibrary libppdtest.a :
Parser.cpp
PPDFile.cpp
PPDParser.cpp
Scanner.cpp
;
@@ -0,0 +1,79 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "CharacterClasses.h"
#include "Scanner.h"
#include <stdio.h>
int FileBuffer::Read()
{
if (fIndex >= fSize) {
fSize = fFile->Read(fBuffer, kReadBufferSize);
fIndex = 0;
}
if (fSize <= 0) {
return -1;
}
return (int)fBuffer[fIndex ++];
}
PPDFile::PPDFile(const char* file, PPDFile* previousFile)
: fFileName(file)
, fFile(file, B_READ_ONLY)
, fPreviousFile(previousFile)
, fCurrentPosition(0, 1)
, fCurrentChar(-1)
, fBuffer(&fFile)
{
}
PPDFile::~PPDFile()
{
// nothing to do
}
status_t PPDFile::InitCheck()
{
return fFile.InitCheck();
}
int PPDFile::GetCurrentChar()
{
return fCurrentChar;
}
void PPDFile::NextChar() {
fCurrentChar = fBuffer.Read();
if (fCurrentChar != -1) {
#if TRACE_SCANNER
fprintf(stderr, "%c ", fCurrentChar);
#endif
if (fCurrentChar == kCr) {
fCurrentPosition.x = 0;
fCurrentPosition.y ++;
} else {
fCurrentPosition.x ++;
}
}
}
Position PPDFile::GetPosition()
{
return fCurrentPosition;
}
PPDFile* PPDFile::GetPreviousFile()
{
return fPreviousFile;
}
const char* PPDFile::GetFileName()
{
return fFileName.String();
}
@@ -0,0 +1,70 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PPD_FILE_H
#define _PPD_FILE_H
#include <File.h>
#include <String.h>
class Position {
public:
int x;
int y;
Position() : x(0), y(0) {}
Position(int x, int y) : x(x), y(y) {}
};
#define kReadBufferSize 1024
class FileBuffer {
BFile* fFile;
unsigned char fBuffer[kReadBufferSize];
int fIndex;
int fSize;
public:
FileBuffer(BFile* file) : fFile(file), fIndex(0), fSize(0) {}
int Read();
};
class PPDFile {
private:
BString fFileName;
BFile fFile;
PPDFile* fPreviousFile; // single linked list of PPD files (stack)
Position fCurrentPosition;
int fCurrentChar;
FileBuffer fBuffer;
public:
// Opens the file for reading. Use IsValid to check if the file could
// be opened successfully.
// PPDFile also maintance a single linked list. The parameter previousFile
// can be used to store a reference to a previous file.
PPDFile(const char* file, PPDFile* previousFile = NULL);
// Closes the file.
~PPDFile();
// Returns the status of the constructor.
status_t InitCheck();
// Returns the current character or -1 if on EOF.
int GetCurrentChar();
// Reads the next character. Use GetChar to read the current
void NextChar();
// Returns the position of the current character.
Position GetPosition();
// The previous file from the constructor.
PPDFile* GetPreviousFile();
// Returns the file name
const char* GetFileName();
};
#endif
@@ -0,0 +1,395 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "PPDParser.h"
#include "AutoDelete.h"
#include <stdio.h>
#include <stdlib.h>
// #define VERBOSE 1
struct Keyword {
const char* name;
const char* since;
int major;
int minor;
bool found;
};
static const Keyword gRequiredKeywords[] = {
{"DefaultImageableArea", NULL},
{"DefaultPageRegion", NULL},
{"DefaultPageSize", NULL},
{"DefaultPaperDimension", NULL},
// sometimes missing
// {"FileVersion", NULL},
//
// {"FormatVersion", NULL},
{"ImageableArea", NULL},
// "since" is not specified in standard!
{"LanguageEncoding", "4.3"},
{"LanguageVersion", NULL},
{"Manufacturer", "4.3"},
{"ModelName", NULL},
{"NickName", NULL},
{"PageRegion", NULL},
{"PageSize", NULL},
{"PaperDimension", NULL},
{"PCFileName", NULL},
{"PPD-Adobe", NULL},
{"Product", NULL},
{"PSVersion", NULL},
// sometimes missing
// {"ShortNickName", "4.3"},
};
// e.g. *PPD.Adobe: "4.3"
const char* kPPDAdobe = "PPD-Adobe";
#define NUMBER_OF_REQUIRED_KEYWORDS (int)(sizeof(gRequiredKeywords) / sizeof(struct Keyword))
class RequiredKeywords
{
private:
Keyword fKeywords[NUMBER_OF_REQUIRED_KEYWORDS];
bool fGotVersion;
int fMajorVersion;
int fMinorVersion;
void ExtractVersion(int& major, int& minor, const char* version);
bool IsVersionRequired(Keyword* keyword);
public:
RequiredKeywords();
bool IsRequired(Statement* statement);
bool IsComplete();
void AppendMissing(BString* string);
};
RequiredKeywords::RequiredKeywords()
: fGotVersion(false)
{
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
fKeywords[i] = gRequiredKeywords[i];
fKeywords[i].found = false;
const char* since = fKeywords[i].since;
if (since != NULL) {
ExtractVersion(fKeywords[i].major, fKeywords[i].minor, since);
}
}
}
void RequiredKeywords::ExtractVersion(int& major, int& minor, const char* version)
{
major = atoi(version);
minor = 0;
version = strchr(version, '.');
if (version != NULL) {
version ++;
minor = atoi(version);
}
}
bool RequiredKeywords::IsVersionRequired(Keyword* keyword)
{
if (keyword->since == NULL) return true;
// be conservative if version is missing
if (!fGotVersion) return true;
// keyword is not required if file version < since
if (fMajorVersion < keyword->major) return false;
if (fMajorVersion == keyword->major &&
fMinorVersion < keyword->minor) return false;
return true;
}
bool RequiredKeywords::IsRequired(Statement* statement)
{
const char* keyword = statement->GetKeyword()->String();
if (!fGotVersion && strcmp(kPPDAdobe, keyword) == 0 &&
statement->GetValue() != NULL) {
Value* value = statement->GetValue();
BString* string = value->GetValue();
fGotVersion = true;
ExtractVersion(fMajorVersion, fMinorVersion, string->String());
}
BString defaultKeyword;
if (statement->GetType() == Statement::kDefault) {
defaultKeyword << "Default" << keyword;
keyword = defaultKeyword.String();
}
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
const char* name = fKeywords[i].name;
if (strcmp(name, keyword) == 0) {
fKeywords[i].found = true;
return true;
}
}
return false;
}
bool RequiredKeywords::IsComplete()
{
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
if (!fKeywords[i].found && IsVersionRequired(&fKeywords[i])) {
return false;
}
}
return true;
}
void RequiredKeywords::AppendMissing(BString* string)
{
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
if (!fKeywords[i].found && IsVersionRequired(&fKeywords[i])) {
*string << "Keyword " << fKeywords[i].name;
if (fKeywords[i].since != NULL) {
*string << fKeywords[i].major << ". " << fKeywords[i].minor
<< " < " <<
fMajorVersion << "." << fMinorVersion << " ";
}
*string << " is missing\n";
}
}
}
// Constants
static const char* kEndStatement = "End";
// Implementation
PPDParser::PPDParser(const char* file)
: Parser(file)
, fStack(false)
, fRequiredKeywords(new RequiredKeywords)
{
}
PPDParser::~PPDParser()
{
delete fRequiredKeywords;
}
void PPDParser::Push(Statement* statement)
{
fStack.Add(statement);
}
Statement* PPDParser::Top()
{
if (fStack.Size() > 0) {
return fStack.StatementAt(fStack.Size()-1);
}
return NULL;
}
void PPDParser::Pop()
{
fStack.Remove(Top());
}
void PPDParser::AddStatement(Statement* statement)
{
fRequiredKeywords->IsRequired(statement);
Statement* top = Top();
if (top != NULL) {
top->AddChild(statement);
} else {
fPPD->Add(statement);
}
}
bool PPDParser::IsValidOpenStatement(GroupStatement* statement)
{
if (statement->GetGroupName() == NULL) {
Error("Missing group ID in open statement");
return false;
}
return true;
}
bool PPDParser::IsValidCloseStatement(GroupStatement* statement)
{
if (statement->GetGroupName() == NULL) {
Error("Missing option in close statement");
return false;
}
if (Top() == NULL) {
Error("Close statement without an open statement");
return false;
}
GroupStatement openStatement(Top());
// check if corresponding Open* is on top of stack
BString open = openStatement.GetKeyword();
open.RemoveFirst("Open");
BString close = statement->GetKeyword();
close.RemoveFirst("Close");
if (open != close) {
Error("Close statement has no corresponding open statement");
#ifdef VERBOSE
printf("********* OPEN ************\n");
openStatement.GetStatement()->Print();
printf("********* CLOSE ***********\n");
statement->GetStatement()->Print();
#endif
return false;
}
BString openValue(openStatement.GetGroupName());
BString closeValue(statement->GetGroupName());
const char* whiteSpaces = " \t";
openValue.RemoveSet(whiteSpaces);
closeValue.RemoveSet(whiteSpaces);
if (openValue != closeValue) {
BString message("Open name does not match close name ");
message << openValue << " != " << closeValue << "\n";
Warning(message.String());
}
return true;
}
bool PPDParser::ParseStatement(Statement* _statement)
{
AutoDelete<Statement> statement(_statement);
if (_statement->GetKeyword() == NULL) {
Error("Keyword missing");
return false;
}
if (_statement->GetOption() != NULL &&
_statement->GetOption()->GetValue() == NULL) {
// The parser should not provide an option without a value
Error("Option has no value");
return false;
}
if (_statement->GetValue() != NULL &&
_statement->GetValue()->GetValue() == NULL) {
// The parser should not provide a value without a value
Error("Value has no value");
return false;
}
const char* keyword = statement.Get()->GetKeyword()->String();
if (strcmp(keyword, kEndStatement) == 0) {
// End is ignored
return true;
}
GroupStatement group(statement.Get());
if (group.IsOpenGroup()) {
if (!IsValidOpenStatement(&group)) {
return false;
}
// Add() has to be infront of Push()!
AddStatement(statement.Release());
// begin of nested statement
Push(statement.Get());
return true;
}
if (group.IsCloseGroup()) {
// end of nested statement
if (!IsValidCloseStatement(&group)) {
return false;
}
Pop();
// The closing statement is not stored
return true;
}
AddStatement(statement.Release());
return true;
}
bool PPDParser::ParseStatements()
{
Statement* statement;
while ((statement = Parser::Parse()) != NULL) {
#ifdef VERBOSE
statement->Print(); fflush(stdout);
#endif
if (!ParseStatement(statement)) {
return false;
}
if (!fParseAll && fRequiredKeywords->IsComplete()) {
break;
}
}
if (HasError()) {
return false;
}
if (Top() != NULL) {
BString error("Missing close statement for:\n");
do {
error << " * " <<
Top()->GetKeywordString() << " " <<
Top()->GetOptionString() << "\n";
Pop();
} while (Top() != NULL);
Error(error.String());
return false;
}
return true;
}
PPD* PPDParser::Parse(bool all)
{
fParseAll = all;
if (InitCheck() != B_OK) return NULL;
fPPD = new PPD();
ParseStatements();
if (!HasError() && !fRequiredKeywords->IsComplete()) {
BString string;
fRequiredKeywords->AppendMissing(&string);
Error(string.String());
}
if (HasError()) {
delete fPPD; fPPD = NULL;
return NULL;
}
return fPPD;
}
PPD* PPDParser::ParseAll()
{
return Parse(true);
}
PPD* PPDParser::ParseHeader()
{
return Parse(false);
}
@@ -0,0 +1,49 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PPD_PARSER_H
#define _PPD_PARSER_H
#include "PPD.h"
#include "Parser.h"
#include "Statement.h"
#include "StatementWrapper.h"
class RequiredKeywords;
class PPDParser : public Parser
{
private:
PPD* fPPD;
StatementList fStack; // of nested statements
RequiredKeywords* fRequiredKeywords;
bool fParseAll;
void Push(Statement* statement);
Statement* Top();
void Pop();
// Add statement to PPD or the children of a
// nested statement
void AddStatement(Statement* statement);
bool IsValidOpenStatement(GroupStatement* statement);
bool IsValidCloseStatement(GroupStatement* statement);
bool ParseStatement(Statement* statement);
bool ParseStatements();
PPD* Parse(bool all);
public:
PPDParser(const char* file);
~PPDParser();
PPD* ParseAll();
PPD* ParseHeader();
};
#endif
@@ -0,0 +1,254 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "AutoDelete.h"
#include "CharacterClasses.h"
#include "Parser.h"
Parser::Parser(const char* file)
: fScanner(file)
{
if (InitCheck() == B_OK) {
NextChar();
}
}
status_t Parser::InitCheck()
{
return fScanner.InitCheck();
}
void Parser::SkipWhitespaces()
{
while (IsWhitespace(GetCurrentChar())) {
NextChar();
}
}
void Parser::SkipComment()
{
while (GetCurrentChar() != kEof && GetCurrentChar() != kCr) {
NextChar();
}
NextChar();
}
void Parser::SkipWhitespaceSeparator()
{
while (IsWhitespaceSeparator(GetCurrentChar())) {
NextChar();
}
}
bool Parser::ParseKeyword(Statement* statement)
{
// ["?"]
if (GetCurrentChar() == '?') {
NextChar();
statement->SetType(Statement::kQuery);
}
// Keyword
BString* keyword = fScanner.ScanIdent();
if (keyword == NULL) {
Error("Identifier expected");
return false;
}
statement->SetKeyword(keyword);
return true;
}
bool Parser::ParseTranslation(Value* value, int separator)
{
BString* translation = fScanner.ScanTranslationValue(separator);
if (translation == NULL) {
Error("Out of memory scanning translationn!");
return false;
}
value->SetTranslation(translation);
return true;
}
bool Parser::ParseOption(Statement* statement)
{
// ["^"]
bool isSymbolValue = GetCurrentChar() == '^';
if (isSymbolValue) {
NextChar();
}
// [ ... Option ...]
if (IsOptionChar(GetCurrentChar())) {
BString* option = fScanner.ScanOption();
if (option == NULL) {
Error("Out of memory scanning option!");
return false;
}
Value::Type type;
if (isSymbolValue) {
type = Value::kSymbolValue;
} else {
type = Value::kStringValue;
}
Value* value = new Value(option, type);
statement->SetOption(value);
SkipWhitespaceSeparator();
// ["/" Translation ]
if (GetCurrentChar() == '/') {
NextChar();
return ParseTranslation(value, ':');
}
} else {
if (isSymbolValue) {
Error("Expected symbol value!");
return false;
}
}
return true;
}
bool Parser::ParseValue(Statement* statement)
{
if (GetCurrentChar() == '"') {
NextChar();
// "..."
AutoDelete<Value> value(new Value());
BString* string;
if (statement->GetOption() != NULL) {
string = fScanner.ScanInvocationValue();
value.Get()->SetType(Value::kInvocationValue);
} else {
string = fScanner.ScanQuotedValue();
value.Get()->SetType(Value::kQuotedValue);
}
if (string == NULL) {
Error("Expected value");
return false;
}
// " is expected
if (GetCurrentChar() != '"') {
Error("Expected \" at end of value");
return false;
}
NextChar();
value.Get()->SetValue(string);
statement->SetValue(value.Release());
} else if (GetCurrentChar() == '^') {
// ^ SymbolValue
BString* symbol = fScanner.ScanOption();
if (symbol == NULL) {
Error("Symbol expected!");
return false;
}
Value* value = new Value(symbol, Value::kSymbolValue);
statement->SetValue(value);
} else {
// StringValue
BString* stringValue = fScanner.ScanStringValue();
if (stringValue == NULL) {
Error("String value expected!");
return false;
}
Value* value = new Value(stringValue, Value::kStringValue);
statement->SetValue(value);
}
if (GetCurrentChar() == '/') {
NextChar();
return ParseTranslation(statement->GetValue(), kCr);
}
return true;
}
void Parser::UpdateStatementType(Statement* statement)
{
if (statement->GetType() != Statement::kUnknown) return;
BString* keyword = statement->GetKeyword();
Statement::Type type;
if (keyword->FindFirst("Default") == 0) {
type = Statement::kDefault;
keyword->RemoveFirst("Default");
} else if (keyword->FindFirst("Param") == 0) {
type = Statement::kParam;
keyword->RemoveFirst("Param");
} else {
type = Statement::kValue;
}
statement->SetType(type);
}
// ["?"]Keyword [["^"]Option["/"Translation]]
// [":"
// ["^"]Value ["/" Translation].
// ]
Statement* Parser::ParseStatement()
{
AutoDelete<Statement> statement(new Statement());
if (!ParseKeyword(statement.Get())) {
return NULL;
}
SkipWhitespaceSeparator();
if (!ParseOption(statement.Get())) {
return NULL;
}
SkipWhitespaceSeparator();
// [":" ... ]
if (GetCurrentChar() == ':') {
NextChar();
SkipWhitespaceSeparator();
if (!ParseValue(statement.Get())) {
return NULL;
}
}
SkipWhitespaceSeparator();
if (GetCurrentChar() == kEof || GetCurrentChar() == kLf || GetCurrentChar() == kCr) {
UpdateStatementType(statement.Get());
Statement* result = statement.Release();
return result;
} else {
Error("Newline expected at end of statement");
return NULL;
}
}
Statement* Parser::Parse()
{
while (true) {
int ch = GetCurrentChar();
if (ch == -1) {
return NULL;
}
if (IsWhitespace(ch)) {
SkipWhitespaces();
} else if (ch == '*') {
// begin of comment or statement
NextChar();
ch = GetCurrentChar();
if (ch == '%') {
SkipComment();
} else {
return ParseStatement();
}
} else {
Error("Expected *");
return NULL;
}
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PARSER_H
#define _PARSER_H
#include "Scanner.h"
#include "Statement.h"
// PPD statement parser
class Parser
{
private:
Scanner fScanner;
int GetCurrentChar() { return fScanner.GetCurrentChar(); }
void NextChar() { fScanner.NextChar(); }
void SkipWhitespaces();
void SkipComment();
void SkipWhitespaceSeparator();
bool ParseKeyword(Statement* statement);
bool ParseTranslation(Value* value, int separator = -1);
bool ParseOption(Statement* statement);
bool ParseValue(Statement* statement);
void UpdateStatementType(Statement* statement);
Statement* ParseStatement();
protected:
void Warning(const char* message) { fScanner.Warning(message); }
void Error(const char* message) { fScanner.Error(message); }
public:
// Initializes the parser with the file
Parser(const char* file);
// Returns B_OK if the constructor could open the file
// successfully
status_t InitCheck();
// Includes the file for parsing
bool Include(const char* file) { return fScanner.Include(file); }
// Returns the statement or null on eof or on error
Statement* Parse();
// Returns true if there was a parsing error
bool HasError() { return fScanner.HasError(); }
// The error message of the parsing error
const char* GetErrorMessage() { return fScanner.GetErrorMessage(); }
// Returns true if there are any warnings
bool HasWarning() { return fScanner.HasWarning(); }
// Returns the waring message
const char* GetWarningMessage() { return fScanner.GetWarningMessage(); }
};
#endif
@@ -0,0 +1,203 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "CharacterClasses.h"
#include "Scanner.h"
Scanner::Scanner(const char* file)
{
fCurrentFile = new PPDFile(file);
}
Scanner::~Scanner()
{
while (fCurrentFile != NULL) {
PPDFile* file = fCurrentFile->GetPreviousFile();
delete fCurrentFile;
fCurrentFile = file;
}
}
status_t Scanner::InitCheck()
{
return fCurrentFile->InitCheck();
}
void Scanner::Warning(const char* message)
{
fWarnings << "Line " << GetPosition().y <<
", column " << GetPosition().x << ": " << message;
}
const char* Scanner::GetWarningMessage()
{
return fWarnings.String();
}
bool Scanner::HasWarning()
{
return fWarnings.Length() > 0;
}
void Scanner::Error(const char* message)
{
fLastError = GetFileName();
fLastError << " (line " << GetPosition().y <<
", column " << GetPosition().x << "): " <<
message;
}
const char* Scanner::GetErrorMessage()
{
return fLastError.String();
}
bool Scanner::HasError()
{
const char* message = GetErrorMessage();
return message != NULL && strcmp(message, "") != 0;
}
BString* Scanner::Scan(bool (cond)(int ch))
{
BString* text = new BString();
while (cond(GetCurrentChar())) {
text->Append(GetCurrentChar(), 1);
NextChar();
}
return text;
}
static inline int getHexadecimalDigit(int ch) {
if ('0' <= ch && '9' <= ch) {
return ch - '0';
}
if ('a' <= ch || ch <= 'f') {
return 10 + ch - 'a';
}
if ('A' <= ch || ch <= 'F') {
return 10 + ch - 'A';
}
return -1;
}
bool Scanner::ScanHexadecimalSubstring(BString* literal)
{
int digit = 0;
int value = 0;
while(true) {
NextChar();
int ch = GetCurrentChar();
if (ch == '>') {
// end of hexadecimal substring reached
return digit == 0;
}
if (ch == -1) {
Error("Unexpected EOF in hexadecimal substring!");
return false;
}
if (IsWhitespace(ch)) {
// ignore white spaces
continue;
}
int d = getHexadecimalDigit(ch);
if (d == -1) {
Error("Character is not a hexadecimal digit!");
return false;
}
if (d == 0) {
// first digit
value = d << 8;
d = 1;
} else {
// second digit
value |= d;
literal->Append((unsigned char)value, 1);
d = 0;
}
}
}
// !quotedValue means Translation String
BString* Scanner::ScanLiteral(bool quotedValue, int separator)
{
BString* literal = new BString();
while (true) {
int ch = GetCurrentChar();
if (ch == '<') {
if (!ScanHexadecimalSubstring(literal)) {
delete literal;
return NULL;
}
} else if (quotedValue && (ch == kLf || ch == kCr)) {
// nothing to do
} else if (!quotedValue && ch == '"') {
// translation string allows '"'
} else if (!IsChar(ch) || ch == separator) {
return literal;
}
literal->Append(ch, 1);
NextChar();
}
}
int Scanner::GetCurrentChar()
{
if (fCurrentFile != NULL) {
return fCurrentFile->GetCurrentChar();
}
return -1;
}
void Scanner::NextChar()
{
if (fCurrentFile != NULL) {
fCurrentFile->NextChar();
if (fCurrentFile->GetCurrentChar() == kEof) {
PPDFile* file = fCurrentFile->GetPreviousFile();
delete fCurrentFile;
fCurrentFile = file;
}
}
}
Position Scanner::GetPosition()
{
if (fCurrentFile != NULL) {
return fCurrentFile->GetPosition();
}
return Position();
}
const char* Scanner::GetFileName()
{
if (fCurrentFile != NULL) {
return fCurrentFile->GetFileName();
}
return NULL;
}
bool Scanner::Include(const char* file)
{
PPDFile* newFile = new PPDFile(file, fCurrentFile);
if (newFile->InitCheck() != B_OK) {
delete newFile;
return false;
}
fCurrentFile = newFile;
NextChar();
return true;
}
@@ -0,0 +1,61 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _SCANNER_H
#define _SCANNER_H
#include "CharacterClasses.h"
#include "PPDFile.h"
class Scanner {
private:
PPDFile* fCurrentFile;
BString fLastError;
BString fWarnings;
BString* Scan(bool (cond)(int ch));
bool ScanHexadecimalSubstring(BString* literal);
BString* ScanLiteral(bool quotedValue, int separator);
public:
Scanner(const char* file);
virtual ~Scanner();
status_t InitCheck();
// Returns the current character or -1 if on EOF.
int GetCurrentChar();
// Reads the next character. Use GetChar to read the current
void NextChar();
// Returns the position of the current character
Position GetPosition();
// Returns the file name of the current character
const char* GetFileName();
BString* ScanIdent() { return Scan(IsIdentChar); }
BString* ScanOption() { return Scan(IsOptionChar); }
BString* ScanSymbolValue() { return Scan(IsPrintableWithoutWhitespaces); }
BString* ScanInvocationValue() { return Scan(IsPrintableWithWhitespaces); }
BString* ScanStringValue() { return Scan(IsStringChar); }
BString* ScanTranslationValue(int separator = kEof)
{ return ScanLiteral(false, separator); }
BString* ScanQuotedValue() { return ScanLiteral(true, kEof); }
// Include the file at the current position and read the first char.
bool Include(const char* file);
void Warning(const char* message);
const char* GetWarningMessage();
bool HasWarning();
void Error(const char* message);
const char* GetErrorMessage();
bool HasError();
};
#endif
@@ -0,0 +1,92 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _AUTO_DELETE_H
#define _AUTO_DELETE_H
#include <stdlib.h>
/*
Typical usage of this class:
AClass* Klass::Method(int arg) {
AutoDelete<AClass> variable(new AClass());
if (!IsValid(arg)) {
...
// AutoDelete automatically deletes the AClass object.
return NULL;
}
variable.Get()->MethodOfAClass();
// Use Release() to prevent deletion of AClass object.
return variable.Release();
}
*/
template <class Tp>
class AutoDelete {
private:
Tp* fObject;
bool fOwnsObject;
// Deletes the object if it owns it
void Delete()
{
if (fOwnsObject) {
delete fObject; fObject = NULL;
}
}
public:
// Sets the object the class owns
AutoDelete(Tp* object = NULL) : fObject(object), fOwnsObject(true) { }
// Deletes the object if it owns it
virtual ~AutoDelete()
{
Delete();
}
// Sets the object the class owns.
// Deletes a previously owned object and
// sets the owning flag for the new object.
void Set(Tp* object)
{
if (fObject == object) return;
Delete();
fOwnsObject = true;
fObject = object;
}
// Returns the object
Tp* Get()
{
return fObject;
}
// Returns the object and sets owning to false
// The Get method can still be used to retrieve the object.
Tp* Release()
{
fOwnsObject = false;
return fObject;
}
// Sets the owning flag
void SetOwnsObject(bool ownsObject)
{
fOwnsObject = ownsObject;
}
};
#endif
+24
View File
@@ -0,0 +1,24 @@
SubDir HAIKU_TOP src tests add-ons print ppd test ;
# SetSubDirSupportedPlatformsBeOSCompatible ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd model ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd parser ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd ui ;
# TODO convert .rsrc to .rdef
# AddResources PPDConfig : PPDConfigApplication.rsrc ;
SimpleTest PPDConfig :
PPDConfigApplication.cpp
: be root libppdtest.a
;
SimpleTest PPDTest :
Test.cpp
TestParser.cpp
TestScanner.cpp
:
be root libppdtest.a
;
@@ -0,0 +1,14 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef MSG_CONSTS_H
#define MSG_CONSTS_H
#define MENU_APP_NEW 'APnw'
#endif
@@ -0,0 +1,96 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "PPDConfigApplication.h"
#include "PrinterSelection.h"
AppWindow::AppWindow(BRect aRect)
: BWindow(aRect, APPLICATION, B_TITLED_WINDOW, 0) {
// add menu bar
BRect rect = BRect(0, 0, aRect.Width(), aRect.Height());
fMenuBar = new BMenuBar(rect, "menu_bar");
BMenu *menu;
menu = new BMenu("File");
menu->AddItem(new BMenuItem("About ...", new BMessage(B_ABOUT_REQUESTED), 'A'));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
fMenuBar->AddItem(menu);
AddChild(fMenuBar);
float x = aRect.Width() / 2 - 3;
float right = rect.right - 3;
// add view
aRect.Set(0, fMenuBar->Bounds().Height()+1, x, aRect.Height());
PrinterSelectionView* printerSelection = new PrinterSelectionView(aRect,
"printer-selection",
B_FOLLOW_TOP_BOTTOM,
B_WILL_DRAW);
AddChild(printerSelection);
printerSelection->SetMessage(new BMessage('prnt'));
printerSelection->SetTarget(this);
aRect.left = x + 3;
aRect.right = right;
AddChild(fConfig = new PPDConfigView(aRect, "ppd-config",
B_FOLLOW_ALL_SIDES,
B_WILL_DRAW));
// make window visible
Show();
}
void AppWindow::MessageReceived(BMessage *message) {
const char* file;
switch(message->what) {
case MENU_APP_NEW:
break;
case B_ABOUT_REQUESTED:
AboutRequested();
break;
case 'prnt':
if (message->FindString("file", &file) == B_OK) {
BMessage settings;
fConfig->Set(file, settings);
}
break;
default:
BWindow::MessageReceived(message);
}
}
bool AppWindow::QuitRequested() {
be_app->PostMessage(B_QUIT_REQUESTED);
return(true);
}
void AppWindow::AboutRequested() {
BAlert *about = new BAlert(APPLICATION,
APPLICATION " " VERSION "\nPrototype for PPD printer selection and configuration.\n\n"
"Written 2008.\n\n"
"By Michael Pfeiffer.\n\n"
"EMail: [email protected].","Close");
about->Go();
}
PPDConfigApplication::PPDConfigApplication() : BApplication(SIGNATURE) {
BRect aRect;
// set up a rectangle and instantiate a new window
aRect.Set(100, 80, 950, 580);
window = NULL;
window = new AppWindow(aRect);
}
int main(int argc, char *argv[]) {
PPDConfigApplication app;
app.Run();
return 0;
}
@@ -0,0 +1,41 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PPD_CONFIG_H
#define _PPD_CONFIG_H
#include <AppKit.h>
#include <InterfaceKit.h>
#include "PPDConfigView.h"
#include "MsgConsts.h"
#define APPLICATION "PPD Printer Selection and Configuration Prototype"
#define SIGNATURE "application/x-vnd.mwp-ppd-prototype"
#define VERSION "1.0"
class AppWindow : public BWindow {
public:
AppWindow(BRect);
bool QuitRequested();
void AboutRequested();
void MessageReceived(BMessage *message);
private:
BMenuBar *fMenuBar;
PPDConfigView *fConfig;
};
class PPDConfigApplication : public BApplication {
public:
AppWindow *window;
PPDConfigApplication();
};
#define my_app ((PPDConfigApplication*)be_app)
#endif
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <List.h>
void TestScanner();
void TestParser();
void TestPPDParser(bool all, bool verbose = true);
void TestExtractUI();
const char* gPPDFile = "aptollw1.ppd";
static BList gArgs;
bool enabled(const char* name, const char* arg)
{
gArgs.AddItem((void*)name);
if (arg == NULL) return false;
if (strcmp(arg, "all") == 0) return true;
return strcmp(arg, name) == 0;
}
void printArgs(const char* programName)
{
fprintf(stderr, "%s: argument\n", programName);
fprintf(stderr, "Argument is missing. The available arguments are:\n");
fprintf(stderr, " all\n");
for (int i = 0; i < gArgs.CountItems(); i ++) {
fprintf(stderr, " %s\n", (const char*)gArgs.ItemAt(i));
}
}
int main(int argc, char* argv[])
{
const char* arg = argc >= 2 ? argv[1] : NULL;
if (argc >= 3) {
gPPDFile = argv[2];
}
if (enabled("scanner", arg)) {
TestScanner();
}
if (enabled("parser", arg)) {
TestParser();
}
if (enabled("ppd", arg)) {
TestPPDParser(true);
}
if (enabled("header", arg)) {
TestPPDParser(false);
}
if (enabled("ui", arg)) {
TestExtractUI();
}
if (enabled("ppd-timing", arg)) {
TestPPDParser(true, false);
}
if (enabled("header-timing", arg)) {
TestPPDParser(false, false);
}
if (arg == NULL) {
printArgs(argv[0]);
}
}
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _TEST_H
#define _TEST_H
extern const char* gPPDFile;
#endif
@@ -0,0 +1,148 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "Parser.h"
#include "Test.h"
#include <StopWatch.h>
#include <stdio.h>
void TestParser()
{
Parser parser(gPPDFile);
if (parser.InitCheck() != B_OK) {
fprintf(stderr, "Could not open ppd file %s\n", gPPDFile);
return;
}
Statement* statement;
do {
statement = parser.Parse();
if (statement != NULL) {
statement->Print();
}
delete statement;
} while (statement != NULL);
}
#include "PPDParser.h"
static PPD* OpenTestFile(bool all, bool timing)
{
BStopWatch* stopWatch = NULL;
if (timing) {
stopWatch = new BStopWatch("PPDParser");
}
PPDParser parser(gPPDFile);
if (parser.InitCheck() != B_OK) {
fprintf(stderr, "Could not open ppd file %s\n", gPPDFile);
return NULL;
}
PPD* ppd = all ? parser.ParseAll() : parser.ParseHeader();
delete stopWatch;
if (ppd == NULL) {
fprintf(stderr, "Parser returned NULL\n");
fprintf(stderr, "%s\n", parser.GetErrorMessage());
return NULL;
}
return ppd;
}
void TestPPDParser(bool all, bool verbose)
{
PPD* ppd = OpenTestFile(all, !verbose);
if (ppd == NULL) return;
if (verbose) {
ppd->Print();
}
delete ppd;
}
void ExtractChildren(StatementList* list, int level);
void Indent(int level)
{
for (; level > 0; level --) {
printf(" ");
}
}
void PrintValue(const char* label, Value* arg, int level)
{
Indent(level);
if (label != NULL) {
printf("%s ", label);
}
if (arg != NULL) {
BString* value = arg->GetValue();
BString* translation = arg->GetTranslation();
if (translation != NULL) {
printf("%s", translation->String());
}
if (value != NULL) {
printf(" [%s]", value->String());
}
} else {
printf("NULL");
}
printf("\n");
}
bool ExtractGroup(Statement* statement, int level)
{
GroupStatement group(statement);
if (group.IsOpenGroup()) {
const char* translation = group.GetGroupTranslation();
Indent(level);
if (translation != NULL) {
printf("%s", translation);
}
const char* name = group.GetGroupName();
if (name != NULL) {
printf("[%s]", name);
}
printf("\n");
ExtractChildren(statement->GetChildren(), level+1);
return true;
}
return false;
}
void ExtractChildren(StatementList* list, int level)
{
if (list == NULL) return;
for (int32 i = 0; i < list->Size(); i ++) {
Statement* statement = list->StatementAt(i);
if (!ExtractGroup(statement, level)) {
if (statement->GetType() == Statement::kValue) {
PrintValue(NULL, statement->GetOption(), level);
} else if (statement->GetType() == Statement::kDefault) {
PrintValue("Default", statement->GetValue(), level);
}
}
}
}
void TestExtractUI()
{
PPD* ppd = OpenTestFile(true, false);
if (ppd == NULL) return;
for (int32 i = 0; i < ppd->Size(); i++) {
Statement* statement = ppd->StatementAt(i);
ExtractGroup(statement, 0);
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "CharacterClasses.h"
#include "Scanner.h"
#include <stdio.h>
void Print(Scanner* scanner)
{
Position position = scanner->GetPosition();
const char* filename = scanner->GetFileName();
int ch = scanner->GetCurrentChar();
printf("[%d, %d] (%s) %c\n", position.x, position.y, filename, ch);
}
void TestScanner()
{
Scanner scanner("main.ppd");
if (scanner.InitCheck() != B_OK) {
fprintf(stderr, "Could not open file main.ppd\n");
return;
}
scanner.NextChar();
for (int i = 0; i < 10; i ++) {
int ch = scanner.GetCurrentChar();
if (ch == kEof) {
fprintf(stderr, "Unexpected end of file!\n");
return;
}
Print(&scanner);
scanner.NextChar();
}
if (!scanner.Include("include.ppd")) {
fprintf(stderr, "Could not include file include.ppd\n");
return;
}
while (scanner.GetCurrentChar() != kEof) {
Print(&scanner);
scanner.NextChar();
}
BString string;
string.Append('a', 1);
printf("%d\n", (int)string.Length());
string.Append((char)0, 1);
string.Append('b', 1);
printf("%d\n", (int)string.Length());
for (int i = 0; i < string.Length(); i ++) {
printf("%c ", string.String()[i]);
}
printf("%d\n", '"');
}
@@ -0,0 +1,656 @@
*PPD-Adobe: "4.1"
*% Adobe Systems PostScript(R) Printer Description File
*% Copyright 1987-1994 Adobe Systems Incorporated.
*% All Rights Reserved.
*% Permission is granted for redistribution of this file as
*% long as this copyright notice is intact and the contents
*% of the file is not altered in any way from its original form.
*% End of Copyright statement
*FormatVersion: "4.1"
*FileVersion: "1.15"
*LanguageEncoding: ISOLatin1
*LanguageVersion: English
*PCFileName: "APTOLLW1.PPD"
*Product: "(LaserWriter Pro 630)"
*PSVersion: "(2010.130) 1"
*ModelName: "Apple LaserWriter Pro 630"
*ShortNickName: "Apple LaserWriter Pro 630"
*NickName: "Apple LaserWriter Pro 630 v2010.130"
*% === Options and Constraints =========
*OpenGroup: InstallableOptions/Options Installed
*OpenUI *Option1/Memory Configuration: PickOne
*DefaultOption1: None
*Option1 None/Standard 8 MB RAM: ""
*Option1 16Meg/16 MB Upgrade: ""
*Option1 32Meg/32 MB Upgrade: ""
*?Option1: "
(None)currentsystemparams/RamSize get
dup 16777216 eq{pop pop(16Meg)}{33554432 eq{pop(32Meg)}if}ifelse
=
"
*End
*CloseUI: *Option1
*OpenUI *Option2/Cassette (500 Sheets): PickOne
*DefaultOption2: False
*Option2 True/Installed: ""
*Option2 False/Not Installed: ""
*Option2 Preferred/Installed and Preferred: "
1 dict dup /InputAttributes 1 dict dup /Priority [2 0 1 3] put put setpagedevice"
*End
*?Option2: "
save
currentpagedevice
/InputAttributes get
2 known {(True)}{(False)} ifelse = flush
restore "
*End
*CloseUI: *Option2
*OpenUI *Option3/Envelope Feeder: Boolean
*DefaultOption3: False
*Option3 True/Installed: ""
*Option3 False/Not Installed: ""
*?Option3: "
save
currentpagedevice
/InputAttributes get
3 known {(True)}{(False)} ifelse = flush
restore "
*End
*CloseUI: *Option3
*CloseGroup: InstallableOptions
*UIConstraints: *Resolution 600dpi *Smoothing True
*UIConstraints: *Resolution 600dpi *BitsPerPixel 4
*UIConstraints: *Option2 False *InputSlot Lower
*UIConstraints: *Option3 False *InputSlot Envelope
*UIConstraints: *PageSize Letter *InputSlot Envelope
*UIConstraints: *PageSize Legal *InputSlot Envelope
*UIConstraints: *PageSize A4 *InputSlot Envelope
*UIConstraints: *PageSize B5 *InputSlot Envelope
*UIConstraints: *PageSize LetterSmall *InputSlot Envelope
*UIConstraints: *PageSize A4Small *InputSlot Envelope
*UIConstraints: *PageSize LegalSmall *InputSlot Envelope
*UIConstraints: *InputSlot Envelope *PageSize Letter
*UIConstraints: *InputSlot Envelope *PageSize Legal
*UIConstraints: *InputSlot Envelope *PageSize A4
*UIConstraints: *InputSlot Envelope *PageSize B5
*UIConstraints: *InputSlot Envelope *PageSize LetterSmall
*UIConstraints: *InputSlot Envelope *PageSize A4Small
*UIConstraints: *InputSlot Envelope *PageSize LegalSmall
*UIConstraints: *PageRegion Letter *InputSlot Envelope
*UIConstraints: *PageRegion Legal *InputSlot Envelope
*UIConstraints: *PageRegion A4 *InputSlot Envelope
*UIConstraints: *PageRegion B5 *InputSlot Envelope
*UIConstraints: *PageRegion LetterSmall *InputSlot Envelope
*UIConstraints: *PageRegion A4Small *InputSlot Envelope
*UIConstraints: *PageRegion LegalSmall *InputSlot Envelope
*UIConstraints: *InputSlot Envelope *PageRegion Letter
*UIConstraints: *InputSlot Envelope *PageRegion Legal
*UIConstraints: *InputSlot Envelope *PageRegion A4
*UIConstraints: *InputSlot Envelope *PageRegion B5
*UIConstraints: *InputSlot Envelope *PageRegion LetterSmall
*UIConstraints: *InputSlot Envelope *PageRegion A4Small
*UIConstraints: *InputSlot Envelope *PageRegion LegalSmall
*UIConstraints: *Option1 None *VMOption
*UIConstraints: *Option1 16Meg *VMOption None
*UIConstraints: *Option1 16Meg *VMOption 32Meg
*UIConstraints: *Option1 32Meg *VMOption None
*UIConstraints: *Option1 32Meg *VMOption 16Meg
*% ==== Device Capabilities ===============
*LanguageLevel: "2"
*Protocols: BCP
*Emulators: hpcl
*StartEmulator_hpcl: "currentfile /hpcl statusdict /emulate get exec "
*StopEmulator_hpcl: "<1B7F>0"
*FreeVM: "2406169"
*VMOption None/Standard 8 MB RAM: "2406169"
*VMOption 16Meg/16 MB Upgrade: "10851725"
*VMOption 32Meg/32 MB Upgrade: "27598075"
*ColorDevice: False
*DefaultColorSpace: Gray
*VariablePaperSize: False
*FileSystem: True
*?FileSystem: "
save false
(%disk?%)
{ currentdevparams dup /Writeable known
{ /Writeable get {pop true} if } { pop } ifelse
} 10 string /IODevice resourceforall
{(True)}{(False)} ifelse = flush
restore"
*End
*Throughput: "8"
*Password: "()"
*ExitServer: "
count 0 eq
{ false } { true exch startjob } ifelse
not {
(WARNING: Cannot modify initial VM.) =
(Missing or invalid password.) =
(Please contact the author of this software.) = flush quit
} if
"
*End
*Reset: "
count 0 eq
{ false } { true exch startjob } ifelse
not {
(WARNING: Cannot reset printer.) =
(Missing or invalid password.) =
(Please contact the author of this software.) = flush quit
} if
systemdict /quit get exec
(WARNING : Printer Reset Failed.) = flush
"
*End
*OpenUI *Resolution/Choose Resolution: PickOne
*OrderDependency: 10 AnySetup *Resolution
*DefaultResolution: 600dpi
*Resolution 600dpi: "1 dict dup /HWResolution [600 600] put setpagedevice"
*Resolution 300dpi: "1 dict dup /HWResolution [300 300] put setpagedevice"
*?Resolution: "
save
/ActualValues /ProcSet findresource
begin HWResolution end
0 get
( ) cvs print
(dpi)
= flush
restore
"
*End
*CloseUI: *Resolution
*% Halftone Information ===============
*ScreenFreq: "85.0"
*ScreenAngle: "45.0"
*DefaultScreenProc: Dot
*ScreenProc Dot: "
{abs exch abs 2 copy add 1 gt {1 sub dup mul exch
1 sub dup mul add 1 sub } {dup mul exch dup mul
add 1 exch sub } ifelse }
"
*End
*ScreenProc Line: "{ pop }"
*ScreenProc Ellipse: "{ dup 5 mul 8 div mul exch dup mul exch add sqrt 1 exch sub }"
*DefaultTransfer: Null
*Transfer Null: "{ }"
*Transfer Null.Inverse: "{ 1 exch sub }"
*OpenUI *Smoothing/FinePrint(TM): Boolean
*OrderDependency: 50 AnySetup *Smoothing
*DefaultSmoothing: False
*Smoothing True/On: "
2 dict
dup /PostRenderingEnhance true put
dup /PostRenderingEnhanceDetails
2 dict
dup /Type 1 put
dup /ActualPostRenderingEnhance true put
put
setpagedevice
"
*End
*Smoothing False/Off: "
2 dict
dup /PostRenderingEnhance false put
dup /PostRenderingEnhanceDetails
2 dict
dup /Type 1 put
dup /ActualPostRenderingEnhance false put
put
setpagedevice
"
*End
*?Smoothing: "
save currentpagedevice /PostRenderingEnhanceDetails get
/ActualPostRenderingEnhance get
{(True)}{(False)} ifelse = flush restore"
*End
*CloseUI: *Smoothing
*OpenUI *BitsPerPixel/PhotoGrade(TM): Boolean
*OrderDependency: 50 AnySetup *BitsPerPixel
*DefaultBitsPerPixel: None
*BitsPerPixel 4/On: "
2 dict
dup /PreRenderingEnhance true put
dup /PreRenderingEnhanceDetails
2 dict
dup /Type 1 put
dup /ActualPreRenderingEnhance true put
put
setpagedevice
"
*End
*BitsPerPixel None/Off: "
2 dict
dup /PreRenderingEnhance false put
dup /PreRenderingEnhanceDetails
2 dict
dup /Type 1 put
dup /ActualPreRenderingEnhance false put
put
setpagedevice
"
*End
*?BitsPerPixel: "
save currentpagedevice /PreRenderingEnhanceDetails get
/ActualPreRenderingEnhance get
{(4)}{(None)} ifelse = flush restore"
*End
*CloseUI: *BitsPerPixel
*% Paper Handling ===================
*% Code in this section both selects a tray and sets up a frame buffer.
*OpenUI *PageSize: PickOne
*OrderDependency: 30 AnySetup *PageSize
*DefaultPageSize: Letter
*PageSize Letter/US Letter: "
2 dict dup /PageSize [612 792] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize Legal/US Legal: "
2 dict dup /PageSize [612 1008] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize A4: "
2 dict dup /PageSize [595 842] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize B5: "
2 dict dup /PageSize [516 729] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize LetterSmall/US Letter Small: "
2 dict dup /PageSize [612 792] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize A4Small/A4 Small: "
2 dict dup /PageSize [595 842] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize LegalSmall/US Legal Small: "
2 dict dup /PageSize [612 1008] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize Monarch/Monarch Envelope Edge Fed: "
2 dict dup /PageSize [611 792] put dup /ImagingBBox null put setpagedevice"
*End
*PageSize Com10/Com10 Envelope Edge Fed: "
2 dict dup /PageSize [610 792] put dup /ImagingBBox null put setpagedevice"
*End
*?PageSize: "
save
currentpagedevice /PageSize get aload pop
2 copy gt {exch} if
(Unknown)
7 dict
dup [612 792] (Letter) put
dup [612 1008] (Legal) put
dup [595 842] (A4) put
dup [516 729] (B5) put
dup [610 792] (Comm10) put
dup [611 792] (Monarch) put
{ exch aload pop 4 index sub abs 5 le exch
5 index sub abs 5 le and
{exch pop exit} {pop} ifelse
} bind forall
= flush pop pop
restore
"
*End
*CloseUI: *PageSize
*OpenUI *PageRegion: PickOne
*OrderDependency: 40 AnySetup *PageRegion
*DefaultPageRegion: Letter
*PageRegion Letter/US Letter: "
2 dict dup /PageSize [612 792] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion Legal/US Legal: "
2 dict dup /PageSize [612 1008] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion A4: "
2 dict dup /PageSize [595 842] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion B5: "
2 dict dup /PageSize [516 729] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion LetterSmall/US Letter Small: "
2 dict dup /PageSize [612 792] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion A4Small/A4 Small: "
2 dict dup /PageSize [595 842] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion LegalSmall/US Legal Small: "
2 dict dup /PageSize [612 1008] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion Monarch/Monarch Envelope Edge Fed: "
2 dict dup /PageSize [611 792] put dup /ImagingBBox null put setpagedevice"
*End
*PageRegion Com10/Com10 Envelope Edge Fed: "
2 dict dup /PageSize [610 792] put dup /ImagingBBox null put setpagedevice"
*End
*CloseUI: *PageRegion
*% The following entries provide information about specific paper keywords.
*DefaultImageableArea: Letter
*ImageableArea Letter/US Letter: "9.84 14.2201 601.2 783.66 "
*ImageableArea Legal/US Legal: "9.84 14.2201 601.2 999.66 "
*ImageableArea A4: "9.84 14.2201 578.16 833.82 "
*ImageableArea B5: "9.84 14.22 501.36 720.78 "
*ImageableArea LetterSmall/US Letter Small: "31 31 583 761 "
*ImageableArea A4Small/A4 Small: "29 31 567 812 "
*ImageableArea LegalSmall/US Legal Small: "64 54 548 954 "
*ImageableArea Monarch/Monarch Envelope Edge Fed: "9.84 257 274 783.66 "
*ImageableArea Com10/Com10 Envelope Edge Fed: "9.84 113 292 783.66 "
*?ImageableArea: "
save
/cvp { ( ) cvs print ( ) print } bind def
/upperright {10000 mul floor 10000 div} bind def
/lowerleft {10000 mul ceiling 10000 div} bind def
newpath clippath pathbbox
4 -2 roll exch 2 {lowerleft cvp} repeat
exch 2 {upperright cvp} repeat flush
restore
"
*End
*% These provide the physical dimensions of the paper (by keyword)
*DefaultPaperDimension: Letter
*PaperDimension Letter/US Letter: "612 792"
*PaperDimension Legal/US Legal: "612 1008"
*PaperDimension A4: "595 842"
*PaperDimension B5: "516 729"
*PaperDimension LetterSmall/US Letter Small: "612 792"
*PaperDimension A4Small/A4 Small: "595 842"
*PaperDimension LegalSmall/US Legal Small: "612 1008"
*PaperDimension Monarch/Monarch Envelope Edge Fed: "611 792"
*PaperDimension Com10/Com10 Envelope Edge Fed: "610 792"
*RequiresPageRegion Multipurpose: True
*OpenUI *InputSlot: PickOne
*OrderDependency: 20 AnySetup *InputSlot
*DefaultInputSlot: Upper
*InputSlot Upper/Cassette (250 Sheets): "
currentpagedevice /InputAttributes get 0 get
dup null eq
{ pop }
{ dup length 1 add dict copy
dup /InputAttributes
1 dict dup /Priority [0] put
put setpagedevice
} ifelse"
*End
*InputSlot Multipurpose/Multipurpose Tray: "
1 dict dup /ManualFeed true put setpagedevice"
*End
*InputSlot Lower/Cassette (500 Sheets): "
currentpagedevice /InputAttributes get 2 get
dup null eq
{ pop }
{ dup length 1 add dict copy
dup /InputAttributes
1 dict dup /Priority [2 0] put
put setpagedevice
} ifelse"
*End
*InputSlot Envelope/Envelope Feeder: "
currentpagedevice /InputAttributes get 3 get
dup null eq
{ pop }
{ dup length 1 add dict copy
dup /InputAttributes
1 dict dup /Priority [3 0] put
put setpagedevice
} ifelse"
*End
*?InputSlot: "
save
3 dict
dup /0 (Upper) put
dup /1 (Multipurpose) put
dup /2 (Lower) put
dup /3 (Envelope) put
currentpagedevice /InputAttributes get
dup /Priority known
{ /Priority get 0 get ( ) cvs cvn get }
{
dup length 1 eq
{ {pop} forall ( ) cvs cvn get }
{ pop pop (Unknown) } ifelse
} ifelse
= flush
restore
"
*End
*CloseUI: *InputSlot
*DefaultOutputBin: OnlyOne
*DefaultOutputOrder: Normal
*OpenUI *ManualFeed/Manual Feed: Boolean
*OrderDependency: 20 AnySetup *ManualFeed
*DefaultManualFeed: False
*ManualFeed True: "1 dict dup /ManualFeed true put setpagedevice"
*ManualFeed False: "1 dict dup /ManualFeed false put setpagedevice"
*?ManualFeed: "
save
currentpagedevice /ManualFeed get
{(True)}{(False)}ifelse = flush
restore
"
*End
*CloseUI: *ManualFeed
*OpenUI *TraySwitch: Boolean
*OrderDependency: 50 AnySetup *TraySwitch
*DefaultTraySwitch: False
*TraySwitch True: "1 dict dup /TraySwitch true put setpagedevice"
*TraySwitch False: "1 dict dup /TraySwitch false put setpagedevice"
*?TraySwitch: "
save
currentpagedevice /TraySwitch get
{(True)}{(False)}ifelse = flush
restore
"
*End
*CloseUI: *TraySwitch
*% Font Information =====================
*DefaultFont: Courier
*Font AvantGarde-Book: Standard "(001.002)" Standard ROM
*Font AvantGarde-BookOblique: Standard "(001.002)" Standard ROM
*Font AvantGarde-Demi: Standard "(001.003)" Standard ROM
*Font AvantGarde-DemiOblique: Standard "(001.003)" Standard ROM
*Font Bookman-Demi: Standard "(001.003S)" Standard ROM
*Font Bookman-DemiItalic: Standard "(001.003S)" Standard ROM
*Font Bookman-Light: Standard "(001.003S)" Standard ROM
*Font Bookman-LightItalic: Standard "(001.003S)" Standard ROM
*Font Courier: Standard "(002.003)" Standard ROM
*Font Courier-Bold: Standard "(002.003)" Standard ROM
*Font Courier-BoldOblique: Standard "(002.003)" Standard ROM
*Font Courier-Oblique: Standard "(002.003)" Standard ROM
*Font Helvetica: Standard "(001.006S)" Standard ROM
*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
*Font NewCenturySchlbk-Bold: Standard "(001.008S)" Standard ROM
*Font NewCenturySchlbk-BoldItalic: Standard "(001.006S)" Standard ROM
*Font NewCenturySchlbk-Italic: Standard "(001.005S)" Standard ROM
*Font NewCenturySchlbk-Roman: Standard "(001.006S)" Standard ROM
*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
*Font Symbol: Special "(001.007S)" Special ROM
*Font Times-Bold: Standard "(001.007S)" Standard ROM
*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
*Font Times-Italic: Standard "(001.007S)" Standard ROM
*Font Times-Roman: Standard "(001.007S)" Standard ROM
*Font ZapfChancery-MediumItalic: Standard "(001.006)" Standard ROM
*Font ZapfDingbats: Special "(001.004S)" Special ROM
*?FontQuery: "
save
{ count 1 gt
{ exch dup 127 string cvs (/) print print (:) print
/Font resourcestatus {pop pop (Yes)} {(No)} ifelse =
} { exit } ifelse
} bind loop
(*) = flush
restore
"
*End
*?FontList: "
save
(*) {cvn ==} 128 string /Font resourceforall
(*) = flush
restore
"
*End
*% Printer Messages (verbatim from printer):
*Message: "%%[ exitserver: permanent state may be changed ]%%"
*Message: "%%[ Flushing: rest of job (to end-of-file) will be ignored ]%%"
*Message: "\FontName\ not found, using Courier"
*% Status (format: %%[ status: <one of these> ] %%)
*Status: "initializing"
*Status: "idle"
*Status: "holding"
*Status: "busy"
*Status: "waiting"
*Status: "PrinterError: cover open"
*Status: "PrinterError: warming up"
*Status: "PrinterError: out of paper"
*Status: "PrinterError: toner cartridge missing or incorrect"
*Status: "PrinterError: paper jam"
*Status: "PrinterError: Cassette (250 Sheets): no paper tray"
*Status: "PrinterError: Cassette (250 Sheets): out of paper"
*Status: "PrinterError: Cassette (500 Sheets): no paper tray"
*Status: "PrinterError: Cassette (500 Sheets): out of paper"
*Status: "PrinterError: Multipurpose Tray: out of paper"
*Status: "PrinterError: Envelope Feeder: out of paper"
*Status: "PrinterError: Manual Feed: out of paper"
*Status: "PrinterError: waiting for manual feed"
*Status: "PrinterError: fixing temperature malfunction"
*Status: "PrinterError: scanner motor malfunction"
*% Input Sources (format: %%[ status: <stat>; source: <one of these> ]%% )
*Source: "Serial"
*Source: "SerialB"
*Source: "LocalTalk"
*Source: "Parallel"
*Source: "EtherTalk"
*% Printer Error (format: %%[ PrinterError: <one of these> ]%%)
*PrinterError: "cover open"
*PrinterError: "warming up"
*PrinterError: "out of paper"
*PrinterError: "toner cartridge missing or incorrect"
*PrinterError: "paper jam"
*PrinterError: "Cassette (250 Sheets): no paper tray"
*PrinterError: "Cassette (250 Sheets): out of paper"
*PrinterError: "Cassette (500 Sheets): no paper tray"
*PrinterError: "Cassette (500 Sheets): out of paper"
*PrinterError: "Multipurpose Tray: out of paper"
*PrinterError: "Envelope Feeder: out of paper"
*PrinterError: "Manual Feed: out of paper"
*PrinterError: "waiting for manual feed"
*PrinterError: "fixing temperature malfunction"
*PrinterError: "scanner motor malfunction"
*%DeviceAdjustMatrix: "[1 0 0 1 0 0]"
*% Color Separation Information =====================
*DefaultColorSep: ProcessBlack.85lpi.600dpi/85 lpi / 600 dpi
*InkName: ProcessBlack/Process Black
*InkName: CustomColor/Custom Color
*InkName: ProcessCyan/Process Cyan
*InkName: ProcessMagenta/Process Magenta
*InkName: ProcessYellow/Process Yellow
*% For 60 lpi / 300 dpi ===============================
*ColorSepScreenAngle ProcessBlack.60lpi.300dpi/60 lpi / 300 dpi: "45"
*ColorSepScreenAngle CustomColor.60lpi.300dpi/60 lpi / 300 dpi: "45"
*ColorSepScreenAngle ProcessCyan.60lpi.300dpi/60 lpi / 300 dpi: "15"
*ColorSepScreenAngle ProcessMagenta.60lpi.300dpi/60 lpi / 300 dpi: "75"
*ColorSepScreenAngle ProcessYellow.60lpi.300dpi/60 lpi / 300 dpi: "0"
*ColorSepScreenFreq ProcessBlack.60lpi.300dpi/60 lpi / 300 dpi: "60"
*ColorSepScreenFreq CustomColor.60lpi.300dpi/60 lpi / 300 dpi: "60"
*ColorSepScreenFreq ProcessCyan.60lpi.300dpi/60 lpi / 300 dpi: "60"
*ColorSepScreenFreq ProcessMagenta.60lpi.300dpi/60 lpi / 300 dpi: "60"
*ColorSepScreenFreq ProcessYellow.60lpi.300dpi/60 lpi / 300 dpi: "60"
*% For 53 lpi / 300 dpi ===============================
*ColorSepScreenAngle ProcessBlack.53lpi.300dpi/53 lpi / 300 dpi: "45.0"
*ColorSepScreenAngle CustomColor.53lpi.300dpi/53 lpi / 300 dpi: "45.0"
*ColorSepScreenAngle ProcessCyan.53lpi.300dpi/53 lpi / 300 dpi: "71.5651"
*ColorSepScreenAngle ProcessMagenta.53lpi.300dpi/53 lpi / 300 dpi: "18.4349"
*ColorSepScreenAngle ProcessYellow.53lpi.300dpi/53 lpi / 300 dpi: "0.0"
*ColorSepScreenFreq ProcessBlack.53lpi.300dpi/53 lpi / 300 dpi: "53.033"
*ColorSepScreenFreq CustomColor.53lpi.300dpi/53 lpi / 300 dpi: "53.033"
*ColorSepScreenFreq ProcessCyan.53lpi.300dpi/53 lpi / 300 dpi: "47.4342"
*ColorSepScreenFreq ProcessMagenta.53lpi.300dpi/53 lpi / 300 dpi: "47.4342"
*ColorSepScreenFreq ProcessYellow.53lpi.300dpi/53 lpi / 300 dpi: "50.0"
*% For 85 lpi / 600 dpi (5,5,2,6,6,2,20/3,0) =====================
*ColorSepScreenAngle ProcessBlack.85lpi.600dpi/85 lpi / 600 dpi: "45.0"
*ColorSepScreenAngle CustomColor.85lpi.600dpi/85 lpi / 600 dpi: "45.0"
*ColorSepScreenAngle ProcessCyan.85lpi.600dpi/85 lpi / 600 dpi: "71.5651"
*ColorSepScreenAngle ProcessMagenta.85lpi.600dpi/85 lpi / 600 dpi: "18.4349"
*ColorSepScreenAngle ProcessYellow.85lpi.600dpi/85 lpi / 600 dpi: "0.0"
*ColorSepScreenFreq ProcessBlack.85lpi.600dpi/85 lpi / 600 dpi: "84.8528"
*ColorSepScreenFreq CustomColor.85lpi.600dpi/85 lpi / 600 dpi: "84.8528"
*ColorSepScreenFreq ProcessCyan.85lpi.600dpi/85 lpi / 600 dpi: "94.8683"
*ColorSepScreenFreq ProcessMagenta.85lpi.600dpi/85 lpi / 600 dpi: "94.8683"
*ColorSepScreenFreq ProcessYellow.85lpi.600dpi/85 lpi / 600 dpi: "30.0"
*ColorSepScreenProc ProcessYellow.85lpi.600dpi/85 lpi / 600 dpi: "
{1 add 2 div 3 mul dup floor sub 2 mul 1 sub exch
1 add 2 div 3 mul dup floor sub 2 mul 1 sub exch
abs exch abs 2 copy add 1 gt {1 sub dup mul exch 1 sub dup mul add 1
sub }{dup mul exch dup mul add 1 exch sub }ifelse }"
*End
*% For 71 lpi / 600 dpi ===============================
*ColorSepScreenAngle ProcessBlack.71lpi.600dpi/71 lpi / 600 dpi: "45.0"
*ColorSepScreenAngle CustomColor.71lpi.600dpi/71 lpi / 600 dpi: "45.0"
*ColorSepScreenAngle ProcessCyan.71lpi.600dpi/71 lpi / 600 dpi: "71.5651"
*ColorSepScreenAngle ProcessMagenta.71lpi.600dpi/71 lpi / 600 dpi: "18.4349"
*ColorSepScreenAngle ProcessYellow.71lpi.600dpi/71 lpi / 600 dpi: "0.0"
*ColorSepScreenFreq ProcessBlack.71lpi.600dpi/71 lpi / 600 dpi: "70.7107"
*ColorSepScreenFreq CustomColor.71lpi.600dpi/71 lpi / 600 dpi: "70.7107"
*ColorSepScreenFreq ProcessCyan.71lpi.600dpi/71 lpi / 600 dpi: "63.2456"
*ColorSepScreenFreq ProcessMagenta.71lpi.600dpi/71 lpi / 600 dpi: "63.2456"
*ColorSepScreenFreq ProcessYellow.71lpi.600dpi/71 lpi / 600 dpi: "66.6667"
*% Produced by "bldppd42.ps" version 4.0 edit 11
*% Last Edit Date: Mar 23 1994
*% The byte count of this file should be exactly 022556 or 023212
*% depending on the filesystem it resides in.
*% end of PPD file for LaserWriter Pro 630
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,2 @@
12345
67890
@@ -0,0 +1,4 @@
abcdef
ghijklmn
opqrstu
vwxyz.
@@ -0,0 +1,22 @@
Contents of directory "test"
PPDConfig UI Application
- Application.cpp
- Application.h
- Application.rsrc
- MsgConsts.h
- PPDConfig.x86.proj (BeIDE project file)
Test command line application
- Test.cpp
- Test.h
- Test.proj (BeIDE project file)
- TestParser.cpp
- TestScanner.cpp
Files used by test command line application:
- aptollw1.ppd
- include.ppd
- main.ppd
File header.txt contains parts of a PPD file that are typically part of the "header".
+14
View File
@@ -0,0 +1,14 @@
SubDir HAIKU_TOP src tests add-ons print ppd ui ;
# SetSubDirSupportedPlatformsBeOSCompatible ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd model ;
SubDirHdrs $(HAIKU_TOP) src tests add-ons print ppd parser ;
StaticLibrary
libppdtest.a
:
PPDConfigView.cpp
PrinterSelection.cpp
UIUtils.cpp
;
@@ -0,0 +1,537 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "PPDConfigView.h"
#include "PPDParser.h"
#include "StatementListVisitor.h"
#include "UIUtils.h"
#include <Box.h>
#include <CheckBox.h>
#include <Menu.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <RadioButton.h>
#include <ScrollView.h>
#include <StringView.h>
#include <Window.h>
// margin
const float kLeftMargin = 3.0;
const float kRightMargin = 3.0;
const float kTopMargin = 3.0;
const float kBottomMargin = 3.0;
// space between views
const float kHorizontalSpace = 8.0;
const float kVerticalSpace = 8.0;
// Message what values
const uint32 kMsgBooleanChanged = 'bool';
const uint32 kMsgStringChanged = 'strc';
#include <stdio.h>
class DefaultValueExtractor : public StatementListVisitor
{
BMessage fDefaultValues;
public:
void DoDefault(Statement* statement)
{
const char* keyword = statement->GetKeywordString();
const char* value = statement->GetValueString();
if (keyword != NULL && value != NULL) {
fDefaultValues.AddString(keyword, value);
}
}
const BMessage& GetDefaultValues()
{
return fDefaultValues;
}
};
class CategoryBuilder : public StatementListVisitor
{
BOutlineListView* fCategories;
public:
CategoryBuilder(BOutlineListView* categories)
: fCategories(categories)
{}
void AddStatement(const char* text, Statement* statement)
{
if (text != NULL) {
BStringItem* item = new CategoryItem(text, statement, GetLevel());
fCategories->AddItem(item);
}
}
void BeginGroup(GroupStatement* group)
{
const char* translation = group->GetGroupTranslation();
const char* name = group->GetGroupName();
const char* text = NULL;
if (translation != NULL) {
text = translation;
} else {
text = name;
}
AddStatement(text, group->GetStatement());
}
};
/*
DetailsBuilder adds views to the details view and sets the
value to the one specified in the settings.
The group type determines the view to be used for user input:
Type Input
---------------------
Boolean BRadioButton
PickOne BMenuField
PickMany BCheckBox
Unknown BCheckBox
*/
class DetailsBuilder : public StatementListVisitor
{
BView* fParent;
BView* fDetails;
BRect fBounds;
const char* fKeyword;
const char* fValue;
GroupStatement fGroup;
BMenu* fMenu;
BMenuField* fMenuField;
const BMessage& fSettings;
void AddView(BView* view);
BMessage* GetMessage(uint32 what, const char* option);
public:
DetailsBuilder(BView* parent, BView* details, BRect bounds, Statement* group, const BMessage& settings);
BRect GetBounds() { return fBounds; }
void Visit(StatementList* list);
void DoValue(Statement* statement);
};
void DetailsBuilder::AddView(BView* view)
{
if (view != NULL) {
fDetails->AddChild(view);
view->ResizeToPreferred();
fBounds.OffsetBy(0, view->Bounds().Height()+1);
BControl* control = dynamic_cast<BControl*>(view);
if (control != NULL) {
control->SetTarget(fParent);
}
}
}
DetailsBuilder::DetailsBuilder(BView* parent, BView* details, BRect bounds, Statement* group, const BMessage& settings)
: fParent(parent)
, fDetails(details)
, fBounds(bounds)
, fGroup(group)
, fMenu(NULL)
, fMenuField(NULL)
, fSettings(settings)
{
fKeyword = fGroup.GetGroupName();
if (fKeyword == NULL) return;
fValue = settings.FindString(fKeyword);
const char* label = fGroup.GetGroupTranslation();
if (label == NULL) {
label = fKeyword;
}
BView* view = NULL;
if (fGroup.GetType() == GroupStatement::kPickOne) {
fMenu = new BMenu("<pick one>");
fMenu->SetRadioMode(true);
fMenu->SetLabelFromMarked(true);
fMenuField = new BMenuField(fBounds, "menuField", label, fMenu);
view = fMenuField;
} else if (fGroup.GetType() == GroupStatement::kBoolean) {
BMessage* message = GetMessage(kMsgBooleanChanged, "");
BCheckBox* cb = new BCheckBox(fBounds, "", label, message);
view = cb;
cb->SetValue((fValue != NULL && strcmp(fValue, "True") == 0)
? B_CONTROL_ON
: B_CONTROL_OFF);
}
AddView(view);
}
void DetailsBuilder::Visit(StatementList* list)
{
if (fKeyword == NULL) return;
StatementListVisitor::Visit(list);
}
BMessage* DetailsBuilder::GetMessage(uint32 what, const char* option)
{
BMessage* message = new BMessage(what);
message->AddString("keyword", fKeyword);
if (option != NULL) {
message->AddString("option", option);
}
return message;
}
void DetailsBuilder::DoValue(Statement* statement)
{
if (GetLevel() != 0) return;
if (strcmp(fKeyword, statement->GetKeywordString()) != 0) return;
const char* text = NULL;
const char* option = statement->GetOptionString();
if (statement->GetTranslationString() != NULL) {
text = statement->GetTranslationString();
} else if (option != NULL) {
text = option;
}
if (text == NULL) return;
BView* view = NULL;
BMessage* message = NULL;
if (fGroup.GetType() == GroupStatement::kPickMany ||
fGroup.GetType() == GroupStatement::kUnknown) {
message = GetMessage(kMsgStringChanged, option);
view = new BCheckBox(fBounds, "", text, message);
} else if (fGroup.GetType() == GroupStatement::kPickOne) {
message = GetMessage(kMsgStringChanged, option);
BMenuItem* item = new BMenuItem(text, message);
item->SetTarget(fParent);
fMenu->AddItem(item);
if (fValue != NULL && option != NULL && strcmp(fValue, option) == 0) {
item->SetMarked(true);
}
}
AddView(view);
}
#define kBoxHeight 20
#define kBoxBottomMargin 4
#define kBoxLeftMargin 8
#define kBoxRightMargin 8
#define kItemLeftMargin 5
#define kItemRightMargin 5
class PPDBuilder : public StatementListVisitor
{
BView* fParent;
BView* fView;
BRect fBounds;
BMessage& fSettings;
BList fNestedBoxes;
bool IsTop()
{
return fNestedBoxes.CountItems() == 0;
}
void Push(BView* view)
{
fNestedBoxes.AddItem(view);
}
void Pop()
{
fNestedBoxes.RemoveItem((int32)fNestedBoxes.CountItems()-1);
}
BView* GetView()
{
if (IsTop()) {
return fView;
} else {
return (BView*)fNestedBoxes.ItemAt(fNestedBoxes.CountItems()-1);
}
}
BRect GetControlBounds()
{
if (IsTop()) {
BRect bounds(fBounds);
bounds.left += kItemLeftMargin /** GetLevel()*/;
bounds.right -= kItemRightMargin /** GetLevel()*/;
return bounds;
}
BView* box = GetView();
BRect bounds(box->Bounds());
bounds.top = bounds.bottom - kBoxBottomMargin;
bounds.bottom = bounds.top + kBoxHeight;
bounds.left += kBoxLeftMargin;
bounds.right -= kBoxRightMargin;
return bounds;
}
bool IsUIGroup(GroupStatement* group)
{
return group->IsUIGroup() || group->IsJCL();
}
void UpdateParentHeight(float height)
{
if (IsTop()) {
fBounds.OffsetBy(0, height);
} else {
BView* parent = GetView();
parent->ResizeBy(0, height);
}
}
public:
PPDBuilder(BView* parent, BView* view, BMessage& settings)
: fParent(parent)
, fView(view)
, fBounds(view->Bounds())
, fSettings(settings)
{
RemoveChildren(view);
fBounds.OffsetTo(0, 0);
fBounds.left += kLeftMargin;
fBounds.top += kTopMargin;
fBounds.right -= kRightMargin;
}
BRect GetBounds()
{
return BRect(0, 0, fView->Bounds().Width(), fBounds.top);
}
void AddUIGroup(const char* text, Statement* statement)
{
if (statement->GetChildren() == NULL) return;
DetailsBuilder builder(fParent, GetView(), GetControlBounds(), statement, fSettings);
builder.Visit(statement->GetChildren());
if (IsTop()) {
fBounds.OffsetTo(fBounds.left, builder.GetBounds().top);
} else {
BView* box = GetView();
box->ResizeTo(box->Bounds().Width(), builder.GetBounds().top + kBoxBottomMargin);
}
}
void OpenGroup(const char* text)
{
if (text != NULL) {
BBox* box = new BBox(GetControlBounds(), text);
box->SetLabel(text);
GetView()->AddChild(box);
Push(box);
box->ResizeTo(box->Bounds().Width(), kBoxHeight);
}
}
void CloseGroup()
{
if (!IsTop()) {
BView* box = GetView();
Pop();
UpdateParentHeight(box->Bounds().Height());
}
}
void BeginGroup(GroupStatement* group)
{
const char* translation = group->GetGroupTranslation();
const char* name = group->GetGroupName();
const char* text = NULL;
if (translation != NULL) {
text = translation;
} else {
text = name;
}
if (IsUIGroup(group)) {
AddUIGroup(text, group->GetStatement());
} else {
OpenGroup(text);
}
}
void EndGroup(GroupStatement* group)
{
if (!IsUIGroup(group)) {
CloseGroup();
}
}
};
PPDConfigView::PPDConfigView(BRect bounds, const char *name, uint32 resizeMask, uint32 flags)
: BView(bounds, name, resizeMask, flags)
, fPPD(NULL)
{
// add category outline list view
bounds.OffsetTo(0, 0);
BRect listBounds(bounds.left + kLeftMargin, bounds.top + kTopMargin,
bounds.right - kHorizontalSpace, bounds.bottom - kBottomMargin);
listBounds.right -= B_V_SCROLL_BAR_WIDTH;
listBounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
BStringView* label = new BStringView(listBounds, "printer-settings", "Printer Settings:");
AddChild(label);
label->ResizeToPreferred();
listBounds.top += label->Bounds().bottom + 5;
// add details view
fDetails = new BView(listBounds, "details", B_FOLLOW_ALL_SIDES, B_WILL_DRAW);
fDetails->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
BScrollView* scrollView = new BScrollView("details-scroll-view",
fDetails, B_FOLLOW_ALL_SIDES, 0, true, true);
AddChild(scrollView);
}
void SetScrollBar(BScrollBar* scroller, float contents, float client)
{
if (scroller != NULL) {
float extent = contents - client;
if (extent >= 0) {
scroller->SetRange(0, extent);
scroller->SetProportion(1-extent / contents);
scroller->SetSteps(20, client);
} else {
scroller->SetRange(0, 0);
}
}
}
void PPDConfigView::FillCategories()
{
if (fPPD == NULL) return;
PPDBuilder builder(this, fDetails, fSettings);
builder.Visit(fPPD);
BScrollBar* scroller = fDetails->ScrollBar(B_VERTICAL);
SetScrollBar(scroller, builder.GetBounds().Height(), fDetails->Bounds().Height());
scroller = fDetails->ScrollBar(B_HORIZONTAL);
SetScrollBar(scroller, builder.GetBounds().Width(), fDetails->Bounds().Width());
}
void PPDConfigView::FillDetails(Statement* statement)
{
RemoveChildren(fDetails);
if (statement == NULL) {
return;
}
StatementList* children= statement->GetChildren();
if (children == NULL) {
return;
}
BRect bounds(fDetails->Bounds());
bounds.OffsetTo(kLeftMargin, kTopMargin);
DetailsBuilder builder(this, fDetails, bounds, statement, fSettings);
builder.Visit(children);
}
void PPDConfigView::BooleanChanged(BMessage* msg)
{
const char* keyword = msg->FindString("keyword");
int32 value;
if (msg->FindInt32("be:value", &value) == B_OK) {
const char* option;
if (value) {
option = "True";
} else {
option = "False";
}
fSettings.ReplaceString(keyword, option);
}
}
void PPDConfigView::StringChanged(BMessage* msg)
{
const char* keyword = msg->FindString("keyword");
const char* option = msg->FindString("option");
if (keyword != NULL && keyword != NULL) {
fSettings.ReplaceString(keyword, option);
}
}
void PPDConfigView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kMsgBooleanChanged: BooleanChanged(msg);
break;
case kMsgStringChanged: StringChanged(msg);
break;
}
BView::MessageReceived(msg);
}
void PPDConfigView::SetupSettings(const BMessage& currentSettings)
{
DefaultValueExtractor extractor;
extractor.Visit(fPPD);
const BMessage &defaultValues(extractor.GetDefaultValues());
fSettings.MakeEmpty();
char* name;
type_code code;
for (int32 index = 0; defaultValues.GetInfo(B_STRING_TYPE, index, &name, &code) == B_OK; index ++) {
const char* value = currentSettings.FindString(name);
if (value == NULL) {
value = defaultValues.FindString(name);
}
if (value != NULL) {
fSettings.AddString(name, value);
}
}
}
void PPDConfigView::Set(const char* file, const BMessage& currentSettings)
{
delete fPPD;
PPDParser parser(file);
fPPD = parser.ParseAll();
if (fPPD == NULL) {
fprintf(stderr, "Parsing error (%s): %s\n", file, parser.GetErrorMessage());
}
SetupSettings(currentSettings);
FillCategories();
}
@@ -0,0 +1,57 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PPD_CONFIG_VIEW_H
#define _PPD_CONFIG_VIEW_H
#include "PPD.h"
#include <View.h>
#include <ListItem.h>
#include <OutlineListView.h>
class CategoryItem : public BStringItem
{
private:
Statement* fStatement;
public:
CategoryItem(const char* text, Statement* statement, uint32 level)
: BStringItem(text, level)
, fStatement(statement)
{
}
Statement* GetStatement() { return fStatement; }
};
class PPDConfigView : public BView {
private:
PPD* fPPD;
BView* fDetails;
BMessage fSettings;
void SetupSettings(const BMessage& settings);
void BooleanChanged(BMessage* msg);
void StringChanged(BMessage* msg);
public:
PPDConfigView(BRect rect, const char *name, uint32 resizeMask, uint32 flags);
// The view has to be attached to a window when this
// method is called.
void Set(const char* ppdFile, const BMessage& settings);
const BMessage& GetSettings();
void FillCategories();
void FillDetails(Statement* statement);
void MessageReceived(BMessage* msg);
};
#endif
@@ -0,0 +1,172 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "PrinterSelection.h"
#include "PPDParser.h"
#include "StatementListVisitor.h"
#include "UIUtils.h"
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#include <ScrollView.h>
#include <StringView.h>
// margin
const float kLeftMargin = 3.0;
const float kRightMargin = 3.0;
const float kTopMargin = 3.0;
const float kBottomMargin = 3.0;
// space between views
const float kHorizontalSpace = 8.0;
const float kVerticalSpace = 8.0;
#include <stdio.h>
PrinterSelectionView::PrinterSelectionView(BRect bounds, const char *name, uint32 resizeMask, uint32 flags)
: BView(bounds, name, resizeMask, flags)
{
// add vendor list view
bounds.OffsetTo(0, 0);
BRect listBounds(bounds.left + kLeftMargin, bounds.top + kTopMargin,
bounds.right / 3.0 - kHorizontalSpace / 2, bounds.bottom - kBottomMargin);
listBounds.right -= B_V_SCROLL_BAR_WIDTH;
listBounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
BStringView* label = new BStringView(listBounds, "vendors-label", "Vendors:");
AddChild(label);
label->ResizeToPreferred();
listBounds.top += label->Bounds().bottom + 5;
fVendors = new BListView(listBounds, "vendors", B_SINGLE_SELECTION_LIST,
B_FOLLOW_ALL);
FillVendors();
BScrollView* scrollView = new BScrollView("vendors-scroll-view",
fVendors, B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, 0, true, true);
AddChild(scrollView);
// add details view
BRect printerBounds(listBounds);
printerBounds.left = B_V_SCROLL_BAR_WIDTH + printerBounds.right + kHorizontalSpace;
printerBounds.right = bounds.right - kRightMargin - B_V_SCROLL_BAR_WIDTH;
printerBounds.top = bounds.top + kTopMargin;
label = new BStringView(printerBounds, "printers-label", "Printers:");
AddChild(label);
label->ResizeToPreferred();
BRect detailBounds(listBounds);
detailBounds.left = B_V_SCROLL_BAR_WIDTH + detailBounds.right + kHorizontalSpace;
detailBounds.right = bounds.right - kRightMargin - B_V_SCROLL_BAR_WIDTH;
fPrinters = new BListView(detailBounds, "printers", B_SINGLE_SELECTION_LIST,
B_FOLLOW_ALL);
scrollView = new BScrollView("printers-scroll-view",
fPrinters, B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, 0, true, true);
AddChild(scrollView);
}
void PrinterSelectionView::AttachedToWindow()
{
fVendors->SetSelectionMessage(new BMessage('sel'));
fVendors->SetTarget(this);
fPrinters->SetSelectionMessage(new BMessage('prnt'));
fPrinters->SetTarget(this);
}
void PrinterSelectionView::FillVendors()
{
BDirectory directory("/boot/beos/etc/ppd");
BEntry entry;
while (directory.GetNextEntry(&entry) == B_OK) {
char name[B_FILE_NAME_LENGTH];
entry.GetName(name);
BPath path;
entry.GetPath(&path);
fVendors->AddItem(new FileItem(name, path.Path()));
}
}
void PrinterSelectionView::FillPrinters(const char* vendor)
{
MakeEmpty(fPrinters);
BList printers;
BDirectory directory(vendor);
BEntry entry;
while (directory.GetNextEntry(&entry) == B_OK) {
char name[B_FILE_NAME_LENGTH];
entry.GetName(name);
BPath path;
entry.GetPath(&path);
PPDParser parser(path.Path());
PPD* ppd = parser.ParseHeader();
if (parser.HasWarning()) {
fprintf(stderr, "Warning(s): %s", parser.GetWarningMessage());
}
if (ppd != NULL) {
BString label;
const char* s;
s = ppd->GetValue("ModelName");
if (s != NULL) {
label << s;
}
s = ppd->GetValue("PCFileName");
if (s != NULL) {
label << " [" << s << "]";
}
s = ppd->GetValue("Manufacturer");
if (s != NULL) {
label << " (" << s << ")";
}
printers.AddItem(new FileItem(label.String(), path.Path()));
delete ppd;
} else {
fprintf(stderr, "Parsing error (%s)\n%s\n", path.Path(),
parser.GetErrorMessage());
}
}
fPrinters->AddList(&printers);
}
void PrinterSelectionView::MessageReceived(BMessage* msg)
{
int32 index;
switch (msg->what) {
case 'sel':
if (msg->FindInt32("index", &index) == B_OK) {
FileItem* file = (FileItem*)fVendors->ItemAt(index);
if (file != NULL) {
FillPrinters(file->GetFile());
}
}
break;
case 'prnt':
if (msg->FindInt32("index", &index) == B_OK) {
FileItem* file = (FileItem*)fPrinters->ItemAt(index);
if (file != NULL) {
BMessage copy(*Message());
copy.AddString("file", file->GetFile());
InvokeNotify(&copy);
}
}
break;
}
BView::MessageReceived(msg);
}
@@ -0,0 +1,49 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _PRINTER_SELECTION_H
#define _PRINTER_SELECTION_H
#include "PPD.h"
#include <Invoker.h>
#include <View.h>
#include <ListItem.h>
#include <ListView.h>
class FileItem : public BStringItem
{
private:
BString fFile;
public:
FileItem(const char* label, const char* file)
: BStringItem(label)
, fFile(file)
{
}
const char* GetFile() { return fFile.String(); }
};
class PrinterSelectionView : public BView, public BInvoker
{
private:
BListView* fVendors;
BListView* fPrinters;
public:
PrinterSelectionView(BRect rect, const char *name, uint32 resizeMask, uint32 flags);
void AttachedToWindow();
void FillVendors();
void FillPrinters(const char* vendor);
void MessageReceived(BMessage* msg);
};
#endif
@@ -0,0 +1,31 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#include "UIUtils.h"
void MakeEmpty(BListView* list)
{
if (list != NULL) {
BListItem* item;
while ((item = list->RemoveItem((int32)0)) != NULL) {
delete item;
}
}
}
void RemoveChildren(BView* view)
{
if (view != NULL) {
BView* child;
while ((child = view->ChildAt(0)) != NULL) {
child->RemoveSelf();
delete child;
}
}
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <[email protected]>
*/
#ifndef _UI_UTILS_H
#define _UI_UTILS_H
#include <ListView.h>
void MakeEmpty(BListView* list);
void RemoveChildren(BView* view);
#endif