FREE (&Context);
}
+ if (Labels)
+ hash_destroy(&Labels, NULL);
+
mutt_sleep (0);
/* Set CurrentMenu to MENU_MAIN before executing any folder
(option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
MUTT_READONLY : 0, NULL)) != NULL)
{
+ Labels = hash_create(131, 0);
+ mutt_scan_labels(Context);
menu->current = ci_first_message ();
}
else
<row><entry>^E or <End></entry><entry><literal><eol></literal></entry><entry>move to the end of the line</entry></row>
<row><entry>^F or <Right></entry><entry><literal><forward-char></literal></entry><entry>move forward one char</entry></row>
<row><entry>Esc F</entry><entry><literal><forward-word></literal></entry><entry>move forward one word</entry></row>
-<row><entry><Tab></entry><entry><literal><complete></literal></entry><entry>complete filename or alias</entry></row>
+<row><entry><Tab></entry><entry><literal><complete></literal></entry><entry>complete filename, alias, or label</entry></row>
<row><entry>^T</entry><entry><literal><complete-query></literal></entry><entry>complete address with query</entry></row>
<row><entry>^K</entry><entry><literal><kill-eol></literal></entry><entry>delete to the end of the line</entry></row>
<row><entry>Esc d</entry><entry><literal><kill-eow></literal></entry><entry>delete to the end of the word</entry></row>
You can change or delete the <quote>X-Label:</quote> field within
Mutt using the <quote>edit-label</quote> command, bound to the
<quote>y</quote> key by default. This works for tagged messages, too.
+While in the edit-label function, pressing the <complete>
+binding (TAB, by default) will perform completion against all labels
+currently in use.
</para>
<para>
}
break;
}
+ else if (flags & MUTT_LABEL && ch == OP_EDITOR_COMPLETE)
+ {
+ /* invoke the alias-menu to get more addresses */
+ for (i = state->curpos; i && state->wbuf[i-1] != ',' &&
+ state->wbuf[i-1] != ':'; i--)
+ ;
+ for (; i < state->lastchar && state->wbuf[i] == ' '; i++)
+ ;
+ my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
+ r = mutt_label_complete (buf, buflen, i, state->tabs);
+ replace_part (state, i, buf);
+ if (!r)
+ {
+ rv = 1;
+ goto bye;
+ }
+ break;
+ }
else if (flags & MUTT_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY)
{
/* invoke the query-menu to get more addresses */
WHERE const char *ReleaseDate;
WHERE HASH *Groups;
+WHERE HASH *Labels;
WHERE HASH *ReverseAlias;
WHERE LIST *AutoViewList INITVAL(0);
}
}
+static void label_ref_dec(char *label)
+{
+ uintptr_t count;
+
+ count = (uintptr_t)hash_find(Labels, label);
+ if (count)
+ {
+ hash_delete(Labels, label, NULL, NULL);
+ count--;
+ if (count > 0)
+ hash_insert(Labels, label, (void *)count, 0);
+ }
+}
+
+static void label_ref_inc(char *label)
+{
+ uintptr_t count;
+
+ count = (uintptr_t)hash_find(Labels, label);
+ if (count)
+ hash_delete(Labels, label, NULL, NULL);
+ count++; /* was zero if not found */
+ hash_insert(Labels, label, (void *)count, 0);
+}
+
/*
* add an X-Label: field.
*/
return 0;
if (mutt_strcmp (hdr->env->x_label, new) == 0)
return 0;
+ if (hdr->env->x_label != NULL)
+ label_ref_dec(hdr->env->x_label);
mutt_str_replace (&hdr->env->x_label, new);
+ if (hdr->env->x_label != NULL)
+ label_ref_inc(hdr->env->x_label);
return hdr->changed = hdr->xlabel_changed = 1;
}
strncpy(buf, hdr->env->x_label, LONG_STRING);
}
- if (mutt_get_field("Label: ", buf, sizeof(buf), 0 /* | MUTT_CLEAR */) != 0)
+ if (mutt_get_field("Label: ", buf, sizeof(buf), MUTT_LABEL /* | MUTT_CLEAR */) != 0)
return 0;
new = buf;
return changed;
}
+
+/* scan a context (mailbox) and hash all labels we find */
+void mutt_scan_labels(CONTEXT *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->msgcount; i++)
+ if (ctx->hdrs[i]->env->x_label)
+ label_ref_inc(ctx->hdrs[i]->env->x_label);
+}
+
return NULL;
}
+
+int mutt_label_complete (char *buffer, size_t len, int pos, int numtabs)
+{
+ char *pt = buffer;
+ int spaces; /* keep track of the number of leading spaces on the line */
+
+ SKIPWS (buffer);
+ spaces = buffer - pt;
+
+ pt = buffer + pos - spaces;
+ while ((pt > buffer) && !isspace ((unsigned char) *pt))
+ pt--;
+
+ /* first TAB. Collect all the matches */
+ if (numtabs == 1)
+ {
+ struct hash_elem *entry;
+ struct hash_walk_state state;
+
+ Num_matched = 0;
+ strfcpy (User_typed, pt, sizeof (User_typed));
+ memset (Matches, 0, Matches_listsize);
+ memset (Completed, 0, sizeof (Completed));
+ memset (&state, 0, sizeof(state));
+ while ((entry = hash_walk(Labels, &state)))
+ candidate (Completed, User_typed, entry->key.strkey, sizeof (Completed));
+ matches_ensure_morespace (Num_matched);
+ qsort(Matches, Num_matched, sizeof(char *), (sort_t *) mutt_strcasecmp);
+ Matches[Num_matched++] = User_typed;
+
+ /* All matches are stored. Longest non-ambiguous string is ""
+ * i.e. dont change 'buffer'. Fake successful return this time */
+ if (User_typed[0] == 0)
+ return 1;
+ }
+
+ if (Completed[0] == 0 && User_typed[0])
+ return 0;
+
+ /* Num_matched will _always_ be atleast 1 since the initial
+ * user-typed string is always stored */
+ if (numtabs == 1 && Num_matched == 2)
+ snprintf(Completed, sizeof(Completed), "%s", Matches[0]);
+ else if (numtabs > 1 && Num_matched > 2)
+ /* cycle thru all the matches */
+ snprintf(Completed, sizeof(Completed), "%s",
+ Matches[(numtabs - 2) % Num_matched]);
+
+ /* return the completed label */
+ strncpy (buffer, Completed, len - spaces);
+
+ return 1;
+}
+
if((Context = mx_open_mailbox (folder, ((flags & MUTT_RO) || option (OPTREADONLY)) ? MUTT_READONLY : 0, NULL))
|| !explicit_folder)
{
+ Labels = hash_create (131, 0);
+ mutt_scan_labels(Context);
#ifdef USE_SIDEBAR
mutt_sb_set_open_buffy ();
#endif
mutt_index_menu ();
if (Context)
FREE (&Context);
+ if (Labels)
+ hash_destroy(&Labels, NULL);
}
#ifdef USE_IMAP
imap_logout_all ();
#define MUTT_CLEAR (1<<5) /* clear input if printable character is pressed */
#define MUTT_COMMAND (1<<6) /* do command completion */
#define MUTT_PATTERN (1<<7) /* pattern mode - only used for history classes */
+#define MUTT_LABEL (1<<8) /* do label completion */
/* flags for mutt_get_token() */
#define MUTT_TOKEN_EQUAL 1 /* treat '=' as a special */
void mutt_edit_headers (const char *, const char *, HEADER *, char *, size_t);
int mutt_filter_unprintable (char **);
int mutt_label_message (HEADER *);
+void mutt_scan_labels (CONTEXT *);
+int mutt_label_complete (char *, size_t, int, int);
void mutt_curses_error (const char *, ...);
void mutt_curses_message (const char *, ...);
void mutt_encode_descriptions (BODY *, short);