* Added BTextView derived class HyperTextView, which allows for

associating text with an action performed when clicked. Very
  bare-bones, but sufficient for our purposes.
* Turned all URLs into proper URLs (i.e. prepended "http://" where
  missing).
* Added new AboutView::AddCopyrightEntry() versions. One can take an
  additional licenses list, the other extracts all info from a supplied
  BMessage.
* The displayed licenses and URLs are hyperlinks now. Clicking the
  former opens the license file in /etc/licenses, the latter try to open
  a browser (works with NetPositive at least).
* The "COPYRIGHTS" attribute of the executable can contain descriptions
  of optional packages. Those are read, parsed, and added as copyright
  entries.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25049 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-04-19 15:51:18 +00:00
parent d2e1e87261
commit b14a49c9bd
8 changed files with 651 additions and 20 deletions

View File

@ -7,6 +7,10 @@
* René Gollent
*/
#include <ctype.h>
#include <stdio.h>
#include <sys/utsname.h>
#include <time.h>
#include <AppFileInfo.h>
#include <Application.h>
@ -14,6 +18,7 @@
#include <File.h>
#include <FindDirectory.h>
#include <Font.h>
#include <fs_attr.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <OS.h>
@ -24,7 +29,6 @@
#include <ScrollView.h>
#include <String.h>
#include <StringView.h>
#include <TextView.h>
#include <TranslationUtils.h>
#include <TranslatorFormats.h>
#include <View.h>
@ -32,11 +36,13 @@
#include <VolumeRoster.h>
#include <Window.h>
#include <AppMisc.h>
#include <AutoDeleter.h>
#include <cpu_type.h>
#include <stdio.h>
#include <time.h>
#include <sys/utsname.h>
#include "HyperTextActions.h"
#include "HyperTextView.h"
#include "Utilities.h"
#define SCROLL_CREDITS_VIEW 'mviv'
@ -78,15 +84,20 @@ class AboutView : public BView {
virtual void MessageReceived(BMessage *msg);
virtual void MouseDown(BPoint pt);
void AddCopyrightEntry(const char *name, const char *text,
const Licenses& licenses, const char *url);
void AddCopyrightEntry(const char *name, const char *text,
const char *url = NULL);
void AddCopyrightEntry(const BMessage& packageDescription);
void PickRandomHaiku();
private:
void _AddCopyrightsFromAttribute();
BStringView *fMemView;
BStringView *fUptimeView;
BView *fInfoView;
BTextView *fCreditsView;
HyperTextView *fCreditsView;
BBitmap *fLogo;
@ -318,7 +329,7 @@ AboutView::AboutView(const BRect &rect)
r.left += fInfoView->Bounds().right + 1;
r.right -= B_V_SCROLL_BAR_WIDTH;
fCreditsView = new BTextView(r, "credits",
fCreditsView = new HyperTextView(r, "credits",
r.OffsetToCopy(0, 0).InsetByCopy(5, 5), B_FOLLOW_ALL);
fCreditsView->SetFlags(fCreditsView->Flags() | B_FRAME_EVENTS );
fCreditsView->SetStylable(true);
@ -352,7 +363,9 @@ AboutView::AboutView(const BRect &rect)
fCreditsView->Insert(string);
fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
fCreditsView->Insert("http://haiku-os.org\n\n");
fCreditsView->InsertHyperText("http://haiku-os.org",
new URLAction("http://haiku-os.org"));
fCreditsView->Insert("\n\n");
fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
fCreditsView->Insert("Team Leads:\n");
@ -507,7 +520,8 @@ AboutView::AboutView(const BRect &rect)
"gdb, wget, ncurses, termcap, "
"Bourne Again Shell.\n"
"Copyright " B_UTF8_COPYRIGHT " The Free Software Foundation.",
"www.gnu.org");
Licenses("GNU LGPL v2.1", "GNU GPL v2", "GNU GPL v3", NULL),
"http://www.gnu.org");
// FreeBSD copyrights
AddCopyrightEntry("The FreeBSD Project",
@ -516,7 +530,7 @@ AboutView::AboutView(const BRect &rect)
"ping, telnet, telnetd, traceroute\n"
"Copyright " B_UTF8_COPYRIGHT " 1994-2008 The FreeBSD Project. "
"All rights reserved.",
"www.freebsd.org");
"http://www.freebsd.org");
// NetBSD copyrights
AddCopyrightEntry("The NetBSD Project",
@ -525,36 +539,36 @@ AboutView::AboutView(const BRect &rect)
"ftp\n"
"Copyright " B_UTF8_COPYRIGHT " 1996-2008 The NetBSD Foundation, Inc. "
"All rights reserved.",
"www.netbsd.org");
"http://www.netbsd.org");
// FFMpeg copyrights
AddCopyrightEntry("FFMpeg libavcodec",
"Copyright " B_UTF8_COPYRIGHT " 2000-2007 Fabrice Bellard, et al.",
"www.ffmpeg.org");
"http://www.ffmpeg.org");
// AGG copyrights
AddCopyrightEntry("AntiGrain Geometry",
"Copyright " B_UTF8_COPYRIGHT " 2002-2006 Maxim Shemanarev (McSeem).",
"www.antigrain.com");
"http://www.antigrain.com");
// PDFLib copyrights
AddCopyrightEntry("PDFLib",
"Copyright " B_UTF8_COPYRIGHT " 1997-2006 PDFlib GmbH and Thomas Merz. "
"All rights reserved.\n"
"PDFlib and PDFlib logo are registered trademarks of PDFlib GmbH.",
"www.pdflib.com");
"http://www.pdflib.com");
// FreeType copyrights
AddCopyrightEntry("FreeType2",
"Portions of this software are copyright " B_UTF8_COPYRIGHT " 1996-2006 "
"The FreeType Project. All rights reserved.",
"www.freetype.org");
"http://www.freetype.org");
// Mesa3D (http://www.mesa3d.org) copyrights
AddCopyrightEntry("Mesa",
"Copyright " B_UTF8_COPYRIGHT " 1999-2006 Brian Paul. "
"Mesa3D project. All rights reserved.",
"www.mesa3d.org");
"http://www.mesa3d.org");
// SGI's GLU implementation copyrights
AddCopyrightEntry("GLU",
@ -619,7 +633,7 @@ AboutView::AboutView(const BRect &rect)
// Bullet copyrights
AddCopyrightEntry("Bullet",
"Copyright " B_UTF8_COPYRIGHT " 2003-2008 Erwin Coumans",
"www.bulletphysics.com");
"http://www.bulletphysics.com");
// atftp copyrights
AddCopyrightEntry("atftp",
@ -673,12 +687,14 @@ AboutView::AboutView(const BRect &rect)
AddCopyrightEntry("Xiph.org Foundation",
"libvorbis, libogg, libtheora, libspeex"
"Copyright " B_UTF8_COPYRIGHT " 1994-2008 Xiph.Org. All rights "
"reserved.", "www.xiph.org");
"reserved.", "http://www.xiph.org");
// The Tcpdump Group
AddCopyrightEntry("The Tcpdump Group",
"tcpdump, libpcap",
"www.tcpdump.org");
"http://www.tcpdump.org");
_AddCopyrightsFromAttribute();
// Build a list of installed applications and show their
// long version info. Well-behaved apps usually give
@ -848,6 +864,14 @@ AboutView::MessageReceived(BMessage *msg)
void
AboutView::AddCopyrightEntry(const char *name, const char *text,
const char *url)
{
AddCopyrightEntry(name, text, NULL, url);
}
void
AboutView::AddCopyrightEntry(const char *name, const char *text,
const Licenses& licenses, const char *url)
{
BFont font(be_bold_font);
//font.SetSize(be_bold_font->Size());
@ -859,14 +883,61 @@ AboutView::AddCopyrightEntry(const char *name, const char *text,
fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
fCreditsView->Insert(text);
fCreditsView->Insert("\n");
if (licenses.CountLicenses() > 0) {
if (licenses.CountLicenses() > 1)
fCreditsView->Insert("Licenses: ");
else
fCreditsView->Insert("License: ");
for (int32 i = 0; i < licenses.CountLicenses(); i++) {
const char* license = licenses.LicenseAt(i);
BString licensePath("/etc/licenses/");
licensePath += license;
if (i > 0)
fCreditsView->Insert(", ");
fCreditsView->InsertHyperText(license,
new OpenFileAction(licensePath));
}
fCreditsView->Insert("\n");
}
if (url) {
fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
fCreditsView->Insert(url);
fCreditsView->InsertHyperText(url, new URLAction(url));
fCreditsView->Insert("\n");
}
fCreditsView->Insert("\n");
}
void
AboutView::AddCopyrightEntry(const BMessage& packageDescription)
{
const char* package;
const char* copyright;
const char* url;
// package and copyright are mandatory
if (packageDescription.FindString("Package", &package) != B_OK
|| packageDescription.FindString("Copyright", &copyright) != B_OK) {
return;
}
// URL is optional
if (packageDescription.FindString("URL", &url) != B_OK)
url = NULL;
BString copyrightLine("Copyright " B_UTF8_COPYRIGHT " ");
copyrightLine += copyright;
AddCopyrightEntry(package, copyrightLine.String(), packageDescription, url);
}
void
AboutView::PickRandomHaiku()
{
@ -918,6 +989,91 @@ AboutView::PickRandomHaiku()
}
void
AboutView::_AddCopyrightsFromAttribute()
{
// open the app executable file
char appPath[B_PATH_NAME_LENGTH];
int appFD;
if (BPrivate::get_app_path(appPath) != B_OK
|| (appFD = open(appPath, O_RDONLY)) < 0) {
return;
}
// open the attribute
int attrFD = fs_open_attr(appFD, "COPYRIGHTS", B_STRING_TYPE, O_RDONLY);
close(appFD);
if (attrFD < 0)
return;
// attach it to a FILE
FILE* attrFile = fdopen(attrFD, "r");
if (attrFile == NULL) {
close(attrFD);
return;
}
CObjectDeleter<FILE, int> _(attrFile, fclose);
// read and parse the copyrights
BMessage package;
BString fieldName;
BString fieldValue;
char lineBuffer[LINE_MAX];
while (char* line = fgets(lineBuffer, sizeof(lineBuffer), attrFile)) {
// chop off line break
size_t lineLen = strlen(line);
if (lineLen > 0 && line[lineLen - 1] == '\n')
line[--lineLen] = '\0';
// flush previous field, if a new field begins, otherwise append
if (lineLen == 0 || !isspace(line[0])) {
// new field -- flush the previous one
if (fieldName.Length() > 0) {
fieldValue = trim_string(fieldValue.String(),
fieldValue.Length());
package.AddString(fieldName.String(), fieldValue);
fieldName = "";
}
} else if (fieldName.Length() > 0) {
// append to current field
fieldValue += line;
continue;
} else {
// bogus line -- ignore
continue;
}
if (lineLen == 0)
continue;
// parse new field
char* colon = strchr(line, ':');
if (colon == NULL) {
// bogus line -- ignore
continue;
}
fieldName.SetTo(line, colon - line);
fieldName = trim_string(line, colon - line);
if (fieldName.Length() == 0) {
// invalid field name
continue;
}
fieldValue = colon + 1;
if (fieldName == "Package") {
// flush the current package
AddCopyrightEntry(package);
package.MakeEmpty();
}
}
// flush current package
AddCopyrightEntry(package);
}
// #pragma mark -

View File

@ -0,0 +1,74 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT license.
*/
#include "HyperTextActions.h"
#include <Entry.h>
#include <Message.h>
#include <Roster.h>
// #pragma mark - URLAction
URLAction::URLAction(const BString& url)
:
fURL(url)
{
}
URLAction::~URLAction()
{
}
void
URLAction::Clicked(HyperTextView* view, BPoint where, BMessage* message)
{
// be lazy and let /bin/open open the URL
entry_ref ref;
if (get_ref_for_path("/bin/open", &ref))
return;
const char* args[] = { "/bin/open", fURL.String(), NULL };
be_roster->Launch(&ref, 2, args);
}
// #pragma mark - OpenFileAction
OpenFileAction::OpenFileAction(const BString& file)
:
fFile(file)
{
}
OpenFileAction::~OpenFileAction()
{
}
void
OpenFileAction::Clicked(HyperTextView* view, BPoint where, BMessage* message)
{
// get the entry ref and let Tracker open the file
entry_ref ref;
if (get_ref_for_path(fFile.String(), &ref) != B_OK
|| !BEntry(&ref).Exists()) {
return;
}
BMessenger tracker("application/x-vnd.Be-TRAK");
if (tracker.IsValid()) {
BMessage message(B_REFS_RECEIVED);
message.AddRef("refs", &ref);
tracker.SendMessage(&message);
} else
be_roster->Launch(&ref);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT license.
*/
#ifndef HYPER_TEXT_ACTIONS_H
#define HYPER_TEXT_ACTIONS_H
#include <String.h>
#include "HyperTextView.h"
class URLAction : public HyperTextAction {
public:
URLAction(const BString& url);
virtual ~URLAction();
virtual void Clicked(HyperTextView* view, BPoint where,
BMessage* message);
private:
BString fURL;
};
class OpenFileAction : public HyperTextAction {
public:
OpenFileAction(const BString& file);
virtual ~OpenFileAction();
virtual void Clicked(HyperTextView* view, BPoint where,
BMessage* message);
private:
BString fFile;
};
#endif // HYPER_TEXT_ACTIONS_H

View File

@ -0,0 +1,162 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT license.
*/
#include "HyperTextView.h"
#include <Message.h>
#include <Region.h>
#include <Window.h>
#include <ObjectList.h>
// #pragma mark - HyperTextAction
HyperTextAction::HyperTextAction()
{
}
HyperTextAction::~HyperTextAction()
{
}
void
HyperTextAction::Clicked(HyperTextView* view, BPoint where, BMessage* message)
{
}
// #pragma mark - HyperTextView
struct HyperTextView::ActionInfo {
ActionInfo(int32 startOffset, int32 endOffset, HyperTextAction* action)
:
startOffset(startOffset),
endOffset(endOffset),
action(action)
{
}
~ActionInfo()
{
delete action;
}
static int Compare(const ActionInfo* a, const ActionInfo* b)
{
return a->startOffset - b->startOffset;
}
static int CompareEqualIfIntersecting(const ActionInfo* a,
const ActionInfo* b)
{
if (a->startOffset < b->endOffset && b->startOffset < a->endOffset)
return 0;
return a->startOffset - b->startOffset;
}
int32 startOffset;
int32 endOffset;
HyperTextAction* action;
};
class HyperTextView::ActionInfoList
: public BObjectList<HyperTextView::ActionInfo> {
public:
ActionInfoList(int32 itemsPerBlock = 20, bool owning = false)
: BObjectList<HyperTextView::ActionInfo>(itemsPerBlock, owning)
{
}
};
HyperTextView::HyperTextView(BRect frame, const char* name, BRect textRect,
uint32 resizeMask, uint32 flags)
:
BTextView(frame, name, textRect, resizeMask, flags),
fActionInfos(new ActionInfoList(100, true))
{
}
HyperTextView::~HyperTextView()
{
delete fActionInfos;
}
void
HyperTextView::MouseDown(BPoint where)
{
// We eat all mouse button events.
}
void
HyperTextView::MouseUp(BPoint where)
{
BMessage* message = Window()->CurrentMessage();
int32 offset = OffsetAt(where);
ActionInfo pointer(offset, offset + 1, NULL);
const ActionInfo* action = fActionInfos->BinarySearch(pointer,
ActionInfo::CompareEqualIfIntersecting);
if (action != NULL) {
// verify that the text region was hit
BRegion textRegion;
GetTextRegion(action->startOffset, action->endOffset, &textRegion);
if (textRegion.Contains(where))
action->action->Clicked(this, where, message);
}
}
void
HyperTextView::AddHyperTextAction(int32 startOffset, int32 endOffset,
HyperTextAction* action)
{
if (action == NULL || startOffset >= endOffset) {
delete action;
return;
}
fActionInfos->BinaryInsert(new ActionInfo(startOffset, endOffset, action),
ActionInfo::Compare);
// TODO: Of course we should check for overlaps...
}
void
HyperTextView::InsertHyperText(const char* inText, HyperTextAction* action,
const text_run_array* inRuns)
{
int32 startOffset = TextLength();
Insert(inText, inRuns);
int32 endOffset = TextLength();
AddHyperTextAction(startOffset, endOffset, action);
}
void
HyperTextView::InsertHyperText(const char* inText, int32 inLength,
HyperTextAction* action, const text_run_array* inRuns)
{
int32 startOffset = TextLength();
Insert(inText, inLength, inRuns);
int32 endOffset = TextLength();
AddHyperTextAction(startOffset, endOffset, action);
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT license.
*/
#ifndef HYPER_TEXT_VIEW_H
#define HYPER_TEXT_VIEW_H
#include <TextView.h>
// TODO: The current implementation works correctly only for insertions at the
// end of the text. It doesn't keep track of any other insertions or deletions.
class HyperTextView;
class HyperTextAction {
public:
HyperTextAction();
virtual ~HyperTextAction();
virtual void Clicked(HyperTextView* view, BPoint where,
BMessage* message);
};
class HyperTextView : public BTextView {
public:
HyperTextView(BRect frame, const char* name,
BRect textRect, uint32 resizeMask,
uint32 flags = B_WILL_DRAW
| B_PULSE_NEEDED);
virtual ~HyperTextView();
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
void AddHyperTextAction(int32 startOffset,
int32 endOffset, HyperTextAction* action);
void InsertHyperText(const char* inText,
HyperTextAction* action,
const text_run_array* inRuns = NULL);
void InsertHyperText(const char* inText,
int32 inLength, HyperTextAction* action,
const text_run_array* inRuns = NULL);
private:
struct ActionInfo;
class ActionInfoList;
ActionInfoList* fActionInfos;
};
#endif // HYPER_TEXT_VIEW_H

View File

@ -1,6 +1,6 @@
SubDir HAIKU_TOP src apps aboutsystem ;
UsePrivateHeaders shared ;
UsePrivateHeaders app shared ;
SetSubDirSupportedPlatformsBeOSCompatible ;
@ -16,6 +16,9 @@ if $(TARGET_PLATFORM) = dano {
Application AboutSystem :
AboutSystem.cpp
HyperTextActions.cpp
HyperTextView.cpp
Utilities.cpp
: be translation
: AboutSystem.rdef
;

View File

@ -0,0 +1,107 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT license.
*/
#include "Utilities.h"
#include <ctype.h>
#include <stdarg.h>
#include <Message.h>
BString
trim_string(const char* string, size_t len)
{
BString trimmed;
size_t i = 0;
bool addSpace = false;
while (i < len) {
// skip space block
while (i < len && isspace(string[i]))
i++;
// write non-spaced (if any)
if (i < len && !isspace(string[i])) {
// pad with a single space, for all but the first block
if (addSpace)
trimmed << ' ';
else
addSpace = true;
// append chars
while (i < len && !isspace(string[i]))
trimmed << string[i++];
}
}
return trimmed;
}
// #pragma mark - Licenses
Licenses::Licenses(const char* license,...)
:
fLicenses(NULL),
fCount(0)
{
if (license == NULL)
return;
va_list list;
// count licenses
va_start(list, license);
fCount = 1;
while (va_arg(list, const char*) != NULL)
fCount++;
va_end(list);
// create array and copy them
fLicenses = new BString[fCount];
fLicenses[0] = license;
va_start(list, license);
for (int32 i = 1; i < fCount; i++)
fLicenses[i] = va_arg(list, const char*);
va_end(list);
}
Licenses::Licenses(const BMessage& licenses)
:
fLicenses(NULL),
fCount(0)
{
type_code type;
int32 count;
if (licenses.GetInfo("License", &type, &count) != B_OK
|| type != B_STRING_TYPE) {
return;
}
fLicenses = new BString[count];
for (int32 i = 0; i < count; i++) {
if (licenses.FindString("License", i, &fLicenses[i]) != B_OK)
return;
fCount++;
}
}
Licenses::~Licenses()
{
delete[] fLicenses;
}
const char*
Licenses::LicenseAt(int32 index) const
{
return (index >= 0 && index < fCount ? fLicenses[index].String() : NULL);
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT license.
*/
#ifndef UTILITIES_H
#define UTILITIES_H
#include <String.h>
class BMessage;
BString trim_string(const char* string, size_t len);
// Removes leading and trailing white space and replaces all sequences
// of white space by a single space.
class Licenses {
public:
Licenses(const char* license,...);
// NULL terminated list
Licenses(const BMessage& licenses);
// all elements of field "License"
~Licenses();
int32 CountLicenses() const { return fCount; }
const char* LicenseAt(int32 index) const;
private:
BString* fLicenses;
int32 fCount;
};
#endif // UTILITIES_H