Mail: work in progress to use the layout API.

This commit is contained in:
Axel Dörfler
2015-09-04 17:32:48 +02:00
parent e047b40a2f
commit df946869a1
18 changed files with 2482 additions and 3766 deletions
+987
View File
@@ -0,0 +1,987 @@
/*
* Copyright 2015, Axel Dörfler, [email protected].
* Copyright 2010 Stephan Aßmus <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include "AddressTextControl.h"
#include <Autolock.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Clipboard.h>
#include <File.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <LayoutUtils.h>
#include <NodeInfo.h>
#include <PopUpMenu.h>
#include <SeparatorView.h>
#include <TextView.h>
#include <Window.h>
#include <stdio.h>
#include <stdlib.h>
#include "MailApp.h"
#include "Messages.h"
#include "QueryList.h"
#include "TextViewCompleter.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "AddressTextControl"
static const uint32 kMsgAddAddress = 'adad';
static const float kHorizontalTextRectInset = 4.0;
static const float kVerticalTextRectInset = 2.0;
class AddressTextControl::TextView : public BTextView {
private:
static const uint32 MSG_CLEAR = 'cler';
public:
TextView(AddressTextControl* parent);
virtual ~TextView();
virtual void MessageReceived(BMessage* message);
virtual void FrameResized(float width, float height);
virtual void KeyDown(const char* bytes, int32 numBytes);
virtual void MakeFocus(bool focused = true);
virtual BSize MinSize();
virtual BSize MaxSize();
const BMessage* ModificationMessage() const;
void SetModificationMessage(BMessage* message);
void SetUpdateAutoCompleterChoices(bool update);
protected:
virtual void InsertText(const char* text, int32 length,
int32 offset,
const text_run_array* runs);
virtual void DeleteText(int32 fromOffset, int32 toOffset);
private:
void _AlignTextRect();
private:
AddressTextControl* fAddressTextControl;
TextViewCompleter* fAutoCompleter;
BString fPreviousText;
bool fUpdateAutoCompleterChoices;
BMessage* fModificationMessage;
};
class AddressPopUpMenu : public BPopUpMenu, public QueryListener {
public:
AddressPopUpMenu();
virtual ~AddressPopUpMenu();
protected:
virtual void EntryCreated(QueryList& source,
const entry_ref& ref, ino_t node);
virtual void EntryRemoved(QueryList& source,
const node_ref& nodeRef);
private:
void _RebuildMenu();
void _AddGroup(const char* label, const char* group,
PersonList& peopleList);
void _AddPeople(BMenu* menu, PersonList& peopleList,
const char* group,
bool addSeparator = false);
bool _MatchesGroup(const Person& person,
const char* group);
};
class AddressTextControl::PopUpButton : public BControl {
public:
PopUpButton();
virtual ~PopUpButton();
virtual BSize MinSize();
virtual BSize PreferredSize();
virtual BSize MaxSize();
virtual void MouseDown(BPoint where);
virtual void Draw(BRect updateRect);
private:
AddressPopUpMenu* fPopUpMenu;
};
class PeopleChoiceModel : public BAutoCompleter::ChoiceModel {
public:
PeopleChoiceModel()
:
fChoices(5, true)
{
}
~PeopleChoiceModel()
{
}
virtual void FetchChoicesFor(const BString& pattern)
{
// Remove all existing choices
fChoices.MakeEmpty();
// Search through the people list for any matches
PersonList& peopleList = static_cast<TMailApp*>(be_app)->People();
BAutolock locker(peopleList);
for (int32 index = 0; index < peopleList.CountPersons(); index++) {
const Person* person = peopleList.PersonAt(index);
const BString& baseText = person->Name();
for (int32 addressIndex = 0;
addressIndex < person->CountAddresses(); addressIndex++) {
BString choiceText = baseText;
choiceText << " <" << person->AddressAt(addressIndex) << ">";
int32 match = choiceText.IFindFirst(pattern);
if (match < 0)
continue;
fChoices.AddItem(new BAutoCompleter::Choice(choiceText,
choiceText, match, pattern.Length()));
}
}
locker.Unlock();
fChoices.SortItems(_CompareChoices);
}
virtual int32 CountChoices() const
{
return fChoices.CountItems();
}
virtual const BAutoCompleter::Choice* ChoiceAt(int32 index) const
{
return fChoices.ItemAt(index);
}
static int _CompareChoices(const BAutoCompleter::Choice* a,
const BAutoCompleter::Choice* b)
{
return a->DisplayText().Compare(b->DisplayText());
}
private:
BObjectList<BAutoCompleter::Choice> fChoices;
};
// #pragma mark - TextView
AddressTextControl::TextView::TextView(AddressTextControl* parent)
:
BTextView("mail"),
fAddressTextControl(parent),
fAutoCompleter(new TextViewCompleter(this,
new PeopleChoiceModel())),
fPreviousText(""),
fUpdateAutoCompleterChoices(true)
{
MakeResizable(true);
SetStylable(true);
fAutoCompleter->SetModificationsReported(true);
}
AddressTextControl::TextView::~TextView()
{
delete fAutoCompleter;
}
void
AddressTextControl::TextView::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_CLEAR:
SetText("");
break;
default:
BTextView::MessageReceived(message);
break;
}
}
void
AddressTextControl::TextView::FrameResized(float width, float height)
{
BTextView::FrameResized(width, height);
_AlignTextRect();
}
void
AddressTextControl::TextView::KeyDown(const char* bytes, int32 numBytes)
{
switch (bytes[0]) {
case B_TAB:
BView::KeyDown(bytes, numBytes);
break;
case B_ESCAPE:
// Revert to text as it was when we received keyboard focus.
SetText(fPreviousText.String());
SelectAll();
break;
case B_RETURN:
// Don't let this through to the text view.
break;
default:
BTextView::KeyDown(bytes, numBytes);
break;
}
}
void
AddressTextControl::TextView::MakeFocus(bool focus)
{
if (focus == IsFocus())
return;
BTextView::MakeFocus(focus);
if (focus) {
fPreviousText = Text();
SelectAll();
}
fAddressTextControl->Invalidate();
}
BSize
AddressTextControl::TextView::MinSize()
{
BSize min;
min.height = ceilf(LineHeight(0) + kVerticalTextRectInset);
// we always add at least one pixel vertical inset top/bottom for
// the text rect.
min.width = min.height * 3;
return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
}
BSize
AddressTextControl::TextView::MaxSize()
{
BSize max(MinSize());
max.width = B_SIZE_UNLIMITED;
return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
}
const BMessage*
AddressTextControl::TextView::ModificationMessage() const
{
return fModificationMessage;
}
void
AddressTextControl::TextView::SetModificationMessage(BMessage* message)
{
fModificationMessage = message;
}
void
AddressTextControl::TextView::SetUpdateAutoCompleterChoices(bool update)
{
fUpdateAutoCompleterChoices = update;
}
void
AddressTextControl::TextView::InsertText(const char* text,
int32 length, int32 offset, const text_run_array* runs)
{
if (!strncmp(text, "mailto:", 7)) {
text += 7;
length -= 7;
if (runs != NULL)
runs = NULL;
}
// Filter all line breaks, note that text is not terminated.
if (length == 1) {
if (*text == '\n' || *text == '\r')
BTextView::InsertText(" ", 1, offset, runs);
else
BTextView::InsertText(text, 1, offset, runs);
} else {
BString filteredText(text, length);
filteredText.ReplaceAll('\n', ' ');
filteredText.ReplaceAll('\r', ' ');
BTextView::InsertText(filteredText.String(), length, offset,
runs);
}
// TODO: change E-mail representation
/*
// Make the base URL part bold.
BString text(Text(), TextLength());
int32 baseUrlStart = text.FindFirst("://");
if (baseUrlStart >= 0)
baseUrlStart += 3;
else
baseUrlStart = 0;
int32 baseUrlEnd = text.FindFirst("/", baseUrlStart);
if (baseUrlEnd < 0)
baseUrlEnd = TextLength();
BFont font;
GetFont(&font);
const rgb_color black = (rgb_color) { 0, 0, 0, 255 };
const rgb_color gray = (rgb_color) { 60, 60, 60, 255 };
if (baseUrlStart > 0)
SetFontAndColor(0, baseUrlStart, &font, B_FONT_ALL, &gray);
if (baseUrlEnd > baseUrlStart) {
font.SetFace(B_BOLD_FACE);
SetFontAndColor(baseUrlStart, baseUrlEnd, &font, B_FONT_ALL, &black);
}
if (baseUrlEnd < TextLength()) {
font.SetFace(B_REGULAR_FACE);
SetFontAndColor(baseUrlEnd, TextLength(), &font, B_FONT_ALL, &gray);
}
*/
fAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
fAddressTextControl->InvokeNotify(fModificationMessage,
B_CONTROL_MODIFIED);
}
void
AddressTextControl::TextView::DeleteText(int32 fromOffset,
int32 toOffset)
{
BTextView::DeleteText(fromOffset, toOffset);
fAutoCompleter->TextModified(fUpdateAutoCompleterChoices);
fAddressTextControl->InvokeNotify(fModificationMessage,
B_CONTROL_MODIFIED);
}
void
AddressTextControl::TextView::_AlignTextRect()
{
// Layout the text rect to be in the middle, normally this means there
// is one pixel spacing on each side.
BRect textRect(Bounds());
textRect.left = 0.0;
float vInset = max_c(1,
floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
float hInset = kHorizontalTextRectInset;
if (be_control_look)
hInset = be_control_look->DefaultLabelSpacing();
textRect.InsetBy(hInset, vInset);
SetTextRect(textRect);
}
// #pragma mark - PopUpButton
AddressTextControl::PopUpButton::PopUpButton()
:
BControl(NULL, NULL, NULL, B_WILL_DRAW)
{
fPopUpMenu = new AddressPopUpMenu();
}
AddressTextControl::PopUpButton::~PopUpButton()
{
delete fPopUpMenu;
}
BSize
AddressTextControl::PopUpButton::MinSize()
{
// TODO: BControlLook does not give us any size information!
return BSize(10, 10);
}
BSize
AddressTextControl::PopUpButton::PreferredSize()
{
return BSize(10, B_SIZE_UNSET);
}
BSize
AddressTextControl::PopUpButton::MaxSize()
{
return BSize(10, B_SIZE_UNLIMITED);
}
void
AddressTextControl::PopUpButton::MouseDown(BPoint where)
{
if (fPopUpMenu->Parent() != NULL)
return;
float width;
fPopUpMenu->GetPreferredSize(&width, NULL);
fPopUpMenu->SetTargetForItems(Parent());
BPoint point(Bounds().Width() - width, Bounds().Height() + 2);
ConvertToScreen(&point);
fPopUpMenu->Go(point, true, true, true);
}
void
AddressTextControl::PopUpButton::Draw(BRect updateRect)
{
uint32 flags = 0;
if (!IsEnabled())
flags |= BControlLook::B_DISABLED;
if (IsFocus() && Window()->IsActive())
flags |= BControlLook::B_FOCUSED;
rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR);
BRect rect = Bounds();
be_control_look->DrawMenuFieldBackground(this, rect,
updateRect, base, true, flags);
}
// #pragma mark - PopUpMenu
AddressPopUpMenu::AddressPopUpMenu()
:
BPopUpMenu("", true)
{
static_cast<TMailApp*>(be_app)->PeopleQueryList().AddListener(this);
}
AddressPopUpMenu::~AddressPopUpMenu()
{
static_cast<TMailApp*>(be_app)->PeopleQueryList().RemoveListener(this);
}
void
AddressPopUpMenu::EntryCreated(QueryList& source,
const entry_ref& ref, ino_t node)
{
_RebuildMenu();
}
void
AddressPopUpMenu::EntryRemoved(QueryList& source,
const node_ref& nodeRef)
{
_RebuildMenu();
}
void
AddressPopUpMenu::_RebuildMenu()
{
// Remove all items
int32 index = CountItems();
while (index-- > 0) {
delete RemoveItem(index);
}
// Rebuild contents
PersonList& peopleList = static_cast<TMailApp*>(be_app)->People();
BAutolock locker(peopleList);
if (peopleList.CountPersons() > 0)
_AddGroup(B_TRANSLATE("All people"), NULL, peopleList);
GroupList& groupList = static_cast<TMailApp*>(be_app)->PeopleGroups();
BAutolock groupLocker(groupList);
for (int32 index = 0; index < groupList.CountGroups(); index++) {
BString group = groupList.GroupAt(index);
_AddGroup(group, group, peopleList);
}
groupLocker.Unlock();
_AddPeople(this, peopleList, "", true);
}
void
AddressPopUpMenu::_AddGroup(const char* label, const char* group,
PersonList& peopleList)
{
BMenu* menu = new BMenu(label);
AddItem(menu);
menu->Superitem()->SetMessage(new BMessage(kMsgAddAddress));
_AddPeople(menu, peopleList, group);
}
void
AddressPopUpMenu::_AddPeople(BMenu* menu, PersonList& peopleList,
const char* group, bool addSeparator)
{
for (int32 index = 0; index < peopleList.CountPersons(); index++) {
const Person* person = peopleList.PersonAt(index);
if (!_MatchesGroup(*person, group))
continue;
if (person->CountAddresses() != 0 && addSeparator) {
menu->AddSeparatorItem();
addSeparator = false;
}
for (int32 addressIndex = 0; addressIndex < person->CountAddresses();
addressIndex++) {
BString email = person->Name();
email << " <" << person->AddressAt(addressIndex) << ">";
BMessage* message = new BMessage(kMsgAddAddress);
message->AddString("email", email);
menu->AddItem(new BMenuItem(email, message));
if (menu->Superitem() != NULL)
menu->Superitem()->Message()->AddString("email", email);
}
}
}
bool
AddressPopUpMenu::_MatchesGroup(const Person& person, const char* group)
{
if (group == NULL)
return true;
if (group[0] == '\0')
return person.CountGroups() == 0;
return person.IsInGroup(group);
}
// TODO: sort lists!
/*
void
AddressTextControl::PopUpMenu::_AddPersonItem(const entry_ref *ref, ino_t node, BString &name,
BString &email, const char *attr, BMenu *groupMenu, BMenuItem *superItem)
{
BString label;
BString sortKey;
// For alphabetical order sorting, usually last name.
// if we have no Name, just use the email address
if (name.Length() == 0) {
label = email;
sortKey = email;
} else {
// otherwise, pretty-format it
label << name << " (" << email << ")";
// Extract the last name (last word in the name),
// removing trailing and leading spaces.
const char *nameStart = name.String();
const char *string = nameStart + strlen(nameStart) - 1;
const char *wordEnd;
while (string >= nameStart && isspace(*string))
string--;
wordEnd = string + 1; // Points to just after last word.
while (string >= nameStart && !isspace(*string))
string--;
string++; // Point to first letter in the word.
if (wordEnd > string)
sortKey.SetTo(string, wordEnd - string);
else // Blank name, pretend that the last name is after it.
string = nameStart + strlen(nameStart);
// Append the first names to the end, so that people with the same last
// name get sorted by first name. Note no space between the end of the
// last name and the start of the first names, but that shouldn't
// matter for sorting.
sortKey.Append(nameStart, string - nameStart);
}
}
*/
// #pragma mark - AddressTextControl
AddressTextControl::AddressTextControl(const char* name, BMessage* message)
:
BControl(name, NULL, message, B_WILL_DRAW),
fRefDropMenu(NULL),
fWindowActive(false),
fEditable(true)
{
fTextView = new TextView(this);
fTextView->SetExplicitMinSize(BSize(100, B_SIZE_UNSET));
fPopUpButton = new PopUpButton();
BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0)
.SetInsets(2)
.Add(fTextView)
.Add(fPopUpButton);
SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
SetLowColor(ViewColor());
SetViewColor(fTextView->ViewColor());
SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
B_ALIGN_VERTICAL_CENTER));
}
AddressTextControl::~AddressTextControl()
{
}
void
AddressTextControl::AttachedToWindow()
{
BControl::AttachedToWindow();
fWindowActive = Window()->IsActive();
}
void
AddressTextControl::WindowActivated(bool active)
{
BControl::WindowActivated(active);
if (fWindowActive != active) {
fWindowActive = active;
Invalidate();
}
}
void
AddressTextControl::Draw(BRect updateRect)
{
BRect bounds(Bounds());
rgb_color base(LowColor());
uint32 flags = 0;
if (!IsEnabled())
flags |= BControlLook::B_DISABLED;
if (fWindowActive && fTextView->IsFocus())
flags |= BControlLook::B_FOCUSED;
be_control_look->DrawTextControlBorder(this, bounds, updateRect, base,
flags);
}
void
AddressTextControl::MakeFocus(bool focus)
{
// Forward this to the text view, we never accept focus ourselves.
fTextView->MakeFocus(focus);
}
void
AddressTextControl::SetEnabled(bool enabled)
{
BControl::SetEnabled(enabled);
fTextView->MakeEditable(enabled && fEditable);
if (enabled)
fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE);
else
fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE);
fPopUpButton->SetEnabled(enabled);
_UpdateTextViewColors(enabled);
}
void
AddressTextControl::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SIMPLE_DATA:
{
int32 buttons = -1;
BPoint point;
if (message->FindInt32("buttons", &buttons) != B_OK)
buttons = B_PRIMARY_MOUSE_BUTTON;
if (buttons != B_PRIMARY_MOUSE_BUTTON
&& message->FindPoint("_drop_point_", &point) != B_OK)
return;
BMessage forwardRefs(B_REFS_RECEIVED);
bool forward = false;
entry_ref ref;
for (int32 index = 0;message->FindRef("refs", index, &ref) == B_OK; index++) {
BFile file(&ref, B_READ_ONLY);
if (file.InitCheck() == B_NO_ERROR) {
BNodeInfo info(&file);
char type[B_FILE_NAME_LENGTH];
info.GetType(type);
if (!strcmp(type,"application/x-person")) {
// add person's E-mail address to the To: field
BString attr = "";
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
if (message->FindString("attr", &attr) < B_OK)
attr = "META:email";
} else {
BNode node(&ref);
node.RewindAttrs();
char buffer[B_ATTR_NAME_LENGTH];
delete fRefDropMenu;
fRefDropMenu = new BPopUpMenu("RecipientMenu");
while (node.GetNextAttrName(buffer) == B_OK) {
if (strstr(buffer, "email") <= 0)
continue;
attr = buffer;
BString address;
node.ReadAttrString(buffer, &address);
if (address.Length() <= 0)
continue;
BMessage* itemMsg
= new BMessage(kMsgAddAddress);
itemMsg->AddString("email", address.String());
BMenuItem* item = new BMenuItem(
address.String(), itemMsg);
fRefDropMenu->AddItem(item);
}
if (fRefDropMenu->CountItems() > 1) {
fRefDropMenu->SetTargetForItems(this);
fRefDropMenu->Go(point, true, true, true);
return;
} else {
delete fRefDropMenu;
fRefDropMenu = NULL;
}
}
BString email;
file.ReadAttrString(attr.String(), &email);
// we got something...
if (email.Length() > 0) {
// see if we can get a username as well
BString name;
file.ReadAttrString("META:name", &name);
BString address;
if (name.Length() == 0) {
// if we have no Name, just use the email address
address = email;
} else {
// otherwise, pretty-format it
address << "\"" << name << "\" <" << email << ">";
}
_AddAddress(address);
}
} else {
forward = true;
forwardRefs.AddRef("refs", &ref);
}
}
}
if (forward) {
// Pass on to parent
Window()->PostMessage(&forwardRefs, Parent());
}
break;
}
case M_SELECT:
{
BTextView *textView = (BTextView *)ChildAt(0);
if (textView != NULL)
textView->Select(0, textView->TextLength());
break;
}
case kMsgAddAddress:
{
const char* email;
for (int32 index = 0;
message->FindString("email", index++, &email) == B_OK;)
_AddAddress(email);
break;
}
default:
BControl::MessageReceived(message);
break;
}
}
const BMessage*
AddressTextControl::ModificationMessage() const
{
return fTextView->ModificationMessage();
}
void
AddressTextControl::SetModificationMessage(BMessage* message)
{
fTextView->SetModificationMessage(message);
}
bool
AddressTextControl::IsEditable() const
{
return fEditable;
}
void
AddressTextControl::SetEditable(bool editable)
{
fTextView->MakeEditable(IsEnabled() && editable);
fEditable = editable;
if (editable && fPopUpButton->IsHidden(this))
fPopUpButton->Show();
else if (!editable && !fPopUpButton->IsHidden(this))
fPopUpButton->Hide();
}
void
AddressTextControl::SetText(const char* text)
{
if (text == NULL || Text() == NULL || strcmp(Text(), text) != 0) {
fTextView->SetUpdateAutoCompleterChoices(false);
fTextView->SetText(text);
fTextView->SetUpdateAutoCompleterChoices(true);
}
}
const char*
AddressTextControl::Text() const
{
return fTextView->Text();
}
int32
AddressTextControl::TextLength() const
{
return fTextView->TextLength();
}
void
AddressTextControl::GetSelection(int32* start, int32* end) const
{
fTextView->GetSelection(start, end);
}
void
AddressTextControl::Select(int32 start, int32 end)
{
fTextView->Select(start, end);
}
void
AddressTextControl::SelectAll()
{
fTextView->Select(0, TextLength());
}
bool
AddressTextControl::HasFocus()
{
return fTextView->IsFocus();
}
void
AddressTextControl::_AddAddress(const char* text)
{
int last = fTextView->TextLength();
if (last != 0) {
fTextView->Select(last, last);
// TODO: test if there is already a ','
fTextView->Insert(", ");
}
fTextView->Insert(text);
}
void
AddressTextControl::_UpdateTextViewColors(bool enabled)
{
BFont font;
fTextView->GetFontAndColor(0, &font);
rgb_color textColor;
if (enabled)
textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
else {
textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DISABLED_LABEL_TINT);
}
fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor);
rgb_color color;
if (enabled)
color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
else {
color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_LIGHTEN_2_TINT);
}
fTextView->SetViewColor(color);
fTextView->SetLowColor(color);
}
+62
View File
@@ -0,0 +1,62 @@
/*
* Copyright 2015, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef ADDRESS_TEXT_CONTROL_H
#define ADDRESS_TEXT_CONTROL_H
#include <Control.h>
class BButton;
class BPopUpMenu;
class BTextView;
class AddressTextControl : public BControl {
public:
AddressTextControl(const char* name,
BMessage* message);
virtual ~AddressTextControl();
virtual void AttachedToWindow();
virtual void WindowActivated(bool active);
virtual void Draw(BRect updateRect);
virtual void MakeFocus(bool focus = true);
virtual void SetEnabled(bool enabled);
virtual void MessageReceived(BMessage* message);
const BMessage* ModificationMessage() const;
void SetModificationMessage(BMessage* message);
bool IsEditable() const;
void SetEditable(bool editable);
void SetText(const char* text);
const char* Text() const;
int32 TextLength() const;
void GetSelection(int32* start, int32* end) const;
void Select(int32 start, int32 end);
void SelectAll();
bool HasFocus();
private:
void _AddAddress(const char* text);
void _UpdateTextViewColors(bool enabled);
private:
class TextView;
class PopUpButton;
TextView* fTextView;
PopUpButton* fPopUpButton;
BPopUpMenu* fRefDropMenu;
bool fWindowActive;
bool fEditable;
};
#endif // ADDRESS_TEXT_CONTROL_H
File diff suppressed because it is too large Load Diff
-233
View File
@@ -1,233 +0,0 @@
/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
//
// ComboBox.h
//
// A view that is the combination of a text control and a pop-up list
//
//
#ifndef _COMBOBOX_H
#define _COMBOBOX_H
#include <Control.h>
#include <View.h>
class BButton;
class BComboBox;
class BList;
class BTextControl;
class BTextView;
class BWindow;
struct text_run_array;
typedef const char* (*text_input_filter_hook)(const char* inText, int32& length,
const text_run_array*& runs);
/*
// Abstract class provides an interface for BComboBox to access possible choices.
// Choices are used for auto-completion and for showing the pop-up list
class BChoiceList {
public:
// Returns the choice at index or NULL if the index is invalid
virtual const char *ChoiceAt(int32 index) = 0;
// Looks for a match at or after startIndex which contains a choice
// that starts with prefix. If a match is found, B_OK is returned,
// matchIndex is set to the list index that should be selected, and completionText
// is set to point at the text that should be appended to the text input.
// If no match is found, a negative value is returned.
virtual status_t GetMatch(const char *prefix, int32 startIndex,
int32 *matchIndex, const char **completionText) = 0;
// Returns the number of choices
virtual int32 CountChoices() = 0;
};
*/
class StringObjectList;
// Implementation of BChoiceList. Keeps copies of each choice added, and frees
// the memory when the choices are removed.
class BDefaultChoiceList // : public BChoiceList
{
public:
BDefaultChoiceList(BComboBox *owner = NULL);
virtual ~BDefaultChoiceList();
virtual const char *ChoiceAt(int32 index);
virtual status_t GetMatch(const char *prefix, int32 startIndex,
int32 *matchIndex, const char **completionText);
virtual int32 CountChoices();
status_t AddChoice(const char *toAdd);
status_t AddChoiceAt(const char *toAdd, int32 index);
status_t RemoveChoice(const char *toRemove);
status_t RemoveChoiceAt(int32 index);
void SetOwner(BComboBox *owner);
BComboBox *Owner();
private:
StringObjectList *fList;
BComboBox *fOwner;
};
typedef BDefaultChoiceList BChoiceList;
class BComboBox : public BControl {
public:
BComboBox(BRect frame, const char *name, const char *label,
BMessage *message, uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP,
uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
// BComboBox(BMessage *data);
virtual ~BComboBox();
// BArchivable methods
// static BArchivable *Instantiate(BMessage *data);
// virtual status_t Archive(BMessage *data, bool deep = true) const;
// SetChoiceList causes the BComboBox to delete the old BChoiceList object,
// take ownership of the new choice list, and then invalidate the pop-up list
void SetChoiceList(BChoiceList *list);
// ChoiceList returns a pointer to the current choice list
BChoiceList *ChoiceList();
// ChoiceListUpdated should be called whenever an item in the choice list changes
// so that BComboBox can perform the proper updating.
virtual void ChoiceListUpdated();
// Select changes the list selection to the specified index, and if the
// changeTextSelection flag is true, changes the text in the TextView to
// the value at index and selects all the text in the TextView.
virtual void Select(int32 index, bool changeTextSelection = false);
virtual void Deselect();
// Returns the index of the current selection, or a negative value
int32 CurrentSelection();
// SetAutoComplete enables or disables auto-completion
virtual void SetAutoComplete(bool on);
bool GetAutoComplete();
// The following methods are mostly identical to their BTextControl counterparts
virtual void SetValue(int32 value);
virtual void SetEnabled(bool enabled);
virtual void SetLabel(const char *text);
virtual void SetText(const char *text);
const char *Text() const;
int32 TextLength() const;
BTextView *TextView();
virtual void SetDivider(float dividing_line);
float Divider() const;
virtual void SetAlignment(alignment label, alignment text);
void GetAlignment(alignment *label, alignment *text) const;
virtual void SetModificationMessage(BMessage *message);
BMessage *ModificationMessage() const;
void SetFilter(text_input_filter_hook hook);
virtual void GetPreferredSize(float *width, float *height);
virtual void ResizeToPreferred();
virtual void FrameMoved(BPoint new_position);
virtual void FrameResized(float new_width, float new_height);
virtual void WindowActivated(bool active);
virtual void MakeFocus(bool state);
virtual void Draw(BRect update);
virtual void MessageReceived(BMessage *msg);
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage *dragMessage);
// virtual void AllAttached();
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void SetFlags(uint32 flags);
// virtual void SetFont(const BFont *font, uint32 properties = B_FONT_ALL);
virtual status_t Invoke(BMessage *msg = NULL);
// virtual BHandler *ResolveSpecifier(BMessage *msg, int32 index,
// BMessage *specifier, int32 form, const char *property);
// virtual status_t GetSupportedSuites(BMessage *data);
//
//
// virtual status_t Perform(perform_code d, void *arg);
private:
class ComboBoxWindow;
class ChoiceListView;
class TextInput;
class MovedMessageFilter;
protected:
ComboBoxWindow *CreatePopupWindow();
void CommitValue();
void TryAutoComplete();
void ShowPopupWindow();
void HidePopupWindow();
BRect fButtonRect;
int32 fSelected;
int32 fCompletionIndex;
float fDivider;
TextInput *fText;
ComboBoxWindow *fPopupWindow;
BMessage *fModificationMessage;
BChoiceList *fChoiceList;
alignment fLabelAlign;
bool fAutoComplete;
bool fButtonDepressed;
bool fDepressedWhenClicked;
bool fTrackingButtonDown;
/*----- Private or reserved -----------------------------------------*/
private:
BRect fFrameCache;
MovedMessageFilter *fWinMovedFilter;
int32 fTextEnd;
bool fSkipSetFlags;
friend class ChoiceListView;
friend class ComboBoxWindow;
friend class TextInput;
};
#endif // #ifndef _COMBOBOX_H
+89 -105
View File
@@ -635,38 +635,106 @@ TextRunArray::~TextRunArray()
}
//====================================================================
// #pragma mark -
TContentView::TContentView(BRect rect, bool incoming, BFont* font,
TContentView::TContentView(bool incoming, BFont* font,
bool showHeader, bool coloredQuotes)
:
BView(rect, "m_content", B_FOLLOW_ALL,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
BView("m_content", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fFocus(false),
fIncoming(incoming)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
fOffset = 12;
BRect r(rect);
r.OffsetTo(0, 0);
r.right -= B_V_SCROLL_BAR_WIDTH;
r.bottom -= B_H_SCROLL_BAR_HEIGHT;
r.top += 4;
BRect text(r);
text.OffsetTo(0, 0);
text.InsetBy(5, 5);
BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0);
// TODO: control look should give us the spacing information
layout->SetInsets(-2, 0, -2, -2);
SetLayout(layout);
fTextView = new TTextView(r, text, fIncoming, this, font, showHeader,
fTextView = new TTextView(fIncoming, this, font, showHeader,
coloredQuotes);
BScrollView *scroll = new BScrollView("", fTextView, B_FOLLOW_ALL, 0, true, true);
BScrollView *scroll = new BScrollView("", fTextView, 0, true, true);
AddChild(scroll);
}
void
TContentView::FindString(const char *str)
{
int32 finish;
int32 pass = 0;
int32 start = 0;
if (str == NULL)
return;
//
// Start from current selection or from the beginning of the pool
//
const char *text = fTextView->Text();
int32 count = fTextView->TextLength();
fTextView->GetSelection(&start, &finish);
if (start != finish)
start = finish;
if (!count || text == NULL)
return;
//
// Do the find
//
while (pass < 2) {
long found = -1;
char lc = tolower(str[0]);
char uc = toupper(str[0]);
for (long i = start; i < count; i++) {
if (text[i] == lc || text[i] == uc) {
const char *s = str;
const char *t = text + i;
while (*s && (tolower(*s) == tolower(*t))) {
s++;
t++;
}
if (*s == 0) {
found = i;
break;
}
}
}
//
// Select the text if it worked
//
if (found != -1) {
Window()->Activate();
fTextView->Select(found, found + strlen(str));
fTextView->ScrollToSelection();
fTextView->MakeFocus(true);
return;
}
else if (start) {
start = 0;
text = fTextView->Text();
count = fTextView->TextLength();
pass++;
} else {
beep();
return;
}
}
}
void
TContentView::Focus(bool focus)
{
if (fFocus != focus) {
fFocus = focus;
Draw(Frame());
}
}
void
TContentView::MessageReceived(BMessage *msg)
{
@@ -769,100 +837,13 @@ TContentView::MessageReceived(BMessage *msg)
}
void
TContentView::FindString(const char *str)
{
int32 finish;
int32 pass = 0;
int32 start = 0;
if (str == NULL)
return;
//
// Start from current selection or from the beginning of the pool
//
const char *text = fTextView->Text();
int32 count = fTextView->TextLength();
fTextView->GetSelection(&start, &finish);
if (start != finish)
start = finish;
if (!count || text == NULL)
return;
//
// Do the find
//
while (pass < 2) {
long found = -1;
char lc = tolower(str[0]);
char uc = toupper(str[0]);
for (long i = start; i < count; i++) {
if (text[i] == lc || text[i] == uc) {
const char *s = str;
const char *t = text + i;
while (*s && (tolower(*s) == tolower(*t))) {
s++;
t++;
}
if (*s == 0) {
found = i;
break;
}
}
}
//
// Select the text if it worked
//
if (found != -1) {
Window()->Activate();
fTextView->Select(found, found + strlen(str));
fTextView->ScrollToSelection();
fTextView->MakeFocus(true);
return;
}
else if (start) {
start = 0;
text = fTextView->Text();
count = fTextView->TextLength();
pass++;
} else {
beep();
return;
}
}
}
void
TContentView::Focus(bool focus)
{
if (fFocus != focus) {
fFocus = focus;
Draw(Frame());
}
}
void
TContentView::FrameResized(float /* width */, float /* height */)
{
BRect r(fTextView->Bounds());
r.OffsetTo(0, 0);
r.InsetBy(5, 5);
fTextView->SetTextRect(r);
}
//====================================================================
// #pragma mark -
TTextView::TTextView(BRect frame, BRect text, bool incoming, TContentView *view,
TTextView::TTextView(bool incoming, TContentView *view,
BFont *font, bool showHeader, bool coloredQuotes)
:
BTextView(frame, "", text, B_FOLLOW_ALL, B_WILL_DRAW | B_NAVIGABLE),
BTextView("", B_WILL_DRAW | B_NAVIGABLE),
fHeader(showHeader),
fColoredQuotes(coloredQuotes),
@@ -883,6 +864,9 @@ TTextView::TTextView(BRect frame, BRect text, bool incoming, TContentView *view,
{
fStopSem = create_sem(1, "reader_sem");
SetStylable(true);
SetInsets(4, 4, 4, 4);
// TODO: have some font size related value here
// (ideally the same as in BTextControl, etc. from BControlLook)
fEnclosures = new BList();
+16 -17
View File
@@ -102,26 +102,25 @@ struct hyper_text {
class TSavePanel;
//====================================================================
class TContentView : public BView {
public:
TContentView(BRect, bool incoming, BFont*,
bool showHeader, bool coloredQuotes);
virtual void MessageReceived(BMessage *);
void FindString(const char *);
void Focus(bool);
void FrameResized(float, float);
public:
TContentView(bool incoming, BFont* font,
bool showHeader, bool coloredQuotes);
TTextView *fTextView;
void FindString(const char *);
void Focus(bool);
private:
bool fFocus;
bool fIncoming;
float fOffset;
TTextView* TextView() const { return fTextView; }
virtual void MessageReceived(BMessage* message);
private:
TTextView* fTextView;
bool fFocus;
bool fIncoming;
float fOffset;
};
//====================================================================
enum {
S_CLEAR_ERRORS = 1,
@@ -148,7 +147,7 @@ struct quote_context {
class TTextView : public BTextView {
public:
TTextView(BRect, BRect, bool incoming,
TTextView(bool incoming,
TContentView*, BFont*, bool showHeader,
bool coloredQuotes);
~TTextView();
@@ -283,7 +282,7 @@ class TSavePanel : public BFilePanel {
TSavePanel(hyper_text*, TTextView*);
virtual void SendMessage(const BMessenger*, BMessage*);
void SetEnclosure(hyper_text*);
private:
hyper_text *fEnclosure;
TTextView *fView;
+364 -1051
View File
File diff suppressed because it is too large Load Diff
+49 -75
View File
@@ -35,103 +35,77 @@ All rights reserved.
#define _HEADER_H
#include "ComboBox.h"
#include <Box.h>
#include <GridView.h>
#include <NodeInfo.h>
#include <Point.h>
#include <Rect.h>
#include <TextControl.h>
#include <View.h>
#include <Window.h>
#include <fs_attr.h>
#include "AddressTextControl.h"
#define TO_FIELD_H 39
#define FROM_FIELD_H 31
#define TO_FIELD_V 7
#define TO_FIELD_WIDTH 270
#define FROM_FIELD_WIDTH 280
#define ACCOUNT_FIELD_WIDTH 165
#define SUBJECT_FIELD_H 18
#define SUBJECT_FIELD_V 33
#define SUBJECT_FIELD_WIDTH 270
#define SUBJECT_FIELD_HEIGHT 16
#define CC_FIELD_H 40
#define CC_FIELD_V 58
#define CC_FIELD_WIDTH 192
#define CC_FIELD_HEIGHT 16
#define BCC_FIELD_H 268
#define BCC_FIELD_V 58
#define BCC_FIELD_WIDTH 197
#define BCC_FIELD_HEIGHT 16
class BEmailMessage;
class BFile;
class BMenuField;
class BMenuItem;
class BPopUpMenu;
class BStringView;
class QPopupMenu;
class TTextControl;
class LabelView;
class THeaderView : public BBox {
class THeaderView : public BGridView {
public:
THeaderView(BRect, BRect, bool incoming,
bool resending, uint32 defaultCharacterSet,
int32 defaultAccount);
THeaderView(bool incoming, bool resending,
int32 defaultAccount);
virtual void MessageReceived(BMessage*);
virtual void AttachedToWindow();
status_t LoadMessage(BEmailMessage*);
const char* From() const;
void SetFrom(const char* from);
BPopUpMenu* fAccountMenu;
BPopUpMenu* fEncodingMenu;
int32 fAccountID;
TTextControl* fAccountTo;
TTextControl* fAccount;
TTextControl* fBcc;
TTextControl* fCc;
TTextControl* fSubject;
TTextControl* fTo;
BStringView* fDateLabel;
BStringView* fDate;
bool fIncoming;
uint32 fCharacterSetUserSees;
AddressTextControl* ToControl() const
{ return fToControl; }
bool IsToEmpty() const;
const char* To() const;
void SetTo(const char* to);
private:
void InitEmailCompletion();
void InitGroupCompletion();
bool IsCcEmpty() const;
const char* Cc() const;
void SetCc(const char* cc);
bool fResending;
QPopupMenu* fBccMenu;
QPopupMenu* fCcMenu;
QPopupMenu* fToMenu;
BDefaultChoiceList fEmailList;
};
bool IsBccEmpty() const;
const char* Bcc() const;
void SetBcc(const char* bcc);
bool IsSubjectEmpty() const;
const char* Subject() const;
void SetSubject(const char* subject);
class TTextControl : public BComboBox {
public:
TTextControl(BRect, const char*, BMessage*, bool,
bool, int32 resizingMode = B_FOLLOW_NONE);
bool IsDateEmpty() const;
const char* Date() const;
void SetDate(const char* date);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage*);
int32 AccountID() const;
const char* AccountName() const;
void SetAccount(int32 id);
void SetAccount(const char* name);
bool HasFocus();
status_t SetFromMessage(BEmailMessage* mail);
virtual void MessageReceived(BMessage*);
virtual void AttachedToWindow();
private:
bool fIncoming;
bool fResending;
char fLabel[100];
BPopUpMenu* fRefDropMenu;
int32 fCommand;
BPopUpMenu* fAccountMenu;
int32 fAccountID;
BTextControl* fAccount;
BTextControl* fFromControl;
LabelView* fToLabel;
AddressTextControl* fToControl;
LabelView* fCcLabel;
AddressTextControl* fCcControl;
AddressTextControl* fBccControl;
BTextControl* fSubjectControl;
BStringView* fDateView;
bool fIncoming;
bool fResending;
};
#endif /* _HEADER_H */
#endif /* _HEADER_H */
+11 -1
View File
@@ -10,10 +10,12 @@ UsePrivateHeaders textencoding ;
UsePrivateHeaders shared ;
UsePrivateHeaders storage ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps webpositive autocompletion ] ;
AddResources Mail : pictures.rdef ;
Application Mail :
ComboBox.cpp
AddressTextControl.cpp
Content.cpp
Enclosures.cpp
FindWindow.cpp
@@ -23,7 +25,9 @@ Application Mail :
MailSupport.cpp
MailWindow.cpp
MessageStatus.cpp
People.cpp
Prefs.cpp
QueryList.cpp
QueryMenu.cpp
Signature.cpp
Status.cpp
@@ -31,6 +35,12 @@ Application Mail :
WIndex.cpp
Words.cpp
KUndoBuffer.cpp
# autocompletion
AutoCompleter.cpp
AutoCompleterDefaultImpl.cpp
TextViewCompleter.cpp
: libshared.a be tracker [ TargetLibstdc++ ] [ TargetLibsupc++ ]
localestub libmail.so libtextencoding.so
: Mail.rdef
+7 -1
View File
@@ -116,7 +116,10 @@ TMailApp::TMailApp()
fStartWithSpellCheckOn(false),
fShowSpamGUI(true),
fMailCharacterSet(B_MAIL_UTF8_CONVERSION),
fContentFont(be_fixed_font)
fContentFont(be_fixed_font),
fPeople(fPeopleQueryList),
fPeopleGroups(fPeopleQueryList)
{
// set default values
fContentFont.SetSize(12.0);
@@ -461,6 +464,9 @@ TMailApp::ReadyToRun()
fs_create_index(volume.Device(), INDEX_STATUS, B_STRING_TYPE, 0);
fs_create_index(volume.Device(), B_MAIL_ATTR_FLAGS, B_INT32_TYPE, 0);
// Start people queries
fPeopleQueryList.Init("META:email=**");
// Load dictionaries
BPath indexDir;
BPath dictionaryDir;
+15
View File
@@ -34,6 +34,7 @@ All rights reserved.
#ifndef _MAIL_APP_H
#define _MAIL_APP_H
#include <Application.h>
#include <Catalog.h>
#include <Entry.h>
@@ -41,6 +42,9 @@ All rights reserved.
#include <List.h>
#include <String.h>
#include "People.h"
#include "QueryList.h"
class BFile;
class BMessenger;
@@ -90,6 +94,13 @@ class TMailApp : public BApplication {
{ return fShowSpamGUI; }
BFont ContentFont();
QueryList& PeopleQueryList()
{ return fPeopleQueryList; }
PersonList& People()
{ return fPeople; }
GroupList& PeopleGroups()
{ return fPeopleGroups; }
private:
void _ClearPrintSettings();
void _CheckForSpamFilterExistence();
@@ -128,6 +139,10 @@ class TMailApp : public BApplication {
int32 fUseAccountFrom;
uint32 fMailCharacterSet;
BFont fContentFont;
QueryList fPeopleQueryList;
PersonList fPeople;
GroupList fPeopleGroups;
};
-1
View File
@@ -166,4 +166,3 @@ add_query_menu_items(BMenu* menu, const char* attribute, uint32 what,
return index;
}
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -124,8 +124,6 @@ protected:
status_t TrainMessageAs(const char* commandWord);
private:
void _UpdateSizeLimits();
status_t _GetQueryPath(BPath* path) const;
void _RebuildQueryMenu(bool firstTime = false);
char* _BuildQueryString(BEntry* entry) const;
@@ -134,6 +132,7 @@ private:
void _UpdateReadButton();
void _SetDownloading(bool downloading);
uint32 _CurrentCharacterSet() const;
static BBitmap* _RetrieveVectorIcon(int32 id);
@@ -169,6 +168,7 @@ private:
BMenu* fQueryMenu;
BMenu* fLeaveStatusMenu;
BMenu* fEncodingMenu;
struct BitmapItem {
BBitmap* bm;
+194
View File
@@ -0,0 +1,194 @@
/*
* Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "People.h"
#include <Autolock.h>
#include <Node.h>
static BString
PersonName(BNode& node)
{
BString fullName;
node.ReadAttrString("META:name", &fullName);
return fullName;
}
static void
AddPersonAddresses(BNode& node, BStringList& addresses)
{
BString email;
if (node.ReadAttrString("META:email", &email) != B_OK || email.IsEmpty())
return;
addresses.Add(email);
// Support for 3rd-party People apps
for (int i = 2; i < 99; i++) {
char attr[32];
snprintf(attr, sizeof(attr), "META:email%d", i);
if (node.ReadAttrString(attr, &email) != B_OK)
break;
addresses.Add(email);
}
}
static void
AddPersonGroups(BNode& node, BStringList& groups)
{
BString groupString;
if (node.ReadAttrString("META:group", &groupString) != B_OK
|| groupString.IsEmpty()) {
return;
}
int first = 0;
while (first < groupString.Length()) {
int end = groupString.FindFirst(',', first);
if (end < 0)
end = groupString.Length();
BString group;
groupString.CopyInto(group, first, end - first);
group.Trim();
groups.Add(group);
first = end + 1;
}
}
// #pragma mark - Person
Person::Person(const entry_ref& ref)
{
BNode node(&ref);
if (node.InitCheck() != B_OK)
return;
fName = PersonName(node);
AddPersonAddresses(node, fAddresses);
AddPersonGroups(node, fGroups);
}
Person::~Person()
{
}
bool
Person::IsInGroup(const char* group) const
{
for (int32 index = 0; index < CountGroups(); index++) {
if (GroupAt(index) == group)
return true;
}
return false;
}
// #pragma mark - PersonList
PersonList::PersonList(QueryList& query)
:
fQueryList(query),
fPersons(10, true)
{
fQueryList.AddListener(this);
}
PersonList::~PersonList()
{
fQueryList.RemoveListener(this);
}
void
PersonList::EntryCreated(QueryList& source, const entry_ref& ref, ino_t node)
{
BAutolock locker(this);
Person* person = new Person(ref);
fPersons.AddItem(person);
fPersonMap.insert(std::make_pair(node_ref(ref.device, node), person));
}
void
PersonList::EntryRemoved(QueryList& source, const node_ref& nodeRef)
{
BAutolock locker(this);
PersonMap::iterator found = fPersonMap.find(nodeRef);
if (found != fPersonMap.end()) {
Person* person = found->second;
fPersons.RemoveItem(person);
fPersonMap.erase(found);
delete person;
}
}
// #pragma mark - GroupList
GroupList::GroupList(QueryList& query)
:
fQueryList(query)
{
fQueryList.AddListener(this);
}
GroupList::~GroupList()
{
fQueryList.RemoveListener(this);
}
void
GroupList::EntryCreated(QueryList& source, const entry_ref& ref, ino_t _node)
{
BNode node(&ref);
if (node.InitCheck() != B_OK)
return;
BAutolock locker(this);
BStringList groups;
AddPersonGroups(node, groups);
for (int32 index = 0; index < groups.CountStrings(); index++) {
BString group = groups.StringAt(index);
StringCountMap::iterator found = fGroupMap.find(group);
if (found != fGroupMap.end())
found->second++;
else {
fGroupMap[group] = 1;
fGroups.Add(group);
}
}
// TODO: sort groups
}
void
GroupList::EntryRemoved(QueryList& source, const node_ref& nodeRef)
{
// TODO!
}
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef PEOPLE_H
#define PEOPLE_H
#include <map>
#include <Locker.h>
#include <ObjectList.h>
#include <String.h>
#include <StringList.h>
#include "QueryList.h"
class Person {
public:
Person(const entry_ref& ref);
virtual ~Person();
const BString& Name() const
{ return fName; }
int32 CountAddresses() const
{ return fAddresses.CountStrings(); }
BString AddressAt(int32 index) const
{ return fAddresses.StringAt(index); }
int32 CountGroups() const
{ return fGroups.CountStrings(); }
BString GroupAt(int32 index) const
{ return fGroups.StringAt(index); }
bool IsInGroup(const char* group) const;
private:
BString fName;
BStringList fAddresses;
BStringList fGroups;
};
class PersonList : public QueryListener, public BLocker {
public:
PersonList(QueryList& query);
~PersonList();
int32 CountPersons() const
{ return fPersons.CountItems(); }
const Person* PersonAt(int32 index) const
{ return fPersons.ItemAt(index); }
virtual void EntryCreated(QueryList& source,
const entry_ref& ref, ino_t node);
virtual void EntryRemoved(QueryList& source,
const node_ref& nodeRef);
private:
typedef std::map<node_ref, Person*> PersonMap;
QueryList& fQueryList;
BObjectList<Person> fPersons;
PersonMap fPersonMap;
};
class GroupList : public QueryListener, public BLocker {
public:
GroupList(QueryList& query);
~GroupList();
int32 CountGroups() const
{ return fGroups.CountStrings(); }
BString GroupAt(int32 index) const
{ return fGroups.StringAt(index); }
virtual void EntryCreated(QueryList& source,
const entry_ref& ref, ino_t node);
virtual void EntryRemoved(QueryList& source,
const node_ref& nodeRef);
private:
typedef std::map<BString, int> StringCountMap;
QueryList& fQueryList;
BStringList fGroups;
StringCountMap fGroupMap;
};
#endif // ADDRESS_TEXT_CONTROL_H
+245
View File
@@ -0,0 +1,245 @@
/*
* Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "QueryList.h"
#include <Autolock.h>
#include <Debug.h>
#include <NodeMonitor.h>
#include <VolumeRoster.h>
static BLooper* sQueryLooper = NULL;
static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
static void
initQueryLooper()
{
sQueryLooper = new BLooper("query looper");
sQueryLooper->Run();
}
// #pragma mark -
QueryList::QueryList()
:
fQuit(false),
fListeners(5, true)
{
}
QueryList::~QueryList()
{
fQuit = true;
ThreadVector::const_iterator threadIterator = fFetchThreads.begin();
for (; threadIterator != fFetchThreads.end(); threadIterator++) {
wait_for_thread(*threadIterator, NULL);
}
QueryVector::iterator queryIterator = fQueries.begin();
for (; queryIterator != fQueries.end(); queryIterator++) {
delete *queryIterator;
}
}
status_t
QueryList::Init(const char* predicate, BVolume* specificVolume)
{
if (sQueryLooper == NULL)
pthread_once(&sInitOnce, &initQueryLooper);
if (Looper() != NULL)
debugger("Init() called twice!");
sQueryLooper->Lock();
sQueryLooper->AddHandler(this);
sQueryLooper->Unlock();
if (specificVolume == NULL) {
BVolumeRoster roster;
BVolume volume;
while (roster.GetNextVolume(&volume) == B_OK) {
if (volume.KnowsQuery() && volume.KnowsAttr() && volume.KnowsMime())
_AddVolume(volume, predicate);
}
} else
_AddVolume(*specificVolume, predicate);
return B_OK;
}
void
QueryList::AddListener(QueryListener* listener)
{
BAutolock locker(this);
// Add all entries that were already retrieved
RefMap::const_iterator iterator = fRefs.begin();
for (; iterator != fRefs.end(); iterator++) {
listener->EntryCreated(*this, iterator->second, iterator->first.node);
}
fListeners.AddItem(listener);
}
void
QueryList::RemoveListener(QueryListener* listener)
{
BAutolock locker(this);
fListeners.RemoveItem(listener, false);
}
void
QueryList::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_QUERY_UPDATE:
{
int32 opcode = message->GetInt32("opcode", -1);
int64 directory = message->GetInt64("directory", -1);
int32 device = message->GetInt32("device", -1);
int64 node = message->GetInt64("node", -1);
if (opcode == B_ENTRY_CREATED) {
const char* name = message->GetString("name");
if (name != NULL) {
entry_ref ref(device, directory, name);
_AddEntry(ref, node);
}
} else if (opcode == B_ENTRY_REMOVED) {
node_ref nodeRef(device, node);
_RemoveEntry(nodeRef);
}
break;
}
default:
BHandler::MessageReceived(message);
break;
}
}
void
QueryList::_AddEntry(const entry_ref& ref, ino_t node)
{
BAutolock locker(this);
// TODO: catch bad_alloc
fRefs.insert(std::make_pair(node_ref(ref.device, node), ref));
_NotifyEntryCreated(ref, node);
}
void
QueryList::_RemoveEntry(const node_ref& nodeRef)
{
BAutolock locker(this);
RefMap::iterator found = fRefs.find(nodeRef);
if (found != fRefs.end())
_NotifyEntryRemoved(nodeRef);
}
void
QueryList::_NotifyEntryCreated(const entry_ref& ref, ino_t node)
{
ASSERT(IsLocked());
int32 count = fListeners.CountItems();
for (int32 index = 0; index < count; index++) {
fListeners.ItemAt(index)->EntryCreated(*this, ref, node);
}
}
void
QueryList::_NotifyEntryRemoved(const node_ref& nodeRef)
{
ASSERT(IsLocked());
int32 count = fListeners.CountItems();
for (int32 index = 0; index < count; index++) {
fListeners.ItemAt(index)->EntryRemoved(*this, nodeRef);
}
}
void
QueryList::_AddVolume(BVolume& volume, const char* predicate)
{
BQuery* query = new BQuery();
if (query->SetVolume(&volume) != B_OK
|| query->SetPredicate(predicate) != B_OK
|| query->SetTarget(this) != B_OK) {
delete query;
}
// TODO: catch bad_alloc
fQueries.push_back(query);
Lock();
fQueryQueue.push_back(query);
Unlock();
thread_id thread = spawn_thread(_FetchQuery, "query fetcher",
B_NORMAL_PRIORITY, this);
if (thread >= B_OK) {
resume_thread(thread);
fFetchThreads.push_back(thread);
}
}
/*static*/ status_t
QueryList::_FetchQuery(void* self)
{
return static_cast<QueryList*>(self)->_FetchQuery();
}
status_t
QueryList::_FetchQuery()
{
RefMap map;
BAutolock locker(this);
BQuery* query = fQueryQueue.back();
fQueryQueue.pop_back();
locker.Unlock();
query->Fetch();
entry_ref ref;
while (!fQuit && query->GetNextRef(&ref) == B_OK) {
BEntry entry(&ref);
node_ref nodeRef;
if (entry.GetNodeRef(&nodeRef) == B_OK)
map.insert(std::make_pair(nodeRef, ref));
}
if (fQuit)
return B_INTERRUPTED;
locker.Lock();
RefMap::const_iterator iterator = map.begin();
for (; iterator != map.end(); iterator++) {
_AddEntry(iterator->second, iterator->first.node);
}
return B_OK;
}
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef QUERY_LIST_H
#define QUERY_LIST_H
#include <map>
#include <vector>
#include <Entry.h>
#include <Handler.h>
#include <Locker.h>
#include <ObjectList.h>
#include <Query.h>
struct QueryList;
class QueryListener {
public:
virtual void EntryCreated(QueryList& source,
const entry_ref& ref, ino_t node) = 0;
virtual void EntryRemoved(QueryList& source,
const node_ref& nodeRef) = 0;
};
typedef std::map<node_ref, entry_ref> RefMap;
class QueryList : public BHandler, public BLocker {
public:
QueryList();
virtual ~QueryList();
status_t Init(const char* predicate,
BVolume* volume = NULL);
void AddListener(QueryListener* listener);
void RemoveListener(QueryListener* listener);
const RefMap& Entries() const
{ return fRefs; }
virtual void MessageReceived(BMessage* message);
private:
void _AddEntry(const entry_ref& ref, ino_t node);
void _RemoveEntry(const node_ref& nodeRef);
void _NotifyEntryCreated(const entry_ref& ref,
ino_t node);
void _NotifyEntryRemoved(const node_ref& nodeRef);
void _AddVolume(BVolume& volume,
const char* predicate);
static status_t _FetchQuery(void* self);
status_t _FetchQuery();
private:
typedef std::vector<thread_id> ThreadVector;
typedef std::vector<BQuery*> QueryVector;
bool fQuit;
RefMap fRefs;
QueryVector fQueries;
QueryVector fQueryQueue;
ThreadVector fFetchThreads;
BObjectList<QueryListener> fListeners;
};
#endif // QUERY_LIST_H