* Copyright 2001-2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Pfeiffer
*/
#include "PrinterListView.h"
#include <Application.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Directory.h>
#include <IconUtils.h>
#include <Locale.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <Resources.h>
#include <String.h>
#include <StringFormat.h>
#include "pr_server.h"
#include "Messages.h"
#include "Globals.h"
#include "PrintersWindow.h"
#include "SpoolFolder.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PrinterListView"
PrinterListView::PrinterListView(BRect frame)
: Inherited(frame, "printers_list", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL,
B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE),
fFolder(NULL),
fActivePrinter(NULL)
{
fLayoutData.fLeftColumnMaximumWidth = 100;
fLayoutData.fRightColumnMaximumWidth = 100;
}
PrinterListView::~PrinterListView()
{
while (!IsEmpty())
delete RemoveItem((int32)0);
}
void
PrinterListView::BuildPrinterList()
{
while (!IsEmpty())
delete RemoveItem((int32)0);
BPath path;
if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK)
return;
BDirectory dir(path.Path());
if (dir.InitCheck() != B_OK)
return;
BEntry entry;
while(dir.GetNextEntry(&entry) == B_OK) {
BDirectory printer(&entry);
_AddPrinter(printer, false);
}
_LayoutPrinterItems();
}
void
PrinterListView::AttachedToWindow()
{
Inherited::AttachedToWindow();
SetSelectionMessage(new BMessage(kMsgPrinterSelected));
SetInvocationMessage(new BMessage(kMsgMakeDefaultPrinter));
SetTarget(Window());
BPath path;
if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK)
return;
BDirectory dir(path.Path());
if (dir.InitCheck() != B_OK) {
if (create_directory(path.Path(), 0777) != B_OK)
return;
dir.SetTo(path.Path());
}
fFolder = new FolderWatcher(Window(), dir, true);
fFolder->SetListener(this);
BuildPrinterList();
BString activePrinterName(ActivePrinterName());
for (int32 i = 0; i < CountItems(); i ++) {
PrinterItem* item = dynamic_cast<PrinterItem*>(ItemAt(i));
if (item != NULL && item->Name() == activePrinterName) {
Select(i);
fActivePrinter = item;
break;
}
}
}
bool
PrinterListView::QuitRequested()
{
delete fFolder;
return true;
}
void
PrinterListView::UpdateItem(PrinterItem* item)
{
item->UpdatePendingJobs();
InvalidateItem(IndexOf(item));
}
PrinterItem*
PrinterListView::ActivePrinter() const
{
return fActivePrinter;
}
void
PrinterListView::SetActivePrinter(PrinterItem* item)
{
fActivePrinter = item;
}
PrinterItem*
PrinterListView::SelectedItem() const
{
return dynamic_cast<PrinterItem*>(ItemAt(CurrentSelection()));
}
void
PrinterListView::EntryCreated(node_ref* node, entry_ref* entry)
{
BDirectory printer(node);
_AddPrinter(printer, true);
}
void
PrinterListView::EntryRemoved(node_ref* node)
{
PrinterItem* item = _FindItem(node);
if (item) {
if (item == fActivePrinter)
fActivePrinter = NULL;
RemoveItem(item);
delete item;
}
}
void
PrinterListView::AttributeChanged(node_ref* node)
{
BDirectory printer(node);
_AddPrinter(printer, true);
}
void
PrinterListView::_AddPrinter(BDirectory& printer, bool calculateLayout)
{
BString state;
node_ref node;
if (printer.InitCheck() == B_OK
&& printer.GetNodeRef(&node) == B_OK
&& _FindItem(&node) == NULL
&& printer.ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK
&& state == "free") {
BNodeInfo info(&printer);
char buffer[256];
if (info.GetType(buffer) == B_OK
&& strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) {
AddItem(new PrinterItem(static_cast<PrintersWindow*>(Window()),
printer, fLayoutData));
if (calculateLayout)
_LayoutPrinterItems();
}
}
}
void
PrinterListView::_LayoutPrinterItems()
{
float& leftColumnMaximumWidth = fLayoutData.fLeftColumnMaximumWidth;
float& rightColumnMaximumWidth = fLayoutData.fRightColumnMaximumWidth;
for (int32 i = 0; i < CountItems(); i ++) {
PrinterItem* item = static_cast<PrinterItem*>(ItemAt(i));
float leftColumnWidth = 0;
float rightColumnWidth = 0;
item->GetColumnWidth(this, leftColumnWidth, rightColumnWidth);
leftColumnMaximumWidth = MAX(leftColumnMaximumWidth,
leftColumnWidth);
rightColumnMaximumWidth = MAX(rightColumnMaximumWidth,
rightColumnWidth);
}
Invalidate();
}
PrinterItem*
PrinterListView::_FindItem(node_ref* node) const
{
for (int32 i = CountItems() - 1; i >= 0; i--) {
PrinterItem* item = dynamic_cast<PrinterItem*>(ItemAt(i));
node_ref ref;
if (item && item->Node()->GetNodeRef(&ref) == B_OK && ref == *node)
return item;
}
return NULL;
}
BBitmap* PrinterItem::sIcon = NULL;
BBitmap* PrinterItem::sSelectedIcon = NULL;
PrinterItem::PrinterItem(PrintersWindow* window, const BDirectory& node,
PrinterListLayoutData& layoutData)
: BListItem(0, false),
fFolder(NULL),
fNode(node),
fLayoutData(layoutData)
{
BRect rect(BPoint(0, 0), be_control_look->ComposeIconSize(B_LARGE_ICON));
if (sIcon == NULL) {
sIcon = new BBitmap(rect, B_RGBA32);
BMimeType type(PSRV_PRINTER_FILETYPE);
type.GetIcon(sIcon, (icon_size)(rect.IntegerHeight() + 1));
}
if (sIcon && sIcon->IsValid() && sSelectedIcon == NULL) {
const float checkMarkIconSize = be_control_look->ComposeIconSize(20).Height();
BBitmap *checkMark = _LoadVectorIcon("check_mark_icon",
checkMarkIconSize);
if (checkMark && checkMark->IsValid()) {
sSelectedIcon = new BBitmap(rect, B_RGBA32, true);
if (sSelectedIcon && sSelectedIcon->IsValid()) {
BView *view = new BView(rect, "offscreen", B_FOLLOW_ALL,
B_WILL_DRAW);
float y = rect.Height() - checkMark->Bounds().Height();
sSelectedIcon->Lock();
sSelectedIcon->AddChild(view);
view->DrawBitmap(sIcon);
view->SetDrawingMode(B_OP_ALPHA);
view->DrawBitmap(checkMark, BPoint(0, y));
view->Sync();
view->RemoveSelf();
sSelectedIcon->Unlock();
delete view;
}
}
delete checkMark;
}
_GetStringProperty(PSRV_PRINTER_ATTR_PRT_NAME, fName);
_GetStringProperty(PSRV_PRINTER_ATTR_COMMENTS, fComments);
_GetStringProperty(PSRV_PRINTER_ATTR_TRANSPORT, fTransport);
_GetStringProperty(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, fTransportAddress);
_GetStringProperty(PSRV_PRINTER_ATTR_DRV_NAME, fDriverName);
BPath path;
if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) != B_OK)
return;
path.Append(fName.String());
BDirectory dir(path.Path());
if (dir.InitCheck() == B_OK) {
fFolder = new SpoolFolder(window, this, dir);
UpdatePendingJobs();
}
}
PrinterItem::~PrinterItem()
{
delete fFolder;
}
void
PrinterItem::GetColumnWidth(BView* view, float& leftColumn, float& rightColumn)
{
BFont font;
view->GetFont(&font);
leftColumn = font.StringWidth(fName.String());
leftColumn = MAX(leftColumn, font.StringWidth(fDriverName.String()));
rightColumn = font.StringWidth(fPendingJobs.String());
rightColumn = MAX(rightColumn, font.StringWidth(fTransport.String()));
rightColumn = MAX(rightColumn, font.StringWidth(fComments.String()));
}
void
PrinterItem::Update(BView *owner, const BFont *font)
{
BListItem::Update(owner,font);
font_height height;
font->GetHeight(&height);
SetHeight((height.ascent + height.descent + height.leading) * 3.0 + 8.0);
}
bool PrinterItem::Remove(BListView* view)
{
BMessenger msgr;
if (GetPrinterServerMessenger(msgr) == B_OK) {
BMessage script(B_DELETE_PROPERTY);
script.AddSpecifier("Printer", view->IndexOf(this));
BMessage reply;
if (msgr.SendMessage(&script,&reply) == B_OK)
return true;
}
return false;
}
void
PrinterItem::DrawItem(BView *owner, BRect , bool complete)
{
BListView* list = dynamic_cast<BListView*>(owner);
if (list == NULL)
return;
BFont font;
owner->GetFont(&font);
font_height height;
font.GetHeight(&height);
float fntheight = height.ascent + height.descent + height.leading;
BRect bounds = list->ItemFrame(list->IndexOf(this));
rgb_color color = owner->ViewColor();
rgb_color oldLowColor = owner->LowColor();
rgb_color oldHighColor = owner->HighColor();
if (IsSelected())
color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
owner->SetLowColor(color);
owner->SetHighColor(color);
owner->FillRect(bounds);
owner->SetLowColor(oldLowColor);
owner->SetHighColor(oldHighColor);
float iconColumnWidth = B_LARGE_ICON + 8.0;
if (sIcon)
iconColumnWidth = sIcon->Bounds().Height() + 8.0;
float x = iconColumnWidth;
BPoint iconPt(bounds.LeftTop() + BPoint(2.0, 2.0));
BPoint namePt(iconPt + BPoint(x, fntheight));
BPoint driverPt(iconPt + BPoint(x, fntheight * 2.0));
BPoint defaultPt(iconPt + BPoint(x, fntheight * 3.0));
BPoint transportPt(iconPt + BPoint(x, fntheight * 3.0));
float totalWidth = bounds.Width() - iconColumnWidth;
float maximumWidth = fLayoutData.fLeftColumnMaximumWidth +
fLayoutData.fRightColumnMaximumWidth;
float width;
if (totalWidth < maximumWidth) {
width = fLayoutData.fRightColumnMaximumWidth * totalWidth /
maximumWidth;
} else {
width = fLayoutData.fRightColumnMaximumWidth;
}
BPoint pendingPt(bounds.right - width - 8.0, namePt.y);
BPoint commentPt(bounds.right - width - 8.0, driverPt.y);
drawing_mode mode = owner->DrawingMode();
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
owner->SetDrawingMode(B_OP_ALPHA);
#else
owner->SetDrawingMode(B_OP_OVER);
#endif
if (IsActivePrinter()) {
if (sSelectedIcon && sSelectedIcon->IsValid())
owner->DrawBitmap(sSelectedIcon, iconPt);
else
owner->DrawString(B_TRANSLATE("Default Printer"), defaultPt);
} else {
if (sIcon && sIcon->IsValid())
owner->DrawBitmap(sIcon, iconPt);
}
owner->SetDrawingMode(B_OP_OVER);
BString s = fName;
owner->SetFont(be_bold_font);
owner->TruncateString(&s, B_TRUNCATE_MIDDLE, pendingPt.x - namePt.x);
owner->DrawString(s.String(), s.Length(), namePt);
owner->SetFont(&font);
s = B_TRANSLATE("Driver: %driver%");
s.ReplaceFirst("%driver%", fDriverName);
owner->TruncateString(&s, B_TRUNCATE_END, commentPt.x - driverPt.x);
owner->DrawString(s.String(), s.Length(), driverPt);
if (fTransport.Length() > 0) {
s = B_TRANSLATE("Transport: %transport% %transport_address%");
s.ReplaceFirst("%transport%", fTransport);
s.ReplaceFirst("%transport_address%", fTransportAddress);
owner->TruncateString(&s, B_TRUNCATE_BEGINNING, totalWidth);
owner->DrawString(s.String(), s.Length(), transportPt);
}
s = fPendingJobs;
owner->TruncateString(&s, B_TRUNCATE_END, bounds.Width() - pendingPt.x);
owner->DrawString(s.String(), s.Length(), pendingPt);
s = fComments;
owner->TruncateString(&s, B_TRUNCATE_MIDDLE, bounds.Width() - commentPt.x);
owner->DrawString(s.String(), s.Length(), commentPt);
owner->SetDrawingMode(mode);
}
bool
PrinterItem::IsActivePrinter() const
{
return fName == ActivePrinterName();
}
bool
PrinterItem::HasPendingJobs() const
{
return fFolder && fFolder->CountJobs() > 0;
}
SpoolFolder*
PrinterItem::Folder() const
{
return fFolder;
}
BDirectory*
PrinterItem::Node()
{
return &fNode;
}
void
PrinterItem::UpdatePendingJobs()
{
uint32 pendingJobs = 0;
if (fFolder)
pendingJobs = fFolder->CountJobs();
static BStringFormat format(B_TRANSLATE("{0, plural,"
"=0{No pending jobs}"
"=1{1 pending job}"
"other{# pending jobs}}"));
format.Format(fPendingJobs, pendingJobs);
}
void
PrinterItem::_GetStringProperty(const char* propName, BString& outString)
{
fNode.ReadAttrString(propName, &outString);
}
BBitmap*
PrinterItem::_LoadVectorIcon(const char* resourceName, float iconSize)
{
size_t dataSize;
BResources* resources = BApplication::AppResources();
const void* data = resources->LoadResource(B_VECTOR_ICON_TYPE,
resourceName, &dataSize);
if (data != NULL){
BBitmap *iconBitmap = new BBitmap(BRect(0, 0, iconSize - 1,
iconSize - 1), 0, B_RGBA32);
if (BIconUtils::GetVectorIcon(
reinterpret_cast<const uint8*>(data),
dataSize, iconBitmap) == B_OK)
return iconBitmap;
else
delete iconBitmap;
};
return NULL;
}