1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-09-22 19:45:54 -04:00
profanity/src/ui/statusbar.c
Michael Vetter 19de066008 Call ncurses resize function before move function
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
2019-12-02 14:03:53 +01:00

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;
}