merge: sync upstream profanity-im/profanity
All checks were successful
CI Code / Check spelling (push) Successful in 18s
CI Code / Check coding style (push) Successful in 34s
CI Code / Code Coverage (push) Successful in 2m36s
CI Code / Linux (debian) (push) Successful in 4m41s
CI Code / Linux (ubuntu) (push) Successful in 4m52s
CI Code / Linux (arch) (push) Successful in 5m40s
All checks were successful
CI Code / Check spelling (push) Successful in 18s
CI Code / Check coding style (push) Successful in 34s
CI Code / Code Coverage (push) Successful in 2m36s
CI Code / Linux (debian) (push) Successful in 4m41s
CI Code / Linux (ubuntu) (push) Successful in 4m52s
CI Code / Linux (arch) (push) Successful in 5m40s
Sync with upstream profanity-im/profanity.
Major upstream changes incorporated:
Memory management
- Replace malloc+memset with g_new0 throughout codebase
- Adopt auto_gchar / auto_gcharv / auto_gerror / auto_jid cleanup macros
- Replace free() with g_free() for GAlloc'd memory
Editor rewrite
- Remove pthread-based async editor; use GChildWatch callback API
- New launch_editor(initial_content, callback, user_data) interface
- Proper signal handling (SIGINT, SIGTSTP, SIGPIPE reset in child)
- ui_suspend()/ui_resume() integration for TTY management
OMEMO improvements
- Dual backend support: libsignal-protocol-c and libomemo-c
- Proper pre-key removal after use (XEP-0384 compliance)
- Automatic pre-key regeneration when store drops below threshold
- New functions: omemo_is_device_active(), omemo_is_jid_trusted()
- omemo_get_jid_untrusted_fingerprints() for better error messages
- Fingerprint notifications on new device identity discovery
- Deterministic pre-key ID generation tracking max_pre_key_id
- omemo_trust_changed() UI updates on trust state changes
JID validation
- RFC 6122-compliant validation in jid_is_valid()
- Character-level checks (RFC 6122 forbidden chars: & ' / : < > @)
- Length limits: 1023 per component, 3071 total
- New jid_is_valid_user_jid() for user vs. service JID distinction
Database
- Schema migration v3: UNIQUE constraint on archive_id for deduplication
- Triggers for corrected message tracking (replaces_db_id / replaced_by_db_id)
- db_history_result_t return type, _truncate_datetime_suffix()
UI / console
- win_warn_needed() / win_warn_sent() warning deduplication hash table
- PAD_MIN_HEIGHT dynamic pad sizing with PAD_THRESHOLD auto-cleanup
- Spellcheck integration in input field with Unicode word detection
- cons_spellcheck_setting() for /settings ui output
- /[command]? shortcut for command help
Account config
- Account name sanitization for GKeyFile special chars ([ ] = # \n \r)
- Replace popen() with g_spawn_sync() for eval_password
- TLS policy: add "direct" option alongside legacy
Connection
- Port validation with g_assert (0–65535)
- SHA-256 certificate fingerprint support (XMPP_CERT_PUBKEY_FINGERPRINT_SHA256)
- "direct" TLS policy alias for legacy SSL
Common utilities
- str_xml_sanitize() for XML 1.0 illegal character removal
- string_matches_one_of() with formatted error messages
- valid_tls_policy_option() helper
- prof_date_time_format_iso8601() utility
- Improved strip_arg_quotes() with backslash unescaping
- prof_occurrences() uses g_slist_prepend + reverse for performance
PGP / OX
- Proper GPGME resource cleanup with goto-cleanup pattern
- g_string_free(xmppuri) leak fix in _ox_key_lookup
CSV export
- Use GString + g_file_set_contents instead of raw write() syscalls
Tests
- Restructured into subdirectories: command/, config/, xmpp/, ui/, omemo/, otr/, pgp/
- New test_cmd_ac.c for autocompleter unit tests
- Updated stubs for new UI suspend/resume functions
License headers
- Migrate to SPDX-3.0 identifiers (GPL-3.0-or-later WITH OpenSSL-exception)
────────────────────────────────────────────────────
cproof-specific preservations:
- XEP-0308 LMC: replace_id ?: id logic in message/stanza/omemo
- Force encryption: cmd_force_encryption, test_forced_encryption
- CWE-134: format string protection (cons_show("%s", ...))
- y_start_pos-based paging in window.c
- db_history_result_t return type, _truncate_datetime_suffix()
Merge-time fixes:
- common.c: format-security (-Werror) — cons_show(errmsg) → cons_show("%s", errmsg)
- database.c: null-deref guard — !msg->timestamp → msg && !msg->timestamp
- console.c: implicit size_t → int cast — (int)(maxlen + 1)
- tlscerts.c: %d for size_t — %zu
Build system:
- Kept autotools (Makefile.am, configure.ac); upstream uses Meson
- Restored deleted files: bootstrap.sh, autogen.sh, ax_valgrind_check.m4, configure-debug
- Updated Makefile.am test paths for subdirectory structure
- Added test_cmd_ac, test_forced_encryption to test sources
Functional tests:
- Use cproof version; upstream requires stbbr_for_xmlns from updated stabber
- Not yet available in devs/stabber fork
Closes #64
Merge author: jabber.developer2
Commits authors:
Michael Vetter <jubalh@iodoru.org>
& Steffen Jaeckel <s@jaeckel.eu>
This commit is contained in:
261
src/ui/window.c
261
src/ui/window.c
@@ -3,41 +3,18 @@
|
||||
* vim: expandtab:ts=4:sts=4:sw=4
|
||||
*
|
||||
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
|
||||
*
|
||||
* This file is part of Profanity.
|
||||
*
|
||||
* Profanity 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Profanity 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 Profanity. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link the code of portions of this program with the OpenSSL library under
|
||||
* certain conditions as described in each individual source file, and
|
||||
* distribute linked combinations including the two.
|
||||
*
|
||||
* You must obey the GNU General Public License in all respects for all of the
|
||||
* code used other than OpenSSL. If you modify file(s) with this exception, you
|
||||
* may extend this exception to your version of the file(s), but you are not
|
||||
* obligated to do so. If you do not wish to do so, delete this exception
|
||||
* statement from your version. If you delete this exception statement from all
|
||||
* source files in the program, then also delete it here.
|
||||
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "database.h"
|
||||
#include "ui/win_types.h"
|
||||
#include "ui/window_list.h"
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -65,6 +42,31 @@
|
||||
#include "xmpp/roster_list.h"
|
||||
#include "ai/ai_client.h"
|
||||
|
||||
static const int PAD_MIN_HEIGHT = 100;
|
||||
static const int PAD_THRESHOLD = 3000;
|
||||
static gboolean _in_redraw = FALSE;
|
||||
|
||||
static void
|
||||
_win_ensure_pad_capacity(ProfWin* window, WINDOW* win, int lines_needed)
|
||||
{
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
int cur_height = getmaxy(win);
|
||||
int cur_width = getmaxx(win);
|
||||
if (lines_needed >= cur_height - 1) {
|
||||
// If we are getting too large, trigger a redraw to clean up old lines
|
||||
// but only if we are not already in a redraw process.
|
||||
if (window && cur_height >= PAD_THRESHOLD && !_in_redraw) {
|
||||
win_redraw(window);
|
||||
} else {
|
||||
// resize to required lines + some buffer for next messages
|
||||
int new_height = lines_needed + 100;
|
||||
wresize(win, new_height, cur_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
static const char* LOADING_MESSAGE = "Loading older messages…";
|
||||
static const char* CONS_WIN_TITLE = "CProof. Type /help for help information.";
|
||||
static const char* XML_WIN_TITLE = "XML Console";
|
||||
@@ -75,7 +77,7 @@ static void
|
||||
_win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const char* const message, ...);
|
||||
static void _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDateTime* time,
|
||||
int flags, theme_item_t theme_item, const char* const from, const char* const message, DeliveryReceipt* receipt);
|
||||
static void _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent);
|
||||
static void _win_print_wrapped(WINDOW* win, const char* const message, int indent, int pad_indent);
|
||||
|
||||
// Helper: clamp a subwindow width to a sane range [1, cols-1] if possible
|
||||
static int
|
||||
@@ -111,9 +113,9 @@ _win_create_simple_layout(void)
|
||||
{
|
||||
int cols = getmaxx(stdscr);
|
||||
|
||||
ProfLayoutSimple* layout = malloc(sizeof(ProfLayoutSimple));
|
||||
ProfLayoutSimple* layout = g_new0(ProfLayoutSimple, 1);
|
||||
layout->base.type = LAYOUT_SIMPLE;
|
||||
layout->base.win = newpad(PAD_SIZE, cols);
|
||||
layout->base.win = newpad(PAD_MIN_HEIGHT, cols);
|
||||
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
|
||||
layout->base.buffer = buffer_create();
|
||||
layout->base.y_pos = 0;
|
||||
@@ -129,9 +131,9 @@ _win_create_split_layout(void)
|
||||
{
|
||||
int cols = getmaxx(stdscr);
|
||||
|
||||
ProfLayoutSplit* layout = malloc(sizeof(ProfLayoutSplit));
|
||||
ProfLayoutSplit* layout = g_new0(ProfLayoutSplit, 1);
|
||||
layout->base.type = LAYOUT_SPLIT;
|
||||
layout->base.win = newpad(PAD_SIZE, cols);
|
||||
layout->base.win = newpad(PAD_MIN_HEIGHT, cols);
|
||||
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
|
||||
layout->base.buffer = buffer_create();
|
||||
layout->base.y_pos = 0;
|
||||
@@ -148,7 +150,7 @@ _win_create_split_layout(void)
|
||||
ProfWin*
|
||||
win_create_console(void)
|
||||
{
|
||||
ProfConsoleWin* new_win = malloc(sizeof(ProfConsoleWin));
|
||||
ProfConsoleWin* new_win = g_new0(ProfConsoleWin, 1);
|
||||
new_win->window.type = WIN_CONSOLE;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_split_layout();
|
||||
@@ -159,8 +161,7 @@ win_create_console(void)
|
||||
ProfWin*
|
||||
win_create_chat(const char* const barejid)
|
||||
{
|
||||
assert(barejid != NULL);
|
||||
ProfChatWin* new_win = malloc(sizeof(ProfChatWin));
|
||||
ProfChatWin* new_win = g_new0(ProfChatWin, 1);
|
||||
new_win->window.type = WIN_CHAT;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_simple_layout();
|
||||
@@ -172,6 +173,9 @@ win_create_chat(const char* const barejid)
|
||||
new_win->pgp_recv = FALSE;
|
||||
new_win->pgp_send = FALSE;
|
||||
new_win->is_omemo = FALSE;
|
||||
#ifdef HAVE_OMEMO
|
||||
new_win->omemo_trusted = omemo_is_jid_trusted(barejid);
|
||||
#endif
|
||||
new_win->is_ox = FALSE;
|
||||
new_win->history_shown = FALSE;
|
||||
new_win->unread = 0;
|
||||
@@ -191,23 +195,22 @@ win_create_chat(const char* const barejid)
|
||||
ProfWin*
|
||||
win_create_muc(const char* const roomjid)
|
||||
{
|
||||
assert(roomjid != NULL);
|
||||
ProfMucWin* new_win = malloc(sizeof(ProfMucWin));
|
||||
ProfMucWin* new_win = g_new0(ProfMucWin, 1);
|
||||
int cols = getmaxx(stdscr);
|
||||
|
||||
new_win->window.type = WIN_MUC;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
ProfLayoutSplit* layout = malloc(sizeof(ProfLayoutSplit));
|
||||
ProfLayoutSplit* layout = g_new0(ProfLayoutSplit, 1);
|
||||
layout->base.type = LAYOUT_SPLIT;
|
||||
|
||||
if (prefs_get_boolean(PREF_OCCUPANTS)) {
|
||||
int subwin_cols = win_occpuants_cols();
|
||||
layout->base.win = newpad(PAD_SIZE, cols - subwin_cols);
|
||||
layout->base.win = newpad(PAD_MIN_HEIGHT, cols - subwin_cols);
|
||||
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
|
||||
layout->subwin = newpad(PAD_SIZE, subwin_cols);
|
||||
layout->subwin = newpad(PAD_MIN_HEIGHT, subwin_cols);
|
||||
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
|
||||
} else {
|
||||
layout->base.win = newpad(PAD_SIZE, (cols));
|
||||
layout->base.win = newpad(PAD_MIN_HEIGHT, (cols));
|
||||
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
|
||||
layout->subwin = NULL;
|
||||
}
|
||||
@@ -238,6 +241,9 @@ win_create_muc(const char* const roomjid)
|
||||
new_win->enctext = NULL;
|
||||
new_win->message_char = NULL;
|
||||
new_win->is_omemo = FALSE;
|
||||
#ifdef HAVE_OMEMO
|
||||
new_win->omemo_trusted = omemo_is_jid_trusted(roomjid);
|
||||
#endif
|
||||
new_win->last_message = NULL;
|
||||
new_win->last_msg_id = NULL;
|
||||
new_win->has_attention = FALSE;
|
||||
@@ -250,9 +256,7 @@ win_create_muc(const char* const roomjid)
|
||||
ProfWin*
|
||||
win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback submit, ProfConfWinCallback cancel, const void* userdata)
|
||||
{
|
||||
assert(roomjid != NULL);
|
||||
assert(form != NULL);
|
||||
ProfConfWin* new_win = malloc(sizeof(ProfConfWin));
|
||||
ProfConfWin* new_win = g_new0(ProfConfWin, 1);
|
||||
new_win->window.type = WIN_CONFIG;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_simple_layout();
|
||||
@@ -270,8 +274,7 @@ win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback
|
||||
ProfWin*
|
||||
win_create_private(const char* const fulljid)
|
||||
{
|
||||
assert(fulljid != NULL);
|
||||
ProfPrivateWin* new_win = malloc(sizeof(ProfPrivateWin));
|
||||
ProfPrivateWin* new_win = g_new0(ProfPrivateWin, 1);
|
||||
new_win->window.type = WIN_PRIVATE;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_simple_layout();
|
||||
@@ -288,7 +291,7 @@ win_create_private(const char* const fulljid)
|
||||
ProfWin*
|
||||
win_create_xmlconsole(void)
|
||||
{
|
||||
ProfXMLWin* new_win = malloc(sizeof(ProfXMLWin));
|
||||
ProfXMLWin* new_win = g_new0(ProfXMLWin, 1);
|
||||
new_win->window.type = WIN_XML;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_simple_layout();
|
||||
@@ -301,9 +304,7 @@ win_create_xmlconsole(void)
|
||||
ProfWin*
|
||||
win_create_plugin(const char* const plugin_name, const char* const tag)
|
||||
{
|
||||
assert(plugin_name != NULL);
|
||||
assert(tag != NULL);
|
||||
ProfPluginWin* new_win = malloc(sizeof(ProfPluginWin));
|
||||
ProfPluginWin* new_win = g_new0(ProfPluginWin, 1);
|
||||
new_win->window.type = WIN_PLUGIN;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_simple_layout();
|
||||
@@ -319,8 +320,7 @@ win_create_plugin(const char* const plugin_name, const char* const tag)
|
||||
ProfWin*
|
||||
win_create_vcard(vCard* vcard)
|
||||
{
|
||||
assert(vcard != NULL);
|
||||
ProfVcardWin* new_win = malloc(sizeof(ProfVcardWin));
|
||||
ProfVcardWin* new_win = g_new0(ProfVcardWin, 1);
|
||||
new_win->window.type = WIN_VCARD;
|
||||
new_win->window.scroll_state = WIN_SCROLL_INNER;
|
||||
new_win->window.layout = _win_create_simple_layout();
|
||||
@@ -586,11 +586,11 @@ win_hide_subwin(ProfWin* window)
|
||||
layout->subwin = NULL;
|
||||
layout->sub_y_pos = 0;
|
||||
int cols = getmaxx(stdscr);
|
||||
wresize(layout->base.win, PAD_SIZE, cols);
|
||||
wresize(layout->base.win, PAD_MIN_HEIGHT, cols);
|
||||
win_redraw(window);
|
||||
} else {
|
||||
int cols = getmaxx(stdscr);
|
||||
wresize(window->layout->win, PAD_SIZE, cols);
|
||||
wresize(window->layout->win, PAD_MIN_HEIGHT, cols);
|
||||
win_redraw(window);
|
||||
}
|
||||
}
|
||||
@@ -612,27 +612,14 @@ win_show_subwin(ProfWin* window)
|
||||
}
|
||||
|
||||
ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
|
||||
// If a subwindow already exists (e.g. repeated call), destroy it to avoid leaks
|
||||
/* Free a previous pad if win_show_subwin was called twice in a row
|
||||
* without an intervening win_hide_subwin. */
|
||||
if (layout->subwin) {
|
||||
delwin(layout->subwin);
|
||||
layout->subwin = NULL;
|
||||
}
|
||||
|
||||
// Ensure minimum width to avoid creating a zero-width pad
|
||||
if (subwin_cols <= 0) {
|
||||
subwin_cols = 1;
|
||||
}
|
||||
|
||||
layout->subwin = newpad(PAD_SIZE, subwin_cols);
|
||||
if (layout->subwin == NULL) {
|
||||
// Failed to allocate subwindow; keep base window resized to full width
|
||||
log_error("Failed to create subwindow pad (cols=%d)", subwin_cols);
|
||||
wresize(layout->base.win, PAD_SIZE, cols);
|
||||
win_redraw(window);
|
||||
return;
|
||||
}
|
||||
layout->subwin = newpad(PAD_MIN_HEIGHT, subwin_cols);
|
||||
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
|
||||
wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
|
||||
wresize(layout->base.win, PAD_MIN_HEIGHT, cols - subwin_cols);
|
||||
win_redraw(window);
|
||||
}
|
||||
|
||||
@@ -652,6 +639,10 @@ win_free(ProfWin* window)
|
||||
}
|
||||
free(window->layout);
|
||||
|
||||
if (window->warned_jids) {
|
||||
g_hash_table_destroy(window->warned_jids);
|
||||
}
|
||||
|
||||
switch (window->type) {
|
||||
case WIN_CHAT:
|
||||
{
|
||||
@@ -715,6 +706,44 @@ win_free(ProfWin* window)
|
||||
free(window);
|
||||
}
|
||||
|
||||
gboolean
|
||||
win_warn_needed(ProfWin* window, const char* const type, const char* const jid)
|
||||
{
|
||||
if (!window || !type || !jid) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!window->warned_jids) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto_gchar gchar* key = g_strdup_printf("%s:%s", type, jid);
|
||||
return !g_hash_table_contains(window->warned_jids, key);
|
||||
}
|
||||
|
||||
void
|
||||
win_warn_sent(ProfWin* window, const char* const type, const char* const jid)
|
||||
{
|
||||
if (!window || !type || !jid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window->warned_jids) {
|
||||
window->warned_jids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||
}
|
||||
|
||||
gchar* key = g_strdup_printf("%s:%s", type, jid);
|
||||
g_hash_table_insert(window->warned_jids, key, GINT_TO_POINTER(1));
|
||||
}
|
||||
|
||||
void
|
||||
win_clear_warned_jids(ProfWin* window)
|
||||
{
|
||||
if (window && window->warned_jids) {
|
||||
g_hash_table_remove_all(window->warned_jids);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win_page_up(ProfWin* window, int scroll_size)
|
||||
{
|
||||
@@ -811,7 +840,7 @@ win_page_down(ProfWin* window, int scroll_size)
|
||||
GDateTime* now = g_date_time_new_now_local();
|
||||
ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1);
|
||||
auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time);
|
||||
auto_gchar gchar* end_date = g_date_time_format_iso8601(now);
|
||||
auto_gchar gchar* end_date = prof_date_time_format_iso8601(NULL);
|
||||
db_history_result_t db_response = chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE);
|
||||
if (db_response == DB_RESPONSE_EMPTY)
|
||||
*scroll_state = WIN_SCROLL_REACHED_BOTTOM;
|
||||
@@ -934,9 +963,9 @@ win_resize(ProfWin* window)
|
||||
subwin_cols = win_occpuants_cols();
|
||||
}
|
||||
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
|
||||
wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
|
||||
wresize(layout->base.win, PAD_MIN_HEIGHT, cols - subwin_cols);
|
||||
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
|
||||
wresize(layout->subwin, PAD_SIZE, subwin_cols);
|
||||
wresize(layout->subwin, PAD_MIN_HEIGHT, subwin_cols);
|
||||
if (window->type == WIN_CONSOLE) {
|
||||
rosterwin_roster();
|
||||
} else if (window->type == WIN_MUC) {
|
||||
@@ -946,11 +975,11 @@ win_resize(ProfWin* window)
|
||||
}
|
||||
} else {
|
||||
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
|
||||
wresize(layout->base.win, PAD_SIZE, cols);
|
||||
wresize(layout->base.win, PAD_MIN_HEIGHT, cols);
|
||||
}
|
||||
} else {
|
||||
wbkgd(window->layout->win, theme_attrs(THEME_TEXT));
|
||||
wresize(window->layout->win, PAD_SIZE, cols);
|
||||
wresize(window->layout->win, PAD_MIN_HEIGHT, cols);
|
||||
}
|
||||
|
||||
win_redraw(window);
|
||||
@@ -1078,11 +1107,11 @@ win_show_contact(ProfWin* window, PContact contact)
|
||||
GTimeSpan span = g_date_time_difference(now, last_activity);
|
||||
g_date_time_unref(now);
|
||||
|
||||
int hours = span / G_TIME_SPAN_HOUR;
|
||||
int hours = (int)(span / G_TIME_SPAN_HOUR);
|
||||
span = span - hours * G_TIME_SPAN_HOUR;
|
||||
int minutes = span / G_TIME_SPAN_MINUTE;
|
||||
int minutes = (int)(span / G_TIME_SPAN_MINUTE);
|
||||
span = span - minutes * G_TIME_SPAN_MINUTE;
|
||||
int seconds = span / G_TIME_SPAN_SECOND;
|
||||
int seconds = (int)(span / G_TIME_SPAN_SECOND);
|
||||
|
||||
if (hours > 0) {
|
||||
win_append(window, presence_colour, ", idle %dh%dm%ds", hours, minutes, seconds);
|
||||
@@ -1203,11 +1232,11 @@ win_show_info(ProfWin* window, PContact contact)
|
||||
GDateTime* now = g_date_time_new_now_local();
|
||||
GTimeSpan span = g_date_time_difference(now, last_activity);
|
||||
|
||||
int hours = span / G_TIME_SPAN_HOUR;
|
||||
int hours = (int)(span / G_TIME_SPAN_HOUR);
|
||||
span = span - hours * G_TIME_SPAN_HOUR;
|
||||
int minutes = span / G_TIME_SPAN_MINUTE;
|
||||
int minutes = (int)(span / G_TIME_SPAN_MINUTE);
|
||||
span = span - minutes * G_TIME_SPAN_MINUTE;
|
||||
int seconds = span / G_TIME_SPAN_SECOND;
|
||||
int seconds = (int)(span / G_TIME_SPAN_SECOND);
|
||||
|
||||
if (hours > 0) {
|
||||
win_println(window, THEME_DEFAULT, "-", "Last activity: %dh%dm%ds", hours, minutes, seconds);
|
||||
@@ -1743,7 +1772,7 @@ win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* con
|
||||
void
|
||||
win_print_http_transfer(ProfWin* window, const char* const message, char* id)
|
||||
{
|
||||
win_print_outgoing_with_receipt(window, "!", NULL, message, id, NULL);
|
||||
win_print_status_with_id(window, message, id, THEME_DEFAULT, 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1751,7 +1780,7 @@ win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const ch
|
||||
{
|
||||
GDateTime* time = g_date_time_new_now_local();
|
||||
|
||||
DeliveryReceipt* receipt = malloc(sizeof(struct delivery_receipt_t));
|
||||
DeliveryReceipt* receipt = g_new0(struct delivery_receipt_t, 1);
|
||||
receipt->received = FALSE;
|
||||
|
||||
const char* myjid = connection_get_fulljid();
|
||||
@@ -1792,6 +1821,33 @@ win_update_entry_message(ProfWin* window, const char* const id, const char* cons
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win_update_entry(ProfWin* window, const char* const id, const char* const message, theme_item_t theme_item, int flags)
|
||||
{
|
||||
if (window->type == WIN_CONSOLE)
|
||||
return;
|
||||
ProfBuffEntry* entry = buffer_get_entry_by_id(window->layout->buffer, id);
|
||||
if (entry) {
|
||||
if (message) {
|
||||
free(entry->message);
|
||||
entry->message = strdup(message);
|
||||
}
|
||||
entry->theme_item = theme_item;
|
||||
entry->flags |= flags;
|
||||
win_redraw(window);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win_print_status_with_id(ProfWin* window, const char* const message, char* id, theme_item_t theme_item, int flags)
|
||||
{
|
||||
GDateTime* time = g_date_time_new_now_local();
|
||||
int y_start_pos = getcury(window->layout->win);
|
||||
_win_print_internal(window, "!", 0, time, flags, theme_item, NULL, message, NULL);
|
||||
buffer_append(window->layout->buffer, "!", 0, time, flags, theme_item, NULL, NULL, message, NULL, id, y_start_pos, getcury(window->layout->win));
|
||||
g_date_time_unref(time);
|
||||
}
|
||||
|
||||
void
|
||||
win_remove_entry_message(ProfWin* window, const char* const id)
|
||||
{
|
||||
@@ -1851,7 +1907,7 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
|
||||
gboolean me_message = FALSE;
|
||||
int offset = 0;
|
||||
int colour = theme_attrs(THEME_ME);
|
||||
size_t indent = 0;
|
||||
int indent = 0;
|
||||
|
||||
auto_gchar gchar* time_pref = NULL;
|
||||
switch (window->type) {
|
||||
@@ -1884,7 +1940,7 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
|
||||
assert(date_fmt != NULL);
|
||||
|
||||
if (strlen(date_fmt) != 0) {
|
||||
indent = 3 + strlen(date_fmt);
|
||||
indent = 3 + (int)strlen(date_fmt);
|
||||
}
|
||||
|
||||
if ((flags & NO_DATE) == 0) {
|
||||
@@ -1945,6 +2001,8 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
|
||||
}
|
||||
}
|
||||
|
||||
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
|
||||
|
||||
if (prefs_get_boolean(PREF_WRAP)) {
|
||||
_win_print_wrapped(window->layout->win, message + offset, indent, pad_indent);
|
||||
} else {
|
||||
@@ -1978,11 +2036,11 @@ _win_indent(WINDOW* win, int size)
|
||||
}
|
||||
|
||||
static void
|
||||
_win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent)
|
||||
_win_print_wrapped(WINDOW* win, const char* const message, int indent, int pad_indent)
|
||||
{
|
||||
int starty = getcury(win);
|
||||
int wordi = 0;
|
||||
auto_char char* word = malloc(strlen(message) + 1);
|
||||
auto_gchar gchar* word = g_malloc(strlen(message) + 1);
|
||||
|
||||
gchar* curr_ch = g_utf8_offset_to_pointer(message, 0);
|
||||
|
||||
@@ -2005,11 +2063,12 @@ _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pa
|
||||
int wordlen = 0;
|
||||
while (*curr_ch != ' ' && *curr_ch != '\n' && *curr_ch != '\0') {
|
||||
size_t ch_len = mbrlen(curr_ch, MB_CUR_MAX, NULL);
|
||||
if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1)) {
|
||||
// 0 / invalid step: advance one byte to avoid spinning.
|
||||
if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1) || ch_len == 0) {
|
||||
curr_ch++;
|
||||
continue;
|
||||
}
|
||||
int offset = 0;
|
||||
size_t offset = 0;
|
||||
while (offset < ch_len) {
|
||||
word[wordi++] = curr_ch[offset++];
|
||||
}
|
||||
@@ -2094,6 +2153,7 @@ _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pa
|
||||
void
|
||||
win_print_trackbar(ProfWin* window)
|
||||
{
|
||||
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
|
||||
int cols = getmaxx(window->layout->win);
|
||||
|
||||
wbkgdset(window->layout->win, theme_attrs(THEME_TRACKBAR));
|
||||
@@ -2109,12 +2169,20 @@ win_print_trackbar(ProfWin* window)
|
||||
void
|
||||
win_redraw(ProfWin* window)
|
||||
{
|
||||
int size = buffer_size(window->layout->buffer);
|
||||
unsigned int size = buffer_size(window->layout->buffer);
|
||||
_in_redraw = TRUE;
|
||||
|
||||
// shrink pad back to minimum size and erase it
|
||||
int cols = getmaxx(window->layout->win);
|
||||
wresize(window->layout->win, PAD_MIN_HEIGHT, cols);
|
||||
werase(window->layout->win);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i);
|
||||
|
||||
// check if we need more space before printing
|
||||
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
|
||||
|
||||
e->y_start_pos = getcury(window->layout->win);
|
||||
if (e->display_from == NULL && e->message && e->message[0] == '-') {
|
||||
// just an indicator to print the trackbar/separator not the actual message
|
||||
@@ -2129,6 +2197,8 @@ win_redraw(ProfWin* window)
|
||||
e->_lines = e->y_end_pos - e->y_start_pos;
|
||||
window->layout->buffer->lines += e->_lines;
|
||||
}
|
||||
|
||||
_in_redraw = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2263,6 +2333,7 @@ win_toggle_attention(ProfWin* window)
|
||||
void
|
||||
win_sub_print(WINDOW* win, char* msg, gboolean newline, gboolean wrap, int indent)
|
||||
{
|
||||
_win_ensure_pad_capacity(NULL, win, getcury(win));
|
||||
int maxx = getmaxx(win);
|
||||
int curx = getcurx(win);
|
||||
int cury = getcury(win);
|
||||
@@ -2351,11 +2422,11 @@ win_handle_command_exec_result_note(ProfWin* window, const char* const type, con
|
||||
void
|
||||
win_insert_last_read_position_marker(ProfWin* window, char* id)
|
||||
{
|
||||
int size = buffer_size(window->layout->buffer);
|
||||
unsigned int size = buffer_size(window->layout->buffer);
|
||||
|
||||
// TODO: this is somewhat costly. We should improve this later.
|
||||
// check if we already have a separator present
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i);
|
||||
|
||||
// if yes, don't print a new one
|
||||
|
||||
Reference in New Issue
Block a user