branch: elpa/jabber
commit 00ab3d984be7e54216e0388beabeaa9198f3a87c
Author: Thanos Apollo <[email protected]>
Commit: Thanos Apollo <[email protected]>
roster: Separate command menus
---
lisp/{jabber-roster.el => jabber-roster-menu.el} | 266 ++-----------
lisp/jabber-roster.el | 477 -----------------------
lisp/jabber.el | 1 +
3 files changed, 30 insertions(+), 714 deletions(-)
diff --git a/lisp/jabber-roster.el b/lisp/jabber-roster-menu.el
similarity index 68%
copy from lisp/jabber-roster.el
copy to lisp/jabber-roster-menu.el
index e586e1feb2..2c0a219041 100644
--- a/lisp/jabber-roster.el
+++ b/lisp/jabber-roster-menu.el
@@ -1,8 +1,5 @@
-;;; jabber-roster.el --- roster management -*- coding: utf-8;
lexical-binding: t; -*-
+;;; jabber-roster-menu.el --- Roster commands and popup menus -*-
lexical-binding: t; -*-
-;; Copyright (C) 2009 - Kirill A. Korinskiy - [email protected]
-;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - [email protected]
-;; Copyright (C) 2002, 2003, 2004 - tom berger - [email protected]
;; Copyright (C) 2026 Thanos Apollo
;; Maintainer: Thanos Apollo <[email protected]>
@@ -11,129 +8,47 @@
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
;;; Commentary:
-;;
+
+;; Interactive roster commands and their cross-feature popup menus.
;;; Code:
(require 'cl-lib)
-(require 'jabber-util)
-(require 'jabber-alert)
-(require 'jabber-private)
-(require 'jabber-presence)
-(require 'jabber-carbons)
-(require 'jabber-muc-state)
(require 'keymap-popup)
+(require 'jabber-activity)
+(require 'jabber-blocking)
+(require 'jabber-bookmarks)
+(require 'jabber-browse)
+(require 'jabber-chat)
+(require 'jabber-core)
+(require 'jabber-disco)
+(require 'jabber-info)
+(require 'jabber-muc)
+(require 'jabber-presence)
+(require 'jabber-roster)
+(require 'jabber-version)
-(defgroup jabber-roster nil "Roster options."
- :group 'jabber)
-
-(defcustom jabber-remove-newlines t
- "Remove newlines in status messages?
-Newlines in status messages mess up the roster display. However,
-they are essential to status message poets. Therefore, you get to
-choose the behaviour.
-
-Trailing newlines are always removed, regardless of this variable."
- :type 'boolean)
-
-(defcustom jabber-roster-default-group-name "Ungrouped"
- "Default group name for buddies without groups."
- :type 'string
- :get (lambda (var)
- (let ((val (symbol-value var)))
- (when (stringp val)
- (set-text-properties 0 (length val) nil val))
- val))
- :set (lambda (var val)
- (when (stringp val)
- (set-text-properties 0 (length val) nil val))
- (custom-set-default var val)))
-
-;;; Faces
-
-(defface jabber-roster-user-online
- '((t :inherit success :weight bold))
- "Face for displaying online users.")
-
-(defface jabber-roster-user-xa
- '((t :inherit shadow :slant italic))
- "Face for displaying extended away users.")
-
-(defface jabber-roster-user-dnd
- '((t :inherit error :weight bold))
- "Face for displaying do not disturb users.")
-
-(defface jabber-roster-user-away
- '((t :inherit warning :slant italic))
- "Face for displaying away users.")
-
-(defface jabber-roster-user-chatty
- '((t :inherit success :weight bold :slant italic))
- "Face for displaying chatty users.")
-
-(defface jabber-roster-user-error
- '((t :inherit error :slant italic))
- "Face for displaying users sending presence errors.")
-
-(defface jabber-roster-user-offline
- '((t :inherit shadow :slant italic))
- "Face for displaying offline users.")
-
-(defface jabber-roster-groupchat
- '((t :inherit font-lock-type-face))
- "Face for groupchat room names in the roster buffer.")
-
-(defface jabber-roster-groupchat-nick
- '((t :inherit shadow))
- "Face for the user's nickname in groupchat roster entries.")
-
-(defface jabber-roster-unread
- '((t :inherit font-lock-warning-face :weight bold))
- "Face for roster entries with unread messages.")
-
-;;; Declarations
-
-(declare-function jabber-get-info "jabber-info.el" (jc to))
-(declare-function jabber-blocking-block-jid "jabber-blocking.el" (jc jid))
-(declare-function jabber-activity-switch-to "jabber-activity.el" (&optional
jid-param))
-(declare-function jabber-chat-with "jabber-chat.el"
- (jc jid &optional other-window))
-(declare-function jabber-edit-bookmarks "jabber-bookmarks.el" (jc))
-(declare-function jabber-get-browse "jabber-browse.el" (jc to))
-(declare-function jabber-get-conference-data "jabber-bookmarks.el"
- (jc conference-jid cont &optional key))
-(declare-function jabber-get-version "jabber-version.el" (jc to))
-(declare-function jabber-muc-join "jabber-muc.el"
- (jc group nickname &optional popup))
-(declare-function jabber-muc-switch-to "jabber-muc.el" (group))
(declare-function jabber-omemo-show-fingerprints "jabber-omemo-trust.el" (jc))
-(defvar jabber-current-show) ; jabber.el
-(defvar jabber-presence-strings) ; jabber.el
-(defvar jabber-activity-jids) ; jabber-activity.el
-(defvar jabber-muc--rooms) ; jabber-muc.el
-
-(defvar jabber-roster-debug nil
- "Debug roster operations.")
+(defvar jabber-current-show) ; jabber.el
+(defvar jabber-presence-strings) ; jabber.el
+(defvar jabber-activity-jids) ; jabber-activity.el
+(defvar jabber-muc--rooms) ; jabber-muc.el
+(defvar jabber-roster-account-action-map)
+(defvar jabber-roster-contact-action-map)
+(defvar jabber-roster-discovery-map)
+(defvar jabber-roster-popup-map)
+(defvar jabber-roster-presence-map)
(defvar jabber-roster--scoped-connection nil
"When non-nil, a connection object to scope roster views to.
Only contacts and rooms belonging to this connection are shown.")
-;;; Popup interface
+;;; Roster commands
(defun jabber-roster--contacts ()
"Return roster contacts, filtered by scope if active."
@@ -580,129 +495,6 @@ entry is offered to clear the scope."
(let ((jc (jabber-roster--jc-for-jid jabber-roster--selected-jid)))
(jabber-blocking-block-jid jc jabber-roster--selected-jid))))
-;;; Roster data management
-
-(defun jabber-roster-prepare-roster (jc)
- "Make a hash based roster.
-JC is the Jabber connection."
- (let* ((state-data (fsm-get-state-data jc))
- (hash (make-hash-table :test 'equal))
- (buddies (plist-get state-data :roster))
- (all-groups '()))
- (dolist (buddy buddies)
- (let ((groups (or (get buddy 'groups)
- (list jabber-roster-default-group-name))))
- (dolist (group groups)
- (push group all-groups)
- (puthash group
- (cons buddy (gethash group hash))
- hash))))
- (maphash (lambda (key val) (puthash key (nreverse val) hash)) hash)
- (setq all-groups (sort
- (cl-remove-duplicates all-groups
- :test #'string=)
- #'string<))
- (plist-put state-data :roster-groups
- (mapcar #'list all-groups))
- (plist-put state-data :roster-hash
- hash)))
-
-(defun jabber-fix-status (status)
- "Make STATUS strings more readable."
- (when status
- (when (string-match "\n+$" status)
- (setq status (replace-match "" t t status)))
- (when jabber-remove-newlines
- (while (string-match "\n" status)
- (setq status (replace-match " " t t status))))
- status))
+(provide 'jabber-roster-menu)
-;;;###autoload
-(defun jabber-roster-update (jc new-items changed-items deleted-items)
- "Update roster in memory.
-Add NEW-ITEMS, update CHANGED-ITEMS and remove DELETED-ITEMS, all
-three being lists of JID symbols.
-JC is the Jabber connection."
- (let* ((roster (plist-get (fsm-get-state-data jc) :roster))
- (hash (plist-get (fsm-get-state-data jc) :roster-hash))
- (all-groups (plist-get (fsm-get-state-data jc) :roster-groups)))
-
- (dolist (delete-this deleted-items)
- (setq roster (delq delete-this roster)))
- (setq roster (append new-items roster))
- (plist-put (fsm-get-state-data jc) :roster roster)
-
- (if (not hash)
- (jabber-roster-prepare-roster jc)
-
- (when jabber-roster-debug
- (message "update hash-based roster"))
-
- (dolist (delete-this (append deleted-items changed-items))
- (when jabber-roster-debug
- (message "delete jid: %s" (symbol-name delete-this)))
- (dolist (group (mapcar #'car all-groups))
- (puthash group
- (delq delete-this (gethash group hash))
- hash)))
-
- (dolist (insert-this (append changed-items new-items))
- (when jabber-roster-debug
- (message "insert jid: %s" (symbol-name insert-this)))
- (dolist (group (or (get insert-this 'groups)
- (list jabber-roster-default-group-name)))
- (puthash group
- (cons insert-this (gethash group hash))
- hash)
- (push (list group) all-groups)))
-
- (setq all-groups (sort
- (cl-remove-duplicates all-groups
- :key #'car :test #'string=)
- (lambda (a b) (string< (car a) (car b)))))
-
- (plist-put (fsm-get-state-data jc) :roster-groups all-groups))))
-
-;;; Private storage (group rolling state)
-
-(defun jabber-roster-restore-groups (jc)
- "Restore roster's groups rolling state from private storage.
-JC is the Jabber connection."
- (interactive (list (jabber-read-account)))
- (jabber-private-get jc 'roster "emacs-jabber"
- 'jabber-roster-restore-groups-1 'ignore))
-
-(defun jabber-roster-restore-groups-1 (jc xml-data)
- "Parse roster groups and restore rolling state.
-
-JC is the Jabber connection.
-XML-DATA is the parsed tree data from the stream (stanzas)
-obtained from `xml-parse-region'."
- (when (string= (jabber-xml-get-xmlns xml-data) "emacs-jabber")
- (let* ((data (car (last xml-data)))
- (groups (if (stringp data) (split-string data "\n") nil)))
- (dolist (group groups)
- (let* ((state-data (fsm-get-state-data jc))
- (roll-groups (plist-get state-data :roster-roll-groups)))
- (unless (cl-find group roll-groups :test #'string=)
- (plist-put state-data :roster-roll-groups
- (cons group roll-groups))))))))
-
-(defun jabber-roster-save-groups ()
- "Save roster's groups rolling state in private storage."
- (interactive)
- (dolist (jc jabber-connections)
- (let* ((groups (plist-get (fsm-get-state-data jc) :roster-roll-groups))
- (roll-groups
- (if groups
- (mapconcat (lambda (a) (substring-no-properties a)) groups
"\n")
- "")))
- (jabber-private-set jc
- `(roster ((xmlns . "emacs-jabber"))
- ,roll-groups)
- 'jabber-report-success "Roster groups saved"
- 'jabber-report-success "Failed to save roster
groups"))))
-
-(provide 'jabber-roster)
-
-;;; jabber-roster.el ends here
+;;; jabber-roster-menu.el ends here
diff --git a/lisp/jabber-roster.el b/lisp/jabber-roster.el
index e586e1feb2..92c29c8962 100644
--- a/lisp/jabber-roster.el
+++ b/lisp/jabber-roster.el
@@ -30,12 +30,7 @@
(require 'cl-lib)
(require 'jabber-util)
-(require 'jabber-alert)
(require 'jabber-private)
-(require 'jabber-presence)
-(require 'jabber-carbons)
-(require 'jabber-muc-state)
-(require 'keymap-popup)
(defgroup jabber-roster nil "Roster options."
:group 'jabber)
@@ -104,481 +99,9 @@ Trailing newlines are always removed, regardless of this
variable."
'((t :inherit font-lock-warning-face :weight bold))
"Face for roster entries with unread messages.")
-;;; Declarations
-
-(declare-function jabber-get-info "jabber-info.el" (jc to))
-(declare-function jabber-blocking-block-jid "jabber-blocking.el" (jc jid))
-(declare-function jabber-activity-switch-to "jabber-activity.el" (&optional
jid-param))
-(declare-function jabber-chat-with "jabber-chat.el"
- (jc jid &optional other-window))
-(declare-function jabber-edit-bookmarks "jabber-bookmarks.el" (jc))
-(declare-function jabber-get-browse "jabber-browse.el" (jc to))
-(declare-function jabber-get-conference-data "jabber-bookmarks.el"
- (jc conference-jid cont &optional key))
-(declare-function jabber-get-version "jabber-version.el" (jc to))
-(declare-function jabber-muc-join "jabber-muc.el"
- (jc group nickname &optional popup))
-(declare-function jabber-muc-switch-to "jabber-muc.el" (group))
-(declare-function jabber-omemo-show-fingerprints "jabber-omemo-trust.el" (jc))
-
-(defvar jabber-current-show) ; jabber.el
-(defvar jabber-presence-strings) ; jabber.el
-(defvar jabber-activity-jids) ; jabber-activity.el
-(defvar jabber-muc--rooms) ; jabber-muc.el
-
(defvar jabber-roster-debug nil
"Debug roster operations.")
-(defvar jabber-roster--scoped-connection nil
- "When non-nil, a connection object to scope roster views to.
-Only contacts and rooms belonging to this connection are shown.")
-
-;;; Popup interface
-
-(defun jabber-roster--contacts ()
- "Return roster contacts, filtered by scope if active."
- (if jabber-roster--scoped-connection
- (plist-get (fsm-get-state-data jabber-roster--scoped-connection)
- :roster)
- (jabber-concat-rosters)))
-
-(defun jabber-roster--online-count ()
- "Return count of contacts with at least one connected resource."
- (cl-count-if (lambda (buddy) (get buddy 'connected))
- (jabber-roster--contacts)))
-
-(defun jabber-roster--total-count ()
- "Return total number of contacts across all accounts."
- (length (jabber-roster--contacts)))
-
-(defun jabber-roster--unread-count ()
- "Return number of JIDs with unread activity."
- (length (bound-and-true-p jabber-activity-jids)))
-
-(defun jabber-roster--muc-count ()
- "Return number of currently joined MUC rooms."
- (if jabber-roster--scoped-connection
- (let ((count 0))
- (maphash (lambda (_room entries)
- (when (assq jabber-roster--scoped-connection entries)
- (cl-incf count)))
- jabber-muc--rooms)
- count)
- (hash-table-count jabber-muc--rooms)))
-
-(keymap-popup-define jabber-roster-presence-map
- "Set presence."
- :description (lambda ()
- (format "Presence (current: %s)"
- (propertize
- (or (cdr (assoc jabber-current-show
- jabber-presence-strings))
- "Offline")
- 'face 'keymap-popup-value)))
- "o" ("Online" jabber-send-default-presence)
- "a" ("Away" jabber-send-away-presence)
- "x" ("Extended away" jabber-send-xa-presence)
- "p" ("Custom" jabber-send-presence))
-
-(keymap-popup-define jabber-roster-discovery-map
- "Discovery commands."
- "i" ("Disco items" jabber-get-disco-items)
- "I" ("Disco info" jabber-get-disco-info)
- "b" ("Browse" jabber-get-browse)
- "v" ("Client version" jabber-get-version))
-
-(defun jabber-roster--presence-menu ()
- "Show roster presence menu."
- (interactive)
- (keymap-popup jabber-roster-presence-map))
-
-(defun jabber-roster--discovery-menu ()
- "Show roster discovery menu."
- (interactive)
- (keymap-popup jabber-roster-discovery-map))
-
-(defvar jabber-roster--selected-jid nil
- "JID selected by `completing-read', used by action submenu.")
-
-(keymap-popup-define jabber-roster-contact-action-map
- "Action for selected contact."
- :description (lambda ()
- (format "Contact: %s"
- (propertize (or jabber-roster--selected-jid "?")
- 'face 'font-lock-constant-face)))
- "c" ("Chat" jabber-roster--action-chat)
- "i" ("Info" jabber-roster--action-info)
- "e" ("Edit" jabber-roster--action-edit)
- "d" ("Delete" jabber-roster--action-delete)
- "b" ("Block" jabber-roster--action-block))
-
-(keymap-popup-define jabber-roster-popup-map
- "Jabber roster."
- :description (lambda ()
- (if jabber-connections
- (format "Jabber: %s"
- (propertize
- (if jabber-roster--scoped-connection
- (jabber-connection-bare-jid
- jabber-roster--scoped-connection)
- (string-join
- (mapcar #'jabber-connection-bare-jid
- jabber-connections)
- ", "))
- 'face 'font-lock-constant-face))
- "Jabber (not connected)"))
- :group "Contacts"
- "o" ((lambda ()
- (format "Online %s"
- (propertize (number-to-string (jabber-roster--online-count))
- 'face 'success)))
- jabber-roster-chat-online
- :if (lambda () jabber-connections))
- "c" ((lambda ()
- (format "All contacts %s"
- (propertize (number-to-string (jabber-roster--total-count))
- 'face 'keymap-popup-value)))
- jabber-roster-chat-any
- :if (lambda () jabber-connections))
- "u" ((lambda ()
- (format "Unread %s"
- (propertize (number-to-string (jabber-roster--unread-count))
- 'face 'warning)))
- jabber-roster-chat-unread
- :if (lambda () (bound-and-true-p jabber-activity-jids)))
- :group "MUC"
- "m" ((lambda ()
- (format "Joined rooms %s"
- (propertize (number-to-string (jabber-roster--muc-count))
- 'face 'keymap-popup-value)))
- jabber-roster-switch-muc
- :if (lambda () (> (jabber-roster--muc-count) 0))
- :c-u "match by room name")
- "j" ("Join room" jabber-muc-join
- :if (lambda () jabber-connections))
- "B" ("Bookmarks" jabber-edit-bookmarks
- :if (lambda () jabber-connections))
- :group "Roster"
- "a" ("Add contact" jabber-roster-change
- :if (lambda () jabber-connections))
- "s" ("Subscribe" jabber-send-subscription-request
- :if (lambda () jabber-connections))
- :row
- :group "Presence"
- "p" ("Presence" jabber-roster--presence-menu
- :if (lambda () jabber-connections))
- :group "Discovery"
- "d" ("Discovery" jabber-roster--discovery-menu
- :if (lambda () jabber-connections))
- :group "Connection"
- "C" ("Connect" jabber-connect-all
- :if (lambda () (null jabber-connections)))
- "D" ("Disconnect all" jabber-disconnect
- :if (lambda () jabber-connections))
- "A" ((lambda ()
- (format "Accounts %s"
- (propertize
- (format "[%s]"
- (if jabber-roster--scoped-connection
- (jabber-connection-bare-jid
- jabber-roster--scoped-connection)
- "all"))
- 'face 'font-lock-constant-face)))
- jabber-roster-accounts
- :if (lambda () (cdr jabber-connections)))
- :group "OMEMO"
- "f" ("Fingerprints" jabber-omemo-show-fingerprints
- :if (lambda () jabber-connections)))
-
-;;;###autoload
-(defun jabber-roster-popup ()
- "Show the Jabber roster popup menu."
- (interactive)
- (keymap-popup jabber-roster-popup-map))
-
-;;;###autoload
-(defalias 'jabber-roster #'jabber-roster-popup)
-
-(defun jabber-roster-chat-online ()
- "Select an online contact and open chat."
- (interactive)
- (let* ((online (cl-remove-if-not
- (lambda (buddy) (get buddy 'connected))
- (jabber-roster--contacts)))
- (jid (jabber-read-jid-completing "Chat with (online): "
- online t)))
- (when jid
- (let ((jc (jabber-roster--jc-for-jid jid)))
- (jabber-chat-with jc jid)))))
-
-(defun jabber-roster-chat-any ()
- "Select any contact and show action submenu."
- (interactive)
- (let ((jid (jabber-read-jid-completing "Contact: " nil t)))
- (when jid
- (setq jabber-roster--selected-jid jid)
- (keymap-popup jabber-roster-contact-action-map))))
-
-(defun jabber-roster-chat-unread ()
- "Select a JID with unread activity and switch to its buffer."
- (interactive)
- (if (null jabber-activity-jids)
- (message "No unread messages")
- (let ((jid (completing-read "Unread: " jabber-activity-jids nil t)))
- (when (and jid (not (string-empty-p jid)))
- (jabber-activity-switch-to jid)))))
-
-(defun jabber-roster--muc-room-name (room)
- "Return cached bookmark name for ROOM, or nil if absent."
- (when-let* ((jc (jabber-muc-connection room))
- (name (jabber-get-conference-data jc room nil :name)))
- (unless (or (string-empty-p name)
- (string= name room))
- name)))
-
-(defun jabber-roster--muc-completion-entries ()
- "Return active MUC completion entries as (ROOM . NAME)."
- (mapcar (lambda (room)
- (cons room (jabber-roster--muc-room-name room)))
- (jabber-muc-active-rooms)))
-
-(defun jabber-roster--muc-name-counts (entries)
- "Return hash table of cached-name counts from MUC ENTRIES."
- (let ((counts (make-hash-table :test 'equal)))
- (dolist (entry entries counts)
- (when-let* ((name (cdr entry)))
- (puthash name (1+ (gethash name counts 0)) counts)))))
-
-(defun jabber-roster--muc-room-jids (entries)
- "Return hash table of active room JIDs from MUC ENTRIES."
- (let ((rooms (make-hash-table :test 'equal)))
- (dolist (entry entries rooms)
- (puthash (car entry) t rooms))))
-
-(defun jabber-roster--muc-safe-name-candidate-p (name name-counts room-jids)
- "Return non-nil when NAME is safe as a completion candidate.
-NAME-COUNTS records cached-name frequency. ROOM-JIDS records
-active room JIDs in the same completion set."
- (and name
- (= (gethash name name-counts 0) 1)
- (not (gethash name room-jids))))
-
-(defun jabber-roster--muc-completion-candidate
- (entry name-counts room-jids use-names)
- "Return completion candidate for ENTRY using NAME-COUNTS and ROOM-JIDS.
-When USE-NAMES is non-nil, use a cached name only if it is safe."
- (let ((room (car entry))
- (name (cdr entry)))
- (if (and use-names
- (jabber-roster--muc-safe-name-candidate-p
- name name-counts room-jids))
- name
- room)))
-
-(defun jabber-roster--muc-completion-item
- (entry name-counts room-jids use-names)
- "Return a completion item plist for MUC ENTRY.
-NAME-COUNTS records cached-name frequency. ROOM-JIDS records
-active room JIDs. When USE-NAMES is non-nil, safe cached names
-become candidates."
- (let* ((room (car entry))
- (name (cdr entry))
- (candidate (jabber-roster--muc-completion-candidate
- entry name-counts room-jids use-names))
- (annotation (if (string= candidate room) name room)))
- (list :room room
- :candidate candidate
- :annotation annotation)))
-
-(defun jabber-roster--muc-completion-items (entries use-names)
- "Return completion item plists for MUC ENTRIES.
-When USE-NAMES is non-nil, safe cached names become candidates
-and room JIDs become annotations."
- (let ((name-counts (jabber-roster--muc-name-counts entries))
- (room-jids (jabber-roster--muc-room-jids entries)))
- (mapcar (lambda (entry)
- (jabber-roster--muc-completion-item
- entry name-counts room-jids use-names))
- entries)))
-
-(defun jabber-roster--muc-completion-table (items)
- "Return completion table for MUC ITEMS."
- (let ((candidates (mapcar (lambda (item)
- (cons (plist-get item :candidate)
- (plist-get item :room)))
- items))
- (annotations (make-hash-table :test 'equal)))
- (dolist (item items)
- (when-let* ((annotation (plist-get item :annotation)))
- (puthash (plist-get item :candidate) annotation annotations)))
- (lambda (string pred action)
- (if (eq action 'metadata)
- `(metadata
- (annotation-function
- . ,(lambda (candidate)
- (when-let* ((annotation (gethash candidate annotations)))
- (concat " " annotation)))))
- (complete-with-action action candidates string pred)))))
-
-(defun jabber-roster--muc-completion-normalize (candidate items)
- "Return CANDIDATE normalized to a room JID from MUC ITEMS."
- (or (cdr (assoc-string candidate
- (mapcar (lambda (item)
- (cons (plist-get item :candidate)
- (plist-get item :room)))
- items)))
- (and (cl-find candidate items
- :key (lambda (item) (plist-get item :room))
- :test #'string=)
- candidate)))
-
-(defun jabber-roster-switch-muc (use-names)
- "Select a joined MUC room and switch to it.
-With prefix argument USE-NAMES, complete on unique cached room
-names and annotate them with room JIDs."
- (interactive "P")
- (let* ((entries (jabber-roster--muc-completion-entries))
- (items (jabber-roster--muc-completion-items entries use-names))
- (candidate (completing-read "Room: "
- (jabber-roster--muc-completion-table
items)
- nil t)))
- (when-let* ((room (and (not (string-empty-p candidate))
- (jabber-roster--muc-completion-normalize
- candidate items))))
- (jabber-muc-switch-to room))))
-
-;;; Account management
-
-(defvar jabber-roster--selected-account nil
- "Connection selected in the accounts menu.")
-
-(keymap-popup-define jabber-roster-account-action-map
- "Account actions."
- :description (lambda ()
- (if jabber-roster--selected-account
- (jabber-connection-bare-jid
jabber-roster--selected-account)
- "Account"))
- "i" ((lambda ()
- (if (eq jabber-roster--scoped-connection
- jabber-roster--selected-account)
- "Show all accounts"
- "Isolate"))
- jabber-roster--account-toggle-scope)
- "d" ("Disconnect" jabber-roster--account-disconnect))
-
-(defconst jabber-roster--all-accounts-label "All accounts"
- "Synthetic entry in the account picker that clears scope.")
-
-(defun jabber-roster-accounts ()
- "Select a connected account and show actions.
-When the roster is scoped to a single account, an \"All accounts\"
-entry is offered to clear the scope."
- (interactive)
- (let* ((accounts (mapcar (lambda (jc)
- (cons (jabber-connection-bare-jid jc) jc))
- jabber-connections))
- (candidates (if jabber-roster--scoped-connection
- (cons jabber-roster--all-accounts-label
- (mapcar #'car accounts))
- (mapcar #'car accounts)))
- (choice (completing-read "Account: " candidates nil t)))
- (cond
- ((or (null choice) (string-empty-p choice)))
- ((string= choice jabber-roster--all-accounts-label)
- (setq jabber-roster--scoped-connection nil)
- (keymap-popup jabber-roster-popup-map))
- (t
- (setq jabber-roster--selected-account (cdr (assoc choice accounts)))
- (keymap-popup jabber-roster-account-action-map)))))
-
-(defun jabber-roster--account-disconnect ()
- "Disconnect the selected account."
- (interactive)
- (when jabber-roster--selected-account
- (when (eq jabber-roster--scoped-connection jabber-roster--selected-account)
- (setq jabber-roster--scoped-connection nil))
- (jabber-disconnect-one jabber-roster--selected-account)
- (setq jabber-roster--selected-account nil)))
-
-(defun jabber-roster--account-toggle-scope ()
- "Toggle roster scope to/from the selected account."
- (interactive)
- (setq jabber-roster--scoped-connection
- (if (eq jabber-roster--scoped-connection
- jabber-roster--selected-account)
- nil
- jabber-roster--selected-account))
- (setq jabber-roster--selected-account nil)
- (keymap-popup jabber-roster-popup-map))
-
-(defun jabber-roster--clear-scope ()
- "Clear scope if the scoped connection is no longer active."
- (when (and jabber-roster--scoped-connection
- (not (memq jabber-roster--scoped-connection jabber-connections)))
- (setq jabber-roster--scoped-connection nil)))
-
-(add-hook 'jabber-post-disconnect-hook #'jabber-roster--clear-scope)
-
-(defun jabber-roster--jc-for-jid (jid)
- "Return the connection that has JID in its roster."
- (or (cl-find-if (lambda (jc) (jabber-roster-contact-p jc jid))
- jabber-connections)
- (car jabber-connections)))
-
-(defun jabber-roster--action-chat ()
- "Open chat with the selected contact."
- (interactive)
- (when jabber-roster--selected-jid
- (let ((jc (jabber-roster--jc-for-jid jabber-roster--selected-jid)))
- (jabber-chat-with jc jabber-roster--selected-jid))))
-
-(defun jabber-roster--action-info ()
- "Get info for the selected contact."
- (interactive)
- (when jabber-roster--selected-jid
- (let ((jc (jabber-roster--jc-for-jid jabber-roster--selected-jid)))
- (jabber-get-info jc jabber-roster--selected-jid))))
-
-(defun jabber-roster--action-edit ()
- "Edit name and groups of the selected contact."
- (interactive)
- (when jabber-roster--selected-jid
- (let* ((jc (jabber-roster--jc-for-jid jabber-roster--selected-jid))
- (sym (jabber-jid-symbol jabber-roster--selected-jid))
- (name (get sym 'name))
- (groups (get sym 'groups))
- (all-groups
- (apply #'append
- (mapcar (lambda (j) (get j 'groups))
- (plist-get (fsm-get-state-data jc) :roster))))
- (new-name (jabber-read-with-input-method
- (format "Name: (default `%s') " name) nil nil name))
- (new-groups (delete ""
- (completing-read-multiple
- (format "Groups, comma-separated: (default %s)
"
- (if groups (string-join groups ",")
"none"))
- all-groups
- nil nil nil
- 'jabber-roster-group-history
- (string-join groups ",")
- t))))
- (jabber-roster-change jc sym new-name new-groups))))
-
-(defun jabber-roster--action-delete ()
- "Delete the selected contact from roster."
- (interactive)
- (when jabber-roster--selected-jid
- (when (yes-or-no-p (format "Delete %s from roster? "
- jabber-roster--selected-jid))
- (let ((jc (jabber-roster--jc-for-jid jabber-roster--selected-jid)))
- (jabber-roster-delete jc jabber-roster--selected-jid)))))
-
-(defun jabber-roster--action-block ()
- "Block the selected contact."
- (interactive)
- (when jabber-roster--selected-jid
- (let ((jc (jabber-roster--jc-for-jid jabber-roster--selected-jid)))
- (jabber-blocking-block-jid jc jabber-roster--selected-jid))))
;;; Roster data management
diff --git a/lisp/jabber.el b/lisp/jabber.el
index a9f99130e3..1250dcafb8 100644
--- a/lisp/jabber.el
+++ b/lisp/jabber.el
@@ -194,6 +194,7 @@ One disabled account with a non-standard port:
(require 'jabber-autoaway)
(require 'jabber-time)
(require 'jabber-disco-menu)
+(require 'jabber-roster-menu)
(require 'jabber-chat-commands)
(require 'jabber-truncate)
(when (featurep 'dbusbind)