Added "Label as" Tracker add-on

Similar to the "Mark as" Tracker add-on, but for custom labels
stored as files in ~/config/settings/Mail/labels.

* Sets the MAIL:label file attribute.

* The pop-up menu with all available labels also has an item to
  remove a currently set label from an email, and to open the
  labels folder to "Manage labels…" (add, remove, rename etc.).

* Added File_Mail_label icon to distinguish from the "Mark as"
  add-on icon. It's supposed to be a narrow post-it as label…

"Mark as" Tracker add-on:
* Remove custom statuses from the "Mark as" Tracker add-on. That's
  what the new labels were introduced for.

Change-Id: I912a747156da3538457f622c1e2b163700c0c7eb
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10539
Reviewed-by: nephele nephele <[email protected]>
This commit is contained in:
Humdinger
2026-03-23 16:30:01 +00:00
committed by nephele nephele
parent 043ad4d92d
commit fa0e6b2192
6 changed files with 186 additions and 26 deletions
Binary file not shown.
+1
View File
@@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons tracker ;
SubInclude HAIKU_TOP src add-ons tracker filetype ; SubInclude HAIKU_TOP src add-ons tracker filetype ;
SubInclude HAIKU_TOP src add-ons tracker iconvader ; SubInclude HAIKU_TOP src add-ons tracker iconvader ;
SubInclude HAIKU_TOP src add-ons tracker label_as ;
SubInclude HAIKU_TOP src add-ons tracker opentargetfolder ; SubInclude HAIKU_TOP src add-ons tracker opentargetfolder ;
SubInclude HAIKU_TOP src add-ons tracker mark_as ; SubInclude HAIKU_TOP src add-ons tracker mark_as ;
SubInclude HAIKU_TOP src add-ons tracker openterminal ; SubInclude HAIKU_TOP src add-ons tracker openterminal ;
+16
View File
@@ -0,0 +1,16 @@
SubDir HAIKU_TOP src add-ons tracker label_as ;
UsePrivateHeaders mail ;
AddResources Label\ as… : LabelAs.rdef ;
Addon Label\ as… :
LabelAs.cpp
: be tracker [ TargetLibsupc++ ] localestub
;
DoCatalogs Label\ as… :
x-vnd.Haiku-LabelAs
:
LabelAs.cpp
;
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright 2026 Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <Catalog.h>
#include <Collator.h>
#include <Directory.h>
#include <E-mail.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <MenuItem.h>
#include <Message.h>
#include <Node.h>
#include <ObjectList.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <String.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Label-as add-on"
BCollator collator;
static int
compare_menu_items(const BMenuItem* a, const BMenuItem* b)
{
return collator.Compare(a->Label(), b->Label());
}
static BPoint
mouse_position()
{
BPoint position;
get_mouse(&position, NULL);
return position;
}
void
open_labels_folder()
{
BPath path;
find_directory(B_USER_SETTINGS_DIRECTORY, &path);
path.Append("Mail/labels");
BEntry entry(path.Path());
if (!entry.Exists())
create_directory(path.Path(), 0777);
BEntry folderEntry;
if (folderEntry.SetTo(path.Path()) == B_OK
&& folderEntry.Exists()) {
BMessage openFolderCommand(B_REFS_RECEIVED);
BMessenger tracker("application/x-vnd.Be-TRAK");
entry_ref ref;
folderEntry.GetRef(&ref);
openFolderCommand.AddRef("refs", &ref);
tracker.SendMessage(&openFolderCommand);
}
}
static void
retrieve_label_items(BMenu* menu)
{
menu->AddItem(new BMenuItem(B_TRANSLATE("Remove label"), NULL));
menu->AddSeparatorItem();
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append("Mail/labels");
BDirectory directory(path.Path());
BObjectList<BMenuItem> items;
entry_ref ref;
while (directory.GetNextRef(&ref) == B_OK)
items.AddItem(new BMenuItem(ref.name, NULL));
if (!items.IsEmpty()) {
if (BLocale::Default()->GetCollator(&collator) == B_OK) {
collator.SetNumericSorting(true);
items.SortItems(compare_menu_items);
}
for (int32 i = 0; i < items.CountItems(); i++)
menu->AddItem(items.ItemAt(i));
menu->AddSeparatorItem();
}
menu->AddItem(new BMenuItem(B_TRANSLATE("Manage labels" B_UTF8_ELLIPSIS), NULL));
}
// #pragma mark -
extern "C" void
process_refs(entry_ref dir, BMessage* message, void* /*reserved*/)
{
BPopUpMenu* menu = new BPopUpMenu("labels");
retrieve_label_items(menu);
BMenuItem* item = menu->Go(mouse_position() - BPoint(5, 5), false, true);
if (item == NULL)
return;
BString label = item->Label();
entry_ref ref;
for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
BNode node(&ref);
BString type;
BString previousLabel;
if (node.InitCheck() == B_OK && node.ReadAttrString("BEOS:TYPE", &type) == B_OK
&& (type == B_MAIL_TYPE || type == B_PARTIAL_MAIL_TYPE)) {
if (label == B_TRANSLATE("Remove label"))
node.RemoveAttr(B_MAIL_ATTR_LABEL);
else if (label == B_TRANSLATE("Manage labels" B_UTF8_ELLIPSIS))
open_labels_folder();
else if (node.ReadAttrString(B_MAIL_ATTR_LABEL, &previousLabel) != B_OK
|| previousLabel != label)
node.WriteAttrString(B_MAIL_ATTR_LABEL, &label);
}
}
}
+34
View File
@@ -0,0 +1,34 @@
resource app_signature "application/x-vnd.Haiku-LabelAs";
resource file_types message {
"types" = "text/x-email",
"types" = "text/x-partial-email"
};
resource app_version {
major = 1,
middle = 0,
minor = 0,
/* 0 = development 1 = alpha 2 = beta
3 = gamma 4 = golden master 5 = final */
variety = 0,
internal = 1,
short_info = "LabelAs, Copyright 2026 Haiku, Inc.",
long_info = "A Tracker Add-on that marks emails with the chosen label."
};
resource vector_icon {
$"6E636966060500040167020006023A94CE3C078BBE370C3CD1394B9EF94A1372"
$"00FAF9D2FFDDDB8F03FF0000020006023AC26B3A272ABB621E3C2B0B4A03F64A"
$"005700010101FF807E5D020006033959AFBA52CE3AE8E03A190A49C920491610"
$"2F67CBFFA80594DDFD1BAFFB070A04495D4C5E604A5C480A04363A5C49485D22"
$"4A0A044F464A4B504E55490802384244480802BB11C23D3E4B0802B946C3AF3E"
$"510606AE0B383D4434BFC9BB81C22FBAAD46314D33C41EBBC74D354936404008"
$"0A010100000A0001011001178402040A020101000A030102000A040303040510"
$"01178100040A00010630242201178300040A0501062024220A00001001178200"
$"04"
};
+3 -26
View File
@@ -48,31 +48,6 @@ add_status_item(BMenu* menu, const char* name)
} }
static void
retrieve_status_items(BMenu* menu)
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append("Mail/status");
BDirectory directory(path.Path());
entry_ref ref;
while (directory.GetNextRef(&ref) == B_OK) {
if (!strcmp(ref.name, ".") || !strcmp(ref.name, ".."))
continue;
add_status_item(menu, ref.name);
}
add_status_item(menu, "New");
add_status_item(menu, "Read");
add_status_item(menu, "Replied");
}
// #pragma mark - // #pragma mark -
@@ -80,7 +55,9 @@ extern "C" void
process_refs(entry_ref dir, BMessage* message, void* /*reserved*/) process_refs(entry_ref dir, BMessage* message, void* /*reserved*/)
{ {
BPopUpMenu* menu = new BPopUpMenu("status"); BPopUpMenu* menu = new BPopUpMenu("status");
retrieve_status_items(menu); add_status_item(menu, "New");
add_status_item(menu, "Read");
add_status_item(menu, "Replied");
BMenuItem* item = menu->Go(mouse_position() - BPoint(5, 5), false, true); BMenuItem* item = menu->Go(mouse_position() - BPoint(5, 5), false, true);
if (item == NULL) if (item == NULL)