mirror of
https://github.com/profanity-im/profanity.git
synced 2024-09-22 19:45:54 -04:00
19de066008
From @xaizek s comment on issue #1235: ``` If the move would cause the window to be off the screen, it is an error and the window is not moved. Resize on the other hand doesn't fail like this according to its documentation. So new size needs to be applied first. ``` Big thanks to @xaizek for taking a look at our code and helping us!! Regards https://github.com/profanity-im/profanity/issues/1235
633 lines
17 KiB
C
633 lines
17 KiB
C
/*
|
|
* statusbar.c
|
|
* vim: expandtab:ts=4:sts=4:sw=4
|
|
*
|
|
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
|
* Copyright (C) 2019 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.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_NCURSESW_NCURSES_H
|
|
#include <ncursesw/ncurses.h>
|
|
#elif HAVE_NCURSES_H
|
|
#include <ncurses.h>
|
|
#endif
|
|
|
|
#include "config/theme.h"
|
|
#include "config/preferences.h"
|
|
#include "ui/ui.h"
|
|
#include "ui/statusbar.h"
|
|
#include "ui/inputwin.h"
|
|
#include "ui/screen.h"
|
|
#include "xmpp/roster_list.h"
|
|
#include "xmpp/contact.h"
|
|
|
|
typedef struct _status_bar_tab_t {
|
|
win_type_t window_type;
|
|
char *identifier;
|
|
gboolean highlight;
|
|
char *display_name;
|
|
} StatusBarTab;
|
|
|
|
typedef struct _status_bar_t {
|
|
gchar *time;
|
|
char *prompt;
|
|
char *fulljid;
|
|
GHashTable *tabs;
|
|
int current_tab;
|
|
} StatusBar;
|
|
|
|
static GTimeZone *tz;
|
|
static StatusBar *statusbar;
|
|
static WINDOW *statusbar_win;
|
|
|
|
static int _status_bar_draw_time(int pos);
|
|
static void _status_bar_draw_maintext(int pos);
|
|
static int _status_bar_draw_bracket(gboolean current, int pos, char* ch);
|
|
static int _status_bar_draw_extended_tabs(int pos);
|
|
static int _status_bar_draw_tab(StatusBarTab *tab, int pos, int num);
|
|
static void _destroy_tab(StatusBarTab *tab);
|
|
static int _tabs_width(void);
|
|
static char* _display_name(StatusBarTab *tab);
|
|
static gboolean _extended_new(void);
|
|
|
|
void
|
|
status_bar_init(void)
|
|
{
|
|
tz = g_time_zone_new_local();
|
|
|
|
statusbar = malloc(sizeof(StatusBar));
|
|
statusbar->time = NULL;
|
|
statusbar->prompt = NULL;
|
|
statusbar->fulljid = NULL;
|
|
statusbar->tabs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)_destroy_tab);
|
|
StatusBarTab *console = calloc(1, sizeof(StatusBarTab));
|
|
console->window_type = WIN_CONSOLE;
|
|
console->identifier = strdup("console");
|
|
console->display_name = NULL;
|
|
g_hash_table_insert(statusbar->tabs, GINT_TO_POINTER(1), console);
|
|
statusbar->current_tab = 1;
|
|
|
|
int row = screen_statusbar_row();
|
|
int cols = getmaxx(stdscr);
|
|
statusbar_win = newwin(1, cols, row, 0);
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_close(void)
|
|
{
|
|
if (tz) {
|
|
g_time_zone_unref(tz);
|
|
}
|
|
if (statusbar) {
|
|
if (statusbar->time) {
|
|
g_free(statusbar->time);
|
|
}
|
|
if (statusbar->prompt) {
|
|
free(statusbar->prompt);
|
|
}
|
|
if (statusbar->fulljid) {
|
|
free(statusbar->fulljid);
|
|
}
|
|
if (statusbar->tabs) {
|
|
g_hash_table_destroy(statusbar->tabs);
|
|
}
|
|
free(statusbar);
|
|
}
|
|
}
|
|
|
|
void
|
|
status_bar_resize(void)
|
|
{
|
|
int cols = getmaxx(stdscr);
|
|
werase(statusbar_win);
|
|
int row = screen_statusbar_row();
|
|
wresize(statusbar_win, 1, cols);
|
|
mvwin(statusbar_win, row, 0);
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_set_all_inactive(void)
|
|
{
|
|
g_hash_table_remove_all(statusbar->tabs);
|
|
}
|
|
|
|
void
|
|
status_bar_current(int i)
|
|
{
|
|
if (i == 0) {
|
|
statusbar->current_tab = 10;
|
|
} else {
|
|
statusbar->current_tab = i;
|
|
}
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_inactive(const int win)
|
|
{
|
|
int true_win = win;
|
|
if (true_win == 0) {
|
|
true_win = 10;
|
|
}
|
|
|
|
g_hash_table_remove(statusbar->tabs, GINT_TO_POINTER(true_win));
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
_create_tab(const int win, win_type_t wintype, char *identifier, gboolean highlight)
|
|
{
|
|
int true_win = win;
|
|
if (true_win == 0) {
|
|
true_win = 10;
|
|
}
|
|
|
|
StatusBarTab *tab = malloc(sizeof(StatusBarTab));
|
|
tab->identifier = strdup(identifier);
|
|
tab->highlight = highlight;
|
|
tab->window_type = wintype;
|
|
tab->display_name = NULL;
|
|
|
|
if (tab->window_type == WIN_CHAT) {
|
|
PContact contact = NULL;
|
|
if (roster_exists()) {
|
|
contact = roster_get_contact(tab->identifier);
|
|
}
|
|
if (contact && p_contact_name(contact)) {
|
|
tab->display_name = strdup(p_contact_name(contact));
|
|
} else {
|
|
char *pref = prefs_get_string(PREF_STATUSBAR_CHAT);
|
|
if (g_strcmp0("user", pref) == 0) {
|
|
Jid *jidp = jid_create(tab->identifier);
|
|
tab->display_name = jidp->localpart != NULL ?
|
|
strdup(jidp->localpart) :
|
|
strdup(jidp->barejid);
|
|
jid_destroy(jidp);
|
|
} else {
|
|
tab->display_name = strdup(tab->identifier);
|
|
}
|
|
prefs_free_string(pref);
|
|
}
|
|
}
|
|
|
|
g_hash_table_replace(statusbar->tabs, GINT_TO_POINTER(true_win), tab);
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_active(const int win, win_type_t wintype, char *identifier)
|
|
{
|
|
_create_tab(win, wintype, identifier, FALSE);
|
|
}
|
|
|
|
void
|
|
status_bar_new(const int win, win_type_t wintype, char* identifier)
|
|
{
|
|
_create_tab(win, wintype, identifier, TRUE);
|
|
}
|
|
|
|
void
|
|
status_bar_set_prompt(const char *const prompt)
|
|
{
|
|
if (statusbar->prompt) {
|
|
free(statusbar->prompt);
|
|
statusbar->prompt = NULL;
|
|
}
|
|
statusbar->prompt = strdup(prompt);
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_clear_prompt(void)
|
|
{
|
|
if (statusbar->prompt) {
|
|
free(statusbar->prompt);
|
|
statusbar->prompt = NULL;
|
|
}
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_set_fulljid(const char *const fulljid)
|
|
{
|
|
if (statusbar->fulljid) {
|
|
free(statusbar->fulljid);
|
|
statusbar->fulljid = NULL;
|
|
}
|
|
statusbar->fulljid = strdup(fulljid);
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_clear_fulljid(void)
|
|
{
|
|
if (statusbar->fulljid) {
|
|
free(statusbar->fulljid);
|
|
statusbar->fulljid = NULL;
|
|
}
|
|
|
|
status_bar_draw();
|
|
}
|
|
|
|
void
|
|
status_bar_draw(void)
|
|
{
|
|
werase(statusbar_win);
|
|
wbkgd(statusbar_win, theme_attrs(THEME_STATUS_TEXT));
|
|
|
|
int pos = 1;
|
|
|
|
pos = _status_bar_draw_time(pos);
|
|
|
|
_status_bar_draw_maintext(pos);
|
|
|
|
pos = getmaxx(stdscr) - _tabs_width();
|
|
if (pos < 0) {
|
|
pos = 0;
|
|
}
|
|
gint max_tabs = prefs_get_statusbartabs();
|
|
int i = 1;
|
|
for (i = 1; i <= max_tabs; i++) {
|
|
StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
|
|
if (tab) {
|
|
pos = _status_bar_draw_tab(tab, pos, i);
|
|
}
|
|
}
|
|
|
|
_status_bar_draw_extended_tabs(pos);
|
|
|
|
wnoutrefresh(statusbar_win);
|
|
inp_put_back();
|
|
}
|
|
|
|
static gboolean
|
|
_extended_new(void)
|
|
{
|
|
gint max_tabs = prefs_get_statusbartabs();
|
|
int tabs_count = g_hash_table_size(statusbar->tabs);
|
|
if (tabs_count <= max_tabs) {
|
|
return FALSE;
|
|
}
|
|
|
|
int i = 0;
|
|
for (i = max_tabs + 1; i <= tabs_count; i++) {
|
|
StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
|
|
if (tab && tab->highlight) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
_status_bar_draw_extended_tabs(int pos)
|
|
{
|
|
gint max_tabs = prefs_get_statusbartabs();
|
|
if (max_tabs == 0) {
|
|
return pos;
|
|
}
|
|
|
|
if (g_hash_table_size(statusbar->tabs) > max_tabs) {
|
|
gboolean is_current = statusbar->current_tab > max_tabs;
|
|
|
|
pos = _status_bar_draw_bracket(is_current, pos, "[");
|
|
|
|
int status_attrs;
|
|
if (is_current) {
|
|
// currently selected
|
|
status_attrs = theme_attrs(THEME_STATUS_CURRENT);
|
|
} else if (_extended_new()) {
|
|
// new one
|
|
status_attrs = theme_attrs(THEME_STATUS_NEW);
|
|
} else {
|
|
// all other
|
|
status_attrs = theme_attrs(THEME_STATUS_ACTIVE);
|
|
}
|
|
wattron(statusbar_win, status_attrs);
|
|
mvwprintw(statusbar_win, 0, pos, ">");
|
|
wattroff(statusbar_win, status_attrs);
|
|
pos++;
|
|
|
|
pos = _status_bar_draw_bracket(is_current, pos, "]");
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
static int
|
|
_status_bar_draw_tab(StatusBarTab *tab, int pos, int num)
|
|
{
|
|
int display_num = num == 10 ? 0 : num;
|
|
gboolean is_current = num == statusbar->current_tab;
|
|
|
|
gboolean show_number = prefs_get_boolean(PREF_STATUSBAR_SHOW_NUMBER);
|
|
gboolean show_name = prefs_get_boolean(PREF_STATUSBAR_SHOW_NAME);
|
|
|
|
pos = _status_bar_draw_bracket(is_current, pos, "[");
|
|
|
|
int status_attrs;
|
|
if (is_current) {
|
|
status_attrs = theme_attrs(THEME_STATUS_CURRENT);
|
|
} else if (tab->highlight) {
|
|
status_attrs = theme_attrs(THEME_STATUS_NEW);
|
|
} else {
|
|
status_attrs = theme_attrs(THEME_STATUS_ACTIVE);
|
|
}
|
|
wattron(statusbar_win, status_attrs);
|
|
if (show_number) {
|
|
mvwprintw(statusbar_win, 0, pos, "%d", display_num);
|
|
pos++;
|
|
}
|
|
if (show_number && show_name) {
|
|
mvwprintw(statusbar_win, 0, pos, ":");
|
|
pos++;
|
|
}
|
|
if (show_name) {
|
|
char *display_name = _display_name(tab);
|
|
mvwprintw(statusbar_win, 0, pos, display_name);
|
|
pos += utf8_display_len(display_name);
|
|
free(display_name);
|
|
}
|
|
wattroff(statusbar_win, status_attrs);
|
|
|
|
pos = _status_bar_draw_bracket(is_current, pos, "]");
|
|
|
|
return pos;
|
|
}
|
|
|
|
static int
|
|
_status_bar_draw_bracket(gboolean current, int pos, char* ch)
|
|
{
|
|
int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
|
|
wattron(statusbar_win, bracket_attrs);
|
|
if (current) {
|
|
mvwprintw(statusbar_win, 0, pos, "-");
|
|
} else {
|
|
mvwprintw(statusbar_win, 0, pos, ch);
|
|
}
|
|
wattroff(statusbar_win, bracket_attrs);
|
|
pos++;
|
|
|
|
return pos;
|
|
}
|
|
|
|
static int
|
|
_status_bar_draw_time(int pos)
|
|
{
|
|
char *time_pref = prefs_get_string(PREF_TIME_STATUSBAR);
|
|
if (g_strcmp0(time_pref, "off") == 0) {
|
|
prefs_free_string(time_pref);
|
|
return pos;
|
|
}
|
|
|
|
if (statusbar->time) {
|
|
g_free(statusbar->time);
|
|
statusbar->time = NULL;
|
|
}
|
|
|
|
GDateTime *datetime = g_date_time_new_now(tz);
|
|
statusbar->time = g_date_time_format(datetime, time_pref);
|
|
assert(statusbar->time != NULL);
|
|
g_date_time_unref(datetime);
|
|
|
|
int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
|
|
int time_attrs = theme_attrs(THEME_STATUS_TIME);
|
|
|
|
size_t len = strlen(statusbar->time);
|
|
wattron(statusbar_win, bracket_attrs);
|
|
mvwaddch(statusbar_win, 0, pos, '[');
|
|
pos++;
|
|
wattroff(statusbar_win, bracket_attrs);
|
|
wattron(statusbar_win, time_attrs);
|
|
mvwprintw(statusbar_win, 0, pos, statusbar->time);
|
|
pos += len;
|
|
wattroff(statusbar_win, time_attrs);
|
|
wattron(statusbar_win, bracket_attrs);
|
|
mvwaddch(statusbar_win, 0, pos, ']');
|
|
wattroff(statusbar_win, bracket_attrs);
|
|
pos += 2;
|
|
|
|
prefs_free_string(time_pref);
|
|
|
|
return pos;
|
|
}
|
|
|
|
static void
|
|
_status_bar_draw_maintext(int pos)
|
|
{
|
|
if (statusbar->prompt) {
|
|
mvwprintw(statusbar_win, 0, pos, statusbar->prompt);
|
|
return;
|
|
}
|
|
|
|
gboolean stop = FALSE;
|
|
|
|
if (statusbar->fulljid) {
|
|
char *pref = prefs_get_string(PREF_STATUSBAR_SELF);
|
|
|
|
if (g_strcmp0(pref, "off") == 0) {
|
|
stop = true;
|
|
} else if (g_strcmp0(pref, "user") == 0) {
|
|
Jid *jidp = jid_create(statusbar->fulljid);
|
|
mvwprintw(statusbar_win, 0, pos, jidp->localpart);
|
|
jid_destroy(jidp);
|
|
stop = true;
|
|
} else if (g_strcmp0(pref, "barejid") == 0) {
|
|
Jid *jidp = jid_create(statusbar->fulljid);
|
|
mvwprintw(statusbar_win, 0, pos, jidp->barejid);
|
|
jid_destroy(jidp);
|
|
stop = true;
|
|
}
|
|
|
|
prefs_free_string(pref);
|
|
if (stop) {
|
|
return;
|
|
}
|
|
mvwprintw(statusbar_win, 0, pos, statusbar->fulljid);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_destroy_tab(StatusBarTab *tab)
|
|
{
|
|
if (tab) {
|
|
if (tab->identifier) {
|
|
free(tab->identifier);
|
|
}
|
|
if (tab->display_name) {
|
|
free(tab->display_name);
|
|
}
|
|
free(tab);
|
|
}
|
|
tab = NULL;
|
|
}
|
|
|
|
static int
|
|
_tabs_width(void)
|
|
{
|
|
gboolean show_number = prefs_get_boolean(PREF_STATUSBAR_SHOW_NUMBER);
|
|
gboolean show_name = prefs_get_boolean(PREF_STATUSBAR_SHOW_NAME);
|
|
gint max_tabs = prefs_get_statusbartabs();
|
|
|
|
if (show_name && show_number) {
|
|
int width = g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1;
|
|
int i = 0;
|
|
for (i = 1; i <= max_tabs; i++) {
|
|
StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
|
|
if (tab) {
|
|
char *display_name = _display_name(tab);
|
|
width += utf8_display_len(display_name);
|
|
width += 4;
|
|
free(display_name);
|
|
}
|
|
}
|
|
return width;
|
|
}
|
|
|
|
if (show_name && !show_number) {
|
|
int width = g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1;
|
|
int i = 0;
|
|
for (i = 1; i <= max_tabs; i++) {
|
|
StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
|
|
if (tab) {
|
|
char *display_name = _display_name(tab);
|
|
width += utf8_display_len(display_name);
|
|
width += 2;
|
|
free(display_name);
|
|
}
|
|
}
|
|
return width;
|
|
}
|
|
|
|
if (g_hash_table_size(statusbar->tabs) > max_tabs) {
|
|
return max_tabs * 3 + (g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1);
|
|
}
|
|
return g_hash_table_size(statusbar->tabs) * 3 + (g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1);
|
|
}
|
|
|
|
static char*
|
|
_display_name(StatusBarTab *tab)
|
|
{
|
|
char *fullname = NULL;
|
|
|
|
if (tab->window_type == WIN_CONSOLE) {
|
|
fullname = strdup("console");
|
|
} else if (tab->window_type == WIN_XML) {
|
|
fullname = strdup("xmlconsole");
|
|
} else if (tab->window_type == WIN_PLUGIN) {
|
|
fullname = strdup(tab->identifier);
|
|
} else if (tab->window_type == WIN_CHAT) {
|
|
if (tab && tab->display_name) {
|
|
fullname = strdup(tab->display_name);
|
|
}
|
|
} else if (tab->window_type == WIN_MUC) {
|
|
char *pref = prefs_get_string(PREF_STATUSBAR_ROOM);
|
|
if (g_strcmp0("room", pref) == 0) {
|
|
Jid *jidp = jid_create(tab->identifier);
|
|
char *room = strdup(jidp->localpart);
|
|
jid_destroy(jidp);
|
|
fullname = room;
|
|
} else {
|
|
fullname = strdup(tab->identifier);
|
|
}
|
|
prefs_free_string(pref);
|
|
} else if (tab->window_type == WIN_CONFIG) {
|
|
char *pref = prefs_get_string(PREF_STATUSBAR_ROOM);
|
|
GString *display_str = g_string_new("");
|
|
|
|
if (g_strcmp0("room", pref) == 0) {
|
|
Jid *jidp = jid_create(tab->identifier);
|
|
g_string_append(display_str, jidp->localpart);
|
|
jid_destroy(jidp);
|
|
} else {
|
|
g_string_append(display_str, tab->identifier);
|
|
}
|
|
|
|
prefs_free_string(pref);
|
|
g_string_append(display_str, " conf");
|
|
char *result = strdup(display_str->str);
|
|
g_string_free(display_str, TRUE);
|
|
fullname = result;
|
|
} else if (tab->window_type == WIN_PRIVATE) {
|
|
char *pref = prefs_get_string(PREF_STATUSBAR_ROOM);
|
|
if (g_strcmp0("room", pref) == 0) {
|
|
GString *display_str = g_string_new("");
|
|
Jid *jidp = jid_create(tab->identifier);
|
|
g_string_append(display_str, jidp->localpart);
|
|
g_string_append(display_str, "/");
|
|
g_string_append(display_str, jidp->resourcepart);
|
|
jid_destroy(jidp);
|
|
char *result = strdup(display_str->str);
|
|
g_string_free(display_str, TRUE);
|
|
fullname = result;
|
|
} else {
|
|
fullname = strdup(tab->identifier);
|
|
}
|
|
prefs_free_string(pref);
|
|
} else {
|
|
fullname = strdup("window");
|
|
}
|
|
|
|
gint tablen = prefs_get_statusbartablen();
|
|
if (tablen == 0) {
|
|
return fullname;
|
|
}
|
|
|
|
int namelen = utf8_display_len(fullname);
|
|
if (namelen < tablen) {
|
|
return fullname;
|
|
}
|
|
|
|
gchar *trimmed = g_utf8_substring(fullname, 0, tablen);
|
|
free(fullname);
|
|
char *trimmedname = strdup(trimmed);
|
|
g_free(trimmed);
|
|
|
|
return trimmedname;
|
|
}
|