mc/src/widget.c

2522 lines
52 KiB
C
Raw Normal View History

1998-02-27 07:54:42 +03:00
/* Widgets for the Midnight Commander
Copyright (C) 1994, 1995, 1996 the Free Software Foundation
Authors: 1994, 1995 Radek Doulik
1994, 1995 Miguel de Icaza
1995 Jakub Jelinek
1996 Andrej Borsenkow
1997 Norbert Warmuth
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.
1998-02-27 07:54:42 +03:00
*/
#include <config.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
1998-02-27 07:54:42 +03:00
#include <ctype.h>
#include "global.h"
#include "tty.h"
1998-02-27 07:54:42 +03:00
#include "color.h"
#include "mouse.h"
#include "dlg.h"
#include "widget.h"
#include "win.h"
#include "complete.h"
#include "key.h" /* XCTRL and ALT macros */
#include "profile.h" /* for history loading and saving */
#ifndef HAVE_X
# define x_create_button(a,b,c) 1
# define x_create_radio(a,b,c) 1
# define x_create_check(a,b,c) 1
# define x_create_label(a,b,c) 1
# define x_create_input(a,b,c) 1
# define x_create_listbox(a,b,c) 1
# define x_create_buttonbar(a,b,c) 1
# define x_create_gauge(a,b,c) 1
# define x_listbox_select_nth(a,b)
# define x_list_insert(a,b,c)
# define x_redefine_label(a,b)
#endif
#ifndef PORT_HAS_DESTROY_CMD
1998-02-27 07:54:42 +03:00
# define x_destroy_cmd(w)
#endif
#ifndef PORT_HAS_RADIO_FOCUS_ITEM
# define x_radio_focus_item(r)
#endif
#ifndef PORT_HAS_RADIO_TOGGLE
# define x_radio_toggle(r)
#endif
1998-02-27 07:54:42 +03:00
static int button_event (Gpm_Event *event, WButton *b);
int quote = 0;
static int
button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
{
#ifndef HAVE_X
2000-04-18 12:58:42 +04:00
char buf[BUF_SMALL];
#endif
1998-02-27 07:54:42 +03:00
int stop = 0;
int off = 0;
switch (Msg){
case WIDGET_INIT:
return x_create_button (h, h->wdata, b);
1998-02-27 07:54:42 +03:00
case WIDGET_HOTKEY:
if (b->hotkey == Par || toupper(b->hotkey) == Par){
1998-02-27 07:54:42 +03:00
button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
return 1;
} else
return 0;
case WIDGET_KEY:
if (Par != ' ' && Par != '\n')
break;
if (b->callback)
stop = (*b->callback)(b->action, b->callback_data);
if (!b->callback || stop){
h->ret_value = b->action;
dlg_stop (h);
1998-02-27 07:54:42 +03:00
}
return 1;
case WIDGET_CURSOR:
switch (b->flags) {
case DEFPUSH_BUTTON:
off = 3;
break;
case NORMAL_BUTTON:
off = 2;
break;
case NARROW_BUTTON:
off = 1;
break;
case HIDDEN_BUTTON:
default:
off = 0;
break;
}
widget_move (&b->widget, 0, b->hotpos + off);
1998-02-27 07:54:42 +03:00
return 1;
case WIDGET_UNFOCUS:
case WIDGET_FOCUS:
case WIDGET_DRAW:
#ifndef HAVE_X
1998-02-27 07:54:42 +03:00
if (Msg==WIDGET_UNFOCUS)
b->selected = 0;
else if (Msg==WIDGET_FOCUS)
b->selected = 1;
switch (b->flags){
case DEFPUSH_BUTTON:
g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
1998-02-27 07:54:42 +03:00
off = 3;
break;
case NORMAL_BUTTON:
g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
1998-02-27 07:54:42 +03:00
off = 2;
break;
case NARROW_BUTTON:
g_snprintf (buf, sizeof(buf), "[%s]", b->text);
1998-02-27 07:54:42 +03:00
off = 1;
break;
case HIDDEN_BUTTON:
default:
buf[0] = '\0';
off = 0;
break;
}
attrset ((b->selected) ? FOCUSC : NORMALC);
widget_move (&b->widget, 0, 0);
2000-04-18 12:58:42 +04:00
addstr (buf);
1998-02-27 07:54:42 +03:00
if (b->hotpos >= 0){
attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
widget_move (&b->widget, 0, b->hotpos+off);
addch ((unsigned char)b->text [b->hotpos]);
1998-02-27 07:54:42 +03:00
}
#endif /* HAVE_X */
if (Msg == WIDGET_FOCUS)
break;
else
return 1;
break;
1998-02-27 07:54:42 +03:00
}
return default_proc (h, Msg, Par);
}
static int
button_event (Gpm_Event *event, WButton *b)
{
#ifndef HAVE_X
if (event->type & (GPM_DOWN|GPM_UP)){
Dlg_head *h=b->widget.parent;
dlg_select_widget (h, b);
if (event->type & GPM_UP){
button_callback (h, b, WIDGET_KEY, ' ');
(*h->callback) (h, ' ', DLG_POST_KEY);
return MOU_NORMAL;
}
}
#endif
return MOU_NORMAL;
}
static void
button_destroy (WButton *b)
{
x_destroy_cmd (b);
g_free (b->text);
1998-02-27 07:54:42 +03:00
}
static int
button_len (const char *text, unsigned int flags)
{
#ifndef HAVE_X
int ret = strlen (text);
switch (flags){
case DEFPUSH_BUTTON:
ret += 6;
break;
case NORMAL_BUTTON:
ret += 4;
break;
case NARROW_BUTTON:
ret += 2;
break;
case HIDDEN_BUTTON:
default:
return 0;
}
return ret;
#else
return strlen (text);
#endif
}
/*
* Assuming that button text is malloc'ed, we may safely change it
* (as opposed to statically allocated); from other hand, excluding &
* and shifting data past it to the left results to one unused byte.
* This does not harm though :)
*/
static void
button_scan_hotkey(WButton* b)
{
1998-04-16 08:27:44 +04:00
char* cp = strchr (b->text, '&');
if (cp != NULL && cp[1] != '\0'){
strcpy (cp, cp+1);
b->hotkey = tolower (*cp);
b->hotpos = cp - b->text;
}
}
1998-02-27 07:54:42 +03:00
WButton *
button_new (int y, int x, int action, int flags, char *text,
int (*callback)(int, void *), void *callback_data, char *tkname)
{
WButton *b = g_new (WButton, 1);
1998-02-27 07:54:42 +03:00
init_widget (&b->widget, y, x, 1, button_len (text, flags),
(callback_fn) button_callback,
(destroy_fn) button_destroy, (mouse_h)button_event, tkname);
b->action = action;
b->flags = flags;
b->selected = 0;
b->text = g_strdup (text);
1998-02-27 07:54:42 +03:00
b->callback = callback;
b->callback_data = callback_data;
widget_want_hotkey (b->widget, 1);
b->hotkey = 0;
b->hotpos = -1;
button_scan_hotkey(b);
1998-02-27 07:54:42 +03:00
return b;
}
void
button_set_text (WButton *b, char *text)
{
g_free (b->text);
b->text = g_strdup (text);
1998-02-27 07:54:42 +03:00
b->widget.cols = button_len (text, b->flags);
button_scan_hotkey(b);
1998-02-27 07:54:42 +03:00
#ifdef HAVE_X
x_button_set (b, b->text);
1998-02-27 07:54:42 +03:00
#else
dlg_redraw (b->widget.parent);
#endif
}
/* Radio button widget */
static int radio_event (Gpm_Event *event, WRadio *r);
static int
radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
{
#ifndef HAVE_X
1998-02-27 07:54:42 +03:00
int i;
#endif
1998-02-27 07:54:42 +03:00
switch (Msg) {
case WIDGET_INIT:
return x_create_radio (h, h->wdata, r);
case WIDGET_HOTKEY:
{
1998-04-16 08:27:44 +04:00
int i, lp = tolower(Par);
char *cp;
for (i = 0; i < r->count; i++){
cp = strchr (r->texts [i], '&');
if (cp != NULL && cp[1] != '\0'){
int c = tolower (cp [1]);
if (c != lp)
continue;
r->pos = i;
radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
return 1;
}
1998-02-27 07:54:42 +03:00
}
}
return 0;
case WIDGET_KEY:
switch (Par){
case ' ':
r->sel = r->pos;
(*h->callback) (h, h->current->dlg_id, DLG_ACTION);
radio_callback (h, r, WIDGET_FOCUS, ' ');
1998-04-16 08:27:44 +04:00
x_radio_toggle (r);
1998-02-27 07:54:42 +03:00
return 1;
case KEY_UP:
case KEY_LEFT:
if (r->pos > 0){
r->pos--;
x_radio_focus_item (r);
1998-02-27 07:54:42 +03:00
return 1;
}
return 0;
case KEY_DOWN:
case KEY_RIGHT:
if (r->count - 1 > r->pos) {
r->pos++;
x_radio_focus_item (r);
1998-02-27 07:54:42 +03:00
return 1;
}
}
return 0;
#ifdef HAVE_X
1998-02-27 07:54:42 +03:00
case WIDGET_FOCUS:
case WIDGET_CURSOR:
x_radio_focus_item (r);
return 1;
1998-02-27 07:54:42 +03:00
#endif
#ifndef HAVE_X
case WIDGET_CURSOR:
(*h->callback) (h, h->current->dlg_id, DLG_ACTION);
radio_callback (h, r, WIDGET_FOCUS, ' ');
widget_move (&r->widget, r->pos, 1);
break;
case WIDGET_UNFOCUS:
case WIDGET_FOCUS:
case WIDGET_DRAW:
for (i = 0; i < r->count; i++){
register unsigned char* cp;
1998-02-27 07:54:42 +03:00
attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
widget_move (&r->widget, i, 0);
printw("(%c) ", (r->sel == i) ? '*' : ' ');
for (cp = r->texts[i]; *cp; cp++)
{
if (*cp == '&')
{
attrset ((i==r->pos && Msg==WIDGET_FOCUS)
? HOT_FOCUSC : HOT_NORMALC);
addch(*++cp);
attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
}
else
addch(*cp);
1998-02-27 07:54:42 +03:00
}
}
return 1;
break;
#endif
}
return default_proc (h, Msg, Par);
}
static int
radio_event (Gpm_Event *event, WRadio *r)
{
#ifndef HAVE_X
if (event->type & (GPM_DOWN|GPM_UP)){
Dlg_head *h = r->widget.parent;
r->pos = event->y - 1;
dlg_select_widget (h, r);
if (event->type & GPM_UP){
radio_callback (h, r, WIDGET_KEY, ' ');
radio_callback (h, r, WIDGET_FOCUS, 0);
(*h->callback) (h, ' ', DLG_POST_KEY);
return MOU_NORMAL;
}
}
#endif
return MOU_NORMAL;
}
WRadio *
radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
{
WRadio *r = g_new (WRadio, 1);
1998-02-27 07:54:42 +03:00
int i, max, m;
/* Compute the longest string */
max = 0;
for (i = 0; i < count; i++){
m = strlen (texts [i]);
if (m > max)
max = m;
}
init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
0, (mouse_h) radio_event, tkname);
1998-02-27 07:54:42 +03:00
r->state = 1;
r->pos = 0;
r->sel = 0;
r->count = count;
r->texts = texts;
r->upper_letter_is_hotkey = use_hotkey;
widget_want_hotkey (r->widget, 1);
return r;
}
/* Checkbutton widget */
static int check_event (Gpm_Event *event, WCheck *b);
static int
check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
{
switch (Msg) {
case WIDGET_INIT:
return x_create_check (h, h->wdata, c);
case WIDGET_HOTKEY:
if (c->hotkey==Par ||
(c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
check_callback (h, c, WIDGET_KEY, ' '); /* make action */
return 1;
} else
return 0;
case WIDGET_KEY:
if (Par != ' ')
break;
c->state ^= C_BOOL;
c->state ^= C_CHANGE;
(*h->callback) (h, h->current->dlg_id, DLG_ACTION);
check_callback (h, c, WIDGET_FOCUS, ' ');
return 1;
#ifndef HAVE_X
case WIDGET_CURSOR:
widget_move (&c->widget, 0, 1);
break;
case WIDGET_FOCUS:
case WIDGET_UNFOCUS:
case WIDGET_DRAW:
attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
widget_move (&c->widget, 0, 0);
printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
if (c->hotpos >= 0){
attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
widget_move (&c->widget, 0, + c->hotpos+4);
addch ((unsigned char)c->text [c->hotpos]);
1998-02-27 07:54:42 +03:00
}
return 1;
#endif /* !HAVE_X */
}
return default_proc (h, Msg, Par);
}
static int
check_event (Gpm_Event *event, WCheck *c)
{
#ifndef HAVE_X
if (event->type & (GPM_DOWN|GPM_UP)){
Dlg_head *h = c->widget.parent;
dlg_select_widget (h, c);
if (event->type & GPM_UP){
check_callback (h, c, WIDGET_KEY, ' ');
check_callback (h, c, WIDGET_FOCUS, 0);
(*h->callback) (h, ' ', DLG_POST_KEY);
return MOU_NORMAL;
}
}
#endif
return MOU_NORMAL;
}
static void
check_destroy (WCheck *c)
{
x_destroy_cmd (c);
g_free (c->text);
1998-02-27 07:54:42 +03:00
}
WCheck *
check_new (int y, int x, int state, char *text, char *tkname)
{
WCheck *c = g_new (WCheck, 1);
1998-02-27 07:54:42 +03:00
char *s, *t;
init_widget (&c->widget, y, x, 1, strlen (text),
(callback_fn)check_callback,
(destroy_fn)check_destroy, (mouse_h) check_event, tkname);
c->state = state ? C_BOOL : 0;
c->text = g_strdup (text);
1998-02-27 07:54:42 +03:00
c->hotkey = 0;
c->hotpos = -1;
widget_want_hotkey (c->widget, 1);
/* Scan for the hotkey */
for (s = text, t = c->text; *s; s++, t++){
if (*s != '&'){
*t = *s;
continue;
}
s++;
if (*s){
c->hotkey = tolower (*s);
c->hotpos = t - c->text;
}
*t = *s;
}
*t = 0;
return c;
}
/* Label widget */
static int
label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
{
if (Msg == WIDGET_INIT)
return x_create_label (h, h->wdata, l);
/* We don't want to get the focus */
if (Msg == WIDGET_FOCUS)
return 0;
#ifndef HAVE_X
if (Msg == WIDGET_DRAW && l->text){
char *p = l->text, *q, c = 0;
int y = 0;
if (l->transparent)
attrset (DEFAULT_COLOR);
else
attrset (NORMALC);
for (;;){
int xlen;
q = strchr (p, '\n');
if (q){
c = *q;
*q = 0;
}
widget_move (&l->widget, y, 0);
printw ("%s", p);
xlen = l->widget.cols - strlen (p);
if (xlen > 0)
printw ("%*s", xlen, " ");
if (!q)
break;
*q = c;
p = q + 1;
y++;
}
return 1;
}
#endif
return default_proc (h, Msg, Par);
}
void
label_set_text (WLabel *label, char *text)
{
int newcols = label->widget.cols;
if (label->text && text && !strcmp (label->text, text))
return; /* Flickering is not nice */
if (label->text){
g_free (label->text);
1998-02-27 07:54:42 +03:00
}
if (text){
label->text = g_strdup (text);
1998-02-27 07:54:42 +03:00
if (label->auto_adjust_cols) {
newcols = strlen (text);
if (newcols > label->widget.cols)
label->widget.cols = newcols;
}
} else
label->text = 0;
if (label->widget.parent)
#ifdef HAVE_X
x_label_set_text (label, text);
#else
label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
#endif
if (newcols < label->widget.cols)
label->widget.cols = newcols;
}
static void
label_destroy (WLabel *l)
{
x_destroy_cmd (l);
if (l->text)
g_free (l->text);
1998-02-27 07:54:42 +03:00
}
WLabel *
label_new (int y, int x, const char *text, char *tkname)
1998-02-27 07:54:42 +03:00
{
WLabel *l = g_new (WLabel, 1);
1998-02-27 07:54:42 +03:00
init_widget (&l->widget, y, x, 1, text ? strlen (text) : 0,
1998-02-27 07:54:42 +03:00
(callback_fn) label_callback,
(destroy_fn) label_destroy, NULL, tkname);
l->text = text ? g_strdup (text) : 0;
1998-02-27 07:54:42 +03:00
l->auto_adjust_cols = 1;
l->transparent = 0;
widget_want_cursor (l->widget, 0);
return l;
}
/* Gauge widget (progress indicator) */
/* Currently width is hardcoded here for text mode */
#define gauge_len 47
static int
gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
{
if (Msg == WIDGET_INIT)
return x_create_gauge (h, h->wdata, g);
/* We don't want to get the focus */
if (Msg == WIDGET_FOCUS)
return 0;
#ifndef HAVE_X
if (Msg == WIDGET_DRAW){
widget_move (&g->widget, 0, 0);
attrset (NORMALC);
if (!g->shown)
printw ("%*s", gauge_len, "");
else {
long percentage, columns;
long total = g->max, done = g->current;
if (total <= 0 || done < 0) {
done = 0;
total = 100;
}
if (done > total)
done = total;
while (total > 65535) {
total /= 256;
done /= 256;
}
percentage = (200 * done / total + 1) / 2;
columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
addch ('[');
attrset (GAUGE_COLOR);
printw ("%*s", columns, "");
attrset (NORMALC);
printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
}
return 1;
}
#endif
return default_proc (h, Msg, Par);
}
void
gauge_set_value (WGauge *g, int max, int current)
{
if (g->current == current && g->max == max)
return; /* Do not flicker */
if (max == 0)
max = 1; /* I do not like division by zero :) */
#ifdef HAVE_X
/* NOTE: x_gauge_set_value has to be called before we change actual
* max and current values in g, since it assumes g->max and
* g->current as the previous values and max and current
* as the new ones :) */
x_gauge_set_value (g, max, current);
#endif
g->current = current;
g->max = max;
#ifndef HAVE_X
gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
#endif
}
void
gauge_show (WGauge *g, int shown)
{
if (g->shown == shown)
return;
g->shown = shown;
#ifdef HAVE_X
x_gauge_show (g);
#else
gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
#endif
}
static void
gauge_destroy (WGauge *g)
{
/* nothing */
}
WGauge *
gauge_new (int y, int x, int shown, int max, int current, char *tkname)
{
WGauge *g = g_new (WGauge, 1);
1998-02-27 07:54:42 +03:00
init_widget (&g->widget, y, x, 1, gauge_len,
(callback_fn) gauge_callback,
(destroy_fn) gauge_destroy, NULL, tkname);
g->shown = shown;
if (max == 0)
max = 1; /* I do not like division by zero :) */
g->max = max;
g->current = current;
g->pixels = 0;
widget_want_cursor (g->widget, 0);
return g;
}
/* Input widget */
/* {{{ history button */
#define LARGE_HISTORY_BUTTON 1
#ifdef LARGE_HISTORY_BUTTON
# define HISTORY_BUTTON_WIDTH 3
#else
# define HISTORY_BUTTON_WIDTH 1
#endif
#define should_show_history_button(in) \
(in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
#ifndef HAVE_X
1998-02-27 07:54:42 +03:00
static void draw_history_button (WInput * in)
{
char c;
c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
#ifdef LARGE_HISTORY_BUTTON
{
Dlg_head *h;
h = in->widget.parent;
#if 0
attrset (NORMALC); /* button has the same colour as other buttons */
addstr ("[ ]");
attrset (HOT_NORMALC);
#else
attrset (NORMAL_COLOR);
addstr ("[ ]");
/* Too distracting: attrset (MARKED_COLOR); */
#endif
widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
addch (c);
}
#else
attrset (MARKED_COLOR);
addch (c);
#endif
}
#endif
1998-02-27 07:54:42 +03:00
/* }}} history button */
/* Input widgets now have a global kill ring */
/* Pointer to killed data */
static char *kill_buffer = 0;
void
update_input (WInput *in, int clear_first)
1998-02-27 07:54:42 +03:00
{
int has_history = 0;
#ifndef HAVE_X
1998-02-27 07:54:42 +03:00
int i, j;
unsigned char c;
#endif
1998-02-27 07:54:42 +03:00
int buf_len = strlen (in->buffer);
if (should_show_history_button (in))
has_history = HISTORY_BUTTON_WIDTH;
if (in->disable_update)
return;
/* Make the point visible */
if ((in->point < in->first_shown) ||
(in->point >= in->first_shown+in->field_len - has_history)){
in->first_shown = in->point - (in->field_len / 3);
if (in->first_shown < 0)
in->first_shown = 0;
}
/* Adjust the mark */
if (in->mark > buf_len)
in->mark = buf_len;
1998-02-27 07:54:42 +03:00
#ifdef HAVE_X
if (clear_first && in->first)
in->first = -1;
1998-02-27 07:54:42 +03:00
x_update_input (in);
#else
if (has_history)
draw_history_button (in);
attrset (in->color);
widget_move (&in->widget, 0, 0);
for (i = 0; i < in->field_len - has_history; i++)
addch (' ');
widget_move (&in->widget, 0, 0);
for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
c = in->buffer [j++];
c = is_printable (c) ? c : '.';
if (in->is_password)
c = '*';
addch (c);
}
widget_move (&in->widget, 0, in->point - in->first_shown);
if (clear_first)
in->first = 0;
1998-02-27 07:54:42 +03:00
#endif
}
void
winput_set_origin (WInput *in, int x, int field_len)
{
in->widget.x = x;
in->field_len = in->widget.cols = field_len;
update_input (in, 0);
1998-02-27 07:54:42 +03:00
}
/* {{{ history saving and loading */
/*
This loads and saves the history of an input line to and from the
widget. It is called with the widgets tk name on creation of the
widget, and returns the Hist list. It stores histories in the file
~/.mc/history in using the profile code.
If def_text is passed as INPUT_LAST_TEXT (to the input_new()
function) then input_new assigns the default text to be the last text
entered, or "" if not found.
*/
int num_history_items_recorded = 60;
Hist *history_get (char *input_name)
{
int i;
Hist *old, *new;
1998-02-27 07:54:42 +03:00
char *profile;
old = new = NULL;
1998-02-27 07:54:42 +03:00
if (!num_history_items_recorded) /* this is how to disable */
return 0;
if (!input_name)
return 0;
if (!*input_name)
return 0;
profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
for (i = 0;; i++) {
char key_name[BUF_TINY];
char this_entry[BUF_LARGE];
g_snprintf (key_name, sizeof (key_name), "%d", i);
1998-02-27 07:54:42 +03:00
GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
if (!*this_entry)
break;
new = g_new0 (Hist, 1);
new->text = g_strdup (this_entry);
1998-02-27 07:54:42 +03:00
new->prev = old; /* set up list pointers */
if (old)
old->next = new;
old = new;
}
g_free (profile);
1998-02-27 07:54:42 +03:00
return new; /* return pointer to last entry in list */
}
void history_put (char *input_name, Hist *h)
{
2000-04-18 12:58:42 +04:00
#ifdef PORT_WIDGET_WANTS_HISTORY
1998-02-27 07:54:42 +03:00
int i;
char *profile;
if (!input_name)
return;
if (!*input_name)
return;
if (!h)
return;
if (!num_history_items_recorded) /* this is how to disable */
return;
profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
close (i);
/* Just in case I forgot to strip passwords somewhere -- Norbert */
2000-04-18 12:58:42 +04:00
if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
g_free (profile);
return;
2000-04-18 12:58:42 +04:00
}
1998-02-27 07:54:42 +03:00
while (h->next) /* go to end of list */
h = h->next;
/* go back 60 places */
for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
h = h->prev;
if (input_name)
profile_clean_section (input_name, profile);
/* dump histories into profile */
for (i = 0; h; h = h->next){
1998-02-27 07:54:42 +03:00
if (h->text){
/* probably aren't any null entries, but lets be sure */
if (*(h->text)){
char key_name[BUF_TINY];
g_snprintf (key_name, sizeof(key_name), "%d", i++);
1998-02-27 07:54:42 +03:00
WritePrivateProfileString (input_name, key_name, h->text, profile);
}
}
}
g_free (profile);
1998-05-15 22:52:27 +04:00
#endif
2000-04-18 12:58:42 +04:00
}
1998-02-27 07:54:42 +03:00
/* }}} history saving and loading */
/* {{{ history display */
static char *
i18n_htitle (void)
{
static char *history_title = NULL;
if (history_title == NULL)
history_title = _(" History ");
return history_title;
}
1998-02-27 07:54:42 +03:00
1998-12-03 00:27:27 +03:00
static int
history_callback (Dlg_head * h, int Par, int Msg)
1998-02-27 07:54:42 +03:00
{
#ifndef HAVE_X
switch (Msg) {
case DLG_DRAW:
attrset (COLOR_NORMAL);
dlg_erase (h);
draw_box (h, 0, 0, h->lines, h->cols);
attrset (COLOR_HOT_NORMAL);
dlg_move (h, 0, (h->cols - strlen (i18n_htitle())) / 2);
printw (i18n_htitle());
1998-02-27 07:54:42 +03:00
break;
}
#endif
return 0;
}
static inline int listbox_fwd (WListbox *l);
char *show_hist (Hist *history, int widget_x, int widget_y)
1998-02-27 07:54:42 +03:00
{
Hist *hi, *z;
size_t maxlen = strlen (i18n_htitle()), i, count = 0;
1998-02-27 07:54:42 +03:00
int x, y, w, h;
char *q, *r = 0;
1998-02-27 07:54:42 +03:00
Dlg_head *query_dlg;
WListbox *query_list;
z = history;
1998-02-27 07:54:42 +03:00
if (!z)
return 0;
1998-02-27 07:54:42 +03:00
while (z->prev) /* goto first */
z = z->prev;
hi = z;
while (hi) {
if ((i = strlen (hi->text)) > maxlen)
maxlen = i;
count++;
hi = hi->next;
}
y = widget_y;
1998-02-27 07:54:42 +03:00
h = count + 2;
if (h <= y || y > LINES - 6)
{
h = min(h, y - 1);
y -= h;
}
else
{
y++;
h = min(h, LINES - y);
}
if (widget_x > 2)
x = widget_x - 2;
else
x = 0;
1998-02-27 07:54:42 +03:00
if ((w = maxlen + 4) + x > COLS)
{
w = min(w,COLS);
x = COLS - w;
}
query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
"[History-query]", "history", DLG_NONE);
query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
add_widget (query_dlg, query_list);
hi = z;
if (y < widget_y) {
1998-02-27 07:54:42 +03:00
while (hi) { /* traverse */
listbox_add_item (query_list, 0, 0, hi->text, NULL);
hi = hi->next;
}
while (listbox_fwd (query_list));
} else {
while (hi->next)
hi = hi->next;
while (hi) { /* traverse backwards */
listbox_add_item (query_list, 0, 0, hi->text, NULL);
hi = hi->prev;
}
}
run_dlg (query_dlg);
q = NULL;
if (query_dlg->ret_value != B_CANCEL) {
listbox_get_current (query_list, &q, NULL);
if (q)
r = g_strdup (q);
1998-02-27 07:54:42 +03:00
}
destroy_dlg (query_dlg);
return r;
1998-02-27 07:54:42 +03:00
}
static void do_show_hist (WInput * in)
{
char *r;
r = show_hist (in->history, in->widget.x, in->widget.y);
if (r) {
assign_text (in, r);
g_free (r);
}
}
1998-02-27 07:54:42 +03:00
/* }}} history display */
static void
input_destroy (WInput *in)
{
if (!in){
fprintf (stderr, "Internal error: null Input *\n");
exit (1);
}
new_input (in);
if (in->history){
Hist *current, *old;
1998-05-15 22:52:27 +04:00
if (!in->is_password && PORT_WIDGET_WANTS_HISTORY) /* don't save passwords ;-) */
1998-02-27 07:54:42 +03:00
history_put (in->history_name, in->history);
current = in->history;
while (current->next)
current = current->next;
while (current){
old = current;
current = current->prev;
g_free (old->text);
g_free (old);
1998-02-27 07:54:42 +03:00
}
}
x_destroy_cmd (in);
g_free (in->buffer);
1998-02-27 07:54:42 +03:00
free_completions (in);
if (in->history_name)
g_free (in->history_name);
1998-02-27 07:54:42 +03:00
}
static char disable_update = 0;
void
input_disable_update (WInput *in)
{
in->disable_update++;
}
void
input_enable_update (WInput *in)
{
in->disable_update--;
update_input (in, 0);
1998-02-27 07:54:42 +03:00
}
#define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1998-02-27 07:54:42 +03:00
int
push_history (WInput *in, char *text)
{
static int i18n;
/* input widget where urls with passwords are entered without any
vfs prefix */
static const char *password_input_fields[] = {
2000-04-18 12:58:42 +04:00
N_(" Link to a remote machine "),
N_(" FTP to machine "),
N_(" SMB link to machine ")
};
1998-02-27 07:54:42 +03:00
Hist *new;
char *p;
int i;
if (!i18n) {
i18n = 1;
for (i = 0; i < ELEMENTS(password_input_fields); i++)
password_input_fields[i] = _(password_input_fields[i]);
}
1998-02-27 07:54:42 +03:00
for (p = text; *p == ' ' || *p == '\t'; p++);
if (!*p)
return 0;
if (in->history){
while (in->history->next)
in->history = in->history->next;
if (!strcmp (in->history->text, text))
return 1;
new = g_new (Hist, 1);
1998-02-27 07:54:42 +03:00
in->history->next = new;
} else
new = g_new (Hist, 1);
1998-02-27 07:54:42 +03:00
in->need_push = 0;
new->next = 0;
new->prev = in->history;
new->text = g_strdup (text);
if (in->history_name) {
p = in->history_name + 3;
for (i = 0; i < ELEMENTS(password_input_fields); i++)
if (strcmp (p, password_input_fields[i]) == 0)
break;
if (i < ELEMENTS(password_input_fields))
strip_password (new->text, 0);
else
strip_password (new->text, 1);
}
1998-02-27 07:54:42 +03:00
in->history = new;
return 2;
}
#undef ELEMENTS
1998-02-27 07:54:42 +03:00
/* Cleans the input line and adds the current text to the history */
void
new_input (WInput *in)
{
if (in->buffer)
push_history (in, in->buffer);
in->need_push = 1;
in->buffer [0] = 0;
in->point = 0;
in->mark = 0;
free_completions (in);
update_input (in, 0);
1998-02-27 07:54:42 +03:00
}
static int
insert_char (WInput *in, int c_code)
{
int i;
if (c_code == -1)
return 0;
in->need_push = 1;
if (strlen (in->buffer)+1 == in->current_max_len){
/* Expand the buffer */
char *narea = g_malloc (in->current_max_len + in->field_len);
1998-02-27 07:54:42 +03:00
if (narea){
char *p = in->buffer;
strcpy (narea, in->buffer);
in->buffer = narea;
in->current_max_len += in->field_len;
g_free (p);
1998-02-27 07:54:42 +03:00
}
}
if (strlen (in->buffer)+1 < in->current_max_len){
int l = strlen (&in->buffer [in->point]);
for (i = l+1; i > 0; i--)
in->buffer [in->point+i] = in->buffer [in->point+i-1];
in->buffer [in->point] = c_code;
in->point++;
}
return 1;
}
static void
beginning_of_line (WInput *in)
{
in->point = 0;
}
static void
end_of_line (WInput *in)
{
in->point = strlen (in->buffer);
}
static void
backward_char (WInput *in)
{
if (in->point)
in->point--;
}
static void
forward_char (WInput *in)
{
if (in->buffer [in->point])
in->point++;
}
static void
forward_word (WInput *in)
{
unsigned char *p = in->buffer+in->point;
while (*p && (isspace (*p) || ispunct (*p)))
p++;
while (*p && isalnum (*p))
p++;
1998-02-27 07:54:42 +03:00
in->point = p - in->buffer;
}
static void
backward_word (WInput *in)
{
unsigned char *p = in->buffer+in->point;
1998-02-27 07:54:42 +03:00
while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
p--;
while (p-1 > in->buffer-1 && isalnum (*(p-1)))
p--;
1998-02-27 07:54:42 +03:00
in->point = p - in->buffer;
}
#ifndef HAVE_X
1998-02-27 07:54:42 +03:00
static void
key_left (WInput *in)
{
if (ctrl_pressed ())
backward_word (in);
else
backward_char (in);
}
static void
key_right (WInput *in)
{
if (ctrl_pressed ())
forward_word (in);
else
forward_char (in);
}
#else
#define key_left backward_char
#define key_right forward_char
#endif /* HAVE_X */
1998-02-27 07:54:42 +03:00
static void
backward_delete (WInput *in)
{
int i;
if (!in->point)
return;
for (i = in->point; in->buffer [i-1]; i++)
in->buffer [i-1] = in->buffer [i];
in->need_push = 1;
in->point--;
}
static void
delete_char (WInput *in)
{
int i;
for (i = in->point; in->buffer [i]; i++)
in->buffer [i] = in->buffer [i+1];
in->need_push = 1;
}
static void
copy_region (WInput *in, int x_first, int x_last)
{
int first = min (x_first, x_last);
int last = max (x_first, x_last);
if (last == first)
return;
if (kill_buffer)
g_free (kill_buffer);
1998-02-27 07:54:42 +03:00
kill_buffer = g_malloc (last-first + 1);
1998-02-27 07:54:42 +03:00
strncpy (kill_buffer, in->buffer+first, last-first);
kill_buffer [last-first] = 0;
}
static void
delete_region (WInput *in, int x_first, int x_last)
{
int first = min (x_first, x_last);
int last = max (x_first, x_last);
in->point = first;
in->mark = first;
strcpy (&in->buffer [first], &in->buffer [last]);
in->need_push = 1;
}
static void
kill_word (WInput *in)
{
int old_point = in->point;
int new_point;
forward_word (in);
new_point = in->point;
in->point = old_point;
copy_region (in, old_point, new_point);
delete_region (in, old_point, new_point);
in->need_push = 1;
}
static void
back_kill_word (WInput *in)
{
int old_point = in->point;
int new_point;
backward_word (in);
new_point = in->point;
in->point = old_point;
copy_region (in, old_point, new_point);
delete_region (in, old_point, new_point);
in->need_push = 1;
}
static void
set_mark (WInput *in)
{
in->mark = in->point;
}
static void
kill_save (WInput *in)
{
copy_region (in, in->mark, in->point);
}
static void
kill_region (WInput *in)
{
kill_save (in);
delete_region (in, in->point, in->mark);
}
static void
yank (WInput *in)
{
char *p;
if (!kill_buffer)
return;
for (p = kill_buffer; *p; p++)
insert_char (in, *p);
}
static void
kill_line (WInput *in)
{
if (kill_buffer)
g_free (kill_buffer);
kill_buffer = g_strdup (&in->buffer [in->point]);
1998-02-27 07:54:42 +03:00
in->buffer [in->point] = 0;
}
void
assign_text (WInput *in, char *text)
{
free_completions (in);
g_free (in->buffer);
in->buffer = g_strdup (text); /* was in->buffer->text */
1998-02-27 07:54:42 +03:00
in->current_max_len = strlen (in->buffer) + 1;
in->point = strlen (in->buffer);
in->mark = 0;
in->need_push = 1;
}
static void
hist_prev (WInput *in)
{
if (!in->history)
return;
if (in->need_push) {
switch (push_history (in, in->buffer)) {
case 2: in->history = in->history->prev; break;
case 1: if (in->history->prev) in->history = in->history->prev; break;
case 0: break;
}
} else if (in->history->prev)
in->history = in->history->prev;
else
return;
assign_text (in, in->history->text);
in->need_push = 0;
}
static void
hist_next (WInput *in)
{
if (in->need_push) {
switch (push_history (in, in->buffer)) {
case 2:
assign_text (in, "");
return;
case 0:
return;
}
}
if (!in->history)
return;
if (!in->history->next) {
assign_text (in, "");
return;
}
in->history = in->history->next;
assign_text (in, in->history->text);
in->need_push = 0;
}
2000-04-18 12:58:42 +04:00
static const struct {
1998-02-27 07:54:42 +03:00
int key_code;
void (*fn)(WInput *in);
} input_map [] = {
/* Motion */
{ XCTRL('a'), beginning_of_line },
{ KEY_HOME, beginning_of_line },
{ KEY_A1, beginning_of_line },
{ XCTRL('e'), end_of_line },
{ KEY_END, end_of_line },
{ KEY_C1, end_of_line },
{ KEY_LEFT, key_left },
{ XCTRL('b'), backward_char },
{ ALT('b'), backward_word },
{ KEY_RIGHT, key_right },
{ XCTRL('f'), forward_char },
{ ALT('f'), forward_word },
/* Editing */
{ 0177, backward_delete },
{ KEY_BACKSPACE, backward_delete },
{ XCTRL('h'), backward_delete },
{ KEY_DC, delete_char },
{ XCTRL('d'), delete_char },
{ ALT('d'), kill_word },
1998-02-27 07:54:42 +03:00
{ ALT(KEY_BACKSPACE), back_kill_word },
{ ALT(XCTRL('h')), back_kill_word },
{ ALT(127), back_kill_word },
1998-02-27 07:54:42 +03:00
/* Region manipulation */
{ 0, set_mark },
{ XCTRL('w'), kill_region },
{ ALT('w'), kill_save },
{ XCTRL('y'), yank },
{ XCTRL('k'), kill_line },
/* History */
{ ALT('p'), hist_prev },
{ ALT('n'), hist_next },
{ ALT('h'), do_show_hist },
1998-02-27 07:54:42 +03:00
/* Completion */
{ ALT('\t'), complete },
{ 0, 0 }
};
/* This function is a test for a special input key used in complete.c */
/* Returns 0 if it is not a special key, 1 if it is a non-complete key
and 2 if it is a complete key */
int
is_in_input_map (WInput *in, int c_code)
{
int i;
for (i = 0; input_map [i].fn; i++)
if (c_code == input_map [i].key_code) {
1998-02-27 07:54:42 +03:00
if (input_map [i].fn == complete)
return 2;
else
return 1;
}
1998-02-27 07:54:42 +03:00
return 0;
}
#ifdef PORT_WINPUT_DELETES_MARKED
static void
port_region_marked_for_delete (WInput *in)
{
if (in->first == 1 && (in->point == in->mark))
in->point = strlen (in->buffer);
kill_region (in);
in->first = 0;
}
#else
static void
port_region_marked_for_delete (WInput *in)
{
*in->buffer = 0;
in->point = 0;
in->first = 0;
}
#endif
1998-02-27 07:54:42 +03:00
int
handle_char (WInput *in, int c_code)
{
int i;
int v;
v = 0;
if (quote){
free_completions (in);
v = insert_char (in, c_code);
update_input (in, 1);
1998-02-27 07:54:42 +03:00
quote = 0;
return v;
}
for (i = 0; input_map [i].fn; i++){
if (c_code == input_map [i].key_code){
if (input_map [i].fn != complete)
free_completions (in);
(*input_map [i].fn)(in);
v = 1;
break;
}
}
if (!input_map [i].fn){
if (c_code > 255 || !is_printable (c_code))
return 0;
if (in->first){
port_region_marked_for_delete (in);
1998-02-27 07:54:42 +03:00
}
free_completions (in);
v = insert_char (in, c_code);
in->inserted_one = c_code;
}
if (!disable_update)
update_input (in, 1);
1998-02-27 07:54:42 +03:00
return v;
}
/* Inserts text in input line */
void
stuff (WInput *in, char *text, int insert_extra_space)
{
input_disable_update (in);
while (*text)
handle_char (in, *text++);
if (insert_extra_space)
handle_char (in, ' ');
input_enable_update (in);
update_input (in, 1);
1998-02-27 07:54:42 +03:00
}
void
input_set_point (WInput *in, int pos)
{
if (pos > in->current_max_len)
pos = in->current_max_len;
if (pos != in->point)
free_completions (in);
in->point = pos;
update_input (in, 1);
1998-02-27 07:54:42 +03:00
}
int input_event (Gpm_Event *event, WInput *b);
static int
input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
{
switch (Msg){
case WIDGET_INIT:
return x_create_input (h, h->wdata, in);
case WIDGET_KEY:
#ifndef HAVE_X
1998-02-27 07:54:42 +03:00
if (Par == XCTRL('q')){
int v;
quote = 1;
v = handle_char (in, mi_getch ());
quote = 0;
return v;
}
#endif /* !HAVE_X */
1998-02-27 07:54:42 +03:00
if (Par == KEY_UP || Par == KEY_DOWN ||
Par == ESC_CHAR || Par == KEY_F(10) ||
Par == XCTRL('g'))
return 0; /* We don't handle up/down */
if (Par == '\n'){
dlg_one_down (h);
return 1;
}
return handle_char (in, Par);
case WIDGET_FOCUS:
case WIDGET_UNFOCUS:
case WIDGET_DRAW:
update_input (in, 0);
1998-02-27 07:54:42 +03:00
break;
#ifndef HAVE_X
case WIDGET_CURSOR:
widget_move (&in->widget, 0, in->point - in->first_shown);
return 1;
#endif /* !HAVE_X */
1998-02-27 07:54:42 +03:00
}
return default_proc (h, Msg, Par);
}
/* Not declared static, since we check against this value in dlg.c */
/* FIXME: Declare static again and provide an identification mechanism */
int
input_event (Gpm_Event *event, WInput *in)
{
#ifndef HAVE_X
if (event->type & (GPM_DOWN|GPM_DRAG)){
dlg_select_widget (in->widget.parent, in);
if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) {
do_show_hist (in);
1998-02-27 07:54:42 +03:00
} else {
in->point = strlen (in->buffer);
if (event->x - in->first_shown - 1 < in->point)
in->point = event->x - in->first_shown - 1;
if (in->point < 0)
in->point = 0;
}
update_input (in, 1);
1998-02-27 07:54:42 +03:00
}
#endif
return MOU_NORMAL;
}
1998-02-27 07:54:42 +03:00
WInput *
This commit mainly adds a password dialog to smbfs. In the Gnome Edition this dialog doesn't look perfect but at least it doesn't break gmc. 1999-09-14 Norbert Warmuth <nwarmuth@privat.circular.de> * gnome/layout: Added the new samba password dialog. * src/widget.[ch] (input_new): added const qualifier * src/util.c (wipe_password): Check for NULL. 1999-09-14 Norbert Warmuth <nwarmuth@privat.circular.de> * vfs/smbfs.c (various places): Store filenames in memory with the unix character set and convert it back to the dos character set when filenames are passed back to the samba server. If samba is configured correctly this will fix the problems with filenames which contain national characters. (bucket_set_authinfo): Set domain, username and password which will be used to log on and authenticate against the samba server and remember this information on a host/share basis. If set use PASSWD to get the password. If already authenticated against the host and share use this information. If already authenticated against host and IPC$ use this information. If neither of the former is true ask the user how to log on. (authinfo_get_authinfo_from_user): New function. Query the user for domain, username and password to use for authentication. (authinfo_free): New function. Free memory in authentication structure. (authinfo_free_all): New function. Free the list used to remember authentication information. (authinfo_compare_host_and_share, authinfo_compare_host): New functions. Helper functions used to search the authentication list. (authinfo_add): New function. Add one authentication entry to the authlist. (authinfo_remove): New function. Remove one entry from the authlist. (free_bucket): Free memory associated with one bucket. (smbfs_get_free_bucket): Initialize the complete connection array with 0. Free the memory allocated for a bucket and initialize the bucket before reusing it. (smbfs_open_link): use new functions to get authentication information (get_stat_info, smbfs_stat): remove unused variables.
1999-09-14 22:20:02 +04:00
input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1998-02-27 07:54:42 +03:00
{
WInput *in = g_new (WInput, 1);
1998-02-27 07:54:42 +03:00
int initial_buffer_len;
init_widget (&in->widget, y, x, 1, len,
(callback_fn) input_callback,
(destroy_fn) input_destroy, (mouse_h) input_event, tkname);
/* history setup */
in->history = NULL;
in->history_name = 0;
if (tkname && PORT_WIDGET_WANTS_HISTORY){
1998-02-27 07:54:42 +03:00
if (*tkname) {
in->history_name = g_strdup (tkname);
1998-02-27 07:54:42 +03:00
in->history = history_get (tkname);
}
}
if (!def_text)
2000-04-18 12:58:42 +04:00
def_text = "";
1998-02-27 07:54:42 +03:00
if (def_text == INPUT_LAST_TEXT) {
def_text = "";
if (in->history)
if (in->history->text)
def_text = in->history->text;
}
initial_buffer_len = 1 + max (len, strlen (def_text));
in->widget.options |= W_IS_INPUT;
in->completions = NULL;
in->completion_flags =
INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
in->current_max_len = initial_buffer_len;
in->buffer = g_malloc (initial_buffer_len);
1998-02-27 07:54:42 +03:00
in->color = color;
in->field_len = len;
in->first = 1;
in->first_shown = 0;
in->disable_update = 0;
in->mark = 0;
in->need_push = 1;
in->is_password = 0;
strcpy (in->buffer, def_text);
in->point = strlen (in->buffer);
return in;
}
/* Listbox widget */
/* Should draw the scrollbar, but currently draws only
* indications that there is more information
*/
static int listbox_cdiff (WLEntry *s, WLEntry *e);
#ifdef HAVE_X
static void
listbox_draw (WListbox *l, Dlg_head *h, int focused)
{
/* nothing */
}
#else
1998-02-27 07:54:42 +03:00
static void
listbox_drawscroll (WListbox *l)
{
extern int slow_terminal;
int line;
int i, top;
int max_line = l->height-1;
/* Are we at the top? */
widget_move (&l->widget, 0, l->width);
if (l->list == l->top)
one_vline ();
else
addch ('^');
/* Are we at the bottom? */
widget_move (&l->widget, max_line, l->width);
top = listbox_cdiff (l->list, l->top);
if ((top + l->height == l->count) || l->height >= l->count)
one_vline ();
else
addch ('v');
/* Now draw the nice relative pointer */
if (l->count)
line = 1+ ((l->pos * (l->height-2)) / l->count);
else
line = 0;
for (i = 1; i < max_line; i++){
widget_move (&l->widget, i, l->width);
if (i != line)
one_vline ();
else
addch ('*');
}
}
static void
listbox_draw (WListbox *l, Dlg_head *h, int focused)
{
WLEntry *e;
int i;
int sel_line;
2000-04-18 12:58:42 +04:00
int normalc = NORMALC;
int selc;
1998-02-27 07:54:42 +03:00
char *text;
if (focused){
selc = FOCUSC;
} else {
selc = HOT_FOCUSC;
}
sel_line = -1;
for (e = l->top, i = 0; (i < l->height); i++){
/* Display the entry */
if (e == l->current && sel_line == -1){
sel_line = i;
attrset (selc);
} else
attrset (normalc);
widget_move (&l->widget, i, 0);
if ((i > 0 && e == l->list) || !l->list)
text = "";
else {
text = e->text;
e = e->next;
}
printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
}
l->cursor_y = sel_line;
if (!l->scrollbar)
return;
attrset (normalc);
listbox_drawscroll (l);
}
#endif /* HAVE_X */
1998-02-27 07:54:42 +03:00
/* Returns the number of items between s and e,
must be on the same linked list */
static int
listbox_cdiff (WLEntry *s, WLEntry *e)
{
int count;
for (count = 0; s != e; count++)
s = s->next;
return count;
}
static WLEntry *
listbox_check_hotkey (WListbox *l, int key)
{
int i;
WLEntry *e;
i = 0;
e = l->list;
if (!e)
return 0;
while (1){
/* If we didn't find anything, return */
if (i && e == l->list)
return 0;
if (e->hotkey == key)
return e;
i++;
e = e->next;
}
}
/* Used only for display updating, for avoiding line at a time scroll */
void
listbox_select_last (WListbox *l, int set_top)
{
if (l->list){
l->current = l->list->prev;
l->pos = l->count - 1;
if (set_top)
l->top = l->list->prev;
x_listbox_select_nth (l, l->pos);
}
}
void
listbox_remove_list (WListbox *l)
{
WLEntry *p, *q;
if (!l->count)
return;
#ifdef HAVE_X
if (l->widget.wdata != (widget_data) NULL) {
int i;
for (i = 0; i < l->count; i++)
x_listbox_delete_nth (l, i);
}
#endif
p = l->list;
while (l->count--) {
q = p->next;
g_free (p->text);
g_free (p);
1998-02-27 07:54:42 +03:00
p = q;
}
l->pos = l->count = 0;
l->list = l->top = l->current = 0;
}
/*
* bor 30.10.96: added force flag to remove *last* entry as well
* bor 30.10.96: corrected selection bug if last entry was removed
*/
void
listbox_remove_current (WListbox *l, int force)
{
WLEntry *p;
/* Ok, note: this won't allow for emtpy lists */
if (!force && (!l->count || l->count == 1))
return;
#ifdef HAVE_X
if (l->widget.wdata != (widget_data) NULL) {
x_listbox_delete_nth (l, l->pos);
if (l->count > 1) {
1998-02-27 07:54:42 +03:00
if (l->current->next != l->list)
x_listbox_select_nth (l, l->pos);
else if (l->current != l->list)
x_listbox_select_nth (l, l->pos - 1);
} else
1998-02-27 07:54:42 +03:00
x_listbox_select_nth (l, 0);
}
#endif
l->count--;
p = l->current;
if (l->count) {
l->current->next->prev = l->current->prev;
l->current->prev->next = l->current->next;
if (p->next == l->list) {
l->current = p->prev;
l->pos--;
}
else
l->current = p->next;
if (p == l->list)
l->list = l->top = p->next;
} else {
l->pos = 0;
l->list = l->top = l->current = 0;
}
g_free (p->text);
g_free (p);
1998-02-27 07:54:42 +03:00
}
/* Makes *e the selected entry (sets current and pos) */
void
listbox_select_entry (WListbox *l, WLEntry *dest)
{
WLEntry *e;
int pos;
int top_seen;
top_seen = 0;
/* Special case */
for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
if (e == l->top)
top_seen = 1;
if (e == dest){
l->current = e;
if (top_seen){
while (listbox_cdiff (l->top, l->current) >= l->height)
l->top = l->top->next;
} else {
l->top = l->current;
}
l->pos = pos;
x_listbox_select_nth (l, l->pos);
return;
}
}
/* If we are unable to find it, set decent values */
l->current = l->top = l->list;
l->pos = 0;
x_listbox_select_nth (l, l->pos);
}
/* Selects from base the pos element */
static WLEntry *
listbox_select_pos (WListbox *l, WLEntry *base, int pos)
{
WLEntry *last = l->list->prev;
if (base == last)
return last;
while (pos--){
base = base->next;
if (base == last)
break;
}
return base;
}
static inline int
listbox_back (WListbox *l)
{
if (l->pos){
listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
return 1;
}
return 0;
}
static inline int
listbox_fwd (WListbox *l)
{
if (l->current != l->list->prev){
listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
return 1;
}
return 0;
}
/* Returns 1 if we want a redraw */
static int
listbox_key (WListbox *l, int key)
{
int i;
int j = 0;
if (!l->list)
return 0;
switch (key){
case KEY_HOME:
case KEY_A1:
l->current = l->top = l->list;
l->pos = 0;
return 1;
case KEY_END:
case KEY_C1:
l->current = l->top = l->list->prev;
for (i = min (l->height - 1, l->count - 1); i; i--)
l->top = l->top->prev;
l->pos = l->count - 1;
return 1;
case XCTRL('p'):
case KEY_UP:
listbox_back (l);
return 1;
case XCTRL('n'):
case KEY_DOWN:
listbox_fwd (l);
return 1;
case KEY_NPAGE:
case XCTRL('v'):
for (i = 0; i < l->height-1; i++)
j |= listbox_fwd (l);
return j > 0;
case KEY_PPAGE:
case ALT('v'):
for (i = 0; i < l->height-1; i++)
j |= listbox_back (l);
return j > 0;
}
return 0;
}
static int listbox_event (Gpm_Event *event, WListbox *l);
static int
listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
{
WLEntry *e;
/* int selected_color; Never used */
int ret_code;
switch (msg){
case WIDGET_INIT:
return x_create_listbox (h, h->wdata, l);
case WIDGET_HOTKEY:
if ((e = listbox_check_hotkey (l, par)) != NULL){
listbox_select_entry (l, e);
/* Take the appropriate action */
if (l->action == listbox_finish){
l->widget.parent->running = 0;
l->widget.parent->ret_value = B_ENTER;
} else if (l->action == listbox_cback){
if ((*l->cback)(l) == listbox_finish){
l->widget.parent->running = 0;
l->widget.parent->ret_value = B_ENTER;
}
}
return 1;
} else
return 0;
case WIDGET_KEY:
if ((ret_code = listbox_key (l, par)))
listbox_draw (l, h, 1);
return ret_code;
#ifndef HAVE_X
case WIDGET_CURSOR:
widget_move (&l->widget, l->cursor_y, 0);
return 1;
case WIDGET_FOCUS:
case WIDGET_UNFOCUS:
case WIDGET_DRAW:
listbox_draw (l, h, msg != WIDGET_UNFOCUS);
return 1;
#endif
}
return default_proc (h, msg, par);
}
static int
listbox_event (Gpm_Event *event, WListbox *l)
{
#ifndef HAVE_X
int i;
Dlg_head *h = l->widget.parent;
/* Single click */
if (event->type & GPM_DOWN)
dlg_select_widget (l->widget.parent, l);
if (!l->list)
return MOU_NORMAL;
if (event->type & (GPM_DOWN|GPM_DRAG)){
if (event->x < 0 || event->x >= l->width)
return MOU_REPEAT;
if (event->y < 1)
for (i = -event->y; i >= 0; i--)
listbox_back (l);
else if (event->y > l->height)
for (i = event->y - l->height; i > 0; i--)
listbox_fwd (l);
else
listbox_select_entry (l, listbox_select_pos (l, l->top,
event->y - 1));
/* We need to refresh ourselves since the dialog manager doesn't */
/* know about this event */
listbox_callback (h, l, WIDGET_DRAW, 0);
mc_refresh ();
return MOU_REPEAT;
}
/* Double click */
if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
if (event->x < 0 || event->x >= l->width)
return MOU_NORMAL;
if (event->y < 1 || event->y > l->height)
return MOU_NORMAL;
dlg_select_widget (l->widget.parent, l);
listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
switch (l->action){
case listbox_nothing:
break;
case listbox_finish:
h->ret_value = B_ENTER;
dlg_stop (h);
1998-02-27 07:54:42 +03:00
return MOU_ENDLOOP;
case listbox_cback:
if ((*l->cback)(l) == listbox_finish)
return MOU_ENDLOOP;
}
}
#endif
return MOU_NORMAL;
}
static void
listbox_destroy (WListbox *l)
{
WLEntry *n, *p = l->list;
int i;
x_destroy_cmd (l);
for (i = 0; i < l->count; i++){
n = p->next;
g_free (p->text);
g_free (p);
1998-02-27 07:54:42 +03:00
p = n;
}
}
WListbox *
listbox_new (int y, int x, int width, int height,
int action, lcback callback, char *tkname)
{
WListbox *l = g_new (WListbox, 1);
1998-02-27 07:54:42 +03:00
extern int slow_terminal;
init_widget (&l->widget, y, x, height, width,
(callback_fn)listbox_callback,
(destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
l->list = l->top = l->current = 0;
l->pos = 0;
l->width = width;
if (height <= 0)
l->height = 1;
else
l->height = height;
1998-02-27 07:54:42 +03:00
l->count = 0;
l->top = 0;
l->current= 0;
l->cback = callback;
l->action = action;
l->allow_duplicates = 1;
l->scrollbar = slow_terminal ? 0 : 1;
widget_want_hotkey (l->widget, 1);
return l;
}
/* Listbox item adding function. They still lack a lot of functionality */
/* any takers? */
/* 1.11.96 bor: added pos argument to control placement of new entry */
static void
listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
{
if (!l->list){
l->list = e;
l->top = e;
l->current = e;
e->next = l->list;
e->prev = l->list;
} else if (pos == LISTBOX_APPEND_AT_END) {
e->next = l->list;
e->prev = l->list->prev;
l->list->prev->next = e;
l->list->prev = e;
} else if (pos == LISTBOX_APPEND_BEFORE){
e->next = l->current;
e->prev = l->current->prev;
l->current->prev->next = e;
l->current->prev = e;
if (l->list == l->current) { /* move list one position down */
l->list = e;
l->top = e;
}
} else if (pos == LISTBOX_APPEND_AFTER) {
e->prev = l->current;
e->next = l->current->next;
l->current->next->prev = e;
l->current->next = e;
}
x_list_insert (l, l->list, e);
l->count++;
}
char *
listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
void *data)
{
WLEntry *entry;
if (!l)
return 0;
if (!l->allow_duplicates)
if (listbox_search_text (l, text))
return 0;
entry = g_new (WLEntry, 1);
entry->text = g_strdup (text);
1998-02-27 07:54:42 +03:00
entry->data = data;
entry->hotkey = hotkey;
listbox_append_item (l, entry, pos);
return entry->text;
}
/* Selects the nth entry in the listbox */
void
listbox_select_by_number (WListbox *l, int n)
{
listbox_select_entry (l, listbox_select_pos (l, l->list, n));
}
WLEntry *
listbox_search_text (WListbox *l, char *text)
{
WLEntry *e;
e = l->list;
if (!e)
return NULL;
do {
if(!strcmp (e->text, text))
return e;
e = e->next;
} while (e!=l->list);
return NULL;
}
/* Returns the current string text as well as the associated extra data */
void
listbox_get_current (WListbox *l, char **string, char **extra)
{
if (!l->current){
*string = 0;
*extra = 0;
}
if (string && l->current)
*string = l->current->text;
if (extra && l->current)
*extra = l->current->data;
}
1998-12-03 00:27:27 +03:00
static int
1998-02-27 07:54:42 +03:00
buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
{
int i;
switch (msg){
case WIDGET_INIT:
return x_create_buttonbar (h, h->wdata, bb);
case WIDGET_FOCUS:
return 0;
case WIDGET_HOTKEY:
for (i = 0; i < 10; i++){
if (par == KEY_F(i+1) && bb->labels [i].function){
(*bb->labels [i].function)(bb->labels [i].data);
return 1;
}
}
return 0;
#ifndef HAVE_X
case WIDGET_DRAW:
if (!bb->visible)
return 1;
widget_move (&bb->widget, 0, 0);
attrset (DEFAULT_COLOR);
printw ("%-*s", bb->widget.cols - 1, "");
for (i = 0; i < COLS/8 && i < 10; i++){
widget_move (&bb->widget, 0, i*8);
attrset (DEFAULT_COLOR);
printw ("%d", i+1);
attrset (SELECTED_COLOR);
printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
bb->labels [i].text ? bb->labels [i].text : "");
attrset (DEFAULT_COLOR);
}
attrset (SELECTED_COLOR);
return 1;
#endif
}
return default_proc (h, msg, par);
}
static void
buttonbar_destroy (WButtonBar *bb)
{
int i;
for (i = 0; i < 10; i++){
if (bb->labels [i].text)
g_free (bb->labels [i].text);
1998-02-27 07:54:42 +03:00
}
}
static int
buttonbar_event (Gpm_Event *event, WButtonBar *bb)
{
#ifndef HAVE_X
int button;
if (!(event->type & GPM_UP))
return MOU_NORMAL;
if (event->y == 2)
return MOU_NORMAL;
button = event->x / 8;
if (button < 10 && bb->labels [button].function)
(*bb->labels [button].function)(bb->labels [button].data);
#endif
return MOU_NORMAL;
}
WButtonBar *
buttonbar_new (int visible)
{
int i;
WButtonBar *bb = g_new (WButtonBar, 1);
1998-02-27 07:54:42 +03:00
init_widget (&bb->widget, LINES-1, 0, 1, COLS,
(callback_fn) buttonbar_callback,
(destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
bb->visible = visible;
for (i = 0; i < 10; i++){
bb->labels [i].text = 0;
bb->labels [i].function = 0;
}
widget_want_hotkey (bb->widget, 1);
widget_want_cursor (bb->widget, 0);
return bb;
}
void
set_label_text (WButtonBar *bb, int index, char *text)
{
if (bb->labels [index-1].text)
g_free (bb->labels [index-1].text);
1998-02-27 07:54:42 +03:00
bb->labels [index-1].text = g_strdup (text);
1998-02-27 07:54:42 +03:00
}
/* paneletc is either the panel widget, or info or view or tree widget */
WButtonBar *
find_buttonbar (Dlg_head *h, Widget *paneletc)
{
WButtonBar *bb;
Widget_Item *item;
int i;
bb = 0;
for (i = 0, item = h->current; i < h->count; i++, item = item->next){
if (item->widget->callback == (callback_fn) buttonbar_callback){
bb = (WButtonBar *) item->widget;
break;
}
}
return bb;
}
void
define_label_data (Dlg_head *h, Widget *paneletc, int idx, char *text,
buttonbarfn cback, void *data)
{
WButtonBar *bb = find_buttonbar (h, paneletc);
if (!bb)
return;
set_label_text (bb, idx, text);
bb->labels [idx-1].function = (void (*)(void *)) cback;
bb->labels [idx-1].data = data;
x_redefine_label (bb, idx);
}
void
define_label (Dlg_head *h, Widget *paneletc, int idx, char *text, void (*cback)(void))
{
define_label_data (h, paneletc, idx, text, (void (*)(void *)) cback, 0);
}
#ifdef HAVE_X
void redraw_labels (Dlg_head *h, Widget *paneletc)
{
}
#else
void
redraw_labels (Dlg_head *h, Widget *paneletc)
{
Widget_Item *item;
int i;
for (i = 0, item = h->current; i < h->count; i++, item = item->next){
if (item->widget->callback == (callback_fn) buttonbar_callback){
widget_redraw (h, item);
return;
}
}
}
#endif